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