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>
17cfb24f6eSIvan T. Ivanov #include <linux/pinctrl/pinconf-generic.h>
18cfb24f6eSIvan T. Ivanov #include <linux/pinctrl/pinconf.h>
19cfb24f6eSIvan T. Ivanov #include <linux/pinctrl/pinmux.h>
20cfb24f6eSIvan T. Ivanov #include <linux/platform_device.h>
21cfb24f6eSIvan T. Ivanov #include <linux/regmap.h>
22cfb24f6eSIvan T. Ivanov #include <linux/slab.h>
23cfb24f6eSIvan T. Ivanov #include <linux/types.h>
24cfb24f6eSIvan T. Ivanov 
25cfb24f6eSIvan T. Ivanov #include <dt-bindings/pinctrl/qcom,pmic-mpp.h>
26cfb24f6eSIvan T. Ivanov 
27cfb24f6eSIvan T. Ivanov #include "../core.h"
28cfb24f6eSIvan T. Ivanov #include "../pinctrl-utils.h"
29cfb24f6eSIvan T. Ivanov 
30cfb24f6eSIvan T. Ivanov #define PMIC_MPP_ADDRESS_RANGE			0x100
31cfb24f6eSIvan T. Ivanov 
32cfb24f6eSIvan T. Ivanov /*
33cfb24f6eSIvan T. Ivanov  * Pull Up Values - it indicates whether a pull-up should be
34cfb24f6eSIvan T. Ivanov  * applied for bidirectional mode only. The hardware ignores the
35cfb24f6eSIvan T. Ivanov  * configuration when operating in other modes.
36cfb24f6eSIvan T. Ivanov  */
37cfb24f6eSIvan T. Ivanov #define PMIC_MPP_PULL_UP_0P6KOHM		0
38cfb24f6eSIvan T. Ivanov #define PMIC_MPP_PULL_UP_10KOHM			1
39cfb24f6eSIvan T. Ivanov #define PMIC_MPP_PULL_UP_30KOHM			2
40cfb24f6eSIvan T. Ivanov #define PMIC_MPP_PULL_UP_OPEN			3
41cfb24f6eSIvan T. Ivanov 
42cfb24f6eSIvan T. Ivanov /* type registers base address bases */
43cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_TYPE			0x4
44cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_SUBTYPE			0x5
45cfb24f6eSIvan T. Ivanov 
46cfb24f6eSIvan T. Ivanov /* mpp peripheral type and subtype values */
47cfb24f6eSIvan T. Ivanov #define PMIC_MPP_TYPE				0x11
48cfb24f6eSIvan T. Ivanov #define PMIC_MPP_SUBTYPE_4CH_NO_ANA_OUT		0x3
49cfb24f6eSIvan T. Ivanov #define PMIC_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT	0x4
50cfb24f6eSIvan T. Ivanov #define PMIC_MPP_SUBTYPE_4CH_NO_SINK		0x5
51cfb24f6eSIvan T. Ivanov #define PMIC_MPP_SUBTYPE_ULT_4CH_NO_SINK	0x6
52cfb24f6eSIvan T. Ivanov #define PMIC_MPP_SUBTYPE_4CH_FULL_FUNC		0x7
53cfb24f6eSIvan T. Ivanov #define PMIC_MPP_SUBTYPE_8CH_FULL_FUNC		0xf
54cfb24f6eSIvan T. Ivanov 
55cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_RT_STS			0x10
56cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_RT_STS_VAL_MASK		0x1
57cfb24f6eSIvan T. Ivanov 
58cfb24f6eSIvan T. Ivanov /* control register base address bases */
59cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_MODE_CTL			0x40
60cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_DIG_VIN_CTL		0x41
61cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_DIG_PULL_CTL		0x42
62cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_DIG_IN_CTL			0x43
63cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_EN_CTL			0x46
64099f3e4aSBjorn Andersson #define PMIC_MPP_REG_AOUT_CTL			0x48
65cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_AIN_CTL			0x4a
660e948042SBjorn Andersson #define PMIC_MPP_REG_SINK_CTL			0x4c
67cfb24f6eSIvan T. Ivanov 
68cfb24f6eSIvan T. Ivanov /* PMIC_MPP_REG_MODE_CTL */
69cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_MODE_VALUE_MASK		0x1
70cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_MODE_FUNCTION_SHIFT	1
71cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_MODE_FUNCTION_MASK		0x7
72cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_MODE_DIR_SHIFT		4
73cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_MODE_DIR_MASK		0x7
74cfb24f6eSIvan T. Ivanov 
75cfb24f6eSIvan T. Ivanov /* PMIC_MPP_REG_DIG_VIN_CTL */
76cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_VIN_SHIFT			0
77cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_VIN_MASK			0x7
78cfb24f6eSIvan T. Ivanov 
79cfb24f6eSIvan T. Ivanov /* PMIC_MPP_REG_DIG_PULL_CTL */
80cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_PULL_SHIFT			0
81cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_PULL_MASK			0x7
82cfb24f6eSIvan T. Ivanov 
83cfb24f6eSIvan T. Ivanov /* PMIC_MPP_REG_EN_CTL */
84cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_MASTER_EN_SHIFT		7
85cfb24f6eSIvan T. Ivanov 
86cfb24f6eSIvan T. Ivanov /* PMIC_MPP_REG_AIN_CTL */
87cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_AIN_ROUTE_SHIFT		0
88cfb24f6eSIvan T. Ivanov #define PMIC_MPP_REG_AIN_ROUTE_MASK		0x7
89cfb24f6eSIvan T. Ivanov 
90eaaf5dd4SBjorn Andersson #define PMIC_MPP_MODE_DIGITAL_INPUT		0
91eaaf5dd4SBjorn Andersson #define PMIC_MPP_MODE_DIGITAL_OUTPUT		1
92eaaf5dd4SBjorn Andersson #define PMIC_MPP_MODE_DIGITAL_BIDIR		2
93eaaf5dd4SBjorn Andersson #define PMIC_MPP_MODE_ANALOG_BIDIR		3
94eaaf5dd4SBjorn Andersson #define PMIC_MPP_MODE_ANALOG_INPUT		4
95eaaf5dd4SBjorn Andersson #define PMIC_MPP_MODE_ANALOG_OUTPUT		5
96eaaf5dd4SBjorn Andersson #define PMIC_MPP_MODE_CURRENT_SINK		6
97eaaf5dd4SBjorn Andersson 
98eb5c144cSBjorn Andersson #define PMIC_MPP_SELECTOR_NORMAL		0
99eb5c144cSBjorn Andersson #define PMIC_MPP_SELECTOR_PAIRED		1
100eb5c144cSBjorn Andersson #define PMIC_MPP_SELECTOR_DTEST_FIRST		4
101eb5c144cSBjorn Andersson 
102cfb24f6eSIvan T. Ivanov #define PMIC_MPP_PHYSICAL_OFFSET		1
103cfb24f6eSIvan T. Ivanov 
104cfb24f6eSIvan T. Ivanov /* Qualcomm specific pin configurations */
105cfb24f6eSIvan T. Ivanov #define PMIC_MPP_CONF_AMUX_ROUTE		(PIN_CONFIG_END + 1)
106eb5c144cSBjorn Andersson #define PMIC_MPP_CONF_ANALOG_LEVEL		(PIN_CONFIG_END + 2)
107eb5c144cSBjorn Andersson #define PMIC_MPP_CONF_DTEST_SELECTOR		(PIN_CONFIG_END + 3)
108eb5c144cSBjorn Andersson #define PMIC_MPP_CONF_PAIRED			(PIN_CONFIG_END + 4)
109cfb24f6eSIvan T. Ivanov 
110cfb24f6eSIvan T. Ivanov /**
111cfb24f6eSIvan T. Ivanov  * struct pmic_mpp_pad - keep current MPP settings
112cfb24f6eSIvan T. Ivanov  * @base: Address base in SPMI device.
113cfb24f6eSIvan T. Ivanov  * @irq: IRQ number which this MPP generate.
114cfb24f6eSIvan T. Ivanov  * @is_enabled: Set to false when MPP should be put in high Z state.
115cfb24f6eSIvan T. Ivanov  * @out_value: Cached pin output value.
116cfb24f6eSIvan T. Ivanov  * @output_enabled: Set to true if MPP output logic is enabled.
117cfb24f6eSIvan T. Ivanov  * @input_enabled: Set to true if MPP input buffer logic is enabled.
118eb5c144cSBjorn Andersson  * @paired: Pin operates in paired mode
119cfb24f6eSIvan T. Ivanov  * @num_sources: Number of power-sources supported by this MPP.
120cfb24f6eSIvan T. Ivanov  * @power_source: Current power-source used.
121cfb24f6eSIvan T. Ivanov  * @amux_input: Set the source for analog input.
122099f3e4aSBjorn Andersson  * @aout_level: Analog output level
123cfb24f6eSIvan T. Ivanov  * @pullup: Pullup resistor value. Valid in Bidirectional mode only.
124cfb24f6eSIvan T. Ivanov  * @function: See pmic_mpp_functions[].
1250e948042SBjorn Andersson  * @drive_strength: Amount of current in sink mode
126eb5c144cSBjorn Andersson  * @dtest: DTEST route selector
127cfb24f6eSIvan T. Ivanov  */
128cfb24f6eSIvan T. Ivanov struct pmic_mpp_pad {
129cfb24f6eSIvan T. Ivanov 	u16		base;
130cfb24f6eSIvan T. Ivanov 	int		irq;
131cfb24f6eSIvan T. Ivanov 	bool		is_enabled;
132cfb24f6eSIvan T. Ivanov 	bool		out_value;
133cfb24f6eSIvan T. Ivanov 	bool		output_enabled;
134cfb24f6eSIvan T. Ivanov 	bool		input_enabled;
135eb5c144cSBjorn Andersson 	bool		paired;
136cfb24f6eSIvan T. Ivanov 	unsigned int	num_sources;
137cfb24f6eSIvan T. Ivanov 	unsigned int	power_source;
138cfb24f6eSIvan T. Ivanov 	unsigned int	amux_input;
139099f3e4aSBjorn Andersson 	unsigned int	aout_level;
140cfb24f6eSIvan T. Ivanov 	unsigned int	pullup;
141cfb24f6eSIvan T. Ivanov 	unsigned int	function;
1420e948042SBjorn Andersson 	unsigned int	drive_strength;
143eb5c144cSBjorn Andersson 	unsigned int	dtest;
144cfb24f6eSIvan T. Ivanov };
145cfb24f6eSIvan T. Ivanov 
146cfb24f6eSIvan T. Ivanov struct pmic_mpp_state {
147cfb24f6eSIvan T. Ivanov 	struct device	*dev;
148cfb24f6eSIvan T. Ivanov 	struct regmap	*map;
149cfb24f6eSIvan T. Ivanov 	struct pinctrl_dev *ctrl;
150cfb24f6eSIvan T. Ivanov 	struct gpio_chip chip;
151cfb24f6eSIvan T. Ivanov };
152cfb24f6eSIvan T. Ivanov 
153ba5f94cdSBjorn Andersson static const struct pinconf_generic_params pmic_mpp_bindings[] = {
154ba5f94cdSBjorn Andersson 	{"qcom,amux-route",	PMIC_MPP_CONF_AMUX_ROUTE,	0},
155099f3e4aSBjorn Andersson 	{"qcom,analog-level",	PMIC_MPP_CONF_ANALOG_LEVEL,	0},
156eb5c144cSBjorn Andersson 	{"qcom,dtest",		PMIC_MPP_CONF_DTEST_SELECTOR,	0},
157eb5c144cSBjorn Andersson 	{"qcom,paired",		PMIC_MPP_CONF_PAIRED,		0},
158cfb24f6eSIvan T. Ivanov };
159cfb24f6eSIvan T. Ivanov 
160ba5f94cdSBjorn Andersson #ifdef CONFIG_DEBUG_FS
161ba5f94cdSBjorn Andersson static const struct pin_config_item pmic_conf_items[] = {
162ba5f94cdSBjorn Andersson 	PCONFDUMP(PMIC_MPP_CONF_AMUX_ROUTE, "analog mux", NULL, true),
163099f3e4aSBjorn Andersson 	PCONFDUMP(PMIC_MPP_CONF_ANALOG_LEVEL, "analog level", NULL, true),
164eb5c144cSBjorn Andersson 	PCONFDUMP(PMIC_MPP_CONF_DTEST_SELECTOR, "dtest", NULL, true),
165eb5c144cSBjorn Andersson 	PCONFDUMP(PMIC_MPP_CONF_PAIRED, "paired", NULL, false),
166cfb24f6eSIvan T. Ivanov };
167ba5f94cdSBjorn Andersson #endif
168cfb24f6eSIvan T. Ivanov 
169cfb24f6eSIvan T. Ivanov static const char *const pmic_mpp_groups[] = {
170cfb24f6eSIvan T. Ivanov 	"mpp1", "mpp2", "mpp3", "mpp4", "mpp5", "mpp6", "mpp7", "mpp8",
171cfb24f6eSIvan T. Ivanov };
172cfb24f6eSIvan T. Ivanov 
173eb5c144cSBjorn Andersson #define PMIC_MPP_DIGITAL	0
174eb5c144cSBjorn Andersson #define PMIC_MPP_ANALOG		1
175eb5c144cSBjorn Andersson #define PMIC_MPP_SINK		2
176eb5c144cSBjorn Andersson 
177cfb24f6eSIvan T. Ivanov static const char *const pmic_mpp_functions[] = {
178eb5c144cSBjorn Andersson 	"digital", "analog", "sink"
179cfb24f6eSIvan T. Ivanov };
180cfb24f6eSIvan T. Ivanov 
181cfb24f6eSIvan T. Ivanov static int pmic_mpp_read(struct pmic_mpp_state *state,
182cfb24f6eSIvan T. Ivanov 			 struct pmic_mpp_pad *pad, unsigned int addr)
183cfb24f6eSIvan T. Ivanov {
184cfb24f6eSIvan T. Ivanov 	unsigned int val;
185cfb24f6eSIvan T. Ivanov 	int ret;
186cfb24f6eSIvan T. Ivanov 
187cfb24f6eSIvan T. Ivanov 	ret = regmap_read(state->map, pad->base + addr, &val);
188cfb24f6eSIvan T. Ivanov 	if (ret < 0)
189cfb24f6eSIvan T. Ivanov 		dev_err(state->dev, "read 0x%x failed\n", addr);
190cfb24f6eSIvan T. Ivanov 	else
191cfb24f6eSIvan T. Ivanov 		ret = val;
192cfb24f6eSIvan T. Ivanov 
193cfb24f6eSIvan T. Ivanov 	return ret;
194cfb24f6eSIvan T. Ivanov }
195cfb24f6eSIvan T. Ivanov 
196cfb24f6eSIvan T. Ivanov static int pmic_mpp_write(struct pmic_mpp_state *state,
197cfb24f6eSIvan T. Ivanov 			  struct pmic_mpp_pad *pad, unsigned int addr,
198cfb24f6eSIvan T. Ivanov 			  unsigned int val)
199cfb24f6eSIvan T. Ivanov {
200cfb24f6eSIvan T. Ivanov 	int ret;
201cfb24f6eSIvan T. Ivanov 
202cfb24f6eSIvan T. Ivanov 	ret = regmap_write(state->map, pad->base + addr, val);
203cfb24f6eSIvan T. Ivanov 	if (ret < 0)
204cfb24f6eSIvan T. Ivanov 		dev_err(state->dev, "write 0x%x failed\n", addr);
205cfb24f6eSIvan T. Ivanov 
206cfb24f6eSIvan T. Ivanov 	return ret;
207cfb24f6eSIvan T. Ivanov }
208cfb24f6eSIvan T. Ivanov 
209cfb24f6eSIvan T. Ivanov static int pmic_mpp_get_groups_count(struct pinctrl_dev *pctldev)
210cfb24f6eSIvan T. Ivanov {
211cfb24f6eSIvan T. Ivanov 	/* Every PIN is a group */
212cfb24f6eSIvan T. Ivanov 	return pctldev->desc->npins;
213cfb24f6eSIvan T. Ivanov }
214cfb24f6eSIvan T. Ivanov 
215cfb24f6eSIvan T. Ivanov static const char *pmic_mpp_get_group_name(struct pinctrl_dev *pctldev,
216cfb24f6eSIvan T. Ivanov 					   unsigned pin)
217cfb24f6eSIvan T. Ivanov {
218cfb24f6eSIvan T. Ivanov 	return pctldev->desc->pins[pin].name;
219cfb24f6eSIvan T. Ivanov }
220cfb24f6eSIvan T. Ivanov 
221cfb24f6eSIvan T. Ivanov static int pmic_mpp_get_group_pins(struct pinctrl_dev *pctldev,
222cfb24f6eSIvan T. Ivanov 				   unsigned pin,
223cfb24f6eSIvan T. Ivanov 				   const unsigned **pins, unsigned *num_pins)
224cfb24f6eSIvan T. Ivanov {
225cfb24f6eSIvan T. Ivanov 	*pins = &pctldev->desc->pins[pin].number;
226cfb24f6eSIvan T. Ivanov 	*num_pins = 1;
227cfb24f6eSIvan T. Ivanov 	return 0;
228cfb24f6eSIvan T. Ivanov }
229cfb24f6eSIvan T. Ivanov 
230cfb24f6eSIvan T. Ivanov static const struct pinctrl_ops pmic_mpp_pinctrl_ops = {
231cfb24f6eSIvan T. Ivanov 	.get_groups_count	= pmic_mpp_get_groups_count,
232cfb24f6eSIvan T. Ivanov 	.get_group_name		= pmic_mpp_get_group_name,
233cfb24f6eSIvan T. Ivanov 	.get_group_pins		= pmic_mpp_get_group_pins,
234ba5f94cdSBjorn Andersson 	.dt_node_to_map		= pinconf_generic_dt_node_to_map_group,
235cfb24f6eSIvan T. Ivanov 	.dt_free_map		= pinctrl_utils_dt_free_map,
236cfb24f6eSIvan T. Ivanov };
237cfb24f6eSIvan T. Ivanov 
238cfb24f6eSIvan T. Ivanov static int pmic_mpp_get_functions_count(struct pinctrl_dev *pctldev)
239cfb24f6eSIvan T. Ivanov {
240cfb24f6eSIvan T. Ivanov 	return ARRAY_SIZE(pmic_mpp_functions);
241cfb24f6eSIvan T. Ivanov }
242cfb24f6eSIvan T. Ivanov 
243cfb24f6eSIvan T. Ivanov static const char *pmic_mpp_get_function_name(struct pinctrl_dev *pctldev,
244cfb24f6eSIvan T. Ivanov 					      unsigned function)
245cfb24f6eSIvan T. Ivanov {
246cfb24f6eSIvan T. Ivanov 	return pmic_mpp_functions[function];
247cfb24f6eSIvan T. Ivanov }
248cfb24f6eSIvan T. Ivanov 
249cfb24f6eSIvan T. Ivanov static int pmic_mpp_get_function_groups(struct pinctrl_dev *pctldev,
250cfb24f6eSIvan T. Ivanov 					unsigned function,
251cfb24f6eSIvan T. Ivanov 					const char *const **groups,
252cfb24f6eSIvan T. Ivanov 					unsigned *const num_qgroups)
253cfb24f6eSIvan T. Ivanov {
254cfb24f6eSIvan T. Ivanov 	*groups = pmic_mpp_groups;
255cfb24f6eSIvan T. Ivanov 	*num_qgroups = pctldev->desc->npins;
256cfb24f6eSIvan T. Ivanov 	return 0;
257cfb24f6eSIvan T. Ivanov }
258cfb24f6eSIvan T. Ivanov 
2590e948042SBjorn Andersson static int pmic_mpp_write_mode_ctl(struct pmic_mpp_state *state,
2600e948042SBjorn Andersson 				   struct pmic_mpp_pad *pad)
2610e948042SBjorn Andersson {
262eb5c144cSBjorn Andersson 	unsigned int mode;
263eb5c144cSBjorn Andersson 	unsigned int sel;
2640e948042SBjorn Andersson 	unsigned int val;
265eb5c144cSBjorn Andersson 	unsigned int en;
2660e948042SBjorn Andersson 
267eb5c144cSBjorn Andersson 	switch (pad->function) {
268eb5c144cSBjorn Andersson 	case PMIC_MPP_ANALOG:
269eb5c144cSBjorn Andersson 		if (pad->input_enabled && pad->output_enabled)
270eb5c144cSBjorn Andersson 			mode = PMIC_MPP_MODE_ANALOG_BIDIR;
271eb5c144cSBjorn Andersson 		else if (pad->input_enabled)
272eb5c144cSBjorn Andersson 			mode = PMIC_MPP_MODE_ANALOG_INPUT;
2730e948042SBjorn Andersson 		else
274eb5c144cSBjorn Andersson 			mode = PMIC_MPP_MODE_ANALOG_OUTPUT;
275eb5c144cSBjorn Andersson 		break;
276eb5c144cSBjorn Andersson 	case PMIC_MPP_DIGITAL:
277eb5c144cSBjorn Andersson 		if (pad->input_enabled && pad->output_enabled)
278eb5c144cSBjorn Andersson 			mode = PMIC_MPP_MODE_DIGITAL_BIDIR;
279eb5c144cSBjorn Andersson 		else if (pad->input_enabled)
280eb5c144cSBjorn Andersson 			mode = PMIC_MPP_MODE_DIGITAL_INPUT;
2810e948042SBjorn Andersson 		else
282eb5c144cSBjorn Andersson 			mode = PMIC_MPP_MODE_DIGITAL_OUTPUT;
283eb5c144cSBjorn Andersson 		break;
284eb5c144cSBjorn Andersson 	case PMIC_MPP_SINK:
285eb5c144cSBjorn Andersson 	default:
286eb5c144cSBjorn Andersson 		mode = PMIC_MPP_MODE_CURRENT_SINK;
287eb5c144cSBjorn Andersson 		break;
2880e948042SBjorn Andersson 	}
2890e948042SBjorn Andersson 
290eb5c144cSBjorn Andersson 	if (pad->dtest)
291eb5c144cSBjorn Andersson 		sel = PMIC_MPP_SELECTOR_DTEST_FIRST + pad->dtest - 1;
292eb5c144cSBjorn Andersson 	else if (pad->paired)
293eb5c144cSBjorn Andersson 		sel = PMIC_MPP_SELECTOR_PAIRED;
294eb5c144cSBjorn Andersson 	else
295eb5c144cSBjorn Andersson 		sel = PMIC_MPP_SELECTOR_NORMAL;
296eb5c144cSBjorn Andersson 
297eb5c144cSBjorn Andersson 	en = !!pad->out_value;
298eb5c144cSBjorn Andersson 
299eb5c144cSBjorn Andersson 	val = mode << PMIC_MPP_REG_MODE_DIR_SHIFT |
300eb5c144cSBjorn Andersson 	      sel << PMIC_MPP_REG_MODE_FUNCTION_SHIFT |
301eb5c144cSBjorn Andersson 	      en;
3020e948042SBjorn Andersson 
3030e948042SBjorn Andersson 	return pmic_mpp_write(state, pad, PMIC_MPP_REG_MODE_CTL, val);
3040e948042SBjorn Andersson }
3050e948042SBjorn Andersson 
306cfb24f6eSIvan T. Ivanov static int pmic_mpp_set_mux(struct pinctrl_dev *pctldev, unsigned function,
307cfb24f6eSIvan T. Ivanov 				unsigned pin)
308cfb24f6eSIvan T. Ivanov {
309cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_state *state = pinctrl_dev_get_drvdata(pctldev);
310cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_pad *pad;
311cfb24f6eSIvan T. Ivanov 	unsigned int val;
312cfb24f6eSIvan T. Ivanov 	int ret;
313cfb24f6eSIvan T. Ivanov 
314cfb24f6eSIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
315cfb24f6eSIvan T. Ivanov 
316cfb24f6eSIvan T. Ivanov 	pad->function = function;
317cfb24f6eSIvan T. Ivanov 
3180e948042SBjorn Andersson 	ret = pmic_mpp_write_mode_ctl(state, pad);
319cfb24f6eSIvan T. Ivanov 
320cfb24f6eSIvan T. Ivanov 	val = pad->is_enabled << PMIC_MPP_REG_MASTER_EN_SHIFT;
321cfb24f6eSIvan T. Ivanov 
322cfb24f6eSIvan T. Ivanov 	return pmic_mpp_write(state, pad, PMIC_MPP_REG_EN_CTL, val);
323cfb24f6eSIvan T. Ivanov }
324cfb24f6eSIvan T. Ivanov 
325cfb24f6eSIvan T. Ivanov static const struct pinmux_ops pmic_mpp_pinmux_ops = {
326cfb24f6eSIvan T. Ivanov 	.get_functions_count	= pmic_mpp_get_functions_count,
327cfb24f6eSIvan T. Ivanov 	.get_function_name	= pmic_mpp_get_function_name,
328cfb24f6eSIvan T. Ivanov 	.get_function_groups	= pmic_mpp_get_function_groups,
329cfb24f6eSIvan T. Ivanov 	.set_mux		= pmic_mpp_set_mux,
330cfb24f6eSIvan T. Ivanov };
331cfb24f6eSIvan T. Ivanov 
332cfb24f6eSIvan T. Ivanov static int pmic_mpp_config_get(struct pinctrl_dev *pctldev,
333cfb24f6eSIvan T. Ivanov 			       unsigned int pin, unsigned long *config)
334cfb24f6eSIvan T. Ivanov {
335cfb24f6eSIvan T. Ivanov 	unsigned param = pinconf_to_config_param(*config);
336cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_pad *pad;
337cfb24f6eSIvan T. Ivanov 	unsigned arg = 0;
338cfb24f6eSIvan T. Ivanov 
339cfb24f6eSIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
340cfb24f6eSIvan T. Ivanov 
341cfb24f6eSIvan T. Ivanov 	switch (param) {
342cfb24f6eSIvan T. Ivanov 	case PIN_CONFIG_BIAS_DISABLE:
343cfb24f6eSIvan T. Ivanov 		arg = pad->pullup == PMIC_MPP_PULL_UP_OPEN;
344cfb24f6eSIvan T. Ivanov 		break;
345cfb24f6eSIvan T. Ivanov 	case PIN_CONFIG_BIAS_PULL_UP:
346cfb24f6eSIvan T. Ivanov 		switch (pad->pullup) {
347cfb24f6eSIvan T. Ivanov 		case PMIC_MPP_PULL_UP_OPEN:
348cfb24f6eSIvan T. Ivanov 			arg = 0;
349cfb24f6eSIvan T. Ivanov 			break;
350cfb24f6eSIvan T. Ivanov 		case PMIC_MPP_PULL_UP_0P6KOHM:
351cfb24f6eSIvan T. Ivanov 			arg = 600;
352cfb24f6eSIvan T. Ivanov 			break;
353cfb24f6eSIvan T. Ivanov 		case PMIC_MPP_PULL_UP_10KOHM:
354cfb24f6eSIvan T. Ivanov 			arg = 10000;
355cfb24f6eSIvan T. Ivanov 			break;
356cfb24f6eSIvan T. Ivanov 		case PMIC_MPP_PULL_UP_30KOHM:
357cfb24f6eSIvan T. Ivanov 			arg = 30000;
358cfb24f6eSIvan T. Ivanov 			break;
359cfb24f6eSIvan T. Ivanov 		default:
360cfb24f6eSIvan T. Ivanov 			return -EINVAL;
361cfb24f6eSIvan T. Ivanov 		}
362cfb24f6eSIvan T. Ivanov 		break;
363cfb24f6eSIvan T. Ivanov 	case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
364cfb24f6eSIvan T. Ivanov 		arg = !pad->is_enabled;
365cfb24f6eSIvan T. Ivanov 		break;
366cfb24f6eSIvan T. Ivanov 	case PIN_CONFIG_POWER_SOURCE:
367cfb24f6eSIvan T. Ivanov 		arg = pad->power_source;
368cfb24f6eSIvan T. Ivanov 		break;
369cfb24f6eSIvan T. Ivanov 	case PIN_CONFIG_INPUT_ENABLE:
370cfb24f6eSIvan T. Ivanov 		arg = pad->input_enabled;
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:
382eb5c144cSBjorn Andersson 		arg = pad->paired;
383eb5c144cSBjorn Andersson 		break;
3840e948042SBjorn Andersson 	case PIN_CONFIG_DRIVE_STRENGTH:
3850e948042SBjorn Andersson 		arg = pad->drive_strength;
3860e948042SBjorn Andersson 		break;
387099f3e4aSBjorn Andersson 	case PMIC_MPP_CONF_ANALOG_LEVEL:
388099f3e4aSBjorn Andersson 		arg = pad->aout_level;
389099f3e4aSBjorn Andersson 		break;
390cfb24f6eSIvan T. Ivanov 	default:
391cfb24f6eSIvan T. Ivanov 		return -EINVAL;
392cfb24f6eSIvan T. Ivanov 	}
393cfb24f6eSIvan T. Ivanov 
394cfb24f6eSIvan T. Ivanov 	/* Convert register value to pinconf value */
395cfb24f6eSIvan T. Ivanov 	*config = pinconf_to_config_packed(param, arg);
396cfb24f6eSIvan T. Ivanov 	return 0;
397cfb24f6eSIvan T. Ivanov }
398cfb24f6eSIvan T. Ivanov 
399cfb24f6eSIvan T. Ivanov static int pmic_mpp_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
400cfb24f6eSIvan T. Ivanov 			       unsigned long *configs, unsigned nconfs)
401cfb24f6eSIvan T. Ivanov {
402cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_state *state = pinctrl_dev_get_drvdata(pctldev);
403cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_pad *pad;
404cfb24f6eSIvan T. Ivanov 	unsigned param, arg;
405cfb24f6eSIvan T. Ivanov 	unsigned int val;
406cfb24f6eSIvan T. Ivanov 	int i, ret;
407cfb24f6eSIvan T. Ivanov 
408cfb24f6eSIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
409cfb24f6eSIvan T. Ivanov 
4107682b374SBjorn Andersson 	/* Make it possible to enable the pin, by not setting high impedance */
4117682b374SBjorn Andersson 	pad->is_enabled = true;
4127682b374SBjorn Andersson 
413cfb24f6eSIvan T. Ivanov 	for (i = 0; i < nconfs; i++) {
414cfb24f6eSIvan T. Ivanov 		param = pinconf_to_config_param(configs[i]);
415cfb24f6eSIvan T. Ivanov 		arg = pinconf_to_config_argument(configs[i]);
416cfb24f6eSIvan T. Ivanov 
417cfb24f6eSIvan T. Ivanov 		switch (param) {
418cfb24f6eSIvan T. Ivanov 		case PIN_CONFIG_BIAS_DISABLE:
419cfb24f6eSIvan T. Ivanov 			pad->pullup = PMIC_MPP_PULL_UP_OPEN;
420cfb24f6eSIvan T. Ivanov 			break;
421cfb24f6eSIvan T. Ivanov 		case PIN_CONFIG_BIAS_PULL_UP:
422cfb24f6eSIvan T. Ivanov 			switch (arg) {
423cfb24f6eSIvan T. Ivanov 			case 600:
424cfb24f6eSIvan T. Ivanov 				pad->pullup = PMIC_MPP_PULL_UP_0P6KOHM;
425cfb24f6eSIvan T. Ivanov 				break;
426cfb24f6eSIvan T. Ivanov 			case 10000:
427cfb24f6eSIvan T. Ivanov 				pad->pullup = PMIC_MPP_PULL_UP_10KOHM;
428cfb24f6eSIvan T. Ivanov 				break;
429cfb24f6eSIvan T. Ivanov 			case 30000:
430cfb24f6eSIvan T. Ivanov 				pad->pullup = PMIC_MPP_PULL_UP_30KOHM;
431cfb24f6eSIvan T. Ivanov 				break;
432cfb24f6eSIvan T. Ivanov 			default:
433cfb24f6eSIvan T. Ivanov 				return -EINVAL;
434cfb24f6eSIvan T. Ivanov 			}
435cfb24f6eSIvan T. Ivanov 			break;
436cfb24f6eSIvan T. Ivanov 		case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
437cfb24f6eSIvan T. Ivanov 			pad->is_enabled = false;
438cfb24f6eSIvan T. Ivanov 			break;
439cfb24f6eSIvan T. Ivanov 		case PIN_CONFIG_POWER_SOURCE:
440cfb24f6eSIvan T. Ivanov 			if (arg >= pad->num_sources)
441cfb24f6eSIvan T. Ivanov 				return -EINVAL;
442cfb24f6eSIvan T. Ivanov 			pad->power_source = arg;
443cfb24f6eSIvan T. Ivanov 			break;
444cfb24f6eSIvan T. Ivanov 		case PIN_CONFIG_INPUT_ENABLE:
445cfb24f6eSIvan T. Ivanov 			pad->input_enabled = arg ? true : false;
446cfb24f6eSIvan T. Ivanov 			break;
447cfb24f6eSIvan T. Ivanov 		case PIN_CONFIG_OUTPUT:
448cfb24f6eSIvan T. Ivanov 			pad->output_enabled = true;
449cfb24f6eSIvan T. Ivanov 			pad->out_value = arg;
450cfb24f6eSIvan T. Ivanov 			break;
451eb5c144cSBjorn Andersson 		case PMIC_MPP_CONF_DTEST_SELECTOR:
452eb5c144cSBjorn Andersson 			pad->dtest = arg;
453eb5c144cSBjorn Andersson 			break;
4540e948042SBjorn Andersson 		case PIN_CONFIG_DRIVE_STRENGTH:
4550e948042SBjorn Andersson 			arg = pad->drive_strength;
4560e948042SBjorn Andersson 			break;
457cfb24f6eSIvan T. Ivanov 		case PMIC_MPP_CONF_AMUX_ROUTE:
458cfb24f6eSIvan T. Ivanov 			if (arg >= PMIC_MPP_AMUX_ROUTE_ABUS4)
459cfb24f6eSIvan T. Ivanov 				return -EINVAL;
460cfb24f6eSIvan T. Ivanov 			pad->amux_input = arg;
461cfb24f6eSIvan T. Ivanov 			break;
462099f3e4aSBjorn Andersson 		case PMIC_MPP_CONF_ANALOG_LEVEL:
463099f3e4aSBjorn Andersson 			pad->aout_level = arg;
464099f3e4aSBjorn Andersson 			break;
465eb5c144cSBjorn Andersson 		case PMIC_MPP_CONF_PAIRED:
466eb5c144cSBjorn Andersson 			pad->paired = !!arg;
467cfb24f6eSIvan T. Ivanov 			break;
468cfb24f6eSIvan T. Ivanov 		default:
469cfb24f6eSIvan T. Ivanov 			return -EINVAL;
470cfb24f6eSIvan T. Ivanov 		}
471cfb24f6eSIvan T. Ivanov 	}
472cfb24f6eSIvan T. Ivanov 
473cfb24f6eSIvan T. Ivanov 	val = pad->power_source << PMIC_MPP_REG_VIN_SHIFT;
474cfb24f6eSIvan T. Ivanov 
475cfb24f6eSIvan T. Ivanov 	ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_DIG_VIN_CTL, val);
476cfb24f6eSIvan T. Ivanov 	if (ret < 0)
477cfb24f6eSIvan T. Ivanov 		return ret;
478cfb24f6eSIvan T. Ivanov 
479cfb24f6eSIvan T. Ivanov 	val = pad->pullup << PMIC_MPP_REG_PULL_SHIFT;
480cfb24f6eSIvan T. Ivanov 
481cfb24f6eSIvan T. Ivanov 	ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_DIG_PULL_CTL, val);
482cfb24f6eSIvan T. Ivanov 	if (ret < 0)
483cfb24f6eSIvan T. Ivanov 		return ret;
484cfb24f6eSIvan T. Ivanov 
485cfb24f6eSIvan T. Ivanov 	val = pad->amux_input & PMIC_MPP_REG_AIN_ROUTE_MASK;
486cfb24f6eSIvan T. Ivanov 
487cfb24f6eSIvan T. Ivanov 	ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_AIN_CTL, val);
488cfb24f6eSIvan T. Ivanov 	if (ret < 0)
489cfb24f6eSIvan T. Ivanov 		return ret;
490cfb24f6eSIvan T. Ivanov 
491099f3e4aSBjorn Andersson 	ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_AOUT_CTL, pad->aout_level);
492099f3e4aSBjorn Andersson 	if (ret < 0)
493099f3e4aSBjorn Andersson 		return ret;
494099f3e4aSBjorn Andersson 
4950e948042SBjorn Andersson 	ret = pmic_mpp_write_mode_ctl(state, pad);
4967682b374SBjorn Andersson 	if (ret < 0)
4977682b374SBjorn Andersson 		return ret;
4987682b374SBjorn Andersson 
4997682b374SBjorn Andersson 	val = pad->is_enabled << PMIC_MPP_REG_MASTER_EN_SHIFT;
5007682b374SBjorn Andersson 
5017682b374SBjorn Andersson 	return pmic_mpp_write(state, pad, PMIC_MPP_REG_EN_CTL, val);
502cfb24f6eSIvan T. Ivanov }
503cfb24f6eSIvan T. Ivanov 
504cfb24f6eSIvan T. Ivanov static void pmic_mpp_config_dbg_show(struct pinctrl_dev *pctldev,
505cfb24f6eSIvan T. Ivanov 				     struct seq_file *s, unsigned pin)
506cfb24f6eSIvan T. Ivanov {
507cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_state *state = pinctrl_dev_get_drvdata(pctldev);
508cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_pad *pad;
5097682b374SBjorn Andersson 	int ret;
510cfb24f6eSIvan T. Ivanov 
511cfb24f6eSIvan T. Ivanov 	static const char *const biases[] = {
512cfb24f6eSIvan T. Ivanov 		"0.6kOhm", "10kOhm", "30kOhm", "Disabled"
513cfb24f6eSIvan T. Ivanov 	};
514cfb24f6eSIvan T. Ivanov 
515cfb24f6eSIvan T. Ivanov 	pad = pctldev->desc->pins[pin].drv_data;
516cfb24f6eSIvan T. Ivanov 
517cfb24f6eSIvan T. Ivanov 	seq_printf(s, " mpp%-2d:", pin + PMIC_MPP_PHYSICAL_OFFSET);
518cfb24f6eSIvan T. Ivanov 
5197682b374SBjorn Andersson 	if (!pad->is_enabled) {
520cfb24f6eSIvan T. Ivanov 		seq_puts(s, " ---");
521cfb24f6eSIvan T. Ivanov 	} else {
522cfb24f6eSIvan T. Ivanov 
523cfb24f6eSIvan T. Ivanov 		if (pad->input_enabled) {
524cfb24f6eSIvan T. Ivanov 			ret = pmic_mpp_read(state, pad, PMIC_MPP_REG_RT_STS);
5254e637ac2SIvan T. Ivanov 			if (ret < 0)
5264e637ac2SIvan T. Ivanov 				return;
5274e637ac2SIvan T. Ivanov 
528cfb24f6eSIvan T. Ivanov 			ret &= PMIC_MPP_REG_RT_STS_VAL_MASK;
529cfb24f6eSIvan T. Ivanov 			pad->out_value = ret;
530cfb24f6eSIvan T. Ivanov 		}
531cfb24f6eSIvan T. Ivanov 
532cfb24f6eSIvan T. Ivanov 		seq_printf(s, " %-4s", pad->output_enabled ? "out" : "in");
533cfb24f6eSIvan T. Ivanov 		seq_printf(s, " %-7s", pmic_mpp_functions[pad->function]);
534cfb24f6eSIvan T. Ivanov 		seq_printf(s, " vin-%d", pad->power_source);
535099f3e4aSBjorn Andersson 		seq_printf(s, " %d", pad->aout_level);
536cfb24f6eSIvan T. Ivanov 		seq_printf(s, " %-8s", biases[pad->pullup]);
537cfb24f6eSIvan T. Ivanov 		seq_printf(s, " %-4s", pad->out_value ? "high" : "low");
538eb5c144cSBjorn Andersson 		if (pad->dtest)
539eb5c144cSBjorn Andersson 			seq_printf(s, " dtest%d", pad->dtest);
540eb5c144cSBjorn Andersson 		if (pad->paired)
541eb5c144cSBjorn Andersson 			seq_puts(s, " paired");
542cfb24f6eSIvan T. Ivanov 	}
543cfb24f6eSIvan T. Ivanov }
544cfb24f6eSIvan T. Ivanov 
545cfb24f6eSIvan T. Ivanov static const struct pinconf_ops pmic_mpp_pinconf_ops = {
546ba5f94cdSBjorn Andersson 	.is_generic = true,
547cfb24f6eSIvan T. Ivanov 	.pin_config_group_get		= pmic_mpp_config_get,
548cfb24f6eSIvan T. Ivanov 	.pin_config_group_set		= pmic_mpp_config_set,
549cfb24f6eSIvan T. Ivanov 	.pin_config_group_dbg_show	= pmic_mpp_config_dbg_show,
550cfb24f6eSIvan T. Ivanov };
551cfb24f6eSIvan T. Ivanov 
552cfb24f6eSIvan T. Ivanov static int pmic_mpp_direction_input(struct gpio_chip *chip, unsigned pin)
553cfb24f6eSIvan T. Ivanov {
554064761d1SLinus Walleij 	struct pmic_mpp_state *state = gpiochip_get_data(chip);
555cfb24f6eSIvan T. Ivanov 	unsigned long config;
556cfb24f6eSIvan T. Ivanov 
557cfb24f6eSIvan T. Ivanov 	config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1);
558cfb24f6eSIvan T. Ivanov 
559cfb24f6eSIvan T. Ivanov 	return pmic_mpp_config_set(state->ctrl, pin, &config, 1);
560cfb24f6eSIvan T. Ivanov }
561cfb24f6eSIvan T. Ivanov 
562cfb24f6eSIvan T. Ivanov static int pmic_mpp_direction_output(struct gpio_chip *chip,
563cfb24f6eSIvan T. Ivanov 				     unsigned pin, int val)
564cfb24f6eSIvan T. Ivanov {
565064761d1SLinus Walleij 	struct pmic_mpp_state *state = gpiochip_get_data(chip);
566cfb24f6eSIvan T. Ivanov 	unsigned long config;
567cfb24f6eSIvan T. Ivanov 
568cfb24f6eSIvan T. Ivanov 	config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val);
569cfb24f6eSIvan T. Ivanov 
570cfb24f6eSIvan T. Ivanov 	return pmic_mpp_config_set(state->ctrl, pin, &config, 1);
571cfb24f6eSIvan T. Ivanov }
572cfb24f6eSIvan T. Ivanov 
573cfb24f6eSIvan T. Ivanov static int pmic_mpp_get(struct gpio_chip *chip, unsigned pin)
574cfb24f6eSIvan T. Ivanov {
575064761d1SLinus Walleij 	struct pmic_mpp_state *state = gpiochip_get_data(chip);
576cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_pad *pad;
577cfb24f6eSIvan T. Ivanov 	int ret;
578cfb24f6eSIvan T. Ivanov 
579cfb24f6eSIvan T. Ivanov 	pad = state->ctrl->desc->pins[pin].drv_data;
580cfb24f6eSIvan T. Ivanov 
581cfb24f6eSIvan T. Ivanov 	if (pad->input_enabled) {
582cfb24f6eSIvan T. Ivanov 		ret = pmic_mpp_read(state, pad, PMIC_MPP_REG_RT_STS);
583cfb24f6eSIvan T. Ivanov 		if (ret < 0)
584cfb24f6eSIvan T. Ivanov 			return ret;
585cfb24f6eSIvan T. Ivanov 
586cfb24f6eSIvan T. Ivanov 		pad->out_value = ret & PMIC_MPP_REG_RT_STS_VAL_MASK;
587cfb24f6eSIvan T. Ivanov 	}
588cfb24f6eSIvan T. Ivanov 
58959663a4cSLinus Walleij 	return !!pad->out_value;
590cfb24f6eSIvan T. Ivanov }
591cfb24f6eSIvan T. Ivanov 
592cfb24f6eSIvan T. Ivanov static void pmic_mpp_set(struct gpio_chip *chip, unsigned pin, int value)
593cfb24f6eSIvan T. Ivanov {
594064761d1SLinus Walleij 	struct pmic_mpp_state *state = gpiochip_get_data(chip);
595cfb24f6eSIvan T. Ivanov 	unsigned long config;
596cfb24f6eSIvan T. Ivanov 
597cfb24f6eSIvan T. Ivanov 	config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value);
598cfb24f6eSIvan T. Ivanov 
599cfb24f6eSIvan T. Ivanov 	pmic_mpp_config_set(state->ctrl, pin, &config, 1);
600cfb24f6eSIvan T. Ivanov }
601cfb24f6eSIvan T. Ivanov 
602cfb24f6eSIvan T. Ivanov static int pmic_mpp_of_xlate(struct gpio_chip *chip,
603cfb24f6eSIvan T. Ivanov 			     const struct of_phandle_args *gpio_desc,
604cfb24f6eSIvan T. Ivanov 			     u32 *flags)
605cfb24f6eSIvan T. Ivanov {
606cfb24f6eSIvan T. Ivanov 	if (chip->of_gpio_n_cells < 2)
607cfb24f6eSIvan T. Ivanov 		return -EINVAL;
608cfb24f6eSIvan T. Ivanov 
609cfb24f6eSIvan T. Ivanov 	if (flags)
610cfb24f6eSIvan T. Ivanov 		*flags = gpio_desc->args[1];
611cfb24f6eSIvan T. Ivanov 
612cfb24f6eSIvan T. Ivanov 	return gpio_desc->args[0] - PMIC_MPP_PHYSICAL_OFFSET;
613cfb24f6eSIvan T. Ivanov }
614cfb24f6eSIvan T. Ivanov 
615cfb24f6eSIvan T. Ivanov static int pmic_mpp_to_irq(struct gpio_chip *chip, unsigned pin)
616cfb24f6eSIvan T. Ivanov {
617064761d1SLinus Walleij 	struct pmic_mpp_state *state = gpiochip_get_data(chip);
618cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_pad *pad;
619cfb24f6eSIvan T. Ivanov 
620cfb24f6eSIvan T. Ivanov 	pad = state->ctrl->desc->pins[pin].drv_data;
621cfb24f6eSIvan T. Ivanov 
622cfb24f6eSIvan T. Ivanov 	return pad->irq;
623cfb24f6eSIvan T. Ivanov }
624cfb24f6eSIvan T. Ivanov 
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 	.to_irq			= pmic_mpp_to_irq,
645cfb24f6eSIvan T. Ivanov 	.dbg_show		= pmic_mpp_dbg_show,
646cfb24f6eSIvan T. Ivanov };
647cfb24f6eSIvan T. Ivanov 
648cfb24f6eSIvan T. Ivanov static int pmic_mpp_populate(struct pmic_mpp_state *state,
649cfb24f6eSIvan T. Ivanov 			     struct pmic_mpp_pad *pad)
650cfb24f6eSIvan T. Ivanov {
651cfb24f6eSIvan T. Ivanov 	int type, subtype, val, dir;
652eb5c144cSBjorn Andersson 	unsigned int sel;
653cfb24f6eSIvan T. Ivanov 
654cfb24f6eSIvan T. Ivanov 	type = pmic_mpp_read(state, pad, PMIC_MPP_REG_TYPE);
655cfb24f6eSIvan T. Ivanov 	if (type < 0)
656cfb24f6eSIvan T. Ivanov 		return type;
657cfb24f6eSIvan T. Ivanov 
658cfb24f6eSIvan T. Ivanov 	if (type != PMIC_MPP_TYPE) {
659cfb24f6eSIvan T. Ivanov 		dev_err(state->dev, "incorrect block type 0x%x at 0x%x\n",
660cfb24f6eSIvan T. Ivanov 			type, pad->base);
661cfb24f6eSIvan T. Ivanov 		return -ENODEV;
662cfb24f6eSIvan T. Ivanov 	}
663cfb24f6eSIvan T. Ivanov 
664cfb24f6eSIvan T. Ivanov 	subtype = pmic_mpp_read(state, pad, PMIC_MPP_REG_SUBTYPE);
665cfb24f6eSIvan T. Ivanov 	if (subtype < 0)
666cfb24f6eSIvan T. Ivanov 		return subtype;
667cfb24f6eSIvan T. Ivanov 
668cfb24f6eSIvan T. Ivanov 	switch (subtype) {
669cfb24f6eSIvan T. Ivanov 	case PMIC_MPP_SUBTYPE_4CH_NO_ANA_OUT:
670cfb24f6eSIvan T. Ivanov 	case PMIC_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT:
671cfb24f6eSIvan T. Ivanov 	case PMIC_MPP_SUBTYPE_4CH_NO_SINK:
672cfb24f6eSIvan T. Ivanov 	case PMIC_MPP_SUBTYPE_ULT_4CH_NO_SINK:
673cfb24f6eSIvan T. Ivanov 	case PMIC_MPP_SUBTYPE_4CH_FULL_FUNC:
674cfb24f6eSIvan T. Ivanov 		pad->num_sources = 4;
675cfb24f6eSIvan T. Ivanov 		break;
676cfb24f6eSIvan T. Ivanov 	case PMIC_MPP_SUBTYPE_8CH_FULL_FUNC:
677cfb24f6eSIvan T. Ivanov 		pad->num_sources = 8;
678cfb24f6eSIvan T. Ivanov 		break;
679cfb24f6eSIvan T. Ivanov 	default:
680cfb24f6eSIvan T. Ivanov 		dev_err(state->dev, "unknown MPP type 0x%x at 0x%x\n",
681cfb24f6eSIvan T. Ivanov 			subtype, pad->base);
682cfb24f6eSIvan T. Ivanov 		return -ENODEV;
683cfb24f6eSIvan T. Ivanov 	}
684cfb24f6eSIvan T. Ivanov 
685cfb24f6eSIvan T. Ivanov 	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_MODE_CTL);
686cfb24f6eSIvan T. Ivanov 	if (val < 0)
687cfb24f6eSIvan T. Ivanov 		return val;
688cfb24f6eSIvan T. Ivanov 
689cfb24f6eSIvan T. Ivanov 	pad->out_value = val & PMIC_MPP_REG_MODE_VALUE_MASK;
690cfb24f6eSIvan T. Ivanov 
691cfb24f6eSIvan T. Ivanov 	dir = val >> PMIC_MPP_REG_MODE_DIR_SHIFT;
692cfb24f6eSIvan T. Ivanov 	dir &= PMIC_MPP_REG_MODE_DIR_MASK;
693cfb24f6eSIvan T. Ivanov 
694cfb24f6eSIvan T. Ivanov 	switch (dir) {
695eaaf5dd4SBjorn Andersson 	case PMIC_MPP_MODE_DIGITAL_INPUT:
696cfb24f6eSIvan T. Ivanov 		pad->input_enabled = true;
697cfb24f6eSIvan T. Ivanov 		pad->output_enabled = false;
698eb5c144cSBjorn Andersson 		pad->function = PMIC_MPP_DIGITAL;
699cfb24f6eSIvan T. Ivanov 		break;
700eaaf5dd4SBjorn Andersson 	case PMIC_MPP_MODE_DIGITAL_OUTPUT:
701cfb24f6eSIvan T. Ivanov 		pad->input_enabled = false;
702cfb24f6eSIvan T. Ivanov 		pad->output_enabled = true;
703eb5c144cSBjorn Andersson 		pad->function = PMIC_MPP_DIGITAL;
704cfb24f6eSIvan T. Ivanov 		break;
705eaaf5dd4SBjorn Andersson 	case PMIC_MPP_MODE_DIGITAL_BIDIR:
706cfb24f6eSIvan T. Ivanov 		pad->input_enabled = true;
707cfb24f6eSIvan T. Ivanov 		pad->output_enabled = true;
708eb5c144cSBjorn Andersson 		pad->function = PMIC_MPP_DIGITAL;
709cfb24f6eSIvan T. Ivanov 		break;
710eaaf5dd4SBjorn Andersson 	case PMIC_MPP_MODE_ANALOG_BIDIR:
711cfb24f6eSIvan T. Ivanov 		pad->input_enabled = true;
712cfb24f6eSIvan T. Ivanov 		pad->output_enabled = true;
713eb5c144cSBjorn Andersson 		pad->function = PMIC_MPP_ANALOG;
714cfb24f6eSIvan T. Ivanov 		break;
715eaaf5dd4SBjorn Andersson 	case PMIC_MPP_MODE_ANALOG_INPUT:
716cfb24f6eSIvan T. Ivanov 		pad->input_enabled = true;
717cfb24f6eSIvan T. Ivanov 		pad->output_enabled = false;
718eb5c144cSBjorn Andersson 		pad->function = PMIC_MPP_ANALOG;
719cfb24f6eSIvan T. Ivanov 		break;
720eaaf5dd4SBjorn Andersson 	case PMIC_MPP_MODE_ANALOG_OUTPUT:
721cfb24f6eSIvan T. Ivanov 		pad->input_enabled = false;
722cfb24f6eSIvan T. Ivanov 		pad->output_enabled = true;
723eb5c144cSBjorn Andersson 		pad->function = PMIC_MPP_ANALOG;
7240e948042SBjorn Andersson 		break;
7250e948042SBjorn Andersson 	case PMIC_MPP_MODE_CURRENT_SINK:
7260e948042SBjorn Andersson 		pad->input_enabled = false;
7270e948042SBjorn Andersson 		pad->output_enabled = true;
728eb5c144cSBjorn Andersson 		pad->function = PMIC_MPP_SINK;
729cfb24f6eSIvan T. Ivanov 		break;
730cfb24f6eSIvan T. Ivanov 	default:
731cfb24f6eSIvan T. Ivanov 		dev_err(state->dev, "unknown MPP direction\n");
732cfb24f6eSIvan T. Ivanov 		return -ENODEV;
733cfb24f6eSIvan T. Ivanov 	}
734cfb24f6eSIvan T. Ivanov 
735eb5c144cSBjorn Andersson 	sel = val >> PMIC_MPP_REG_MODE_FUNCTION_SHIFT;
736eb5c144cSBjorn Andersson 	sel &= PMIC_MPP_REG_MODE_FUNCTION_MASK;
737eb5c144cSBjorn Andersson 
738eb5c144cSBjorn Andersson 	if (sel >= PMIC_MPP_SELECTOR_DTEST_FIRST)
739eb5c144cSBjorn Andersson 		pad->dtest = sel + 1;
740eb5c144cSBjorn Andersson 	else if (sel == PMIC_MPP_SELECTOR_PAIRED)
741eb5c144cSBjorn Andersson 		pad->paired = true;
742cfb24f6eSIvan T. Ivanov 
743cfb24f6eSIvan T. Ivanov 	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_DIG_VIN_CTL);
744cfb24f6eSIvan T. Ivanov 	if (val < 0)
745cfb24f6eSIvan T. Ivanov 		return val;
746cfb24f6eSIvan T. Ivanov 
747cfb24f6eSIvan T. Ivanov 	pad->power_source = val >> PMIC_MPP_REG_VIN_SHIFT;
748cfb24f6eSIvan T. Ivanov 	pad->power_source &= PMIC_MPP_REG_VIN_MASK;
749cfb24f6eSIvan T. Ivanov 
750cfb24f6eSIvan T. Ivanov 	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_DIG_PULL_CTL);
751cfb24f6eSIvan T. Ivanov 	if (val < 0)
752cfb24f6eSIvan T. Ivanov 		return val;
753cfb24f6eSIvan T. Ivanov 
754cfb24f6eSIvan T. Ivanov 	pad->pullup = val >> PMIC_MPP_REG_PULL_SHIFT;
755cfb24f6eSIvan T. Ivanov 	pad->pullup &= PMIC_MPP_REG_PULL_MASK;
756cfb24f6eSIvan T. Ivanov 
757cfb24f6eSIvan T. Ivanov 	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_AIN_CTL);
758cfb24f6eSIvan T. Ivanov 	if (val < 0)
759cfb24f6eSIvan T. Ivanov 		return val;
760cfb24f6eSIvan T. Ivanov 
761cfb24f6eSIvan T. Ivanov 	pad->amux_input = val >> PMIC_MPP_REG_AIN_ROUTE_SHIFT;
762cfb24f6eSIvan T. Ivanov 	pad->amux_input &= PMIC_MPP_REG_AIN_ROUTE_MASK;
763cfb24f6eSIvan T. Ivanov 
7640e948042SBjorn Andersson 	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_SINK_CTL);
7650e948042SBjorn Andersson 	if (val < 0)
7660e948042SBjorn Andersson 		return val;
7670e948042SBjorn Andersson 
7680e948042SBjorn Andersson 	pad->drive_strength = val;
7690e948042SBjorn Andersson 
770099f3e4aSBjorn Andersson 	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_AOUT_CTL);
771099f3e4aSBjorn Andersson 	if (val < 0)
772099f3e4aSBjorn Andersson 		return val;
773099f3e4aSBjorn Andersson 
774099f3e4aSBjorn Andersson 	pad->aout_level = val;
775099f3e4aSBjorn Andersson 
7767682b374SBjorn Andersson 	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_EN_CTL);
7777682b374SBjorn Andersson 	if (val < 0)
7787682b374SBjorn Andersson 		return val;
7797682b374SBjorn Andersson 
7807682b374SBjorn Andersson 	pad->is_enabled = !!val;
7817682b374SBjorn Andersson 
782cfb24f6eSIvan T. Ivanov 	return 0;
783cfb24f6eSIvan T. Ivanov }
784cfb24f6eSIvan T. Ivanov 
785cfb24f6eSIvan T. Ivanov static int pmic_mpp_probe(struct platform_device *pdev)
786cfb24f6eSIvan T. Ivanov {
787cfb24f6eSIvan T. Ivanov 	struct device *dev = &pdev->dev;
788cfb24f6eSIvan T. Ivanov 	struct pinctrl_pin_desc *pindesc;
789cfb24f6eSIvan T. Ivanov 	struct pinctrl_desc *pctrldesc;
790cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_pad *pad, *pads;
791cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_state *state;
792cfb24f6eSIvan T. Ivanov 	int ret, npins, i;
793cfb24f6eSIvan T. Ivanov 	u32 res[2];
794cfb24f6eSIvan T. Ivanov 
795cfb24f6eSIvan T. Ivanov 	ret = of_property_read_u32_array(dev->of_node, "reg", res, 2);
796cfb24f6eSIvan T. Ivanov 	if (ret < 0) {
797cfb24f6eSIvan T. Ivanov 		dev_err(dev, "missing base address and/or range");
798cfb24f6eSIvan T. Ivanov 		return ret;
799cfb24f6eSIvan T. Ivanov 	}
800cfb24f6eSIvan T. Ivanov 
801cfb24f6eSIvan T. Ivanov 	npins = res[1] / PMIC_MPP_ADDRESS_RANGE;
802cfb24f6eSIvan T. Ivanov 	if (!npins)
803cfb24f6eSIvan T. Ivanov 		return -EINVAL;
804cfb24f6eSIvan T. Ivanov 
805cfb24f6eSIvan T. Ivanov 	BUG_ON(npins > ARRAY_SIZE(pmic_mpp_groups));
806cfb24f6eSIvan T. Ivanov 
807cfb24f6eSIvan T. Ivanov 	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
808cfb24f6eSIvan T. Ivanov 	if (!state)
809cfb24f6eSIvan T. Ivanov 		return -ENOMEM;
810cfb24f6eSIvan T. Ivanov 
811cfb24f6eSIvan T. Ivanov 	platform_set_drvdata(pdev, state);
812cfb24f6eSIvan T. Ivanov 
813cfb24f6eSIvan T. Ivanov 	state->dev = &pdev->dev;
814cfb24f6eSIvan T. Ivanov 	state->map = dev_get_regmap(dev->parent, NULL);
815cfb24f6eSIvan T. Ivanov 
816cfb24f6eSIvan T. Ivanov 	pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
817cfb24f6eSIvan T. Ivanov 	if (!pindesc)
818cfb24f6eSIvan T. Ivanov 		return -ENOMEM;
819cfb24f6eSIvan T. Ivanov 
820cfb24f6eSIvan T. Ivanov 	pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
821cfb24f6eSIvan T. Ivanov 	if (!pads)
822cfb24f6eSIvan T. Ivanov 		return -ENOMEM;
823cfb24f6eSIvan T. Ivanov 
824cfb24f6eSIvan T. Ivanov 	pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL);
825cfb24f6eSIvan T. Ivanov 	if (!pctrldesc)
826cfb24f6eSIvan T. Ivanov 		return -ENOMEM;
827cfb24f6eSIvan T. Ivanov 
828cfb24f6eSIvan T. Ivanov 	pctrldesc->pctlops = &pmic_mpp_pinctrl_ops;
829cfb24f6eSIvan T. Ivanov 	pctrldesc->pmxops = &pmic_mpp_pinmux_ops;
830cfb24f6eSIvan T. Ivanov 	pctrldesc->confops = &pmic_mpp_pinconf_ops;
831cfb24f6eSIvan T. Ivanov 	pctrldesc->owner = THIS_MODULE;
832cfb24f6eSIvan T. Ivanov 	pctrldesc->name = dev_name(dev);
833cfb24f6eSIvan T. Ivanov 	pctrldesc->pins = pindesc;
834cfb24f6eSIvan T. Ivanov 	pctrldesc->npins = npins;
835cfb24f6eSIvan T. Ivanov 
836ba5f94cdSBjorn Andersson 	pctrldesc->num_custom_params = ARRAY_SIZE(pmic_mpp_bindings);
837ba5f94cdSBjorn Andersson 	pctrldesc->custom_params = pmic_mpp_bindings;
838ba5f94cdSBjorn Andersson #ifdef CONFIG_DEBUG_FS
839ba5f94cdSBjorn Andersson 	pctrldesc->custom_conf_items = pmic_conf_items;
840ba5f94cdSBjorn Andersson #endif
841ba5f94cdSBjorn Andersson 
842cfb24f6eSIvan T. Ivanov 	for (i = 0; i < npins; i++, pindesc++) {
843cfb24f6eSIvan T. Ivanov 		pad = &pads[i];
844cfb24f6eSIvan T. Ivanov 		pindesc->drv_data = pad;
845cfb24f6eSIvan T. Ivanov 		pindesc->number = i;
846cfb24f6eSIvan T. Ivanov 		pindesc->name = pmic_mpp_groups[i];
847cfb24f6eSIvan T. Ivanov 
848cfb24f6eSIvan T. Ivanov 		pad->irq = platform_get_irq(pdev, i);
849cfb24f6eSIvan T. Ivanov 		if (pad->irq < 0)
850cfb24f6eSIvan T. Ivanov 			return pad->irq;
851cfb24f6eSIvan T. Ivanov 
852cfb24f6eSIvan T. Ivanov 		pad->base = res[0] + i * PMIC_MPP_ADDRESS_RANGE;
853cfb24f6eSIvan T. Ivanov 
854cfb24f6eSIvan T. Ivanov 		ret = pmic_mpp_populate(state, pad);
855cfb24f6eSIvan T. Ivanov 		if (ret < 0)
856cfb24f6eSIvan T. Ivanov 			return ret;
857cfb24f6eSIvan T. Ivanov 	}
858cfb24f6eSIvan T. Ivanov 
859cfb24f6eSIvan T. Ivanov 	state->chip = pmic_mpp_gpio_template;
86058383c78SLinus Walleij 	state->chip.parent = dev;
861cfb24f6eSIvan T. Ivanov 	state->chip.base = -1;
862cfb24f6eSIvan T. Ivanov 	state->chip.ngpio = npins;
863cfb24f6eSIvan T. Ivanov 	state->chip.label = dev_name(dev);
864cfb24f6eSIvan T. Ivanov 	state->chip.of_gpio_n_cells = 2;
865cfb24f6eSIvan T. Ivanov 	state->chip.can_sleep = false;
866cfb24f6eSIvan T. Ivanov 
867cfb24f6eSIvan T. Ivanov 	state->ctrl = pinctrl_register(pctrldesc, dev, state);
868323de9efSMasahiro Yamada 	if (IS_ERR(state->ctrl))
869323de9efSMasahiro Yamada 		return PTR_ERR(state->ctrl);
870cfb24f6eSIvan T. Ivanov 
871064761d1SLinus Walleij 	ret = gpiochip_add_data(&state->chip, state);
872cfb24f6eSIvan T. Ivanov 	if (ret) {
873cfb24f6eSIvan T. Ivanov 		dev_err(state->dev, "can't add gpio chip\n");
874cfb24f6eSIvan T. Ivanov 		goto err_chip;
875cfb24f6eSIvan T. Ivanov 	}
876cfb24f6eSIvan T. Ivanov 
877cfb24f6eSIvan T. Ivanov 	ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, npins);
878cfb24f6eSIvan T. Ivanov 	if (ret) {
879cfb24f6eSIvan T. Ivanov 		dev_err(dev, "failed to add pin range\n");
880cfb24f6eSIvan T. Ivanov 		goto err_range;
881cfb24f6eSIvan T. Ivanov 	}
882cfb24f6eSIvan T. Ivanov 
883cfb24f6eSIvan T. Ivanov 	return 0;
884cfb24f6eSIvan T. Ivanov 
885cfb24f6eSIvan T. Ivanov err_range:
886cfb24f6eSIvan T. Ivanov 	gpiochip_remove(&state->chip);
887cfb24f6eSIvan T. Ivanov err_chip:
888cfb24f6eSIvan T. Ivanov 	pinctrl_unregister(state->ctrl);
889cfb24f6eSIvan T. Ivanov 	return ret;
890cfb24f6eSIvan T. Ivanov }
891cfb24f6eSIvan T. Ivanov 
892cfb24f6eSIvan T. Ivanov static int pmic_mpp_remove(struct platform_device *pdev)
893cfb24f6eSIvan T. Ivanov {
894cfb24f6eSIvan T. Ivanov 	struct pmic_mpp_state *state = platform_get_drvdata(pdev);
895cfb24f6eSIvan T. Ivanov 
896cfb24f6eSIvan T. Ivanov 	gpiochip_remove(&state->chip);
897cfb24f6eSIvan T. Ivanov 	pinctrl_unregister(state->ctrl);
898cfb24f6eSIvan T. Ivanov 	return 0;
899cfb24f6eSIvan T. Ivanov }
900cfb24f6eSIvan T. Ivanov 
901cfb24f6eSIvan T. Ivanov static const struct of_device_id pmic_mpp_of_match[] = {
902cfb24f6eSIvan T. Ivanov 	{ .compatible = "qcom,pm8841-mpp" },	/* 4 MPP's */
9037414b099SIvan T. Ivanov 	{ .compatible = "qcom,pm8916-mpp" },	/* 4 MPP's */
904cfb24f6eSIvan T. Ivanov 	{ .compatible = "qcom,pm8941-mpp" },	/* 8 MPP's */
905cfb24f6eSIvan T. Ivanov 	{ .compatible = "qcom,pma8084-mpp" },	/* 8 MPP's */
906cfb24f6eSIvan T. Ivanov 	{ },
907cfb24f6eSIvan T. Ivanov };
908cfb24f6eSIvan T. Ivanov 
909cfb24f6eSIvan T. Ivanov MODULE_DEVICE_TABLE(of, pmic_mpp_of_match);
910cfb24f6eSIvan T. Ivanov 
911cfb24f6eSIvan T. Ivanov static struct platform_driver pmic_mpp_driver = {
912cfb24f6eSIvan T. Ivanov 	.driver = {
913cfb24f6eSIvan T. Ivanov 		   .name = "qcom-spmi-mpp",
914cfb24f6eSIvan T. Ivanov 		   .of_match_table = pmic_mpp_of_match,
915cfb24f6eSIvan T. Ivanov 	},
916cfb24f6eSIvan T. Ivanov 	.probe	= pmic_mpp_probe,
917cfb24f6eSIvan T. Ivanov 	.remove = pmic_mpp_remove,
918cfb24f6eSIvan T. Ivanov };
919cfb24f6eSIvan T. Ivanov 
920cfb24f6eSIvan T. Ivanov module_platform_driver(pmic_mpp_driver);
921cfb24f6eSIvan T. Ivanov 
922cfb24f6eSIvan T. Ivanov MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
923cfb24f6eSIvan T. Ivanov MODULE_DESCRIPTION("Qualcomm SPMI PMIC MPP pin control driver");
924cfb24f6eSIvan T. Ivanov MODULE_ALIAS("platform:qcom-spmi-mpp");
925cfb24f6eSIvan T. Ivanov MODULE_LICENSE("GPL v2");
926