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