xref: /linux/drivers/gpio/gpio-tps65219.c (revision e814f3fd16acfb7f9966773953de8f740a1e3202)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * GPIO driver for TI TPS65219 PMICs
4  *
5  * Copyright (C) 2022 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 TPS65219_GPIO0_OFFSET		2
17 #define TPS65219_GPIO0_IDX		0
18 
19 struct tps65219_gpio {
20 	struct gpio_chip gpio_chip;
21 	struct tps65219 *tps;
22 };
23 
24 static int tps65219_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
25 {
26 	struct tps65219_gpio *gpio = gpiochip_get_data(gc);
27 	int ret, val;
28 
29 	if (offset != TPS65219_GPIO0_IDX)
30 		return GPIO_LINE_DIRECTION_OUT;
31 
32 	ret = regmap_read(gpio->tps->regmap, TPS65219_REG_MFP_1_CONFIG, &val);
33 	if (ret)
34 		return ret;
35 
36 	return !!(val & TPS65219_GPIO0_DIR_MASK);
37 }
38 
39 static int tps65219_gpio_get(struct gpio_chip *gc, unsigned int offset)
40 {
41 	struct tps65219_gpio *gpio = gpiochip_get_data(gc);
42 	struct device *dev = gpio->tps->dev;
43 	int ret, val;
44 
45 	if (offset != TPS65219_GPIO0_IDX) {
46 		dev_err(dev, "GPIO%d is output only, cannot get\n", offset);
47 		return -ENOTSUPP;
48 	}
49 
50 	ret = regmap_read(gpio->tps->regmap, TPS65219_REG_MFP_CTRL, &val);
51 	if (ret)
52 		return ret;
53 
54 	ret = !!(val & BIT(TPS65219_MFP_GPIO_STATUS_MASK));
55 	dev_warn(dev, "GPIO%d = %d, MULTI_DEVICE_ENABLE, not a standard GPIO\n", offset, ret);
56 
57 	/*
58 	 * Depending on NVM config, return an error if direction is output, otherwise the GPIO0
59 	 * status bit.
60 	 */
61 
62 	if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT)
63 		return -ENOTSUPP;
64 
65 	return ret;
66 }
67 
68 static void tps65219_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
69 {
70 	struct tps65219_gpio *gpio = gpiochip_get_data(gc);
71 	struct device *dev = gpio->tps->dev;
72 	int v, mask, bit;
73 
74 	bit = (offset == TPS65219_GPIO0_IDX) ? TPS65219_GPIO0_OFFSET : offset - 1;
75 
76 	mask = BIT(bit);
77 	v = value ? mask : 0;
78 
79 	if (regmap_update_bits(gpio->tps->regmap, TPS65219_REG_GENERAL_CONFIG, mask, v))
80 		dev_err(dev, "GPIO%d, set to value %d failed.\n", offset, value);
81 }
82 
83 static int tps65219_gpio_change_direction(struct gpio_chip *gc, unsigned int offset,
84 					  unsigned int direction)
85 {
86 	struct tps65219_gpio *gpio = gpiochip_get_data(gc);
87 	struct device *dev = gpio->tps->dev;
88 
89 	/*
90 	 * Documentation is stating that GPIO0 direction must not be changed in Linux:
91 	 * Table 8-34. MFP_1_CONFIG(3): MULTI_DEVICE_ENABLE, should only be changed in INITIALIZE
92 	 * state (prior to ON Request).
93 	 * Set statically by NVM, changing direction in application can cause a hang.
94 	 * Below can be used for test purpose only.
95 	 */
96 
97 #if 0
98 	int ret = regmap_update_bits(gpio->tps->regmap, TPS65219_REG_MFP_1_CONFIG,
99 				     TPS65219_GPIO0_DIR_MASK, direction);
100 	if (ret) {
101 		dev_err(dev,
102 			"GPIO DEBUG enabled: Fail to change direction to %u for GPIO%d.\n",
103 			direction, offset);
104 		return ret;
105 	}
106 #endif
107 
108 	dev_err(dev,
109 		"GPIO%d direction set by NVM, change to %u failed, not allowed by specification\n",
110 		 offset, direction);
111 
112 	return -ENOTSUPP;
113 }
114 
115 static int tps65219_gpio_direction_input(struct gpio_chip *gc, unsigned int offset)
116 {
117 	struct tps65219_gpio *gpio = gpiochip_get_data(gc);
118 	struct device *dev = gpio->tps->dev;
119 
120 	if (offset != TPS65219_GPIO0_IDX) {
121 		dev_err(dev, "GPIO%d is output only, cannot change to input\n", offset);
122 		return -ENOTSUPP;
123 	}
124 
125 	if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_IN)
126 		return 0;
127 
128 	return tps65219_gpio_change_direction(gc, offset, GPIO_LINE_DIRECTION_IN);
129 }
130 
131 static int tps65219_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int value)
132 {
133 	tps65219_gpio_set(gc, offset, value);
134 	if (offset != TPS65219_GPIO0_IDX)
135 		return 0;
136 
137 	if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT)
138 		return 0;
139 
140 	return tps65219_gpio_change_direction(gc, offset, GPIO_LINE_DIRECTION_OUT);
141 }
142 
143 static const struct gpio_chip tps65219_template_chip = {
144 	.label			= "tps65219-gpio",
145 	.owner			= THIS_MODULE,
146 	.get_direction		= tps65219_gpio_get_direction,
147 	.direction_input	= tps65219_gpio_direction_input,
148 	.direction_output	= tps65219_gpio_direction_output,
149 	.get			= tps65219_gpio_get,
150 	.set			= tps65219_gpio_set,
151 	.base			= -1,
152 	.ngpio			= 3,
153 	.can_sleep		= true,
154 };
155 
156 static int tps65219_gpio_probe(struct platform_device *pdev)
157 {
158 	struct tps65219 *tps = dev_get_drvdata(pdev->dev.parent);
159 	struct tps65219_gpio *gpio;
160 
161 	gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
162 	if (!gpio)
163 		return -ENOMEM;
164 
165 	gpio->tps = tps;
166 	gpio->gpio_chip = tps65219_template_chip;
167 	gpio->gpio_chip.parent = tps->dev;
168 
169 	return devm_gpiochip_add_data(&pdev->dev, &gpio->gpio_chip, gpio);
170 }
171 
172 static struct platform_driver tps65219_gpio_driver = {
173 	.driver = {
174 		.name = "tps65219-gpio",
175 	},
176 	.probe = tps65219_gpio_probe,
177 };
178 module_platform_driver(tps65219_gpio_driver);
179 
180 MODULE_ALIAS("platform:tps65219-gpio");
181 MODULE_AUTHOR("Jonathan Cormier <jcormier@criticallink.com>");
182 MODULE_DESCRIPTION("TPS65219 GPIO driver");
183 MODULE_LICENSE("GPL");
184