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