xref: /linux/drivers/gpio/gpio-ltc4283.c (revision b2f1e6301efa4a80becdb0715416c3cbc693fbb4)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Analog Devices LTC4283 GPIO driver
4  *
5  * Copyright 2025 Analog Devices Inc.
6  */
7 
8 #include <linux/auxiliary_bus.h>
9 #include <linux/bitfield.h>
10 #include <linux/bitmap.h>
11 #include <linux/bits.h>
12 #include <linux/device.h>
13 #include <linux/gpio/driver.h>
14 #include <linux/mod_devicetable.h>
15 #include <linux/module.h>
16 #include <linux/regmap.h>
17 
18 #define LTC4283_PINS_MAX			8
19 #define LTC4283_PGIOX_START_NR			4
20 #define LTC4283_INPUT_STATUS			0x02
21 #define LTC4283_PGIO_CONFIG			0x10
22 #define   LTC4283_PGIO_CFG_MASK(pin) \
23 	GENMASK(((pin) - LTC4283_PGIOX_START_NR) * 2 + 1, (((pin) - LTC4283_PGIOX_START_NR) * 2))
24 #define LTC4283_PGIO_CONFIG_2			0x11
25 
26 #define LTC4283_ADIO_CONFIG			0x12
27 /* starts at bit 4 */
28 #define   LTC4283_ADIOX_CONFIG_MASK(pin)	BIT((pin) + 4)
29 #define LTC4283_PGIO_DIR_IN			3
30 #define LTC4283_PGIO_DIR_OUT			2
31 
32 struct ltc4283_gpio {
33 	struct gpio_chip gpio_chip;
34 	struct regmap *regmap;
35 };
36 
37 static int ltc4283_pgio_get_direction(const struct ltc4283_gpio *st, unsigned int off)
38 {
39 	unsigned int val;
40 	int ret;
41 
42 	ret = regmap_read(st->regmap, LTC4283_PGIO_CONFIG, &val);
43 	if (ret)
44 		return ret;
45 
46 	val = field_get(LTC4283_PGIO_CFG_MASK(off), val);
47 	if (val == LTC4283_PGIO_DIR_IN)
48 		return GPIO_LINE_DIRECTION_IN;
49 
50 	return GPIO_LINE_DIRECTION_OUT;
51 }
52 
53 static int ltc4283_gpio_get_direction(struct gpio_chip *gc, unsigned int off)
54 {
55 	struct ltc4283_gpio *st = gpiochip_get_data(gc);
56 	unsigned int val;
57 	int ret;
58 
59 	if (off >= LTC4283_PGIOX_START_NR)
60 		return ltc4283_pgio_get_direction(st, off);
61 
62 	ret = regmap_read(st->regmap, LTC4283_ADIO_CONFIG, &val);
63 	if (ret)
64 		return ret;
65 
66 	if (val & LTC4283_ADIOX_CONFIG_MASK(off))
67 		return GPIO_LINE_DIRECTION_IN;
68 
69 	return GPIO_LINE_DIRECTION_OUT;
70 }
71 
72 static int ltc4283_gpio_direction_set(const struct ltc4283_gpio *st,
73 				      unsigned int off, bool input)
74 {
75 	if (off >= LTC4283_PGIOX_START_NR) {
76 		unsigned int val = LTC4283_PGIO_DIR_OUT;
77 
78 		if (input)
79 			val = LTC4283_PGIO_DIR_IN;
80 
81 		val = field_prep(LTC4283_PGIO_CFG_MASK(off), val);
82 		return regmap_update_bits(st->regmap, LTC4283_PGIO_CONFIG,
83 					  LTC4283_PGIO_CFG_MASK(off), val);
84 	}
85 
86 	return regmap_update_bits(st->regmap, LTC4283_ADIO_CONFIG,
87 				  LTC4283_ADIOX_CONFIG_MASK(off),
88 				  field_prep(LTC4283_ADIOX_CONFIG_MASK(off), input));
89 }
90 
91 static int __ltc4283_gpio_set_value(const struct ltc4283_gpio *st,
92 				    unsigned int off, int val)
93 {
94 	u32 reg = off < LTC4283_PGIOX_START_NR ? LTC4283_ADIO_CONFIG : LTC4283_PGIO_CONFIG_2;
95 
96 	return regmap_update_bits(st->regmap, reg, BIT(off),
97 				  field_prep(BIT(off), !!val));
98 }
99 
100 static int ltc4283_gpio_direction_input(struct gpio_chip *gc, unsigned int off)
101 {
102 	struct ltc4283_gpio *st = gpiochip_get_data(gc);
103 
104 	return ltc4283_gpio_direction_set(st, off, true);
105 }
106 
107 static int ltc4283_gpio_direction_output(struct gpio_chip *gc, unsigned int off, int val)
108 {
109 	struct ltc4283_gpio *st = gpiochip_get_data(gc);
110 	int ret;
111 
112 	ret = ltc4283_gpio_direction_set(st, off, false);
113 	if (ret)
114 		return ret;
115 
116 	return __ltc4283_gpio_set_value(st, off, val);
117 }
118 
119 static int ltc4283_gpio_get_value(struct gpio_chip *gc, unsigned int off)
120 {
121 	struct ltc4283_gpio *st = gpiochip_get_data(gc);
122 	unsigned int val, reg;
123 	int ret, dir;
124 
125 	dir = ltc4283_gpio_get_direction(gc, off);
126 	if (dir < 0)
127 		return dir;
128 
129 	if (dir == GPIO_LINE_DIRECTION_IN) {
130 		ret = regmap_read(st->regmap, LTC4283_INPUT_STATUS, &val);
131 		if (ret)
132 			return ret;
133 
134 		/* ADIO1 is at bit 3. */
135 		if (off < LTC4283_PGIOX_START_NR)
136 			return !!(val & BIT(3 - off));
137 
138 		/* PGIO1 is at bit 7. */
139 		return !!(val & BIT(7 - (off - LTC4283_PGIOX_START_NR)));
140 	}
141 
142 	if (off < LTC4283_PGIOX_START_NR)
143 		reg = LTC4283_ADIO_CONFIG;
144 	else
145 		reg = LTC4283_PGIO_CONFIG_2;
146 
147 	ret = regmap_read(st->regmap, reg, &val);
148 	if (ret)
149 		return ret;
150 
151 	return !!(val & BIT(off));
152 }
153 
154 static int ltc4283_gpio_set_value(struct gpio_chip *gc, unsigned int off, int val)
155 {
156 	struct ltc4283_gpio *st = gpiochip_get_data(gc);
157 
158 	return __ltc4283_gpio_set_value(st, off, val);
159 }
160 
161 static int ltc4283_init_valid_mask(struct gpio_chip *gc, unsigned long *valid_mask,
162 				   unsigned int ngpios)
163 {
164 	unsigned long *mask = dev_get_platdata(gc->parent);
165 
166 	bitmap_copy(valid_mask, mask, ngpios);
167 	return 0;
168 }
169 
170 static int ltc4283_gpio_probe(struct auxiliary_device *adev,
171 			      const struct auxiliary_device_id *id)
172 {
173 	struct device *dev = &adev->dev;
174 	struct ltc4283_gpio *st;
175 	struct gpio_chip *gc;
176 
177 	st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
178 	if (!st)
179 		return -ENOMEM;
180 
181 	st->regmap = dev_get_regmap(dev->parent, NULL);
182 	if (!st->regmap)
183 		return dev_err_probe(dev, -ENODEV,
184 				     "Failed to get regmap\n");
185 
186 	gc = &st->gpio_chip;
187 	gc->parent = dev;
188 	gc->get_direction = ltc4283_gpio_get_direction;
189 	gc->direction_input = ltc4283_gpio_direction_input;
190 	gc->direction_output = ltc4283_gpio_direction_output;
191 	gc->get = ltc4283_gpio_get_value;
192 	gc->set = ltc4283_gpio_set_value;
193 	gc->init_valid_mask = ltc4283_init_valid_mask;
194 	gc->can_sleep = true;
195 
196 	gc->base = -1;
197 	gc->ngpio = LTC4283_PINS_MAX;
198 	gc->label = adev->name;
199 	gc->owner = THIS_MODULE;
200 
201 	return devm_gpiochip_add_data(dev, &st->gpio_chip, st);
202 }
203 
204 static const struct auxiliary_device_id ltc4283_aux_id_table[] = {
205 	{ "ltc4283.gpio" },
206 	{ }
207 };
208 MODULE_DEVICE_TABLE(auxiliary, ltc4283_aux_id_table);
209 
210 static struct auxiliary_driver ltc4283_gpio_driver = {
211 	.probe = ltc4283_gpio_probe,
212 	.id_table = ltc4283_aux_id_table,
213 };
214 module_auxiliary_driver(ltc4283_gpio_driver);
215 
216 MODULE_AUTHOR("Nuno Sá <nuno.sa@analog.com>");
217 MODULE_DESCRIPTION("GPIO LTC4283 Driver");
218 MODULE_LICENSE("GPL");
219