Commit | Line | Data |
---|---|---|
ebe36319 MW |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* | |
3 | * regmap based generic GPIO driver | |
4 | * | |
5 | * Copyright 2020 Michael Walle <michael@walle.cc> | |
6 | */ | |
7 | ||
e79098ac AS |
8 | #include <linux/bits.h> |
9 | #include <linux/device.h> | |
10 | #include <linux/err.h> | |
11 | #include <linux/io.h> | |
ebe36319 MW |
12 | #include <linux/module.h> |
13 | #include <linux/regmap.h> | |
e79098ac AS |
14 | #include <linux/slab.h> |
15 | #include <linux/types.h> | |
16 | ||
17 | #include <linux/gpio/driver.h> | |
18 | #include <linux/gpio/regmap.h> | |
ebe36319 MW |
19 | |
20 | struct gpio_regmap { | |
21 | struct device *parent; | |
22 | struct regmap *regmap; | |
23 | struct gpio_chip gpio_chip; | |
24 | ||
25 | int reg_stride; | |
26 | int ngpio_per_reg; | |
27 | unsigned int reg_dat_base; | |
28 | unsigned int reg_set_base; | |
29 | unsigned int reg_clr_base; | |
30 | unsigned int reg_dir_in_base; | |
31 | unsigned int reg_dir_out_base; | |
32 | ||
33 | int (*reg_mask_xlate)(struct gpio_regmap *gpio, unsigned int base, | |
34 | unsigned int offset, unsigned int *reg, | |
35 | unsigned int *mask); | |
36 | ||
37 | void *driver_data; | |
38 | }; | |
39 | ||
40 | static unsigned int gpio_regmap_addr(unsigned int addr) | |
41 | { | |
42 | if (addr == GPIO_REGMAP_ADDR_ZERO) | |
43 | return 0; | |
44 | ||
45 | return addr; | |
46 | } | |
47 | ||
48 | static int gpio_regmap_simple_xlate(struct gpio_regmap *gpio, | |
49 | unsigned int base, unsigned int offset, | |
50 | unsigned int *reg, unsigned int *mask) | |
51 | { | |
52 | unsigned int line = offset % gpio->ngpio_per_reg; | |
53 | unsigned int stride = offset / gpio->ngpio_per_reg; | |
54 | ||
55 | *reg = base + stride * gpio->reg_stride; | |
56 | *mask = BIT(line); | |
57 | ||
58 | return 0; | |
59 | } | |
60 | ||
61 | static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset) | |
62 | { | |
63 | struct gpio_regmap *gpio = gpiochip_get_data(chip); | |
64 | unsigned int base, val, reg, mask; | |
65 | int ret; | |
66 | ||
67 | /* we might not have an output register if we are input only */ | |
68 | if (gpio->reg_dat_base) | |
69 | base = gpio_regmap_addr(gpio->reg_dat_base); | |
70 | else | |
71 | base = gpio_regmap_addr(gpio->reg_set_base); | |
72 | ||
73 | ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask); | |
74 | if (ret) | |
75 | return ret; | |
76 | ||
77 | ret = regmap_read(gpio->regmap, reg, &val); | |
78 | if (ret) | |
79 | return ret; | |
80 | ||
81 | return !!(val & mask); | |
82 | } | |
83 | ||
84 | static void gpio_regmap_set(struct gpio_chip *chip, unsigned int offset, | |
85 | int val) | |
86 | { | |
87 | struct gpio_regmap *gpio = gpiochip_get_data(chip); | |
88 | unsigned int base = gpio_regmap_addr(gpio->reg_set_base); | |
89 | unsigned int reg, mask; | |
90 | ||
91 | gpio->reg_mask_xlate(gpio, base, offset, ®, &mask); | |
92 | if (val) | |
93 | regmap_update_bits(gpio->regmap, reg, mask, mask); | |
94 | else | |
95 | regmap_update_bits(gpio->regmap, reg, mask, 0); | |
96 | } | |
97 | ||
98 | static void gpio_regmap_set_with_clear(struct gpio_chip *chip, | |
99 | unsigned int offset, int val) | |
100 | { | |
101 | struct gpio_regmap *gpio = gpiochip_get_data(chip); | |
102 | unsigned int base, reg, mask; | |
103 | ||
104 | if (val) | |
105 | base = gpio_regmap_addr(gpio->reg_set_base); | |
106 | else | |
107 | base = gpio_regmap_addr(gpio->reg_clr_base); | |
108 | ||
109 | gpio->reg_mask_xlate(gpio, base, offset, ®, &mask); | |
110 | regmap_write(gpio->regmap, reg, mask); | |
111 | } | |
112 | ||
113 | static int gpio_regmap_get_direction(struct gpio_chip *chip, | |
114 | unsigned int offset) | |
115 | { | |
116 | struct gpio_regmap *gpio = gpiochip_get_data(chip); | |
117 | unsigned int base, val, reg, mask; | |
118 | int invert, ret; | |
119 | ||
8978277c WBG |
120 | if (gpio->reg_dat_base && !gpio->reg_set_base) |
121 | return GPIO_LINE_DIRECTION_IN; | |
122 | if (gpio->reg_set_base && !gpio->reg_dat_base) | |
123 | return GPIO_LINE_DIRECTION_OUT; | |
124 | ||
ebe36319 MW |
125 | if (gpio->reg_dir_out_base) { |
126 | base = gpio_regmap_addr(gpio->reg_dir_out_base); | |
127 | invert = 0; | |
128 | } else if (gpio->reg_dir_in_base) { | |
129 | base = gpio_regmap_addr(gpio->reg_dir_in_base); | |
130 | invert = 1; | |
131 | } else { | |
132 | return -EOPNOTSUPP; | |
133 | } | |
134 | ||
135 | ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask); | |
136 | if (ret) | |
137 | return ret; | |
138 | ||
139 | ret = regmap_read(gpio->regmap, reg, &val); | |
140 | if (ret) | |
141 | return ret; | |
142 | ||
143 | if (!!(val & mask) ^ invert) | |
144 | return GPIO_LINE_DIRECTION_OUT; | |
145 | else | |
146 | return GPIO_LINE_DIRECTION_IN; | |
147 | } | |
148 | ||
149 | static int gpio_regmap_set_direction(struct gpio_chip *chip, | |
150 | unsigned int offset, bool output) | |
151 | { | |
152 | struct gpio_regmap *gpio = gpiochip_get_data(chip); | |
153 | unsigned int base, val, reg, mask; | |
154 | int invert, ret; | |
155 | ||
156 | if (gpio->reg_dir_out_base) { | |
157 | base = gpio_regmap_addr(gpio->reg_dir_out_base); | |
158 | invert = 0; | |
159 | } else if (gpio->reg_dir_in_base) { | |
160 | base = gpio_regmap_addr(gpio->reg_dir_in_base); | |
161 | invert = 1; | |
162 | } else { | |
163 | return -EOPNOTSUPP; | |
164 | } | |
165 | ||
166 | ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask); | |
167 | if (ret) | |
168 | return ret; | |
169 | ||
170 | if (invert) | |
171 | val = output ? 0 : mask; | |
172 | else | |
173 | val = output ? mask : 0; | |
174 | ||
175 | return regmap_update_bits(gpio->regmap, reg, mask, val); | |
176 | } | |
177 | ||
178 | static int gpio_regmap_direction_input(struct gpio_chip *chip, | |
179 | unsigned int offset) | |
180 | { | |
181 | return gpio_regmap_set_direction(chip, offset, false); | |
182 | } | |
183 | ||
184 | static int gpio_regmap_direction_output(struct gpio_chip *chip, | |
185 | unsigned int offset, int value) | |
186 | { | |
187 | gpio_regmap_set(chip, offset, value); | |
188 | ||
189 | return gpio_regmap_set_direction(chip, offset, true); | |
190 | } | |
191 | ||
ebe36319 MW |
192 | void *gpio_regmap_get_drvdata(struct gpio_regmap *gpio) |
193 | { | |
194 | return gpio->driver_data; | |
195 | } | |
196 | EXPORT_SYMBOL_GPL(gpio_regmap_get_drvdata); | |
197 | ||
198 | /** | |
199 | * gpio_regmap_register() - Register a generic regmap GPIO controller | |
200 | * @config: configuration for gpio_regmap | |
201 | * | |
202 | * Return: A pointer to the registered gpio_regmap or ERR_PTR error value. | |
203 | */ | |
204 | struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config) | |
205 | { | |
206 | struct gpio_regmap *gpio; | |
207 | struct gpio_chip *chip; | |
208 | int ret; | |
209 | ||
210 | if (!config->parent) | |
211 | return ERR_PTR(-EINVAL); | |
212 | ||
213 | if (!config->ngpio) | |
214 | return ERR_PTR(-EINVAL); | |
215 | ||
216 | /* we need at least one */ | |
217 | if (!config->reg_dat_base && !config->reg_set_base) | |
218 | return ERR_PTR(-EINVAL); | |
219 | ||
220 | /* if we have a direction register we need both input and output */ | |
221 | if ((config->reg_dir_out_base || config->reg_dir_in_base) && | |
222 | (!config->reg_dat_base || !config->reg_set_base)) | |
223 | return ERR_PTR(-EINVAL); | |
224 | ||
225 | /* we don't support having both registers simultaneously for now */ | |
226 | if (config->reg_dir_out_base && config->reg_dir_in_base) | |
227 | return ERR_PTR(-EINVAL); | |
228 | ||
229 | gpio = kzalloc(sizeof(*gpio), GFP_KERNEL); | |
230 | if (!gpio) | |
231 | return ERR_PTR(-ENOMEM); | |
232 | ||
233 | gpio->parent = config->parent; | |
9b3c47f1 | 234 | gpio->driver_data = config->drvdata; |
ebe36319 MW |
235 | gpio->regmap = config->regmap; |
236 | gpio->ngpio_per_reg = config->ngpio_per_reg; | |
237 | gpio->reg_stride = config->reg_stride; | |
238 | gpio->reg_mask_xlate = config->reg_mask_xlate; | |
239 | gpio->reg_dat_base = config->reg_dat_base; | |
240 | gpio->reg_set_base = config->reg_set_base; | |
241 | gpio->reg_clr_base = config->reg_clr_base; | |
242 | gpio->reg_dir_in_base = config->reg_dir_in_base; | |
243 | gpio->reg_dir_out_base = config->reg_dir_out_base; | |
244 | ||
245 | /* if not set, assume there is only one register */ | |
246 | if (!gpio->ngpio_per_reg) | |
247 | gpio->ngpio_per_reg = config->ngpio; | |
248 | ||
249 | /* if not set, assume they are consecutive */ | |
250 | if (!gpio->reg_stride) | |
251 | gpio->reg_stride = 1; | |
252 | ||
253 | if (!gpio->reg_mask_xlate) | |
254 | gpio->reg_mask_xlate = gpio_regmap_simple_xlate; | |
255 | ||
256 | chip = &gpio->gpio_chip; | |
257 | chip->parent = config->parent; | |
f21ecad4 | 258 | chip->fwnode = config->fwnode; |
ebe36319 MW |
259 | chip->base = -1; |
260 | chip->ngpio = config->ngpio; | |
261 | chip->names = config->names; | |
262 | chip->label = config->label ?: dev_name(config->parent); | |
297a44f6 | 263 | chip->can_sleep = regmap_might_sleep(config->regmap); |
ebe36319 MW |
264 | |
265 | chip->get = gpio_regmap_get; | |
266 | if (gpio->reg_set_base && gpio->reg_clr_base) | |
267 | chip->set = gpio_regmap_set_with_clear; | |
268 | else if (gpio->reg_set_base) | |
269 | chip->set = gpio_regmap_set; | |
270 | ||
8978277c | 271 | chip->get_direction = gpio_regmap_get_direction; |
ebe36319 | 272 | if (gpio->reg_dir_in_base || gpio->reg_dir_out_base) { |
ebe36319 MW |
273 | chip->direction_input = gpio_regmap_direction_input; |
274 | chip->direction_output = gpio_regmap_direction_output; | |
275 | } | |
276 | ||
277 | ret = gpiochip_add_data(chip, gpio); | |
278 | if (ret < 0) | |
279 | goto err_free_gpio; | |
280 | ||
281 | if (config->irq_domain) { | |
282 | ret = gpiochip_irqchip_add_domain(chip, config->irq_domain); | |
283 | if (ret) | |
284 | goto err_remove_gpiochip; | |
285 | } | |
286 | ||
287 | return gpio; | |
288 | ||
289 | err_remove_gpiochip: | |
290 | gpiochip_remove(chip); | |
291 | err_free_gpio: | |
292 | kfree(gpio); | |
293 | return ERR_PTR(ret); | |
294 | } | |
295 | EXPORT_SYMBOL_GPL(gpio_regmap_register); | |
296 | ||
297 | /** | |
298 | * gpio_regmap_unregister() - Unregister a generic regmap GPIO controller | |
299 | * @gpio: gpio_regmap device to unregister | |
300 | */ | |
301 | void gpio_regmap_unregister(struct gpio_regmap *gpio) | |
302 | { | |
303 | gpiochip_remove(&gpio->gpio_chip); | |
304 | kfree(gpio); | |
305 | } | |
306 | EXPORT_SYMBOL_GPL(gpio_regmap_unregister); | |
307 | ||
40e568f9 | 308 | static void devm_gpio_regmap_unregister(void *res) |
ebe36319 | 309 | { |
40e568f9 | 310 | gpio_regmap_unregister(res); |
ebe36319 MW |
311 | } |
312 | ||
313 | /** | |
314 | * devm_gpio_regmap_register() - resource managed gpio_regmap_register() | |
315 | * @dev: device that is registering this GPIO device | |
316 | * @config: configuration for gpio_regmap | |
317 | * | |
318 | * Managed gpio_regmap_register(). For generic regmap GPIO device registered by | |
319 | * this function, gpio_regmap_unregister() is automatically called on driver | |
320 | * detach. See gpio_regmap_register() for more information. | |
321 | * | |
322 | * Return: A pointer to the registered gpio_regmap or ERR_PTR error value. | |
323 | */ | |
324 | struct gpio_regmap *devm_gpio_regmap_register(struct device *dev, | |
325 | const struct gpio_regmap_config *config) | |
326 | { | |
40e568f9 MV |
327 | struct gpio_regmap *gpio; |
328 | int ret; | |
ebe36319 MW |
329 | |
330 | gpio = gpio_regmap_register(config); | |
40e568f9 MV |
331 | |
332 | if (IS_ERR(gpio)) | |
333 | return gpio; | |
334 | ||
335 | ret = devm_add_action_or_reset(dev, devm_gpio_regmap_unregister, gpio); | |
336 | if (ret) | |
337 | return ERR_PTR(ret); | |
ebe36319 MW |
338 | |
339 | return gpio; | |
340 | } | |
341 | EXPORT_SYMBOL_GPL(devm_gpio_regmap_register); | |
342 | ||
343 | MODULE_AUTHOR("Michael Walle <michael@walle.cc>"); | |
344 | MODULE_DESCRIPTION("GPIO generic regmap driver core"); | |
345 | MODULE_LICENSE("GPL"); |