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. 4eadff302SIvan T. Ivanov */ 5eadff302SIvan T. Ivanov 61c5fb66aSLinus Walleij #include <linux/gpio/driver.h> 7ca69e2d1SBrian Masney #include <linux/interrupt.h> 8eadff302SIvan T. Ivanov #include <linux/module.h> 9eadff302SIvan T. Ivanov #include <linux/of.h> 10ab4256cfSStephen Boyd #include <linux/of_irq.h> 11eadff302SIvan T. Ivanov #include <linux/pinctrl/pinconf-generic.h> 12eadff302SIvan T. Ivanov #include <linux/pinctrl/pinconf.h> 13eadff302SIvan T. Ivanov #include <linux/pinctrl/pinmux.h> 14eadff302SIvan T. Ivanov #include <linux/platform_device.h> 15eadff302SIvan T. Ivanov #include <linux/regmap.h> 16eadff302SIvan T. Ivanov #include <linux/slab.h> 17d36a9773SDavid Collins #include <linux/spmi.h> 18eadff302SIvan T. Ivanov #include <linux/types.h> 19eadff302SIvan T. Ivanov 20eadff302SIvan T. Ivanov #include <dt-bindings/pinctrl/qcom,pmic-gpio.h> 21eadff302SIvan T. Ivanov 22eadff302SIvan T. Ivanov #include "../core.h" 23eadff302SIvan T. Ivanov #include "../pinctrl-utils.h" 24eadff302SIvan T. Ivanov 25eadff302SIvan T. Ivanov #define PMIC_GPIO_ADDRESS_RANGE 0x100 26eadff302SIvan T. Ivanov 27eadff302SIvan T. Ivanov /* type and subtype registers base address offsets */ 28eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_TYPE 0x4 29eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_SUBTYPE 0x5 30eadff302SIvan T. Ivanov 31eadff302SIvan T. Ivanov /* GPIO peripheral type and subtype out_values */ 32eadff302SIvan T. Ivanov #define PMIC_GPIO_TYPE 0x10 33eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIO_4CH 0x1 34eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIOC_4CH 0x5 35eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIO_8CH 0x9 36eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIOC_8CH 0xd 37d7b5f5ccSFenglin Wu #define PMIC_GPIO_SUBTYPE_GPIO_LV 0x10 38d7b5f5ccSFenglin Wu #define PMIC_GPIO_SUBTYPE_GPIO_MV 0x11 39eadff302SIvan T. Ivanov 40eadff302SIvan T. Ivanov #define PMIC_MPP_REG_RT_STS 0x10 41eadff302SIvan T. Ivanov #define PMIC_MPP_REG_RT_STS_VAL_MASK 0x1 42eadff302SIvan T. Ivanov 43eadff302SIvan T. Ivanov /* control register base address offsets */ 44eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_CTL 0x40 45eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_VIN_CTL 0x41 46eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_PULL_CTL 0x42 47d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL 0x44 48223463fcSFenglin Wu #define PMIC_GPIO_REG_DIG_IN_CTL 0x43 49eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_OUT_CTL 0x45 50eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_EN_CTL 0x46 51d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL 0x4A 52eadff302SIvan T. Ivanov 53eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_MODE_CTL */ 54eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_VALUE_SHIFT 0x1 55eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_FUNCTION_SHIFT 1 56eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_FUNCTION_MASK 0x7 57eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_DIR_SHIFT 4 58eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_DIR_MASK 0x7 59eadff302SIvan T. Ivanov 60d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_INPUT 0 61d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_OUTPUT 1 62d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT 2 63d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_ANALOG_PASS_THRU 3 64d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_MODE_DIR_MASK 0x3 65d7b5f5ccSFenglin Wu 66eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_VIN_CTL */ 67eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_VIN_SHIFT 0 68eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_VIN_MASK 0x7 69eadff302SIvan T. Ivanov 70eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_PULL_CTL */ 71eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_PULL_SHIFT 0 72eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_PULL_MASK 0x7 73eadff302SIvan T. Ivanov 74eadff302SIvan T. Ivanov #define PMIC_GPIO_PULL_DOWN 4 75eadff302SIvan T. Ivanov #define PMIC_GPIO_PULL_DISABLE 5 76eadff302SIvan T. Ivanov 77d7b5f5ccSFenglin Wu /* PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL for LV/MV */ 78d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_INVERT 0x80 79d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT 7 80d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK 0xF 81d7b5f5ccSFenglin Wu 82223463fcSFenglin Wu /* PMIC_GPIO_REG_DIG_IN_CTL */ 83223463fcSFenglin Wu #define PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN 0x80 84223463fcSFenglin Wu #define PMIC_GPIO_LV_MV_DIG_IN_DTEST_SEL_MASK 0x7 85223463fcSFenglin Wu #define PMIC_GPIO_DIG_IN_DTEST_SEL_MASK 0xf 86223463fcSFenglin Wu 87eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_OUT_CTL */ 88eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_STRENGTH_SHIFT 0 89eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_STRENGTH_MASK 0x3 90eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_TYPE_SHIFT 4 91eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_TYPE_MASK 0x3 92eadff302SIvan T. Ivanov 93eadff302SIvan T. Ivanov /* 94eadff302SIvan T. Ivanov * Output type - indicates pin should be configured as push-pull, 95eadff302SIvan T. Ivanov * open drain or open source. 96eadff302SIvan T. Ivanov */ 97eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_CMOS 0 98eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS 1 99eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS 2 100eadff302SIvan T. Ivanov 101eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_EN_CTL */ 102eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MASTER_EN_SHIFT 7 103eadff302SIvan T. Ivanov 104eadff302SIvan T. Ivanov #define PMIC_GPIO_PHYSICAL_OFFSET 1 105eadff302SIvan T. Ivanov 106d7b5f5ccSFenglin Wu /* PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL */ 107d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_ANA_MUX_SEL_MASK 0x3 108d7b5f5ccSFenglin Wu 109eadff302SIvan T. Ivanov /* Qualcomm specific pin configurations */ 110eadff302SIvan T. Ivanov #define PMIC_GPIO_CONF_PULL_UP (PIN_CONFIG_END + 1) 111eadff302SIvan T. Ivanov #define PMIC_GPIO_CONF_STRENGTH (PIN_CONFIG_END + 2) 112d7b5f5ccSFenglin Wu #define PMIC_GPIO_CONF_ATEST (PIN_CONFIG_END + 3) 113d7b5f5ccSFenglin Wu #define PMIC_GPIO_CONF_ANALOG_PASS (PIN_CONFIG_END + 4) 114223463fcSFenglin Wu #define PMIC_GPIO_CONF_DTEST_BUFFER (PIN_CONFIG_END + 5) 115d7b5f5ccSFenglin Wu 116d7b5f5ccSFenglin Wu /* The index of each function in pmic_gpio_functions[] array */ 117d7b5f5ccSFenglin Wu enum pmic_gpio_func_index { 118d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_NORMAL, 119d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_PAIRED, 120d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_FUNC1, 121d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_FUNC2, 122d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_FUNC3, 123d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_FUNC4, 124d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_DTEST1, 125d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_DTEST2, 126d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_DTEST3, 127d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_DTEST4, 128d7b5f5ccSFenglin Wu }; 129eadff302SIvan T. Ivanov 130eadff302SIvan T. Ivanov /** 131eadff302SIvan T. Ivanov * struct pmic_gpio_pad - keep current GPIO settings 132eadff302SIvan T. Ivanov * @base: Address base in SPMI device. 133eadff302SIvan T. Ivanov * @is_enabled: Set to false when GPIO should be put in high Z state. 134eadff302SIvan T. Ivanov * @out_value: Cached pin output value 135eadff302SIvan T. Ivanov * @have_buffer: Set to true if GPIO output could be configured in push-pull, 136eadff302SIvan T. Ivanov * open-drain or open-source mode. 137eadff302SIvan T. Ivanov * @output_enabled: Set to true if GPIO output logic is enabled. 138eadff302SIvan T. Ivanov * @input_enabled: Set to true if GPIO input buffer logic is enabled. 139d7b5f5ccSFenglin Wu * @analog_pass: Set to true if GPIO is in analog-pass-through mode. 140d7b5f5ccSFenglin Wu * @lv_mv_type: Set to true if GPIO subtype is GPIO_LV(0x10) or GPIO_MV(0x11). 141eadff302SIvan T. Ivanov * @num_sources: Number of power-sources supported by this GPIO. 142eadff302SIvan T. Ivanov * @power_source: Current power-source used. 143eadff302SIvan T. Ivanov * @buffer_type: Push-pull, open-drain or open-source. 144eadff302SIvan T. Ivanov * @pullup: Constant current which flow trough GPIO output buffer. 145eadff302SIvan T. Ivanov * @strength: No, Low, Medium, High 146eadff302SIvan T. Ivanov * @function: See pmic_gpio_functions[] 147d7b5f5ccSFenglin Wu * @atest: the ATEST selection for GPIO analog-pass-through mode 148223463fcSFenglin Wu * @dtest_buffer: the DTEST buffer selection for digital input mode. 149eadff302SIvan T. Ivanov */ 150eadff302SIvan T. Ivanov struct pmic_gpio_pad { 151eadff302SIvan T. Ivanov u16 base; 152eadff302SIvan T. Ivanov bool is_enabled; 153eadff302SIvan T. Ivanov bool out_value; 154eadff302SIvan T. Ivanov bool have_buffer; 155eadff302SIvan T. Ivanov bool output_enabled; 156eadff302SIvan T. Ivanov bool input_enabled; 157d7b5f5ccSFenglin Wu bool analog_pass; 158d7b5f5ccSFenglin Wu bool lv_mv_type; 159eadff302SIvan T. Ivanov unsigned int num_sources; 160eadff302SIvan T. Ivanov unsigned int power_source; 161eadff302SIvan T. Ivanov unsigned int buffer_type; 162eadff302SIvan T. Ivanov unsigned int pullup; 163eadff302SIvan T. Ivanov unsigned int strength; 164eadff302SIvan T. Ivanov unsigned int function; 165d7b5f5ccSFenglin Wu unsigned int atest; 166223463fcSFenglin Wu unsigned int dtest_buffer; 167eadff302SIvan T. Ivanov }; 168eadff302SIvan T. Ivanov 169eadff302SIvan T. Ivanov struct pmic_gpio_state { 170eadff302SIvan T. Ivanov struct device *dev; 171eadff302SIvan T. Ivanov struct regmap *map; 172eadff302SIvan T. Ivanov struct pinctrl_dev *ctrl; 173eadff302SIvan T. Ivanov struct gpio_chip chip; 174d36a9773SDavid Collins u8 usid; 175d36a9773SDavid Collins u8 pid_base; 176eadff302SIvan T. Ivanov }; 177eadff302SIvan T. Ivanov 178f684e4acSLinus Walleij static const struct pinconf_generic_params pmic_gpio_bindings[] = { 1797382b623SSoren Brinkmann {"qcom,pull-up-strength", PMIC_GPIO_CONF_PULL_UP, 0}, 1807382b623SSoren Brinkmann {"qcom,drive-strength", PMIC_GPIO_CONF_STRENGTH, 0}, 181d7b5f5ccSFenglin Wu {"qcom,atest", PMIC_GPIO_CONF_ATEST, 0}, 182d7b5f5ccSFenglin Wu {"qcom,analog-pass", PMIC_GPIO_CONF_ANALOG_PASS, 0}, 183223463fcSFenglin Wu {"qcom,dtest-buffer", PMIC_GPIO_CONF_DTEST_BUFFER, 0}, 184eadff302SIvan T. Ivanov }; 185eadff302SIvan T. Ivanov 1864f06266aSArnd Bergmann #ifdef CONFIG_DEBUG_FS 1877382b623SSoren Brinkmann static const struct pin_config_item pmic_conf_items[ARRAY_SIZE(pmic_gpio_bindings)] = { 1887382b623SSoren Brinkmann PCONFDUMP(PMIC_GPIO_CONF_PULL_UP, "pull up strength", NULL, true), 1897382b623SSoren Brinkmann PCONFDUMP(PMIC_GPIO_CONF_STRENGTH, "drive-strength", NULL, true), 190d7b5f5ccSFenglin Wu PCONFDUMP(PMIC_GPIO_CONF_ATEST, "atest", NULL, true), 191d7b5f5ccSFenglin Wu PCONFDUMP(PMIC_GPIO_CONF_ANALOG_PASS, "analog-pass", NULL, true), 192223463fcSFenglin Wu PCONFDUMP(PMIC_GPIO_CONF_DTEST_BUFFER, "dtest-buffer", NULL, true), 193eadff302SIvan T. Ivanov }; 1944f06266aSArnd Bergmann #endif 195eadff302SIvan T. Ivanov 196eadff302SIvan T. Ivanov static const char *const pmic_gpio_groups[] = { 197eadff302SIvan T. Ivanov "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", "gpio8", 198eadff302SIvan T. Ivanov "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", "gpio15", 199eadff302SIvan T. Ivanov "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22", 200eadff302SIvan T. Ivanov "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29", 201eadff302SIvan T. Ivanov "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36", 202eadff302SIvan T. Ivanov }; 203eadff302SIvan T. Ivanov 204eadff302SIvan T. Ivanov static const char *const pmic_gpio_functions[] = { 205d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_NORMAL] = PMIC_GPIO_FUNC_NORMAL, 206d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_PAIRED] = PMIC_GPIO_FUNC_PAIRED, 207d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_FUNC1] = PMIC_GPIO_FUNC_FUNC1, 208d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_FUNC2] = PMIC_GPIO_FUNC_FUNC2, 209d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_FUNC3] = PMIC_GPIO_FUNC_FUNC3, 210d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_FUNC4] = PMIC_GPIO_FUNC_FUNC4, 211d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_DTEST1] = PMIC_GPIO_FUNC_DTEST1, 212d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_DTEST2] = PMIC_GPIO_FUNC_DTEST2, 213d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_DTEST3] = PMIC_GPIO_FUNC_DTEST3, 214d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_DTEST4] = PMIC_GPIO_FUNC_DTEST4, 215eadff302SIvan T. Ivanov }; 216eadff302SIvan T. Ivanov 217eadff302SIvan T. Ivanov static int pmic_gpio_read(struct pmic_gpio_state *state, 218eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad, unsigned int addr) 219eadff302SIvan T. Ivanov { 220eadff302SIvan T. Ivanov unsigned int val; 221eadff302SIvan T. Ivanov int ret; 222eadff302SIvan T. Ivanov 223eadff302SIvan T. Ivanov ret = regmap_read(state->map, pad->base + addr, &val); 224eadff302SIvan T. Ivanov if (ret < 0) 225eadff302SIvan T. Ivanov dev_err(state->dev, "read 0x%x failed\n", addr); 226eadff302SIvan T. Ivanov else 227eadff302SIvan T. Ivanov ret = val; 228eadff302SIvan T. Ivanov 229eadff302SIvan T. Ivanov return ret; 230eadff302SIvan T. Ivanov } 231eadff302SIvan T. Ivanov 232eadff302SIvan T. Ivanov static int pmic_gpio_write(struct pmic_gpio_state *state, 233eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad, unsigned int addr, 234eadff302SIvan T. Ivanov unsigned int val) 235eadff302SIvan T. Ivanov { 236eadff302SIvan T. Ivanov int ret; 237eadff302SIvan T. Ivanov 238eadff302SIvan T. Ivanov ret = regmap_write(state->map, pad->base + addr, val); 239eadff302SIvan T. Ivanov if (ret < 0) 240eadff302SIvan T. Ivanov dev_err(state->dev, "write 0x%x failed\n", addr); 241eadff302SIvan T. Ivanov 242eadff302SIvan T. Ivanov return ret; 243eadff302SIvan T. Ivanov } 244eadff302SIvan T. Ivanov 245eadff302SIvan T. Ivanov static int pmic_gpio_get_groups_count(struct pinctrl_dev *pctldev) 246eadff302SIvan T. Ivanov { 247eadff302SIvan T. Ivanov /* Every PIN is a group */ 248eadff302SIvan T. Ivanov return pctldev->desc->npins; 249eadff302SIvan T. Ivanov } 250eadff302SIvan T. Ivanov 251eadff302SIvan T. Ivanov static const char *pmic_gpio_get_group_name(struct pinctrl_dev *pctldev, 252eadff302SIvan T. Ivanov unsigned pin) 253eadff302SIvan T. Ivanov { 254eadff302SIvan T. Ivanov return pctldev->desc->pins[pin].name; 255eadff302SIvan T. Ivanov } 256eadff302SIvan T. Ivanov 257eadff302SIvan T. Ivanov static int pmic_gpio_get_group_pins(struct pinctrl_dev *pctldev, unsigned pin, 258eadff302SIvan T. Ivanov const unsigned **pins, unsigned *num_pins) 259eadff302SIvan T. Ivanov { 260eadff302SIvan T. Ivanov *pins = &pctldev->desc->pins[pin].number; 261eadff302SIvan T. Ivanov *num_pins = 1; 262eadff302SIvan T. Ivanov return 0; 263eadff302SIvan T. Ivanov } 264eadff302SIvan T. Ivanov 265eadff302SIvan T. Ivanov static const struct pinctrl_ops pmic_gpio_pinctrl_ops = { 266eadff302SIvan T. Ivanov .get_groups_count = pmic_gpio_get_groups_count, 267eadff302SIvan T. Ivanov .get_group_name = pmic_gpio_get_group_name, 268eadff302SIvan T. Ivanov .get_group_pins = pmic_gpio_get_group_pins, 2697382b623SSoren Brinkmann .dt_node_to_map = pinconf_generic_dt_node_to_map_group, 270d32f7fd3SIrina Tirdea .dt_free_map = pinctrl_utils_free_map, 271eadff302SIvan T. Ivanov }; 272eadff302SIvan T. Ivanov 273eadff302SIvan T. Ivanov static int pmic_gpio_get_functions_count(struct pinctrl_dev *pctldev) 274eadff302SIvan T. Ivanov { 275eadff302SIvan T. Ivanov return ARRAY_SIZE(pmic_gpio_functions); 276eadff302SIvan T. Ivanov } 277eadff302SIvan T. Ivanov 278eadff302SIvan T. Ivanov static const char *pmic_gpio_get_function_name(struct pinctrl_dev *pctldev, 279eadff302SIvan T. Ivanov unsigned function) 280eadff302SIvan T. Ivanov { 281eadff302SIvan T. Ivanov return pmic_gpio_functions[function]; 282eadff302SIvan T. Ivanov } 283eadff302SIvan T. Ivanov 284eadff302SIvan T. Ivanov static int pmic_gpio_get_function_groups(struct pinctrl_dev *pctldev, 285eadff302SIvan T. Ivanov unsigned function, 286eadff302SIvan T. Ivanov const char *const **groups, 287eadff302SIvan T. Ivanov unsigned *const num_qgroups) 288eadff302SIvan T. Ivanov { 289eadff302SIvan T. Ivanov *groups = pmic_gpio_groups; 290eadff302SIvan T. Ivanov *num_qgroups = pctldev->desc->npins; 291eadff302SIvan T. Ivanov return 0; 292eadff302SIvan T. Ivanov } 293eadff302SIvan T. Ivanov 294eadff302SIvan T. Ivanov static int pmic_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned function, 295eadff302SIvan T. Ivanov unsigned pin) 296eadff302SIvan T. Ivanov { 297eadff302SIvan T. Ivanov struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev); 298eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad; 299eadff302SIvan T. Ivanov unsigned int val; 300eadff302SIvan T. Ivanov int ret; 301eadff302SIvan T. Ivanov 302d7b5f5ccSFenglin Wu if (function > PMIC_GPIO_FUNC_INDEX_DTEST4) { 303d7b5f5ccSFenglin Wu pr_err("function: %d is not defined\n", function); 304d7b5f5ccSFenglin Wu return -EINVAL; 305d7b5f5ccSFenglin Wu } 306d7b5f5ccSFenglin Wu 307eadff302SIvan T. Ivanov pad = pctldev->desc->pins[pin].drv_data; 308d7b5f5ccSFenglin Wu /* 309d7b5f5ccSFenglin Wu * Non-LV/MV subtypes only support 2 special functions, 310d7b5f5ccSFenglin Wu * offsetting the dtestx function values by 2 311d7b5f5ccSFenglin Wu */ 312d7b5f5ccSFenglin Wu if (!pad->lv_mv_type) { 313d7b5f5ccSFenglin Wu if (function == PMIC_GPIO_FUNC_INDEX_FUNC3 || 314d7b5f5ccSFenglin Wu function == PMIC_GPIO_FUNC_INDEX_FUNC4) { 315d7b5f5ccSFenglin Wu pr_err("LV/MV subtype doesn't have func3/func4\n"); 316d7b5f5ccSFenglin Wu return -EINVAL; 317d7b5f5ccSFenglin Wu } 318d7b5f5ccSFenglin Wu if (function >= PMIC_GPIO_FUNC_INDEX_DTEST1) 319d7b5f5ccSFenglin Wu function -= (PMIC_GPIO_FUNC_INDEX_DTEST1 - 320d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_FUNC3); 321d7b5f5ccSFenglin Wu } 322eadff302SIvan T. Ivanov 323eadff302SIvan T. Ivanov pad->function = function; 324eadff302SIvan T. Ivanov 325d7b5f5ccSFenglin Wu if (pad->analog_pass) 326d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_ANALOG_PASS_THRU; 327d7b5f5ccSFenglin Wu else if (pad->output_enabled && pad->input_enabled) 328d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT; 329d7b5f5ccSFenglin Wu else if (pad->output_enabled) 330d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_OUTPUT; 331eadff302SIvan T. Ivanov else 332d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_INPUT; 333eadff302SIvan T. Ivanov 334d7b5f5ccSFenglin Wu if (pad->lv_mv_type) { 335d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 336d7b5f5ccSFenglin Wu PMIC_GPIO_REG_MODE_CTL, val); 337d7b5f5ccSFenglin Wu if (ret < 0) 338d7b5f5ccSFenglin Wu return ret; 339d7b5f5ccSFenglin Wu 340d7b5f5ccSFenglin Wu val = pad->atest - 1; 341d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 342d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL, val); 343d7b5f5ccSFenglin Wu if (ret < 0) 344d7b5f5ccSFenglin Wu return ret; 345d7b5f5ccSFenglin Wu 346d7b5f5ccSFenglin Wu val = pad->out_value 347d7b5f5ccSFenglin Wu << PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT; 348d7b5f5ccSFenglin Wu val |= pad->function 349d7b5f5ccSFenglin Wu & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK; 350d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 351d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL, val); 352d7b5f5ccSFenglin Wu if (ret < 0) 353d7b5f5ccSFenglin Wu return ret; 354d7b5f5ccSFenglin Wu } else { 355dc391502SIvan T. Ivanov val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT; 356eadff302SIvan T. Ivanov val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT; 357eadff302SIvan T. Ivanov val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT; 358eadff302SIvan T. Ivanov 359eadff302SIvan T. Ivanov ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); 360eadff302SIvan T. Ivanov if (ret < 0) 361eadff302SIvan T. Ivanov return ret; 362d7b5f5ccSFenglin Wu } 363eadff302SIvan T. Ivanov 364eadff302SIvan T. Ivanov val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT; 365eadff302SIvan T. Ivanov 366eadff302SIvan T. Ivanov return pmic_gpio_write(state, pad, PMIC_GPIO_REG_EN_CTL, val); 367eadff302SIvan T. Ivanov } 368eadff302SIvan T. Ivanov 369eadff302SIvan T. Ivanov static const struct pinmux_ops pmic_gpio_pinmux_ops = { 370eadff302SIvan T. Ivanov .get_functions_count = pmic_gpio_get_functions_count, 371eadff302SIvan T. Ivanov .get_function_name = pmic_gpio_get_function_name, 372eadff302SIvan T. Ivanov .get_function_groups = pmic_gpio_get_function_groups, 373eadff302SIvan T. Ivanov .set_mux = pmic_gpio_set_mux, 374eadff302SIvan T. Ivanov }; 375eadff302SIvan T. Ivanov 376eadff302SIvan T. Ivanov static int pmic_gpio_config_get(struct pinctrl_dev *pctldev, 377eadff302SIvan T. Ivanov unsigned int pin, unsigned long *config) 378eadff302SIvan T. Ivanov { 379eadff302SIvan T. Ivanov unsigned param = pinconf_to_config_param(*config); 380eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad; 381eadff302SIvan T. Ivanov unsigned arg; 382eadff302SIvan T. Ivanov 383eadff302SIvan T. Ivanov pad = pctldev->desc->pins[pin].drv_data; 384eadff302SIvan T. Ivanov 385eadff302SIvan T. Ivanov switch (param) { 386eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_PUSH_PULL: 3871cf86bc2SDouglas Anderson if (pad->buffer_type != PMIC_GPIO_OUT_BUF_CMOS) 3881cf86bc2SDouglas Anderson return -EINVAL; 3891cf86bc2SDouglas Anderson arg = 1; 390eadff302SIvan T. Ivanov break; 391eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_OPEN_DRAIN: 3921cf86bc2SDouglas Anderson if (pad->buffer_type != PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS) 3931cf86bc2SDouglas Anderson return -EINVAL; 3941cf86bc2SDouglas Anderson arg = 1; 395eadff302SIvan T. Ivanov break; 396eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_OPEN_SOURCE: 3971cf86bc2SDouglas Anderson if (pad->buffer_type != PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS) 3981cf86bc2SDouglas Anderson return -EINVAL; 3991cf86bc2SDouglas Anderson arg = 1; 400eadff302SIvan T. Ivanov break; 401eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_PULL_DOWN: 4021cf86bc2SDouglas Anderson if (pad->pullup != PMIC_GPIO_PULL_DOWN) 4031cf86bc2SDouglas Anderson return -EINVAL; 4041cf86bc2SDouglas Anderson arg = 1; 405eadff302SIvan T. Ivanov break; 406eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_DISABLE: 4071cf86bc2SDouglas Anderson if (pad->pullup != PMIC_GPIO_PULL_DISABLE) 4081cf86bc2SDouglas Anderson return -EINVAL; 4091cf86bc2SDouglas Anderson arg = 1; 410eadff302SIvan T. Ivanov break; 411eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_PULL_UP: 4121cf86bc2SDouglas Anderson if (pad->pullup != PMIC_GPIO_PULL_UP_30) 4131cf86bc2SDouglas Anderson return -EINVAL; 4141cf86bc2SDouglas Anderson arg = 1; 415eadff302SIvan T. Ivanov break; 416eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: 4171cf86bc2SDouglas Anderson if (pad->is_enabled) 4181cf86bc2SDouglas Anderson return -EINVAL; 4191cf86bc2SDouglas Anderson arg = 1; 420eadff302SIvan T. Ivanov break; 421eadff302SIvan T. Ivanov case PIN_CONFIG_POWER_SOURCE: 422eadff302SIvan T. Ivanov arg = pad->power_source; 423eadff302SIvan T. Ivanov break; 424eadff302SIvan T. Ivanov case PIN_CONFIG_INPUT_ENABLE: 4251cf86bc2SDouglas Anderson if (!pad->input_enabled) 4261cf86bc2SDouglas Anderson return -EINVAL; 4271cf86bc2SDouglas Anderson arg = 1; 428eadff302SIvan T. Ivanov break; 429727293a8SSubbaraman Narayanamurthy case PIN_CONFIG_OUTPUT_ENABLE: 430727293a8SSubbaraman Narayanamurthy arg = pad->output_enabled; 431727293a8SSubbaraman Narayanamurthy break; 432eadff302SIvan T. Ivanov case PIN_CONFIG_OUTPUT: 433eadff302SIvan T. Ivanov arg = pad->out_value; 434eadff302SIvan T. Ivanov break; 435eadff302SIvan T. Ivanov case PMIC_GPIO_CONF_PULL_UP: 436eadff302SIvan T. Ivanov arg = pad->pullup; 437eadff302SIvan T. Ivanov break; 438eadff302SIvan T. Ivanov case PMIC_GPIO_CONF_STRENGTH: 439eadff302SIvan T. Ivanov arg = pad->strength; 440eadff302SIvan T. Ivanov break; 441d7b5f5ccSFenglin Wu case PMIC_GPIO_CONF_ATEST: 442d7b5f5ccSFenglin Wu arg = pad->atest; 443d7b5f5ccSFenglin Wu break; 444d7b5f5ccSFenglin Wu case PMIC_GPIO_CONF_ANALOG_PASS: 445d7b5f5ccSFenglin Wu arg = pad->analog_pass; 446d7b5f5ccSFenglin Wu break; 447223463fcSFenglin Wu case PMIC_GPIO_CONF_DTEST_BUFFER: 448223463fcSFenglin Wu arg = pad->dtest_buffer; 449223463fcSFenglin Wu break; 450eadff302SIvan T. Ivanov default: 451eadff302SIvan T. Ivanov return -EINVAL; 452eadff302SIvan T. Ivanov } 453eadff302SIvan T. Ivanov 454eadff302SIvan T. Ivanov *config = pinconf_to_config_packed(param, arg); 455eadff302SIvan T. Ivanov return 0; 456eadff302SIvan T. Ivanov } 457eadff302SIvan T. Ivanov 458eadff302SIvan T. Ivanov static int pmic_gpio_config_set(struct pinctrl_dev *pctldev, unsigned int pin, 459eadff302SIvan T. Ivanov unsigned long *configs, unsigned nconfs) 460eadff302SIvan T. Ivanov { 461eadff302SIvan T. Ivanov struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev); 462eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad; 463eadff302SIvan T. Ivanov unsigned param, arg; 464eadff302SIvan T. Ivanov unsigned int val; 465eadff302SIvan T. Ivanov int i, ret; 466eadff302SIvan T. Ivanov 467eadff302SIvan T. Ivanov pad = pctldev->desc->pins[pin].drv_data; 468eadff302SIvan T. Ivanov 4696cb74f44SFenglin Wu pad->is_enabled = true; 470eadff302SIvan T. Ivanov for (i = 0; i < nconfs; i++) { 471eadff302SIvan T. Ivanov param = pinconf_to_config_param(configs[i]); 472eadff302SIvan T. Ivanov arg = pinconf_to_config_argument(configs[i]); 473eadff302SIvan T. Ivanov 474eadff302SIvan T. Ivanov switch (param) { 475eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_PUSH_PULL: 476eadff302SIvan T. Ivanov pad->buffer_type = PMIC_GPIO_OUT_BUF_CMOS; 477eadff302SIvan T. Ivanov break; 478eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_OPEN_DRAIN: 479eadff302SIvan T. Ivanov if (!pad->have_buffer) 480eadff302SIvan T. Ivanov return -EINVAL; 481eadff302SIvan T. Ivanov pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS; 482eadff302SIvan T. Ivanov break; 483eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_OPEN_SOURCE: 484eadff302SIvan T. Ivanov if (!pad->have_buffer) 485eadff302SIvan T. Ivanov return -EINVAL; 486eadff302SIvan T. Ivanov pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS; 487eadff302SIvan T. Ivanov break; 488eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_DISABLE: 489eadff302SIvan T. Ivanov pad->pullup = PMIC_GPIO_PULL_DISABLE; 490eadff302SIvan T. Ivanov break; 491eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_PULL_UP: 492eadff302SIvan T. Ivanov pad->pullup = PMIC_GPIO_PULL_UP_30; 493eadff302SIvan T. Ivanov break; 494eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_PULL_DOWN: 495eadff302SIvan T. Ivanov if (arg) 496eadff302SIvan T. Ivanov pad->pullup = PMIC_GPIO_PULL_DOWN; 497eadff302SIvan T. Ivanov else 498eadff302SIvan T. Ivanov pad->pullup = PMIC_GPIO_PULL_DISABLE; 499eadff302SIvan T. Ivanov break; 500eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: 501eadff302SIvan T. Ivanov pad->is_enabled = false; 502eadff302SIvan T. Ivanov break; 503eadff302SIvan T. Ivanov case PIN_CONFIG_POWER_SOURCE: 5044e83ac4cSFenglin Wu if (arg >= pad->num_sources) 505eadff302SIvan T. Ivanov return -EINVAL; 506eadff302SIvan T. Ivanov pad->power_source = arg; 507eadff302SIvan T. Ivanov break; 508eadff302SIvan T. Ivanov case PIN_CONFIG_INPUT_ENABLE: 509eadff302SIvan T. Ivanov pad->input_enabled = arg ? true : false; 510eadff302SIvan T. Ivanov break; 511727293a8SSubbaraman Narayanamurthy case PIN_CONFIG_OUTPUT_ENABLE: 512727293a8SSubbaraman Narayanamurthy pad->output_enabled = arg ? true : false; 513727293a8SSubbaraman Narayanamurthy 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"); 677202ba5ebSBjorn Andersson seq_printf(s, " %-4s", pad->out_value ? "high" : "low"); 678d7b5f5ccSFenglin Wu seq_printf(s, " %-7s", pmic_gpio_functions[function]); 679eadff302SIvan T. Ivanov seq_printf(s, " vin-%d", pad->power_source); 680eadff302SIvan T. Ivanov seq_printf(s, " %-27s", biases[pad->pullup]); 681eadff302SIvan T. Ivanov seq_printf(s, " %-10s", buffer_types[pad->buffer_type]); 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 void pmic_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) 762eadff302SIvan T. Ivanov { 763c52d9df1SLinus Walleij struct pmic_gpio_state *state = gpiochip_get_data(chip); 764eadff302SIvan T. Ivanov unsigned i; 765eadff302SIvan T. Ivanov 766eadff302SIvan T. Ivanov for (i = 0; i < chip->ngpio; i++) { 767eadff302SIvan T. Ivanov pmic_gpio_config_dbg_show(state->ctrl, s, i); 768eadff302SIvan T. Ivanov seq_puts(s, "\n"); 769eadff302SIvan T. Ivanov } 770eadff302SIvan T. Ivanov } 771eadff302SIvan T. Ivanov 772eadff302SIvan T. Ivanov static const struct gpio_chip pmic_gpio_gpio_template = { 773eadff302SIvan T. Ivanov .direction_input = pmic_gpio_direction_input, 774eadff302SIvan T. Ivanov .direction_output = pmic_gpio_direction_output, 775eadff302SIvan T. Ivanov .get = pmic_gpio_get, 776eadff302SIvan T. Ivanov .set = pmic_gpio_set, 77798c85d58SJonas Gorski .request = gpiochip_generic_request, 77898c85d58SJonas Gorski .free = gpiochip_generic_free, 779eadff302SIvan T. Ivanov .of_xlate = pmic_gpio_of_xlate, 780eadff302SIvan T. Ivanov .dbg_show = pmic_gpio_dbg_show, 781eadff302SIvan T. Ivanov }; 782eadff302SIvan T. Ivanov 783eadff302SIvan T. Ivanov static int pmic_gpio_populate(struct pmic_gpio_state *state, 784eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad) 785eadff302SIvan T. Ivanov { 786eadff302SIvan T. Ivanov int type, subtype, val, dir; 787eadff302SIvan T. Ivanov 788eadff302SIvan T. Ivanov type = pmic_gpio_read(state, pad, PMIC_GPIO_REG_TYPE); 789eadff302SIvan T. Ivanov if (type < 0) 790eadff302SIvan T. Ivanov return type; 791eadff302SIvan T. Ivanov 792eadff302SIvan T. Ivanov if (type != PMIC_GPIO_TYPE) { 793eadff302SIvan T. Ivanov dev_err(state->dev, "incorrect block type 0x%x at 0x%x\n", 794eadff302SIvan T. Ivanov type, pad->base); 795eadff302SIvan T. Ivanov return -ENODEV; 796eadff302SIvan T. Ivanov } 797eadff302SIvan T. Ivanov 798eadff302SIvan T. Ivanov subtype = pmic_gpio_read(state, pad, PMIC_GPIO_REG_SUBTYPE); 799eadff302SIvan T. Ivanov if (subtype < 0) 800eadff302SIvan T. Ivanov return subtype; 801eadff302SIvan T. Ivanov 802eadff302SIvan T. Ivanov switch (subtype) { 803eadff302SIvan T. Ivanov case PMIC_GPIO_SUBTYPE_GPIO_4CH: 804eadff302SIvan T. Ivanov pad->have_buffer = true; 8051586f556SGustavo A. R. Silva fallthrough; 806eadff302SIvan T. Ivanov case PMIC_GPIO_SUBTYPE_GPIOC_4CH: 807eadff302SIvan T. Ivanov pad->num_sources = 4; 808eadff302SIvan T. Ivanov break; 809eadff302SIvan T. Ivanov case PMIC_GPIO_SUBTYPE_GPIO_8CH: 810eadff302SIvan T. Ivanov pad->have_buffer = true; 8111586f556SGustavo A. R. Silva fallthrough; 812eadff302SIvan T. Ivanov case PMIC_GPIO_SUBTYPE_GPIOC_8CH: 813eadff302SIvan T. Ivanov pad->num_sources = 8; 814eadff302SIvan T. Ivanov break; 815d7b5f5ccSFenglin Wu case PMIC_GPIO_SUBTYPE_GPIO_LV: 816d7b5f5ccSFenglin Wu pad->num_sources = 1; 817d7b5f5ccSFenglin Wu pad->have_buffer = true; 818d7b5f5ccSFenglin Wu pad->lv_mv_type = true; 819d7b5f5ccSFenglin Wu break; 820d7b5f5ccSFenglin Wu case PMIC_GPIO_SUBTYPE_GPIO_MV: 821d7b5f5ccSFenglin Wu pad->num_sources = 2; 822d7b5f5ccSFenglin Wu pad->have_buffer = true; 823d7b5f5ccSFenglin Wu pad->lv_mv_type = true; 824d7b5f5ccSFenglin Wu break; 825eadff302SIvan T. Ivanov default: 826eadff302SIvan T. Ivanov dev_err(state->dev, "unknown GPIO type 0x%x\n", subtype); 827eadff302SIvan T. Ivanov return -ENODEV; 828eadff302SIvan T. Ivanov } 829eadff302SIvan T. Ivanov 830d7b5f5ccSFenglin Wu if (pad->lv_mv_type) { 831d7b5f5ccSFenglin Wu val = pmic_gpio_read(state, pad, 832d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL); 833d7b5f5ccSFenglin Wu if (val < 0) 834d7b5f5ccSFenglin Wu return val; 835d7b5f5ccSFenglin Wu 836d7b5f5ccSFenglin Wu pad->out_value = !!(val & PMIC_GPIO_LV_MV_OUTPUT_INVERT); 837d7b5f5ccSFenglin Wu pad->function = val & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK; 838d7b5f5ccSFenglin Wu 839d7b5f5ccSFenglin Wu val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL); 840d7b5f5ccSFenglin Wu if (val < 0) 841d7b5f5ccSFenglin Wu return val; 842d7b5f5ccSFenglin Wu 843d7b5f5ccSFenglin Wu dir = val & PMIC_GPIO_REG_LV_MV_MODE_DIR_MASK; 844d7b5f5ccSFenglin Wu } else { 845eadff302SIvan T. Ivanov val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL); 846eadff302SIvan T. Ivanov if (val < 0) 847eadff302SIvan T. Ivanov return val; 848eadff302SIvan T. Ivanov 849eadff302SIvan T. Ivanov pad->out_value = val & PMIC_GPIO_REG_MODE_VALUE_SHIFT; 850eadff302SIvan T. Ivanov 851eadff302SIvan T. Ivanov dir = val >> PMIC_GPIO_REG_MODE_DIR_SHIFT; 852eadff302SIvan T. Ivanov dir &= PMIC_GPIO_REG_MODE_DIR_MASK; 853d7b5f5ccSFenglin Wu pad->function = val >> PMIC_GPIO_REG_MODE_FUNCTION_SHIFT; 854d7b5f5ccSFenglin Wu pad->function &= PMIC_GPIO_REG_MODE_FUNCTION_MASK; 855d7b5f5ccSFenglin Wu } 856d7b5f5ccSFenglin Wu 857eadff302SIvan T. Ivanov switch (dir) { 858d7b5f5ccSFenglin Wu case PMIC_GPIO_MODE_DIGITAL_INPUT: 859eadff302SIvan T. Ivanov pad->input_enabled = true; 860eadff302SIvan T. Ivanov pad->output_enabled = false; 861eadff302SIvan T. Ivanov break; 862d7b5f5ccSFenglin Wu case PMIC_GPIO_MODE_DIGITAL_OUTPUT: 863eadff302SIvan T. Ivanov pad->input_enabled = false; 864eadff302SIvan T. Ivanov pad->output_enabled = true; 865eadff302SIvan T. Ivanov break; 866d7b5f5ccSFenglin Wu case PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT: 867eadff302SIvan T. Ivanov pad->input_enabled = true; 868eadff302SIvan T. Ivanov pad->output_enabled = true; 869eadff302SIvan T. Ivanov break; 870d7b5f5ccSFenglin Wu case PMIC_GPIO_MODE_ANALOG_PASS_THRU: 871d7b5f5ccSFenglin Wu if (!pad->lv_mv_type) 872d7b5f5ccSFenglin Wu return -ENODEV; 873d7b5f5ccSFenglin Wu pad->analog_pass = true; 874d7b5f5ccSFenglin Wu break; 875eadff302SIvan T. Ivanov default: 876eadff302SIvan T. Ivanov dev_err(state->dev, "unknown GPIO direction\n"); 877eadff302SIvan T. Ivanov return -ENODEV; 878eadff302SIvan T. Ivanov } 879eadff302SIvan T. Ivanov 880eadff302SIvan T. Ivanov val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL); 881eadff302SIvan T. Ivanov if (val < 0) 882eadff302SIvan T. Ivanov return val; 883eadff302SIvan T. Ivanov 884eadff302SIvan T. Ivanov pad->power_source = val >> PMIC_GPIO_REG_VIN_SHIFT; 885eadff302SIvan T. Ivanov pad->power_source &= PMIC_GPIO_REG_VIN_MASK; 886eadff302SIvan T. Ivanov 887eadff302SIvan T. Ivanov val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_PULL_CTL); 888eadff302SIvan T. Ivanov if (val < 0) 889eadff302SIvan T. Ivanov return val; 890eadff302SIvan T. Ivanov 891eadff302SIvan T. Ivanov pad->pullup = val >> PMIC_GPIO_REG_PULL_SHIFT; 892eadff302SIvan T. Ivanov pad->pullup &= PMIC_GPIO_REG_PULL_MASK; 893eadff302SIvan T. Ivanov 894223463fcSFenglin Wu val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_IN_CTL); 895223463fcSFenglin Wu if (val < 0) 896223463fcSFenglin Wu return val; 897223463fcSFenglin Wu 898223463fcSFenglin Wu if (pad->lv_mv_type && (val & PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN)) 899223463fcSFenglin Wu pad->dtest_buffer = 900223463fcSFenglin Wu (val & PMIC_GPIO_LV_MV_DIG_IN_DTEST_SEL_MASK) + 1; 901223463fcSFenglin Wu else if (!pad->lv_mv_type) 902223463fcSFenglin Wu pad->dtest_buffer = ffs(val); 903223463fcSFenglin Wu else 904223463fcSFenglin Wu pad->dtest_buffer = 0; 905223463fcSFenglin Wu 906223463fcSFenglin Wu val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL); 907223463fcSFenglin Wu if (val < 0) 908223463fcSFenglin Wu return val; 909223463fcSFenglin Wu 910eadff302SIvan T. Ivanov pad->strength = val >> PMIC_GPIO_REG_OUT_STRENGTH_SHIFT; 911eadff302SIvan T. Ivanov pad->strength &= PMIC_GPIO_REG_OUT_STRENGTH_MASK; 912eadff302SIvan T. Ivanov 913eadff302SIvan T. Ivanov pad->buffer_type = val >> PMIC_GPIO_REG_OUT_TYPE_SHIFT; 914eadff302SIvan T. Ivanov pad->buffer_type &= PMIC_GPIO_REG_OUT_TYPE_MASK; 915eadff302SIvan T. Ivanov 916d7b5f5ccSFenglin Wu if (pad->lv_mv_type) { 917d7b5f5ccSFenglin Wu val = pmic_gpio_read(state, pad, 918d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL); 919d7b5f5ccSFenglin Wu if (val < 0) 920d7b5f5ccSFenglin Wu return val; 921d7b5f5ccSFenglin Wu pad->atest = (val & PMIC_GPIO_LV_MV_ANA_MUX_SEL_MASK) + 1; 922d7b5f5ccSFenglin Wu } 923d7b5f5ccSFenglin Wu 924eadff302SIvan T. Ivanov /* Pin could be disabled with PIN_CONFIG_BIAS_HIGH_IMPEDANCE */ 925eadff302SIvan T. Ivanov pad->is_enabled = true; 926eadff302SIvan T. Ivanov return 0; 927eadff302SIvan T. Ivanov } 928eadff302SIvan T. Ivanov 929ca69e2d1SBrian Masney static int pmic_gpio_domain_translate(struct irq_domain *domain, 930ca69e2d1SBrian Masney struct irq_fwspec *fwspec, 931ca69e2d1SBrian Masney unsigned long *hwirq, 932ca69e2d1SBrian Masney unsigned int *type) 933ca69e2d1SBrian Masney { 934ca69e2d1SBrian Masney struct pmic_gpio_state *state = container_of(domain->host_data, 935ca69e2d1SBrian Masney struct pmic_gpio_state, 936ca69e2d1SBrian Masney chip); 937ca69e2d1SBrian Masney 938dac7da98SBjorn Andersson if (fwspec->param_count != 2 || 939dac7da98SBjorn Andersson fwspec->param[0] < 1 || fwspec->param[0] > state->chip.ngpio) 940ca69e2d1SBrian Masney return -EINVAL; 941ca69e2d1SBrian Masney 942ca69e2d1SBrian Masney *hwirq = fwspec->param[0] - PMIC_GPIO_PHYSICAL_OFFSET; 943ca69e2d1SBrian Masney *type = fwspec->param[1]; 944ca69e2d1SBrian Masney 945ca69e2d1SBrian Masney return 0; 946ca69e2d1SBrian Masney } 947ca69e2d1SBrian Masney 948821c76c4SBrian Masney static unsigned int pmic_gpio_child_offset_to_irq(struct gpio_chip *chip, 949821c76c4SBrian Masney unsigned int offset) 950ca69e2d1SBrian Masney { 951821c76c4SBrian Masney return offset + PMIC_GPIO_PHYSICAL_OFFSET; 952ca69e2d1SBrian Masney } 953ca69e2d1SBrian Masney 954821c76c4SBrian Masney static int pmic_gpio_child_to_parent_hwirq(struct gpio_chip *chip, 955821c76c4SBrian Masney unsigned int child_hwirq, 956821c76c4SBrian Masney unsigned int child_type, 957821c76c4SBrian Masney unsigned int *parent_hwirq, 958821c76c4SBrian Masney unsigned int *parent_type) 959821c76c4SBrian Masney { 960d36a9773SDavid Collins struct pmic_gpio_state *state = gpiochip_get_data(chip); 961d36a9773SDavid Collins 962d36a9773SDavid Collins *parent_hwirq = child_hwirq + state->pid_base; 963821c76c4SBrian Masney *parent_type = child_type; 964821c76c4SBrian Masney 965821c76c4SBrian Masney return 0; 966821c76c4SBrian Masney } 967ca69e2d1SBrian Masney 96891a29af4SMarc Zyngier static int pmic_gpio_populate_parent_fwspec(struct gpio_chip *chip, 96991a29af4SMarc Zyngier union gpio_irq_fwspec *gfwspec, 970d36a9773SDavid Collins unsigned int parent_hwirq, 971d36a9773SDavid Collins unsigned int parent_type) 972d36a9773SDavid Collins { 973d36a9773SDavid Collins struct pmic_gpio_state *state = gpiochip_get_data(chip); 97491a29af4SMarc Zyngier struct irq_fwspec *fwspec = &gfwspec->fwspec; 975d36a9773SDavid Collins 976d36a9773SDavid Collins fwspec->fwnode = chip->irq.parent_domain->fwnode; 977d36a9773SDavid Collins 978d36a9773SDavid Collins fwspec->param_count = 4; 979d36a9773SDavid Collins fwspec->param[0] = state->usid; 980d36a9773SDavid Collins fwspec->param[1] = parent_hwirq; 981d36a9773SDavid Collins /* param[2] must be left as 0 */ 982d36a9773SDavid Collins fwspec->param[3] = parent_type; 983d36a9773SDavid Collins 98491a29af4SMarc Zyngier return 0; 985d36a9773SDavid Collins } 986d36a9773SDavid Collins 987*1a41d1e5SManivannan Sadhasivam static void pmic_gpio_irq_mask(struct irq_data *data) 988*1a41d1e5SManivannan Sadhasivam { 989*1a41d1e5SManivannan Sadhasivam struct gpio_chip *gc = irq_data_get_irq_chip_data(data); 990*1a41d1e5SManivannan Sadhasivam 991*1a41d1e5SManivannan Sadhasivam irq_chip_mask_parent(data); 992*1a41d1e5SManivannan Sadhasivam gpiochip_disable_irq(gc, data->hwirq); 993*1a41d1e5SManivannan Sadhasivam } 994*1a41d1e5SManivannan Sadhasivam 995*1a41d1e5SManivannan Sadhasivam static void pmic_gpio_irq_unmask(struct irq_data *data) 996*1a41d1e5SManivannan Sadhasivam { 997*1a41d1e5SManivannan Sadhasivam struct gpio_chip *gc = irq_data_get_irq_chip_data(data); 998*1a41d1e5SManivannan Sadhasivam 999*1a41d1e5SManivannan Sadhasivam gpiochip_enable_irq(gc, data->hwirq); 1000*1a41d1e5SManivannan Sadhasivam irq_chip_unmask_parent(data); 1001*1a41d1e5SManivannan Sadhasivam } 1002*1a41d1e5SManivannan Sadhasivam 1003*1a41d1e5SManivannan Sadhasivam static const struct irq_chip spmi_gpio_irq_chip = { 1004*1a41d1e5SManivannan Sadhasivam .name = "spmi-gpio", 1005*1a41d1e5SManivannan Sadhasivam .irq_ack = irq_chip_ack_parent, 1006*1a41d1e5SManivannan Sadhasivam .irq_mask = pmic_gpio_irq_mask, 1007*1a41d1e5SManivannan Sadhasivam .irq_unmask = pmic_gpio_irq_unmask, 1008*1a41d1e5SManivannan Sadhasivam .irq_set_type = irq_chip_set_type_parent, 1009*1a41d1e5SManivannan Sadhasivam .irq_set_wake = irq_chip_set_wake_parent, 1010*1a41d1e5SManivannan Sadhasivam .flags = IRQCHIP_IMMUTABLE | IRQCHIP_MASK_ON_SUSPEND, 1011*1a41d1e5SManivannan Sadhasivam GPIOCHIP_IRQ_RESOURCE_HELPERS, 1012*1a41d1e5SManivannan Sadhasivam }; 1013*1a41d1e5SManivannan Sadhasivam 1014eadff302SIvan T. Ivanov static int pmic_gpio_probe(struct platform_device *pdev) 1015eadff302SIvan T. Ivanov { 1016ca69e2d1SBrian Masney struct irq_domain *parent_domain; 1017ca69e2d1SBrian Masney struct device_node *parent_node; 1018eadff302SIvan T. Ivanov struct device *dev = &pdev->dev; 1019eadff302SIvan T. Ivanov struct pinctrl_pin_desc *pindesc; 1020eadff302SIvan T. Ivanov struct pinctrl_desc *pctrldesc; 1021eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad, *pads; 1022eadff302SIvan T. Ivanov struct pmic_gpio_state *state; 1023821c76c4SBrian Masney struct gpio_irq_chip *girq; 1024d36a9773SDavid Collins const struct spmi_device *parent_spmi_dev; 1025eadff302SIvan T. Ivanov int ret, npins, i; 1026ab4256cfSStephen Boyd u32 reg; 1027eadff302SIvan T. Ivanov 1028ab4256cfSStephen Boyd ret = of_property_read_u32(dev->of_node, "reg", ®); 1029eadff302SIvan T. Ivanov if (ret < 0) { 1030ab4256cfSStephen Boyd dev_err(dev, "missing base address"); 1031eadff302SIvan T. Ivanov return ret; 1032eadff302SIvan T. Ivanov } 1033eadff302SIvan T. Ivanov 1034cfacef37SBrian Masney npins = (uintptr_t) device_get_match_data(&pdev->dev); 1035eadff302SIvan T. Ivanov 1036eadff302SIvan T. Ivanov state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); 1037eadff302SIvan T. Ivanov if (!state) 1038eadff302SIvan T. Ivanov return -ENOMEM; 1039eadff302SIvan T. Ivanov 1040eadff302SIvan T. Ivanov platform_set_drvdata(pdev, state); 1041eadff302SIvan T. Ivanov 1042eadff302SIvan T. Ivanov state->dev = &pdev->dev; 1043eadff302SIvan T. Ivanov state->map = dev_get_regmap(dev->parent, NULL); 1044d36a9773SDavid Collins parent_spmi_dev = to_spmi_device(dev->parent); 1045d36a9773SDavid Collins state->usid = parent_spmi_dev->usid; 1046d36a9773SDavid Collins state->pid_base = reg >> 8; 1047eadff302SIvan T. Ivanov 1048eadff302SIvan T. Ivanov pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL); 1049eadff302SIvan T. Ivanov if (!pindesc) 1050eadff302SIvan T. Ivanov return -ENOMEM; 1051eadff302SIvan T. Ivanov 1052eadff302SIvan T. Ivanov pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL); 1053eadff302SIvan T. Ivanov if (!pads) 1054eadff302SIvan T. Ivanov return -ENOMEM; 1055eadff302SIvan T. Ivanov 1056eadff302SIvan T. Ivanov pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL); 1057eadff302SIvan T. Ivanov if (!pctrldesc) 1058eadff302SIvan T. Ivanov return -ENOMEM; 1059eadff302SIvan T. Ivanov 1060eadff302SIvan T. Ivanov pctrldesc->pctlops = &pmic_gpio_pinctrl_ops; 1061eadff302SIvan T. Ivanov pctrldesc->pmxops = &pmic_gpio_pinmux_ops; 1062eadff302SIvan T. Ivanov pctrldesc->confops = &pmic_gpio_pinconf_ops; 1063eadff302SIvan T. Ivanov pctrldesc->owner = THIS_MODULE; 1064eadff302SIvan T. Ivanov pctrldesc->name = dev_name(dev); 1065eadff302SIvan T. Ivanov pctrldesc->pins = pindesc; 1066eadff302SIvan T. Ivanov pctrldesc->npins = npins; 1067f684e4acSLinus Walleij pctrldesc->num_custom_params = ARRAY_SIZE(pmic_gpio_bindings); 1068f684e4acSLinus Walleij pctrldesc->custom_params = pmic_gpio_bindings; 10694f06266aSArnd Bergmann #ifdef CONFIG_DEBUG_FS 1070f684e4acSLinus Walleij pctrldesc->custom_conf_items = pmic_conf_items; 10714f06266aSArnd Bergmann #endif 1072eadff302SIvan T. Ivanov 1073eadff302SIvan T. Ivanov for (i = 0; i < npins; i++, pindesc++) { 1074eadff302SIvan T. Ivanov pad = &pads[i]; 1075eadff302SIvan T. Ivanov pindesc->drv_data = pad; 1076eadff302SIvan T. Ivanov pindesc->number = i; 1077eadff302SIvan T. Ivanov pindesc->name = pmic_gpio_groups[i]; 1078eadff302SIvan T. Ivanov 1079ab4256cfSStephen Boyd pad->base = reg + i * PMIC_GPIO_ADDRESS_RANGE; 1080eadff302SIvan T. Ivanov 1081eadff302SIvan T. Ivanov ret = pmic_gpio_populate(state, pad); 1082eadff302SIvan T. Ivanov if (ret < 0) 1083eadff302SIvan T. Ivanov return ret; 1084eadff302SIvan T. Ivanov } 1085eadff302SIvan T. Ivanov 1086eadff302SIvan T. Ivanov state->chip = pmic_gpio_gpio_template; 108758383c78SLinus Walleij state->chip.parent = dev; 1088eadff302SIvan T. Ivanov state->chip.base = -1; 1089eadff302SIvan T. Ivanov state->chip.ngpio = npins; 1090eadff302SIvan T. Ivanov state->chip.label = dev_name(dev); 1091eadff302SIvan T. Ivanov state->chip.of_gpio_n_cells = 2; 1092eadff302SIvan T. Ivanov state->chip.can_sleep = false; 1093eadff302SIvan T. Ivanov 1094b46ddfe6SLaxman Dewangan state->ctrl = devm_pinctrl_register(dev, pctrldesc, state); 1095323de9efSMasahiro Yamada if (IS_ERR(state->ctrl)) 1096323de9efSMasahiro Yamada return PTR_ERR(state->ctrl); 1097eadff302SIvan T. Ivanov 1098ca69e2d1SBrian Masney parent_node = of_irq_find_parent(state->dev->of_node); 1099ca69e2d1SBrian Masney if (!parent_node) 1100ca69e2d1SBrian Masney return -ENXIO; 1101ca69e2d1SBrian Masney 1102ca69e2d1SBrian Masney parent_domain = irq_find_host(parent_node); 1103ca69e2d1SBrian Masney of_node_put(parent_node); 1104ca69e2d1SBrian Masney if (!parent_domain) 1105ca69e2d1SBrian Masney return -ENXIO; 1106ca69e2d1SBrian Masney 1107821c76c4SBrian Masney girq = &state->chip.irq; 1108*1a41d1e5SManivannan Sadhasivam gpio_irq_chip_set_chip(girq, &spmi_gpio_irq_chip); 1109821c76c4SBrian Masney girq->default_type = IRQ_TYPE_NONE; 1110821c76c4SBrian Masney girq->handler = handle_level_irq; 1111821c76c4SBrian Masney girq->fwnode = of_node_to_fwnode(state->dev->of_node); 1112821c76c4SBrian Masney girq->parent_domain = parent_domain; 1113821c76c4SBrian Masney girq->child_to_parent_hwirq = pmic_gpio_child_to_parent_hwirq; 1114d36a9773SDavid Collins girq->populate_parent_alloc_arg = pmic_gpio_populate_parent_fwspec; 1115821c76c4SBrian Masney girq->child_offset_to_irq = pmic_gpio_child_offset_to_irq; 1116821c76c4SBrian Masney girq->child_irq_domain_ops.translate = pmic_gpio_domain_translate; 1117ca69e2d1SBrian Masney 1118c52d9df1SLinus Walleij ret = gpiochip_add_data(&state->chip, state); 1119eadff302SIvan T. Ivanov if (ret) { 1120eadff302SIvan T. Ivanov dev_err(state->dev, "can't add gpio chip\n"); 1121821c76c4SBrian Masney return ret; 1122eadff302SIvan T. Ivanov } 1123eadff302SIvan T. Ivanov 1124149a9604SBrian Masney /* 1125149a9604SBrian Masney * For DeviceTree-supported systems, the gpio core checks the 1126149a9604SBrian Masney * pinctrl's device node for the "gpio-ranges" property. 1127149a9604SBrian Masney * If it is present, it takes care of adding the pin ranges 1128149a9604SBrian Masney * for the driver. In this case the driver can skip ahead. 1129149a9604SBrian Masney * 1130149a9604SBrian Masney * In order to remain compatible with older, existing DeviceTree 1131149a9604SBrian Masney * files which don't set the "gpio-ranges" property or systems that 1132149a9604SBrian Masney * utilize ACPI the driver has to call gpiochip_add_pin_range(). 1133149a9604SBrian Masney */ 1134149a9604SBrian Masney if (!of_property_read_bool(dev->of_node, "gpio-ranges")) { 1135149a9604SBrian Masney ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, 1136149a9604SBrian Masney npins); 1137eadff302SIvan T. Ivanov if (ret) { 1138eadff302SIvan T. Ivanov dev_err(dev, "failed to add pin range\n"); 1139eadff302SIvan T. Ivanov goto err_range; 1140eadff302SIvan T. Ivanov } 1141149a9604SBrian Masney } 1142eadff302SIvan T. Ivanov 1143eadff302SIvan T. Ivanov return 0; 1144eadff302SIvan T. Ivanov 1145eadff302SIvan T. Ivanov err_range: 1146eadff302SIvan T. Ivanov gpiochip_remove(&state->chip); 1147eadff302SIvan T. Ivanov return ret; 1148eadff302SIvan T. Ivanov } 1149eadff302SIvan T. Ivanov 1150eadff302SIvan T. Ivanov static int pmic_gpio_remove(struct platform_device *pdev) 1151eadff302SIvan T. Ivanov { 1152eadff302SIvan T. Ivanov struct pmic_gpio_state *state = platform_get_drvdata(pdev); 1153eadff302SIvan T. Ivanov 1154eadff302SIvan T. Ivanov gpiochip_remove(&state->chip); 1155eadff302SIvan T. Ivanov return 0; 1156eadff302SIvan T. Ivanov } 1157eadff302SIvan T. Ivanov 1158eadff302SIvan T. Ivanov static const struct of_device_id pmic_gpio_of_match[] = { 1159ef874e03SLoic Poulain { .compatible = "qcom,pm2250-gpio", .data = (void *) 10 }, 116017cc38e7SKonrad Dybcio /* pm660 has 13 GPIOs with holes on 1, 5, 6, 7, 8 and 10 */ 116117cc38e7SKonrad Dybcio { .compatible = "qcom,pm660-gpio", .data = (void *) 13 }, 116217cc38e7SKonrad Dybcio /* pm660l has 12 GPIOs with holes on 1, 2, 10, 11 and 12 */ 116317cc38e7SKonrad Dybcio { .compatible = "qcom,pm660l-gpio", .data = (void *) 12 }, 11644d8a768eSMarijn Suijten { .compatible = "qcom,pm6125-gpio", .data = (void *) 9 }, 11654afc2a0cSBhupesh Sharma { .compatible = "qcom,pm6150-gpio", .data = (void *) 10 }, 11664afc2a0cSBhupesh Sharma { .compatible = "qcom,pm6150l-gpio", .data = (void *) 12 }, 116783917856SLuca Weiss { .compatible = "qcom,pm6350-gpio", .data = (void *) 9 }, 11684afc2a0cSBhupesh Sharma { .compatible = "qcom,pm7325-gpio", .data = (void *) 10 }, 11694afc2a0cSBhupesh Sharma { .compatible = "qcom,pm8005-gpio", .data = (void *) 4 }, 11704afc2a0cSBhupesh Sharma { .compatible = "qcom,pm8008-gpio", .data = (void *) 2 }, 1171f3474383SKonrad Dybcio { .compatible = "qcom,pm8019-gpio", .data = (void *) 6 }, 1172d67070c3SVinod Koul /* pm8150 has 10 GPIOs with holes on 2, 5, 7 and 8 */ 1173d67070c3SVinod Koul { .compatible = "qcom,pm8150-gpio", .data = (void *) 10 }, 1174182700f2SBjorn Andersson { .compatible = "qcom,pmc8180-gpio", .data = (void *) 10 }, 11759bdacaddSVinod Koul /* pm8150b has 12 GPIOs with holes on 3, r and 7 */ 11769bdacaddSVinod Koul { .compatible = "qcom,pm8150b-gpio", .data = (void *) 12 }, 11772dc889a8SVinod Koul /* pm8150l has 12 GPIOs with holes on 7 */ 11782dc889a8SVinod Koul { .compatible = "qcom,pm8150l-gpio", .data = (void *) 12 }, 1179182700f2SBjorn Andersson { .compatible = "qcom,pmc8180c-gpio", .data = (void *) 12 }, 1180eebe11b5SDominik Kobinski { .compatible = "qcom,pm8226-gpio", .data = (void *) 8 }, 118157c0a4f0SVinod Koul { .compatible = "qcom,pm8350-gpio", .data = (void *) 10 }, 118257c0a4f0SVinod Koul { .compatible = "qcom,pm8350b-gpio", .data = (void *) 8 }, 118357c0a4f0SVinod Koul { .compatible = "qcom,pm8350c-gpio", .data = (void *) 9 }, 1184168a0abfSDmitry Baryshkov { .compatible = "qcom,pm8450-gpio", .data = (void *) 4 }, 11854afc2a0cSBhupesh Sharma { .compatible = "qcom,pm8916-gpio", .data = (void *) 4 }, 11864afc2a0cSBhupesh Sharma { .compatible = "qcom,pm8941-gpio", .data = (void *) 36 }, 11874afc2a0cSBhupesh Sharma /* pm8950 has 8 GPIOs with holes on 3 */ 11884afc2a0cSBhupesh Sharma { .compatible = "qcom,pm8950-gpio", .data = (void *) 8 }, 11894afc2a0cSBhupesh Sharma { .compatible = "qcom,pm8994-gpio", .data = (void *) 22 }, 11904afc2a0cSBhupesh Sharma { .compatible = "qcom,pm8998-gpio", .data = (void *) 26 }, 11914afc2a0cSBhupesh Sharma { .compatible = "qcom,pma8084-gpio", .data = (void *) 22 }, 11924afc2a0cSBhupesh Sharma { .compatible = "qcom,pmi8950-gpio", .data = (void *) 2 }, 11934afc2a0cSBhupesh Sharma { .compatible = "qcom,pmi8994-gpio", .data = (void *) 10 }, 11944afc2a0cSBhupesh Sharma { .compatible = "qcom,pmi8998-gpio", .data = (void *) 14 }, 119557c0a4f0SVinod Koul { .compatible = "qcom,pmk8350-gpio", .data = (void *) 4 }, 119679e2311cSBhupesh Sharma { .compatible = "qcom,pmm8155au-gpio", .data = (void *) 10 }, 11976cd81a86SRobert Marko /* pmp8074 has 12 GPIOs with holes on 1 and 12 */ 11986cd81a86SRobert Marko { .compatible = "qcom,pmp8074-gpio", .data = (void *) 12 }, 119957c0a4f0SVinod Koul { .compatible = "qcom,pmr735a-gpio", .data = (void *) 4 }, 120057c0a4f0SVinod Koul { .compatible = "qcom,pmr735b-gpio", .data = (void *) 4 }, 12014afc2a0cSBhupesh Sharma /* pms405 has 12 GPIOs with holes on 1, 9, and 10 */ 12024afc2a0cSBhupesh Sharma { .compatible = "qcom,pms405-gpio", .data = (void *) 12 }, 1203ceb58de4SVinod Koul /* pmx55 has 11 GPIOs with holes on 3, 7, 10, 11 */ 1204ceb58de4SVinod Koul { .compatible = "qcom,pmx55-gpio", .data = (void *) 11 }, 1205203638fdSRohit Agarwal { .compatible = "qcom,pmx65-gpio", .data = (void *) 16 }, 1206eadff302SIvan T. Ivanov { }, 1207eadff302SIvan T. Ivanov }; 1208eadff302SIvan T. Ivanov 1209eadff302SIvan T. Ivanov MODULE_DEVICE_TABLE(of, pmic_gpio_of_match); 1210eadff302SIvan T. Ivanov 1211eadff302SIvan T. Ivanov static struct platform_driver pmic_gpio_driver = { 1212eadff302SIvan T. Ivanov .driver = { 1213eadff302SIvan T. Ivanov .name = "qcom-spmi-gpio", 1214eadff302SIvan T. Ivanov .of_match_table = pmic_gpio_of_match, 1215eadff302SIvan T. Ivanov }, 1216eadff302SIvan T. Ivanov .probe = pmic_gpio_probe, 1217eadff302SIvan T. Ivanov .remove = pmic_gpio_remove, 1218eadff302SIvan T. Ivanov }; 1219eadff302SIvan T. Ivanov 1220eadff302SIvan T. Ivanov module_platform_driver(pmic_gpio_driver); 1221eadff302SIvan T. Ivanov 1222eadff302SIvan T. Ivanov MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>"); 1223eadff302SIvan T. Ivanov MODULE_DESCRIPTION("Qualcomm SPMI PMIC GPIO pin control driver"); 1224eadff302SIvan T. Ivanov MODULE_ALIAS("platform:qcom-spmi-gpio"); 1225eadff302SIvan T. Ivanov MODULE_LICENSE("GPL v2"); 1226