1eadff302SIvan T. Ivanov /*
2eadff302SIvan T. Ivanov  * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
3eadff302SIvan T. Ivanov  *
4eadff302SIvan T. Ivanov  * This program is free software; you can redistribute it and/or modify
5eadff302SIvan T. Ivanov  * it under the terms of the GNU General Public License version 2 and
6eadff302SIvan T. Ivanov  * only version 2 as published by the Free Software Foundation.
7eadff302SIvan T. Ivanov  *
8eadff302SIvan T. Ivanov  * This program is distributed in the hope that it will be useful,
9eadff302SIvan T. Ivanov  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10eadff302SIvan T. Ivanov  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11eadff302SIvan T. Ivanov  * GNU General Public License for more details.
12eadff302SIvan T. Ivanov  */
13eadff302SIvan T. Ivanov 
141c5fb66aSLinus Walleij #include <linux/gpio/driver.h>
15ca69e2d1SBrian Masney #include <linux/interrupt.h>
16eadff302SIvan T. Ivanov #include <linux/module.h>
17eadff302SIvan T. Ivanov #include <linux/of.h>
18ab4256cfSStephen Boyd #include <linux/of_irq.h>
19eadff302SIvan T. Ivanov #include <linux/pinctrl/pinconf-generic.h>
20eadff302SIvan T. Ivanov #include <linux/pinctrl/pinconf.h>
21eadff302SIvan T. Ivanov #include <linux/pinctrl/pinmux.h>
22eadff302SIvan T. Ivanov #include <linux/platform_device.h>
23eadff302SIvan T. Ivanov #include <linux/regmap.h>
24eadff302SIvan T. Ivanov #include <linux/slab.h>
25eadff302SIvan T. Ivanov #include <linux/types.h>
26eadff302SIvan T. Ivanov 
27eadff302SIvan T. Ivanov #include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
28eadff302SIvan T. Ivanov 
29eadff302SIvan T. Ivanov #include "../core.h"
30eadff302SIvan T. Ivanov #include "../pinctrl-utils.h"
31eadff302SIvan T. Ivanov 
32eadff302SIvan T. Ivanov #define PMIC_GPIO_ADDRESS_RANGE			0x100
33eadff302SIvan T. Ivanov 
34eadff302SIvan T. Ivanov /* type and subtype registers base address offsets */
35eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_TYPE			0x4
36eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_SUBTYPE			0x5
37eadff302SIvan T. Ivanov 
38eadff302SIvan T. Ivanov /* GPIO peripheral type and subtype out_values */
39eadff302SIvan T. Ivanov #define PMIC_GPIO_TYPE				0x10
40eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIO_4CH		0x1
41eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIOC_4CH		0x5
42eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIO_8CH		0x9
43eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIOC_8CH		0xd
44d7b5f5ccSFenglin Wu #define PMIC_GPIO_SUBTYPE_GPIO_LV		0x10
45d7b5f5ccSFenglin Wu #define PMIC_GPIO_SUBTYPE_GPIO_MV		0x11
46eadff302SIvan T. Ivanov 
47eadff302SIvan T. Ivanov #define PMIC_MPP_REG_RT_STS			0x10
48eadff302SIvan T. Ivanov #define PMIC_MPP_REG_RT_STS_VAL_MASK		0x1
49eadff302SIvan T. Ivanov 
50eadff302SIvan T. Ivanov /* control register base address offsets */
51eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_CTL			0x40
52eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_VIN_CTL		0x41
53eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_PULL_CTL		0x42
54d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL	0x44
55223463fcSFenglin Wu #define PMIC_GPIO_REG_DIG_IN_CTL		0x43
56eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_OUT_CTL		0x45
57eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_EN_CTL			0x46
58d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL	0x4A
59eadff302SIvan T. Ivanov 
60eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_MODE_CTL */
61eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_VALUE_SHIFT		0x1
62eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_FUNCTION_SHIFT	1
63eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_FUNCTION_MASK	0x7
64eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_DIR_SHIFT		4
65eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_DIR_MASK		0x7
66eadff302SIvan T. Ivanov 
67d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_INPUT		0
68d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_OUTPUT		1
69d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT	2
70d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_ANALOG_PASS_THRU		3
71d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_MODE_DIR_MASK	0x3
72d7b5f5ccSFenglin Wu 
73eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_VIN_CTL */
74eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_VIN_SHIFT			0
75eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_VIN_MASK			0x7
76eadff302SIvan T. Ivanov 
77eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_PULL_CTL */
78eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_PULL_SHIFT		0
79eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_PULL_MASK			0x7
80eadff302SIvan T. Ivanov 
81eadff302SIvan T. Ivanov #define PMIC_GPIO_PULL_DOWN			4
82eadff302SIvan T. Ivanov #define PMIC_GPIO_PULL_DISABLE			5
83eadff302SIvan T. Ivanov 
84d7b5f5ccSFenglin Wu /* PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL for LV/MV */
85d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_INVERT		0x80
86d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT	7
87d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK	0xF
88d7b5f5ccSFenglin Wu 
89223463fcSFenglin Wu /* PMIC_GPIO_REG_DIG_IN_CTL */
90223463fcSFenglin Wu #define PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN		0x80
91223463fcSFenglin Wu #define PMIC_GPIO_LV_MV_DIG_IN_DTEST_SEL_MASK	0x7
92223463fcSFenglin Wu #define PMIC_GPIO_DIG_IN_DTEST_SEL_MASK		0xf
93223463fcSFenglin Wu 
94eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_OUT_CTL */
95eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_STRENGTH_SHIFT	0
96eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_STRENGTH_MASK		0x3
97eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_TYPE_SHIFT		4
98eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_TYPE_MASK		0x3
99eadff302SIvan T. Ivanov 
100eadff302SIvan T. Ivanov /*
101eadff302SIvan T. Ivanov  * Output type - indicates pin should be configured as push-pull,
102eadff302SIvan T. Ivanov  * open drain or open source.
103eadff302SIvan T. Ivanov  */
104eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_CMOS			0
105eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS	1
106eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS	2
107eadff302SIvan T. Ivanov 
108eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_EN_CTL */
109eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MASTER_EN_SHIFT		7
110eadff302SIvan T. Ivanov 
111eadff302SIvan T. Ivanov #define PMIC_GPIO_PHYSICAL_OFFSET		1
112eadff302SIvan T. Ivanov 
113d7b5f5ccSFenglin Wu /* PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL */
114d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_ANA_MUX_SEL_MASK		0x3
115d7b5f5ccSFenglin Wu 
116eadff302SIvan T. Ivanov /* Qualcomm specific pin configurations */
117eadff302SIvan T. Ivanov #define PMIC_GPIO_CONF_PULL_UP			(PIN_CONFIG_END + 1)
118eadff302SIvan T. Ivanov #define PMIC_GPIO_CONF_STRENGTH			(PIN_CONFIG_END + 2)
119d7b5f5ccSFenglin Wu #define PMIC_GPIO_CONF_ATEST			(PIN_CONFIG_END + 3)
120d7b5f5ccSFenglin Wu #define PMIC_GPIO_CONF_ANALOG_PASS		(PIN_CONFIG_END + 4)
121223463fcSFenglin Wu #define PMIC_GPIO_CONF_DTEST_BUFFER		(PIN_CONFIG_END + 5)
122d7b5f5ccSFenglin Wu 
123d7b5f5ccSFenglin Wu /* The index of each function in pmic_gpio_functions[] array */
124d7b5f5ccSFenglin Wu enum pmic_gpio_func_index {
125d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_NORMAL,
126d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_PAIRED,
127d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_FUNC1,
128d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_FUNC2,
129d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_FUNC3,
130d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_FUNC4,
131d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_DTEST1,
132d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_DTEST2,
133d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_DTEST3,
134d7b5f5ccSFenglin Wu 	PMIC_GPIO_FUNC_INDEX_DTEST4,
135d7b5f5ccSFenglin Wu };
136eadff302SIvan T. Ivanov 
137eadff302SIvan T. Ivanov /**
138eadff302SIvan T. Ivanov  * struct pmic_gpio_pad - keep current GPIO settings
139eadff302SIvan T. Ivanov  * @base: Address base in SPMI device.
140eadff302SIvan T. Ivanov  * @is_enabled: Set to false when GPIO should be put in high Z state.
141eadff302SIvan T. Ivanov  * @out_value: Cached pin output value
142eadff302SIvan T. Ivanov  * @have_buffer: Set to true if GPIO output could be configured in push-pull,
143eadff302SIvan T. Ivanov  *	open-drain or open-source mode.
144eadff302SIvan T. Ivanov  * @output_enabled: Set to true if GPIO output logic is enabled.
145eadff302SIvan T. Ivanov  * @input_enabled: Set to true if GPIO input buffer logic is enabled.
146d7b5f5ccSFenglin Wu  * @analog_pass: Set to true if GPIO is in analog-pass-through mode.
147d7b5f5ccSFenglin Wu  * @lv_mv_type: Set to true if GPIO subtype is GPIO_LV(0x10) or GPIO_MV(0x11).
148eadff302SIvan T. Ivanov  * @num_sources: Number of power-sources supported by this GPIO.
149eadff302SIvan T. Ivanov  * @power_source: Current power-source used.
150eadff302SIvan T. Ivanov  * @buffer_type: Push-pull, open-drain or open-source.
151eadff302SIvan T. Ivanov  * @pullup: Constant current which flow trough GPIO output buffer.
152eadff302SIvan T. Ivanov  * @strength: No, Low, Medium, High
153eadff302SIvan T. Ivanov  * @function: See pmic_gpio_functions[]
154d7b5f5ccSFenglin Wu  * @atest: the ATEST selection for GPIO analog-pass-through mode
155223463fcSFenglin Wu  * @dtest_buffer: the DTEST buffer selection for digital input mode.
156eadff302SIvan T. Ivanov  */
157eadff302SIvan T. Ivanov struct pmic_gpio_pad {
158eadff302SIvan T. Ivanov 	u16		base;
159eadff302SIvan T. Ivanov 	bool		is_enabled;
160eadff302SIvan T. Ivanov 	bool		out_value;
161eadff302SIvan T. Ivanov 	bool		have_buffer;
162eadff302SIvan T. Ivanov 	bool		output_enabled;
163eadff302SIvan T. Ivanov 	bool		input_enabled;
164d7b5f5ccSFenglin Wu 	bool		analog_pass;
165d7b5f5ccSFenglin Wu 	bool		lv_mv_type;
166eadff302SIvan T. Ivanov 	unsigned int	num_sources;
167eadff302SIvan T. Ivanov 	unsigned int	power_source;
168eadff302SIvan T. Ivanov 	unsigned int	buffer_type;
169eadff302SIvan T. Ivanov 	unsigned int	pullup;
170eadff302SIvan T. Ivanov 	unsigned int	strength;
171eadff302SIvan T. Ivanov 	unsigned int	function;
172d7b5f5ccSFenglin Wu 	unsigned int	atest;
173223463fcSFenglin Wu 	unsigned int	dtest_buffer;
174eadff302SIvan T. Ivanov };
175eadff302SIvan T. Ivanov 
176eadff302SIvan T. Ivanov struct pmic_gpio_state {
177eadff302SIvan T. Ivanov 	struct device	*dev;
178eadff302SIvan T. Ivanov 	struct regmap	*map;
179eadff302SIvan T. Ivanov 	struct pinctrl_dev *ctrl;
180eadff302SIvan T. Ivanov 	struct gpio_chip chip;
181ca69e2d1SBrian Masney 	struct fwnode_handle *fwnode;
182ca69e2d1SBrian Masney 	struct irq_domain *domain;
183eadff302SIvan T. Ivanov };
184eadff302SIvan T. Ivanov 
185f684e4acSLinus Walleij static const struct pinconf_generic_params pmic_gpio_bindings[] = {
1867382b623SSoren Brinkmann 	{"qcom,pull-up-strength",	PMIC_GPIO_CONF_PULL_UP,		0},
1877382b623SSoren Brinkmann 	{"qcom,drive-strength",		PMIC_GPIO_CONF_STRENGTH,	0},
188d7b5f5ccSFenglin Wu 	{"qcom,atest",			PMIC_GPIO_CONF_ATEST,		0},
189d7b5f5ccSFenglin Wu 	{"qcom,analog-pass",		PMIC_GPIO_CONF_ANALOG_PASS,	0},
190223463fcSFenglin Wu 	{"qcom,dtest-buffer",           PMIC_GPIO_CONF_DTEST_BUFFER,    0},
191eadff302SIvan T. Ivanov };
192eadff302SIvan T. Ivanov 
1934f06266aSArnd Bergmann #ifdef CONFIG_DEBUG_FS
1947382b623SSoren Brinkmann static const struct pin_config_item pmic_conf_items[ARRAY_SIZE(pmic_gpio_bindings)] = {
1957382b623SSoren Brinkmann 	PCONFDUMP(PMIC_GPIO_CONF_PULL_UP,  "pull up strength", NULL, true),
1967382b623SSoren Brinkmann 	PCONFDUMP(PMIC_GPIO_CONF_STRENGTH, "drive-strength", NULL, true),
197d7b5f5ccSFenglin Wu 	PCONFDUMP(PMIC_GPIO_CONF_ATEST, "atest", NULL, true),
198d7b5f5ccSFenglin Wu 	PCONFDUMP(PMIC_GPIO_CONF_ANALOG_PASS, "analog-pass", NULL, true),
199223463fcSFenglin Wu 	PCONFDUMP(PMIC_GPIO_CONF_DTEST_BUFFER, "dtest-buffer", NULL, true),
200eadff302SIvan T. Ivanov };
2014f06266aSArnd Bergmann #endif
202eadff302SIvan T. Ivanov 
203eadff302SIvan T. Ivanov static const char *const pmic_gpio_groups[] = {
204eadff302SIvan T. Ivanov 	"gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", "gpio8",
205eadff302SIvan T. Ivanov 	"gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", "gpio15",
206eadff302SIvan T. Ivanov 	"gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22",
207eadff302SIvan T. Ivanov 	"gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29",
208eadff302SIvan T. Ivanov 	"gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36",
209eadff302SIvan T. Ivanov };
210eadff302SIvan T. Ivanov 
211eadff302SIvan T. Ivanov static const char *const pmic_gpio_functions[] = {
212d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_NORMAL]	= PMIC_GPIO_FUNC_NORMAL,
213d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_PAIRED]	= PMIC_GPIO_FUNC_PAIRED,
214d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_FUNC1]	= PMIC_GPIO_FUNC_FUNC1,
215d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_FUNC2]	= PMIC_GPIO_FUNC_FUNC2,
216d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_FUNC3]	= PMIC_GPIO_FUNC_FUNC3,
217d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_FUNC4]	= PMIC_GPIO_FUNC_FUNC4,
218d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_DTEST1]	= PMIC_GPIO_FUNC_DTEST1,
219d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_DTEST2]	= PMIC_GPIO_FUNC_DTEST2,
220d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_DTEST3]	= PMIC_GPIO_FUNC_DTEST3,
221d7b5f5ccSFenglin Wu 	[PMIC_GPIO_FUNC_INDEX_DTEST4]	= PMIC_GPIO_FUNC_DTEST4,
222eadff302SIvan T. Ivanov };
223eadff302SIvan T. Ivanov 
224eadff302SIvan T. Ivanov static int pmic_gpio_read(struct pmic_gpio_state *state,
225eadff302SIvan T. Ivanov 			  struct pmic_gpio_pad *pad, unsigned int addr)
226eadff302SIvan T. Ivanov {
227eadff302SIvan T. Ivanov 	unsigned int val;
228eadff302SIvan T. Ivanov 	int ret;
229eadff302SIvan T. Ivanov 
230eadff302SIvan T. Ivanov 	ret = regmap_read(state->map, pad->base + addr, &val);
231eadff302SIvan T. Ivanov 	if (ret < 0)
232eadff302SIvan T. Ivanov 		dev_err(state->dev, "read 0x%x failed\n", addr);
233eadff302SIvan T. Ivanov 	else
234eadff302SIvan T. Ivanov 		ret = val;
235eadff302SIvan T. Ivanov 
236eadff302SIvan T. Ivanov 	return ret;
237eadff302SIvan T. Ivanov }
238eadff302SIvan T. Ivanov 
239eadff302SIvan T. Ivanov static int pmic_gpio_write(struct pmic_gpio_state *state,
240eadff302SIvan T. Ivanov 			   struct pmic_gpio_pad *pad, unsigned int addr,
241eadff302SIvan T. Ivanov 			   unsigned int val)
242eadff302SIvan T. Ivanov {
243eadff302SIvan T. Ivanov 	int ret;
244eadff302SIvan T. Ivanov 
245eadff302SIvan T. Ivanov 	ret = regmap_write(state->map, pad->base + addr, val);
246eadff302SIvan T. Ivanov 	if (ret < 0)
247eadff302SIvan T. Ivanov 		dev_err(state->dev, "write 0x%x failed\n", addr);
248eadff302SIvan T. Ivanov 
249eadff302SIvan T. Ivanov 	return ret;
250eadff302SIvan T. Ivanov }
251eadff302SIvan T. Ivanov 
252eadff302SIvan T. Ivanov static int pmic_gpio_get_groups_count(struct pinctrl_dev *pctldev)
253eadff302SIvan T. Ivanov {
254eadff302SIvan T. Ivanov 	/* Every PIN is a group */
255eadff302SIvan T. Ivanov 	return pctldev->desc->npins;
256eadff302SIvan T. Ivanov }
257eadff302SIvan T. Ivanov 
258eadff302SIvan T. Ivanov static const char *pmic_gpio_get_group_name(struct pinctrl_dev *pctldev,
259eadff302SIvan T. Ivanov 					    unsigned pin)
260eadff302SIvan T. Ivanov {
261eadff302SIvan T. Ivanov 	return pctldev->desc->pins[pin].name;
262eadff302SIvan T. Ivanov }
263eadff302SIvan T. Ivanov 
264eadff302SIvan T. Ivanov static int pmic_gpio_get_group_pins(struct pinctrl_dev *pctldev, unsigned pin,
265eadff302SIvan T. Ivanov 				    const unsigned **pins, unsigned *num_pins)
266eadff302SIvan T. Ivanov {
267eadff302SIvan T. Ivanov 	*pins = &pctldev->desc->pins[pin].number;
268eadff302SIvan T. Ivanov 	*num_pins = 1;
269eadff302SIvan T. Ivanov 	return 0;
270eadff302SIvan T. Ivanov }
271eadff302SIvan T. Ivanov 
272eadff302SIvan T. Ivanov static const struct pinctrl_ops pmic_gpio_pinctrl_ops = {
273eadff302SIvan T. Ivanov 	.get_groups_count	= pmic_gpio_get_groups_count,
274eadff302SIvan T. Ivanov 	.get_group_name		= pmic_gpio_get_group_name,
275eadff302SIvan T. Ivanov 	.get_group_pins		= pmic_gpio_get_group_pins,
2767382b623SSoren Brinkmann 	.dt_node_to_map		= pinconf_generic_dt_node_to_map_group,
277d32f7fd3SIrina Tirdea 	.dt_free_map		= pinctrl_utils_free_map,
278eadff302SIvan T. Ivanov };
279eadff302SIvan T. Ivanov 
280eadff302SIvan T. Ivanov static int pmic_gpio_get_functions_count(struct pinctrl_dev *pctldev)
281eadff302SIvan T. Ivanov {
282eadff302SIvan T. Ivanov 	return ARRAY_SIZE(pmic_gpio_functions);
283eadff302SIvan T. Ivanov }
284eadff302SIvan T. Ivanov 
285eadff302SIvan T. Ivanov static const char *pmic_gpio_get_function_name(struct pinctrl_dev *pctldev,
286eadff302SIvan T. Ivanov 					       unsigned function)
287eadff302SIvan T. Ivanov {
288eadff302SIvan T. Ivanov 	return pmic_gpio_functions[function];
289eadff302SIvan T. Ivanov }
290eadff302SIvan T. Ivanov 
291eadff302SIvan T. Ivanov static int pmic_gpio_get_function_groups(struct pinctrl_dev *pctldev,
292eadff302SIvan T. Ivanov 					 unsigned function,
293eadff302SIvan T. Ivanov 					 const char *const **groups,
294eadff302SIvan T. Ivanov 					 unsigned *const num_qgroups)
295eadff302SIvan T. Ivanov {
296eadff302SIvan T. Ivanov 	*groups = pmic_gpio_groups;
297eadff302SIvan T. Ivanov 	*num_qgroups = pctldev->desc->npins;
298eadff302SIvan T. Ivanov 	return 0;
299eadff302SIvan T. Ivanov }
300eadff302SIvan T. Ivanov 
301eadff302SIvan T. Ivanov static int pmic_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned function,
302eadff302SIvan T. Ivanov 				unsigned pin)
303eadff302SIvan T. Ivanov {
304eadff302SIvan T. Ivanov 	struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev);
305eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad;
306eadff302SIvan T. Ivanov 	unsigned int val;
307eadff302SIvan T. Ivanov 	int ret;
308eadff302SIvan T. Ivanov 
309d7b5f5ccSFenglin Wu 	if (function > PMIC_GPIO_FUNC_INDEX_DTEST4) {
310d7b5f5ccSFenglin Wu 		pr_err("function: %d is not defined\n", function);
311d7b5f5ccSFenglin Wu 		return -EINVAL;
312d7b5f5ccSFenglin Wu 	}
313d7b5f5ccSFenglin Wu 
314eadff302SIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
315d7b5f5ccSFenglin Wu 	/*
316d7b5f5ccSFenglin Wu 	 * Non-LV/MV subtypes only support 2 special functions,
317d7b5f5ccSFenglin Wu 	 * offsetting the dtestx function values by 2
318d7b5f5ccSFenglin Wu 	 */
319d7b5f5ccSFenglin Wu 	if (!pad->lv_mv_type) {
320d7b5f5ccSFenglin Wu 		if (function == PMIC_GPIO_FUNC_INDEX_FUNC3 ||
321d7b5f5ccSFenglin Wu 				function == PMIC_GPIO_FUNC_INDEX_FUNC4) {
322d7b5f5ccSFenglin Wu 			pr_err("LV/MV subtype doesn't have func3/func4\n");
323d7b5f5ccSFenglin Wu 			return -EINVAL;
324d7b5f5ccSFenglin Wu 		}
325d7b5f5ccSFenglin Wu 		if (function >= PMIC_GPIO_FUNC_INDEX_DTEST1)
326d7b5f5ccSFenglin Wu 			function -= (PMIC_GPIO_FUNC_INDEX_DTEST1 -
327d7b5f5ccSFenglin Wu 					PMIC_GPIO_FUNC_INDEX_FUNC3);
328d7b5f5ccSFenglin Wu 	}
329eadff302SIvan T. Ivanov 
330eadff302SIvan T. Ivanov 	pad->function = function;
331eadff302SIvan T. Ivanov 
332d7b5f5ccSFenglin Wu 	if (pad->analog_pass)
333d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_ANALOG_PASS_THRU;
334d7b5f5ccSFenglin Wu 	else if (pad->output_enabled && pad->input_enabled)
335d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT;
336d7b5f5ccSFenglin Wu 	else if (pad->output_enabled)
337d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_OUTPUT;
338eadff302SIvan T. Ivanov 	else
339d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_INPUT;
340eadff302SIvan T. Ivanov 
341d7b5f5ccSFenglin Wu 	if (pad->lv_mv_type) {
342d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
343d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_MODE_CTL, val);
344d7b5f5ccSFenglin Wu 		if (ret < 0)
345d7b5f5ccSFenglin Wu 			return ret;
346d7b5f5ccSFenglin Wu 
347d7b5f5ccSFenglin Wu 		val = pad->atest - 1;
348d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
349d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL, val);
350d7b5f5ccSFenglin Wu 		if (ret < 0)
351d7b5f5ccSFenglin Wu 			return ret;
352d7b5f5ccSFenglin Wu 
353d7b5f5ccSFenglin Wu 		val = pad->out_value
354d7b5f5ccSFenglin Wu 			<< PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT;
355d7b5f5ccSFenglin Wu 		val |= pad->function
356d7b5f5ccSFenglin Wu 			& PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK;
357d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
358d7b5f5ccSFenglin Wu 			PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL, val);
359d7b5f5ccSFenglin Wu 		if (ret < 0)
360d7b5f5ccSFenglin Wu 			return ret;
361d7b5f5ccSFenglin Wu 	} else {
362dc391502SIvan T. Ivanov 		val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT;
363eadff302SIvan T. Ivanov 		val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT;
364eadff302SIvan T. Ivanov 		val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT;
365eadff302SIvan T. Ivanov 
366eadff302SIvan T. Ivanov 		ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val);
367eadff302SIvan T. Ivanov 		if (ret < 0)
368eadff302SIvan T. Ivanov 			return ret;
369d7b5f5ccSFenglin Wu 	}
370eadff302SIvan T. Ivanov 
371eadff302SIvan T. Ivanov 	val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT;
372eadff302SIvan T. Ivanov 
373eadff302SIvan T. Ivanov 	return pmic_gpio_write(state, pad, PMIC_GPIO_REG_EN_CTL, val);
374eadff302SIvan T. Ivanov }
375eadff302SIvan T. Ivanov 
376eadff302SIvan T. Ivanov static const struct pinmux_ops pmic_gpio_pinmux_ops = {
377eadff302SIvan T. Ivanov 	.get_functions_count	= pmic_gpio_get_functions_count,
378eadff302SIvan T. Ivanov 	.get_function_name	= pmic_gpio_get_function_name,
379eadff302SIvan T. Ivanov 	.get_function_groups	= pmic_gpio_get_function_groups,
380eadff302SIvan T. Ivanov 	.set_mux		= pmic_gpio_set_mux,
381eadff302SIvan T. Ivanov };
382eadff302SIvan T. Ivanov 
383eadff302SIvan T. Ivanov static int pmic_gpio_config_get(struct pinctrl_dev *pctldev,
384eadff302SIvan T. Ivanov 				unsigned int pin, unsigned long *config)
385eadff302SIvan T. Ivanov {
386eadff302SIvan T. Ivanov 	unsigned param = pinconf_to_config_param(*config);
387eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad;
388eadff302SIvan T. Ivanov 	unsigned arg;
389eadff302SIvan T. Ivanov 
390eadff302SIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
391eadff302SIvan T. Ivanov 
392eadff302SIvan T. Ivanov 	switch (param) {
393eadff302SIvan T. Ivanov 	case PIN_CONFIG_DRIVE_PUSH_PULL:
3941cf86bc2SDouglas Anderson 		if (pad->buffer_type != PMIC_GPIO_OUT_BUF_CMOS)
3951cf86bc2SDouglas Anderson 			return -EINVAL;
3961cf86bc2SDouglas Anderson 		arg = 1;
397eadff302SIvan T. Ivanov 		break;
398eadff302SIvan T. Ivanov 	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
3991cf86bc2SDouglas Anderson 		if (pad->buffer_type != PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS)
4001cf86bc2SDouglas Anderson 			return -EINVAL;
4011cf86bc2SDouglas Anderson 		arg = 1;
402eadff302SIvan T. Ivanov 		break;
403eadff302SIvan T. Ivanov 	case PIN_CONFIG_DRIVE_OPEN_SOURCE:
4041cf86bc2SDouglas Anderson 		if (pad->buffer_type != PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS)
4051cf86bc2SDouglas Anderson 			return -EINVAL;
4061cf86bc2SDouglas Anderson 		arg = 1;
407eadff302SIvan T. Ivanov 		break;
408eadff302SIvan T. Ivanov 	case PIN_CONFIG_BIAS_PULL_DOWN:
4091cf86bc2SDouglas Anderson 		if (pad->pullup != PMIC_GPIO_PULL_DOWN)
4101cf86bc2SDouglas Anderson 			return -EINVAL;
4111cf86bc2SDouglas Anderson 		arg = 1;
412eadff302SIvan T. Ivanov 		break;
413eadff302SIvan T. Ivanov 	case PIN_CONFIG_BIAS_DISABLE:
4141cf86bc2SDouglas Anderson 		if (pad->pullup != PMIC_GPIO_PULL_DISABLE)
4151cf86bc2SDouglas Anderson 			return -EINVAL;
4161cf86bc2SDouglas Anderson 		arg = 1;
417eadff302SIvan T. Ivanov 		break;
418eadff302SIvan T. Ivanov 	case PIN_CONFIG_BIAS_PULL_UP:
4191cf86bc2SDouglas Anderson 		if (pad->pullup != PMIC_GPIO_PULL_UP_30)
4201cf86bc2SDouglas Anderson 			return -EINVAL;
4211cf86bc2SDouglas Anderson 		arg = 1;
422eadff302SIvan T. Ivanov 		break;
423eadff302SIvan T. Ivanov 	case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
4241cf86bc2SDouglas Anderson 		if (pad->is_enabled)
4251cf86bc2SDouglas Anderson 			return -EINVAL;
4261cf86bc2SDouglas Anderson 		arg = 1;
427eadff302SIvan T. Ivanov 		break;
428eadff302SIvan T. Ivanov 	case PIN_CONFIG_POWER_SOURCE:
429eadff302SIvan T. Ivanov 		arg = pad->power_source;
430eadff302SIvan T. Ivanov 		break;
431eadff302SIvan T. Ivanov 	case PIN_CONFIG_INPUT_ENABLE:
4321cf86bc2SDouglas Anderson 		if (!pad->input_enabled)
4331cf86bc2SDouglas Anderson 			return -EINVAL;
4341cf86bc2SDouglas Anderson 		arg = 1;
435eadff302SIvan T. Ivanov 		break;
436eadff302SIvan T. Ivanov 	case PIN_CONFIG_OUTPUT:
437eadff302SIvan T. Ivanov 		arg = pad->out_value;
438eadff302SIvan T. Ivanov 		break;
439eadff302SIvan T. Ivanov 	case PMIC_GPIO_CONF_PULL_UP:
440eadff302SIvan T. Ivanov 		arg = pad->pullup;
441eadff302SIvan T. Ivanov 		break;
442eadff302SIvan T. Ivanov 	case PMIC_GPIO_CONF_STRENGTH:
443eadff302SIvan T. Ivanov 		arg = pad->strength;
444eadff302SIvan T. Ivanov 		break;
445d7b5f5ccSFenglin Wu 	case PMIC_GPIO_CONF_ATEST:
446d7b5f5ccSFenglin Wu 		arg = pad->atest;
447d7b5f5ccSFenglin Wu 		break;
448d7b5f5ccSFenglin Wu 	case PMIC_GPIO_CONF_ANALOG_PASS:
449d7b5f5ccSFenglin Wu 		arg = pad->analog_pass;
450d7b5f5ccSFenglin Wu 		break;
451223463fcSFenglin Wu 	case PMIC_GPIO_CONF_DTEST_BUFFER:
452223463fcSFenglin Wu 		arg = pad->dtest_buffer;
453223463fcSFenglin Wu 		break;
454eadff302SIvan T. Ivanov 	default:
455eadff302SIvan T. Ivanov 		return -EINVAL;
456eadff302SIvan T. Ivanov 	}
457eadff302SIvan T. Ivanov 
458eadff302SIvan T. Ivanov 	*config = pinconf_to_config_packed(param, arg);
459eadff302SIvan T. Ivanov 	return 0;
460eadff302SIvan T. Ivanov }
461eadff302SIvan T. Ivanov 
462eadff302SIvan T. Ivanov static int pmic_gpio_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
463eadff302SIvan T. Ivanov 				unsigned long *configs, unsigned nconfs)
464eadff302SIvan T. Ivanov {
465eadff302SIvan T. Ivanov 	struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev);
466eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad;
467eadff302SIvan T. Ivanov 	unsigned param, arg;
468eadff302SIvan T. Ivanov 	unsigned int val;
469eadff302SIvan T. Ivanov 	int i, ret;
470eadff302SIvan T. Ivanov 
471eadff302SIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
472eadff302SIvan T. Ivanov 
4736cb74f44SFenglin Wu 	pad->is_enabled = true;
474eadff302SIvan T. Ivanov 	for (i = 0; i < nconfs; i++) {
475eadff302SIvan T. Ivanov 		param = pinconf_to_config_param(configs[i]);
476eadff302SIvan T. Ivanov 		arg = pinconf_to_config_argument(configs[i]);
477eadff302SIvan T. Ivanov 
478eadff302SIvan T. Ivanov 		switch (param) {
479eadff302SIvan T. Ivanov 		case PIN_CONFIG_DRIVE_PUSH_PULL:
480eadff302SIvan T. Ivanov 			pad->buffer_type = PMIC_GPIO_OUT_BUF_CMOS;
481eadff302SIvan T. Ivanov 			break;
482eadff302SIvan T. Ivanov 		case PIN_CONFIG_DRIVE_OPEN_DRAIN:
483eadff302SIvan T. Ivanov 			if (!pad->have_buffer)
484eadff302SIvan T. Ivanov 				return -EINVAL;
485eadff302SIvan T. Ivanov 			pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS;
486eadff302SIvan T. Ivanov 			break;
487eadff302SIvan T. Ivanov 		case PIN_CONFIG_DRIVE_OPEN_SOURCE:
488eadff302SIvan T. Ivanov 			if (!pad->have_buffer)
489eadff302SIvan T. Ivanov 				return -EINVAL;
490eadff302SIvan T. Ivanov 			pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS;
491eadff302SIvan T. Ivanov 			break;
492eadff302SIvan T. Ivanov 		case PIN_CONFIG_BIAS_DISABLE:
493eadff302SIvan T. Ivanov 			pad->pullup = PMIC_GPIO_PULL_DISABLE;
494eadff302SIvan T. Ivanov 			break;
495eadff302SIvan T. Ivanov 		case PIN_CONFIG_BIAS_PULL_UP:
496eadff302SIvan T. Ivanov 			pad->pullup = PMIC_GPIO_PULL_UP_30;
497eadff302SIvan T. Ivanov 			break;
498eadff302SIvan T. Ivanov 		case PIN_CONFIG_BIAS_PULL_DOWN:
499eadff302SIvan T. Ivanov 			if (arg)
500eadff302SIvan T. Ivanov 				pad->pullup = PMIC_GPIO_PULL_DOWN;
501eadff302SIvan T. Ivanov 			else
502eadff302SIvan T. Ivanov 				pad->pullup = PMIC_GPIO_PULL_DISABLE;
503eadff302SIvan T. Ivanov 			break;
504eadff302SIvan T. Ivanov 		case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
505eadff302SIvan T. Ivanov 			pad->is_enabled = false;
506eadff302SIvan T. Ivanov 			break;
507eadff302SIvan T. Ivanov 		case PIN_CONFIG_POWER_SOURCE:
5084e83ac4cSFenglin Wu 			if (arg >= pad->num_sources)
509eadff302SIvan T. Ivanov 				return -EINVAL;
510eadff302SIvan T. Ivanov 			pad->power_source = arg;
511eadff302SIvan T. Ivanov 			break;
512eadff302SIvan T. Ivanov 		case PIN_CONFIG_INPUT_ENABLE:
513eadff302SIvan T. Ivanov 			pad->input_enabled = arg ? true : false;
514eadff302SIvan T. Ivanov 			break;
515eadff302SIvan T. Ivanov 		case PIN_CONFIG_OUTPUT:
516eadff302SIvan T. Ivanov 			pad->output_enabled = true;
517eadff302SIvan T. Ivanov 			pad->out_value = arg;
518eadff302SIvan T. Ivanov 			break;
519eadff302SIvan T. Ivanov 		case PMIC_GPIO_CONF_PULL_UP:
520eadff302SIvan T. Ivanov 			if (arg > PMIC_GPIO_PULL_UP_1P5_30)
521eadff302SIvan T. Ivanov 				return -EINVAL;
522eadff302SIvan T. Ivanov 			pad->pullup = arg;
523eadff302SIvan T. Ivanov 			break;
524eadff302SIvan T. Ivanov 		case PMIC_GPIO_CONF_STRENGTH:
525eadff302SIvan T. Ivanov 			if (arg > PMIC_GPIO_STRENGTH_LOW)
526eadff302SIvan T. Ivanov 				return -EINVAL;
527eadff302SIvan T. Ivanov 			pad->strength = arg;
528eadff302SIvan T. Ivanov 			break;
529d7b5f5ccSFenglin Wu 		case PMIC_GPIO_CONF_ATEST:
530d7b5f5ccSFenglin Wu 			if (!pad->lv_mv_type || arg > 4)
531d7b5f5ccSFenglin Wu 				return -EINVAL;
532d7b5f5ccSFenglin Wu 			pad->atest = arg;
533d7b5f5ccSFenglin Wu 			break;
534d7b5f5ccSFenglin Wu 		case PMIC_GPIO_CONF_ANALOG_PASS:
535d7b5f5ccSFenglin Wu 			if (!pad->lv_mv_type)
536d7b5f5ccSFenglin Wu 				return -EINVAL;
537d7b5f5ccSFenglin Wu 			pad->analog_pass = true;
538d7b5f5ccSFenglin Wu 			break;
539223463fcSFenglin Wu 		case PMIC_GPIO_CONF_DTEST_BUFFER:
540223463fcSFenglin Wu 			if (arg > 4)
541223463fcSFenglin Wu 				return -EINVAL;
542223463fcSFenglin Wu 			pad->dtest_buffer = arg;
543223463fcSFenglin Wu 			break;
544eadff302SIvan T. Ivanov 		default:
545eadff302SIvan T. Ivanov 			return -EINVAL;
546eadff302SIvan T. Ivanov 		}
547eadff302SIvan T. Ivanov 	}
548eadff302SIvan T. Ivanov 
549eadff302SIvan T. Ivanov 	val = pad->power_source << PMIC_GPIO_REG_VIN_SHIFT;
550eadff302SIvan T. Ivanov 
551eadff302SIvan T. Ivanov 	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL, val);
552eadff302SIvan T. Ivanov 	if (ret < 0)
553eadff302SIvan T. Ivanov 		return ret;
554eadff302SIvan T. Ivanov 
555eadff302SIvan T. Ivanov 	val = pad->pullup << PMIC_GPIO_REG_PULL_SHIFT;
556eadff302SIvan T. Ivanov 
557eadff302SIvan T. Ivanov 	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_PULL_CTL, val);
558eadff302SIvan T. Ivanov 	if (ret < 0)
559eadff302SIvan T. Ivanov 		return ret;
560eadff302SIvan T. Ivanov 
561eadff302SIvan T. Ivanov 	val = pad->buffer_type << PMIC_GPIO_REG_OUT_TYPE_SHIFT;
562982df6aeSIvan T. Ivanov 	val |= pad->strength << PMIC_GPIO_REG_OUT_STRENGTH_SHIFT;
563eadff302SIvan T. Ivanov 
564eadff302SIvan T. Ivanov 	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL, val);
565eadff302SIvan T. Ivanov 	if (ret < 0)
566eadff302SIvan T. Ivanov 		return ret;
567eadff302SIvan T. Ivanov 
568223463fcSFenglin Wu 	if (pad->dtest_buffer == 0) {
569223463fcSFenglin Wu 		val = 0;
570223463fcSFenglin Wu 	} else {
571223463fcSFenglin Wu 		if (pad->lv_mv_type) {
572223463fcSFenglin Wu 			val = pad->dtest_buffer - 1;
573223463fcSFenglin Wu 			val |= PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN;
574223463fcSFenglin Wu 		} else {
575223463fcSFenglin Wu 			val = BIT(pad->dtest_buffer - 1);
576223463fcSFenglin Wu 		}
577223463fcSFenglin Wu 	}
578223463fcSFenglin Wu 	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_IN_CTL, val);
579223463fcSFenglin Wu 	if (ret < 0)
580223463fcSFenglin Wu 		return ret;
581223463fcSFenglin Wu 
582d7b5f5ccSFenglin Wu 	if (pad->analog_pass)
583d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_ANALOG_PASS_THRU;
584d7b5f5ccSFenglin Wu 	else if (pad->output_enabled && pad->input_enabled)
585d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT;
586d7b5f5ccSFenglin Wu 	else if (pad->output_enabled)
587d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_OUTPUT;
588eadff302SIvan T. Ivanov 	else
589d7b5f5ccSFenglin Wu 		val = PMIC_GPIO_MODE_DIGITAL_INPUT;
590eadff302SIvan T. Ivanov 
591d7b5f5ccSFenglin Wu 	if (pad->lv_mv_type) {
592d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
593d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_MODE_CTL, val);
594d7b5f5ccSFenglin Wu 		if (ret < 0)
595d7b5f5ccSFenglin Wu 			return ret;
596d7b5f5ccSFenglin Wu 
597d7b5f5ccSFenglin Wu 		val = pad->atest - 1;
598d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
599d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL, val);
600d7b5f5ccSFenglin Wu 		if (ret < 0)
601d7b5f5ccSFenglin Wu 			return ret;
602d7b5f5ccSFenglin Wu 
603d7b5f5ccSFenglin Wu 		val = pad->out_value
604d7b5f5ccSFenglin Wu 			<< PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT;
605d7b5f5ccSFenglin Wu 		val |= pad->function
606d7b5f5ccSFenglin Wu 			& PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK;
607d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad,
608d7b5f5ccSFenglin Wu 			PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL, val);
609d7b5f5ccSFenglin Wu 		if (ret < 0)
610d7b5f5ccSFenglin Wu 			return ret;
611d7b5f5ccSFenglin Wu 	} else {
612eadff302SIvan T. Ivanov 		val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT;
613eadff302SIvan T. Ivanov 		val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT;
614eadff302SIvan T. Ivanov 		val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT;
615eadff302SIvan T. Ivanov 
616d7b5f5ccSFenglin Wu 		ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val);
617d7b5f5ccSFenglin Wu 		if (ret < 0)
618d7b5f5ccSFenglin Wu 			return ret;
619d7b5f5ccSFenglin Wu 	}
620d7b5f5ccSFenglin Wu 
6216cb74f44SFenglin Wu 	val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT;
6226cb74f44SFenglin Wu 
6236cb74f44SFenglin Wu 	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_EN_CTL, val);
6246cb74f44SFenglin Wu 
625d7b5f5ccSFenglin Wu 	return ret;
626eadff302SIvan T. Ivanov }
627eadff302SIvan T. Ivanov 
628eadff302SIvan T. Ivanov static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev,
629eadff302SIvan T. Ivanov 				      struct seq_file *s, unsigned pin)
630eadff302SIvan T. Ivanov {
631eadff302SIvan T. Ivanov 	struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev);
632eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad;
633d7b5f5ccSFenglin Wu 	int ret, val, function;
634eadff302SIvan T. Ivanov 
635eadff302SIvan T. Ivanov 	static const char *const biases[] = {
636eadff302SIvan T. Ivanov 		"pull-up 30uA", "pull-up 1.5uA", "pull-up 31.5uA",
637eadff302SIvan T. Ivanov 		"pull-up 1.5uA + 30uA boost", "pull-down 10uA", "no pull"
638eadff302SIvan T. Ivanov 	};
639eadff302SIvan T. Ivanov 	static const char *const buffer_types[] = {
640eadff302SIvan T. Ivanov 		"push-pull", "open-drain", "open-source"
641eadff302SIvan T. Ivanov 	};
642eadff302SIvan T. Ivanov 	static const char *const strengths[] = {
643eadff302SIvan T. Ivanov 		"no", "high", "medium", "low"
644eadff302SIvan T. Ivanov 	};
645eadff302SIvan T. Ivanov 
646eadff302SIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
647eadff302SIvan T. Ivanov 
648eadff302SIvan T. Ivanov 	seq_printf(s, " gpio%-2d:", pin + PMIC_GPIO_PHYSICAL_OFFSET);
649eadff302SIvan T. Ivanov 
650eadff302SIvan T. Ivanov 	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_EN_CTL);
651eadff302SIvan T. Ivanov 
652eadff302SIvan T. Ivanov 	if (val < 0 || !(val >> PMIC_GPIO_REG_MASTER_EN_SHIFT)) {
653eadff302SIvan T. Ivanov 		seq_puts(s, " ---");
654eadff302SIvan T. Ivanov 	} else {
65524a66618SIvan T. Ivanov 		if (pad->input_enabled) {
656eadff302SIvan T. Ivanov 			ret = pmic_gpio_read(state, pad, PMIC_MPP_REG_RT_STS);
65724a66618SIvan T. Ivanov 			if (ret < 0)
65824a66618SIvan T. Ivanov 				return;
65924a66618SIvan T. Ivanov 
660eadff302SIvan T. Ivanov 			ret &= PMIC_MPP_REG_RT_STS_VAL_MASK;
661eadff302SIvan T. Ivanov 			pad->out_value = ret;
662eadff302SIvan T. Ivanov 		}
663d7b5f5ccSFenglin Wu 		/*
664d7b5f5ccSFenglin Wu 		 * For the non-LV/MV subtypes only 2 special functions are
665d7b5f5ccSFenglin Wu 		 * available, offsetting the dtest function values by 2.
666d7b5f5ccSFenglin Wu 		 */
667d7b5f5ccSFenglin Wu 		function = pad->function;
668d7b5f5ccSFenglin Wu 		if (!pad->lv_mv_type &&
669d7b5f5ccSFenglin Wu 				pad->function >= PMIC_GPIO_FUNC_INDEX_FUNC3)
670d7b5f5ccSFenglin Wu 			function += PMIC_GPIO_FUNC_INDEX_DTEST1 -
671d7b5f5ccSFenglin Wu 				PMIC_GPIO_FUNC_INDEX_FUNC3;
672eadff302SIvan T. Ivanov 
673d7b5f5ccSFenglin Wu 		if (pad->analog_pass)
674d7b5f5ccSFenglin Wu 			seq_puts(s, " analog-pass");
675d7b5f5ccSFenglin Wu 		else
676d7b5f5ccSFenglin Wu 			seq_printf(s, " %-4s",
677d7b5f5ccSFenglin Wu 					pad->output_enabled ? "out" : "in");
678d7b5f5ccSFenglin Wu 		seq_printf(s, " %-7s", pmic_gpio_functions[function]);
679eadff302SIvan T. Ivanov 		seq_printf(s, " vin-%d", pad->power_source);
680eadff302SIvan T. Ivanov 		seq_printf(s, " %-27s", biases[pad->pullup]);
681eadff302SIvan T. Ivanov 		seq_printf(s, " %-10s", buffer_types[pad->buffer_type]);
682eadff302SIvan T. Ivanov 		seq_printf(s, " %-4s", pad->out_value ? "high" : "low");
683eadff302SIvan T. Ivanov 		seq_printf(s, " %-7s", strengths[pad->strength]);
684d7b5f5ccSFenglin Wu 		seq_printf(s, " atest-%d", pad->atest);
685223463fcSFenglin Wu 		seq_printf(s, " dtest-%d", pad->dtest_buffer);
686eadff302SIvan T. Ivanov 	}
687eadff302SIvan T. Ivanov }
688eadff302SIvan T. Ivanov 
689eadff302SIvan T. Ivanov static const struct pinconf_ops pmic_gpio_pinconf_ops = {
6907382b623SSoren Brinkmann 	.is_generic			= true,
691eadff302SIvan T. Ivanov 	.pin_config_group_get		= pmic_gpio_config_get,
692eadff302SIvan T. Ivanov 	.pin_config_group_set		= pmic_gpio_config_set,
693eadff302SIvan T. Ivanov 	.pin_config_group_dbg_show	= pmic_gpio_config_dbg_show,
694eadff302SIvan T. Ivanov };
695eadff302SIvan T. Ivanov 
696eadff302SIvan T. Ivanov static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned pin)
697eadff302SIvan T. Ivanov {
698c52d9df1SLinus Walleij 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
699eadff302SIvan T. Ivanov 	unsigned long config;
700eadff302SIvan T. Ivanov 
701eadff302SIvan T. Ivanov 	config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1);
702eadff302SIvan T. Ivanov 
703eadff302SIvan T. Ivanov 	return pmic_gpio_config_set(state->ctrl, pin, &config, 1);
704eadff302SIvan T. Ivanov }
705eadff302SIvan T. Ivanov 
706eadff302SIvan T. Ivanov static int pmic_gpio_direction_output(struct gpio_chip *chip,
707eadff302SIvan T. Ivanov 				      unsigned pin, int val)
708eadff302SIvan T. Ivanov {
709c52d9df1SLinus Walleij 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
710eadff302SIvan T. Ivanov 	unsigned long config;
711eadff302SIvan T. Ivanov 
712eadff302SIvan T. Ivanov 	config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val);
713eadff302SIvan T. Ivanov 
714eadff302SIvan T. Ivanov 	return pmic_gpio_config_set(state->ctrl, pin, &config, 1);
715eadff302SIvan T. Ivanov }
716eadff302SIvan T. Ivanov 
717eadff302SIvan T. Ivanov static int pmic_gpio_get(struct gpio_chip *chip, unsigned pin)
718eadff302SIvan T. Ivanov {
719c52d9df1SLinus Walleij 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
720eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad;
721eadff302SIvan T. Ivanov 	int ret;
722eadff302SIvan T. Ivanov 
723eadff302SIvan T. Ivanov 	pad = state->ctrl->desc->pins[pin].drv_data;
724eadff302SIvan T. Ivanov 
725eadff302SIvan T. Ivanov 	if (!pad->is_enabled)
726eadff302SIvan T. Ivanov 		return -EINVAL;
727eadff302SIvan T. Ivanov 
728eadff302SIvan T. Ivanov 	if (pad->input_enabled) {
729eadff302SIvan T. Ivanov 		ret = pmic_gpio_read(state, pad, PMIC_MPP_REG_RT_STS);
730eadff302SIvan T. Ivanov 		if (ret < 0)
731eadff302SIvan T. Ivanov 			return ret;
732eadff302SIvan T. Ivanov 
733eadff302SIvan T. Ivanov 		pad->out_value = ret & PMIC_MPP_REG_RT_STS_VAL_MASK;
734eadff302SIvan T. Ivanov 	}
735eadff302SIvan T. Ivanov 
73686c1a219SLinus Walleij 	return !!pad->out_value;
737eadff302SIvan T. Ivanov }
738eadff302SIvan T. Ivanov 
739eadff302SIvan T. Ivanov static void pmic_gpio_set(struct gpio_chip *chip, unsigned pin, int value)
740eadff302SIvan T. Ivanov {
741c52d9df1SLinus Walleij 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
742eadff302SIvan T. Ivanov 	unsigned long config;
743eadff302SIvan T. Ivanov 
744eadff302SIvan T. Ivanov 	config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value);
745eadff302SIvan T. Ivanov 
746eadff302SIvan T. Ivanov 	pmic_gpio_config_set(state->ctrl, pin, &config, 1);
747eadff302SIvan T. Ivanov }
748eadff302SIvan T. Ivanov 
749eadff302SIvan T. Ivanov static int pmic_gpio_of_xlate(struct gpio_chip *chip,
750eadff302SIvan T. Ivanov 			      const struct of_phandle_args *gpio_desc,
751eadff302SIvan T. Ivanov 			      u32 *flags)
752eadff302SIvan T. Ivanov {
753eadff302SIvan T. Ivanov 	if (chip->of_gpio_n_cells < 2)
754eadff302SIvan T. Ivanov 		return -EINVAL;
755eadff302SIvan T. Ivanov 
756eadff302SIvan T. Ivanov 	if (flags)
757eadff302SIvan T. Ivanov 		*flags = gpio_desc->args[1];
758eadff302SIvan T. Ivanov 
759eadff302SIvan T. Ivanov 	return gpio_desc->args[0] - PMIC_GPIO_PHYSICAL_OFFSET;
760eadff302SIvan T. Ivanov }
761eadff302SIvan T. Ivanov 
762eadff302SIvan T. Ivanov static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned pin)
763eadff302SIvan T. Ivanov {
764c52d9df1SLinus Walleij 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
765ca69e2d1SBrian Masney 	struct irq_fwspec fwspec;
766eadff302SIvan T. Ivanov 
767ca69e2d1SBrian Masney 	fwspec.fwnode = state->fwnode;
768ca69e2d1SBrian Masney 	fwspec.param_count = 2;
769ca69e2d1SBrian Masney 	fwspec.param[0] = pin + PMIC_GPIO_PHYSICAL_OFFSET;
770ca69e2d1SBrian Masney 	/*
771ca69e2d1SBrian Masney 	 * Set the type to a safe value temporarily. This will be overwritten
772ca69e2d1SBrian Masney 	 * later with the proper value by irq_set_type.
773ca69e2d1SBrian Masney 	 */
774ca69e2d1SBrian Masney 	fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
775eadff302SIvan T. Ivanov 
776ca69e2d1SBrian Masney 	return irq_create_fwspec_mapping(&fwspec);
777eadff302SIvan T. Ivanov }
778eadff302SIvan T. Ivanov 
779eadff302SIvan T. Ivanov static void pmic_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
780eadff302SIvan T. Ivanov {
781c52d9df1SLinus Walleij 	struct pmic_gpio_state *state = gpiochip_get_data(chip);
782eadff302SIvan T. Ivanov 	unsigned i;
783eadff302SIvan T. Ivanov 
784eadff302SIvan T. Ivanov 	for (i = 0; i < chip->ngpio; i++) {
785eadff302SIvan T. Ivanov 		pmic_gpio_config_dbg_show(state->ctrl, s, i);
786eadff302SIvan T. Ivanov 		seq_puts(s, "\n");
787eadff302SIvan T. Ivanov 	}
788eadff302SIvan T. Ivanov }
789eadff302SIvan T. Ivanov 
790eadff302SIvan T. Ivanov static const struct gpio_chip pmic_gpio_gpio_template = {
791eadff302SIvan T. Ivanov 	.direction_input	= pmic_gpio_direction_input,
792eadff302SIvan T. Ivanov 	.direction_output	= pmic_gpio_direction_output,
793eadff302SIvan T. Ivanov 	.get			= pmic_gpio_get,
794eadff302SIvan T. Ivanov 	.set			= pmic_gpio_set,
79598c85d58SJonas Gorski 	.request		= gpiochip_generic_request,
79698c85d58SJonas Gorski 	.free			= gpiochip_generic_free,
797eadff302SIvan T. Ivanov 	.of_xlate		= pmic_gpio_of_xlate,
798eadff302SIvan T. Ivanov 	.to_irq			= pmic_gpio_to_irq,
799eadff302SIvan T. Ivanov 	.dbg_show		= pmic_gpio_dbg_show,
800eadff302SIvan T. Ivanov };
801eadff302SIvan T. Ivanov 
802eadff302SIvan T. Ivanov static int pmic_gpio_populate(struct pmic_gpio_state *state,
803eadff302SIvan T. Ivanov 			      struct pmic_gpio_pad *pad)
804eadff302SIvan T. Ivanov {
805eadff302SIvan T. Ivanov 	int type, subtype, val, dir;
806eadff302SIvan T. Ivanov 
807eadff302SIvan T. Ivanov 	type = pmic_gpio_read(state, pad, PMIC_GPIO_REG_TYPE);
808eadff302SIvan T. Ivanov 	if (type < 0)
809eadff302SIvan T. Ivanov 		return type;
810eadff302SIvan T. Ivanov 
811eadff302SIvan T. Ivanov 	if (type != PMIC_GPIO_TYPE) {
812eadff302SIvan T. Ivanov 		dev_err(state->dev, "incorrect block type 0x%x at 0x%x\n",
813eadff302SIvan T. Ivanov 			type, pad->base);
814eadff302SIvan T. Ivanov 		return -ENODEV;
815eadff302SIvan T. Ivanov 	}
816eadff302SIvan T. Ivanov 
817eadff302SIvan T. Ivanov 	subtype = pmic_gpio_read(state, pad, PMIC_GPIO_REG_SUBTYPE);
818eadff302SIvan T. Ivanov 	if (subtype < 0)
819eadff302SIvan T. Ivanov 		return subtype;
820eadff302SIvan T. Ivanov 
821eadff302SIvan T. Ivanov 	switch (subtype) {
822eadff302SIvan T. Ivanov 	case PMIC_GPIO_SUBTYPE_GPIO_4CH:
823eadff302SIvan T. Ivanov 		pad->have_buffer = true;
824eadff302SIvan T. Ivanov 	case PMIC_GPIO_SUBTYPE_GPIOC_4CH:
825eadff302SIvan T. Ivanov 		pad->num_sources = 4;
826eadff302SIvan T. Ivanov 		break;
827eadff302SIvan T. Ivanov 	case PMIC_GPIO_SUBTYPE_GPIO_8CH:
828eadff302SIvan T. Ivanov 		pad->have_buffer = true;
829eadff302SIvan T. Ivanov 	case PMIC_GPIO_SUBTYPE_GPIOC_8CH:
830eadff302SIvan T. Ivanov 		pad->num_sources = 8;
831eadff302SIvan T. Ivanov 		break;
832d7b5f5ccSFenglin Wu 	case PMIC_GPIO_SUBTYPE_GPIO_LV:
833d7b5f5ccSFenglin Wu 		pad->num_sources = 1;
834d7b5f5ccSFenglin Wu 		pad->have_buffer = true;
835d7b5f5ccSFenglin Wu 		pad->lv_mv_type = true;
836d7b5f5ccSFenglin Wu 		break;
837d7b5f5ccSFenglin Wu 	case PMIC_GPIO_SUBTYPE_GPIO_MV:
838d7b5f5ccSFenglin Wu 		pad->num_sources = 2;
839d7b5f5ccSFenglin Wu 		pad->have_buffer = true;
840d7b5f5ccSFenglin Wu 		pad->lv_mv_type = true;
841d7b5f5ccSFenglin Wu 		break;
842eadff302SIvan T. Ivanov 	default:
843eadff302SIvan T. Ivanov 		dev_err(state->dev, "unknown GPIO type 0x%x\n", subtype);
844eadff302SIvan T. Ivanov 		return -ENODEV;
845eadff302SIvan T. Ivanov 	}
846eadff302SIvan T. Ivanov 
847d7b5f5ccSFenglin Wu 	if (pad->lv_mv_type) {
848d7b5f5ccSFenglin Wu 		val = pmic_gpio_read(state, pad,
849d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL);
850d7b5f5ccSFenglin Wu 		if (val < 0)
851d7b5f5ccSFenglin Wu 			return val;
852d7b5f5ccSFenglin Wu 
853d7b5f5ccSFenglin Wu 		pad->out_value = !!(val & PMIC_GPIO_LV_MV_OUTPUT_INVERT);
854d7b5f5ccSFenglin Wu 		pad->function = val & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK;
855d7b5f5ccSFenglin Wu 
856d7b5f5ccSFenglin Wu 		val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL);
857d7b5f5ccSFenglin Wu 		if (val < 0)
858d7b5f5ccSFenglin Wu 			return val;
859d7b5f5ccSFenglin Wu 
860d7b5f5ccSFenglin Wu 		dir = val & PMIC_GPIO_REG_LV_MV_MODE_DIR_MASK;
861d7b5f5ccSFenglin Wu 	} else {
862eadff302SIvan T. Ivanov 		val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL);
863eadff302SIvan T. Ivanov 		if (val < 0)
864eadff302SIvan T. Ivanov 			return val;
865eadff302SIvan T. Ivanov 
866eadff302SIvan T. Ivanov 		pad->out_value = val & PMIC_GPIO_REG_MODE_VALUE_SHIFT;
867eadff302SIvan T. Ivanov 
868eadff302SIvan T. Ivanov 		dir = val >> PMIC_GPIO_REG_MODE_DIR_SHIFT;
869eadff302SIvan T. Ivanov 		dir &= PMIC_GPIO_REG_MODE_DIR_MASK;
870d7b5f5ccSFenglin Wu 		pad->function = val >> PMIC_GPIO_REG_MODE_FUNCTION_SHIFT;
871d7b5f5ccSFenglin Wu 		pad->function &= PMIC_GPIO_REG_MODE_FUNCTION_MASK;
872d7b5f5ccSFenglin Wu 	}
873d7b5f5ccSFenglin Wu 
874eadff302SIvan T. Ivanov 	switch (dir) {
875d7b5f5ccSFenglin Wu 	case PMIC_GPIO_MODE_DIGITAL_INPUT:
876eadff302SIvan T. Ivanov 		pad->input_enabled = true;
877eadff302SIvan T. Ivanov 		pad->output_enabled = false;
878eadff302SIvan T. Ivanov 		break;
879d7b5f5ccSFenglin Wu 	case PMIC_GPIO_MODE_DIGITAL_OUTPUT:
880eadff302SIvan T. Ivanov 		pad->input_enabled = false;
881eadff302SIvan T. Ivanov 		pad->output_enabled = true;
882eadff302SIvan T. Ivanov 		break;
883d7b5f5ccSFenglin Wu 	case PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT:
884eadff302SIvan T. Ivanov 		pad->input_enabled = true;
885eadff302SIvan T. Ivanov 		pad->output_enabled = true;
886eadff302SIvan T. Ivanov 		break;
887d7b5f5ccSFenglin Wu 	case PMIC_GPIO_MODE_ANALOG_PASS_THRU:
888d7b5f5ccSFenglin Wu 		if (!pad->lv_mv_type)
889d7b5f5ccSFenglin Wu 			return -ENODEV;
890d7b5f5ccSFenglin Wu 		pad->analog_pass = true;
891d7b5f5ccSFenglin Wu 		break;
892eadff302SIvan T. Ivanov 	default:
893eadff302SIvan T. Ivanov 		dev_err(state->dev, "unknown GPIO direction\n");
894eadff302SIvan T. Ivanov 		return -ENODEV;
895eadff302SIvan T. Ivanov 	}
896eadff302SIvan T. Ivanov 
897eadff302SIvan T. Ivanov 	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL);
898eadff302SIvan T. Ivanov 	if (val < 0)
899eadff302SIvan T. Ivanov 		return val;
900eadff302SIvan T. Ivanov 
901eadff302SIvan T. Ivanov 	pad->power_source = val >> PMIC_GPIO_REG_VIN_SHIFT;
902eadff302SIvan T. Ivanov 	pad->power_source &= PMIC_GPIO_REG_VIN_MASK;
903eadff302SIvan T. Ivanov 
904eadff302SIvan T. Ivanov 	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_PULL_CTL);
905eadff302SIvan T. Ivanov 	if (val < 0)
906eadff302SIvan T. Ivanov 		return val;
907eadff302SIvan T. Ivanov 
908eadff302SIvan T. Ivanov 	pad->pullup = val >> PMIC_GPIO_REG_PULL_SHIFT;
909eadff302SIvan T. Ivanov 	pad->pullup &= PMIC_GPIO_REG_PULL_MASK;
910eadff302SIvan T. Ivanov 
911223463fcSFenglin Wu 	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_IN_CTL);
912223463fcSFenglin Wu 	if (val < 0)
913223463fcSFenglin Wu 		return val;
914223463fcSFenglin Wu 
915223463fcSFenglin Wu 	if (pad->lv_mv_type && (val & PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN))
916223463fcSFenglin Wu 		pad->dtest_buffer =
917223463fcSFenglin Wu 			(val & PMIC_GPIO_LV_MV_DIG_IN_DTEST_SEL_MASK) + 1;
918223463fcSFenglin Wu 	else if (!pad->lv_mv_type)
919223463fcSFenglin Wu 		pad->dtest_buffer = ffs(val);
920223463fcSFenglin Wu 	else
921223463fcSFenglin Wu 		pad->dtest_buffer = 0;
922223463fcSFenglin Wu 
923223463fcSFenglin Wu 	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL);
924223463fcSFenglin Wu 	if (val < 0)
925223463fcSFenglin Wu 		return val;
926223463fcSFenglin Wu 
927eadff302SIvan T. Ivanov 	pad->strength = val >> PMIC_GPIO_REG_OUT_STRENGTH_SHIFT;
928eadff302SIvan T. Ivanov 	pad->strength &= PMIC_GPIO_REG_OUT_STRENGTH_MASK;
929eadff302SIvan T. Ivanov 
930eadff302SIvan T. Ivanov 	pad->buffer_type = val >> PMIC_GPIO_REG_OUT_TYPE_SHIFT;
931eadff302SIvan T. Ivanov 	pad->buffer_type &= PMIC_GPIO_REG_OUT_TYPE_MASK;
932eadff302SIvan T. Ivanov 
933d7b5f5ccSFenglin Wu 	if (pad->lv_mv_type) {
934d7b5f5ccSFenglin Wu 		val = pmic_gpio_read(state, pad,
935d7b5f5ccSFenglin Wu 				PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL);
936d7b5f5ccSFenglin Wu 		if (val < 0)
937d7b5f5ccSFenglin Wu 			return val;
938d7b5f5ccSFenglin Wu 		pad->atest = (val & PMIC_GPIO_LV_MV_ANA_MUX_SEL_MASK) + 1;
939d7b5f5ccSFenglin Wu 	}
940d7b5f5ccSFenglin Wu 
941eadff302SIvan T. Ivanov 	/* Pin could be disabled with PIN_CONFIG_BIAS_HIGH_IMPEDANCE */
942eadff302SIvan T. Ivanov 	pad->is_enabled = true;
943eadff302SIvan T. Ivanov 	return 0;
944eadff302SIvan T. Ivanov }
945eadff302SIvan T. Ivanov 
946ca69e2d1SBrian Masney static struct irq_chip pmic_gpio_irq_chip = {
947ca69e2d1SBrian Masney 	.name = "spmi-gpio",
948ca69e2d1SBrian Masney 	.irq_ack = irq_chip_ack_parent,
949ca69e2d1SBrian Masney 	.irq_mask = irq_chip_mask_parent,
950ca69e2d1SBrian Masney 	.irq_unmask = irq_chip_unmask_parent,
951ca69e2d1SBrian Masney 	.irq_set_type = irq_chip_set_type_parent,
952ca69e2d1SBrian Masney 	.irq_set_wake = irq_chip_set_wake_parent,
953ca69e2d1SBrian Masney 	.flags = IRQCHIP_MASK_ON_SUSPEND,
954ca69e2d1SBrian Masney };
955ca69e2d1SBrian Masney 
956ca69e2d1SBrian Masney static int pmic_gpio_domain_translate(struct irq_domain *domain,
957ca69e2d1SBrian Masney 				      struct irq_fwspec *fwspec,
958ca69e2d1SBrian Masney 				      unsigned long *hwirq,
959ca69e2d1SBrian Masney 				      unsigned int *type)
960ca69e2d1SBrian Masney {
961ca69e2d1SBrian Masney 	struct pmic_gpio_state *state = container_of(domain->host_data,
962ca69e2d1SBrian Masney 						     struct pmic_gpio_state,
963ca69e2d1SBrian Masney 						     chip);
964ca69e2d1SBrian Masney 
965ca69e2d1SBrian Masney 	if (fwspec->param_count != 2 || fwspec->param[0] >= state->chip.ngpio)
966ca69e2d1SBrian Masney 		return -EINVAL;
967ca69e2d1SBrian Masney 
968ca69e2d1SBrian Masney 	*hwirq = fwspec->param[0] - PMIC_GPIO_PHYSICAL_OFFSET;
969ca69e2d1SBrian Masney 	*type = fwspec->param[1];
970ca69e2d1SBrian Masney 
971ca69e2d1SBrian Masney 	return 0;
972ca69e2d1SBrian Masney }
973ca69e2d1SBrian Masney 
974ca69e2d1SBrian Masney static int pmic_gpio_domain_alloc(struct irq_domain *domain, unsigned int virq,
975ca69e2d1SBrian Masney 				  unsigned int nr_irqs, void *data)
976ca69e2d1SBrian Masney {
977ca69e2d1SBrian Masney 	struct pmic_gpio_state *state = container_of(domain->host_data,
978ca69e2d1SBrian Masney 						     struct pmic_gpio_state,
979ca69e2d1SBrian Masney 						     chip);
980ca69e2d1SBrian Masney 	struct irq_fwspec *fwspec = data;
981ca69e2d1SBrian Masney 	struct irq_fwspec parent_fwspec;
982ca69e2d1SBrian Masney 	irq_hw_number_t hwirq;
983ca69e2d1SBrian Masney 	unsigned int type;
984ca69e2d1SBrian Masney 	int ret, i;
985ca69e2d1SBrian Masney 
986ca69e2d1SBrian Masney 	ret = pmic_gpio_domain_translate(domain, fwspec, &hwirq, &type);
987ca69e2d1SBrian Masney 	if (ret)
988ca69e2d1SBrian Masney 		return ret;
989ca69e2d1SBrian Masney 
990ca69e2d1SBrian Masney 	for (i = 0; i < nr_irqs; i++)
991ca69e2d1SBrian Masney 		irq_domain_set_info(domain, virq + i, hwirq + i,
992ca69e2d1SBrian Masney 				    &pmic_gpio_irq_chip, state,
993ca69e2d1SBrian Masney 				    handle_level_irq, NULL, NULL);
994ca69e2d1SBrian Masney 
995ca69e2d1SBrian Masney 	parent_fwspec.fwnode = domain->parent->fwnode;
996ca69e2d1SBrian Masney 	parent_fwspec.param_count = 4;
997ca69e2d1SBrian Masney 	parent_fwspec.param[0] = 0;
998ca69e2d1SBrian Masney 	parent_fwspec.param[1] = hwirq + 0xc0;
999ca69e2d1SBrian Masney 	parent_fwspec.param[2] = 0;
1000ca69e2d1SBrian Masney 	parent_fwspec.param[3] = fwspec->param[1];
1001ca69e2d1SBrian Masney 
1002ca69e2d1SBrian Masney 	return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
1003ca69e2d1SBrian Masney 					    &parent_fwspec);
1004ca69e2d1SBrian Masney }
1005ca69e2d1SBrian Masney 
1006ca69e2d1SBrian Masney static const struct irq_domain_ops pmic_gpio_domain_ops = {
1007ca69e2d1SBrian Masney 	.activate = gpiochip_irq_domain_activate,
1008ca69e2d1SBrian Masney 	.alloc = pmic_gpio_domain_alloc,
1009ca69e2d1SBrian Masney 	.deactivate = gpiochip_irq_domain_deactivate,
1010ca69e2d1SBrian Masney 	.free = irq_domain_free_irqs_common,
1011ca69e2d1SBrian Masney 	.translate = pmic_gpio_domain_translate,
1012ca69e2d1SBrian Masney };
1013ca69e2d1SBrian Masney 
1014eadff302SIvan T. Ivanov static int pmic_gpio_probe(struct platform_device *pdev)
1015eadff302SIvan T. Ivanov {
1016ca69e2d1SBrian Masney 	struct irq_domain *parent_domain;
1017ca69e2d1SBrian Masney 	struct device_node *parent_node;
1018eadff302SIvan T. Ivanov 	struct device *dev = &pdev->dev;
1019eadff302SIvan T. Ivanov 	struct pinctrl_pin_desc *pindesc;
1020eadff302SIvan T. Ivanov 	struct pinctrl_desc *pctrldesc;
1021eadff302SIvan T. Ivanov 	struct pmic_gpio_pad *pad, *pads;
1022eadff302SIvan T. Ivanov 	struct pmic_gpio_state *state;
1023eadff302SIvan T. Ivanov 	int ret, npins, i;
1024ab4256cfSStephen Boyd 	u32 reg;
1025eadff302SIvan T. Ivanov 
1026ab4256cfSStephen Boyd 	ret = of_property_read_u32(dev->of_node, "reg", &reg);
1027eadff302SIvan T. Ivanov 	if (ret < 0) {
1028ab4256cfSStephen Boyd 		dev_err(dev, "missing base address");
1029eadff302SIvan T. Ivanov 		return ret;
1030eadff302SIvan T. Ivanov 	}
1031eadff302SIvan T. Ivanov 
1032cfacef37SBrian Masney 	npins = (uintptr_t) device_get_match_data(&pdev->dev);
1033eadff302SIvan T. Ivanov 
1034eadff302SIvan T. Ivanov 	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
1035eadff302SIvan T. Ivanov 	if (!state)
1036eadff302SIvan T. Ivanov 		return -ENOMEM;
1037eadff302SIvan T. Ivanov 
1038eadff302SIvan T. Ivanov 	platform_set_drvdata(pdev, state);
1039eadff302SIvan T. Ivanov 
1040eadff302SIvan T. Ivanov 	state->dev = &pdev->dev;
1041eadff302SIvan T. Ivanov 	state->map = dev_get_regmap(dev->parent, NULL);
1042eadff302SIvan T. Ivanov 
1043eadff302SIvan T. Ivanov 	pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
1044eadff302SIvan T. Ivanov 	if (!pindesc)
1045eadff302SIvan T. Ivanov 		return -ENOMEM;
1046eadff302SIvan T. Ivanov 
1047eadff302SIvan T. Ivanov 	pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
1048eadff302SIvan T. Ivanov 	if (!pads)
1049eadff302SIvan T. Ivanov 		return -ENOMEM;
1050eadff302SIvan T. Ivanov 
1051eadff302SIvan T. Ivanov 	pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL);
1052eadff302SIvan T. Ivanov 	if (!pctrldesc)
1053eadff302SIvan T. Ivanov 		return -ENOMEM;
1054eadff302SIvan T. Ivanov 
1055eadff302SIvan T. Ivanov 	pctrldesc->pctlops = &pmic_gpio_pinctrl_ops;
1056eadff302SIvan T. Ivanov 	pctrldesc->pmxops = &pmic_gpio_pinmux_ops;
1057eadff302SIvan T. Ivanov 	pctrldesc->confops = &pmic_gpio_pinconf_ops;
1058eadff302SIvan T. Ivanov 	pctrldesc->owner = THIS_MODULE;
1059eadff302SIvan T. Ivanov 	pctrldesc->name = dev_name(dev);
1060eadff302SIvan T. Ivanov 	pctrldesc->pins = pindesc;
1061eadff302SIvan T. Ivanov 	pctrldesc->npins = npins;
1062f684e4acSLinus Walleij 	pctrldesc->num_custom_params = ARRAY_SIZE(pmic_gpio_bindings);
1063f684e4acSLinus Walleij 	pctrldesc->custom_params = pmic_gpio_bindings;
10644f06266aSArnd Bergmann #ifdef CONFIG_DEBUG_FS
1065f684e4acSLinus Walleij 	pctrldesc->custom_conf_items = pmic_conf_items;
10664f06266aSArnd Bergmann #endif
1067eadff302SIvan T. Ivanov 
1068eadff302SIvan T. Ivanov 	for (i = 0; i < npins; i++, pindesc++) {
1069eadff302SIvan T. Ivanov 		pad = &pads[i];
1070eadff302SIvan T. Ivanov 		pindesc->drv_data = pad;
1071eadff302SIvan T. Ivanov 		pindesc->number = i;
1072eadff302SIvan T. Ivanov 		pindesc->name = pmic_gpio_groups[i];
1073eadff302SIvan T. Ivanov 
1074ab4256cfSStephen Boyd 		pad->base = reg + i * PMIC_GPIO_ADDRESS_RANGE;
1075eadff302SIvan T. Ivanov 
1076eadff302SIvan T. Ivanov 		ret = pmic_gpio_populate(state, pad);
1077eadff302SIvan T. Ivanov 		if (ret < 0)
1078eadff302SIvan T. Ivanov 			return ret;
1079eadff302SIvan T. Ivanov 	}
1080eadff302SIvan T. Ivanov 
1081eadff302SIvan T. Ivanov 	state->chip = pmic_gpio_gpio_template;
108258383c78SLinus Walleij 	state->chip.parent = dev;
1083eadff302SIvan T. Ivanov 	state->chip.base = -1;
1084eadff302SIvan T. Ivanov 	state->chip.ngpio = npins;
1085eadff302SIvan T. Ivanov 	state->chip.label = dev_name(dev);
1086eadff302SIvan T. Ivanov 	state->chip.of_gpio_n_cells = 2;
1087eadff302SIvan T. Ivanov 	state->chip.can_sleep = false;
1088eadff302SIvan T. Ivanov 
1089b46ddfe6SLaxman Dewangan 	state->ctrl = devm_pinctrl_register(dev, pctrldesc, state);
1090323de9efSMasahiro Yamada 	if (IS_ERR(state->ctrl))
1091323de9efSMasahiro Yamada 		return PTR_ERR(state->ctrl);
1092eadff302SIvan T. Ivanov 
1093ca69e2d1SBrian Masney 	parent_node = of_irq_find_parent(state->dev->of_node);
1094ca69e2d1SBrian Masney 	if (!parent_node)
1095ca69e2d1SBrian Masney 		return -ENXIO;
1096ca69e2d1SBrian Masney 
1097ca69e2d1SBrian Masney 	parent_domain = irq_find_host(parent_node);
1098ca69e2d1SBrian Masney 	of_node_put(parent_node);
1099ca69e2d1SBrian Masney 	if (!parent_domain)
1100ca69e2d1SBrian Masney 		return -ENXIO;
1101ca69e2d1SBrian Masney 
1102ca69e2d1SBrian Masney 	state->fwnode = of_node_to_fwnode(state->dev->of_node);
1103ca69e2d1SBrian Masney 	state->domain = irq_domain_create_hierarchy(parent_domain, 0,
1104ca69e2d1SBrian Masney 						    state->chip.ngpio,
1105ca69e2d1SBrian Masney 						    state->fwnode,
1106ca69e2d1SBrian Masney 						    &pmic_gpio_domain_ops,
1107ca69e2d1SBrian Masney 						    &state->chip);
1108ca69e2d1SBrian Masney 	if (!state->domain)
1109ca69e2d1SBrian Masney 		return -ENODEV;
1110ca69e2d1SBrian Masney 
1111c52d9df1SLinus Walleij 	ret = gpiochip_add_data(&state->chip, state);
1112eadff302SIvan T. Ivanov 	if (ret) {
1113eadff302SIvan T. Ivanov 		dev_err(state->dev, "can't add gpio chip\n");
1114ca69e2d1SBrian Masney 		goto err_chip_add_data;
1115eadff302SIvan T. Ivanov 	}
1116eadff302SIvan T. Ivanov 
1117149a9604SBrian Masney 	/*
1118149a9604SBrian Masney 	 * For DeviceTree-supported systems, the gpio core checks the
1119149a9604SBrian Masney 	 * pinctrl's device node for the "gpio-ranges" property.
1120149a9604SBrian Masney 	 * If it is present, it takes care of adding the pin ranges
1121149a9604SBrian Masney 	 * for the driver. In this case the driver can skip ahead.
1122149a9604SBrian Masney 	 *
1123149a9604SBrian Masney 	 * In order to remain compatible with older, existing DeviceTree
1124149a9604SBrian Masney 	 * files which don't set the "gpio-ranges" property or systems that
1125149a9604SBrian Masney 	 * utilize ACPI the driver has to call gpiochip_add_pin_range().
1126149a9604SBrian Masney 	 */
1127149a9604SBrian Masney 	if (!of_property_read_bool(dev->of_node, "gpio-ranges")) {
1128149a9604SBrian Masney 		ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0,
1129149a9604SBrian Masney 					     npins);
1130eadff302SIvan T. Ivanov 		if (ret) {
1131eadff302SIvan T. Ivanov 			dev_err(dev, "failed to add pin range\n");
1132eadff302SIvan T. Ivanov 			goto err_range;
1133eadff302SIvan T. Ivanov 		}
1134149a9604SBrian Masney 	}
1135eadff302SIvan T. Ivanov 
1136eadff302SIvan T. Ivanov 	return 0;
1137eadff302SIvan T. Ivanov 
1138eadff302SIvan T. Ivanov err_range:
1139eadff302SIvan T. Ivanov 	gpiochip_remove(&state->chip);
1140ca69e2d1SBrian Masney err_chip_add_data:
1141ca69e2d1SBrian Masney 	irq_domain_remove(state->domain);
1142eadff302SIvan T. Ivanov 	return ret;
1143eadff302SIvan T. Ivanov }
1144eadff302SIvan T. Ivanov 
1145eadff302SIvan T. Ivanov static int pmic_gpio_remove(struct platform_device *pdev)
1146eadff302SIvan T. Ivanov {
1147eadff302SIvan T. Ivanov 	struct pmic_gpio_state *state = platform_get_drvdata(pdev);
1148eadff302SIvan T. Ivanov 
1149eadff302SIvan T. Ivanov 	gpiochip_remove(&state->chip);
1150ca69e2d1SBrian Masney 	irq_domain_remove(state->domain);
1151eadff302SIvan T. Ivanov 	return 0;
1152eadff302SIvan T. Ivanov }
1153eadff302SIvan T. Ivanov 
1154eadff302SIvan T. Ivanov static const struct of_device_id pmic_gpio_of_match[] = {
1155cfacef37SBrian Masney 	{ .compatible = "qcom,pm8005-gpio", .data = (void *) 4 },
1156cfacef37SBrian Masney 	{ .compatible = "qcom,pm8916-gpio", .data = (void *) 4 },
1157cfacef37SBrian Masney 	{ .compatible = "qcom,pm8941-gpio", .data = (void *) 36 },
1158cfacef37SBrian Masney 	{ .compatible = "qcom,pm8994-gpio", .data = (void *) 22 },
1159cfacef37SBrian Masney 	{ .compatible = "qcom,pmi8994-gpio", .data = (void *) 10 },
1160cfacef37SBrian Masney 	{ .compatible = "qcom,pm8998-gpio", .data = (void *) 26 },
1161cfacef37SBrian Masney 	{ .compatible = "qcom,pmi8998-gpio", .data = (void *) 14 },
1162cfacef37SBrian Masney 	{ .compatible = "qcom,pma8084-gpio", .data = (void *) 22 },
1163cfacef37SBrian Masney 	/* pms405 has 12 GPIOs with holes on 1, 9, and 10 */
1164cfacef37SBrian Masney 	{ .compatible = "qcom,pms405-gpio", .data = (void *) 12 },
1165eadff302SIvan T. Ivanov 	{ },
1166eadff302SIvan T. Ivanov };
1167eadff302SIvan T. Ivanov 
1168eadff302SIvan T. Ivanov MODULE_DEVICE_TABLE(of, pmic_gpio_of_match);
1169eadff302SIvan T. Ivanov 
1170eadff302SIvan T. Ivanov static struct platform_driver pmic_gpio_driver = {
1171eadff302SIvan T. Ivanov 	.driver = {
1172eadff302SIvan T. Ivanov 		   .name = "qcom-spmi-gpio",
1173eadff302SIvan T. Ivanov 		   .of_match_table = pmic_gpio_of_match,
1174eadff302SIvan T. Ivanov 	},
1175eadff302SIvan T. Ivanov 	.probe	= pmic_gpio_probe,
1176eadff302SIvan T. Ivanov 	.remove = pmic_gpio_remove,
1177eadff302SIvan T. Ivanov };
1178eadff302SIvan T. Ivanov 
1179eadff302SIvan T. Ivanov module_platform_driver(pmic_gpio_driver);
1180eadff302SIvan T. Ivanov 
1181eadff302SIvan T. Ivanov MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
1182eadff302SIvan T. Ivanov MODULE_DESCRIPTION("Qualcomm SPMI PMIC GPIO pin control driver");
1183eadff302SIvan T. Ivanov MODULE_ALIAS("platform:qcom-spmi-gpio");
1184eadff302SIvan T. Ivanov MODULE_LICENSE("GPL v2");
1185