1cfb24f6eSIvan T. Ivanov /*
2cfb24f6eSIvan T. Ivanov  * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
3cfb24f6eSIvan T. Ivanov  *
4cfb24f6eSIvan T. Ivanov  * This program is free software; you can redistribute it and/or modify
5cfb24f6eSIvan T. Ivanov  * it under the terms of the GNU General Public License version 2 and
6cfb24f6eSIvan T. Ivanov  * only version 2 as published by the Free Software Foundation.
7cfb24f6eSIvan T. Ivanov  *
8cfb24f6eSIvan T. Ivanov  * This program is distributed in the hope that it will be useful,
9cfb24f6eSIvan T. Ivanov  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10cfb24f6eSIvan T. Ivanov  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11cfb24f6eSIvan T. Ivanov  * GNU General Public License for more details.
12cfb24f6eSIvan T. Ivanov  */
13cfb24f6eSIvan T. Ivanov 
14cfb24f6eSIvan T. Ivanov #include <linux/gpio.h>
15cfb24f6eSIvan T. Ivanov #include <linux/module.h>
16cfb24f6eSIvan T. Ivanov #include <linux/of.h>
17ab4256cfSStephen Boyd #include <linux/of_irq.h>
18cfb24f6eSIvan T. Ivanov #include <linux/pinctrl/pinconf-generic.h>
19cfb24f6eSIvan T. Ivanov #include <linux/pinctrl/pinconf.h>
20cfb24f6eSIvan T. Ivanov #include <linux/pinctrl/pinmux.h>
21cfb24f6eSIvan T. Ivanov #include <linux/platform_device.h>
22cfb24f6eSIvan T. Ivanov #include <linux/regmap.h>
23cfb24f6eSIvan T. Ivanov #include <linux/slab.h>
24cfb24f6eSIvan T. Ivanov #include <linux/types.h>
25cfb24f6eSIvan T. Ivanov 
26cfb24f6eSIvan T. Ivanov #include <dt-bindings/pinctrl/qcom,pmic-mpp.h>
27cfb24f6eSIvan T. Ivanov 
28cfb24f6eSIvan T. Ivanov #include "../core.h"
29cfb24f6eSIvan T. Ivanov #include "../pinctrl-utils.h"
30cfb24f6eSIvan T. Ivanov 
31cfb24f6eSIvan T. Ivanov #define PMIC_MPP_ADDRESS_RANGE			0x100
32cfb24f6eSIvan T. Ivanov 
33cfb24f6eSIvan T. Ivanov /*
34cfb24f6eSIvan T. Ivanov  * Pull Up Values - it indicates whether a pull-up should be
35cfb24f6eSIvan T. Ivanov  * applied for bidirectional mode only. The hardware ignores the
36cfb24f6eSIvan T. Ivanov  * configuration when operating in other modes.
37cfb24f6eSIvan T. Ivanov  */
38cfb24f6eSIvan T. Ivanov #define PMIC_MPP_PULL_UP_0P6KOHM		0
39cfb24f6eSIvan T. Ivanov #define PMIC_MPP_PULL_UP_10KOHM			1
40cfb24f6eSIvan T. Ivanov #define PMIC_MPP_PULL_UP_30KOHM			2
41cfb24f6eSIvan T. Ivanov #define PMIC_MPP_PULL_UP_OPEN			3
42cfb24f6eSIvan T. Ivanov 
43cfb24f6eSIvan T. Ivanov /* type registers base address bases */
44cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_TYPE			0x4
45cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_SUBTYPE			0x5
46cfb24f6eSIvan T. Ivanov 
47cfb24f6eSIvan T. Ivanov /* mpp peripheral type and subtype values */
48cfb24f6eSIvan T. Ivanov #define PMIC_MPP_TYPE				0x11
49cfb24f6eSIvan T. Ivanov #define PMIC_MPP_SUBTYPE_4CH_NO_ANA_OUT		0x3
50cfb24f6eSIvan T. Ivanov #define PMIC_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT	0x4
51cfb24f6eSIvan T. Ivanov #define PMIC_MPP_SUBTYPE_4CH_NO_SINK		0x5
52cfb24f6eSIvan T. Ivanov #define PMIC_MPP_SUBTYPE_ULT_4CH_NO_SINK	0x6
53cfb24f6eSIvan T. Ivanov #define PMIC_MPP_SUBTYPE_4CH_FULL_FUNC		0x7
54cfb24f6eSIvan T. Ivanov #define PMIC_MPP_SUBTYPE_8CH_FULL_FUNC		0xf
55cfb24f6eSIvan T. Ivanov 
56cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_RT_STS			0x10
57cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_RT_STS_VAL_MASK		0x1
58cfb24f6eSIvan T. Ivanov 
59cfb24f6eSIvan T. Ivanov /* control register base address bases */
60cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_MODE_CTL			0x40
61cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_DIG_VIN_CTL		0x41
62cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_DIG_PULL_CTL		0x42
63cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_DIG_IN_CTL			0x43
64cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_EN_CTL			0x46
65099f3e4aSBjorn Andersson #define PMIC_MPP_REG_AOUT_CTL			0x48
66cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_AIN_CTL			0x4a
670e948042SBjorn Andersson #define PMIC_MPP_REG_SINK_CTL			0x4c
68cfb24f6eSIvan T. Ivanov 
69cfb24f6eSIvan T. Ivanov /* PMIC_MPP_REG_MODE_CTL */
70cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_MODE_VALUE_MASK		0x1
71cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_MODE_FUNCTION_SHIFT	1
72cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_MODE_FUNCTION_MASK		0x7
73cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_MODE_DIR_SHIFT		4
74cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_MODE_DIR_MASK		0x7
75cfb24f6eSIvan T. Ivanov 
76cfb24f6eSIvan T. Ivanov /* PMIC_MPP_REG_DIG_VIN_CTL */
77cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_VIN_SHIFT			0
78cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_VIN_MASK			0x7
79cfb24f6eSIvan T. Ivanov 
80cfb24f6eSIvan T. Ivanov /* PMIC_MPP_REG_DIG_PULL_CTL */
81cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_PULL_SHIFT			0
82cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_PULL_MASK			0x7
83cfb24f6eSIvan T. Ivanov 
84cfb24f6eSIvan T. Ivanov /* PMIC_MPP_REG_EN_CTL */
85cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_MASTER_EN_SHIFT		7
86cfb24f6eSIvan T. Ivanov 
87cfb24f6eSIvan T. Ivanov /* PMIC_MPP_REG_AIN_CTL */
88cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_AIN_ROUTE_SHIFT		0
89cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_AIN_ROUTE_MASK		0x7
90cfb24f6eSIvan T. Ivanov 
91eaaf5dd4SBjorn Andersson #define PMIC_MPP_MODE_DIGITAL_INPUT		0
92eaaf5dd4SBjorn Andersson #define PMIC_MPP_MODE_DIGITAL_OUTPUT		1
93eaaf5dd4SBjorn Andersson #define PMIC_MPP_MODE_DIGITAL_BIDIR		2
94eaaf5dd4SBjorn Andersson #define PMIC_MPP_MODE_ANALOG_BIDIR		3
95eaaf5dd4SBjorn Andersson #define PMIC_MPP_MODE_ANALOG_INPUT		4
96eaaf5dd4SBjorn Andersson #define PMIC_MPP_MODE_ANALOG_OUTPUT		5
97eaaf5dd4SBjorn Andersson #define PMIC_MPP_MODE_CURRENT_SINK		6
98eaaf5dd4SBjorn Andersson 
99eb5c144cSBjorn Andersson #define PMIC_MPP_SELECTOR_NORMAL		0
100eb5c144cSBjorn Andersson #define PMIC_MPP_SELECTOR_PAIRED		1
101eb5c144cSBjorn Andersson #define PMIC_MPP_SELECTOR_DTEST_FIRST		4
102eb5c144cSBjorn Andersson 
103cfb24f6eSIvan T. Ivanov #define PMIC_MPP_PHYSICAL_OFFSET		1
104cfb24f6eSIvan T. Ivanov 
105cfb24f6eSIvan T. Ivanov /* Qualcomm specific pin configurations */
106cfb24f6eSIvan T. Ivanov #define PMIC_MPP_CONF_AMUX_ROUTE		(PIN_CONFIG_END + 1)
107eb5c144cSBjorn Andersson #define PMIC_MPP_CONF_ANALOG_LEVEL		(PIN_CONFIG_END + 2)
108eb5c144cSBjorn Andersson #define PMIC_MPP_CONF_DTEST_SELECTOR		(PIN_CONFIG_END + 3)
109eb5c144cSBjorn Andersson #define PMIC_MPP_CONF_PAIRED			(PIN_CONFIG_END + 4)
110cfb24f6eSIvan T. Ivanov 
111cfb24f6eSIvan T. Ivanov /**
112cfb24f6eSIvan T. Ivanov  * struct pmic_mpp_pad - keep current MPP settings
113cfb24f6eSIvan T. Ivanov  * @base: Address base in SPMI device.
114cfb24f6eSIvan T. Ivanov  * @irq: IRQ number which this MPP generate.
115cfb24f6eSIvan T. Ivanov  * @is_enabled: Set to false when MPP should be put in high Z state.
116cfb24f6eSIvan T. Ivanov  * @out_value: Cached pin output value.
117cfb24f6eSIvan T. Ivanov  * @output_enabled: Set to true if MPP output logic is enabled.
118cfb24f6eSIvan T. Ivanov  * @input_enabled: Set to true if MPP input buffer logic is enabled.
119eb5c144cSBjorn Andersson  * @paired: Pin operates in paired mode
120ae6d54fdSStephen Boyd  * @has_pullup: Pin has support to configure pullup
121cfb24f6eSIvan T. Ivanov  * @num_sources: Number of power-sources supported by this MPP.
122cfb24f6eSIvan T. Ivanov  * @power_source: Current power-source used.
123cfb24f6eSIvan T. Ivanov  * @amux_input: Set the source for analog input.
124099f3e4aSBjorn Andersson  * @aout_level: Analog output level
125cfb24f6eSIvan T. Ivanov  * @pullup: Pullup resistor value. Valid in Bidirectional mode only.
126cfb24f6eSIvan T. Ivanov  * @function: See pmic_mpp_functions[].
1270e948042SBjorn Andersson  * @drive_strength: Amount of current in sink mode
128eb5c144cSBjorn Andersson  * @dtest: DTEST route selector
129cfb24f6eSIvan T. Ivanov  */
130cfb24f6eSIvan T. Ivanov struct pmic_mpp_pad {
131cfb24f6eSIvan T. Ivanov 	u16		base;
132cfb24f6eSIvan T. Ivanov 	int		irq;
133cfb24f6eSIvan T. Ivanov 	bool		is_enabled;
134cfb24f6eSIvan T. Ivanov 	bool		out_value;
135cfb24f6eSIvan T. Ivanov 	bool		output_enabled;
136cfb24f6eSIvan T. Ivanov 	bool		input_enabled;
137eb5c144cSBjorn Andersson 	bool		paired;
138ae6d54fdSStephen Boyd 	bool		has_pullup;
139cfb24f6eSIvan T. Ivanov 	unsigned int	num_sources;
140cfb24f6eSIvan T. Ivanov 	unsigned int	power_source;
141cfb24f6eSIvan T. Ivanov 	unsigned int	amux_input;
142099f3e4aSBjorn Andersson 	unsigned int	aout_level;
143cfb24f6eSIvan T. Ivanov 	unsigned int	pullup;
144cfb24f6eSIvan T. Ivanov 	unsigned int	function;
1450e948042SBjorn Andersson 	unsigned int	drive_strength;
146eb5c144cSBjorn Andersson 	unsigned int	dtest;
147cfb24f6eSIvan T. Ivanov };
148cfb24f6eSIvan T. Ivanov 
149cfb24f6eSIvan T. Ivanov struct pmic_mpp_state {
150cfb24f6eSIvan T. Ivanov 	struct device	*dev;
151cfb24f6eSIvan T. Ivanov 	struct regmap	*map;
152cfb24f6eSIvan T. Ivanov 	struct pinctrl_dev *ctrl;
153cfb24f6eSIvan T. Ivanov 	struct gpio_chip chip;
154cfb24f6eSIvan T. Ivanov };
155cfb24f6eSIvan T. Ivanov 
156ba5f94cdSBjorn Andersson static const struct pinconf_generic_params pmic_mpp_bindings[] = {
157ba5f94cdSBjorn Andersson 	{"qcom,amux-route",	PMIC_MPP_CONF_AMUX_ROUTE,	0},
158099f3e4aSBjorn Andersson 	{"qcom,analog-level",	PMIC_MPP_CONF_ANALOG_LEVEL,	0},
159eb5c144cSBjorn Andersson 	{"qcom,dtest",		PMIC_MPP_CONF_DTEST_SELECTOR,	0},
160eb5c144cSBjorn Andersson 	{"qcom,paired",		PMIC_MPP_CONF_PAIRED,		0},
161cfb24f6eSIvan T. Ivanov };
162cfb24f6eSIvan T. Ivanov 
163ba5f94cdSBjorn Andersson #ifdef CONFIG_DEBUG_FS
164ba5f94cdSBjorn Andersson static const struct pin_config_item pmic_conf_items[] = {
165ba5f94cdSBjorn Andersson 	PCONFDUMP(PMIC_MPP_CONF_AMUX_ROUTE, "analog mux", NULL, true),
166099f3e4aSBjorn Andersson 	PCONFDUMP(PMIC_MPP_CONF_ANALOG_LEVEL, "analog level", NULL, true),
167eb5c144cSBjorn Andersson 	PCONFDUMP(PMIC_MPP_CONF_DTEST_SELECTOR, "dtest", NULL, true),
168eb5c144cSBjorn Andersson 	PCONFDUMP(PMIC_MPP_CONF_PAIRED, "paired", NULL, false),
169cfb24f6eSIvan T. Ivanov };
170ba5f94cdSBjorn Andersson #endif
171cfb24f6eSIvan T. Ivanov 
172cfb24f6eSIvan T. Ivanov static const char *const pmic_mpp_groups[] = {
173cfb24f6eSIvan T. Ivanov 	"mpp1", "mpp2", "mpp3", "mpp4", "mpp5", "mpp6", "mpp7", "mpp8",
174cfb24f6eSIvan T. Ivanov };
175cfb24f6eSIvan T. Ivanov 
176eb5c144cSBjorn Andersson #define PMIC_MPP_DIGITAL	0
177eb5c144cSBjorn Andersson #define PMIC_MPP_ANALOG		1
178eb5c144cSBjorn Andersson #define PMIC_MPP_SINK		2
179eb5c144cSBjorn Andersson 
180cfb24f6eSIvan T. Ivanov static const char *const pmic_mpp_functions[] = {
181eb5c144cSBjorn Andersson 	"digital", "analog", "sink"
182cfb24f6eSIvan T. Ivanov };
183cfb24f6eSIvan T. Ivanov 
184cfb24f6eSIvan T. Ivanov static int pmic_mpp_read(struct pmic_mpp_state *state,
185cfb24f6eSIvan T. Ivanov 			 struct pmic_mpp_pad *pad, unsigned int addr)
186cfb24f6eSIvan T. Ivanov {
187cfb24f6eSIvan T. Ivanov 	unsigned int val;
188cfb24f6eSIvan T. Ivanov 	int ret;
189cfb24f6eSIvan T. Ivanov 
190cfb24f6eSIvan T. Ivanov 	ret = regmap_read(state->map, pad->base + addr, &val);
191cfb24f6eSIvan T. Ivanov 	if (ret < 0)
192cfb24f6eSIvan T. Ivanov 		dev_err(state->dev, "read 0x%x failed\n", addr);
193cfb24f6eSIvan T. Ivanov 	else
194cfb24f6eSIvan T. Ivanov 		ret = val;
195cfb24f6eSIvan T. Ivanov 
196cfb24f6eSIvan T. Ivanov 	return ret;
197cfb24f6eSIvan T. Ivanov }
198cfb24f6eSIvan T. Ivanov 
199cfb24f6eSIvan T. Ivanov static int pmic_mpp_write(struct pmic_mpp_state *state,
200cfb24f6eSIvan T. Ivanov 			  struct pmic_mpp_pad *pad, unsigned int addr,
201cfb24f6eSIvan T. Ivanov 			  unsigned int val)
202cfb24f6eSIvan T. Ivanov {
203cfb24f6eSIvan T. Ivanov 	int ret;
204cfb24f6eSIvan T. Ivanov 
205cfb24f6eSIvan T. Ivanov 	ret = regmap_write(state->map, pad->base + addr, val);
206cfb24f6eSIvan T. Ivanov 	if (ret < 0)
207cfb24f6eSIvan T. Ivanov 		dev_err(state->dev, "write 0x%x failed\n", addr);
208cfb24f6eSIvan T. Ivanov 
209cfb24f6eSIvan T. Ivanov 	return ret;
210cfb24f6eSIvan T. Ivanov }
211cfb24f6eSIvan T. Ivanov 
212cfb24f6eSIvan T. Ivanov static int pmic_mpp_get_groups_count(struct pinctrl_dev *pctldev)
213cfb24f6eSIvan T. Ivanov {
214cfb24f6eSIvan T. Ivanov 	/* Every PIN is a group */
215cfb24f6eSIvan T. Ivanov 	return pctldev->desc->npins;
216cfb24f6eSIvan T. Ivanov }
217cfb24f6eSIvan T. Ivanov 
218cfb24f6eSIvan T. Ivanov static const char *pmic_mpp_get_group_name(struct pinctrl_dev *pctldev,
219cfb24f6eSIvan T. Ivanov 					   unsigned pin)
220cfb24f6eSIvan T. Ivanov {
221cfb24f6eSIvan T. Ivanov 	return pctldev->desc->pins[pin].name;
222cfb24f6eSIvan T. Ivanov }
223cfb24f6eSIvan T. Ivanov 
224cfb24f6eSIvan T. Ivanov static int pmic_mpp_get_group_pins(struct pinctrl_dev *pctldev,
225cfb24f6eSIvan T. Ivanov 				   unsigned pin,
226cfb24f6eSIvan T. Ivanov 				   const unsigned **pins, unsigned *num_pins)
227cfb24f6eSIvan T. Ivanov {
228cfb24f6eSIvan T. Ivanov 	*pins = &pctldev->desc->pins[pin].number;
229cfb24f6eSIvan T. Ivanov 	*num_pins = 1;
230cfb24f6eSIvan T. Ivanov 	return 0;
231cfb24f6eSIvan T. Ivanov }
232cfb24f6eSIvan T. Ivanov 
233cfb24f6eSIvan T. Ivanov static const struct pinctrl_ops pmic_mpp_pinctrl_ops = {
234cfb24f6eSIvan T. Ivanov 	.get_groups_count	= pmic_mpp_get_groups_count,
235cfb24f6eSIvan T. Ivanov 	.get_group_name		= pmic_mpp_get_group_name,
236cfb24f6eSIvan T. Ivanov 	.get_group_pins		= pmic_mpp_get_group_pins,
237ba5f94cdSBjorn Andersson 	.dt_node_to_map		= pinconf_generic_dt_node_to_map_group,
238d32f7fd3SIrina Tirdea 	.dt_free_map		= pinctrl_utils_free_map,
239cfb24f6eSIvan T. Ivanov };
240cfb24f6eSIvan T. Ivanov 
241cfb24f6eSIvan T. Ivanov static int pmic_mpp_get_functions_count(struct pinctrl_dev *pctldev)
242cfb24f6eSIvan T. Ivanov {
243cfb24f6eSIvan T. Ivanov 	return ARRAY_SIZE(pmic_mpp_functions);
244cfb24f6eSIvan T. Ivanov }
245cfb24f6eSIvan T. Ivanov 
246cfb24f6eSIvan T. Ivanov static const char *pmic_mpp_get_function_name(struct pinctrl_dev *pctldev,
247cfb24f6eSIvan T. Ivanov 					      unsigned function)
248cfb24f6eSIvan T. Ivanov {
249cfb24f6eSIvan T. Ivanov 	return pmic_mpp_functions[function];
250cfb24f6eSIvan T. Ivanov }
251cfb24f6eSIvan T. Ivanov 
252cfb24f6eSIvan T. Ivanov static int pmic_mpp_get_function_groups(struct pinctrl_dev *pctldev,
253cfb24f6eSIvan T. Ivanov 					unsigned function,
254cfb24f6eSIvan T. Ivanov 					const char *const **groups,
255cfb24f6eSIvan T. Ivanov 					unsigned *const num_qgroups)
256cfb24f6eSIvan T. Ivanov {
257cfb24f6eSIvan T. Ivanov 	*groups = pmic_mpp_groups;
258cfb24f6eSIvan T. Ivanov 	*num_qgroups = pctldev->desc->npins;
259cfb24f6eSIvan T. Ivanov 	return 0;
260cfb24f6eSIvan T. Ivanov }
261cfb24f6eSIvan T. Ivanov 
2620e948042SBjorn Andersson static int pmic_mpp_write_mode_ctl(struct pmic_mpp_state *state,
2630e948042SBjorn Andersson 				   struct pmic_mpp_pad *pad)
2640e948042SBjorn Andersson {
265eb5c144cSBjorn Andersson 	unsigned int mode;
266eb5c144cSBjorn Andersson 	unsigned int sel;
2670e948042SBjorn Andersson 	unsigned int val;
268eb5c144cSBjorn Andersson 	unsigned int en;
2690e948042SBjorn Andersson 
270eb5c144cSBjorn Andersson 	switch (pad->function) {
271eb5c144cSBjorn Andersson 	case PMIC_MPP_ANALOG:
272eb5c144cSBjorn Andersson 		if (pad->input_enabled && pad->output_enabled)
273eb5c144cSBjorn Andersson 			mode = PMIC_MPP_MODE_ANALOG_BIDIR;
274eb5c144cSBjorn Andersson 		else if (pad->input_enabled)
275eb5c144cSBjorn Andersson 			mode = PMIC_MPP_MODE_ANALOG_INPUT;
2760e948042SBjorn Andersson 		else
277eb5c144cSBjorn Andersson 			mode = PMIC_MPP_MODE_ANALOG_OUTPUT;
278eb5c144cSBjorn Andersson 		break;
279eb5c144cSBjorn Andersson 	case PMIC_MPP_DIGITAL:
280eb5c144cSBjorn Andersson 		if (pad->input_enabled && pad->output_enabled)
281eb5c144cSBjorn Andersson 			mode = PMIC_MPP_MODE_DIGITAL_BIDIR;
282eb5c144cSBjorn Andersson 		else if (pad->input_enabled)
283eb5c144cSBjorn Andersson 			mode = PMIC_MPP_MODE_DIGITAL_INPUT;
2840e948042SBjorn Andersson 		else
285eb5c144cSBjorn Andersson 			mode = PMIC_MPP_MODE_DIGITAL_OUTPUT;
286eb5c144cSBjorn Andersson 		break;
287eb5c144cSBjorn Andersson 	case PMIC_MPP_SINK:
288eb5c144cSBjorn Andersson 	default:
289eb5c144cSBjorn Andersson 		mode = PMIC_MPP_MODE_CURRENT_SINK;
290eb5c144cSBjorn Andersson 		break;
2910e948042SBjorn Andersson 	}
2920e948042SBjorn Andersson 
293eb5c144cSBjorn Andersson 	if (pad->dtest)
294eb5c144cSBjorn Andersson 		sel = PMIC_MPP_SELECTOR_DTEST_FIRST + pad->dtest - 1;
295eb5c144cSBjorn Andersson 	else if (pad->paired)
296eb5c144cSBjorn Andersson 		sel = PMIC_MPP_SELECTOR_PAIRED;
297eb5c144cSBjorn Andersson 	else
298eb5c144cSBjorn Andersson 		sel = PMIC_MPP_SELECTOR_NORMAL;
299eb5c144cSBjorn Andersson 
300eb5c144cSBjorn Andersson 	en = !!pad->out_value;
301eb5c144cSBjorn Andersson 
302eb5c144cSBjorn Andersson 	val = mode << PMIC_MPP_REG_MODE_DIR_SHIFT |
303eb5c144cSBjorn Andersson 	      sel << PMIC_MPP_REG_MODE_FUNCTION_SHIFT |
304eb5c144cSBjorn Andersson 	      en;
3050e948042SBjorn Andersson 
3060e948042SBjorn Andersson 	return pmic_mpp_write(state, pad, PMIC_MPP_REG_MODE_CTL, val);
3070e948042SBjorn Andersson }
3080e948042SBjorn Andersson 
309cfb24f6eSIvan T. Ivanov static int pmic_mpp_set_mux(struct pinctrl_dev *pctldev, unsigned function,
310cfb24f6eSIvan T. Ivanov 				unsigned pin)
311cfb24f6eSIvan T. Ivanov {
312cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_state *state = pinctrl_dev_get_drvdata(pctldev);
313cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_pad *pad;
314cfb24f6eSIvan T. Ivanov 	unsigned int val;
315cfb24f6eSIvan T. Ivanov 	int ret;
316cfb24f6eSIvan T. Ivanov 
317cfb24f6eSIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
318cfb24f6eSIvan T. Ivanov 
319cfb24f6eSIvan T. Ivanov 	pad->function = function;
320cfb24f6eSIvan T. Ivanov 
3210e948042SBjorn Andersson 	ret = pmic_mpp_write_mode_ctl(state, pad);
322cfb24f6eSIvan T. Ivanov 
323cfb24f6eSIvan T. Ivanov 	val = pad->is_enabled << PMIC_MPP_REG_MASTER_EN_SHIFT;
324cfb24f6eSIvan T. Ivanov 
325cfb24f6eSIvan T. Ivanov 	return pmic_mpp_write(state, pad, PMIC_MPP_REG_EN_CTL, val);
326cfb24f6eSIvan T. Ivanov }
327cfb24f6eSIvan T. Ivanov 
328cfb24f6eSIvan T. Ivanov static const struct pinmux_ops pmic_mpp_pinmux_ops = {
329cfb24f6eSIvan T. Ivanov 	.get_functions_count	= pmic_mpp_get_functions_count,
330cfb24f6eSIvan T. Ivanov 	.get_function_name	= pmic_mpp_get_function_name,
331cfb24f6eSIvan T. Ivanov 	.get_function_groups	= pmic_mpp_get_function_groups,
332cfb24f6eSIvan T. Ivanov 	.set_mux		= pmic_mpp_set_mux,
333cfb24f6eSIvan T. Ivanov };
334cfb24f6eSIvan T. Ivanov 
335cfb24f6eSIvan T. Ivanov static int pmic_mpp_config_get(struct pinctrl_dev *pctldev,
336cfb24f6eSIvan T. Ivanov 			       unsigned int pin, unsigned long *config)
337cfb24f6eSIvan T. Ivanov {
338cfb24f6eSIvan T. Ivanov 	unsigned param = pinconf_to_config_param(*config);
339cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_pad *pad;
340cfb24f6eSIvan T. Ivanov 	unsigned arg = 0;
341cfb24f6eSIvan T. Ivanov 
342cfb24f6eSIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
343cfb24f6eSIvan T. Ivanov 
344cfb24f6eSIvan T. Ivanov 	switch (param) {
345cfb24f6eSIvan T. Ivanov 	case PIN_CONFIG_BIAS_DISABLE:
346cfb24f6eSIvan T. Ivanov 		arg = pad->pullup == PMIC_MPP_PULL_UP_OPEN;
347cfb24f6eSIvan T. Ivanov 		break;
348cfb24f6eSIvan T. Ivanov 	case PIN_CONFIG_BIAS_PULL_UP:
349cfb24f6eSIvan T. Ivanov 		switch (pad->pullup) {
350cfb24f6eSIvan T. Ivanov 		case PMIC_MPP_PULL_UP_OPEN:
351cfb24f6eSIvan T. Ivanov 			arg = 0;
352cfb24f6eSIvan T. Ivanov 			break;
353cfb24f6eSIvan T. Ivanov 		case PMIC_MPP_PULL_UP_0P6KOHM:
354cfb24f6eSIvan T. Ivanov 			arg = 600;
355cfb24f6eSIvan T. Ivanov 			break;
356cfb24f6eSIvan T. Ivanov 		case PMIC_MPP_PULL_UP_10KOHM:
357cfb24f6eSIvan T. Ivanov 			arg = 10000;
358cfb24f6eSIvan T. Ivanov 			break;
359cfb24f6eSIvan T. Ivanov 		case PMIC_MPP_PULL_UP_30KOHM:
360cfb24f6eSIvan T. Ivanov 			arg = 30000;
361cfb24f6eSIvan T. Ivanov 			break;
362cfb24f6eSIvan T. Ivanov 		default:
363cfb24f6eSIvan T. Ivanov 			return -EINVAL;
364cfb24f6eSIvan T. Ivanov 		}
365cfb24f6eSIvan T. Ivanov 		break;
366cfb24f6eSIvan T. Ivanov 	case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
367cfb24f6eSIvan T. Ivanov 		arg = !pad->is_enabled;
368cfb24f6eSIvan T. Ivanov 		break;
369cfb24f6eSIvan T. Ivanov 	case PIN_CONFIG_POWER_SOURCE:
370cfb24f6eSIvan T. Ivanov 		arg = pad->power_source;
371cfb24f6eSIvan T. Ivanov 		break;
372cfb24f6eSIvan T. Ivanov 	case PIN_CONFIG_INPUT_ENABLE:
373cfb24f6eSIvan T. Ivanov 		arg = pad->input_enabled;
374cfb24f6eSIvan T. Ivanov 		break;
375cfb24f6eSIvan T. Ivanov 	case PIN_CONFIG_OUTPUT:
376cfb24f6eSIvan T. Ivanov 		arg = pad->out_value;
377cfb24f6eSIvan T. Ivanov 		break;
378eb5c144cSBjorn Andersson 	case PMIC_MPP_CONF_DTEST_SELECTOR:
379eb5c144cSBjorn Andersson 		arg = pad->dtest;
380eb5c144cSBjorn Andersson 		break;
381cfb24f6eSIvan T. Ivanov 	case PMIC_MPP_CONF_AMUX_ROUTE:
382cfb24f6eSIvan T. Ivanov 		arg = pad->amux_input;
383cfb24f6eSIvan T. Ivanov 		break;
384eb5c144cSBjorn Andersson 	case PMIC_MPP_CONF_PAIRED:
385eb5c144cSBjorn Andersson 		arg = pad->paired;
386eb5c144cSBjorn Andersson 		break;
3870e948042SBjorn Andersson 	case PIN_CONFIG_DRIVE_STRENGTH:
3880e948042SBjorn Andersson 		arg = pad->drive_strength;
3890e948042SBjorn Andersson 		break;
390099f3e4aSBjorn Andersson 	case PMIC_MPP_CONF_ANALOG_LEVEL:
391099f3e4aSBjorn Andersson 		arg = pad->aout_level;
392099f3e4aSBjorn Andersson 		break;
393cfb24f6eSIvan T. Ivanov 	default:
394cfb24f6eSIvan T. Ivanov 		return -EINVAL;
395cfb24f6eSIvan T. Ivanov 	}
396cfb24f6eSIvan T. Ivanov 
397cfb24f6eSIvan T. Ivanov 	/* Convert register value to pinconf value */
398cfb24f6eSIvan T. Ivanov 	*config = pinconf_to_config_packed(param, arg);
399cfb24f6eSIvan T. Ivanov 	return 0;
400cfb24f6eSIvan T. Ivanov }
401cfb24f6eSIvan T. Ivanov 
402cfb24f6eSIvan T. Ivanov static int pmic_mpp_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
403cfb24f6eSIvan T. Ivanov 			       unsigned long *configs, unsigned nconfs)
404cfb24f6eSIvan T. Ivanov {
405cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_state *state = pinctrl_dev_get_drvdata(pctldev);
406cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_pad *pad;
407cfb24f6eSIvan T. Ivanov 	unsigned param, arg;
408cfb24f6eSIvan T. Ivanov 	unsigned int val;
409cfb24f6eSIvan T. Ivanov 	int i, ret;
410cfb24f6eSIvan T. Ivanov 
411cfb24f6eSIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
412cfb24f6eSIvan T. Ivanov 
4137682b374SBjorn Andersson 	/* Make it possible to enable the pin, by not setting high impedance */
4147682b374SBjorn Andersson 	pad->is_enabled = true;
4157682b374SBjorn Andersson 
416cfb24f6eSIvan T. Ivanov 	for (i = 0; i < nconfs; i++) {
417cfb24f6eSIvan T. Ivanov 		param = pinconf_to_config_param(configs[i]);
418cfb24f6eSIvan T. Ivanov 		arg = pinconf_to_config_argument(configs[i]);
419cfb24f6eSIvan T. Ivanov 
420cfb24f6eSIvan T. Ivanov 		switch (param) {
421cfb24f6eSIvan T. Ivanov 		case PIN_CONFIG_BIAS_DISABLE:
422cfb24f6eSIvan T. Ivanov 			pad->pullup = PMIC_MPP_PULL_UP_OPEN;
423cfb24f6eSIvan T. Ivanov 			break;
424cfb24f6eSIvan T. Ivanov 		case PIN_CONFIG_BIAS_PULL_UP:
425cfb24f6eSIvan T. Ivanov 			switch (arg) {
426cfb24f6eSIvan T. Ivanov 			case 600:
427cfb24f6eSIvan T. Ivanov 				pad->pullup = PMIC_MPP_PULL_UP_0P6KOHM;
428cfb24f6eSIvan T. Ivanov 				break;
429cfb24f6eSIvan T. Ivanov 			case 10000:
430cfb24f6eSIvan T. Ivanov 				pad->pullup = PMIC_MPP_PULL_UP_10KOHM;
431cfb24f6eSIvan T. Ivanov 				break;
432cfb24f6eSIvan T. Ivanov 			case 30000:
433cfb24f6eSIvan T. Ivanov 				pad->pullup = PMIC_MPP_PULL_UP_30KOHM;
434cfb24f6eSIvan T. Ivanov 				break;
435cfb24f6eSIvan T. Ivanov 			default:
436cfb24f6eSIvan T. Ivanov 				return -EINVAL;
437cfb24f6eSIvan T. Ivanov 			}
438cfb24f6eSIvan T. Ivanov 			break;
439cfb24f6eSIvan T. Ivanov 		case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
440cfb24f6eSIvan T. Ivanov 			pad->is_enabled = false;
441cfb24f6eSIvan T. Ivanov 			break;
442cfb24f6eSIvan T. Ivanov 		case PIN_CONFIG_POWER_SOURCE:
443cfb24f6eSIvan T. Ivanov 			if (arg >= pad->num_sources)
444cfb24f6eSIvan T. Ivanov 				return -EINVAL;
445cfb24f6eSIvan T. Ivanov 			pad->power_source = arg;
446cfb24f6eSIvan T. Ivanov 			break;
447cfb24f6eSIvan T. Ivanov 		case PIN_CONFIG_INPUT_ENABLE:
448cfb24f6eSIvan T. Ivanov 			pad->input_enabled = arg ? true : false;
449cfb24f6eSIvan T. Ivanov 			break;
450cfb24f6eSIvan T. Ivanov 		case PIN_CONFIG_OUTPUT:
451cfb24f6eSIvan T. Ivanov 			pad->output_enabled = true;
452cfb24f6eSIvan T. Ivanov 			pad->out_value = arg;
453cfb24f6eSIvan T. Ivanov 			break;
454eb5c144cSBjorn Andersson 		case PMIC_MPP_CONF_DTEST_SELECTOR:
455eb5c144cSBjorn Andersson 			pad->dtest = arg;
456eb5c144cSBjorn Andersson 			break;
4570e948042SBjorn Andersson 		case PIN_CONFIG_DRIVE_STRENGTH:
4580e948042SBjorn Andersson 			arg = pad->drive_strength;
4590e948042SBjorn Andersson 			break;
460cfb24f6eSIvan T. Ivanov 		case PMIC_MPP_CONF_AMUX_ROUTE:
461cfb24f6eSIvan T. Ivanov 			if (arg >= PMIC_MPP_AMUX_ROUTE_ABUS4)
462cfb24f6eSIvan T. Ivanov 				return -EINVAL;
463cfb24f6eSIvan T. Ivanov 			pad->amux_input = arg;
464cfb24f6eSIvan T. Ivanov 			break;
465099f3e4aSBjorn Andersson 		case PMIC_MPP_CONF_ANALOG_LEVEL:
466099f3e4aSBjorn Andersson 			pad->aout_level = arg;
467099f3e4aSBjorn Andersson 			break;
468eb5c144cSBjorn Andersson 		case PMIC_MPP_CONF_PAIRED:
469eb5c144cSBjorn Andersson 			pad->paired = !!arg;
470cfb24f6eSIvan T. Ivanov 			break;
471cfb24f6eSIvan T. Ivanov 		default:
472cfb24f6eSIvan T. Ivanov 			return -EINVAL;
473cfb24f6eSIvan T. Ivanov 		}
474cfb24f6eSIvan T. Ivanov 	}
475cfb24f6eSIvan T. Ivanov 
476cfb24f6eSIvan T. Ivanov 	val = pad->power_source << PMIC_MPP_REG_VIN_SHIFT;
477cfb24f6eSIvan T. Ivanov 
478cfb24f6eSIvan T. Ivanov 	ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_DIG_VIN_CTL, val);
479cfb24f6eSIvan T. Ivanov 	if (ret < 0)
480cfb24f6eSIvan T. Ivanov 		return ret;
481cfb24f6eSIvan T. Ivanov 
482ae6d54fdSStephen Boyd 	if (pad->has_pullup) {
483cfb24f6eSIvan T. Ivanov 		val = pad->pullup << PMIC_MPP_REG_PULL_SHIFT;
484cfb24f6eSIvan T. Ivanov 
485ae6d54fdSStephen Boyd 		ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_DIG_PULL_CTL,
486ae6d54fdSStephen Boyd 				     val);
487cfb24f6eSIvan T. Ivanov 		if (ret < 0)
488cfb24f6eSIvan T. Ivanov 			return ret;
489ae6d54fdSStephen Boyd 	}
490cfb24f6eSIvan T. Ivanov 
491cfb24f6eSIvan T. Ivanov 	val = pad->amux_input & PMIC_MPP_REG_AIN_ROUTE_MASK;
492cfb24f6eSIvan T. Ivanov 
493cfb24f6eSIvan T. Ivanov 	ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_AIN_CTL, val);
494cfb24f6eSIvan T. Ivanov 	if (ret < 0)
495cfb24f6eSIvan T. Ivanov 		return ret;
496cfb24f6eSIvan T. Ivanov 
497099f3e4aSBjorn Andersson 	ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_AOUT_CTL, pad->aout_level);
498099f3e4aSBjorn Andersson 	if (ret < 0)
499099f3e4aSBjorn Andersson 		return ret;
500099f3e4aSBjorn Andersson 
5010e948042SBjorn Andersson 	ret = pmic_mpp_write_mode_ctl(state, pad);
5027682b374SBjorn Andersson 	if (ret < 0)
5037682b374SBjorn Andersson 		return ret;
5047682b374SBjorn Andersson 
5057682b374SBjorn Andersson 	val = pad->is_enabled << PMIC_MPP_REG_MASTER_EN_SHIFT;
5067682b374SBjorn Andersson 
5077682b374SBjorn Andersson 	return pmic_mpp_write(state, pad, PMIC_MPP_REG_EN_CTL, val);
508cfb24f6eSIvan T. Ivanov }
509cfb24f6eSIvan T. Ivanov 
510cfb24f6eSIvan T. Ivanov static void pmic_mpp_config_dbg_show(struct pinctrl_dev *pctldev,
511cfb24f6eSIvan T. Ivanov 				     struct seq_file *s, unsigned pin)
512cfb24f6eSIvan T. Ivanov {
513cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_state *state = pinctrl_dev_get_drvdata(pctldev);
514cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_pad *pad;
5157682b374SBjorn Andersson 	int ret;
516cfb24f6eSIvan T. Ivanov 
517cfb24f6eSIvan T. Ivanov 	static const char *const biases[] = {
518cfb24f6eSIvan T. Ivanov 		"0.6kOhm", "10kOhm", "30kOhm", "Disabled"
519cfb24f6eSIvan T. Ivanov 	};
520cfb24f6eSIvan T. Ivanov 
521cfb24f6eSIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
522cfb24f6eSIvan T. Ivanov 
523cfb24f6eSIvan T. Ivanov 	seq_printf(s, " mpp%-2d:", pin + PMIC_MPP_PHYSICAL_OFFSET);
524cfb24f6eSIvan T. Ivanov 
5257682b374SBjorn Andersson 	if (!pad->is_enabled) {
526cfb24f6eSIvan T. Ivanov 		seq_puts(s, " ---");
527cfb24f6eSIvan T. Ivanov 	} else {
528cfb24f6eSIvan T. Ivanov 
529cfb24f6eSIvan T. Ivanov 		if (pad->input_enabled) {
530cfb24f6eSIvan T. Ivanov 			ret = pmic_mpp_read(state, pad, PMIC_MPP_REG_RT_STS);
5314e637ac2SIvan T. Ivanov 			if (ret < 0)
5324e637ac2SIvan T. Ivanov 				return;
5334e637ac2SIvan T. Ivanov 
534cfb24f6eSIvan T. Ivanov 			ret &= PMIC_MPP_REG_RT_STS_VAL_MASK;
535cfb24f6eSIvan T. Ivanov 			pad->out_value = ret;
536cfb24f6eSIvan T. Ivanov 		}
537cfb24f6eSIvan T. Ivanov 
538cfb24f6eSIvan T. Ivanov 		seq_printf(s, " %-4s", pad->output_enabled ? "out" : "in");
539cfb24f6eSIvan T. Ivanov 		seq_printf(s, " %-7s", pmic_mpp_functions[pad->function]);
540cfb24f6eSIvan T. Ivanov 		seq_printf(s, " vin-%d", pad->power_source);
541099f3e4aSBjorn Andersson 		seq_printf(s, " %d", pad->aout_level);
542ae6d54fdSStephen Boyd 		if (pad->has_pullup)
543cfb24f6eSIvan T. Ivanov 			seq_printf(s, " %-8s", biases[pad->pullup]);
544cfb24f6eSIvan T. Ivanov 		seq_printf(s, " %-4s", pad->out_value ? "high" : "low");
545eb5c144cSBjorn Andersson 		if (pad->dtest)
546eb5c144cSBjorn Andersson 			seq_printf(s, " dtest%d", pad->dtest);
547eb5c144cSBjorn Andersson 		if (pad->paired)
548eb5c144cSBjorn Andersson 			seq_puts(s, " paired");
549cfb24f6eSIvan T. Ivanov 	}
550cfb24f6eSIvan T. Ivanov }
551cfb24f6eSIvan T. Ivanov 
552cfb24f6eSIvan T. Ivanov static const struct pinconf_ops pmic_mpp_pinconf_ops = {
553ba5f94cdSBjorn Andersson 	.is_generic = true,
554cfb24f6eSIvan T. Ivanov 	.pin_config_group_get		= pmic_mpp_config_get,
555cfb24f6eSIvan T. Ivanov 	.pin_config_group_set		= pmic_mpp_config_set,
556cfb24f6eSIvan T. Ivanov 	.pin_config_group_dbg_show	= pmic_mpp_config_dbg_show,
557cfb24f6eSIvan T. Ivanov };
558cfb24f6eSIvan T. Ivanov 
559cfb24f6eSIvan T. Ivanov static int pmic_mpp_direction_input(struct gpio_chip *chip, unsigned pin)
560cfb24f6eSIvan T. Ivanov {
561064761d1SLinus Walleij 	struct pmic_mpp_state *state = gpiochip_get_data(chip);
562cfb24f6eSIvan T. Ivanov 	unsigned long config;
563cfb24f6eSIvan T. Ivanov 
564cfb24f6eSIvan T. Ivanov 	config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1);
565cfb24f6eSIvan T. Ivanov 
566cfb24f6eSIvan T. Ivanov 	return pmic_mpp_config_set(state->ctrl, pin, &config, 1);
567cfb24f6eSIvan T. Ivanov }
568cfb24f6eSIvan T. Ivanov 
569cfb24f6eSIvan T. Ivanov static int pmic_mpp_direction_output(struct gpio_chip *chip,
570cfb24f6eSIvan T. Ivanov 				     unsigned pin, int val)
571cfb24f6eSIvan T. Ivanov {
572064761d1SLinus Walleij 	struct pmic_mpp_state *state = gpiochip_get_data(chip);
573cfb24f6eSIvan T. Ivanov 	unsigned long config;
574cfb24f6eSIvan T. Ivanov 
575cfb24f6eSIvan T. Ivanov 	config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val);
576cfb24f6eSIvan T. Ivanov 
577cfb24f6eSIvan T. Ivanov 	return pmic_mpp_config_set(state->ctrl, pin, &config, 1);
578cfb24f6eSIvan T. Ivanov }
579cfb24f6eSIvan T. Ivanov 
580cfb24f6eSIvan T. Ivanov static int pmic_mpp_get(struct gpio_chip *chip, unsigned pin)
581cfb24f6eSIvan T. Ivanov {
582064761d1SLinus Walleij 	struct pmic_mpp_state *state = gpiochip_get_data(chip);
583cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_pad *pad;
584cfb24f6eSIvan T. Ivanov 	int ret;
585cfb24f6eSIvan T. Ivanov 
586cfb24f6eSIvan T. Ivanov 	pad = state->ctrl->desc->pins[pin].drv_data;
587cfb24f6eSIvan T. Ivanov 
588cfb24f6eSIvan T. Ivanov 	if (pad->input_enabled) {
589cfb24f6eSIvan T. Ivanov 		ret = pmic_mpp_read(state, pad, PMIC_MPP_REG_RT_STS);
590cfb24f6eSIvan T. Ivanov 		if (ret < 0)
591cfb24f6eSIvan T. Ivanov 			return ret;
592cfb24f6eSIvan T. Ivanov 
593cfb24f6eSIvan T. Ivanov 		pad->out_value = ret & PMIC_MPP_REG_RT_STS_VAL_MASK;
594cfb24f6eSIvan T. Ivanov 	}
595cfb24f6eSIvan T. Ivanov 
59659663a4cSLinus Walleij 	return !!pad->out_value;
597cfb24f6eSIvan T. Ivanov }
598cfb24f6eSIvan T. Ivanov 
599cfb24f6eSIvan T. Ivanov static void pmic_mpp_set(struct gpio_chip *chip, unsigned pin, int value)
600cfb24f6eSIvan T. Ivanov {
601064761d1SLinus Walleij 	struct pmic_mpp_state *state = gpiochip_get_data(chip);
602cfb24f6eSIvan T. Ivanov 	unsigned long config;
603cfb24f6eSIvan T. Ivanov 
604cfb24f6eSIvan T. Ivanov 	config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value);
605cfb24f6eSIvan T. Ivanov 
606cfb24f6eSIvan T. Ivanov 	pmic_mpp_config_set(state->ctrl, pin, &config, 1);
607cfb24f6eSIvan T. Ivanov }
608cfb24f6eSIvan T. Ivanov 
609cfb24f6eSIvan T. Ivanov static int pmic_mpp_of_xlate(struct gpio_chip *chip,
610cfb24f6eSIvan T. Ivanov 			     const struct of_phandle_args *gpio_desc,
611cfb24f6eSIvan T. Ivanov 			     u32 *flags)
612cfb24f6eSIvan T. Ivanov {
613cfb24f6eSIvan T. Ivanov 	if (chip->of_gpio_n_cells < 2)
614cfb24f6eSIvan T. Ivanov 		return -EINVAL;
615cfb24f6eSIvan T. Ivanov 
616cfb24f6eSIvan T. Ivanov 	if (flags)
617cfb24f6eSIvan T. Ivanov 		*flags = gpio_desc->args[1];
618cfb24f6eSIvan T. Ivanov 
619cfb24f6eSIvan T. Ivanov 	return gpio_desc->args[0] - PMIC_MPP_PHYSICAL_OFFSET;
620cfb24f6eSIvan T. Ivanov }
621cfb24f6eSIvan T. Ivanov 
622cfb24f6eSIvan T. Ivanov static int pmic_mpp_to_irq(struct gpio_chip *chip, unsigned pin)
623cfb24f6eSIvan T. Ivanov {
624064761d1SLinus Walleij 	struct pmic_mpp_state *state = gpiochip_get_data(chip);
625cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_pad *pad;
626cfb24f6eSIvan T. Ivanov 
627cfb24f6eSIvan T. Ivanov 	pad = state->ctrl->desc->pins[pin].drv_data;
628cfb24f6eSIvan T. Ivanov 
629cfb24f6eSIvan T. Ivanov 	return pad->irq;
630cfb24f6eSIvan T. Ivanov }
631cfb24f6eSIvan T. Ivanov 
632cfb24f6eSIvan T. Ivanov static void pmic_mpp_dbg_show(struct seq_file *s, struct gpio_chip *chip)
633cfb24f6eSIvan T. Ivanov {
634064761d1SLinus Walleij 	struct pmic_mpp_state *state = gpiochip_get_data(chip);
635cfb24f6eSIvan T. Ivanov 	unsigned i;
636cfb24f6eSIvan T. Ivanov 
637cfb24f6eSIvan T. Ivanov 	for (i = 0; i < chip->ngpio; i++) {
638cfb24f6eSIvan T. Ivanov 		pmic_mpp_config_dbg_show(state->ctrl, s, i);
639cfb24f6eSIvan T. Ivanov 		seq_puts(s, "\n");
640cfb24f6eSIvan T. Ivanov 	}
641cfb24f6eSIvan T. Ivanov }
642cfb24f6eSIvan T. Ivanov 
643cfb24f6eSIvan T. Ivanov static const struct gpio_chip pmic_mpp_gpio_template = {
644cfb24f6eSIvan T. Ivanov 	.direction_input	= pmic_mpp_direction_input,
645cfb24f6eSIvan T. Ivanov 	.direction_output	= pmic_mpp_direction_output,
646cfb24f6eSIvan T. Ivanov 	.get			= pmic_mpp_get,
647cfb24f6eSIvan T. Ivanov 	.set			= pmic_mpp_set,
64898c85d58SJonas Gorski 	.request		= gpiochip_generic_request,
64998c85d58SJonas Gorski 	.free			= gpiochip_generic_free,
650cfb24f6eSIvan T. Ivanov 	.of_xlate		= pmic_mpp_of_xlate,
651cfb24f6eSIvan T. Ivanov 	.to_irq			= pmic_mpp_to_irq,
652cfb24f6eSIvan T. Ivanov 	.dbg_show		= pmic_mpp_dbg_show,
653cfb24f6eSIvan T. Ivanov };
654cfb24f6eSIvan T. Ivanov 
655cfb24f6eSIvan T. Ivanov static int pmic_mpp_populate(struct pmic_mpp_state *state,
656cfb24f6eSIvan T. Ivanov 			     struct pmic_mpp_pad *pad)
657cfb24f6eSIvan T. Ivanov {
658cfb24f6eSIvan T. Ivanov 	int type, subtype, val, dir;
659eb5c144cSBjorn Andersson 	unsigned int sel;
660cfb24f6eSIvan T. Ivanov 
661cfb24f6eSIvan T. Ivanov 	type = pmic_mpp_read(state, pad, PMIC_MPP_REG_TYPE);
662cfb24f6eSIvan T. Ivanov 	if (type < 0)
663cfb24f6eSIvan T. Ivanov 		return type;
664cfb24f6eSIvan T. Ivanov 
665cfb24f6eSIvan T. Ivanov 	if (type != PMIC_MPP_TYPE) {
666cfb24f6eSIvan T. Ivanov 		dev_err(state->dev, "incorrect block type 0x%x at 0x%x\n",
667cfb24f6eSIvan T. Ivanov 			type, pad->base);
668cfb24f6eSIvan T. Ivanov 		return -ENODEV;
669cfb24f6eSIvan T. Ivanov 	}
670cfb24f6eSIvan T. Ivanov 
671cfb24f6eSIvan T. Ivanov 	subtype = pmic_mpp_read(state, pad, PMIC_MPP_REG_SUBTYPE);
672cfb24f6eSIvan T. Ivanov 	if (subtype < 0)
673cfb24f6eSIvan T. Ivanov 		return subtype;
674cfb24f6eSIvan T. Ivanov 
675cfb24f6eSIvan T. Ivanov 	switch (subtype) {
676cfb24f6eSIvan T. Ivanov 	case PMIC_MPP_SUBTYPE_4CH_NO_ANA_OUT:
677cfb24f6eSIvan T. Ivanov 	case PMIC_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT:
678cfb24f6eSIvan T. Ivanov 	case PMIC_MPP_SUBTYPE_4CH_NO_SINK:
679cfb24f6eSIvan T. Ivanov 	case PMIC_MPP_SUBTYPE_ULT_4CH_NO_SINK:
680cfb24f6eSIvan T. Ivanov 	case PMIC_MPP_SUBTYPE_4CH_FULL_FUNC:
681cfb24f6eSIvan T. Ivanov 		pad->num_sources = 4;
682cfb24f6eSIvan T. Ivanov 		break;
683cfb24f6eSIvan T. Ivanov 	case PMIC_MPP_SUBTYPE_8CH_FULL_FUNC:
684cfb24f6eSIvan T. Ivanov 		pad->num_sources = 8;
685cfb24f6eSIvan T. Ivanov 		break;
686cfb24f6eSIvan T. Ivanov 	default:
687cfb24f6eSIvan T. Ivanov 		dev_err(state->dev, "unknown MPP type 0x%x at 0x%x\n",
688cfb24f6eSIvan T. Ivanov 			subtype, pad->base);
689cfb24f6eSIvan T. Ivanov 		return -ENODEV;
690cfb24f6eSIvan T. Ivanov 	}
691cfb24f6eSIvan T. Ivanov 
692cfb24f6eSIvan T. Ivanov 	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_MODE_CTL);
693cfb24f6eSIvan T. Ivanov 	if (val < 0)
694cfb24f6eSIvan T. Ivanov 		return val;
695cfb24f6eSIvan T. Ivanov 
696cfb24f6eSIvan T. Ivanov 	pad->out_value = val & PMIC_MPP_REG_MODE_VALUE_MASK;
697cfb24f6eSIvan T. Ivanov 
698cfb24f6eSIvan T. Ivanov 	dir = val >> PMIC_MPP_REG_MODE_DIR_SHIFT;
699cfb24f6eSIvan T. Ivanov 	dir &= PMIC_MPP_REG_MODE_DIR_MASK;
700cfb24f6eSIvan T. Ivanov 
701cfb24f6eSIvan T. Ivanov 	switch (dir) {
702eaaf5dd4SBjorn Andersson 	case PMIC_MPP_MODE_DIGITAL_INPUT:
703cfb24f6eSIvan T. Ivanov 		pad->input_enabled = true;
704cfb24f6eSIvan T. Ivanov 		pad->output_enabled = false;
705eb5c144cSBjorn Andersson 		pad->function = PMIC_MPP_DIGITAL;
706cfb24f6eSIvan T. Ivanov 		break;
707eaaf5dd4SBjorn Andersson 	case PMIC_MPP_MODE_DIGITAL_OUTPUT:
708cfb24f6eSIvan T. Ivanov 		pad->input_enabled = false;
709cfb24f6eSIvan T. Ivanov 		pad->output_enabled = true;
710eb5c144cSBjorn Andersson 		pad->function = PMIC_MPP_DIGITAL;
711cfb24f6eSIvan T. Ivanov 		break;
712eaaf5dd4SBjorn Andersson 	case PMIC_MPP_MODE_DIGITAL_BIDIR:
713cfb24f6eSIvan T. Ivanov 		pad->input_enabled = true;
714cfb24f6eSIvan T. Ivanov 		pad->output_enabled = true;
715eb5c144cSBjorn Andersson 		pad->function = PMIC_MPP_DIGITAL;
716cfb24f6eSIvan T. Ivanov 		break;
717eaaf5dd4SBjorn Andersson 	case PMIC_MPP_MODE_ANALOG_BIDIR:
718cfb24f6eSIvan T. Ivanov 		pad->input_enabled = true;
719cfb24f6eSIvan T. Ivanov 		pad->output_enabled = true;
720eb5c144cSBjorn Andersson 		pad->function = PMIC_MPP_ANALOG;
721cfb24f6eSIvan T. Ivanov 		break;
722eaaf5dd4SBjorn Andersson 	case PMIC_MPP_MODE_ANALOG_INPUT:
723cfb24f6eSIvan T. Ivanov 		pad->input_enabled = true;
724cfb24f6eSIvan T. Ivanov 		pad->output_enabled = false;
725eb5c144cSBjorn Andersson 		pad->function = PMIC_MPP_ANALOG;
726cfb24f6eSIvan T. Ivanov 		break;
727eaaf5dd4SBjorn Andersson 	case PMIC_MPP_MODE_ANALOG_OUTPUT:
728cfb24f6eSIvan T. Ivanov 		pad->input_enabled = false;
729cfb24f6eSIvan T. Ivanov 		pad->output_enabled = true;
730eb5c144cSBjorn Andersson 		pad->function = PMIC_MPP_ANALOG;
7310e948042SBjorn Andersson 		break;
7320e948042SBjorn Andersson 	case PMIC_MPP_MODE_CURRENT_SINK:
7330e948042SBjorn Andersson 		pad->input_enabled = false;
7340e948042SBjorn Andersson 		pad->output_enabled = true;
735eb5c144cSBjorn Andersson 		pad->function = PMIC_MPP_SINK;
736cfb24f6eSIvan T. Ivanov 		break;
737cfb24f6eSIvan T. Ivanov 	default:
738cfb24f6eSIvan T. Ivanov 		dev_err(state->dev, "unknown MPP direction\n");
739cfb24f6eSIvan T. Ivanov 		return -ENODEV;
740cfb24f6eSIvan T. Ivanov 	}
741cfb24f6eSIvan T. Ivanov 
742eb5c144cSBjorn Andersson 	sel = val >> PMIC_MPP_REG_MODE_FUNCTION_SHIFT;
743eb5c144cSBjorn Andersson 	sel &= PMIC_MPP_REG_MODE_FUNCTION_MASK;
744eb5c144cSBjorn Andersson 
745eb5c144cSBjorn Andersson 	if (sel >= PMIC_MPP_SELECTOR_DTEST_FIRST)
746eb5c144cSBjorn Andersson 		pad->dtest = sel + 1;
747eb5c144cSBjorn Andersson 	else if (sel == PMIC_MPP_SELECTOR_PAIRED)
748eb5c144cSBjorn Andersson 		pad->paired = true;
749cfb24f6eSIvan T. Ivanov 
750cfb24f6eSIvan T. Ivanov 	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_DIG_VIN_CTL);
751cfb24f6eSIvan T. Ivanov 	if (val < 0)
752cfb24f6eSIvan T. Ivanov 		return val;
753cfb24f6eSIvan T. Ivanov 
754cfb24f6eSIvan T. Ivanov 	pad->power_source = val >> PMIC_MPP_REG_VIN_SHIFT;
755cfb24f6eSIvan T. Ivanov 	pad->power_source &= PMIC_MPP_REG_VIN_MASK;
756cfb24f6eSIvan T. Ivanov 
757ae6d54fdSStephen Boyd 	if (subtype != PMIC_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT &&
758ae6d54fdSStephen Boyd 	    subtype != PMIC_MPP_SUBTYPE_ULT_4CH_NO_SINK) {
759cfb24f6eSIvan T. Ivanov 		val = pmic_mpp_read(state, pad, PMIC_MPP_REG_DIG_PULL_CTL);
760cfb24f6eSIvan T. Ivanov 		if (val < 0)
761cfb24f6eSIvan T. Ivanov 			return val;
762cfb24f6eSIvan T. Ivanov 
763cfb24f6eSIvan T. Ivanov 		pad->pullup = val >> PMIC_MPP_REG_PULL_SHIFT;
764cfb24f6eSIvan T. Ivanov 		pad->pullup &= PMIC_MPP_REG_PULL_MASK;
765ae6d54fdSStephen Boyd 		pad->has_pullup = true;
766ae6d54fdSStephen Boyd 	}
767cfb24f6eSIvan T. Ivanov 
768cfb24f6eSIvan T. Ivanov 	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_AIN_CTL);
769cfb24f6eSIvan T. Ivanov 	if (val < 0)
770cfb24f6eSIvan T. Ivanov 		return val;
771cfb24f6eSIvan T. Ivanov 
772cfb24f6eSIvan T. Ivanov 	pad->amux_input = val >> PMIC_MPP_REG_AIN_ROUTE_SHIFT;
773cfb24f6eSIvan T. Ivanov 	pad->amux_input &= PMIC_MPP_REG_AIN_ROUTE_MASK;
774cfb24f6eSIvan T. Ivanov 
7750e948042SBjorn Andersson 	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_SINK_CTL);
7760e948042SBjorn Andersson 	if (val < 0)
7770e948042SBjorn Andersson 		return val;
7780e948042SBjorn Andersson 
7790e948042SBjorn Andersson 	pad->drive_strength = val;
7800e948042SBjorn Andersson 
781099f3e4aSBjorn Andersson 	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_AOUT_CTL);
782099f3e4aSBjorn Andersson 	if (val < 0)
783099f3e4aSBjorn Andersson 		return val;
784099f3e4aSBjorn Andersson 
785099f3e4aSBjorn Andersson 	pad->aout_level = val;
786099f3e4aSBjorn Andersson 
7877682b374SBjorn Andersson 	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_EN_CTL);
7887682b374SBjorn Andersson 	if (val < 0)
7897682b374SBjorn Andersson 		return val;
7907682b374SBjorn Andersson 
7917682b374SBjorn Andersson 	pad->is_enabled = !!val;
7927682b374SBjorn Andersson 
793cfb24f6eSIvan T. Ivanov 	return 0;
794cfb24f6eSIvan T. Ivanov }
795cfb24f6eSIvan T. Ivanov 
796cfb24f6eSIvan T. Ivanov static int pmic_mpp_probe(struct platform_device *pdev)
797cfb24f6eSIvan T. Ivanov {
798cfb24f6eSIvan T. Ivanov 	struct device *dev = &pdev->dev;
799cfb24f6eSIvan T. Ivanov 	struct pinctrl_pin_desc *pindesc;
800cfb24f6eSIvan T. Ivanov 	struct pinctrl_desc *pctrldesc;
801cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_pad *pad, *pads;
802cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_state *state;
803cfb24f6eSIvan T. Ivanov 	int ret, npins, i;
804ab4256cfSStephen Boyd 	u32 reg;
805cfb24f6eSIvan T. Ivanov 
806ab4256cfSStephen Boyd 	ret = of_property_read_u32(dev->of_node, "reg", &reg);
807cfb24f6eSIvan T. Ivanov 	if (ret < 0) {
808ab4256cfSStephen Boyd 		dev_err(dev, "missing base address");
809cfb24f6eSIvan T. Ivanov 		return ret;
810cfb24f6eSIvan T. Ivanov 	}
811cfb24f6eSIvan T. Ivanov 
812a5ea13f0SStephen Boyd 	npins = platform_irq_count(pdev);
813cfb24f6eSIvan T. Ivanov 	if (!npins)
814cfb24f6eSIvan T. Ivanov 		return -EINVAL;
815a5ea13f0SStephen Boyd 	if (npins < 0)
816a5ea13f0SStephen Boyd 		return npins;
817cfb24f6eSIvan T. Ivanov 
818cfb24f6eSIvan T. Ivanov 	BUG_ON(npins > ARRAY_SIZE(pmic_mpp_groups));
819cfb24f6eSIvan T. Ivanov 
820cfb24f6eSIvan T. Ivanov 	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
821cfb24f6eSIvan T. Ivanov 	if (!state)
822cfb24f6eSIvan T. Ivanov 		return -ENOMEM;
823cfb24f6eSIvan T. Ivanov 
824cfb24f6eSIvan T. Ivanov 	platform_set_drvdata(pdev, state);
825cfb24f6eSIvan T. Ivanov 
826cfb24f6eSIvan T. Ivanov 	state->dev = &pdev->dev;
827cfb24f6eSIvan T. Ivanov 	state->map = dev_get_regmap(dev->parent, NULL);
828cfb24f6eSIvan T. Ivanov 
829cfb24f6eSIvan T. Ivanov 	pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
830cfb24f6eSIvan T. Ivanov 	if (!pindesc)
831cfb24f6eSIvan T. Ivanov 		return -ENOMEM;
832cfb24f6eSIvan T. Ivanov 
833cfb24f6eSIvan T. Ivanov 	pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
834cfb24f6eSIvan T. Ivanov 	if (!pads)
835cfb24f6eSIvan T. Ivanov 		return -ENOMEM;
836cfb24f6eSIvan T. Ivanov 
837cfb24f6eSIvan T. Ivanov 	pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL);
838cfb24f6eSIvan T. Ivanov 	if (!pctrldesc)
839cfb24f6eSIvan T. Ivanov 		return -ENOMEM;
840cfb24f6eSIvan T. Ivanov 
841cfb24f6eSIvan T. Ivanov 	pctrldesc->pctlops = &pmic_mpp_pinctrl_ops;
842cfb24f6eSIvan T. Ivanov 	pctrldesc->pmxops = &pmic_mpp_pinmux_ops;
843cfb24f6eSIvan T. Ivanov 	pctrldesc->confops = &pmic_mpp_pinconf_ops;
844cfb24f6eSIvan T. Ivanov 	pctrldesc->owner = THIS_MODULE;
845cfb24f6eSIvan T. Ivanov 	pctrldesc->name = dev_name(dev);
846cfb24f6eSIvan T. Ivanov 	pctrldesc->pins = pindesc;
847cfb24f6eSIvan T. Ivanov 	pctrldesc->npins = npins;
848cfb24f6eSIvan T. Ivanov 
849ba5f94cdSBjorn Andersson 	pctrldesc->num_custom_params = ARRAY_SIZE(pmic_mpp_bindings);
850ba5f94cdSBjorn Andersson 	pctrldesc->custom_params = pmic_mpp_bindings;
851ba5f94cdSBjorn Andersson #ifdef CONFIG_DEBUG_FS
852ba5f94cdSBjorn Andersson 	pctrldesc->custom_conf_items = pmic_conf_items;
853ba5f94cdSBjorn Andersson #endif
854ba5f94cdSBjorn Andersson 
855cfb24f6eSIvan T. Ivanov 	for (i = 0; i < npins; i++, pindesc++) {
856cfb24f6eSIvan T. Ivanov 		pad = &pads[i];
857cfb24f6eSIvan T. Ivanov 		pindesc->drv_data = pad;
858cfb24f6eSIvan T. Ivanov 		pindesc->number = i;
859cfb24f6eSIvan T. Ivanov 		pindesc->name = pmic_mpp_groups[i];
860cfb24f6eSIvan T. Ivanov 
861cfb24f6eSIvan T. Ivanov 		pad->irq = platform_get_irq(pdev, i);
862cfb24f6eSIvan T. Ivanov 		if (pad->irq < 0)
863cfb24f6eSIvan T. Ivanov 			return pad->irq;
864cfb24f6eSIvan T. Ivanov 
865ab4256cfSStephen Boyd 		pad->base = reg + i * PMIC_MPP_ADDRESS_RANGE;
866cfb24f6eSIvan T. Ivanov 
867cfb24f6eSIvan T. Ivanov 		ret = pmic_mpp_populate(state, pad);
868cfb24f6eSIvan T. Ivanov 		if (ret < 0)
869cfb24f6eSIvan T. Ivanov 			return ret;
870cfb24f6eSIvan T. Ivanov 	}
871cfb24f6eSIvan T. Ivanov 
872cfb24f6eSIvan T. Ivanov 	state->chip = pmic_mpp_gpio_template;
87358383c78SLinus Walleij 	state->chip.parent = dev;
874cfb24f6eSIvan T. Ivanov 	state->chip.base = -1;
875cfb24f6eSIvan T. Ivanov 	state->chip.ngpio = npins;
876cfb24f6eSIvan T. Ivanov 	state->chip.label = dev_name(dev);
877cfb24f6eSIvan T. Ivanov 	state->chip.of_gpio_n_cells = 2;
878cfb24f6eSIvan T. Ivanov 	state->chip.can_sleep = false;
879cfb24f6eSIvan T. Ivanov 
880cfb24f6eSIvan T. Ivanov 	state->ctrl = pinctrl_register(pctrldesc, dev, state);
881323de9efSMasahiro Yamada 	if (IS_ERR(state->ctrl))
882323de9efSMasahiro Yamada 		return PTR_ERR(state->ctrl);
883cfb24f6eSIvan T. Ivanov 
884064761d1SLinus Walleij 	ret = gpiochip_add_data(&state->chip, state);
885cfb24f6eSIvan T. Ivanov 	if (ret) {
886cfb24f6eSIvan T. Ivanov 		dev_err(state->dev, "can't add gpio chip\n");
887cfb24f6eSIvan T. Ivanov 		goto err_chip;
888cfb24f6eSIvan T. Ivanov 	}
889cfb24f6eSIvan T. Ivanov 
890cfb24f6eSIvan T. Ivanov 	ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, npins);
891cfb24f6eSIvan T. Ivanov 	if (ret) {
892cfb24f6eSIvan T. Ivanov 		dev_err(dev, "failed to add pin range\n");
893cfb24f6eSIvan T. Ivanov 		goto err_range;
894cfb24f6eSIvan T. Ivanov 	}
895cfb24f6eSIvan T. Ivanov 
896cfb24f6eSIvan T. Ivanov 	return 0;
897cfb24f6eSIvan T. Ivanov 
898cfb24f6eSIvan T. Ivanov err_range:
899cfb24f6eSIvan T. Ivanov 	gpiochip_remove(&state->chip);
900cfb24f6eSIvan T. Ivanov err_chip:
901cfb24f6eSIvan T. Ivanov 	pinctrl_unregister(state->ctrl);
902cfb24f6eSIvan T. Ivanov 	return ret;
903cfb24f6eSIvan T. Ivanov }
904cfb24f6eSIvan T. Ivanov 
905cfb24f6eSIvan T. Ivanov static int pmic_mpp_remove(struct platform_device *pdev)
906cfb24f6eSIvan T. Ivanov {
907cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_state *state = platform_get_drvdata(pdev);
908cfb24f6eSIvan T. Ivanov 
909cfb24f6eSIvan T. Ivanov 	gpiochip_remove(&state->chip);
910cfb24f6eSIvan T. Ivanov 	pinctrl_unregister(state->ctrl);
911cfb24f6eSIvan T. Ivanov 	return 0;
912cfb24f6eSIvan T. Ivanov }
913cfb24f6eSIvan T. Ivanov 
914cfb24f6eSIvan T. Ivanov static const struct of_device_id pmic_mpp_of_match[] = {
915cfb24f6eSIvan T. Ivanov 	{ .compatible = "qcom,pm8841-mpp" },	/* 4 MPP's */
9167414b099SIvan T. Ivanov 	{ .compatible = "qcom,pm8916-mpp" },	/* 4 MPP's */
917cfb24f6eSIvan T. Ivanov 	{ .compatible = "qcom,pm8941-mpp" },	/* 8 MPP's */
918eb9610f3SStephen Boyd 	{ .compatible = "qcom,pm8994-mpp" },	/* 8 MPP's */
919cfb24f6eSIvan T. Ivanov 	{ .compatible = "qcom,pma8084-mpp" },	/* 8 MPP's */
920cfb24f6eSIvan T. Ivanov 	{ },
921cfb24f6eSIvan T. Ivanov };
922cfb24f6eSIvan T. Ivanov 
923cfb24f6eSIvan T. Ivanov MODULE_DEVICE_TABLE(of, pmic_mpp_of_match);
924cfb24f6eSIvan T. Ivanov 
925cfb24f6eSIvan T. Ivanov static struct platform_driver pmic_mpp_driver = {
926cfb24f6eSIvan T. Ivanov 	.driver = {
927cfb24f6eSIvan T. Ivanov 		   .name = "qcom-spmi-mpp",
928cfb24f6eSIvan T. Ivanov 		   .of_match_table = pmic_mpp_of_match,
929cfb24f6eSIvan T. Ivanov 	},
930cfb24f6eSIvan T. Ivanov 	.probe	= pmic_mpp_probe,
931cfb24f6eSIvan T. Ivanov 	.remove = pmic_mpp_remove,
932cfb24f6eSIvan T. Ivanov };
933cfb24f6eSIvan T. Ivanov 
934cfb24f6eSIvan T. Ivanov module_platform_driver(pmic_mpp_driver);
935cfb24f6eSIvan T. Ivanov 
936cfb24f6eSIvan T. Ivanov MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
937cfb24f6eSIvan T. Ivanov MODULE_DESCRIPTION("Qualcomm SPMI PMIC MPP pin control driver");
938cfb24f6eSIvan T. Ivanov MODULE_ALIAS("platform:qcom-spmi-mpp");
939cfb24f6eSIvan T. Ivanov MODULE_LICENSE("GPL v2");
940