1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * GPIO driver for TI TPS65214/TPS65215/TPS65219 PMICs
4 *
5 * Copyright (C) 2022, 2025 Texas Instruments Incorporated - http://www.ti.com/
6 */
7
8 #include <linux/bits.h>
9 #include <linux/gpio/driver.h>
10 #include <linux/mfd/tps65219.h>
11 #include <linux/module.h>
12 #include <linux/platform_device.h>
13 #include <linux/regmap.h>
14
15 #define TPS65219_GPIO0_DIR_MASK BIT(3)
16 #define TPS65214_GPIO0_DIR_MASK BIT(1)
17 #define TPS6521X_GPIO0_OFFSET 2
18 #define TPS6521X_GPIO0_IDX 0
19
20 /*
21 * TPS65214 GPIO mapping
22 * Linux gpio offset 0 -> GPIO (pin16) -> bit_offset 2
23 * Linux gpio offset 1 -> GPO1 (pin9 ) -> bit_offset 0
24 *
25 * TPS65215 & TPS65219 GPIO mapping
26 * Linux gpio offset 0 -> GPIO (pin16) -> bit_offset 2
27 * Linux gpio offset 1 -> GPO1 (pin8 ) -> bit_offset 0
28 * Linux gpio offset 2 -> GPO2 (pin17) -> bit_offset 1
29 */
30
31 struct tps65219_gpio {
32 int (*change_dir)(struct gpio_chip *gc, unsigned int offset, unsigned int dir);
33 struct gpio_chip gpio_chip;
34 struct tps65219 *tps;
35 };
36
tps65214_gpio_get_direction(struct gpio_chip * gc,unsigned int offset)37 static int tps65214_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
38 {
39 struct tps65219_gpio *gpio = gpiochip_get_data(gc);
40 int ret, val;
41
42 if (offset != TPS6521X_GPIO0_IDX)
43 return GPIO_LINE_DIRECTION_OUT;
44
45 ret = regmap_read(gpio->tps->regmap, TPS65219_REG_GENERAL_CONFIG, &val);
46 if (ret)
47 return ret;
48
49 return !(val & TPS65214_GPIO0_DIR_MASK);
50 }
51
tps65219_gpio_get_direction(struct gpio_chip * gc,unsigned int offset)52 static int tps65219_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
53 {
54 struct tps65219_gpio *gpio = gpiochip_get_data(gc);
55 int ret, val;
56
57 if (offset != TPS6521X_GPIO0_IDX)
58 return GPIO_LINE_DIRECTION_OUT;
59
60 ret = regmap_read(gpio->tps->regmap, TPS65219_REG_MFP_1_CONFIG, &val);
61 if (ret)
62 return ret;
63
64 return !!(val & TPS65219_GPIO0_DIR_MASK);
65 }
66
tps65219_gpio_get(struct gpio_chip * gc,unsigned int offset)67 static int tps65219_gpio_get(struct gpio_chip *gc, unsigned int offset)
68 {
69 struct tps65219_gpio *gpio = gpiochip_get_data(gc);
70 struct device *dev = gpio->tps->dev;
71 int ret, val;
72
73 if (offset != TPS6521X_GPIO0_IDX) {
74 dev_err(dev, "GPIO%d is output only, cannot get\n", offset);
75 return -ENOTSUPP;
76 }
77
78 ret = regmap_read(gpio->tps->regmap, TPS65219_REG_MFP_CTRL, &val);
79 if (ret)
80 return ret;
81
82 ret = !!(val & BIT(TPS65219_MFP_GPIO_STATUS_MASK));
83 dev_warn(dev, "GPIO%d = %d, MULTI_DEVICE_ENABLE, not a standard GPIO\n", offset, ret);
84
85 /*
86 * Depending on NVM config, return an error if direction is output, otherwise the GPIO0
87 * status bit.
88 */
89
90 if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT)
91 return -ENOTSUPP;
92
93 return ret;
94 }
95
tps65219_gpio_set(struct gpio_chip * gc,unsigned int offset,int value)96 static int tps65219_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
97 {
98 struct tps65219_gpio *gpio = gpiochip_get_data(gc);
99 int v, mask, bit;
100
101 bit = (offset == TPS6521X_GPIO0_IDX) ? TPS6521X_GPIO0_OFFSET : offset - 1;
102
103 mask = BIT(bit);
104 v = value ? mask : 0;
105
106 return regmap_update_bits(gpio->tps->regmap,
107 TPS65219_REG_GENERAL_CONFIG, mask, v);
108 }
109
tps65219_gpio_change_direction(struct gpio_chip * gc,unsigned int offset,unsigned int direction)110 static int tps65219_gpio_change_direction(struct gpio_chip *gc, unsigned int offset,
111 unsigned int direction)
112 {
113 struct tps65219_gpio *gpio = gpiochip_get_data(gc);
114 struct device *dev = gpio->tps->dev;
115
116 /*
117 * Documentation is stating that GPIO0 direction must not be changed in Linux:
118 * Table 8-34. MFP_1_CONFIG(3): MULTI_DEVICE_ENABLE, should only be changed in INITIALIZE
119 * state (prior to ON Request).
120 * Set statically by NVM, changing direction in application can cause a hang.
121 * Below can be used for test purpose only.
122 */
123
124 #if 0
125 int ret = regmap_update_bits(gpio->tps->regmap, TPS65219_REG_MFP_1_CONFIG,
126 TPS65219_GPIO0_DIR_MASK, direction);
127 if (ret) {
128 dev_err(dev,
129 "GPIO DEBUG enabled: Fail to change direction to %u for GPIO%d.\n",
130 direction, offset);
131 return ret;
132 }
133 #endif
134
135 dev_err(dev,
136 "GPIO%d direction set by NVM, change to %u failed, not allowed by specification\n",
137 offset, direction);
138
139 return -ENOTSUPP;
140 }
141
tps65214_gpio_change_direction(struct gpio_chip * gc,unsigned int offset,unsigned int direction)142 static int tps65214_gpio_change_direction(struct gpio_chip *gc, unsigned int offset,
143 unsigned int direction)
144 {
145 struct tps65219_gpio *gpio = gpiochip_get_data(gc);
146 struct device *dev = gpio->tps->dev;
147 int val, ret;
148
149 /**
150 * Verified if GPIO or GPO in parent function
151 * Masked value: 0 = GPIO, 1 = VSEL
152 */
153 ret = regmap_read(gpio->tps->regmap, TPS65219_REG_MFP_1_CONFIG, &val);
154 if (ret)
155 return ret;
156
157 ret = !!(val & BIT(TPS65219_GPIO0_DIR_MASK));
158 if (ret)
159 dev_err(dev, "GPIO%d configured as VSEL, not GPIO\n", offset);
160
161 ret = regmap_update_bits(gpio->tps->regmap, TPS65219_REG_GENERAL_CONFIG,
162 TPS65214_GPIO0_DIR_MASK, direction);
163 if (ret)
164 dev_err(dev, "Fail to change direction to %u for GPIO%d.\n", direction, offset);
165
166 return ret;
167 }
168
tps65219_gpio_direction_input(struct gpio_chip * gc,unsigned int offset)169 static int tps65219_gpio_direction_input(struct gpio_chip *gc, unsigned int offset)
170 {
171 struct tps65219_gpio *gpio = gpiochip_get_data(gc);
172 struct device *dev = gpio->tps->dev;
173
174 if (offset != TPS6521X_GPIO0_IDX) {
175 dev_err(dev, "GPIO%d is output only, cannot change to input\n", offset);
176 return -ENOTSUPP;
177 }
178
179 if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_IN)
180 return 0;
181
182 return gpio->change_dir(gc, offset, GPIO_LINE_DIRECTION_IN);
183 }
184
tps65219_gpio_direction_output(struct gpio_chip * gc,unsigned int offset,int value)185 static int tps65219_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int value)
186 {
187 struct tps65219_gpio *gpio = gpiochip_get_data(gc);
188
189 tps65219_gpio_set(gc, offset, value);
190 if (offset != TPS6521X_GPIO0_IDX)
191 return 0;
192
193 if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT)
194 return 0;
195
196 return gpio->change_dir(gc, offset, GPIO_LINE_DIRECTION_OUT);
197 }
198
199 static const struct gpio_chip tps65214_template_chip = {
200 .label = "tps65214-gpio",
201 .owner = THIS_MODULE,
202 .get_direction = tps65214_gpio_get_direction,
203 .direction_input = tps65219_gpio_direction_input,
204 .direction_output = tps65219_gpio_direction_output,
205 .get = tps65219_gpio_get,
206 .set_rv = tps65219_gpio_set,
207 .base = -1,
208 .ngpio = 2,
209 .can_sleep = true,
210 };
211
212 static const struct gpio_chip tps65219_template_chip = {
213 .label = "tps65219-gpio",
214 .owner = THIS_MODULE,
215 .get_direction = tps65219_gpio_get_direction,
216 .direction_input = tps65219_gpio_direction_input,
217 .direction_output = tps65219_gpio_direction_output,
218 .get = tps65219_gpio_get,
219 .set_rv = tps65219_gpio_set,
220 .base = -1,
221 .ngpio = 3,
222 .can_sleep = true,
223 };
224
tps65219_gpio_probe(struct platform_device * pdev)225 static int tps65219_gpio_probe(struct platform_device *pdev)
226 {
227 enum pmic_id chip = platform_get_device_id(pdev)->driver_data;
228 struct tps65219 *tps = dev_get_drvdata(pdev->dev.parent);
229 struct tps65219_gpio *gpio;
230
231 gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
232 if (!gpio)
233 return -ENOMEM;
234
235 if (chip == TPS65214) {
236 gpio->gpio_chip = tps65214_template_chip;
237 gpio->change_dir = tps65214_gpio_change_direction;
238 } else if (chip == TPS65219) {
239 gpio->gpio_chip = tps65219_template_chip;
240 gpio->change_dir = tps65219_gpio_change_direction;
241 } else {
242 return -ENODATA;
243 }
244
245 gpio->tps = tps;
246 gpio->gpio_chip.parent = tps->dev;
247
248 return devm_gpiochip_add_data(&pdev->dev, &gpio->gpio_chip, gpio);
249 }
250
251 static const struct platform_device_id tps6521x_gpio_id_table[] = {
252 { "tps65214-gpio", TPS65214 },
253 { "tps65219-gpio", TPS65219 },
254 { /* sentinel */ }
255 };
256 MODULE_DEVICE_TABLE(platform, tps6521x_gpio_id_table);
257
258 static struct platform_driver tps65219_gpio_driver = {
259 .driver = {
260 .name = "tps65219-gpio",
261 },
262 .probe = tps65219_gpio_probe,
263 .id_table = tps6521x_gpio_id_table,
264 };
265 module_platform_driver(tps65219_gpio_driver);
266
267 MODULE_AUTHOR("Jonathan Cormier <jcormier@criticallink.com>");
268 MODULE_DESCRIPTION("TPS65214/TPS65215/TPS65219 GPIO driver");
269 MODULE_LICENSE("GPL");
270