]>
Commit | Line | Data |
---|---|---|
fe963fd8 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
c366c76a NSJ |
2 | /* |
3 | * Copyright 2015 Verifone Int. | |
4 | * | |
5 | * Author: Nicolas Saenz Julienne <[email protected]> | |
6 | * | |
c366c76a NSJ |
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> | |
0aced355 | 15 | #include <linux/regmap.h> |
c366c76a | 16 | #include <linux/mfd/tps65218.h> |
c366c76a NSJ |
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 | ||
0aced355 | 30 | ret = regmap_read(tps65218->regmap, TPS65218_REG_ENABLE2, &val); |
c366c76a NSJ |
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(tps65218, TPS65218_REG_ENABLE2, | |
45 | TPS65218_ENABLE2_GPIO1 << offset, | |
46 | TPS65218_ENABLE2_GPIO1 << offset, | |
47 | TPS65218_PROTECT_L1); | |
48 | else | |
49 | tps65218_clear_bits(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 */ | |
818cc6a5 | 58 | tps65218_gpio_set(gc, offset, value); |
c366c76a NSJ |
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; | |
c366c76a NSJ |
71 | int ret; |
72 | ||
143b65d6 | 73 | if (gpiochip_line_is_open_source(gc, offset)) { |
c366c76a NSJ |
74 | dev_err(gc->parent, "can't work as open source\n"); |
75 | return -EINVAL; | |
76 | } | |
77 | ||
78 | switch (offset) { | |
79 | case 0: | |
143b65d6 | 80 | if (!gpiochip_line_is_open_drain(gc, offset)) { |
c366c76a NSJ |
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(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(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: | |
c366c76a NSJ |
101 | /* Setup GPO2 */ |
102 | ret = tps65218_clear_bits(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: | |
143b65d6 | 111 | if (!gpiochip_line_is_open_drain(gc, offset)) { |
c366c76a NSJ |
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(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(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 | ||
2956b5d9 MW |
138 | static int tps65218_gpio_set_config(struct gpio_chip *gc, unsigned offset, |
139 | unsigned long config) | |
f30e49f1 LW |
140 | { |
141 | struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc); | |
142 | struct tps65218 *tps65218 = tps65218_gpio->tps65218; | |
2956b5d9 | 143 | enum pin_config_param param = pinconf_to_config_param(config); |
f30e49f1 LW |
144 | |
145 | switch (offset) { | |
146 | case 0: | |
147 | case 2: | |
148 | /* GPO1 is hardwired to be open drain */ | |
2956b5d9 | 149 | if (param == PIN_CONFIG_DRIVE_OPEN_DRAIN) |
f30e49f1 LW |
150 | return 0; |
151 | return -ENOTSUPP; | |
152 | case 1: | |
153 | /* GPO2 is push-pull by default, can be set as open drain. */ | |
2956b5d9 | 154 | if (param == PIN_CONFIG_DRIVE_OPEN_DRAIN) |
f30e49f1 LW |
155 | return tps65218_clear_bits(tps65218, |
156 | TPS65218_REG_CONFIG1, | |
157 | TPS65218_CONFIG1_GPO2_BUF, | |
158 | TPS65218_PROTECT_L1); | |
2956b5d9 | 159 | if (param == PIN_CONFIG_DRIVE_PUSH_PULL) |
f30e49f1 LW |
160 | return tps65218_set_bits(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 | ||
e35b5ab0 | 172 | static const struct gpio_chip template_chip = { |
c366c76a NSJ |
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, | |
2956b5d9 | 180 | .set_config = tps65218_gpio_set_config, |
c366c76a NSJ |
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(pdev->dev.parent); | |
189 | struct tps65218_gpio *tps65218_gpio; | |
c366c76a NSJ |
190 | |
191 | tps65218_gpio = devm_kzalloc(&pdev->dev, 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; | |
c366c76a | 199 | |
0cef30b8 AA |
200 | return devm_gpiochip_add_data(&pdev->dev, &tps65218_gpio->gpio_chip, |
201 | tps65218_gpio); | |
c366c76a NSJ |
202 | } |
203 | ||
c366c76a NSJ |
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 | ||
a944a892 K |
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 | ||
c366c76a NSJ |
216 | static struct platform_driver tps65218_gpio_driver = { |
217 | .driver = { | |
218 | .name = "tps65218-gpio", | |
c4dc167c | 219 | .of_match_table = tps65218_dt_match, |
c366c76a NSJ |
220 | }, |
221 | .probe = tps65218_gpio_probe, | |
a944a892 | 222 | .id_table = tps65218_gpio_id_table, |
c366c76a NSJ |
223 | }; |
224 | ||
225 | module_platform_driver(tps65218_gpio_driver); | |
226 | ||
227 | MODULE_AUTHOR("Nicolas Saenz Julienne <[email protected]>"); | |
228 | MODULE_DESCRIPTION("GPO interface for TPS65218 PMICs"); | |
229 | MODULE_LICENSE("GPL v2"); |