197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2eadff302SIvan T. Ivanov /* 3d36a9773SDavid Collins * Copyright (c) 2012-2014, 2016-2021 The Linux Foundation. All rights reserved. 44af95d09SDavid Collins * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. 5eadff302SIvan T. Ivanov */ 6eadff302SIvan T. Ivanov 71c5fb66aSLinus Walleij #include <linux/gpio/driver.h> 8ca69e2d1SBrian Masney #include <linux/interrupt.h> 9eadff302SIvan T. Ivanov #include <linux/module.h> 10eadff302SIvan T. Ivanov #include <linux/of.h> 11ab4256cfSStephen Boyd #include <linux/of_irq.h> 12eadff302SIvan T. Ivanov #include <linux/pinctrl/pinconf-generic.h> 13eadff302SIvan T. Ivanov #include <linux/pinctrl/pinconf.h> 14eadff302SIvan T. Ivanov #include <linux/pinctrl/pinmux.h> 15eadff302SIvan T. Ivanov #include <linux/platform_device.h> 16eadff302SIvan T. Ivanov #include <linux/regmap.h> 17eadff302SIvan T. Ivanov #include <linux/slab.h> 18d36a9773SDavid Collins #include <linux/spmi.h> 19eadff302SIvan T. Ivanov #include <linux/types.h> 20eadff302SIvan T. Ivanov 21eadff302SIvan T. Ivanov #include <dt-bindings/pinctrl/qcom,pmic-gpio.h> 22eadff302SIvan T. Ivanov 23eadff302SIvan T. Ivanov #include "../core.h" 24eadff302SIvan T. Ivanov #include "../pinctrl-utils.h" 25eadff302SIvan T. Ivanov 26eadff302SIvan T. Ivanov #define PMIC_GPIO_ADDRESS_RANGE 0x100 27eadff302SIvan T. Ivanov 28eadff302SIvan T. Ivanov /* type and subtype registers base address offsets */ 29eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_TYPE 0x4 30eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_SUBTYPE 0x5 31eadff302SIvan T. Ivanov 32eadff302SIvan T. Ivanov /* GPIO peripheral type and subtype out_values */ 33eadff302SIvan T. Ivanov #define PMIC_GPIO_TYPE 0x10 34eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIO_4CH 0x1 35eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIOC_4CH 0x5 36eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIO_8CH 0x9 37eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIOC_8CH 0xd 38d7b5f5ccSFenglin Wu #define PMIC_GPIO_SUBTYPE_GPIO_LV 0x10 39d7b5f5ccSFenglin Wu #define PMIC_GPIO_SUBTYPE_GPIO_MV 0x11 404af95d09SDavid Collins #define PMIC_GPIO_SUBTYPE_GPIO_LV_VIN2 0x12 414af95d09SDavid Collins #define PMIC_GPIO_SUBTYPE_GPIO_MV_VIN3 0x13 42eadff302SIvan T. Ivanov 43eadff302SIvan T. Ivanov #define PMIC_MPP_REG_RT_STS 0x10 44eadff302SIvan T. Ivanov #define PMIC_MPP_REG_RT_STS_VAL_MASK 0x1 45eadff302SIvan T. Ivanov 46eadff302SIvan T. Ivanov /* control register base address offsets */ 47eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_CTL 0x40 48eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_VIN_CTL 0x41 49eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_PULL_CTL 0x42 50d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL 0x44 51223463fcSFenglin Wu #define PMIC_GPIO_REG_DIG_IN_CTL 0x43 52eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_OUT_CTL 0x45 53eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_EN_CTL 0x46 54d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL 0x4A 55eadff302SIvan T. Ivanov 56eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_MODE_CTL */ 57eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_VALUE_SHIFT 0x1 58eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_FUNCTION_SHIFT 1 59eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_FUNCTION_MASK 0x7 60eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_DIR_SHIFT 4 61eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_DIR_MASK 0x7 62eadff302SIvan T. Ivanov 63d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_INPUT 0 64d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_OUTPUT 1 65d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT 2 66d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_ANALOG_PASS_THRU 3 67d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_MODE_DIR_MASK 0x3 68d7b5f5ccSFenglin Wu 69eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_VIN_CTL */ 70eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_VIN_SHIFT 0 71eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_VIN_MASK 0x7 72eadff302SIvan T. Ivanov 73eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_PULL_CTL */ 74eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_PULL_SHIFT 0 75eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_PULL_MASK 0x7 76eadff302SIvan T. Ivanov 77eadff302SIvan T. Ivanov #define PMIC_GPIO_PULL_DOWN 4 78eadff302SIvan T. Ivanov #define PMIC_GPIO_PULL_DISABLE 5 79eadff302SIvan T. Ivanov 80d7b5f5ccSFenglin Wu /* PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL for LV/MV */ 81d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_INVERT 0x80 82d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT 7 83d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK 0xF 84d7b5f5ccSFenglin Wu 85223463fcSFenglin Wu /* PMIC_GPIO_REG_DIG_IN_CTL */ 86223463fcSFenglin Wu #define PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN 0x80 87223463fcSFenglin Wu #define PMIC_GPIO_LV_MV_DIG_IN_DTEST_SEL_MASK 0x7 88223463fcSFenglin Wu #define PMIC_GPIO_DIG_IN_DTEST_SEL_MASK 0xf 89223463fcSFenglin Wu 90eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_OUT_CTL */ 91eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_STRENGTH_SHIFT 0 92eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_STRENGTH_MASK 0x3 93eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_TYPE_SHIFT 4 94eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_TYPE_MASK 0x3 95eadff302SIvan T. Ivanov 96eadff302SIvan T. Ivanov /* 97eadff302SIvan T. Ivanov * Output type - indicates pin should be configured as push-pull, 98eadff302SIvan T. Ivanov * open drain or open source. 99eadff302SIvan T. Ivanov */ 100eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_CMOS 0 101eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS 1 102eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS 2 103eadff302SIvan T. Ivanov 104*723e8462SAnjelique Melendez #define PMIC_GPIO_OUT_STRENGTH_LOW 1 105*723e8462SAnjelique Melendez #define PMIC_GPIO_OUT_STRENGTH_HIGH 3 106*723e8462SAnjelique Melendez 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 * @is_enabled: Set to false when GPIO should be put in high Z state. 140eadff302SIvan T. Ivanov * @out_value: Cached pin output value 141eadff302SIvan T. Ivanov * @have_buffer: Set to true if GPIO output could be configured in push-pull, 142eadff302SIvan T. Ivanov * open-drain or open-source mode. 143eadff302SIvan T. Ivanov * @output_enabled: Set to true if GPIO output logic is enabled. 144eadff302SIvan T. Ivanov * @input_enabled: Set to true if GPIO input buffer logic is enabled. 145d7b5f5ccSFenglin Wu * @analog_pass: Set to true if GPIO is in analog-pass-through mode. 146d7b5f5ccSFenglin Wu * @lv_mv_type: Set to true if GPIO subtype is GPIO_LV(0x10) or GPIO_MV(0x11). 147eadff302SIvan T. Ivanov * @num_sources: Number of power-sources supported by this GPIO. 148eadff302SIvan T. Ivanov * @power_source: Current power-source used. 149eadff302SIvan T. Ivanov * @buffer_type: Push-pull, open-drain or open-source. 150eadff302SIvan T. Ivanov * @pullup: Constant current which flow trough GPIO output buffer. 151eadff302SIvan T. Ivanov * @strength: No, Low, Medium, High 152eadff302SIvan T. Ivanov * @function: See pmic_gpio_functions[] 153d7b5f5ccSFenglin Wu * @atest: the ATEST selection for GPIO analog-pass-through mode 154223463fcSFenglin Wu * @dtest_buffer: the DTEST buffer selection for digital input mode. 155eadff302SIvan T. Ivanov */ 156eadff302SIvan T. Ivanov struct pmic_gpio_pad { 157eadff302SIvan T. Ivanov u16 base; 158eadff302SIvan T. Ivanov bool is_enabled; 159eadff302SIvan T. Ivanov bool out_value; 160eadff302SIvan T. Ivanov bool have_buffer; 161eadff302SIvan T. Ivanov bool output_enabled; 162eadff302SIvan T. Ivanov bool input_enabled; 163d7b5f5ccSFenglin Wu bool analog_pass; 164d7b5f5ccSFenglin Wu bool lv_mv_type; 165eadff302SIvan T. Ivanov unsigned int num_sources; 166eadff302SIvan T. Ivanov unsigned int power_source; 167eadff302SIvan T. Ivanov unsigned int buffer_type; 168eadff302SIvan T. Ivanov unsigned int pullup; 169eadff302SIvan T. Ivanov unsigned int strength; 170eadff302SIvan T. Ivanov unsigned int function; 171d7b5f5ccSFenglin Wu unsigned int atest; 172223463fcSFenglin Wu unsigned int dtest_buffer; 173eadff302SIvan T. Ivanov }; 174eadff302SIvan T. Ivanov 175eadff302SIvan T. Ivanov struct pmic_gpio_state { 176eadff302SIvan T. Ivanov struct device *dev; 177eadff302SIvan T. Ivanov struct regmap *map; 178eadff302SIvan T. Ivanov struct pinctrl_dev *ctrl; 179eadff302SIvan T. Ivanov struct gpio_chip chip; 180d36a9773SDavid Collins u8 usid; 181d36a9773SDavid Collins u8 pid_base; 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; 435727293a8SSubbaraman Narayanamurthy case PIN_CONFIG_OUTPUT_ENABLE: 436727293a8SSubbaraman Narayanamurthy arg = pad->output_enabled; 437727293a8SSubbaraman Narayanamurthy break; 438eadff302SIvan T. Ivanov case PIN_CONFIG_OUTPUT: 439eadff302SIvan T. Ivanov arg = pad->out_value; 440eadff302SIvan T. Ivanov break; 441eadff302SIvan T. Ivanov case PMIC_GPIO_CONF_PULL_UP: 442eadff302SIvan T. Ivanov arg = pad->pullup; 443eadff302SIvan T. Ivanov break; 444eadff302SIvan T. Ivanov case PMIC_GPIO_CONF_STRENGTH: 445*723e8462SAnjelique Melendez switch (pad->strength) { 446*723e8462SAnjelique Melendez case PMIC_GPIO_OUT_STRENGTH_HIGH: 447*723e8462SAnjelique Melendez arg = PMIC_GPIO_STRENGTH_HIGH; 448*723e8462SAnjelique Melendez break; 449*723e8462SAnjelique Melendez case PMIC_GPIO_OUT_STRENGTH_LOW: 450*723e8462SAnjelique Melendez arg = PMIC_GPIO_STRENGTH_LOW; 451*723e8462SAnjelique Melendez break; 452*723e8462SAnjelique Melendez default: 453eadff302SIvan T. Ivanov arg = pad->strength; 454eadff302SIvan T. Ivanov break; 455*723e8462SAnjelique Melendez } 456*723e8462SAnjelique Melendez break; 457d7b5f5ccSFenglin Wu case PMIC_GPIO_CONF_ATEST: 458d7b5f5ccSFenglin Wu arg = pad->atest; 459d7b5f5ccSFenglin Wu break; 460d7b5f5ccSFenglin Wu case PMIC_GPIO_CONF_ANALOG_PASS: 461d7b5f5ccSFenglin Wu arg = pad->analog_pass; 462d7b5f5ccSFenglin Wu break; 463223463fcSFenglin Wu case PMIC_GPIO_CONF_DTEST_BUFFER: 464223463fcSFenglin Wu arg = pad->dtest_buffer; 465223463fcSFenglin Wu break; 466eadff302SIvan T. Ivanov default: 467eadff302SIvan T. Ivanov return -EINVAL; 468eadff302SIvan T. Ivanov } 469eadff302SIvan T. Ivanov 470eadff302SIvan T. Ivanov *config = pinconf_to_config_packed(param, arg); 471eadff302SIvan T. Ivanov return 0; 472eadff302SIvan T. Ivanov } 473eadff302SIvan T. Ivanov 474eadff302SIvan T. Ivanov static int pmic_gpio_config_set(struct pinctrl_dev *pctldev, unsigned int pin, 475eadff302SIvan T. Ivanov unsigned long *configs, unsigned nconfs) 476eadff302SIvan T. Ivanov { 477eadff302SIvan T. Ivanov struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev); 478eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad; 479eadff302SIvan T. Ivanov unsigned param, arg; 480eadff302SIvan T. Ivanov unsigned int val; 481eadff302SIvan T. Ivanov int i, ret; 482eadff302SIvan T. Ivanov 483eadff302SIvan T. Ivanov pad = pctldev->desc->pins[pin].drv_data; 484eadff302SIvan T. Ivanov 4856cb74f44SFenglin Wu pad->is_enabled = true; 486eadff302SIvan T. Ivanov for (i = 0; i < nconfs; i++) { 487eadff302SIvan T. Ivanov param = pinconf_to_config_param(configs[i]); 488eadff302SIvan T. Ivanov arg = pinconf_to_config_argument(configs[i]); 489eadff302SIvan T. Ivanov 490eadff302SIvan T. Ivanov switch (param) { 491eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_PUSH_PULL: 492eadff302SIvan T. Ivanov pad->buffer_type = PMIC_GPIO_OUT_BUF_CMOS; 493eadff302SIvan T. Ivanov break; 494eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_OPEN_DRAIN: 495eadff302SIvan T. Ivanov if (!pad->have_buffer) 496eadff302SIvan T. Ivanov return -EINVAL; 497eadff302SIvan T. Ivanov pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS; 498eadff302SIvan T. Ivanov break; 499eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_OPEN_SOURCE: 500eadff302SIvan T. Ivanov if (!pad->have_buffer) 501eadff302SIvan T. Ivanov return -EINVAL; 502eadff302SIvan T. Ivanov pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS; 503eadff302SIvan T. Ivanov break; 504eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_DISABLE: 505eadff302SIvan T. Ivanov pad->pullup = PMIC_GPIO_PULL_DISABLE; 506eadff302SIvan T. Ivanov break; 507eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_PULL_UP: 508eadff302SIvan T. Ivanov pad->pullup = PMIC_GPIO_PULL_UP_30; 509eadff302SIvan T. Ivanov break; 510eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_PULL_DOWN: 511eadff302SIvan T. Ivanov if (arg) 512eadff302SIvan T. Ivanov pad->pullup = PMIC_GPIO_PULL_DOWN; 513eadff302SIvan T. Ivanov else 514eadff302SIvan T. Ivanov pad->pullup = PMIC_GPIO_PULL_DISABLE; 515eadff302SIvan T. Ivanov break; 516eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: 517eadff302SIvan T. Ivanov pad->is_enabled = false; 518eadff302SIvan T. Ivanov break; 519eadff302SIvan T. Ivanov case PIN_CONFIG_POWER_SOURCE: 5204e83ac4cSFenglin Wu if (arg >= pad->num_sources) 521eadff302SIvan T. Ivanov return -EINVAL; 522eadff302SIvan T. Ivanov pad->power_source = arg; 523eadff302SIvan T. Ivanov break; 524eadff302SIvan T. Ivanov case PIN_CONFIG_INPUT_ENABLE: 525eadff302SIvan T. Ivanov pad->input_enabled = arg ? true : false; 526eadff302SIvan T. Ivanov break; 527727293a8SSubbaraman Narayanamurthy case PIN_CONFIG_OUTPUT_ENABLE: 528727293a8SSubbaraman Narayanamurthy pad->output_enabled = arg ? true : false; 529727293a8SSubbaraman Narayanamurthy break; 530eadff302SIvan T. Ivanov case PIN_CONFIG_OUTPUT: 531eadff302SIvan T. Ivanov pad->output_enabled = true; 532eadff302SIvan T. Ivanov pad->out_value = arg; 533eadff302SIvan T. Ivanov break; 534eadff302SIvan T. Ivanov case PMIC_GPIO_CONF_PULL_UP: 535eadff302SIvan T. Ivanov if (arg > PMIC_GPIO_PULL_UP_1P5_30) 536eadff302SIvan T. Ivanov return -EINVAL; 537eadff302SIvan T. Ivanov pad->pullup = arg; 538eadff302SIvan T. Ivanov break; 539eadff302SIvan T. Ivanov case PMIC_GPIO_CONF_STRENGTH: 540eadff302SIvan T. Ivanov if (arg > PMIC_GPIO_STRENGTH_LOW) 541eadff302SIvan T. Ivanov return -EINVAL; 542*723e8462SAnjelique Melendez switch (arg) { 543*723e8462SAnjelique Melendez case PMIC_GPIO_STRENGTH_HIGH: 544*723e8462SAnjelique Melendez pad->strength = PMIC_GPIO_OUT_STRENGTH_HIGH; 545*723e8462SAnjelique Melendez break; 546*723e8462SAnjelique Melendez case PMIC_GPIO_STRENGTH_LOW: 547*723e8462SAnjelique Melendez pad->strength = PMIC_GPIO_OUT_STRENGTH_LOW; 548*723e8462SAnjelique Melendez break; 549*723e8462SAnjelique Melendez default: 550eadff302SIvan T. Ivanov pad->strength = arg; 551eadff302SIvan T. Ivanov break; 552*723e8462SAnjelique Melendez } 553*723e8462SAnjelique Melendez break; 554d7b5f5ccSFenglin Wu case PMIC_GPIO_CONF_ATEST: 555d7b5f5ccSFenglin Wu if (!pad->lv_mv_type || arg > 4) 556d7b5f5ccSFenglin Wu return -EINVAL; 557d7b5f5ccSFenglin Wu pad->atest = arg; 558d7b5f5ccSFenglin Wu break; 559d7b5f5ccSFenglin Wu case PMIC_GPIO_CONF_ANALOG_PASS: 560d7b5f5ccSFenglin Wu if (!pad->lv_mv_type) 561d7b5f5ccSFenglin Wu return -EINVAL; 562d7b5f5ccSFenglin Wu pad->analog_pass = true; 563d7b5f5ccSFenglin Wu break; 564223463fcSFenglin Wu case PMIC_GPIO_CONF_DTEST_BUFFER: 565223463fcSFenglin Wu if (arg > 4) 566223463fcSFenglin Wu return -EINVAL; 567223463fcSFenglin Wu pad->dtest_buffer = arg; 568223463fcSFenglin Wu break; 569eadff302SIvan T. Ivanov default: 570eadff302SIvan T. Ivanov return -EINVAL; 571eadff302SIvan T. Ivanov } 572eadff302SIvan T. Ivanov } 573eadff302SIvan T. Ivanov 574eadff302SIvan T. Ivanov val = pad->power_source << PMIC_GPIO_REG_VIN_SHIFT; 575eadff302SIvan T. Ivanov 576eadff302SIvan T. Ivanov ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL, val); 577eadff302SIvan T. Ivanov if (ret < 0) 578eadff302SIvan T. Ivanov return ret; 579eadff302SIvan T. Ivanov 580eadff302SIvan T. Ivanov val = pad->pullup << PMIC_GPIO_REG_PULL_SHIFT; 581eadff302SIvan T. Ivanov 582eadff302SIvan T. Ivanov ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_PULL_CTL, val); 583eadff302SIvan T. Ivanov if (ret < 0) 584eadff302SIvan T. Ivanov return ret; 585eadff302SIvan T. Ivanov 586eadff302SIvan T. Ivanov val = pad->buffer_type << PMIC_GPIO_REG_OUT_TYPE_SHIFT; 587982df6aeSIvan T. Ivanov val |= pad->strength << PMIC_GPIO_REG_OUT_STRENGTH_SHIFT; 588eadff302SIvan T. Ivanov 589eadff302SIvan T. Ivanov ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL, val); 590eadff302SIvan T. Ivanov if (ret < 0) 591eadff302SIvan T. Ivanov return ret; 592eadff302SIvan T. Ivanov 593223463fcSFenglin Wu if (pad->dtest_buffer == 0) { 594223463fcSFenglin Wu val = 0; 595223463fcSFenglin Wu } else { 596223463fcSFenglin Wu if (pad->lv_mv_type) { 597223463fcSFenglin Wu val = pad->dtest_buffer - 1; 598223463fcSFenglin Wu val |= PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN; 599223463fcSFenglin Wu } else { 600223463fcSFenglin Wu val = BIT(pad->dtest_buffer - 1); 601223463fcSFenglin Wu } 602223463fcSFenglin Wu } 603223463fcSFenglin Wu ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_IN_CTL, val); 604223463fcSFenglin Wu if (ret < 0) 605223463fcSFenglin Wu return ret; 606223463fcSFenglin Wu 607d7b5f5ccSFenglin Wu if (pad->analog_pass) 608d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_ANALOG_PASS_THRU; 609d7b5f5ccSFenglin Wu else if (pad->output_enabled && pad->input_enabled) 610d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT; 611d7b5f5ccSFenglin Wu else if (pad->output_enabled) 612d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_OUTPUT; 613eadff302SIvan T. Ivanov else 614d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_INPUT; 615eadff302SIvan T. Ivanov 616d7b5f5ccSFenglin Wu if (pad->lv_mv_type) { 617d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 618d7b5f5ccSFenglin Wu PMIC_GPIO_REG_MODE_CTL, val); 619d7b5f5ccSFenglin Wu if (ret < 0) 620d7b5f5ccSFenglin Wu return ret; 621d7b5f5ccSFenglin Wu 622d7b5f5ccSFenglin Wu val = pad->atest - 1; 623d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 624d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL, val); 625d7b5f5ccSFenglin Wu if (ret < 0) 626d7b5f5ccSFenglin Wu return ret; 627d7b5f5ccSFenglin Wu 628d7b5f5ccSFenglin Wu val = pad->out_value 629d7b5f5ccSFenglin Wu << PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT; 630d7b5f5ccSFenglin Wu val |= pad->function 631d7b5f5ccSFenglin Wu & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK; 632d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 633d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL, val); 634d7b5f5ccSFenglin Wu if (ret < 0) 635d7b5f5ccSFenglin Wu return ret; 636d7b5f5ccSFenglin Wu } else { 637eadff302SIvan T. Ivanov val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT; 638eadff302SIvan T. Ivanov val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT; 639eadff302SIvan T. Ivanov val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT; 640eadff302SIvan T. Ivanov 641d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); 642d7b5f5ccSFenglin Wu if (ret < 0) 643d7b5f5ccSFenglin Wu return ret; 644d7b5f5ccSFenglin Wu } 645d7b5f5ccSFenglin Wu 6466cb74f44SFenglin Wu val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT; 6476cb74f44SFenglin Wu 6486cb74f44SFenglin Wu ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_EN_CTL, val); 6496cb74f44SFenglin Wu 650d7b5f5ccSFenglin Wu return ret; 651eadff302SIvan T. Ivanov } 652eadff302SIvan T. Ivanov 653eadff302SIvan T. Ivanov static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev, 654eadff302SIvan T. Ivanov struct seq_file *s, unsigned pin) 655eadff302SIvan T. Ivanov { 656eadff302SIvan T. Ivanov struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev); 657eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad; 658d7b5f5ccSFenglin Wu int ret, val, function; 659eadff302SIvan T. Ivanov 660eadff302SIvan T. Ivanov static const char *const biases[] = { 661eadff302SIvan T. Ivanov "pull-up 30uA", "pull-up 1.5uA", "pull-up 31.5uA", 662eadff302SIvan T. Ivanov "pull-up 1.5uA + 30uA boost", "pull-down 10uA", "no pull" 663eadff302SIvan T. Ivanov }; 664eadff302SIvan T. Ivanov static const char *const buffer_types[] = { 665eadff302SIvan T. Ivanov "push-pull", "open-drain", "open-source" 666eadff302SIvan T. Ivanov }; 667eadff302SIvan T. Ivanov static const char *const strengths[] = { 668eadff302SIvan T. Ivanov "no", "high", "medium", "low" 669eadff302SIvan T. Ivanov }; 670eadff302SIvan T. Ivanov 671eadff302SIvan T. Ivanov pad = pctldev->desc->pins[pin].drv_data; 672eadff302SIvan T. Ivanov 673eadff302SIvan T. Ivanov seq_printf(s, " gpio%-2d:", pin + PMIC_GPIO_PHYSICAL_OFFSET); 674eadff302SIvan T. Ivanov 675eadff302SIvan T. Ivanov val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_EN_CTL); 676eadff302SIvan T. Ivanov 677eadff302SIvan T. Ivanov if (val < 0 || !(val >> PMIC_GPIO_REG_MASTER_EN_SHIFT)) { 678eadff302SIvan T. Ivanov seq_puts(s, " ---"); 679eadff302SIvan T. Ivanov } else { 68024a66618SIvan T. Ivanov if (pad->input_enabled) { 681eadff302SIvan T. Ivanov ret = pmic_gpio_read(state, pad, PMIC_MPP_REG_RT_STS); 68224a66618SIvan T. Ivanov if (ret < 0) 68324a66618SIvan T. Ivanov return; 68424a66618SIvan T. Ivanov 685eadff302SIvan T. Ivanov ret &= PMIC_MPP_REG_RT_STS_VAL_MASK; 686eadff302SIvan T. Ivanov pad->out_value = ret; 687eadff302SIvan T. Ivanov } 688d7b5f5ccSFenglin Wu /* 689d7b5f5ccSFenglin Wu * For the non-LV/MV subtypes only 2 special functions are 690d7b5f5ccSFenglin Wu * available, offsetting the dtest function values by 2. 691d7b5f5ccSFenglin Wu */ 692d7b5f5ccSFenglin Wu function = pad->function; 693d7b5f5ccSFenglin Wu if (!pad->lv_mv_type && 694d7b5f5ccSFenglin Wu pad->function >= PMIC_GPIO_FUNC_INDEX_FUNC3) 695d7b5f5ccSFenglin Wu function += PMIC_GPIO_FUNC_INDEX_DTEST1 - 696d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_FUNC3; 697eadff302SIvan T. Ivanov 698d7b5f5ccSFenglin Wu if (pad->analog_pass) 699d7b5f5ccSFenglin Wu seq_puts(s, " analog-pass"); 700d7b5f5ccSFenglin Wu else 701d7b5f5ccSFenglin Wu seq_printf(s, " %-4s", 702d7b5f5ccSFenglin Wu pad->output_enabled ? "out" : "in"); 703202ba5ebSBjorn Andersson seq_printf(s, " %-4s", pad->out_value ? "high" : "low"); 704d7b5f5ccSFenglin Wu seq_printf(s, " %-7s", pmic_gpio_functions[function]); 705eadff302SIvan T. Ivanov seq_printf(s, " vin-%d", pad->power_source); 706eadff302SIvan T. Ivanov seq_printf(s, " %-27s", biases[pad->pullup]); 707eadff302SIvan T. Ivanov seq_printf(s, " %-10s", buffer_types[pad->buffer_type]); 708eadff302SIvan T. Ivanov seq_printf(s, " %-7s", strengths[pad->strength]); 709d7b5f5ccSFenglin Wu seq_printf(s, " atest-%d", pad->atest); 710223463fcSFenglin Wu seq_printf(s, " dtest-%d", pad->dtest_buffer); 711eadff302SIvan T. Ivanov } 712eadff302SIvan T. Ivanov } 713eadff302SIvan T. Ivanov 714eadff302SIvan T. Ivanov static const struct pinconf_ops pmic_gpio_pinconf_ops = { 7157382b623SSoren Brinkmann .is_generic = true, 716eadff302SIvan T. Ivanov .pin_config_group_get = pmic_gpio_config_get, 717eadff302SIvan T. Ivanov .pin_config_group_set = pmic_gpio_config_set, 718eadff302SIvan T. Ivanov .pin_config_group_dbg_show = pmic_gpio_config_dbg_show, 719eadff302SIvan T. Ivanov }; 720eadff302SIvan T. Ivanov 721eadff302SIvan T. Ivanov static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned pin) 722eadff302SIvan T. Ivanov { 723c52d9df1SLinus Walleij struct pmic_gpio_state *state = gpiochip_get_data(chip); 724eadff302SIvan T. Ivanov unsigned long config; 725eadff302SIvan T. Ivanov 726eadff302SIvan T. Ivanov config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1); 727eadff302SIvan T. Ivanov 728eadff302SIvan T. Ivanov return pmic_gpio_config_set(state->ctrl, pin, &config, 1); 729eadff302SIvan T. Ivanov } 730eadff302SIvan T. Ivanov 731eadff302SIvan T. Ivanov static int pmic_gpio_direction_output(struct gpio_chip *chip, 732eadff302SIvan T. Ivanov unsigned pin, int val) 733eadff302SIvan T. Ivanov { 734c52d9df1SLinus Walleij struct pmic_gpio_state *state = gpiochip_get_data(chip); 735eadff302SIvan T. Ivanov unsigned long config; 736eadff302SIvan T. Ivanov 737eadff302SIvan T. Ivanov config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val); 738eadff302SIvan T. Ivanov 739eadff302SIvan T. Ivanov return pmic_gpio_config_set(state->ctrl, pin, &config, 1); 740eadff302SIvan T. Ivanov } 741eadff302SIvan T. Ivanov 742eadff302SIvan T. Ivanov static int pmic_gpio_get(struct gpio_chip *chip, unsigned pin) 743eadff302SIvan T. Ivanov { 744c52d9df1SLinus Walleij struct pmic_gpio_state *state = gpiochip_get_data(chip); 745eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad; 746eadff302SIvan T. Ivanov int ret; 747eadff302SIvan T. Ivanov 748eadff302SIvan T. Ivanov pad = state->ctrl->desc->pins[pin].drv_data; 749eadff302SIvan T. Ivanov 750eadff302SIvan T. Ivanov if (!pad->is_enabled) 751eadff302SIvan T. Ivanov return -EINVAL; 752eadff302SIvan T. Ivanov 753eadff302SIvan T. Ivanov if (pad->input_enabled) { 754eadff302SIvan T. Ivanov ret = pmic_gpio_read(state, pad, PMIC_MPP_REG_RT_STS); 755eadff302SIvan T. Ivanov if (ret < 0) 756eadff302SIvan T. Ivanov return ret; 757eadff302SIvan T. Ivanov 758eadff302SIvan T. Ivanov pad->out_value = ret & PMIC_MPP_REG_RT_STS_VAL_MASK; 759eadff302SIvan T. Ivanov } 760eadff302SIvan T. Ivanov 76186c1a219SLinus Walleij return !!pad->out_value; 762eadff302SIvan T. Ivanov } 763eadff302SIvan T. Ivanov 764eadff302SIvan T. Ivanov static void pmic_gpio_set(struct gpio_chip *chip, unsigned pin, int value) 765eadff302SIvan T. Ivanov { 766c52d9df1SLinus Walleij struct pmic_gpio_state *state = gpiochip_get_data(chip); 767eadff302SIvan T. Ivanov unsigned long config; 768eadff302SIvan T. Ivanov 769eadff302SIvan T. Ivanov config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value); 770eadff302SIvan T. Ivanov 771eadff302SIvan T. Ivanov pmic_gpio_config_set(state->ctrl, pin, &config, 1); 772eadff302SIvan T. Ivanov } 773eadff302SIvan T. Ivanov 774eadff302SIvan T. Ivanov static int pmic_gpio_of_xlate(struct gpio_chip *chip, 775eadff302SIvan T. Ivanov const struct of_phandle_args *gpio_desc, 776eadff302SIvan T. Ivanov u32 *flags) 777eadff302SIvan T. Ivanov { 778eadff302SIvan T. Ivanov if (chip->of_gpio_n_cells < 2) 779eadff302SIvan T. Ivanov return -EINVAL; 780eadff302SIvan T. Ivanov 781eadff302SIvan T. Ivanov if (flags) 782eadff302SIvan T. Ivanov *flags = gpio_desc->args[1]; 783eadff302SIvan T. Ivanov 784eadff302SIvan T. Ivanov return gpio_desc->args[0] - PMIC_GPIO_PHYSICAL_OFFSET; 785eadff302SIvan T. Ivanov } 786eadff302SIvan T. Ivanov 787eadff302SIvan T. Ivanov static void pmic_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) 788eadff302SIvan T. Ivanov { 789c52d9df1SLinus Walleij struct pmic_gpio_state *state = gpiochip_get_data(chip); 790eadff302SIvan T. Ivanov unsigned i; 791eadff302SIvan T. Ivanov 792eadff302SIvan T. Ivanov for (i = 0; i < chip->ngpio; i++) { 793eadff302SIvan T. Ivanov pmic_gpio_config_dbg_show(state->ctrl, s, i); 794eadff302SIvan T. Ivanov seq_puts(s, "\n"); 795eadff302SIvan T. Ivanov } 796eadff302SIvan T. Ivanov } 797eadff302SIvan T. Ivanov 798eadff302SIvan T. Ivanov static const struct gpio_chip pmic_gpio_gpio_template = { 799eadff302SIvan T. Ivanov .direction_input = pmic_gpio_direction_input, 800eadff302SIvan T. Ivanov .direction_output = pmic_gpio_direction_output, 801eadff302SIvan T. Ivanov .get = pmic_gpio_get, 802eadff302SIvan T. Ivanov .set = pmic_gpio_set, 80398c85d58SJonas Gorski .request = gpiochip_generic_request, 80498c85d58SJonas Gorski .free = gpiochip_generic_free, 805eadff302SIvan T. Ivanov .of_xlate = pmic_gpio_of_xlate, 806eadff302SIvan T. Ivanov .dbg_show = pmic_gpio_dbg_show, 807eadff302SIvan T. Ivanov }; 808eadff302SIvan T. Ivanov 809eadff302SIvan T. Ivanov static int pmic_gpio_populate(struct pmic_gpio_state *state, 810eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad) 811eadff302SIvan T. Ivanov { 812eadff302SIvan T. Ivanov int type, subtype, val, dir; 813eadff302SIvan T. Ivanov 814eadff302SIvan T. Ivanov type = pmic_gpio_read(state, pad, PMIC_GPIO_REG_TYPE); 815eadff302SIvan T. Ivanov if (type < 0) 816eadff302SIvan T. Ivanov return type; 817eadff302SIvan T. Ivanov 818eadff302SIvan T. Ivanov if (type != PMIC_GPIO_TYPE) { 819eadff302SIvan T. Ivanov dev_err(state->dev, "incorrect block type 0x%x at 0x%x\n", 820eadff302SIvan T. Ivanov type, pad->base); 821eadff302SIvan T. Ivanov return -ENODEV; 822eadff302SIvan T. Ivanov } 823eadff302SIvan T. Ivanov 824eadff302SIvan T. Ivanov subtype = pmic_gpio_read(state, pad, PMIC_GPIO_REG_SUBTYPE); 825eadff302SIvan T. Ivanov if (subtype < 0) 826eadff302SIvan T. Ivanov return subtype; 827eadff302SIvan T. Ivanov 828eadff302SIvan T. Ivanov switch (subtype) { 829eadff302SIvan T. Ivanov case PMIC_GPIO_SUBTYPE_GPIO_4CH: 830eadff302SIvan T. Ivanov pad->have_buffer = true; 8311586f556SGustavo A. R. Silva fallthrough; 832eadff302SIvan T. Ivanov case PMIC_GPIO_SUBTYPE_GPIOC_4CH: 833eadff302SIvan T. Ivanov pad->num_sources = 4; 834eadff302SIvan T. Ivanov break; 835eadff302SIvan T. Ivanov case PMIC_GPIO_SUBTYPE_GPIO_8CH: 836eadff302SIvan T. Ivanov pad->have_buffer = true; 8371586f556SGustavo A. R. Silva fallthrough; 838eadff302SIvan T. Ivanov case PMIC_GPIO_SUBTYPE_GPIOC_8CH: 839eadff302SIvan T. Ivanov pad->num_sources = 8; 840eadff302SIvan T. Ivanov break; 841d7b5f5ccSFenglin Wu case PMIC_GPIO_SUBTYPE_GPIO_LV: 842d7b5f5ccSFenglin Wu pad->num_sources = 1; 843d7b5f5ccSFenglin Wu pad->have_buffer = true; 844d7b5f5ccSFenglin Wu pad->lv_mv_type = true; 845d7b5f5ccSFenglin Wu break; 846d7b5f5ccSFenglin Wu case PMIC_GPIO_SUBTYPE_GPIO_MV: 847d7b5f5ccSFenglin Wu pad->num_sources = 2; 848d7b5f5ccSFenglin Wu pad->have_buffer = true; 849d7b5f5ccSFenglin Wu pad->lv_mv_type = true; 850d7b5f5ccSFenglin Wu break; 8514af95d09SDavid Collins case PMIC_GPIO_SUBTYPE_GPIO_LV_VIN2: 8524af95d09SDavid Collins pad->num_sources = 2; 8534af95d09SDavid Collins pad->have_buffer = true; 8544af95d09SDavid Collins pad->lv_mv_type = true; 8554af95d09SDavid Collins break; 8564af95d09SDavid Collins case PMIC_GPIO_SUBTYPE_GPIO_MV_VIN3: 8574af95d09SDavid Collins pad->num_sources = 3; 8584af95d09SDavid Collins pad->have_buffer = true; 8594af95d09SDavid Collins pad->lv_mv_type = true; 8604af95d09SDavid Collins break; 861eadff302SIvan T. Ivanov default: 862eadff302SIvan T. Ivanov dev_err(state->dev, "unknown GPIO type 0x%x\n", subtype); 863eadff302SIvan T. Ivanov return -ENODEV; 864eadff302SIvan T. Ivanov } 865eadff302SIvan T. Ivanov 866d7b5f5ccSFenglin Wu if (pad->lv_mv_type) { 867d7b5f5ccSFenglin Wu val = pmic_gpio_read(state, pad, 868d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL); 869d7b5f5ccSFenglin Wu if (val < 0) 870d7b5f5ccSFenglin Wu return val; 871d7b5f5ccSFenglin Wu 872d7b5f5ccSFenglin Wu pad->out_value = !!(val & PMIC_GPIO_LV_MV_OUTPUT_INVERT); 873d7b5f5ccSFenglin Wu pad->function = val & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK; 874d7b5f5ccSFenglin Wu 875d7b5f5ccSFenglin Wu val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL); 876d7b5f5ccSFenglin Wu if (val < 0) 877d7b5f5ccSFenglin Wu return val; 878d7b5f5ccSFenglin Wu 879d7b5f5ccSFenglin Wu dir = val & PMIC_GPIO_REG_LV_MV_MODE_DIR_MASK; 880d7b5f5ccSFenglin Wu } else { 881eadff302SIvan T. Ivanov val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL); 882eadff302SIvan T. Ivanov if (val < 0) 883eadff302SIvan T. Ivanov return val; 884eadff302SIvan T. Ivanov 885eadff302SIvan T. Ivanov pad->out_value = val & PMIC_GPIO_REG_MODE_VALUE_SHIFT; 886eadff302SIvan T. Ivanov 887eadff302SIvan T. Ivanov dir = val >> PMIC_GPIO_REG_MODE_DIR_SHIFT; 888eadff302SIvan T. Ivanov dir &= PMIC_GPIO_REG_MODE_DIR_MASK; 889d7b5f5ccSFenglin Wu pad->function = val >> PMIC_GPIO_REG_MODE_FUNCTION_SHIFT; 890d7b5f5ccSFenglin Wu pad->function &= PMIC_GPIO_REG_MODE_FUNCTION_MASK; 891d7b5f5ccSFenglin Wu } 892d7b5f5ccSFenglin Wu 893eadff302SIvan T. Ivanov switch (dir) { 894d7b5f5ccSFenglin Wu case PMIC_GPIO_MODE_DIGITAL_INPUT: 895eadff302SIvan T. Ivanov pad->input_enabled = true; 896eadff302SIvan T. Ivanov pad->output_enabled = false; 897eadff302SIvan T. Ivanov break; 898d7b5f5ccSFenglin Wu case PMIC_GPIO_MODE_DIGITAL_OUTPUT: 899eadff302SIvan T. Ivanov pad->input_enabled = false; 900eadff302SIvan T. Ivanov pad->output_enabled = true; 901eadff302SIvan T. Ivanov break; 902d7b5f5ccSFenglin Wu case PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT: 903eadff302SIvan T. Ivanov pad->input_enabled = true; 904eadff302SIvan T. Ivanov pad->output_enabled = true; 905eadff302SIvan T. Ivanov break; 906d7b5f5ccSFenglin Wu case PMIC_GPIO_MODE_ANALOG_PASS_THRU: 907d7b5f5ccSFenglin Wu if (!pad->lv_mv_type) 908d7b5f5ccSFenglin Wu return -ENODEV; 909d7b5f5ccSFenglin Wu pad->analog_pass = true; 910d7b5f5ccSFenglin Wu break; 911eadff302SIvan T. Ivanov default: 912eadff302SIvan T. Ivanov dev_err(state->dev, "unknown GPIO direction\n"); 913eadff302SIvan T. Ivanov return -ENODEV; 914eadff302SIvan T. Ivanov } 915eadff302SIvan T. Ivanov 916eadff302SIvan T. Ivanov val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL); 917eadff302SIvan T. Ivanov if (val < 0) 918eadff302SIvan T. Ivanov return val; 919eadff302SIvan T. Ivanov 920eadff302SIvan T. Ivanov pad->power_source = val >> PMIC_GPIO_REG_VIN_SHIFT; 921eadff302SIvan T. Ivanov pad->power_source &= PMIC_GPIO_REG_VIN_MASK; 922eadff302SIvan T. Ivanov 923eadff302SIvan T. Ivanov val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_PULL_CTL); 924eadff302SIvan T. Ivanov if (val < 0) 925eadff302SIvan T. Ivanov return val; 926eadff302SIvan T. Ivanov 927eadff302SIvan T. Ivanov pad->pullup = val >> PMIC_GPIO_REG_PULL_SHIFT; 928eadff302SIvan T. Ivanov pad->pullup &= PMIC_GPIO_REG_PULL_MASK; 929eadff302SIvan T. Ivanov 930223463fcSFenglin Wu val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_IN_CTL); 931223463fcSFenglin Wu if (val < 0) 932223463fcSFenglin Wu return val; 933223463fcSFenglin Wu 934223463fcSFenglin Wu if (pad->lv_mv_type && (val & PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN)) 935223463fcSFenglin Wu pad->dtest_buffer = 936223463fcSFenglin Wu (val & PMIC_GPIO_LV_MV_DIG_IN_DTEST_SEL_MASK) + 1; 937223463fcSFenglin Wu else if (!pad->lv_mv_type) 938223463fcSFenglin Wu pad->dtest_buffer = ffs(val); 939223463fcSFenglin Wu else 940223463fcSFenglin Wu pad->dtest_buffer = 0; 941223463fcSFenglin Wu 942223463fcSFenglin Wu val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL); 943223463fcSFenglin Wu if (val < 0) 944223463fcSFenglin Wu return val; 945223463fcSFenglin Wu 946eadff302SIvan T. Ivanov pad->strength = val >> PMIC_GPIO_REG_OUT_STRENGTH_SHIFT; 947eadff302SIvan T. Ivanov pad->strength &= PMIC_GPIO_REG_OUT_STRENGTH_MASK; 948eadff302SIvan T. Ivanov 949eadff302SIvan T. Ivanov pad->buffer_type = val >> PMIC_GPIO_REG_OUT_TYPE_SHIFT; 950eadff302SIvan T. Ivanov pad->buffer_type &= PMIC_GPIO_REG_OUT_TYPE_MASK; 951eadff302SIvan T. Ivanov 952d7b5f5ccSFenglin Wu if (pad->lv_mv_type) { 953d7b5f5ccSFenglin Wu val = pmic_gpio_read(state, pad, 954d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL); 955d7b5f5ccSFenglin Wu if (val < 0) 956d7b5f5ccSFenglin Wu return val; 957d7b5f5ccSFenglin Wu pad->atest = (val & PMIC_GPIO_LV_MV_ANA_MUX_SEL_MASK) + 1; 958d7b5f5ccSFenglin Wu } 959d7b5f5ccSFenglin Wu 960eadff302SIvan T. Ivanov /* Pin could be disabled with PIN_CONFIG_BIAS_HIGH_IMPEDANCE */ 961eadff302SIvan T. Ivanov pad->is_enabled = true; 962eadff302SIvan T. Ivanov return 0; 963eadff302SIvan T. Ivanov } 964eadff302SIvan T. Ivanov 965ca69e2d1SBrian Masney static int pmic_gpio_domain_translate(struct irq_domain *domain, 966ca69e2d1SBrian Masney struct irq_fwspec *fwspec, 967ca69e2d1SBrian Masney unsigned long *hwirq, 968ca69e2d1SBrian Masney unsigned int *type) 969ca69e2d1SBrian Masney { 970ca69e2d1SBrian Masney struct pmic_gpio_state *state = container_of(domain->host_data, 971ca69e2d1SBrian Masney struct pmic_gpio_state, 972ca69e2d1SBrian Masney chip); 973ca69e2d1SBrian Masney 974dac7da98SBjorn Andersson if (fwspec->param_count != 2 || 975dac7da98SBjorn Andersson fwspec->param[0] < 1 || fwspec->param[0] > state->chip.ngpio) 976ca69e2d1SBrian Masney return -EINVAL; 977ca69e2d1SBrian Masney 978ca69e2d1SBrian Masney *hwirq = fwspec->param[0] - PMIC_GPIO_PHYSICAL_OFFSET; 979ca69e2d1SBrian Masney *type = fwspec->param[1]; 980ca69e2d1SBrian Masney 981ca69e2d1SBrian Masney return 0; 982ca69e2d1SBrian Masney } 983ca69e2d1SBrian Masney 984821c76c4SBrian Masney static unsigned int pmic_gpio_child_offset_to_irq(struct gpio_chip *chip, 985821c76c4SBrian Masney unsigned int offset) 986ca69e2d1SBrian Masney { 987821c76c4SBrian Masney return offset + PMIC_GPIO_PHYSICAL_OFFSET; 988ca69e2d1SBrian Masney } 989ca69e2d1SBrian Masney 990821c76c4SBrian Masney static int pmic_gpio_child_to_parent_hwirq(struct gpio_chip *chip, 991821c76c4SBrian Masney unsigned int child_hwirq, 992821c76c4SBrian Masney unsigned int child_type, 993821c76c4SBrian Masney unsigned int *parent_hwirq, 994821c76c4SBrian Masney unsigned int *parent_type) 995821c76c4SBrian Masney { 996d36a9773SDavid Collins struct pmic_gpio_state *state = gpiochip_get_data(chip); 997d36a9773SDavid Collins 998d36a9773SDavid Collins *parent_hwirq = child_hwirq + state->pid_base; 999821c76c4SBrian Masney *parent_type = child_type; 1000821c76c4SBrian Masney 1001821c76c4SBrian Masney return 0; 1002821c76c4SBrian Masney } 1003ca69e2d1SBrian Masney 100491a29af4SMarc Zyngier static int pmic_gpio_populate_parent_fwspec(struct gpio_chip *chip, 100591a29af4SMarc Zyngier union gpio_irq_fwspec *gfwspec, 1006d36a9773SDavid Collins unsigned int parent_hwirq, 1007d36a9773SDavid Collins unsigned int parent_type) 1008d36a9773SDavid Collins { 1009d36a9773SDavid Collins struct pmic_gpio_state *state = gpiochip_get_data(chip); 101091a29af4SMarc Zyngier struct irq_fwspec *fwspec = &gfwspec->fwspec; 1011d36a9773SDavid Collins 1012d36a9773SDavid Collins fwspec->fwnode = chip->irq.parent_domain->fwnode; 1013d36a9773SDavid Collins 1014d36a9773SDavid Collins fwspec->param_count = 4; 1015d36a9773SDavid Collins fwspec->param[0] = state->usid; 1016d36a9773SDavid Collins fwspec->param[1] = parent_hwirq; 1017d36a9773SDavid Collins /* param[2] must be left as 0 */ 1018d36a9773SDavid Collins fwspec->param[3] = parent_type; 1019d36a9773SDavid Collins 102091a29af4SMarc Zyngier return 0; 1021d36a9773SDavid Collins } 1022d36a9773SDavid Collins 10231a41d1e5SManivannan Sadhasivam static void pmic_gpio_irq_mask(struct irq_data *data) 10241a41d1e5SManivannan Sadhasivam { 10251a41d1e5SManivannan Sadhasivam struct gpio_chip *gc = irq_data_get_irq_chip_data(data); 10261a41d1e5SManivannan Sadhasivam 10271a41d1e5SManivannan Sadhasivam irq_chip_mask_parent(data); 10281a41d1e5SManivannan Sadhasivam gpiochip_disable_irq(gc, data->hwirq); 10291a41d1e5SManivannan Sadhasivam } 10301a41d1e5SManivannan Sadhasivam 10311a41d1e5SManivannan Sadhasivam static void pmic_gpio_irq_unmask(struct irq_data *data) 10321a41d1e5SManivannan Sadhasivam { 10331a41d1e5SManivannan Sadhasivam struct gpio_chip *gc = irq_data_get_irq_chip_data(data); 10341a41d1e5SManivannan Sadhasivam 10351a41d1e5SManivannan Sadhasivam gpiochip_enable_irq(gc, data->hwirq); 10361a41d1e5SManivannan Sadhasivam irq_chip_unmask_parent(data); 10371a41d1e5SManivannan Sadhasivam } 10381a41d1e5SManivannan Sadhasivam 10391a41d1e5SManivannan Sadhasivam static const struct irq_chip spmi_gpio_irq_chip = { 10401a41d1e5SManivannan Sadhasivam .name = "spmi-gpio", 10411a41d1e5SManivannan Sadhasivam .irq_ack = irq_chip_ack_parent, 10421a41d1e5SManivannan Sadhasivam .irq_mask = pmic_gpio_irq_mask, 10431a41d1e5SManivannan Sadhasivam .irq_unmask = pmic_gpio_irq_unmask, 10441a41d1e5SManivannan Sadhasivam .irq_set_type = irq_chip_set_type_parent, 10451a41d1e5SManivannan Sadhasivam .irq_set_wake = irq_chip_set_wake_parent, 10461a41d1e5SManivannan Sadhasivam .flags = IRQCHIP_IMMUTABLE | IRQCHIP_MASK_ON_SUSPEND, 10471a41d1e5SManivannan Sadhasivam GPIOCHIP_IRQ_RESOURCE_HELPERS, 10481a41d1e5SManivannan Sadhasivam }; 10491a41d1e5SManivannan Sadhasivam 1050eadff302SIvan T. Ivanov static int pmic_gpio_probe(struct platform_device *pdev) 1051eadff302SIvan T. Ivanov { 1052ca69e2d1SBrian Masney struct irq_domain *parent_domain; 1053ca69e2d1SBrian Masney struct device_node *parent_node; 1054eadff302SIvan T. Ivanov struct device *dev = &pdev->dev; 1055eadff302SIvan T. Ivanov struct pinctrl_pin_desc *pindesc; 1056eadff302SIvan T. Ivanov struct pinctrl_desc *pctrldesc; 1057eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad, *pads; 1058eadff302SIvan T. Ivanov struct pmic_gpio_state *state; 1059821c76c4SBrian Masney struct gpio_irq_chip *girq; 1060d36a9773SDavid Collins const struct spmi_device *parent_spmi_dev; 1061eadff302SIvan T. Ivanov int ret, npins, i; 1062ab4256cfSStephen Boyd u32 reg; 1063eadff302SIvan T. Ivanov 1064ab4256cfSStephen Boyd ret = of_property_read_u32(dev->of_node, "reg", ®); 1065eadff302SIvan T. Ivanov if (ret < 0) { 1066ab4256cfSStephen Boyd dev_err(dev, "missing base address"); 1067eadff302SIvan T. Ivanov return ret; 1068eadff302SIvan T. Ivanov } 1069eadff302SIvan T. Ivanov 1070cfacef37SBrian Masney npins = (uintptr_t) device_get_match_data(&pdev->dev); 1071eadff302SIvan T. Ivanov 1072eadff302SIvan T. Ivanov state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); 1073eadff302SIvan T. Ivanov if (!state) 1074eadff302SIvan T. Ivanov return -ENOMEM; 1075eadff302SIvan T. Ivanov 1076eadff302SIvan T. Ivanov platform_set_drvdata(pdev, state); 1077eadff302SIvan T. Ivanov 1078eadff302SIvan T. Ivanov state->dev = &pdev->dev; 1079eadff302SIvan T. Ivanov state->map = dev_get_regmap(dev->parent, NULL); 1080d36a9773SDavid Collins parent_spmi_dev = to_spmi_device(dev->parent); 1081d36a9773SDavid Collins state->usid = parent_spmi_dev->usid; 1082d36a9773SDavid Collins state->pid_base = reg >> 8; 1083eadff302SIvan T. Ivanov 1084eadff302SIvan T. Ivanov pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL); 1085eadff302SIvan T. Ivanov if (!pindesc) 1086eadff302SIvan T. Ivanov return -ENOMEM; 1087eadff302SIvan T. Ivanov 1088eadff302SIvan T. Ivanov pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL); 1089eadff302SIvan T. Ivanov if (!pads) 1090eadff302SIvan T. Ivanov return -ENOMEM; 1091eadff302SIvan T. Ivanov 1092eadff302SIvan T. Ivanov pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL); 1093eadff302SIvan T. Ivanov if (!pctrldesc) 1094eadff302SIvan T. Ivanov return -ENOMEM; 1095eadff302SIvan T. Ivanov 1096eadff302SIvan T. Ivanov pctrldesc->pctlops = &pmic_gpio_pinctrl_ops; 1097eadff302SIvan T. Ivanov pctrldesc->pmxops = &pmic_gpio_pinmux_ops; 1098eadff302SIvan T. Ivanov pctrldesc->confops = &pmic_gpio_pinconf_ops; 1099eadff302SIvan T. Ivanov pctrldesc->owner = THIS_MODULE; 1100eadff302SIvan T. Ivanov pctrldesc->name = dev_name(dev); 1101eadff302SIvan T. Ivanov pctrldesc->pins = pindesc; 1102eadff302SIvan T. Ivanov pctrldesc->npins = npins; 1103f684e4acSLinus Walleij pctrldesc->num_custom_params = ARRAY_SIZE(pmic_gpio_bindings); 1104f684e4acSLinus Walleij pctrldesc->custom_params = pmic_gpio_bindings; 11054f06266aSArnd Bergmann #ifdef CONFIG_DEBUG_FS 1106f684e4acSLinus Walleij pctrldesc->custom_conf_items = pmic_conf_items; 11074f06266aSArnd Bergmann #endif 1108eadff302SIvan T. Ivanov 1109eadff302SIvan T. Ivanov for (i = 0; i < npins; i++, pindesc++) { 1110eadff302SIvan T. Ivanov pad = &pads[i]; 1111eadff302SIvan T. Ivanov pindesc->drv_data = pad; 1112eadff302SIvan T. Ivanov pindesc->number = i; 1113eadff302SIvan T. Ivanov pindesc->name = pmic_gpio_groups[i]; 1114eadff302SIvan T. Ivanov 1115ab4256cfSStephen Boyd pad->base = reg + i * PMIC_GPIO_ADDRESS_RANGE; 1116eadff302SIvan T. Ivanov 1117eadff302SIvan T. Ivanov ret = pmic_gpio_populate(state, pad); 1118eadff302SIvan T. Ivanov if (ret < 0) 1119eadff302SIvan T. Ivanov return ret; 1120eadff302SIvan T. Ivanov } 1121eadff302SIvan T. Ivanov 1122eadff302SIvan T. Ivanov state->chip = pmic_gpio_gpio_template; 112358383c78SLinus Walleij state->chip.parent = dev; 1124eadff302SIvan T. Ivanov state->chip.base = -1; 1125eadff302SIvan T. Ivanov state->chip.ngpio = npins; 1126eadff302SIvan T. Ivanov state->chip.label = dev_name(dev); 1127eadff302SIvan T. Ivanov state->chip.of_gpio_n_cells = 2; 1128eadff302SIvan T. Ivanov state->chip.can_sleep = false; 1129eadff302SIvan T. Ivanov 1130b46ddfe6SLaxman Dewangan state->ctrl = devm_pinctrl_register(dev, pctrldesc, state); 1131323de9efSMasahiro Yamada if (IS_ERR(state->ctrl)) 1132323de9efSMasahiro Yamada return PTR_ERR(state->ctrl); 1133eadff302SIvan T. Ivanov 1134ca69e2d1SBrian Masney parent_node = of_irq_find_parent(state->dev->of_node); 1135ca69e2d1SBrian Masney if (!parent_node) 1136ca69e2d1SBrian Masney return -ENXIO; 1137ca69e2d1SBrian Masney 1138ca69e2d1SBrian Masney parent_domain = irq_find_host(parent_node); 1139ca69e2d1SBrian Masney of_node_put(parent_node); 1140ca69e2d1SBrian Masney if (!parent_domain) 1141ca69e2d1SBrian Masney return -ENXIO; 1142ca69e2d1SBrian Masney 1143821c76c4SBrian Masney girq = &state->chip.irq; 11441a41d1e5SManivannan Sadhasivam gpio_irq_chip_set_chip(girq, &spmi_gpio_irq_chip); 1145821c76c4SBrian Masney girq->default_type = IRQ_TYPE_NONE; 1146821c76c4SBrian Masney girq->handler = handle_level_irq; 1147821c76c4SBrian Masney girq->fwnode = of_node_to_fwnode(state->dev->of_node); 1148821c76c4SBrian Masney girq->parent_domain = parent_domain; 1149821c76c4SBrian Masney girq->child_to_parent_hwirq = pmic_gpio_child_to_parent_hwirq; 1150d36a9773SDavid Collins girq->populate_parent_alloc_arg = pmic_gpio_populate_parent_fwspec; 1151821c76c4SBrian Masney girq->child_offset_to_irq = pmic_gpio_child_offset_to_irq; 1152821c76c4SBrian Masney girq->child_irq_domain_ops.translate = pmic_gpio_domain_translate; 1153ca69e2d1SBrian Masney 1154c52d9df1SLinus Walleij ret = gpiochip_add_data(&state->chip, state); 1155eadff302SIvan T. Ivanov if (ret) { 1156eadff302SIvan T. Ivanov dev_err(state->dev, "can't add gpio chip\n"); 1157821c76c4SBrian Masney return ret; 1158eadff302SIvan T. Ivanov } 1159eadff302SIvan T. Ivanov 1160149a9604SBrian Masney /* 1161149a9604SBrian Masney * For DeviceTree-supported systems, the gpio core checks the 1162149a9604SBrian Masney * pinctrl's device node for the "gpio-ranges" property. 1163149a9604SBrian Masney * If it is present, it takes care of adding the pin ranges 1164149a9604SBrian Masney * for the driver. In this case the driver can skip ahead. 1165149a9604SBrian Masney * 1166149a9604SBrian Masney * In order to remain compatible with older, existing DeviceTree 1167149a9604SBrian Masney * files which don't set the "gpio-ranges" property or systems that 1168149a9604SBrian Masney * utilize ACPI the driver has to call gpiochip_add_pin_range(). 1169149a9604SBrian Masney */ 1170149a9604SBrian Masney if (!of_property_read_bool(dev->of_node, "gpio-ranges")) { 1171149a9604SBrian Masney ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, 1172149a9604SBrian Masney npins); 1173eadff302SIvan T. Ivanov if (ret) { 1174eadff302SIvan T. Ivanov dev_err(dev, "failed to add pin range\n"); 1175eadff302SIvan T. Ivanov goto err_range; 1176eadff302SIvan T. Ivanov } 1177149a9604SBrian Masney } 1178eadff302SIvan T. Ivanov 1179eadff302SIvan T. Ivanov return 0; 1180eadff302SIvan T. Ivanov 1181eadff302SIvan T. Ivanov err_range: 1182eadff302SIvan T. Ivanov gpiochip_remove(&state->chip); 1183eadff302SIvan T. Ivanov return ret; 1184eadff302SIvan T. Ivanov } 1185eadff302SIvan T. Ivanov 1186eadff302SIvan T. Ivanov static int pmic_gpio_remove(struct platform_device *pdev) 1187eadff302SIvan T. Ivanov { 1188eadff302SIvan T. Ivanov struct pmic_gpio_state *state = platform_get_drvdata(pdev); 1189eadff302SIvan T. Ivanov 1190eadff302SIvan T. Ivanov gpiochip_remove(&state->chip); 1191eadff302SIvan T. Ivanov return 0; 1192eadff302SIvan T. Ivanov } 1193eadff302SIvan T. Ivanov 1194eadff302SIvan T. Ivanov static const struct of_device_id pmic_gpio_of_match[] = { 1195ef874e03SLoic Poulain { .compatible = "qcom,pm2250-gpio", .data = (void *) 10 }, 119617cc38e7SKonrad Dybcio /* pm660 has 13 GPIOs with holes on 1, 5, 6, 7, 8 and 10 */ 119717cc38e7SKonrad Dybcio { .compatible = "qcom,pm660-gpio", .data = (void *) 13 }, 119817cc38e7SKonrad Dybcio /* pm660l has 12 GPIOs with holes on 1, 2, 10, 11 and 12 */ 119917cc38e7SKonrad Dybcio { .compatible = "qcom,pm660l-gpio", .data = (void *) 12 }, 12004d8a768eSMarijn Suijten { .compatible = "qcom,pm6125-gpio", .data = (void *) 9 }, 12014afc2a0cSBhupesh Sharma { .compatible = "qcom,pm6150-gpio", .data = (void *) 10 }, 12024afc2a0cSBhupesh Sharma { .compatible = "qcom,pm6150l-gpio", .data = (void *) 12 }, 120383917856SLuca Weiss { .compatible = "qcom,pm6350-gpio", .data = (void *) 9 }, 12044afc2a0cSBhupesh Sharma { .compatible = "qcom,pm7325-gpio", .data = (void *) 10 }, 12054afc2a0cSBhupesh Sharma { .compatible = "qcom,pm8005-gpio", .data = (void *) 4 }, 12064afc2a0cSBhupesh Sharma { .compatible = "qcom,pm8008-gpio", .data = (void *) 2 }, 1207f3474383SKonrad Dybcio { .compatible = "qcom,pm8019-gpio", .data = (void *) 6 }, 1208d67070c3SVinod Koul /* pm8150 has 10 GPIOs with holes on 2, 5, 7 and 8 */ 1209d67070c3SVinod Koul { .compatible = "qcom,pm8150-gpio", .data = (void *) 10 }, 1210182700f2SBjorn Andersson { .compatible = "qcom,pmc8180-gpio", .data = (void *) 10 }, 12119bdacaddSVinod Koul /* pm8150b has 12 GPIOs with holes on 3, r and 7 */ 12129bdacaddSVinod Koul { .compatible = "qcom,pm8150b-gpio", .data = (void *) 12 }, 12132dc889a8SVinod Koul /* pm8150l has 12 GPIOs with holes on 7 */ 12142dc889a8SVinod Koul { .compatible = "qcom,pm8150l-gpio", .data = (void *) 12 }, 1215182700f2SBjorn Andersson { .compatible = "qcom,pmc8180c-gpio", .data = (void *) 12 }, 1216eebe11b5SDominik Kobinski { .compatible = "qcom,pm8226-gpio", .data = (void *) 8 }, 121757c0a4f0SVinod Koul { .compatible = "qcom,pm8350-gpio", .data = (void *) 10 }, 121857c0a4f0SVinod Koul { .compatible = "qcom,pm8350b-gpio", .data = (void *) 8 }, 121957c0a4f0SVinod Koul { .compatible = "qcom,pm8350c-gpio", .data = (void *) 9 }, 1220168a0abfSDmitry Baryshkov { .compatible = "qcom,pm8450-gpio", .data = (void *) 4 }, 12214afc2a0cSBhupesh Sharma { .compatible = "qcom,pm8916-gpio", .data = (void *) 4 }, 12224afc2a0cSBhupesh Sharma { .compatible = "qcom,pm8941-gpio", .data = (void *) 36 }, 12234afc2a0cSBhupesh Sharma /* pm8950 has 8 GPIOs with holes on 3 */ 12244afc2a0cSBhupesh Sharma { .compatible = "qcom,pm8950-gpio", .data = (void *) 8 }, 12254afc2a0cSBhupesh Sharma { .compatible = "qcom,pm8994-gpio", .data = (void *) 22 }, 12264afc2a0cSBhupesh Sharma { .compatible = "qcom,pm8998-gpio", .data = (void *) 26 }, 12274afc2a0cSBhupesh Sharma { .compatible = "qcom,pma8084-gpio", .data = (void *) 22 }, 12284afc2a0cSBhupesh Sharma { .compatible = "qcom,pmi8950-gpio", .data = (void *) 2 }, 12294afc2a0cSBhupesh Sharma { .compatible = "qcom,pmi8994-gpio", .data = (void *) 10 }, 12304afc2a0cSBhupesh Sharma { .compatible = "qcom,pmi8998-gpio", .data = (void *) 14 }, 123157c0a4f0SVinod Koul { .compatible = "qcom,pmk8350-gpio", .data = (void *) 4 }, 123279e2311cSBhupesh Sharma { .compatible = "qcom,pmm8155au-gpio", .data = (void *) 10 }, 12336cd81a86SRobert Marko /* pmp8074 has 12 GPIOs with holes on 1 and 12 */ 12346cd81a86SRobert Marko { .compatible = "qcom,pmp8074-gpio", .data = (void *) 12 }, 123557c0a4f0SVinod Koul { .compatible = "qcom,pmr735a-gpio", .data = (void *) 4 }, 123657c0a4f0SVinod Koul { .compatible = "qcom,pmr735b-gpio", .data = (void *) 4 }, 12374afc2a0cSBhupesh Sharma /* pms405 has 12 GPIOs with holes on 1, 9, and 10 */ 12384afc2a0cSBhupesh Sharma { .compatible = "qcom,pms405-gpio", .data = (void *) 12 }, 1239ceb58de4SVinod Koul /* pmx55 has 11 GPIOs with holes on 3, 7, 10, 11 */ 1240ceb58de4SVinod Koul { .compatible = "qcom,pmx55-gpio", .data = (void *) 11 }, 1241203638fdSRohit Agarwal { .compatible = "qcom,pmx65-gpio", .data = (void *) 16 }, 1242eadff302SIvan T. Ivanov { }, 1243eadff302SIvan T. Ivanov }; 1244eadff302SIvan T. Ivanov 1245eadff302SIvan T. Ivanov MODULE_DEVICE_TABLE(of, pmic_gpio_of_match); 1246eadff302SIvan T. Ivanov 1247eadff302SIvan T. Ivanov static struct platform_driver pmic_gpio_driver = { 1248eadff302SIvan T. Ivanov .driver = { 1249eadff302SIvan T. Ivanov .name = "qcom-spmi-gpio", 1250eadff302SIvan T. Ivanov .of_match_table = pmic_gpio_of_match, 1251eadff302SIvan T. Ivanov }, 1252eadff302SIvan T. Ivanov .probe = pmic_gpio_probe, 1253eadff302SIvan T. Ivanov .remove = pmic_gpio_remove, 1254eadff302SIvan T. Ivanov }; 1255eadff302SIvan T. Ivanov 1256eadff302SIvan T. Ivanov module_platform_driver(pmic_gpio_driver); 1257eadff302SIvan T. Ivanov 1258eadff302SIvan T. Ivanov MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>"); 1259eadff302SIvan T. Ivanov MODULE_DESCRIPTION("Qualcomm SPMI PMIC GPIO pin control driver"); 1260eadff302SIvan T. Ivanov MODULE_ALIAS("platform:qcom-spmi-gpio"); 1261eadff302SIvan T. Ivanov MODULE_LICENSE("GPL v2"); 1262