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; 1735e503115SDmitry Baryshkov struct irq_chip irq; 174eadff302SIvan T. Ivanov }; 175eadff302SIvan T. Ivanov 176f684e4acSLinus Walleij static const struct pinconf_generic_params pmic_gpio_bindings[] = { 1777382b623SSoren Brinkmann {"qcom,pull-up-strength", PMIC_GPIO_CONF_PULL_UP, 0}, 1787382b623SSoren Brinkmann {"qcom,drive-strength", PMIC_GPIO_CONF_STRENGTH, 0}, 179d7b5f5ccSFenglin Wu {"qcom,atest", PMIC_GPIO_CONF_ATEST, 0}, 180d7b5f5ccSFenglin Wu {"qcom,analog-pass", PMIC_GPIO_CONF_ANALOG_PASS, 0}, 181223463fcSFenglin Wu {"qcom,dtest-buffer", PMIC_GPIO_CONF_DTEST_BUFFER, 0}, 182eadff302SIvan T. Ivanov }; 183eadff302SIvan T. Ivanov 1844f06266aSArnd Bergmann #ifdef CONFIG_DEBUG_FS 1857382b623SSoren Brinkmann static const struct pin_config_item pmic_conf_items[ARRAY_SIZE(pmic_gpio_bindings)] = { 1867382b623SSoren Brinkmann PCONFDUMP(PMIC_GPIO_CONF_PULL_UP, "pull up strength", NULL, true), 1877382b623SSoren Brinkmann PCONFDUMP(PMIC_GPIO_CONF_STRENGTH, "drive-strength", NULL, true), 188d7b5f5ccSFenglin Wu PCONFDUMP(PMIC_GPIO_CONF_ATEST, "atest", NULL, true), 189d7b5f5ccSFenglin Wu PCONFDUMP(PMIC_GPIO_CONF_ANALOG_PASS, "analog-pass", NULL, true), 190223463fcSFenglin Wu PCONFDUMP(PMIC_GPIO_CONF_DTEST_BUFFER, "dtest-buffer", NULL, true), 191eadff302SIvan T. Ivanov }; 1924f06266aSArnd Bergmann #endif 193eadff302SIvan T. Ivanov 194eadff302SIvan T. Ivanov static const char *const pmic_gpio_groups[] = { 195eadff302SIvan T. Ivanov "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", "gpio8", 196eadff302SIvan T. Ivanov "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", "gpio15", 197eadff302SIvan T. Ivanov "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22", 198eadff302SIvan T. Ivanov "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29", 199eadff302SIvan T. Ivanov "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36", 200eadff302SIvan T. Ivanov }; 201eadff302SIvan T. Ivanov 202eadff302SIvan T. Ivanov static const char *const pmic_gpio_functions[] = { 203d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_NORMAL] = PMIC_GPIO_FUNC_NORMAL, 204d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_PAIRED] = PMIC_GPIO_FUNC_PAIRED, 205d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_FUNC1] = PMIC_GPIO_FUNC_FUNC1, 206d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_FUNC2] = PMIC_GPIO_FUNC_FUNC2, 207d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_FUNC3] = PMIC_GPIO_FUNC_FUNC3, 208d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_FUNC4] = PMIC_GPIO_FUNC_FUNC4, 209d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_DTEST1] = PMIC_GPIO_FUNC_DTEST1, 210d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_DTEST2] = PMIC_GPIO_FUNC_DTEST2, 211d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_DTEST3] = PMIC_GPIO_FUNC_DTEST3, 212d7b5f5ccSFenglin Wu [PMIC_GPIO_FUNC_INDEX_DTEST4] = PMIC_GPIO_FUNC_DTEST4, 213eadff302SIvan T. Ivanov }; 214eadff302SIvan T. Ivanov 215eadff302SIvan T. Ivanov static int pmic_gpio_read(struct pmic_gpio_state *state, 216eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad, unsigned int addr) 217eadff302SIvan T. Ivanov { 218eadff302SIvan T. Ivanov unsigned int val; 219eadff302SIvan T. Ivanov int ret; 220eadff302SIvan T. Ivanov 221eadff302SIvan T. Ivanov ret = regmap_read(state->map, pad->base + addr, &val); 222eadff302SIvan T. Ivanov if (ret < 0) 223eadff302SIvan T. Ivanov dev_err(state->dev, "read 0x%x failed\n", addr); 224eadff302SIvan T. Ivanov else 225eadff302SIvan T. Ivanov ret = val; 226eadff302SIvan T. Ivanov 227eadff302SIvan T. Ivanov return ret; 228eadff302SIvan T. Ivanov } 229eadff302SIvan T. Ivanov 230eadff302SIvan T. Ivanov static int pmic_gpio_write(struct pmic_gpio_state *state, 231eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad, unsigned int addr, 232eadff302SIvan T. Ivanov unsigned int val) 233eadff302SIvan T. Ivanov { 234eadff302SIvan T. Ivanov int ret; 235eadff302SIvan T. Ivanov 236eadff302SIvan T. Ivanov ret = regmap_write(state->map, pad->base + addr, val); 237eadff302SIvan T. Ivanov if (ret < 0) 238eadff302SIvan T. Ivanov dev_err(state->dev, "write 0x%x failed\n", addr); 239eadff302SIvan T. Ivanov 240eadff302SIvan T. Ivanov return ret; 241eadff302SIvan T. Ivanov } 242eadff302SIvan T. Ivanov 243eadff302SIvan T. Ivanov static int pmic_gpio_get_groups_count(struct pinctrl_dev *pctldev) 244eadff302SIvan T. Ivanov { 245eadff302SIvan T. Ivanov /* Every PIN is a group */ 246eadff302SIvan T. Ivanov return pctldev->desc->npins; 247eadff302SIvan T. Ivanov } 248eadff302SIvan T. Ivanov 249eadff302SIvan T. Ivanov static const char *pmic_gpio_get_group_name(struct pinctrl_dev *pctldev, 250eadff302SIvan T. Ivanov unsigned pin) 251eadff302SIvan T. Ivanov { 252eadff302SIvan T. Ivanov return pctldev->desc->pins[pin].name; 253eadff302SIvan T. Ivanov } 254eadff302SIvan T. Ivanov 255eadff302SIvan T. Ivanov static int pmic_gpio_get_group_pins(struct pinctrl_dev *pctldev, unsigned pin, 256eadff302SIvan T. Ivanov const unsigned **pins, unsigned *num_pins) 257eadff302SIvan T. Ivanov { 258eadff302SIvan T. Ivanov *pins = &pctldev->desc->pins[pin].number; 259eadff302SIvan T. Ivanov *num_pins = 1; 260eadff302SIvan T. Ivanov return 0; 261eadff302SIvan T. Ivanov } 262eadff302SIvan T. Ivanov 263eadff302SIvan T. Ivanov static const struct pinctrl_ops pmic_gpio_pinctrl_ops = { 264eadff302SIvan T. Ivanov .get_groups_count = pmic_gpio_get_groups_count, 265eadff302SIvan T. Ivanov .get_group_name = pmic_gpio_get_group_name, 266eadff302SIvan T. Ivanov .get_group_pins = pmic_gpio_get_group_pins, 2677382b623SSoren Brinkmann .dt_node_to_map = pinconf_generic_dt_node_to_map_group, 268d32f7fd3SIrina Tirdea .dt_free_map = pinctrl_utils_free_map, 269eadff302SIvan T. Ivanov }; 270eadff302SIvan T. Ivanov 271eadff302SIvan T. Ivanov static int pmic_gpio_get_functions_count(struct pinctrl_dev *pctldev) 272eadff302SIvan T. Ivanov { 273eadff302SIvan T. Ivanov return ARRAY_SIZE(pmic_gpio_functions); 274eadff302SIvan T. Ivanov } 275eadff302SIvan T. Ivanov 276eadff302SIvan T. Ivanov static const char *pmic_gpio_get_function_name(struct pinctrl_dev *pctldev, 277eadff302SIvan T. Ivanov unsigned function) 278eadff302SIvan T. Ivanov { 279eadff302SIvan T. Ivanov return pmic_gpio_functions[function]; 280eadff302SIvan T. Ivanov } 281eadff302SIvan T. Ivanov 282eadff302SIvan T. Ivanov static int pmic_gpio_get_function_groups(struct pinctrl_dev *pctldev, 283eadff302SIvan T. Ivanov unsigned function, 284eadff302SIvan T. Ivanov const char *const **groups, 285eadff302SIvan T. Ivanov unsigned *const num_qgroups) 286eadff302SIvan T. Ivanov { 287eadff302SIvan T. Ivanov *groups = pmic_gpio_groups; 288eadff302SIvan T. Ivanov *num_qgroups = pctldev->desc->npins; 289eadff302SIvan T. Ivanov return 0; 290eadff302SIvan T. Ivanov } 291eadff302SIvan T. Ivanov 292eadff302SIvan T. Ivanov static int pmic_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned function, 293eadff302SIvan T. Ivanov unsigned pin) 294eadff302SIvan T. Ivanov { 295eadff302SIvan T. Ivanov struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev); 296eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad; 297eadff302SIvan T. Ivanov unsigned int val; 298eadff302SIvan T. Ivanov int ret; 299eadff302SIvan T. Ivanov 300d7b5f5ccSFenglin Wu if (function > PMIC_GPIO_FUNC_INDEX_DTEST4) { 301d7b5f5ccSFenglin Wu pr_err("function: %d is not defined\n", function); 302d7b5f5ccSFenglin Wu return -EINVAL; 303d7b5f5ccSFenglin Wu } 304d7b5f5ccSFenglin Wu 305eadff302SIvan T. Ivanov pad = pctldev->desc->pins[pin].drv_data; 306d7b5f5ccSFenglin Wu /* 307d7b5f5ccSFenglin Wu * Non-LV/MV subtypes only support 2 special functions, 308d7b5f5ccSFenglin Wu * offsetting the dtestx function values by 2 309d7b5f5ccSFenglin Wu */ 310d7b5f5ccSFenglin Wu if (!pad->lv_mv_type) { 311d7b5f5ccSFenglin Wu if (function == PMIC_GPIO_FUNC_INDEX_FUNC3 || 312d7b5f5ccSFenglin Wu function == PMIC_GPIO_FUNC_INDEX_FUNC4) { 313d7b5f5ccSFenglin Wu pr_err("LV/MV subtype doesn't have func3/func4\n"); 314d7b5f5ccSFenglin Wu return -EINVAL; 315d7b5f5ccSFenglin Wu } 316d7b5f5ccSFenglin Wu if (function >= PMIC_GPIO_FUNC_INDEX_DTEST1) 317d7b5f5ccSFenglin Wu function -= (PMIC_GPIO_FUNC_INDEX_DTEST1 - 318d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_FUNC3); 319d7b5f5ccSFenglin Wu } 320eadff302SIvan T. Ivanov 321eadff302SIvan T. Ivanov pad->function = function; 322eadff302SIvan T. Ivanov 323d7b5f5ccSFenglin Wu if (pad->analog_pass) 324d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_ANALOG_PASS_THRU; 325d7b5f5ccSFenglin Wu else if (pad->output_enabled && pad->input_enabled) 326d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT; 327d7b5f5ccSFenglin Wu else if (pad->output_enabled) 328d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_OUTPUT; 329eadff302SIvan T. Ivanov else 330d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_INPUT; 331eadff302SIvan T. Ivanov 332d7b5f5ccSFenglin Wu if (pad->lv_mv_type) { 333d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 334d7b5f5ccSFenglin Wu PMIC_GPIO_REG_MODE_CTL, val); 335d7b5f5ccSFenglin Wu if (ret < 0) 336d7b5f5ccSFenglin Wu return ret; 337d7b5f5ccSFenglin Wu 338d7b5f5ccSFenglin Wu val = pad->atest - 1; 339d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 340d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL, val); 341d7b5f5ccSFenglin Wu if (ret < 0) 342d7b5f5ccSFenglin Wu return ret; 343d7b5f5ccSFenglin Wu 344d7b5f5ccSFenglin Wu val = pad->out_value 345d7b5f5ccSFenglin Wu << PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT; 346d7b5f5ccSFenglin Wu val |= pad->function 347d7b5f5ccSFenglin Wu & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK; 348d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 349d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL, val); 350d7b5f5ccSFenglin Wu if (ret < 0) 351d7b5f5ccSFenglin Wu return ret; 352d7b5f5ccSFenglin Wu } else { 353dc391502SIvan T. Ivanov val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT; 354eadff302SIvan T. Ivanov val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT; 355eadff302SIvan T. Ivanov val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT; 356eadff302SIvan T. Ivanov 357eadff302SIvan T. Ivanov ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); 358eadff302SIvan T. Ivanov if (ret < 0) 359eadff302SIvan T. Ivanov return ret; 360d7b5f5ccSFenglin Wu } 361eadff302SIvan T. Ivanov 362eadff302SIvan T. Ivanov val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT; 363eadff302SIvan T. Ivanov 364eadff302SIvan T. Ivanov return pmic_gpio_write(state, pad, PMIC_GPIO_REG_EN_CTL, val); 365eadff302SIvan T. Ivanov } 366eadff302SIvan T. Ivanov 367eadff302SIvan T. Ivanov static const struct pinmux_ops pmic_gpio_pinmux_ops = { 368eadff302SIvan T. Ivanov .get_functions_count = pmic_gpio_get_functions_count, 369eadff302SIvan T. Ivanov .get_function_name = pmic_gpio_get_function_name, 370eadff302SIvan T. Ivanov .get_function_groups = pmic_gpio_get_function_groups, 371eadff302SIvan T. Ivanov .set_mux = pmic_gpio_set_mux, 372eadff302SIvan T. Ivanov }; 373eadff302SIvan T. Ivanov 374eadff302SIvan T. Ivanov static int pmic_gpio_config_get(struct pinctrl_dev *pctldev, 375eadff302SIvan T. Ivanov unsigned int pin, unsigned long *config) 376eadff302SIvan T. Ivanov { 377eadff302SIvan T. Ivanov unsigned param = pinconf_to_config_param(*config); 378eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad; 379eadff302SIvan T. Ivanov unsigned arg; 380eadff302SIvan T. Ivanov 381eadff302SIvan T. Ivanov pad = pctldev->desc->pins[pin].drv_data; 382eadff302SIvan T. Ivanov 383eadff302SIvan T. Ivanov switch (param) { 384eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_PUSH_PULL: 3851cf86bc2SDouglas Anderson if (pad->buffer_type != PMIC_GPIO_OUT_BUF_CMOS) 3861cf86bc2SDouglas Anderson return -EINVAL; 3871cf86bc2SDouglas Anderson arg = 1; 388eadff302SIvan T. Ivanov break; 389eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_OPEN_DRAIN: 3901cf86bc2SDouglas Anderson if (pad->buffer_type != PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS) 3911cf86bc2SDouglas Anderson return -EINVAL; 3921cf86bc2SDouglas Anderson arg = 1; 393eadff302SIvan T. Ivanov break; 394eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_OPEN_SOURCE: 3951cf86bc2SDouglas Anderson if (pad->buffer_type != PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS) 3961cf86bc2SDouglas Anderson return -EINVAL; 3971cf86bc2SDouglas Anderson arg = 1; 398eadff302SIvan T. Ivanov break; 399eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_PULL_DOWN: 4001cf86bc2SDouglas Anderson if (pad->pullup != PMIC_GPIO_PULL_DOWN) 4011cf86bc2SDouglas Anderson return -EINVAL; 4021cf86bc2SDouglas Anderson arg = 1; 403eadff302SIvan T. Ivanov break; 404eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_DISABLE: 4051cf86bc2SDouglas Anderson if (pad->pullup != PMIC_GPIO_PULL_DISABLE) 4061cf86bc2SDouglas Anderson return -EINVAL; 4071cf86bc2SDouglas Anderson arg = 1; 408eadff302SIvan T. Ivanov break; 409eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_PULL_UP: 4101cf86bc2SDouglas Anderson if (pad->pullup != PMIC_GPIO_PULL_UP_30) 4111cf86bc2SDouglas Anderson return -EINVAL; 4121cf86bc2SDouglas Anderson arg = 1; 413eadff302SIvan T. Ivanov break; 414eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: 4151cf86bc2SDouglas Anderson if (pad->is_enabled) 4161cf86bc2SDouglas Anderson return -EINVAL; 4171cf86bc2SDouglas Anderson arg = 1; 418eadff302SIvan T. Ivanov break; 419eadff302SIvan T. Ivanov case PIN_CONFIG_POWER_SOURCE: 420eadff302SIvan T. Ivanov arg = pad->power_source; 421eadff302SIvan T. Ivanov break; 422eadff302SIvan T. Ivanov case PIN_CONFIG_INPUT_ENABLE: 4231cf86bc2SDouglas Anderson if (!pad->input_enabled) 4241cf86bc2SDouglas Anderson return -EINVAL; 4251cf86bc2SDouglas Anderson arg = 1; 426eadff302SIvan T. Ivanov break; 427eadff302SIvan T. Ivanov case PIN_CONFIG_OUTPUT: 428eadff302SIvan T. Ivanov arg = pad->out_value; 429eadff302SIvan T. Ivanov break; 430eadff302SIvan T. Ivanov case PMIC_GPIO_CONF_PULL_UP: 431eadff302SIvan T. Ivanov arg = pad->pullup; 432eadff302SIvan T. Ivanov break; 433eadff302SIvan T. Ivanov case PMIC_GPIO_CONF_STRENGTH: 434eadff302SIvan T. Ivanov arg = pad->strength; 435eadff302SIvan T. Ivanov break; 436d7b5f5ccSFenglin Wu case PMIC_GPIO_CONF_ATEST: 437d7b5f5ccSFenglin Wu arg = pad->atest; 438d7b5f5ccSFenglin Wu break; 439d7b5f5ccSFenglin Wu case PMIC_GPIO_CONF_ANALOG_PASS: 440d7b5f5ccSFenglin Wu arg = pad->analog_pass; 441d7b5f5ccSFenglin Wu break; 442223463fcSFenglin Wu case PMIC_GPIO_CONF_DTEST_BUFFER: 443223463fcSFenglin Wu arg = pad->dtest_buffer; 444223463fcSFenglin Wu break; 445eadff302SIvan T. Ivanov default: 446eadff302SIvan T. Ivanov return -EINVAL; 447eadff302SIvan T. Ivanov } 448eadff302SIvan T. Ivanov 449eadff302SIvan T. Ivanov *config = pinconf_to_config_packed(param, arg); 450eadff302SIvan T. Ivanov return 0; 451eadff302SIvan T. Ivanov } 452eadff302SIvan T. Ivanov 453eadff302SIvan T. Ivanov static int pmic_gpio_config_set(struct pinctrl_dev *pctldev, unsigned int pin, 454eadff302SIvan T. Ivanov unsigned long *configs, unsigned nconfs) 455eadff302SIvan T. Ivanov { 456eadff302SIvan T. Ivanov struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev); 457eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad; 458eadff302SIvan T. Ivanov unsigned param, arg; 459eadff302SIvan T. Ivanov unsigned int val; 460eadff302SIvan T. Ivanov int i, ret; 461eadff302SIvan T. Ivanov 462eadff302SIvan T. Ivanov pad = pctldev->desc->pins[pin].drv_data; 463eadff302SIvan T. Ivanov 4646cb74f44SFenglin Wu pad->is_enabled = true; 465eadff302SIvan T. Ivanov for (i = 0; i < nconfs; i++) { 466eadff302SIvan T. Ivanov param = pinconf_to_config_param(configs[i]); 467eadff302SIvan T. Ivanov arg = pinconf_to_config_argument(configs[i]); 468eadff302SIvan T. Ivanov 469eadff302SIvan T. Ivanov switch (param) { 470eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_PUSH_PULL: 471eadff302SIvan T. Ivanov pad->buffer_type = PMIC_GPIO_OUT_BUF_CMOS; 472eadff302SIvan T. Ivanov break; 473eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_OPEN_DRAIN: 474eadff302SIvan T. Ivanov if (!pad->have_buffer) 475eadff302SIvan T. Ivanov return -EINVAL; 476eadff302SIvan T. Ivanov pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS; 477eadff302SIvan T. Ivanov break; 478eadff302SIvan T. Ivanov case PIN_CONFIG_DRIVE_OPEN_SOURCE: 479eadff302SIvan T. Ivanov if (!pad->have_buffer) 480eadff302SIvan T. Ivanov return -EINVAL; 481eadff302SIvan T. Ivanov pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS; 482eadff302SIvan T. Ivanov break; 483eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_DISABLE: 484eadff302SIvan T. Ivanov pad->pullup = PMIC_GPIO_PULL_DISABLE; 485eadff302SIvan T. Ivanov break; 486eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_PULL_UP: 487eadff302SIvan T. Ivanov pad->pullup = PMIC_GPIO_PULL_UP_30; 488eadff302SIvan T. Ivanov break; 489eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_PULL_DOWN: 490eadff302SIvan T. Ivanov if (arg) 491eadff302SIvan T. Ivanov pad->pullup = PMIC_GPIO_PULL_DOWN; 492eadff302SIvan T. Ivanov else 493eadff302SIvan T. Ivanov pad->pullup = PMIC_GPIO_PULL_DISABLE; 494eadff302SIvan T. Ivanov break; 495eadff302SIvan T. Ivanov case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: 496eadff302SIvan T. Ivanov pad->is_enabled = false; 497eadff302SIvan T. Ivanov break; 498eadff302SIvan T. Ivanov case PIN_CONFIG_POWER_SOURCE: 4994e83ac4cSFenglin Wu if (arg >= pad->num_sources) 500eadff302SIvan T. Ivanov return -EINVAL; 501eadff302SIvan T. Ivanov pad->power_source = arg; 502eadff302SIvan T. Ivanov break; 503eadff302SIvan T. Ivanov case PIN_CONFIG_INPUT_ENABLE: 504eadff302SIvan T. Ivanov pad->input_enabled = arg ? true : false; 505eadff302SIvan T. Ivanov break; 506eadff302SIvan T. Ivanov case PIN_CONFIG_OUTPUT: 507eadff302SIvan T. Ivanov pad->output_enabled = true; 508eadff302SIvan T. Ivanov pad->out_value = arg; 509eadff302SIvan T. Ivanov break; 510eadff302SIvan T. Ivanov case PMIC_GPIO_CONF_PULL_UP: 511eadff302SIvan T. Ivanov if (arg > PMIC_GPIO_PULL_UP_1P5_30) 512eadff302SIvan T. Ivanov return -EINVAL; 513eadff302SIvan T. Ivanov pad->pullup = arg; 514eadff302SIvan T. Ivanov break; 515eadff302SIvan T. Ivanov case PMIC_GPIO_CONF_STRENGTH: 516eadff302SIvan T. Ivanov if (arg > PMIC_GPIO_STRENGTH_LOW) 517eadff302SIvan T. Ivanov return -EINVAL; 518eadff302SIvan T. Ivanov pad->strength = arg; 519eadff302SIvan T. Ivanov break; 520d7b5f5ccSFenglin Wu case PMIC_GPIO_CONF_ATEST: 521d7b5f5ccSFenglin Wu if (!pad->lv_mv_type || arg > 4) 522d7b5f5ccSFenglin Wu return -EINVAL; 523d7b5f5ccSFenglin Wu pad->atest = arg; 524d7b5f5ccSFenglin Wu break; 525d7b5f5ccSFenglin Wu case PMIC_GPIO_CONF_ANALOG_PASS: 526d7b5f5ccSFenglin Wu if (!pad->lv_mv_type) 527d7b5f5ccSFenglin Wu return -EINVAL; 528d7b5f5ccSFenglin Wu pad->analog_pass = true; 529d7b5f5ccSFenglin Wu break; 530223463fcSFenglin Wu case PMIC_GPIO_CONF_DTEST_BUFFER: 531223463fcSFenglin Wu if (arg > 4) 532223463fcSFenglin Wu return -EINVAL; 533223463fcSFenglin Wu pad->dtest_buffer = arg; 534223463fcSFenglin Wu break; 535eadff302SIvan T. Ivanov default: 536eadff302SIvan T. Ivanov return -EINVAL; 537eadff302SIvan T. Ivanov } 538eadff302SIvan T. Ivanov } 539eadff302SIvan T. Ivanov 540eadff302SIvan T. Ivanov val = pad->power_source << PMIC_GPIO_REG_VIN_SHIFT; 541eadff302SIvan T. Ivanov 542eadff302SIvan T. Ivanov ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL, val); 543eadff302SIvan T. Ivanov if (ret < 0) 544eadff302SIvan T. Ivanov return ret; 545eadff302SIvan T. Ivanov 546eadff302SIvan T. Ivanov val = pad->pullup << PMIC_GPIO_REG_PULL_SHIFT; 547eadff302SIvan T. Ivanov 548eadff302SIvan T. Ivanov ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_PULL_CTL, val); 549eadff302SIvan T. Ivanov if (ret < 0) 550eadff302SIvan T. Ivanov return ret; 551eadff302SIvan T. Ivanov 552eadff302SIvan T. Ivanov val = pad->buffer_type << PMIC_GPIO_REG_OUT_TYPE_SHIFT; 553982df6aeSIvan T. Ivanov val |= pad->strength << PMIC_GPIO_REG_OUT_STRENGTH_SHIFT; 554eadff302SIvan T. Ivanov 555eadff302SIvan T. Ivanov ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL, val); 556eadff302SIvan T. Ivanov if (ret < 0) 557eadff302SIvan T. Ivanov return ret; 558eadff302SIvan T. Ivanov 559223463fcSFenglin Wu if (pad->dtest_buffer == 0) { 560223463fcSFenglin Wu val = 0; 561223463fcSFenglin Wu } else { 562223463fcSFenglin Wu if (pad->lv_mv_type) { 563223463fcSFenglin Wu val = pad->dtest_buffer - 1; 564223463fcSFenglin Wu val |= PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN; 565223463fcSFenglin Wu } else { 566223463fcSFenglin Wu val = BIT(pad->dtest_buffer - 1); 567223463fcSFenglin Wu } 568223463fcSFenglin Wu } 569223463fcSFenglin Wu ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_IN_CTL, val); 570223463fcSFenglin Wu if (ret < 0) 571223463fcSFenglin Wu return ret; 572223463fcSFenglin Wu 573d7b5f5ccSFenglin Wu if (pad->analog_pass) 574d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_ANALOG_PASS_THRU; 575d7b5f5ccSFenglin Wu else if (pad->output_enabled && pad->input_enabled) 576d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT; 577d7b5f5ccSFenglin Wu else if (pad->output_enabled) 578d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_OUTPUT; 579eadff302SIvan T. Ivanov else 580d7b5f5ccSFenglin Wu val = PMIC_GPIO_MODE_DIGITAL_INPUT; 581eadff302SIvan T. Ivanov 582d7b5f5ccSFenglin Wu if (pad->lv_mv_type) { 583d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 584d7b5f5ccSFenglin Wu PMIC_GPIO_REG_MODE_CTL, val); 585d7b5f5ccSFenglin Wu if (ret < 0) 586d7b5f5ccSFenglin Wu return ret; 587d7b5f5ccSFenglin Wu 588d7b5f5ccSFenglin Wu val = pad->atest - 1; 589d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 590d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL, val); 591d7b5f5ccSFenglin Wu if (ret < 0) 592d7b5f5ccSFenglin Wu return ret; 593d7b5f5ccSFenglin Wu 594d7b5f5ccSFenglin Wu val = pad->out_value 595d7b5f5ccSFenglin Wu << PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT; 596d7b5f5ccSFenglin Wu val |= pad->function 597d7b5f5ccSFenglin Wu & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK; 598d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, 599d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL, val); 600d7b5f5ccSFenglin Wu if (ret < 0) 601d7b5f5ccSFenglin Wu return ret; 602d7b5f5ccSFenglin Wu } else { 603eadff302SIvan T. Ivanov val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT; 604eadff302SIvan T. Ivanov val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT; 605eadff302SIvan T. Ivanov val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT; 606eadff302SIvan T. Ivanov 607d7b5f5ccSFenglin Wu ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); 608d7b5f5ccSFenglin Wu if (ret < 0) 609d7b5f5ccSFenglin Wu return ret; 610d7b5f5ccSFenglin Wu } 611d7b5f5ccSFenglin Wu 6126cb74f44SFenglin Wu val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT; 6136cb74f44SFenglin Wu 6146cb74f44SFenglin Wu ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_EN_CTL, val); 6156cb74f44SFenglin Wu 616d7b5f5ccSFenglin Wu return ret; 617eadff302SIvan T. Ivanov } 618eadff302SIvan T. Ivanov 619eadff302SIvan T. Ivanov static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev, 620eadff302SIvan T. Ivanov struct seq_file *s, unsigned pin) 621eadff302SIvan T. Ivanov { 622eadff302SIvan T. Ivanov struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev); 623eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad; 624d7b5f5ccSFenglin Wu int ret, val, function; 625eadff302SIvan T. Ivanov 626eadff302SIvan T. Ivanov static const char *const biases[] = { 627eadff302SIvan T. Ivanov "pull-up 30uA", "pull-up 1.5uA", "pull-up 31.5uA", 628eadff302SIvan T. Ivanov "pull-up 1.5uA + 30uA boost", "pull-down 10uA", "no pull" 629eadff302SIvan T. Ivanov }; 630eadff302SIvan T. Ivanov static const char *const buffer_types[] = { 631eadff302SIvan T. Ivanov "push-pull", "open-drain", "open-source" 632eadff302SIvan T. Ivanov }; 633eadff302SIvan T. Ivanov static const char *const strengths[] = { 634eadff302SIvan T. Ivanov "no", "high", "medium", "low" 635eadff302SIvan T. Ivanov }; 636eadff302SIvan T. Ivanov 637eadff302SIvan T. Ivanov pad = pctldev->desc->pins[pin].drv_data; 638eadff302SIvan T. Ivanov 639eadff302SIvan T. Ivanov seq_printf(s, " gpio%-2d:", pin + PMIC_GPIO_PHYSICAL_OFFSET); 640eadff302SIvan T. Ivanov 641eadff302SIvan T. Ivanov val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_EN_CTL); 642eadff302SIvan T. Ivanov 643eadff302SIvan T. Ivanov if (val < 0 || !(val >> PMIC_GPIO_REG_MASTER_EN_SHIFT)) { 644eadff302SIvan T. Ivanov seq_puts(s, " ---"); 645eadff302SIvan T. Ivanov } else { 64624a66618SIvan T. Ivanov if (pad->input_enabled) { 647eadff302SIvan T. Ivanov ret = pmic_gpio_read(state, pad, PMIC_MPP_REG_RT_STS); 64824a66618SIvan T. Ivanov if (ret < 0) 64924a66618SIvan T. Ivanov return; 65024a66618SIvan T. Ivanov 651eadff302SIvan T. Ivanov ret &= PMIC_MPP_REG_RT_STS_VAL_MASK; 652eadff302SIvan T. Ivanov pad->out_value = ret; 653eadff302SIvan T. Ivanov } 654d7b5f5ccSFenglin Wu /* 655d7b5f5ccSFenglin Wu * For the non-LV/MV subtypes only 2 special functions are 656d7b5f5ccSFenglin Wu * available, offsetting the dtest function values by 2. 657d7b5f5ccSFenglin Wu */ 658d7b5f5ccSFenglin Wu function = pad->function; 659d7b5f5ccSFenglin Wu if (!pad->lv_mv_type && 660d7b5f5ccSFenglin Wu pad->function >= PMIC_GPIO_FUNC_INDEX_FUNC3) 661d7b5f5ccSFenglin Wu function += PMIC_GPIO_FUNC_INDEX_DTEST1 - 662d7b5f5ccSFenglin Wu PMIC_GPIO_FUNC_INDEX_FUNC3; 663eadff302SIvan T. Ivanov 664d7b5f5ccSFenglin Wu if (pad->analog_pass) 665d7b5f5ccSFenglin Wu seq_puts(s, " analog-pass"); 666d7b5f5ccSFenglin Wu else 667d7b5f5ccSFenglin Wu seq_printf(s, " %-4s", 668d7b5f5ccSFenglin Wu pad->output_enabled ? "out" : "in"); 669202ba5ebSBjorn Andersson seq_printf(s, " %-4s", pad->out_value ? "high" : "low"); 670d7b5f5ccSFenglin Wu seq_printf(s, " %-7s", pmic_gpio_functions[function]); 671eadff302SIvan T. Ivanov seq_printf(s, " vin-%d", pad->power_source); 672eadff302SIvan T. Ivanov seq_printf(s, " %-27s", biases[pad->pullup]); 673eadff302SIvan T. Ivanov seq_printf(s, " %-10s", buffer_types[pad->buffer_type]); 674eadff302SIvan T. Ivanov seq_printf(s, " %-7s", strengths[pad->strength]); 675d7b5f5ccSFenglin Wu seq_printf(s, " atest-%d", pad->atest); 676223463fcSFenglin Wu seq_printf(s, " dtest-%d", pad->dtest_buffer); 677eadff302SIvan T. Ivanov } 678eadff302SIvan T. Ivanov } 679eadff302SIvan T. Ivanov 680eadff302SIvan T. Ivanov static const struct pinconf_ops pmic_gpio_pinconf_ops = { 6817382b623SSoren Brinkmann .is_generic = true, 682eadff302SIvan T. Ivanov .pin_config_group_get = pmic_gpio_config_get, 683eadff302SIvan T. Ivanov .pin_config_group_set = pmic_gpio_config_set, 684eadff302SIvan T. Ivanov .pin_config_group_dbg_show = pmic_gpio_config_dbg_show, 685eadff302SIvan T. Ivanov }; 686eadff302SIvan T. Ivanov 687eadff302SIvan T. Ivanov static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned pin) 688eadff302SIvan T. Ivanov { 689c52d9df1SLinus Walleij struct pmic_gpio_state *state = gpiochip_get_data(chip); 690eadff302SIvan T. Ivanov unsigned long config; 691eadff302SIvan T. Ivanov 692eadff302SIvan T. Ivanov config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1); 693eadff302SIvan T. Ivanov 694eadff302SIvan T. Ivanov return pmic_gpio_config_set(state->ctrl, pin, &config, 1); 695eadff302SIvan T. Ivanov } 696eadff302SIvan T. Ivanov 697eadff302SIvan T. Ivanov static int pmic_gpio_direction_output(struct gpio_chip *chip, 698eadff302SIvan T. Ivanov unsigned pin, int val) 699eadff302SIvan T. Ivanov { 700c52d9df1SLinus Walleij struct pmic_gpio_state *state = gpiochip_get_data(chip); 701eadff302SIvan T. Ivanov unsigned long config; 702eadff302SIvan T. Ivanov 703eadff302SIvan T. Ivanov config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val); 704eadff302SIvan T. Ivanov 705eadff302SIvan T. Ivanov return pmic_gpio_config_set(state->ctrl, pin, &config, 1); 706eadff302SIvan T. Ivanov } 707eadff302SIvan T. Ivanov 708eadff302SIvan T. Ivanov static int pmic_gpio_get(struct gpio_chip *chip, unsigned pin) 709eadff302SIvan T. Ivanov { 710c52d9df1SLinus Walleij struct pmic_gpio_state *state = gpiochip_get_data(chip); 711eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad; 712eadff302SIvan T. Ivanov int ret; 713eadff302SIvan T. Ivanov 714eadff302SIvan T. Ivanov pad = state->ctrl->desc->pins[pin].drv_data; 715eadff302SIvan T. Ivanov 716eadff302SIvan T. Ivanov if (!pad->is_enabled) 717eadff302SIvan T. Ivanov return -EINVAL; 718eadff302SIvan T. Ivanov 719eadff302SIvan T. Ivanov if (pad->input_enabled) { 720eadff302SIvan T. Ivanov ret = pmic_gpio_read(state, pad, PMIC_MPP_REG_RT_STS); 721eadff302SIvan T. Ivanov if (ret < 0) 722eadff302SIvan T. Ivanov return ret; 723eadff302SIvan T. Ivanov 724eadff302SIvan T. Ivanov pad->out_value = ret & PMIC_MPP_REG_RT_STS_VAL_MASK; 725eadff302SIvan T. Ivanov } 726eadff302SIvan T. Ivanov 72786c1a219SLinus Walleij return !!pad->out_value; 728eadff302SIvan T. Ivanov } 729eadff302SIvan T. Ivanov 730eadff302SIvan T. Ivanov static void pmic_gpio_set(struct gpio_chip *chip, unsigned pin, int value) 731eadff302SIvan T. Ivanov { 732c52d9df1SLinus Walleij struct pmic_gpio_state *state = gpiochip_get_data(chip); 733eadff302SIvan T. Ivanov unsigned long config; 734eadff302SIvan T. Ivanov 735eadff302SIvan T. Ivanov config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value); 736eadff302SIvan T. Ivanov 737eadff302SIvan T. Ivanov pmic_gpio_config_set(state->ctrl, pin, &config, 1); 738eadff302SIvan T. Ivanov } 739eadff302SIvan T. Ivanov 740eadff302SIvan T. Ivanov static int pmic_gpio_of_xlate(struct gpio_chip *chip, 741eadff302SIvan T. Ivanov const struct of_phandle_args *gpio_desc, 742eadff302SIvan T. Ivanov u32 *flags) 743eadff302SIvan T. Ivanov { 744eadff302SIvan T. Ivanov if (chip->of_gpio_n_cells < 2) 745eadff302SIvan T. Ivanov return -EINVAL; 746eadff302SIvan T. Ivanov 747eadff302SIvan T. Ivanov if (flags) 748eadff302SIvan T. Ivanov *flags = gpio_desc->args[1]; 749eadff302SIvan T. Ivanov 750eadff302SIvan T. Ivanov return gpio_desc->args[0] - PMIC_GPIO_PHYSICAL_OFFSET; 751eadff302SIvan T. Ivanov } 752eadff302SIvan T. Ivanov 753eadff302SIvan T. Ivanov static void pmic_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) 754eadff302SIvan T. Ivanov { 755c52d9df1SLinus Walleij struct pmic_gpio_state *state = gpiochip_get_data(chip); 756eadff302SIvan T. Ivanov unsigned i; 757eadff302SIvan T. Ivanov 758eadff302SIvan T. Ivanov for (i = 0; i < chip->ngpio; i++) { 759eadff302SIvan T. Ivanov pmic_gpio_config_dbg_show(state->ctrl, s, i); 760eadff302SIvan T. Ivanov seq_puts(s, "\n"); 761eadff302SIvan T. Ivanov } 762eadff302SIvan T. Ivanov } 763eadff302SIvan T. Ivanov 764eadff302SIvan T. Ivanov static const struct gpio_chip pmic_gpio_gpio_template = { 765eadff302SIvan T. Ivanov .direction_input = pmic_gpio_direction_input, 766eadff302SIvan T. Ivanov .direction_output = pmic_gpio_direction_output, 767eadff302SIvan T. Ivanov .get = pmic_gpio_get, 768eadff302SIvan T. Ivanov .set = pmic_gpio_set, 76998c85d58SJonas Gorski .request = gpiochip_generic_request, 77098c85d58SJonas Gorski .free = gpiochip_generic_free, 771eadff302SIvan T. Ivanov .of_xlate = pmic_gpio_of_xlate, 772eadff302SIvan T. Ivanov .dbg_show = pmic_gpio_dbg_show, 773eadff302SIvan T. Ivanov }; 774eadff302SIvan T. Ivanov 775eadff302SIvan T. Ivanov static int pmic_gpio_populate(struct pmic_gpio_state *state, 776eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad) 777eadff302SIvan T. Ivanov { 778eadff302SIvan T. Ivanov int type, subtype, val, dir; 779eadff302SIvan T. Ivanov 780eadff302SIvan T. Ivanov type = pmic_gpio_read(state, pad, PMIC_GPIO_REG_TYPE); 781eadff302SIvan T. Ivanov if (type < 0) 782eadff302SIvan T. Ivanov return type; 783eadff302SIvan T. Ivanov 784eadff302SIvan T. Ivanov if (type != PMIC_GPIO_TYPE) { 785eadff302SIvan T. Ivanov dev_err(state->dev, "incorrect block type 0x%x at 0x%x\n", 786eadff302SIvan T. Ivanov type, pad->base); 787eadff302SIvan T. Ivanov return -ENODEV; 788eadff302SIvan T. Ivanov } 789eadff302SIvan T. Ivanov 790eadff302SIvan T. Ivanov subtype = pmic_gpio_read(state, pad, PMIC_GPIO_REG_SUBTYPE); 791eadff302SIvan T. Ivanov if (subtype < 0) 792eadff302SIvan T. Ivanov return subtype; 793eadff302SIvan T. Ivanov 794eadff302SIvan T. Ivanov switch (subtype) { 795eadff302SIvan T. Ivanov case PMIC_GPIO_SUBTYPE_GPIO_4CH: 796eadff302SIvan T. Ivanov pad->have_buffer = true; 7971586f556SGustavo A. R. Silva fallthrough; 798eadff302SIvan T. Ivanov case PMIC_GPIO_SUBTYPE_GPIOC_4CH: 799eadff302SIvan T. Ivanov pad->num_sources = 4; 800eadff302SIvan T. Ivanov break; 801eadff302SIvan T. Ivanov case PMIC_GPIO_SUBTYPE_GPIO_8CH: 802eadff302SIvan T. Ivanov pad->have_buffer = true; 8031586f556SGustavo A. R. Silva fallthrough; 804eadff302SIvan T. Ivanov case PMIC_GPIO_SUBTYPE_GPIOC_8CH: 805eadff302SIvan T. Ivanov pad->num_sources = 8; 806eadff302SIvan T. Ivanov break; 807d7b5f5ccSFenglin Wu case PMIC_GPIO_SUBTYPE_GPIO_LV: 808d7b5f5ccSFenglin Wu pad->num_sources = 1; 809d7b5f5ccSFenglin Wu pad->have_buffer = true; 810d7b5f5ccSFenglin Wu pad->lv_mv_type = true; 811d7b5f5ccSFenglin Wu break; 812d7b5f5ccSFenglin Wu case PMIC_GPIO_SUBTYPE_GPIO_MV: 813d7b5f5ccSFenglin Wu pad->num_sources = 2; 814d7b5f5ccSFenglin Wu pad->have_buffer = true; 815d7b5f5ccSFenglin Wu pad->lv_mv_type = true; 816d7b5f5ccSFenglin Wu break; 817eadff302SIvan T. Ivanov default: 818eadff302SIvan T. Ivanov dev_err(state->dev, "unknown GPIO type 0x%x\n", subtype); 819eadff302SIvan T. Ivanov return -ENODEV; 820eadff302SIvan T. Ivanov } 821eadff302SIvan T. Ivanov 822d7b5f5ccSFenglin Wu if (pad->lv_mv_type) { 823d7b5f5ccSFenglin Wu val = pmic_gpio_read(state, pad, 824d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL); 825d7b5f5ccSFenglin Wu if (val < 0) 826d7b5f5ccSFenglin Wu return val; 827d7b5f5ccSFenglin Wu 828d7b5f5ccSFenglin Wu pad->out_value = !!(val & PMIC_GPIO_LV_MV_OUTPUT_INVERT); 829d7b5f5ccSFenglin Wu pad->function = val & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK; 830d7b5f5ccSFenglin Wu 831d7b5f5ccSFenglin Wu val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL); 832d7b5f5ccSFenglin Wu if (val < 0) 833d7b5f5ccSFenglin Wu return val; 834d7b5f5ccSFenglin Wu 835d7b5f5ccSFenglin Wu dir = val & PMIC_GPIO_REG_LV_MV_MODE_DIR_MASK; 836d7b5f5ccSFenglin Wu } else { 837eadff302SIvan T. Ivanov val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL); 838eadff302SIvan T. Ivanov if (val < 0) 839eadff302SIvan T. Ivanov return val; 840eadff302SIvan T. Ivanov 841eadff302SIvan T. Ivanov pad->out_value = val & PMIC_GPIO_REG_MODE_VALUE_SHIFT; 842eadff302SIvan T. Ivanov 843eadff302SIvan T. Ivanov dir = val >> PMIC_GPIO_REG_MODE_DIR_SHIFT; 844eadff302SIvan T. Ivanov dir &= PMIC_GPIO_REG_MODE_DIR_MASK; 845d7b5f5ccSFenglin Wu pad->function = val >> PMIC_GPIO_REG_MODE_FUNCTION_SHIFT; 846d7b5f5ccSFenglin Wu pad->function &= PMIC_GPIO_REG_MODE_FUNCTION_MASK; 847d7b5f5ccSFenglin Wu } 848d7b5f5ccSFenglin Wu 849eadff302SIvan T. Ivanov switch (dir) { 850d7b5f5ccSFenglin Wu case PMIC_GPIO_MODE_DIGITAL_INPUT: 851eadff302SIvan T. Ivanov pad->input_enabled = true; 852eadff302SIvan T. Ivanov pad->output_enabled = false; 853eadff302SIvan T. Ivanov break; 854d7b5f5ccSFenglin Wu case PMIC_GPIO_MODE_DIGITAL_OUTPUT: 855eadff302SIvan T. Ivanov pad->input_enabled = false; 856eadff302SIvan T. Ivanov pad->output_enabled = true; 857eadff302SIvan T. Ivanov break; 858d7b5f5ccSFenglin Wu case PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT: 859eadff302SIvan T. Ivanov pad->input_enabled = true; 860eadff302SIvan T. Ivanov pad->output_enabled = true; 861eadff302SIvan T. Ivanov break; 862d7b5f5ccSFenglin Wu case PMIC_GPIO_MODE_ANALOG_PASS_THRU: 863d7b5f5ccSFenglin Wu if (!pad->lv_mv_type) 864d7b5f5ccSFenglin Wu return -ENODEV; 865d7b5f5ccSFenglin Wu pad->analog_pass = true; 866d7b5f5ccSFenglin Wu break; 867eadff302SIvan T. Ivanov default: 868eadff302SIvan T. Ivanov dev_err(state->dev, "unknown GPIO direction\n"); 869eadff302SIvan T. Ivanov return -ENODEV; 870eadff302SIvan T. Ivanov } 871eadff302SIvan T. Ivanov 872eadff302SIvan T. Ivanov val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL); 873eadff302SIvan T. Ivanov if (val < 0) 874eadff302SIvan T. Ivanov return val; 875eadff302SIvan T. Ivanov 876eadff302SIvan T. Ivanov pad->power_source = val >> PMIC_GPIO_REG_VIN_SHIFT; 877eadff302SIvan T. Ivanov pad->power_source &= PMIC_GPIO_REG_VIN_MASK; 878eadff302SIvan T. Ivanov 879eadff302SIvan T. Ivanov val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_PULL_CTL); 880eadff302SIvan T. Ivanov if (val < 0) 881eadff302SIvan T. Ivanov return val; 882eadff302SIvan T. Ivanov 883eadff302SIvan T. Ivanov pad->pullup = val >> PMIC_GPIO_REG_PULL_SHIFT; 884eadff302SIvan T. Ivanov pad->pullup &= PMIC_GPIO_REG_PULL_MASK; 885eadff302SIvan T. Ivanov 886223463fcSFenglin Wu val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_IN_CTL); 887223463fcSFenglin Wu if (val < 0) 888223463fcSFenglin Wu return val; 889223463fcSFenglin Wu 890223463fcSFenglin Wu if (pad->lv_mv_type && (val & PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN)) 891223463fcSFenglin Wu pad->dtest_buffer = 892223463fcSFenglin Wu (val & PMIC_GPIO_LV_MV_DIG_IN_DTEST_SEL_MASK) + 1; 893223463fcSFenglin Wu else if (!pad->lv_mv_type) 894223463fcSFenglin Wu pad->dtest_buffer = ffs(val); 895223463fcSFenglin Wu else 896223463fcSFenglin Wu pad->dtest_buffer = 0; 897223463fcSFenglin Wu 898223463fcSFenglin Wu val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL); 899223463fcSFenglin Wu if (val < 0) 900223463fcSFenglin Wu return val; 901223463fcSFenglin Wu 902eadff302SIvan T. Ivanov pad->strength = val >> PMIC_GPIO_REG_OUT_STRENGTH_SHIFT; 903eadff302SIvan T. Ivanov pad->strength &= PMIC_GPIO_REG_OUT_STRENGTH_MASK; 904eadff302SIvan T. Ivanov 905eadff302SIvan T. Ivanov pad->buffer_type = val >> PMIC_GPIO_REG_OUT_TYPE_SHIFT; 906eadff302SIvan T. Ivanov pad->buffer_type &= PMIC_GPIO_REG_OUT_TYPE_MASK; 907eadff302SIvan T. Ivanov 908d7b5f5ccSFenglin Wu if (pad->lv_mv_type) { 909d7b5f5ccSFenglin Wu val = pmic_gpio_read(state, pad, 910d7b5f5ccSFenglin Wu PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL); 911d7b5f5ccSFenglin Wu if (val < 0) 912d7b5f5ccSFenglin Wu return val; 913d7b5f5ccSFenglin Wu pad->atest = (val & PMIC_GPIO_LV_MV_ANA_MUX_SEL_MASK) + 1; 914d7b5f5ccSFenglin Wu } 915d7b5f5ccSFenglin Wu 916eadff302SIvan T. Ivanov /* Pin could be disabled with PIN_CONFIG_BIAS_HIGH_IMPEDANCE */ 917eadff302SIvan T. Ivanov pad->is_enabled = true; 918eadff302SIvan T. Ivanov return 0; 919eadff302SIvan T. Ivanov } 920eadff302SIvan T. Ivanov 921ca69e2d1SBrian Masney static int pmic_gpio_domain_translate(struct irq_domain *domain, 922ca69e2d1SBrian Masney struct irq_fwspec *fwspec, 923ca69e2d1SBrian Masney unsigned long *hwirq, 924ca69e2d1SBrian Masney unsigned int *type) 925ca69e2d1SBrian Masney { 926ca69e2d1SBrian Masney struct pmic_gpio_state *state = container_of(domain->host_data, 927ca69e2d1SBrian Masney struct pmic_gpio_state, 928ca69e2d1SBrian Masney chip); 929ca69e2d1SBrian Masney 930dac7da98SBjorn Andersson if (fwspec->param_count != 2 || 931dac7da98SBjorn Andersson fwspec->param[0] < 1 || fwspec->param[0] > state->chip.ngpio) 932ca69e2d1SBrian Masney return -EINVAL; 933ca69e2d1SBrian Masney 934ca69e2d1SBrian Masney *hwirq = fwspec->param[0] - PMIC_GPIO_PHYSICAL_OFFSET; 935ca69e2d1SBrian Masney *type = fwspec->param[1]; 936ca69e2d1SBrian Masney 937ca69e2d1SBrian Masney return 0; 938ca69e2d1SBrian Masney } 939ca69e2d1SBrian Masney 940821c76c4SBrian Masney static unsigned int pmic_gpio_child_offset_to_irq(struct gpio_chip *chip, 941821c76c4SBrian Masney unsigned int offset) 942ca69e2d1SBrian Masney { 943821c76c4SBrian Masney return offset + PMIC_GPIO_PHYSICAL_OFFSET; 944ca69e2d1SBrian Masney } 945ca69e2d1SBrian Masney 946821c76c4SBrian Masney static int pmic_gpio_child_to_parent_hwirq(struct gpio_chip *chip, 947821c76c4SBrian Masney unsigned int child_hwirq, 948821c76c4SBrian Masney unsigned int child_type, 949821c76c4SBrian Masney unsigned int *parent_hwirq, 950821c76c4SBrian Masney unsigned int *parent_type) 951821c76c4SBrian Masney { 952821c76c4SBrian Masney *parent_hwirq = child_hwirq + 0xc0; 953821c76c4SBrian Masney *parent_type = child_type; 954821c76c4SBrian Masney 955821c76c4SBrian Masney return 0; 956821c76c4SBrian Masney } 957ca69e2d1SBrian Masney 958eadff302SIvan T. Ivanov static int pmic_gpio_probe(struct platform_device *pdev) 959eadff302SIvan T. Ivanov { 960ca69e2d1SBrian Masney struct irq_domain *parent_domain; 961ca69e2d1SBrian Masney struct device_node *parent_node; 962eadff302SIvan T. Ivanov struct device *dev = &pdev->dev; 963eadff302SIvan T. Ivanov struct pinctrl_pin_desc *pindesc; 964eadff302SIvan T. Ivanov struct pinctrl_desc *pctrldesc; 965eadff302SIvan T. Ivanov struct pmic_gpio_pad *pad, *pads; 966eadff302SIvan T. Ivanov struct pmic_gpio_state *state; 967821c76c4SBrian Masney struct gpio_irq_chip *girq; 968eadff302SIvan T. Ivanov int ret, npins, i; 969ab4256cfSStephen Boyd u32 reg; 970eadff302SIvan T. Ivanov 971ab4256cfSStephen Boyd ret = of_property_read_u32(dev->of_node, "reg", ®); 972eadff302SIvan T. Ivanov if (ret < 0) { 973ab4256cfSStephen Boyd dev_err(dev, "missing base address"); 974eadff302SIvan T. Ivanov return ret; 975eadff302SIvan T. Ivanov } 976eadff302SIvan T. Ivanov 977cfacef37SBrian Masney npins = (uintptr_t) device_get_match_data(&pdev->dev); 978eadff302SIvan T. Ivanov 979eadff302SIvan T. Ivanov state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); 980eadff302SIvan T. Ivanov if (!state) 981eadff302SIvan T. Ivanov return -ENOMEM; 982eadff302SIvan T. Ivanov 983eadff302SIvan T. Ivanov platform_set_drvdata(pdev, state); 984eadff302SIvan T. Ivanov 985eadff302SIvan T. Ivanov state->dev = &pdev->dev; 986eadff302SIvan T. Ivanov state->map = dev_get_regmap(dev->parent, NULL); 987eadff302SIvan T. Ivanov 988eadff302SIvan T. Ivanov pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL); 989eadff302SIvan T. Ivanov if (!pindesc) 990eadff302SIvan T. Ivanov return -ENOMEM; 991eadff302SIvan T. Ivanov 992eadff302SIvan T. Ivanov pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL); 993eadff302SIvan T. Ivanov if (!pads) 994eadff302SIvan T. Ivanov return -ENOMEM; 995eadff302SIvan T. Ivanov 996eadff302SIvan T. Ivanov pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL); 997eadff302SIvan T. Ivanov if (!pctrldesc) 998eadff302SIvan T. Ivanov return -ENOMEM; 999eadff302SIvan T. Ivanov 1000eadff302SIvan T. Ivanov pctrldesc->pctlops = &pmic_gpio_pinctrl_ops; 1001eadff302SIvan T. Ivanov pctrldesc->pmxops = &pmic_gpio_pinmux_ops; 1002eadff302SIvan T. Ivanov pctrldesc->confops = &pmic_gpio_pinconf_ops; 1003eadff302SIvan T. Ivanov pctrldesc->owner = THIS_MODULE; 1004eadff302SIvan T. Ivanov pctrldesc->name = dev_name(dev); 1005eadff302SIvan T. Ivanov pctrldesc->pins = pindesc; 1006eadff302SIvan T. Ivanov pctrldesc->npins = npins; 1007f684e4acSLinus Walleij pctrldesc->num_custom_params = ARRAY_SIZE(pmic_gpio_bindings); 1008f684e4acSLinus Walleij pctrldesc->custom_params = pmic_gpio_bindings; 10094f06266aSArnd Bergmann #ifdef CONFIG_DEBUG_FS 1010f684e4acSLinus Walleij pctrldesc->custom_conf_items = pmic_conf_items; 10114f06266aSArnd Bergmann #endif 1012eadff302SIvan T. Ivanov 1013eadff302SIvan T. Ivanov for (i = 0; i < npins; i++, pindesc++) { 1014eadff302SIvan T. Ivanov pad = &pads[i]; 1015eadff302SIvan T. Ivanov pindesc->drv_data = pad; 1016eadff302SIvan T. Ivanov pindesc->number = i; 1017eadff302SIvan T. Ivanov pindesc->name = pmic_gpio_groups[i]; 1018eadff302SIvan T. Ivanov 1019ab4256cfSStephen Boyd pad->base = reg + i * PMIC_GPIO_ADDRESS_RANGE; 1020eadff302SIvan T. Ivanov 1021eadff302SIvan T. Ivanov ret = pmic_gpio_populate(state, pad); 1022eadff302SIvan T. Ivanov if (ret < 0) 1023eadff302SIvan T. Ivanov return ret; 1024eadff302SIvan T. Ivanov } 1025eadff302SIvan T. Ivanov 1026eadff302SIvan T. Ivanov state->chip = pmic_gpio_gpio_template; 102758383c78SLinus Walleij state->chip.parent = dev; 1028eadff302SIvan T. Ivanov state->chip.base = -1; 1029eadff302SIvan T. Ivanov state->chip.ngpio = npins; 1030eadff302SIvan T. Ivanov state->chip.label = dev_name(dev); 1031eadff302SIvan T. Ivanov state->chip.of_gpio_n_cells = 2; 1032eadff302SIvan T. Ivanov state->chip.can_sleep = false; 1033eadff302SIvan T. Ivanov 1034b46ddfe6SLaxman Dewangan state->ctrl = devm_pinctrl_register(dev, pctrldesc, state); 1035323de9efSMasahiro Yamada if (IS_ERR(state->ctrl)) 1036323de9efSMasahiro Yamada return PTR_ERR(state->ctrl); 1037eadff302SIvan T. Ivanov 1038ca69e2d1SBrian Masney parent_node = of_irq_find_parent(state->dev->of_node); 1039ca69e2d1SBrian Masney if (!parent_node) 1040ca69e2d1SBrian Masney return -ENXIO; 1041ca69e2d1SBrian Masney 1042ca69e2d1SBrian Masney parent_domain = irq_find_host(parent_node); 1043ca69e2d1SBrian Masney of_node_put(parent_node); 1044ca69e2d1SBrian Masney if (!parent_domain) 1045ca69e2d1SBrian Masney return -ENXIO; 1046ca69e2d1SBrian Masney 10475e503115SDmitry Baryshkov state->irq.name = "spmi-gpio", 10485e503115SDmitry Baryshkov state->irq.irq_ack = irq_chip_ack_parent, 10495e503115SDmitry Baryshkov state->irq.irq_mask = irq_chip_mask_parent, 10505e503115SDmitry Baryshkov state->irq.irq_unmask = irq_chip_unmask_parent, 10515e503115SDmitry Baryshkov state->irq.irq_set_type = irq_chip_set_type_parent, 10525e503115SDmitry Baryshkov state->irq.irq_set_wake = irq_chip_set_wake_parent, 10535e503115SDmitry Baryshkov state->irq.flags = IRQCHIP_MASK_ON_SUSPEND, 10545e503115SDmitry Baryshkov 1055821c76c4SBrian Masney girq = &state->chip.irq; 10565e503115SDmitry Baryshkov girq->chip = &state->irq; 1057821c76c4SBrian Masney girq->default_type = IRQ_TYPE_NONE; 1058821c76c4SBrian Masney girq->handler = handle_level_irq; 1059821c76c4SBrian Masney girq->fwnode = of_node_to_fwnode(state->dev->of_node); 1060821c76c4SBrian Masney girq->parent_domain = parent_domain; 1061821c76c4SBrian Masney girq->child_to_parent_hwirq = pmic_gpio_child_to_parent_hwirq; 106224258761SKevin Hao girq->populate_parent_alloc_arg = gpiochip_populate_parent_fwspec_fourcell; 1063821c76c4SBrian Masney girq->child_offset_to_irq = pmic_gpio_child_offset_to_irq; 1064821c76c4SBrian Masney girq->child_irq_domain_ops.translate = pmic_gpio_domain_translate; 1065ca69e2d1SBrian Masney 1066c52d9df1SLinus Walleij ret = gpiochip_add_data(&state->chip, state); 1067eadff302SIvan T. Ivanov if (ret) { 1068eadff302SIvan T. Ivanov dev_err(state->dev, "can't add gpio chip\n"); 1069821c76c4SBrian Masney return ret; 1070eadff302SIvan T. Ivanov } 1071eadff302SIvan T. Ivanov 1072149a9604SBrian Masney /* 1073149a9604SBrian Masney * For DeviceTree-supported systems, the gpio core checks the 1074149a9604SBrian Masney * pinctrl's device node for the "gpio-ranges" property. 1075149a9604SBrian Masney * If it is present, it takes care of adding the pin ranges 1076149a9604SBrian Masney * for the driver. In this case the driver can skip ahead. 1077149a9604SBrian Masney * 1078149a9604SBrian Masney * In order to remain compatible with older, existing DeviceTree 1079149a9604SBrian Masney * files which don't set the "gpio-ranges" property or systems that 1080149a9604SBrian Masney * utilize ACPI the driver has to call gpiochip_add_pin_range(). 1081149a9604SBrian Masney */ 1082149a9604SBrian Masney if (!of_property_read_bool(dev->of_node, "gpio-ranges")) { 1083149a9604SBrian Masney ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, 1084149a9604SBrian Masney npins); 1085eadff302SIvan T. Ivanov if (ret) { 1086eadff302SIvan T. Ivanov dev_err(dev, "failed to add pin range\n"); 1087eadff302SIvan T. Ivanov goto err_range; 1088eadff302SIvan T. Ivanov } 1089149a9604SBrian Masney } 1090eadff302SIvan T. Ivanov 1091eadff302SIvan T. Ivanov return 0; 1092eadff302SIvan T. Ivanov 1093eadff302SIvan T. Ivanov err_range: 1094eadff302SIvan T. Ivanov gpiochip_remove(&state->chip); 1095eadff302SIvan T. Ivanov return ret; 1096eadff302SIvan T. Ivanov } 1097eadff302SIvan T. Ivanov 1098eadff302SIvan T. Ivanov static int pmic_gpio_remove(struct platform_device *pdev) 1099eadff302SIvan T. Ivanov { 1100eadff302SIvan T. Ivanov struct pmic_gpio_state *state = platform_get_drvdata(pdev); 1101eadff302SIvan T. Ivanov 1102eadff302SIvan T. Ivanov gpiochip_remove(&state->chip); 1103eadff302SIvan T. Ivanov return 0; 1104eadff302SIvan T. Ivanov } 1105eadff302SIvan T. Ivanov 1106eadff302SIvan T. Ivanov static const struct of_device_id pmic_gpio_of_match[] = { 110717cc38e7SKonrad Dybcio /* pm660 has 13 GPIOs with holes on 1, 5, 6, 7, 8 and 10 */ 110817cc38e7SKonrad Dybcio { .compatible = "qcom,pm660-gpio", .data = (void *) 13 }, 110917cc38e7SKonrad Dybcio /* pm660l has 12 GPIOs with holes on 1, 2, 10, 11 and 12 */ 111017cc38e7SKonrad Dybcio { .compatible = "qcom,pm660l-gpio", .data = (void *) 12 }, 1111*4afc2a0cSBhupesh Sharma { .compatible = "qcom,pm6150-gpio", .data = (void *) 10 }, 1112*4afc2a0cSBhupesh Sharma { .compatible = "qcom,pm6150l-gpio", .data = (void *) 12 }, 1113*4afc2a0cSBhupesh Sharma { .compatible = "qcom,pm7325-gpio", .data = (void *) 10 }, 1114*4afc2a0cSBhupesh Sharma { .compatible = "qcom,pm8005-gpio", .data = (void *) 4 }, 1115*4afc2a0cSBhupesh Sharma { .compatible = "qcom,pm8008-gpio", .data = (void *) 2 }, 1116d67070c3SVinod Koul /* pm8150 has 10 GPIOs with holes on 2, 5, 7 and 8 */ 1117d67070c3SVinod Koul { .compatible = "qcom,pm8150-gpio", .data = (void *) 10 }, 11189bdacaddSVinod Koul /* pm8150b has 12 GPIOs with holes on 3, r and 7 */ 11199bdacaddSVinod Koul { .compatible = "qcom,pm8150b-gpio", .data = (void *) 12 }, 11202dc889a8SVinod Koul /* pm8150l has 12 GPIOs with holes on 7 */ 11212dc889a8SVinod Koul { .compatible = "qcom,pm8150l-gpio", .data = (void *) 12 }, 112257c0a4f0SVinod Koul { .compatible = "qcom,pm8350-gpio", .data = (void *) 10 }, 112357c0a4f0SVinod Koul { .compatible = "qcom,pm8350b-gpio", .data = (void *) 8 }, 112457c0a4f0SVinod Koul { .compatible = "qcom,pm8350c-gpio", .data = (void *) 9 }, 1125*4afc2a0cSBhupesh Sharma { .compatible = "qcom,pm8916-gpio", .data = (void *) 4 }, 1126*4afc2a0cSBhupesh Sharma { .compatible = "qcom,pm8941-gpio", .data = (void *) 36 }, 1127*4afc2a0cSBhupesh Sharma /* pm8950 has 8 GPIOs with holes on 3 */ 1128*4afc2a0cSBhupesh Sharma { .compatible = "qcom,pm8950-gpio", .data = (void *) 8 }, 1129*4afc2a0cSBhupesh Sharma { .compatible = "qcom,pm8994-gpio", .data = (void *) 22 }, 1130*4afc2a0cSBhupesh Sharma { .compatible = "qcom,pm8998-gpio", .data = (void *) 26 }, 1131*4afc2a0cSBhupesh Sharma { .compatible = "qcom,pma8084-gpio", .data = (void *) 22 }, 1132*4afc2a0cSBhupesh Sharma { .compatible = "qcom,pmi8950-gpio", .data = (void *) 2 }, 1133*4afc2a0cSBhupesh Sharma { .compatible = "qcom,pmi8994-gpio", .data = (void *) 10 }, 1134*4afc2a0cSBhupesh Sharma { .compatible = "qcom,pmi8998-gpio", .data = (void *) 14 }, 113557c0a4f0SVinod Koul { .compatible = "qcom,pmk8350-gpio", .data = (void *) 4 }, 113657c0a4f0SVinod Koul { .compatible = "qcom,pmr735a-gpio", .data = (void *) 4 }, 113757c0a4f0SVinod Koul { .compatible = "qcom,pmr735b-gpio", .data = (void *) 4 }, 1138*4afc2a0cSBhupesh Sharma /* pms405 has 12 GPIOs with holes on 1, 9, and 10 */ 1139*4afc2a0cSBhupesh Sharma { .compatible = "qcom,pms405-gpio", .data = (void *) 12 }, 1140ceb58de4SVinod Koul /* pmx55 has 11 GPIOs with holes on 3, 7, 10, 11 */ 1141ceb58de4SVinod Koul { .compatible = "qcom,pmx55-gpio", .data = (void *) 11 }, 1142eadff302SIvan T. Ivanov { }, 1143eadff302SIvan T. Ivanov }; 1144eadff302SIvan T. Ivanov 1145eadff302SIvan T. Ivanov MODULE_DEVICE_TABLE(of, pmic_gpio_of_match); 1146eadff302SIvan T. Ivanov 1147eadff302SIvan T. Ivanov static struct platform_driver pmic_gpio_driver = { 1148eadff302SIvan T. Ivanov .driver = { 1149eadff302SIvan T. Ivanov .name = "qcom-spmi-gpio", 1150eadff302SIvan T. Ivanov .of_match_table = pmic_gpio_of_match, 1151eadff302SIvan T. Ivanov }, 1152eadff302SIvan T. Ivanov .probe = pmic_gpio_probe, 1153eadff302SIvan T. Ivanov .remove = pmic_gpio_remove, 1154eadff302SIvan T. Ivanov }; 1155eadff302SIvan T. Ivanov 1156eadff302SIvan T. Ivanov module_platform_driver(pmic_gpio_driver); 1157eadff302SIvan T. Ivanov 1158eadff302SIvan T. Ivanov MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>"); 1159eadff302SIvan T. Ivanov MODULE_DESCRIPTION("Qualcomm SPMI PMIC GPIO pin control driver"); 1160eadff302SIvan T. Ivanov MODULE_ALIAS("platform:qcom-spmi-gpio"); 1161eadff302SIvan T. Ivanov MODULE_LICENSE("GPL v2"); 1162