197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2cfb24f6eSIvan T. Ivanov /*
3cfb24f6eSIvan T. Ivanov  * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
4cfb24f6eSIvan T. Ivanov  */
5cfb24f6eSIvan T. Ivanov 
61c5fb66aSLinus Walleij #include <linux/gpio/driver.h>
7cfb24f6eSIvan T. Ivanov #include <linux/module.h>
8cfb24f6eSIvan T. Ivanov #include <linux/of.h>
9ab4256cfSStephen Boyd #include <linux/of_irq.h>
10aa9430f8SAndy Shevchenko #include <linux/platform_device.h>
11aa9430f8SAndy Shevchenko #include <linux/regmap.h>
12aa9430f8SAndy Shevchenko #include <linux/seq_file.h>
13aa9430f8SAndy Shevchenko #include <linux/slab.h>
14aa9430f8SAndy Shevchenko #include <linux/types.h>
15aa9430f8SAndy Shevchenko 
16cfb24f6eSIvan T. Ivanov #include <linux/pinctrl/pinconf-generic.h>
17cfb24f6eSIvan T. Ivanov #include <linux/pinctrl/pinconf.h>
18cfb24f6eSIvan T. Ivanov #include <linux/pinctrl/pinmux.h>
19cfb24f6eSIvan T. Ivanov 
20cfb24f6eSIvan T. Ivanov #include <dt-bindings/pinctrl/qcom,pmic-mpp.h>
21cfb24f6eSIvan T. Ivanov 
22cfb24f6eSIvan T. Ivanov #include "../core.h"
23cfb24f6eSIvan T. Ivanov #include "../pinctrl-utils.h"
24cfb24f6eSIvan T. Ivanov 
25cfb24f6eSIvan T. Ivanov #define PMIC_MPP_ADDRESS_RANGE			0x100
26cfb24f6eSIvan T. Ivanov 
27cfb24f6eSIvan T. Ivanov /*
28cfb24f6eSIvan T. Ivanov  * Pull Up Values - it indicates whether a pull-up should be
29cfb24f6eSIvan T. Ivanov  * applied for bidirectional mode only. The hardware ignores the
30cfb24f6eSIvan T. Ivanov  * configuration when operating in other modes.
31cfb24f6eSIvan T. Ivanov  */
32cfb24f6eSIvan T. Ivanov #define PMIC_MPP_PULL_UP_0P6KOHM		0
33cfb24f6eSIvan T. Ivanov #define PMIC_MPP_PULL_UP_10KOHM			1
34cfb24f6eSIvan T. Ivanov #define PMIC_MPP_PULL_UP_30KOHM			2
35cfb24f6eSIvan T. Ivanov #define PMIC_MPP_PULL_UP_OPEN			3
36cfb24f6eSIvan T. Ivanov 
37cfb24f6eSIvan T. Ivanov /* type registers base address bases */
38cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_TYPE			0x4
39cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_SUBTYPE			0x5
40cfb24f6eSIvan T. Ivanov 
41cfb24f6eSIvan T. Ivanov /* mpp peripheral type and subtype values */
42cfb24f6eSIvan T. Ivanov #define PMIC_MPP_TYPE				0x11
43cfb24f6eSIvan T. Ivanov #define PMIC_MPP_SUBTYPE_4CH_NO_ANA_OUT		0x3
44cfb24f6eSIvan T. Ivanov #define PMIC_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT	0x4
45cfb24f6eSIvan T. Ivanov #define PMIC_MPP_SUBTYPE_4CH_NO_SINK		0x5
46cfb24f6eSIvan T. Ivanov #define PMIC_MPP_SUBTYPE_ULT_4CH_NO_SINK	0x6
47cfb24f6eSIvan T. Ivanov #define PMIC_MPP_SUBTYPE_4CH_FULL_FUNC		0x7
48cfb24f6eSIvan T. Ivanov #define PMIC_MPP_SUBTYPE_8CH_FULL_FUNC		0xf
49cfb24f6eSIvan T. Ivanov 
50cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_RT_STS			0x10
51cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_RT_STS_VAL_MASK		0x1
52cfb24f6eSIvan T. Ivanov 
53cfb24f6eSIvan T. Ivanov /* control register base address bases */
54cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_MODE_CTL			0x40
55cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_DIG_VIN_CTL		0x41
56cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_DIG_PULL_CTL		0x42
57cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_DIG_IN_CTL			0x43
58cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_EN_CTL			0x46
59099f3e4aSBjorn Andersson #define PMIC_MPP_REG_AOUT_CTL			0x48
60cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_AIN_CTL			0x4a
610e948042SBjorn Andersson #define PMIC_MPP_REG_SINK_CTL			0x4c
62cfb24f6eSIvan T. Ivanov 
63cfb24f6eSIvan T. Ivanov /* PMIC_MPP_REG_MODE_CTL */
64cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_MODE_VALUE_MASK		0x1
65cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_MODE_FUNCTION_SHIFT	1
66cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_MODE_FUNCTION_MASK		0x7
67cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_MODE_DIR_SHIFT		4
68cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_MODE_DIR_MASK		0x7
69cfb24f6eSIvan T. Ivanov 
70cfb24f6eSIvan T. Ivanov /* PMIC_MPP_REG_DIG_VIN_CTL */
71cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_VIN_SHIFT			0
72cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_VIN_MASK			0x7
73cfb24f6eSIvan T. Ivanov 
74cfb24f6eSIvan T. Ivanov /* PMIC_MPP_REG_DIG_PULL_CTL */
75cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_PULL_SHIFT			0
76cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_PULL_MASK			0x7
77cfb24f6eSIvan T. Ivanov 
78cfb24f6eSIvan T. Ivanov /* PMIC_MPP_REG_EN_CTL */
79cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_MASTER_EN_SHIFT		7
80cfb24f6eSIvan T. Ivanov 
81cfb24f6eSIvan T. Ivanov /* PMIC_MPP_REG_AIN_CTL */
82cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_AIN_ROUTE_SHIFT		0
83cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_AIN_ROUTE_MASK		0x7
84cfb24f6eSIvan T. Ivanov 
85eaaf5dd4SBjorn Andersson #define PMIC_MPP_MODE_DIGITAL_INPUT		0
86eaaf5dd4SBjorn Andersson #define PMIC_MPP_MODE_DIGITAL_OUTPUT		1
87eaaf5dd4SBjorn Andersson #define PMIC_MPP_MODE_DIGITAL_BIDIR		2
88eaaf5dd4SBjorn Andersson #define PMIC_MPP_MODE_ANALOG_BIDIR		3
89eaaf5dd4SBjorn Andersson #define PMIC_MPP_MODE_ANALOG_INPUT		4
90eaaf5dd4SBjorn Andersson #define PMIC_MPP_MODE_ANALOG_OUTPUT		5
91eaaf5dd4SBjorn Andersson #define PMIC_MPP_MODE_CURRENT_SINK		6
92eaaf5dd4SBjorn Andersson 
93eb5c144cSBjorn Andersson #define PMIC_MPP_SELECTOR_NORMAL		0
94eb5c144cSBjorn Andersson #define PMIC_MPP_SELECTOR_PAIRED		1
95eb5c144cSBjorn Andersson #define PMIC_MPP_SELECTOR_DTEST_FIRST		4
96eb5c144cSBjorn Andersson 
97cfb24f6eSIvan T. Ivanov #define PMIC_MPP_PHYSICAL_OFFSET		1
98cfb24f6eSIvan T. Ivanov 
99cfb24f6eSIvan T. Ivanov /* Qualcomm specific pin configurations */
100cfb24f6eSIvan T. Ivanov #define PMIC_MPP_CONF_AMUX_ROUTE		(PIN_CONFIG_END + 1)
101eb5c144cSBjorn Andersson #define PMIC_MPP_CONF_ANALOG_LEVEL		(PIN_CONFIG_END + 2)
102eb5c144cSBjorn Andersson #define PMIC_MPP_CONF_DTEST_SELECTOR		(PIN_CONFIG_END + 3)
103eb5c144cSBjorn Andersson #define PMIC_MPP_CONF_PAIRED			(PIN_CONFIG_END + 4)
104cfb24f6eSIvan T. Ivanov 
105cfb24f6eSIvan T. Ivanov /**
106cfb24f6eSIvan T. Ivanov  * struct pmic_mpp_pad - keep current MPP settings
107cfb24f6eSIvan T. Ivanov  * @base: Address base in SPMI device.
108cfb24f6eSIvan T. Ivanov  * @is_enabled: Set to false when MPP should be put in high Z state.
109cfb24f6eSIvan T. Ivanov  * @out_value: Cached pin output value.
110cfb24f6eSIvan T. Ivanov  * @output_enabled: Set to true if MPP output logic is enabled.
111cfb24f6eSIvan T. Ivanov  * @input_enabled: Set to true if MPP input buffer logic is enabled.
112eb5c144cSBjorn Andersson  * @paired: Pin operates in paired mode
113ae6d54fdSStephen Boyd  * @has_pullup: Pin has support to configure pullup
114cfb24f6eSIvan T. Ivanov  * @num_sources: Number of power-sources supported by this MPP.
115cfb24f6eSIvan T. Ivanov  * @power_source: Current power-source used.
116cfb24f6eSIvan T. Ivanov  * @amux_input: Set the source for analog input.
117099f3e4aSBjorn Andersson  * @aout_level: Analog output level
118cfb24f6eSIvan T. Ivanov  * @pullup: Pullup resistor value. Valid in Bidirectional mode only.
119cfb24f6eSIvan T. Ivanov  * @function: See pmic_mpp_functions[].
1200e948042SBjorn Andersson  * @drive_strength: Amount of current in sink mode
121eb5c144cSBjorn Andersson  * @dtest: DTEST route selector
122cfb24f6eSIvan T. Ivanov  */
123cfb24f6eSIvan T. Ivanov struct pmic_mpp_pad {
124cfb24f6eSIvan T. Ivanov 	u16		base;
125cfb24f6eSIvan T. Ivanov 	bool		is_enabled;
126cfb24f6eSIvan T. Ivanov 	bool		out_value;
127cfb24f6eSIvan T. Ivanov 	bool		output_enabled;
128cfb24f6eSIvan T. Ivanov 	bool		input_enabled;
129eb5c144cSBjorn Andersson 	bool		paired;
130ae6d54fdSStephen Boyd 	bool		has_pullup;
131cfb24f6eSIvan T. Ivanov 	unsigned int	num_sources;
132cfb24f6eSIvan T. Ivanov 	unsigned int	power_source;
133cfb24f6eSIvan T. Ivanov 	unsigned int	amux_input;
134099f3e4aSBjorn Andersson 	unsigned int	aout_level;
135cfb24f6eSIvan T. Ivanov 	unsigned int	pullup;
136cfb24f6eSIvan T. Ivanov 	unsigned int	function;
1370e948042SBjorn Andersson 	unsigned int	drive_strength;
138eb5c144cSBjorn Andersson 	unsigned int	dtest;
139cfb24f6eSIvan T. Ivanov };
140cfb24f6eSIvan T. Ivanov 
141cfb24f6eSIvan T. Ivanov struct pmic_mpp_state {
142cfb24f6eSIvan T. Ivanov 	struct device	*dev;
143cfb24f6eSIvan T. Ivanov 	struct regmap	*map;
144cfb24f6eSIvan T. Ivanov 	struct pinctrl_dev *ctrl;
145cfb24f6eSIvan T. Ivanov 	struct gpio_chip chip;
146cfb24f6eSIvan T. Ivanov };
147cfb24f6eSIvan T. Ivanov 
148ba5f94cdSBjorn Andersson static const struct pinconf_generic_params pmic_mpp_bindings[] = {
149ba5f94cdSBjorn Andersson 	{"qcom,amux-route",	PMIC_MPP_CONF_AMUX_ROUTE,	0},
150099f3e4aSBjorn Andersson 	{"qcom,analog-level",	PMIC_MPP_CONF_ANALOG_LEVEL,	0},
151eb5c144cSBjorn Andersson 	{"qcom,dtest",		PMIC_MPP_CONF_DTEST_SELECTOR,	0},
152eb5c144cSBjorn Andersson 	{"qcom,paired",		PMIC_MPP_CONF_PAIRED,		0},
153cfb24f6eSIvan T. Ivanov };
154cfb24f6eSIvan T. Ivanov 
155ba5f94cdSBjorn Andersson #ifdef CONFIG_DEBUG_FS
156ba5f94cdSBjorn Andersson static const struct pin_config_item pmic_conf_items[] = {
157ba5f94cdSBjorn Andersson 	PCONFDUMP(PMIC_MPP_CONF_AMUX_ROUTE, "analog mux", NULL, true),
158099f3e4aSBjorn Andersson 	PCONFDUMP(PMIC_MPP_CONF_ANALOG_LEVEL, "analog level", NULL, true),
159eb5c144cSBjorn Andersson 	PCONFDUMP(PMIC_MPP_CONF_DTEST_SELECTOR, "dtest", NULL, true),
160eb5c144cSBjorn Andersson 	PCONFDUMP(PMIC_MPP_CONF_PAIRED, "paired", NULL, false),
161cfb24f6eSIvan T. Ivanov };
162ba5f94cdSBjorn Andersson #endif
163cfb24f6eSIvan T. Ivanov 
164cfb24f6eSIvan T. Ivanov static const char *const pmic_mpp_groups[] = {
165cfb24f6eSIvan T. Ivanov 	"mpp1", "mpp2", "mpp3", "mpp4", "mpp5", "mpp6", "mpp7", "mpp8",
166cfb24f6eSIvan T. Ivanov };
167cfb24f6eSIvan T. Ivanov 
168eb5c144cSBjorn Andersson #define PMIC_MPP_DIGITAL	0
169eb5c144cSBjorn Andersson #define PMIC_MPP_ANALOG		1
170eb5c144cSBjorn Andersson #define PMIC_MPP_SINK		2
171eb5c144cSBjorn Andersson 
172cfb24f6eSIvan T. Ivanov static const char *const pmic_mpp_functions[] = {
173eb5c144cSBjorn Andersson 	"digital", "analog", "sink"
174cfb24f6eSIvan T. Ivanov };
175cfb24f6eSIvan T. Ivanov 
pmic_mpp_read(struct pmic_mpp_state * state,struct pmic_mpp_pad * pad,unsigned int addr)176cfb24f6eSIvan T. Ivanov static int pmic_mpp_read(struct pmic_mpp_state *state,
177cfb24f6eSIvan T. Ivanov 			 struct pmic_mpp_pad *pad, unsigned int addr)
178cfb24f6eSIvan T. Ivanov {
179cfb24f6eSIvan T. Ivanov 	unsigned int val;
180cfb24f6eSIvan T. Ivanov 	int ret;
181cfb24f6eSIvan T. Ivanov 
182cfb24f6eSIvan T. Ivanov 	ret = regmap_read(state->map, pad->base + addr, &val);
183cfb24f6eSIvan T. Ivanov 	if (ret < 0)
184cfb24f6eSIvan T. Ivanov 		dev_err(state->dev, "read 0x%x failed\n", addr);
185cfb24f6eSIvan T. Ivanov 	else
186cfb24f6eSIvan T. Ivanov 		ret = val;
187cfb24f6eSIvan T. Ivanov 
188cfb24f6eSIvan T. Ivanov 	return ret;
189cfb24f6eSIvan T. Ivanov }
190cfb24f6eSIvan T. Ivanov 
pmic_mpp_write(struct pmic_mpp_state * state,struct pmic_mpp_pad * pad,unsigned int addr,unsigned int val)191cfb24f6eSIvan T. Ivanov static int pmic_mpp_write(struct pmic_mpp_state *state,
192cfb24f6eSIvan T. Ivanov 			  struct pmic_mpp_pad *pad, unsigned int addr,
193cfb24f6eSIvan T. Ivanov 			  unsigned int val)
194cfb24f6eSIvan T. Ivanov {
195cfb24f6eSIvan T. Ivanov 	int ret;
196cfb24f6eSIvan T. Ivanov 
197cfb24f6eSIvan T. Ivanov 	ret = regmap_write(state->map, pad->base + addr, val);
198cfb24f6eSIvan T. Ivanov 	if (ret < 0)
199cfb24f6eSIvan T. Ivanov 		dev_err(state->dev, "write 0x%x failed\n", addr);
200cfb24f6eSIvan T. Ivanov 
201cfb24f6eSIvan T. Ivanov 	return ret;
202cfb24f6eSIvan T. Ivanov }
203cfb24f6eSIvan T. Ivanov 
pmic_mpp_get_groups_count(struct pinctrl_dev * pctldev)204cfb24f6eSIvan T. Ivanov static int pmic_mpp_get_groups_count(struct pinctrl_dev *pctldev)
205cfb24f6eSIvan T. Ivanov {
206cfb24f6eSIvan T. Ivanov 	/* Every PIN is a group */
207cfb24f6eSIvan T. Ivanov 	return pctldev->desc->npins;
208cfb24f6eSIvan T. Ivanov }
209cfb24f6eSIvan T. Ivanov 
pmic_mpp_get_group_name(struct pinctrl_dev * pctldev,unsigned pin)210cfb24f6eSIvan T. Ivanov static const char *pmic_mpp_get_group_name(struct pinctrl_dev *pctldev,
211cfb24f6eSIvan T. Ivanov 					   unsigned pin)
212cfb24f6eSIvan T. Ivanov {
213cfb24f6eSIvan T. Ivanov 	return pctldev->desc->pins[pin].name;
214cfb24f6eSIvan T. Ivanov }
215cfb24f6eSIvan T. Ivanov 
pmic_mpp_get_group_pins(struct pinctrl_dev * pctldev,unsigned pin,const unsigned ** pins,unsigned * num_pins)216cfb24f6eSIvan T. Ivanov static int pmic_mpp_get_group_pins(struct pinctrl_dev *pctldev,
217cfb24f6eSIvan T. Ivanov 				   unsigned pin,
218cfb24f6eSIvan T. Ivanov 				   const unsigned **pins, unsigned *num_pins)
219cfb24f6eSIvan T. Ivanov {
220cfb24f6eSIvan T. Ivanov 	*pins = &pctldev->desc->pins[pin].number;
221cfb24f6eSIvan T. Ivanov 	*num_pins = 1;
222cfb24f6eSIvan T. Ivanov 	return 0;
223cfb24f6eSIvan T. Ivanov }
224cfb24f6eSIvan T. Ivanov 
225cfb24f6eSIvan T. Ivanov static const struct pinctrl_ops pmic_mpp_pinctrl_ops = {
226cfb24f6eSIvan T. Ivanov 	.get_groups_count	= pmic_mpp_get_groups_count,
227cfb24f6eSIvan T. Ivanov 	.get_group_name		= pmic_mpp_get_group_name,
228cfb24f6eSIvan T. Ivanov 	.get_group_pins		= pmic_mpp_get_group_pins,
229ba5f94cdSBjorn Andersson 	.dt_node_to_map		= pinconf_generic_dt_node_to_map_group,
230d32f7fd3SIrina Tirdea 	.dt_free_map		= pinctrl_utils_free_map,
231cfb24f6eSIvan T. Ivanov };
232cfb24f6eSIvan T. Ivanov 
pmic_mpp_get_functions_count(struct pinctrl_dev * pctldev)233cfb24f6eSIvan T. Ivanov static int pmic_mpp_get_functions_count(struct pinctrl_dev *pctldev)
234cfb24f6eSIvan T. Ivanov {
235cfb24f6eSIvan T. Ivanov 	return ARRAY_SIZE(pmic_mpp_functions);
236cfb24f6eSIvan T. Ivanov }
237cfb24f6eSIvan T. Ivanov 
pmic_mpp_get_function_name(struct pinctrl_dev * pctldev,unsigned function)238cfb24f6eSIvan T. Ivanov static const char *pmic_mpp_get_function_name(struct pinctrl_dev *pctldev,
239cfb24f6eSIvan T. Ivanov 					      unsigned function)
240cfb24f6eSIvan T. Ivanov {
241cfb24f6eSIvan T. Ivanov 	return pmic_mpp_functions[function];
242cfb24f6eSIvan T. Ivanov }
243cfb24f6eSIvan T. Ivanov 
pmic_mpp_get_function_groups(struct pinctrl_dev * pctldev,unsigned function,const char * const ** groups,unsigned * const num_qgroups)244cfb24f6eSIvan T. Ivanov static int pmic_mpp_get_function_groups(struct pinctrl_dev *pctldev,
245cfb24f6eSIvan T. Ivanov 					unsigned function,
246cfb24f6eSIvan T. Ivanov 					const char *const **groups,
247cfb24f6eSIvan T. Ivanov 					unsigned *const num_qgroups)
248cfb24f6eSIvan T. Ivanov {
249cfb24f6eSIvan T. Ivanov 	*groups = pmic_mpp_groups;
250cfb24f6eSIvan T. Ivanov 	*num_qgroups = pctldev->desc->npins;
251cfb24f6eSIvan T. Ivanov 	return 0;
252cfb24f6eSIvan T. Ivanov }
253cfb24f6eSIvan T. Ivanov 
pmic_mpp_write_mode_ctl(struct pmic_mpp_state * state,struct pmic_mpp_pad * pad)2540e948042SBjorn Andersson static int pmic_mpp_write_mode_ctl(struct pmic_mpp_state *state,
2550e948042SBjorn Andersson 				   struct pmic_mpp_pad *pad)
2560e948042SBjorn Andersson {
257eb5c144cSBjorn Andersson 	unsigned int mode;
258eb5c144cSBjorn Andersson 	unsigned int sel;
2590e948042SBjorn Andersson 	unsigned int val;
260eb5c144cSBjorn Andersson 	unsigned int en;
2610e948042SBjorn Andersson 
262eb5c144cSBjorn Andersson 	switch (pad->function) {
263eb5c144cSBjorn Andersson 	case PMIC_MPP_ANALOG:
264eb5c144cSBjorn Andersson 		if (pad->input_enabled && pad->output_enabled)
265eb5c144cSBjorn Andersson 			mode = PMIC_MPP_MODE_ANALOG_BIDIR;
266eb5c144cSBjorn Andersson 		else if (pad->input_enabled)
267eb5c144cSBjorn Andersson 			mode = PMIC_MPP_MODE_ANALOG_INPUT;
2680e948042SBjorn Andersson 		else
269eb5c144cSBjorn Andersson 			mode = PMIC_MPP_MODE_ANALOG_OUTPUT;
270eb5c144cSBjorn Andersson 		break;
271eb5c144cSBjorn Andersson 	case PMIC_MPP_DIGITAL:
272eb5c144cSBjorn Andersson 		if (pad->input_enabled && pad->output_enabled)
273eb5c144cSBjorn Andersson 			mode = PMIC_MPP_MODE_DIGITAL_BIDIR;
274eb5c144cSBjorn Andersson 		else if (pad->input_enabled)
275eb5c144cSBjorn Andersson 			mode = PMIC_MPP_MODE_DIGITAL_INPUT;
2760e948042SBjorn Andersson 		else
277eb5c144cSBjorn Andersson 			mode = PMIC_MPP_MODE_DIGITAL_OUTPUT;
278eb5c144cSBjorn Andersson 		break;
279eb5c144cSBjorn Andersson 	case PMIC_MPP_SINK:
280eb5c144cSBjorn Andersson 	default:
281eb5c144cSBjorn Andersson 		mode = PMIC_MPP_MODE_CURRENT_SINK;
282eb5c144cSBjorn Andersson 		break;
2830e948042SBjorn Andersson 	}
2840e948042SBjorn Andersson 
285eb5c144cSBjorn Andersson 	if (pad->dtest)
286eb5c144cSBjorn Andersson 		sel = PMIC_MPP_SELECTOR_DTEST_FIRST + pad->dtest - 1;
287eb5c144cSBjorn Andersson 	else if (pad->paired)
288eb5c144cSBjorn Andersson 		sel = PMIC_MPP_SELECTOR_PAIRED;
289eb5c144cSBjorn Andersson 	else
290eb5c144cSBjorn Andersson 		sel = PMIC_MPP_SELECTOR_NORMAL;
291eb5c144cSBjorn Andersson 
292eb5c144cSBjorn Andersson 	en = !!pad->out_value;
293eb5c144cSBjorn Andersson 
294eb5c144cSBjorn Andersson 	val = mode << PMIC_MPP_REG_MODE_DIR_SHIFT |
295eb5c144cSBjorn Andersson 	      sel << PMIC_MPP_REG_MODE_FUNCTION_SHIFT |
296eb5c144cSBjorn Andersson 	      en;
2970e948042SBjorn Andersson 
2980e948042SBjorn Andersson 	return pmic_mpp_write(state, pad, PMIC_MPP_REG_MODE_CTL, val);
2990e948042SBjorn Andersson }
3000e948042SBjorn Andersson 
pmic_mpp_set_mux(struct pinctrl_dev * pctldev,unsigned function,unsigned pin)301cfb24f6eSIvan T. Ivanov static int pmic_mpp_set_mux(struct pinctrl_dev *pctldev, unsigned function,
302cfb24f6eSIvan T. Ivanov 				unsigned pin)
303cfb24f6eSIvan T. Ivanov {
304cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_state *state = pinctrl_dev_get_drvdata(pctldev);
305cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_pad *pad;
306cfb24f6eSIvan T. Ivanov 	unsigned int val;
307cfb24f6eSIvan T. Ivanov 	int ret;
308cfb24f6eSIvan T. Ivanov 
309cfb24f6eSIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
310cfb24f6eSIvan T. Ivanov 
311cfb24f6eSIvan T. Ivanov 	pad->function = function;
312cfb24f6eSIvan T. Ivanov 
3130e948042SBjorn Andersson 	ret = pmic_mpp_write_mode_ctl(state, pad);
31469f8455fSYueHaibing 	if (ret < 0)
31569f8455fSYueHaibing 		return ret;
316cfb24f6eSIvan T. Ivanov 
317cfb24f6eSIvan T. Ivanov 	val = pad->is_enabled << PMIC_MPP_REG_MASTER_EN_SHIFT;
318cfb24f6eSIvan T. Ivanov 
319cfb24f6eSIvan T. Ivanov 	return pmic_mpp_write(state, pad, PMIC_MPP_REG_EN_CTL, val);
320cfb24f6eSIvan T. Ivanov }
321cfb24f6eSIvan T. Ivanov 
322cfb24f6eSIvan T. Ivanov static const struct pinmux_ops pmic_mpp_pinmux_ops = {
323cfb24f6eSIvan T. Ivanov 	.get_functions_count	= pmic_mpp_get_functions_count,
324cfb24f6eSIvan T. Ivanov 	.get_function_name	= pmic_mpp_get_function_name,
325cfb24f6eSIvan T. Ivanov 	.get_function_groups	= pmic_mpp_get_function_groups,
326cfb24f6eSIvan T. Ivanov 	.set_mux		= pmic_mpp_set_mux,
327cfb24f6eSIvan T. Ivanov };
328cfb24f6eSIvan T. Ivanov 
pmic_mpp_config_get(struct pinctrl_dev * pctldev,unsigned int pin,unsigned long * config)329cfb24f6eSIvan T. Ivanov static int pmic_mpp_config_get(struct pinctrl_dev *pctldev,
330cfb24f6eSIvan T. Ivanov 			       unsigned int pin, unsigned long *config)
331cfb24f6eSIvan T. Ivanov {
332cfb24f6eSIvan T. Ivanov 	unsigned param = pinconf_to_config_param(*config);
333cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_pad *pad;
334cfb24f6eSIvan T. Ivanov 	unsigned arg = 0;
335cfb24f6eSIvan T. Ivanov 
336cfb24f6eSIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
337cfb24f6eSIvan T. Ivanov 
338cfb24f6eSIvan T. Ivanov 	switch (param) {
339cfb24f6eSIvan T. Ivanov 	case PIN_CONFIG_BIAS_DISABLE:
3400d5b476fSDouglas Anderson 		if (pad->pullup != PMIC_MPP_PULL_UP_OPEN)
3410d5b476fSDouglas Anderson 			return -EINVAL;
3420d5b476fSDouglas Anderson 		arg = 1;
343cfb24f6eSIvan T. Ivanov 		break;
344cfb24f6eSIvan T. Ivanov 	case PIN_CONFIG_BIAS_PULL_UP:
345cfb24f6eSIvan T. Ivanov 		switch (pad->pullup) {
346cfb24f6eSIvan T. Ivanov 		case PMIC_MPP_PULL_UP_0P6KOHM:
347cfb24f6eSIvan T. Ivanov 			arg = 600;
348cfb24f6eSIvan T. Ivanov 			break;
349cfb24f6eSIvan T. Ivanov 		case PMIC_MPP_PULL_UP_10KOHM:
350cfb24f6eSIvan T. Ivanov 			arg = 10000;
351cfb24f6eSIvan T. Ivanov 			break;
352cfb24f6eSIvan T. Ivanov 		case PMIC_MPP_PULL_UP_30KOHM:
353cfb24f6eSIvan T. Ivanov 			arg = 30000;
354cfb24f6eSIvan T. Ivanov 			break;
355cfb24f6eSIvan T. Ivanov 		default:
356cfb24f6eSIvan T. Ivanov 			return -EINVAL;
357cfb24f6eSIvan T. Ivanov 		}
358cfb24f6eSIvan T. Ivanov 		break;
359cfb24f6eSIvan T. Ivanov 	case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
3600d5b476fSDouglas Anderson 		if (pad->is_enabled)
3610d5b476fSDouglas Anderson 			return -EINVAL;
3620d5b476fSDouglas Anderson 		arg = 1;
363cfb24f6eSIvan T. Ivanov 		break;
364cfb24f6eSIvan T. Ivanov 	case PIN_CONFIG_POWER_SOURCE:
365cfb24f6eSIvan T. Ivanov 		arg = pad->power_source;
366cfb24f6eSIvan T. Ivanov 		break;
367cfb24f6eSIvan T. Ivanov 	case PIN_CONFIG_INPUT_ENABLE:
3680d5b476fSDouglas Anderson 		if (!pad->input_enabled)
3690d5b476fSDouglas Anderson 			return -EINVAL;
3700d5b476fSDouglas Anderson 		arg = 1;
371cfb24f6eSIvan T. Ivanov 		break;
372cfb24f6eSIvan T. Ivanov 	case PIN_CONFIG_OUTPUT:
373cfb24f6eSIvan T. Ivanov 		arg = pad->out_value;
374cfb24f6eSIvan T. Ivanov 		break;
375eb5c144cSBjorn Andersson 	case PMIC_MPP_CONF_DTEST_SELECTOR:
376eb5c144cSBjorn Andersson 		arg = pad->dtest;
377eb5c144cSBjorn Andersson 		break;
378cfb24f6eSIvan T. Ivanov 	case PMIC_MPP_CONF_AMUX_ROUTE:
379cfb24f6eSIvan T. Ivanov 		arg = pad->amux_input;
380cfb24f6eSIvan T. Ivanov 		break;
381eb5c144cSBjorn Andersson 	case PMIC_MPP_CONF_PAIRED:
3820d5b476fSDouglas Anderson 		if (!pad->paired)
3830d5b476fSDouglas Anderson 			return -EINVAL;
3840d5b476fSDouglas Anderson 		arg = 1;
385eb5c144cSBjorn Andersson 		break;
3860e948042SBjorn Andersson 	case PIN_CONFIG_DRIVE_STRENGTH:
3870e948042SBjorn Andersson 		arg = pad->drive_strength;
3880e948042SBjorn Andersson 		break;
389099f3e4aSBjorn Andersson 	case PMIC_MPP_CONF_ANALOG_LEVEL:
390099f3e4aSBjorn Andersson 		arg = pad->aout_level;
391099f3e4aSBjorn Andersson 		break;
392cfb24f6eSIvan T. Ivanov 	default:
393cfb24f6eSIvan T. Ivanov 		return -EINVAL;
394cfb24f6eSIvan T. Ivanov 	}
395cfb24f6eSIvan T. Ivanov 
396cfb24f6eSIvan T. Ivanov 	/* Convert register value to pinconf value */
397cfb24f6eSIvan T. Ivanov 	*config = pinconf_to_config_packed(param, arg);
398cfb24f6eSIvan T. Ivanov 	return 0;
399cfb24f6eSIvan T. Ivanov }
400cfb24f6eSIvan T. Ivanov 
pmic_mpp_config_set(struct pinctrl_dev * pctldev,unsigned int pin,unsigned long * configs,unsigned nconfs)401cfb24f6eSIvan T. Ivanov static int pmic_mpp_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
402cfb24f6eSIvan T. Ivanov 			       unsigned long *configs, unsigned nconfs)
403cfb24f6eSIvan T. Ivanov {
404cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_state *state = pinctrl_dev_get_drvdata(pctldev);
405cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_pad *pad;
406cfb24f6eSIvan T. Ivanov 	unsigned param, arg;
407cfb24f6eSIvan T. Ivanov 	unsigned int val;
408cfb24f6eSIvan T. Ivanov 	int i, ret;
409cfb24f6eSIvan T. Ivanov 
410cfb24f6eSIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
411cfb24f6eSIvan T. Ivanov 
4127682b374SBjorn Andersson 	/* Make it possible to enable the pin, by not setting high impedance */
4137682b374SBjorn Andersson 	pad->is_enabled = true;
4147682b374SBjorn Andersson 
415cfb24f6eSIvan T. Ivanov 	for (i = 0; i < nconfs; i++) {
416cfb24f6eSIvan T. Ivanov 		param = pinconf_to_config_param(configs[i]);
417cfb24f6eSIvan T. Ivanov 		arg = pinconf_to_config_argument(configs[i]);
418cfb24f6eSIvan T. Ivanov 
419cfb24f6eSIvan T. Ivanov 		switch (param) {
420cfb24f6eSIvan T. Ivanov 		case PIN_CONFIG_BIAS_DISABLE:
421cfb24f6eSIvan T. Ivanov 			pad->pullup = PMIC_MPP_PULL_UP_OPEN;
422cfb24f6eSIvan T. Ivanov 			break;
423cfb24f6eSIvan T. Ivanov 		case PIN_CONFIG_BIAS_PULL_UP:
424cfb24f6eSIvan T. Ivanov 			switch (arg) {
425cfb24f6eSIvan T. Ivanov 			case 600:
426cfb24f6eSIvan T. Ivanov 				pad->pullup = PMIC_MPP_PULL_UP_0P6KOHM;
427cfb24f6eSIvan T. Ivanov 				break;
428cfb24f6eSIvan T. Ivanov 			case 10000:
429cfb24f6eSIvan T. Ivanov 				pad->pullup = PMIC_MPP_PULL_UP_10KOHM;
430cfb24f6eSIvan T. Ivanov 				break;
431cfb24f6eSIvan T. Ivanov 			case 30000:
432cfb24f6eSIvan T. Ivanov 				pad->pullup = PMIC_MPP_PULL_UP_30KOHM;
433cfb24f6eSIvan T. Ivanov 				break;
434cfb24f6eSIvan T. Ivanov 			default:
435cfb24f6eSIvan T. Ivanov 				return -EINVAL;
436cfb24f6eSIvan T. Ivanov 			}
437cfb24f6eSIvan T. Ivanov 			break;
438cfb24f6eSIvan T. Ivanov 		case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
439cfb24f6eSIvan T. Ivanov 			pad->is_enabled = false;
440cfb24f6eSIvan T. Ivanov 			break;
441cfb24f6eSIvan T. Ivanov 		case PIN_CONFIG_POWER_SOURCE:
442cfb24f6eSIvan T. Ivanov 			if (arg >= pad->num_sources)
443cfb24f6eSIvan T. Ivanov 				return -EINVAL;
444cfb24f6eSIvan T. Ivanov 			pad->power_source = arg;
445cfb24f6eSIvan T. Ivanov 			break;
446cfb24f6eSIvan T. Ivanov 		case PIN_CONFIG_INPUT_ENABLE:
447cfb24f6eSIvan T. Ivanov 			pad->input_enabled = arg ? true : false;
448cfb24f6eSIvan T. Ivanov 			break;
449cfb24f6eSIvan T. Ivanov 		case PIN_CONFIG_OUTPUT:
450cfb24f6eSIvan T. Ivanov 			pad->output_enabled = true;
451cfb24f6eSIvan T. Ivanov 			pad->out_value = arg;
452cfb24f6eSIvan T. Ivanov 			break;
453eb5c144cSBjorn Andersson 		case PMIC_MPP_CONF_DTEST_SELECTOR:
454eb5c144cSBjorn Andersson 			pad->dtest = arg;
455eb5c144cSBjorn Andersson 			break;
4560e948042SBjorn Andersson 		case PIN_CONFIG_DRIVE_STRENGTH:
45789c68b10SStephen Boyd 			pad->drive_strength = arg;
4580e948042SBjorn Andersson 			break;
459cfb24f6eSIvan T. Ivanov 		case PMIC_MPP_CONF_AMUX_ROUTE:
460cfb24f6eSIvan T. Ivanov 			if (arg >= PMIC_MPP_AMUX_ROUTE_ABUS4)
461cfb24f6eSIvan T. Ivanov 				return -EINVAL;
462cfb24f6eSIvan T. Ivanov 			pad->amux_input = arg;
463cfb24f6eSIvan T. Ivanov 			break;
464099f3e4aSBjorn Andersson 		case PMIC_MPP_CONF_ANALOG_LEVEL:
465099f3e4aSBjorn Andersson 			pad->aout_level = arg;
466099f3e4aSBjorn Andersson 			break;
467eb5c144cSBjorn Andersson 		case PMIC_MPP_CONF_PAIRED:
468eb5c144cSBjorn Andersson 			pad->paired = !!arg;
469cfb24f6eSIvan T. Ivanov 			break;
470cfb24f6eSIvan T. Ivanov 		default:
471cfb24f6eSIvan T. Ivanov 			return -EINVAL;
472cfb24f6eSIvan T. Ivanov 		}
473cfb24f6eSIvan T. Ivanov 	}
474cfb24f6eSIvan T. Ivanov 
475cfb24f6eSIvan T. Ivanov 	val = pad->power_source << PMIC_MPP_REG_VIN_SHIFT;
476cfb24f6eSIvan T. Ivanov 
477cfb24f6eSIvan T. Ivanov 	ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_DIG_VIN_CTL, val);
478cfb24f6eSIvan T. Ivanov 	if (ret < 0)
479cfb24f6eSIvan T. Ivanov 		return ret;
480cfb24f6eSIvan T. Ivanov 
481ae6d54fdSStephen Boyd 	if (pad->has_pullup) {
482cfb24f6eSIvan T. Ivanov 		val = pad->pullup << PMIC_MPP_REG_PULL_SHIFT;
483cfb24f6eSIvan T. Ivanov 
484ae6d54fdSStephen Boyd 		ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_DIG_PULL_CTL,
485ae6d54fdSStephen Boyd 				     val);
486cfb24f6eSIvan T. Ivanov 		if (ret < 0)
487cfb24f6eSIvan T. Ivanov 			return ret;
488ae6d54fdSStephen Boyd 	}
489cfb24f6eSIvan T. Ivanov 
490cfb24f6eSIvan T. Ivanov 	val = pad->amux_input & PMIC_MPP_REG_AIN_ROUTE_MASK;
491cfb24f6eSIvan T. Ivanov 
492cfb24f6eSIvan T. Ivanov 	ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_AIN_CTL, val);
493cfb24f6eSIvan T. Ivanov 	if (ret < 0)
494cfb24f6eSIvan T. Ivanov 		return ret;
495cfb24f6eSIvan T. Ivanov 
496099f3e4aSBjorn Andersson 	ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_AOUT_CTL, pad->aout_level);
497099f3e4aSBjorn Andersson 	if (ret < 0)
498099f3e4aSBjorn Andersson 		return ret;
499099f3e4aSBjorn Andersson 
5000e948042SBjorn Andersson 	ret = pmic_mpp_write_mode_ctl(state, pad);
5017682b374SBjorn Andersson 	if (ret < 0)
5027682b374SBjorn Andersson 		return ret;
5037682b374SBjorn Andersson 
50489c68b10SStephen Boyd 	ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_SINK_CTL, pad->drive_strength);
50589c68b10SStephen Boyd 	if (ret < 0)
50689c68b10SStephen Boyd 		return ret;
50789c68b10SStephen Boyd 
5087682b374SBjorn Andersson 	val = pad->is_enabled << PMIC_MPP_REG_MASTER_EN_SHIFT;
5097682b374SBjorn Andersson 
5107682b374SBjorn Andersson 	return pmic_mpp_write(state, pad, PMIC_MPP_REG_EN_CTL, val);
511cfb24f6eSIvan T. Ivanov }
512cfb24f6eSIvan T. Ivanov 
pmic_mpp_config_dbg_show(struct pinctrl_dev * pctldev,struct seq_file * s,unsigned pin)513cfb24f6eSIvan T. Ivanov static void pmic_mpp_config_dbg_show(struct pinctrl_dev *pctldev,
514cfb24f6eSIvan T. Ivanov 				     struct seq_file *s, unsigned pin)
515cfb24f6eSIvan T. Ivanov {
516cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_state *state = pinctrl_dev_get_drvdata(pctldev);
517cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_pad *pad;
5187682b374SBjorn Andersson 	int ret;
519cfb24f6eSIvan T. Ivanov 
520cfb24f6eSIvan T. Ivanov 	static const char *const biases[] = {
521cfb24f6eSIvan T. Ivanov 		"0.6kOhm", "10kOhm", "30kOhm", "Disabled"
522cfb24f6eSIvan T. Ivanov 	};
523cfb24f6eSIvan T. Ivanov 
524cfb24f6eSIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
525cfb24f6eSIvan T. Ivanov 
526cfb24f6eSIvan T. Ivanov 	seq_printf(s, " mpp%-2d:", pin + PMIC_MPP_PHYSICAL_OFFSET);
527cfb24f6eSIvan T. Ivanov 
5287682b374SBjorn Andersson 	if (!pad->is_enabled) {
529cfb24f6eSIvan T. Ivanov 		seq_puts(s, " ---");
530cfb24f6eSIvan T. Ivanov 	} else {
531cfb24f6eSIvan T. Ivanov 
532cfb24f6eSIvan T. Ivanov 		if (pad->input_enabled) {
533cfb24f6eSIvan T. Ivanov 			ret = pmic_mpp_read(state, pad, PMIC_MPP_REG_RT_STS);
5344e637ac2SIvan T. Ivanov 			if (ret < 0)
5354e637ac2SIvan T. Ivanov 				return;
5364e637ac2SIvan T. Ivanov 
537cfb24f6eSIvan T. Ivanov 			ret &= PMIC_MPP_REG_RT_STS_VAL_MASK;
538cfb24f6eSIvan T. Ivanov 			pad->out_value = ret;
539cfb24f6eSIvan T. Ivanov 		}
540cfb24f6eSIvan T. Ivanov 
541cfb24f6eSIvan T. Ivanov 		seq_printf(s, " %-4s", pad->output_enabled ? "out" : "in");
542cfb24f6eSIvan T. Ivanov 		seq_printf(s, " %-7s", pmic_mpp_functions[pad->function]);
543cfb24f6eSIvan T. Ivanov 		seq_printf(s, " vin-%d", pad->power_source);
544099f3e4aSBjorn Andersson 		seq_printf(s, " %d", pad->aout_level);
545ae6d54fdSStephen Boyd 		if (pad->has_pullup)
546cfb24f6eSIvan T. Ivanov 			seq_printf(s, " %-8s", biases[pad->pullup]);
547cfb24f6eSIvan T. Ivanov 		seq_printf(s, " %-4s", pad->out_value ? "high" : "low");
548eb5c144cSBjorn Andersson 		if (pad->dtest)
549eb5c144cSBjorn Andersson 			seq_printf(s, " dtest%d", pad->dtest);
550eb5c144cSBjorn Andersson 		if (pad->paired)
551eb5c144cSBjorn Andersson 			seq_puts(s, " paired");
552cfb24f6eSIvan T. Ivanov 	}
553cfb24f6eSIvan T. Ivanov }
554cfb24f6eSIvan T. Ivanov 
555cfb24f6eSIvan T. Ivanov static const struct pinconf_ops pmic_mpp_pinconf_ops = {
556ba5f94cdSBjorn Andersson 	.is_generic = true,
557cfb24f6eSIvan T. Ivanov 	.pin_config_group_get		= pmic_mpp_config_get,
558cfb24f6eSIvan T. Ivanov 	.pin_config_group_set		= pmic_mpp_config_set,
559cfb24f6eSIvan T. Ivanov 	.pin_config_group_dbg_show	= pmic_mpp_config_dbg_show,
560cfb24f6eSIvan T. Ivanov };
561cfb24f6eSIvan T. Ivanov 
pmic_mpp_direction_input(struct gpio_chip * chip,unsigned pin)562cfb24f6eSIvan T. Ivanov static int pmic_mpp_direction_input(struct gpio_chip *chip, unsigned pin)
563cfb24f6eSIvan T. Ivanov {
564064761d1SLinus Walleij 	struct pmic_mpp_state *state = gpiochip_get_data(chip);
565cfb24f6eSIvan T. Ivanov 	unsigned long config;
566cfb24f6eSIvan T. Ivanov 
567cfb24f6eSIvan T. Ivanov 	config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1);
568cfb24f6eSIvan T. Ivanov 
569cfb24f6eSIvan T. Ivanov 	return pmic_mpp_config_set(state->ctrl, pin, &config, 1);
570cfb24f6eSIvan T. Ivanov }
571cfb24f6eSIvan T. Ivanov 
pmic_mpp_direction_output(struct gpio_chip * chip,unsigned pin,int val)572cfb24f6eSIvan T. Ivanov static int pmic_mpp_direction_output(struct gpio_chip *chip,
573cfb24f6eSIvan T. Ivanov 				     unsigned pin, int val)
574cfb24f6eSIvan T. Ivanov {
575064761d1SLinus Walleij 	struct pmic_mpp_state *state = gpiochip_get_data(chip);
576cfb24f6eSIvan T. Ivanov 	unsigned long config;
577cfb24f6eSIvan T. Ivanov 
578cfb24f6eSIvan T. Ivanov 	config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val);
579cfb24f6eSIvan T. Ivanov 
580cfb24f6eSIvan T. Ivanov 	return pmic_mpp_config_set(state->ctrl, pin, &config, 1);
581cfb24f6eSIvan T. Ivanov }
582cfb24f6eSIvan T. Ivanov 
pmic_mpp_get(struct gpio_chip * chip,unsigned pin)583cfb24f6eSIvan T. Ivanov static int pmic_mpp_get(struct gpio_chip *chip, unsigned pin)
584cfb24f6eSIvan T. Ivanov {
585064761d1SLinus Walleij 	struct pmic_mpp_state *state = gpiochip_get_data(chip);
586cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_pad *pad;
587cfb24f6eSIvan T. Ivanov 	int ret;
588cfb24f6eSIvan T. Ivanov 
589cfb24f6eSIvan T. Ivanov 	pad = state->ctrl->desc->pins[pin].drv_data;
590cfb24f6eSIvan T. Ivanov 
591cfb24f6eSIvan T. Ivanov 	if (pad->input_enabled) {
592cfb24f6eSIvan T. Ivanov 		ret = pmic_mpp_read(state, pad, PMIC_MPP_REG_RT_STS);
593cfb24f6eSIvan T. Ivanov 		if (ret < 0)
594cfb24f6eSIvan T. Ivanov 			return ret;
595cfb24f6eSIvan T. Ivanov 
596cfb24f6eSIvan T. Ivanov 		pad->out_value = ret & PMIC_MPP_REG_RT_STS_VAL_MASK;
597cfb24f6eSIvan T. Ivanov 	}
598cfb24f6eSIvan T. Ivanov 
59959663a4cSLinus Walleij 	return !!pad->out_value;
600cfb24f6eSIvan T. Ivanov }
601cfb24f6eSIvan T. Ivanov 
pmic_mpp_set(struct gpio_chip * chip,unsigned pin,int value)602cfb24f6eSIvan T. Ivanov static void pmic_mpp_set(struct gpio_chip *chip, unsigned pin, int value)
603cfb24f6eSIvan T. Ivanov {
604064761d1SLinus Walleij 	struct pmic_mpp_state *state = gpiochip_get_data(chip);
605cfb24f6eSIvan T. Ivanov 	unsigned long config;
606cfb24f6eSIvan T. Ivanov 
607cfb24f6eSIvan T. Ivanov 	config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value);
608cfb24f6eSIvan T. Ivanov 
609cfb24f6eSIvan T. Ivanov 	pmic_mpp_config_set(state->ctrl, pin, &config, 1);
610cfb24f6eSIvan T. Ivanov }
611cfb24f6eSIvan T. Ivanov 
pmic_mpp_of_xlate(struct gpio_chip * chip,const struct of_phandle_args * gpio_desc,u32 * flags)612cfb24f6eSIvan T. Ivanov static int pmic_mpp_of_xlate(struct gpio_chip *chip,
613cfb24f6eSIvan T. Ivanov 			     const struct of_phandle_args *gpio_desc,
614cfb24f6eSIvan T. Ivanov 			     u32 *flags)
615cfb24f6eSIvan T. Ivanov {
616cfb24f6eSIvan T. Ivanov 	if (chip->of_gpio_n_cells < 2)
617cfb24f6eSIvan T. Ivanov 		return -EINVAL;
618cfb24f6eSIvan T. Ivanov 
619cfb24f6eSIvan T. Ivanov 	if (flags)
620cfb24f6eSIvan T. Ivanov 		*flags = gpio_desc->args[1];
621cfb24f6eSIvan T. Ivanov 
622cfb24f6eSIvan T. Ivanov 	return gpio_desc->args[0] - PMIC_MPP_PHYSICAL_OFFSET;
623cfb24f6eSIvan T. Ivanov }
624cfb24f6eSIvan T. Ivanov 
pmic_mpp_dbg_show(struct seq_file * s,struct gpio_chip * chip)625cfb24f6eSIvan T. Ivanov static void pmic_mpp_dbg_show(struct seq_file *s, struct gpio_chip *chip)
626cfb24f6eSIvan T. Ivanov {
627064761d1SLinus Walleij 	struct pmic_mpp_state *state = gpiochip_get_data(chip);
628cfb24f6eSIvan T. Ivanov 	unsigned i;
629cfb24f6eSIvan T. Ivanov 
630cfb24f6eSIvan T. Ivanov 	for (i = 0; i < chip->ngpio; i++) {
631cfb24f6eSIvan T. Ivanov 		pmic_mpp_config_dbg_show(state->ctrl, s, i);
632cfb24f6eSIvan T. Ivanov 		seq_puts(s, "\n");
633cfb24f6eSIvan T. Ivanov 	}
634cfb24f6eSIvan T. Ivanov }
635cfb24f6eSIvan T. Ivanov 
636cfb24f6eSIvan T. Ivanov static const struct gpio_chip pmic_mpp_gpio_template = {
637cfb24f6eSIvan T. Ivanov 	.direction_input	= pmic_mpp_direction_input,
638cfb24f6eSIvan T. Ivanov 	.direction_output	= pmic_mpp_direction_output,
639cfb24f6eSIvan T. Ivanov 	.get			= pmic_mpp_get,
640cfb24f6eSIvan T. Ivanov 	.set			= pmic_mpp_set,
64198c85d58SJonas Gorski 	.request		= gpiochip_generic_request,
64298c85d58SJonas Gorski 	.free			= gpiochip_generic_free,
643cfb24f6eSIvan T. Ivanov 	.of_xlate		= pmic_mpp_of_xlate,
644cfb24f6eSIvan T. Ivanov 	.dbg_show		= pmic_mpp_dbg_show,
645cfb24f6eSIvan T. Ivanov };
646cfb24f6eSIvan T. Ivanov 
pmic_mpp_populate(struct pmic_mpp_state * state,struct pmic_mpp_pad * pad)647cfb24f6eSIvan T. Ivanov static int pmic_mpp_populate(struct pmic_mpp_state *state,
648cfb24f6eSIvan T. Ivanov 			     struct pmic_mpp_pad *pad)
649cfb24f6eSIvan T. Ivanov {
650cfb24f6eSIvan T. Ivanov 	int type, subtype, val, dir;
651eb5c144cSBjorn Andersson 	unsigned int sel;
652cfb24f6eSIvan T. Ivanov 
653cfb24f6eSIvan T. Ivanov 	type = pmic_mpp_read(state, pad, PMIC_MPP_REG_TYPE);
654cfb24f6eSIvan T. Ivanov 	if (type < 0)
655cfb24f6eSIvan T. Ivanov 		return type;
656cfb24f6eSIvan T. Ivanov 
657cfb24f6eSIvan T. Ivanov 	if (type != PMIC_MPP_TYPE) {
658cfb24f6eSIvan T. Ivanov 		dev_err(state->dev, "incorrect block type 0x%x at 0x%x\n",
659cfb24f6eSIvan T. Ivanov 			type, pad->base);
660cfb24f6eSIvan T. Ivanov 		return -ENODEV;
661cfb24f6eSIvan T. Ivanov 	}
662cfb24f6eSIvan T. Ivanov 
663cfb24f6eSIvan T. Ivanov 	subtype = pmic_mpp_read(state, pad, PMIC_MPP_REG_SUBTYPE);
664cfb24f6eSIvan T. Ivanov 	if (subtype < 0)
665cfb24f6eSIvan T. Ivanov 		return subtype;
666cfb24f6eSIvan T. Ivanov 
667cfb24f6eSIvan T. Ivanov 	switch (subtype) {
668cfb24f6eSIvan T. Ivanov 	case PMIC_MPP_SUBTYPE_4CH_NO_ANA_OUT:
669cfb24f6eSIvan T. Ivanov 	case PMIC_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT:
670cfb24f6eSIvan T. Ivanov 	case PMIC_MPP_SUBTYPE_4CH_NO_SINK:
671cfb24f6eSIvan T. Ivanov 	case PMIC_MPP_SUBTYPE_ULT_4CH_NO_SINK:
672cfb24f6eSIvan T. Ivanov 	case PMIC_MPP_SUBTYPE_4CH_FULL_FUNC:
673cfb24f6eSIvan T. Ivanov 		pad->num_sources = 4;
674cfb24f6eSIvan T. Ivanov 		break;
675cfb24f6eSIvan T. Ivanov 	case PMIC_MPP_SUBTYPE_8CH_FULL_FUNC:
676cfb24f6eSIvan T. Ivanov 		pad->num_sources = 8;
677cfb24f6eSIvan T. Ivanov 		break;
678cfb24f6eSIvan T. Ivanov 	default:
679cfb24f6eSIvan T. Ivanov 		dev_err(state->dev, "unknown MPP type 0x%x at 0x%x\n",
680cfb24f6eSIvan T. Ivanov 			subtype, pad->base);
681cfb24f6eSIvan T. Ivanov 		return -ENODEV;
682cfb24f6eSIvan T. Ivanov 	}
683cfb24f6eSIvan T. Ivanov 
684cfb24f6eSIvan T. Ivanov 	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_MODE_CTL);
685cfb24f6eSIvan T. Ivanov 	if (val < 0)
686cfb24f6eSIvan T. Ivanov 		return val;
687cfb24f6eSIvan T. Ivanov 
688cfb24f6eSIvan T. Ivanov 	pad->out_value = val & PMIC_MPP_REG_MODE_VALUE_MASK;
689cfb24f6eSIvan T. Ivanov 
690cfb24f6eSIvan T. Ivanov 	dir = val >> PMIC_MPP_REG_MODE_DIR_SHIFT;
691cfb24f6eSIvan T. Ivanov 	dir &= PMIC_MPP_REG_MODE_DIR_MASK;
692cfb24f6eSIvan T. Ivanov 
693cfb24f6eSIvan T. Ivanov 	switch (dir) {
694eaaf5dd4SBjorn Andersson 	case PMIC_MPP_MODE_DIGITAL_INPUT:
695cfb24f6eSIvan T. Ivanov 		pad->input_enabled = true;
696cfb24f6eSIvan T. Ivanov 		pad->output_enabled = false;
697eb5c144cSBjorn Andersson 		pad->function = PMIC_MPP_DIGITAL;
698cfb24f6eSIvan T. Ivanov 		break;
699eaaf5dd4SBjorn Andersson 	case PMIC_MPP_MODE_DIGITAL_OUTPUT:
700cfb24f6eSIvan T. Ivanov 		pad->input_enabled = false;
701cfb24f6eSIvan T. Ivanov 		pad->output_enabled = true;
702eb5c144cSBjorn Andersson 		pad->function = PMIC_MPP_DIGITAL;
703cfb24f6eSIvan T. Ivanov 		break;
704eaaf5dd4SBjorn Andersson 	case PMIC_MPP_MODE_DIGITAL_BIDIR:
705cfb24f6eSIvan T. Ivanov 		pad->input_enabled = true;
706cfb24f6eSIvan T. Ivanov 		pad->output_enabled = true;
707eb5c144cSBjorn Andersson 		pad->function = PMIC_MPP_DIGITAL;
708cfb24f6eSIvan T. Ivanov 		break;
709eaaf5dd4SBjorn Andersson 	case PMIC_MPP_MODE_ANALOG_BIDIR:
710cfb24f6eSIvan T. Ivanov 		pad->input_enabled = true;
711cfb24f6eSIvan T. Ivanov 		pad->output_enabled = true;
712eb5c144cSBjorn Andersson 		pad->function = PMIC_MPP_ANALOG;
713cfb24f6eSIvan T. Ivanov 		break;
714eaaf5dd4SBjorn Andersson 	case PMIC_MPP_MODE_ANALOG_INPUT:
715cfb24f6eSIvan T. Ivanov 		pad->input_enabled = true;
716cfb24f6eSIvan T. Ivanov 		pad->output_enabled = false;
717eb5c144cSBjorn Andersson 		pad->function = PMIC_MPP_ANALOG;
718cfb24f6eSIvan T. Ivanov 		break;
719eaaf5dd4SBjorn Andersson 	case PMIC_MPP_MODE_ANALOG_OUTPUT:
720cfb24f6eSIvan T. Ivanov 		pad->input_enabled = false;
721cfb24f6eSIvan T. Ivanov 		pad->output_enabled = true;
722eb5c144cSBjorn Andersson 		pad->function = PMIC_MPP_ANALOG;
7230e948042SBjorn Andersson 		break;
7240e948042SBjorn Andersson 	case PMIC_MPP_MODE_CURRENT_SINK:
7250e948042SBjorn Andersson 		pad->input_enabled = false;
7260e948042SBjorn Andersson 		pad->output_enabled = true;
727eb5c144cSBjorn Andersson 		pad->function = PMIC_MPP_SINK;
728cfb24f6eSIvan T. Ivanov 		break;
729cfb24f6eSIvan T. Ivanov 	default:
730cfb24f6eSIvan T. Ivanov 		dev_err(state->dev, "unknown MPP direction\n");
731cfb24f6eSIvan T. Ivanov 		return -ENODEV;
732cfb24f6eSIvan T. Ivanov 	}
733cfb24f6eSIvan T. Ivanov 
734eb5c144cSBjorn Andersson 	sel = val >> PMIC_MPP_REG_MODE_FUNCTION_SHIFT;
735eb5c144cSBjorn Andersson 	sel &= PMIC_MPP_REG_MODE_FUNCTION_MASK;
736eb5c144cSBjorn Andersson 
737eb5c144cSBjorn Andersson 	if (sel >= PMIC_MPP_SELECTOR_DTEST_FIRST)
738eb5c144cSBjorn Andersson 		pad->dtest = sel + 1;
739eb5c144cSBjorn Andersson 	else if (sel == PMIC_MPP_SELECTOR_PAIRED)
740eb5c144cSBjorn Andersson 		pad->paired = true;
741cfb24f6eSIvan T. Ivanov 
742cfb24f6eSIvan T. Ivanov 	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_DIG_VIN_CTL);
743cfb24f6eSIvan T. Ivanov 	if (val < 0)
744cfb24f6eSIvan T. Ivanov 		return val;
745cfb24f6eSIvan T. Ivanov 
746cfb24f6eSIvan T. Ivanov 	pad->power_source = val >> PMIC_MPP_REG_VIN_SHIFT;
747cfb24f6eSIvan T. Ivanov 	pad->power_source &= PMIC_MPP_REG_VIN_MASK;
748cfb24f6eSIvan T. Ivanov 
749ae6d54fdSStephen Boyd 	if (subtype != PMIC_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT &&
750ae6d54fdSStephen Boyd 	    subtype != PMIC_MPP_SUBTYPE_ULT_4CH_NO_SINK) {
751cfb24f6eSIvan T. Ivanov 		val = pmic_mpp_read(state, pad, PMIC_MPP_REG_DIG_PULL_CTL);
752cfb24f6eSIvan T. Ivanov 		if (val < 0)
753cfb24f6eSIvan T. Ivanov 			return val;
754cfb24f6eSIvan T. Ivanov 
755cfb24f6eSIvan T. Ivanov 		pad->pullup = val >> PMIC_MPP_REG_PULL_SHIFT;
756cfb24f6eSIvan T. Ivanov 		pad->pullup &= PMIC_MPP_REG_PULL_MASK;
757ae6d54fdSStephen Boyd 		pad->has_pullup = true;
758ae6d54fdSStephen Boyd 	}
759cfb24f6eSIvan T. Ivanov 
760cfb24f6eSIvan T. Ivanov 	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_AIN_CTL);
761cfb24f6eSIvan T. Ivanov 	if (val < 0)
762cfb24f6eSIvan T. Ivanov 		return val;
763cfb24f6eSIvan T. Ivanov 
764cfb24f6eSIvan T. Ivanov 	pad->amux_input = val >> PMIC_MPP_REG_AIN_ROUTE_SHIFT;
765cfb24f6eSIvan T. Ivanov 	pad->amux_input &= PMIC_MPP_REG_AIN_ROUTE_MASK;
766cfb24f6eSIvan T. Ivanov 
7670e948042SBjorn Andersson 	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_SINK_CTL);
7680e948042SBjorn Andersson 	if (val < 0)
7690e948042SBjorn Andersson 		return val;
7700e948042SBjorn Andersson 
7710e948042SBjorn Andersson 	pad->drive_strength = val;
7720e948042SBjorn Andersson 
773099f3e4aSBjorn Andersson 	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_AOUT_CTL);
774099f3e4aSBjorn Andersson 	if (val < 0)
775099f3e4aSBjorn Andersson 		return val;
776099f3e4aSBjorn Andersson 
777099f3e4aSBjorn Andersson 	pad->aout_level = val;
778099f3e4aSBjorn Andersson 
7797682b374SBjorn Andersson 	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_EN_CTL);
7807682b374SBjorn Andersson 	if (val < 0)
7817682b374SBjorn Andersson 		return val;
7827682b374SBjorn Andersson 
7837682b374SBjorn Andersson 	pad->is_enabled = !!val;
7847682b374SBjorn Andersson 
785cfb24f6eSIvan T. Ivanov 	return 0;
786cfb24f6eSIvan T. Ivanov }
787cfb24f6eSIvan T. Ivanov 
pmic_mpp_domain_translate(struct irq_domain * domain,struct irq_fwspec * fwspec,unsigned long * hwirq,unsigned int * type)788afe6777fSDmitry Baryshkov static int pmic_mpp_domain_translate(struct irq_domain *domain,
789afe6777fSDmitry Baryshkov 				      struct irq_fwspec *fwspec,
790afe6777fSDmitry Baryshkov 				      unsigned long *hwirq,
791afe6777fSDmitry Baryshkov 				      unsigned int *type)
792afe6777fSDmitry Baryshkov {
793afe6777fSDmitry Baryshkov 	struct pmic_mpp_state *state = container_of(domain->host_data,
794afe6777fSDmitry Baryshkov 						     struct pmic_mpp_state,
795afe6777fSDmitry Baryshkov 						     chip);
796afe6777fSDmitry Baryshkov 
797afe6777fSDmitry Baryshkov 	if (fwspec->param_count != 2 ||
798afe6777fSDmitry Baryshkov 	    fwspec->param[0] < 1 || fwspec->param[0] > state->chip.ngpio)
799afe6777fSDmitry Baryshkov 		return -EINVAL;
800afe6777fSDmitry Baryshkov 
801afe6777fSDmitry Baryshkov 	*hwirq = fwspec->param[0] - PMIC_MPP_PHYSICAL_OFFSET;
802afe6777fSDmitry Baryshkov 	*type = fwspec->param[1];
803afe6777fSDmitry Baryshkov 
804afe6777fSDmitry Baryshkov 	return 0;
805afe6777fSDmitry Baryshkov }
806afe6777fSDmitry Baryshkov 
pmic_mpp_child_offset_to_irq(struct gpio_chip * chip,unsigned int offset)807afe6777fSDmitry Baryshkov static unsigned int pmic_mpp_child_offset_to_irq(struct gpio_chip *chip,
808afe6777fSDmitry Baryshkov 						  unsigned int offset)
809afe6777fSDmitry Baryshkov {
810afe6777fSDmitry Baryshkov 	return offset + PMIC_MPP_PHYSICAL_OFFSET;
811afe6777fSDmitry Baryshkov }
812afe6777fSDmitry Baryshkov 
pmic_mpp_child_to_parent_hwirq(struct gpio_chip * chip,unsigned int child_hwirq,unsigned int child_type,unsigned int * parent_hwirq,unsigned int * parent_type)813afe6777fSDmitry Baryshkov static int pmic_mpp_child_to_parent_hwirq(struct gpio_chip *chip,
814afe6777fSDmitry Baryshkov 					   unsigned int child_hwirq,
815afe6777fSDmitry Baryshkov 					   unsigned int child_type,
816afe6777fSDmitry Baryshkov 					   unsigned int *parent_hwirq,
817afe6777fSDmitry Baryshkov 					   unsigned int *parent_type)
818afe6777fSDmitry Baryshkov {
819afe6777fSDmitry Baryshkov 	*parent_hwirq = child_hwirq + 0xc0;
820afe6777fSDmitry Baryshkov 	*parent_type = child_type;
821afe6777fSDmitry Baryshkov 
822afe6777fSDmitry Baryshkov 	return 0;
823afe6777fSDmitry Baryshkov }
824afe6777fSDmitry Baryshkov 
pmic_mpp_irq_mask(struct irq_data * d)825*9890a043SLinus Walleij static void pmic_mpp_irq_mask(struct irq_data *d)
826*9890a043SLinus Walleij {
827*9890a043SLinus Walleij 	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
828*9890a043SLinus Walleij 
829*9890a043SLinus Walleij 	irq_chip_mask_parent(d);
830*9890a043SLinus Walleij 	gpiochip_disable_irq(gc, irqd_to_hwirq(d));
831*9890a043SLinus Walleij }
832*9890a043SLinus Walleij 
pmic_mpp_irq_unmask(struct irq_data * d)833*9890a043SLinus Walleij static void pmic_mpp_irq_unmask(struct irq_data *d)
834*9890a043SLinus Walleij {
835*9890a043SLinus Walleij 	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
836*9890a043SLinus Walleij 
837*9890a043SLinus Walleij 	gpiochip_enable_irq(gc, irqd_to_hwirq(d));
838*9890a043SLinus Walleij 	irq_chip_unmask_parent(d);
839*9890a043SLinus Walleij }
840*9890a043SLinus Walleij 
841*9890a043SLinus Walleij static const struct irq_chip pmic_mpp_irq_chip = {
842*9890a043SLinus Walleij 	.name = "spmi-mpp",
843*9890a043SLinus Walleij 	.irq_ack = irq_chip_ack_parent,
844*9890a043SLinus Walleij 	.irq_mask = pmic_mpp_irq_mask,
845*9890a043SLinus Walleij 	.irq_unmask = pmic_mpp_irq_unmask,
846*9890a043SLinus Walleij 	.irq_set_type = irq_chip_set_type_parent,
847*9890a043SLinus Walleij 	.irq_set_wake = irq_chip_set_wake_parent,
848*9890a043SLinus Walleij 	.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_IMMUTABLE,
849*9890a043SLinus Walleij 	GPIOCHIP_IRQ_RESOURCE_HELPERS,
850*9890a043SLinus Walleij };
851*9890a043SLinus Walleij 
pmic_mpp_probe(struct platform_device * pdev)852cfb24f6eSIvan T. Ivanov static int pmic_mpp_probe(struct platform_device *pdev)
853cfb24f6eSIvan T. Ivanov {
854afe6777fSDmitry Baryshkov 	struct irq_domain *parent_domain;
855afe6777fSDmitry Baryshkov 	struct device_node *parent_node;
856cfb24f6eSIvan T. Ivanov 	struct device *dev = &pdev->dev;
857cfb24f6eSIvan T. Ivanov 	struct pinctrl_pin_desc *pindesc;
858cfb24f6eSIvan T. Ivanov 	struct pinctrl_desc *pctrldesc;
859cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_pad *pad, *pads;
860cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_state *state;
861afe6777fSDmitry Baryshkov 	struct gpio_irq_chip *girq;
862cfb24f6eSIvan T. Ivanov 	int ret, npins, i;
863ab4256cfSStephen Boyd 	u32 reg;
864cfb24f6eSIvan T. Ivanov 
865ab4256cfSStephen Boyd 	ret = of_property_read_u32(dev->of_node, "reg", &reg);
866cfb24f6eSIvan T. Ivanov 	if (ret < 0) {
867ab4256cfSStephen Boyd 		dev_err(dev, "missing base address");
868cfb24f6eSIvan T. Ivanov 		return ret;
869cfb24f6eSIvan T. Ivanov 	}
870cfb24f6eSIvan T. Ivanov 
871f24dbaaaSDmitry Baryshkov 	npins = (uintptr_t) device_get_match_data(&pdev->dev);
872cfb24f6eSIvan T. Ivanov 
873cfb24f6eSIvan T. Ivanov 	BUG_ON(npins > ARRAY_SIZE(pmic_mpp_groups));
874cfb24f6eSIvan T. Ivanov 
875cfb24f6eSIvan T. Ivanov 	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
876cfb24f6eSIvan T. Ivanov 	if (!state)
877cfb24f6eSIvan T. Ivanov 		return -ENOMEM;
878cfb24f6eSIvan T. Ivanov 
879cfb24f6eSIvan T. Ivanov 	platform_set_drvdata(pdev, state);
880cfb24f6eSIvan T. Ivanov 
881cfb24f6eSIvan T. Ivanov 	state->dev = &pdev->dev;
882cfb24f6eSIvan T. Ivanov 	state->map = dev_get_regmap(dev->parent, NULL);
883cfb24f6eSIvan T. Ivanov 
884cfb24f6eSIvan T. Ivanov 	pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
885cfb24f6eSIvan T. Ivanov 	if (!pindesc)
886cfb24f6eSIvan T. Ivanov 		return -ENOMEM;
887cfb24f6eSIvan T. Ivanov 
888cfb24f6eSIvan T. Ivanov 	pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
889cfb24f6eSIvan T. Ivanov 	if (!pads)
890cfb24f6eSIvan T. Ivanov 		return -ENOMEM;
891cfb24f6eSIvan T. Ivanov 
892cfb24f6eSIvan T. Ivanov 	pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL);
893cfb24f6eSIvan T. Ivanov 	if (!pctrldesc)
894cfb24f6eSIvan T. Ivanov 		return -ENOMEM;
895cfb24f6eSIvan T. Ivanov 
896cfb24f6eSIvan T. Ivanov 	pctrldesc->pctlops = &pmic_mpp_pinctrl_ops;
897cfb24f6eSIvan T. Ivanov 	pctrldesc->pmxops = &pmic_mpp_pinmux_ops;
898cfb24f6eSIvan T. Ivanov 	pctrldesc->confops = &pmic_mpp_pinconf_ops;
899cfb24f6eSIvan T. Ivanov 	pctrldesc->owner = THIS_MODULE;
900cfb24f6eSIvan T. Ivanov 	pctrldesc->name = dev_name(dev);
901cfb24f6eSIvan T. Ivanov 	pctrldesc->pins = pindesc;
902cfb24f6eSIvan T. Ivanov 	pctrldesc->npins = npins;
903cfb24f6eSIvan T. Ivanov 
904ba5f94cdSBjorn Andersson 	pctrldesc->num_custom_params = ARRAY_SIZE(pmic_mpp_bindings);
905ba5f94cdSBjorn Andersson 	pctrldesc->custom_params = pmic_mpp_bindings;
906ba5f94cdSBjorn Andersson #ifdef CONFIG_DEBUG_FS
907ba5f94cdSBjorn Andersson 	pctrldesc->custom_conf_items = pmic_conf_items;
908ba5f94cdSBjorn Andersson #endif
909ba5f94cdSBjorn Andersson 
910cfb24f6eSIvan T. Ivanov 	for (i = 0; i < npins; i++, pindesc++) {
911cfb24f6eSIvan T. Ivanov 		pad = &pads[i];
912cfb24f6eSIvan T. Ivanov 		pindesc->drv_data = pad;
913cfb24f6eSIvan T. Ivanov 		pindesc->number = i;
914cfb24f6eSIvan T. Ivanov 		pindesc->name = pmic_mpp_groups[i];
915cfb24f6eSIvan T. Ivanov 
916ab4256cfSStephen Boyd 		pad->base = reg + i * PMIC_MPP_ADDRESS_RANGE;
917cfb24f6eSIvan T. Ivanov 
918cfb24f6eSIvan T. Ivanov 		ret = pmic_mpp_populate(state, pad);
919cfb24f6eSIvan T. Ivanov 		if (ret < 0)
920cfb24f6eSIvan T. Ivanov 			return ret;
921cfb24f6eSIvan T. Ivanov 	}
922cfb24f6eSIvan T. Ivanov 
923cfb24f6eSIvan T. Ivanov 	state->chip = pmic_mpp_gpio_template;
92458383c78SLinus Walleij 	state->chip.parent = dev;
925cfb24f6eSIvan T. Ivanov 	state->chip.base = -1;
926cfb24f6eSIvan T. Ivanov 	state->chip.ngpio = npins;
927cfb24f6eSIvan T. Ivanov 	state->chip.label = dev_name(dev);
928cfb24f6eSIvan T. Ivanov 	state->chip.of_gpio_n_cells = 2;
929cfb24f6eSIvan T. Ivanov 	state->chip.can_sleep = false;
930cfb24f6eSIvan T. Ivanov 
931ce18e595SLaxman Dewangan 	state->ctrl = devm_pinctrl_register(dev, pctrldesc, state);
932323de9efSMasahiro Yamada 	if (IS_ERR(state->ctrl))
933323de9efSMasahiro Yamada 		return PTR_ERR(state->ctrl);
934cfb24f6eSIvan T. Ivanov 
935afe6777fSDmitry Baryshkov 	parent_node = of_irq_find_parent(state->dev->of_node);
936afe6777fSDmitry Baryshkov 	if (!parent_node)
937afe6777fSDmitry Baryshkov 		return -ENXIO;
938afe6777fSDmitry Baryshkov 
939afe6777fSDmitry Baryshkov 	parent_domain = irq_find_host(parent_node);
940afe6777fSDmitry Baryshkov 	of_node_put(parent_node);
941afe6777fSDmitry Baryshkov 	if (!parent_domain)
942afe6777fSDmitry Baryshkov 		return -ENXIO;
943afe6777fSDmitry Baryshkov 
944afe6777fSDmitry Baryshkov 	girq = &state->chip.irq;
945*9890a043SLinus Walleij 	gpio_irq_chip_set_chip(girq, &pmic_mpp_irq_chip);
946afe6777fSDmitry Baryshkov 	girq->default_type = IRQ_TYPE_NONE;
947afe6777fSDmitry Baryshkov 	girq->handler = handle_level_irq;
9489bd73ce0SAndy Shevchenko 	girq->fwnode = dev_fwnode(state->dev);
949afe6777fSDmitry Baryshkov 	girq->parent_domain = parent_domain;
950afe6777fSDmitry Baryshkov 	girq->child_to_parent_hwirq = pmic_mpp_child_to_parent_hwirq;
951afe6777fSDmitry Baryshkov 	girq->populate_parent_alloc_arg = gpiochip_populate_parent_fwspec_fourcell;
952afe6777fSDmitry Baryshkov 	girq->child_offset_to_irq = pmic_mpp_child_offset_to_irq;
953afe6777fSDmitry Baryshkov 	girq->child_irq_domain_ops.translate = pmic_mpp_domain_translate;
954afe6777fSDmitry Baryshkov 
955064761d1SLinus Walleij 	ret = gpiochip_add_data(&state->chip, state);
956cfb24f6eSIvan T. Ivanov 	if (ret) {
957cfb24f6eSIvan T. Ivanov 		dev_err(state->dev, "can't add gpio chip\n");
958ce18e595SLaxman Dewangan 		return ret;
959cfb24f6eSIvan T. Ivanov 	}
960cfb24f6eSIvan T. Ivanov 
961cfb24f6eSIvan T. Ivanov 	ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, npins);
962cfb24f6eSIvan T. Ivanov 	if (ret) {
963cfb24f6eSIvan T. Ivanov 		dev_err(dev, "failed to add pin range\n");
964cfb24f6eSIvan T. Ivanov 		goto err_range;
965cfb24f6eSIvan T. Ivanov 	}
966cfb24f6eSIvan T. Ivanov 
967cfb24f6eSIvan T. Ivanov 	return 0;
968cfb24f6eSIvan T. Ivanov 
969cfb24f6eSIvan T. Ivanov err_range:
970cfb24f6eSIvan T. Ivanov 	gpiochip_remove(&state->chip);
971cfb24f6eSIvan T. Ivanov 	return ret;
972cfb24f6eSIvan T. Ivanov }
973cfb24f6eSIvan T. Ivanov 
pmic_mpp_remove(struct platform_device * pdev)974cfb24f6eSIvan T. Ivanov static int pmic_mpp_remove(struct platform_device *pdev)
975cfb24f6eSIvan T. Ivanov {
976cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_state *state = platform_get_drvdata(pdev);
977cfb24f6eSIvan T. Ivanov 
978cfb24f6eSIvan T. Ivanov 	gpiochip_remove(&state->chip);
979cfb24f6eSIvan T. Ivanov 	return 0;
980cfb24f6eSIvan T. Ivanov }
981cfb24f6eSIvan T. Ivanov 
982cfb24f6eSIvan T. Ivanov static const struct of_device_id pmic_mpp_of_match[] = {
983f24dbaaaSDmitry Baryshkov 	{ .compatible = "qcom,pm8019-mpp", .data = (void *) 6 },
984d8420f5aSRayyan Ansari 	{ .compatible = "qcom,pm8226-mpp", .data = (void *) 8 },
985f24dbaaaSDmitry Baryshkov 	{ .compatible = "qcom,pm8841-mpp", .data = (void *) 4 },
986f24dbaaaSDmitry Baryshkov 	{ .compatible = "qcom,pm8916-mpp", .data = (void *) 4 },
987f24dbaaaSDmitry Baryshkov 	{ .compatible = "qcom,pm8941-mpp", .data = (void *) 8 },
988f24dbaaaSDmitry Baryshkov 	{ .compatible = "qcom,pm8950-mpp", .data = (void *) 4 },
989f24dbaaaSDmitry Baryshkov 	{ .compatible = "qcom,pmi8950-mpp", .data = (void *) 4 },
990f24dbaaaSDmitry Baryshkov 	{ .compatible = "qcom,pm8994-mpp", .data = (void *) 8 },
991f24dbaaaSDmitry Baryshkov 	{ .compatible = "qcom,pma8084-mpp", .data = (void *) 8 },
992f24dbaaaSDmitry Baryshkov 	{ .compatible = "qcom,pmi8994-mpp", .data = (void *) 4 },
993cfb24f6eSIvan T. Ivanov 	{ },
994cfb24f6eSIvan T. Ivanov };
995cfb24f6eSIvan T. Ivanov 
996cfb24f6eSIvan T. Ivanov MODULE_DEVICE_TABLE(of, pmic_mpp_of_match);
997cfb24f6eSIvan T. Ivanov 
998cfb24f6eSIvan T. Ivanov static struct platform_driver pmic_mpp_driver = {
999cfb24f6eSIvan T. Ivanov 	.driver = {
1000cfb24f6eSIvan T. Ivanov 		   .name = "qcom-spmi-mpp",
1001cfb24f6eSIvan T. Ivanov 		   .of_match_table = pmic_mpp_of_match,
1002cfb24f6eSIvan T. Ivanov 	},
1003cfb24f6eSIvan T. Ivanov 	.probe	= pmic_mpp_probe,
1004cfb24f6eSIvan T. Ivanov 	.remove = pmic_mpp_remove,
1005cfb24f6eSIvan T. Ivanov };
1006cfb24f6eSIvan T. Ivanov 
1007cfb24f6eSIvan T. Ivanov module_platform_driver(pmic_mpp_driver);
1008cfb24f6eSIvan T. Ivanov 
1009cfb24f6eSIvan T. Ivanov MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
1010cfb24f6eSIvan T. Ivanov MODULE_DESCRIPTION("Qualcomm SPMI PMIC MPP pin control driver");
1011cfb24f6eSIvan T. Ivanov MODULE_ALIAS("platform:qcom-spmi-mpp");
1012cfb24f6eSIvan T. Ivanov MODULE_LICENSE("GPL v2");
1013