197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2b4c45fe9SBjorn Andersson /*
3b4c45fe9SBjorn Andersson  * Copyright (c) 2015, Sony Mobile Communications AB.
4b4c45fe9SBjorn Andersson  * Copyright (c) 2013, The Linux Foundation. All rights reserved.
5b4c45fe9SBjorn Andersson  */
6b4c45fe9SBjorn Andersson 
71c5fb66aSLinus Walleij #include <linux/gpio/driver.h>
8b4c45fe9SBjorn Andersson #include <linux/interrupt.h>
9aa9430f8SAndy Shevchenko #include <linux/module.h>
10060f03e9SRob Herring #include <linux/of.h>
11ab4256cfSStephen Boyd #include <linux/of_irq.h>
12aa9430f8SAndy Shevchenko #include <linux/platform_device.h>
13aa9430f8SAndy Shevchenko #include <linux/regmap.h>
14aa9430f8SAndy Shevchenko #include <linux/seq_file.h>
15aa9430f8SAndy Shevchenko #include <linux/slab.h>
16aa9430f8SAndy Shevchenko 
17aa9430f8SAndy Shevchenko #include <linux/pinctrl/pinconf-generic.h>
18aa9430f8SAndy Shevchenko #include <linux/pinctrl/pinconf.h>
19aa9430f8SAndy Shevchenko #include <linux/pinctrl/pinctrl.h>
20aa9430f8SAndy Shevchenko #include <linux/pinctrl/pinmux.h>
21b4c45fe9SBjorn Andersson 
22b4c45fe9SBjorn Andersson #include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
23b4c45fe9SBjorn Andersson 
24b4c45fe9SBjorn Andersson #include "../core.h"
25b4c45fe9SBjorn Andersson #include "../pinctrl-utils.h"
26b4c45fe9SBjorn Andersson 
27b4c45fe9SBjorn Andersson /* mode */
28b4c45fe9SBjorn Andersson #define PM8XXX_GPIO_MODE_ENABLED	BIT(0)
29b4c45fe9SBjorn Andersson #define PM8XXX_GPIO_MODE_INPUT		0
30b4c45fe9SBjorn Andersson #define PM8XXX_GPIO_MODE_OUTPUT		2
31b4c45fe9SBjorn Andersson 
32b4c45fe9SBjorn Andersson /* output buffer */
33b4c45fe9SBjorn Andersson #define PM8XXX_GPIO_PUSH_PULL		0
34b4c45fe9SBjorn Andersson #define PM8XXX_GPIO_OPEN_DRAIN		1
35b4c45fe9SBjorn Andersson 
36b4c45fe9SBjorn Andersson /* bias */
37b4c45fe9SBjorn Andersson #define PM8XXX_GPIO_BIAS_PU_30		0
38b4c45fe9SBjorn Andersson #define PM8XXX_GPIO_BIAS_PU_1P5		1
39b4c45fe9SBjorn Andersson #define PM8XXX_GPIO_BIAS_PU_31P5	2
40b4c45fe9SBjorn Andersson #define PM8XXX_GPIO_BIAS_PU_1P5_30	3
41b4c45fe9SBjorn Andersson #define PM8XXX_GPIO_BIAS_PD		4
42b4c45fe9SBjorn Andersson #define PM8XXX_GPIO_BIAS_NP		5
43b4c45fe9SBjorn Andersson 
44b4c45fe9SBjorn Andersson /* GPIO registers */
45b4c45fe9SBjorn Andersson #define SSBI_REG_ADDR_GPIO_BASE		0x150
46b4c45fe9SBjorn Andersson #define SSBI_REG_ADDR_GPIO(n)		(SSBI_REG_ADDR_GPIO_BASE + n)
47b4c45fe9SBjorn Andersson 
48b4c45fe9SBjorn Andersson #define PM8XXX_BANK_WRITE		BIT(7)
49b4c45fe9SBjorn Andersson 
50b4c45fe9SBjorn Andersson #define PM8XXX_MAX_GPIOS               44
51b4c45fe9SBjorn Andersson 
529d2b563bSBrian Masney #define PM8XXX_GPIO_PHYSICAL_OFFSET	1
539d2b563bSBrian Masney 
54b4c45fe9SBjorn Andersson /* custom pinconf parameters */
55b4c45fe9SBjorn Andersson #define PM8XXX_QCOM_DRIVE_STRENGH      (PIN_CONFIG_END + 1)
56b4c45fe9SBjorn Andersson #define PM8XXX_QCOM_PULL_UP_STRENGTH   (PIN_CONFIG_END + 2)
57b4c45fe9SBjorn Andersson 
58b4c45fe9SBjorn Andersson /**
59b4c45fe9SBjorn Andersson  * struct pm8xxx_pin_data - dynamic configuration for a pin
60b4c45fe9SBjorn Andersson  * @reg:               address of the control register
61b4c45fe9SBjorn Andersson  * @power_source:      logical selected voltage source, mapping in static data
62b4c45fe9SBjorn Andersson  *                     is used translate to register values
63b4c45fe9SBjorn Andersson  * @mode:              operating mode for the pin (input/output)
64b4c45fe9SBjorn Andersson  * @open_drain:        output buffer configured as open-drain (vs push-pull)
65b4c45fe9SBjorn Andersson  * @output_value:      configured output value
66b4c45fe9SBjorn Andersson  * @bias:              register view of configured bias
67b4c45fe9SBjorn Andersson  * @pull_up_strength:  placeholder for selected pull up strength
68b4c45fe9SBjorn Andersson  *                     only used to configure bias when pull up is selected
69b4c45fe9SBjorn Andersson  * @output_strength:   selector of output-strength
70b4c45fe9SBjorn Andersson  * @disable:           pin disabled / configured as tristate
71b4c45fe9SBjorn Andersson  * @function:          pinmux selector
72b4c45fe9SBjorn Andersson  * @inverted:          pin logic is inverted
73b4c45fe9SBjorn Andersson  */
74b4c45fe9SBjorn Andersson struct pm8xxx_pin_data {
75b4c45fe9SBjorn Andersson 	unsigned reg;
76b4c45fe9SBjorn Andersson 	u8 power_source;
77b4c45fe9SBjorn Andersson 	u8 mode;
78b4c45fe9SBjorn Andersson 	bool open_drain;
79b4c45fe9SBjorn Andersson 	bool output_value;
80b4c45fe9SBjorn Andersson 	u8 bias;
81b4c45fe9SBjorn Andersson 	u8 pull_up_strength;
82b4c45fe9SBjorn Andersson 	u8 output_strength;
83b4c45fe9SBjorn Andersson 	bool disable;
84b4c45fe9SBjorn Andersson 	u8 function;
85b4c45fe9SBjorn Andersson 	bool inverted;
86b4c45fe9SBjorn Andersson };
87b4c45fe9SBjorn Andersson 
88b4c45fe9SBjorn Andersson struct pm8xxx_gpio {
89b4c45fe9SBjorn Andersson 	struct device *dev;
90b4c45fe9SBjorn Andersson 	struct regmap *regmap;
91b4c45fe9SBjorn Andersson 	struct pinctrl_dev *pctrl;
92b4c45fe9SBjorn Andersson 	struct gpio_chip chip;
93b4c45fe9SBjorn Andersson 
94b4c45fe9SBjorn Andersson 	struct pinctrl_desc desc;
95b4c45fe9SBjorn Andersson 	unsigned npins;
96b4c45fe9SBjorn Andersson };
97b4c45fe9SBjorn Andersson 
98b4c45fe9SBjorn Andersson static const struct pinconf_generic_params pm8xxx_gpio_bindings[] = {
99b4c45fe9SBjorn Andersson 	{"qcom,drive-strength",		PM8XXX_QCOM_DRIVE_STRENGH,	0},
100b4c45fe9SBjorn Andersson 	{"qcom,pull-up-strength",	PM8XXX_QCOM_PULL_UP_STRENGTH,	0},
101b4c45fe9SBjorn Andersson };
102b4c45fe9SBjorn Andersson 
103b4c45fe9SBjorn Andersson #ifdef CONFIG_DEBUG_FS
104b4c45fe9SBjorn Andersson static const struct pin_config_item pm8xxx_conf_items[ARRAY_SIZE(pm8xxx_gpio_bindings)] = {
105b4c45fe9SBjorn Andersson 	PCONFDUMP(PM8XXX_QCOM_DRIVE_STRENGH, "drive-strength", NULL, true),
106b4c45fe9SBjorn Andersson 	PCONFDUMP(PM8XXX_QCOM_PULL_UP_STRENGTH,  "pull up strength", NULL, true),
107b4c45fe9SBjorn Andersson };
108b4c45fe9SBjorn Andersson #endif
109b4c45fe9SBjorn Andersson 
110b4c45fe9SBjorn Andersson static const char * const pm8xxx_groups[PM8XXX_MAX_GPIOS] = {
111b4c45fe9SBjorn Andersson 	"gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", "gpio8",
112b4c45fe9SBjorn Andersson 	"gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", "gpio15",
113b4c45fe9SBjorn Andersson 	"gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22",
114b4c45fe9SBjorn Andersson 	"gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29",
115b4c45fe9SBjorn Andersson 	"gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36",
116b4c45fe9SBjorn Andersson 	"gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", "gpio43",
117b4c45fe9SBjorn Andersson 	"gpio44",
118b4c45fe9SBjorn Andersson };
119b4c45fe9SBjorn Andersson 
120b4c45fe9SBjorn Andersson static const char * const pm8xxx_gpio_functions[] = {
121b4c45fe9SBjorn Andersson 	PMIC_GPIO_FUNC_NORMAL, PMIC_GPIO_FUNC_PAIRED,
122b4c45fe9SBjorn Andersson 	PMIC_GPIO_FUNC_FUNC1, PMIC_GPIO_FUNC_FUNC2,
123b4c45fe9SBjorn Andersson 	PMIC_GPIO_FUNC_DTEST1, PMIC_GPIO_FUNC_DTEST2,
124b4c45fe9SBjorn Andersson 	PMIC_GPIO_FUNC_DTEST3, PMIC_GPIO_FUNC_DTEST4,
125b4c45fe9SBjorn Andersson };
126b4c45fe9SBjorn Andersson 
pm8xxx_read_bank(struct pm8xxx_gpio * pctrl,struct pm8xxx_pin_data * pin,int bank)127b4c45fe9SBjorn Andersson static int pm8xxx_read_bank(struct pm8xxx_gpio *pctrl,
128b4c45fe9SBjorn Andersson 			    struct pm8xxx_pin_data *pin, int bank)
129b4c45fe9SBjorn Andersson {
130b4c45fe9SBjorn Andersson 	unsigned int val = bank << 4;
131b4c45fe9SBjorn Andersson 	int ret;
132b4c45fe9SBjorn Andersson 
133b4c45fe9SBjorn Andersson 	ret = regmap_write(pctrl->regmap, pin->reg, val);
134b4c45fe9SBjorn Andersson 	if (ret) {
135b4c45fe9SBjorn Andersson 		dev_err(pctrl->dev, "failed to select bank %d\n", bank);
136b4c45fe9SBjorn Andersson 		return ret;
137b4c45fe9SBjorn Andersson 	}
138b4c45fe9SBjorn Andersson 
139b4c45fe9SBjorn Andersson 	ret = regmap_read(pctrl->regmap, pin->reg, &val);
140b4c45fe9SBjorn Andersson 	if (ret) {
141b4c45fe9SBjorn Andersson 		dev_err(pctrl->dev, "failed to read register %d\n", bank);
142b4c45fe9SBjorn Andersson 		return ret;
143b4c45fe9SBjorn Andersson 	}
144b4c45fe9SBjorn Andersson 
145b4c45fe9SBjorn Andersson 	return val;
146b4c45fe9SBjorn Andersson }
147b4c45fe9SBjorn Andersson 
pm8xxx_write_bank(struct pm8xxx_gpio * pctrl,struct pm8xxx_pin_data * pin,int bank,u8 val)148b4c45fe9SBjorn Andersson static int pm8xxx_write_bank(struct pm8xxx_gpio *pctrl,
149b4c45fe9SBjorn Andersson 			     struct pm8xxx_pin_data *pin,
150b4c45fe9SBjorn Andersson 			     int bank,
151b4c45fe9SBjorn Andersson 			     u8 val)
152b4c45fe9SBjorn Andersson {
153b4c45fe9SBjorn Andersson 	int ret;
154b4c45fe9SBjorn Andersson 
155b4c45fe9SBjorn Andersson 	val |= PM8XXX_BANK_WRITE;
156b4c45fe9SBjorn Andersson 	val |= bank << 4;
157b4c45fe9SBjorn Andersson 
158b4c45fe9SBjorn Andersson 	ret = regmap_write(pctrl->regmap, pin->reg, val);
159b4c45fe9SBjorn Andersson 	if (ret)
160b4c45fe9SBjorn Andersson 		dev_err(pctrl->dev, "failed to write register\n");
161b4c45fe9SBjorn Andersson 
162b4c45fe9SBjorn Andersson 	return ret;
163b4c45fe9SBjorn Andersson }
164b4c45fe9SBjorn Andersson 
pm8xxx_get_groups_count(struct pinctrl_dev * pctldev)165b4c45fe9SBjorn Andersson static int pm8xxx_get_groups_count(struct pinctrl_dev *pctldev)
166b4c45fe9SBjorn Andersson {
167b4c45fe9SBjorn Andersson 	struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
168b4c45fe9SBjorn Andersson 
169b4c45fe9SBjorn Andersson 	return pctrl->npins;
170b4c45fe9SBjorn Andersson }
171b4c45fe9SBjorn Andersson 
pm8xxx_get_group_name(struct pinctrl_dev * pctldev,unsigned group)172b4c45fe9SBjorn Andersson static const char *pm8xxx_get_group_name(struct pinctrl_dev *pctldev,
173b4c45fe9SBjorn Andersson 					 unsigned group)
174b4c45fe9SBjorn Andersson {
175b4c45fe9SBjorn Andersson 	return pm8xxx_groups[group];
176b4c45fe9SBjorn Andersson }
177b4c45fe9SBjorn Andersson 
178b4c45fe9SBjorn Andersson 
pm8xxx_get_group_pins(struct pinctrl_dev * pctldev,unsigned group,const unsigned ** pins,unsigned * num_pins)179b4c45fe9SBjorn Andersson static int pm8xxx_get_group_pins(struct pinctrl_dev *pctldev,
180b4c45fe9SBjorn Andersson 				 unsigned group,
181b4c45fe9SBjorn Andersson 				 const unsigned **pins,
182b4c45fe9SBjorn Andersson 				 unsigned *num_pins)
183b4c45fe9SBjorn Andersson {
184b4c45fe9SBjorn Andersson 	struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
185b4c45fe9SBjorn Andersson 
186b4c45fe9SBjorn Andersson 	*pins = &pctrl->desc.pins[group].number;
187b4c45fe9SBjorn Andersson 	*num_pins = 1;
188b4c45fe9SBjorn Andersson 
189b4c45fe9SBjorn Andersson 	return 0;
190b4c45fe9SBjorn Andersson }
191b4c45fe9SBjorn Andersson 
192b4c45fe9SBjorn Andersson static const struct pinctrl_ops pm8xxx_pinctrl_ops = {
193b4c45fe9SBjorn Andersson 	.get_groups_count	= pm8xxx_get_groups_count,
194b4c45fe9SBjorn Andersson 	.get_group_name		= pm8xxx_get_group_name,
195b4c45fe9SBjorn Andersson 	.get_group_pins         = pm8xxx_get_group_pins,
196b4c45fe9SBjorn Andersson 	.dt_node_to_map		= pinconf_generic_dt_node_to_map_group,
197d32f7fd3SIrina Tirdea 	.dt_free_map		= pinctrl_utils_free_map,
198b4c45fe9SBjorn Andersson };
199b4c45fe9SBjorn Andersson 
pm8xxx_get_functions_count(struct pinctrl_dev * pctldev)200b4c45fe9SBjorn Andersson static int pm8xxx_get_functions_count(struct pinctrl_dev *pctldev)
201b4c45fe9SBjorn Andersson {
202b4c45fe9SBjorn Andersson 	return ARRAY_SIZE(pm8xxx_gpio_functions);
203b4c45fe9SBjorn Andersson }
204b4c45fe9SBjorn Andersson 
pm8xxx_get_function_name(struct pinctrl_dev * pctldev,unsigned function)205b4c45fe9SBjorn Andersson static const char *pm8xxx_get_function_name(struct pinctrl_dev *pctldev,
206b4c45fe9SBjorn Andersson 					    unsigned function)
207b4c45fe9SBjorn Andersson {
208b4c45fe9SBjorn Andersson 	return pm8xxx_gpio_functions[function];
209b4c45fe9SBjorn Andersson }
210b4c45fe9SBjorn Andersson 
pm8xxx_get_function_groups(struct pinctrl_dev * pctldev,unsigned function,const char * const ** groups,unsigned * const num_groups)211b4c45fe9SBjorn Andersson static int pm8xxx_get_function_groups(struct pinctrl_dev *pctldev,
212b4c45fe9SBjorn Andersson 				      unsigned function,
213b4c45fe9SBjorn Andersson 				      const char * const **groups,
214b4c45fe9SBjorn Andersson 				      unsigned * const num_groups)
215b4c45fe9SBjorn Andersson {
216b4c45fe9SBjorn Andersson 	struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
217b4c45fe9SBjorn Andersson 
218b4c45fe9SBjorn Andersson 	*groups = pm8xxx_groups;
219b4c45fe9SBjorn Andersson 	*num_groups = pctrl->npins;
220b4c45fe9SBjorn Andersson 	return 0;
221b4c45fe9SBjorn Andersson }
222b4c45fe9SBjorn Andersson 
pm8xxx_pinmux_set_mux(struct pinctrl_dev * pctldev,unsigned function,unsigned group)223b4c45fe9SBjorn Andersson static int pm8xxx_pinmux_set_mux(struct pinctrl_dev *pctldev,
224b4c45fe9SBjorn Andersson 				 unsigned function,
225b4c45fe9SBjorn Andersson 				 unsigned group)
226b4c45fe9SBjorn Andersson {
227b4c45fe9SBjorn Andersson 	struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
228b4c45fe9SBjorn Andersson 	struct pm8xxx_pin_data *pin = pctrl->desc.pins[group].drv_data;
229b4c45fe9SBjorn Andersson 	u8 val;
230b4c45fe9SBjorn Andersson 
231b4c45fe9SBjorn Andersson 	pin->function = function;
232b4c45fe9SBjorn Andersson 	val = pin->function << 1;
233b4c45fe9SBjorn Andersson 
234b4c45fe9SBjorn Andersson 	pm8xxx_write_bank(pctrl, pin, 4, val);
235b4c45fe9SBjorn Andersson 
236b4c45fe9SBjorn Andersson 	return 0;
237b4c45fe9SBjorn Andersson }
238b4c45fe9SBjorn Andersson 
239b4c45fe9SBjorn Andersson static const struct pinmux_ops pm8xxx_pinmux_ops = {
240b4c45fe9SBjorn Andersson 	.get_functions_count	= pm8xxx_get_functions_count,
241b4c45fe9SBjorn Andersson 	.get_function_name	= pm8xxx_get_function_name,
242b4c45fe9SBjorn Andersson 	.get_function_groups	= pm8xxx_get_function_groups,
243b4c45fe9SBjorn Andersson 	.set_mux		= pm8xxx_pinmux_set_mux,
244b4c45fe9SBjorn Andersson };
245b4c45fe9SBjorn Andersson 
pm8xxx_pin_config_get(struct pinctrl_dev * pctldev,unsigned int offset,unsigned long * config)246b4c45fe9SBjorn Andersson static int pm8xxx_pin_config_get(struct pinctrl_dev *pctldev,
247b4c45fe9SBjorn Andersson 				 unsigned int offset,
248b4c45fe9SBjorn Andersson 				 unsigned long *config)
249b4c45fe9SBjorn Andersson {
250b4c45fe9SBjorn Andersson 	struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
251b4c45fe9SBjorn Andersson 	struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data;
252b4c45fe9SBjorn Andersson 	unsigned param = pinconf_to_config_param(*config);
253b4c45fe9SBjorn Andersson 	unsigned arg;
254b4c45fe9SBjorn Andersson 
255b4c45fe9SBjorn Andersson 	switch (param) {
256b4c45fe9SBjorn Andersson 	case PIN_CONFIG_BIAS_DISABLE:
257b432414bSDouglas Anderson 		if (pin->bias != PM8XXX_GPIO_BIAS_NP)
258b432414bSDouglas Anderson 			return -EINVAL;
259b432414bSDouglas Anderson 		arg = 1;
260b4c45fe9SBjorn Andersson 		break;
261b4c45fe9SBjorn Andersson 	case PIN_CONFIG_BIAS_PULL_DOWN:
262b432414bSDouglas Anderson 		if (pin->bias != PM8XXX_GPIO_BIAS_PD)
263b432414bSDouglas Anderson 			return -EINVAL;
264b432414bSDouglas Anderson 		arg = 1;
265b4c45fe9SBjorn Andersson 		break;
266b4c45fe9SBjorn Andersson 	case PIN_CONFIG_BIAS_PULL_UP:
267b432414bSDouglas Anderson 		if (pin->bias > PM8XXX_GPIO_BIAS_PU_1P5_30)
268b432414bSDouglas Anderson 			return -EINVAL;
269b432414bSDouglas Anderson 		arg = 1;
270b4c45fe9SBjorn Andersson 		break;
271b4c45fe9SBjorn Andersson 	case PM8XXX_QCOM_PULL_UP_STRENGTH:
272b4c45fe9SBjorn Andersson 		arg = pin->pull_up_strength;
273b4c45fe9SBjorn Andersson 		break;
274b4c45fe9SBjorn Andersson 	case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
275b432414bSDouglas Anderson 		if (!pin->disable)
276b432414bSDouglas Anderson 			return -EINVAL;
277b432414bSDouglas Anderson 		arg = 1;
278b4c45fe9SBjorn Andersson 		break;
279b4c45fe9SBjorn Andersson 	case PIN_CONFIG_INPUT_ENABLE:
280b432414bSDouglas Anderson 		if (pin->mode != PM8XXX_GPIO_MODE_INPUT)
281b432414bSDouglas Anderson 			return -EINVAL;
282b432414bSDouglas Anderson 		arg = 1;
283b4c45fe9SBjorn Andersson 		break;
284b4c45fe9SBjorn Andersson 	case PIN_CONFIG_OUTPUT:
285b4c45fe9SBjorn Andersson 		if (pin->mode & PM8XXX_GPIO_MODE_OUTPUT)
286b4c45fe9SBjorn Andersson 			arg = pin->output_value;
287b4c45fe9SBjorn Andersson 		else
288b4c45fe9SBjorn Andersson 			arg = 0;
289b4c45fe9SBjorn Andersson 		break;
290b4c45fe9SBjorn Andersson 	case PIN_CONFIG_POWER_SOURCE:
291b4c45fe9SBjorn Andersson 		arg = pin->power_source;
292b4c45fe9SBjorn Andersson 		break;
293b4c45fe9SBjorn Andersson 	case PM8XXX_QCOM_DRIVE_STRENGH:
294b4c45fe9SBjorn Andersson 		arg = pin->output_strength;
295b4c45fe9SBjorn Andersson 		break;
296b4c45fe9SBjorn Andersson 	case PIN_CONFIG_DRIVE_PUSH_PULL:
297b432414bSDouglas Anderson 		if (pin->open_drain)
298b432414bSDouglas Anderson 			return -EINVAL;
299b432414bSDouglas Anderson 		arg = 1;
300b4c45fe9SBjorn Andersson 		break;
301b4c45fe9SBjorn Andersson 	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
302b432414bSDouglas Anderson 		if (!pin->open_drain)
303b432414bSDouglas Anderson 			return -EINVAL;
304b432414bSDouglas Anderson 		arg = 1;
305b4c45fe9SBjorn Andersson 		break;
306b4c45fe9SBjorn Andersson 	default:
307b4c45fe9SBjorn Andersson 		return -EINVAL;
308b4c45fe9SBjorn Andersson 	}
309b4c45fe9SBjorn Andersson 
310b4c45fe9SBjorn Andersson 	*config = pinconf_to_config_packed(param, arg);
311b4c45fe9SBjorn Andersson 
312b4c45fe9SBjorn Andersson 	return 0;
313b4c45fe9SBjorn Andersson }
314b4c45fe9SBjorn Andersson 
pm8xxx_pin_config_set(struct pinctrl_dev * pctldev,unsigned int offset,unsigned long * configs,unsigned num_configs)315b4c45fe9SBjorn Andersson static int pm8xxx_pin_config_set(struct pinctrl_dev *pctldev,
316b4c45fe9SBjorn Andersson 				 unsigned int offset,
317b4c45fe9SBjorn Andersson 				 unsigned long *configs,
318b4c45fe9SBjorn Andersson 				 unsigned num_configs)
319b4c45fe9SBjorn Andersson {
320b4c45fe9SBjorn Andersson 	struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
321b4c45fe9SBjorn Andersson 	struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data;
322b4c45fe9SBjorn Andersson 	unsigned param;
323b4c45fe9SBjorn Andersson 	unsigned arg;
324b4c45fe9SBjorn Andersson 	unsigned i;
325b4c45fe9SBjorn Andersson 	u8 banks = 0;
326b4c45fe9SBjorn Andersson 	u8 val;
327b4c45fe9SBjorn Andersson 
328b4c45fe9SBjorn Andersson 	for (i = 0; i < num_configs; i++) {
329b4c45fe9SBjorn Andersson 		param = pinconf_to_config_param(configs[i]);
330b4c45fe9SBjorn Andersson 		arg = pinconf_to_config_argument(configs[i]);
331b4c45fe9SBjorn Andersson 
332b4c45fe9SBjorn Andersson 		switch (param) {
333b4c45fe9SBjorn Andersson 		case PIN_CONFIG_BIAS_DISABLE:
334b4c45fe9SBjorn Andersson 			pin->bias = PM8XXX_GPIO_BIAS_NP;
335b4c45fe9SBjorn Andersson 			banks |= BIT(2);
336b4c45fe9SBjorn Andersson 			pin->disable = 0;
337b4c45fe9SBjorn Andersson 			banks |= BIT(3);
338b4c45fe9SBjorn Andersson 			break;
339b4c45fe9SBjorn Andersson 		case PIN_CONFIG_BIAS_PULL_DOWN:
340b4c45fe9SBjorn Andersson 			pin->bias = PM8XXX_GPIO_BIAS_PD;
341b4c45fe9SBjorn Andersson 			banks |= BIT(2);
342b4c45fe9SBjorn Andersson 			pin->disable = 0;
343b4c45fe9SBjorn Andersson 			banks |= BIT(3);
344b4c45fe9SBjorn Andersson 			break;
345b4c45fe9SBjorn Andersson 		case PM8XXX_QCOM_PULL_UP_STRENGTH:
346b4c45fe9SBjorn Andersson 			if (arg > PM8XXX_GPIO_BIAS_PU_1P5_30) {
347b4c45fe9SBjorn Andersson 				dev_err(pctrl->dev, "invalid pull-up strength\n");
348b4c45fe9SBjorn Andersson 				return -EINVAL;
349b4c45fe9SBjorn Andersson 			}
350b4c45fe9SBjorn Andersson 			pin->pull_up_strength = arg;
3511586f556SGustavo A. R. Silva 			fallthrough;
352b4c45fe9SBjorn Andersson 		case PIN_CONFIG_BIAS_PULL_UP:
353b4c45fe9SBjorn Andersson 			pin->bias = pin->pull_up_strength;
354b4c45fe9SBjorn Andersson 			banks |= BIT(2);
355b4c45fe9SBjorn Andersson 			pin->disable = 0;
356b4c45fe9SBjorn Andersson 			banks |= BIT(3);
357b4c45fe9SBjorn Andersson 			break;
358b4c45fe9SBjorn Andersson 		case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
359b4c45fe9SBjorn Andersson 			pin->disable = 1;
360b4c45fe9SBjorn Andersson 			banks |= BIT(3);
361b4c45fe9SBjorn Andersson 			break;
362b4c45fe9SBjorn Andersson 		case PIN_CONFIG_INPUT_ENABLE:
363b4c45fe9SBjorn Andersson 			pin->mode = PM8XXX_GPIO_MODE_INPUT;
364b4c45fe9SBjorn Andersson 			banks |= BIT(0) | BIT(1);
365b4c45fe9SBjorn Andersson 			break;
366b4c45fe9SBjorn Andersson 		case PIN_CONFIG_OUTPUT:
367b4c45fe9SBjorn Andersson 			pin->mode = PM8XXX_GPIO_MODE_OUTPUT;
368b4c45fe9SBjorn Andersson 			pin->output_value = !!arg;
369b4c45fe9SBjorn Andersson 			banks |= BIT(0) | BIT(1);
370b4c45fe9SBjorn Andersson 			break;
371b4c45fe9SBjorn Andersson 		case PIN_CONFIG_POWER_SOURCE:
372b4c45fe9SBjorn Andersson 			pin->power_source = arg;
373b4c45fe9SBjorn Andersson 			banks |= BIT(0);
374b4c45fe9SBjorn Andersson 			break;
375b4c45fe9SBjorn Andersson 		case PM8XXX_QCOM_DRIVE_STRENGH:
376b4c45fe9SBjorn Andersson 			if (arg > PMIC_GPIO_STRENGTH_LOW) {
377b4c45fe9SBjorn Andersson 				dev_err(pctrl->dev, "invalid drive strength\n");
378b4c45fe9SBjorn Andersson 				return -EINVAL;
379b4c45fe9SBjorn Andersson 			}
380b4c45fe9SBjorn Andersson 			pin->output_strength = arg;
381b4c45fe9SBjorn Andersson 			banks |= BIT(3);
382b4c45fe9SBjorn Andersson 			break;
383b4c45fe9SBjorn Andersson 		case PIN_CONFIG_DRIVE_PUSH_PULL:
384b4c45fe9SBjorn Andersson 			pin->open_drain = 0;
385b4c45fe9SBjorn Andersson 			banks |= BIT(1);
386b4c45fe9SBjorn Andersson 			break;
387b4c45fe9SBjorn Andersson 		case PIN_CONFIG_DRIVE_OPEN_DRAIN:
388b4c45fe9SBjorn Andersson 			pin->open_drain = 1;
389b4c45fe9SBjorn Andersson 			banks |= BIT(1);
390b4c45fe9SBjorn Andersson 			break;
391b4c45fe9SBjorn Andersson 		default:
392b4c45fe9SBjorn Andersson 			dev_err(pctrl->dev,
393b4c45fe9SBjorn Andersson 				"unsupported config parameter: %x\n",
394b4c45fe9SBjorn Andersson 				param);
395b4c45fe9SBjorn Andersson 			return -EINVAL;
396b4c45fe9SBjorn Andersson 		}
397b4c45fe9SBjorn Andersson 	}
398b4c45fe9SBjorn Andersson 
399b4c45fe9SBjorn Andersson 	if (banks & BIT(0)) {
400b4c45fe9SBjorn Andersson 		val = pin->power_source << 1;
401b4c45fe9SBjorn Andersson 		val |= PM8XXX_GPIO_MODE_ENABLED;
402b4c45fe9SBjorn Andersson 		pm8xxx_write_bank(pctrl, pin, 0, val);
403b4c45fe9SBjorn Andersson 	}
404b4c45fe9SBjorn Andersson 
405b4c45fe9SBjorn Andersson 	if (banks & BIT(1)) {
406b4c45fe9SBjorn Andersson 		val = pin->mode << 2;
407b4c45fe9SBjorn Andersson 		val |= pin->open_drain << 1;
408b4c45fe9SBjorn Andersson 		val |= pin->output_value;
409b4c45fe9SBjorn Andersson 		pm8xxx_write_bank(pctrl, pin, 1, val);
410b4c45fe9SBjorn Andersson 	}
411b4c45fe9SBjorn Andersson 
412b4c45fe9SBjorn Andersson 	if (banks & BIT(2)) {
413b4c45fe9SBjorn Andersson 		val = pin->bias << 1;
414b4c45fe9SBjorn Andersson 		pm8xxx_write_bank(pctrl, pin, 2, val);
415b4c45fe9SBjorn Andersson 	}
416b4c45fe9SBjorn Andersson 
417b4c45fe9SBjorn Andersson 	if (banks & BIT(3)) {
418b4c45fe9SBjorn Andersson 		val = pin->output_strength << 2;
419b4c45fe9SBjorn Andersson 		val |= pin->disable;
420b4c45fe9SBjorn Andersson 		pm8xxx_write_bank(pctrl, pin, 3, val);
421b4c45fe9SBjorn Andersson 	}
422b4c45fe9SBjorn Andersson 
423b4c45fe9SBjorn Andersson 	if (banks & BIT(4)) {
424b4c45fe9SBjorn Andersson 		val = pin->function << 1;
425b4c45fe9SBjorn Andersson 		pm8xxx_write_bank(pctrl, pin, 4, val);
426b4c45fe9SBjorn Andersson 	}
427b4c45fe9SBjorn Andersson 
428b4c45fe9SBjorn Andersson 	if (banks & BIT(5)) {
429b4c45fe9SBjorn Andersson 		val = 0;
430b4c45fe9SBjorn Andersson 		if (!pin->inverted)
431b4c45fe9SBjorn Andersson 			val |= BIT(3);
432b4c45fe9SBjorn Andersson 		pm8xxx_write_bank(pctrl, pin, 5, val);
433b4c45fe9SBjorn Andersson 	}
434b4c45fe9SBjorn Andersson 
435b4c45fe9SBjorn Andersson 	return 0;
436b4c45fe9SBjorn Andersson }
437b4c45fe9SBjorn Andersson 
438b4c45fe9SBjorn Andersson static const struct pinconf_ops pm8xxx_pinconf_ops = {
439b4c45fe9SBjorn Andersson 	.is_generic = true,
440b4c45fe9SBjorn Andersson 	.pin_config_group_get = pm8xxx_pin_config_get,
441b4c45fe9SBjorn Andersson 	.pin_config_group_set = pm8xxx_pin_config_set,
442b4c45fe9SBjorn Andersson };
443b4c45fe9SBjorn Andersson 
4448b1704bdSJulia Lawall static const struct pinctrl_desc pm8xxx_pinctrl_desc = {
445b4c45fe9SBjorn Andersson 	.name = "pm8xxx_gpio",
446b4c45fe9SBjorn Andersson 	.pctlops = &pm8xxx_pinctrl_ops,
447b4c45fe9SBjorn Andersson 	.pmxops = &pm8xxx_pinmux_ops,
448b4c45fe9SBjorn Andersson 	.confops = &pm8xxx_pinconf_ops,
449b4c45fe9SBjorn Andersson 	.owner = THIS_MODULE,
450b4c45fe9SBjorn Andersson };
451b4c45fe9SBjorn Andersson 
pm8xxx_gpio_direction_input(struct gpio_chip * chip,unsigned offset)452b4c45fe9SBjorn Andersson static int pm8xxx_gpio_direction_input(struct gpio_chip *chip,
453b4c45fe9SBjorn Andersson 				       unsigned offset)
454b4c45fe9SBjorn Andersson {
455378596f9SLinus Walleij 	struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip);
456b4c45fe9SBjorn Andersson 	struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data;
457b4c45fe9SBjorn Andersson 	u8 val;
458b4c45fe9SBjorn Andersson 
459b4c45fe9SBjorn Andersson 	pin->mode = PM8XXX_GPIO_MODE_INPUT;
460b4c45fe9SBjorn Andersson 	val = pin->mode << 2;
461b4c45fe9SBjorn Andersson 
462b4c45fe9SBjorn Andersson 	pm8xxx_write_bank(pctrl, pin, 1, val);
463b4c45fe9SBjorn Andersson 
464b4c45fe9SBjorn Andersson 	return 0;
465b4c45fe9SBjorn Andersson }
466b4c45fe9SBjorn Andersson 
pm8xxx_gpio_direction_output(struct gpio_chip * chip,unsigned offset,int value)467b4c45fe9SBjorn Andersson static int pm8xxx_gpio_direction_output(struct gpio_chip *chip,
468b4c45fe9SBjorn Andersson 					unsigned offset,
469b4c45fe9SBjorn Andersson 					int value)
470b4c45fe9SBjorn Andersson {
471378596f9SLinus Walleij 	struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip);
472b4c45fe9SBjorn Andersson 	struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data;
473b4c45fe9SBjorn Andersson 	u8 val;
474b4c45fe9SBjorn Andersson 
475b4c45fe9SBjorn Andersson 	pin->mode = PM8XXX_GPIO_MODE_OUTPUT;
476b4c45fe9SBjorn Andersson 	pin->output_value = !!value;
477b4c45fe9SBjorn Andersson 
478b4c45fe9SBjorn Andersson 	val = pin->mode << 2;
479b4c45fe9SBjorn Andersson 	val |= pin->open_drain << 1;
480b4c45fe9SBjorn Andersson 	val |= pin->output_value;
481b4c45fe9SBjorn Andersson 
482b4c45fe9SBjorn Andersson 	pm8xxx_write_bank(pctrl, pin, 1, val);
483b4c45fe9SBjorn Andersson 
484b4c45fe9SBjorn Andersson 	return 0;
485b4c45fe9SBjorn Andersson }
486b4c45fe9SBjorn Andersson 
pm8xxx_gpio_get(struct gpio_chip * chip,unsigned offset)487b4c45fe9SBjorn Andersson static int pm8xxx_gpio_get(struct gpio_chip *chip, unsigned offset)
488b4c45fe9SBjorn Andersson {
489378596f9SLinus Walleij 	struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip);
490b4c45fe9SBjorn Andersson 	struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data;
491ae436fe8SBrian Masney 	int ret, irq;
492b4c45fe9SBjorn Andersson 	bool state;
493b4c45fe9SBjorn Andersson 
494ae436fe8SBrian Masney 	if (pin->mode == PM8XXX_GPIO_MODE_OUTPUT)
495ae436fe8SBrian Masney 		return pin->output_value;
496ae436fe8SBrian Masney 
497ae436fe8SBrian Masney 	irq = chip->to_irq(chip, offset);
498ae436fe8SBrian Masney 	if (irq >= 0) {
499ae436fe8SBrian Masney 		ret = irq_get_irqchip_state(irq, IRQCHIP_STATE_LINE_LEVEL,
500ae436fe8SBrian Masney 					    &state);
501b4c45fe9SBjorn Andersson 		if (!ret)
502b4c45fe9SBjorn Andersson 			ret = !!state;
5039d2b563bSBrian Masney 	} else
5049d2b563bSBrian Masney 		ret = -EINVAL;
505b4c45fe9SBjorn Andersson 
506b4c45fe9SBjorn Andersson 	return ret;
507b4c45fe9SBjorn Andersson }
508b4c45fe9SBjorn Andersson 
pm8xxx_gpio_set(struct gpio_chip * chip,unsigned offset,int value)509b4c45fe9SBjorn Andersson static void pm8xxx_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
510b4c45fe9SBjorn Andersson {
511378596f9SLinus Walleij 	struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip);
512b4c45fe9SBjorn Andersson 	struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data;
513b4c45fe9SBjorn Andersson 	u8 val;
514b4c45fe9SBjorn Andersson 
515b4c45fe9SBjorn Andersson 	pin->output_value = !!value;
516b4c45fe9SBjorn Andersson 
517b4c45fe9SBjorn Andersson 	val = pin->mode << 2;
518b4c45fe9SBjorn Andersson 	val |= pin->open_drain << 1;
519b4c45fe9SBjorn Andersson 	val |= pin->output_value;
520b4c45fe9SBjorn Andersson 
521b4c45fe9SBjorn Andersson 	pm8xxx_write_bank(pctrl, pin, 1, val);
522b4c45fe9SBjorn Andersson }
523b4c45fe9SBjorn Andersson 
pm8xxx_gpio_of_xlate(struct gpio_chip * chip,const struct of_phandle_args * gpio_desc,u32 * flags)524b4c45fe9SBjorn Andersson static int pm8xxx_gpio_of_xlate(struct gpio_chip *chip,
525b4c45fe9SBjorn Andersson 				const struct of_phandle_args *gpio_desc,
526b4c45fe9SBjorn Andersson 				u32 *flags)
527b4c45fe9SBjorn Andersson {
528b4c45fe9SBjorn Andersson 	if (chip->of_gpio_n_cells < 2)
529b4c45fe9SBjorn Andersson 		return -EINVAL;
530b4c45fe9SBjorn Andersson 
531b4c45fe9SBjorn Andersson 	if (flags)
532b4c45fe9SBjorn Andersson 		*flags = gpio_desc->args[1];
533b4c45fe9SBjorn Andersson 
5349d2b563bSBrian Masney 	return gpio_desc->args[0] - PM8XXX_GPIO_PHYSICAL_OFFSET;
535b4c45fe9SBjorn Andersson }
536b4c45fe9SBjorn Andersson 
537b4c45fe9SBjorn Andersson 
538b4c45fe9SBjorn Andersson #ifdef CONFIG_DEBUG_FS
539b4c45fe9SBjorn Andersson 
pm8xxx_gpio_dbg_show_one(struct seq_file * s,struct pinctrl_dev * pctldev,struct gpio_chip * chip,unsigned offset,unsigned gpio)540b4c45fe9SBjorn Andersson static void pm8xxx_gpio_dbg_show_one(struct seq_file *s,
541b4c45fe9SBjorn Andersson 				  struct pinctrl_dev *pctldev,
542b4c45fe9SBjorn Andersson 				  struct gpio_chip *chip,
543b4c45fe9SBjorn Andersson 				  unsigned offset,
544b4c45fe9SBjorn Andersson 				  unsigned gpio)
545b4c45fe9SBjorn Andersson {
546378596f9SLinus Walleij 	struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip);
547b4c45fe9SBjorn Andersson 	struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data;
548b4c45fe9SBjorn Andersson 
549b4c45fe9SBjorn Andersson 	static const char * const modes[] = {
550b4c45fe9SBjorn Andersson 		"in", "both", "out", "off"
551b4c45fe9SBjorn Andersson 	};
552b4c45fe9SBjorn Andersson 	static const char * const biases[] = {
553b4c45fe9SBjorn Andersson 		"pull-up 30uA", "pull-up 1.5uA", "pull-up 31.5uA",
554b4c45fe9SBjorn Andersson 		"pull-up 1.5uA + 30uA boost", "pull-down 10uA", "no pull"
555b4c45fe9SBjorn Andersson 	};
556b4c45fe9SBjorn Andersson 	static const char * const buffer_types[] = {
557b4c45fe9SBjorn Andersson 		"push-pull", "open-drain"
558b4c45fe9SBjorn Andersson 	};
559b4c45fe9SBjorn Andersson 	static const char * const strengths[] = {
560b4c45fe9SBjorn Andersson 		"no", "high", "medium", "low"
561b4c45fe9SBjorn Andersson 	};
562b4c45fe9SBjorn Andersson 
5639d2b563bSBrian Masney 	seq_printf(s, " gpio%-2d:", offset + PM8XXX_GPIO_PHYSICAL_OFFSET);
564b4c45fe9SBjorn Andersson 	if (pin->disable) {
565b4c45fe9SBjorn Andersson 		seq_puts(s, " ---");
566b4c45fe9SBjorn Andersson 	} else {
567b4c45fe9SBjorn Andersson 		seq_printf(s, " %-4s", modes[pin->mode]);
568b4c45fe9SBjorn Andersson 		seq_printf(s, " %-7s", pm8xxx_gpio_functions[pin->function]);
569b4c45fe9SBjorn Andersson 		seq_printf(s, " VIN%d", pin->power_source);
570b4c45fe9SBjorn Andersson 		seq_printf(s, " %-27s", biases[pin->bias]);
571b4c45fe9SBjorn Andersson 		seq_printf(s, " %-10s", buffer_types[pin->open_drain]);
572b4c45fe9SBjorn Andersson 		seq_printf(s, " %-4s", pin->output_value ? "high" : "low");
573b4c45fe9SBjorn Andersson 		seq_printf(s, " %-7s", strengths[pin->output_strength]);
574b4c45fe9SBjorn Andersson 		if (pin->inverted)
575b4c45fe9SBjorn Andersson 			seq_puts(s, " inverted");
576b4c45fe9SBjorn Andersson 	}
577b4c45fe9SBjorn Andersson }
578b4c45fe9SBjorn Andersson 
pm8xxx_gpio_dbg_show(struct seq_file * s,struct gpio_chip * chip)579b4c45fe9SBjorn Andersson static void pm8xxx_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
580b4c45fe9SBjorn Andersson {
581b4c45fe9SBjorn Andersson 	unsigned gpio = chip->base;
582b4c45fe9SBjorn Andersson 	unsigned i;
583b4c45fe9SBjorn Andersson 
584b4c45fe9SBjorn Andersson 	for (i = 0; i < chip->ngpio; i++, gpio++) {
585b4c45fe9SBjorn Andersson 		pm8xxx_gpio_dbg_show_one(s, NULL, chip, i, gpio);
586b4c45fe9SBjorn Andersson 		seq_puts(s, "\n");
587b4c45fe9SBjorn Andersson 	}
588b4c45fe9SBjorn Andersson }
589b4c45fe9SBjorn Andersson 
590b4c45fe9SBjorn Andersson #else
59111091fb0SJonas Gorski #define pm8xxx_gpio_dbg_show NULL
592b4c45fe9SBjorn Andersson #endif
593b4c45fe9SBjorn Andersson 
59475db1ba1SGustavo A. R. Silva static const struct gpio_chip pm8xxx_gpio_template = {
595b4c45fe9SBjorn Andersson 	.direction_input = pm8xxx_gpio_direction_input,
596b4c45fe9SBjorn Andersson 	.direction_output = pm8xxx_gpio_direction_output,
597b4c45fe9SBjorn Andersson 	.get = pm8xxx_gpio_get,
598b4c45fe9SBjorn Andersson 	.set = pm8xxx_gpio_set,
599b4c45fe9SBjorn Andersson 	.of_xlate = pm8xxx_gpio_of_xlate,
600b4c45fe9SBjorn Andersson 	.dbg_show = pm8xxx_gpio_dbg_show,
601b4c45fe9SBjorn Andersson 	.owner = THIS_MODULE,
602b4c45fe9SBjorn Andersson };
603b4c45fe9SBjorn Andersson 
pm8xxx_pin_populate(struct pm8xxx_gpio * pctrl,struct pm8xxx_pin_data * pin)604b4c45fe9SBjorn Andersson static int pm8xxx_pin_populate(struct pm8xxx_gpio *pctrl,
605b4c45fe9SBjorn Andersson 			       struct pm8xxx_pin_data *pin)
606b4c45fe9SBjorn Andersson {
607b4c45fe9SBjorn Andersson 	int val;
608b4c45fe9SBjorn Andersson 
609b4c45fe9SBjorn Andersson 	val = pm8xxx_read_bank(pctrl, pin, 0);
610b4c45fe9SBjorn Andersson 	if (val < 0)
611b4c45fe9SBjorn Andersson 		return val;
612b4c45fe9SBjorn Andersson 
613b4c45fe9SBjorn Andersson 	pin->power_source = (val >> 1) & 0x7;
614b4c45fe9SBjorn Andersson 
615b4c45fe9SBjorn Andersson 	val = pm8xxx_read_bank(pctrl, pin, 1);
616b4c45fe9SBjorn Andersson 	if (val < 0)
617b4c45fe9SBjorn Andersson 		return val;
618b4c45fe9SBjorn Andersson 
619b4c45fe9SBjorn Andersson 	pin->mode = (val >> 2) & 0x3;
620b4c45fe9SBjorn Andersson 	pin->open_drain = !!(val & BIT(1));
621b4c45fe9SBjorn Andersson 	pin->output_value = val & BIT(0);
622b4c45fe9SBjorn Andersson 
623b4c45fe9SBjorn Andersson 	val = pm8xxx_read_bank(pctrl, pin, 2);
624b4c45fe9SBjorn Andersson 	if (val < 0)
625b4c45fe9SBjorn Andersson 		return val;
626b4c45fe9SBjorn Andersson 
627b4c45fe9SBjorn Andersson 	pin->bias = (val >> 1) & 0x7;
628b4c45fe9SBjorn Andersson 	if (pin->bias <= PM8XXX_GPIO_BIAS_PU_1P5_30)
629b4c45fe9SBjorn Andersson 		pin->pull_up_strength = pin->bias;
630b4c45fe9SBjorn Andersson 	else
631b4c45fe9SBjorn Andersson 		pin->pull_up_strength = PM8XXX_GPIO_BIAS_PU_30;
632b4c45fe9SBjorn Andersson 
633b4c45fe9SBjorn Andersson 	val = pm8xxx_read_bank(pctrl, pin, 3);
634b4c45fe9SBjorn Andersson 	if (val < 0)
635b4c45fe9SBjorn Andersson 		return val;
636b4c45fe9SBjorn Andersson 
637b4c45fe9SBjorn Andersson 	pin->output_strength = (val >> 2) & 0x3;
638b4c45fe9SBjorn Andersson 	pin->disable = val & BIT(0);
639b4c45fe9SBjorn Andersson 
640b4c45fe9SBjorn Andersson 	val = pm8xxx_read_bank(pctrl, pin, 4);
641b4c45fe9SBjorn Andersson 	if (val < 0)
642b4c45fe9SBjorn Andersson 		return val;
643b4c45fe9SBjorn Andersson 
644b4c45fe9SBjorn Andersson 	pin->function = (val >> 1) & 0x7;
645b4c45fe9SBjorn Andersson 
646b4c45fe9SBjorn Andersson 	val = pm8xxx_read_bank(pctrl, pin, 5);
647b4c45fe9SBjorn Andersson 	if (val < 0)
648b4c45fe9SBjorn Andersson 		return val;
649b4c45fe9SBjorn Andersson 
650b4c45fe9SBjorn Andersson 	pin->inverted = !(val & BIT(3));
651b4c45fe9SBjorn Andersson 
652b4c45fe9SBjorn Andersson 	return 0;
653b4c45fe9SBjorn Andersson }
654b4c45fe9SBjorn Andersson 
pm8xxx_irq_disable(struct irq_data * d)65519bcff7eSLinus Walleij static void pm8xxx_irq_disable(struct irq_data *d)
65619bcff7eSLinus Walleij {
65719bcff7eSLinus Walleij 	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
65819bcff7eSLinus Walleij 
65919bcff7eSLinus Walleij 	gpiochip_disable_irq(gc, irqd_to_hwirq(d));
66019bcff7eSLinus Walleij }
66119bcff7eSLinus Walleij 
pm8xxx_irq_enable(struct irq_data * d)66219bcff7eSLinus Walleij static void pm8xxx_irq_enable(struct irq_data *d)
66319bcff7eSLinus Walleij {
66419bcff7eSLinus Walleij 	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
66519bcff7eSLinus Walleij 
66619bcff7eSLinus Walleij 	gpiochip_enable_irq(gc, irqd_to_hwirq(d));
66719bcff7eSLinus Walleij }
66819bcff7eSLinus Walleij 
66919bcff7eSLinus Walleij static const struct irq_chip pm8xxx_irq_chip = {
6709d2b563bSBrian Masney 	.name = "ssbi-gpio",
6719d2b563bSBrian Masney 	.irq_mask_ack = irq_chip_mask_ack_parent,
6729d2b563bSBrian Masney 	.irq_unmask = irq_chip_unmask_parent,
67319bcff7eSLinus Walleij 	.irq_disable = pm8xxx_irq_disable,
67419bcff7eSLinus Walleij 	.irq_enable = pm8xxx_irq_enable,
6759d2b563bSBrian Masney 	.irq_set_type = irq_chip_set_type_parent,
67619bcff7eSLinus Walleij 	.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE |
67719bcff7eSLinus Walleij 		IRQCHIP_IMMUTABLE,
67819bcff7eSLinus Walleij 	GPIOCHIP_IRQ_RESOURCE_HELPERS,
6799d2b563bSBrian Masney };
6809d2b563bSBrian Masney 
pm8xxx_domain_translate(struct irq_domain * domain,struct irq_fwspec * fwspec,unsigned long * hwirq,unsigned int * type)6819d2b563bSBrian Masney static int pm8xxx_domain_translate(struct irq_domain *domain,
6829d2b563bSBrian Masney 				   struct irq_fwspec *fwspec,
6839d2b563bSBrian Masney 				   unsigned long *hwirq,
6849d2b563bSBrian Masney 				   unsigned int *type)
6859d2b563bSBrian Masney {
6869d2b563bSBrian Masney 	struct pm8xxx_gpio *pctrl = container_of(domain->host_data,
6879d2b563bSBrian Masney 						 struct pm8xxx_gpio, chip);
6889d2b563bSBrian Masney 
68979890c2eSBrian Masney 	if (fwspec->param_count != 2 || fwspec->param[0] < 1 ||
69079890c2eSBrian Masney 	    fwspec->param[0] > pctrl->chip.ngpio)
6919d2b563bSBrian Masney 		return -EINVAL;
6929d2b563bSBrian Masney 
6939d2b563bSBrian Masney 	*hwirq = fwspec->param[0] - PM8XXX_GPIO_PHYSICAL_OFFSET;
6949d2b563bSBrian Masney 	*type = fwspec->param[1];
6959d2b563bSBrian Masney 
6969d2b563bSBrian Masney 	return 0;
6979d2b563bSBrian Masney }
6989d2b563bSBrian Masney 
pm8xxx_child_offset_to_irq(struct gpio_chip * chip,unsigned int offset)699ae436fe8SBrian Masney static unsigned int pm8xxx_child_offset_to_irq(struct gpio_chip *chip,
700ae436fe8SBrian Masney 					       unsigned int offset)
7019d2b563bSBrian Masney {
702ae436fe8SBrian Masney 	return offset + PM8XXX_GPIO_PHYSICAL_OFFSET;
7039d2b563bSBrian Masney }
7049d2b563bSBrian Masney 
pm8xxx_child_to_parent_hwirq(struct gpio_chip * chip,unsigned int child_hwirq,unsigned int child_type,unsigned int * parent_hwirq,unsigned int * parent_type)705ae436fe8SBrian Masney static int pm8xxx_child_to_parent_hwirq(struct gpio_chip *chip,
706ae436fe8SBrian Masney 					unsigned int child_hwirq,
707ae436fe8SBrian Masney 					unsigned int child_type,
708ae436fe8SBrian Masney 					unsigned int *parent_hwirq,
709ae436fe8SBrian Masney 					unsigned int *parent_type)
710ae436fe8SBrian Masney {
711ae436fe8SBrian Masney 	*parent_hwirq = child_hwirq + 0xc0;
712ae436fe8SBrian Masney 	*parent_type = child_type;
713ae436fe8SBrian Masney 
714ae436fe8SBrian Masney 	return 0;
715ae436fe8SBrian Masney }
7169d2b563bSBrian Masney 
717b4c45fe9SBjorn Andersson static const struct of_device_id pm8xxx_gpio_of_match[] = {
71886291029SBrian Masney 	{ .compatible = "qcom,pm8018-gpio", .data = (void *) 6 },
71986291029SBrian Masney 	{ .compatible = "qcom,pm8038-gpio", .data = (void *) 12 },
72086291029SBrian Masney 	{ .compatible = "qcom,pm8058-gpio", .data = (void *) 44 },
72186291029SBrian Masney 	{ .compatible = "qcom,pm8917-gpio", .data = (void *) 38 },
72286291029SBrian Masney 	{ .compatible = "qcom,pm8921-gpio", .data = (void *) 44 },
723b4c45fe9SBjorn Andersson 	{ },
724b4c45fe9SBjorn Andersson };
725b4c45fe9SBjorn Andersson MODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match);
726b4c45fe9SBjorn Andersson 
pm8xxx_gpio_probe(struct platform_device * pdev)727b4c45fe9SBjorn Andersson static int pm8xxx_gpio_probe(struct platform_device *pdev)
728b4c45fe9SBjorn Andersson {
729b4c45fe9SBjorn Andersson 	struct pm8xxx_pin_data *pin_data;
7309d2b563bSBrian Masney 	struct irq_domain *parent_domain;
7319d2b563bSBrian Masney 	struct device_node *parent_node;
732b4c45fe9SBjorn Andersson 	struct pinctrl_pin_desc *pins;
733ae436fe8SBrian Masney 	struct gpio_irq_chip *girq;
734b4c45fe9SBjorn Andersson 	struct pm8xxx_gpio *pctrl;
73586291029SBrian Masney 	int ret, i;
736b4c45fe9SBjorn Andersson 
737b4c45fe9SBjorn Andersson 	pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL);
738b4c45fe9SBjorn Andersson 	if (!pctrl)
739b4c45fe9SBjorn Andersson 		return -ENOMEM;
740b4c45fe9SBjorn Andersson 
741b4c45fe9SBjorn Andersson 	pctrl->dev = &pdev->dev;
74286291029SBrian Masney 	pctrl->npins = (uintptr_t) device_get_match_data(&pdev->dev);
743b4c45fe9SBjorn Andersson 
744b4c45fe9SBjorn Andersson 	pctrl->regmap = dev_get_regmap(pdev->dev.parent, NULL);
745b4c45fe9SBjorn Andersson 	if (!pctrl->regmap) {
746b4c45fe9SBjorn Andersson 		dev_err(&pdev->dev, "parent regmap unavailable\n");
747b4c45fe9SBjorn Andersson 		return -ENXIO;
748b4c45fe9SBjorn Andersson 	}
749b4c45fe9SBjorn Andersson 
750b4c45fe9SBjorn Andersson 	pctrl->desc = pm8xxx_pinctrl_desc;
751b4c45fe9SBjorn Andersson 	pctrl->desc.npins = pctrl->npins;
752b4c45fe9SBjorn Andersson 
753b4c45fe9SBjorn Andersson 	pins = devm_kcalloc(&pdev->dev,
754b4c45fe9SBjorn Andersson 			    pctrl->desc.npins,
755b4c45fe9SBjorn Andersson 			    sizeof(struct pinctrl_pin_desc),
756b4c45fe9SBjorn Andersson 			    GFP_KERNEL);
757b4c45fe9SBjorn Andersson 	if (!pins)
758b4c45fe9SBjorn Andersson 		return -ENOMEM;
759b4c45fe9SBjorn Andersson 
760b4c45fe9SBjorn Andersson 	pin_data = devm_kcalloc(&pdev->dev,
761b4c45fe9SBjorn Andersson 				pctrl->desc.npins,
762b4c45fe9SBjorn Andersson 				sizeof(struct pm8xxx_pin_data),
763b4c45fe9SBjorn Andersson 				GFP_KERNEL);
764b4c45fe9SBjorn Andersson 	if (!pin_data)
765b4c45fe9SBjorn Andersson 		return -ENOMEM;
766b4c45fe9SBjorn Andersson 
767b4c45fe9SBjorn Andersson 	for (i = 0; i < pctrl->desc.npins; i++) {
768b4c45fe9SBjorn Andersson 		pin_data[i].reg = SSBI_REG_ADDR_GPIO(i);
769b4c45fe9SBjorn Andersson 
770b4c45fe9SBjorn Andersson 		ret = pm8xxx_pin_populate(pctrl, &pin_data[i]);
771b4c45fe9SBjorn Andersson 		if (ret)
772b4c45fe9SBjorn Andersson 			return ret;
773b4c45fe9SBjorn Andersson 
774b4c45fe9SBjorn Andersson 		pins[i].number = i;
775b4c45fe9SBjorn Andersson 		pins[i].name = pm8xxx_groups[i];
776b4c45fe9SBjorn Andersson 		pins[i].drv_data = &pin_data[i];
777b4c45fe9SBjorn Andersson 	}
778b4c45fe9SBjorn Andersson 	pctrl->desc.pins = pins;
779b4c45fe9SBjorn Andersson 
780b4c45fe9SBjorn Andersson 	pctrl->desc.num_custom_params = ARRAY_SIZE(pm8xxx_gpio_bindings);
781b4c45fe9SBjorn Andersson 	pctrl->desc.custom_params = pm8xxx_gpio_bindings;
782b4c45fe9SBjorn Andersson #ifdef CONFIG_DEBUG_FS
783b4c45fe9SBjorn Andersson 	pctrl->desc.custom_conf_items = pm8xxx_conf_items;
784b4c45fe9SBjorn Andersson #endif
785b4c45fe9SBjorn Andersson 
78616f3b9c3SLaxman Dewangan 	pctrl->pctrl = devm_pinctrl_register(&pdev->dev, &pctrl->desc, pctrl);
787d259ec26SJulia Lawall 	if (IS_ERR(pctrl->pctrl)) {
788b4c45fe9SBjorn Andersson 		dev_err(&pdev->dev, "couldn't register pm8xxx gpio driver\n");
789d259ec26SJulia Lawall 		return PTR_ERR(pctrl->pctrl);
790b4c45fe9SBjorn Andersson 	}
791b4c45fe9SBjorn Andersson 
792b4c45fe9SBjorn Andersson 	pctrl->chip = pm8xxx_gpio_template;
793b4c45fe9SBjorn Andersson 	pctrl->chip.base = -1;
79458383c78SLinus Walleij 	pctrl->chip.parent = &pdev->dev;
795b4c45fe9SBjorn Andersson 	pctrl->chip.of_gpio_n_cells = 2;
796b4c45fe9SBjorn Andersson 	pctrl->chip.label = dev_name(pctrl->dev);
797b4c45fe9SBjorn Andersson 	pctrl->chip.ngpio = pctrl->npins;
7989d2b563bSBrian Masney 
7999d2b563bSBrian Masney 	parent_node = of_irq_find_parent(pctrl->dev->of_node);
8009d2b563bSBrian Masney 	if (!parent_node)
8019d2b563bSBrian Masney 		return -ENXIO;
8029d2b563bSBrian Masney 
8039d2b563bSBrian Masney 	parent_domain = irq_find_host(parent_node);
8049d2b563bSBrian Masney 	of_node_put(parent_node);
8059d2b563bSBrian Masney 	if (!parent_domain)
8069d2b563bSBrian Masney 		return -ENXIO;
8079d2b563bSBrian Masney 
808ae436fe8SBrian Masney 	girq = &pctrl->chip.irq;
80919bcff7eSLinus Walleij 	gpio_irq_chip_set_chip(girq, &pm8xxx_irq_chip);
810ae436fe8SBrian Masney 	girq->default_type = IRQ_TYPE_NONE;
811ae436fe8SBrian Masney 	girq->handler = handle_level_irq;
8129bd73ce0SAndy Shevchenko 	girq->fwnode = dev_fwnode(pctrl->dev);
813ae436fe8SBrian Masney 	girq->parent_domain = parent_domain;
814ae436fe8SBrian Masney 	girq->child_to_parent_hwirq = pm8xxx_child_to_parent_hwirq;
815f9837147SLinus Walleij 	girq->populate_parent_alloc_arg = gpiochip_populate_parent_fwspec_twocell;
816ae436fe8SBrian Masney 	girq->child_offset_to_irq = pm8xxx_child_offset_to_irq;
817ae436fe8SBrian Masney 	girq->child_irq_domain_ops.translate = pm8xxx_domain_translate;
8189d2b563bSBrian Masney 
819378596f9SLinus Walleij 	ret = gpiochip_add_data(&pctrl->chip, pctrl);
820b4c45fe9SBjorn Andersson 	if (ret) {
821b4c45fe9SBjorn Andersson 		dev_err(&pdev->dev, "failed register gpiochip\n");
822ae436fe8SBrian Masney 		return ret;
823b4c45fe9SBjorn Andersson 	}
824b4c45fe9SBjorn Andersson 
8257ed07855SBrian Masney 	/*
8267ed07855SBrian Masney 	 * For DeviceTree-supported systems, the gpio core checks the
8277ed07855SBrian Masney 	 * pinctrl's device node for the "gpio-ranges" property.
8287ed07855SBrian Masney 	 * If it is present, it takes care of adding the pin ranges
8297ed07855SBrian Masney 	 * for the driver. In this case the driver can skip ahead.
8307ed07855SBrian Masney 	 *
8317ed07855SBrian Masney 	 * In order to remain compatible with older, existing DeviceTree
8327ed07855SBrian Masney 	 * files which don't set the "gpio-ranges" property or systems that
8337ed07855SBrian Masney 	 * utilize ACPI the driver has to call gpiochip_add_pin_range().
8347ed07855SBrian Masney 	 */
8357ed07855SBrian Masney 	if (!of_property_read_bool(pctrl->dev->of_node, "gpio-ranges")) {
8367ed07855SBrian Masney 		ret = gpiochip_add_pin_range(&pctrl->chip, dev_name(pctrl->dev),
837b4c45fe9SBjorn Andersson 					     0, 0, pctrl->chip.ngpio);
838b4c45fe9SBjorn Andersson 		if (ret) {
839b4c45fe9SBjorn Andersson 			dev_err(pctrl->dev, "failed to add pin range\n");
840b4c45fe9SBjorn Andersson 			goto unregister_gpiochip;
841b4c45fe9SBjorn Andersson 		}
8427ed07855SBrian Masney 	}
843b4c45fe9SBjorn Andersson 
844b4c45fe9SBjorn Andersson 	platform_set_drvdata(pdev, pctrl);
845b4c45fe9SBjorn Andersson 
846b4c45fe9SBjorn Andersson 	dev_dbg(&pdev->dev, "Qualcomm pm8xxx gpio driver probed\n");
847b4c45fe9SBjorn Andersson 
848b4c45fe9SBjorn Andersson 	return 0;
849b4c45fe9SBjorn Andersson 
850b4c45fe9SBjorn Andersson unregister_gpiochip:
851b4c45fe9SBjorn Andersson 	gpiochip_remove(&pctrl->chip);
852b4c45fe9SBjorn Andersson 
853b4c45fe9SBjorn Andersson 	return ret;
854b4c45fe9SBjorn Andersson }
855b4c45fe9SBjorn Andersson 
pm8xxx_gpio_remove(struct platform_device * pdev)856*a37b0613SUwe Kleine-König static void pm8xxx_gpio_remove(struct platform_device *pdev)
857b4c45fe9SBjorn Andersson {
858b4c45fe9SBjorn Andersson 	struct pm8xxx_gpio *pctrl = platform_get_drvdata(pdev);
859b4c45fe9SBjorn Andersson 
860b4c45fe9SBjorn Andersson 	gpiochip_remove(&pctrl->chip);
861b4c45fe9SBjorn Andersson }
862b4c45fe9SBjorn Andersson 
863b4c45fe9SBjorn Andersson static struct platform_driver pm8xxx_gpio_driver = {
864b4c45fe9SBjorn Andersson 	.driver = {
865b4c45fe9SBjorn Andersson 		.name = "qcom-ssbi-gpio",
866b4c45fe9SBjorn Andersson 		.of_match_table = pm8xxx_gpio_of_match,
867b4c45fe9SBjorn Andersson 	},
868b4c45fe9SBjorn Andersson 	.probe = pm8xxx_gpio_probe,
869*a37b0613SUwe Kleine-König 	.remove_new = pm8xxx_gpio_remove,
870b4c45fe9SBjorn Andersson };
871b4c45fe9SBjorn Andersson 
872b4c45fe9SBjorn Andersson module_platform_driver(pm8xxx_gpio_driver);
873b4c45fe9SBjorn Andersson 
874b4c45fe9SBjorn Andersson MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
875b4c45fe9SBjorn Andersson MODULE_DESCRIPTION("Qualcomm PM8xxx GPIO driver");
876b4c45fe9SBjorn Andersson MODULE_LICENSE("GPL v2");
877