diff options
author | Ivan T. Ivanov <iivanov@mm-sol.com> | 2014-10-22 05:58:46 -0400 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2014-10-29 04:28:37 -0400 |
commit | eadff3024472f8a7955fae7e5484d235ed407453 (patch) | |
tree | f3fa17047fba6efcca794f8980bab982a17d95bd /drivers/pinctrl/qcom | |
parent | 89a7117d56d405b49b7d1f8ed30e01188e9d5a05 (diff) |
pinctrl: Qualcomm SPMI PMIC GPIO pin controller driver
This is the pinctrl, pinmux, pinconf and gpiolib driver for the
Qualcomm GPIO sub-function blocks found in the PMIC chips.
Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
Acked-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/pinctrl/qcom')
-rw-r--r-- | drivers/pinctrl/qcom/Kconfig | 13 | ||||
-rw-r--r-- | drivers/pinctrl/qcom/Makefile | 1 | ||||
-rw-r--r-- | drivers/pinctrl/qcom/pinctrl-spmi-gpio.c | 933 |
3 files changed, 947 insertions, 0 deletions
diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig index 81275af9638b..3cd243c26b7d 100644 --- a/drivers/pinctrl/qcom/Kconfig +++ b/drivers/pinctrl/qcom/Kconfig | |||
@@ -47,4 +47,17 @@ config PINCTRL_MSM8X74 | |||
47 | This is the pinctrl, pinmux, pinconf and gpiolib driver for the | 47 | This is the pinctrl, pinmux, pinconf and gpiolib driver for the |
48 | Qualcomm TLMM block found in the Qualcomm 8974 platform. | 48 | Qualcomm TLMM block found in the Qualcomm 8974 platform. |
49 | 49 | ||
50 | config PINCTRL_QCOM_SPMI_PMIC | ||
51 | tristate "Qualcomm SPMI PMIC pin controller driver" | ||
52 | depends on GPIOLIB && OF && SPMI | ||
53 | select REGMAP_SPMI | ||
54 | select PINMUX | ||
55 | select PINCONF | ||
56 | select GENERIC_PINCONF | ||
57 | help | ||
58 | This is the pinctrl, pinmux, pinconf and gpiolib driver for the | ||
59 | Qualcomm GPIO and MPP blocks found in the Qualcomm PMIC's chips, | ||
60 | which are using SPMI for communication with SoC. Example PMIC's | ||
61 | devices are pm8841, pm8941 and pma8084. | ||
62 | |||
50 | endif | 63 | endif |
diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile index ba8519fcd8d3..9b49c65a1cab 100644 --- a/drivers/pinctrl/qcom/Makefile +++ b/drivers/pinctrl/qcom/Makefile | |||
@@ -5,3 +5,4 @@ obj-$(CONFIG_PINCTRL_APQ8084) += pinctrl-apq8084.o | |||
5 | obj-$(CONFIG_PINCTRL_IPQ8064) += pinctrl-ipq8064.o | 5 | obj-$(CONFIG_PINCTRL_IPQ8064) += pinctrl-ipq8064.o |
6 | obj-$(CONFIG_PINCTRL_MSM8960) += pinctrl-msm8960.o | 6 | obj-$(CONFIG_PINCTRL_MSM8960) += pinctrl-msm8960.o |
7 | obj-$(CONFIG_PINCTRL_MSM8X74) += pinctrl-msm8x74.o | 7 | obj-$(CONFIG_PINCTRL_MSM8X74) += pinctrl-msm8x74.o |
8 | obj-$(CONFIG_PINCTRL_QCOM_SPMI_PMIC) += pinctrl-spmi-gpio.o | ||
diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c new file mode 100644 index 000000000000..b863b5080890 --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c | |||
@@ -0,0 +1,933 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 and | ||
6 | * only version 2 as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #include <linux/gpio.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/of.h> | ||
17 | #include <linux/pinctrl/pinconf-generic.h> | ||
18 | #include <linux/pinctrl/pinconf.h> | ||
19 | #include <linux/pinctrl/pinmux.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/regmap.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/types.h> | ||
24 | |||
25 | #include <dt-bindings/pinctrl/qcom,pmic-gpio.h> | ||
26 | |||
27 | #include "../core.h" | ||
28 | #include "../pinctrl-utils.h" | ||
29 | |||
30 | #define PMIC_GPIO_ADDRESS_RANGE 0x100 | ||
31 | |||
32 | /* type and subtype registers base address offsets */ | ||
33 | #define PMIC_GPIO_REG_TYPE 0x4 | ||
34 | #define PMIC_GPIO_REG_SUBTYPE 0x5 | ||
35 | |||
36 | /* GPIO peripheral type and subtype out_values */ | ||
37 | #define PMIC_GPIO_TYPE 0x10 | ||
38 | #define PMIC_GPIO_SUBTYPE_GPIO_4CH 0x1 | ||
39 | #define PMIC_GPIO_SUBTYPE_GPIOC_4CH 0x5 | ||
40 | #define PMIC_GPIO_SUBTYPE_GPIO_8CH 0x9 | ||
41 | #define PMIC_GPIO_SUBTYPE_GPIOC_8CH 0xd | ||
42 | |||
43 | #define PMIC_MPP_REG_RT_STS 0x10 | ||
44 | #define PMIC_MPP_REG_RT_STS_VAL_MASK 0x1 | ||
45 | |||
46 | /* control register base address offsets */ | ||
47 | #define PMIC_GPIO_REG_MODE_CTL 0x40 | ||
48 | #define PMIC_GPIO_REG_DIG_VIN_CTL 0x41 | ||
49 | #define PMIC_GPIO_REG_DIG_PULL_CTL 0x42 | ||
50 | #define PMIC_GPIO_REG_DIG_OUT_CTL 0x45 | ||
51 | #define PMIC_GPIO_REG_EN_CTL 0x46 | ||
52 | |||
53 | /* PMIC_GPIO_REG_MODE_CTL */ | ||
54 | #define PMIC_GPIO_REG_MODE_VALUE_SHIFT 0x1 | ||
55 | #define PMIC_GPIO_REG_MODE_FUNCTION_SHIFT 1 | ||
56 | #define PMIC_GPIO_REG_MODE_FUNCTION_MASK 0x7 | ||
57 | #define PMIC_GPIO_REG_MODE_DIR_SHIFT 4 | ||
58 | #define PMIC_GPIO_REG_MODE_DIR_MASK 0x7 | ||
59 | |||
60 | /* PMIC_GPIO_REG_DIG_VIN_CTL */ | ||
61 | #define PMIC_GPIO_REG_VIN_SHIFT 0 | ||
62 | #define PMIC_GPIO_REG_VIN_MASK 0x7 | ||
63 | |||
64 | /* PMIC_GPIO_REG_DIG_PULL_CTL */ | ||
65 | #define PMIC_GPIO_REG_PULL_SHIFT 0 | ||
66 | #define PMIC_GPIO_REG_PULL_MASK 0x7 | ||
67 | |||
68 | #define PMIC_GPIO_PULL_DOWN 4 | ||
69 | #define PMIC_GPIO_PULL_DISABLE 5 | ||
70 | |||
71 | /* PMIC_GPIO_REG_DIG_OUT_CTL */ | ||
72 | #define PMIC_GPIO_REG_OUT_STRENGTH_SHIFT 0 | ||
73 | #define PMIC_GPIO_REG_OUT_STRENGTH_MASK 0x3 | ||
74 | #define PMIC_GPIO_REG_OUT_TYPE_SHIFT 4 | ||
75 | #define PMIC_GPIO_REG_OUT_TYPE_MASK 0x3 | ||
76 | |||
77 | /* | ||
78 | * Output type - indicates pin should be configured as push-pull, | ||
79 | * open drain or open source. | ||
80 | */ | ||
81 | #define PMIC_GPIO_OUT_BUF_CMOS 0 | ||
82 | #define PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS 1 | ||
83 | #define PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS 2 | ||
84 | |||
85 | /* PMIC_GPIO_REG_EN_CTL */ | ||
86 | #define PMIC_GPIO_REG_MASTER_EN_SHIFT 7 | ||
87 | |||
88 | #define PMIC_GPIO_PHYSICAL_OFFSET 1 | ||
89 | |||
90 | /* Qualcomm specific pin configurations */ | ||
91 | #define PMIC_GPIO_CONF_PULL_UP (PIN_CONFIG_END + 1) | ||
92 | #define PMIC_GPIO_CONF_STRENGTH (PIN_CONFIG_END + 2) | ||
93 | |||
94 | /** | ||
95 | * struct pmic_gpio_pad - keep current GPIO settings | ||
96 | * @base: Address base in SPMI device. | ||
97 | * @irq: IRQ number which this GPIO generate. | ||
98 | * @is_enabled: Set to false when GPIO should be put in high Z state. | ||
99 | * @out_value: Cached pin output value | ||
100 | * @have_buffer: Set to true if GPIO output could be configured in push-pull, | ||
101 | * open-drain or open-source mode. | ||
102 | * @output_enabled: Set to true if GPIO output logic is enabled. | ||
103 | * @input_enabled: Set to true if GPIO input buffer logic is enabled. | ||
104 | * @num_sources: Number of power-sources supported by this GPIO. | ||
105 | * @power_source: Current power-source used. | ||
106 | * @buffer_type: Push-pull, open-drain or open-source. | ||
107 | * @pullup: Constant current which flow trough GPIO output buffer. | ||
108 | * @strength: No, Low, Medium, High | ||
109 | * @function: See pmic_gpio_functions[] | ||
110 | */ | ||
111 | struct pmic_gpio_pad { | ||
112 | u16 base; | ||
113 | int irq; | ||
114 | bool is_enabled; | ||
115 | bool out_value; | ||
116 | bool have_buffer; | ||
117 | bool output_enabled; | ||
118 | bool input_enabled; | ||
119 | unsigned int num_sources; | ||
120 | unsigned int power_source; | ||
121 | unsigned int buffer_type; | ||
122 | unsigned int pullup; | ||
123 | unsigned int strength; | ||
124 | unsigned int function; | ||
125 | }; | ||
126 | |||
127 | struct pmic_gpio_state { | ||
128 | struct device *dev; | ||
129 | struct regmap *map; | ||
130 | struct pinctrl_dev *ctrl; | ||
131 | struct gpio_chip chip; | ||
132 | }; | ||
133 | |||
134 | struct pmic_gpio_bindings { | ||
135 | const char *property; | ||
136 | unsigned param; | ||
137 | }; | ||
138 | |||
139 | static struct pmic_gpio_bindings pmic_gpio_bindings[] = { | ||
140 | {"qcom,pull-up-strength", PMIC_GPIO_CONF_PULL_UP}, | ||
141 | {"qcom,drive-strength", PMIC_GPIO_CONF_STRENGTH}, | ||
142 | }; | ||
143 | |||
144 | static const char *const pmic_gpio_groups[] = { | ||
145 | "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", "gpio8", | ||
146 | "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", "gpio15", | ||
147 | "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22", | ||
148 | "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29", | ||
149 | "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36", | ||
150 | }; | ||
151 | |||
152 | static const char *const pmic_gpio_functions[] = { | ||
153 | PMIC_GPIO_FUNC_NORMAL, PMIC_GPIO_FUNC_PAIRED, | ||
154 | PMIC_GPIO_FUNC_FUNC1, PMIC_GPIO_FUNC_FUNC2, | ||
155 | PMIC_GPIO_FUNC_DTEST1, PMIC_GPIO_FUNC_DTEST2, | ||
156 | PMIC_GPIO_FUNC_DTEST3, PMIC_GPIO_FUNC_DTEST4, | ||
157 | }; | ||
158 | |||
159 | static inline struct pmic_gpio_state *to_gpio_state(struct gpio_chip *chip) | ||
160 | { | ||
161 | return container_of(chip, struct pmic_gpio_state, chip); | ||
162 | }; | ||
163 | |||
164 | static int pmic_gpio_read(struct pmic_gpio_state *state, | ||
165 | struct pmic_gpio_pad *pad, unsigned int addr) | ||
166 | { | ||
167 | unsigned int val; | ||
168 | int ret; | ||
169 | |||
170 | ret = regmap_read(state->map, pad->base + addr, &val); | ||
171 | if (ret < 0) | ||
172 | dev_err(state->dev, "read 0x%x failed\n", addr); | ||
173 | else | ||
174 | ret = val; | ||
175 | |||
176 | return ret; | ||
177 | } | ||
178 | |||
179 | static int pmic_gpio_write(struct pmic_gpio_state *state, | ||
180 | struct pmic_gpio_pad *pad, unsigned int addr, | ||
181 | unsigned int val) | ||
182 | { | ||
183 | int ret; | ||
184 | |||
185 | ret = regmap_write(state->map, pad->base + addr, val); | ||
186 | if (ret < 0) | ||
187 | dev_err(state->dev, "write 0x%x failed\n", addr); | ||
188 | |||
189 | return ret; | ||
190 | } | ||
191 | |||
192 | static int pmic_gpio_get_groups_count(struct pinctrl_dev *pctldev) | ||
193 | { | ||
194 | /* Every PIN is a group */ | ||
195 | return pctldev->desc->npins; | ||
196 | } | ||
197 | |||
198 | static const char *pmic_gpio_get_group_name(struct pinctrl_dev *pctldev, | ||
199 | unsigned pin) | ||
200 | { | ||
201 | return pctldev->desc->pins[pin].name; | ||
202 | } | ||
203 | |||
204 | static int pmic_gpio_get_group_pins(struct pinctrl_dev *pctldev, unsigned pin, | ||
205 | const unsigned **pins, unsigned *num_pins) | ||
206 | { | ||
207 | *pins = &pctldev->desc->pins[pin].number; | ||
208 | *num_pins = 1; | ||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | static int pmic_gpio_parse_dt_config(struct device_node *np, | ||
213 | struct pinctrl_dev *pctldev, | ||
214 | unsigned long **configs, | ||
215 | unsigned int *nconfs) | ||
216 | { | ||
217 | struct pmic_gpio_bindings *par; | ||
218 | unsigned long cfg; | ||
219 | int ret, i; | ||
220 | u32 val; | ||
221 | |||
222 | for (i = 0; i < ARRAY_SIZE(pmic_gpio_bindings); i++) { | ||
223 | par = &pmic_gpio_bindings[i]; | ||
224 | ret = of_property_read_u32(np, par->property, &val); | ||
225 | |||
226 | /* property not found */ | ||
227 | if (ret == -EINVAL) | ||
228 | continue; | ||
229 | |||
230 | /* use zero as default value */ | ||
231 | if (ret) | ||
232 | val = 0; | ||
233 | |||
234 | dev_dbg(pctldev->dev, "found %s with value %u\n", | ||
235 | par->property, val); | ||
236 | |||
237 | cfg = pinconf_to_config_packed(par->param, val); | ||
238 | |||
239 | ret = pinctrl_utils_add_config(pctldev, configs, nconfs, cfg); | ||
240 | if (ret) | ||
241 | return ret; | ||
242 | } | ||
243 | |||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | static int pmic_gpio_dt_subnode_to_map(struct pinctrl_dev *pctldev, | ||
248 | struct device_node *np, | ||
249 | struct pinctrl_map **map, | ||
250 | unsigned *reserv, unsigned *nmaps, | ||
251 | enum pinctrl_map_type type) | ||
252 | { | ||
253 | unsigned long *configs = NULL; | ||
254 | unsigned nconfs = 0; | ||
255 | struct property *prop; | ||
256 | const char *group; | ||
257 | int ret; | ||
258 | |||
259 | ret = pmic_gpio_parse_dt_config(np, pctldev, &configs, &nconfs); | ||
260 | if (ret < 0) | ||
261 | return ret; | ||
262 | |||
263 | if (!nconfs) | ||
264 | return 0; | ||
265 | |||
266 | ret = of_property_count_strings(np, "pins"); | ||
267 | if (ret < 0) | ||
268 | goto exit; | ||
269 | |||
270 | ret = pinctrl_utils_reserve_map(pctldev, map, reserv, nmaps, ret); | ||
271 | if (ret < 0) | ||
272 | goto exit; | ||
273 | |||
274 | of_property_for_each_string(np, "pins", prop, group) { | ||
275 | ret = pinctrl_utils_add_map_configs(pctldev, map, | ||
276 | reserv, nmaps, group, | ||
277 | configs, nconfs, type); | ||
278 | if (ret < 0) | ||
279 | break; | ||
280 | } | ||
281 | exit: | ||
282 | kfree(configs); | ||
283 | return ret; | ||
284 | } | ||
285 | |||
286 | static int pmic_gpio_dt_node_to_map(struct pinctrl_dev *pctldev, | ||
287 | struct device_node *np_config, | ||
288 | struct pinctrl_map **map, unsigned *nmaps) | ||
289 | { | ||
290 | enum pinctrl_map_type type; | ||
291 | struct device_node *np; | ||
292 | unsigned reserv; | ||
293 | int ret; | ||
294 | |||
295 | ret = 0; | ||
296 | *map = NULL; | ||
297 | *nmaps = 0; | ||
298 | reserv = 0; | ||
299 | type = PIN_MAP_TYPE_CONFIGS_GROUP; | ||
300 | |||
301 | for_each_child_of_node(np_config, np) { | ||
302 | ret = pinconf_generic_dt_subnode_to_map(pctldev, np, map, | ||
303 | &reserv, nmaps, type); | ||
304 | if (ret) | ||
305 | break; | ||
306 | |||
307 | ret = pmic_gpio_dt_subnode_to_map(pctldev, np, map, &reserv, | ||
308 | nmaps, type); | ||
309 | if (ret) | ||
310 | break; | ||
311 | } | ||
312 | |||
313 | if (ret < 0) | ||
314 | pinctrl_utils_dt_free_map(pctldev, *map, *nmaps); | ||
315 | |||
316 | return ret; | ||
317 | } | ||
318 | |||
319 | static const struct pinctrl_ops pmic_gpio_pinctrl_ops = { | ||
320 | .get_groups_count = pmic_gpio_get_groups_count, | ||
321 | .get_group_name = pmic_gpio_get_group_name, | ||
322 | .get_group_pins = pmic_gpio_get_group_pins, | ||
323 | .dt_node_to_map = pmic_gpio_dt_node_to_map, | ||
324 | .dt_free_map = pinctrl_utils_dt_free_map, | ||
325 | }; | ||
326 | |||
327 | static int pmic_gpio_get_functions_count(struct pinctrl_dev *pctldev) | ||
328 | { | ||
329 | return ARRAY_SIZE(pmic_gpio_functions); | ||
330 | } | ||
331 | |||
332 | static const char *pmic_gpio_get_function_name(struct pinctrl_dev *pctldev, | ||
333 | unsigned function) | ||
334 | { | ||
335 | return pmic_gpio_functions[function]; | ||
336 | } | ||
337 | |||
338 | static int pmic_gpio_get_function_groups(struct pinctrl_dev *pctldev, | ||
339 | unsigned function, | ||
340 | const char *const **groups, | ||
341 | unsigned *const num_qgroups) | ||
342 | { | ||
343 | *groups = pmic_gpio_groups; | ||
344 | *num_qgroups = pctldev->desc->npins; | ||
345 | return 0; | ||
346 | } | ||
347 | |||
348 | static int pmic_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned function, | ||
349 | unsigned pin) | ||
350 | { | ||
351 | struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev); | ||
352 | struct pmic_gpio_pad *pad; | ||
353 | unsigned int val; | ||
354 | int ret; | ||
355 | |||
356 | pad = pctldev->desc->pins[pin].drv_data; | ||
357 | |||
358 | pad->function = function; | ||
359 | |||
360 | val = 0; | ||
361 | if (pad->output_enabled) { | ||
362 | if (pad->input_enabled) | ||
363 | val = 2; | ||
364 | else | ||
365 | val = 1; | ||
366 | } | ||
367 | |||
368 | val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT; | ||
369 | val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT; | ||
370 | |||
371 | ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); | ||
372 | if (ret < 0) | ||
373 | return ret; | ||
374 | |||
375 | val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT; | ||
376 | |||
377 | return pmic_gpio_write(state, pad, PMIC_GPIO_REG_EN_CTL, val); | ||
378 | } | ||
379 | |||
380 | static const struct pinmux_ops pmic_gpio_pinmux_ops = { | ||
381 | .get_functions_count = pmic_gpio_get_functions_count, | ||
382 | .get_function_name = pmic_gpio_get_function_name, | ||
383 | .get_function_groups = pmic_gpio_get_function_groups, | ||
384 | .set_mux = pmic_gpio_set_mux, | ||
385 | }; | ||
386 | |||
387 | static int pmic_gpio_config_get(struct pinctrl_dev *pctldev, | ||
388 | unsigned int pin, unsigned long *config) | ||
389 | { | ||
390 | unsigned param = pinconf_to_config_param(*config); | ||
391 | struct pmic_gpio_pad *pad; | ||
392 | unsigned arg; | ||
393 | |||
394 | pad = pctldev->desc->pins[pin].drv_data; | ||
395 | |||
396 | switch (param) { | ||
397 | case PIN_CONFIG_DRIVE_PUSH_PULL: | ||
398 | arg = pad->buffer_type == PMIC_GPIO_OUT_BUF_CMOS; | ||
399 | break; | ||
400 | case PIN_CONFIG_DRIVE_OPEN_DRAIN: | ||
401 | arg = pad->buffer_type == PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS; | ||
402 | break; | ||
403 | case PIN_CONFIG_DRIVE_OPEN_SOURCE: | ||
404 | arg = pad->buffer_type == PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS; | ||
405 | break; | ||
406 | case PIN_CONFIG_BIAS_PULL_DOWN: | ||
407 | arg = pad->pullup == PMIC_GPIO_PULL_DOWN; | ||
408 | break; | ||
409 | case PIN_CONFIG_BIAS_DISABLE: | ||
410 | arg = pad->pullup = PMIC_GPIO_PULL_DISABLE; | ||
411 | break; | ||
412 | case PIN_CONFIG_BIAS_PULL_UP: | ||
413 | arg = pad->pullup == PMIC_GPIO_PULL_UP_30; | ||
414 | break; | ||
415 | case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: | ||
416 | arg = !pad->is_enabled; | ||
417 | break; | ||
418 | case PIN_CONFIG_POWER_SOURCE: | ||
419 | arg = pad->power_source; | ||
420 | break; | ||
421 | case PIN_CONFIG_INPUT_ENABLE: | ||
422 | arg = pad->input_enabled; | ||
423 | break; | ||
424 | case PIN_CONFIG_OUTPUT: | ||
425 | arg = pad->out_value; | ||
426 | break; | ||
427 | case PMIC_GPIO_CONF_PULL_UP: | ||
428 | arg = pad->pullup; | ||
429 | break; | ||
430 | case PMIC_GPIO_CONF_STRENGTH: | ||
431 | arg = pad->strength; | ||
432 | break; | ||
433 | default: | ||
434 | return -EINVAL; | ||
435 | } | ||
436 | |||
437 | *config = pinconf_to_config_packed(param, arg); | ||
438 | return 0; | ||
439 | } | ||
440 | |||
441 | static int pmic_gpio_config_set(struct pinctrl_dev *pctldev, unsigned int pin, | ||
442 | unsigned long *configs, unsigned nconfs) | ||
443 | { | ||
444 | struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev); | ||
445 | struct pmic_gpio_pad *pad; | ||
446 | unsigned param, arg; | ||
447 | unsigned int val; | ||
448 | int i, ret; | ||
449 | |||
450 | pad = pctldev->desc->pins[pin].drv_data; | ||
451 | |||
452 | for (i = 0; i < nconfs; i++) { | ||
453 | param = pinconf_to_config_param(configs[i]); | ||
454 | arg = pinconf_to_config_argument(configs[i]); | ||
455 | |||
456 | switch (param) { | ||
457 | case PIN_CONFIG_DRIVE_PUSH_PULL: | ||
458 | pad->buffer_type = PMIC_GPIO_OUT_BUF_CMOS; | ||
459 | break; | ||
460 | case PIN_CONFIG_DRIVE_OPEN_DRAIN: | ||
461 | if (!pad->have_buffer) | ||
462 | return -EINVAL; | ||
463 | pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS; | ||
464 | break; | ||
465 | case PIN_CONFIG_DRIVE_OPEN_SOURCE: | ||
466 | if (!pad->have_buffer) | ||
467 | return -EINVAL; | ||
468 | pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS; | ||
469 | break; | ||
470 | case PIN_CONFIG_BIAS_DISABLE: | ||
471 | pad->pullup = PMIC_GPIO_PULL_DISABLE; | ||
472 | break; | ||
473 | case PIN_CONFIG_BIAS_PULL_UP: | ||
474 | pad->pullup = PMIC_GPIO_PULL_UP_30; | ||
475 | break; | ||
476 | case PIN_CONFIG_BIAS_PULL_DOWN: | ||
477 | if (arg) | ||
478 | pad->pullup = PMIC_GPIO_PULL_DOWN; | ||
479 | else | ||
480 | pad->pullup = PMIC_GPIO_PULL_DISABLE; | ||
481 | break; | ||
482 | case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: | ||
483 | pad->is_enabled = false; | ||
484 | break; | ||
485 | case PIN_CONFIG_POWER_SOURCE: | ||
486 | if (arg > pad->num_sources) | ||
487 | return -EINVAL; | ||
488 | pad->power_source = arg; | ||
489 | break; | ||
490 | case PIN_CONFIG_INPUT_ENABLE: | ||
491 | pad->input_enabled = arg ? true : false; | ||
492 | break; | ||
493 | case PIN_CONFIG_OUTPUT: | ||
494 | pad->output_enabled = true; | ||
495 | pad->out_value = arg; | ||
496 | break; | ||
497 | case PMIC_GPIO_CONF_PULL_UP: | ||
498 | if (arg > PMIC_GPIO_PULL_UP_1P5_30) | ||
499 | return -EINVAL; | ||
500 | pad->pullup = arg; | ||
501 | break; | ||
502 | case PMIC_GPIO_CONF_STRENGTH: | ||
503 | if (arg > PMIC_GPIO_STRENGTH_LOW) | ||
504 | return -EINVAL; | ||
505 | pad->strength = arg; | ||
506 | break; | ||
507 | default: | ||
508 | return -EINVAL; | ||
509 | } | ||
510 | } | ||
511 | |||
512 | val = pad->power_source << PMIC_GPIO_REG_VIN_SHIFT; | ||
513 | |||
514 | ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL, val); | ||
515 | if (ret < 0) | ||
516 | return ret; | ||
517 | |||
518 | val = pad->pullup << PMIC_GPIO_REG_PULL_SHIFT; | ||
519 | |||
520 | ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_PULL_CTL, val); | ||
521 | if (ret < 0) | ||
522 | return ret; | ||
523 | |||
524 | val = pad->buffer_type << PMIC_GPIO_REG_OUT_TYPE_SHIFT; | ||
525 | val = pad->strength << PMIC_GPIO_REG_OUT_STRENGTH_SHIFT; | ||
526 | |||
527 | ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL, val); | ||
528 | if (ret < 0) | ||
529 | return ret; | ||
530 | |||
531 | val = 0; | ||
532 | if (pad->output_enabled) { | ||
533 | if (pad->input_enabled) | ||
534 | val = 2; | ||
535 | else | ||
536 | val = 1; | ||
537 | } | ||
538 | |||
539 | val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT; | ||
540 | val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT; | ||
541 | val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT; | ||
542 | |||
543 | return pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); | ||
544 | } | ||
545 | |||
546 | static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev, | ||
547 | struct seq_file *s, unsigned pin) | ||
548 | { | ||
549 | struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev); | ||
550 | struct pmic_gpio_pad *pad; | ||
551 | int ret, val; | ||
552 | |||
553 | static const char *const biases[] = { | ||
554 | "pull-up 30uA", "pull-up 1.5uA", "pull-up 31.5uA", | ||
555 | "pull-up 1.5uA + 30uA boost", "pull-down 10uA", "no pull" | ||
556 | }; | ||
557 | static const char *const buffer_types[] = { | ||
558 | "push-pull", "open-drain", "open-source" | ||
559 | }; | ||
560 | static const char *const strengths[] = { | ||
561 | "no", "high", "medium", "low" | ||
562 | }; | ||
563 | |||
564 | pad = pctldev->desc->pins[pin].drv_data; | ||
565 | |||
566 | seq_printf(s, " gpio%-2d:", pin + PMIC_GPIO_PHYSICAL_OFFSET); | ||
567 | |||
568 | val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_EN_CTL); | ||
569 | |||
570 | if (val < 0 || !(val >> PMIC_GPIO_REG_MASTER_EN_SHIFT)) { | ||
571 | seq_puts(s, " ---"); | ||
572 | } else { | ||
573 | |||
574 | if (!pad->input_enabled) { | ||
575 | ret = pmic_gpio_read(state, pad, PMIC_MPP_REG_RT_STS); | ||
576 | if (!ret) { | ||
577 | ret &= PMIC_MPP_REG_RT_STS_VAL_MASK; | ||
578 | pad->out_value = ret; | ||
579 | } | ||
580 | } | ||
581 | |||
582 | seq_printf(s, " %-4s", pad->output_enabled ? "out" : "in"); | ||
583 | seq_printf(s, " %-7s", pmic_gpio_functions[pad->function]); | ||
584 | seq_printf(s, " vin-%d", pad->power_source); | ||
585 | seq_printf(s, " %-27s", biases[pad->pullup]); | ||
586 | seq_printf(s, " %-10s", buffer_types[pad->buffer_type]); | ||
587 | seq_printf(s, " %-4s", pad->out_value ? "high" : "low"); | ||
588 | seq_printf(s, " %-7s", strengths[pad->strength]); | ||
589 | } | ||
590 | } | ||
591 | |||
592 | static const struct pinconf_ops pmic_gpio_pinconf_ops = { | ||
593 | .pin_config_group_get = pmic_gpio_config_get, | ||
594 | .pin_config_group_set = pmic_gpio_config_set, | ||
595 | .pin_config_group_dbg_show = pmic_gpio_config_dbg_show, | ||
596 | }; | ||
597 | |||
598 | static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned pin) | ||
599 | { | ||
600 | struct pmic_gpio_state *state = to_gpio_state(chip); | ||
601 | unsigned long config; | ||
602 | |||
603 | config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1); | ||
604 | |||
605 | return pmic_gpio_config_set(state->ctrl, pin, &config, 1); | ||
606 | } | ||
607 | |||
608 | static int pmic_gpio_direction_output(struct gpio_chip *chip, | ||
609 | unsigned pin, int val) | ||
610 | { | ||
611 | struct pmic_gpio_state *state = to_gpio_state(chip); | ||
612 | unsigned long config; | ||
613 | |||
614 | config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val); | ||
615 | |||
616 | return pmic_gpio_config_set(state->ctrl, pin, &config, 1); | ||
617 | } | ||
618 | |||
619 | static int pmic_gpio_get(struct gpio_chip *chip, unsigned pin) | ||
620 | { | ||
621 | struct pmic_gpio_state *state = to_gpio_state(chip); | ||
622 | struct pmic_gpio_pad *pad; | ||
623 | int ret; | ||
624 | |||
625 | pad = state->ctrl->desc->pins[pin].drv_data; | ||
626 | |||
627 | if (!pad->is_enabled) | ||
628 | return -EINVAL; | ||
629 | |||
630 | if (pad->input_enabled) { | ||
631 | ret = pmic_gpio_read(state, pad, PMIC_MPP_REG_RT_STS); | ||
632 | if (ret < 0) | ||
633 | return ret; | ||
634 | |||
635 | pad->out_value = ret & PMIC_MPP_REG_RT_STS_VAL_MASK; | ||
636 | } | ||
637 | |||
638 | return pad->out_value; | ||
639 | } | ||
640 | |||
641 | static void pmic_gpio_set(struct gpio_chip *chip, unsigned pin, int value) | ||
642 | { | ||
643 | struct pmic_gpio_state *state = to_gpio_state(chip); | ||
644 | unsigned long config; | ||
645 | |||
646 | config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value); | ||
647 | |||
648 | pmic_gpio_config_set(state->ctrl, pin, &config, 1); | ||
649 | } | ||
650 | |||
651 | static int pmic_gpio_request(struct gpio_chip *chip, unsigned base) | ||
652 | { | ||
653 | return pinctrl_request_gpio(chip->base + base); | ||
654 | } | ||
655 | |||
656 | static void pmic_gpio_free(struct gpio_chip *chip, unsigned base) | ||
657 | { | ||
658 | pinctrl_free_gpio(chip->base + base); | ||
659 | } | ||
660 | |||
661 | static int pmic_gpio_of_xlate(struct gpio_chip *chip, | ||
662 | const struct of_phandle_args *gpio_desc, | ||
663 | u32 *flags) | ||
664 | { | ||
665 | if (chip->of_gpio_n_cells < 2) | ||
666 | return -EINVAL; | ||
667 | |||
668 | if (flags) | ||
669 | *flags = gpio_desc->args[1]; | ||
670 | |||
671 | return gpio_desc->args[0] - PMIC_GPIO_PHYSICAL_OFFSET; | ||
672 | } | ||
673 | |||
674 | static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned pin) | ||
675 | { | ||
676 | struct pmic_gpio_state *state = to_gpio_state(chip); | ||
677 | struct pmic_gpio_pad *pad; | ||
678 | |||
679 | pad = state->ctrl->desc->pins[pin].drv_data; | ||
680 | |||
681 | return pad->irq; | ||
682 | } | ||
683 | |||
684 | static void pmic_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) | ||
685 | { | ||
686 | struct pmic_gpio_state *state = to_gpio_state(chip); | ||
687 | unsigned i; | ||
688 | |||
689 | for (i = 0; i < chip->ngpio; i++) { | ||
690 | pmic_gpio_config_dbg_show(state->ctrl, s, i); | ||
691 | seq_puts(s, "\n"); | ||
692 | } | ||
693 | } | ||
694 | |||
695 | static const struct gpio_chip pmic_gpio_gpio_template = { | ||
696 | .direction_input = pmic_gpio_direction_input, | ||
697 | .direction_output = pmic_gpio_direction_output, | ||
698 | .get = pmic_gpio_get, | ||
699 | .set = pmic_gpio_set, | ||
700 | .request = pmic_gpio_request, | ||
701 | .free = pmic_gpio_free, | ||
702 | .of_xlate = pmic_gpio_of_xlate, | ||
703 | .to_irq = pmic_gpio_to_irq, | ||
704 | .dbg_show = pmic_gpio_dbg_show, | ||
705 | }; | ||
706 | |||
707 | static int pmic_gpio_populate(struct pmic_gpio_state *state, | ||
708 | struct pmic_gpio_pad *pad) | ||
709 | { | ||
710 | int type, subtype, val, dir; | ||
711 | |||
712 | type = pmic_gpio_read(state, pad, PMIC_GPIO_REG_TYPE); | ||
713 | if (type < 0) | ||
714 | return type; | ||
715 | |||
716 | if (type != PMIC_GPIO_TYPE) { | ||
717 | dev_err(state->dev, "incorrect block type 0x%x at 0x%x\n", | ||
718 | type, pad->base); | ||
719 | return -ENODEV; | ||
720 | } | ||
721 | |||
722 | subtype = pmic_gpio_read(state, pad, PMIC_GPIO_REG_SUBTYPE); | ||
723 | if (subtype < 0) | ||
724 | return subtype; | ||
725 | |||
726 | switch (subtype) { | ||
727 | case PMIC_GPIO_SUBTYPE_GPIO_4CH: | ||
728 | pad->have_buffer = true; | ||
729 | case PMIC_GPIO_SUBTYPE_GPIOC_4CH: | ||
730 | pad->num_sources = 4; | ||
731 | break; | ||
732 | case PMIC_GPIO_SUBTYPE_GPIO_8CH: | ||
733 | pad->have_buffer = true; | ||
734 | case PMIC_GPIO_SUBTYPE_GPIOC_8CH: | ||
735 | pad->num_sources = 8; | ||
736 | break; | ||
737 | default: | ||
738 | dev_err(state->dev, "unknown GPIO type 0x%x\n", subtype); | ||
739 | return -ENODEV; | ||
740 | } | ||
741 | |||
742 | val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL); | ||
743 | if (val < 0) | ||
744 | return val; | ||
745 | |||
746 | pad->out_value = val & PMIC_GPIO_REG_MODE_VALUE_SHIFT; | ||
747 | |||
748 | dir = val >> PMIC_GPIO_REG_MODE_DIR_SHIFT; | ||
749 | dir &= PMIC_GPIO_REG_MODE_DIR_MASK; | ||
750 | switch (dir) { | ||
751 | case 0: | ||
752 | pad->input_enabled = true; | ||
753 | pad->output_enabled = false; | ||
754 | break; | ||
755 | case 1: | ||
756 | pad->input_enabled = false; | ||
757 | pad->output_enabled = true; | ||
758 | break; | ||
759 | case 2: | ||
760 | pad->input_enabled = true; | ||
761 | pad->output_enabled = true; | ||
762 | break; | ||
763 | default: | ||
764 | dev_err(state->dev, "unknown GPIO direction\n"); | ||
765 | return -ENODEV; | ||
766 | } | ||
767 | |||
768 | pad->function = val >> PMIC_GPIO_REG_MODE_FUNCTION_SHIFT; | ||
769 | pad->function &= PMIC_GPIO_REG_MODE_FUNCTION_MASK; | ||
770 | |||
771 | val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL); | ||
772 | if (val < 0) | ||
773 | return val; | ||
774 | |||
775 | pad->power_source = val >> PMIC_GPIO_REG_VIN_SHIFT; | ||
776 | pad->power_source &= PMIC_GPIO_REG_VIN_MASK; | ||
777 | |||
778 | val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_PULL_CTL); | ||
779 | if (val < 0) | ||
780 | return val; | ||
781 | |||
782 | pad->pullup = val >> PMIC_GPIO_REG_PULL_SHIFT; | ||
783 | pad->pullup &= PMIC_GPIO_REG_PULL_MASK; | ||
784 | |||
785 | val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL); | ||
786 | if (val < 0) | ||
787 | return val; | ||
788 | |||
789 | pad->strength = val >> PMIC_GPIO_REG_OUT_STRENGTH_SHIFT; | ||
790 | pad->strength &= PMIC_GPIO_REG_OUT_STRENGTH_MASK; | ||
791 | |||
792 | pad->buffer_type = val >> PMIC_GPIO_REG_OUT_TYPE_SHIFT; | ||
793 | pad->buffer_type &= PMIC_GPIO_REG_OUT_TYPE_MASK; | ||
794 | |||
795 | /* Pin could be disabled with PIN_CONFIG_BIAS_HIGH_IMPEDANCE */ | ||
796 | pad->is_enabled = true; | ||
797 | return 0; | ||
798 | } | ||
799 | |||
800 | static int pmic_gpio_probe(struct platform_device *pdev) | ||
801 | { | ||
802 | struct device *dev = &pdev->dev; | ||
803 | struct pinctrl_pin_desc *pindesc; | ||
804 | struct pinctrl_desc *pctrldesc; | ||
805 | struct pmic_gpio_pad *pad, *pads; | ||
806 | struct pmic_gpio_state *state; | ||
807 | int ret, npins, i; | ||
808 | u32 res[2]; | ||
809 | |||
810 | ret = of_property_read_u32_array(dev->of_node, "reg", res, 2); | ||
811 | if (ret < 0) { | ||
812 | dev_err(dev, "missing base address and/or range"); | ||
813 | return ret; | ||
814 | } | ||
815 | |||
816 | npins = res[1] / PMIC_GPIO_ADDRESS_RANGE; | ||
817 | |||
818 | if (!npins) | ||
819 | return -EINVAL; | ||
820 | |||
821 | BUG_ON(npins > ARRAY_SIZE(pmic_gpio_groups)); | ||
822 | |||
823 | state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); | ||
824 | if (!state) | ||
825 | return -ENOMEM; | ||
826 | |||
827 | platform_set_drvdata(pdev, state); | ||
828 | |||
829 | state->dev = &pdev->dev; | ||
830 | state->map = dev_get_regmap(dev->parent, NULL); | ||
831 | |||
832 | pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL); | ||
833 | if (!pindesc) | ||
834 | return -ENOMEM; | ||
835 | |||
836 | pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL); | ||
837 | if (!pads) | ||
838 | return -ENOMEM; | ||
839 | |||
840 | pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL); | ||
841 | if (!pctrldesc) | ||
842 | return -ENOMEM; | ||
843 | |||
844 | pctrldesc->pctlops = &pmic_gpio_pinctrl_ops; | ||
845 | pctrldesc->pmxops = &pmic_gpio_pinmux_ops; | ||
846 | pctrldesc->confops = &pmic_gpio_pinconf_ops; | ||
847 | pctrldesc->owner = THIS_MODULE; | ||
848 | pctrldesc->name = dev_name(dev); | ||
849 | pctrldesc->pins = pindesc; | ||
850 | pctrldesc->npins = npins; | ||
851 | |||
852 | for (i = 0; i < npins; i++, pindesc++) { | ||
853 | pad = &pads[i]; | ||
854 | pindesc->drv_data = pad; | ||
855 | pindesc->number = i; | ||
856 | pindesc->name = pmic_gpio_groups[i]; | ||
857 | |||
858 | pad->irq = platform_get_irq(pdev, i); | ||
859 | if (pad->irq < 0) | ||
860 | return pad->irq; | ||
861 | |||
862 | pad->base = res[0] + i * PMIC_GPIO_ADDRESS_RANGE; | ||
863 | |||
864 | ret = pmic_gpio_populate(state, pad); | ||
865 | if (ret < 0) | ||
866 | return ret; | ||
867 | } | ||
868 | |||
869 | state->chip = pmic_gpio_gpio_template; | ||
870 | state->chip.dev = dev; | ||
871 | state->chip.base = -1; | ||
872 | state->chip.ngpio = npins; | ||
873 | state->chip.label = dev_name(dev); | ||
874 | state->chip.of_gpio_n_cells = 2; | ||
875 | state->chip.can_sleep = false; | ||
876 | |||
877 | state->ctrl = pinctrl_register(pctrldesc, dev, state); | ||
878 | if (!state->ctrl) | ||
879 | return -ENODEV; | ||
880 | |||
881 | ret = gpiochip_add(&state->chip); | ||
882 | if (ret) { | ||
883 | dev_err(state->dev, "can't add gpio chip\n"); | ||
884 | goto err_chip; | ||
885 | } | ||
886 | |||
887 | ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, npins); | ||
888 | if (ret) { | ||
889 | dev_err(dev, "failed to add pin range\n"); | ||
890 | goto err_range; | ||
891 | } | ||
892 | |||
893 | return 0; | ||
894 | |||
895 | err_range: | ||
896 | gpiochip_remove(&state->chip); | ||
897 | err_chip: | ||
898 | pinctrl_unregister(state->ctrl); | ||
899 | return ret; | ||
900 | } | ||
901 | |||
902 | static int pmic_gpio_remove(struct platform_device *pdev) | ||
903 | { | ||
904 | struct pmic_gpio_state *state = platform_get_drvdata(pdev); | ||
905 | |||
906 | gpiochip_remove(&state->chip); | ||
907 | pinctrl_unregister(state->ctrl); | ||
908 | return 0; | ||
909 | } | ||
910 | |||
911 | static const struct of_device_id pmic_gpio_of_match[] = { | ||
912 | { .compatible = "qcom,pm8941-gpio" }, /* 36 GPIO's */ | ||
913 | { .compatible = "qcom,pma8084-gpio" }, /* 22 GPIO's */ | ||
914 | { }, | ||
915 | }; | ||
916 | |||
917 | MODULE_DEVICE_TABLE(of, pmic_gpio_of_match); | ||
918 | |||
919 | static struct platform_driver pmic_gpio_driver = { | ||
920 | .driver = { | ||
921 | .name = "qcom-spmi-gpio", | ||
922 | .of_match_table = pmic_gpio_of_match, | ||
923 | }, | ||
924 | .probe = pmic_gpio_probe, | ||
925 | .remove = pmic_gpio_remove, | ||
926 | }; | ||
927 | |||
928 | module_platform_driver(pmic_gpio_driver); | ||
929 | |||
930 | MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>"); | ||
931 | MODULE_DESCRIPTION("Qualcomm SPMI PMIC GPIO pin control driver"); | ||
932 | MODULE_ALIAS("platform:qcom-spmi-gpio"); | ||
933 | MODULE_LICENSE("GPL v2"); | ||