blob: e52a72348a67f336a5dd922a094b0a8419add306 [file] [log] [blame]
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +03001/*
2 * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/gpio.h>
15#include <linux/module.h>
16#include <linux/of.h>
17#include <linux/pinctrl/pinconf-generic.h>
18#include <linux/pinctrl/pinconf.h>
19#include <linux/pinctrl/pinmux.h>
20#include <linux/platform_device.h>
21#include <linux/regmap.h>
22#include <linux/slab.h>
23#include <linux/types.h>
24
25#include <dt-bindings/pinctrl/qcom,pmic-mpp.h>
26
27#include "../core.h"
28#include "../pinctrl-utils.h"
29
30#define PMIC_MPP_ADDRESS_RANGE 0x100
31
32/*
33 * Pull Up Values - it indicates whether a pull-up should be
34 * applied for bidirectional mode only. The hardware ignores the
35 * configuration when operating in other modes.
36 */
37#define PMIC_MPP_PULL_UP_0P6KOHM 0
38#define PMIC_MPP_PULL_UP_10KOHM 1
39#define PMIC_MPP_PULL_UP_30KOHM 2
40#define PMIC_MPP_PULL_UP_OPEN 3
41
42/* type registers base address bases */
43#define PMIC_MPP_REG_TYPE 0x4
44#define PMIC_MPP_REG_SUBTYPE 0x5
45
46/* mpp peripheral type and subtype values */
47#define PMIC_MPP_TYPE 0x11
48#define PMIC_MPP_SUBTYPE_4CH_NO_ANA_OUT 0x3
49#define PMIC_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT 0x4
50#define PMIC_MPP_SUBTYPE_4CH_NO_SINK 0x5
51#define PMIC_MPP_SUBTYPE_ULT_4CH_NO_SINK 0x6
52#define PMIC_MPP_SUBTYPE_4CH_FULL_FUNC 0x7
53#define PMIC_MPP_SUBTYPE_8CH_FULL_FUNC 0xf
54
55#define PMIC_MPP_REG_RT_STS 0x10
56#define PMIC_MPP_REG_RT_STS_VAL_MASK 0x1
57
58/* control register base address bases */
59#define PMIC_MPP_REG_MODE_CTL 0x40
60#define PMIC_MPP_REG_DIG_VIN_CTL 0x41
61#define PMIC_MPP_REG_DIG_PULL_CTL 0x42
62#define PMIC_MPP_REG_DIG_IN_CTL 0x43
63#define PMIC_MPP_REG_EN_CTL 0x46
Bjorn Andersson099f3e42015-07-14 23:40:33 -070064#define PMIC_MPP_REG_AOUT_CTL 0x48
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +030065#define PMIC_MPP_REG_AIN_CTL 0x4a
Bjorn Andersson0e948042015-06-17 23:47:28 -070066#define PMIC_MPP_REG_SINK_CTL 0x4c
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +030067
68/* PMIC_MPP_REG_MODE_CTL */
69#define PMIC_MPP_REG_MODE_VALUE_MASK 0x1
70#define PMIC_MPP_REG_MODE_FUNCTION_SHIFT 1
71#define PMIC_MPP_REG_MODE_FUNCTION_MASK 0x7
72#define PMIC_MPP_REG_MODE_DIR_SHIFT 4
73#define PMIC_MPP_REG_MODE_DIR_MASK 0x7
74
75/* PMIC_MPP_REG_DIG_VIN_CTL */
76#define PMIC_MPP_REG_VIN_SHIFT 0
77#define PMIC_MPP_REG_VIN_MASK 0x7
78
79/* PMIC_MPP_REG_DIG_PULL_CTL */
80#define PMIC_MPP_REG_PULL_SHIFT 0
81#define PMIC_MPP_REG_PULL_MASK 0x7
82
83/* PMIC_MPP_REG_EN_CTL */
84#define PMIC_MPP_REG_MASTER_EN_SHIFT 7
85
86/* PMIC_MPP_REG_AIN_CTL */
87#define PMIC_MPP_REG_AIN_ROUTE_SHIFT 0
88#define PMIC_MPP_REG_AIN_ROUTE_MASK 0x7
89
Bjorn Anderssoneaaf5dd2015-06-17 23:47:27 -070090#define PMIC_MPP_MODE_DIGITAL_INPUT 0
91#define PMIC_MPP_MODE_DIGITAL_OUTPUT 1
92#define PMIC_MPP_MODE_DIGITAL_BIDIR 2
93#define PMIC_MPP_MODE_ANALOG_BIDIR 3
94#define PMIC_MPP_MODE_ANALOG_INPUT 4
95#define PMIC_MPP_MODE_ANALOG_OUTPUT 5
96#define PMIC_MPP_MODE_CURRENT_SINK 6
97
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +030098#define PMIC_MPP_PHYSICAL_OFFSET 1
99
100/* Qualcomm specific pin configurations */
101#define PMIC_MPP_CONF_AMUX_ROUTE (PIN_CONFIG_END + 1)
102#define PMIC_MPP_CONF_ANALOG_MODE (PIN_CONFIG_END + 2)
Bjorn Andersson0e948042015-06-17 23:47:28 -0700103#define PMIC_MPP_CONF_SINK_MODE (PIN_CONFIG_END + 3)
Bjorn Andersson099f3e42015-07-14 23:40:33 -0700104#define PMIC_MPP_CONF_ANALOG_LEVEL (PIN_CONFIG_END + 4)
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300105
106/**
107 * struct pmic_mpp_pad - keep current MPP settings
108 * @base: Address base in SPMI device.
109 * @irq: IRQ number which this MPP generate.
110 * @is_enabled: Set to false when MPP should be put in high Z state.
111 * @out_value: Cached pin output value.
112 * @output_enabled: Set to true if MPP output logic is enabled.
113 * @input_enabled: Set to true if MPP input buffer logic is enabled.
114 * @analog_mode: Set to true when MPP should operate in Analog Input, Analog
115 * Output or Bidirectional Analog mode.
Bjorn Andersson0e948042015-06-17 23:47:28 -0700116 * @sink_mode: Boolean indicating if ink mode is slected
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300117 * @num_sources: Number of power-sources supported by this MPP.
118 * @power_source: Current power-source used.
119 * @amux_input: Set the source for analog input.
Bjorn Andersson099f3e42015-07-14 23:40:33 -0700120 * @aout_level: Analog output level
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300121 * @pullup: Pullup resistor value. Valid in Bidirectional mode only.
122 * @function: See pmic_mpp_functions[].
Bjorn Andersson0e948042015-06-17 23:47:28 -0700123 * @drive_strength: Amount of current in sink mode
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300124 */
125struct pmic_mpp_pad {
126 u16 base;
127 int irq;
128 bool is_enabled;
129 bool out_value;
130 bool output_enabled;
131 bool input_enabled;
132 bool analog_mode;
Bjorn Andersson0e948042015-06-17 23:47:28 -0700133 bool sink_mode;
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300134 unsigned int num_sources;
135 unsigned int power_source;
136 unsigned int amux_input;
Bjorn Andersson099f3e42015-07-14 23:40:33 -0700137 unsigned int aout_level;
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300138 unsigned int pullup;
139 unsigned int function;
Bjorn Andersson0e948042015-06-17 23:47:28 -0700140 unsigned int drive_strength;
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300141};
142
143struct pmic_mpp_state {
144 struct device *dev;
145 struct regmap *map;
146 struct pinctrl_dev *ctrl;
147 struct gpio_chip chip;
148};
149
Bjorn Anderssonba5f94c2015-06-17 23:47:25 -0700150static const struct pinconf_generic_params pmic_mpp_bindings[] = {
151 {"qcom,amux-route", PMIC_MPP_CONF_AMUX_ROUTE, 0},
Bjorn Andersson099f3e42015-07-14 23:40:33 -0700152 {"qcom,analog-level", PMIC_MPP_CONF_ANALOG_LEVEL, 0},
Bjorn Anderssonba5f94c2015-06-17 23:47:25 -0700153 {"qcom,analog-mode", PMIC_MPP_CONF_ANALOG_MODE, 0},
Bjorn Andersson0e948042015-06-17 23:47:28 -0700154 {"qcom,sink-mode", PMIC_MPP_CONF_SINK_MODE, 0},
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300155};
156
Bjorn Anderssonba5f94c2015-06-17 23:47:25 -0700157#ifdef CONFIG_DEBUG_FS
158static const struct pin_config_item pmic_conf_items[] = {
159 PCONFDUMP(PMIC_MPP_CONF_AMUX_ROUTE, "analog mux", NULL, true),
Bjorn Andersson099f3e42015-07-14 23:40:33 -0700160 PCONFDUMP(PMIC_MPP_CONF_ANALOG_LEVEL, "analog level", NULL, true),
Bjorn Anderssonba5f94c2015-06-17 23:47:25 -0700161 PCONFDUMP(PMIC_MPP_CONF_ANALOG_MODE, "analog output", NULL, false),
Bjorn Andersson0e948042015-06-17 23:47:28 -0700162 PCONFDUMP(PMIC_MPP_CONF_SINK_MODE, "sink mode", NULL, false),
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300163};
Bjorn Anderssonba5f94c2015-06-17 23:47:25 -0700164#endif
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300165
166static const char *const pmic_mpp_groups[] = {
167 "mpp1", "mpp2", "mpp3", "mpp4", "mpp5", "mpp6", "mpp7", "mpp8",
168};
169
170static const char *const pmic_mpp_functions[] = {
171 PMIC_MPP_FUNC_NORMAL, PMIC_MPP_FUNC_PAIRED,
172 "reserved1", "reserved2",
173 PMIC_MPP_FUNC_DTEST1, PMIC_MPP_FUNC_DTEST2,
174 PMIC_MPP_FUNC_DTEST3, PMIC_MPP_FUNC_DTEST4,
175};
176
177static inline struct pmic_mpp_state *to_mpp_state(struct gpio_chip *chip)
178{
179 return container_of(chip, struct pmic_mpp_state, chip);
180};
181
182static int pmic_mpp_read(struct pmic_mpp_state *state,
183 struct pmic_mpp_pad *pad, unsigned int addr)
184{
185 unsigned int val;
186 int ret;
187
188 ret = regmap_read(state->map, pad->base + addr, &val);
189 if (ret < 0)
190 dev_err(state->dev, "read 0x%x failed\n", addr);
191 else
192 ret = val;
193
194 return ret;
195}
196
197static int pmic_mpp_write(struct pmic_mpp_state *state,
198 struct pmic_mpp_pad *pad, unsigned int addr,
199 unsigned int val)
200{
201 int ret;
202
203 ret = regmap_write(state->map, pad->base + addr, val);
204 if (ret < 0)
205 dev_err(state->dev, "write 0x%x failed\n", addr);
206
207 return ret;
208}
209
210static int pmic_mpp_get_groups_count(struct pinctrl_dev *pctldev)
211{
212 /* Every PIN is a group */
213 return pctldev->desc->npins;
214}
215
216static const char *pmic_mpp_get_group_name(struct pinctrl_dev *pctldev,
217 unsigned pin)
218{
219 return pctldev->desc->pins[pin].name;
220}
221
222static int pmic_mpp_get_group_pins(struct pinctrl_dev *pctldev,
223 unsigned pin,
224 const unsigned **pins, unsigned *num_pins)
225{
226 *pins = &pctldev->desc->pins[pin].number;
227 *num_pins = 1;
228 return 0;
229}
230
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300231static const struct pinctrl_ops pmic_mpp_pinctrl_ops = {
232 .get_groups_count = pmic_mpp_get_groups_count,
233 .get_group_name = pmic_mpp_get_group_name,
234 .get_group_pins = pmic_mpp_get_group_pins,
Bjorn Anderssonba5f94c2015-06-17 23:47:25 -0700235 .dt_node_to_map = pinconf_generic_dt_node_to_map_group,
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300236 .dt_free_map = pinctrl_utils_dt_free_map,
237};
238
239static int pmic_mpp_get_functions_count(struct pinctrl_dev *pctldev)
240{
241 return ARRAY_SIZE(pmic_mpp_functions);
242}
243
244static const char *pmic_mpp_get_function_name(struct pinctrl_dev *pctldev,
245 unsigned function)
246{
247 return pmic_mpp_functions[function];
248}
249
250static int pmic_mpp_get_function_groups(struct pinctrl_dev *pctldev,
251 unsigned function,
252 const char *const **groups,
253 unsigned *const num_qgroups)
254{
255 *groups = pmic_mpp_groups;
256 *num_qgroups = pctldev->desc->npins;
257 return 0;
258}
259
Bjorn Andersson0e948042015-06-17 23:47:28 -0700260static int pmic_mpp_write_mode_ctl(struct pmic_mpp_state *state,
261 struct pmic_mpp_pad *pad)
262{
263 unsigned int val;
264
265 if (pad->analog_mode) {
266 val = PMIC_MPP_MODE_ANALOG_INPUT;
267 if (pad->output_enabled) {
268 if (pad->input_enabled)
269 val = PMIC_MPP_MODE_ANALOG_BIDIR;
270 else
271 val = PMIC_MPP_MODE_ANALOG_OUTPUT;
272 }
273 } else if (pad->sink_mode) {
274 val = PMIC_MPP_MODE_CURRENT_SINK;
275 } else {
276 val = PMIC_MPP_MODE_DIGITAL_INPUT;
277 if (pad->output_enabled) {
278 if (pad->input_enabled)
279 val = PMIC_MPP_MODE_DIGITAL_BIDIR;
280 else
281 val = PMIC_MPP_MODE_DIGITAL_OUTPUT;
282 }
283 }
284
285 val = val << PMIC_MPP_REG_MODE_DIR_SHIFT;
286 val |= pad->function << PMIC_MPP_REG_MODE_FUNCTION_SHIFT;
287 val |= pad->out_value & PMIC_MPP_REG_MODE_VALUE_MASK;
288
289 return pmic_mpp_write(state, pad, PMIC_MPP_REG_MODE_CTL, val);
290}
291
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300292static int pmic_mpp_set_mux(struct pinctrl_dev *pctldev, unsigned function,
293 unsigned pin)
294{
295 struct pmic_mpp_state *state = pinctrl_dev_get_drvdata(pctldev);
296 struct pmic_mpp_pad *pad;
297 unsigned int val;
298 int ret;
299
300 pad = pctldev->desc->pins[pin].drv_data;
301
302 pad->function = function;
303
Bjorn Andersson0e948042015-06-17 23:47:28 -0700304 ret = pmic_mpp_write_mode_ctl(state, pad);
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300305
306 val = pad->is_enabled << PMIC_MPP_REG_MASTER_EN_SHIFT;
307
308 return pmic_mpp_write(state, pad, PMIC_MPP_REG_EN_CTL, val);
309}
310
311static const struct pinmux_ops pmic_mpp_pinmux_ops = {
312 .get_functions_count = pmic_mpp_get_functions_count,
313 .get_function_name = pmic_mpp_get_function_name,
314 .get_function_groups = pmic_mpp_get_function_groups,
315 .set_mux = pmic_mpp_set_mux,
316};
317
318static int pmic_mpp_config_get(struct pinctrl_dev *pctldev,
319 unsigned int pin, unsigned long *config)
320{
321 unsigned param = pinconf_to_config_param(*config);
322 struct pmic_mpp_pad *pad;
323 unsigned arg = 0;
324
325 pad = pctldev->desc->pins[pin].drv_data;
326
327 switch (param) {
328 case PIN_CONFIG_BIAS_DISABLE:
329 arg = pad->pullup == PMIC_MPP_PULL_UP_OPEN;
330 break;
331 case PIN_CONFIG_BIAS_PULL_UP:
332 switch (pad->pullup) {
333 case PMIC_MPP_PULL_UP_OPEN:
334 arg = 0;
335 break;
336 case PMIC_MPP_PULL_UP_0P6KOHM:
337 arg = 600;
338 break;
339 case PMIC_MPP_PULL_UP_10KOHM:
340 arg = 10000;
341 break;
342 case PMIC_MPP_PULL_UP_30KOHM:
343 arg = 30000;
344 break;
345 default:
346 return -EINVAL;
347 }
348 break;
349 case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
350 arg = !pad->is_enabled;
351 break;
352 case PIN_CONFIG_POWER_SOURCE:
353 arg = pad->power_source;
354 break;
355 case PIN_CONFIG_INPUT_ENABLE:
356 arg = pad->input_enabled;
357 break;
358 case PIN_CONFIG_OUTPUT:
359 arg = pad->out_value;
360 break;
361 case PMIC_MPP_CONF_AMUX_ROUTE:
362 arg = pad->amux_input;
363 break;
Bjorn Andersson0e948042015-06-17 23:47:28 -0700364 case PIN_CONFIG_DRIVE_STRENGTH:
365 arg = pad->drive_strength;
366 break;
Bjorn Andersson099f3e42015-07-14 23:40:33 -0700367 case PMIC_MPP_CONF_ANALOG_LEVEL:
368 arg = pad->aout_level;
369 break;
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300370 case PMIC_MPP_CONF_ANALOG_MODE:
371 arg = pad->analog_mode;
372 break;
Bjorn Andersson0e948042015-06-17 23:47:28 -0700373 case PMIC_MPP_CONF_SINK_MODE:
374 arg = pad->sink_mode;
375 break;
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300376 default:
377 return -EINVAL;
378 }
379
380 /* Convert register value to pinconf value */
381 *config = pinconf_to_config_packed(param, arg);
382 return 0;
383}
384
385static int pmic_mpp_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
386 unsigned long *configs, unsigned nconfs)
387{
388 struct pmic_mpp_state *state = pinctrl_dev_get_drvdata(pctldev);
389 struct pmic_mpp_pad *pad;
390 unsigned param, arg;
391 unsigned int val;
392 int i, ret;
393
394 pad = pctldev->desc->pins[pin].drv_data;
395
Bjorn Andersson7682b372015-06-17 23:47:26 -0700396 /* Make it possible to enable the pin, by not setting high impedance */
397 pad->is_enabled = true;
398
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300399 for (i = 0; i < nconfs; i++) {
400 param = pinconf_to_config_param(configs[i]);
401 arg = pinconf_to_config_argument(configs[i]);
402
403 switch (param) {
404 case PIN_CONFIG_BIAS_DISABLE:
405 pad->pullup = PMIC_MPP_PULL_UP_OPEN;
406 break;
407 case PIN_CONFIG_BIAS_PULL_UP:
408 switch (arg) {
409 case 600:
410 pad->pullup = PMIC_MPP_PULL_UP_0P6KOHM;
411 break;
412 case 10000:
413 pad->pullup = PMIC_MPP_PULL_UP_10KOHM;
414 break;
415 case 30000:
416 pad->pullup = PMIC_MPP_PULL_UP_30KOHM;
417 break;
418 default:
419 return -EINVAL;
420 }
421 break;
422 case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
423 pad->is_enabled = false;
424 break;
425 case PIN_CONFIG_POWER_SOURCE:
426 if (arg >= pad->num_sources)
427 return -EINVAL;
428 pad->power_source = arg;
429 break;
430 case PIN_CONFIG_INPUT_ENABLE:
431 pad->input_enabled = arg ? true : false;
432 break;
433 case PIN_CONFIG_OUTPUT:
434 pad->output_enabled = true;
435 pad->out_value = arg;
436 break;
Bjorn Andersson0e948042015-06-17 23:47:28 -0700437 case PIN_CONFIG_DRIVE_STRENGTH:
438 arg = pad->drive_strength;
439 break;
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300440 case PMIC_MPP_CONF_AMUX_ROUTE:
441 if (arg >= PMIC_MPP_AMUX_ROUTE_ABUS4)
442 return -EINVAL;
443 pad->amux_input = arg;
444 break;
Bjorn Andersson099f3e42015-07-14 23:40:33 -0700445 case PMIC_MPP_CONF_ANALOG_LEVEL:
446 pad->aout_level = arg;
447 break;
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300448 case PMIC_MPP_CONF_ANALOG_MODE:
Bjorn Andersson0e948042015-06-17 23:47:28 -0700449 pad->analog_mode = !!arg;
450 break;
451 case PMIC_MPP_CONF_SINK_MODE:
452 pad->sink_mode = !!arg;
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300453 break;
454 default:
455 return -EINVAL;
456 }
457 }
458
459 val = pad->power_source << PMIC_MPP_REG_VIN_SHIFT;
460
461 ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_DIG_VIN_CTL, val);
462 if (ret < 0)
463 return ret;
464
465 val = pad->pullup << PMIC_MPP_REG_PULL_SHIFT;
466
467 ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_DIG_PULL_CTL, val);
468 if (ret < 0)
469 return ret;
470
471 val = pad->amux_input & PMIC_MPP_REG_AIN_ROUTE_MASK;
472
473 ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_AIN_CTL, val);
474 if (ret < 0)
475 return ret;
476
Bjorn Andersson099f3e42015-07-14 23:40:33 -0700477 ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_AOUT_CTL, pad->aout_level);
478 if (ret < 0)
479 return ret;
480
Bjorn Andersson0e948042015-06-17 23:47:28 -0700481 ret = pmic_mpp_write_mode_ctl(state, pad);
Bjorn Andersson7682b372015-06-17 23:47:26 -0700482 if (ret < 0)
483 return ret;
484
485 val = pad->is_enabled << PMIC_MPP_REG_MASTER_EN_SHIFT;
486
487 return pmic_mpp_write(state, pad, PMIC_MPP_REG_EN_CTL, val);
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300488}
489
490static void pmic_mpp_config_dbg_show(struct pinctrl_dev *pctldev,
491 struct seq_file *s, unsigned pin)
492{
493 struct pmic_mpp_state *state = pinctrl_dev_get_drvdata(pctldev);
494 struct pmic_mpp_pad *pad;
Bjorn Andersson7682b372015-06-17 23:47:26 -0700495 int ret;
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300496
497 static const char *const biases[] = {
498 "0.6kOhm", "10kOhm", "30kOhm", "Disabled"
499 };
500
Bjorn Andersson0e948042015-06-17 23:47:28 -0700501 static const char *const modes[] = {
502 "digital", "analog", "sink"
503 };
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300504
505 pad = pctldev->desc->pins[pin].drv_data;
506
507 seq_printf(s, " mpp%-2d:", pin + PMIC_MPP_PHYSICAL_OFFSET);
508
Bjorn Andersson7682b372015-06-17 23:47:26 -0700509 if (!pad->is_enabled) {
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300510 seq_puts(s, " ---");
511 } else {
512
513 if (pad->input_enabled) {
514 ret = pmic_mpp_read(state, pad, PMIC_MPP_REG_RT_STS);
Ivan T. Ivanov4e637ac2015-04-09 18:18:37 +0300515 if (ret < 0)
516 return;
517
518 ret &= PMIC_MPP_REG_RT_STS_VAL_MASK;
519 pad->out_value = ret;
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300520 }
521
522 seq_printf(s, " %-4s", pad->output_enabled ? "out" : "in");
Bjorn Andersson0e948042015-06-17 23:47:28 -0700523 seq_printf(s, " %-7s", modes[pad->analog_mode ? 1 : (pad->sink_mode ? 2 : 0)]);
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300524 seq_printf(s, " %-7s", pmic_mpp_functions[pad->function]);
525 seq_printf(s, " vin-%d", pad->power_source);
Bjorn Andersson099f3e42015-07-14 23:40:33 -0700526 seq_printf(s, " %d", pad->aout_level);
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300527 seq_printf(s, " %-8s", biases[pad->pullup]);
528 seq_printf(s, " %-4s", pad->out_value ? "high" : "low");
529 }
530}
531
532static const struct pinconf_ops pmic_mpp_pinconf_ops = {
Bjorn Anderssonba5f94c2015-06-17 23:47:25 -0700533 .is_generic = true,
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300534 .pin_config_group_get = pmic_mpp_config_get,
535 .pin_config_group_set = pmic_mpp_config_set,
536 .pin_config_group_dbg_show = pmic_mpp_config_dbg_show,
537};
538
539static int pmic_mpp_direction_input(struct gpio_chip *chip, unsigned pin)
540{
541 struct pmic_mpp_state *state = to_mpp_state(chip);
542 unsigned long config;
543
544 config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1);
545
546 return pmic_mpp_config_set(state->ctrl, pin, &config, 1);
547}
548
549static int pmic_mpp_direction_output(struct gpio_chip *chip,
550 unsigned pin, int val)
551{
552 struct pmic_mpp_state *state = to_mpp_state(chip);
553 unsigned long config;
554
555 config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val);
556
557 return pmic_mpp_config_set(state->ctrl, pin, &config, 1);
558}
559
560static int pmic_mpp_get(struct gpio_chip *chip, unsigned pin)
561{
562 struct pmic_mpp_state *state = to_mpp_state(chip);
563 struct pmic_mpp_pad *pad;
564 int ret;
565
566 pad = state->ctrl->desc->pins[pin].drv_data;
567
568 if (pad->input_enabled) {
569 ret = pmic_mpp_read(state, pad, PMIC_MPP_REG_RT_STS);
570 if (ret < 0)
571 return ret;
572
573 pad->out_value = ret & PMIC_MPP_REG_RT_STS_VAL_MASK;
574 }
575
576 return pad->out_value;
577}
578
579static void pmic_mpp_set(struct gpio_chip *chip, unsigned pin, int value)
580{
581 struct pmic_mpp_state *state = to_mpp_state(chip);
582 unsigned long config;
583
584 config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value);
585
586 pmic_mpp_config_set(state->ctrl, pin, &config, 1);
587}
588
589static int pmic_mpp_request(struct gpio_chip *chip, unsigned base)
590{
591 return pinctrl_request_gpio(chip->base + base);
592}
593
594static void pmic_mpp_free(struct gpio_chip *chip, unsigned base)
595{
596 pinctrl_free_gpio(chip->base + base);
597}
598
599static int pmic_mpp_of_xlate(struct gpio_chip *chip,
600 const struct of_phandle_args *gpio_desc,
601 u32 *flags)
602{
603 if (chip->of_gpio_n_cells < 2)
604 return -EINVAL;
605
606 if (flags)
607 *flags = gpio_desc->args[1];
608
609 return gpio_desc->args[0] - PMIC_MPP_PHYSICAL_OFFSET;
610}
611
612static int pmic_mpp_to_irq(struct gpio_chip *chip, unsigned pin)
613{
614 struct pmic_mpp_state *state = to_mpp_state(chip);
615 struct pmic_mpp_pad *pad;
616
617 pad = state->ctrl->desc->pins[pin].drv_data;
618
619 return pad->irq;
620}
621
622static void pmic_mpp_dbg_show(struct seq_file *s, struct gpio_chip *chip)
623{
624 struct pmic_mpp_state *state = to_mpp_state(chip);
625 unsigned i;
626
627 for (i = 0; i < chip->ngpio; i++) {
628 pmic_mpp_config_dbg_show(state->ctrl, s, i);
629 seq_puts(s, "\n");
630 }
631}
632
633static const struct gpio_chip pmic_mpp_gpio_template = {
634 .direction_input = pmic_mpp_direction_input,
635 .direction_output = pmic_mpp_direction_output,
636 .get = pmic_mpp_get,
637 .set = pmic_mpp_set,
638 .request = pmic_mpp_request,
639 .free = pmic_mpp_free,
640 .of_xlate = pmic_mpp_of_xlate,
641 .to_irq = pmic_mpp_to_irq,
642 .dbg_show = pmic_mpp_dbg_show,
643};
644
645static int pmic_mpp_populate(struct pmic_mpp_state *state,
646 struct pmic_mpp_pad *pad)
647{
648 int type, subtype, val, dir;
649
650 type = pmic_mpp_read(state, pad, PMIC_MPP_REG_TYPE);
651 if (type < 0)
652 return type;
653
654 if (type != PMIC_MPP_TYPE) {
655 dev_err(state->dev, "incorrect block type 0x%x at 0x%x\n",
656 type, pad->base);
657 return -ENODEV;
658 }
659
660 subtype = pmic_mpp_read(state, pad, PMIC_MPP_REG_SUBTYPE);
661 if (subtype < 0)
662 return subtype;
663
664 switch (subtype) {
665 case PMIC_MPP_SUBTYPE_4CH_NO_ANA_OUT:
666 case PMIC_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT:
667 case PMIC_MPP_SUBTYPE_4CH_NO_SINK:
668 case PMIC_MPP_SUBTYPE_ULT_4CH_NO_SINK:
669 case PMIC_MPP_SUBTYPE_4CH_FULL_FUNC:
670 pad->num_sources = 4;
671 break;
672 case PMIC_MPP_SUBTYPE_8CH_FULL_FUNC:
673 pad->num_sources = 8;
674 break;
675 default:
676 dev_err(state->dev, "unknown MPP type 0x%x at 0x%x\n",
677 subtype, pad->base);
678 return -ENODEV;
679 }
680
681 val = pmic_mpp_read(state, pad, PMIC_MPP_REG_MODE_CTL);
682 if (val < 0)
683 return val;
684
685 pad->out_value = val & PMIC_MPP_REG_MODE_VALUE_MASK;
686
687 dir = val >> PMIC_MPP_REG_MODE_DIR_SHIFT;
688 dir &= PMIC_MPP_REG_MODE_DIR_MASK;
689
690 switch (dir) {
Bjorn Anderssoneaaf5dd2015-06-17 23:47:27 -0700691 case PMIC_MPP_MODE_DIGITAL_INPUT:
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300692 pad->input_enabled = true;
693 pad->output_enabled = false;
694 pad->analog_mode = false;
Bjorn Andersson0e948042015-06-17 23:47:28 -0700695 pad->sink_mode = false;
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300696 break;
Bjorn Anderssoneaaf5dd2015-06-17 23:47:27 -0700697 case PMIC_MPP_MODE_DIGITAL_OUTPUT:
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300698 pad->input_enabled = false;
699 pad->output_enabled = true;
700 pad->analog_mode = false;
Bjorn Andersson0e948042015-06-17 23:47:28 -0700701 pad->sink_mode = false;
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300702 break;
Bjorn Anderssoneaaf5dd2015-06-17 23:47:27 -0700703 case PMIC_MPP_MODE_DIGITAL_BIDIR:
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300704 pad->input_enabled = true;
705 pad->output_enabled = true;
706 pad->analog_mode = false;
Bjorn Andersson0e948042015-06-17 23:47:28 -0700707 pad->sink_mode = false;
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300708 break;
Bjorn Anderssoneaaf5dd2015-06-17 23:47:27 -0700709 case PMIC_MPP_MODE_ANALOG_BIDIR:
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300710 pad->input_enabled = true;
711 pad->output_enabled = true;
712 pad->analog_mode = true;
Bjorn Andersson0e948042015-06-17 23:47:28 -0700713 pad->sink_mode = false;
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300714 break;
Bjorn Anderssoneaaf5dd2015-06-17 23:47:27 -0700715 case PMIC_MPP_MODE_ANALOG_INPUT:
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300716 pad->input_enabled = true;
717 pad->output_enabled = false;
718 pad->analog_mode = true;
Bjorn Andersson0e948042015-06-17 23:47:28 -0700719 pad->sink_mode = false;
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300720 break;
Bjorn Anderssoneaaf5dd2015-06-17 23:47:27 -0700721 case PMIC_MPP_MODE_ANALOG_OUTPUT:
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300722 pad->input_enabled = false;
723 pad->output_enabled = true;
724 pad->analog_mode = true;
Bjorn Andersson0e948042015-06-17 23:47:28 -0700725 pad->sink_mode = false;
726 break;
727 case PMIC_MPP_MODE_CURRENT_SINK:
728 pad->input_enabled = false;
729 pad->output_enabled = true;
730 pad->analog_mode = false;
731 pad->sink_mode = true;
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300732 break;
733 default:
734 dev_err(state->dev, "unknown MPP direction\n");
735 return -ENODEV;
736 }
737
738 pad->function = val >> PMIC_MPP_REG_MODE_FUNCTION_SHIFT;
739 pad->function &= PMIC_MPP_REG_MODE_FUNCTION_MASK;
740
741 val = pmic_mpp_read(state, pad, PMIC_MPP_REG_DIG_VIN_CTL);
742 if (val < 0)
743 return val;
744
745 pad->power_source = val >> PMIC_MPP_REG_VIN_SHIFT;
746 pad->power_source &= PMIC_MPP_REG_VIN_MASK;
747
748 val = pmic_mpp_read(state, pad, PMIC_MPP_REG_DIG_PULL_CTL);
749 if (val < 0)
750 return val;
751
752 pad->pullup = val >> PMIC_MPP_REG_PULL_SHIFT;
753 pad->pullup &= PMIC_MPP_REG_PULL_MASK;
754
755 val = pmic_mpp_read(state, pad, PMIC_MPP_REG_AIN_CTL);
756 if (val < 0)
757 return val;
758
759 pad->amux_input = val >> PMIC_MPP_REG_AIN_ROUTE_SHIFT;
760 pad->amux_input &= PMIC_MPP_REG_AIN_ROUTE_MASK;
761
Bjorn Andersson0e948042015-06-17 23:47:28 -0700762 val = pmic_mpp_read(state, pad, PMIC_MPP_REG_SINK_CTL);
763 if (val < 0)
764 return val;
765
766 pad->drive_strength = val;
767
Bjorn Andersson099f3e42015-07-14 23:40:33 -0700768 val = pmic_mpp_read(state, pad, PMIC_MPP_REG_AOUT_CTL);
769 if (val < 0)
770 return val;
771
772 pad->aout_level = val;
773
Bjorn Andersson7682b372015-06-17 23:47:26 -0700774 val = pmic_mpp_read(state, pad, PMIC_MPP_REG_EN_CTL);
775 if (val < 0)
776 return val;
777
778 pad->is_enabled = !!val;
779
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300780 return 0;
781}
782
783static int pmic_mpp_probe(struct platform_device *pdev)
784{
785 struct device *dev = &pdev->dev;
786 struct pinctrl_pin_desc *pindesc;
787 struct pinctrl_desc *pctrldesc;
788 struct pmic_mpp_pad *pad, *pads;
789 struct pmic_mpp_state *state;
790 int ret, npins, i;
791 u32 res[2];
792
793 ret = of_property_read_u32_array(dev->of_node, "reg", res, 2);
794 if (ret < 0) {
795 dev_err(dev, "missing base address and/or range");
796 return ret;
797 }
798
799 npins = res[1] / PMIC_MPP_ADDRESS_RANGE;
800 if (!npins)
801 return -EINVAL;
802
803 BUG_ON(npins > ARRAY_SIZE(pmic_mpp_groups));
804
805 state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
806 if (!state)
807 return -ENOMEM;
808
809 platform_set_drvdata(pdev, state);
810
811 state->dev = &pdev->dev;
812 state->map = dev_get_regmap(dev->parent, NULL);
813
814 pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
815 if (!pindesc)
816 return -ENOMEM;
817
818 pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
819 if (!pads)
820 return -ENOMEM;
821
822 pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL);
823 if (!pctrldesc)
824 return -ENOMEM;
825
826 pctrldesc->pctlops = &pmic_mpp_pinctrl_ops;
827 pctrldesc->pmxops = &pmic_mpp_pinmux_ops;
828 pctrldesc->confops = &pmic_mpp_pinconf_ops;
829 pctrldesc->owner = THIS_MODULE;
830 pctrldesc->name = dev_name(dev);
831 pctrldesc->pins = pindesc;
832 pctrldesc->npins = npins;
833
Bjorn Anderssonba5f94c2015-06-17 23:47:25 -0700834 pctrldesc->num_custom_params = ARRAY_SIZE(pmic_mpp_bindings);
835 pctrldesc->custom_params = pmic_mpp_bindings;
836#ifdef CONFIG_DEBUG_FS
837 pctrldesc->custom_conf_items = pmic_conf_items;
838#endif
839
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300840 for (i = 0; i < npins; i++, pindesc++) {
841 pad = &pads[i];
842 pindesc->drv_data = pad;
843 pindesc->number = i;
844 pindesc->name = pmic_mpp_groups[i];
845
846 pad->irq = platform_get_irq(pdev, i);
847 if (pad->irq < 0)
848 return pad->irq;
849
850 pad->base = res[0] + i * PMIC_MPP_ADDRESS_RANGE;
851
852 ret = pmic_mpp_populate(state, pad);
853 if (ret < 0)
854 return ret;
855 }
856
857 state->chip = pmic_mpp_gpio_template;
858 state->chip.dev = dev;
859 state->chip.base = -1;
860 state->chip.ngpio = npins;
861 state->chip.label = dev_name(dev);
862 state->chip.of_gpio_n_cells = 2;
863 state->chip.can_sleep = false;
864
865 state->ctrl = pinctrl_register(pctrldesc, dev, state);
Masahiro Yamada323de9e2015-06-09 13:01:16 +0900866 if (IS_ERR(state->ctrl))
867 return PTR_ERR(state->ctrl);
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300868
869 ret = gpiochip_add(&state->chip);
870 if (ret) {
871 dev_err(state->dev, "can't add gpio chip\n");
872 goto err_chip;
873 }
874
875 ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, npins);
876 if (ret) {
877 dev_err(dev, "failed to add pin range\n");
878 goto err_range;
879 }
880
881 return 0;
882
883err_range:
884 gpiochip_remove(&state->chip);
885err_chip:
886 pinctrl_unregister(state->ctrl);
887 return ret;
888}
889
890static int pmic_mpp_remove(struct platform_device *pdev)
891{
892 struct pmic_mpp_state *state = platform_get_drvdata(pdev);
893
894 gpiochip_remove(&state->chip);
895 pinctrl_unregister(state->ctrl);
896 return 0;
897}
898
899static const struct of_device_id pmic_mpp_of_match[] = {
900 { .compatible = "qcom,pm8841-mpp" }, /* 4 MPP's */
Ivan T. Ivanov7414b092015-03-31 12:37:18 +0300901 { .compatible = "qcom,pm8916-mpp" }, /* 4 MPP's */
Ivan T. Ivanovcfb24f62014-10-22 12:58:47 +0300902 { .compatible = "qcom,pm8941-mpp" }, /* 8 MPP's */
903 { .compatible = "qcom,pma8084-mpp" }, /* 8 MPP's */
904 { },
905};
906
907MODULE_DEVICE_TABLE(of, pmic_mpp_of_match);
908
909static struct platform_driver pmic_mpp_driver = {
910 .driver = {
911 .name = "qcom-spmi-mpp",
912 .of_match_table = pmic_mpp_of_match,
913 },
914 .probe = pmic_mpp_probe,
915 .remove = pmic_mpp_remove,
916};
917
918module_platform_driver(pmic_mpp_driver);
919
920MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
921MODULE_DESCRIPTION("Qualcomm SPMI PMIC MPP pin control driver");
922MODULE_ALIAS("platform:qcom-spmi-mpp");
923MODULE_LICENSE("GPL v2");