197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2eadff302SIvan T. Ivanov /* 3eadff302SIvan T. Ivanov * Copyright (c) 2012-2014, 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> 17eadff302SIvan T. Ivanov #include <linux/types.h> 18eadff302SIvan T. Ivanov 19eadff302SIvan T. Ivanov #include <dt-bindings/pinctrl/qcom,pmic-gpio.h> 20eadff302SIvan T. Ivanov 21eadff302SIvan T. Ivanov #include "../core.h" 22eadff302SIvan T. Ivanov #include "../pinctrl-utils.h" 23eadff302SIvan T. Ivanov 24eadff302SIvan T. Ivanov #define PMIC_GPIO_ADDRESS_RANGE 0x100 25eadff302SIvan T. Ivanov 26eadff302SIvan T. Ivanov /* type and subtype registers base address offsets */ 27eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_TYPE 0x4 28eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_SUBTYPE 0x5 29eadff302SIvan T. Ivanov 30eadff302SIvan T. Ivanov /* GPIO peripheral type and subtype out_values */ 31eadff302SIvan T. Ivanov #define PMIC_GPIO_TYPE 0x10 32eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIO_4CH 0x1 33eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIOC_4CH 0x5 34eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIO_8CH 0x9 35eadff302SIvan T. Ivanov #define PMIC_GPIO_SUBTYPE_GPIOC_8CH 0xd 36d7b5f5ccSFenglin Wu #define PMIC_GPIO_SUBTYPE_GPIO_LV 0x10 37d7b5f5ccSFenglin Wu #define PMIC_GPIO_SUBTYPE_GPIO_MV 0x11 38eadff302SIvan T. Ivanov 39eadff302SIvan T. Ivanov #define PMIC_MPP_REG_RT_STS 0x10 40eadff302SIvan T. Ivanov #define PMIC_MPP_REG_RT_STS_VAL_MASK 0x1 41eadff302SIvan T. Ivanov 42eadff302SIvan T. Ivanov /* control register base address offsets */ 43eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_CTL 0x40 44eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_VIN_CTL 0x41 45eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_PULL_CTL 0x42 46d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL 0x44 47223463fcSFenglin Wu #define PMIC_GPIO_REG_DIG_IN_CTL 0x43 48eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_DIG_OUT_CTL 0x45 49eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_EN_CTL 0x46 50d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL 0x4A 51eadff302SIvan T. Ivanov 52eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_MODE_CTL */ 53eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_VALUE_SHIFT 0x1 54eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_FUNCTION_SHIFT 1 55eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_FUNCTION_MASK 0x7 56eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_DIR_SHIFT 4 57eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MODE_DIR_MASK 0x7 58eadff302SIvan T. Ivanov 59d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_INPUT 0 60d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_OUTPUT 1 61d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT 2 62d7b5f5ccSFenglin Wu #define PMIC_GPIO_MODE_ANALOG_PASS_THRU 3 63d7b5f5ccSFenglin Wu #define PMIC_GPIO_REG_LV_MV_MODE_DIR_MASK 0x3 64d7b5f5ccSFenglin Wu 65eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_VIN_CTL */ 66eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_VIN_SHIFT 0 67eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_VIN_MASK 0x7 68eadff302SIvan T. Ivanov 69eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_PULL_CTL */ 70eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_PULL_SHIFT 0 71eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_PULL_MASK 0x7 72eadff302SIvan T. Ivanov 73eadff302SIvan T. Ivanov #define PMIC_GPIO_PULL_DOWN 4 74eadff302SIvan T. Ivanov #define PMIC_GPIO_PULL_DISABLE 5 75eadff302SIvan T. Ivanov 76d7b5f5ccSFenglin Wu /* PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL for LV/MV */ 77d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_INVERT 0x80 78d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT 7 79d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK 0xF 80d7b5f5ccSFenglin Wu 81223463fcSFenglin Wu /* PMIC_GPIO_REG_DIG_IN_CTL */ 82223463fcSFenglin Wu #define PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN 0x80 83223463fcSFenglin Wu #define PMIC_GPIO_LV_MV_DIG_IN_DTEST_SEL_MASK 0x7 84223463fcSFenglin Wu #define PMIC_GPIO_DIG_IN_DTEST_SEL_MASK 0xf 85223463fcSFenglin Wu 86eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_DIG_OUT_CTL */ 87eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_STRENGTH_SHIFT 0 88eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_STRENGTH_MASK 0x3 89eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_TYPE_SHIFT 4 90eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_OUT_TYPE_MASK 0x3 91eadff302SIvan T. Ivanov 92eadff302SIvan T. Ivanov /* 93eadff302SIvan T. Ivanov * Output type - indicates pin should be configured as push-pull, 94eadff302SIvan T. Ivanov * open drain or open source. 95eadff302SIvan T. Ivanov */ 96eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_CMOS 0 97eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS 1 98eadff302SIvan T. Ivanov #define PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS 2 99eadff302SIvan T. Ivanov 100eadff302SIvan T. Ivanov /* PMIC_GPIO_REG_EN_CTL */ 101eadff302SIvan T. Ivanov #define PMIC_GPIO_REG_MASTER_EN_SHIFT 7 102eadff302SIvan T. Ivanov 103eadff302SIvan T. Ivanov #define PMIC_GPIO_PHYSICAL_OFFSET 1 104eadff302SIvan T. Ivanov 105d7b5f5ccSFenglin Wu /* PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL */ 106d7b5f5ccSFenglin Wu #define PMIC_GPIO_LV_MV_ANA_MUX_SEL_MASK 0x3 107d7b5f5ccSFenglin Wu 108eadff302SIvan T. Ivanov /* Qualcomm specific pin configurations */ 109eadff302SIvan T. Ivanov #define PMIC_GPIO_CONF_PULL_UP (PIN_CONFIG_END + 1) 110eadff302SIvan T. Ivanov #define PMIC_GPIO_CONF_STRENGTH (PIN_CONFIG_END + 2) 111d7b5f5ccSFenglin Wu #define PMIC_GPIO_CONF_ATEST (PIN_CONFIG_END + 3) 112d7b5f5ccSFenglin Wu #define PMIC_GPIO_CONF_ANALOG_PASS (PIN_CONFIG_END + 4) 113223463fcSFenglin Wu #define PMIC_GPIO_CONF_DTEST_BUFFER (PIN_CONFIG_END + 5) 114d7b5f5ccSFenglin Wu 115d7b5f5ccSFenglin Wu /* The index of each function in pmic_gpio_functions[] array */ 116d7b5f5ccSFenglin Wu enum pmic_gpio_func_index { 117d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_NORMAL, 118d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_PAIRED, 119d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_FUNC1, 120d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_FUNC2, 121d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_FUNC3, 122d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_FUNC4, 123d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_DTEST1, 124d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_DTEST2, 125d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_DTEST3, 126d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_DTEST4, 127d7b5f5ccSFenglin Wu }; 128eadff302SIvan T. Ivanov 129eadff302SIvan T. Ivanov /** 130eadff302SIvan T. Ivanov * struct pmic_gpio_pad - keep current GPIO settings 131eadff302SIvan T. Ivanov * @base: Address base in SPMI device. 132eadff302SIvan T. Ivanov * @is_enabled: Set to false when GPIO should be put in high Z state. 133eadff302SIvan T. Ivanov * @out_value: Cached pin output value 134eadff302SIvan T. Ivanov * @have_buffer: Set to true if GPIO output could be configured in push-pull, 135eadff302SIvan T. Ivanov * open-drain or open-source mode. 136eadff302SIvan T. Ivanov * @output_enabled: Set to true if GPIO output logic is enabled. 137eadff302SIvan T. Ivanov * @input_enabled: Set to true if GPIO input buffer logic is enabled. 138d7b5f5ccSFenglin Wu * @analog_pass: Set to true if GPIO is in analog-pass-through mode. 139d7b5f5ccSFenglin Wu * @lv_mv_type: Set to true if GPIO subtype is GPIO_LV(0x10) or GPIO_MV(0x11). 140eadff302SIvan T. Ivanov * @num_sources: Number of power-sources supported by this GPIO. 141eadff302SIvan T. Ivanov * @power_source: Current power-source used. 142eadff302SIvan T. Ivanov * @buffer_type: Push-pull, open-drain or open-source. 143eadff302SIvan T. Ivanov * @pullup: Constant current which flow trough GPIO output buffer. 144eadff302SIvan T. Ivanov * @strength: No, Low, Medium, High 145eadff302SIvan T. Ivanov * @function: See pmic_gpio_functions[] 146d7b5f5ccSFenglin Wu * @atest: the ATEST selection for GPIO analog-pass-through mode 147223463fcSFenglin Wu * @dtest_buffer: the DTEST buffer selection for digital input mode. 148eadff302SIvan T. Ivanov */ 149eadff302SIvan T. Ivanov struct pmic_gpio_pad { 150eadff302SIvan T. Ivanov u16 base; 151eadff302SIvan T. Ivanov bool is_enabled; 152eadff302SIvan T. Ivanov bool out_value; 153eadff302SIvan T. Ivanov bool have_buffer; 154eadff302SIvan T. Ivanov bool output_enabled; 155eadff302SIvan T. Ivanov bool input_enabled; 156d7b5f5ccSFenglin Wu bool analog_pass; 157d7b5f5ccSFenglin Wu bool lv_mv_type; 158eadff302SIvan T. Ivanov unsigned int num_sources; 159eadff302SIvan T. Ivanov unsigned int power_source; 160eadff302SIvan T. Ivanov unsigned int buffer_type; 161eadff302SIvan T. Ivanov unsigned int pullup; 162eadff302SIvan T. Ivanov unsigned int strength; 163eadff302SIvan T. Ivanov unsigned int function; 164d7b5f5ccSFenglin Wu unsigned int atest; 165223463fcSFenglin Wu unsigned int dtest_buffer; 166eadff302SIvan T. Ivanov }; 167eadff302SIvan T. Ivanov 168eadff302SIvan T. Ivanov struct pmic_gpio_state { 169eadff302SIvan T. Ivanov struct device *dev; 170eadff302SIvan T. Ivanov struct regmap *map; 171eadff302SIvan T. Ivanov struct pinctrl_dev *ctrl; 172eadff302SIvan T. Ivanov struct gpio_chip chip; 173ca69e2d1SBrian Masney struct fwnode_handle *fwnode; 174ca69e2d1SBrian Masney struct irq_domain *domain; 175eadff302SIvan T. Ivanov }; 176eadff302SIvan T. Ivanov 177f684e4acSLinus Walleij static const struct pinconf_generic_params pmic_gpio_bindings[] = { 1787382b623SSoren Brinkmann {"qcom,pull-up-strength", PMIC_GPIO_CONF_PULL_UP, 0}, 1797382b623SSoren Brinkmann {"qcom,drive-strength", PMIC_GPIO_CONF_STRENGTH, 0}, 180d7b5f5ccSFenglin Wu {"qcom,atest", PMIC_GPIO_CONF_ATEST, 0}, 181d7b5f5ccSFenglin Wu {"qcom,analog-pass", PMIC_GPIO_CONF_ANALOG_PASS, 0}, 182223463fcSFenglin Wu {"qcom,dtest-buffer", PMIC_GPIO_CONF_DTEST_BUFFER, 0}, 183eadff302SIvan T. Ivanov }; 184eadff302SIvan T. Ivanov 1854f06266aSArnd Bergmann #ifdef CONFIG_DEBUG_FS 1867382b623SSoren Brinkmann static const struct pin_config_item pmic_conf_items[ARRAY_SIZE(pmic_gpio_bindings)] = { 1877382b623SSoren Brinkmann PCONFDUMP(PMIC_GPIO_CONF_PULL_UP, "pull up strength", NULL, true), 1887382b623SSoren Brinkmann PCONFDUMP(PMIC_GPIO_CONF_STRENGTH, "drive-strength", NULL, true), 189d7b5f5ccSFenglin Wu PCONFDUMP(PMIC_GPIO_CONF_ATEST, "atest", NULL, true), 190d7b5f5ccSFenglin Wu PCONFDUMP(PMIC_GPIO_CONF_ANALOG_PASS, "analog-pass", NULL, true), 191223463fcSFenglin Wu PCONFDUMP(PMIC_GPIO_CONF_DTEST_BUFFER, "dtest-buffer", NULL, true), 192eadff302SIvan T. Ivanov }; 1934f06266aSArnd Bergmann #endif 194eadff302SIvan T. Ivanov 195eadff302SIvan T. Ivanov static const char *const pmic_gpio_groups[] = { 196eadff302SIvan T. Ivanov "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", "gpio8", 197eadff302SIvan T. Ivanov "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", "gpio15", 198eadff302SIvan T. Ivanov "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22", 199eadff302SIvan T. Ivanov "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29", 200eadff302SIvan T. Ivanov "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36", 201eadff302SIvan T. Ivanov }; 202eadff302SIvan T. Ivanov 203eadff302SIvan T. Ivanov static const char *const pmic_gpio_functions[] = { 204d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_NORMAL] = PMIC_GPIO_FUNC_NORMAL, 205d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_PAIRED] = PMIC_GPIO_FUNC_PAIRED, 206d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_FUNC1] = PMIC_GPIO_FUNC_FUNC1, 207d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_FUNC2] = PMIC_GPIO_FUNC_FUNC2, 208d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_FUNC3] = PMIC_GPIO_FUNC_FUNC3, 209d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_FUNC4] = PMIC_GPIO_FUNC_FUNC4, 210d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_DTEST1] = PMIC_GPIO_FUNC_DTEST1, 211d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_DTEST2] = PMIC_GPIO_FUNC_DTEST2, 212d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_DTEST3] = PMIC_GPIO_FUNC_DTEST3, 213d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_DTEST4] = PMIC_GPIO_FUNC_DTEST4, 214eadff302SIvan T. Ivanov }; 215eadff302SIvan T. Ivanov 216eadff302SIvan T. Ivanov static int pmic_gpio_read(struct pmic_gpio_state *state, 217eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad, unsigned int addr) 218eadff302SIvan T. Ivanov { 219eadff302SIvan T. Ivanov unsigned int val; 220eadff302SIvan T. Ivanov int ret; 221eadff302SIvan T. Ivanov 222eadff302SIvan T. Ivanov ret = regmap_read(state->map, pad->base + addr, &val); 223eadff302SIvan T. Ivanov if (ret < 0) 224eadff302SIvan T. Ivanov dev_err(state->dev, "read 0x%x failed\n", addr); 225eadff302SIvan T. Ivanov else 226eadff302SIvan T. Ivanov ret = val; 227eadff302SIvan T. Ivanov 228eadff302SIvan T. Ivanov return ret; 229eadff302SIvan T. Ivanov } 230eadff302SIvan T. Ivanov 231eadff302SIvan T. Ivanov static int pmic_gpio_write(struct pmic_gpio_state *state, 232eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad, unsigned int addr, 233eadff302SIvan T. Ivanov unsigned int val) 234eadff302SIvan T. Ivanov { 235eadff302SIvan T. Ivanov int ret; 236eadff302SIvan T. Ivanov 237eadff302SIvan T. Ivanov ret = regmap_write(state->map, pad->base + addr, val); 238eadff302SIvan T. Ivanov if (ret < 0) 239eadff302SIvan T. Ivanov dev_err(state->dev, "write 0x%x failed\n", addr); 240eadff302SIvan T. Ivanov 241eadff302SIvan T. Ivanov return ret; 242eadff302SIvan T. Ivanov } 243eadff302SIvan T. Ivanov 244eadff302SIvan T. Ivanov static int pmic_gpio_get_groups_count(struct pinctrl_dev *pctldev) 245eadff302SIvan T. Ivanov { 246eadff302SIvan T. Ivanov /* Every PIN is a group */ 247eadff302SIvan T. Ivanov return pctldev->desc->npins; 248eadff302SIvan T. Ivanov } 249eadff302SIvan T. Ivanov 250eadff302SIvan T. Ivanov static const char *pmic_gpio_get_group_name(struct pinctrl_dev *pctldev, 251eadff302SIvan T. Ivanov unsigned pin) 252eadff302SIvan T. Ivanov { 253eadff302SIvan T. Ivanov return pctldev->desc->pins[pin].name; 254eadff302SIvan T. Ivanov } 255eadff302SIvan T. Ivanov 256eadff302SIvan T. Ivanov static int pmic_gpio_get_group_pins(struct pinctrl_dev *pctldev, unsigned pin, 257eadff302SIvan T. Ivanov const unsigned **pins, unsigned *num_pins) 258eadff302SIvan T. Ivanov { 259eadff302SIvan T. Ivanov *pins = &pctldev->desc->pins[pin].number; 260eadff302SIvan T. Ivanov *num_pins = 1; 261eadff302SIvan T. Ivanov return 0; 262eadff302SIvan T. Ivanov } 263eadff302SIvan T. Ivanov 264eadff302SIvan T. Ivanov static const struct pinctrl_ops pmic_gpio_pinctrl_ops = { 265eadff302SIvan T. Ivanov .get_groups_count = pmic_gpio_get_groups_count, 266eadff302SIvan T. Ivanov .get_group_name = pmic_gpio_get_group_name, 267eadff302SIvan T. Ivanov .get_group_pins = pmic_gpio_get_group_pins, 2687382b623SSoren Brinkmann .dt_node_to_map = pinconf_generic_dt_node_to_map_group, 269d32f7fd3SIrina Tirdea .dt_free_map = pinctrl_utils_free_map, 270eadff302SIvan T. Ivanov }; 271eadff302SIvan T. Ivanov 272eadff302SIvan T. Ivanov static int pmic_gpio_get_functions_count(struct pinctrl_dev *pctldev) 273eadff302SIvan T. Ivanov { 274eadff302SIvan T. Ivanov return ARRAY_SIZE(pmic_gpio_functions); 275eadff302SIvan T. Ivanov } 276eadff302SIvan T. Ivanov 277eadff302SIvan T. Ivanov static const char *pmic_gpio_get_function_name(struct pinctrl_dev *pctldev, 278eadff302SIvan T. Ivanov unsigned function) 279eadff302SIvan T. Ivanov { 280eadff302SIvan T. Ivanov return pmic_gpio_functions[function]; 281eadff302SIvan T. Ivanov } 282eadff302SIvan T. Ivanov 283eadff302SIvan T. Ivanov static int pmic_gpio_get_function_groups(struct pinctrl_dev *pctldev, 284eadff302SIvan T. Ivanov unsigned function, 285eadff302SIvan T. Ivanov const char *const **groups, 286eadff302SIvan T. Ivanov unsigned *const num_qgroups) 287eadff302SIvan T. Ivanov { 288eadff302SIvan T. Ivanov *groups = pmic_gpio_groups; 289eadff302SIvan T. Ivanov *num_qgroups = pctldev->desc->npins; 290eadff302SIvan T. Ivanov return 0; 291eadff302SIvan T. Ivanov } 292eadff302SIvan T. Ivanov 293eadff302SIvan T. Ivanov static int pmic_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned function, 294eadff302SIvan T. Ivanov unsigned pin) 295eadff302SIvan T. Ivanov { 296eadff302SIvan T. Ivanov struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev); 297eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad; 298eadff302SIvan T. Ivanov unsigned int val; 299eadff302SIvan T. Ivanov int ret; 300eadff302SIvan T. Ivanov 301d7b5f5ccSFenglin Wu if (function > PMIC_GPIO_FUNC_INDEX_DTEST4) { 302d7b5f5ccSFenglin Wu pr_err("function: %d is not defined\n", function); 303d7b5f5ccSFenglin Wu return -EINVAL; 304d7b5f5ccSFenglin Wu } 305d7b5f5ccSFenglin Wu 306eadff302SIvan T. Ivanov pad = pctldev->desc->pins[pin].drv_data; 307d7b5f5ccSFenglin Wu /* 308d7b5f5ccSFenglin Wu * Non-LV/MV subtypes only support 2 special functions, 309d7b5f5ccSFenglin Wu * offsetting the dtestx function values by 2 310d7b5f5ccSFenglin Wu */ 311d7b5f5ccSFenglin Wu if (!pad->lv_mv_type) { 312d7b5f5ccSFenglin Wu if (function == PMIC_GPIO_FUNC_INDEX_FUNC3 || 313d7b5f5ccSFenglin Wu function == PMIC_GPIO_FUNC_INDEX_FUNC4) { 314d7b5f5ccSFenglin Wu pr_err("LV/MV subtype doesn't have func3/func4\n"); 315d7b5f5ccSFenglin Wu return -EINVAL; 316d7b5f5ccSFenglin Wu } 317d7b5f5ccSFenglin Wu if (function >= PMIC_GPIO_FUNC_INDEX_DTEST1) 318d7b5f5ccSFenglin Wu function -= (PMIC_GPIO_FUNC_INDEX_DTEST1 - 319d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_FUNC3); 320d7b5f5ccSFenglin Wu } 321eadff302SIvan T. Ivanov 322eadff302SIvan T. Ivanov pad->function = function; 323eadff302SIvan T. Ivanov 324d7b5f5ccSFenglin Wu if (pad->analog_pass) 325d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_ANALOG_PASS_THRU; 326d7b5f5ccSFenglin Wu else if (pad->output_enabled && pad->input_enabled) 327d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT; 328d7b5f5ccSFenglin Wu else if (pad->output_enabled) 329d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_OUTPUT; 330eadff302SIvan T. Ivanov else 331d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_INPUT; 332eadff302SIvan T. Ivanov 333d7b5f5ccSFenglin Wu if (pad->lv_mv_type) { 334d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 335d7b5f5ccSFenglin Wu PMIC_GPIO_REG_MODE_CTL, val); 336d7b5f5ccSFenglin Wu if (ret < 0) 337d7b5f5ccSFenglin Wu return ret; 338d7b5f5ccSFenglin Wu 339d7b5f5ccSFenglin Wu val = pad->atest - 1; 340d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 341d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL, val); 342d7b5f5ccSFenglin Wu if (ret < 0) 343d7b5f5ccSFenglin Wu return ret; 344d7b5f5ccSFenglin Wu 345d7b5f5ccSFenglin Wu val = pad->out_value 346d7b5f5ccSFenglin Wu << PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT; 347d7b5f5ccSFenglin Wu val |= pad->function 348d7b5f5ccSFenglin Wu & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK; 349d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 350d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL, val); 351d7b5f5ccSFenglin Wu if (ret < 0) 352d7b5f5ccSFenglin Wu return ret; 353d7b5f5ccSFenglin Wu } else { 354dc391502SIvan T. Ivanov val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT; 355eadff302SIvan T. Ivanov val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT; 356eadff302SIvan T. Ivanov val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT; 357eadff302SIvan T. Ivanov 358eadff302SIvan T. Ivanov ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); 359eadff302SIvan T. Ivanov if (ret < 0) 360eadff302SIvan T. Ivanov return ret; 361d7b5f5ccSFenglin Wu } 362eadff302SIvan T. Ivanov 363eadff302SIvan T. Ivanov val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT; 364eadff302SIvan T. Ivanov 365eadff302SIvan T. Ivanov return pmic_gpio_write(state, pad, PMIC_GPIO_REG_EN_CTL, val); 366eadff302SIvan T. Ivanov } 367eadff302SIvan T. Ivanov 368eadff302SIvan T. Ivanov static const struct pinmux_ops pmic_gpio_pinmux_ops = { 369eadff302SIvan T. Ivanov .get_functions_count = pmic_gpio_get_functions_count, 370eadff302SIvan T. Ivanov .get_function_name = pmic_gpio_get_function_name, 371eadff302SIvan T. Ivanov .get_function_groups = pmic_gpio_get_function_groups, 372eadff302SIvan T. Ivanov .set_mux = pmic_gpio_set_mux, 373eadff302SIvan T. Ivanov }; 374eadff302SIvan T. Ivanov 375eadff302SIvan T. Ivanov static int pmic_gpio_config_get(struct pinctrl_dev *pctldev, 376eadff302SIvan T. Ivanov unsigned int pin, unsigned long *config) 377eadff302SIvan T. Ivanov { 378eadff302SIvan T. Ivanov unsigned param = pinconf_to_config_param(*config); 379eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad; 380eadff302SIvan T. Ivanov unsigned arg; 381eadff302SIvan T. Ivanov 382eadff302SIvan T. Ivanov pad = pctldev->desc->pins[pin].drv_data; 383eadff302SIvan T. Ivanov 384eadff302SIvan T. Ivanov switch (param) { 385eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_PUSH_PULL: 3861cf86bc2SDouglas Anderson if (pad->buffer_type != PMIC_GPIO_OUT_BUF_CMOS) 3871cf86bc2SDouglas Anderson return -EINVAL; 3881cf86bc2SDouglas Anderson arg = 1; 389eadff302SIvan T. Ivanov break; 390eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_OPEN_DRAIN: 3911cf86bc2SDouglas Anderson if (pad->buffer_type != PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS) 3921cf86bc2SDouglas Anderson return -EINVAL; 3931cf86bc2SDouglas Anderson arg = 1; 394eadff302SIvan T. Ivanov break; 395eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_OPEN_SOURCE: 3961cf86bc2SDouglas Anderson if (pad->buffer_type != PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS) 3971cf86bc2SDouglas Anderson return -EINVAL; 3981cf86bc2SDouglas Anderson arg = 1; 399eadff302SIvan T. Ivanov break; 400eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_PULL_DOWN: 4011cf86bc2SDouglas Anderson if (pad->pullup != PMIC_GPIO_PULL_DOWN) 4021cf86bc2SDouglas Anderson return -EINVAL; 4031cf86bc2SDouglas Anderson arg = 1; 404eadff302SIvan T. Ivanov break; 405eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_DISABLE: 4061cf86bc2SDouglas Anderson if (pad->pullup != PMIC_GPIO_PULL_DISABLE) 4071cf86bc2SDouglas Anderson return -EINVAL; 4081cf86bc2SDouglas Anderson arg = 1; 409eadff302SIvan T. Ivanov break; 410eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_PULL_UP: 4111cf86bc2SDouglas Anderson if (pad->pullup != PMIC_GPIO_PULL_UP_30) 4121cf86bc2SDouglas Anderson return -EINVAL; 4131cf86bc2SDouglas Anderson arg = 1; 414eadff302SIvan T. Ivanov break; 415eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: 4161cf86bc2SDouglas Anderson if (pad->is_enabled) 4171cf86bc2SDouglas Anderson return -EINVAL; 4181cf86bc2SDouglas Anderson arg = 1; 419eadff302SIvan T. Ivanov break; 420eadff302SIvan T. Ivanov case PIN_CONFIG_POWER_SOURCE: 421eadff302SIvan T. Ivanov arg = pad->power_source; 422eadff302SIvan T. Ivanov break; 423eadff302SIvan T. Ivanov case PIN_CONFIG_INPUT_ENABLE: 4241cf86bc2SDouglas Anderson if (!pad->input_enabled) 4251cf86bc2SDouglas Anderson return -EINVAL; 4261cf86bc2SDouglas Anderson arg = 1; 427eadff302SIvan T. Ivanov break; 428eadff302SIvan T. Ivanov case PIN_CONFIG_OUTPUT: 429eadff302SIvan T. Ivanov arg = pad->out_value; 430eadff302SIvan T. Ivanov break; 431eadff302SIvan T. Ivanov case PMIC_GPIO_CONF_PULL_UP: 432eadff302SIvan T. Ivanov arg = pad->pullup; 433eadff302SIvan T. Ivanov break; 434eadff302SIvan T. Ivanov case PMIC_GPIO_CONF_STRENGTH: 435eadff302SIvan T. Ivanov arg = pad->strength; 436eadff302SIvan T. Ivanov break; 437d7b5f5ccSFenglin Wu case PMIC_GPIO_CONF_ATEST: 438d7b5f5ccSFenglin Wu arg = pad->atest; 439d7b5f5ccSFenglin Wu break; 440d7b5f5ccSFenglin Wu case PMIC_GPIO_CONF_ANALOG_PASS: 441d7b5f5ccSFenglin Wu arg = pad->analog_pass; 442d7b5f5ccSFenglin Wu break; 443223463fcSFenglin Wu case PMIC_GPIO_CONF_DTEST_BUFFER: 444223463fcSFenglin Wu arg = pad->dtest_buffer; 445223463fcSFenglin Wu break; 446eadff302SIvan T. Ivanov default: 447eadff302SIvan T. Ivanov return -EINVAL; 448eadff302SIvan T. Ivanov } 449eadff302SIvan T. Ivanov 450eadff302SIvan T. Ivanov *config = pinconf_to_config_packed(param, arg); 451eadff302SIvan T. Ivanov return 0; 452eadff302SIvan T. Ivanov } 453eadff302SIvan T. Ivanov 454eadff302SIvan T. Ivanov static int pmic_gpio_config_set(struct pinctrl_dev *pctldev, unsigned int pin, 455eadff302SIvan T. Ivanov unsigned long *configs, unsigned nconfs) 456eadff302SIvan T. Ivanov { 457eadff302SIvan T. Ivanov struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev); 458eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad; 459eadff302SIvan T. Ivanov unsigned param, arg; 460eadff302SIvan T. Ivanov unsigned int val; 461eadff302SIvan T. Ivanov int i, ret; 462eadff302SIvan T. Ivanov 463eadff302SIvan T. Ivanov pad = pctldev->desc->pins[pin].drv_data; 464eadff302SIvan T. Ivanov 4656cb74f44SFenglin Wu pad->is_enabled = true; 466eadff302SIvan T. Ivanov for (i = 0; i < nconfs; i++) { 467eadff302SIvan T. Ivanov param = pinconf_to_config_param(configs[i]); 468eadff302SIvan T. Ivanov arg = pinconf_to_config_argument(configs[i]); 469eadff302SIvan T. Ivanov 470eadff302SIvan T. Ivanov switch (param) { 471eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_PUSH_PULL: 472eadff302SIvan T. Ivanov pad->buffer_type = PMIC_GPIO_OUT_BUF_CMOS; 473eadff302SIvan T. Ivanov break; 474eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_OPEN_DRAIN: 475eadff302SIvan T. Ivanov if (!pad->have_buffer) 476eadff302SIvan T. Ivanov return -EINVAL; 477eadff302SIvan T. Ivanov pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS; 478eadff302SIvan T. Ivanov break; 479eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_OPEN_SOURCE: 480eadff302SIvan T. Ivanov if (!pad->have_buffer) 481eadff302SIvan T. Ivanov return -EINVAL; 482eadff302SIvan T. Ivanov pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS; 483eadff302SIvan T. Ivanov break; 484eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_DISABLE: 485eadff302SIvan T. Ivanov pad->pullup = PMIC_GPIO_PULL_DISABLE; 486eadff302SIvan T. Ivanov break; 487eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_PULL_UP: 488eadff302SIvan T. Ivanov pad->pullup = PMIC_GPIO_PULL_UP_30; 489eadff302SIvan T. Ivanov break; 490eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_PULL_DOWN: 491eadff302SIvan T. Ivanov if (arg) 492eadff302SIvan T. Ivanov pad->pullup = PMIC_GPIO_PULL_DOWN; 493eadff302SIvan T. Ivanov else 494eadff302SIvan T. Ivanov pad->pullup = PMIC_GPIO_PULL_DISABLE; 495eadff302SIvan T. Ivanov break; 496eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: 497eadff302SIvan T. Ivanov pad->is_enabled = false; 498eadff302SIvan T. Ivanov break; 499eadff302SIvan T. Ivanov case PIN_CONFIG_POWER_SOURCE: 5004e83ac4cSFenglin Wu if (arg >= pad->num_sources) 501eadff302SIvan T. Ivanov return -EINVAL; 502eadff302SIvan T. Ivanov pad->power_source = arg; 503eadff302SIvan T. Ivanov break; 504eadff302SIvan T. Ivanov case PIN_CONFIG_INPUT_ENABLE: 505eadff302SIvan T. Ivanov pad->input_enabled = arg ? true : false; 506eadff302SIvan T. Ivanov break; 507eadff302SIvan T. Ivanov case PIN_CONFIG_OUTPUT: 508eadff302SIvan T. Ivanov pad->output_enabled = true; 509eadff302SIvan T. Ivanov pad->out_value = arg; 510eadff302SIvan T. Ivanov break; 511eadff302SIvan T. Ivanov case PMIC_GPIO_CONF_PULL_UP: 512eadff302SIvan T. Ivanov if (arg > PMIC_GPIO_PULL_UP_1P5_30) 513eadff302SIvan T. Ivanov return -EINVAL; 514eadff302SIvan T. Ivanov pad->pullup = arg; 515eadff302SIvan T. Ivanov break; 516eadff302SIvan T. Ivanov case PMIC_GPIO_CONF_STRENGTH: 517eadff302SIvan T. Ivanov if (arg > PMIC_GPIO_STRENGTH_LOW) 518eadff302SIvan T. Ivanov return -EINVAL; 519eadff302SIvan T. Ivanov pad->strength = arg; 520eadff302SIvan T. Ivanov break; 521d7b5f5ccSFenglin Wu case PMIC_GPIO_CONF_ATEST: 522d7b5f5ccSFenglin Wu if (!pad->lv_mv_type || arg > 4) 523d7b5f5ccSFenglin Wu return -EINVAL; 524d7b5f5ccSFenglin Wu pad->atest = arg; 525d7b5f5ccSFenglin Wu break; 526d7b5f5ccSFenglin Wu case PMIC_GPIO_CONF_ANALOG_PASS: 527d7b5f5ccSFenglin Wu if (!pad->lv_mv_type) 528d7b5f5ccSFenglin Wu return -EINVAL; 529d7b5f5ccSFenglin Wu pad->analog_pass = true; 530d7b5f5ccSFenglin Wu break; 531223463fcSFenglin Wu case PMIC_GPIO_CONF_DTEST_BUFFER: 532223463fcSFenglin Wu if (arg > 4) 533223463fcSFenglin Wu return -EINVAL; 534223463fcSFenglin Wu pad->dtest_buffer = arg; 535223463fcSFenglin Wu break; 536eadff302SIvan T. Ivanov default: 537eadff302SIvan T. Ivanov return -EINVAL; 538eadff302SIvan T. Ivanov } 539eadff302SIvan T. Ivanov } 540eadff302SIvan T. Ivanov 541eadff302SIvan T. Ivanov val = pad->power_source << PMIC_GPIO_REG_VIN_SHIFT; 542eadff302SIvan T. Ivanov 543eadff302SIvan T. Ivanov ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL, val); 544eadff302SIvan T. Ivanov if (ret < 0) 545eadff302SIvan T. Ivanov return ret; 546eadff302SIvan T. Ivanov 547eadff302SIvan T. Ivanov val = pad->pullup << PMIC_GPIO_REG_PULL_SHIFT; 548eadff302SIvan T. Ivanov 549eadff302SIvan T. Ivanov ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_PULL_CTL, val); 550eadff302SIvan T. Ivanov if (ret < 0) 551eadff302SIvan T. Ivanov return ret; 552eadff302SIvan T. Ivanov 553eadff302SIvan T. Ivanov val = pad->buffer_type << PMIC_GPIO_REG_OUT_TYPE_SHIFT; 554982df6aeSIvan T. Ivanov val |= pad->strength << PMIC_GPIO_REG_OUT_STRENGTH_SHIFT; 555eadff302SIvan T. Ivanov 556eadff302SIvan T. Ivanov ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL, val); 557eadff302SIvan T. Ivanov if (ret < 0) 558eadff302SIvan T. Ivanov return ret; 559eadff302SIvan T. Ivanov 560223463fcSFenglin Wu if (pad->dtest_buffer == 0) { 561223463fcSFenglin Wu val = 0; 562223463fcSFenglin Wu } else { 563223463fcSFenglin Wu if (pad->lv_mv_type) { 564223463fcSFenglin Wu val = pad->dtest_buffer - 1; 565223463fcSFenglin Wu val |= PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN; 566223463fcSFenglin Wu } else { 567223463fcSFenglin Wu val = BIT(pad->dtest_buffer - 1); 568223463fcSFenglin Wu } 569223463fcSFenglin Wu } 570223463fcSFenglin Wu ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_IN_CTL, val); 571223463fcSFenglin Wu if (ret < 0) 572223463fcSFenglin Wu return ret; 573223463fcSFenglin Wu 574d7b5f5ccSFenglin Wu if (pad->analog_pass) 575d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_ANALOG_PASS_THRU; 576d7b5f5ccSFenglin Wu else if (pad->output_enabled && pad->input_enabled) 577d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT; 578d7b5f5ccSFenglin Wu else if (pad->output_enabled) 579d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_OUTPUT; 580eadff302SIvan T. Ivanov else 581d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_INPUT; 582eadff302SIvan T. Ivanov 583d7b5f5ccSFenglin Wu if (pad->lv_mv_type) { 584d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 585d7b5f5ccSFenglin Wu PMIC_GPIO_REG_MODE_CTL, val); 586d7b5f5ccSFenglin Wu if (ret < 0) 587d7b5f5ccSFenglin Wu return ret; 588d7b5f5ccSFenglin Wu 589d7b5f5ccSFenglin Wu val = pad->atest - 1; 590d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 591d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL, val); 592d7b5f5ccSFenglin Wu if (ret < 0) 593d7b5f5ccSFenglin Wu return ret; 594d7b5f5ccSFenglin Wu 595d7b5f5ccSFenglin Wu val = pad->out_value 596d7b5f5ccSFenglin Wu << PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT; 597d7b5f5ccSFenglin Wu val |= pad->function 598d7b5f5ccSFenglin Wu & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK; 599d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 600d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL, val); 601d7b5f5ccSFenglin Wu if (ret < 0) 602d7b5f5ccSFenglin Wu return ret; 603d7b5f5ccSFenglin Wu } else { 604eadff302SIvan T. Ivanov val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT; 605eadff302SIvan T. Ivanov val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT; 606eadff302SIvan T. Ivanov val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT; 607eadff302SIvan T. Ivanov 608d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); 609d7b5f5ccSFenglin Wu if (ret < 0) 610d7b5f5ccSFenglin Wu return ret; 611d7b5f5ccSFenglin Wu } 612d7b5f5ccSFenglin Wu 6136cb74f44SFenglin Wu val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT; 6146cb74f44SFenglin Wu 6156cb74f44SFenglin Wu ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_EN_CTL, val); 6166cb74f44SFenglin Wu 617d7b5f5ccSFenglin Wu return ret; 618eadff302SIvan T. Ivanov } 619eadff302SIvan T. Ivanov 620eadff302SIvan T. Ivanov static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev, 621eadff302SIvan T. Ivanov struct seq_file *s, unsigned pin) 622eadff302SIvan T. Ivanov { 623eadff302SIvan T. Ivanov struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev); 624eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad; 625d7b5f5ccSFenglin Wu int ret, val, function; 626eadff302SIvan T. Ivanov 627eadff302SIvan T. Ivanov static const char *const biases[] = { 628eadff302SIvan T. Ivanov "pull-up 30uA", "pull-up 1.5uA", "pull-up 31.5uA", 629eadff302SIvan T. Ivanov "pull-up 1.5uA + 30uA boost", "pull-down 10uA", "no pull" 630eadff302SIvan T. Ivanov }; 631eadff302SIvan T. Ivanov static const char *const buffer_types[] = { 632eadff302SIvan T. Ivanov "push-pull", "open-drain", "open-source" 633eadff302SIvan T. Ivanov }; 634eadff302SIvan T. Ivanov static const char *const strengths[] = { 635eadff302SIvan T. Ivanov "no", "high", "medium", "low" 636eadff302SIvan T. Ivanov }; 637eadff302SIvan T. Ivanov 638eadff302SIvan T. Ivanov pad = pctldev->desc->pins[pin].drv_data; 639eadff302SIvan T. Ivanov 640eadff302SIvan T. Ivanov seq_printf(s, " gpio%-2d:", pin + PMIC_GPIO_PHYSICAL_OFFSET); 641eadff302SIvan T. Ivanov 642eadff302SIvan T. Ivanov val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_EN_CTL); 643eadff302SIvan T. Ivanov 644eadff302SIvan T. Ivanov if (val < 0 || !(val >> PMIC_GPIO_REG_MASTER_EN_SHIFT)) { 645eadff302SIvan T. Ivanov seq_puts(s, " ---"); 646eadff302SIvan T. Ivanov } else { 64724a66618SIvan T. Ivanov if (pad->input_enabled) { 648eadff302SIvan T. Ivanov ret = pmic_gpio_read(state, pad, PMIC_MPP_REG_RT_STS); 64924a66618SIvan T. Ivanov if (ret < 0) 65024a66618SIvan T. Ivanov return; 65124a66618SIvan T. Ivanov 652eadff302SIvan T. Ivanov ret &= PMIC_MPP_REG_RT_STS_VAL_MASK; 653eadff302SIvan T. Ivanov pad->out_value = ret; 654eadff302SIvan T. Ivanov } 655d7b5f5ccSFenglin Wu /* 656d7b5f5ccSFenglin Wu * For the non-LV/MV subtypes only 2 special functions are 657d7b5f5ccSFenglin Wu * available, offsetting the dtest function values by 2. 658d7b5f5ccSFenglin Wu */ 659d7b5f5ccSFenglin Wu function = pad->function; 660d7b5f5ccSFenglin Wu if (!pad->lv_mv_type && 661d7b5f5ccSFenglin Wu pad->function >= PMIC_GPIO_FUNC_INDEX_FUNC3) 662d7b5f5ccSFenglin Wu function += PMIC_GPIO_FUNC_INDEX_DTEST1 - 663d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_FUNC3; 664eadff302SIvan T. Ivanov 665d7b5f5ccSFenglin Wu if (pad->analog_pass) 666d7b5f5ccSFenglin Wu seq_puts(s, " analog-pass"); 667d7b5f5ccSFenglin Wu else 668d7b5f5ccSFenglin Wu seq_printf(s, " %-4s", 669d7b5f5ccSFenglin Wu pad->output_enabled ? "out" : "in"); 670202ba5ebSBjorn Andersson seq_printf(s, " %-4s", pad->out_value ? "high" : "low"); 671d7b5f5ccSFenglin Wu seq_printf(s, " %-7s", pmic_gpio_functions[function]); 672eadff302SIvan T. Ivanov seq_printf(s, " vin-%d", pad->power_source); 673eadff302SIvan T. Ivanov seq_printf(s, " %-27s", biases[pad->pullup]); 674eadff302SIvan T. Ivanov seq_printf(s, " %-10s", buffer_types[pad->buffer_type]); 675eadff302SIvan T. Ivanov seq_printf(s, " %-7s", strengths[pad->strength]); 676d7b5f5ccSFenglin Wu seq_printf(s, " atest-%d", pad->atest); 677223463fcSFenglin Wu seq_printf(s, " dtest-%d", pad->dtest_buffer); 678eadff302SIvan T. Ivanov } 679eadff302SIvan T. Ivanov } 680eadff302SIvan T. Ivanov 681eadff302SIvan T. Ivanov static const struct pinconf_ops pmic_gpio_pinconf_ops = { 6827382b623SSoren Brinkmann .is_generic = true, 683eadff302SIvan T. Ivanov .pin_config_group_get = pmic_gpio_config_get, 684eadff302SIvan T. Ivanov .pin_config_group_set = pmic_gpio_config_set, 685eadff302SIvan T. Ivanov .pin_config_group_dbg_show = pmic_gpio_config_dbg_show, 686eadff302SIvan T. Ivanov }; 687eadff302SIvan T. Ivanov 688eadff302SIvan T. Ivanov static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned pin) 689eadff302SIvan T. Ivanov { 690c52d9df1SLinus Walleij struct pmic_gpio_state *state = gpiochip_get_data(chip); 691eadff302SIvan T. Ivanov unsigned long config; 692eadff302SIvan T. Ivanov 693eadff302SIvan T. Ivanov config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1); 694eadff302SIvan T. Ivanov 695eadff302SIvan T. Ivanov return pmic_gpio_config_set(state->ctrl, pin, &config, 1); 696eadff302SIvan T. Ivanov } 697eadff302SIvan T. Ivanov 698eadff302SIvan T. Ivanov static int pmic_gpio_direction_output(struct gpio_chip *chip, 699eadff302SIvan T. Ivanov unsigned pin, int val) 700eadff302SIvan T. Ivanov { 701c52d9df1SLinus Walleij struct pmic_gpio_state *state = gpiochip_get_data(chip); 702eadff302SIvan T. Ivanov unsigned long config; 703eadff302SIvan T. Ivanov 704eadff302SIvan T. Ivanov config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val); 705eadff302SIvan T. Ivanov 706eadff302SIvan T. Ivanov return pmic_gpio_config_set(state->ctrl, pin, &config, 1); 707eadff302SIvan T. Ivanov } 708eadff302SIvan T. Ivanov 709eadff302SIvan T. Ivanov static int pmic_gpio_get(struct gpio_chip *chip, unsigned pin) 710eadff302SIvan T. Ivanov { 711c52d9df1SLinus Walleij struct pmic_gpio_state *state = gpiochip_get_data(chip); 712eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad; 713eadff302SIvan T. Ivanov int ret; 714eadff302SIvan T. Ivanov 715eadff302SIvan T. Ivanov pad = state->ctrl->desc->pins[pin].drv_data; 716eadff302SIvan T. Ivanov 717eadff302SIvan T. Ivanov if (!pad->is_enabled) 718eadff302SIvan T. Ivanov return -EINVAL; 719eadff302SIvan T. Ivanov 720eadff302SIvan T. Ivanov if (pad->input_enabled) { 721eadff302SIvan T. Ivanov ret = pmic_gpio_read(state, pad, PMIC_MPP_REG_RT_STS); 722eadff302SIvan T. Ivanov if (ret < 0) 723eadff302SIvan T. Ivanov return ret; 724eadff302SIvan T. Ivanov 725eadff302SIvan T. Ivanov pad->out_value = ret & PMIC_MPP_REG_RT_STS_VAL_MASK; 726eadff302SIvan T. Ivanov } 727eadff302SIvan T. Ivanov 72886c1a219SLinus Walleij return !!pad->out_value; 729eadff302SIvan T. Ivanov } 730eadff302SIvan T. Ivanov 731eadff302SIvan T. Ivanov static void pmic_gpio_set(struct gpio_chip *chip, unsigned pin, int value) 732eadff302SIvan T. Ivanov { 733c52d9df1SLinus Walleij struct pmic_gpio_state *state = gpiochip_get_data(chip); 734eadff302SIvan T. Ivanov unsigned long config; 735eadff302SIvan T. Ivanov 736eadff302SIvan T. Ivanov config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value); 737eadff302SIvan T. Ivanov 738eadff302SIvan T. Ivanov pmic_gpio_config_set(state->ctrl, pin, &config, 1); 739eadff302SIvan T. Ivanov } 740eadff302SIvan T. Ivanov 741eadff302SIvan T. Ivanov static int pmic_gpio_of_xlate(struct gpio_chip *chip, 742eadff302SIvan T. Ivanov const struct of_phandle_args *gpio_desc, 743eadff302SIvan T. Ivanov u32 *flags) 744eadff302SIvan T. Ivanov { 745eadff302SIvan T. Ivanov if (chip->of_gpio_n_cells < 2) 746eadff302SIvan T. Ivanov return -EINVAL; 747eadff302SIvan T. Ivanov 748eadff302SIvan T. Ivanov if (flags) 749eadff302SIvan T. Ivanov *flags = gpio_desc->args[1]; 750eadff302SIvan T. Ivanov 751eadff302SIvan T. Ivanov return gpio_desc->args[0] - PMIC_GPIO_PHYSICAL_OFFSET; 752eadff302SIvan T. Ivanov } 753eadff302SIvan T. Ivanov 754eadff302SIvan T. Ivanov static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned pin) 755eadff302SIvan T. Ivanov { 756c52d9df1SLinus Walleij struct pmic_gpio_state *state = gpiochip_get_data(chip); 757ca69e2d1SBrian Masney struct irq_fwspec fwspec; 758eadff302SIvan T. Ivanov 759ca69e2d1SBrian Masney fwspec.fwnode = state->fwnode; 760ca69e2d1SBrian Masney fwspec.param_count = 2; 761ca69e2d1SBrian Masney fwspec.param[0] = pin + PMIC_GPIO_PHYSICAL_OFFSET; 762ca69e2d1SBrian Masney /* 763ca69e2d1SBrian Masney * Set the type to a safe value temporarily. This will be overwritten 764ca69e2d1SBrian Masney * later with the proper value by irq_set_type. 765ca69e2d1SBrian Masney */ 766ca69e2d1SBrian Masney fwspec.param[1] = IRQ_TYPE_EDGE_RISING; 767eadff302SIvan T. Ivanov 768ca69e2d1SBrian Masney return irq_create_fwspec_mapping(&fwspec); 769eadff302SIvan T. Ivanov } 770eadff302SIvan T. Ivanov 771eadff302SIvan T. Ivanov static void pmic_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) 772eadff302SIvan T. Ivanov { 773c52d9df1SLinus Walleij struct pmic_gpio_state *state = gpiochip_get_data(chip); 774eadff302SIvan T. Ivanov unsigned i; 775eadff302SIvan T. Ivanov 776eadff302SIvan T. Ivanov for (i = 0; i < chip->ngpio; i++) { 777eadff302SIvan T. Ivanov pmic_gpio_config_dbg_show(state->ctrl, s, i); 778eadff302SIvan T. Ivanov seq_puts(s, "\n"); 779eadff302SIvan T. Ivanov } 780eadff302SIvan T. Ivanov } 781eadff302SIvan T. Ivanov 782eadff302SIvan T. Ivanov static const struct gpio_chip pmic_gpio_gpio_template = { 783eadff302SIvan T. Ivanov .direction_input = pmic_gpio_direction_input, 784eadff302SIvan T. Ivanov .direction_output = pmic_gpio_direction_output, 785eadff302SIvan T. Ivanov .get = pmic_gpio_get, 786eadff302SIvan T. Ivanov .set = pmic_gpio_set, 78798c85d58SJonas Gorski .request = gpiochip_generic_request, 78898c85d58SJonas Gorski .free = gpiochip_generic_free, 789eadff302SIvan T. Ivanov .of_xlate = pmic_gpio_of_xlate, 790eadff302SIvan T. Ivanov .to_irq = pmic_gpio_to_irq, 791eadff302SIvan T. Ivanov .dbg_show = pmic_gpio_dbg_show, 792eadff302SIvan T. Ivanov }; 793eadff302SIvan T. Ivanov 794eadff302SIvan T. Ivanov static int pmic_gpio_populate(struct pmic_gpio_state *state, 795eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad) 796eadff302SIvan T. Ivanov { 797eadff302SIvan T. Ivanov int type, subtype, val, dir; 798eadff302SIvan T. Ivanov 799eadff302SIvan T. Ivanov type = pmic_gpio_read(state, pad, PMIC_GPIO_REG_TYPE); 800eadff302SIvan T. Ivanov if (type < 0) 801eadff302SIvan T. Ivanov return type; 802eadff302SIvan T. Ivanov 803eadff302SIvan T. Ivanov if (type != PMIC_GPIO_TYPE) { 804eadff302SIvan T. Ivanov dev_err(state->dev, "incorrect block type 0x%x at 0x%x\n", 805eadff302SIvan T. Ivanov type, pad->base); 806eadff302SIvan T. Ivanov return -ENODEV; 807eadff302SIvan T. Ivanov } 808eadff302SIvan T. Ivanov 809eadff302SIvan T. Ivanov subtype = pmic_gpio_read(state, pad, PMIC_GPIO_REG_SUBTYPE); 810eadff302SIvan T. Ivanov if (subtype < 0) 811eadff302SIvan T. Ivanov return subtype; 812eadff302SIvan T. Ivanov 813eadff302SIvan T. Ivanov switch (subtype) { 814eadff302SIvan T. Ivanov case PMIC_GPIO_SUBTYPE_GPIO_4CH: 815eadff302SIvan T. Ivanov pad->have_buffer = true; 816eadff302SIvan T. Ivanov case PMIC_GPIO_SUBTYPE_GPIOC_4CH: 817eadff302SIvan T. Ivanov pad->num_sources = 4; 818eadff302SIvan T. Ivanov break; 819eadff302SIvan T. Ivanov case PMIC_GPIO_SUBTYPE_GPIO_8CH: 820eadff302SIvan T. Ivanov pad->have_buffer = true; 821eadff302SIvan T. Ivanov case PMIC_GPIO_SUBTYPE_GPIOC_8CH: 822eadff302SIvan T. Ivanov pad->num_sources = 8; 823eadff302SIvan T. Ivanov break; 824d7b5f5ccSFenglin Wu case PMIC_GPIO_SUBTYPE_GPIO_LV: 825d7b5f5ccSFenglin Wu pad->num_sources = 1; 826d7b5f5ccSFenglin Wu pad->have_buffer = true; 827d7b5f5ccSFenglin Wu pad->lv_mv_type = true; 828d7b5f5ccSFenglin Wu break; 829d7b5f5ccSFenglin Wu case PMIC_GPIO_SUBTYPE_GPIO_MV: 830d7b5f5ccSFenglin Wu pad->num_sources = 2; 831d7b5f5ccSFenglin Wu pad->have_buffer = true; 832d7b5f5ccSFenglin Wu pad->lv_mv_type = true; 833d7b5f5ccSFenglin Wu break; 834eadff302SIvan T. Ivanov default: 835eadff302SIvan T. Ivanov dev_err(state->dev, "unknown GPIO type 0x%x\n", subtype); 836eadff302SIvan T. Ivanov return -ENODEV; 837eadff302SIvan T. Ivanov } 838eadff302SIvan T. Ivanov 839d7b5f5ccSFenglin Wu if (pad->lv_mv_type) { 840d7b5f5ccSFenglin Wu val = pmic_gpio_read(state, pad, 841d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL); 842d7b5f5ccSFenglin Wu if (val < 0) 843d7b5f5ccSFenglin Wu return val; 844d7b5f5ccSFenglin Wu 845d7b5f5ccSFenglin Wu pad->out_value = !!(val & PMIC_GPIO_LV_MV_OUTPUT_INVERT); 846d7b5f5ccSFenglin Wu pad->function = val & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK; 847d7b5f5ccSFenglin Wu 848d7b5f5ccSFenglin Wu val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL); 849d7b5f5ccSFenglin Wu if (val < 0) 850d7b5f5ccSFenglin Wu return val; 851d7b5f5ccSFenglin Wu 852d7b5f5ccSFenglin Wu dir = val & PMIC_GPIO_REG_LV_MV_MODE_DIR_MASK; 853d7b5f5ccSFenglin Wu } else { 854eadff302SIvan T. Ivanov val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL); 855eadff302SIvan T. Ivanov if (val < 0) 856eadff302SIvan T. Ivanov return val; 857eadff302SIvan T. Ivanov 858eadff302SIvan T. Ivanov pad->out_value = val & PMIC_GPIO_REG_MODE_VALUE_SHIFT; 859eadff302SIvan T. Ivanov 860eadff302SIvan T. Ivanov dir = val >> PMIC_GPIO_REG_MODE_DIR_SHIFT; 861eadff302SIvan T. Ivanov dir &= PMIC_GPIO_REG_MODE_DIR_MASK; 862d7b5f5ccSFenglin Wu pad->function = val >> PMIC_GPIO_REG_MODE_FUNCTION_SHIFT; 863d7b5f5ccSFenglin Wu pad->function &= PMIC_GPIO_REG_MODE_FUNCTION_MASK; 864d7b5f5ccSFenglin Wu } 865d7b5f5ccSFenglin Wu 866eadff302SIvan T. Ivanov switch (dir) { 867d7b5f5ccSFenglin Wu case PMIC_GPIO_MODE_DIGITAL_INPUT: 868eadff302SIvan T. Ivanov pad->input_enabled = true; 869eadff302SIvan T. Ivanov pad->output_enabled = false; 870eadff302SIvan T. Ivanov break; 871d7b5f5ccSFenglin Wu case PMIC_GPIO_MODE_DIGITAL_OUTPUT: 872eadff302SIvan T. Ivanov pad->input_enabled = false; 873eadff302SIvan T. Ivanov pad->output_enabled = true; 874eadff302SIvan T. Ivanov break; 875d7b5f5ccSFenglin Wu case PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT: 876eadff302SIvan T. Ivanov pad->input_enabled = true; 877eadff302SIvan T. Ivanov pad->output_enabled = true; 878eadff302SIvan T. Ivanov break; 879d7b5f5ccSFenglin Wu case PMIC_GPIO_MODE_ANALOG_PASS_THRU: 880d7b5f5ccSFenglin Wu if (!pad->lv_mv_type) 881d7b5f5ccSFenglin Wu return -ENODEV; 882d7b5f5ccSFenglin Wu pad->analog_pass = true; 883d7b5f5ccSFenglin Wu break; 884eadff302SIvan T. Ivanov default: 885eadff302SIvan T. Ivanov dev_err(state->dev, "unknown GPIO direction\n"); 886eadff302SIvan T. Ivanov return -ENODEV; 887eadff302SIvan T. Ivanov } 888eadff302SIvan T. Ivanov 889eadff302SIvan T. Ivanov val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL); 890eadff302SIvan T. Ivanov if (val < 0) 891eadff302SIvan T. Ivanov return val; 892eadff302SIvan T. Ivanov 893eadff302SIvan T. Ivanov pad->power_source = val >> PMIC_GPIO_REG_VIN_SHIFT; 894eadff302SIvan T. Ivanov pad->power_source &= PMIC_GPIO_REG_VIN_MASK; 895eadff302SIvan T. Ivanov 896eadff302SIvan T. Ivanov val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_PULL_CTL); 897eadff302SIvan T. Ivanov if (val < 0) 898eadff302SIvan T. Ivanov return val; 899eadff302SIvan T. Ivanov 900eadff302SIvan T. Ivanov pad->pullup = val >> PMIC_GPIO_REG_PULL_SHIFT; 901eadff302SIvan T. Ivanov pad->pullup &= PMIC_GPIO_REG_PULL_MASK; 902eadff302SIvan T. Ivanov 903223463fcSFenglin Wu val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_IN_CTL); 904223463fcSFenglin Wu if (val < 0) 905223463fcSFenglin Wu return val; 906223463fcSFenglin Wu 907223463fcSFenglin Wu if (pad->lv_mv_type && (val & PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN)) 908223463fcSFenglin Wu pad->dtest_buffer = 909223463fcSFenglin Wu (val & PMIC_GPIO_LV_MV_DIG_IN_DTEST_SEL_MASK) + 1; 910223463fcSFenglin Wu else if (!pad->lv_mv_type) 911223463fcSFenglin Wu pad->dtest_buffer = ffs(val); 912223463fcSFenglin Wu else 913223463fcSFenglin Wu pad->dtest_buffer = 0; 914223463fcSFenglin Wu 915223463fcSFenglin Wu val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL); 916223463fcSFenglin Wu if (val < 0) 917223463fcSFenglin Wu return val; 918223463fcSFenglin Wu 919eadff302SIvan T. Ivanov pad->strength = val >> PMIC_GPIO_REG_OUT_STRENGTH_SHIFT; 920eadff302SIvan T. Ivanov pad->strength &= PMIC_GPIO_REG_OUT_STRENGTH_MASK; 921eadff302SIvan T. Ivanov 922eadff302SIvan T. Ivanov pad->buffer_type = val >> PMIC_GPIO_REG_OUT_TYPE_SHIFT; 923eadff302SIvan T. Ivanov pad->buffer_type &= PMIC_GPIO_REG_OUT_TYPE_MASK; 924eadff302SIvan T. Ivanov 925d7b5f5ccSFenglin Wu if (pad->lv_mv_type) { 926d7b5f5ccSFenglin Wu val = pmic_gpio_read(state, pad, 927d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL); 928d7b5f5ccSFenglin Wu if (val < 0) 929d7b5f5ccSFenglin Wu return val; 930d7b5f5ccSFenglin Wu pad->atest = (val & PMIC_GPIO_LV_MV_ANA_MUX_SEL_MASK) + 1; 931d7b5f5ccSFenglin Wu } 932d7b5f5ccSFenglin Wu 933eadff302SIvan T. Ivanov /* Pin could be disabled with PIN_CONFIG_BIAS_HIGH_IMPEDANCE */ 934eadff302SIvan T. Ivanov pad->is_enabled = true; 935eadff302SIvan T. Ivanov return 0; 936eadff302SIvan T. Ivanov } 937eadff302SIvan T. Ivanov 938ca69e2d1SBrian Masney static struct irq_chip pmic_gpio_irq_chip = { 939ca69e2d1SBrian Masney .name = "spmi-gpio", 940ca69e2d1SBrian Masney .irq_ack = irq_chip_ack_parent, 941ca69e2d1SBrian Masney .irq_mask = irq_chip_mask_parent, 942ca69e2d1SBrian Masney .irq_unmask = irq_chip_unmask_parent, 943ca69e2d1SBrian Masney .irq_set_type = irq_chip_set_type_parent, 944ca69e2d1SBrian Masney .irq_set_wake = irq_chip_set_wake_parent, 945ca69e2d1SBrian Masney .flags = IRQCHIP_MASK_ON_SUSPEND, 946ca69e2d1SBrian Masney }; 947ca69e2d1SBrian Masney 948ca69e2d1SBrian Masney static int pmic_gpio_domain_translate(struct irq_domain *domain, 949ca69e2d1SBrian Masney struct irq_fwspec *fwspec, 950ca69e2d1SBrian Masney unsigned long *hwirq, 951ca69e2d1SBrian Masney unsigned int *type) 952ca69e2d1SBrian Masney { 953ca69e2d1SBrian Masney struct pmic_gpio_state *state = container_of(domain->host_data, 954ca69e2d1SBrian Masney struct pmic_gpio_state, 955ca69e2d1SBrian Masney chip); 956ca69e2d1SBrian Masney 957dac7da98SBjorn Andersson if (fwspec->param_count != 2 || 958dac7da98SBjorn Andersson fwspec->param[0] < 1 || fwspec->param[0] > state->chip.ngpio) 959ca69e2d1SBrian Masney return -EINVAL; 960ca69e2d1SBrian Masney 961ca69e2d1SBrian Masney *hwirq = fwspec->param[0] - PMIC_GPIO_PHYSICAL_OFFSET; 962ca69e2d1SBrian Masney *type = fwspec->param[1]; 963ca69e2d1SBrian Masney 964ca69e2d1SBrian Masney return 0; 965ca69e2d1SBrian Masney } 966ca69e2d1SBrian Masney 967ca69e2d1SBrian Masney static int pmic_gpio_domain_alloc(struct irq_domain *domain, unsigned int virq, 968ca69e2d1SBrian Masney unsigned int nr_irqs, void *data) 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 struct irq_fwspec *fwspec = data; 974ca69e2d1SBrian Masney struct irq_fwspec parent_fwspec; 975ca69e2d1SBrian Masney irq_hw_number_t hwirq; 976ca69e2d1SBrian Masney unsigned int type; 977ca69e2d1SBrian Masney int ret, i; 978ca69e2d1SBrian Masney 979ca69e2d1SBrian Masney ret = pmic_gpio_domain_translate(domain, fwspec, &hwirq, &type); 980ca69e2d1SBrian Masney if (ret) 981ca69e2d1SBrian Masney return ret; 982ca69e2d1SBrian Masney 983ca69e2d1SBrian Masney for (i = 0; i < nr_irqs; i++) 984ca69e2d1SBrian Masney irq_domain_set_info(domain, virq + i, hwirq + i, 985ca69e2d1SBrian Masney &pmic_gpio_irq_chip, state, 986ca69e2d1SBrian Masney handle_level_irq, NULL, NULL); 987ca69e2d1SBrian Masney 988ca69e2d1SBrian Masney parent_fwspec.fwnode = domain->parent->fwnode; 989ca69e2d1SBrian Masney parent_fwspec.param_count = 4; 990ca69e2d1SBrian Masney parent_fwspec.param[0] = 0; 991ca69e2d1SBrian Masney parent_fwspec.param[1] = hwirq + 0xc0; 992ca69e2d1SBrian Masney parent_fwspec.param[2] = 0; 993ca69e2d1SBrian Masney parent_fwspec.param[3] = fwspec->param[1]; 994ca69e2d1SBrian Masney 995ca69e2d1SBrian Masney return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, 996ca69e2d1SBrian Masney &parent_fwspec); 997ca69e2d1SBrian Masney } 998ca69e2d1SBrian Masney 999ca69e2d1SBrian Masney static const struct irq_domain_ops pmic_gpio_domain_ops = { 1000ca69e2d1SBrian Masney .activate = gpiochip_irq_domain_activate, 1001ca69e2d1SBrian Masney .alloc = pmic_gpio_domain_alloc, 1002ca69e2d1SBrian Masney .deactivate = gpiochip_irq_domain_deactivate, 1003ca69e2d1SBrian Masney .free = irq_domain_free_irqs_common, 1004ca69e2d1SBrian Masney .translate = pmic_gpio_domain_translate, 1005ca69e2d1SBrian Masney }; 1006ca69e2d1SBrian Masney 1007eadff302SIvan T. Ivanov static int pmic_gpio_probe(struct platform_device *pdev) 1008eadff302SIvan T. Ivanov { 1009ca69e2d1SBrian Masney struct irq_domain *parent_domain; 1010ca69e2d1SBrian Masney struct device_node *parent_node; 1011eadff302SIvan T. Ivanov struct device *dev = &pdev->dev; 1012eadff302SIvan T. Ivanov struct pinctrl_pin_desc *pindesc; 1013eadff302SIvan T. Ivanov struct pinctrl_desc *pctrldesc; 1014eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad, *pads; 1015eadff302SIvan T. Ivanov struct pmic_gpio_state *state; 1016eadff302SIvan T. Ivanov int ret, npins, i; 1017ab4256cfSStephen Boyd u32 reg; 1018eadff302SIvan T. Ivanov 1019ab4256cfSStephen Boyd ret = of_property_read_u32(dev->of_node, "reg", ®); 1020eadff302SIvan T. Ivanov if (ret < 0) { 1021ab4256cfSStephen Boyd dev_err(dev, "missing base address"); 1022eadff302SIvan T. Ivanov return ret; 1023eadff302SIvan T. Ivanov } 1024eadff302SIvan T. Ivanov 1025cfacef37SBrian Masney npins = (uintptr_t) device_get_match_data(&pdev->dev); 1026eadff302SIvan T. Ivanov 1027eadff302SIvan T. Ivanov state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); 1028eadff302SIvan T. Ivanov if (!state) 1029eadff302SIvan T. Ivanov return -ENOMEM; 1030eadff302SIvan T. Ivanov 1031eadff302SIvan T. Ivanov platform_set_drvdata(pdev, state); 1032eadff302SIvan T. Ivanov 1033eadff302SIvan T. Ivanov state->dev = &pdev->dev; 1034eadff302SIvan T. Ivanov state->map = dev_get_regmap(dev->parent, NULL); 1035eadff302SIvan T. Ivanov 1036eadff302SIvan T. Ivanov pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL); 1037eadff302SIvan T. Ivanov if (!pindesc) 1038eadff302SIvan T. Ivanov return -ENOMEM; 1039eadff302SIvan T. Ivanov 1040eadff302SIvan T. Ivanov pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL); 1041eadff302SIvan T. Ivanov if (!pads) 1042eadff302SIvan T. Ivanov return -ENOMEM; 1043eadff302SIvan T. Ivanov 1044eadff302SIvan T. Ivanov pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL); 1045eadff302SIvan T. Ivanov if (!pctrldesc) 1046eadff302SIvan T. Ivanov return -ENOMEM; 1047eadff302SIvan T. Ivanov 1048eadff302SIvan T. Ivanov pctrldesc->pctlops = &pmic_gpio_pinctrl_ops; 1049eadff302SIvan T. Ivanov pctrldesc->pmxops = &pmic_gpio_pinmux_ops; 1050eadff302SIvan T. Ivanov pctrldesc->confops = &pmic_gpio_pinconf_ops; 1051eadff302SIvan T. Ivanov pctrldesc->owner = THIS_MODULE; 1052eadff302SIvan T. Ivanov pctrldesc->name = dev_name(dev); 1053eadff302SIvan T. Ivanov pctrldesc->pins = pindesc; 1054eadff302SIvan T. Ivanov pctrldesc->npins = npins; 1055f684e4acSLinus Walleij pctrldesc->num_custom_params = ARRAY_SIZE(pmic_gpio_bindings); 1056f684e4acSLinus Walleij pctrldesc->custom_params = pmic_gpio_bindings; 10574f06266aSArnd Bergmann #ifdef CONFIG_DEBUG_FS 1058f684e4acSLinus Walleij pctrldesc->custom_conf_items = pmic_conf_items; 10594f06266aSArnd Bergmann #endif 1060eadff302SIvan T. Ivanov 1061eadff302SIvan T. Ivanov for (i = 0; i < npins; i++, pindesc++) { 1062eadff302SIvan T. Ivanov pad = &pads[i]; 1063eadff302SIvan T. Ivanov pindesc->drv_data = pad; 1064eadff302SIvan T. Ivanov pindesc->number = i; 1065eadff302SIvan T. Ivanov pindesc->name = pmic_gpio_groups[i]; 1066eadff302SIvan T. Ivanov 1067ab4256cfSStephen Boyd pad->base = reg + i * PMIC_GPIO_ADDRESS_RANGE; 1068eadff302SIvan T. Ivanov 1069eadff302SIvan T. Ivanov ret = pmic_gpio_populate(state, pad); 1070eadff302SIvan T. Ivanov if (ret < 0) 1071eadff302SIvan T. Ivanov return ret; 1072eadff302SIvan T. Ivanov } 1073eadff302SIvan T. Ivanov 1074eadff302SIvan T. Ivanov state->chip = pmic_gpio_gpio_template; 107558383c78SLinus Walleij state->chip.parent = dev; 1076eadff302SIvan T. Ivanov state->chip.base = -1; 1077eadff302SIvan T. Ivanov state->chip.ngpio = npins; 1078eadff302SIvan T. Ivanov state->chip.label = dev_name(dev); 1079eadff302SIvan T. Ivanov state->chip.of_gpio_n_cells = 2; 1080eadff302SIvan T. Ivanov state->chip.can_sleep = false; 1081eadff302SIvan T. Ivanov 1082b46ddfe6SLaxman Dewangan state->ctrl = devm_pinctrl_register(dev, pctrldesc, state); 1083323de9efSMasahiro Yamada if (IS_ERR(state->ctrl)) 1084323de9efSMasahiro Yamada return PTR_ERR(state->ctrl); 1085eadff302SIvan T. Ivanov 1086ca69e2d1SBrian Masney parent_node = of_irq_find_parent(state->dev->of_node); 1087ca69e2d1SBrian Masney if (!parent_node) 1088ca69e2d1SBrian Masney return -ENXIO; 1089ca69e2d1SBrian Masney 1090ca69e2d1SBrian Masney parent_domain = irq_find_host(parent_node); 1091ca69e2d1SBrian Masney of_node_put(parent_node); 1092ca69e2d1SBrian Masney if (!parent_domain) 1093ca69e2d1SBrian Masney return -ENXIO; 1094ca69e2d1SBrian Masney 1095ca69e2d1SBrian Masney state->fwnode = of_node_to_fwnode(state->dev->of_node); 1096ca69e2d1SBrian Masney state->domain = irq_domain_create_hierarchy(parent_domain, 0, 1097ca69e2d1SBrian Masney state->chip.ngpio, 1098ca69e2d1SBrian Masney state->fwnode, 1099ca69e2d1SBrian Masney &pmic_gpio_domain_ops, 1100ca69e2d1SBrian Masney &state->chip); 1101ca69e2d1SBrian Masney if (!state->domain) 1102ca69e2d1SBrian Masney return -ENODEV; 1103ca69e2d1SBrian Masney 1104c52d9df1SLinus Walleij ret = gpiochip_add_data(&state->chip, state); 1105eadff302SIvan T. Ivanov if (ret) { 1106eadff302SIvan T. Ivanov dev_err(state->dev, "can't add gpio chip\n"); 1107ca69e2d1SBrian Masney goto err_chip_add_data; 1108eadff302SIvan T. Ivanov } 1109eadff302SIvan T. Ivanov 1110149a9604SBrian Masney /* 1111149a9604SBrian Masney * For DeviceTree-supported systems, the gpio core checks the 1112149a9604SBrian Masney * pinctrl's device node for the "gpio-ranges" property. 1113149a9604SBrian Masney * If it is present, it takes care of adding the pin ranges 1114149a9604SBrian Masney * for the driver. In this case the driver can skip ahead. 1115149a9604SBrian Masney * 1116149a9604SBrian Masney * In order to remain compatible with older, existing DeviceTree 1117149a9604SBrian Masney * files which don't set the "gpio-ranges" property or systems that 1118149a9604SBrian Masney * utilize ACPI the driver has to call gpiochip_add_pin_range(). 1119149a9604SBrian Masney */ 1120149a9604SBrian Masney if (!of_property_read_bool(dev->of_node, "gpio-ranges")) { 1121149a9604SBrian Masney ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, 1122149a9604SBrian Masney npins); 1123eadff302SIvan T. Ivanov if (ret) { 1124eadff302SIvan T. Ivanov dev_err(dev, "failed to add pin range\n"); 1125eadff302SIvan T. Ivanov goto err_range; 1126eadff302SIvan T. Ivanov } 1127149a9604SBrian Masney } 1128eadff302SIvan T. Ivanov 1129eadff302SIvan T. Ivanov return 0; 1130eadff302SIvan T. Ivanov 1131eadff302SIvan T. Ivanov err_range: 1132eadff302SIvan T. Ivanov gpiochip_remove(&state->chip); 1133ca69e2d1SBrian Masney err_chip_add_data: 1134ca69e2d1SBrian Masney irq_domain_remove(state->domain); 1135eadff302SIvan T. Ivanov return ret; 1136eadff302SIvan T. Ivanov } 1137eadff302SIvan T. Ivanov 1138eadff302SIvan T. Ivanov static int pmic_gpio_remove(struct platform_device *pdev) 1139eadff302SIvan T. Ivanov { 1140eadff302SIvan T. Ivanov struct pmic_gpio_state *state = platform_get_drvdata(pdev); 1141eadff302SIvan T. Ivanov 1142eadff302SIvan T. Ivanov gpiochip_remove(&state->chip); 1143ca69e2d1SBrian Masney irq_domain_remove(state->domain); 1144eadff302SIvan T. Ivanov return 0; 1145eadff302SIvan T. Ivanov } 1146eadff302SIvan T. Ivanov 1147eadff302SIvan T. Ivanov static const struct of_device_id pmic_gpio_of_match[] = { 1148cfacef37SBrian Masney { .compatible = "qcom,pm8005-gpio", .data = (void *) 4 }, 1149cfacef37SBrian Masney { .compatible = "qcom,pm8916-gpio", .data = (void *) 4 }, 1150cfacef37SBrian Masney { .compatible = "qcom,pm8941-gpio", .data = (void *) 36 }, 1151cfacef37SBrian Masney { .compatible = "qcom,pm8994-gpio", .data = (void *) 22 }, 1152cfacef37SBrian Masney { .compatible = "qcom,pmi8994-gpio", .data = (void *) 10 }, 1153cfacef37SBrian Masney { .compatible = "qcom,pm8998-gpio", .data = (void *) 26 }, 1154cfacef37SBrian Masney { .compatible = "qcom,pmi8998-gpio", .data = (void *) 14 }, 1155cfacef37SBrian Masney { .compatible = "qcom,pma8084-gpio", .data = (void *) 22 }, 1156cfacef37SBrian Masney /* pms405 has 12 GPIOs with holes on 1, 9, and 10 */ 1157cfacef37SBrian Masney { .compatible = "qcom,pms405-gpio", .data = (void *) 12 }, 1158eadff302SIvan T. Ivanov { }, 1159eadff302SIvan T. Ivanov }; 1160eadff302SIvan T. Ivanov 1161eadff302SIvan T. Ivanov MODULE_DEVICE_TABLE(of, pmic_gpio_of_match); 1162eadff302SIvan T. Ivanov 1163eadff302SIvan T. Ivanov static struct platform_driver pmic_gpio_driver = { 1164eadff302SIvan T. Ivanov .driver = { 1165eadff302SIvan T. Ivanov .name = "qcom-spmi-gpio", 1166eadff302SIvan T. Ivanov .of_match_table = pmic_gpio_of_match, 1167eadff302SIvan T. Ivanov }, 1168eadff302SIvan T. Ivanov .probe = pmic_gpio_probe, 1169eadff302SIvan T. Ivanov .remove = pmic_gpio_remove, 1170eadff302SIvan T. Ivanov }; 1171eadff302SIvan T. Ivanov 1172eadff302SIvan T. Ivanov module_platform_driver(pmic_gpio_driver); 1173eadff302SIvan T. Ivanov 1174eadff302SIvan T. Ivanov MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>"); 1175eadff302SIvan T. Ivanov MODULE_DESCRIPTION("Qualcomm SPMI PMIC GPIO pin control driver"); 1176eadff302SIvan T. Ivanov MODULE_ALIAS("platform:qcom-spmi-gpio"); 1177eadff302SIvan T. Ivanov MODULE_LICENSE("GPL v2"); 1178