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.
44af95d09SDavid Collins  * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
5eadff302SIvan T. Ivanov  */
6eadff302SIvan T. Ivanov 
71c5fb66aSLinus Walleij #include <linux/gpio/driver.h>
8ca69e2d1SBrian Masney #include <linux/interrupt.h>
9eadff302SIvan T. Ivanov #include <linux/module.h>
10eadff302SIvan T. Ivanov #include <linux/of.h>
11ab4256cfSStephen Boyd #include <linux/of_irq.h>
12eadff302SIvan T. Ivanov #include <linux/pinctrl/pinconf-generic.h>
13eadff302SIvan T. Ivanov #include <linux/pinctrl/pinconf.h>
14eadff302SIvan T. Ivanov #include <linux/pinctrl/pinmux.h>
15eadff302SIvan T. Ivanov #include <linux/platform_device.h>
16eadff302SIvan T. Ivanov #include <linux/regmap.h>
17eadff302SIvan T. Ivanov #include <linux/slab.h>
18d36a9773SDavid Collins #include <linux/spmi.h>
19eadff302SIvan T. Ivanov #include <linux/types.h>
20eadff302SIvan T. Ivanov 
21eadff302SIvan T. Ivanov #include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
22eadff302SIvan T. Ivanov 
23eadff302SIvan T. Ivanov #include "../core.h"
24eadff302SIvan T. Ivanov #include "../pinctrl-utils.h"
25eadff302SIvan T. Ivanov 
26eadff302SIvan T. Ivanov #define PMIC_GPIO_ADDRESS_RANGE			0x100
27eadff302SIvan T. Ivanov 
28eadff302SIvan T. Ivanov /* type and subtype registers base address offsets */
29eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_TYPE			0x4
30eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_SUBTYPE			0x5
31eadff302SIvan T. Ivanov 
32eadff302SIvan T. Ivanov /* GPIO peripheral type and subtype out_values */
33eadff302SIvan T. Ivanov #define PMIC_GPIO_TYPE				0x10
34eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIO_4CH		0x1
35eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIOC_4CH		0x5
36eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIO_8CH		0x9
37eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIOC_8CH		0xd
38d7b5f5ccSFenglin Wu #define PMIC_GPIO_SUBTYPE_GPIO_LV		0x10
39d7b5f5ccSFenglin Wu #define PMIC_GPIO_SUBTYPE_GPIO_MV		0x11
404af95d09SDavid Collins #define PMIC_GPIO_SUBTYPE_GPIO_LV_VIN2		0x12
414af95d09SDavid Collins #define PMIC_GPIO_SUBTYPE_GPIO_MV_VIN3		0x13
42eadff302SIvan T. Ivanov 
43eadff302SIvan T. Ivanov #define PMIC_MPP_REG_RT_STS			0x10
44eadff302SIvan T. Ivanov #define PMIC_MPP_REG_RT_STS_VAL_MASK		0x1
45eadff302SIvan T. Ivanov 
46eadff302SIvan T. Ivanov /* control register base address offsets */
47eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_CTL			0x40
48eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_VIN_CTL		0x41
49eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_PULL_CTL		0x42
50d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL	0x44
51223463fcSFenglin Wu #define PMIC_GPIO_REG_DIG_IN_CTL		0x43
52eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_OUT_CTL		0x45
53eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_EN_CTL			0x46
54d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL	0x4A
55eadff302SIvan T. Ivanov 
56eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_MODE_CTL */
57eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_VALUE_SHIFT		0x1
58eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_FUNCTION_SHIFT	1
59eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_FUNCTION_MASK	0x7
60eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_DIR_SHIFT		4
61eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_DIR_MASK		0x7
62eadff302SIvan T. Ivanov 
63d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_INPUT		0
64d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_OUTPUT		1
65d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT	2
66d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_ANALOG_PASS_THRU		3
67d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_MODE_DIR_MASK	0x3
68d7b5f5ccSFenglin Wu 
69eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_VIN_CTL */
70eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_VIN_SHIFT			0
71eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_VIN_MASK			0x7
72eadff302SIvan T. Ivanov 
73eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_PULL_CTL */
74eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_PULL_SHIFT		0
75eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_PULL_MASK			0x7
76eadff302SIvan T. Ivanov 
77eadff302SIvan T. Ivanov #define PMIC_GPIO_PULL_DOWN			4
78eadff302SIvan T. Ivanov #define PMIC_GPIO_PULL_DISABLE			5
79eadff302SIvan T. Ivanov 
80d7b5f5ccSFenglin Wu /* PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL for LV/MV */
81d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_INVERT		0x80
82d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT	7
83d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK	0xF
84d7b5f5ccSFenglin Wu 
85223463fcSFenglin Wu /* PMIC_GPIO_REG_DIG_IN_CTL */
86223463fcSFenglin Wu #define PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN		0x80
87223463fcSFenglin Wu #define PMIC_GPIO_LV_MV_DIG_IN_DTEST_SEL_MASK	0x7
88223463fcSFenglin Wu #define PMIC_GPIO_DIG_IN_DTEST_SEL_MASK		0xf
89223463fcSFenglin Wu 
90eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_OUT_CTL */
91eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_STRENGTH_SHIFT	0
92eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_STRENGTH_MASK		0x3
93eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_TYPE_SHIFT		4
94eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_TYPE_MASK		0x3
95eadff302SIvan T. Ivanov 
96eadff302SIvan T. Ivanov /*
97eadff302SIvan T. Ivanov  * Output type - indicates pin should be configured as push-pull,
98eadff302SIvan T. Ivanov  * open drain or open source.
99eadff302SIvan T. Ivanov  */
100eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_CMOS			0
101eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS	1
102eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS	2
103eadff302SIvan T. Ivanov 
104*723e8462SAnjelique Melendez #define PMIC_GPIO_OUT_STRENGTH_LOW		1
105*723e8462SAnjelique Melendez #define PMIC_GPIO_OUT_STRENGTH_HIGH		3
106*723e8462SAnjelique Melendez 
107eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_EN_CTL */
108eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MASTER_EN_SHIFT		7
109eadff302SIvan T. Ivanov 
110eadff302SIvan T. Ivanov #define PMIC_GPIO_PHYSICAL_OFFSET		1
111eadff302SIvan T. Ivanov 
112d7b5f5ccSFenglin Wu /* PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL */
113d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_ANA_MUX_SEL_MASK		0x3
114d7b5f5ccSFenglin Wu 
115eadff302SIvan T. Ivanov /* Qualcomm specific pin configurations */
116eadff302SIvan T. Ivanov #define PMIC_GPIO_CONF_PULL_UP			(PIN_CONFIG_END + 1)
117eadff302SIvan T. Ivanov #define PMIC_GPIO_CONF_STRENGTH			(PIN_CONFIG_END + 2)
118d7b5f5ccSFenglin Wu #define PMIC_GPIO_CONF_ATEST			(PIN_CONFIG_END + 3)
119d7b5f5ccSFenglin Wu #define PMIC_GPIO_CONF_ANALOG_PASS		(PIN_CONFIG_END + 4)
120223463fcSFenglin Wu #define PMIC_GPIO_CONF_DTEST_BUFFER		(PIN_CONFIG_END + 5)
121d7b5f5ccSFenglin Wu 
122d7b5f5ccSFenglin Wu /* The index of each function in pmic_gpio_functions[] array */
123d7b5f5ccSFenglin Wu enum pmic_gpio_func_index {
124d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_NORMAL,
125d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_PAIRED,
126d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_FUNC1,
127d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_FUNC2,
128d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_FUNC3,
129d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_FUNC4,
130d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_DTEST1,
131d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_DTEST2,
132d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_DTEST3,
133d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_DTEST4,
134d7b5f5ccSFenglin Wu };
135eadff302SIvan T. Ivanov 
136eadff302SIvan T. Ivanov /**
137eadff302SIvan T. Ivanov  * struct pmic_gpio_pad - keep current GPIO settings
138eadff302SIvan T. Ivanov  * @base: Address base in SPMI device.
139eadff302SIvan T. Ivanov  * @is_enabled: Set to false when GPIO should be put in high Z state.
140eadff302SIvan T. Ivanov  * @out_value: Cached pin output value
141eadff302SIvan T. Ivanov  * @have_buffer: Set to true if GPIO output could be configured in push-pull,
142eadff302SIvan T. Ivanov  *	open-drain or open-source mode.
143eadff302SIvan T. Ivanov  * @output_enabled: Set to true if GPIO output logic is enabled.
144eadff302SIvan T. Ivanov  * @input_enabled: Set to true if GPIO input buffer logic is enabled.
145d7b5f5ccSFenglin Wu  * @analog_pass: Set to true if GPIO is in analog-pass-through mode.
146d7b5f5ccSFenglin Wu  * @lv_mv_type: Set to true if GPIO subtype is GPIO_LV(0x10) or GPIO_MV(0x11).
147eadff302SIvan T. Ivanov  * @num_sources: Number of power-sources supported by this GPIO.
148eadff302SIvan T. Ivanov  * @power_source: Current power-source used.
149eadff302SIvan T. Ivanov  * @buffer_type: Push-pull, open-drain or open-source.
150eadff302SIvan T. Ivanov  * @pullup: Constant current which flow trough GPIO output buffer.
151eadff302SIvan T. Ivanov  * @strength: No, Low, Medium, High
152eadff302SIvan T. Ivanov  * @function: See pmic_gpio_functions[]
153d7b5f5ccSFenglin Wu  * @atest: the ATEST selection for GPIO analog-pass-through mode
154223463fcSFenglin Wu  * @dtest_buffer: the DTEST buffer selection for digital input mode.
155eadff302SIvan T. Ivanov  */
156eadff302SIvan T. Ivanov struct pmic_gpio_pad {
157eadff302SIvan T. Ivanov 	u16		base;
158eadff302SIvan T. Ivanov 	bool		is_enabled;
159eadff302SIvan T. Ivanov 	bool		out_value;
160eadff302SIvan T. Ivanov 	bool		have_buffer;
161eadff302SIvan T. Ivanov 	bool		output_enabled;
162eadff302SIvan T. Ivanov 	bool		input_enabled;
163d7b5f5ccSFenglin Wu 	bool		analog_pass;
164d7b5f5ccSFenglin Wu 	bool		lv_mv_type;
165eadff302SIvan T. Ivanov 	unsigned int	num_sources;
166eadff302SIvan T. Ivanov 	unsigned int	power_source;
167eadff302SIvan T. Ivanov 	unsigned int	buffer_type;
168eadff302SIvan T. Ivanov 	unsigned int	pullup;
169eadff302SIvan T. Ivanov 	unsigned int	strength;
170eadff302SIvan T. Ivanov 	unsigned int	function;
171d7b5f5ccSFenglin Wu 	unsigned int	atest;
172223463fcSFenglin Wu 	unsigned int	dtest_buffer;
173eadff302SIvan T. Ivanov };
174eadff302SIvan T. Ivanov 
175eadff302SIvan T. Ivanov struct pmic_gpio_state {
176eadff302SIvan T. Ivanov 	struct device	*dev;
177eadff302SIvan T. Ivanov 	struct regmap	*map;
178eadff302SIvan T. Ivanov 	struct pinctrl_dev *ctrl;
179eadff302SIvan T. Ivanov 	struct gpio_chip chip;
180d36a9773SDavid Collins 	u8 usid;
181d36a9773SDavid Collins 	u8 pid_base;
182eadff302SIvan T. Ivanov };
183eadff302SIvan T. Ivanov 
184f684e4acSLinus Walleij static const struct pinconf_generic_params pmic_gpio_bindings[] = {
1857382b623SSoren Brinkmann 	{"qcom,pull-up-strength",	PMIC_GPIO_CONF_PULL_UP,		0},
1867382b623SSoren Brinkmann 	{"qcom,drive-strength",		PMIC_GPIO_CONF_STRENGTH,	0},
187d7b5f5ccSFenglin Wu 	{"qcom,atest",			PMIC_GPIO_CONF_ATEST,		0},
188d7b5f5ccSFenglin Wu 	{"qcom,analog-pass",		PMIC_GPIO_CONF_ANALOG_PASS,	0},
189223463fcSFenglin Wu 	{"qcom,dtest-buffer",           PMIC_GPIO_CONF_DTEST_BUFFER,    0},
190eadff302SIvan T. Ivanov };
191eadff302SIvan T. Ivanov 
1924f06266aSArnd Bergmann #ifdef CONFIG_DEBUG_FS
1937382b623SSoren Brinkmann static const struct pin_config_item pmic_conf_items[ARRAY_SIZE(pmic_gpio_bindings)] = {
1947382b623SSoren Brinkmann 	PCONFDUMP(PMIC_GPIO_CONF_PULL_UP,  "pull up strength", NULL, true),
1957382b623SSoren Brinkmann 	PCONFDUMP(PMIC_GPIO_CONF_STRENGTH, "drive-strength", NULL, true),
196d7b5f5ccSFenglin Wu 	PCONFDUMP(PMIC_GPIO_CONF_ATEST, "atest", NULL, true),
197d7b5f5ccSFenglin Wu 	PCONFDUMP(PMIC_GPIO_CONF_ANALOG_PASS, "analog-pass", NULL, true),
198223463fcSFenglin Wu 	PCONFDUMP(PMIC_GPIO_CONF_DTEST_BUFFER, "dtest-buffer", NULL, true),
199eadff302SIvan T. Ivanov };
2004f06266aSArnd Bergmann #endif
201eadff302SIvan T. Ivanov 
202eadff302SIvan T. Ivanov static const char *const pmic_gpio_groups[] = {
203eadff302SIvan T. Ivanov 	"gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", "gpio8",
204eadff302SIvan T. Ivanov 	"gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", "gpio15",
205eadff302SIvan T. Ivanov 	"gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22",
206eadff302SIvan T. Ivanov 	"gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29",
207eadff302SIvan T. Ivanov 	"gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36",
208eadff302SIvan T. Ivanov };
209eadff302SIvan T. Ivanov 
210eadff302SIvan T. Ivanov static const char *const pmic_gpio_functions[] = {
211d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_NORMAL]	= PMIC_GPIO_FUNC_NORMAL,
212d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_PAIRED]	= PMIC_GPIO_FUNC_PAIRED,
213d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_FUNC1]	= PMIC_GPIO_FUNC_FUNC1,
214d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_FUNC2]	= PMIC_GPIO_FUNC_FUNC2,
215d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_FUNC3]	= PMIC_GPIO_FUNC_FUNC3,
216d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_FUNC4]	= PMIC_GPIO_FUNC_FUNC4,
217d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_DTEST1]	= PMIC_GPIO_FUNC_DTEST1,
218d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_DTEST2]	= PMIC_GPIO_FUNC_DTEST2,
219d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_DTEST3]	= PMIC_GPIO_FUNC_DTEST3,
220d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_DTEST4]	= PMIC_GPIO_FUNC_DTEST4,
221eadff302SIvan T. Ivanov };
222eadff302SIvan T. Ivanov 
223eadff302SIvan T. Ivanov static int pmic_gpio_read(struct pmic_gpio_state *state,
224eadff302SIvan T. Ivanov 			  struct pmic_gpio_pad *pad, unsigned int addr)
225eadff302SIvan T. Ivanov {
226eadff302SIvan T. Ivanov 	unsigned int val;
227eadff302SIvan T. Ivanov 	int ret;
228eadff302SIvan T. Ivanov 
229eadff302SIvan T. Ivanov 	ret = regmap_read(state->map, pad->base + addr, &val);
230eadff302SIvan T. Ivanov 	if (ret < 0)
231eadff302SIvan T. Ivanov 		dev_err(state->dev, "read 0x%x failed\n", addr);
232eadff302SIvan T. Ivanov 	else
233eadff302SIvan T. Ivanov 		ret = val;
234eadff302SIvan T. Ivanov 
235eadff302SIvan T. Ivanov 	return ret;
236eadff302SIvan T. Ivanov }
237eadff302SIvan T. Ivanov 
238eadff302SIvan T. Ivanov static int pmic_gpio_write(struct pmic_gpio_state *state,
239eadff302SIvan T. Ivanov 			   struct pmic_gpio_pad *pad, unsigned int addr,
240eadff302SIvan T. Ivanov 			   unsigned int val)
241eadff302SIvan T. Ivanov {
242eadff302SIvan T. Ivanov 	int ret;
243eadff302SIvan T. Ivanov 
244eadff302SIvan T. Ivanov 	ret = regmap_write(state->map, pad->base + addr, val);
245eadff302SIvan T. Ivanov 	if (ret < 0)
246eadff302SIvan T. Ivanov 		dev_err(state->dev, "write 0x%x failed\n", addr);
247eadff302SIvan T. Ivanov 
248eadff302SIvan T. Ivanov 	return ret;
249eadff302SIvan T. Ivanov }
250eadff302SIvan T. Ivanov 
251eadff302SIvan T. Ivanov static int pmic_gpio_get_groups_count(struct pinctrl_dev *pctldev)
252eadff302SIvan T. Ivanov {
253eadff302SIvan T. Ivanov 	/* Every PIN is a group */
254eadff302SIvan T. Ivanov 	return pctldev->desc->npins;
255eadff302SIvan T. Ivanov }
256eadff302SIvan T. Ivanov 
257eadff302SIvan T. Ivanov static const char *pmic_gpio_get_group_name(struct pinctrl_dev *pctldev,
258eadff302SIvan T. Ivanov 					    unsigned pin)
259eadff302SIvan T. Ivanov {
260eadff302SIvan T. Ivanov 	return pctldev->desc->pins[pin].name;
261eadff302SIvan T. Ivanov }
262eadff302SIvan T. Ivanov 
263eadff302SIvan T. Ivanov static int pmic_gpio_get_group_pins(struct pinctrl_dev *pctldev, unsigned pin,
264eadff302SIvan T. Ivanov 				    const unsigned **pins, unsigned *num_pins)
265eadff302SIvan T. Ivanov {
266eadff302SIvan T. Ivanov 	*pins = &pctldev->desc->pins[pin].number;
267eadff302SIvan T. Ivanov 	*num_pins = 1;
268eadff302SIvan T. Ivanov 	return 0;
269eadff302SIvan T. Ivanov }
270eadff302SIvan T. Ivanov 
271eadff302SIvan T. Ivanov static const struct pinctrl_ops pmic_gpio_pinctrl_ops = {
272eadff302SIvan T. Ivanov 	.get_groups_count	= pmic_gpio_get_groups_count,
273eadff302SIvan T. Ivanov 	.get_group_name		= pmic_gpio_get_group_name,
274eadff302SIvan T. Ivanov 	.get_group_pins		= pmic_gpio_get_group_pins,
2757382b623SSoren Brinkmann 	.dt_node_to_map		= pinconf_generic_dt_node_to_map_group,
276d32f7fd3SIrina Tirdea 	.dt_free_map		= pinctrl_utils_free_map,
277eadff302SIvan T. Ivanov };
278eadff302SIvan T. Ivanov 
279eadff302SIvan T. Ivanov static int pmic_gpio_get_functions_count(struct pinctrl_dev *pctldev)
280eadff302SIvan T. Ivanov {
281eadff302SIvan T. Ivanov 	return ARRAY_SIZE(pmic_gpio_functions);
282eadff302SIvan T. Ivanov }
283eadff302SIvan T. Ivanov 
284eadff302SIvan T. Ivanov static const char *pmic_gpio_get_function_name(struct pinctrl_dev *pctldev,
285eadff302SIvan T. Ivanov 					       unsigned function)
286eadff302SIvan T. Ivanov {
287eadff302SIvan T. Ivanov 	return pmic_gpio_functions[function];
288eadff302SIvan T. Ivanov }
289eadff302SIvan T. Ivanov 
290eadff302SIvan T. Ivanov static int pmic_gpio_get_function_groups(struct pinctrl_dev *pctldev,
291eadff302SIvan T. Ivanov 					 unsigned function,
292eadff302SIvan T. Ivanov 					 const char *const **groups,
293eadff302SIvan T. Ivanov 					 unsigned *const num_qgroups)
294eadff302SIvan T. Ivanov {
295eadff302SIvan T. Ivanov 	*groups = pmic_gpio_groups;
296eadff302SIvan T. Ivanov 	*num_qgroups = pctldev->desc->npins;
297eadff302SIvan T. Ivanov 	return 0;
298eadff302SIvan T. Ivanov }
299eadff302SIvan T. Ivanov 
300eadff302SIvan T. Ivanov static int pmic_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned function,
301eadff302SIvan T. Ivanov 				unsigned pin)
302eadff302SIvan T. Ivanov {
303eadff302SIvan T. Ivanov 	struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev);
304eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad;
305eadff302SIvan T. Ivanov 	unsigned int val;
306eadff302SIvan T. Ivanov 	int ret;
307eadff302SIvan T. Ivanov 
308d7b5f5ccSFenglin Wu 	if (function > PMIC_GPIO_FUNC_INDEX_DTEST4) {
309d7b5f5ccSFenglin Wu 		pr_err("function: %d is not defined\n", function);
310d7b5f5ccSFenglin Wu 		return -EINVAL;
311d7b5f5ccSFenglin Wu 	}
312d7b5f5ccSFenglin Wu 
313eadff302SIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
314d7b5f5ccSFenglin Wu 	/*
315d7b5f5ccSFenglin Wu 	 * Non-LV/MV subtypes only support 2 special functions,
316d7b5f5ccSFenglin Wu 	 * offsetting the dtestx function values by 2
317d7b5f5ccSFenglin Wu 	 */
318d7b5f5ccSFenglin Wu 	if (!pad->lv_mv_type) {
319d7b5f5ccSFenglin Wu 		if (function == PMIC_GPIO_FUNC_INDEX_FUNC3 ||
320d7b5f5ccSFenglin Wu 				function == PMIC_GPIO_FUNC_INDEX_FUNC4) {
321d7b5f5ccSFenglin Wu 			pr_err("LV/MV subtype doesn't have func3/func4\n");
322d7b5f5ccSFenglin Wu 			return -EINVAL;
323d7b5f5ccSFenglin Wu 		}
324d7b5f5ccSFenglin Wu 		if (function >= PMIC_GPIO_FUNC_INDEX_DTEST1)
325d7b5f5ccSFenglin Wu 			function -= (PMIC_GPIO_FUNC_INDEX_DTEST1 -
326d7b5f5ccSFenglin Wu 					PMIC_GPIO_FUNC_INDEX_FUNC3);
327d7b5f5ccSFenglin Wu 	}
328eadff302SIvan T. Ivanov 
329eadff302SIvan T. Ivanov 	pad->function = function;
330eadff302SIvan T. Ivanov 
331d7b5f5ccSFenglin Wu 	if (pad->analog_pass)
332d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_ANALOG_PASS_THRU;
333d7b5f5ccSFenglin Wu 	else if (pad->output_enabled && pad->input_enabled)
334d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT;
335d7b5f5ccSFenglin Wu 	else if (pad->output_enabled)
336d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_OUTPUT;
337eadff302SIvan T. Ivanov 	else
338d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_INPUT;
339eadff302SIvan T. Ivanov 
340d7b5f5ccSFenglin Wu 	if (pad->lv_mv_type) {
341d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
342d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_MODE_CTL, val);
343d7b5f5ccSFenglin Wu 		if (ret < 0)
344d7b5f5ccSFenglin Wu 			return ret;
345d7b5f5ccSFenglin Wu 
346d7b5f5ccSFenglin Wu 		val = pad->atest - 1;
347d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
348d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL, val);
349d7b5f5ccSFenglin Wu 		if (ret < 0)
350d7b5f5ccSFenglin Wu 			return ret;
351d7b5f5ccSFenglin Wu 
352d7b5f5ccSFenglin Wu 		val = pad->out_value
353d7b5f5ccSFenglin Wu 			<< PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT;
354d7b5f5ccSFenglin Wu 		val |= pad->function
355d7b5f5ccSFenglin Wu 			& PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK;
356d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
357d7b5f5ccSFenglin Wu 			PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL, val);
358d7b5f5ccSFenglin Wu 		if (ret < 0)
359d7b5f5ccSFenglin Wu 			return ret;
360d7b5f5ccSFenglin Wu 	} else {
361dc391502SIvan T. Ivanov 		val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT;
362eadff302SIvan T. Ivanov 		val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT;
363eadff302SIvan T. Ivanov 		val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT;
364eadff302SIvan T. Ivanov 
365eadff302SIvan T. Ivanov 		ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val);
366eadff302SIvan T. Ivanov 		if (ret < 0)
367eadff302SIvan T. Ivanov 			return ret;
368d7b5f5ccSFenglin Wu 	}
369eadff302SIvan T. Ivanov 
370eadff302SIvan T. Ivanov 	val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT;
371eadff302SIvan T. Ivanov 
372eadff302SIvan T. Ivanov 	return pmic_gpio_write(state, pad, PMIC_GPIO_REG_EN_CTL, val);
373eadff302SIvan T. Ivanov }
374eadff302SIvan T. Ivanov 
375eadff302SIvan T. Ivanov static const struct pinmux_ops pmic_gpio_pinmux_ops = {
376eadff302SIvan T. Ivanov 	.get_functions_count	= pmic_gpio_get_functions_count,
377eadff302SIvan T. Ivanov 	.get_function_name	= pmic_gpio_get_function_name,
378eadff302SIvan T. Ivanov 	.get_function_groups	= pmic_gpio_get_function_groups,
379eadff302SIvan T. Ivanov 	.set_mux		= pmic_gpio_set_mux,
380eadff302SIvan T. Ivanov };
381eadff302SIvan T. Ivanov 
382eadff302SIvan T. Ivanov static int pmic_gpio_config_get(struct pinctrl_dev *pctldev,
383eadff302SIvan T. Ivanov 				unsigned int pin, unsigned long *config)
384eadff302SIvan T. Ivanov {
385eadff302SIvan T. Ivanov 	unsigned param = pinconf_to_config_param(*config);
386eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad;
387eadff302SIvan T. Ivanov 	unsigned arg;
388eadff302SIvan T. Ivanov 
389eadff302SIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
390eadff302SIvan T. Ivanov 
391eadff302SIvan T. Ivanov 	switch (param) {
392eadff302SIvan T. Ivanov 	case PIN_CONFIG_DRIVE_PUSH_PULL:
3931cf86bc2SDouglas Anderson 		if (pad->buffer_type != PMIC_GPIO_OUT_BUF_CMOS)
3941cf86bc2SDouglas Anderson 			return -EINVAL;
3951cf86bc2SDouglas Anderson 		arg = 1;
396eadff302SIvan T. Ivanov 		break;
397eadff302SIvan T. Ivanov 	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
3981cf86bc2SDouglas Anderson 		if (pad->buffer_type != PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS)
3991cf86bc2SDouglas Anderson 			return -EINVAL;
4001cf86bc2SDouglas Anderson 		arg = 1;
401eadff302SIvan T. Ivanov 		break;
402eadff302SIvan T. Ivanov 	case PIN_CONFIG_DRIVE_OPEN_SOURCE:
4031cf86bc2SDouglas Anderson 		if (pad->buffer_type != PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS)
4041cf86bc2SDouglas Anderson 			return -EINVAL;
4051cf86bc2SDouglas Anderson 		arg = 1;
406eadff302SIvan T. Ivanov 		break;
407eadff302SIvan T. Ivanov 	case PIN_CONFIG_BIAS_PULL_DOWN:
4081cf86bc2SDouglas Anderson 		if (pad->pullup != PMIC_GPIO_PULL_DOWN)
4091cf86bc2SDouglas Anderson 			return -EINVAL;
4101cf86bc2SDouglas Anderson 		arg = 1;
411eadff302SIvan T. Ivanov 		break;
412eadff302SIvan T. Ivanov 	case PIN_CONFIG_BIAS_DISABLE:
4131cf86bc2SDouglas Anderson 		if (pad->pullup != PMIC_GPIO_PULL_DISABLE)
4141cf86bc2SDouglas Anderson 			return -EINVAL;
4151cf86bc2SDouglas Anderson 		arg = 1;
416eadff302SIvan T. Ivanov 		break;
417eadff302SIvan T. Ivanov 	case PIN_CONFIG_BIAS_PULL_UP:
4181cf86bc2SDouglas Anderson 		if (pad->pullup != PMIC_GPIO_PULL_UP_30)
4191cf86bc2SDouglas Anderson 			return -EINVAL;
4201cf86bc2SDouglas Anderson 		arg = 1;
421eadff302SIvan T. Ivanov 		break;
422eadff302SIvan T. Ivanov 	case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
4231cf86bc2SDouglas Anderson 		if (pad->is_enabled)
4241cf86bc2SDouglas Anderson 			return -EINVAL;
4251cf86bc2SDouglas Anderson 		arg = 1;
426eadff302SIvan T. Ivanov 		break;
427eadff302SIvan T. Ivanov 	case PIN_CONFIG_POWER_SOURCE:
428eadff302SIvan T. Ivanov 		arg = pad->power_source;
429eadff302SIvan T. Ivanov 		break;
430eadff302SIvan T. Ivanov 	case PIN_CONFIG_INPUT_ENABLE:
4311cf86bc2SDouglas Anderson 		if (!pad->input_enabled)
4321cf86bc2SDouglas Anderson 			return -EINVAL;
4331cf86bc2SDouglas Anderson 		arg = 1;
434eadff302SIvan T. Ivanov 		break;
435727293a8SSubbaraman Narayanamurthy 	case PIN_CONFIG_OUTPUT_ENABLE:
436727293a8SSubbaraman Narayanamurthy 		arg = pad->output_enabled;
437727293a8SSubbaraman Narayanamurthy 		break;
438eadff302SIvan T. Ivanov 	case PIN_CONFIG_OUTPUT:
439eadff302SIvan T. Ivanov 		arg = pad->out_value;
440eadff302SIvan T. Ivanov 		break;
441eadff302SIvan T. Ivanov 	case PMIC_GPIO_CONF_PULL_UP:
442eadff302SIvan T. Ivanov 		arg = pad->pullup;
443eadff302SIvan T. Ivanov 		break;
444eadff302SIvan T. Ivanov 	case PMIC_GPIO_CONF_STRENGTH:
445*723e8462SAnjelique Melendez 		switch (pad->strength) {
446*723e8462SAnjelique Melendez 		case PMIC_GPIO_OUT_STRENGTH_HIGH:
447*723e8462SAnjelique Melendez 			arg = PMIC_GPIO_STRENGTH_HIGH;
448*723e8462SAnjelique Melendez 			break;
449*723e8462SAnjelique Melendez 		case PMIC_GPIO_OUT_STRENGTH_LOW:
450*723e8462SAnjelique Melendez 			arg = PMIC_GPIO_STRENGTH_LOW;
451*723e8462SAnjelique Melendez 			break;
452*723e8462SAnjelique Melendez 		default:
453eadff302SIvan T. Ivanov 			arg = pad->strength;
454eadff302SIvan T. Ivanov 			break;
455*723e8462SAnjelique Melendez 		}
456*723e8462SAnjelique Melendez 		break;
457d7b5f5ccSFenglin Wu 	case PMIC_GPIO_CONF_ATEST:
458d7b5f5ccSFenglin Wu 		arg = pad->atest;
459d7b5f5ccSFenglin Wu 		break;
460d7b5f5ccSFenglin Wu 	case PMIC_GPIO_CONF_ANALOG_PASS:
461d7b5f5ccSFenglin Wu 		arg = pad->analog_pass;
462d7b5f5ccSFenglin Wu 		break;
463223463fcSFenglin Wu 	case PMIC_GPIO_CONF_DTEST_BUFFER:
464223463fcSFenglin Wu 		arg = pad->dtest_buffer;
465223463fcSFenglin Wu 		break;
466eadff302SIvan T. Ivanov 	default:
467eadff302SIvan T. Ivanov 		return -EINVAL;
468eadff302SIvan T. Ivanov 	}
469eadff302SIvan T. Ivanov 
470eadff302SIvan T. Ivanov 	*config = pinconf_to_config_packed(param, arg);
471eadff302SIvan T. Ivanov 	return 0;
472eadff302SIvan T. Ivanov }
473eadff302SIvan T. Ivanov 
474eadff302SIvan T. Ivanov static int pmic_gpio_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
475eadff302SIvan T. Ivanov 				unsigned long *configs, unsigned nconfs)
476eadff302SIvan T. Ivanov {
477eadff302SIvan T. Ivanov 	struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev);
478eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad;
479eadff302SIvan T. Ivanov 	unsigned param, arg;
480eadff302SIvan T. Ivanov 	unsigned int val;
481eadff302SIvan T. Ivanov 	int i, ret;
482eadff302SIvan T. Ivanov 
483eadff302SIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
484eadff302SIvan T. Ivanov 
4856cb74f44SFenglin Wu 	pad->is_enabled = true;
486eadff302SIvan T. Ivanov 	for (i = 0; i < nconfs; i++) {
487eadff302SIvan T. Ivanov 		param = pinconf_to_config_param(configs[i]);
488eadff302SIvan T. Ivanov 		arg = pinconf_to_config_argument(configs[i]);
489eadff302SIvan T. Ivanov 
490eadff302SIvan T. Ivanov 		switch (param) {
491eadff302SIvan T. Ivanov 		case PIN_CONFIG_DRIVE_PUSH_PULL:
492eadff302SIvan T. Ivanov 			pad->buffer_type = PMIC_GPIO_OUT_BUF_CMOS;
493eadff302SIvan T. Ivanov 			break;
494eadff302SIvan T. Ivanov 		case PIN_CONFIG_DRIVE_OPEN_DRAIN:
495eadff302SIvan T. Ivanov 			if (!pad->have_buffer)
496eadff302SIvan T. Ivanov 				return -EINVAL;
497eadff302SIvan T. Ivanov 			pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS;
498eadff302SIvan T. Ivanov 			break;
499eadff302SIvan T. Ivanov 		case PIN_CONFIG_DRIVE_OPEN_SOURCE:
500eadff302SIvan T. Ivanov 			if (!pad->have_buffer)
501eadff302SIvan T. Ivanov 				return -EINVAL;
502eadff302SIvan T. Ivanov 			pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS;
503eadff302SIvan T. Ivanov 			break;
504eadff302SIvan T. Ivanov 		case PIN_CONFIG_BIAS_DISABLE:
505eadff302SIvan T. Ivanov 			pad->pullup = PMIC_GPIO_PULL_DISABLE;
506eadff302SIvan T. Ivanov 			break;
507eadff302SIvan T. Ivanov 		case PIN_CONFIG_BIAS_PULL_UP:
508eadff302SIvan T. Ivanov 			pad->pullup = PMIC_GPIO_PULL_UP_30;
509eadff302SIvan T. Ivanov 			break;
510eadff302SIvan T. Ivanov 		case PIN_CONFIG_BIAS_PULL_DOWN:
511eadff302SIvan T. Ivanov 			if (arg)
512eadff302SIvan T. Ivanov 				pad->pullup = PMIC_GPIO_PULL_DOWN;
513eadff302SIvan T. Ivanov 			else
514eadff302SIvan T. Ivanov 				pad->pullup = PMIC_GPIO_PULL_DISABLE;
515eadff302SIvan T. Ivanov 			break;
516eadff302SIvan T. Ivanov 		case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
517eadff302SIvan T. Ivanov 			pad->is_enabled = false;
518eadff302SIvan T. Ivanov 			break;
519eadff302SIvan T. Ivanov 		case PIN_CONFIG_POWER_SOURCE:
5204e83ac4cSFenglin Wu 			if (arg >= pad->num_sources)
521eadff302SIvan T. Ivanov 				return -EINVAL;
522eadff302SIvan T. Ivanov 			pad->power_source = arg;
523eadff302SIvan T. Ivanov 			break;
524eadff302SIvan T. Ivanov 		case PIN_CONFIG_INPUT_ENABLE:
525eadff302SIvan T. Ivanov 			pad->input_enabled = arg ? true : false;
526eadff302SIvan T. Ivanov 			break;
527727293a8SSubbaraman Narayanamurthy 		case PIN_CONFIG_OUTPUT_ENABLE:
528727293a8SSubbaraman Narayanamurthy 			pad->output_enabled = arg ? true : false;
529727293a8SSubbaraman Narayanamurthy 			break;
530eadff302SIvan T. Ivanov 		case PIN_CONFIG_OUTPUT:
531eadff302SIvan T. Ivanov 			pad->output_enabled = true;
532eadff302SIvan T. Ivanov 			pad->out_value = arg;
533eadff302SIvan T. Ivanov 			break;
534eadff302SIvan T. Ivanov 		case PMIC_GPIO_CONF_PULL_UP:
535eadff302SIvan T. Ivanov 			if (arg > PMIC_GPIO_PULL_UP_1P5_30)
536eadff302SIvan T. Ivanov 				return -EINVAL;
537eadff302SIvan T. Ivanov 			pad->pullup = arg;
538eadff302SIvan T. Ivanov 			break;
539eadff302SIvan T. Ivanov 		case PMIC_GPIO_CONF_STRENGTH:
540eadff302SIvan T. Ivanov 			if (arg > PMIC_GPIO_STRENGTH_LOW)
541eadff302SIvan T. Ivanov 				return -EINVAL;
542*723e8462SAnjelique Melendez 			switch (arg) {
543*723e8462SAnjelique Melendez 			case PMIC_GPIO_STRENGTH_HIGH:
544*723e8462SAnjelique Melendez 				pad->strength = PMIC_GPIO_OUT_STRENGTH_HIGH;
545*723e8462SAnjelique Melendez 				break;
546*723e8462SAnjelique Melendez 			case PMIC_GPIO_STRENGTH_LOW:
547*723e8462SAnjelique Melendez 				pad->strength = PMIC_GPIO_OUT_STRENGTH_LOW;
548*723e8462SAnjelique Melendez 				break;
549*723e8462SAnjelique Melendez 			default:
550eadff302SIvan T. Ivanov 				pad->strength = arg;
551eadff302SIvan T. Ivanov 				break;
552*723e8462SAnjelique Melendez 			}
553*723e8462SAnjelique Melendez 			break;
554d7b5f5ccSFenglin Wu 		case PMIC_GPIO_CONF_ATEST:
555d7b5f5ccSFenglin Wu 			if (!pad->lv_mv_type || arg > 4)
556d7b5f5ccSFenglin Wu 				return -EINVAL;
557d7b5f5ccSFenglin Wu 			pad->atest = arg;
558d7b5f5ccSFenglin Wu 			break;
559d7b5f5ccSFenglin Wu 		case PMIC_GPIO_CONF_ANALOG_PASS:
560d7b5f5ccSFenglin Wu 			if (!pad->lv_mv_type)
561d7b5f5ccSFenglin Wu 				return -EINVAL;
562d7b5f5ccSFenglin Wu 			pad->analog_pass = true;
563d7b5f5ccSFenglin Wu 			break;
564223463fcSFenglin Wu 		case PMIC_GPIO_CONF_DTEST_BUFFER:
565223463fcSFenglin Wu 			if (arg > 4)
566223463fcSFenglin Wu 				return -EINVAL;
567223463fcSFenglin Wu 			pad->dtest_buffer = arg;
568223463fcSFenglin Wu 			break;
569eadff302SIvan T. Ivanov 		default:
570eadff302SIvan T. Ivanov 			return -EINVAL;
571eadff302SIvan T. Ivanov 		}
572eadff302SIvan T. Ivanov 	}
573eadff302SIvan T. Ivanov 
574eadff302SIvan T. Ivanov 	val = pad->power_source << PMIC_GPIO_REG_VIN_SHIFT;
575eadff302SIvan T. Ivanov 
576eadff302SIvan T. Ivanov 	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL, val);
577eadff302SIvan T. Ivanov 	if (ret < 0)
578eadff302SIvan T. Ivanov 		return ret;
579eadff302SIvan T. Ivanov 
580eadff302SIvan T. Ivanov 	val = pad->pullup << PMIC_GPIO_REG_PULL_SHIFT;
581eadff302SIvan T. Ivanov 
582eadff302SIvan T. Ivanov 	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_PULL_CTL, val);
583eadff302SIvan T. Ivanov 	if (ret < 0)
584eadff302SIvan T. Ivanov 		return ret;
585eadff302SIvan T. Ivanov 
586eadff302SIvan T. Ivanov 	val = pad->buffer_type << PMIC_GPIO_REG_OUT_TYPE_SHIFT;
587982df6aeSIvan T. Ivanov 	val |= pad->strength << PMIC_GPIO_REG_OUT_STRENGTH_SHIFT;
588eadff302SIvan T. Ivanov 
589eadff302SIvan T. Ivanov 	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL, val);
590eadff302SIvan T. Ivanov 	if (ret < 0)
591eadff302SIvan T. Ivanov 		return ret;
592eadff302SIvan T. Ivanov 
593223463fcSFenglin Wu 	if (pad->dtest_buffer == 0) {
594223463fcSFenglin Wu 		val = 0;
595223463fcSFenglin Wu 	} else {
596223463fcSFenglin Wu 		if (pad->lv_mv_type) {
597223463fcSFenglin Wu 			val = pad->dtest_buffer - 1;
598223463fcSFenglin Wu 			val |= PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN;
599223463fcSFenglin Wu 		} else {
600223463fcSFenglin Wu 			val = BIT(pad->dtest_buffer - 1);
601223463fcSFenglin Wu 		}
602223463fcSFenglin Wu 	}
603223463fcSFenglin Wu 	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_IN_CTL, val);
604223463fcSFenglin Wu 	if (ret < 0)
605223463fcSFenglin Wu 		return ret;
606223463fcSFenglin Wu 
607d7b5f5ccSFenglin Wu 	if (pad->analog_pass)
608d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_ANALOG_PASS_THRU;
609d7b5f5ccSFenglin Wu 	else if (pad->output_enabled && pad->input_enabled)
610d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT;
611d7b5f5ccSFenglin Wu 	else if (pad->output_enabled)
612d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_OUTPUT;
613eadff302SIvan T. Ivanov 	else
614d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_INPUT;
615eadff302SIvan T. Ivanov 
616d7b5f5ccSFenglin Wu 	if (pad->lv_mv_type) {
617d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
618d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_MODE_CTL, val);
619d7b5f5ccSFenglin Wu 		if (ret < 0)
620d7b5f5ccSFenglin Wu 			return ret;
621d7b5f5ccSFenglin Wu 
622d7b5f5ccSFenglin Wu 		val = pad->atest - 1;
623d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
624d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL, val);
625d7b5f5ccSFenglin Wu 		if (ret < 0)
626d7b5f5ccSFenglin Wu 			return ret;
627d7b5f5ccSFenglin Wu 
628d7b5f5ccSFenglin Wu 		val = pad->out_value
629d7b5f5ccSFenglin Wu 			<< PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT;
630d7b5f5ccSFenglin Wu 		val |= pad->function
631d7b5f5ccSFenglin Wu 			& PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK;
632d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
633d7b5f5ccSFenglin Wu 			PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL, val);
634d7b5f5ccSFenglin Wu 		if (ret < 0)
635d7b5f5ccSFenglin Wu 			return ret;
636d7b5f5ccSFenglin Wu 	} else {
637eadff302SIvan T. Ivanov 		val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT;
638eadff302SIvan T. Ivanov 		val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT;
639eadff302SIvan T. Ivanov 		val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT;
640eadff302SIvan T. Ivanov 
641d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val);
642d7b5f5ccSFenglin Wu 		if (ret < 0)
643d7b5f5ccSFenglin Wu 			return ret;
644d7b5f5ccSFenglin Wu 	}
645d7b5f5ccSFenglin Wu 
6466cb74f44SFenglin Wu 	val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT;
6476cb74f44SFenglin Wu 
6486cb74f44SFenglin Wu 	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_EN_CTL, val);
6496cb74f44SFenglin Wu 
650d7b5f5ccSFenglin Wu 	return ret;
651eadff302SIvan T. Ivanov }
652eadff302SIvan T. Ivanov 
653eadff302SIvan T. Ivanov static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev,
654eadff302SIvan T. Ivanov 				      struct seq_file *s, unsigned pin)
655eadff302SIvan T. Ivanov {
656eadff302SIvan T. Ivanov 	struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev);
657eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad;
658d7b5f5ccSFenglin Wu 	int ret, val, function;
659eadff302SIvan T. Ivanov 
660eadff302SIvan T. Ivanov 	static const char *const biases[] = {
661eadff302SIvan T. Ivanov 		"pull-up 30uA", "pull-up 1.5uA", "pull-up 31.5uA",
662eadff302SIvan T. Ivanov 		"pull-up 1.5uA + 30uA boost", "pull-down 10uA", "no pull"
663eadff302SIvan T. Ivanov 	};
664eadff302SIvan T. Ivanov 	static const char *const buffer_types[] = {
665eadff302SIvan T. Ivanov 		"push-pull", "open-drain", "open-source"
666eadff302SIvan T. Ivanov 	};
667eadff302SIvan T. Ivanov 	static const char *const strengths[] = {
668eadff302SIvan T. Ivanov 		"no", "high", "medium", "low"
669eadff302SIvan T. Ivanov 	};
670eadff302SIvan T. Ivanov 
671eadff302SIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
672eadff302SIvan T. Ivanov 
673eadff302SIvan T. Ivanov 	seq_printf(s, " gpio%-2d:", pin + PMIC_GPIO_PHYSICAL_OFFSET);
674eadff302SIvan T. Ivanov 
675eadff302SIvan T. Ivanov 	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_EN_CTL);
676eadff302SIvan T. Ivanov 
677eadff302SIvan T. Ivanov 	if (val < 0 || !(val >> PMIC_GPIO_REG_MASTER_EN_SHIFT)) {
678eadff302SIvan T. Ivanov 		seq_puts(s, " ---");
679eadff302SIvan T. Ivanov 	} else {
68024a66618SIvan T. Ivanov 		if (pad->input_enabled) {
681eadff302SIvan T. Ivanov 			ret = pmic_gpio_read(state, pad, PMIC_MPP_REG_RT_STS);
68224a66618SIvan T. Ivanov 			if (ret < 0)
68324a66618SIvan T. Ivanov 				return;
68424a66618SIvan T. Ivanov 
685eadff302SIvan T. Ivanov 			ret &= PMIC_MPP_REG_RT_STS_VAL_MASK;
686eadff302SIvan T. Ivanov 			pad->out_value = ret;
687eadff302SIvan T. Ivanov 		}
688d7b5f5ccSFenglin Wu 		/*
689d7b5f5ccSFenglin Wu 		 * For the non-LV/MV subtypes only 2 special functions are
690d7b5f5ccSFenglin Wu 		 * available, offsetting the dtest function values by 2.
691d7b5f5ccSFenglin Wu 		 */
692d7b5f5ccSFenglin Wu 		function = pad->function;
693d7b5f5ccSFenglin Wu 		if (!pad->lv_mv_type &&
694d7b5f5ccSFenglin Wu 				pad->function >= PMIC_GPIO_FUNC_INDEX_FUNC3)
695d7b5f5ccSFenglin Wu 			function += PMIC_GPIO_FUNC_INDEX_DTEST1 -
696d7b5f5ccSFenglin Wu 				PMIC_GPIO_FUNC_INDEX_FUNC3;
697eadff302SIvan T. Ivanov 
698d7b5f5ccSFenglin Wu 		if (pad->analog_pass)
699d7b5f5ccSFenglin Wu 			seq_puts(s, " analog-pass");
700d7b5f5ccSFenglin Wu 		else
701d7b5f5ccSFenglin Wu 			seq_printf(s, " %-4s",
702d7b5f5ccSFenglin Wu 					pad->output_enabled ? "out" : "in");
703202ba5ebSBjorn Andersson 		seq_printf(s, " %-4s", pad->out_value ? "high" : "low");
704d7b5f5ccSFenglin Wu 		seq_printf(s, " %-7s", pmic_gpio_functions[function]);
705eadff302SIvan T. Ivanov 		seq_printf(s, " vin-%d", pad->power_source);
706eadff302SIvan T. Ivanov 		seq_printf(s, " %-27s", biases[pad->pullup]);
707eadff302SIvan T. Ivanov 		seq_printf(s, " %-10s", buffer_types[pad->buffer_type]);
708eadff302SIvan T. Ivanov 		seq_printf(s, " %-7s", strengths[pad->strength]);
709d7b5f5ccSFenglin Wu 		seq_printf(s, " atest-%d", pad->atest);
710223463fcSFenglin Wu 		seq_printf(s, " dtest-%d", pad->dtest_buffer);
711eadff302SIvan T. Ivanov 	}
712eadff302SIvan T. Ivanov }
713eadff302SIvan T. Ivanov 
714eadff302SIvan T. Ivanov static const struct pinconf_ops pmic_gpio_pinconf_ops = {
7157382b623SSoren Brinkmann 	.is_generic			= true,
716eadff302SIvan T. Ivanov 	.pin_config_group_get		= pmic_gpio_config_get,
717eadff302SIvan T. Ivanov 	.pin_config_group_set		= pmic_gpio_config_set,
718eadff302SIvan T. Ivanov 	.pin_config_group_dbg_show	= pmic_gpio_config_dbg_show,
719eadff302SIvan T. Ivanov };
720eadff302SIvan T. Ivanov 
721eadff302SIvan T. Ivanov static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned pin)
722eadff302SIvan T. Ivanov {
723c52d9df1SLinus Walleij 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
724eadff302SIvan T. Ivanov 	unsigned long config;
725eadff302SIvan T. Ivanov 
726eadff302SIvan T. Ivanov 	config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1);
727eadff302SIvan T. Ivanov 
728eadff302SIvan T. Ivanov 	return pmic_gpio_config_set(state->ctrl, pin, &config, 1);
729eadff302SIvan T. Ivanov }
730eadff302SIvan T. Ivanov 
731eadff302SIvan T. Ivanov static int pmic_gpio_direction_output(struct gpio_chip *chip,
732eadff302SIvan T. Ivanov 				      unsigned pin, int val)
733eadff302SIvan T. Ivanov {
734c52d9df1SLinus Walleij 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
735eadff302SIvan T. Ivanov 	unsigned long config;
736eadff302SIvan T. Ivanov 
737eadff302SIvan T. Ivanov 	config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val);
738eadff302SIvan T. Ivanov 
739eadff302SIvan T. Ivanov 	return pmic_gpio_config_set(state->ctrl, pin, &config, 1);
740eadff302SIvan T. Ivanov }
741eadff302SIvan T. Ivanov 
742eadff302SIvan T. Ivanov static int pmic_gpio_get(struct gpio_chip *chip, unsigned pin)
743eadff302SIvan T. Ivanov {
744c52d9df1SLinus Walleij 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
745eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad;
746eadff302SIvan T. Ivanov 	int ret;
747eadff302SIvan T. Ivanov 
748eadff302SIvan T. Ivanov 	pad = state->ctrl->desc->pins[pin].drv_data;
749eadff302SIvan T. Ivanov 
750eadff302SIvan T. Ivanov 	if (!pad->is_enabled)
751eadff302SIvan T. Ivanov 		return -EINVAL;
752eadff302SIvan T. Ivanov 
753eadff302SIvan T. Ivanov 	if (pad->input_enabled) {
754eadff302SIvan T. Ivanov 		ret = pmic_gpio_read(state, pad, PMIC_MPP_REG_RT_STS);
755eadff302SIvan T. Ivanov 		if (ret < 0)
756eadff302SIvan T. Ivanov 			return ret;
757eadff302SIvan T. Ivanov 
758eadff302SIvan T. Ivanov 		pad->out_value = ret & PMIC_MPP_REG_RT_STS_VAL_MASK;
759eadff302SIvan T. Ivanov 	}
760eadff302SIvan T. Ivanov 
76186c1a219SLinus Walleij 	return !!pad->out_value;
762eadff302SIvan T. Ivanov }
763eadff302SIvan T. Ivanov 
764eadff302SIvan T. Ivanov static void pmic_gpio_set(struct gpio_chip *chip, unsigned pin, int value)
765eadff302SIvan T. Ivanov {
766c52d9df1SLinus Walleij 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
767eadff302SIvan T. Ivanov 	unsigned long config;
768eadff302SIvan T. Ivanov 
769eadff302SIvan T. Ivanov 	config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value);
770eadff302SIvan T. Ivanov 
771eadff302SIvan T. Ivanov 	pmic_gpio_config_set(state->ctrl, pin, &config, 1);
772eadff302SIvan T. Ivanov }
773eadff302SIvan T. Ivanov 
774eadff302SIvan T. Ivanov static int pmic_gpio_of_xlate(struct gpio_chip *chip,
775eadff302SIvan T. Ivanov 			      const struct of_phandle_args *gpio_desc,
776eadff302SIvan T. Ivanov 			      u32 *flags)
777eadff302SIvan T. Ivanov {
778eadff302SIvan T. Ivanov 	if (chip->of_gpio_n_cells < 2)
779eadff302SIvan T. Ivanov 		return -EINVAL;
780eadff302SIvan T. Ivanov 
781eadff302SIvan T. Ivanov 	if (flags)
782eadff302SIvan T. Ivanov 		*flags = gpio_desc->args[1];
783eadff302SIvan T. Ivanov 
784eadff302SIvan T. Ivanov 	return gpio_desc->args[0] - PMIC_GPIO_PHYSICAL_OFFSET;
785eadff302SIvan T. Ivanov }
786eadff302SIvan T. Ivanov 
787eadff302SIvan T. Ivanov static void pmic_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
788eadff302SIvan T. Ivanov {
789c52d9df1SLinus Walleij 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
790eadff302SIvan T. Ivanov 	unsigned i;
791eadff302SIvan T. Ivanov 
792eadff302SIvan T. Ivanov 	for (i = 0; i < chip->ngpio; i++) {
793eadff302SIvan T. Ivanov 		pmic_gpio_config_dbg_show(state->ctrl, s, i);
794eadff302SIvan T. Ivanov 		seq_puts(s, "\n");
795eadff302SIvan T. Ivanov 	}
796eadff302SIvan T. Ivanov }
797eadff302SIvan T. Ivanov 
798eadff302SIvan T. Ivanov static const struct gpio_chip pmic_gpio_gpio_template = {
799eadff302SIvan T. Ivanov 	.direction_input	= pmic_gpio_direction_input,
800eadff302SIvan T. Ivanov 	.direction_output	= pmic_gpio_direction_output,
801eadff302SIvan T. Ivanov 	.get			= pmic_gpio_get,
802eadff302SIvan T. Ivanov 	.set			= pmic_gpio_set,
80398c85d58SJonas Gorski 	.request		= gpiochip_generic_request,
80498c85d58SJonas Gorski 	.free			= gpiochip_generic_free,
805eadff302SIvan T. Ivanov 	.of_xlate		= pmic_gpio_of_xlate,
806eadff302SIvan T. Ivanov 	.dbg_show		= pmic_gpio_dbg_show,
807eadff302SIvan T. Ivanov };
808eadff302SIvan T. Ivanov 
809eadff302SIvan T. Ivanov static int pmic_gpio_populate(struct pmic_gpio_state *state,
810eadff302SIvan T. Ivanov 			      struct pmic_gpio_pad *pad)
811eadff302SIvan T. Ivanov {
812eadff302SIvan T. Ivanov 	int type, subtype, val, dir;
813eadff302SIvan T. Ivanov 
814eadff302SIvan T. Ivanov 	type = pmic_gpio_read(state, pad, PMIC_GPIO_REG_TYPE);
815eadff302SIvan T. Ivanov 	if (type < 0)
816eadff302SIvan T. Ivanov 		return type;
817eadff302SIvan T. Ivanov 
818eadff302SIvan T. Ivanov 	if (type != PMIC_GPIO_TYPE) {
819eadff302SIvan T. Ivanov 		dev_err(state->dev, "incorrect block type 0x%x at 0x%x\n",
820eadff302SIvan T. Ivanov 			type, pad->base);
821eadff302SIvan T. Ivanov 		return -ENODEV;
822eadff302SIvan T. Ivanov 	}
823eadff302SIvan T. Ivanov 
824eadff302SIvan T. Ivanov 	subtype = pmic_gpio_read(state, pad, PMIC_GPIO_REG_SUBTYPE);
825eadff302SIvan T. Ivanov 	if (subtype < 0)
826eadff302SIvan T. Ivanov 		return subtype;
827eadff302SIvan T. Ivanov 
828eadff302SIvan T. Ivanov 	switch (subtype) {
829eadff302SIvan T. Ivanov 	case PMIC_GPIO_SUBTYPE_GPIO_4CH:
830eadff302SIvan T. Ivanov 		pad->have_buffer = true;
8311586f556SGustavo A. R. Silva 		fallthrough;
832eadff302SIvan T. Ivanov 	case PMIC_GPIO_SUBTYPE_GPIOC_4CH:
833eadff302SIvan T. Ivanov 		pad->num_sources = 4;
834eadff302SIvan T. Ivanov 		break;
835eadff302SIvan T. Ivanov 	case PMIC_GPIO_SUBTYPE_GPIO_8CH:
836eadff302SIvan T. Ivanov 		pad->have_buffer = true;
8371586f556SGustavo A. R. Silva 		fallthrough;
838eadff302SIvan T. Ivanov 	case PMIC_GPIO_SUBTYPE_GPIOC_8CH:
839eadff302SIvan T. Ivanov 		pad->num_sources = 8;
840eadff302SIvan T. Ivanov 		break;
841d7b5f5ccSFenglin Wu 	case PMIC_GPIO_SUBTYPE_GPIO_LV:
842d7b5f5ccSFenglin Wu 		pad->num_sources = 1;
843d7b5f5ccSFenglin Wu 		pad->have_buffer = true;
844d7b5f5ccSFenglin Wu 		pad->lv_mv_type = true;
845d7b5f5ccSFenglin Wu 		break;
846d7b5f5ccSFenglin Wu 	case PMIC_GPIO_SUBTYPE_GPIO_MV:
847d7b5f5ccSFenglin Wu 		pad->num_sources = 2;
848d7b5f5ccSFenglin Wu 		pad->have_buffer = true;
849d7b5f5ccSFenglin Wu 		pad->lv_mv_type = true;
850d7b5f5ccSFenglin Wu 		break;
8514af95d09SDavid Collins 	case PMIC_GPIO_SUBTYPE_GPIO_LV_VIN2:
8524af95d09SDavid Collins 		pad->num_sources = 2;
8534af95d09SDavid Collins 		pad->have_buffer = true;
8544af95d09SDavid Collins 		pad->lv_mv_type = true;
8554af95d09SDavid Collins 		break;
8564af95d09SDavid Collins 	case PMIC_GPIO_SUBTYPE_GPIO_MV_VIN3:
8574af95d09SDavid Collins 		pad->num_sources = 3;
8584af95d09SDavid Collins 		pad->have_buffer = true;
8594af95d09SDavid Collins 		pad->lv_mv_type = true;
8604af95d09SDavid Collins 		break;
861eadff302SIvan T. Ivanov 	default:
862eadff302SIvan T. Ivanov 		dev_err(state->dev, "unknown GPIO type 0x%x\n", subtype);
863eadff302SIvan T. Ivanov 		return -ENODEV;
864eadff302SIvan T. Ivanov 	}
865eadff302SIvan T. Ivanov 
866d7b5f5ccSFenglin Wu 	if (pad->lv_mv_type) {
867d7b5f5ccSFenglin Wu 		val = pmic_gpio_read(state, pad,
868d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL);
869d7b5f5ccSFenglin Wu 		if (val < 0)
870d7b5f5ccSFenglin Wu 			return val;
871d7b5f5ccSFenglin Wu 
872d7b5f5ccSFenglin Wu 		pad->out_value = !!(val & PMIC_GPIO_LV_MV_OUTPUT_INVERT);
873d7b5f5ccSFenglin Wu 		pad->function = val & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK;
874d7b5f5ccSFenglin Wu 
875d7b5f5ccSFenglin Wu 		val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL);
876d7b5f5ccSFenglin Wu 		if (val < 0)
877d7b5f5ccSFenglin Wu 			return val;
878d7b5f5ccSFenglin Wu 
879d7b5f5ccSFenglin Wu 		dir = val & PMIC_GPIO_REG_LV_MV_MODE_DIR_MASK;
880d7b5f5ccSFenglin Wu 	} else {
881eadff302SIvan T. Ivanov 		val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL);
882eadff302SIvan T. Ivanov 		if (val < 0)
883eadff302SIvan T. Ivanov 			return val;
884eadff302SIvan T. Ivanov 
885eadff302SIvan T. Ivanov 		pad->out_value = val & PMIC_GPIO_REG_MODE_VALUE_SHIFT;
886eadff302SIvan T. Ivanov 
887eadff302SIvan T. Ivanov 		dir = val >> PMIC_GPIO_REG_MODE_DIR_SHIFT;
888eadff302SIvan T. Ivanov 		dir &= PMIC_GPIO_REG_MODE_DIR_MASK;
889d7b5f5ccSFenglin Wu 		pad->function = val >> PMIC_GPIO_REG_MODE_FUNCTION_SHIFT;
890d7b5f5ccSFenglin Wu 		pad->function &= PMIC_GPIO_REG_MODE_FUNCTION_MASK;
891d7b5f5ccSFenglin Wu 	}
892d7b5f5ccSFenglin Wu 
893eadff302SIvan T. Ivanov 	switch (dir) {
894d7b5f5ccSFenglin Wu 	case PMIC_GPIO_MODE_DIGITAL_INPUT:
895eadff302SIvan T. Ivanov 		pad->input_enabled = true;
896eadff302SIvan T. Ivanov 		pad->output_enabled = false;
897eadff302SIvan T. Ivanov 		break;
898d7b5f5ccSFenglin Wu 	case PMIC_GPIO_MODE_DIGITAL_OUTPUT:
899eadff302SIvan T. Ivanov 		pad->input_enabled = false;
900eadff302SIvan T. Ivanov 		pad->output_enabled = true;
901eadff302SIvan T. Ivanov 		break;
902d7b5f5ccSFenglin Wu 	case PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT:
903eadff302SIvan T. Ivanov 		pad->input_enabled = true;
904eadff302SIvan T. Ivanov 		pad->output_enabled = true;
905eadff302SIvan T. Ivanov 		break;
906d7b5f5ccSFenglin Wu 	case PMIC_GPIO_MODE_ANALOG_PASS_THRU:
907d7b5f5ccSFenglin Wu 		if (!pad->lv_mv_type)
908d7b5f5ccSFenglin Wu 			return -ENODEV;
909d7b5f5ccSFenglin Wu 		pad->analog_pass = true;
910d7b5f5ccSFenglin Wu 		break;
911eadff302SIvan T. Ivanov 	default:
912eadff302SIvan T. Ivanov 		dev_err(state->dev, "unknown GPIO direction\n");
913eadff302SIvan T. Ivanov 		return -ENODEV;
914eadff302SIvan T. Ivanov 	}
915eadff302SIvan T. Ivanov 
916eadff302SIvan T. Ivanov 	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL);
917eadff302SIvan T. Ivanov 	if (val < 0)
918eadff302SIvan T. Ivanov 		return val;
919eadff302SIvan T. Ivanov 
920eadff302SIvan T. Ivanov 	pad->power_source = val >> PMIC_GPIO_REG_VIN_SHIFT;
921eadff302SIvan T. Ivanov 	pad->power_source &= PMIC_GPIO_REG_VIN_MASK;
922eadff302SIvan T. Ivanov 
923eadff302SIvan T. Ivanov 	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_PULL_CTL);
924eadff302SIvan T. Ivanov 	if (val < 0)
925eadff302SIvan T. Ivanov 		return val;
926eadff302SIvan T. Ivanov 
927eadff302SIvan T. Ivanov 	pad->pullup = val >> PMIC_GPIO_REG_PULL_SHIFT;
928eadff302SIvan T. Ivanov 	pad->pullup &= PMIC_GPIO_REG_PULL_MASK;
929eadff302SIvan T. Ivanov 
930223463fcSFenglin Wu 	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_IN_CTL);
931223463fcSFenglin Wu 	if (val < 0)
932223463fcSFenglin Wu 		return val;
933223463fcSFenglin Wu 
934223463fcSFenglin Wu 	if (pad->lv_mv_type && (val & PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN))
935223463fcSFenglin Wu 		pad->dtest_buffer =
936223463fcSFenglin Wu 			(val & PMIC_GPIO_LV_MV_DIG_IN_DTEST_SEL_MASK) + 1;
937223463fcSFenglin Wu 	else if (!pad->lv_mv_type)
938223463fcSFenglin Wu 		pad->dtest_buffer = ffs(val);
939223463fcSFenglin Wu 	else
940223463fcSFenglin Wu 		pad->dtest_buffer = 0;
941223463fcSFenglin Wu 
942223463fcSFenglin Wu 	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL);
943223463fcSFenglin Wu 	if (val < 0)
944223463fcSFenglin Wu 		return val;
945223463fcSFenglin Wu 
946eadff302SIvan T. Ivanov 	pad->strength = val >> PMIC_GPIO_REG_OUT_STRENGTH_SHIFT;
947eadff302SIvan T. Ivanov 	pad->strength &= PMIC_GPIO_REG_OUT_STRENGTH_MASK;
948eadff302SIvan T. Ivanov 
949eadff302SIvan T. Ivanov 	pad->buffer_type = val >> PMIC_GPIO_REG_OUT_TYPE_SHIFT;
950eadff302SIvan T. Ivanov 	pad->buffer_type &= PMIC_GPIO_REG_OUT_TYPE_MASK;
951eadff302SIvan T. Ivanov 
952d7b5f5ccSFenglin Wu 	if (pad->lv_mv_type) {
953d7b5f5ccSFenglin Wu 		val = pmic_gpio_read(state, pad,
954d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL);
955d7b5f5ccSFenglin Wu 		if (val < 0)
956d7b5f5ccSFenglin Wu 			return val;
957d7b5f5ccSFenglin Wu 		pad->atest = (val & PMIC_GPIO_LV_MV_ANA_MUX_SEL_MASK) + 1;
958d7b5f5ccSFenglin Wu 	}
959d7b5f5ccSFenglin Wu 
960eadff302SIvan T. Ivanov 	/* Pin could be disabled with PIN_CONFIG_BIAS_HIGH_IMPEDANCE */
961eadff302SIvan T. Ivanov 	pad->is_enabled = true;
962eadff302SIvan T. Ivanov 	return 0;
963eadff302SIvan T. Ivanov }
964eadff302SIvan T. Ivanov 
965ca69e2d1SBrian Masney static int pmic_gpio_domain_translate(struct irq_domain *domain,
966ca69e2d1SBrian Masney 				      struct irq_fwspec *fwspec,
967ca69e2d1SBrian Masney 				      unsigned long *hwirq,
968ca69e2d1SBrian Masney 				      unsigned int *type)
969ca69e2d1SBrian Masney {
970ca69e2d1SBrian Masney 	struct pmic_gpio_state *state = container_of(domain->host_data,
971ca69e2d1SBrian Masney 						     struct pmic_gpio_state,
972ca69e2d1SBrian Masney 						     chip);
973ca69e2d1SBrian Masney 
974dac7da98SBjorn Andersson 	if (fwspec->param_count != 2 ||
975dac7da98SBjorn Andersson 	    fwspec->param[0] < 1 || fwspec->param[0] > state->chip.ngpio)
976ca69e2d1SBrian Masney 		return -EINVAL;
977ca69e2d1SBrian Masney 
978ca69e2d1SBrian Masney 	*hwirq = fwspec->param[0] - PMIC_GPIO_PHYSICAL_OFFSET;
979ca69e2d1SBrian Masney 	*type = fwspec->param[1];
980ca69e2d1SBrian Masney 
981ca69e2d1SBrian Masney 	return 0;
982ca69e2d1SBrian Masney }
983ca69e2d1SBrian Masney 
984821c76c4SBrian Masney static unsigned int pmic_gpio_child_offset_to_irq(struct gpio_chip *chip,
985821c76c4SBrian Masney 						  unsigned int offset)
986ca69e2d1SBrian Masney {
987821c76c4SBrian Masney 	return offset + PMIC_GPIO_PHYSICAL_OFFSET;
988ca69e2d1SBrian Masney }
989ca69e2d1SBrian Masney 
990821c76c4SBrian Masney static int pmic_gpio_child_to_parent_hwirq(struct gpio_chip *chip,
991821c76c4SBrian Masney 					   unsigned int child_hwirq,
992821c76c4SBrian Masney 					   unsigned int child_type,
993821c76c4SBrian Masney 					   unsigned int *parent_hwirq,
994821c76c4SBrian Masney 					   unsigned int *parent_type)
995821c76c4SBrian Masney {
996d36a9773SDavid Collins 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
997d36a9773SDavid Collins 
998d36a9773SDavid Collins 	*parent_hwirq = child_hwirq + state->pid_base;
999821c76c4SBrian Masney 	*parent_type = child_type;
1000821c76c4SBrian Masney 
1001821c76c4SBrian Masney 	return 0;
1002821c76c4SBrian Masney }
1003ca69e2d1SBrian Masney 
100491a29af4SMarc Zyngier static int pmic_gpio_populate_parent_fwspec(struct gpio_chip *chip,
100591a29af4SMarc Zyngier 					    union gpio_irq_fwspec *gfwspec,
1006d36a9773SDavid Collins 					    unsigned int parent_hwirq,
1007d36a9773SDavid Collins 					    unsigned int parent_type)
1008d36a9773SDavid Collins {
1009d36a9773SDavid Collins 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
101091a29af4SMarc Zyngier 	struct irq_fwspec *fwspec = &gfwspec->fwspec;
1011d36a9773SDavid Collins 
1012d36a9773SDavid Collins 	fwspec->fwnode = chip->irq.parent_domain->fwnode;
1013d36a9773SDavid Collins 
1014d36a9773SDavid Collins 	fwspec->param_count = 4;
1015d36a9773SDavid Collins 	fwspec->param[0] = state->usid;
1016d36a9773SDavid Collins 	fwspec->param[1] = parent_hwirq;
1017d36a9773SDavid Collins 	/* param[2] must be left as 0 */
1018d36a9773SDavid Collins 	fwspec->param[3] = parent_type;
1019d36a9773SDavid Collins 
102091a29af4SMarc Zyngier 	return 0;
1021d36a9773SDavid Collins }
1022d36a9773SDavid Collins 
10231a41d1e5SManivannan Sadhasivam static void pmic_gpio_irq_mask(struct irq_data *data)
10241a41d1e5SManivannan Sadhasivam {
10251a41d1e5SManivannan Sadhasivam 	struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
10261a41d1e5SManivannan Sadhasivam 
10271a41d1e5SManivannan Sadhasivam 	irq_chip_mask_parent(data);
10281a41d1e5SManivannan Sadhasivam 	gpiochip_disable_irq(gc, data->hwirq);
10291a41d1e5SManivannan Sadhasivam }
10301a41d1e5SManivannan Sadhasivam 
10311a41d1e5SManivannan Sadhasivam static void pmic_gpio_irq_unmask(struct irq_data *data)
10321a41d1e5SManivannan Sadhasivam {
10331a41d1e5SManivannan Sadhasivam 	struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
10341a41d1e5SManivannan Sadhasivam 
10351a41d1e5SManivannan Sadhasivam 	gpiochip_enable_irq(gc, data->hwirq);
10361a41d1e5SManivannan Sadhasivam 	irq_chip_unmask_parent(data);
10371a41d1e5SManivannan Sadhasivam }
10381a41d1e5SManivannan Sadhasivam 
10391a41d1e5SManivannan Sadhasivam static const struct irq_chip spmi_gpio_irq_chip = {
10401a41d1e5SManivannan Sadhasivam 	.name		= "spmi-gpio",
10411a41d1e5SManivannan Sadhasivam 	.irq_ack	= irq_chip_ack_parent,
10421a41d1e5SManivannan Sadhasivam 	.irq_mask	= pmic_gpio_irq_mask,
10431a41d1e5SManivannan Sadhasivam 	.irq_unmask	= pmic_gpio_irq_unmask,
10441a41d1e5SManivannan Sadhasivam 	.irq_set_type	= irq_chip_set_type_parent,
10451a41d1e5SManivannan Sadhasivam 	.irq_set_wake	= irq_chip_set_wake_parent,
10461a41d1e5SManivannan Sadhasivam 	.flags		= IRQCHIP_IMMUTABLE | IRQCHIP_MASK_ON_SUSPEND,
10471a41d1e5SManivannan Sadhasivam 	GPIOCHIP_IRQ_RESOURCE_HELPERS,
10481a41d1e5SManivannan Sadhasivam };
10491a41d1e5SManivannan Sadhasivam 
1050eadff302SIvan T. Ivanov static int pmic_gpio_probe(struct platform_device *pdev)
1051eadff302SIvan T. Ivanov {
1052ca69e2d1SBrian Masney 	struct irq_domain *parent_domain;
1053ca69e2d1SBrian Masney 	struct device_node *parent_node;
1054eadff302SIvan T. Ivanov 	struct device *dev = &pdev->dev;
1055eadff302SIvan T. Ivanov 	struct pinctrl_pin_desc *pindesc;
1056eadff302SIvan T. Ivanov 	struct pinctrl_desc *pctrldesc;
1057eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad, *pads;
1058eadff302SIvan T. Ivanov 	struct pmic_gpio_state *state;
1059821c76c4SBrian Masney 	struct gpio_irq_chip *girq;
1060d36a9773SDavid Collins 	const struct spmi_device *parent_spmi_dev;
1061eadff302SIvan T. Ivanov 	int ret, npins, i;
1062ab4256cfSStephen Boyd 	u32 reg;
1063eadff302SIvan T. Ivanov 
1064ab4256cfSStephen Boyd 	ret = of_property_read_u32(dev->of_node, "reg", &reg);
1065eadff302SIvan T. Ivanov 	if (ret < 0) {
1066ab4256cfSStephen Boyd 		dev_err(dev, "missing base address");
1067eadff302SIvan T. Ivanov 		return ret;
1068eadff302SIvan T. Ivanov 	}
1069eadff302SIvan T. Ivanov 
1070cfacef37SBrian Masney 	npins = (uintptr_t) device_get_match_data(&pdev->dev);
1071eadff302SIvan T. Ivanov 
1072eadff302SIvan T. Ivanov 	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
1073eadff302SIvan T. Ivanov 	if (!state)
1074eadff302SIvan T. Ivanov 		return -ENOMEM;
1075eadff302SIvan T. Ivanov 
1076eadff302SIvan T. Ivanov 	platform_set_drvdata(pdev, state);
1077eadff302SIvan T. Ivanov 
1078eadff302SIvan T. Ivanov 	state->dev = &pdev->dev;
1079eadff302SIvan T. Ivanov 	state->map = dev_get_regmap(dev->parent, NULL);
1080d36a9773SDavid Collins 	parent_spmi_dev = to_spmi_device(dev->parent);
1081d36a9773SDavid Collins 	state->usid = parent_spmi_dev->usid;
1082d36a9773SDavid Collins 	state->pid_base = reg >> 8;
1083eadff302SIvan T. Ivanov 
1084eadff302SIvan T. Ivanov 	pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
1085eadff302SIvan T. Ivanov 	if (!pindesc)
1086eadff302SIvan T. Ivanov 		return -ENOMEM;
1087eadff302SIvan T. Ivanov 
1088eadff302SIvan T. Ivanov 	pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
1089eadff302SIvan T. Ivanov 	if (!pads)
1090eadff302SIvan T. Ivanov 		return -ENOMEM;
1091eadff302SIvan T. Ivanov 
1092eadff302SIvan T. Ivanov 	pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL);
1093eadff302SIvan T. Ivanov 	if (!pctrldesc)
1094eadff302SIvan T. Ivanov 		return -ENOMEM;
1095eadff302SIvan T. Ivanov 
1096eadff302SIvan T. Ivanov 	pctrldesc->pctlops = &pmic_gpio_pinctrl_ops;
1097eadff302SIvan T. Ivanov 	pctrldesc->pmxops = &pmic_gpio_pinmux_ops;
1098eadff302SIvan T. Ivanov 	pctrldesc->confops = &pmic_gpio_pinconf_ops;
1099eadff302SIvan T. Ivanov 	pctrldesc->owner = THIS_MODULE;
1100eadff302SIvan T. Ivanov 	pctrldesc->name = dev_name(dev);
1101eadff302SIvan T. Ivanov 	pctrldesc->pins = pindesc;
1102eadff302SIvan T. Ivanov 	pctrldesc->npins = npins;
1103f684e4acSLinus Walleij 	pctrldesc->num_custom_params = ARRAY_SIZE(pmic_gpio_bindings);
1104f684e4acSLinus Walleij 	pctrldesc->custom_params = pmic_gpio_bindings;
11054f06266aSArnd Bergmann #ifdef CONFIG_DEBUG_FS
1106f684e4acSLinus Walleij 	pctrldesc->custom_conf_items = pmic_conf_items;
11074f06266aSArnd Bergmann #endif
1108eadff302SIvan T. Ivanov 
1109eadff302SIvan T. Ivanov 	for (i = 0; i < npins; i++, pindesc++) {
1110eadff302SIvan T. Ivanov 		pad = &pads[i];
1111eadff302SIvan T. Ivanov 		pindesc->drv_data = pad;
1112eadff302SIvan T. Ivanov 		pindesc->number = i;
1113eadff302SIvan T. Ivanov 		pindesc->name = pmic_gpio_groups[i];
1114eadff302SIvan T. Ivanov 
1115ab4256cfSStephen Boyd 		pad->base = reg + i * PMIC_GPIO_ADDRESS_RANGE;
1116eadff302SIvan T. Ivanov 
1117eadff302SIvan T. Ivanov 		ret = pmic_gpio_populate(state, pad);
1118eadff302SIvan T. Ivanov 		if (ret < 0)
1119eadff302SIvan T. Ivanov 			return ret;
1120eadff302SIvan T. Ivanov 	}
1121eadff302SIvan T. Ivanov 
1122eadff302SIvan T. Ivanov 	state->chip = pmic_gpio_gpio_template;
112358383c78SLinus Walleij 	state->chip.parent = dev;
1124eadff302SIvan T. Ivanov 	state->chip.base = -1;
1125eadff302SIvan T. Ivanov 	state->chip.ngpio = npins;
1126eadff302SIvan T. Ivanov 	state->chip.label = dev_name(dev);
1127eadff302SIvan T. Ivanov 	state->chip.of_gpio_n_cells = 2;
1128eadff302SIvan T. Ivanov 	state->chip.can_sleep = false;
1129eadff302SIvan T. Ivanov 
1130b46ddfe6SLaxman Dewangan 	state->ctrl = devm_pinctrl_register(dev, pctrldesc, state);
1131323de9efSMasahiro Yamada 	if (IS_ERR(state->ctrl))
1132323de9efSMasahiro Yamada 		return PTR_ERR(state->ctrl);
1133eadff302SIvan T. Ivanov 
1134ca69e2d1SBrian Masney 	parent_node = of_irq_find_parent(state->dev->of_node);
1135ca69e2d1SBrian Masney 	if (!parent_node)
1136ca69e2d1SBrian Masney 		return -ENXIO;
1137ca69e2d1SBrian Masney 
1138ca69e2d1SBrian Masney 	parent_domain = irq_find_host(parent_node);
1139ca69e2d1SBrian Masney 	of_node_put(parent_node);
1140ca69e2d1SBrian Masney 	if (!parent_domain)
1141ca69e2d1SBrian Masney 		return -ENXIO;
1142ca69e2d1SBrian Masney 
1143821c76c4SBrian Masney 	girq = &state->chip.irq;
11441a41d1e5SManivannan Sadhasivam 	gpio_irq_chip_set_chip(girq, &spmi_gpio_irq_chip);
1145821c76c4SBrian Masney 	girq->default_type = IRQ_TYPE_NONE;
1146821c76c4SBrian Masney 	girq->handler = handle_level_irq;
1147821c76c4SBrian Masney 	girq->fwnode = of_node_to_fwnode(state->dev->of_node);
1148821c76c4SBrian Masney 	girq->parent_domain = parent_domain;
1149821c76c4SBrian Masney 	girq->child_to_parent_hwirq = pmic_gpio_child_to_parent_hwirq;
1150d36a9773SDavid Collins 	girq->populate_parent_alloc_arg = pmic_gpio_populate_parent_fwspec;
1151821c76c4SBrian Masney 	girq->child_offset_to_irq = pmic_gpio_child_offset_to_irq;
1152821c76c4SBrian Masney 	girq->child_irq_domain_ops.translate = pmic_gpio_domain_translate;
1153ca69e2d1SBrian Masney 
1154c52d9df1SLinus Walleij 	ret = gpiochip_add_data(&state->chip, state);
1155eadff302SIvan T. Ivanov 	if (ret) {
1156eadff302SIvan T. Ivanov 		dev_err(state->dev, "can't add gpio chip\n");
1157821c76c4SBrian Masney 		return ret;
1158eadff302SIvan T. Ivanov 	}
1159eadff302SIvan T. Ivanov 
1160149a9604SBrian Masney 	/*
1161149a9604SBrian Masney 	 * For DeviceTree-supported systems, the gpio core checks the
1162149a9604SBrian Masney 	 * pinctrl's device node for the "gpio-ranges" property.
1163149a9604SBrian Masney 	 * If it is present, it takes care of adding the pin ranges
1164149a9604SBrian Masney 	 * for the driver. In this case the driver can skip ahead.
1165149a9604SBrian Masney 	 *
1166149a9604SBrian Masney 	 * In order to remain compatible with older, existing DeviceTree
1167149a9604SBrian Masney 	 * files which don't set the "gpio-ranges" property or systems that
1168149a9604SBrian Masney 	 * utilize ACPI the driver has to call gpiochip_add_pin_range().
1169149a9604SBrian Masney 	 */
1170149a9604SBrian Masney 	if (!of_property_read_bool(dev->of_node, "gpio-ranges")) {
1171149a9604SBrian Masney 		ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0,
1172149a9604SBrian Masney 					     npins);
1173eadff302SIvan T. Ivanov 		if (ret) {
1174eadff302SIvan T. Ivanov 			dev_err(dev, "failed to add pin range\n");
1175eadff302SIvan T. Ivanov 			goto err_range;
1176eadff302SIvan T. Ivanov 		}
1177149a9604SBrian Masney 	}
1178eadff302SIvan T. Ivanov 
1179eadff302SIvan T. Ivanov 	return 0;
1180eadff302SIvan T. Ivanov 
1181eadff302SIvan T. Ivanov err_range:
1182eadff302SIvan T. Ivanov 	gpiochip_remove(&state->chip);
1183eadff302SIvan T. Ivanov 	return ret;
1184eadff302SIvan T. Ivanov }
1185eadff302SIvan T. Ivanov 
1186eadff302SIvan T. Ivanov static int pmic_gpio_remove(struct platform_device *pdev)
1187eadff302SIvan T. Ivanov {
1188eadff302SIvan T. Ivanov 	struct pmic_gpio_state *state = platform_get_drvdata(pdev);
1189eadff302SIvan T. Ivanov 
1190eadff302SIvan T. Ivanov 	gpiochip_remove(&state->chip);
1191eadff302SIvan T. Ivanov 	return 0;
1192eadff302SIvan T. Ivanov }
1193eadff302SIvan T. Ivanov 
1194eadff302SIvan T. Ivanov static const struct of_device_id pmic_gpio_of_match[] = {
1195ef874e03SLoic Poulain 	{ .compatible = "qcom,pm2250-gpio", .data = (void *) 10 },
119617cc38e7SKonrad Dybcio 	/* pm660 has 13 GPIOs with holes on 1, 5, 6, 7, 8 and 10 */
119717cc38e7SKonrad Dybcio 	{ .compatible = "qcom,pm660-gpio", .data = (void *) 13 },
119817cc38e7SKonrad Dybcio 	/* pm660l has 12 GPIOs with holes on 1, 2, 10, 11 and 12 */
119917cc38e7SKonrad Dybcio 	{ .compatible = "qcom,pm660l-gpio", .data = (void *) 12 },
12004d8a768eSMarijn Suijten 	{ .compatible = "qcom,pm6125-gpio", .data = (void *) 9 },
12014afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm6150-gpio", .data = (void *) 10 },
12024afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm6150l-gpio", .data = (void *) 12 },
120383917856SLuca Weiss 	{ .compatible = "qcom,pm6350-gpio", .data = (void *) 9 },
12044afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm7325-gpio", .data = (void *) 10 },
12054afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm8005-gpio", .data = (void *) 4 },
12064afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm8008-gpio", .data = (void *) 2 },
1207f3474383SKonrad Dybcio 	{ .compatible = "qcom,pm8019-gpio", .data = (void *) 6 },
1208d67070c3SVinod Koul 	/* pm8150 has 10 GPIOs with holes on 2, 5, 7 and 8 */
1209d67070c3SVinod Koul 	{ .compatible = "qcom,pm8150-gpio", .data = (void *) 10 },
1210182700f2SBjorn Andersson 	{ .compatible = "qcom,pmc8180-gpio", .data = (void *) 10 },
12119bdacaddSVinod Koul 	/* pm8150b has 12 GPIOs with holes on 3, r and 7 */
12129bdacaddSVinod Koul 	{ .compatible = "qcom,pm8150b-gpio", .data = (void *) 12 },
12132dc889a8SVinod Koul 	/* pm8150l has 12 GPIOs with holes on 7 */
12142dc889a8SVinod Koul 	{ .compatible = "qcom,pm8150l-gpio", .data = (void *) 12 },
1215182700f2SBjorn Andersson 	{ .compatible = "qcom,pmc8180c-gpio", .data = (void *) 12 },
1216eebe11b5SDominik Kobinski 	{ .compatible = "qcom,pm8226-gpio", .data = (void *) 8 },
121757c0a4f0SVinod Koul 	{ .compatible = "qcom,pm8350-gpio", .data = (void *) 10 },
121857c0a4f0SVinod Koul 	{ .compatible = "qcom,pm8350b-gpio", .data = (void *) 8 },
121957c0a4f0SVinod Koul 	{ .compatible = "qcom,pm8350c-gpio", .data = (void *) 9 },
1220168a0abfSDmitry Baryshkov 	{ .compatible = "qcom,pm8450-gpio", .data = (void *) 4 },
12214afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm8916-gpio", .data = (void *) 4 },
12224afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm8941-gpio", .data = (void *) 36 },
12234afc2a0cSBhupesh Sharma 	/* pm8950 has 8 GPIOs with holes on 3 */
12244afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm8950-gpio", .data = (void *) 8 },
12254afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm8994-gpio", .data = (void *) 22 },
12264afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm8998-gpio", .data = (void *) 26 },
12274afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pma8084-gpio", .data = (void *) 22 },
12284afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pmi8950-gpio", .data = (void *) 2 },
12294afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pmi8994-gpio", .data = (void *) 10 },
12304afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pmi8998-gpio", .data = (void *) 14 },
123157c0a4f0SVinod Koul 	{ .compatible = "qcom,pmk8350-gpio", .data = (void *) 4 },
123279e2311cSBhupesh Sharma 	{ .compatible = "qcom,pmm8155au-gpio", .data = (void *) 10 },
12336cd81a86SRobert Marko 	/* pmp8074 has 12 GPIOs with holes on 1 and 12 */
12346cd81a86SRobert Marko 	{ .compatible = "qcom,pmp8074-gpio", .data = (void *) 12 },
123557c0a4f0SVinod Koul 	{ .compatible = "qcom,pmr735a-gpio", .data = (void *) 4 },
123657c0a4f0SVinod Koul 	{ .compatible = "qcom,pmr735b-gpio", .data = (void *) 4 },
12374afc2a0cSBhupesh Sharma 	/* pms405 has 12 GPIOs with holes on 1, 9, and 10 */
12384afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pms405-gpio", .data = (void *) 12 },
1239ceb58de4SVinod Koul 	/* pmx55 has 11 GPIOs with holes on 3, 7, 10, 11 */
1240ceb58de4SVinod Koul 	{ .compatible = "qcom,pmx55-gpio", .data = (void *) 11 },
1241203638fdSRohit Agarwal 	{ .compatible = "qcom,pmx65-gpio", .data = (void *) 16 },
1242eadff302SIvan T. Ivanov 	{ },
1243eadff302SIvan T. Ivanov };
1244eadff302SIvan T. Ivanov 
1245eadff302SIvan T. Ivanov MODULE_DEVICE_TABLE(of, pmic_gpio_of_match);
1246eadff302SIvan T. Ivanov 
1247eadff302SIvan T. Ivanov static struct platform_driver pmic_gpio_driver = {
1248eadff302SIvan T. Ivanov 	.driver = {
1249eadff302SIvan T. Ivanov 		   .name = "qcom-spmi-gpio",
1250eadff302SIvan T. Ivanov 		   .of_match_table = pmic_gpio_of_match,
1251eadff302SIvan T. Ivanov 	},
1252eadff302SIvan T. Ivanov 	.probe	= pmic_gpio_probe,
1253eadff302SIvan T. Ivanov 	.remove = pmic_gpio_remove,
1254eadff302SIvan T. Ivanov };
1255eadff302SIvan T. Ivanov 
1256eadff302SIvan T. Ivanov module_platform_driver(pmic_gpio_driver);
1257eadff302SIvan T. Ivanov 
1258eadff302SIvan T. Ivanov MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
1259eadff302SIvan T. Ivanov MODULE_DESCRIPTION("Qualcomm SPMI PMIC GPIO pin control driver");
1260eadff302SIvan T. Ivanov MODULE_ALIAS("platform:qcom-spmi-gpio");
1261eadff302SIvan T. Ivanov MODULE_LICENSE("GPL v2");
1262