xref: /linux/drivers/gpio/gpio-tps65219.c (revision 80e466f0c8acc545159a1f1fcca62512bf848413)
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 & 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 (gc->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 	val = direction == GPIO_LINE_DIRECTION_OUT ?
162 		TPS65214_GPIO0_DIR_MASK : 0;
163 	ret = regmap_update_bits(gpio->tps->regmap, TPS65219_REG_GENERAL_CONFIG,
164 				 TPS65214_GPIO0_DIR_MASK, val);
165 	if (ret)
166 		dev_err(dev, "Fail to change direction to %u for GPIO%d.\n", direction, offset);
167 
168 	return ret;
169 }
170 
tps65219_gpio_direction_input(struct gpio_chip * gc,unsigned int offset)171 static int tps65219_gpio_direction_input(struct gpio_chip *gc, unsigned int offset)
172 {
173 	struct tps65219_gpio *gpio = gpiochip_get_data(gc);
174 	struct device *dev = gpio->tps->dev;
175 
176 	if (offset != TPS6521X_GPIO0_IDX) {
177 		dev_err(dev, "GPIO%d is output only, cannot change to input\n", offset);
178 		return -ENOTSUPP;
179 	}
180 
181 	if (gc->get_direction(gc, offset) == GPIO_LINE_DIRECTION_IN)
182 		return 0;
183 
184 	return gpio->change_dir(gc, offset, GPIO_LINE_DIRECTION_IN);
185 }
186 
tps65219_gpio_direction_output(struct gpio_chip * gc,unsigned int offset,int value)187 static int tps65219_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int value)
188 {
189 	struct tps65219_gpio *gpio = gpiochip_get_data(gc);
190 
191 	tps65219_gpio_set(gc, offset, value);
192 	if (offset != TPS6521X_GPIO0_IDX)
193 		return 0;
194 
195 	if (gc->get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT)
196 		return 0;
197 
198 	return gpio->change_dir(gc, offset, GPIO_LINE_DIRECTION_OUT);
199 }
200 
201 static const struct gpio_chip tps65214_template_chip = {
202 	.label			= "tps65214-gpio",
203 	.owner			= THIS_MODULE,
204 	.get_direction		= tps65214_gpio_get_direction,
205 	.direction_input	= tps65219_gpio_direction_input,
206 	.direction_output	= tps65219_gpio_direction_output,
207 	.get			= tps65219_gpio_get,
208 	.set			= tps65219_gpio_set,
209 	.base			= -1,
210 	.ngpio			= 2,
211 	.can_sleep		= true,
212 };
213 
214 static const struct gpio_chip tps65219_template_chip = {
215 	.label			= "tps65219-gpio",
216 	.owner			= THIS_MODULE,
217 	.get_direction		= tps65219_gpio_get_direction,
218 	.direction_input	= tps65219_gpio_direction_input,
219 	.direction_output	= tps65219_gpio_direction_output,
220 	.get			= tps65219_gpio_get,
221 	.set			= tps65219_gpio_set,
222 	.base			= -1,
223 	.ngpio			= 3,
224 	.can_sleep		= true,
225 };
226 
tps65219_gpio_probe(struct platform_device * pdev)227 static int tps65219_gpio_probe(struct platform_device *pdev)
228 {
229 	enum pmic_id chip = platform_get_device_id(pdev)->driver_data;
230 	struct tps65219 *tps = dev_get_drvdata(pdev->dev.parent);
231 	struct tps65219_gpio *gpio;
232 
233 	gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
234 	if (!gpio)
235 		return -ENOMEM;
236 
237 	if (chip == TPS65214) {
238 		gpio->gpio_chip = tps65214_template_chip;
239 		gpio->change_dir = tps65214_gpio_change_direction;
240 	} else if (chip == TPS65219) {
241 		gpio->gpio_chip = tps65219_template_chip;
242 		gpio->change_dir = tps65219_gpio_change_direction;
243 	} else {
244 		return -ENODATA;
245 	}
246 
247 	gpio->tps = tps;
248 	gpio->gpio_chip.parent = tps->dev;
249 
250 	return devm_gpiochip_add_data(&pdev->dev, &gpio->gpio_chip, gpio);
251 }
252 
253 static const struct platform_device_id tps6521x_gpio_id_table[] = {
254 	{ .name = "tps65214-gpio", .driver_data = TPS65214 },
255 	{ .name = "tps65219-gpio", .driver_data = TPS65219 },
256 	{ /* sentinel */ }
257 };
258 MODULE_DEVICE_TABLE(platform, tps6521x_gpio_id_table);
259 
260 static struct platform_driver tps65219_gpio_driver = {
261 	.driver = {
262 		.name = "tps65219-gpio",
263 	},
264 	.probe = tps65219_gpio_probe,
265 	.id_table = tps6521x_gpio_id_table,
266 };
267 module_platform_driver(tps65219_gpio_driver);
268 
269 MODULE_AUTHOR("Jonathan Cormier <jcormier@criticallink.com>");
270 MODULE_DESCRIPTION("TPS65214/TPS65215/TPS65219 GPIO driver");
271 MODULE_LICENSE("GPL");
272