File Index
Files and directories
Sign in
FILE

gpio-tps65219.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpio/gpio-tps65219.c C
  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
 31struct 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
 37static 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
 52static 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
 67static 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
 96static 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
110static 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
142static 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
169static 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
185static 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
199static 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			= tps65219_gpio_set,
207	.base			= -1,
208	.ngpio			= 2,
209	.can_sleep		= true,
210};
211
212static 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			= tps65219_gpio_set,
220	.base			= -1,
221	.ngpio			= 3,
222	.can_sleep		= true,
223};
224
225static 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
251static const struct platform_device_id tps6521x_gpio_id_table[] = {
252	{ "tps65214-gpio", TPS65214 },
253	{ "tps65219-gpio", TPS65219 },
254	{ /* sentinel */ }
255};
256MODULE_DEVICE_TABLE(platform, tps6521x_gpio_id_table);
257
258static 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};
265module_platform_driver(tps65219_gpio_driver);
266
267MODULE_AUTHOR("Jonathan Cormier <jcormier@criticallink.com>");
268MODULE_DESCRIPTION("TPS65214/TPS65215/TPS65219 GPIO driver");
269MODULE_LICENSE("GPL");
~/ Read-only file preview.