Merge tag 'icc-6.1-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/djakov/icc...
[platform/kernel/linux-starfive.git] / drivers / gpio / gpio-moxtet.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  *  Turris Mox Moxtet GPIO expander
4  *
5  *  Copyright (C) 2018 Marek BehĂșn <kabel@kernel.org>
6  */
7
8 #include <linux/bitops.h>
9 #include <linux/gpio/driver.h>
10 #include <linux/moxtet.h>
11 #include <linux/module.h>
12
13 #define MOXTET_GPIO_NGPIOS      12
14 #define MOXTET_GPIO_INPUTS      4
15
16 struct moxtet_gpio_desc {
17         u16 in_mask;
18         u16 out_mask;
19 };
20
21 static const struct moxtet_gpio_desc descs[] = {
22         [TURRIS_MOX_MODULE_SFP] = {
23                 .in_mask = GENMASK(2, 0),
24                 .out_mask = GENMASK(5, 4),
25         },
26 };
27
28 struct moxtet_gpio_chip {
29         struct device                   *dev;
30         struct gpio_chip                gpio_chip;
31         const struct moxtet_gpio_desc   *desc;
32 };
33
34 static int moxtet_gpio_get_value(struct gpio_chip *gc, unsigned int offset)
35 {
36         struct moxtet_gpio_chip *chip = gpiochip_get_data(gc);
37         int ret;
38
39         if (chip->desc->in_mask & BIT(offset)) {
40                 ret = moxtet_device_read(chip->dev);
41         } else if (chip->desc->out_mask & BIT(offset)) {
42                 ret = moxtet_device_written(chip->dev);
43                 if (ret >= 0)
44                         ret <<= MOXTET_GPIO_INPUTS;
45         } else {
46                 return -EINVAL;
47         }
48
49         if (ret < 0)
50                 return ret;
51
52         return !!(ret & BIT(offset));
53 }
54
55 static void moxtet_gpio_set_value(struct gpio_chip *gc, unsigned int offset,
56                                   int val)
57 {
58         struct moxtet_gpio_chip *chip = gpiochip_get_data(gc);
59         int state;
60
61         state = moxtet_device_written(chip->dev);
62         if (state < 0)
63                 return;
64
65         offset -= MOXTET_GPIO_INPUTS;
66
67         if (val)
68                 state |= BIT(offset);
69         else
70                 state &= ~BIT(offset);
71
72         moxtet_device_write(chip->dev, state);
73 }
74
75 static int moxtet_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
76 {
77         struct moxtet_gpio_chip *chip = gpiochip_get_data(gc);
78
79         /* All lines are hard wired to be either input or output, not both. */
80         if (chip->desc->in_mask & BIT(offset))
81                 return GPIO_LINE_DIRECTION_IN;
82         else if (chip->desc->out_mask & BIT(offset))
83                 return GPIO_LINE_DIRECTION_OUT;
84         else
85                 return -EINVAL;
86 }
87
88 static int moxtet_gpio_direction_input(struct gpio_chip *gc,
89                                        unsigned int offset)
90 {
91         struct moxtet_gpio_chip *chip = gpiochip_get_data(gc);
92
93         if (chip->desc->in_mask & BIT(offset))
94                 return 0;
95         else if (chip->desc->out_mask & BIT(offset))
96                 return -ENOTSUPP;
97         else
98                 return -EINVAL;
99 }
100
101 static int moxtet_gpio_direction_output(struct gpio_chip *gc,
102                                         unsigned int offset, int val)
103 {
104         struct moxtet_gpio_chip *chip = gpiochip_get_data(gc);
105
106         if (chip->desc->out_mask & BIT(offset))
107                 moxtet_gpio_set_value(gc, offset, val);
108         else if (chip->desc->in_mask & BIT(offset))
109                 return -ENOTSUPP;
110         else
111                 return -EINVAL;
112
113         return 0;
114 }
115
116 static int moxtet_gpio_probe(struct device *dev)
117 {
118         struct moxtet_gpio_chip *chip;
119         struct device_node *nc = dev->of_node;
120         int id;
121
122         id = to_moxtet_device(dev)->id;
123
124         if (id >= ARRAY_SIZE(descs)) {
125                 dev_err(dev, "%pOF Moxtet device id 0x%x is not supported by gpio-moxtet driver\n",
126                         nc, id);
127                 return -ENOTSUPP;
128         }
129
130         chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
131         if (!chip)
132                 return -ENOMEM;
133
134         chip->dev = dev;
135         chip->gpio_chip.parent = dev;
136         chip->desc = &descs[id];
137
138         dev_set_drvdata(dev, chip);
139
140         chip->gpio_chip.label = dev_name(dev);
141         chip->gpio_chip.get_direction = moxtet_gpio_get_direction;
142         chip->gpio_chip.direction_input = moxtet_gpio_direction_input;
143         chip->gpio_chip.direction_output = moxtet_gpio_direction_output;
144         chip->gpio_chip.get = moxtet_gpio_get_value;
145         chip->gpio_chip.set = moxtet_gpio_set_value;
146         chip->gpio_chip.base = -1;
147
148         chip->gpio_chip.ngpio = MOXTET_GPIO_NGPIOS;
149
150         chip->gpio_chip.can_sleep = true;
151         chip->gpio_chip.owner = THIS_MODULE;
152
153         return devm_gpiochip_add_data(dev, &chip->gpio_chip, chip);
154 }
155
156 static const struct of_device_id moxtet_gpio_dt_ids[] = {
157         { .compatible = "cznic,moxtet-gpio", },
158         {},
159 };
160 MODULE_DEVICE_TABLE(of, moxtet_gpio_dt_ids);
161
162 static const enum turris_mox_module_id moxtet_gpio_module_table[] = {
163         TURRIS_MOX_MODULE_SFP,
164         0,
165 };
166
167 static struct moxtet_driver moxtet_gpio_driver = {
168         .driver = {
169                 .name           = "moxtet-gpio",
170                 .of_match_table = moxtet_gpio_dt_ids,
171                 .probe          = moxtet_gpio_probe,
172         },
173         .id_table = moxtet_gpio_module_table,
174 };
175 module_moxtet_driver(moxtet_gpio_driver);
176
177 MODULE_AUTHOR("Marek Behun <kabel@kernel.org>");
178 MODULE_DESCRIPTION("Turris Mox Moxtet GPIO expander");
179 MODULE_LICENSE("GPL v2");