1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright 2015 Verifone Int. |
4 | * |
5 | * Author: Nicolas Saenz Julienne <nicolassaenzj@gmail.com> |
6 | * |
7 | * This driver is based on the gpio-tps65912 implementation. |
8 | */ |
9 | |
10 | #include <linux/kernel.h> |
11 | #include <linux/module.h> |
12 | #include <linux/errno.h> |
13 | #include <linux/gpio/driver.h> |
14 | #include <linux/platform_device.h> |
15 | #include <linux/regmap.h> |
16 | #include <linux/mfd/tps65218.h> |
17 | |
18 | struct tps65218_gpio { |
19 | struct tps65218 *tps65218; |
20 | struct gpio_chip gpio_chip; |
21 | }; |
22 | |
23 | static int tps65218_gpio_get(struct gpio_chip *gc, unsigned offset) |
24 | { |
25 | struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc); |
26 | struct tps65218 *tps65218 = tps65218_gpio->tps65218; |
27 | unsigned int val; |
28 | int ret; |
29 | |
30 | ret = regmap_read(map: tps65218->regmap, TPS65218_REG_ENABLE2, val: &val); |
31 | if (ret) |
32 | return ret; |
33 | |
34 | return !!(val & (TPS65218_ENABLE2_GPIO1 << offset)); |
35 | } |
36 | |
37 | static void tps65218_gpio_set(struct gpio_chip *gc, unsigned offset, |
38 | int value) |
39 | { |
40 | struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc); |
41 | struct tps65218 *tps65218 = tps65218_gpio->tps65218; |
42 | |
43 | if (value) |
44 | tps65218_set_bits(tps: tps65218, TPS65218_REG_ENABLE2, |
45 | TPS65218_ENABLE2_GPIO1 << offset, |
46 | TPS65218_ENABLE2_GPIO1 << offset, |
47 | TPS65218_PROTECT_L1); |
48 | else |
49 | tps65218_clear_bits(tps: tps65218, TPS65218_REG_ENABLE2, |
50 | TPS65218_ENABLE2_GPIO1 << offset, |
51 | TPS65218_PROTECT_L1); |
52 | } |
53 | |
54 | static int tps65218_gpio_output(struct gpio_chip *gc, unsigned offset, |
55 | int value) |
56 | { |
57 | /* Only drives GPOs */ |
58 | tps65218_gpio_set(gc, offset, value); |
59 | return 0; |
60 | } |
61 | |
62 | static int tps65218_gpio_input(struct gpio_chip *gc, unsigned offset) |
63 | { |
64 | return -EPERM; |
65 | } |
66 | |
67 | static int tps65218_gpio_request(struct gpio_chip *gc, unsigned offset) |
68 | { |
69 | struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc); |
70 | struct tps65218 *tps65218 = tps65218_gpio->tps65218; |
71 | int ret; |
72 | |
73 | if (gpiochip_line_is_open_source(gc, offset)) { |
74 | dev_err(gc->parent, "can't work as open source\n" ); |
75 | return -EINVAL; |
76 | } |
77 | |
78 | switch (offset) { |
79 | case 0: |
80 | if (!gpiochip_line_is_open_drain(gc, offset)) { |
81 | dev_err(gc->parent, "GPO1 works only as open drain\n" ); |
82 | return -EINVAL; |
83 | } |
84 | |
85 | /* Disable sequencer for GPO1 */ |
86 | ret = tps65218_clear_bits(tps: tps65218, TPS65218_REG_SEQ7, |
87 | TPS65218_SEQ7_GPO1_SEQ_MASK, |
88 | TPS65218_PROTECT_L1); |
89 | if (ret) |
90 | return ret; |
91 | |
92 | /* Setup GPO1 */ |
93 | ret = tps65218_clear_bits(tps: tps65218, TPS65218_REG_CONFIG1, |
94 | TPS65218_CONFIG1_IO1_SEL, |
95 | TPS65218_PROTECT_L1); |
96 | if (ret) |
97 | return ret; |
98 | |
99 | break; |
100 | case 1: |
101 | /* Setup GPO2 */ |
102 | ret = tps65218_clear_bits(tps: tps65218, TPS65218_REG_CONFIG1, |
103 | TPS65218_CONFIG1_IO1_SEL, |
104 | TPS65218_PROTECT_L1); |
105 | if (ret) |
106 | return ret; |
107 | |
108 | break; |
109 | |
110 | case 2: |
111 | if (!gpiochip_line_is_open_drain(gc, offset)) { |
112 | dev_err(gc->parent, "GPO3 works only as open drain\n" ); |
113 | return -EINVAL; |
114 | } |
115 | |
116 | /* Disable sequencer for GPO3 */ |
117 | ret = tps65218_clear_bits(tps: tps65218, TPS65218_REG_SEQ7, |
118 | TPS65218_SEQ7_GPO3_SEQ_MASK, |
119 | TPS65218_PROTECT_L1); |
120 | if (ret) |
121 | return ret; |
122 | |
123 | /* Setup GPO3 */ |
124 | ret = tps65218_clear_bits(tps: tps65218, TPS65218_REG_CONFIG2, |
125 | TPS65218_CONFIG2_DC12_RST, |
126 | TPS65218_PROTECT_L1); |
127 | if (ret) |
128 | return ret; |
129 | |
130 | break; |
131 | default: |
132 | return -EINVAL; |
133 | } |
134 | |
135 | return 0; |
136 | } |
137 | |
138 | static int tps65218_gpio_set_config(struct gpio_chip *gc, unsigned offset, |
139 | unsigned long config) |
140 | { |
141 | struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc); |
142 | struct tps65218 *tps65218 = tps65218_gpio->tps65218; |
143 | enum pin_config_param param = pinconf_to_config_param(config); |
144 | |
145 | switch (offset) { |
146 | case 0: |
147 | case 2: |
148 | /* GPO1 is hardwired to be open drain */ |
149 | if (param == PIN_CONFIG_DRIVE_OPEN_DRAIN) |
150 | return 0; |
151 | return -ENOTSUPP; |
152 | case 1: |
153 | /* GPO2 is push-pull by default, can be set as open drain. */ |
154 | if (param == PIN_CONFIG_DRIVE_OPEN_DRAIN) |
155 | return tps65218_clear_bits(tps: tps65218, |
156 | TPS65218_REG_CONFIG1, |
157 | TPS65218_CONFIG1_GPO2_BUF, |
158 | TPS65218_PROTECT_L1); |
159 | if (param == PIN_CONFIG_DRIVE_PUSH_PULL) |
160 | return tps65218_set_bits(tps: tps65218, |
161 | TPS65218_REG_CONFIG1, |
162 | TPS65218_CONFIG1_GPO2_BUF, |
163 | TPS65218_CONFIG1_GPO2_BUF, |
164 | TPS65218_PROTECT_L1); |
165 | return -ENOTSUPP; |
166 | default: |
167 | break; |
168 | } |
169 | return -ENOTSUPP; |
170 | } |
171 | |
172 | static const struct gpio_chip template_chip = { |
173 | .label = "gpio-tps65218" , |
174 | .owner = THIS_MODULE, |
175 | .request = tps65218_gpio_request, |
176 | .direction_output = tps65218_gpio_output, |
177 | .direction_input = tps65218_gpio_input, |
178 | .get = tps65218_gpio_get, |
179 | .set = tps65218_gpio_set, |
180 | .set_config = tps65218_gpio_set_config, |
181 | .can_sleep = true, |
182 | .ngpio = 3, |
183 | .base = -1, |
184 | }; |
185 | |
186 | static int tps65218_gpio_probe(struct platform_device *pdev) |
187 | { |
188 | struct tps65218 *tps65218 = dev_get_drvdata(dev: pdev->dev.parent); |
189 | struct tps65218_gpio *tps65218_gpio; |
190 | |
191 | tps65218_gpio = devm_kzalloc(dev: &pdev->dev, size: sizeof(*tps65218_gpio), |
192 | GFP_KERNEL); |
193 | if (!tps65218_gpio) |
194 | return -ENOMEM; |
195 | |
196 | tps65218_gpio->tps65218 = tps65218; |
197 | tps65218_gpio->gpio_chip = template_chip; |
198 | tps65218_gpio->gpio_chip.parent = &pdev->dev; |
199 | |
200 | return devm_gpiochip_add_data(&pdev->dev, &tps65218_gpio->gpio_chip, |
201 | tps65218_gpio); |
202 | } |
203 | |
204 | static const struct of_device_id tps65218_dt_match[] = { |
205 | { .compatible = "ti,tps65218-gpio" }, |
206 | { } |
207 | }; |
208 | MODULE_DEVICE_TABLE(of, tps65218_dt_match); |
209 | |
210 | static const struct platform_device_id tps65218_gpio_id_table[] = { |
211 | { "tps65218-gpio" , }, |
212 | { /* sentinel */ } |
213 | }; |
214 | MODULE_DEVICE_TABLE(platform, tps65218_gpio_id_table); |
215 | |
216 | static struct platform_driver tps65218_gpio_driver = { |
217 | .driver = { |
218 | .name = "tps65218-gpio" , |
219 | .of_match_table = tps65218_dt_match, |
220 | }, |
221 | .probe = tps65218_gpio_probe, |
222 | .id_table = tps65218_gpio_id_table, |
223 | }; |
224 | |
225 | module_platform_driver(tps65218_gpio_driver); |
226 | |
227 | MODULE_AUTHOR("Nicolas Saenz Julienne <nicolassaenzj@gmail.com>" ); |
228 | MODULE_DESCRIPTION("GPO interface for TPS65218 PMICs" ); |
229 | MODULE_LICENSE("GPL v2" ); |
230 | |