File Index
Files and directories
Sign in
FILE

gpio-regmap.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpio/gpio-regmap.c C
  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/bits.h>
  9#include <linux/device.h>
 10#include <linux/err.h>
 11#include <linux/io.h>
 12#include <linux/module.h>
 13#include <linux/regmap.h>
 14#include <linux/slab.h>
 15#include <linux/types.h>
 16
 17#include <linux/gpio/driver.h>
 18#include <linux/gpio/regmap.h>
 19
 20#include "gpiolib.h"
 21
 22struct gpio_regmap {
 23	struct device *parent;
 24	struct regmap *regmap;
 25	struct gpio_chip gpio_chip;
 26
 27	int reg_stride;
 28	int ngpio_per_reg;
 29	unsigned int reg_dat_base;
 30	unsigned int reg_set_base;
 31	unsigned int reg_clr_base;
 32	unsigned int reg_dir_in_base;
 33	unsigned int reg_dir_out_base;
 34	unsigned long *fixed_direction_output;
 35
 36#ifdef CONFIG_REGMAP_IRQ
 37	int regmap_irq_line;
 38	struct regmap_irq_chip_data *irq_chip_data;
 39#endif
 40
 41	int (*reg_mask_xlate)(struct gpio_regmap *gpio, unsigned int base,
 42			      unsigned int offset, unsigned int *reg,
 43			      unsigned int *mask);
 44
 45	void *driver_data;
 46};
 47
 48static unsigned int gpio_regmap_addr(unsigned int addr)
 49{
 50	if (addr == GPIO_REGMAP_ADDR_ZERO)
 51		return 0;
 52
 53	return addr;
 54}
 55
 56static int gpio_regmap_simple_xlate(struct gpio_regmap *gpio,
 57				    unsigned int base, unsigned int offset,
 58				    unsigned int *reg, unsigned int *mask)
 59{
 60	unsigned int line = offset % gpio->ngpio_per_reg;
 61	unsigned int stride = offset / gpio->ngpio_per_reg;
 62
 63	*reg = base + stride * gpio->reg_stride;
 64	*mask = BIT(line);
 65
 66	return 0;
 67}
 68
 69static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset)
 70{
 71	struct gpio_regmap *gpio = gpiochip_get_data(chip);
 72	unsigned int base, val, reg, mask;
 73	int ret;
 74
 75	/* we might not have an output register if we are input only */
 76	if (gpio->reg_dat_base)
 77		base = gpio_regmap_addr(gpio->reg_dat_base);
 78	else
 79		base = gpio_regmap_addr(gpio->reg_set_base);
 80
 81	ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
 82	if (ret)
 83		return ret;
 84
 85	/* ensure we don't spoil any register cache with pin input values */
 86	if (gpio->reg_dat_base == gpio->reg_set_base)
 87		ret = regmap_read_bypassed(gpio->regmap, reg, &val);
 88	else
 89		ret = regmap_read(gpio->regmap, reg, &val);
 90	if (ret)
 91		return ret;
 92
 93	return !!(val & mask);
 94}
 95
 96static int gpio_regmap_set(struct gpio_chip *chip, unsigned int offset,
 97			   int val)
 98{
 99	struct gpio_regmap *gpio = gpiochip_get_data(chip);
100	unsigned int base = gpio_regmap_addr(gpio->reg_set_base);
101	unsigned int reg, mask, mask_val;
102	int ret;
103
104	ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
105	if (ret)
106		return ret;
107
108	if (val)
109		mask_val = mask;
110	else
111		mask_val = 0;
112
113	/* ignore input values which shadow the old output value */
114	if (gpio->reg_dat_base == gpio->reg_set_base)
115		ret = regmap_write_bits(gpio->regmap, reg, mask, mask_val);
116	else
117		ret = regmap_update_bits(gpio->regmap, reg, mask, mask_val);
118
119	return ret;
120}
121
122static int gpio_regmap_set_with_clear(struct gpio_chip *chip,
123				      unsigned int offset, int val)
124{
125	struct gpio_regmap *gpio = gpiochip_get_data(chip);
126	unsigned int base, reg, mask;
127	int ret;
128
129	if (val)
130		base = gpio_regmap_addr(gpio->reg_set_base);
131	else
132		base = gpio_regmap_addr(gpio->reg_clr_base);
133
134	ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
135	if (ret)
136		return ret;
137
138	return regmap_write(gpio->regmap, reg, mask);
139}
140
141static int gpio_regmap_get_direction(struct gpio_chip *chip,
142				     unsigned int offset)
143{
144	struct gpio_regmap *gpio = gpiochip_get_data(chip);
145	unsigned int base, val, reg, mask;
146	int invert, ret;
147
148	if (gpio->fixed_direction_output) {
149		if (test_bit(offset, gpio->fixed_direction_output))
150			return GPIO_LINE_DIRECTION_OUT;
151		else
152			return GPIO_LINE_DIRECTION_IN;
153	}
154
155	if (gpio->reg_dat_base && !gpio->reg_set_base)
156		return GPIO_LINE_DIRECTION_IN;
157	if (gpio->reg_set_base && !gpio->reg_dat_base)
158		return GPIO_LINE_DIRECTION_OUT;
159
160	if (gpio->reg_dir_out_base) {
161		base = gpio_regmap_addr(gpio->reg_dir_out_base);
162		invert = 0;
163	} else if (gpio->reg_dir_in_base) {
164		base = gpio_regmap_addr(gpio->reg_dir_in_base);
165		invert = 1;
166	} else {
167		return -ENOTSUPP;
168	}
169
170	ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
171	if (ret)
172		return ret;
173
174	ret = regmap_read(gpio->regmap, reg, &val);
175	if (ret)
176		return ret;
177
178	if (!!(val & mask) ^ invert)
179		return GPIO_LINE_DIRECTION_OUT;
180	else
181		return GPIO_LINE_DIRECTION_IN;
182}
183
184static int gpio_regmap_set_direction(struct gpio_chip *chip,
185				     unsigned int offset, bool output)
186{
187	struct gpio_regmap *gpio = gpiochip_get_data(chip);
188	unsigned int base, val, reg, mask;
189	int invert, ret;
190
191	if (gpio->reg_dir_out_base) {
192		base = gpio_regmap_addr(gpio->reg_dir_out_base);
193		invert = 0;
194	} else if (gpio->reg_dir_in_base) {
195		base = gpio_regmap_addr(gpio->reg_dir_in_base);
196		invert = 1;
197	} else {
198		return -ENOTSUPP;
199	}
200
201	ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
202	if (ret)
203		return ret;
204
205	if (invert)
206		val = output ? 0 : mask;
207	else
208		val = output ? mask : 0;
209
210	return regmap_update_bits(gpio->regmap, reg, mask, val);
211}
212
213static int gpio_regmap_direction_input(struct gpio_chip *chip,
214				       unsigned int offset)
215{
216	return gpio_regmap_set_direction(chip, offset, false);
217}
218
219static int gpio_regmap_direction_output(struct gpio_chip *chip,
220					unsigned int offset, int value)
221{
222	gpio_regmap_set(chip, offset, value);
223
224	return gpio_regmap_set_direction(chip, offset, true);
225}
226
227void *gpio_regmap_get_drvdata(struct gpio_regmap *gpio)
228{
229	return gpio->driver_data;
230}
231EXPORT_SYMBOL_GPL(gpio_regmap_get_drvdata);
232
233/**
234 * gpio_regmap_register() - Register a generic regmap GPIO controller
235 * @config: configuration for gpio_regmap
236 *
237 * Return: A pointer to the registered gpio_regmap or ERR_PTR error value.
238 */
239struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config)
240{
241	struct irq_domain *irq_domain;
242	struct gpio_regmap *gpio;
243	struct gpio_chip *chip;
244	int ret;
245
246	if (!config->parent)
247		return ERR_PTR(-EINVAL);
248
249	/* we need at least one */
250	if (!config->reg_dat_base && !config->reg_set_base)
251		return ERR_PTR(-EINVAL);
252
253	/* if we have a direction register we need both input and output */
254	if ((config->reg_dir_out_base || config->reg_dir_in_base) &&
255	    (!config->reg_dat_base || !config->reg_set_base))
256		return ERR_PTR(-EINVAL);
257
258	/* we don't support having both registers simultaneously for now */
259	if (config->reg_dir_out_base && config->reg_dir_in_base)
260		return ERR_PTR(-EINVAL);
261
262	gpio = kzalloc(sizeof(*gpio), GFP_KERNEL);
263	if (!gpio)
264		return ERR_PTR(-ENOMEM);
265
266	gpio->parent = config->parent;
267	gpio->driver_data = config->drvdata;
268	gpio->regmap = config->regmap;
269	gpio->reg_dat_base = config->reg_dat_base;
270	gpio->reg_set_base = config->reg_set_base;
271	gpio->reg_clr_base = config->reg_clr_base;
272	gpio->reg_dir_in_base = config->reg_dir_in_base;
273	gpio->reg_dir_out_base = config->reg_dir_out_base;
274
275	chip = &gpio->gpio_chip;
276	chip->parent = config->parent;
277	chip->fwnode = config->fwnode;
278	chip->base = -1;
279	chip->names = config->names;
280	chip->label = config->label ?: dev_name(config->parent);
281	chip->can_sleep = regmap_might_sleep(config->regmap);
282	chip->init_valid_mask = config->init_valid_mask;
283
284	chip->request = gpiochip_generic_request;
285	chip->free = gpiochip_generic_free;
286	chip->get = gpio_regmap_get;
287	if (gpio->reg_set_base && gpio->reg_clr_base)
288		chip->set = gpio_regmap_set_with_clear;
289	else if (gpio->reg_set_base)
290		chip->set = gpio_regmap_set;
291
292	chip->get_direction = gpio_regmap_get_direction;
293	if (gpio->reg_dir_in_base || gpio->reg_dir_out_base) {
294		chip->direction_input = gpio_regmap_direction_input;
295		chip->direction_output = gpio_regmap_direction_output;
296	}
297
298	chip->ngpio = config->ngpio;
299	if (!chip->ngpio) {
300		ret = gpiochip_get_ngpios(chip, chip->parent);
301		if (ret)
302			goto err_free_gpio;
303	}
304
305	if (config->fixed_direction_output) {
306		gpio->fixed_direction_output = bitmap_alloc(chip->ngpio,
307							    GFP_KERNEL);
308		if (!gpio->fixed_direction_output) {
309			ret = -ENOMEM;
310			goto err_free_gpio;
311		}
312		bitmap_copy(gpio->fixed_direction_output,
313			    config->fixed_direction_output, chip->ngpio);
314	}
315
316	/* if not set, assume there is only one register */
317	gpio->ngpio_per_reg = config->ngpio_per_reg;
318	if (!gpio->ngpio_per_reg)
319		gpio->ngpio_per_reg = config->ngpio;
320
321	/* if not set, assume they are consecutive */
322	gpio->reg_stride = config->reg_stride;
323	if (!gpio->reg_stride)
324		gpio->reg_stride = 1;
325
326	gpio->reg_mask_xlate = config->reg_mask_xlate;
327	if (!gpio->reg_mask_xlate)
328		gpio->reg_mask_xlate = gpio_regmap_simple_xlate;
329
330	ret = gpiochip_add_data(chip, gpio);
331	if (ret < 0)
332		goto err_free_bitmap;
333
334#ifdef CONFIG_REGMAP_IRQ
335	if (config->regmap_irq_chip) {
336		gpio->regmap_irq_line = config->regmap_irq_line;
337		ret = regmap_add_irq_chip_fwnode(dev_fwnode(config->parent), config->regmap,
338						 config->regmap_irq_line, config->regmap_irq_flags,
339						 0, config->regmap_irq_chip, &gpio->irq_chip_data);
340		if (ret)
341			goto err_remove_gpiochip;
342
343		irq_domain = regmap_irq_get_domain(gpio->irq_chip_data);
344	} else
345#endif
346	irq_domain = config->irq_domain;
347
348	if (irq_domain) {
349		ret = gpiochip_irqchip_add_domain(chip, irq_domain);
350		if (ret)
351			goto err_remove_gpiochip;
352	}
353
354	return gpio;
355
356err_remove_gpiochip:
357	gpiochip_remove(chip);
358err_free_bitmap:
359	bitmap_free(gpio->fixed_direction_output);
360err_free_gpio:
361	kfree(gpio);
362	return ERR_PTR(ret);
363}
364EXPORT_SYMBOL_GPL(gpio_regmap_register);
365
366/**
367 * gpio_regmap_unregister() - Unregister a generic regmap GPIO controller
368 * @gpio: gpio_regmap device to unregister
369 */
370void gpio_regmap_unregister(struct gpio_regmap *gpio)
371{
372#ifdef CONFIG_REGMAP_IRQ
373	if (gpio->irq_chip_data)
374		regmap_del_irq_chip(gpio->regmap_irq_line, gpio->irq_chip_data);
375#endif
376
377	gpiochip_remove(&gpio->gpio_chip);
378	bitmap_free(gpio->fixed_direction_output);
379	kfree(gpio);
380}
381EXPORT_SYMBOL_GPL(gpio_regmap_unregister);
382
383static void devm_gpio_regmap_unregister(void *res)
384{
385	gpio_regmap_unregister(res);
386}
387
388/**
389 * devm_gpio_regmap_register() - resource managed gpio_regmap_register()
390 * @dev: device that is registering this GPIO device
391 * @config: configuration for gpio_regmap
392 *
393 * Managed gpio_regmap_register(). For generic regmap GPIO device registered by
394 * this function, gpio_regmap_unregister() is automatically called on driver
395 * detach. See gpio_regmap_register() for more information.
396 *
397 * Return: A pointer to the registered gpio_regmap or ERR_PTR error value.
398 */
399struct gpio_regmap *devm_gpio_regmap_register(struct device *dev,
400					      const struct gpio_regmap_config *config)
401{
402	struct gpio_regmap *gpio;
403	int ret;
404
405	gpio = gpio_regmap_register(config);
406
407	if (IS_ERR(gpio))
408		return gpio;
409
410	ret = devm_add_action_or_reset(dev, devm_gpio_regmap_unregister, gpio);
411	if (ret)
412		return ERR_PTR(ret);
413
414	return gpio;
415}
416EXPORT_SYMBOL_GPL(devm_gpio_regmap_register);
417
418MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
419MODULE_DESCRIPTION("GPIO generic regmap driver core");
420MODULE_LICENSE("GPL");
~/ Read-only file preview.