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/platform_device.h>
13eadff302SIvan T. Ivanov #include <linux/regmap.h>
14aa9430f8SAndy Shevchenko #include <linux/seq_file.h>
15eadff302SIvan T. Ivanov #include <linux/slab.h>
16d36a9773SDavid Collins #include <linux/spmi.h>
17eadff302SIvan T. Ivanov #include <linux/types.h>
18eadff302SIvan T. Ivanov 
19aa9430f8SAndy Shevchenko #include <linux/pinctrl/pinconf-generic.h>
20aa9430f8SAndy Shevchenko #include <linux/pinctrl/pinconf.h>
21aa9430f8SAndy Shevchenko #include <linux/pinctrl/pinmux.h>
22aa9430f8SAndy Shevchenko 
23eadff302SIvan T. Ivanov #include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
24eadff302SIvan T. Ivanov 
25eadff302SIvan T. Ivanov #include "../core.h"
26eadff302SIvan T. Ivanov #include "../pinctrl-utils.h"
27eadff302SIvan T. Ivanov 
28eadff302SIvan T. Ivanov #define PMIC_GPIO_ADDRESS_RANGE			0x100
29eadff302SIvan T. Ivanov 
30eadff302SIvan T. Ivanov /* type and subtype registers base address offsets */
31eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_TYPE			0x4
32eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_SUBTYPE			0x5
33eadff302SIvan T. Ivanov 
34eadff302SIvan T. Ivanov /* GPIO peripheral type and subtype out_values */
35eadff302SIvan T. Ivanov #define PMIC_GPIO_TYPE				0x10
36eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIO_4CH		0x1
37eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIOC_4CH		0x5
38eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIO_8CH		0x9
39eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIOC_8CH		0xd
40d7b5f5ccSFenglin Wu #define PMIC_GPIO_SUBTYPE_GPIO_LV		0x10
41d7b5f5ccSFenglin Wu #define PMIC_GPIO_SUBTYPE_GPIO_MV		0x11
424af95d09SDavid Collins #define PMIC_GPIO_SUBTYPE_GPIO_LV_VIN2		0x12
434af95d09SDavid Collins #define PMIC_GPIO_SUBTYPE_GPIO_MV_VIN3		0x13
44eadff302SIvan T. Ivanov 
45eadff302SIvan T. Ivanov #define PMIC_MPP_REG_RT_STS			0x10
46eadff302SIvan T. Ivanov #define PMIC_MPP_REG_RT_STS_VAL_MASK		0x1
47eadff302SIvan T. Ivanov 
48eadff302SIvan T. Ivanov /* control register base address offsets */
49eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_CTL			0x40
50eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_VIN_CTL		0x41
51eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_PULL_CTL		0x42
52d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL	0x44
53223463fcSFenglin Wu #define PMIC_GPIO_REG_DIG_IN_CTL		0x43
54eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_OUT_CTL		0x45
55eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_EN_CTL			0x46
56d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL	0x4A
57eadff302SIvan T. Ivanov 
58eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_MODE_CTL */
59eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_VALUE_SHIFT		0x1
60eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_FUNCTION_SHIFT	1
61eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_FUNCTION_MASK	0x7
62eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_DIR_SHIFT		4
63eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_DIR_MASK		0x7
64eadff302SIvan T. Ivanov 
65d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_INPUT		0
66d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_OUTPUT		1
67d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT	2
68d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_ANALOG_PASS_THRU		3
69d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_MODE_DIR_MASK	0x3
70d7b5f5ccSFenglin Wu 
71eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_VIN_CTL */
72eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_VIN_SHIFT			0
73eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_VIN_MASK			0x7
74eadff302SIvan T. Ivanov 
75eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_PULL_CTL */
76eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_PULL_SHIFT		0
77eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_PULL_MASK			0x7
78eadff302SIvan T. Ivanov 
79eadff302SIvan T. Ivanov #define PMIC_GPIO_PULL_DOWN			4
80eadff302SIvan T. Ivanov #define PMIC_GPIO_PULL_DISABLE			5
81eadff302SIvan T. Ivanov 
82d7b5f5ccSFenglin Wu /* PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL for LV/MV */
83d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_INVERT		0x80
84d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT	7
85d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK	0xF
86d7b5f5ccSFenglin Wu 
87223463fcSFenglin Wu /* PMIC_GPIO_REG_DIG_IN_CTL */
88223463fcSFenglin Wu #define PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN		0x80
89223463fcSFenglin Wu #define PMIC_GPIO_LV_MV_DIG_IN_DTEST_SEL_MASK	0x7
90223463fcSFenglin Wu #define PMIC_GPIO_DIG_IN_DTEST_SEL_MASK		0xf
91223463fcSFenglin Wu 
92eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_OUT_CTL */
93eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_STRENGTH_SHIFT	0
94eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_STRENGTH_MASK		0x3
95eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_TYPE_SHIFT		4
96eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_TYPE_MASK		0x3
97eadff302SIvan T. Ivanov 
98eadff302SIvan T. Ivanov /*
99eadff302SIvan T. Ivanov  * Output type - indicates pin should be configured as push-pull,
100eadff302SIvan T. Ivanov  * open drain or open source.
101eadff302SIvan T. Ivanov  */
102eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_CMOS			0
103eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS	1
104eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS	2
105eadff302SIvan T. Ivanov 
106723e8462SAnjelique Melendez #define PMIC_GPIO_OUT_STRENGTH_LOW		1
107723e8462SAnjelique Melendez #define PMIC_GPIO_OUT_STRENGTH_HIGH		3
108723e8462SAnjelique Melendez 
109eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_EN_CTL */
110eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MASTER_EN_SHIFT		7
111eadff302SIvan T. Ivanov 
112eadff302SIvan T. Ivanov #define PMIC_GPIO_PHYSICAL_OFFSET		1
113eadff302SIvan T. Ivanov 
114d7b5f5ccSFenglin Wu /* PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL */
115d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_ANA_MUX_SEL_MASK		0x3
116d7b5f5ccSFenglin Wu 
117eadff302SIvan T. Ivanov /* Qualcomm specific pin configurations */
118eadff302SIvan T. Ivanov #define PMIC_GPIO_CONF_PULL_UP			(PIN_CONFIG_END + 1)
119eadff302SIvan T. Ivanov #define PMIC_GPIO_CONF_STRENGTH			(PIN_CONFIG_END + 2)
120d7b5f5ccSFenglin Wu #define PMIC_GPIO_CONF_ATEST			(PIN_CONFIG_END + 3)
121d7b5f5ccSFenglin Wu #define PMIC_GPIO_CONF_ANALOG_PASS		(PIN_CONFIG_END + 4)
122223463fcSFenglin Wu #define PMIC_GPIO_CONF_DTEST_BUFFER		(PIN_CONFIG_END + 5)
123d7b5f5ccSFenglin Wu 
124d7b5f5ccSFenglin Wu /* The index of each function in pmic_gpio_functions[] array */
125d7b5f5ccSFenglin Wu enum pmic_gpio_func_index {
126d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_NORMAL,
127d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_PAIRED,
128d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_FUNC1,
129d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_FUNC2,
130d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_FUNC3,
131d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_FUNC4,
132d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_DTEST1,
133d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_DTEST2,
134d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_DTEST3,
135d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_DTEST4,
136d7b5f5ccSFenglin Wu };
137eadff302SIvan T. Ivanov 
138eadff302SIvan T. Ivanov /**
139eadff302SIvan T. Ivanov  * struct pmic_gpio_pad - keep current GPIO settings
140eadff302SIvan T. Ivanov  * @base: Address base in SPMI device.
141eadff302SIvan T. Ivanov  * @is_enabled: Set to false when GPIO should be put in high Z state.
142eadff302SIvan T. Ivanov  * @out_value: Cached pin output value
143eadff302SIvan T. Ivanov  * @have_buffer: Set to true if GPIO output could be configured in push-pull,
144eadff302SIvan T. Ivanov  *	open-drain or open-source mode.
145eadff302SIvan T. Ivanov  * @output_enabled: Set to true if GPIO output logic is enabled.
146eadff302SIvan T. Ivanov  * @input_enabled: Set to true if GPIO input buffer logic is enabled.
147d7b5f5ccSFenglin Wu  * @analog_pass: Set to true if GPIO is in analog-pass-through mode.
148d7b5f5ccSFenglin Wu  * @lv_mv_type: Set to true if GPIO subtype is GPIO_LV(0x10) or GPIO_MV(0x11).
149eadff302SIvan T. Ivanov  * @num_sources: Number of power-sources supported by this GPIO.
150eadff302SIvan T. Ivanov  * @power_source: Current power-source used.
151eadff302SIvan T. Ivanov  * @buffer_type: Push-pull, open-drain or open-source.
152eadff302SIvan T. Ivanov  * @pullup: Constant current which flow trough GPIO output buffer.
153eadff302SIvan T. Ivanov  * @strength: No, Low, Medium, High
154eadff302SIvan T. Ivanov  * @function: See pmic_gpio_functions[]
155d7b5f5ccSFenglin Wu  * @atest: the ATEST selection for GPIO analog-pass-through mode
156223463fcSFenglin Wu  * @dtest_buffer: the DTEST buffer selection for digital input mode.
157eadff302SIvan T. Ivanov  */
158eadff302SIvan T. Ivanov struct pmic_gpio_pad {
159eadff302SIvan T. Ivanov 	u16		base;
160eadff302SIvan T. Ivanov 	bool		is_enabled;
161eadff302SIvan T. Ivanov 	bool		out_value;
162eadff302SIvan T. Ivanov 	bool		have_buffer;
163eadff302SIvan T. Ivanov 	bool		output_enabled;
164eadff302SIvan T. Ivanov 	bool		input_enabled;
165d7b5f5ccSFenglin Wu 	bool		analog_pass;
166d7b5f5ccSFenglin Wu 	bool		lv_mv_type;
167eadff302SIvan T. Ivanov 	unsigned int	num_sources;
168eadff302SIvan T. Ivanov 	unsigned int	power_source;
169eadff302SIvan T. Ivanov 	unsigned int	buffer_type;
170eadff302SIvan T. Ivanov 	unsigned int	pullup;
171eadff302SIvan T. Ivanov 	unsigned int	strength;
172eadff302SIvan T. Ivanov 	unsigned int	function;
173d7b5f5ccSFenglin Wu 	unsigned int	atest;
174223463fcSFenglin Wu 	unsigned int	dtest_buffer;
175eadff302SIvan T. Ivanov };
176eadff302SIvan T. Ivanov 
177eadff302SIvan T. Ivanov struct pmic_gpio_state {
178eadff302SIvan T. Ivanov 	struct device	*dev;
179eadff302SIvan T. Ivanov 	struct regmap	*map;
180eadff302SIvan T. Ivanov 	struct pinctrl_dev *ctrl;
181eadff302SIvan T. Ivanov 	struct gpio_chip chip;
182d36a9773SDavid Collins 	u8 usid;
183d36a9773SDavid Collins 	u8 pid_base;
184eadff302SIvan T. Ivanov };
185eadff302SIvan T. Ivanov 
186f684e4acSLinus Walleij static const struct pinconf_generic_params pmic_gpio_bindings[] = {
1877382b623SSoren Brinkmann 	{"qcom,pull-up-strength",	PMIC_GPIO_CONF_PULL_UP,		0},
1887382b623SSoren Brinkmann 	{"qcom,drive-strength",		PMIC_GPIO_CONF_STRENGTH,	0},
189d7b5f5ccSFenglin Wu 	{"qcom,atest",			PMIC_GPIO_CONF_ATEST,		0},
190d7b5f5ccSFenglin Wu 	{"qcom,analog-pass",		PMIC_GPIO_CONF_ANALOG_PASS,	0},
191223463fcSFenglin Wu 	{"qcom,dtest-buffer",           PMIC_GPIO_CONF_DTEST_BUFFER,    0},
192eadff302SIvan T. Ivanov };
193eadff302SIvan T. Ivanov 
1944f06266aSArnd Bergmann #ifdef CONFIG_DEBUG_FS
1957382b623SSoren Brinkmann static const struct pin_config_item pmic_conf_items[ARRAY_SIZE(pmic_gpio_bindings)] = {
1967382b623SSoren Brinkmann 	PCONFDUMP(PMIC_GPIO_CONF_PULL_UP,  "pull up strength", NULL, true),
1977382b623SSoren Brinkmann 	PCONFDUMP(PMIC_GPIO_CONF_STRENGTH, "drive-strength", NULL, true),
198d7b5f5ccSFenglin Wu 	PCONFDUMP(PMIC_GPIO_CONF_ATEST, "atest", NULL, true),
199d7b5f5ccSFenglin Wu 	PCONFDUMP(PMIC_GPIO_CONF_ANALOG_PASS, "analog-pass", NULL, true),
200223463fcSFenglin Wu 	PCONFDUMP(PMIC_GPIO_CONF_DTEST_BUFFER, "dtest-buffer", NULL, true),
201eadff302SIvan T. Ivanov };
2024f06266aSArnd Bergmann #endif
203eadff302SIvan T. Ivanov 
204eadff302SIvan T. Ivanov static const char *const pmic_gpio_groups[] = {
205eadff302SIvan T. Ivanov 	"gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", "gpio8",
206eadff302SIvan T. Ivanov 	"gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", "gpio15",
207eadff302SIvan T. Ivanov 	"gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22",
208eadff302SIvan T. Ivanov 	"gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29",
209eadff302SIvan T. Ivanov 	"gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36",
210eadff302SIvan T. Ivanov };
211eadff302SIvan T. Ivanov 
212eadff302SIvan T. Ivanov static const char *const pmic_gpio_functions[] = {
213d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_NORMAL]	= PMIC_GPIO_FUNC_NORMAL,
214d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_PAIRED]	= PMIC_GPIO_FUNC_PAIRED,
215d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_FUNC1]	= PMIC_GPIO_FUNC_FUNC1,
216d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_FUNC2]	= PMIC_GPIO_FUNC_FUNC2,
217d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_FUNC3]	= PMIC_GPIO_FUNC_FUNC3,
218d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_FUNC4]	= PMIC_GPIO_FUNC_FUNC4,
219d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_DTEST1]	= PMIC_GPIO_FUNC_DTEST1,
220d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_DTEST2]	= PMIC_GPIO_FUNC_DTEST2,
221d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_DTEST3]	= PMIC_GPIO_FUNC_DTEST3,
222d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_DTEST4]	= PMIC_GPIO_FUNC_DTEST4,
223eadff302SIvan T. Ivanov };
224eadff302SIvan T. Ivanov 
pmic_gpio_read(struct pmic_gpio_state * state,struct pmic_gpio_pad * pad,unsigned int addr)225eadff302SIvan T. Ivanov static int pmic_gpio_read(struct pmic_gpio_state *state,
226eadff302SIvan T. Ivanov 			  struct pmic_gpio_pad *pad, unsigned int addr)
227eadff302SIvan T. Ivanov {
228eadff302SIvan T. Ivanov 	unsigned int val;
229eadff302SIvan T. Ivanov 	int ret;
230eadff302SIvan T. Ivanov 
231eadff302SIvan T. Ivanov 	ret = regmap_read(state->map, pad->base + addr, &val);
232eadff302SIvan T. Ivanov 	if (ret < 0)
233eadff302SIvan T. Ivanov 		dev_err(state->dev, "read 0x%x failed\n", addr);
234eadff302SIvan T. Ivanov 	else
235eadff302SIvan T. Ivanov 		ret = val;
236eadff302SIvan T. Ivanov 
237eadff302SIvan T. Ivanov 	return ret;
238eadff302SIvan T. Ivanov }
239eadff302SIvan T. Ivanov 
pmic_gpio_write(struct pmic_gpio_state * state,struct pmic_gpio_pad * pad,unsigned int addr,unsigned int val)240eadff302SIvan T. Ivanov static int pmic_gpio_write(struct pmic_gpio_state *state,
241eadff302SIvan T. Ivanov 			   struct pmic_gpio_pad *pad, unsigned int addr,
242eadff302SIvan T. Ivanov 			   unsigned int val)
243eadff302SIvan T. Ivanov {
244eadff302SIvan T. Ivanov 	int ret;
245eadff302SIvan T. Ivanov 
246eadff302SIvan T. Ivanov 	ret = regmap_write(state->map, pad->base + addr, val);
247eadff302SIvan T. Ivanov 	if (ret < 0)
248eadff302SIvan T. Ivanov 		dev_err(state->dev, "write 0x%x failed\n", addr);
249eadff302SIvan T. Ivanov 
250eadff302SIvan T. Ivanov 	return ret;
251eadff302SIvan T. Ivanov }
252eadff302SIvan T. Ivanov 
pmic_gpio_get_groups_count(struct pinctrl_dev * pctldev)253eadff302SIvan T. Ivanov static int pmic_gpio_get_groups_count(struct pinctrl_dev *pctldev)
254eadff302SIvan T. Ivanov {
255eadff302SIvan T. Ivanov 	/* Every PIN is a group */
256eadff302SIvan T. Ivanov 	return pctldev->desc->npins;
257eadff302SIvan T. Ivanov }
258eadff302SIvan T. Ivanov 
pmic_gpio_get_group_name(struct pinctrl_dev * pctldev,unsigned pin)259eadff302SIvan T. Ivanov static const char *pmic_gpio_get_group_name(struct pinctrl_dev *pctldev,
260eadff302SIvan T. Ivanov 					    unsigned pin)
261eadff302SIvan T. Ivanov {
262eadff302SIvan T. Ivanov 	return pctldev->desc->pins[pin].name;
263eadff302SIvan T. Ivanov }
264eadff302SIvan T. Ivanov 
pmic_gpio_get_group_pins(struct pinctrl_dev * pctldev,unsigned pin,const unsigned ** pins,unsigned * num_pins)265eadff302SIvan T. Ivanov static int pmic_gpio_get_group_pins(struct pinctrl_dev *pctldev, unsigned pin,
266eadff302SIvan T. Ivanov 				    const unsigned **pins, unsigned *num_pins)
267eadff302SIvan T. Ivanov {
268eadff302SIvan T. Ivanov 	*pins = &pctldev->desc->pins[pin].number;
269eadff302SIvan T. Ivanov 	*num_pins = 1;
270eadff302SIvan T. Ivanov 	return 0;
271eadff302SIvan T. Ivanov }
272eadff302SIvan T. Ivanov 
273eadff302SIvan T. Ivanov static const struct pinctrl_ops pmic_gpio_pinctrl_ops = {
274eadff302SIvan T. Ivanov 	.get_groups_count	= pmic_gpio_get_groups_count,
275eadff302SIvan T. Ivanov 	.get_group_name		= pmic_gpio_get_group_name,
276eadff302SIvan T. Ivanov 	.get_group_pins		= pmic_gpio_get_group_pins,
2777382b623SSoren Brinkmann 	.dt_node_to_map		= pinconf_generic_dt_node_to_map_group,
278d32f7fd3SIrina Tirdea 	.dt_free_map		= pinctrl_utils_free_map,
279eadff302SIvan T. Ivanov };
280eadff302SIvan T. Ivanov 
pmic_gpio_get_functions_count(struct pinctrl_dev * pctldev)281eadff302SIvan T. Ivanov static int pmic_gpio_get_functions_count(struct pinctrl_dev *pctldev)
282eadff302SIvan T. Ivanov {
283eadff302SIvan T. Ivanov 	return ARRAY_SIZE(pmic_gpio_functions);
284eadff302SIvan T. Ivanov }
285eadff302SIvan T. Ivanov 
pmic_gpio_get_function_name(struct pinctrl_dev * pctldev,unsigned function)286eadff302SIvan T. Ivanov static const char *pmic_gpio_get_function_name(struct pinctrl_dev *pctldev,
287eadff302SIvan T. Ivanov 					       unsigned function)
288eadff302SIvan T. Ivanov {
289eadff302SIvan T. Ivanov 	return pmic_gpio_functions[function];
290eadff302SIvan T. Ivanov }
291eadff302SIvan T. Ivanov 
pmic_gpio_get_function_groups(struct pinctrl_dev * pctldev,unsigned function,const char * const ** groups,unsigned * const num_qgroups)292eadff302SIvan T. Ivanov static int pmic_gpio_get_function_groups(struct pinctrl_dev *pctldev,
293eadff302SIvan T. Ivanov 					 unsigned function,
294eadff302SIvan T. Ivanov 					 const char *const **groups,
295eadff302SIvan T. Ivanov 					 unsigned *const num_qgroups)
296eadff302SIvan T. Ivanov {
297eadff302SIvan T. Ivanov 	*groups = pmic_gpio_groups;
298eadff302SIvan T. Ivanov 	*num_qgroups = pctldev->desc->npins;
299eadff302SIvan T. Ivanov 	return 0;
300eadff302SIvan T. Ivanov }
301eadff302SIvan T. Ivanov 
pmic_gpio_set_mux(struct pinctrl_dev * pctldev,unsigned function,unsigned pin)302eadff302SIvan T. Ivanov static int pmic_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned function,
303eadff302SIvan T. Ivanov 				unsigned pin)
304eadff302SIvan T. Ivanov {
305eadff302SIvan T. Ivanov 	struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev);
306eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad;
307eadff302SIvan T. Ivanov 	unsigned int val;
308eadff302SIvan T. Ivanov 	int ret;
309eadff302SIvan T. Ivanov 
310d7b5f5ccSFenglin Wu 	if (function > PMIC_GPIO_FUNC_INDEX_DTEST4) {
311d7b5f5ccSFenglin Wu 		pr_err("function: %d is not defined\n", function);
312d7b5f5ccSFenglin Wu 		return -EINVAL;
313d7b5f5ccSFenglin Wu 	}
314d7b5f5ccSFenglin Wu 
315eadff302SIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
316d7b5f5ccSFenglin Wu 	/*
317d7b5f5ccSFenglin Wu 	 * Non-LV/MV subtypes only support 2 special functions,
318d7b5f5ccSFenglin Wu 	 * offsetting the dtestx function values by 2
319d7b5f5ccSFenglin Wu 	 */
320d7b5f5ccSFenglin Wu 	if (!pad->lv_mv_type) {
321d7b5f5ccSFenglin Wu 		if (function == PMIC_GPIO_FUNC_INDEX_FUNC3 ||
322d7b5f5ccSFenglin Wu 				function == PMIC_GPIO_FUNC_INDEX_FUNC4) {
323d7b5f5ccSFenglin Wu 			pr_err("LV/MV subtype doesn't have func3/func4\n");
324d7b5f5ccSFenglin Wu 			return -EINVAL;
325d7b5f5ccSFenglin Wu 		}
326d7b5f5ccSFenglin Wu 		if (function >= PMIC_GPIO_FUNC_INDEX_DTEST1)
327d7b5f5ccSFenglin Wu 			function -= (PMIC_GPIO_FUNC_INDEX_DTEST1 -
328d7b5f5ccSFenglin Wu 					PMIC_GPIO_FUNC_INDEX_FUNC3);
329d7b5f5ccSFenglin Wu 	}
330eadff302SIvan T. Ivanov 
331eadff302SIvan T. Ivanov 	pad->function = function;
332eadff302SIvan T. Ivanov 
333d7b5f5ccSFenglin Wu 	if (pad->analog_pass)
334d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_ANALOG_PASS_THRU;
335d7b5f5ccSFenglin Wu 	else if (pad->output_enabled && pad->input_enabled)
336d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT;
337d7b5f5ccSFenglin Wu 	else if (pad->output_enabled)
338d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_OUTPUT;
339eadff302SIvan T. Ivanov 	else
340d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_INPUT;
341eadff302SIvan T. Ivanov 
342d7b5f5ccSFenglin Wu 	if (pad->lv_mv_type) {
343d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
344d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_MODE_CTL, val);
345d7b5f5ccSFenglin Wu 		if (ret < 0)
346d7b5f5ccSFenglin Wu 			return ret;
347d7b5f5ccSFenglin Wu 
348d7b5f5ccSFenglin Wu 		val = pad->atest - 1;
349d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
350d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL, val);
351d7b5f5ccSFenglin Wu 		if (ret < 0)
352d7b5f5ccSFenglin Wu 			return ret;
353d7b5f5ccSFenglin Wu 
354d7b5f5ccSFenglin Wu 		val = pad->out_value
355d7b5f5ccSFenglin Wu 			<< PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT;
356d7b5f5ccSFenglin Wu 		val |= pad->function
357d7b5f5ccSFenglin Wu 			& PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK;
358d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
359d7b5f5ccSFenglin Wu 			PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL, val);
360d7b5f5ccSFenglin Wu 		if (ret < 0)
361d7b5f5ccSFenglin Wu 			return ret;
362d7b5f5ccSFenglin Wu 	} else {
363dc391502SIvan T. Ivanov 		val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT;
364eadff302SIvan T. Ivanov 		val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT;
365eadff302SIvan T. Ivanov 		val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT;
366eadff302SIvan T. Ivanov 
367eadff302SIvan T. Ivanov 		ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val);
368eadff302SIvan T. Ivanov 		if (ret < 0)
369eadff302SIvan T. Ivanov 			return ret;
370d7b5f5ccSFenglin Wu 	}
371eadff302SIvan T. Ivanov 
372eadff302SIvan T. Ivanov 	val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT;
373eadff302SIvan T. Ivanov 
374eadff302SIvan T. Ivanov 	return pmic_gpio_write(state, pad, PMIC_GPIO_REG_EN_CTL, val);
375eadff302SIvan T. Ivanov }
376eadff302SIvan T. Ivanov 
377eadff302SIvan T. Ivanov static const struct pinmux_ops pmic_gpio_pinmux_ops = {
378eadff302SIvan T. Ivanov 	.get_functions_count	= pmic_gpio_get_functions_count,
379eadff302SIvan T. Ivanov 	.get_function_name	= pmic_gpio_get_function_name,
380eadff302SIvan T. Ivanov 	.get_function_groups	= pmic_gpio_get_function_groups,
381eadff302SIvan T. Ivanov 	.set_mux		= pmic_gpio_set_mux,
382eadff302SIvan T. Ivanov };
383eadff302SIvan T. Ivanov 
pmic_gpio_config_get(struct pinctrl_dev * pctldev,unsigned int pin,unsigned long * config)384eadff302SIvan T. Ivanov static int pmic_gpio_config_get(struct pinctrl_dev *pctldev,
385eadff302SIvan T. Ivanov 				unsigned int pin, unsigned long *config)
386eadff302SIvan T. Ivanov {
387eadff302SIvan T. Ivanov 	unsigned param = pinconf_to_config_param(*config);
388eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad;
389eadff302SIvan T. Ivanov 	unsigned arg;
390eadff302SIvan T. Ivanov 
391eadff302SIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
392eadff302SIvan T. Ivanov 
393eadff302SIvan T. Ivanov 	switch (param) {
394eadff302SIvan T. Ivanov 	case PIN_CONFIG_DRIVE_PUSH_PULL:
3951cf86bc2SDouglas Anderson 		if (pad->buffer_type != PMIC_GPIO_OUT_BUF_CMOS)
3961cf86bc2SDouglas Anderson 			return -EINVAL;
3971cf86bc2SDouglas Anderson 		arg = 1;
398eadff302SIvan T. Ivanov 		break;
399eadff302SIvan T. Ivanov 	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
4001cf86bc2SDouglas Anderson 		if (pad->buffer_type != PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS)
4011cf86bc2SDouglas Anderson 			return -EINVAL;
4021cf86bc2SDouglas Anderson 		arg = 1;
403eadff302SIvan T. Ivanov 		break;
404eadff302SIvan T. Ivanov 	case PIN_CONFIG_DRIVE_OPEN_SOURCE:
4051cf86bc2SDouglas Anderson 		if (pad->buffer_type != PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS)
4061cf86bc2SDouglas Anderson 			return -EINVAL;
4071cf86bc2SDouglas Anderson 		arg = 1;
408eadff302SIvan T. Ivanov 		break;
409eadff302SIvan T. Ivanov 	case PIN_CONFIG_BIAS_PULL_DOWN:
4101cf86bc2SDouglas Anderson 		if (pad->pullup != PMIC_GPIO_PULL_DOWN)
4111cf86bc2SDouglas Anderson 			return -EINVAL;
4121cf86bc2SDouglas Anderson 		arg = 1;
413eadff302SIvan T. Ivanov 		break;
414eadff302SIvan T. Ivanov 	case PIN_CONFIG_BIAS_DISABLE:
4151cf86bc2SDouglas Anderson 		if (pad->pullup != PMIC_GPIO_PULL_DISABLE)
4161cf86bc2SDouglas Anderson 			return -EINVAL;
4171cf86bc2SDouglas Anderson 		arg = 1;
418eadff302SIvan T. Ivanov 		break;
419eadff302SIvan T. Ivanov 	case PIN_CONFIG_BIAS_PULL_UP:
4201cf86bc2SDouglas Anderson 		if (pad->pullup != PMIC_GPIO_PULL_UP_30)
4211cf86bc2SDouglas Anderson 			return -EINVAL;
4221cf86bc2SDouglas Anderson 		arg = 1;
423eadff302SIvan T. Ivanov 		break;
424eadff302SIvan T. Ivanov 	case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
4251cf86bc2SDouglas Anderson 		if (pad->is_enabled)
4261cf86bc2SDouglas Anderson 			return -EINVAL;
4271cf86bc2SDouglas Anderson 		arg = 1;
428eadff302SIvan T. Ivanov 		break;
429eadff302SIvan T. Ivanov 	case PIN_CONFIG_POWER_SOURCE:
430eadff302SIvan T. Ivanov 		arg = pad->power_source;
431eadff302SIvan T. Ivanov 		break;
432eadff302SIvan T. Ivanov 	case PIN_CONFIG_INPUT_ENABLE:
4331cf86bc2SDouglas Anderson 		if (!pad->input_enabled)
4341cf86bc2SDouglas Anderson 			return -EINVAL;
4351cf86bc2SDouglas Anderson 		arg = 1;
436eadff302SIvan T. Ivanov 		break;
437727293a8SSubbaraman Narayanamurthy 	case PIN_CONFIG_OUTPUT_ENABLE:
438727293a8SSubbaraman Narayanamurthy 		arg = pad->output_enabled;
439727293a8SSubbaraman Narayanamurthy 		break;
440eadff302SIvan T. Ivanov 	case PIN_CONFIG_OUTPUT:
441eadff302SIvan T. Ivanov 		arg = pad->out_value;
442eadff302SIvan T. Ivanov 		break;
443eadff302SIvan T. Ivanov 	case PMIC_GPIO_CONF_PULL_UP:
444eadff302SIvan T. Ivanov 		arg = pad->pullup;
445eadff302SIvan T. Ivanov 		break;
446eadff302SIvan T. Ivanov 	case PMIC_GPIO_CONF_STRENGTH:
447723e8462SAnjelique Melendez 		switch (pad->strength) {
448723e8462SAnjelique Melendez 		case PMIC_GPIO_OUT_STRENGTH_HIGH:
449723e8462SAnjelique Melendez 			arg = PMIC_GPIO_STRENGTH_HIGH;
450723e8462SAnjelique Melendez 			break;
451723e8462SAnjelique Melendez 		case PMIC_GPIO_OUT_STRENGTH_LOW:
452723e8462SAnjelique Melendez 			arg = PMIC_GPIO_STRENGTH_LOW;
453723e8462SAnjelique Melendez 			break;
454723e8462SAnjelique Melendez 		default:
455eadff302SIvan T. Ivanov 			arg = pad->strength;
456eadff302SIvan T. Ivanov 			break;
457723e8462SAnjelique Melendez 		}
458723e8462SAnjelique Melendez 		break;
459d7b5f5ccSFenglin Wu 	case PMIC_GPIO_CONF_ATEST:
460d7b5f5ccSFenglin Wu 		arg = pad->atest;
461d7b5f5ccSFenglin Wu 		break;
462d7b5f5ccSFenglin Wu 	case PMIC_GPIO_CONF_ANALOG_PASS:
463d7b5f5ccSFenglin Wu 		arg = pad->analog_pass;
464d7b5f5ccSFenglin Wu 		break;
465223463fcSFenglin Wu 	case PMIC_GPIO_CONF_DTEST_BUFFER:
466223463fcSFenglin Wu 		arg = pad->dtest_buffer;
467223463fcSFenglin Wu 		break;
468eadff302SIvan T. Ivanov 	default:
469eadff302SIvan T. Ivanov 		return -EINVAL;
470eadff302SIvan T. Ivanov 	}
471eadff302SIvan T. Ivanov 
472eadff302SIvan T. Ivanov 	*config = pinconf_to_config_packed(param, arg);
473eadff302SIvan T. Ivanov 	return 0;
474eadff302SIvan T. Ivanov }
475eadff302SIvan T. Ivanov 
pmic_gpio_config_set(struct pinctrl_dev * pctldev,unsigned int pin,unsigned long * configs,unsigned nconfs)476eadff302SIvan T. Ivanov static int pmic_gpio_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
477eadff302SIvan T. Ivanov 				unsigned long *configs, unsigned nconfs)
478eadff302SIvan T. Ivanov {
479eadff302SIvan T. Ivanov 	struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev);
480eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad;
481eadff302SIvan T. Ivanov 	unsigned param, arg;
482eadff302SIvan T. Ivanov 	unsigned int val;
483eadff302SIvan T. Ivanov 	int i, ret;
484eadff302SIvan T. Ivanov 
485eadff302SIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
486eadff302SIvan T. Ivanov 
4876cb74f44SFenglin Wu 	pad->is_enabled = true;
488eadff302SIvan T. Ivanov 	for (i = 0; i < nconfs; i++) {
489eadff302SIvan T. Ivanov 		param = pinconf_to_config_param(configs[i]);
490eadff302SIvan T. Ivanov 		arg = pinconf_to_config_argument(configs[i]);
491eadff302SIvan T. Ivanov 
492eadff302SIvan T. Ivanov 		switch (param) {
493eadff302SIvan T. Ivanov 		case PIN_CONFIG_DRIVE_PUSH_PULL:
494eadff302SIvan T. Ivanov 			pad->buffer_type = PMIC_GPIO_OUT_BUF_CMOS;
495eadff302SIvan T. Ivanov 			break;
496eadff302SIvan T. Ivanov 		case PIN_CONFIG_DRIVE_OPEN_DRAIN:
497eadff302SIvan T. Ivanov 			if (!pad->have_buffer)
498eadff302SIvan T. Ivanov 				return -EINVAL;
499eadff302SIvan T. Ivanov 			pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS;
500eadff302SIvan T. Ivanov 			break;
501eadff302SIvan T. Ivanov 		case PIN_CONFIG_DRIVE_OPEN_SOURCE:
502eadff302SIvan T. Ivanov 			if (!pad->have_buffer)
503eadff302SIvan T. Ivanov 				return -EINVAL;
504eadff302SIvan T. Ivanov 			pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS;
505eadff302SIvan T. Ivanov 			break;
506eadff302SIvan T. Ivanov 		case PIN_CONFIG_BIAS_DISABLE:
507eadff302SIvan T. Ivanov 			pad->pullup = PMIC_GPIO_PULL_DISABLE;
508eadff302SIvan T. Ivanov 			break;
509eadff302SIvan T. Ivanov 		case PIN_CONFIG_BIAS_PULL_UP:
510eadff302SIvan T. Ivanov 			pad->pullup = PMIC_GPIO_PULL_UP_30;
511eadff302SIvan T. Ivanov 			break;
512eadff302SIvan T. Ivanov 		case PIN_CONFIG_BIAS_PULL_DOWN:
513eadff302SIvan T. Ivanov 			if (arg)
514eadff302SIvan T. Ivanov 				pad->pullup = PMIC_GPIO_PULL_DOWN;
515eadff302SIvan T. Ivanov 			else
516eadff302SIvan T. Ivanov 				pad->pullup = PMIC_GPIO_PULL_DISABLE;
517eadff302SIvan T. Ivanov 			break;
518eadff302SIvan T. Ivanov 		case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
519eadff302SIvan T. Ivanov 			pad->is_enabled = false;
520eadff302SIvan T. Ivanov 			break;
521eadff302SIvan T. Ivanov 		case PIN_CONFIG_POWER_SOURCE:
5224e83ac4cSFenglin Wu 			if (arg >= pad->num_sources)
523eadff302SIvan T. Ivanov 				return -EINVAL;
524eadff302SIvan T. Ivanov 			pad->power_source = arg;
525eadff302SIvan T. Ivanov 			break;
526eadff302SIvan T. Ivanov 		case PIN_CONFIG_INPUT_ENABLE:
527eadff302SIvan T. Ivanov 			pad->input_enabled = arg ? true : false;
528eadff302SIvan T. Ivanov 			break;
529727293a8SSubbaraman Narayanamurthy 		case PIN_CONFIG_OUTPUT_ENABLE:
530727293a8SSubbaraman Narayanamurthy 			pad->output_enabled = arg ? true : false;
531727293a8SSubbaraman Narayanamurthy 			break;
532eadff302SIvan T. Ivanov 		case PIN_CONFIG_OUTPUT:
533eadff302SIvan T. Ivanov 			pad->output_enabled = true;
534eadff302SIvan T. Ivanov 			pad->out_value = arg;
535eadff302SIvan T. Ivanov 			break;
536eadff302SIvan T. Ivanov 		case PMIC_GPIO_CONF_PULL_UP:
537eadff302SIvan T. Ivanov 			if (arg > PMIC_GPIO_PULL_UP_1P5_30)
538eadff302SIvan T. Ivanov 				return -EINVAL;
539eadff302SIvan T. Ivanov 			pad->pullup = arg;
540eadff302SIvan T. Ivanov 			break;
541eadff302SIvan T. Ivanov 		case PMIC_GPIO_CONF_STRENGTH:
542eadff302SIvan T. Ivanov 			if (arg > PMIC_GPIO_STRENGTH_LOW)
543eadff302SIvan T. Ivanov 				return -EINVAL;
544723e8462SAnjelique Melendez 			switch (arg) {
545723e8462SAnjelique Melendez 			case PMIC_GPIO_STRENGTH_HIGH:
546723e8462SAnjelique Melendez 				pad->strength = PMIC_GPIO_OUT_STRENGTH_HIGH;
547723e8462SAnjelique Melendez 				break;
548723e8462SAnjelique Melendez 			case PMIC_GPIO_STRENGTH_LOW:
549723e8462SAnjelique Melendez 				pad->strength = PMIC_GPIO_OUT_STRENGTH_LOW;
550723e8462SAnjelique Melendez 				break;
551723e8462SAnjelique Melendez 			default:
552eadff302SIvan T. Ivanov 				pad->strength = arg;
553eadff302SIvan T. Ivanov 				break;
554723e8462SAnjelique Melendez 			}
555723e8462SAnjelique Melendez 			break;
556d7b5f5ccSFenglin Wu 		case PMIC_GPIO_CONF_ATEST:
557d7b5f5ccSFenglin Wu 			if (!pad->lv_mv_type || arg > 4)
558d7b5f5ccSFenglin Wu 				return -EINVAL;
559d7b5f5ccSFenglin Wu 			pad->atest = arg;
560d7b5f5ccSFenglin Wu 			break;
561d7b5f5ccSFenglin Wu 		case PMIC_GPIO_CONF_ANALOG_PASS:
562d7b5f5ccSFenglin Wu 			if (!pad->lv_mv_type)
563d7b5f5ccSFenglin Wu 				return -EINVAL;
564d7b5f5ccSFenglin Wu 			pad->analog_pass = true;
565d7b5f5ccSFenglin Wu 			break;
566223463fcSFenglin Wu 		case PMIC_GPIO_CONF_DTEST_BUFFER:
567223463fcSFenglin Wu 			if (arg > 4)
568223463fcSFenglin Wu 				return -EINVAL;
569223463fcSFenglin Wu 			pad->dtest_buffer = arg;
570223463fcSFenglin Wu 			break;
571eadff302SIvan T. Ivanov 		default:
572eadff302SIvan T. Ivanov 			return -EINVAL;
573eadff302SIvan T. Ivanov 		}
574eadff302SIvan T. Ivanov 	}
575eadff302SIvan T. Ivanov 
576eadff302SIvan T. Ivanov 	val = pad->power_source << PMIC_GPIO_REG_VIN_SHIFT;
577eadff302SIvan T. Ivanov 
578eadff302SIvan T. Ivanov 	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL, val);
579eadff302SIvan T. Ivanov 	if (ret < 0)
580eadff302SIvan T. Ivanov 		return ret;
581eadff302SIvan T. Ivanov 
582eadff302SIvan T. Ivanov 	val = pad->pullup << PMIC_GPIO_REG_PULL_SHIFT;
583eadff302SIvan T. Ivanov 
584eadff302SIvan T. Ivanov 	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_PULL_CTL, val);
585eadff302SIvan T. Ivanov 	if (ret < 0)
586eadff302SIvan T. Ivanov 		return ret;
587eadff302SIvan T. Ivanov 
588eadff302SIvan T. Ivanov 	val = pad->buffer_type << PMIC_GPIO_REG_OUT_TYPE_SHIFT;
589982df6aeSIvan T. Ivanov 	val |= pad->strength << PMIC_GPIO_REG_OUT_STRENGTH_SHIFT;
590eadff302SIvan T. Ivanov 
591eadff302SIvan T. Ivanov 	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL, val);
592eadff302SIvan T. Ivanov 	if (ret < 0)
593eadff302SIvan T. Ivanov 		return ret;
594eadff302SIvan T. Ivanov 
595223463fcSFenglin Wu 	if (pad->dtest_buffer == 0) {
596223463fcSFenglin Wu 		val = 0;
597223463fcSFenglin Wu 	} else {
598223463fcSFenglin Wu 		if (pad->lv_mv_type) {
599223463fcSFenglin Wu 			val = pad->dtest_buffer - 1;
600223463fcSFenglin Wu 			val |= PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN;
601223463fcSFenglin Wu 		} else {
602223463fcSFenglin Wu 			val = BIT(pad->dtest_buffer - 1);
603223463fcSFenglin Wu 		}
604223463fcSFenglin Wu 	}
605223463fcSFenglin Wu 	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_IN_CTL, val);
606223463fcSFenglin Wu 	if (ret < 0)
607223463fcSFenglin Wu 		return ret;
608223463fcSFenglin Wu 
609d7b5f5ccSFenglin Wu 	if (pad->analog_pass)
610d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_ANALOG_PASS_THRU;
611d7b5f5ccSFenglin Wu 	else if (pad->output_enabled && pad->input_enabled)
612d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT;
613d7b5f5ccSFenglin Wu 	else if (pad->output_enabled)
614d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_OUTPUT;
615eadff302SIvan T. Ivanov 	else
616d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_INPUT;
617eadff302SIvan T. Ivanov 
618d7b5f5ccSFenglin Wu 	if (pad->lv_mv_type) {
619d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
620d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_MODE_CTL, val);
621d7b5f5ccSFenglin Wu 		if (ret < 0)
622d7b5f5ccSFenglin Wu 			return ret;
623d7b5f5ccSFenglin Wu 
624d7b5f5ccSFenglin Wu 		val = pad->atest - 1;
625d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
626d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL, val);
627d7b5f5ccSFenglin Wu 		if (ret < 0)
628d7b5f5ccSFenglin Wu 			return ret;
629d7b5f5ccSFenglin Wu 
630d7b5f5ccSFenglin Wu 		val = pad->out_value
631d7b5f5ccSFenglin Wu 			<< PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT;
632d7b5f5ccSFenglin Wu 		val |= pad->function
633d7b5f5ccSFenglin Wu 			& PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK;
634d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
635d7b5f5ccSFenglin Wu 			PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL, val);
636d7b5f5ccSFenglin Wu 		if (ret < 0)
637d7b5f5ccSFenglin Wu 			return ret;
638d7b5f5ccSFenglin Wu 	} else {
639eadff302SIvan T. Ivanov 		val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT;
640eadff302SIvan T. Ivanov 		val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT;
641eadff302SIvan T. Ivanov 		val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT;
642eadff302SIvan T. Ivanov 
643d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val);
644d7b5f5ccSFenglin Wu 		if (ret < 0)
645d7b5f5ccSFenglin Wu 			return ret;
646d7b5f5ccSFenglin Wu 	}
647d7b5f5ccSFenglin Wu 
6486cb74f44SFenglin Wu 	val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT;
6496cb74f44SFenglin Wu 
6506cb74f44SFenglin Wu 	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_EN_CTL, val);
6516cb74f44SFenglin Wu 
652d7b5f5ccSFenglin Wu 	return ret;
653eadff302SIvan T. Ivanov }
654eadff302SIvan T. Ivanov 
pmic_gpio_config_dbg_show(struct pinctrl_dev * pctldev,struct seq_file * s,unsigned pin)655eadff302SIvan T. Ivanov static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev,
656eadff302SIvan T. Ivanov 				      struct seq_file *s, unsigned pin)
657eadff302SIvan T. Ivanov {
658eadff302SIvan T. Ivanov 	struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev);
659eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad;
660d7b5f5ccSFenglin Wu 	int ret, val, function;
661eadff302SIvan T. Ivanov 
662eadff302SIvan T. Ivanov 	static const char *const biases[] = {
663eadff302SIvan T. Ivanov 		"pull-up 30uA", "pull-up 1.5uA", "pull-up 31.5uA",
664eadff302SIvan T. Ivanov 		"pull-up 1.5uA + 30uA boost", "pull-down 10uA", "no pull"
665eadff302SIvan T. Ivanov 	};
666eadff302SIvan T. Ivanov 	static const char *const buffer_types[] = {
667eadff302SIvan T. Ivanov 		"push-pull", "open-drain", "open-source"
668eadff302SIvan T. Ivanov 	};
669eadff302SIvan T. Ivanov 	static const char *const strengths[] = {
670eadff302SIvan T. Ivanov 		"no", "high", "medium", "low"
671eadff302SIvan T. Ivanov 	};
672eadff302SIvan T. Ivanov 
673eadff302SIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
674eadff302SIvan T. Ivanov 
675eadff302SIvan T. Ivanov 	seq_printf(s, " gpio%-2d:", pin + PMIC_GPIO_PHYSICAL_OFFSET);
676eadff302SIvan T. Ivanov 
677eadff302SIvan T. Ivanov 	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_EN_CTL);
678eadff302SIvan T. Ivanov 
679eadff302SIvan T. Ivanov 	if (val < 0 || !(val >> PMIC_GPIO_REG_MASTER_EN_SHIFT)) {
680eadff302SIvan T. Ivanov 		seq_puts(s, " ---");
681eadff302SIvan T. Ivanov 	} else {
68224a66618SIvan T. Ivanov 		if (pad->input_enabled) {
683eadff302SIvan T. Ivanov 			ret = pmic_gpio_read(state, pad, PMIC_MPP_REG_RT_STS);
68424a66618SIvan T. Ivanov 			if (ret < 0)
68524a66618SIvan T. Ivanov 				return;
68624a66618SIvan T. Ivanov 
687eadff302SIvan T. Ivanov 			ret &= PMIC_MPP_REG_RT_STS_VAL_MASK;
688eadff302SIvan T. Ivanov 			pad->out_value = ret;
689eadff302SIvan T. Ivanov 		}
690d7b5f5ccSFenglin Wu 		/*
691d7b5f5ccSFenglin Wu 		 * For the non-LV/MV subtypes only 2 special functions are
692d7b5f5ccSFenglin Wu 		 * available, offsetting the dtest function values by 2.
693d7b5f5ccSFenglin Wu 		 */
694d7b5f5ccSFenglin Wu 		function = pad->function;
695d7b5f5ccSFenglin Wu 		if (!pad->lv_mv_type &&
696d7b5f5ccSFenglin Wu 				pad->function >= PMIC_GPIO_FUNC_INDEX_FUNC3)
697d7b5f5ccSFenglin Wu 			function += PMIC_GPIO_FUNC_INDEX_DTEST1 -
698d7b5f5ccSFenglin Wu 				PMIC_GPIO_FUNC_INDEX_FUNC3;
699eadff302SIvan T. Ivanov 
700d7b5f5ccSFenglin Wu 		if (pad->analog_pass)
701d7b5f5ccSFenglin Wu 			seq_puts(s, " analog-pass");
702d7b5f5ccSFenglin Wu 		else
703d7b5f5ccSFenglin Wu 			seq_printf(s, " %-4s",
704d7b5f5ccSFenglin Wu 					pad->output_enabled ? "out" : "in");
705202ba5ebSBjorn Andersson 		seq_printf(s, " %-4s", pad->out_value ? "high" : "low");
706d7b5f5ccSFenglin Wu 		seq_printf(s, " %-7s", pmic_gpio_functions[function]);
707eadff302SIvan T. Ivanov 		seq_printf(s, " vin-%d", pad->power_source);
708eadff302SIvan T. Ivanov 		seq_printf(s, " %-27s", biases[pad->pullup]);
709eadff302SIvan T. Ivanov 		seq_printf(s, " %-10s", buffer_types[pad->buffer_type]);
710eadff302SIvan T. Ivanov 		seq_printf(s, " %-7s", strengths[pad->strength]);
711d7b5f5ccSFenglin Wu 		seq_printf(s, " atest-%d", pad->atest);
712223463fcSFenglin Wu 		seq_printf(s, " dtest-%d", pad->dtest_buffer);
713eadff302SIvan T. Ivanov 	}
714eadff302SIvan T. Ivanov }
715eadff302SIvan T. Ivanov 
716eadff302SIvan T. Ivanov static const struct pinconf_ops pmic_gpio_pinconf_ops = {
7177382b623SSoren Brinkmann 	.is_generic			= true,
718eadff302SIvan T. Ivanov 	.pin_config_group_get		= pmic_gpio_config_get,
719eadff302SIvan T. Ivanov 	.pin_config_group_set		= pmic_gpio_config_set,
720eadff302SIvan T. Ivanov 	.pin_config_group_dbg_show	= pmic_gpio_config_dbg_show,
721eadff302SIvan T. Ivanov };
722eadff302SIvan T. Ivanov 
pmic_gpio_direction_input(struct gpio_chip * chip,unsigned pin)723eadff302SIvan T. Ivanov static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned pin)
724eadff302SIvan T. Ivanov {
725c52d9df1SLinus Walleij 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
726eadff302SIvan T. Ivanov 	unsigned long config;
727eadff302SIvan T. Ivanov 
728eadff302SIvan T. Ivanov 	config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1);
729eadff302SIvan T. Ivanov 
730eadff302SIvan T. Ivanov 	return pmic_gpio_config_set(state->ctrl, pin, &config, 1);
731eadff302SIvan T. Ivanov }
732eadff302SIvan T. Ivanov 
pmic_gpio_direction_output(struct gpio_chip * chip,unsigned pin,int val)733eadff302SIvan T. Ivanov static int pmic_gpio_direction_output(struct gpio_chip *chip,
734eadff302SIvan T. Ivanov 				      unsigned pin, int val)
735eadff302SIvan T. Ivanov {
736c52d9df1SLinus Walleij 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
737eadff302SIvan T. Ivanov 	unsigned long config;
738eadff302SIvan T. Ivanov 
739eadff302SIvan T. Ivanov 	config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val);
740eadff302SIvan T. Ivanov 
741eadff302SIvan T. Ivanov 	return pmic_gpio_config_set(state->ctrl, pin, &config, 1);
742eadff302SIvan T. Ivanov }
743eadff302SIvan T. Ivanov 
pmic_gpio_get(struct gpio_chip * chip,unsigned pin)744eadff302SIvan T. Ivanov static int pmic_gpio_get(struct gpio_chip *chip, unsigned pin)
745eadff302SIvan T. Ivanov {
746c52d9df1SLinus Walleij 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
747eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad;
748eadff302SIvan T. Ivanov 	int ret;
749eadff302SIvan T. Ivanov 
750eadff302SIvan T. Ivanov 	pad = state->ctrl->desc->pins[pin].drv_data;
751eadff302SIvan T. Ivanov 
752eadff302SIvan T. Ivanov 	if (!pad->is_enabled)
753eadff302SIvan T. Ivanov 		return -EINVAL;
754eadff302SIvan T. Ivanov 
755eadff302SIvan T. Ivanov 	if (pad->input_enabled) {
756eadff302SIvan T. Ivanov 		ret = pmic_gpio_read(state, pad, PMIC_MPP_REG_RT_STS);
757eadff302SIvan T. Ivanov 		if (ret < 0)
758eadff302SIvan T. Ivanov 			return ret;
759eadff302SIvan T. Ivanov 
760eadff302SIvan T. Ivanov 		pad->out_value = ret & PMIC_MPP_REG_RT_STS_VAL_MASK;
761eadff302SIvan T. Ivanov 	}
762eadff302SIvan T. Ivanov 
76386c1a219SLinus Walleij 	return !!pad->out_value;
764eadff302SIvan T. Ivanov }
765eadff302SIvan T. Ivanov 
pmic_gpio_set(struct gpio_chip * chip,unsigned pin,int value)766eadff302SIvan T. Ivanov static void pmic_gpio_set(struct gpio_chip *chip, unsigned pin, int value)
767eadff302SIvan T. Ivanov {
768c52d9df1SLinus Walleij 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
769eadff302SIvan T. Ivanov 	unsigned long config;
770eadff302SIvan T. Ivanov 
771eadff302SIvan T. Ivanov 	config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value);
772eadff302SIvan T. Ivanov 
773eadff302SIvan T. Ivanov 	pmic_gpio_config_set(state->ctrl, pin, &config, 1);
774eadff302SIvan T. Ivanov }
775eadff302SIvan T. Ivanov 
pmic_gpio_of_xlate(struct gpio_chip * chip,const struct of_phandle_args * gpio_desc,u32 * flags)776eadff302SIvan T. Ivanov static int pmic_gpio_of_xlate(struct gpio_chip *chip,
777eadff302SIvan T. Ivanov 			      const struct of_phandle_args *gpio_desc,
778eadff302SIvan T. Ivanov 			      u32 *flags)
779eadff302SIvan T. Ivanov {
780eadff302SIvan T. Ivanov 	if (chip->of_gpio_n_cells < 2)
781eadff302SIvan T. Ivanov 		return -EINVAL;
782eadff302SIvan T. Ivanov 
783eadff302SIvan T. Ivanov 	if (flags)
784eadff302SIvan T. Ivanov 		*flags = gpio_desc->args[1];
785eadff302SIvan T. Ivanov 
786eadff302SIvan T. Ivanov 	return gpio_desc->args[0] - PMIC_GPIO_PHYSICAL_OFFSET;
787eadff302SIvan T. Ivanov }
788eadff302SIvan T. Ivanov 
pmic_gpio_dbg_show(struct seq_file * s,struct gpio_chip * chip)789eadff302SIvan T. Ivanov static void pmic_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
790eadff302SIvan T. Ivanov {
791c52d9df1SLinus Walleij 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
792eadff302SIvan T. Ivanov 	unsigned i;
793eadff302SIvan T. Ivanov 
794eadff302SIvan T. Ivanov 	for (i = 0; i < chip->ngpio; i++) {
795eadff302SIvan T. Ivanov 		pmic_gpio_config_dbg_show(state->ctrl, s, i);
796eadff302SIvan T. Ivanov 		seq_puts(s, "\n");
797eadff302SIvan T. Ivanov 	}
798eadff302SIvan T. Ivanov }
799eadff302SIvan T. Ivanov 
800eadff302SIvan T. Ivanov static const struct gpio_chip pmic_gpio_gpio_template = {
801eadff302SIvan T. Ivanov 	.direction_input	= pmic_gpio_direction_input,
802eadff302SIvan T. Ivanov 	.direction_output	= pmic_gpio_direction_output,
803eadff302SIvan T. Ivanov 	.get			= pmic_gpio_get,
804eadff302SIvan T. Ivanov 	.set			= pmic_gpio_set,
80598c85d58SJonas Gorski 	.request		= gpiochip_generic_request,
80698c85d58SJonas Gorski 	.free			= gpiochip_generic_free,
807eadff302SIvan T. Ivanov 	.of_xlate		= pmic_gpio_of_xlate,
808eadff302SIvan T. Ivanov 	.dbg_show		= pmic_gpio_dbg_show,
809eadff302SIvan T. Ivanov };
810eadff302SIvan T. Ivanov 
pmic_gpio_populate(struct pmic_gpio_state * state,struct pmic_gpio_pad * pad)811eadff302SIvan T. Ivanov static int pmic_gpio_populate(struct pmic_gpio_state *state,
812eadff302SIvan T. Ivanov 			      struct pmic_gpio_pad *pad)
813eadff302SIvan T. Ivanov {
814eadff302SIvan T. Ivanov 	int type, subtype, val, dir;
815eadff302SIvan T. Ivanov 
816eadff302SIvan T. Ivanov 	type = pmic_gpio_read(state, pad, PMIC_GPIO_REG_TYPE);
817eadff302SIvan T. Ivanov 	if (type < 0)
818eadff302SIvan T. Ivanov 		return type;
819eadff302SIvan T. Ivanov 
820eadff302SIvan T. Ivanov 	if (type != PMIC_GPIO_TYPE) {
821eadff302SIvan T. Ivanov 		dev_err(state->dev, "incorrect block type 0x%x at 0x%x\n",
822eadff302SIvan T. Ivanov 			type, pad->base);
823eadff302SIvan T. Ivanov 		return -ENODEV;
824eadff302SIvan T. Ivanov 	}
825eadff302SIvan T. Ivanov 
826eadff302SIvan T. Ivanov 	subtype = pmic_gpio_read(state, pad, PMIC_GPIO_REG_SUBTYPE);
827eadff302SIvan T. Ivanov 	if (subtype < 0)
828eadff302SIvan T. Ivanov 		return subtype;
829eadff302SIvan T. Ivanov 
830eadff302SIvan T. Ivanov 	switch (subtype) {
831eadff302SIvan T. Ivanov 	case PMIC_GPIO_SUBTYPE_GPIO_4CH:
832eadff302SIvan T. Ivanov 		pad->have_buffer = true;
8331586f556SGustavo A. R. Silva 		fallthrough;
834eadff302SIvan T. Ivanov 	case PMIC_GPIO_SUBTYPE_GPIOC_4CH:
835eadff302SIvan T. Ivanov 		pad->num_sources = 4;
836eadff302SIvan T. Ivanov 		break;
837eadff302SIvan T. Ivanov 	case PMIC_GPIO_SUBTYPE_GPIO_8CH:
838eadff302SIvan T. Ivanov 		pad->have_buffer = true;
8391586f556SGustavo A. R. Silva 		fallthrough;
840eadff302SIvan T. Ivanov 	case PMIC_GPIO_SUBTYPE_GPIOC_8CH:
841eadff302SIvan T. Ivanov 		pad->num_sources = 8;
842eadff302SIvan T. Ivanov 		break;
843d7b5f5ccSFenglin Wu 	case PMIC_GPIO_SUBTYPE_GPIO_LV:
844d7b5f5ccSFenglin Wu 		pad->num_sources = 1;
845d7b5f5ccSFenglin Wu 		pad->have_buffer = true;
846d7b5f5ccSFenglin Wu 		pad->lv_mv_type = true;
847d7b5f5ccSFenglin Wu 		break;
848d7b5f5ccSFenglin Wu 	case PMIC_GPIO_SUBTYPE_GPIO_MV:
849d7b5f5ccSFenglin Wu 		pad->num_sources = 2;
850d7b5f5ccSFenglin Wu 		pad->have_buffer = true;
851d7b5f5ccSFenglin Wu 		pad->lv_mv_type = true;
852d7b5f5ccSFenglin Wu 		break;
8534af95d09SDavid Collins 	case PMIC_GPIO_SUBTYPE_GPIO_LV_VIN2:
8544af95d09SDavid Collins 		pad->num_sources = 2;
8554af95d09SDavid Collins 		pad->have_buffer = true;
8564af95d09SDavid Collins 		pad->lv_mv_type = true;
8574af95d09SDavid Collins 		break;
8584af95d09SDavid Collins 	case PMIC_GPIO_SUBTYPE_GPIO_MV_VIN3:
8594af95d09SDavid Collins 		pad->num_sources = 3;
8604af95d09SDavid Collins 		pad->have_buffer = true;
8614af95d09SDavid Collins 		pad->lv_mv_type = true;
8624af95d09SDavid Collins 		break;
863eadff302SIvan T. Ivanov 	default:
864eadff302SIvan T. Ivanov 		dev_err(state->dev, "unknown GPIO type 0x%x\n", subtype);
865eadff302SIvan T. Ivanov 		return -ENODEV;
866eadff302SIvan T. Ivanov 	}
867eadff302SIvan T. Ivanov 
868d7b5f5ccSFenglin Wu 	if (pad->lv_mv_type) {
869d7b5f5ccSFenglin Wu 		val = pmic_gpio_read(state, pad,
870d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL);
871d7b5f5ccSFenglin Wu 		if (val < 0)
872d7b5f5ccSFenglin Wu 			return val;
873d7b5f5ccSFenglin Wu 
874d7b5f5ccSFenglin Wu 		pad->out_value = !!(val & PMIC_GPIO_LV_MV_OUTPUT_INVERT);
875d7b5f5ccSFenglin Wu 		pad->function = val & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK;
876d7b5f5ccSFenglin Wu 
877d7b5f5ccSFenglin Wu 		val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL);
878d7b5f5ccSFenglin Wu 		if (val < 0)
879d7b5f5ccSFenglin Wu 			return val;
880d7b5f5ccSFenglin Wu 
881d7b5f5ccSFenglin Wu 		dir = val & PMIC_GPIO_REG_LV_MV_MODE_DIR_MASK;
882d7b5f5ccSFenglin Wu 	} else {
883eadff302SIvan T. Ivanov 		val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL);
884eadff302SIvan T. Ivanov 		if (val < 0)
885eadff302SIvan T. Ivanov 			return val;
886eadff302SIvan T. Ivanov 
887eadff302SIvan T. Ivanov 		pad->out_value = val & PMIC_GPIO_REG_MODE_VALUE_SHIFT;
888eadff302SIvan T. Ivanov 
889eadff302SIvan T. Ivanov 		dir = val >> PMIC_GPIO_REG_MODE_DIR_SHIFT;
890eadff302SIvan T. Ivanov 		dir &= PMIC_GPIO_REG_MODE_DIR_MASK;
891d7b5f5ccSFenglin Wu 		pad->function = val >> PMIC_GPIO_REG_MODE_FUNCTION_SHIFT;
892d7b5f5ccSFenglin Wu 		pad->function &= PMIC_GPIO_REG_MODE_FUNCTION_MASK;
893d7b5f5ccSFenglin Wu 	}
894d7b5f5ccSFenglin Wu 
895eadff302SIvan T. Ivanov 	switch (dir) {
896d7b5f5ccSFenglin Wu 	case PMIC_GPIO_MODE_DIGITAL_INPUT:
897eadff302SIvan T. Ivanov 		pad->input_enabled = true;
898eadff302SIvan T. Ivanov 		pad->output_enabled = false;
899eadff302SIvan T. Ivanov 		break;
900d7b5f5ccSFenglin Wu 	case PMIC_GPIO_MODE_DIGITAL_OUTPUT:
901eadff302SIvan T. Ivanov 		pad->input_enabled = false;
902eadff302SIvan T. Ivanov 		pad->output_enabled = true;
903eadff302SIvan T. Ivanov 		break;
904d7b5f5ccSFenglin Wu 	case PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT:
905eadff302SIvan T. Ivanov 		pad->input_enabled = true;
906eadff302SIvan T. Ivanov 		pad->output_enabled = true;
907eadff302SIvan T. Ivanov 		break;
908d7b5f5ccSFenglin Wu 	case PMIC_GPIO_MODE_ANALOG_PASS_THRU:
909d7b5f5ccSFenglin Wu 		if (!pad->lv_mv_type)
910d7b5f5ccSFenglin Wu 			return -ENODEV;
911d7b5f5ccSFenglin Wu 		pad->analog_pass = true;
912d7b5f5ccSFenglin Wu 		break;
913eadff302SIvan T. Ivanov 	default:
914eadff302SIvan T. Ivanov 		dev_err(state->dev, "unknown GPIO direction\n");
915eadff302SIvan T. Ivanov 		return -ENODEV;
916eadff302SIvan T. Ivanov 	}
917eadff302SIvan T. Ivanov 
918eadff302SIvan T. Ivanov 	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL);
919eadff302SIvan T. Ivanov 	if (val < 0)
920eadff302SIvan T. Ivanov 		return val;
921eadff302SIvan T. Ivanov 
922eadff302SIvan T. Ivanov 	pad->power_source = val >> PMIC_GPIO_REG_VIN_SHIFT;
923eadff302SIvan T. Ivanov 	pad->power_source &= PMIC_GPIO_REG_VIN_MASK;
924eadff302SIvan T. Ivanov 
925eadff302SIvan T. Ivanov 	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_PULL_CTL);
926eadff302SIvan T. Ivanov 	if (val < 0)
927eadff302SIvan T. Ivanov 		return val;
928eadff302SIvan T. Ivanov 
929eadff302SIvan T. Ivanov 	pad->pullup = val >> PMIC_GPIO_REG_PULL_SHIFT;
930eadff302SIvan T. Ivanov 	pad->pullup &= PMIC_GPIO_REG_PULL_MASK;
931eadff302SIvan T. Ivanov 
932223463fcSFenglin Wu 	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_IN_CTL);
933223463fcSFenglin Wu 	if (val < 0)
934223463fcSFenglin Wu 		return val;
935223463fcSFenglin Wu 
936223463fcSFenglin Wu 	if (pad->lv_mv_type && (val & PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN))
937223463fcSFenglin Wu 		pad->dtest_buffer =
938223463fcSFenglin Wu 			(val & PMIC_GPIO_LV_MV_DIG_IN_DTEST_SEL_MASK) + 1;
939223463fcSFenglin Wu 	else if (!pad->lv_mv_type)
940223463fcSFenglin Wu 		pad->dtest_buffer = ffs(val);
941223463fcSFenglin Wu 	else
942223463fcSFenglin Wu 		pad->dtest_buffer = 0;
943223463fcSFenglin Wu 
944223463fcSFenglin Wu 	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL);
945223463fcSFenglin Wu 	if (val < 0)
946223463fcSFenglin Wu 		return val;
947223463fcSFenglin Wu 
948eadff302SIvan T. Ivanov 	pad->strength = val >> PMIC_GPIO_REG_OUT_STRENGTH_SHIFT;
949eadff302SIvan T. Ivanov 	pad->strength &= PMIC_GPIO_REG_OUT_STRENGTH_MASK;
950eadff302SIvan T. Ivanov 
951eadff302SIvan T. Ivanov 	pad->buffer_type = val >> PMIC_GPIO_REG_OUT_TYPE_SHIFT;
952eadff302SIvan T. Ivanov 	pad->buffer_type &= PMIC_GPIO_REG_OUT_TYPE_MASK;
953eadff302SIvan T. Ivanov 
954d7b5f5ccSFenglin Wu 	if (pad->lv_mv_type) {
955d7b5f5ccSFenglin Wu 		val = pmic_gpio_read(state, pad,
956d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL);
957d7b5f5ccSFenglin Wu 		if (val < 0)
958d7b5f5ccSFenglin Wu 			return val;
959d7b5f5ccSFenglin Wu 		pad->atest = (val & PMIC_GPIO_LV_MV_ANA_MUX_SEL_MASK) + 1;
960d7b5f5ccSFenglin Wu 	}
961d7b5f5ccSFenglin Wu 
962eadff302SIvan T. Ivanov 	/* Pin could be disabled with PIN_CONFIG_BIAS_HIGH_IMPEDANCE */
963eadff302SIvan T. Ivanov 	pad->is_enabled = true;
964eadff302SIvan T. Ivanov 	return 0;
965eadff302SIvan T. Ivanov }
966eadff302SIvan T. Ivanov 
pmic_gpio_domain_translate(struct irq_domain * domain,struct irq_fwspec * fwspec,unsigned long * hwirq,unsigned int * type)967ca69e2d1SBrian Masney static int pmic_gpio_domain_translate(struct irq_domain *domain,
968ca69e2d1SBrian Masney 				      struct irq_fwspec *fwspec,
969ca69e2d1SBrian Masney 				      unsigned long *hwirq,
970ca69e2d1SBrian Masney 				      unsigned int *type)
971ca69e2d1SBrian Masney {
972ca69e2d1SBrian Masney 	struct pmic_gpio_state *state = container_of(domain->host_data,
973ca69e2d1SBrian Masney 						     struct pmic_gpio_state,
974ca69e2d1SBrian Masney 						     chip);
975ca69e2d1SBrian Masney 
976dac7da98SBjorn Andersson 	if (fwspec->param_count != 2 ||
977dac7da98SBjorn Andersson 	    fwspec->param[0] < 1 || fwspec->param[0] > state->chip.ngpio)
978ca69e2d1SBrian Masney 		return -EINVAL;
979ca69e2d1SBrian Masney 
980ca69e2d1SBrian Masney 	*hwirq = fwspec->param[0] - PMIC_GPIO_PHYSICAL_OFFSET;
981ca69e2d1SBrian Masney 	*type = fwspec->param[1];
982ca69e2d1SBrian Masney 
983ca69e2d1SBrian Masney 	return 0;
984ca69e2d1SBrian Masney }
985ca69e2d1SBrian Masney 
pmic_gpio_child_offset_to_irq(struct gpio_chip * chip,unsigned int offset)986821c76c4SBrian Masney static unsigned int pmic_gpio_child_offset_to_irq(struct gpio_chip *chip,
987821c76c4SBrian Masney 						  unsigned int offset)
988ca69e2d1SBrian Masney {
989821c76c4SBrian Masney 	return offset + PMIC_GPIO_PHYSICAL_OFFSET;
990ca69e2d1SBrian Masney }
991ca69e2d1SBrian Masney 
pmic_gpio_child_to_parent_hwirq(struct gpio_chip * chip,unsigned int child_hwirq,unsigned int child_type,unsigned int * parent_hwirq,unsigned int * parent_type)992821c76c4SBrian Masney static int pmic_gpio_child_to_parent_hwirq(struct gpio_chip *chip,
993821c76c4SBrian Masney 					   unsigned int child_hwirq,
994821c76c4SBrian Masney 					   unsigned int child_type,
995821c76c4SBrian Masney 					   unsigned int *parent_hwirq,
996821c76c4SBrian Masney 					   unsigned int *parent_type)
997821c76c4SBrian Masney {
998d36a9773SDavid Collins 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
999d36a9773SDavid Collins 
1000d36a9773SDavid Collins 	*parent_hwirq = child_hwirq + state->pid_base;
1001821c76c4SBrian Masney 	*parent_type = child_type;
1002821c76c4SBrian Masney 
1003821c76c4SBrian Masney 	return 0;
1004821c76c4SBrian Masney }
1005ca69e2d1SBrian Masney 
pmic_gpio_populate_parent_fwspec(struct gpio_chip * chip,union gpio_irq_fwspec * gfwspec,unsigned int parent_hwirq,unsigned int parent_type)100691a29af4SMarc Zyngier static int pmic_gpio_populate_parent_fwspec(struct gpio_chip *chip,
100791a29af4SMarc Zyngier 					    union gpio_irq_fwspec *gfwspec,
1008d36a9773SDavid Collins 					    unsigned int parent_hwirq,
1009d36a9773SDavid Collins 					    unsigned int parent_type)
1010d36a9773SDavid Collins {
1011d36a9773SDavid Collins 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
101291a29af4SMarc Zyngier 	struct irq_fwspec *fwspec = &gfwspec->fwspec;
1013d36a9773SDavid Collins 
1014d36a9773SDavid Collins 	fwspec->fwnode = chip->irq.parent_domain->fwnode;
1015d36a9773SDavid Collins 
1016d36a9773SDavid Collins 	fwspec->param_count = 4;
1017d36a9773SDavid Collins 	fwspec->param[0] = state->usid;
1018d36a9773SDavid Collins 	fwspec->param[1] = parent_hwirq;
1019d36a9773SDavid Collins 	/* param[2] must be left as 0 */
1020d36a9773SDavid Collins 	fwspec->param[3] = parent_type;
1021d36a9773SDavid Collins 
102291a29af4SMarc Zyngier 	return 0;
1023d36a9773SDavid Collins }
1024d36a9773SDavid Collins 
pmic_gpio_irq_mask(struct irq_data * data)10251a41d1e5SManivannan Sadhasivam static void pmic_gpio_irq_mask(struct irq_data *data)
10261a41d1e5SManivannan Sadhasivam {
10271a41d1e5SManivannan Sadhasivam 	struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
10281a41d1e5SManivannan Sadhasivam 
10291a41d1e5SManivannan Sadhasivam 	irq_chip_mask_parent(data);
10301a41d1e5SManivannan Sadhasivam 	gpiochip_disable_irq(gc, data->hwirq);
10311a41d1e5SManivannan Sadhasivam }
10321a41d1e5SManivannan Sadhasivam 
pmic_gpio_irq_unmask(struct irq_data * data)10331a41d1e5SManivannan Sadhasivam static void pmic_gpio_irq_unmask(struct irq_data *data)
10341a41d1e5SManivannan Sadhasivam {
10351a41d1e5SManivannan Sadhasivam 	struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
10361a41d1e5SManivannan Sadhasivam 
10371a41d1e5SManivannan Sadhasivam 	gpiochip_enable_irq(gc, data->hwirq);
10381a41d1e5SManivannan Sadhasivam 	irq_chip_unmask_parent(data);
10391a41d1e5SManivannan Sadhasivam }
10401a41d1e5SManivannan Sadhasivam 
10411a41d1e5SManivannan Sadhasivam static const struct irq_chip spmi_gpio_irq_chip = {
10421a41d1e5SManivannan Sadhasivam 	.name		= "spmi-gpio",
10431a41d1e5SManivannan Sadhasivam 	.irq_ack	= irq_chip_ack_parent,
10441a41d1e5SManivannan Sadhasivam 	.irq_mask	= pmic_gpio_irq_mask,
10451a41d1e5SManivannan Sadhasivam 	.irq_unmask	= pmic_gpio_irq_unmask,
10461a41d1e5SManivannan Sadhasivam 	.irq_set_type	= irq_chip_set_type_parent,
10471a41d1e5SManivannan Sadhasivam 	.irq_set_wake	= irq_chip_set_wake_parent,
10481a41d1e5SManivannan Sadhasivam 	.flags		= IRQCHIP_IMMUTABLE | IRQCHIP_MASK_ON_SUSPEND,
10491a41d1e5SManivannan Sadhasivam 	GPIOCHIP_IRQ_RESOURCE_HELPERS,
10501a41d1e5SManivannan Sadhasivam };
10511a41d1e5SManivannan Sadhasivam 
pmic_gpio_probe(struct platform_device * pdev)1052eadff302SIvan T. Ivanov static int pmic_gpio_probe(struct platform_device *pdev)
1053eadff302SIvan T. Ivanov {
1054ca69e2d1SBrian Masney 	struct irq_domain *parent_domain;
1055ca69e2d1SBrian Masney 	struct device_node *parent_node;
1056eadff302SIvan T. Ivanov 	struct device *dev = &pdev->dev;
1057eadff302SIvan T. Ivanov 	struct pinctrl_pin_desc *pindesc;
1058eadff302SIvan T. Ivanov 	struct pinctrl_desc *pctrldesc;
1059eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad, *pads;
1060eadff302SIvan T. Ivanov 	struct pmic_gpio_state *state;
1061821c76c4SBrian Masney 	struct gpio_irq_chip *girq;
1062d36a9773SDavid Collins 	const struct spmi_device *parent_spmi_dev;
1063eadff302SIvan T. Ivanov 	int ret, npins, i;
1064ab4256cfSStephen Boyd 	u32 reg;
1065eadff302SIvan T. Ivanov 
1066ab4256cfSStephen Boyd 	ret = of_property_read_u32(dev->of_node, "reg", &reg);
1067eadff302SIvan T. Ivanov 	if (ret < 0) {
1068ab4256cfSStephen Boyd 		dev_err(dev, "missing base address");
1069eadff302SIvan T. Ivanov 		return ret;
1070eadff302SIvan T. Ivanov 	}
1071eadff302SIvan T. Ivanov 
1072cfacef37SBrian Masney 	npins = (uintptr_t) device_get_match_data(&pdev->dev);
1073eadff302SIvan T. Ivanov 
1074eadff302SIvan T. Ivanov 	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
1075eadff302SIvan T. Ivanov 	if (!state)
1076eadff302SIvan T. Ivanov 		return -ENOMEM;
1077eadff302SIvan T. Ivanov 
1078eadff302SIvan T. Ivanov 	platform_set_drvdata(pdev, state);
1079eadff302SIvan T. Ivanov 
1080eadff302SIvan T. Ivanov 	state->dev = &pdev->dev;
1081eadff302SIvan T. Ivanov 	state->map = dev_get_regmap(dev->parent, NULL);
1082d36a9773SDavid Collins 	parent_spmi_dev = to_spmi_device(dev->parent);
1083d36a9773SDavid Collins 	state->usid = parent_spmi_dev->usid;
1084d36a9773SDavid Collins 	state->pid_base = reg >> 8;
1085eadff302SIvan T. Ivanov 
1086eadff302SIvan T. Ivanov 	pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
1087eadff302SIvan T. Ivanov 	if (!pindesc)
1088eadff302SIvan T. Ivanov 		return -ENOMEM;
1089eadff302SIvan T. Ivanov 
1090eadff302SIvan T. Ivanov 	pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
1091eadff302SIvan T. Ivanov 	if (!pads)
1092eadff302SIvan T. Ivanov 		return -ENOMEM;
1093eadff302SIvan T. Ivanov 
1094eadff302SIvan T. Ivanov 	pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL);
1095eadff302SIvan T. Ivanov 	if (!pctrldesc)
1096eadff302SIvan T. Ivanov 		return -ENOMEM;
1097eadff302SIvan T. Ivanov 
1098eadff302SIvan T. Ivanov 	pctrldesc->pctlops = &pmic_gpio_pinctrl_ops;
1099eadff302SIvan T. Ivanov 	pctrldesc->pmxops = &pmic_gpio_pinmux_ops;
1100eadff302SIvan T. Ivanov 	pctrldesc->confops = &pmic_gpio_pinconf_ops;
1101eadff302SIvan T. Ivanov 	pctrldesc->owner = THIS_MODULE;
1102eadff302SIvan T. Ivanov 	pctrldesc->name = dev_name(dev);
1103eadff302SIvan T. Ivanov 	pctrldesc->pins = pindesc;
1104eadff302SIvan T. Ivanov 	pctrldesc->npins = npins;
1105f684e4acSLinus Walleij 	pctrldesc->num_custom_params = ARRAY_SIZE(pmic_gpio_bindings);
1106f684e4acSLinus Walleij 	pctrldesc->custom_params = pmic_gpio_bindings;
11074f06266aSArnd Bergmann #ifdef CONFIG_DEBUG_FS
1108f684e4acSLinus Walleij 	pctrldesc->custom_conf_items = pmic_conf_items;
11094f06266aSArnd Bergmann #endif
1110eadff302SIvan T. Ivanov 
1111eadff302SIvan T. Ivanov 	for (i = 0; i < npins; i++, pindesc++) {
1112eadff302SIvan T. Ivanov 		pad = &pads[i];
1113eadff302SIvan T. Ivanov 		pindesc->drv_data = pad;
1114eadff302SIvan T. Ivanov 		pindesc->number = i;
1115eadff302SIvan T. Ivanov 		pindesc->name = pmic_gpio_groups[i];
1116eadff302SIvan T. Ivanov 
1117ab4256cfSStephen Boyd 		pad->base = reg + i * PMIC_GPIO_ADDRESS_RANGE;
1118eadff302SIvan T. Ivanov 
1119eadff302SIvan T. Ivanov 		ret = pmic_gpio_populate(state, pad);
1120eadff302SIvan T. Ivanov 		if (ret < 0)
1121eadff302SIvan T. Ivanov 			return ret;
1122eadff302SIvan T. Ivanov 	}
1123eadff302SIvan T. Ivanov 
1124eadff302SIvan T. Ivanov 	state->chip = pmic_gpio_gpio_template;
112558383c78SLinus Walleij 	state->chip.parent = dev;
1126eadff302SIvan T. Ivanov 	state->chip.base = -1;
1127eadff302SIvan T. Ivanov 	state->chip.ngpio = npins;
1128eadff302SIvan T. Ivanov 	state->chip.label = dev_name(dev);
1129eadff302SIvan T. Ivanov 	state->chip.of_gpio_n_cells = 2;
1130eadff302SIvan T. Ivanov 	state->chip.can_sleep = false;
1131eadff302SIvan T. Ivanov 
1132b46ddfe6SLaxman Dewangan 	state->ctrl = devm_pinctrl_register(dev, pctrldesc, state);
1133323de9efSMasahiro Yamada 	if (IS_ERR(state->ctrl))
1134323de9efSMasahiro Yamada 		return PTR_ERR(state->ctrl);
1135eadff302SIvan T. Ivanov 
1136ca69e2d1SBrian Masney 	parent_node = of_irq_find_parent(state->dev->of_node);
1137ca69e2d1SBrian Masney 	if (!parent_node)
1138ca69e2d1SBrian Masney 		return -ENXIO;
1139ca69e2d1SBrian Masney 
1140ca69e2d1SBrian Masney 	parent_domain = irq_find_host(parent_node);
1141ca69e2d1SBrian Masney 	of_node_put(parent_node);
1142ca69e2d1SBrian Masney 	if (!parent_domain)
1143ca69e2d1SBrian Masney 		return -ENXIO;
1144ca69e2d1SBrian Masney 
1145821c76c4SBrian Masney 	girq = &state->chip.irq;
11461a41d1e5SManivannan Sadhasivam 	gpio_irq_chip_set_chip(girq, &spmi_gpio_irq_chip);
1147821c76c4SBrian Masney 	girq->default_type = IRQ_TYPE_NONE;
1148821c76c4SBrian Masney 	girq->handler = handle_level_irq;
11499bd73ce0SAndy Shevchenko 	girq->fwnode = dev_fwnode(state->dev);
1150821c76c4SBrian Masney 	girq->parent_domain = parent_domain;
1151821c76c4SBrian Masney 	girq->child_to_parent_hwirq = pmic_gpio_child_to_parent_hwirq;
1152d36a9773SDavid Collins 	girq->populate_parent_alloc_arg = pmic_gpio_populate_parent_fwspec;
1153821c76c4SBrian Masney 	girq->child_offset_to_irq = pmic_gpio_child_offset_to_irq;
1154821c76c4SBrian Masney 	girq->child_irq_domain_ops.translate = pmic_gpio_domain_translate;
1155ca69e2d1SBrian Masney 
1156c52d9df1SLinus Walleij 	ret = gpiochip_add_data(&state->chip, state);
1157eadff302SIvan T. Ivanov 	if (ret) {
1158eadff302SIvan T. Ivanov 		dev_err(state->dev, "can't add gpio chip\n");
1159821c76c4SBrian Masney 		return ret;
1160eadff302SIvan T. Ivanov 	}
1161eadff302SIvan T. Ivanov 
1162149a9604SBrian Masney 	/*
1163149a9604SBrian Masney 	 * For DeviceTree-supported systems, the gpio core checks the
1164149a9604SBrian Masney 	 * pinctrl's device node for the "gpio-ranges" property.
1165149a9604SBrian Masney 	 * If it is present, it takes care of adding the pin ranges
1166149a9604SBrian Masney 	 * for the driver. In this case the driver can skip ahead.
1167149a9604SBrian Masney 	 *
1168149a9604SBrian Masney 	 * In order to remain compatible with older, existing DeviceTree
1169149a9604SBrian Masney 	 * files which don't set the "gpio-ranges" property or systems that
1170149a9604SBrian Masney 	 * utilize ACPI the driver has to call gpiochip_add_pin_range().
1171149a9604SBrian Masney 	 */
1172149a9604SBrian Masney 	if (!of_property_read_bool(dev->of_node, "gpio-ranges")) {
1173149a9604SBrian Masney 		ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0,
1174149a9604SBrian Masney 					     npins);
1175eadff302SIvan T. Ivanov 		if (ret) {
1176eadff302SIvan T. Ivanov 			dev_err(dev, "failed to add pin range\n");
1177eadff302SIvan T. Ivanov 			goto err_range;
1178eadff302SIvan T. Ivanov 		}
1179149a9604SBrian Masney 	}
1180eadff302SIvan T. Ivanov 
1181eadff302SIvan T. Ivanov 	return 0;
1182eadff302SIvan T. Ivanov 
1183eadff302SIvan T. Ivanov err_range:
1184eadff302SIvan T. Ivanov 	gpiochip_remove(&state->chip);
1185eadff302SIvan T. Ivanov 	return ret;
1186eadff302SIvan T. Ivanov }
1187eadff302SIvan T. Ivanov 
pmic_gpio_remove(struct platform_device * pdev)1188eadff302SIvan T. Ivanov static int pmic_gpio_remove(struct platform_device *pdev)
1189eadff302SIvan T. Ivanov {
1190eadff302SIvan T. Ivanov 	struct pmic_gpio_state *state = platform_get_drvdata(pdev);
1191eadff302SIvan T. Ivanov 
1192eadff302SIvan T. Ivanov 	gpiochip_remove(&state->chip);
1193eadff302SIvan T. Ivanov 	return 0;
1194eadff302SIvan T. Ivanov }
1195eadff302SIvan T. Ivanov 
1196eadff302SIvan T. Ivanov static const struct of_device_id pmic_gpio_of_match[] = {
1197ef874e03SLoic Poulain 	{ .compatible = "qcom,pm2250-gpio", .data = (void *) 10 },
119817cc38e7SKonrad Dybcio 	/* pm660 has 13 GPIOs with holes on 1, 5, 6, 7, 8 and 10 */
119917cc38e7SKonrad Dybcio 	{ .compatible = "qcom,pm660-gpio", .data = (void *) 13 },
120017cc38e7SKonrad Dybcio 	/* pm660l has 12 GPIOs with holes on 1, 2, 10, 11 and 12 */
120117cc38e7SKonrad Dybcio 	{ .compatible = "qcom,pm660l-gpio", .data = (void *) 12 },
12024d8a768eSMarijn Suijten 	{ .compatible = "qcom,pm6125-gpio", .data = (void *) 9 },
12034afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm6150-gpio", .data = (void *) 10 },
12044afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm6150l-gpio", .data = (void *) 12 },
120583917856SLuca Weiss 	{ .compatible = "qcom,pm6350-gpio", .data = (void *) 9 },
12063d46ff83SJishnu Prakash 	{ .compatible = "qcom,pm7250b-gpio", .data = (void *) 12 },
12074afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm7325-gpio", .data = (void *) 10 },
12088fff6514SRohit Agarwal 	{ .compatible = "qcom,pm7550ba-gpio", .data = (void *) 8},
12094afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm8005-gpio", .data = (void *) 4 },
12104afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm8008-gpio", .data = (void *) 2 },
1211f3474383SKonrad Dybcio 	{ .compatible = "qcom,pm8019-gpio", .data = (void *) 6 },
1212d67070c3SVinod Koul 	/* pm8150 has 10 GPIOs with holes on 2, 5, 7 and 8 */
1213d67070c3SVinod Koul 	{ .compatible = "qcom,pm8150-gpio", .data = (void *) 10 },
1214182700f2SBjorn Andersson 	{ .compatible = "qcom,pmc8180-gpio", .data = (void *) 10 },
12159bdacaddSVinod Koul 	/* pm8150b has 12 GPIOs with holes on 3, r and 7 */
12169bdacaddSVinod Koul 	{ .compatible = "qcom,pm8150b-gpio", .data = (void *) 12 },
12172dc889a8SVinod Koul 	/* pm8150l has 12 GPIOs with holes on 7 */
12182dc889a8SVinod Koul 	{ .compatible = "qcom,pm8150l-gpio", .data = (void *) 12 },
1219182700f2SBjorn Andersson 	{ .compatible = "qcom,pmc8180c-gpio", .data = (void *) 12 },
1220eebe11b5SDominik Kobinski 	{ .compatible = "qcom,pm8226-gpio", .data = (void *) 8 },
122157c0a4f0SVinod Koul 	{ .compatible = "qcom,pm8350-gpio", .data = (void *) 10 },
122257c0a4f0SVinod Koul 	{ .compatible = "qcom,pm8350b-gpio", .data = (void *) 8 },
122357c0a4f0SVinod Koul 	{ .compatible = "qcom,pm8350c-gpio", .data = (void *) 9 },
1224168a0abfSDmitry Baryshkov 	{ .compatible = "qcom,pm8450-gpio", .data = (void *) 4 },
1225e8c39b3eSNeil Armstrong 	{ .compatible = "qcom,pm8550-gpio", .data = (void *) 12 },
1226e8c39b3eSNeil Armstrong 	{ .compatible = "qcom,pm8550b-gpio", .data = (void *) 12 },
1227e8c39b3eSNeil Armstrong 	{ .compatible = "qcom,pm8550ve-gpio", .data = (void *) 8 },
1228e8c39b3eSNeil Armstrong 	{ .compatible = "qcom,pm8550vs-gpio", .data = (void *) 6 },
12294afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm8916-gpio", .data = (void *) 4 },
12304afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm8941-gpio", .data = (void *) 36 },
12314afc2a0cSBhupesh Sharma 	/* pm8950 has 8 GPIOs with holes on 3 */
12324afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm8950-gpio", .data = (void *) 8 },
1233cbbe0778SLuca Weiss 	/* pm8953 has 8 GPIOs with holes on 3 and 6 */
1234cbbe0778SLuca Weiss 	{ .compatible = "qcom,pm8953-gpio", .data = (void *) 8 },
12354afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm8994-gpio", .data = (void *) 22 },
12364afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pm8998-gpio", .data = (void *) 26 },
12374afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pma8084-gpio", .data = (void *) 22 },
12387abf7f88SLuca Weiss 	{ .compatible = "qcom,pmi632-gpio", .data = (void *) 8 },
12394afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pmi8950-gpio", .data = (void *) 2 },
12404afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pmi8994-gpio", .data = (void *) 10 },
12414afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pmi8998-gpio", .data = (void *) 14 },
124257c0a4f0SVinod Koul 	{ .compatible = "qcom,pmk8350-gpio", .data = (void *) 4 },
1243e8c39b3eSNeil Armstrong 	{ .compatible = "qcom,pmk8550-gpio", .data = (void *) 6 },
124479e2311cSBhupesh Sharma 	{ .compatible = "qcom,pmm8155au-gpio", .data = (void *) 10 },
12450538897aSBartosz Golaszewski 	{ .compatible = "qcom,pmm8654au-gpio", .data = (void *) 12 },
12466cd81a86SRobert Marko 	/* pmp8074 has 12 GPIOs with holes on 1 and 12 */
12476cd81a86SRobert Marko 	{ .compatible = "qcom,pmp8074-gpio", .data = (void *) 12 },
124857c0a4f0SVinod Koul 	{ .compatible = "qcom,pmr735a-gpio", .data = (void *) 4 },
124957c0a4f0SVinod Koul 	{ .compatible = "qcom,pmr735b-gpio", .data = (void *) 4 },
1250e8c39b3eSNeil Armstrong 	{ .compatible = "qcom,pmr735d-gpio", .data = (void *) 2 },
12514afc2a0cSBhupesh Sharma 	/* pms405 has 12 GPIOs with holes on 1, 9, and 10 */
12524afc2a0cSBhupesh Sharma 	{ .compatible = "qcom,pms405-gpio", .data = (void *) 12 },
1253ceb58de4SVinod Koul 	/* pmx55 has 11 GPIOs with holes on 3, 7, 10, 11 */
1254ceb58de4SVinod Koul 	{ .compatible = "qcom,pmx55-gpio", .data = (void *) 11 },
1255203638fdSRohit Agarwal 	{ .compatible = "qcom,pmx65-gpio", .data = (void *) 16 },
1256*1e46c743SRohit Agarwal 	{ .compatible = "qcom,pmx75-gpio", .data = (void *) 16 },
1257eadff302SIvan T. Ivanov 	{ },
1258eadff302SIvan T. Ivanov };
1259eadff302SIvan T. Ivanov 
1260eadff302SIvan T. Ivanov MODULE_DEVICE_TABLE(of, pmic_gpio_of_match);
1261eadff302SIvan T. Ivanov 
1262eadff302SIvan T. Ivanov static struct platform_driver pmic_gpio_driver = {
1263eadff302SIvan T. Ivanov 	.driver = {
1264eadff302SIvan T. Ivanov 		   .name = "qcom-spmi-gpio",
1265eadff302SIvan T. Ivanov 		   .of_match_table = pmic_gpio_of_match,
1266eadff302SIvan T. Ivanov 	},
1267eadff302SIvan T. Ivanov 	.probe	= pmic_gpio_probe,
1268eadff302SIvan T. Ivanov 	.remove = pmic_gpio_remove,
1269eadff302SIvan T. Ivanov };
1270eadff302SIvan T. Ivanov 
1271eadff302SIvan T. Ivanov module_platform_driver(pmic_gpio_driver);
1272eadff302SIvan T. Ivanov 
1273eadff302SIvan T. Ivanov MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
1274eadff302SIvan T. Ivanov MODULE_DESCRIPTION("Qualcomm SPMI PMIC GPIO pin control driver");
1275eadff302SIvan T. Ivanov MODULE_ALIAS("platform:qcom-spmi-gpio");
1276eadff302SIvan T. Ivanov MODULE_LICENSE("GPL v2");
1277