1eadff302SIvan T. Ivanov /* 2eadff302SIvan T. Ivanov * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. 3eadff302SIvan T. Ivanov * 4eadff302SIvan T. Ivanov * This program is free software; you can redistribute it and/or modify 5eadff302SIvan T. Ivanov * it under the terms of the GNU General Public License version 2 and 6eadff302SIvan T. Ivanov * only version 2 as published by the Free Software Foundation. 7eadff302SIvan T. Ivanov * 8eadff302SIvan T. Ivanov * This program is distributed in the hope that it will be useful, 9eadff302SIvan T. Ivanov * but WITHOUT ANY WARRANTY; without even the implied warranty of 10eadff302SIvan T. Ivanov * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11eadff302SIvan T. Ivanov * GNU General Public License for more details. 12eadff302SIvan T. Ivanov */ 13eadff302SIvan T. Ivanov 141c5fb66aSLinus Walleij #include <linux/gpio/driver.h> 15eadff302SIvan T. Ivanov #include <linux/module.h> 16eadff302SIvan T. Ivanov #include <linux/of.h> 17ab4256cfSStephen Boyd #include <linux/of_irq.h> 18eadff302SIvan T. Ivanov #include <linux/pinctrl/pinconf-generic.h> 19eadff302SIvan T. Ivanov #include <linux/pinctrl/pinconf.h> 20eadff302SIvan T. Ivanov #include <linux/pinctrl/pinmux.h> 21eadff302SIvan T. Ivanov #include <linux/platform_device.h> 22eadff302SIvan T. Ivanov #include <linux/regmap.h> 23eadff302SIvan T. Ivanov #include <linux/slab.h> 24eadff302SIvan T. Ivanov #include <linux/types.h> 25eadff302SIvan T. Ivanov 26eadff302SIvan T. Ivanov #include <dt-bindings/pinctrl/qcom,pmic-gpio.h> 27eadff302SIvan T. Ivanov 28eadff302SIvan T. Ivanov #include "../core.h" 29eadff302SIvan T. Ivanov #include "../pinctrl-utils.h" 30eadff302SIvan T. Ivanov 31eadff302SIvan T. Ivanov #define PMIC_GPIO_ADDRESS_RANGE 0x100 32eadff302SIvan T. Ivanov 33eadff302SIvan T. Ivanov /* type and subtype registers base address offsets */ 34eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_TYPE 0x4 35eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_SUBTYPE 0x5 36eadff302SIvan T. Ivanov 37eadff302SIvan T. Ivanov /* GPIO peripheral type and subtype out_values */ 38eadff302SIvan T. Ivanov #define PMIC_GPIO_TYPE 0x10 39eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIO_4CH 0x1 40eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIOC_4CH 0x5 41eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIO_8CH 0x9 42eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIOC_8CH 0xd 43d7b5f5ccSFenglin Wu #define PMIC_GPIO_SUBTYPE_GPIO_LV 0x10 44d7b5f5ccSFenglin Wu #define PMIC_GPIO_SUBTYPE_GPIO_MV 0x11 45eadff302SIvan T. Ivanov 46eadff302SIvan T. Ivanov #define PMIC_MPP_REG_RT_STS 0x10 47eadff302SIvan T. Ivanov #define PMIC_MPP_REG_RT_STS_VAL_MASK 0x1 48eadff302SIvan T. Ivanov 49eadff302SIvan T. Ivanov /* control register base address offsets */ 50eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_CTL 0x40 51eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_VIN_CTL 0x41 52eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_PULL_CTL 0x42 53d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL 0x44 54223463fcSFenglin Wu #define PMIC_GPIO_REG_DIG_IN_CTL 0x43 55eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_OUT_CTL 0x45 56eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_EN_CTL 0x46 57d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL 0x4A 58eadff302SIvan T. Ivanov 59eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_MODE_CTL */ 60eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_VALUE_SHIFT 0x1 61eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_FUNCTION_SHIFT 1 62eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_FUNCTION_MASK 0x7 63eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_DIR_SHIFT 4 64eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_DIR_MASK 0x7 65eadff302SIvan T. Ivanov 66d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_INPUT 0 67d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_OUTPUT 1 68d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT 2 69d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_ANALOG_PASS_THRU 3 70d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_MODE_DIR_MASK 0x3 71d7b5f5ccSFenglin Wu 72eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_VIN_CTL */ 73eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_VIN_SHIFT 0 74eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_VIN_MASK 0x7 75eadff302SIvan T. Ivanov 76eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_PULL_CTL */ 77eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_PULL_SHIFT 0 78eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_PULL_MASK 0x7 79eadff302SIvan T. Ivanov 80eadff302SIvan T. Ivanov #define PMIC_GPIO_PULL_DOWN 4 81eadff302SIvan T. Ivanov #define PMIC_GPIO_PULL_DISABLE 5 82eadff302SIvan T. Ivanov 83d7b5f5ccSFenglin Wu /* PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL for LV/MV */ 84d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_INVERT 0x80 85d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT 7 86d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK 0xF 87d7b5f5ccSFenglin Wu 88223463fcSFenglin Wu /* PMIC_GPIO_REG_DIG_IN_CTL */ 89223463fcSFenglin Wu #define PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN 0x80 90223463fcSFenglin Wu #define PMIC_GPIO_LV_MV_DIG_IN_DTEST_SEL_MASK 0x7 91223463fcSFenglin Wu #define PMIC_GPIO_DIG_IN_DTEST_SEL_MASK 0xf 92223463fcSFenglin Wu 93eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_OUT_CTL */ 94eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_STRENGTH_SHIFT 0 95eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_STRENGTH_MASK 0x3 96eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_TYPE_SHIFT 4 97eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_TYPE_MASK 0x3 98eadff302SIvan T. Ivanov 99eadff302SIvan T. Ivanov /* 100eadff302SIvan T. Ivanov * Output type - indicates pin should be configured as push-pull, 101eadff302SIvan T. Ivanov * open drain or open source. 102eadff302SIvan T. Ivanov */ 103eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_CMOS 0 104eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS 1 105eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS 2 106eadff302SIvan T. Ivanov 107eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_EN_CTL */ 108eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MASTER_EN_SHIFT 7 109eadff302SIvan T. Ivanov 110eadff302SIvan T. Ivanov #define PMIC_GPIO_PHYSICAL_OFFSET 1 111eadff302SIvan T. Ivanov 112d7b5f5ccSFenglin Wu /* PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL */ 113d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_ANA_MUX_SEL_MASK 0x3 114d7b5f5ccSFenglin Wu 115eadff302SIvan T. Ivanov /* Qualcomm specific pin configurations */ 116eadff302SIvan T. Ivanov #define PMIC_GPIO_CONF_PULL_UP (PIN_CONFIG_END + 1) 117eadff302SIvan T. Ivanov #define PMIC_GPIO_CONF_STRENGTH (PIN_CONFIG_END + 2) 118d7b5f5ccSFenglin Wu #define PMIC_GPIO_CONF_ATEST (PIN_CONFIG_END + 3) 119d7b5f5ccSFenglin Wu #define PMIC_GPIO_CONF_ANALOG_PASS (PIN_CONFIG_END + 4) 120223463fcSFenglin Wu #define PMIC_GPIO_CONF_DTEST_BUFFER (PIN_CONFIG_END + 5) 121d7b5f5ccSFenglin Wu 122d7b5f5ccSFenglin Wu /* The index of each function in pmic_gpio_functions[] array */ 123d7b5f5ccSFenglin Wu enum pmic_gpio_func_index { 124d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_NORMAL, 125d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_PAIRED, 126d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_FUNC1, 127d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_FUNC2, 128d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_FUNC3, 129d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_FUNC4, 130d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_DTEST1, 131d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_DTEST2, 132d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_DTEST3, 133d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_DTEST4, 134d7b5f5ccSFenglin Wu }; 135eadff302SIvan T. Ivanov 136eadff302SIvan T. Ivanov /** 137eadff302SIvan T. Ivanov * struct pmic_gpio_pad - keep current GPIO settings 138eadff302SIvan T. Ivanov * @base: Address base in SPMI device. 139eadff302SIvan T. Ivanov * @irq: IRQ number which this GPIO generate. 140eadff302SIvan T. Ivanov * @is_enabled: Set to false when GPIO should be put in high Z state. 141eadff302SIvan T. Ivanov * @out_value: Cached pin output value 142eadff302SIvan T. Ivanov * @have_buffer: Set to true if GPIO output could be configured in push-pull, 143eadff302SIvan T. Ivanov * open-drain or open-source mode. 144eadff302SIvan T. Ivanov * @output_enabled: Set to true if GPIO output logic is enabled. 145eadff302SIvan T. Ivanov * @input_enabled: Set to true if GPIO input buffer logic is enabled. 146d7b5f5ccSFenglin Wu * @analog_pass: Set to true if GPIO is in analog-pass-through mode. 147d7b5f5ccSFenglin Wu * @lv_mv_type: Set to true if GPIO subtype is GPIO_LV(0x10) or GPIO_MV(0x11). 148eadff302SIvan T. Ivanov * @num_sources: Number of power-sources supported by this GPIO. 149eadff302SIvan T. Ivanov * @power_source: Current power-source used. 150eadff302SIvan T. Ivanov * @buffer_type: Push-pull, open-drain or open-source. 151eadff302SIvan T. Ivanov * @pullup: Constant current which flow trough GPIO output buffer. 152eadff302SIvan T. Ivanov * @strength: No, Low, Medium, High 153eadff302SIvan T. Ivanov * @function: See pmic_gpio_functions[] 154d7b5f5ccSFenglin Wu * @atest: the ATEST selection for GPIO analog-pass-through mode 155223463fcSFenglin Wu * @dtest_buffer: the DTEST buffer selection for digital input mode. 156eadff302SIvan T. Ivanov */ 157eadff302SIvan T. Ivanov struct pmic_gpio_pad { 158eadff302SIvan T. Ivanov u16 base; 159eadff302SIvan T. Ivanov int irq; 160eadff302SIvan T. Ivanov bool is_enabled; 161eadff302SIvan T. Ivanov bool out_value; 162eadff302SIvan T. Ivanov bool have_buffer; 163eadff302SIvan T. Ivanov bool output_enabled; 164eadff302SIvan T. Ivanov bool input_enabled; 165d7b5f5ccSFenglin Wu bool analog_pass; 166d7b5f5ccSFenglin Wu bool lv_mv_type; 167eadff302SIvan T. Ivanov unsigned int num_sources; 168eadff302SIvan T. Ivanov unsigned int power_source; 169eadff302SIvan T. Ivanov unsigned int buffer_type; 170eadff302SIvan T. Ivanov unsigned int pullup; 171eadff302SIvan T. Ivanov unsigned int strength; 172eadff302SIvan T. Ivanov unsigned int function; 173d7b5f5ccSFenglin Wu unsigned int atest; 174223463fcSFenglin Wu unsigned int dtest_buffer; 175eadff302SIvan T. Ivanov }; 176eadff302SIvan T. Ivanov 177eadff302SIvan T. Ivanov struct pmic_gpio_state { 178eadff302SIvan T. Ivanov struct device *dev; 179eadff302SIvan T. Ivanov struct regmap *map; 180eadff302SIvan T. Ivanov struct pinctrl_dev *ctrl; 181eadff302SIvan T. Ivanov struct gpio_chip chip; 182eadff302SIvan T. Ivanov }; 183eadff302SIvan T. Ivanov 184f684e4acSLinus Walleij static const struct pinconf_generic_params pmic_gpio_bindings[] = { 1857382b623SSoren Brinkmann {"qcom,pull-up-strength", PMIC_GPIO_CONF_PULL_UP, 0}, 1867382b623SSoren Brinkmann {"qcom,drive-strength", PMIC_GPIO_CONF_STRENGTH, 0}, 187d7b5f5ccSFenglin Wu {"qcom,atest", PMIC_GPIO_CONF_ATEST, 0}, 188d7b5f5ccSFenglin Wu {"qcom,analog-pass", PMIC_GPIO_CONF_ANALOG_PASS, 0}, 189223463fcSFenglin Wu {"qcom,dtest-buffer", PMIC_GPIO_CONF_DTEST_BUFFER, 0}, 190eadff302SIvan T. Ivanov }; 191eadff302SIvan T. Ivanov 1924f06266aSArnd Bergmann #ifdef CONFIG_DEBUG_FS 1937382b623SSoren Brinkmann static const struct pin_config_item pmic_conf_items[ARRAY_SIZE(pmic_gpio_bindings)] = { 1947382b623SSoren Brinkmann PCONFDUMP(PMIC_GPIO_CONF_PULL_UP, "pull up strength", NULL, true), 1957382b623SSoren Brinkmann PCONFDUMP(PMIC_GPIO_CONF_STRENGTH, "drive-strength", NULL, true), 196d7b5f5ccSFenglin Wu PCONFDUMP(PMIC_GPIO_CONF_ATEST, "atest", NULL, true), 197d7b5f5ccSFenglin Wu PCONFDUMP(PMIC_GPIO_CONF_ANALOG_PASS, "analog-pass", NULL, true), 198223463fcSFenglin Wu PCONFDUMP(PMIC_GPIO_CONF_DTEST_BUFFER, "dtest-buffer", NULL, true), 199eadff302SIvan T. Ivanov }; 2004f06266aSArnd Bergmann #endif 201eadff302SIvan T. Ivanov 202eadff302SIvan T. Ivanov static const char *const pmic_gpio_groups[] = { 203eadff302SIvan T. Ivanov "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", "gpio8", 204eadff302SIvan T. Ivanov "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", "gpio15", 205eadff302SIvan T. Ivanov "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22", 206eadff302SIvan T. Ivanov "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29", 207eadff302SIvan T. Ivanov "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36", 208eadff302SIvan T. Ivanov }; 209eadff302SIvan T. Ivanov 210eadff302SIvan T. Ivanov static const char *const pmic_gpio_functions[] = { 211d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_NORMAL] = PMIC_GPIO_FUNC_NORMAL, 212d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_PAIRED] = PMIC_GPIO_FUNC_PAIRED, 213d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_FUNC1] = PMIC_GPIO_FUNC_FUNC1, 214d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_FUNC2] = PMIC_GPIO_FUNC_FUNC2, 215d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_FUNC3] = PMIC_GPIO_FUNC_FUNC3, 216d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_FUNC4] = PMIC_GPIO_FUNC_FUNC4, 217d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_DTEST1] = PMIC_GPIO_FUNC_DTEST1, 218d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_DTEST2] = PMIC_GPIO_FUNC_DTEST2, 219d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_DTEST3] = PMIC_GPIO_FUNC_DTEST3, 220d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_DTEST4] = PMIC_GPIO_FUNC_DTEST4, 221eadff302SIvan T. Ivanov }; 222eadff302SIvan T. Ivanov 223eadff302SIvan T. Ivanov static int pmic_gpio_read(struct pmic_gpio_state *state, 224eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad, unsigned int addr) 225eadff302SIvan T. Ivanov { 226eadff302SIvan T. Ivanov unsigned int val; 227eadff302SIvan T. Ivanov int ret; 228eadff302SIvan T. Ivanov 229eadff302SIvan T. Ivanov ret = regmap_read(state->map, pad->base + addr, &val); 230eadff302SIvan T. Ivanov if (ret < 0) 231eadff302SIvan T. Ivanov dev_err(state->dev, "read 0x%x failed\n", addr); 232eadff302SIvan T. Ivanov else 233eadff302SIvan T. Ivanov ret = val; 234eadff302SIvan T. Ivanov 235eadff302SIvan T. Ivanov return ret; 236eadff302SIvan T. Ivanov } 237eadff302SIvan T. Ivanov 238eadff302SIvan T. Ivanov static int pmic_gpio_write(struct pmic_gpio_state *state, 239eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad, unsigned int addr, 240eadff302SIvan T. Ivanov unsigned int val) 241eadff302SIvan T. Ivanov { 242eadff302SIvan T. Ivanov int ret; 243eadff302SIvan T. Ivanov 244eadff302SIvan T. Ivanov ret = regmap_write(state->map, pad->base + addr, val); 245eadff302SIvan T. Ivanov if (ret < 0) 246eadff302SIvan T. Ivanov dev_err(state->dev, "write 0x%x failed\n", addr); 247eadff302SIvan T. Ivanov 248eadff302SIvan T. Ivanov return ret; 249eadff302SIvan T. Ivanov } 250eadff302SIvan T. Ivanov 251eadff302SIvan T. Ivanov static int pmic_gpio_get_groups_count(struct pinctrl_dev *pctldev) 252eadff302SIvan T. Ivanov { 253eadff302SIvan T. Ivanov /* Every PIN is a group */ 254eadff302SIvan T. Ivanov return pctldev->desc->npins; 255eadff302SIvan T. Ivanov } 256eadff302SIvan T. Ivanov 257eadff302SIvan T. Ivanov static const char *pmic_gpio_get_group_name(struct pinctrl_dev *pctldev, 258eadff302SIvan T. Ivanov unsigned pin) 259eadff302SIvan T. Ivanov { 260eadff302SIvan T. Ivanov return pctldev->desc->pins[pin].name; 261eadff302SIvan T. Ivanov } 262eadff302SIvan T. Ivanov 263eadff302SIvan T. Ivanov static int pmic_gpio_get_group_pins(struct pinctrl_dev *pctldev, unsigned pin, 264eadff302SIvan T. Ivanov const unsigned **pins, unsigned *num_pins) 265eadff302SIvan T. Ivanov { 266eadff302SIvan T. Ivanov *pins = &pctldev->desc->pins[pin].number; 267eadff302SIvan T. Ivanov *num_pins = 1; 268eadff302SIvan T. Ivanov return 0; 269eadff302SIvan T. Ivanov } 270eadff302SIvan T. Ivanov 271eadff302SIvan T. Ivanov static const struct pinctrl_ops pmic_gpio_pinctrl_ops = { 272eadff302SIvan T. Ivanov .get_groups_count = pmic_gpio_get_groups_count, 273eadff302SIvan T. Ivanov .get_group_name = pmic_gpio_get_group_name, 274eadff302SIvan T. Ivanov .get_group_pins = pmic_gpio_get_group_pins, 2757382b623SSoren Brinkmann .dt_node_to_map = pinconf_generic_dt_node_to_map_group, 276d32f7fd3SIrina Tirdea .dt_free_map = pinctrl_utils_free_map, 277eadff302SIvan T. Ivanov }; 278eadff302SIvan T. Ivanov 279eadff302SIvan T. Ivanov static int pmic_gpio_get_functions_count(struct pinctrl_dev *pctldev) 280eadff302SIvan T. Ivanov { 281eadff302SIvan T. Ivanov return ARRAY_SIZE(pmic_gpio_functions); 282eadff302SIvan T. Ivanov } 283eadff302SIvan T. Ivanov 284eadff302SIvan T. Ivanov static const char *pmic_gpio_get_function_name(struct pinctrl_dev *pctldev, 285eadff302SIvan T. Ivanov unsigned function) 286eadff302SIvan T. Ivanov { 287eadff302SIvan T. Ivanov return pmic_gpio_functions[function]; 288eadff302SIvan T. Ivanov } 289eadff302SIvan T. Ivanov 290eadff302SIvan T. Ivanov static int pmic_gpio_get_function_groups(struct pinctrl_dev *pctldev, 291eadff302SIvan T. Ivanov unsigned function, 292eadff302SIvan T. Ivanov const char *const **groups, 293eadff302SIvan T. Ivanov unsigned *const num_qgroups) 294eadff302SIvan T. Ivanov { 295eadff302SIvan T. Ivanov *groups = pmic_gpio_groups; 296eadff302SIvan T. Ivanov *num_qgroups = pctldev->desc->npins; 297eadff302SIvan T. Ivanov return 0; 298eadff302SIvan T. Ivanov } 299eadff302SIvan T. Ivanov 300eadff302SIvan T. Ivanov static int pmic_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned function, 301eadff302SIvan T. Ivanov unsigned pin) 302eadff302SIvan T. Ivanov { 303eadff302SIvan T. Ivanov struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev); 304eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad; 305eadff302SIvan T. Ivanov unsigned int val; 306eadff302SIvan T. Ivanov int ret; 307eadff302SIvan T. Ivanov 308d7b5f5ccSFenglin Wu if (function > PMIC_GPIO_FUNC_INDEX_DTEST4) { 309d7b5f5ccSFenglin Wu pr_err("function: %d is not defined\n", function); 310d7b5f5ccSFenglin Wu return -EINVAL; 311d7b5f5ccSFenglin Wu } 312d7b5f5ccSFenglin Wu 313eadff302SIvan T. Ivanov pad = pctldev->desc->pins[pin].drv_data; 314d7b5f5ccSFenglin Wu /* 315d7b5f5ccSFenglin Wu * Non-LV/MV subtypes only support 2 special functions, 316d7b5f5ccSFenglin Wu * offsetting the dtestx function values by 2 317d7b5f5ccSFenglin Wu */ 318d7b5f5ccSFenglin Wu if (!pad->lv_mv_type) { 319d7b5f5ccSFenglin Wu if (function == PMIC_GPIO_FUNC_INDEX_FUNC3 || 320d7b5f5ccSFenglin Wu function == PMIC_GPIO_FUNC_INDEX_FUNC4) { 321d7b5f5ccSFenglin Wu pr_err("LV/MV subtype doesn't have func3/func4\n"); 322d7b5f5ccSFenglin Wu return -EINVAL; 323d7b5f5ccSFenglin Wu } 324d7b5f5ccSFenglin Wu if (function >= PMIC_GPIO_FUNC_INDEX_DTEST1) 325d7b5f5ccSFenglin Wu function -= (PMIC_GPIO_FUNC_INDEX_DTEST1 - 326d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_FUNC3); 327d7b5f5ccSFenglin Wu } 328eadff302SIvan T. Ivanov 329eadff302SIvan T. Ivanov pad->function = function; 330eadff302SIvan T. Ivanov 331d7b5f5ccSFenglin Wu if (pad->analog_pass) 332d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_ANALOG_PASS_THRU; 333d7b5f5ccSFenglin Wu else if (pad->output_enabled && pad->input_enabled) 334d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT; 335d7b5f5ccSFenglin Wu else if (pad->output_enabled) 336d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_OUTPUT; 337eadff302SIvan T. Ivanov else 338d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_INPUT; 339eadff302SIvan T. Ivanov 340d7b5f5ccSFenglin Wu if (pad->lv_mv_type) { 341d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 342d7b5f5ccSFenglin Wu PMIC_GPIO_REG_MODE_CTL, val); 343d7b5f5ccSFenglin Wu if (ret < 0) 344d7b5f5ccSFenglin Wu return ret; 345d7b5f5ccSFenglin Wu 346d7b5f5ccSFenglin Wu val = pad->atest - 1; 347d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 348d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL, val); 349d7b5f5ccSFenglin Wu if (ret < 0) 350d7b5f5ccSFenglin Wu return ret; 351d7b5f5ccSFenglin Wu 352d7b5f5ccSFenglin Wu val = pad->out_value 353d7b5f5ccSFenglin Wu << PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT; 354d7b5f5ccSFenglin Wu val |= pad->function 355d7b5f5ccSFenglin Wu & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK; 356d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 357d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL, val); 358d7b5f5ccSFenglin Wu if (ret < 0) 359d7b5f5ccSFenglin Wu return ret; 360d7b5f5ccSFenglin Wu } else { 361dc391502SIvan T. Ivanov val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT; 362eadff302SIvan T. Ivanov val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT; 363eadff302SIvan T. Ivanov val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT; 364eadff302SIvan T. Ivanov 365eadff302SIvan T. Ivanov ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); 366eadff302SIvan T. Ivanov if (ret < 0) 367eadff302SIvan T. Ivanov return ret; 368d7b5f5ccSFenglin Wu } 369eadff302SIvan T. Ivanov 370eadff302SIvan T. Ivanov val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT; 371eadff302SIvan T. Ivanov 372eadff302SIvan T. Ivanov return pmic_gpio_write(state, pad, PMIC_GPIO_REG_EN_CTL, val); 373eadff302SIvan T. Ivanov } 374eadff302SIvan T. Ivanov 375eadff302SIvan T. Ivanov static const struct pinmux_ops pmic_gpio_pinmux_ops = { 376eadff302SIvan T. Ivanov .get_functions_count = pmic_gpio_get_functions_count, 377eadff302SIvan T. Ivanov .get_function_name = pmic_gpio_get_function_name, 378eadff302SIvan T. Ivanov .get_function_groups = pmic_gpio_get_function_groups, 379eadff302SIvan T. Ivanov .set_mux = pmic_gpio_set_mux, 380eadff302SIvan T. Ivanov }; 381eadff302SIvan T. Ivanov 382eadff302SIvan T. Ivanov static int pmic_gpio_config_get(struct pinctrl_dev *pctldev, 383eadff302SIvan T. Ivanov unsigned int pin, unsigned long *config) 384eadff302SIvan T. Ivanov { 385eadff302SIvan T. Ivanov unsigned param = pinconf_to_config_param(*config); 386eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad; 387eadff302SIvan T. Ivanov unsigned arg; 388eadff302SIvan T. Ivanov 389eadff302SIvan T. Ivanov pad = pctldev->desc->pins[pin].drv_data; 390eadff302SIvan T. Ivanov 391eadff302SIvan T. Ivanov switch (param) { 392eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_PUSH_PULL: 3931cf86bc2SDouglas Anderson if (pad->buffer_type != PMIC_GPIO_OUT_BUF_CMOS) 3941cf86bc2SDouglas Anderson return -EINVAL; 3951cf86bc2SDouglas Anderson arg = 1; 396eadff302SIvan T. Ivanov break; 397eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_OPEN_DRAIN: 3981cf86bc2SDouglas Anderson if (pad->buffer_type != PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS) 3991cf86bc2SDouglas Anderson return -EINVAL; 4001cf86bc2SDouglas Anderson arg = 1; 401eadff302SIvan T. Ivanov break; 402eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_OPEN_SOURCE: 4031cf86bc2SDouglas Anderson if (pad->buffer_type != PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS) 4041cf86bc2SDouglas Anderson return -EINVAL; 4051cf86bc2SDouglas Anderson arg = 1; 406eadff302SIvan T. Ivanov break; 407eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_PULL_DOWN: 4081cf86bc2SDouglas Anderson if (pad->pullup != PMIC_GPIO_PULL_DOWN) 4091cf86bc2SDouglas Anderson return -EINVAL; 4101cf86bc2SDouglas Anderson arg = 1; 411eadff302SIvan T. Ivanov break; 412eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_DISABLE: 4131cf86bc2SDouglas Anderson if (pad->pullup != PMIC_GPIO_PULL_DISABLE) 4141cf86bc2SDouglas Anderson return -EINVAL; 4151cf86bc2SDouglas Anderson arg = 1; 416eadff302SIvan T. Ivanov break; 417eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_PULL_UP: 4181cf86bc2SDouglas Anderson if (pad->pullup != PMIC_GPIO_PULL_UP_30) 4191cf86bc2SDouglas Anderson return -EINVAL; 4201cf86bc2SDouglas Anderson arg = 1; 421eadff302SIvan T. Ivanov break; 422eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: 4231cf86bc2SDouglas Anderson if (pad->is_enabled) 4241cf86bc2SDouglas Anderson return -EINVAL; 4251cf86bc2SDouglas Anderson arg = 1; 426eadff302SIvan T. Ivanov break; 427eadff302SIvan T. Ivanov case PIN_CONFIG_POWER_SOURCE: 428eadff302SIvan T. Ivanov arg = pad->power_source; 429eadff302SIvan T. Ivanov break; 430eadff302SIvan T. Ivanov case PIN_CONFIG_INPUT_ENABLE: 4311cf86bc2SDouglas Anderson if (!pad->input_enabled) 4321cf86bc2SDouglas Anderson return -EINVAL; 4331cf86bc2SDouglas Anderson arg = 1; 434eadff302SIvan T. Ivanov break; 435eadff302SIvan T. Ivanov case PIN_CONFIG_OUTPUT: 436eadff302SIvan T. Ivanov arg = pad->out_value; 437eadff302SIvan T. Ivanov break; 438eadff302SIvan T. Ivanov case PMIC_GPIO_CONF_PULL_UP: 439eadff302SIvan T. Ivanov arg = pad->pullup; 440eadff302SIvan T. Ivanov break; 441eadff302SIvan T. Ivanov case PMIC_GPIO_CONF_STRENGTH: 442eadff302SIvan T. Ivanov arg = pad->strength; 443eadff302SIvan T. Ivanov break; 444d7b5f5ccSFenglin Wu case PMIC_GPIO_CONF_ATEST: 445d7b5f5ccSFenglin Wu arg = pad->atest; 446d7b5f5ccSFenglin Wu break; 447d7b5f5ccSFenglin Wu case PMIC_GPIO_CONF_ANALOG_PASS: 448d7b5f5ccSFenglin Wu arg = pad->analog_pass; 449d7b5f5ccSFenglin Wu break; 450223463fcSFenglin Wu case PMIC_GPIO_CONF_DTEST_BUFFER: 451223463fcSFenglin Wu arg = pad->dtest_buffer; 452223463fcSFenglin Wu break; 453eadff302SIvan T. Ivanov default: 454eadff302SIvan T. Ivanov return -EINVAL; 455eadff302SIvan T. Ivanov } 456eadff302SIvan T. Ivanov 457eadff302SIvan T. Ivanov *config = pinconf_to_config_packed(param, arg); 458eadff302SIvan T. Ivanov return 0; 459eadff302SIvan T. Ivanov } 460eadff302SIvan T. Ivanov 461eadff302SIvan T. Ivanov static int pmic_gpio_config_set(struct pinctrl_dev *pctldev, unsigned int pin, 462eadff302SIvan T. Ivanov unsigned long *configs, unsigned nconfs) 463eadff302SIvan T. Ivanov { 464eadff302SIvan T. Ivanov struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev); 465eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad; 466eadff302SIvan T. Ivanov unsigned param, arg; 467eadff302SIvan T. Ivanov unsigned int val; 468eadff302SIvan T. Ivanov int i, ret; 469eadff302SIvan T. Ivanov 470eadff302SIvan T. Ivanov pad = pctldev->desc->pins[pin].drv_data; 471eadff302SIvan T. Ivanov 4726cb74f44SFenglin Wu pad->is_enabled = true; 473eadff302SIvan T. Ivanov for (i = 0; i < nconfs; i++) { 474eadff302SIvan T. Ivanov param = pinconf_to_config_param(configs[i]); 475eadff302SIvan T. Ivanov arg = pinconf_to_config_argument(configs[i]); 476eadff302SIvan T. Ivanov 477eadff302SIvan T. Ivanov switch (param) { 478eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_PUSH_PULL: 479eadff302SIvan T. Ivanov pad->buffer_type = PMIC_GPIO_OUT_BUF_CMOS; 480eadff302SIvan T. Ivanov break; 481eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_OPEN_DRAIN: 482eadff302SIvan T. Ivanov if (!pad->have_buffer) 483eadff302SIvan T. Ivanov return -EINVAL; 484eadff302SIvan T. Ivanov pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS; 485eadff302SIvan T. Ivanov break; 486eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_OPEN_SOURCE: 487eadff302SIvan T. Ivanov if (!pad->have_buffer) 488eadff302SIvan T. Ivanov return -EINVAL; 489eadff302SIvan T. Ivanov pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS; 490eadff302SIvan T. Ivanov break; 491eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_DISABLE: 492eadff302SIvan T. Ivanov pad->pullup = PMIC_GPIO_PULL_DISABLE; 493eadff302SIvan T. Ivanov break; 494eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_PULL_UP: 495eadff302SIvan T. Ivanov pad->pullup = PMIC_GPIO_PULL_UP_30; 496eadff302SIvan T. Ivanov break; 497eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_PULL_DOWN: 498eadff302SIvan T. Ivanov if (arg) 499eadff302SIvan T. Ivanov pad->pullup = PMIC_GPIO_PULL_DOWN; 500eadff302SIvan T. Ivanov else 501eadff302SIvan T. Ivanov pad->pullup = PMIC_GPIO_PULL_DISABLE; 502eadff302SIvan T. Ivanov break; 503eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: 504eadff302SIvan T. Ivanov pad->is_enabled = false; 505eadff302SIvan T. Ivanov break; 506eadff302SIvan T. Ivanov case PIN_CONFIG_POWER_SOURCE: 5074e83ac4cSFenglin Wu if (arg >= pad->num_sources) 508eadff302SIvan T. Ivanov return -EINVAL; 509eadff302SIvan T. Ivanov pad->power_source = arg; 510eadff302SIvan T. Ivanov break; 511eadff302SIvan T. Ivanov case PIN_CONFIG_INPUT_ENABLE: 512eadff302SIvan T. Ivanov pad->input_enabled = arg ? true : false; 513eadff302SIvan T. Ivanov break; 514eadff302SIvan T. Ivanov case PIN_CONFIG_OUTPUT: 515eadff302SIvan T. Ivanov pad->output_enabled = true; 516eadff302SIvan T. Ivanov pad->out_value = arg; 517eadff302SIvan T. Ivanov break; 518eadff302SIvan T. Ivanov case PMIC_GPIO_CONF_PULL_UP: 519eadff302SIvan T. Ivanov if (arg > PMIC_GPIO_PULL_UP_1P5_30) 520eadff302SIvan T. Ivanov return -EINVAL; 521eadff302SIvan T. Ivanov pad->pullup = arg; 522eadff302SIvan T. Ivanov break; 523eadff302SIvan T. Ivanov case PMIC_GPIO_CONF_STRENGTH: 524eadff302SIvan T. Ivanov if (arg > PMIC_GPIO_STRENGTH_LOW) 525eadff302SIvan T. Ivanov return -EINVAL; 526eadff302SIvan T. Ivanov pad->strength = arg; 527eadff302SIvan T. Ivanov break; 528d7b5f5ccSFenglin Wu case PMIC_GPIO_CONF_ATEST: 529d7b5f5ccSFenglin Wu if (!pad->lv_mv_type || arg > 4) 530d7b5f5ccSFenglin Wu return -EINVAL; 531d7b5f5ccSFenglin Wu pad->atest = arg; 532d7b5f5ccSFenglin Wu break; 533d7b5f5ccSFenglin Wu case PMIC_GPIO_CONF_ANALOG_PASS: 534d7b5f5ccSFenglin Wu if (!pad->lv_mv_type) 535d7b5f5ccSFenglin Wu return -EINVAL; 536d7b5f5ccSFenglin Wu pad->analog_pass = true; 537d7b5f5ccSFenglin Wu break; 538223463fcSFenglin Wu case PMIC_GPIO_CONF_DTEST_BUFFER: 539223463fcSFenglin Wu if (arg > 4) 540223463fcSFenglin Wu return -EINVAL; 541223463fcSFenglin Wu pad->dtest_buffer = arg; 542223463fcSFenglin Wu break; 543eadff302SIvan T. Ivanov default: 544eadff302SIvan T. Ivanov return -EINVAL; 545eadff302SIvan T. Ivanov } 546eadff302SIvan T. Ivanov } 547eadff302SIvan T. Ivanov 548eadff302SIvan T. Ivanov val = pad->power_source << PMIC_GPIO_REG_VIN_SHIFT; 549eadff302SIvan T. Ivanov 550eadff302SIvan T. Ivanov ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL, val); 551eadff302SIvan T. Ivanov if (ret < 0) 552eadff302SIvan T. Ivanov return ret; 553eadff302SIvan T. Ivanov 554eadff302SIvan T. Ivanov val = pad->pullup << PMIC_GPIO_REG_PULL_SHIFT; 555eadff302SIvan T. Ivanov 556eadff302SIvan T. Ivanov ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_PULL_CTL, val); 557eadff302SIvan T. Ivanov if (ret < 0) 558eadff302SIvan T. Ivanov return ret; 559eadff302SIvan T. Ivanov 560eadff302SIvan T. Ivanov val = pad->buffer_type << PMIC_GPIO_REG_OUT_TYPE_SHIFT; 561982df6aeSIvan T. Ivanov val |= pad->strength << PMIC_GPIO_REG_OUT_STRENGTH_SHIFT; 562eadff302SIvan T. Ivanov 563eadff302SIvan T. Ivanov ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL, val); 564eadff302SIvan T. Ivanov if (ret < 0) 565eadff302SIvan T. Ivanov return ret; 566eadff302SIvan T. Ivanov 567223463fcSFenglin Wu if (pad->dtest_buffer == 0) { 568223463fcSFenglin Wu val = 0; 569223463fcSFenglin Wu } else { 570223463fcSFenglin Wu if (pad->lv_mv_type) { 571223463fcSFenglin Wu val = pad->dtest_buffer - 1; 572223463fcSFenglin Wu val |= PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN; 573223463fcSFenglin Wu } else { 574223463fcSFenglin Wu val = BIT(pad->dtest_buffer - 1); 575223463fcSFenglin Wu } 576223463fcSFenglin Wu } 577223463fcSFenglin Wu ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_IN_CTL, val); 578223463fcSFenglin Wu if (ret < 0) 579223463fcSFenglin Wu return ret; 580223463fcSFenglin Wu 581d7b5f5ccSFenglin Wu if (pad->analog_pass) 582d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_ANALOG_PASS_THRU; 583d7b5f5ccSFenglin Wu else if (pad->output_enabled && pad->input_enabled) 584d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT; 585d7b5f5ccSFenglin Wu else if (pad->output_enabled) 586d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_OUTPUT; 587eadff302SIvan T. Ivanov else 588d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_INPUT; 589eadff302SIvan T. Ivanov 590d7b5f5ccSFenglin Wu if (pad->lv_mv_type) { 591d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 592d7b5f5ccSFenglin Wu PMIC_GPIO_REG_MODE_CTL, val); 593d7b5f5ccSFenglin Wu if (ret < 0) 594d7b5f5ccSFenglin Wu return ret; 595d7b5f5ccSFenglin Wu 596d7b5f5ccSFenglin Wu val = pad->atest - 1; 597d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 598d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL, val); 599d7b5f5ccSFenglin Wu if (ret < 0) 600d7b5f5ccSFenglin Wu return ret; 601d7b5f5ccSFenglin Wu 602d7b5f5ccSFenglin Wu val = pad->out_value 603d7b5f5ccSFenglin Wu << PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT; 604d7b5f5ccSFenglin Wu val |= pad->function 605d7b5f5ccSFenglin Wu & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK; 606d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 607d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL, val); 608d7b5f5ccSFenglin Wu if (ret < 0) 609d7b5f5ccSFenglin Wu return ret; 610d7b5f5ccSFenglin Wu } else { 611eadff302SIvan T. Ivanov val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT; 612eadff302SIvan T. Ivanov val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT; 613eadff302SIvan T. Ivanov val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT; 614eadff302SIvan T. Ivanov 615d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); 616d7b5f5ccSFenglin Wu if (ret < 0) 617d7b5f5ccSFenglin Wu return ret; 618d7b5f5ccSFenglin Wu } 619d7b5f5ccSFenglin Wu 6206cb74f44SFenglin Wu val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT; 6216cb74f44SFenglin Wu 6226cb74f44SFenglin Wu ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_EN_CTL, val); 6236cb74f44SFenglin Wu 624d7b5f5ccSFenglin Wu return ret; 625eadff302SIvan T. Ivanov } 626eadff302SIvan T. Ivanov 627eadff302SIvan T. Ivanov static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev, 628eadff302SIvan T. Ivanov struct seq_file *s, unsigned pin) 629eadff302SIvan T. Ivanov { 630eadff302SIvan T. Ivanov struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev); 631eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad; 632d7b5f5ccSFenglin Wu int ret, val, function; 633eadff302SIvan T. Ivanov 634eadff302SIvan T. Ivanov static const char *const biases[] = { 635eadff302SIvan T. Ivanov "pull-up 30uA", "pull-up 1.5uA", "pull-up 31.5uA", 636eadff302SIvan T. Ivanov "pull-up 1.5uA + 30uA boost", "pull-down 10uA", "no pull" 637eadff302SIvan T. Ivanov }; 638eadff302SIvan T. Ivanov static const char *const buffer_types[] = { 639eadff302SIvan T. Ivanov "push-pull", "open-drain", "open-source" 640eadff302SIvan T. Ivanov }; 641eadff302SIvan T. Ivanov static const char *const strengths[] = { 642eadff302SIvan T. Ivanov "no", "high", "medium", "low" 643eadff302SIvan T. Ivanov }; 644eadff302SIvan T. Ivanov 645eadff302SIvan T. Ivanov pad = pctldev->desc->pins[pin].drv_data; 646eadff302SIvan T. Ivanov 647eadff302SIvan T. Ivanov seq_printf(s, " gpio%-2d:", pin + PMIC_GPIO_PHYSICAL_OFFSET); 648eadff302SIvan T. Ivanov 649eadff302SIvan T. Ivanov val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_EN_CTL); 650eadff302SIvan T. Ivanov 651eadff302SIvan T. Ivanov if (val < 0 || !(val >> PMIC_GPIO_REG_MASTER_EN_SHIFT)) { 652eadff302SIvan T. Ivanov seq_puts(s, " ---"); 653eadff302SIvan T. Ivanov } else { 65424a66618SIvan T. Ivanov if (pad->input_enabled) { 655eadff302SIvan T. Ivanov ret = pmic_gpio_read(state, pad, PMIC_MPP_REG_RT_STS); 65624a66618SIvan T. Ivanov if (ret < 0) 65724a66618SIvan T. Ivanov return; 65824a66618SIvan T. Ivanov 659eadff302SIvan T. Ivanov ret &= PMIC_MPP_REG_RT_STS_VAL_MASK; 660eadff302SIvan T. Ivanov pad->out_value = ret; 661eadff302SIvan T. Ivanov } 662d7b5f5ccSFenglin Wu /* 663d7b5f5ccSFenglin Wu * For the non-LV/MV subtypes only 2 special functions are 664d7b5f5ccSFenglin Wu * available, offsetting the dtest function values by 2. 665d7b5f5ccSFenglin Wu */ 666d7b5f5ccSFenglin Wu function = pad->function; 667d7b5f5ccSFenglin Wu if (!pad->lv_mv_type && 668d7b5f5ccSFenglin Wu pad->function >= PMIC_GPIO_FUNC_INDEX_FUNC3) 669d7b5f5ccSFenglin Wu function += PMIC_GPIO_FUNC_INDEX_DTEST1 - 670d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_FUNC3; 671eadff302SIvan T. Ivanov 672d7b5f5ccSFenglin Wu if (pad->analog_pass) 673d7b5f5ccSFenglin Wu seq_puts(s, " analog-pass"); 674d7b5f5ccSFenglin Wu else 675d7b5f5ccSFenglin Wu seq_printf(s, " %-4s", 676d7b5f5ccSFenglin Wu pad->output_enabled ? "out" : "in"); 677d7b5f5ccSFenglin Wu seq_printf(s, " %-7s", pmic_gpio_functions[function]); 678eadff302SIvan T. Ivanov seq_printf(s, " vin-%d", pad->power_source); 679eadff302SIvan T. Ivanov seq_printf(s, " %-27s", biases[pad->pullup]); 680eadff302SIvan T. Ivanov seq_printf(s, " %-10s", buffer_types[pad->buffer_type]); 681eadff302SIvan T. Ivanov seq_printf(s, " %-4s", pad->out_value ? "high" : "low"); 682eadff302SIvan T. Ivanov seq_printf(s, " %-7s", strengths[pad->strength]); 683d7b5f5ccSFenglin Wu seq_printf(s, " atest-%d", pad->atest); 684223463fcSFenglin Wu seq_printf(s, " dtest-%d", pad->dtest_buffer); 685eadff302SIvan T. Ivanov } 686eadff302SIvan T. Ivanov } 687eadff302SIvan T. Ivanov 688eadff302SIvan T. Ivanov static const struct pinconf_ops pmic_gpio_pinconf_ops = { 6897382b623SSoren Brinkmann .is_generic = true, 690eadff302SIvan T. Ivanov .pin_config_group_get = pmic_gpio_config_get, 691eadff302SIvan T. Ivanov .pin_config_group_set = pmic_gpio_config_set, 692eadff302SIvan T. Ivanov .pin_config_group_dbg_show = pmic_gpio_config_dbg_show, 693eadff302SIvan T. Ivanov }; 694eadff302SIvan T. Ivanov 695eadff302SIvan T. Ivanov static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned pin) 696eadff302SIvan T. Ivanov { 697c52d9df1SLinus Walleij struct pmic_gpio_state *state = gpiochip_get_data(chip); 698eadff302SIvan T. Ivanov unsigned long config; 699eadff302SIvan T. Ivanov 700eadff302SIvan T. Ivanov config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1); 701eadff302SIvan T. Ivanov 702eadff302SIvan T. Ivanov return pmic_gpio_config_set(state->ctrl, pin, &config, 1); 703eadff302SIvan T. Ivanov } 704eadff302SIvan T. Ivanov 705eadff302SIvan T. Ivanov static int pmic_gpio_direction_output(struct gpio_chip *chip, 706eadff302SIvan T. Ivanov unsigned pin, int val) 707eadff302SIvan T. Ivanov { 708c52d9df1SLinus Walleij struct pmic_gpio_state *state = gpiochip_get_data(chip); 709eadff302SIvan T. Ivanov unsigned long config; 710eadff302SIvan T. Ivanov 711eadff302SIvan T. Ivanov config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val); 712eadff302SIvan T. Ivanov 713eadff302SIvan T. Ivanov return pmic_gpio_config_set(state->ctrl, pin, &config, 1); 714eadff302SIvan T. Ivanov } 715eadff302SIvan T. Ivanov 716eadff302SIvan T. Ivanov static int pmic_gpio_get(struct gpio_chip *chip, unsigned pin) 717eadff302SIvan T. Ivanov { 718c52d9df1SLinus Walleij struct pmic_gpio_state *state = gpiochip_get_data(chip); 719eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad; 720eadff302SIvan T. Ivanov int ret; 721eadff302SIvan T. Ivanov 722eadff302SIvan T. Ivanov pad = state->ctrl->desc->pins[pin].drv_data; 723eadff302SIvan T. Ivanov 724eadff302SIvan T. Ivanov if (!pad->is_enabled) 725eadff302SIvan T. Ivanov return -EINVAL; 726eadff302SIvan T. Ivanov 727eadff302SIvan T. Ivanov if (pad->input_enabled) { 728eadff302SIvan T. Ivanov ret = pmic_gpio_read(state, pad, PMIC_MPP_REG_RT_STS); 729eadff302SIvan T. Ivanov if (ret < 0) 730eadff302SIvan T. Ivanov return ret; 731eadff302SIvan T. Ivanov 732eadff302SIvan T. Ivanov pad->out_value = ret & PMIC_MPP_REG_RT_STS_VAL_MASK; 733eadff302SIvan T. Ivanov } 734eadff302SIvan T. Ivanov 73586c1a219SLinus Walleij return !!pad->out_value; 736eadff302SIvan T. Ivanov } 737eadff302SIvan T. Ivanov 738eadff302SIvan T. Ivanov static void pmic_gpio_set(struct gpio_chip *chip, unsigned pin, int value) 739eadff302SIvan T. Ivanov { 740c52d9df1SLinus Walleij struct pmic_gpio_state *state = gpiochip_get_data(chip); 741eadff302SIvan T. Ivanov unsigned long config; 742eadff302SIvan T. Ivanov 743eadff302SIvan T. Ivanov config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value); 744eadff302SIvan T. Ivanov 745eadff302SIvan T. Ivanov pmic_gpio_config_set(state->ctrl, pin, &config, 1); 746eadff302SIvan T. Ivanov } 747eadff302SIvan T. Ivanov 748eadff302SIvan T. Ivanov static int pmic_gpio_of_xlate(struct gpio_chip *chip, 749eadff302SIvan T. Ivanov const struct of_phandle_args *gpio_desc, 750eadff302SIvan T. Ivanov u32 *flags) 751eadff302SIvan T. Ivanov { 752eadff302SIvan T. Ivanov if (chip->of_gpio_n_cells < 2) 753eadff302SIvan T. Ivanov return -EINVAL; 754eadff302SIvan T. Ivanov 755eadff302SIvan T. Ivanov if (flags) 756eadff302SIvan T. Ivanov *flags = gpio_desc->args[1]; 757eadff302SIvan T. Ivanov 758eadff302SIvan T. Ivanov return gpio_desc->args[0] - PMIC_GPIO_PHYSICAL_OFFSET; 759eadff302SIvan T. Ivanov } 760eadff302SIvan T. Ivanov 761eadff302SIvan T. Ivanov static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned pin) 762eadff302SIvan T. Ivanov { 763c52d9df1SLinus Walleij struct pmic_gpio_state *state = gpiochip_get_data(chip); 764eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad; 765eadff302SIvan T. Ivanov 766eadff302SIvan T. Ivanov pad = state->ctrl->desc->pins[pin].drv_data; 767eadff302SIvan T. Ivanov 768eadff302SIvan T. Ivanov return pad->irq; 769eadff302SIvan T. Ivanov } 770eadff302SIvan T. Ivanov 771eadff302SIvan T. Ivanov static void pmic_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) 772eadff302SIvan T. Ivanov { 773c52d9df1SLinus Walleij struct pmic_gpio_state *state = gpiochip_get_data(chip); 774eadff302SIvan T. Ivanov unsigned i; 775eadff302SIvan T. Ivanov 776eadff302SIvan T. Ivanov for (i = 0; i < chip->ngpio; i++) { 777eadff302SIvan T. Ivanov pmic_gpio_config_dbg_show(state->ctrl, s, i); 778eadff302SIvan T. Ivanov seq_puts(s, "\n"); 779eadff302SIvan T. Ivanov } 780eadff302SIvan T. Ivanov } 781eadff302SIvan T. Ivanov 782eadff302SIvan T. Ivanov static const struct gpio_chip pmic_gpio_gpio_template = { 783eadff302SIvan T. Ivanov .direction_input = pmic_gpio_direction_input, 784eadff302SIvan T. Ivanov .direction_output = pmic_gpio_direction_output, 785eadff302SIvan T. Ivanov .get = pmic_gpio_get, 786eadff302SIvan T. Ivanov .set = pmic_gpio_set, 78798c85d58SJonas Gorski .request = gpiochip_generic_request, 78898c85d58SJonas Gorski .free = gpiochip_generic_free, 789eadff302SIvan T. Ivanov .of_xlate = pmic_gpio_of_xlate, 790eadff302SIvan T. Ivanov .to_irq = pmic_gpio_to_irq, 791eadff302SIvan T. Ivanov .dbg_show = pmic_gpio_dbg_show, 792eadff302SIvan T. Ivanov }; 793eadff302SIvan T. Ivanov 794eadff302SIvan T. Ivanov static int pmic_gpio_populate(struct pmic_gpio_state *state, 795eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad) 796eadff302SIvan T. Ivanov { 797eadff302SIvan T. Ivanov int type, subtype, val, dir; 798eadff302SIvan T. Ivanov 799eadff302SIvan T. Ivanov type = pmic_gpio_read(state, pad, PMIC_GPIO_REG_TYPE); 800eadff302SIvan T. Ivanov if (type < 0) 801eadff302SIvan T. Ivanov return type; 802eadff302SIvan T. Ivanov 803eadff302SIvan T. Ivanov if (type != PMIC_GPIO_TYPE) { 804eadff302SIvan T. Ivanov dev_err(state->dev, "incorrect block type 0x%x at 0x%x\n", 805eadff302SIvan T. Ivanov type, pad->base); 806eadff302SIvan T. Ivanov return -ENODEV; 807eadff302SIvan T. Ivanov } 808eadff302SIvan T. Ivanov 809eadff302SIvan T. Ivanov subtype = pmic_gpio_read(state, pad, PMIC_GPIO_REG_SUBTYPE); 810eadff302SIvan T. Ivanov if (subtype < 0) 811eadff302SIvan T. Ivanov return subtype; 812eadff302SIvan T. Ivanov 813eadff302SIvan T. Ivanov switch (subtype) { 814eadff302SIvan T. Ivanov case PMIC_GPIO_SUBTYPE_GPIO_4CH: 815eadff302SIvan T. Ivanov pad->have_buffer = true; 816eadff302SIvan T. Ivanov case PMIC_GPIO_SUBTYPE_GPIOC_4CH: 817eadff302SIvan T. Ivanov pad->num_sources = 4; 818eadff302SIvan T. Ivanov break; 819eadff302SIvan T. Ivanov case PMIC_GPIO_SUBTYPE_GPIO_8CH: 820eadff302SIvan T. Ivanov pad->have_buffer = true; 821eadff302SIvan T. Ivanov case PMIC_GPIO_SUBTYPE_GPIOC_8CH: 822eadff302SIvan T. Ivanov pad->num_sources = 8; 823eadff302SIvan T. Ivanov break; 824d7b5f5ccSFenglin Wu case PMIC_GPIO_SUBTYPE_GPIO_LV: 825d7b5f5ccSFenglin Wu pad->num_sources = 1; 826d7b5f5ccSFenglin Wu pad->have_buffer = true; 827d7b5f5ccSFenglin Wu pad->lv_mv_type = true; 828d7b5f5ccSFenglin Wu break; 829d7b5f5ccSFenglin Wu case PMIC_GPIO_SUBTYPE_GPIO_MV: 830d7b5f5ccSFenglin Wu pad->num_sources = 2; 831d7b5f5ccSFenglin Wu pad->have_buffer = true; 832d7b5f5ccSFenglin Wu pad->lv_mv_type = true; 833d7b5f5ccSFenglin Wu break; 834eadff302SIvan T. Ivanov default: 835eadff302SIvan T. Ivanov dev_err(state->dev, "unknown GPIO type 0x%x\n", subtype); 836eadff302SIvan T. Ivanov return -ENODEV; 837eadff302SIvan T. Ivanov } 838eadff302SIvan T. Ivanov 839d7b5f5ccSFenglin Wu if (pad->lv_mv_type) { 840d7b5f5ccSFenglin Wu val = pmic_gpio_read(state, pad, 841d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL); 842d7b5f5ccSFenglin Wu if (val < 0) 843d7b5f5ccSFenglin Wu return val; 844d7b5f5ccSFenglin Wu 845d7b5f5ccSFenglin Wu pad->out_value = !!(val & PMIC_GPIO_LV_MV_OUTPUT_INVERT); 846d7b5f5ccSFenglin Wu pad->function = val & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK; 847d7b5f5ccSFenglin Wu 848d7b5f5ccSFenglin Wu val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL); 849d7b5f5ccSFenglin Wu if (val < 0) 850d7b5f5ccSFenglin Wu return val; 851d7b5f5ccSFenglin Wu 852d7b5f5ccSFenglin Wu dir = val & PMIC_GPIO_REG_LV_MV_MODE_DIR_MASK; 853d7b5f5ccSFenglin Wu } else { 854eadff302SIvan T. Ivanov val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL); 855eadff302SIvan T. Ivanov if (val < 0) 856eadff302SIvan T. Ivanov return val; 857eadff302SIvan T. Ivanov 858eadff302SIvan T. Ivanov pad->out_value = val & PMIC_GPIO_REG_MODE_VALUE_SHIFT; 859eadff302SIvan T. Ivanov 860eadff302SIvan T. Ivanov dir = val >> PMIC_GPIO_REG_MODE_DIR_SHIFT; 861eadff302SIvan T. Ivanov dir &= PMIC_GPIO_REG_MODE_DIR_MASK; 862d7b5f5ccSFenglin Wu pad->function = val >> PMIC_GPIO_REG_MODE_FUNCTION_SHIFT; 863d7b5f5ccSFenglin Wu pad->function &= PMIC_GPIO_REG_MODE_FUNCTION_MASK; 864d7b5f5ccSFenglin Wu } 865d7b5f5ccSFenglin Wu 866eadff302SIvan T. Ivanov switch (dir) { 867d7b5f5ccSFenglin Wu case PMIC_GPIO_MODE_DIGITAL_INPUT: 868eadff302SIvan T. Ivanov pad->input_enabled = true; 869eadff302SIvan T. Ivanov pad->output_enabled = false; 870eadff302SIvan T. Ivanov break; 871d7b5f5ccSFenglin Wu case PMIC_GPIO_MODE_DIGITAL_OUTPUT: 872eadff302SIvan T. Ivanov pad->input_enabled = false; 873eadff302SIvan T. Ivanov pad->output_enabled = true; 874eadff302SIvan T. Ivanov break; 875d7b5f5ccSFenglin Wu case PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT: 876eadff302SIvan T. Ivanov pad->input_enabled = true; 877eadff302SIvan T. Ivanov pad->output_enabled = true; 878eadff302SIvan T. Ivanov break; 879d7b5f5ccSFenglin Wu case PMIC_GPIO_MODE_ANALOG_PASS_THRU: 880d7b5f5ccSFenglin Wu if (!pad->lv_mv_type) 881d7b5f5ccSFenglin Wu return -ENODEV; 882d7b5f5ccSFenglin Wu pad->analog_pass = true; 883d7b5f5ccSFenglin Wu break; 884eadff302SIvan T. Ivanov default: 885eadff302SIvan T. Ivanov dev_err(state->dev, "unknown GPIO direction\n"); 886eadff302SIvan T. Ivanov return -ENODEV; 887eadff302SIvan T. Ivanov } 888eadff302SIvan T. Ivanov 889eadff302SIvan T. Ivanov val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL); 890eadff302SIvan T. Ivanov if (val < 0) 891eadff302SIvan T. Ivanov return val; 892eadff302SIvan T. Ivanov 893eadff302SIvan T. Ivanov pad->power_source = val >> PMIC_GPIO_REG_VIN_SHIFT; 894eadff302SIvan T. Ivanov pad->power_source &= PMIC_GPIO_REG_VIN_MASK; 895eadff302SIvan T. Ivanov 896eadff302SIvan T. Ivanov val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_PULL_CTL); 897eadff302SIvan T. Ivanov if (val < 0) 898eadff302SIvan T. Ivanov return val; 899eadff302SIvan T. Ivanov 900eadff302SIvan T. Ivanov pad->pullup = val >> PMIC_GPIO_REG_PULL_SHIFT; 901eadff302SIvan T. Ivanov pad->pullup &= PMIC_GPIO_REG_PULL_MASK; 902eadff302SIvan T. Ivanov 903223463fcSFenglin Wu val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_IN_CTL); 904223463fcSFenglin Wu if (val < 0) 905223463fcSFenglin Wu return val; 906223463fcSFenglin Wu 907223463fcSFenglin Wu if (pad->lv_mv_type && (val & PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN)) 908223463fcSFenglin Wu pad->dtest_buffer = 909223463fcSFenglin Wu (val & PMIC_GPIO_LV_MV_DIG_IN_DTEST_SEL_MASK) + 1; 910223463fcSFenglin Wu else if (!pad->lv_mv_type) 911223463fcSFenglin Wu pad->dtest_buffer = ffs(val); 912223463fcSFenglin Wu else 913223463fcSFenglin Wu pad->dtest_buffer = 0; 914223463fcSFenglin Wu 915223463fcSFenglin Wu val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL); 916223463fcSFenglin Wu if (val < 0) 917223463fcSFenglin Wu return val; 918223463fcSFenglin Wu 919eadff302SIvan T. Ivanov pad->strength = val >> PMIC_GPIO_REG_OUT_STRENGTH_SHIFT; 920eadff302SIvan T. Ivanov pad->strength &= PMIC_GPIO_REG_OUT_STRENGTH_MASK; 921eadff302SIvan T. Ivanov 922eadff302SIvan T. Ivanov pad->buffer_type = val >> PMIC_GPIO_REG_OUT_TYPE_SHIFT; 923eadff302SIvan T. Ivanov pad->buffer_type &= PMIC_GPIO_REG_OUT_TYPE_MASK; 924eadff302SIvan T. Ivanov 925d7b5f5ccSFenglin Wu if (pad->lv_mv_type) { 926d7b5f5ccSFenglin Wu val = pmic_gpio_read(state, pad, 927d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL); 928d7b5f5ccSFenglin Wu if (val < 0) 929d7b5f5ccSFenglin Wu return val; 930d7b5f5ccSFenglin Wu pad->atest = (val & PMIC_GPIO_LV_MV_ANA_MUX_SEL_MASK) + 1; 931d7b5f5ccSFenglin Wu } 932d7b5f5ccSFenglin Wu 933eadff302SIvan T. Ivanov /* Pin could be disabled with PIN_CONFIG_BIAS_HIGH_IMPEDANCE */ 934eadff302SIvan T. Ivanov pad->is_enabled = true; 935eadff302SIvan T. Ivanov return 0; 936eadff302SIvan T. Ivanov } 937eadff302SIvan T. Ivanov 938eadff302SIvan T. Ivanov static int pmic_gpio_probe(struct platform_device *pdev) 939eadff302SIvan T. Ivanov { 940eadff302SIvan T. Ivanov struct device *dev = &pdev->dev; 941eadff302SIvan T. Ivanov struct pinctrl_pin_desc *pindesc; 942eadff302SIvan T. Ivanov struct pinctrl_desc *pctrldesc; 943eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad, *pads; 944eadff302SIvan T. Ivanov struct pmic_gpio_state *state; 945eadff302SIvan T. Ivanov int ret, npins, i; 946ab4256cfSStephen Boyd u32 reg; 947eadff302SIvan T. Ivanov 948ab4256cfSStephen Boyd ret = of_property_read_u32(dev->of_node, "reg", ®); 949eadff302SIvan T. Ivanov if (ret < 0) { 950ab4256cfSStephen Boyd dev_err(dev, "missing base address"); 951eadff302SIvan T. Ivanov return ret; 952eadff302SIvan T. Ivanov } 953eadff302SIvan T. Ivanov 954cfacef37SBrian Masney npins = (uintptr_t) device_get_match_data(&pdev->dev); 955eadff302SIvan T. Ivanov 956eadff302SIvan T. Ivanov state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); 957eadff302SIvan T. Ivanov if (!state) 958eadff302SIvan T. Ivanov return -ENOMEM; 959eadff302SIvan T. Ivanov 960eadff302SIvan T. Ivanov platform_set_drvdata(pdev, state); 961eadff302SIvan T. Ivanov 962eadff302SIvan T. Ivanov state->dev = &pdev->dev; 963eadff302SIvan T. Ivanov state->map = dev_get_regmap(dev->parent, NULL); 964eadff302SIvan T. Ivanov 965eadff302SIvan T. Ivanov pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL); 966eadff302SIvan T. Ivanov if (!pindesc) 967eadff302SIvan T. Ivanov return -ENOMEM; 968eadff302SIvan T. Ivanov 969eadff302SIvan T. Ivanov pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL); 970eadff302SIvan T. Ivanov if (!pads) 971eadff302SIvan T. Ivanov return -ENOMEM; 972eadff302SIvan T. Ivanov 973eadff302SIvan T. Ivanov pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL); 974eadff302SIvan T. Ivanov if (!pctrldesc) 975eadff302SIvan T. Ivanov return -ENOMEM; 976eadff302SIvan T. Ivanov 977eadff302SIvan T. Ivanov pctrldesc->pctlops = &pmic_gpio_pinctrl_ops; 978eadff302SIvan T. Ivanov pctrldesc->pmxops = &pmic_gpio_pinmux_ops; 979eadff302SIvan T. Ivanov pctrldesc->confops = &pmic_gpio_pinconf_ops; 980eadff302SIvan T. Ivanov pctrldesc->owner = THIS_MODULE; 981eadff302SIvan T. Ivanov pctrldesc->name = dev_name(dev); 982eadff302SIvan T. Ivanov pctrldesc->pins = pindesc; 983eadff302SIvan T. Ivanov pctrldesc->npins = npins; 984f684e4acSLinus Walleij pctrldesc->num_custom_params = ARRAY_SIZE(pmic_gpio_bindings); 985f684e4acSLinus Walleij pctrldesc->custom_params = pmic_gpio_bindings; 9864f06266aSArnd Bergmann #ifdef CONFIG_DEBUG_FS 987f684e4acSLinus Walleij pctrldesc->custom_conf_items = pmic_conf_items; 9884f06266aSArnd Bergmann #endif 989eadff302SIvan T. Ivanov 990eadff302SIvan T. Ivanov for (i = 0; i < npins; i++, pindesc++) { 991eadff302SIvan T. Ivanov pad = &pads[i]; 992eadff302SIvan T. Ivanov pindesc->drv_data = pad; 993eadff302SIvan T. Ivanov pindesc->number = i; 994eadff302SIvan T. Ivanov pindesc->name = pmic_gpio_groups[i]; 995eadff302SIvan T. Ivanov 996eadff302SIvan T. Ivanov pad->irq = platform_get_irq(pdev, i); 997eadff302SIvan T. Ivanov if (pad->irq < 0) 998eadff302SIvan T. Ivanov return pad->irq; 999eadff302SIvan T. Ivanov 1000ab4256cfSStephen Boyd pad->base = reg + i * PMIC_GPIO_ADDRESS_RANGE; 1001eadff302SIvan T. Ivanov 1002eadff302SIvan T. Ivanov ret = pmic_gpio_populate(state, pad); 1003eadff302SIvan T. Ivanov if (ret < 0) 1004eadff302SIvan T. Ivanov return ret; 1005eadff302SIvan T. Ivanov } 1006eadff302SIvan T. Ivanov 1007eadff302SIvan T. Ivanov state->chip = pmic_gpio_gpio_template; 100858383c78SLinus Walleij state->chip.parent = dev; 1009eadff302SIvan T. Ivanov state->chip.base = -1; 1010eadff302SIvan T. Ivanov state->chip.ngpio = npins; 1011eadff302SIvan T. Ivanov state->chip.label = dev_name(dev); 1012eadff302SIvan T. Ivanov state->chip.of_gpio_n_cells = 2; 1013eadff302SIvan T. Ivanov state->chip.can_sleep = false; 1014eadff302SIvan T. Ivanov 1015b46ddfe6SLaxman Dewangan state->ctrl = devm_pinctrl_register(dev, pctrldesc, state); 1016323de9efSMasahiro Yamada if (IS_ERR(state->ctrl)) 1017323de9efSMasahiro Yamada return PTR_ERR(state->ctrl); 1018eadff302SIvan T. Ivanov 1019c52d9df1SLinus Walleij ret = gpiochip_add_data(&state->chip, state); 1020eadff302SIvan T. Ivanov if (ret) { 1021eadff302SIvan T. Ivanov dev_err(state->dev, "can't add gpio chip\n"); 1022b46ddfe6SLaxman Dewangan return ret; 1023eadff302SIvan T. Ivanov } 1024eadff302SIvan T. Ivanov 1025149a9604SBrian Masney /* 1026149a9604SBrian Masney * For DeviceTree-supported systems, the gpio core checks the 1027149a9604SBrian Masney * pinctrl's device node for the "gpio-ranges" property. 1028149a9604SBrian Masney * If it is present, it takes care of adding the pin ranges 1029149a9604SBrian Masney * for the driver. In this case the driver can skip ahead. 1030149a9604SBrian Masney * 1031149a9604SBrian Masney * In order to remain compatible with older, existing DeviceTree 1032149a9604SBrian Masney * files which don't set the "gpio-ranges" property or systems that 1033149a9604SBrian Masney * utilize ACPI the driver has to call gpiochip_add_pin_range(). 1034149a9604SBrian Masney */ 1035149a9604SBrian Masney if (!of_property_read_bool(dev->of_node, "gpio-ranges")) { 1036149a9604SBrian Masney ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, 1037149a9604SBrian Masney npins); 1038eadff302SIvan T. Ivanov if (ret) { 1039eadff302SIvan T. Ivanov dev_err(dev, "failed to add pin range\n"); 1040eadff302SIvan T. Ivanov goto err_range; 1041eadff302SIvan T. Ivanov } 1042149a9604SBrian Masney } 1043eadff302SIvan T. Ivanov 1044eadff302SIvan T. Ivanov return 0; 1045eadff302SIvan T. Ivanov 1046eadff302SIvan T. Ivanov err_range: 1047eadff302SIvan T. Ivanov gpiochip_remove(&state->chip); 1048eadff302SIvan T. Ivanov return ret; 1049eadff302SIvan T. Ivanov } 1050eadff302SIvan T. Ivanov 1051eadff302SIvan T. Ivanov static int pmic_gpio_remove(struct platform_device *pdev) 1052eadff302SIvan T. Ivanov { 1053eadff302SIvan T. Ivanov struct pmic_gpio_state *state = platform_get_drvdata(pdev); 1054eadff302SIvan T. Ivanov 1055eadff302SIvan T. Ivanov gpiochip_remove(&state->chip); 1056eadff302SIvan T. Ivanov return 0; 1057eadff302SIvan T. Ivanov } 1058eadff302SIvan T. Ivanov 1059eadff302SIvan T. Ivanov static const struct of_device_id pmic_gpio_of_match[] = { 1060cfacef37SBrian Masney { .compatible = "qcom,pm8005-gpio", .data = (void *) 4 }, 1061cfacef37SBrian Masney { .compatible = "qcom,pm8916-gpio", .data = (void *) 4 }, 1062cfacef37SBrian Masney { .compatible = "qcom,pm8941-gpio", .data = (void *) 36 }, 1063cfacef37SBrian Masney { .compatible = "qcom,pm8994-gpio", .data = (void *) 22 }, 1064cfacef37SBrian Masney { .compatible = "qcom,pmi8994-gpio", .data = (void *) 10 }, 1065cfacef37SBrian Masney { .compatible = "qcom,pm8998-gpio", .data = (void *) 26 }, 1066cfacef37SBrian Masney { .compatible = "qcom,pmi8998-gpio", .data = (void *) 14 }, 1067cfacef37SBrian Masney { .compatible = "qcom,pma8084-gpio", .data = (void *) 22 }, 1068cfacef37SBrian Masney /* pms405 has 12 GPIOs with holes on 1, 9, and 10 */ 1069cfacef37SBrian Masney { .compatible = "qcom,pms405-gpio", .data = (void *) 12 }, 1070eadff302SIvan T. Ivanov { }, 1071eadff302SIvan T. Ivanov }; 1072eadff302SIvan T. Ivanov 1073eadff302SIvan T. Ivanov MODULE_DEVICE_TABLE(of, pmic_gpio_of_match); 1074eadff302SIvan T. Ivanov 1075eadff302SIvan T. Ivanov static struct platform_driver pmic_gpio_driver = { 1076eadff302SIvan T. Ivanov .driver = { 1077eadff302SIvan T. Ivanov .name = "qcom-spmi-gpio", 1078eadff302SIvan T. Ivanov .of_match_table = pmic_gpio_of_match, 1079eadff302SIvan T. Ivanov }, 1080eadff302SIvan T. Ivanov .probe = pmic_gpio_probe, 1081eadff302SIvan T. Ivanov .remove = pmic_gpio_remove, 1082eadff302SIvan T. Ivanov }; 1083eadff302SIvan T. Ivanov 1084eadff302SIvan T. Ivanov module_platform_driver(pmic_gpio_driver); 1085eadff302SIvan T. Ivanov 1086eadff302SIvan T. Ivanov MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>"); 1087eadff302SIvan T. Ivanov MODULE_DESCRIPTION("Qualcomm SPMI PMIC GPIO pin control driver"); 1088eadff302SIvan T. Ivanov MODULE_ALIAS("platform:qcom-spmi-gpio"); 1089eadff302SIvan T. Ivanov MODULE_LICENSE("GPL v2"); 1090