197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2eadff302SIvan T. Ivanov /*
3d36a9773SDavid Collins  * Copyright (c) 2012-2014, 2016-2021 The Linux Foundation. All rights reserved.
4eadff302SIvan T. Ivanov  */
5eadff302SIvan T. Ivanov 
61c5fb66aSLinus Walleij #include <linux/gpio/driver.h>
7ca69e2d1SBrian Masney #include <linux/interrupt.h>
8eadff302SIvan T. Ivanov #include <linux/module.h>
9eadff302SIvan T. Ivanov #include <linux/of.h>
10ab4256cfSStephen Boyd #include <linux/of_irq.h>
11eadff302SIvan T. Ivanov #include <linux/pinctrl/pinconf-generic.h>
12eadff302SIvan T. Ivanov #include <linux/pinctrl/pinconf.h>
13eadff302SIvan T. Ivanov #include <linux/pinctrl/pinmux.h>
14eadff302SIvan T. Ivanov #include <linux/platform_device.h>
15eadff302SIvan T. Ivanov #include <linux/regmap.h>
16eadff302SIvan T. Ivanov #include <linux/slab.h>
17d36a9773SDavid Collins #include <linux/spmi.h>
18eadff302SIvan T. Ivanov #include <linux/types.h>
19eadff302SIvan T. Ivanov 
20eadff302SIvan T. Ivanov #include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
21eadff302SIvan T. Ivanov 
22eadff302SIvan T. Ivanov #include "../core.h"
23eadff302SIvan T. Ivanov #include "../pinctrl-utils.h"
24eadff302SIvan T. Ivanov 
25eadff302SIvan T. Ivanov #define PMIC_GPIO_ADDRESS_RANGE			0x100
26eadff302SIvan T. Ivanov 
27eadff302SIvan T. Ivanov /* type and subtype registers base address offsets */
28eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_TYPE			0x4
29eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_SUBTYPE			0x5
30eadff302SIvan T. Ivanov 
31eadff302SIvan T. Ivanov /* GPIO peripheral type and subtype out_values */
32eadff302SIvan T. Ivanov #define PMIC_GPIO_TYPE				0x10
33eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIO_4CH		0x1
34eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIOC_4CH		0x5
35eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIO_8CH		0x9
36eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIOC_8CH		0xd
37d7b5f5ccSFenglin Wu #define PMIC_GPIO_SUBTYPE_GPIO_LV		0x10
38d7b5f5ccSFenglin Wu #define PMIC_GPIO_SUBTYPE_GPIO_MV		0x11
39eadff302SIvan T. Ivanov 
40eadff302SIvan T. Ivanov #define PMIC_MPP_REG_RT_STS			0x10
41eadff302SIvan T. Ivanov #define PMIC_MPP_REG_RT_STS_VAL_MASK		0x1
42eadff302SIvan T. Ivanov 
43eadff302SIvan T. Ivanov /* control register base address offsets */
44eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_CTL			0x40
45eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_VIN_CTL		0x41
46eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_PULL_CTL		0x42
47d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL	0x44
48223463fcSFenglin Wu #define PMIC_GPIO_REG_DIG_IN_CTL		0x43
49eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_OUT_CTL		0x45
50eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_EN_CTL			0x46
51d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL	0x4A
52eadff302SIvan T. Ivanov 
53eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_MODE_CTL */
54eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_VALUE_SHIFT		0x1
55eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_FUNCTION_SHIFT	1
56eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_FUNCTION_MASK	0x7
57eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_DIR_SHIFT		4
58eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_DIR_MASK		0x7
59eadff302SIvan T. Ivanov 
60d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_INPUT		0
61d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_OUTPUT		1
62d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT	2
63d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_ANALOG_PASS_THRU		3
64d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_MODE_DIR_MASK	0x3
65d7b5f5ccSFenglin Wu 
66eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_VIN_CTL */
67eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_VIN_SHIFT			0
68eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_VIN_MASK			0x7
69eadff302SIvan T. Ivanov 
70eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_PULL_CTL */
71eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_PULL_SHIFT		0
72eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_PULL_MASK			0x7
73eadff302SIvan T. Ivanov 
74eadff302SIvan T. Ivanov #define PMIC_GPIO_PULL_DOWN			4
75eadff302SIvan T. Ivanov #define PMIC_GPIO_PULL_DISABLE			5
76eadff302SIvan T. Ivanov 
77d7b5f5ccSFenglin Wu /* PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL for LV/MV */
78d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_INVERT		0x80
79d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT	7
80d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK	0xF
81d7b5f5ccSFenglin Wu 
82223463fcSFenglin Wu /* PMIC_GPIO_REG_DIG_IN_CTL */
83223463fcSFenglin Wu #define PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN		0x80
84223463fcSFenglin Wu #define PMIC_GPIO_LV_MV_DIG_IN_DTEST_SEL_MASK	0x7
85223463fcSFenglin Wu #define PMIC_GPIO_DIG_IN_DTEST_SEL_MASK		0xf
86223463fcSFenglin Wu 
87eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_OUT_CTL */
88eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_STRENGTH_SHIFT	0
89eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_STRENGTH_MASK		0x3
90eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_TYPE_SHIFT		4
91eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_TYPE_MASK		0x3
92eadff302SIvan T. Ivanov 
93eadff302SIvan T. Ivanov /*
94eadff302SIvan T. Ivanov  * Output type - indicates pin should be configured as push-pull,
95eadff302SIvan T. Ivanov  * open drain or open source.
96eadff302SIvan T. Ivanov  */
97eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_CMOS			0
98eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS	1
99eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS	2
100eadff302SIvan T. Ivanov 
101eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_EN_CTL */
102eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MASTER_EN_SHIFT		7
103eadff302SIvan T. Ivanov 
104eadff302SIvan T. Ivanov #define PMIC_GPIO_PHYSICAL_OFFSET		1
105eadff302SIvan T. Ivanov 
106d7b5f5ccSFenglin Wu /* PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL */
107d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_ANA_MUX_SEL_MASK		0x3
108d7b5f5ccSFenglin Wu 
109eadff302SIvan T. Ivanov /* Qualcomm specific pin configurations */
110eadff302SIvan T. Ivanov #define PMIC_GPIO_CONF_PULL_UP			(PIN_CONFIG_END + 1)
111eadff302SIvan T. Ivanov #define PMIC_GPIO_CONF_STRENGTH			(PIN_CONFIG_END + 2)
112d7b5f5ccSFenglin Wu #define PMIC_GPIO_CONF_ATEST			(PIN_CONFIG_END + 3)
113d7b5f5ccSFenglin Wu #define PMIC_GPIO_CONF_ANALOG_PASS		(PIN_CONFIG_END + 4)
114223463fcSFenglin Wu #define PMIC_GPIO_CONF_DTEST_BUFFER		(PIN_CONFIG_END + 5)
115d7b5f5ccSFenglin Wu 
116d7b5f5ccSFenglin Wu /* The index of each function in pmic_gpio_functions[] array */
117d7b5f5ccSFenglin Wu enum pmic_gpio_func_index {
118d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_NORMAL,
119d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_PAIRED,
120d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_FUNC1,
121d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_FUNC2,
122d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_FUNC3,
123d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_FUNC4,
124d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_DTEST1,
125d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_DTEST2,
126d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_DTEST3,
127d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_DTEST4,
128d7b5f5ccSFenglin Wu };
129eadff302SIvan T. Ivanov 
130eadff302SIvan T. Ivanov /**
131eadff302SIvan T. Ivanov  * struct pmic_gpio_pad - keep current GPIO settings
132eadff302SIvan T. Ivanov  * @base: Address base in SPMI device.
133eadff302SIvan T. Ivanov  * @is_enabled: Set to false when GPIO should be put in high Z state.
134eadff302SIvan T. Ivanov  * @out_value: Cached pin output value
135eadff302SIvan T. Ivanov  * @have_buffer: Set to true if GPIO output could be configured in push-pull,
136eadff302SIvan T. Ivanov  *	open-drain or open-source mode.
137eadff302SIvan T. Ivanov  * @output_enabled: Set to true if GPIO output logic is enabled.
138eadff302SIvan T. Ivanov  * @input_enabled: Set to true if GPIO input buffer logic is enabled.
139d7b5f5ccSFenglin Wu  * @analog_pass: Set to true if GPIO is in analog-pass-through mode.
140d7b5f5ccSFenglin Wu  * @lv_mv_type: Set to true if GPIO subtype is GPIO_LV(0x10) or GPIO_MV(0x11).
141eadff302SIvan T. Ivanov  * @num_sources: Number of power-sources supported by this GPIO.
142eadff302SIvan T. Ivanov  * @power_source: Current power-source used.
143eadff302SIvan T. Ivanov  * @buffer_type: Push-pull, open-drain or open-source.
144eadff302SIvan T. Ivanov  * @pullup: Constant current which flow trough GPIO output buffer.
145eadff302SIvan T. Ivanov  * @strength: No, Low, Medium, High
146eadff302SIvan T. Ivanov  * @function: See pmic_gpio_functions[]
147d7b5f5ccSFenglin Wu  * @atest: the ATEST selection for GPIO analog-pass-through mode
148223463fcSFenglin Wu  * @dtest_buffer: the DTEST buffer selection for digital input mode.
149eadff302SIvan T. Ivanov  */
150eadff302SIvan T. Ivanov struct pmic_gpio_pad {
151eadff302SIvan T. Ivanov 	u16		base;
152eadff302SIvan T. Ivanov 	bool		is_enabled;
153eadff302SIvan T. Ivanov 	bool		out_value;
154eadff302SIvan T. Ivanov 	bool		have_buffer;
155eadff302SIvan T. Ivanov 	bool		output_enabled;
156eadff302SIvan T. Ivanov 	bool		input_enabled;
157d7b5f5ccSFenglin Wu 	bool		analog_pass;
158d7b5f5ccSFenglin Wu 	bool		lv_mv_type;
159eadff302SIvan T. Ivanov 	unsigned int	num_sources;
160eadff302SIvan T. Ivanov 	unsigned int	power_source;
161eadff302SIvan T. Ivanov 	unsigned int	buffer_type;
162eadff302SIvan T. Ivanov 	unsigned int	pullup;
163eadff302SIvan T. Ivanov 	unsigned int	strength;
164eadff302SIvan T. Ivanov 	unsigned int	function;
165d7b5f5ccSFenglin Wu 	unsigned int	atest;
166223463fcSFenglin Wu 	unsigned int	dtest_buffer;
167eadff302SIvan T. Ivanov };
168eadff302SIvan T. Ivanov 
169eadff302SIvan T. Ivanov struct pmic_gpio_state {
170eadff302SIvan T. Ivanov 	struct device	*dev;
171eadff302SIvan T. Ivanov 	struct regmap	*map;
172eadff302SIvan T. Ivanov 	struct pinctrl_dev *ctrl;
173eadff302SIvan T. Ivanov 	struct gpio_chip chip;
174d36a9773SDavid Collins 	u8 usid;
175d36a9773SDavid Collins 	u8 pid_base;
176eadff302SIvan T. Ivanov };
177eadff302SIvan T. Ivanov 
178f684e4acSLinus Walleij static const struct pinconf_generic_params pmic_gpio_bindings[] = {
1797382b623SSoren Brinkmann 	{"qcom,pull-up-strength",	PMIC_GPIO_CONF_PULL_UP,		0},
1807382b623SSoren Brinkmann 	{"qcom,drive-strength",		PMIC_GPIO_CONF_STRENGTH,	0},
181d7b5f5ccSFenglin Wu 	{"qcom,atest",			PMIC_GPIO_CONF_ATEST,		0},
182d7b5f5ccSFenglin Wu 	{"qcom,analog-pass",		PMIC_GPIO_CONF_ANALOG_PASS,	0},
183223463fcSFenglin Wu 	{"qcom,dtest-buffer",           PMIC_GPIO_CONF_DTEST_BUFFER,    0},
184eadff302SIvan T. Ivanov };
185eadff302SIvan T. Ivanov 
1864f06266aSArnd Bergmann #ifdef CONFIG_DEBUG_FS
1877382b623SSoren Brinkmann static const struct pin_config_item pmic_conf_items[ARRAY_SIZE(pmic_gpio_bindings)] = {
1887382b623SSoren Brinkmann 	PCONFDUMP(PMIC_GPIO_CONF_PULL_UP,  "pull up strength", NULL, true),
1897382b623SSoren Brinkmann 	PCONFDUMP(PMIC_GPIO_CONF_STRENGTH, "drive-strength", NULL, true),
190d7b5f5ccSFenglin Wu 	PCONFDUMP(PMIC_GPIO_CONF_ATEST, "atest", NULL, true),
191d7b5f5ccSFenglin Wu 	PCONFDUMP(PMIC_GPIO_CONF_ANALOG_PASS, "analog-pass", NULL, true),
192223463fcSFenglin Wu 	PCONFDUMP(PMIC_GPIO_CONF_DTEST_BUFFER, "dtest-buffer", NULL, true),
193eadff302SIvan T. Ivanov };
1944f06266aSArnd Bergmann #endif
195eadff302SIvan T. Ivanov 
196eadff302SIvan T. Ivanov static const char *const pmic_gpio_groups[] = {
197eadff302SIvan T. Ivanov 	"gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", "gpio8",
198eadff302SIvan T. Ivanov 	"gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", "gpio15",
199eadff302SIvan T. Ivanov 	"gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22",
200eadff302SIvan T. Ivanov 	"gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29",
201eadff302SIvan T. Ivanov 	"gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36",
202eadff302SIvan T. Ivanov };
203eadff302SIvan T. Ivanov 
204eadff302SIvan T. Ivanov static const char *const pmic_gpio_functions[] = {
205d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_NORMAL]	= PMIC_GPIO_FUNC_NORMAL,
206d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_PAIRED]	= PMIC_GPIO_FUNC_PAIRED,
207d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_FUNC1]	= PMIC_GPIO_FUNC_FUNC1,
208d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_FUNC2]	= PMIC_GPIO_FUNC_FUNC2,
209d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_FUNC3]	= PMIC_GPIO_FUNC_FUNC3,
210d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_FUNC4]	= PMIC_GPIO_FUNC_FUNC4,
211d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_DTEST1]	= PMIC_GPIO_FUNC_DTEST1,
212d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_DTEST2]	= PMIC_GPIO_FUNC_DTEST2,
213d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_DTEST3]	= PMIC_GPIO_FUNC_DTEST3,
214d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_DTEST4]	= PMIC_GPIO_FUNC_DTEST4,
215eadff302SIvan T. Ivanov };
216eadff302SIvan T. Ivanov 
217eadff302SIvan T. Ivanov static int pmic_gpio_read(struct pmic_gpio_state *state,
218eadff302SIvan T. Ivanov 			  struct pmic_gpio_pad *pad, unsigned int addr)
219eadff302SIvan T. Ivanov {
220eadff302SIvan T. Ivanov 	unsigned int val;
221eadff302SIvan T. Ivanov 	int ret;
222eadff302SIvan T. Ivanov 
223eadff302SIvan T. Ivanov 	ret = regmap_read(state->map, pad->base + addr, &val);
224eadff302SIvan T. Ivanov 	if (ret < 0)
225eadff302SIvan T. Ivanov 		dev_err(state->dev, "read 0x%x failed\n", addr);
226eadff302SIvan T. Ivanov 	else
227eadff302SIvan T. Ivanov 		ret = val;
228eadff302SIvan T. Ivanov 
229eadff302SIvan T. Ivanov 	return ret;
230eadff302SIvan T. Ivanov }
231eadff302SIvan T. Ivanov 
232eadff302SIvan T. Ivanov static int pmic_gpio_write(struct pmic_gpio_state *state,
233eadff302SIvan T. Ivanov 			   struct pmic_gpio_pad *pad, unsigned int addr,
234eadff302SIvan T. Ivanov 			   unsigned int val)
235eadff302SIvan T. Ivanov {
236eadff302SIvan T. Ivanov 	int ret;
237eadff302SIvan T. Ivanov 
238eadff302SIvan T. Ivanov 	ret = regmap_write(state->map, pad->base + addr, val);
239eadff302SIvan T. Ivanov 	if (ret < 0)
240eadff302SIvan T. Ivanov 		dev_err(state->dev, "write 0x%x failed\n", addr);
241eadff302SIvan T. Ivanov 
242eadff302SIvan T. Ivanov 	return ret;
243eadff302SIvan T. Ivanov }
244eadff302SIvan T. Ivanov 
245eadff302SIvan T. Ivanov static int pmic_gpio_get_groups_count(struct pinctrl_dev *pctldev)
246eadff302SIvan T. Ivanov {
247eadff302SIvan T. Ivanov 	/* Every PIN is a group */
248eadff302SIvan T. Ivanov 	return pctldev->desc->npins;
249eadff302SIvan T. Ivanov }
250eadff302SIvan T. Ivanov 
251eadff302SIvan T. Ivanov static const char *pmic_gpio_get_group_name(struct pinctrl_dev *pctldev,
252eadff302SIvan T. Ivanov 					    unsigned pin)
253eadff302SIvan T. Ivanov {
254eadff302SIvan T. Ivanov 	return pctldev->desc->pins[pin].name;
255eadff302SIvan T. Ivanov }
256eadff302SIvan T. Ivanov 
257eadff302SIvan T. Ivanov static int pmic_gpio_get_group_pins(struct pinctrl_dev *pctldev, unsigned pin,
258eadff302SIvan T. Ivanov 				    const unsigned **pins, unsigned *num_pins)
259eadff302SIvan T. Ivanov {
260eadff302SIvan T. Ivanov 	*pins = &pctldev->desc->pins[pin].number;
261eadff302SIvan T. Ivanov 	*num_pins = 1;
262eadff302SIvan T. Ivanov 	return 0;
263eadff302SIvan T. Ivanov }
264eadff302SIvan T. Ivanov 
265eadff302SIvan T. Ivanov static const struct pinctrl_ops pmic_gpio_pinctrl_ops = {
266eadff302SIvan T. Ivanov 	.get_groups_count	= pmic_gpio_get_groups_count,
267eadff302SIvan T. Ivanov 	.get_group_name		= pmic_gpio_get_group_name,
268eadff302SIvan T. Ivanov 	.get_group_pins		= pmic_gpio_get_group_pins,
2697382b623SSoren Brinkmann 	.dt_node_to_map		= pinconf_generic_dt_node_to_map_group,
270d32f7fd3SIrina Tirdea 	.dt_free_map		= pinctrl_utils_free_map,
271eadff302SIvan T. Ivanov };
272eadff302SIvan T. Ivanov 
273eadff302SIvan T. Ivanov static int pmic_gpio_get_functions_count(struct pinctrl_dev *pctldev)
274eadff302SIvan T. Ivanov {
275eadff302SIvan T. Ivanov 	return ARRAY_SIZE(pmic_gpio_functions);
276eadff302SIvan T. Ivanov }
277eadff302SIvan T. Ivanov 
278eadff302SIvan T. Ivanov static const char *pmic_gpio_get_function_name(struct pinctrl_dev *pctldev,
279eadff302SIvan T. Ivanov 					       unsigned function)
280eadff302SIvan T. Ivanov {
281eadff302SIvan T. Ivanov 	return pmic_gpio_functions[function];
282eadff302SIvan T. Ivanov }
283eadff302SIvan T. Ivanov 
284eadff302SIvan T. Ivanov static int pmic_gpio_get_function_groups(struct pinctrl_dev *pctldev,
285eadff302SIvan T. Ivanov 					 unsigned function,
286eadff302SIvan T. Ivanov 					 const char *const **groups,
287eadff302SIvan T. Ivanov 					 unsigned *const num_qgroups)
288eadff302SIvan T. Ivanov {
289eadff302SIvan T. Ivanov 	*groups = pmic_gpio_groups;
290eadff302SIvan T. Ivanov 	*num_qgroups = pctldev->desc->npins;
291eadff302SIvan T. Ivanov 	return 0;
292eadff302SIvan T. Ivanov }
293eadff302SIvan T. Ivanov 
294eadff302SIvan T. Ivanov static int pmic_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned function,
295eadff302SIvan T. Ivanov 				unsigned pin)
296eadff302SIvan T. Ivanov {
297eadff302SIvan T. Ivanov 	struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev);
298eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad;
299eadff302SIvan T. Ivanov 	unsigned int val;
300eadff302SIvan T. Ivanov 	int ret;
301eadff302SIvan T. Ivanov 
302d7b5f5ccSFenglin Wu 	if (function > PMIC_GPIO_FUNC_INDEX_DTEST4) {
303d7b5f5ccSFenglin Wu 		pr_err("function: %d is not defined\n", function);
304d7b5f5ccSFenglin Wu 		return -EINVAL;
305d7b5f5ccSFenglin Wu 	}
306d7b5f5ccSFenglin Wu 
307eadff302SIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
308d7b5f5ccSFenglin Wu 	/*
309d7b5f5ccSFenglin Wu 	 * Non-LV/MV subtypes only support 2 special functions,
310d7b5f5ccSFenglin Wu 	 * offsetting the dtestx function values by 2
311d7b5f5ccSFenglin Wu 	 */
312d7b5f5ccSFenglin Wu 	if (!pad->lv_mv_type) {
313d7b5f5ccSFenglin Wu 		if (function == PMIC_GPIO_FUNC_INDEX_FUNC3 ||
314d7b5f5ccSFenglin Wu 				function == PMIC_GPIO_FUNC_INDEX_FUNC4) {
315d7b5f5ccSFenglin Wu 			pr_err("LV/MV subtype doesn't have func3/func4\n");
316d7b5f5ccSFenglin Wu 			return -EINVAL;
317d7b5f5ccSFenglin Wu 		}
318d7b5f5ccSFenglin Wu 		if (function >= PMIC_GPIO_FUNC_INDEX_DTEST1)
319d7b5f5ccSFenglin Wu 			function -= (PMIC_GPIO_FUNC_INDEX_DTEST1 -
320d7b5f5ccSFenglin Wu 					PMIC_GPIO_FUNC_INDEX_FUNC3);
321d7b5f5ccSFenglin Wu 	}
322eadff302SIvan T. Ivanov 
323eadff302SIvan T. Ivanov 	pad->function = function;
324eadff302SIvan T. Ivanov 
325d7b5f5ccSFenglin Wu 	if (pad->analog_pass)
326d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_ANALOG_PASS_THRU;
327d7b5f5ccSFenglin Wu 	else if (pad->output_enabled && pad->input_enabled)
328d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT;
329d7b5f5ccSFenglin Wu 	else if (pad->output_enabled)
330d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_OUTPUT;
331eadff302SIvan T. Ivanov 	else
332d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_INPUT;
333eadff302SIvan T. Ivanov 
334d7b5f5ccSFenglin Wu 	if (pad->lv_mv_type) {
335d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
336d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_MODE_CTL, val);
337d7b5f5ccSFenglin Wu 		if (ret < 0)
338d7b5f5ccSFenglin Wu 			return ret;
339d7b5f5ccSFenglin Wu 
340d7b5f5ccSFenglin Wu 		val = pad->atest - 1;
341d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
342d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL, val);
343d7b5f5ccSFenglin Wu 		if (ret < 0)
344d7b5f5ccSFenglin Wu 			return ret;
345d7b5f5ccSFenglin Wu 
346d7b5f5ccSFenglin Wu 		val = pad->out_value
347d7b5f5ccSFenglin Wu 			<< PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT;
348d7b5f5ccSFenglin Wu 		val |= pad->function
349d7b5f5ccSFenglin Wu 			& PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK;
350d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
351d7b5f5ccSFenglin Wu 			PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL, val);
352d7b5f5ccSFenglin Wu 		if (ret < 0)
353d7b5f5ccSFenglin Wu 			return ret;
354d7b5f5ccSFenglin Wu 	} else {
355dc391502SIvan T. Ivanov 		val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT;
356eadff302SIvan T. Ivanov 		val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT;
357eadff302SIvan T. Ivanov 		val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT;
358eadff302SIvan T. Ivanov 
359eadff302SIvan T. Ivanov 		ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val);
360eadff302SIvan T. Ivanov 		if (ret < 0)
361eadff302SIvan T. Ivanov 			return ret;
362d7b5f5ccSFenglin Wu 	}
363eadff302SIvan T. Ivanov 
364eadff302SIvan T. Ivanov 	val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT;
365eadff302SIvan T. Ivanov 
366eadff302SIvan T. Ivanov 	return pmic_gpio_write(state, pad, PMIC_GPIO_REG_EN_CTL, val);
367eadff302SIvan T. Ivanov }
368eadff302SIvan T. Ivanov 
369eadff302SIvan T. Ivanov static const struct pinmux_ops pmic_gpio_pinmux_ops = {
370eadff302SIvan T. Ivanov 	.get_functions_count	= pmic_gpio_get_functions_count,
371eadff302SIvan T. Ivanov 	.get_function_name	= pmic_gpio_get_function_name,
372eadff302SIvan T. Ivanov 	.get_function_groups	= pmic_gpio_get_function_groups,
373eadff302SIvan T. Ivanov 	.set_mux		= pmic_gpio_set_mux,
374eadff302SIvan T. Ivanov };
375eadff302SIvan T. Ivanov 
376eadff302SIvan T. Ivanov static int pmic_gpio_config_get(struct pinctrl_dev *pctldev,
377eadff302SIvan T. Ivanov 				unsigned int pin, unsigned long *config)
378eadff302SIvan T. Ivanov {
379eadff302SIvan T. Ivanov 	unsigned param = pinconf_to_config_param(*config);
380eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad;
381eadff302SIvan T. Ivanov 	unsigned arg;
382eadff302SIvan T. Ivanov 
383eadff302SIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
384eadff302SIvan T. Ivanov 
385eadff302SIvan T. Ivanov 	switch (param) {
386eadff302SIvan T. Ivanov 	case PIN_CONFIG_DRIVE_PUSH_PULL:
3871cf86bc2SDouglas Anderson 		if (pad->buffer_type != PMIC_GPIO_OUT_BUF_CMOS)
3881cf86bc2SDouglas Anderson 			return -EINVAL;
3891cf86bc2SDouglas Anderson 		arg = 1;
390eadff302SIvan T. Ivanov 		break;
391eadff302SIvan T. Ivanov 	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
3921cf86bc2SDouglas Anderson 		if (pad->buffer_type != PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS)
3931cf86bc2SDouglas Anderson 			return -EINVAL;
3941cf86bc2SDouglas Anderson 		arg = 1;
395eadff302SIvan T. Ivanov 		break;
396eadff302SIvan T. Ivanov 	case PIN_CONFIG_DRIVE_OPEN_SOURCE:
3971cf86bc2SDouglas Anderson 		if (pad->buffer_type != PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS)
3981cf86bc2SDouglas Anderson 			return -EINVAL;
3991cf86bc2SDouglas Anderson 		arg = 1;
400eadff302SIvan T. Ivanov 		break;
401eadff302SIvan T. Ivanov 	case PIN_CONFIG_BIAS_PULL_DOWN:
4021cf86bc2SDouglas Anderson 		if (pad->pullup != PMIC_GPIO_PULL_DOWN)
4031cf86bc2SDouglas Anderson 			return -EINVAL;
4041cf86bc2SDouglas Anderson 		arg = 1;
405eadff302SIvan T. Ivanov 		break;
406eadff302SIvan T. Ivanov 	case PIN_CONFIG_BIAS_DISABLE:
4071cf86bc2SDouglas Anderson 		if (pad->pullup != PMIC_GPIO_PULL_DISABLE)
4081cf86bc2SDouglas Anderson 			return -EINVAL;
4091cf86bc2SDouglas Anderson 		arg = 1;
410eadff302SIvan T. Ivanov 		break;
411eadff302SIvan T. Ivanov 	case PIN_CONFIG_BIAS_PULL_UP:
4121cf86bc2SDouglas Anderson 		if (pad->pullup != PMIC_GPIO_PULL_UP_30)
4131cf86bc2SDouglas Anderson 			return -EINVAL;
4141cf86bc2SDouglas Anderson 		arg = 1;
415eadff302SIvan T. Ivanov 		break;
416eadff302SIvan T. Ivanov 	case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
4171cf86bc2SDouglas Anderson 		if (pad->is_enabled)
4181cf86bc2SDouglas Anderson 			return -EINVAL;
4191cf86bc2SDouglas Anderson 		arg = 1;
420eadff302SIvan T. Ivanov 		break;
421eadff302SIvan T. Ivanov 	case PIN_CONFIG_POWER_SOURCE:
422eadff302SIvan T. Ivanov 		arg = pad->power_source;
423eadff302SIvan T. Ivanov 		break;
424eadff302SIvan T. Ivanov 	case PIN_CONFIG_INPUT_ENABLE:
4251cf86bc2SDouglas Anderson 		if (!pad->input_enabled)
4261cf86bc2SDouglas Anderson 			return -EINVAL;
4271cf86bc2SDouglas Anderson 		arg = 1;
428eadff302SIvan T. Ivanov 		break;
429727293a8SSubbaraman Narayanamurthy 	case PIN_CONFIG_OUTPUT_ENABLE:
430727293a8SSubbaraman Narayanamurthy 		arg = pad->output_enabled;
431727293a8SSubbaraman Narayanamurthy 		break;
432eadff302SIvan T. Ivanov 	case PIN_CONFIG_OUTPUT:
433eadff302SIvan T. Ivanov 		arg = pad->out_value;
434eadff302SIvan T. Ivanov 		break;
435eadff302SIvan T. Ivanov 	case PMIC_GPIO_CONF_PULL_UP:
436eadff302SIvan T. Ivanov 		arg = pad->pullup;
437eadff302SIvan T. Ivanov 		break;
438eadff302SIvan T. Ivanov 	case PMIC_GPIO_CONF_STRENGTH:
439eadff302SIvan T. Ivanov 		arg = pad->strength;
440eadff302SIvan T. Ivanov 		break;
441d7b5f5ccSFenglin Wu 	case PMIC_GPIO_CONF_ATEST:
442d7b5f5ccSFenglin Wu 		arg = pad->atest;
443d7b5f5ccSFenglin Wu 		break;
444d7b5f5ccSFenglin Wu 	case PMIC_GPIO_CONF_ANALOG_PASS:
445d7b5f5ccSFenglin Wu 		arg = pad->analog_pass;
446d7b5f5ccSFenglin Wu 		break;
447223463fcSFenglin Wu 	case PMIC_GPIO_CONF_DTEST_BUFFER:
448223463fcSFenglin Wu 		arg = pad->dtest_buffer;
449223463fcSFenglin Wu 		break;
450eadff302SIvan T. Ivanov 	default:
451eadff302SIvan T. Ivanov 		return -EINVAL;
452eadff302SIvan T. Ivanov 	}
453eadff302SIvan T. Ivanov 
454eadff302SIvan T. Ivanov 	*config = pinconf_to_config_packed(param, arg);
455eadff302SIvan T. Ivanov 	return 0;
456eadff302SIvan T. Ivanov }
457eadff302SIvan T. Ivanov 
458eadff302SIvan T. Ivanov static int pmic_gpio_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
459eadff302SIvan T. Ivanov 				unsigned long *configs, unsigned nconfs)
460eadff302SIvan T. Ivanov {
461eadff302SIvan T. Ivanov 	struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev);
462eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad;
463eadff302SIvan T. Ivanov 	unsigned param, arg;
464eadff302SIvan T. Ivanov 	unsigned int val;
465eadff302SIvan T. Ivanov 	int i, ret;
466eadff302SIvan T. Ivanov 
467eadff302SIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
468eadff302SIvan T. Ivanov 
4696cb74f44SFenglin Wu 	pad->is_enabled = true;
470eadff302SIvan T. Ivanov 	for (i = 0; i < nconfs; i++) {
471eadff302SIvan T. Ivanov 		param = pinconf_to_config_param(configs[i]);
472eadff302SIvan T. Ivanov 		arg = pinconf_to_config_argument(configs[i]);
473eadff302SIvan T. Ivanov 
474eadff302SIvan T. Ivanov 		switch (param) {
475eadff302SIvan T. Ivanov 		case PIN_CONFIG_DRIVE_PUSH_PULL:
476eadff302SIvan T. Ivanov 			pad->buffer_type = PMIC_GPIO_OUT_BUF_CMOS;
477eadff302SIvan T. Ivanov 			break;
478eadff302SIvan T. Ivanov 		case PIN_CONFIG_DRIVE_OPEN_DRAIN:
479eadff302SIvan T. Ivanov 			if (!pad->have_buffer)
480eadff302SIvan T. Ivanov 				return -EINVAL;
481eadff302SIvan T. Ivanov 			pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS;
482eadff302SIvan T. Ivanov 			break;
483eadff302SIvan T. Ivanov 		case PIN_CONFIG_DRIVE_OPEN_SOURCE:
484eadff302SIvan T. Ivanov 			if (!pad->have_buffer)
485eadff302SIvan T. Ivanov 				return -EINVAL;
486eadff302SIvan T. Ivanov 			pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS;
487eadff302SIvan T. Ivanov 			break;
488eadff302SIvan T. Ivanov 		case PIN_CONFIG_BIAS_DISABLE:
489eadff302SIvan T. Ivanov 			pad->pullup = PMIC_GPIO_PULL_DISABLE;
490eadff302SIvan T. Ivanov 			break;
491eadff302SIvan T. Ivanov 		case PIN_CONFIG_BIAS_PULL_UP:
492eadff302SIvan T. Ivanov 			pad->pullup = PMIC_GPIO_PULL_UP_30;
493eadff302SIvan T. Ivanov 			break;
494eadff302SIvan T. Ivanov 		case PIN_CONFIG_BIAS_PULL_DOWN:
495eadff302SIvan T. Ivanov 			if (arg)
496eadff302SIvan T. Ivanov 				pad->pullup = PMIC_GPIO_PULL_DOWN;
497eadff302SIvan T. Ivanov 			else
498eadff302SIvan T. Ivanov 				pad->pullup = PMIC_GPIO_PULL_DISABLE;
499eadff302SIvan T. Ivanov 			break;
500eadff302SIvan T. Ivanov 		case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
501eadff302SIvan T. Ivanov 			pad->is_enabled = false;
502eadff302SIvan T. Ivanov 			break;
503eadff302SIvan T. Ivanov 		case PIN_CONFIG_POWER_SOURCE:
5044e83ac4cSFenglin Wu 			if (arg >= pad->num_sources)
505eadff302SIvan T. Ivanov 				return -EINVAL;
506eadff302SIvan T. Ivanov 			pad->power_source = arg;
507eadff302SIvan T. Ivanov 			break;
508eadff302SIvan T. Ivanov 		case PIN_CONFIG_INPUT_ENABLE:
509eadff302SIvan T. Ivanov 			pad->input_enabled = arg ? true : false;
510eadff302SIvan T. Ivanov 			break;
511727293a8SSubbaraman Narayanamurthy 		case PIN_CONFIG_OUTPUT_ENABLE:
512727293a8SSubbaraman Narayanamurthy 			pad->output_enabled = arg ? true : false;
513727293a8SSubbaraman Narayanamurthy 			break;
514eadff302SIvan T. Ivanov 		case PIN_CONFIG_OUTPUT:
515eadff302SIvan T. Ivanov 			pad->output_enabled = true;
516eadff302SIvan T. Ivanov 			pad->out_value = arg;
517eadff302SIvan T. Ivanov 			break;
518eadff302SIvan T. Ivanov 		case PMIC_GPIO_CONF_PULL_UP:
519eadff302SIvan T. Ivanov 			if (arg > PMIC_GPIO_PULL_UP_1P5_30)
520eadff302SIvan T. Ivanov 				return -EINVAL;
521eadff302SIvan T. Ivanov 			pad->pullup = arg;
522eadff302SIvan T. Ivanov 			break;
523eadff302SIvan T. Ivanov 		case PMIC_GPIO_CONF_STRENGTH:
524eadff302SIvan T. Ivanov 			if (arg > PMIC_GPIO_STRENGTH_LOW)
525eadff302SIvan T. Ivanov 				return -EINVAL;
526eadff302SIvan T. Ivanov 			pad->strength = arg;
527eadff302SIvan T. Ivanov 			break;
528d7b5f5ccSFenglin Wu 		case PMIC_GPIO_CONF_ATEST:
529d7b5f5ccSFenglin Wu 			if (!pad->lv_mv_type || arg > 4)
530d7b5f5ccSFenglin Wu 				return -EINVAL;
531d7b5f5ccSFenglin Wu 			pad->atest = arg;
532d7b5f5ccSFenglin Wu 			break;
533d7b5f5ccSFenglin Wu 		case PMIC_GPIO_CONF_ANALOG_PASS:
534d7b5f5ccSFenglin Wu 			if (!pad->lv_mv_type)
535d7b5f5ccSFenglin Wu 				return -EINVAL;
536d7b5f5ccSFenglin Wu 			pad->analog_pass = true;
537d7b5f5ccSFenglin Wu 			break;
538223463fcSFenglin Wu 		case PMIC_GPIO_CONF_DTEST_BUFFER:
539223463fcSFenglin Wu 			if (arg > 4)
540223463fcSFenglin Wu 				return -EINVAL;
541223463fcSFenglin Wu 			pad->dtest_buffer = arg;
542223463fcSFenglin Wu 			break;
543eadff302SIvan T. Ivanov 		default:
544eadff302SIvan T. Ivanov 			return -EINVAL;
545eadff302SIvan T. Ivanov 		}
546eadff302SIvan T. Ivanov 	}
547eadff302SIvan T. Ivanov 
548eadff302SIvan T. Ivanov 	val = pad->power_source << PMIC_GPIO_REG_VIN_SHIFT;
549eadff302SIvan T. Ivanov 
550eadff302SIvan T. Ivanov 	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL, val);
551eadff302SIvan T. Ivanov 	if (ret < 0)
552eadff302SIvan T. Ivanov 		return ret;
553eadff302SIvan T. Ivanov 
554eadff302SIvan T. Ivanov 	val = pad->pullup << PMIC_GPIO_REG_PULL_SHIFT;
555eadff302SIvan T. Ivanov 
556eadff302SIvan T. Ivanov 	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_PULL_CTL, val);
557eadff302SIvan T. Ivanov 	if (ret < 0)
558eadff302SIvan T. Ivanov 		return ret;
559eadff302SIvan T. Ivanov 
560eadff302SIvan T. Ivanov 	val = pad->buffer_type << PMIC_GPIO_REG_OUT_TYPE_SHIFT;
561982df6aeSIvan T. Ivanov 	val |= pad->strength << PMIC_GPIO_REG_OUT_STRENGTH_SHIFT;
562eadff302SIvan T. Ivanov 
563eadff302SIvan T. Ivanov 	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL, val);
564eadff302SIvan T. Ivanov 	if (ret < 0)
565eadff302SIvan T. Ivanov 		return ret;
566eadff302SIvan T. Ivanov 
567223463fcSFenglin Wu 	if (pad->dtest_buffer == 0) {
568223463fcSFenglin Wu 		val = 0;
569223463fcSFenglin Wu 	} else {
570223463fcSFenglin Wu 		if (pad->lv_mv_type) {
571223463fcSFenglin Wu 			val = pad->dtest_buffer - 1;
572223463fcSFenglin Wu 			val |= PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN;
573223463fcSFenglin Wu 		} else {
574223463fcSFenglin Wu 			val = BIT(pad->dtest_buffer - 1);
575223463fcSFenglin Wu 		}
576223463fcSFenglin Wu 	}
577223463fcSFenglin Wu 	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_IN_CTL, val);
578223463fcSFenglin Wu 	if (ret < 0)
579223463fcSFenglin Wu 		return ret;
580223463fcSFenglin Wu 
581d7b5f5ccSFenglin Wu 	if (pad->analog_pass)
582d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_ANALOG_PASS_THRU;
583d7b5f5ccSFenglin Wu 	else if (pad->output_enabled && pad->input_enabled)
584d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT;
585d7b5f5ccSFenglin Wu 	else if (pad->output_enabled)
586d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_OUTPUT;
587eadff302SIvan T. Ivanov 	else
588d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_INPUT;
589eadff302SIvan T. Ivanov 
590d7b5f5ccSFenglin Wu 	if (pad->lv_mv_type) {
591d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
592d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_MODE_CTL, val);
593d7b5f5ccSFenglin Wu 		if (ret < 0)
594d7b5f5ccSFenglin Wu 			return ret;
595d7b5f5ccSFenglin Wu 
596d7b5f5ccSFenglin Wu 		val = pad->atest - 1;
597d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
598d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL, val);
599d7b5f5ccSFenglin Wu 		if (ret < 0)
600d7b5f5ccSFenglin Wu 			return ret;
601d7b5f5ccSFenglin Wu 
602d7b5f5ccSFenglin Wu 		val = pad->out_value
603d7b5f5ccSFenglin Wu 			<< PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT;
604d7b5f5ccSFenglin Wu 		val |= pad->function
605d7b5f5ccSFenglin Wu 			& PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK;
606d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
607d7b5f5ccSFenglin Wu 			PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL, val);
608d7b5f5ccSFenglin Wu 		if (ret < 0)
609d7b5f5ccSFenglin Wu 			return ret;
610d7b5f5ccSFenglin Wu 	} else {
611eadff302SIvan T. Ivanov 		val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT;
612eadff302SIvan T. Ivanov 		val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT;
613eadff302SIvan T. Ivanov 		val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT;
614eadff302SIvan T. Ivanov 
615d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val);
616d7b5f5ccSFenglin Wu 		if (ret < 0)
617d7b5f5ccSFenglin Wu 			return ret;
618d7b5f5ccSFenglin Wu 	}
619d7b5f5ccSFenglin Wu 
6206cb74f44SFenglin Wu 	val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT;
6216cb74f44SFenglin Wu 
6226cb74f44SFenglin Wu 	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_EN_CTL, val);
6236cb74f44SFenglin Wu 
624d7b5f5ccSFenglin Wu 	return ret;
625eadff302SIvan T. Ivanov }
626eadff302SIvan T. Ivanov 
627eadff302SIvan T. Ivanov static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev,
628eadff302SIvan T. Ivanov 				      struct seq_file *s, unsigned pin)
629eadff302SIvan T. Ivanov {
630eadff302SIvan T. Ivanov 	struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev);
631eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad;
632d7b5f5ccSFenglin Wu 	int ret, val, function;
633eadff302SIvan T. Ivanov 
634eadff302SIvan T. Ivanov 	static const char *const biases[] = {
635eadff302SIvan T. Ivanov 		"pull-up 30uA", "pull-up 1.5uA", "pull-up 31.5uA",
636eadff302SIvan T. Ivanov 		"pull-up 1.5uA + 30uA boost", "pull-down 10uA", "no pull"
637eadff302SIvan T. Ivanov 	};
638eadff302SIvan T. Ivanov 	static const char *const buffer_types[] = {
639eadff302SIvan T. Ivanov 		"push-pull", "open-drain", "open-source"
640eadff302SIvan T. Ivanov 	};
641eadff302SIvan T. Ivanov 	static const char *const strengths[] = {
642eadff302SIvan T. Ivanov 		"no", "high", "medium", "low"
643eadff302SIvan T. Ivanov 	};
644eadff302SIvan T. Ivanov 
645eadff302SIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
646eadff302SIvan T. Ivanov 
647eadff302SIvan T. Ivanov 	seq_printf(s, " gpio%-2d:", pin + PMIC_GPIO_PHYSICAL_OFFSET);
648eadff302SIvan T. Ivanov 
649eadff302SIvan T. Ivanov 	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_EN_CTL);
650eadff302SIvan T. Ivanov 
651eadff302SIvan T. Ivanov 	if (val < 0 || !(val >> PMIC_GPIO_REG_MASTER_EN_SHIFT)) {
652eadff302SIvan T. Ivanov 		seq_puts(s, " ---");
653eadff302SIvan T. Ivanov 	} else {
65424a66618SIvan T. Ivanov 		if (pad->input_enabled) {
655eadff302SIvan T. Ivanov 			ret = pmic_gpio_read(state, pad, PMIC_MPP_REG_RT_STS);
65624a66618SIvan T. Ivanov 			if (ret < 0)
65724a66618SIvan T. Ivanov 				return;
65824a66618SIvan T. Ivanov 
659eadff302SIvan T. Ivanov 			ret &= PMIC_MPP_REG_RT_STS_VAL_MASK;
660eadff302SIvan T. Ivanov 			pad->out_value = ret;
661eadff302SIvan T. Ivanov 		}
662d7b5f5ccSFenglin Wu 		/*
663d7b5f5ccSFenglin Wu 		 * For the non-LV/MV subtypes only 2 special functions are
664d7b5f5ccSFenglin Wu 		 * available, offsetting the dtest function values by 2.
665d7b5f5ccSFenglin Wu 		 */
666d7b5f5ccSFenglin Wu 		function = pad->function;
667d7b5f5ccSFenglin Wu 		if (!pad->lv_mv_type &&
668d7b5f5ccSFenglin Wu 				pad->function >= PMIC_GPIO_FUNC_INDEX_FUNC3)
669d7b5f5ccSFenglin Wu 			function += PMIC_GPIO_FUNC_INDEX_DTEST1 -
670d7b5f5ccSFenglin Wu 				PMIC_GPIO_FUNC_INDEX_FUNC3;
671eadff302SIvan T. Ivanov 
672d7b5f5ccSFenglin Wu 		if (pad->analog_pass)
673d7b5f5ccSFenglin Wu 			seq_puts(s, " analog-pass");
674d7b5f5ccSFenglin Wu 		else
675d7b5f5ccSFenglin Wu 			seq_printf(s, " %-4s",
676d7b5f5ccSFenglin Wu 					pad->output_enabled ? "out" : "in");
677202ba5ebSBjorn Andersson 		seq_printf(s, " %-4s", pad->out_value ? "high" : "low");
678d7b5f5ccSFenglin Wu 		seq_printf(s, " %-7s", pmic_gpio_functions[function]);
679eadff302SIvan T. Ivanov 		seq_printf(s, " vin-%d", pad->power_source);
680eadff302SIvan T. Ivanov 		seq_printf(s, " %-27s", biases[pad->pullup]);
681eadff302SIvan T. Ivanov 		seq_printf(s, " %-10s", buffer_types[pad->buffer_type]);
682eadff302SIvan T. Ivanov 		seq_printf(s, " %-7s", strengths[pad->strength]);
683d7b5f5ccSFenglin Wu 		seq_printf(s, " atest-%d", pad->atest);
684223463fcSFenglin Wu 		seq_printf(s, " dtest-%d", pad->dtest_buffer);
685eadff302SIvan T. Ivanov 	}
686eadff302SIvan T. Ivanov }
687eadff302SIvan T. Ivanov 
688eadff302SIvan T. Ivanov static const struct pinconf_ops pmic_gpio_pinconf_ops = {
6897382b623SSoren Brinkmann 	.is_generic			= true,
690eadff302SIvan T. Ivanov 	.pin_config_group_get		= pmic_gpio_config_get,
691eadff302SIvan T. Ivanov 	.pin_config_group_set		= pmic_gpio_config_set,
692eadff302SIvan T. Ivanov 	.pin_config_group_dbg_show	= pmic_gpio_config_dbg_show,
693eadff302SIvan T. Ivanov };
694eadff302SIvan T. Ivanov 
695eadff302SIvan T. Ivanov static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned pin)
696eadff302SIvan T. Ivanov {
697c52d9df1SLinus Walleij 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
698eadff302SIvan T. Ivanov 	unsigned long config;
699eadff302SIvan T. Ivanov 
700eadff302SIvan T. Ivanov 	config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1);
701eadff302SIvan T. Ivanov 
702eadff302SIvan T. Ivanov 	return pmic_gpio_config_set(state->ctrl, pin, &config, 1);
703eadff302SIvan T. Ivanov }
704eadff302SIvan T. Ivanov 
705eadff302SIvan T. Ivanov static int pmic_gpio_direction_output(struct gpio_chip *chip,
706eadff302SIvan T. Ivanov 				      unsigned pin, int val)
707eadff302SIvan T. Ivanov {
708c52d9df1SLinus Walleij 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
709eadff302SIvan T. Ivanov 	unsigned long config;
710eadff302SIvan T. Ivanov 
711eadff302SIvan T. Ivanov 	config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val);
712eadff302SIvan T. Ivanov 
713eadff302SIvan T. Ivanov 	return pmic_gpio_config_set(state->ctrl, pin, &config, 1);
714eadff302SIvan T. Ivanov }
715eadff302SIvan T. Ivanov 
716eadff302SIvan T. Ivanov static int pmic_gpio_get(struct gpio_chip *chip, unsigned pin)
717eadff302SIvan T. Ivanov {
718c52d9df1SLinus Walleij 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
719eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad;
720eadff302SIvan T. Ivanov 	int ret;
721eadff302SIvan T. Ivanov 
722eadff302SIvan T. Ivanov 	pad = state->ctrl->desc->pins[pin].drv_data;
723eadff302SIvan T. Ivanov 
724eadff302SIvan T. Ivanov 	if (!pad->is_enabled)
725eadff302SIvan T. Ivanov 		return -EINVAL;
726eadff302SIvan T. Ivanov 
727eadff302SIvan T. Ivanov 	if (pad->input_enabled) {
728eadff302SIvan T. Ivanov 		ret = pmic_gpio_read(state, pad, PMIC_MPP_REG_RT_STS);
729eadff302SIvan T. Ivanov 		if (ret < 0)
730eadff302SIvan T. Ivanov 			return ret;
731eadff302SIvan T. Ivanov 
732eadff302SIvan T. Ivanov 		pad->out_value = ret & PMIC_MPP_REG_RT_STS_VAL_MASK;
733eadff302SIvan T. Ivanov 	}
734eadff302SIvan T. Ivanov 
73586c1a219SLinus Walleij 	return !!pad->out_value;
736eadff302SIvan T. Ivanov }
737eadff302SIvan T. Ivanov 
738eadff302SIvan T. Ivanov static void pmic_gpio_set(struct gpio_chip *chip, unsigned pin, int value)
739eadff302SIvan T. Ivanov {
740c52d9df1SLinus Walleij 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
741eadff302SIvan T. Ivanov 	unsigned long config;
742eadff302SIvan T. Ivanov 
743eadff302SIvan T. Ivanov 	config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value);
744eadff302SIvan T. Ivanov 
745eadff302SIvan T. Ivanov 	pmic_gpio_config_set(state->ctrl, pin, &config, 1);
746eadff302SIvan T. Ivanov }
747eadff302SIvan T. Ivanov 
748eadff302SIvan T. Ivanov static int pmic_gpio_of_xlate(struct gpio_chip *chip,
749eadff302SIvan T. Ivanov 			      const struct of_phandle_args *gpio_desc,
750eadff302SIvan T. Ivanov 			      u32 *flags)
751eadff302SIvan T. Ivanov {
752eadff302SIvan T. Ivanov 	if (chip->of_gpio_n_cells < 2)
753eadff302SIvan T. Ivanov 		return -EINVAL;
754eadff302SIvan T. Ivanov 
755eadff302SIvan T. Ivanov 	if (flags)
756eadff302SIvan T. Ivanov 		*flags = gpio_desc->args[1];
757eadff302SIvan T. Ivanov 
758eadff302SIvan T. Ivanov 	return gpio_desc->args[0] - PMIC_GPIO_PHYSICAL_OFFSET;
759eadff302SIvan T. Ivanov }
760eadff302SIvan T. Ivanov 
761eadff302SIvan T. Ivanov static void pmic_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
762eadff302SIvan T. Ivanov {
763c52d9df1SLinus Walleij 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
764eadff302SIvan T. Ivanov 	unsigned i;
765eadff302SIvan T. Ivanov 
766eadff302SIvan T. Ivanov 	for (i = 0; i < chip->ngpio; i++) {
767eadff302SIvan T. Ivanov 		pmic_gpio_config_dbg_show(state->ctrl, s, i);
768eadff302SIvan T. Ivanov 		seq_puts(s, "\n");
769eadff302SIvan T. Ivanov 	}
770eadff302SIvan T. Ivanov }
771eadff302SIvan T. Ivanov 
772eadff302SIvan T. Ivanov static const struct gpio_chip pmic_gpio_gpio_template = {
773eadff302SIvan T. Ivanov 	.direction_input	= pmic_gpio_direction_input,
774eadff302SIvan T. Ivanov 	.direction_output	= pmic_gpio_direction_output,
775eadff302SIvan T. Ivanov 	.get			= pmic_gpio_get,
776eadff302SIvan T. Ivanov 	.set			= pmic_gpio_set,
77798c85d58SJonas Gorski 	.request		= gpiochip_generic_request,
77898c85d58SJonas Gorski 	.free			= gpiochip_generic_free,
779eadff302SIvan T. Ivanov 	.of_xlate		= pmic_gpio_of_xlate,
780eadff302SIvan T. Ivanov 	.dbg_show		= pmic_gpio_dbg_show,
781eadff302SIvan T. Ivanov };
782eadff302SIvan T. Ivanov 
783eadff302SIvan T. Ivanov static int pmic_gpio_populate(struct pmic_gpio_state *state,
784eadff302SIvan T. Ivanov 			      struct pmic_gpio_pad *pad)
785eadff302SIvan T. Ivanov {
786eadff302SIvan T. Ivanov 	int type, subtype, val, dir;
787eadff302SIvan T. Ivanov 
788eadff302SIvan T. Ivanov 	type = pmic_gpio_read(state, pad, PMIC_GPIO_REG_TYPE);
789eadff302SIvan T. Ivanov 	if (type < 0)
790eadff302SIvan T. Ivanov 		return type;
791eadff302SIvan T. Ivanov 
792eadff302SIvan T. Ivanov 	if (type != PMIC_GPIO_TYPE) {
793eadff302SIvan T. Ivanov 		dev_err(state->dev, "incorrect block type 0x%x at 0x%x\n",
794eadff302SIvan T. Ivanov 			type, pad->base);
795eadff302SIvan T. Ivanov 		return -ENODEV;
796eadff302SIvan T. Ivanov 	}
797eadff302SIvan T. Ivanov 
798eadff302SIvan T. Ivanov 	subtype = pmic_gpio_read(state, pad, PMIC_GPIO_REG_SUBTYPE);
799eadff302SIvan T. Ivanov 	if (subtype < 0)
800eadff302SIvan T. Ivanov 		return subtype;
801eadff302SIvan T. Ivanov 
802eadff302SIvan T. Ivanov 	switch (subtype) {
803eadff302SIvan T. Ivanov 	case PMIC_GPIO_SUBTYPE_GPIO_4CH:
804eadff302SIvan T. Ivanov 		pad->have_buffer = true;
8051586f556SGustavo A. R. Silva 		fallthrough;
806eadff302SIvan T. Ivanov 	case PMIC_GPIO_SUBTYPE_GPIOC_4CH:
807eadff302SIvan T. Ivanov 		pad->num_sources = 4;
808eadff302SIvan T. Ivanov 		break;
809eadff302SIvan T. Ivanov 	case PMIC_GPIO_SUBTYPE_GPIO_8CH:
810eadff302SIvan T. Ivanov 		pad->have_buffer = true;
8111586f556SGustavo A. R. Silva 		fallthrough;
812eadff302SIvan T. Ivanov 	case PMIC_GPIO_SUBTYPE_GPIOC_8CH:
813eadff302SIvan T. Ivanov 		pad->num_sources = 8;
814eadff302SIvan T. Ivanov 		break;
815d7b5f5ccSFenglin Wu 	case PMIC_GPIO_SUBTYPE_GPIO_LV:
816d7b5f5ccSFenglin Wu 		pad->num_sources = 1;
817d7b5f5ccSFenglin Wu 		pad->have_buffer = true;
818d7b5f5ccSFenglin Wu 		pad->lv_mv_type = true;
819d7b5f5ccSFenglin Wu 		break;
820d7b5f5ccSFenglin Wu 	case PMIC_GPIO_SUBTYPE_GPIO_MV:
821d7b5f5ccSFenglin Wu 		pad->num_sources = 2;
822d7b5f5ccSFenglin Wu 		pad->have_buffer = true;
823d7b5f5ccSFenglin Wu 		pad->lv_mv_type = true;
824d7b5f5ccSFenglin Wu 		break;
825eadff302SIvan T. Ivanov 	default:
826eadff302SIvan T. Ivanov 		dev_err(state->dev, "unknown GPIO type 0x%x\n", subtype);
827eadff302SIvan T. Ivanov 		return -ENODEV;
828eadff302SIvan T. Ivanov 	}
829eadff302SIvan T. Ivanov 
830d7b5f5ccSFenglin Wu 	if (pad->lv_mv_type) {
831d7b5f5ccSFenglin Wu 		val = pmic_gpio_read(state, pad,
832d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL);
833d7b5f5ccSFenglin Wu 		if (val < 0)
834d7b5f5ccSFenglin Wu 			return val;
835d7b5f5ccSFenglin Wu 
836d7b5f5ccSFenglin Wu 		pad->out_value = !!(val & PMIC_GPIO_LV_MV_OUTPUT_INVERT);
837d7b5f5ccSFenglin Wu 		pad->function = val & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK;
838d7b5f5ccSFenglin Wu 
839d7b5f5ccSFenglin Wu 		val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL);
840d7b5f5ccSFenglin Wu 		if (val < 0)
841d7b5f5ccSFenglin Wu 			return val;
842d7b5f5ccSFenglin Wu 
843d7b5f5ccSFenglin Wu 		dir = val & PMIC_GPIO_REG_LV_MV_MODE_DIR_MASK;
844d7b5f5ccSFenglin Wu 	} else {
845eadff302SIvan T. Ivanov 		val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL);
846eadff302SIvan T. Ivanov 		if (val < 0)
847eadff302SIvan T. Ivanov 			return val;
848eadff302SIvan T. Ivanov 
849eadff302SIvan T. Ivanov 		pad->out_value = val & PMIC_GPIO_REG_MODE_VALUE_SHIFT;
850eadff302SIvan T. Ivanov 
851eadff302SIvan T. Ivanov 		dir = val >> PMIC_GPIO_REG_MODE_DIR_SHIFT;
852eadff302SIvan T. Ivanov 		dir &= PMIC_GPIO_REG_MODE_DIR_MASK;
853d7b5f5ccSFenglin Wu 		pad->function = val >> PMIC_GPIO_REG_MODE_FUNCTION_SHIFT;
854d7b5f5ccSFenglin Wu 		pad->function &= PMIC_GPIO_REG_MODE_FUNCTION_MASK;
855d7b5f5ccSFenglin Wu 	}
856d7b5f5ccSFenglin Wu 
857eadff302SIvan T. Ivanov 	switch (dir) {
858d7b5f5ccSFenglin Wu 	case PMIC_GPIO_MODE_DIGITAL_INPUT:
859eadff302SIvan T. Ivanov 		pad->input_enabled = true;
860eadff302SIvan T. Ivanov 		pad->output_enabled = false;
861eadff302SIvan T. Ivanov 		break;
862d7b5f5ccSFenglin Wu 	case PMIC_GPIO_MODE_DIGITAL_OUTPUT:
863eadff302SIvan T. Ivanov 		pad->input_enabled = false;
864eadff302SIvan T. Ivanov 		pad->output_enabled = true;
865eadff302SIvan T. Ivanov 		break;
866d7b5f5ccSFenglin Wu 	case PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT:
867eadff302SIvan T. Ivanov 		pad->input_enabled = true;
868eadff302SIvan T. Ivanov 		pad->output_enabled = true;
869eadff302SIvan T. Ivanov 		break;
870d7b5f5ccSFenglin Wu 	case PMIC_GPIO_MODE_ANALOG_PASS_THRU:
871d7b5f5ccSFenglin Wu 		if (!pad->lv_mv_type)
872d7b5f5ccSFenglin Wu 			return -ENODEV;
873d7b5f5ccSFenglin Wu 		pad->analog_pass = true;
874d7b5f5ccSFenglin Wu 		break;
875eadff302SIvan T. Ivanov 	default:
876eadff302SIvan T. Ivanov 		dev_err(state->dev, "unknown GPIO direction\n");
877eadff302SIvan T. Ivanov 		return -ENODEV;
878eadff302SIvan T. Ivanov 	}
879eadff302SIvan T. Ivanov 
880eadff302SIvan T. Ivanov 	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL);
881eadff302SIvan T. Ivanov 	if (val < 0)
882eadff302SIvan T. Ivanov 		return val;
883eadff302SIvan T. Ivanov 
884eadff302SIvan T. Ivanov 	pad->power_source = val >> PMIC_GPIO_REG_VIN_SHIFT;
885eadff302SIvan T. Ivanov 	pad->power_source &= PMIC_GPIO_REG_VIN_MASK;
886eadff302SIvan T. Ivanov 
887eadff302SIvan T. Ivanov 	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_PULL_CTL);
888eadff302SIvan T. Ivanov 	if (val < 0)
889eadff302SIvan T. Ivanov 		return val;
890eadff302SIvan T. Ivanov 
891eadff302SIvan T. Ivanov 	pad->pullup = val >> PMIC_GPIO_REG_PULL_SHIFT;
892eadff302SIvan T. Ivanov 	pad->pullup &= PMIC_GPIO_REG_PULL_MASK;
893eadff302SIvan T. Ivanov 
894223463fcSFenglin Wu 	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_IN_CTL);
895223463fcSFenglin Wu 	if (val < 0)
896223463fcSFenglin Wu 		return val;
897223463fcSFenglin Wu 
898223463fcSFenglin Wu 	if (pad->lv_mv_type && (val & PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN))
899223463fcSFenglin Wu 		pad->dtest_buffer =
900223463fcSFenglin Wu 			(val & PMIC_GPIO_LV_MV_DIG_IN_DTEST_SEL_MASK) + 1;
901223463fcSFenglin Wu 	else if (!pad->lv_mv_type)
902223463fcSFenglin Wu 		pad->dtest_buffer = ffs(val);
903223463fcSFenglin Wu 	else
904223463fcSFenglin Wu 		pad->dtest_buffer = 0;
905223463fcSFenglin Wu 
906223463fcSFenglin Wu 	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL);
907223463fcSFenglin Wu 	if (val < 0)
908223463fcSFenglin Wu 		return val;
909223463fcSFenglin Wu 
910eadff302SIvan T. Ivanov 	pad->strength = val >> PMIC_GPIO_REG_OUT_STRENGTH_SHIFT;
911eadff302SIvan T. Ivanov 	pad->strength &= PMIC_GPIO_REG_OUT_STRENGTH_MASK;
912eadff302SIvan T. Ivanov 
913eadff302SIvan T. Ivanov 	pad->buffer_type = val >> PMIC_GPIO_REG_OUT_TYPE_SHIFT;
914eadff302SIvan T. Ivanov 	pad->buffer_type &= PMIC_GPIO_REG_OUT_TYPE_MASK;
915eadff302SIvan T. Ivanov 
916d7b5f5ccSFenglin Wu 	if (pad->lv_mv_type) {
917d7b5f5ccSFenglin Wu 		val = pmic_gpio_read(state, pad,
918d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL);
919d7b5f5ccSFenglin Wu 		if (val < 0)
920d7b5f5ccSFenglin Wu 			return val;
921d7b5f5ccSFenglin Wu 		pad->atest = (val & PMIC_GPIO_LV_MV_ANA_MUX_SEL_MASK) + 1;
922d7b5f5ccSFenglin Wu 	}
923d7b5f5ccSFenglin Wu 
924eadff302SIvan T. Ivanov 	/* Pin could be disabled with PIN_CONFIG_BIAS_HIGH_IMPEDANCE */
925eadff302SIvan T. Ivanov 	pad->is_enabled = true;
926eadff302SIvan T. Ivanov 	return 0;
927eadff302SIvan T. Ivanov }
928eadff302SIvan T. Ivanov 
929ca69e2d1SBrian Masney static int pmic_gpio_domain_translate(struct irq_domain *domain,
930ca69e2d1SBrian Masney 				      struct irq_fwspec *fwspec,
931ca69e2d1SBrian Masney 				      unsigned long *hwirq,
932ca69e2d1SBrian Masney 				      unsigned int *type)
933ca69e2d1SBrian Masney {
934ca69e2d1SBrian Masney 	struct pmic_gpio_state *state = container_of(domain->host_data,
935ca69e2d1SBrian Masney 						     struct pmic_gpio_state,
936ca69e2d1SBrian Masney 						     chip);
937ca69e2d1SBrian Masney 
938dac7da98SBjorn Andersson 	if (fwspec->param_count != 2 ||
939dac7da98SBjorn Andersson 	    fwspec->param[0] < 1 || fwspec->param[0] > state->chip.ngpio)
940ca69e2d1SBrian Masney 		return -EINVAL;
941ca69e2d1SBrian Masney 
942ca69e2d1SBrian Masney 	*hwirq = fwspec->param[0] - PMIC_GPIO_PHYSICAL_OFFSET;
943ca69e2d1SBrian Masney 	*type = fwspec->param[1];
944ca69e2d1SBrian Masney 
945ca69e2d1SBrian Masney 	return 0;
946ca69e2d1SBrian Masney }
947ca69e2d1SBrian Masney 
948821c76c4SBrian Masney static unsigned int pmic_gpio_child_offset_to_irq(struct gpio_chip *chip,
949821c76c4SBrian Masney 						  unsigned int offset)
950ca69e2d1SBrian Masney {
951821c76c4SBrian Masney 	return offset + PMIC_GPIO_PHYSICAL_OFFSET;
952ca69e2d1SBrian Masney }
953ca69e2d1SBrian Masney 
954821c76c4SBrian Masney static int pmic_gpio_child_to_parent_hwirq(struct gpio_chip *chip,
955821c76c4SBrian Masney 					   unsigned int child_hwirq,
956821c76c4SBrian Masney 					   unsigned int child_type,
957821c76c4SBrian Masney 					   unsigned int *parent_hwirq,
958821c76c4SBrian Masney 					   unsigned int *parent_type)
959821c76c4SBrian Masney {
960d36a9773SDavid Collins 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
961d36a9773SDavid Collins 
962d36a9773SDavid Collins 	*parent_hwirq = child_hwirq + state->pid_base;
963821c76c4SBrian Masney 	*parent_type = child_type;
964821c76c4SBrian Masney 
965821c76c4SBrian Masney 	return 0;
966821c76c4SBrian Masney }
967ca69e2d1SBrian Masney 
96891a29af4SMarc Zyngier static int pmic_gpio_populate_parent_fwspec(struct gpio_chip *chip,
96991a29af4SMarc Zyngier 					    union gpio_irq_fwspec *gfwspec,
970d36a9773SDavid Collins 					    unsigned int parent_hwirq,
971d36a9773SDavid Collins 					    unsigned int parent_type)
972d36a9773SDavid Collins {
973d36a9773SDavid Collins 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
97491a29af4SMarc Zyngier 	struct irq_fwspec *fwspec = &gfwspec->fwspec;
975d36a9773SDavid Collins 
976d36a9773SDavid Collins 	fwspec->fwnode = chip->irq.parent_domain->fwnode;
977d36a9773SDavid Collins 
978d36a9773SDavid Collins 	fwspec->param_count = 4;
979d36a9773SDavid Collins 	fwspec->param[0] = state->usid;
980d36a9773SDavid Collins 	fwspec->param[1] = parent_hwirq;
981d36a9773SDavid Collins 	/* param[2] must be left as 0 */
982d36a9773SDavid Collins 	fwspec->param[3] = parent_type;
983d36a9773SDavid Collins 
98491a29af4SMarc Zyngier 	return 0;
985d36a9773SDavid Collins }
986d36a9773SDavid Collins 
987*1a41d1e5SManivannan Sadhasivam static void pmic_gpio_irq_mask(struct irq_data *data)
988*1a41d1e5SManivannan Sadhasivam {
989*1a41d1e5SManivannan Sadhasivam 	struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
990*1a41d1e5SManivannan Sadhasivam 
991*1a41d1e5SManivannan Sadhasivam 	irq_chip_mask_parent(data);
992*1a41d1e5SManivannan Sadhasivam 	gpiochip_disable_irq(gc, data->hwirq);
993*1a41d1e5SManivannan Sadhasivam }
994*1a41d1e5SManivannan Sadhasivam 
995*1a41d1e5SManivannan Sadhasivam static void pmic_gpio_irq_unmask(struct irq_data *data)
996*1a41d1e5SManivannan Sadhasivam {
997*1a41d1e5SManivannan Sadhasivam 	struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
998*1a41d1e5SManivannan Sadhasivam 
999*1a41d1e5SManivannan Sadhasivam 	gpiochip_enable_irq(gc, data->hwirq);
1000*1a41d1e5SManivannan Sadhasivam 	irq_chip_unmask_parent(data);
1001*1a41d1e5SManivannan Sadhasivam }
1002*1a41d1e5SManivannan Sadhasivam 
1003*1a41d1e5SManivannan Sadhasivam static const struct irq_chip spmi_gpio_irq_chip = {
1004*1a41d1e5SManivannan Sadhasivam 	.name		= "spmi-gpio",
1005*1a41d1e5SManivannan Sadhasivam 	.irq_ack	= irq_chip_ack_parent,
1006*1a41d1e5SManivannan Sadhasivam 	.irq_mask	= pmic_gpio_irq_mask,
1007*1a41d1e5SManivannan Sadhasivam 	.irq_unmask	= pmic_gpio_irq_unmask,
1008*1a41d1e5SManivannan Sadhasivam 	.irq_set_type	= irq_chip_set_type_parent,
1009*1a41d1e5SManivannan Sadhasivam 	.irq_set_wake	= irq_chip_set_wake_parent,
1010*1a41d1e5SManivannan Sadhasivam 	.flags		= IRQCHIP_IMMUTABLE | IRQCHIP_MASK_ON_SUSPEND,
1011*1a41d1e5SManivannan Sadhasivam 	GPIOCHIP_IRQ_RESOURCE_HELPERS,
1012*1a41d1e5SManivannan Sadhasivam };
1013*1a41d1e5SManivannan Sadhasivam 
1014eadff302SIvan T. Ivanov static int pmic_gpio_probe(struct platform_device *pdev)
1015eadff302SIvan T. Ivanov {
1016ca69e2d1SBrian Masney 	struct irq_domain *parent_domain;
1017ca69e2d1SBrian Masney 	struct device_node *parent_node;
1018eadff302SIvan T. Ivanov 	struct device *dev = &pdev->dev;
1019eadff302SIvan T. Ivanov 	struct pinctrl_pin_desc *pindesc;
1020eadff302SIvan T. Ivanov 	struct pinctrl_desc *pctrldesc;
1021eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad, *pads;
1022eadff302SIvan T. Ivanov 	struct pmic_gpio_state *state;
1023821c76c4SBrian Masney 	struct gpio_irq_chip *girq;
1024d36a9773SDavid Collins 	const struct spmi_device *parent_spmi_dev;
1025eadff302SIvan T. Ivanov 	int ret, npins, i;
1026ab4256cfSStephen Boyd 	u32 reg;
1027eadff302SIvan T. Ivanov 
1028ab4256cfSStephen Boyd 	ret = of_property_read_u32(dev->of_node, "reg", &reg);
1029eadff302SIvan T. Ivanov 	if (ret < 0) {
1030ab4256cfSStephen Boyd 		dev_err(dev, "missing base address");
1031eadff302SIvan T. Ivanov 		return ret;
1032eadff302SIvan T. Ivanov 	}
1033eadff302SIvan T. Ivanov 
1034cfacef37SBrian Masney 	npins = (uintptr_t) device_get_match_data(&pdev->dev);
1035eadff302SIvan T. Ivanov 
1036eadff302SIvan T. Ivanov 	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
1037eadff302SIvan T. Ivanov 	if (!state)
1038eadff302SIvan T. Ivanov 		return -ENOMEM;
1039eadff302SIvan T. Ivanov 
1040eadff302SIvan T. Ivanov 	platform_set_drvdata(pdev, state);
1041eadff302SIvan T. Ivanov 
1042eadff302SIvan T. Ivanov 	state->dev = &pdev->dev;
1043eadff302SIvan T. Ivanov 	state->map = dev_get_regmap(dev->parent, NULL);
1044d36a9773SDavid Collins 	parent_spmi_dev = to_spmi_device(dev->parent);
1045d36a9773SDavid Collins 	state->usid = parent_spmi_dev->usid;
1046d36a9773SDavid Collins 	state->pid_base = reg >> 8;
1047eadff302SIvan T. Ivanov 
1048eadff302SIvan T. Ivanov 	pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
1049eadff302SIvan T. Ivanov 	if (!pindesc)
1050eadff302SIvan T. Ivanov 		return -ENOMEM;
1051eadff302SIvan T. Ivanov 
1052eadff302SIvan T. Ivanov 	pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
1053eadff302SIvan T. Ivanov 	if (!pads)
1054eadff302SIvan T. Ivanov 		return -ENOMEM;
1055eadff302SIvan T. Ivanov 
1056eadff302SIvan T. Ivanov 	pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL);
1057eadff302SIvan T. Ivanov 	if (!pctrldesc)
1058eadff302SIvan T. Ivanov 		return -ENOMEM;
1059eadff302SIvan T. Ivanov 
1060eadff302SIvan T. Ivanov 	pctrldesc->pctlops = &pmic_gpio_pinctrl_ops;
1061eadff302SIvan T. Ivanov 	pctrldesc->pmxops = &pmic_gpio_pinmux_ops;
1062eadff302SIvan T. Ivanov 	pctrldesc->confops = &pmic_gpio_pinconf_ops;
1063eadff302SIvan T. Ivanov 	pctrldesc->owner = THIS_MODULE;
1064eadff302SIvan T. Ivanov 	pctrldesc->name = dev_name(dev);
1065eadff302SIvan T. Ivanov 	pctrldesc->pins = pindesc;
1066eadff302SIvan T. Ivanov 	pctrldesc->npins = npins;
1067f684e4acSLinus Walleij 	pctrldesc->num_custom_params = ARRAY_SIZE(pmic_gpio_bindings);
1068f684e4acSLinus Walleij 	pctrldesc->custom_params = pmic_gpio_bindings;
10694f06266aSArnd Bergmann #ifdef CONFIG_DEBUG_FS
1070f684e4acSLinus Walleij 	pctrldesc->custom_conf_items = pmic_conf_items;
10714f06266aSArnd Bergmann #endif
1072eadff302SIvan T. Ivanov 
1073eadff302SIvan T. Ivanov 	for (i = 0; i < npins; i++, pindesc++) {
1074eadff302SIvan T. Ivanov 		pad = &pads[i];
1075eadff302SIvan T. Ivanov 		pindesc->drv_data = pad;
1076eadff302SIvan T. Ivanov 		pindesc->number = i;
1077eadff302SIvan T. Ivanov 		pindesc->name = pmic_gpio_groups[i];
1078eadff302SIvan T. Ivanov 
1079ab4256cfSStephen Boyd 		pad->base = reg + i * PMIC_GPIO_ADDRESS_RANGE;
1080eadff302SIvan T. Ivanov 
1081eadff302SIvan T. Ivanov 		ret = pmic_gpio_populate(state, pad);
1082eadff302SIvan T. Ivanov 		if (ret < 0)
1083eadff302SIvan T. Ivanov 			return ret;
1084eadff302SIvan T. Ivanov 	}
1085eadff302SIvan T. Ivanov 
1086eadff302SIvan T. Ivanov 	state->chip = pmic_gpio_gpio_template;
108758383c78SLinus Walleij 	state->chip.parent = dev;
1088eadff302SIvan T. Ivanov 	state->chip.base = -1;
1089eadff302SIvan T. Ivanov 	state->chip.ngpio = npins;
1090eadff302SIvan T. Ivanov 	state->chip.label = dev_name(dev);
1091eadff302SIvan T. Ivanov 	state->chip.of_gpio_n_cells = 2;
1092eadff302SIvan T. Ivanov 	state->chip.can_sleep = false;
1093eadff302SIvan T. Ivanov 
1094b46ddfe6SLaxman Dewangan 	state->ctrl = devm_pinctrl_register(dev, pctrldesc, state);
1095323de9efSMasahiro Yamada 	if (IS_ERR(state->ctrl))
1096323de9efSMasahiro Yamada 		return PTR_ERR(state->ctrl);
1097eadff302SIvan T. Ivanov 
1098ca69e2d1SBrian Masney 	parent_node = of_irq_find_parent(state->dev->of_node);
1099ca69e2d1SBrian Masney 	if (!parent_node)
1100ca69e2d1SBrian Masney 		return -ENXIO;
1101ca69e2d1SBrian Masney 
1102ca69e2d1SBrian Masney 	parent_domain = irq_find_host(parent_node);
1103ca69e2d1SBrian Masney 	of_node_put(parent_node);
1104ca69e2d1SBrian Masney 	if (!parent_domain)
1105ca69e2d1SBrian Masney 		return -ENXIO;
1106ca69e2d1SBrian Masney 
1107821c76c4SBrian Masney 	girq = &state->chip.irq;
1108*1a41d1e5SManivannan Sadhasivam 	gpio_irq_chip_set_chip(girq, &spmi_gpio_irq_chip);
1109821c76c4SBrian Masney 	girq->default_type = IRQ_TYPE_NONE;
1110821c76c4SBrian Masney 	girq->handler = handle_level_irq;
1111821c76c4SBrian Masney 	girq->fwnode = of_node_to_fwnode(state->dev->of_node);
1112821c76c4SBrian Masney 	girq->parent_domain = parent_domain;
1113821c76c4SBrian Masney 	girq->child_to_parent_hwirq = pmic_gpio_child_to_parent_hwirq;
1114d36a9773SDavid Collins 	girq->populate_parent_alloc_arg = pmic_gpio_populate_parent_fwspec;
1115821c76c4SBrian Masney 	girq->child_offset_to_irq = pmic_gpio_child_offset_to_irq;
1116821c76c4SBrian Masney 	girq->child_irq_domain_ops.translate = pmic_gpio_domain_translate;
1117ca69e2d1SBrian Masney 
1118c52d9df1SLinus Walleij 	ret = gpiochip_add_data(&state->chip, state);
1119eadff302SIvan T. Ivanov 	if (ret) {
1120eadff302SIvan T. Ivanov 		dev_err(state->dev, "can't add gpio chip\n");
1121821c76c4SBrian Masney 		return ret;
1122eadff302SIvan T. Ivanov 	}
1123eadff302SIvan T. Ivanov 
1124149a9604SBrian Masney 	/*
1125149a9604SBrian Masney 	 * For DeviceTree-supported systems, the gpio core checks the
1126149a9604SBrian Masney 	 * pinctrl's device node for the "gpio-ranges" property.
1127149a9604SBrian Masney 	 * If it is present, it takes care of adding the pin ranges
1128149a9604SBrian Masney 	 * for the driver. In this case the driver can skip ahead.
1129149a9604SBrian Masney 	 *
1130149a9604SBrian Masney 	 * In order to remain compatible with older, existing DeviceTree
1131149a9604SBrian Masney 	 * files which don't set the "gpio-ranges" property or systems that
1132149a9604SBrian Masney 	 * utilize ACPI the driver has to call gpiochip_add_pin_range().
1133149a9604SBrian Masney 	 */
1134149a9604SBrian Masney 	if (!of_property_read_bool(dev->of_node, "gpio-ranges")) {
1135149a9604SBrian Masney 		ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0,
1136149a9604SBrian Masney 					     npins);
1137eadff302SIvan T. Ivanov 		if (ret) {
1138eadff302SIvan T. Ivanov 			dev_err(dev, "failed to add pin range\n");
1139eadff302SIvan T. Ivanov 			goto err_range;
1140eadff302SIvan T. Ivanov 		}
1141149a9604SBrian Masney 	}
1142eadff302SIvan T. Ivanov 
1143eadff302SIvan T. Ivanov 	return 0;
1144eadff302SIvan T. Ivanov 
1145eadff302SIvan T. Ivanov err_range:
1146eadff302SIvan T. Ivanov 	gpiochip_remove(&state->chip);
1147eadff302SIvan T. Ivanov 	return ret;
1148eadff302SIvan T. Ivanov }
1149eadff302SIvan T. Ivanov 
1150eadff302SIvan T. Ivanov static int pmic_gpio_remove(struct platform_device *pdev)
1151eadff302SIvan T. Ivanov {
1152eadff302SIvan T. Ivanov 	struct pmic_gpio_state *state = platform_get_drvdata(pdev);
1153eadff302SIvan T. Ivanov 
1154eadff302SIvan T. Ivanov 	gpiochip_remove(&state->chip);
1155eadff302SIvan T. Ivanov 	return 0;
1156eadff302SIvan T. Ivanov }
1157eadff302SIvan T. Ivanov 
1158eadff302SIvan T. Ivanov static const struct of_device_id pmic_gpio_of_match[] = {
1159ef874e03SLoic Poulain 	{ .compatible = "qcom,pm2250-gpio", .data = (void *) 10 },
116017cc38e7SKonrad Dybcio 	/* pm660 has 13 GPIOs with holes on 1, 5, 6, 7, 8 and 10 */
116117cc38e7SKonrad Dybcio 	{ .compatible = "qcom,pm660-gpio", .data = (void *) 13 },
116217cc38e7SKonrad Dybcio 	/* pm660l has 12 GPIOs with holes on 1, 2, 10, 11 and 12 */
116317cc38e7SKonrad Dybcio 	{ .compatible = "qcom,pm660l-gpio", .data = (void *) 12 },
11644d8a768eSMarijn Suijten 	{ .compatible = "qcom,pm6125-gpio", .data = (void *) 9 },
11654afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm6150-gpio", .data = (void *) 10 },
11664afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm6150l-gpio", .data = (void *) 12 },
116783917856SLuca Weiss 	{ .compatible = "qcom,pm6350-gpio", .data = (void *) 9 },
11684afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm7325-gpio", .data = (void *) 10 },
11694afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm8005-gpio", .data = (void *) 4 },
11704afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm8008-gpio", .data = (void *) 2 },
1171f3474383SKonrad Dybcio 	{ .compatible = "qcom,pm8019-gpio", .data = (void *) 6 },
1172d67070c3SVinod Koul 	/* pm8150 has 10 GPIOs with holes on 2, 5, 7 and 8 */
1173d67070c3SVinod Koul 	{ .compatible = "qcom,pm8150-gpio", .data = (void *) 10 },
1174182700f2SBjorn Andersson 	{ .compatible = "qcom,pmc8180-gpio", .data = (void *) 10 },
11759bdacaddSVinod Koul 	/* pm8150b has 12 GPIOs with holes on 3, r and 7 */
11769bdacaddSVinod Koul 	{ .compatible = "qcom,pm8150b-gpio", .data = (void *) 12 },
11772dc889a8SVinod Koul 	/* pm8150l has 12 GPIOs with holes on 7 */
11782dc889a8SVinod Koul 	{ .compatible = "qcom,pm8150l-gpio", .data = (void *) 12 },
1179182700f2SBjorn Andersson 	{ .compatible = "qcom,pmc8180c-gpio", .data = (void *) 12 },
1180eebe11b5SDominik Kobinski 	{ .compatible = "qcom,pm8226-gpio", .data = (void *) 8 },
118157c0a4f0SVinod Koul 	{ .compatible = "qcom,pm8350-gpio", .data = (void *) 10 },
118257c0a4f0SVinod Koul 	{ .compatible = "qcom,pm8350b-gpio", .data = (void *) 8 },
118357c0a4f0SVinod Koul 	{ .compatible = "qcom,pm8350c-gpio", .data = (void *) 9 },
1184168a0abfSDmitry Baryshkov 	{ .compatible = "qcom,pm8450-gpio", .data = (void *) 4 },
11854afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm8916-gpio", .data = (void *) 4 },
11864afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm8941-gpio", .data = (void *) 36 },
11874afc2a0cSBhupesh Sharma 	/* pm8950 has 8 GPIOs with holes on 3 */
11884afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm8950-gpio", .data = (void *) 8 },
11894afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm8994-gpio", .data = (void *) 22 },
11904afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm8998-gpio", .data = (void *) 26 },
11914afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pma8084-gpio", .data = (void *) 22 },
11924afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pmi8950-gpio", .data = (void *) 2 },
11934afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pmi8994-gpio", .data = (void *) 10 },
11944afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pmi8998-gpio", .data = (void *) 14 },
119557c0a4f0SVinod Koul 	{ .compatible = "qcom,pmk8350-gpio", .data = (void *) 4 },
119679e2311cSBhupesh Sharma 	{ .compatible = "qcom,pmm8155au-gpio", .data = (void *) 10 },
11976cd81a86SRobert Marko 	/* pmp8074 has 12 GPIOs with holes on 1 and 12 */
11986cd81a86SRobert Marko 	{ .compatible = "qcom,pmp8074-gpio", .data = (void *) 12 },
119957c0a4f0SVinod Koul 	{ .compatible = "qcom,pmr735a-gpio", .data = (void *) 4 },
120057c0a4f0SVinod Koul 	{ .compatible = "qcom,pmr735b-gpio", .data = (void *) 4 },
12014afc2a0cSBhupesh Sharma 	/* pms405 has 12 GPIOs with holes on 1, 9, and 10 */
12024afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pms405-gpio", .data = (void *) 12 },
1203ceb58de4SVinod Koul 	/* pmx55 has 11 GPIOs with holes on 3, 7, 10, 11 */
1204ceb58de4SVinod Koul 	{ .compatible = "qcom,pmx55-gpio", .data = (void *) 11 },
1205203638fdSRohit Agarwal 	{ .compatible = "qcom,pmx65-gpio", .data = (void *) 16 },
1206eadff302SIvan T. Ivanov 	{ },
1207eadff302SIvan T. Ivanov };
1208eadff302SIvan T. Ivanov 
1209eadff302SIvan T. Ivanov MODULE_DEVICE_TABLE(of, pmic_gpio_of_match);
1210eadff302SIvan T. Ivanov 
1211eadff302SIvan T. Ivanov static struct platform_driver pmic_gpio_driver = {
1212eadff302SIvan T. Ivanov 	.driver = {
1213eadff302SIvan T. Ivanov 		   .name = "qcom-spmi-gpio",
1214eadff302SIvan T. Ivanov 		   .of_match_table = pmic_gpio_of_match,
1215eadff302SIvan T. Ivanov 	},
1216eadff302SIvan T. Ivanov 	.probe	= pmic_gpio_probe,
1217eadff302SIvan T. Ivanov 	.remove = pmic_gpio_remove,
1218eadff302SIvan T. Ivanov };
1219eadff302SIvan T. Ivanov 
1220eadff302SIvan T. Ivanov module_platform_driver(pmic_gpio_driver);
1221eadff302SIvan T. Ivanov 
1222eadff302SIvan T. Ivanov MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
1223eadff302SIvan T. Ivanov MODULE_DESCRIPTION("Qualcomm SPMI PMIC GPIO pin control driver");
1224eadff302SIvan T. Ivanov MODULE_ALIAS("platform:qcom-spmi-gpio");
1225eadff302SIvan T. Ivanov MODULE_LICENSE("GPL v2");
1226