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