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", ®); 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