18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2015, Sony Mobile Communications AB. 48c2ecf20Sopenharmony_ci * Copyright (c) 2013, The Linux Foundation. All rights reserved. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 98c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinctrl.h> 108c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinmux.h> 118c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf.h> 128c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf-generic.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/regmap.h> 158c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/of_device.h> 188c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <dt-bindings/pinctrl/qcom,pmic-gpio.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "../core.h" 238c2ecf20Sopenharmony_ci#include "../pinctrl-utils.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* mode */ 268c2ecf20Sopenharmony_ci#define PM8XXX_GPIO_MODE_ENABLED BIT(0) 278c2ecf20Sopenharmony_ci#define PM8XXX_GPIO_MODE_INPUT 0 288c2ecf20Sopenharmony_ci#define PM8XXX_GPIO_MODE_OUTPUT 2 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* output buffer */ 318c2ecf20Sopenharmony_ci#define PM8XXX_GPIO_PUSH_PULL 0 328c2ecf20Sopenharmony_ci#define PM8XXX_GPIO_OPEN_DRAIN 1 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* bias */ 358c2ecf20Sopenharmony_ci#define PM8XXX_GPIO_BIAS_PU_30 0 368c2ecf20Sopenharmony_ci#define PM8XXX_GPIO_BIAS_PU_1P5 1 378c2ecf20Sopenharmony_ci#define PM8XXX_GPIO_BIAS_PU_31P5 2 388c2ecf20Sopenharmony_ci#define PM8XXX_GPIO_BIAS_PU_1P5_30 3 398c2ecf20Sopenharmony_ci#define PM8XXX_GPIO_BIAS_PD 4 408c2ecf20Sopenharmony_ci#define PM8XXX_GPIO_BIAS_NP 5 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* GPIO registers */ 438c2ecf20Sopenharmony_ci#define SSBI_REG_ADDR_GPIO_BASE 0x150 448c2ecf20Sopenharmony_ci#define SSBI_REG_ADDR_GPIO(n) (SSBI_REG_ADDR_GPIO_BASE + n) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define PM8XXX_BANK_WRITE BIT(7) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define PM8XXX_MAX_GPIOS 44 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define PM8XXX_GPIO_PHYSICAL_OFFSET 1 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* custom pinconf parameters */ 538c2ecf20Sopenharmony_ci#define PM8XXX_QCOM_DRIVE_STRENGH (PIN_CONFIG_END + 1) 548c2ecf20Sopenharmony_ci#define PM8XXX_QCOM_PULL_UP_STRENGTH (PIN_CONFIG_END + 2) 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/** 578c2ecf20Sopenharmony_ci * struct pm8xxx_pin_data - dynamic configuration for a pin 588c2ecf20Sopenharmony_ci * @reg: address of the control register 598c2ecf20Sopenharmony_ci * @power_source: logical selected voltage source, mapping in static data 608c2ecf20Sopenharmony_ci * is used translate to register values 618c2ecf20Sopenharmony_ci * @mode: operating mode for the pin (input/output) 628c2ecf20Sopenharmony_ci * @open_drain: output buffer configured as open-drain (vs push-pull) 638c2ecf20Sopenharmony_ci * @output_value: configured output value 648c2ecf20Sopenharmony_ci * @bias: register view of configured bias 658c2ecf20Sopenharmony_ci * @pull_up_strength: placeholder for selected pull up strength 668c2ecf20Sopenharmony_ci * only used to configure bias when pull up is selected 678c2ecf20Sopenharmony_ci * @output_strength: selector of output-strength 688c2ecf20Sopenharmony_ci * @disable: pin disabled / configured as tristate 698c2ecf20Sopenharmony_ci * @function: pinmux selector 708c2ecf20Sopenharmony_ci * @inverted: pin logic is inverted 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_cistruct pm8xxx_pin_data { 738c2ecf20Sopenharmony_ci unsigned reg; 748c2ecf20Sopenharmony_ci u8 power_source; 758c2ecf20Sopenharmony_ci u8 mode; 768c2ecf20Sopenharmony_ci bool open_drain; 778c2ecf20Sopenharmony_ci bool output_value; 788c2ecf20Sopenharmony_ci u8 bias; 798c2ecf20Sopenharmony_ci u8 pull_up_strength; 808c2ecf20Sopenharmony_ci u8 output_strength; 818c2ecf20Sopenharmony_ci bool disable; 828c2ecf20Sopenharmony_ci u8 function; 838c2ecf20Sopenharmony_ci bool inverted; 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistruct pm8xxx_gpio { 878c2ecf20Sopenharmony_ci struct device *dev; 888c2ecf20Sopenharmony_ci struct regmap *regmap; 898c2ecf20Sopenharmony_ci struct pinctrl_dev *pctrl; 908c2ecf20Sopenharmony_ci struct gpio_chip chip; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci struct pinctrl_desc desc; 938c2ecf20Sopenharmony_ci unsigned npins; 948c2ecf20Sopenharmony_ci}; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic const struct pinconf_generic_params pm8xxx_gpio_bindings[] = { 978c2ecf20Sopenharmony_ci {"qcom,drive-strength", PM8XXX_QCOM_DRIVE_STRENGH, 0}, 988c2ecf20Sopenharmony_ci {"qcom,pull-up-strength", PM8XXX_QCOM_PULL_UP_STRENGTH, 0}, 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 1028c2ecf20Sopenharmony_cistatic const struct pin_config_item pm8xxx_conf_items[ARRAY_SIZE(pm8xxx_gpio_bindings)] = { 1038c2ecf20Sopenharmony_ci PCONFDUMP(PM8XXX_QCOM_DRIVE_STRENGH, "drive-strength", NULL, true), 1048c2ecf20Sopenharmony_ci PCONFDUMP(PM8XXX_QCOM_PULL_UP_STRENGTH, "pull up strength", NULL, true), 1058c2ecf20Sopenharmony_ci}; 1068c2ecf20Sopenharmony_ci#endif 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic const char * const pm8xxx_groups[PM8XXX_MAX_GPIOS] = { 1098c2ecf20Sopenharmony_ci "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", "gpio8", 1108c2ecf20Sopenharmony_ci "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", "gpio15", 1118c2ecf20Sopenharmony_ci "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22", 1128c2ecf20Sopenharmony_ci "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29", 1138c2ecf20Sopenharmony_ci "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36", 1148c2ecf20Sopenharmony_ci "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", "gpio43", 1158c2ecf20Sopenharmony_ci "gpio44", 1168c2ecf20Sopenharmony_ci}; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic const char * const pm8xxx_gpio_functions[] = { 1198c2ecf20Sopenharmony_ci PMIC_GPIO_FUNC_NORMAL, PMIC_GPIO_FUNC_PAIRED, 1208c2ecf20Sopenharmony_ci PMIC_GPIO_FUNC_FUNC1, PMIC_GPIO_FUNC_FUNC2, 1218c2ecf20Sopenharmony_ci PMIC_GPIO_FUNC_DTEST1, PMIC_GPIO_FUNC_DTEST2, 1228c2ecf20Sopenharmony_ci PMIC_GPIO_FUNC_DTEST3, PMIC_GPIO_FUNC_DTEST4, 1238c2ecf20Sopenharmony_ci}; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int pm8xxx_read_bank(struct pm8xxx_gpio *pctrl, 1268c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin, int bank) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci unsigned int val = bank << 4; 1298c2ecf20Sopenharmony_ci int ret; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci ret = regmap_write(pctrl->regmap, pin->reg, val); 1328c2ecf20Sopenharmony_ci if (ret) { 1338c2ecf20Sopenharmony_ci dev_err(pctrl->dev, "failed to select bank %d\n", bank); 1348c2ecf20Sopenharmony_ci return ret; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci ret = regmap_read(pctrl->regmap, pin->reg, &val); 1388c2ecf20Sopenharmony_ci if (ret) { 1398c2ecf20Sopenharmony_ci dev_err(pctrl->dev, "failed to read register %d\n", bank); 1408c2ecf20Sopenharmony_ci return ret; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return val; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int pm8xxx_write_bank(struct pm8xxx_gpio *pctrl, 1478c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin, 1488c2ecf20Sopenharmony_ci int bank, 1498c2ecf20Sopenharmony_ci u8 val) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci int ret; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci val |= PM8XXX_BANK_WRITE; 1548c2ecf20Sopenharmony_ci val |= bank << 4; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci ret = regmap_write(pctrl->regmap, pin->reg, val); 1578c2ecf20Sopenharmony_ci if (ret) 1588c2ecf20Sopenharmony_ci dev_err(pctrl->dev, "failed to write register\n"); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return ret; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int pm8xxx_get_groups_count(struct pinctrl_dev *pctldev) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return pctrl->npins; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic const char *pm8xxx_get_group_name(struct pinctrl_dev *pctldev, 1718c2ecf20Sopenharmony_ci unsigned group) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci return pm8xxx_groups[group]; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic int pm8xxx_get_group_pins(struct pinctrl_dev *pctldev, 1788c2ecf20Sopenharmony_ci unsigned group, 1798c2ecf20Sopenharmony_ci const unsigned **pins, 1808c2ecf20Sopenharmony_ci unsigned *num_pins) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci *pins = &pctrl->desc.pins[group].number; 1858c2ecf20Sopenharmony_ci *num_pins = 1; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic const struct pinctrl_ops pm8xxx_pinctrl_ops = { 1918c2ecf20Sopenharmony_ci .get_groups_count = pm8xxx_get_groups_count, 1928c2ecf20Sopenharmony_ci .get_group_name = pm8xxx_get_group_name, 1938c2ecf20Sopenharmony_ci .get_group_pins = pm8xxx_get_group_pins, 1948c2ecf20Sopenharmony_ci .dt_node_to_map = pinconf_generic_dt_node_to_map_group, 1958c2ecf20Sopenharmony_ci .dt_free_map = pinctrl_utils_free_map, 1968c2ecf20Sopenharmony_ci}; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic int pm8xxx_get_functions_count(struct pinctrl_dev *pctldev) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci return ARRAY_SIZE(pm8xxx_gpio_functions); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic const char *pm8xxx_get_function_name(struct pinctrl_dev *pctldev, 2048c2ecf20Sopenharmony_ci unsigned function) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci return pm8xxx_gpio_functions[function]; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic int pm8xxx_get_function_groups(struct pinctrl_dev *pctldev, 2108c2ecf20Sopenharmony_ci unsigned function, 2118c2ecf20Sopenharmony_ci const char * const **groups, 2128c2ecf20Sopenharmony_ci unsigned * const num_groups) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci *groups = pm8xxx_groups; 2178c2ecf20Sopenharmony_ci *num_groups = pctrl->npins; 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic int pm8xxx_pinmux_set_mux(struct pinctrl_dev *pctldev, 2228c2ecf20Sopenharmony_ci unsigned function, 2238c2ecf20Sopenharmony_ci unsigned group) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev); 2268c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[group].drv_data; 2278c2ecf20Sopenharmony_ci u8 val; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci pin->function = function; 2308c2ecf20Sopenharmony_ci val = pin->function << 1; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci pm8xxx_write_bank(pctrl, pin, 4, val); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci return 0; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic const struct pinmux_ops pm8xxx_pinmux_ops = { 2388c2ecf20Sopenharmony_ci .get_functions_count = pm8xxx_get_functions_count, 2398c2ecf20Sopenharmony_ci .get_function_name = pm8xxx_get_function_name, 2408c2ecf20Sopenharmony_ci .get_function_groups = pm8xxx_get_function_groups, 2418c2ecf20Sopenharmony_ci .set_mux = pm8xxx_pinmux_set_mux, 2428c2ecf20Sopenharmony_ci}; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic int pm8xxx_pin_config_get(struct pinctrl_dev *pctldev, 2458c2ecf20Sopenharmony_ci unsigned int offset, 2468c2ecf20Sopenharmony_ci unsigned long *config) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev); 2498c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 2508c2ecf20Sopenharmony_ci unsigned param = pinconf_to_config_param(*config); 2518c2ecf20Sopenharmony_ci unsigned arg; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci switch (param) { 2548c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_DISABLE: 2558c2ecf20Sopenharmony_ci if (pin->bias != PM8XXX_GPIO_BIAS_NP) 2568c2ecf20Sopenharmony_ci return -EINVAL; 2578c2ecf20Sopenharmony_ci arg = 1; 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_DOWN: 2608c2ecf20Sopenharmony_ci if (pin->bias != PM8XXX_GPIO_BIAS_PD) 2618c2ecf20Sopenharmony_ci return -EINVAL; 2628c2ecf20Sopenharmony_ci arg = 1; 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 2658c2ecf20Sopenharmony_ci if (pin->bias > PM8XXX_GPIO_BIAS_PU_1P5_30) 2668c2ecf20Sopenharmony_ci return -EINVAL; 2678c2ecf20Sopenharmony_ci arg = 1; 2688c2ecf20Sopenharmony_ci break; 2698c2ecf20Sopenharmony_ci case PM8XXX_QCOM_PULL_UP_STRENGTH: 2708c2ecf20Sopenharmony_ci arg = pin->pull_up_strength; 2718c2ecf20Sopenharmony_ci break; 2728c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: 2738c2ecf20Sopenharmony_ci if (!pin->disable) 2748c2ecf20Sopenharmony_ci return -EINVAL; 2758c2ecf20Sopenharmony_ci arg = 1; 2768c2ecf20Sopenharmony_ci break; 2778c2ecf20Sopenharmony_ci case PIN_CONFIG_INPUT_ENABLE: 2788c2ecf20Sopenharmony_ci if (pin->mode != PM8XXX_GPIO_MODE_INPUT) 2798c2ecf20Sopenharmony_ci return -EINVAL; 2808c2ecf20Sopenharmony_ci arg = 1; 2818c2ecf20Sopenharmony_ci break; 2828c2ecf20Sopenharmony_ci case PIN_CONFIG_OUTPUT: 2838c2ecf20Sopenharmony_ci if (pin->mode & PM8XXX_GPIO_MODE_OUTPUT) 2848c2ecf20Sopenharmony_ci arg = pin->output_value; 2858c2ecf20Sopenharmony_ci else 2868c2ecf20Sopenharmony_ci arg = 0; 2878c2ecf20Sopenharmony_ci break; 2888c2ecf20Sopenharmony_ci case PIN_CONFIG_POWER_SOURCE: 2898c2ecf20Sopenharmony_ci arg = pin->power_source; 2908c2ecf20Sopenharmony_ci break; 2918c2ecf20Sopenharmony_ci case PM8XXX_QCOM_DRIVE_STRENGH: 2928c2ecf20Sopenharmony_ci arg = pin->output_strength; 2938c2ecf20Sopenharmony_ci break; 2948c2ecf20Sopenharmony_ci case PIN_CONFIG_DRIVE_PUSH_PULL: 2958c2ecf20Sopenharmony_ci if (pin->open_drain) 2968c2ecf20Sopenharmony_ci return -EINVAL; 2978c2ecf20Sopenharmony_ci arg = 1; 2988c2ecf20Sopenharmony_ci break; 2998c2ecf20Sopenharmony_ci case PIN_CONFIG_DRIVE_OPEN_DRAIN: 3008c2ecf20Sopenharmony_ci if (!pin->open_drain) 3018c2ecf20Sopenharmony_ci return -EINVAL; 3028c2ecf20Sopenharmony_ci arg = 1; 3038c2ecf20Sopenharmony_ci break; 3048c2ecf20Sopenharmony_ci default: 3058c2ecf20Sopenharmony_ci return -EINVAL; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci *config = pinconf_to_config_packed(param, arg); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci return 0; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic int pm8xxx_pin_config_set(struct pinctrl_dev *pctldev, 3148c2ecf20Sopenharmony_ci unsigned int offset, 3158c2ecf20Sopenharmony_ci unsigned long *configs, 3168c2ecf20Sopenharmony_ci unsigned num_configs) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev); 3198c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 3208c2ecf20Sopenharmony_ci unsigned param; 3218c2ecf20Sopenharmony_ci unsigned arg; 3228c2ecf20Sopenharmony_ci unsigned i; 3238c2ecf20Sopenharmony_ci u8 banks = 0; 3248c2ecf20Sopenharmony_ci u8 val; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci for (i = 0; i < num_configs; i++) { 3278c2ecf20Sopenharmony_ci param = pinconf_to_config_param(configs[i]); 3288c2ecf20Sopenharmony_ci arg = pinconf_to_config_argument(configs[i]); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci switch (param) { 3318c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_DISABLE: 3328c2ecf20Sopenharmony_ci pin->bias = PM8XXX_GPIO_BIAS_NP; 3338c2ecf20Sopenharmony_ci banks |= BIT(2); 3348c2ecf20Sopenharmony_ci pin->disable = 0; 3358c2ecf20Sopenharmony_ci banks |= BIT(3); 3368c2ecf20Sopenharmony_ci break; 3378c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_DOWN: 3388c2ecf20Sopenharmony_ci pin->bias = PM8XXX_GPIO_BIAS_PD; 3398c2ecf20Sopenharmony_ci banks |= BIT(2); 3408c2ecf20Sopenharmony_ci pin->disable = 0; 3418c2ecf20Sopenharmony_ci banks |= BIT(3); 3428c2ecf20Sopenharmony_ci break; 3438c2ecf20Sopenharmony_ci case PM8XXX_QCOM_PULL_UP_STRENGTH: 3448c2ecf20Sopenharmony_ci if (arg > PM8XXX_GPIO_BIAS_PU_1P5_30) { 3458c2ecf20Sopenharmony_ci dev_err(pctrl->dev, "invalid pull-up strength\n"); 3468c2ecf20Sopenharmony_ci return -EINVAL; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci pin->pull_up_strength = arg; 3498c2ecf20Sopenharmony_ci fallthrough; 3508c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 3518c2ecf20Sopenharmony_ci pin->bias = pin->pull_up_strength; 3528c2ecf20Sopenharmony_ci banks |= BIT(2); 3538c2ecf20Sopenharmony_ci pin->disable = 0; 3548c2ecf20Sopenharmony_ci banks |= BIT(3); 3558c2ecf20Sopenharmony_ci break; 3568c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: 3578c2ecf20Sopenharmony_ci pin->disable = 1; 3588c2ecf20Sopenharmony_ci banks |= BIT(3); 3598c2ecf20Sopenharmony_ci break; 3608c2ecf20Sopenharmony_ci case PIN_CONFIG_INPUT_ENABLE: 3618c2ecf20Sopenharmony_ci pin->mode = PM8XXX_GPIO_MODE_INPUT; 3628c2ecf20Sopenharmony_ci banks |= BIT(0) | BIT(1); 3638c2ecf20Sopenharmony_ci break; 3648c2ecf20Sopenharmony_ci case PIN_CONFIG_OUTPUT: 3658c2ecf20Sopenharmony_ci pin->mode = PM8XXX_GPIO_MODE_OUTPUT; 3668c2ecf20Sopenharmony_ci pin->output_value = !!arg; 3678c2ecf20Sopenharmony_ci banks |= BIT(0) | BIT(1); 3688c2ecf20Sopenharmony_ci break; 3698c2ecf20Sopenharmony_ci case PIN_CONFIG_POWER_SOURCE: 3708c2ecf20Sopenharmony_ci pin->power_source = arg; 3718c2ecf20Sopenharmony_ci banks |= BIT(0); 3728c2ecf20Sopenharmony_ci break; 3738c2ecf20Sopenharmony_ci case PM8XXX_QCOM_DRIVE_STRENGH: 3748c2ecf20Sopenharmony_ci if (arg > PMIC_GPIO_STRENGTH_LOW) { 3758c2ecf20Sopenharmony_ci dev_err(pctrl->dev, "invalid drive strength\n"); 3768c2ecf20Sopenharmony_ci return -EINVAL; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci pin->output_strength = arg; 3798c2ecf20Sopenharmony_ci banks |= BIT(3); 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci case PIN_CONFIG_DRIVE_PUSH_PULL: 3828c2ecf20Sopenharmony_ci pin->open_drain = 0; 3838c2ecf20Sopenharmony_ci banks |= BIT(1); 3848c2ecf20Sopenharmony_ci break; 3858c2ecf20Sopenharmony_ci case PIN_CONFIG_DRIVE_OPEN_DRAIN: 3868c2ecf20Sopenharmony_ci pin->open_drain = 1; 3878c2ecf20Sopenharmony_ci banks |= BIT(1); 3888c2ecf20Sopenharmony_ci break; 3898c2ecf20Sopenharmony_ci default: 3908c2ecf20Sopenharmony_ci dev_err(pctrl->dev, 3918c2ecf20Sopenharmony_ci "unsupported config parameter: %x\n", 3928c2ecf20Sopenharmony_ci param); 3938c2ecf20Sopenharmony_ci return -EINVAL; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (banks & BIT(0)) { 3988c2ecf20Sopenharmony_ci val = pin->power_source << 1; 3998c2ecf20Sopenharmony_ci val |= PM8XXX_GPIO_MODE_ENABLED; 4008c2ecf20Sopenharmony_ci pm8xxx_write_bank(pctrl, pin, 0, val); 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (banks & BIT(1)) { 4048c2ecf20Sopenharmony_ci val = pin->mode << 2; 4058c2ecf20Sopenharmony_ci val |= pin->open_drain << 1; 4068c2ecf20Sopenharmony_ci val |= pin->output_value; 4078c2ecf20Sopenharmony_ci pm8xxx_write_bank(pctrl, pin, 1, val); 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (banks & BIT(2)) { 4118c2ecf20Sopenharmony_ci val = pin->bias << 1; 4128c2ecf20Sopenharmony_ci pm8xxx_write_bank(pctrl, pin, 2, val); 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (banks & BIT(3)) { 4168c2ecf20Sopenharmony_ci val = pin->output_strength << 2; 4178c2ecf20Sopenharmony_ci val |= pin->disable; 4188c2ecf20Sopenharmony_ci pm8xxx_write_bank(pctrl, pin, 3, val); 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (banks & BIT(4)) { 4228c2ecf20Sopenharmony_ci val = pin->function << 1; 4238c2ecf20Sopenharmony_ci pm8xxx_write_bank(pctrl, pin, 4, val); 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (banks & BIT(5)) { 4278c2ecf20Sopenharmony_ci val = 0; 4288c2ecf20Sopenharmony_ci if (!pin->inverted) 4298c2ecf20Sopenharmony_ci val |= BIT(3); 4308c2ecf20Sopenharmony_ci pm8xxx_write_bank(pctrl, pin, 5, val); 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci return 0; 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic const struct pinconf_ops pm8xxx_pinconf_ops = { 4378c2ecf20Sopenharmony_ci .is_generic = true, 4388c2ecf20Sopenharmony_ci .pin_config_group_get = pm8xxx_pin_config_get, 4398c2ecf20Sopenharmony_ci .pin_config_group_set = pm8xxx_pin_config_set, 4408c2ecf20Sopenharmony_ci}; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic const struct pinctrl_desc pm8xxx_pinctrl_desc = { 4438c2ecf20Sopenharmony_ci .name = "pm8xxx_gpio", 4448c2ecf20Sopenharmony_ci .pctlops = &pm8xxx_pinctrl_ops, 4458c2ecf20Sopenharmony_ci .pmxops = &pm8xxx_pinmux_ops, 4468c2ecf20Sopenharmony_ci .confops = &pm8xxx_pinconf_ops, 4478c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4488c2ecf20Sopenharmony_ci}; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic int pm8xxx_gpio_direction_input(struct gpio_chip *chip, 4518c2ecf20Sopenharmony_ci unsigned offset) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip); 4548c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 4558c2ecf20Sopenharmony_ci u8 val; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci pin->mode = PM8XXX_GPIO_MODE_INPUT; 4588c2ecf20Sopenharmony_ci val = pin->mode << 2; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci pm8xxx_write_bank(pctrl, pin, 1, val); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci return 0; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic int pm8xxx_gpio_direction_output(struct gpio_chip *chip, 4668c2ecf20Sopenharmony_ci unsigned offset, 4678c2ecf20Sopenharmony_ci int value) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip); 4708c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 4718c2ecf20Sopenharmony_ci u8 val; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci pin->mode = PM8XXX_GPIO_MODE_OUTPUT; 4748c2ecf20Sopenharmony_ci pin->output_value = !!value; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci val = pin->mode << 2; 4778c2ecf20Sopenharmony_ci val |= pin->open_drain << 1; 4788c2ecf20Sopenharmony_ci val |= pin->output_value; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci pm8xxx_write_bank(pctrl, pin, 1, val); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci return 0; 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic int pm8xxx_gpio_get(struct gpio_chip *chip, unsigned offset) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip); 4888c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 4898c2ecf20Sopenharmony_ci int ret, irq; 4908c2ecf20Sopenharmony_ci bool state; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci if (pin->mode == PM8XXX_GPIO_MODE_OUTPUT) 4938c2ecf20Sopenharmony_ci return pin->output_value; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci irq = chip->to_irq(chip, offset); 4968c2ecf20Sopenharmony_ci if (irq >= 0) { 4978c2ecf20Sopenharmony_ci ret = irq_get_irqchip_state(irq, IRQCHIP_STATE_LINE_LEVEL, 4988c2ecf20Sopenharmony_ci &state); 4998c2ecf20Sopenharmony_ci if (!ret) 5008c2ecf20Sopenharmony_ci ret = !!state; 5018c2ecf20Sopenharmony_ci } else 5028c2ecf20Sopenharmony_ci ret = -EINVAL; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci return ret; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic void pm8xxx_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip); 5108c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 5118c2ecf20Sopenharmony_ci u8 val; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci pin->output_value = !!value; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci val = pin->mode << 2; 5168c2ecf20Sopenharmony_ci val |= pin->open_drain << 1; 5178c2ecf20Sopenharmony_ci val |= pin->output_value; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci pm8xxx_write_bank(pctrl, pin, 1, val); 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic int pm8xxx_gpio_of_xlate(struct gpio_chip *chip, 5238c2ecf20Sopenharmony_ci const struct of_phandle_args *gpio_desc, 5248c2ecf20Sopenharmony_ci u32 *flags) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci if (chip->of_gpio_n_cells < 2) 5278c2ecf20Sopenharmony_ci return -EINVAL; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci if (flags) 5308c2ecf20Sopenharmony_ci *flags = gpio_desc->args[1]; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci return gpio_desc->args[0] - PM8XXX_GPIO_PHYSICAL_OFFSET; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 5378c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic void pm8xxx_gpio_dbg_show_one(struct seq_file *s, 5408c2ecf20Sopenharmony_ci struct pinctrl_dev *pctldev, 5418c2ecf20Sopenharmony_ci struct gpio_chip *chip, 5428c2ecf20Sopenharmony_ci unsigned offset, 5438c2ecf20Sopenharmony_ci unsigned gpio) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip); 5468c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci static const char * const modes[] = { 5498c2ecf20Sopenharmony_ci "in", "both", "out", "off" 5508c2ecf20Sopenharmony_ci }; 5518c2ecf20Sopenharmony_ci static const char * const biases[] = { 5528c2ecf20Sopenharmony_ci "pull-up 30uA", "pull-up 1.5uA", "pull-up 31.5uA", 5538c2ecf20Sopenharmony_ci "pull-up 1.5uA + 30uA boost", "pull-down 10uA", "no pull" 5548c2ecf20Sopenharmony_ci }; 5558c2ecf20Sopenharmony_ci static const char * const buffer_types[] = { 5568c2ecf20Sopenharmony_ci "push-pull", "open-drain" 5578c2ecf20Sopenharmony_ci }; 5588c2ecf20Sopenharmony_ci static const char * const strengths[] = { 5598c2ecf20Sopenharmony_ci "no", "high", "medium", "low" 5608c2ecf20Sopenharmony_ci }; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci seq_printf(s, " gpio%-2d:", offset + PM8XXX_GPIO_PHYSICAL_OFFSET); 5638c2ecf20Sopenharmony_ci if (pin->disable) { 5648c2ecf20Sopenharmony_ci seq_puts(s, " ---"); 5658c2ecf20Sopenharmony_ci } else { 5668c2ecf20Sopenharmony_ci seq_printf(s, " %-4s", modes[pin->mode]); 5678c2ecf20Sopenharmony_ci seq_printf(s, " %-7s", pm8xxx_gpio_functions[pin->function]); 5688c2ecf20Sopenharmony_ci seq_printf(s, " VIN%d", pin->power_source); 5698c2ecf20Sopenharmony_ci seq_printf(s, " %-27s", biases[pin->bias]); 5708c2ecf20Sopenharmony_ci seq_printf(s, " %-10s", buffer_types[pin->open_drain]); 5718c2ecf20Sopenharmony_ci seq_printf(s, " %-4s", pin->output_value ? "high" : "low"); 5728c2ecf20Sopenharmony_ci seq_printf(s, " %-7s", strengths[pin->output_strength]); 5738c2ecf20Sopenharmony_ci if (pin->inverted) 5748c2ecf20Sopenharmony_ci seq_puts(s, " inverted"); 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cistatic void pm8xxx_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci unsigned gpio = chip->base; 5818c2ecf20Sopenharmony_ci unsigned i; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci for (i = 0; i < chip->ngpio; i++, gpio++) { 5848c2ecf20Sopenharmony_ci pm8xxx_gpio_dbg_show_one(s, NULL, chip, i, gpio); 5858c2ecf20Sopenharmony_ci seq_puts(s, "\n"); 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci#else 5908c2ecf20Sopenharmony_ci#define pm8xxx_gpio_dbg_show NULL 5918c2ecf20Sopenharmony_ci#endif 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic const struct gpio_chip pm8xxx_gpio_template = { 5948c2ecf20Sopenharmony_ci .direction_input = pm8xxx_gpio_direction_input, 5958c2ecf20Sopenharmony_ci .direction_output = pm8xxx_gpio_direction_output, 5968c2ecf20Sopenharmony_ci .get = pm8xxx_gpio_get, 5978c2ecf20Sopenharmony_ci .set = pm8xxx_gpio_set, 5988c2ecf20Sopenharmony_ci .of_xlate = pm8xxx_gpio_of_xlate, 5998c2ecf20Sopenharmony_ci .dbg_show = pm8xxx_gpio_dbg_show, 6008c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 6018c2ecf20Sopenharmony_ci}; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic int pm8xxx_pin_populate(struct pm8xxx_gpio *pctrl, 6048c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci int val; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci val = pm8xxx_read_bank(pctrl, pin, 0); 6098c2ecf20Sopenharmony_ci if (val < 0) 6108c2ecf20Sopenharmony_ci return val; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci pin->power_source = (val >> 1) & 0x7; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci val = pm8xxx_read_bank(pctrl, pin, 1); 6158c2ecf20Sopenharmony_ci if (val < 0) 6168c2ecf20Sopenharmony_ci return val; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci pin->mode = (val >> 2) & 0x3; 6198c2ecf20Sopenharmony_ci pin->open_drain = !!(val & BIT(1)); 6208c2ecf20Sopenharmony_ci pin->output_value = val & BIT(0); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci val = pm8xxx_read_bank(pctrl, pin, 2); 6238c2ecf20Sopenharmony_ci if (val < 0) 6248c2ecf20Sopenharmony_ci return val; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci pin->bias = (val >> 1) & 0x7; 6278c2ecf20Sopenharmony_ci if (pin->bias <= PM8XXX_GPIO_BIAS_PU_1P5_30) 6288c2ecf20Sopenharmony_ci pin->pull_up_strength = pin->bias; 6298c2ecf20Sopenharmony_ci else 6308c2ecf20Sopenharmony_ci pin->pull_up_strength = PM8XXX_GPIO_BIAS_PU_30; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci val = pm8xxx_read_bank(pctrl, pin, 3); 6338c2ecf20Sopenharmony_ci if (val < 0) 6348c2ecf20Sopenharmony_ci return val; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci pin->output_strength = (val >> 2) & 0x3; 6378c2ecf20Sopenharmony_ci pin->disable = val & BIT(0); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci val = pm8xxx_read_bank(pctrl, pin, 4); 6408c2ecf20Sopenharmony_ci if (val < 0) 6418c2ecf20Sopenharmony_ci return val; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci pin->function = (val >> 1) & 0x7; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci val = pm8xxx_read_bank(pctrl, pin, 5); 6468c2ecf20Sopenharmony_ci if (val < 0) 6478c2ecf20Sopenharmony_ci return val; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci pin->inverted = !(val & BIT(3)); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci return 0; 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cistatic struct irq_chip pm8xxx_irq_chip = { 6558c2ecf20Sopenharmony_ci .name = "ssbi-gpio", 6568c2ecf20Sopenharmony_ci .irq_mask_ack = irq_chip_mask_ack_parent, 6578c2ecf20Sopenharmony_ci .irq_unmask = irq_chip_unmask_parent, 6588c2ecf20Sopenharmony_ci .irq_set_type = irq_chip_set_type_parent, 6598c2ecf20Sopenharmony_ci .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, 6608c2ecf20Sopenharmony_ci}; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_cistatic int pm8xxx_domain_translate(struct irq_domain *domain, 6638c2ecf20Sopenharmony_ci struct irq_fwspec *fwspec, 6648c2ecf20Sopenharmony_ci unsigned long *hwirq, 6658c2ecf20Sopenharmony_ci unsigned int *type) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci struct pm8xxx_gpio *pctrl = container_of(domain->host_data, 6688c2ecf20Sopenharmony_ci struct pm8xxx_gpio, chip); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci if (fwspec->param_count != 2 || fwspec->param[0] < 1 || 6718c2ecf20Sopenharmony_ci fwspec->param[0] > pctrl->chip.ngpio) 6728c2ecf20Sopenharmony_ci return -EINVAL; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci *hwirq = fwspec->param[0] - PM8XXX_GPIO_PHYSICAL_OFFSET; 6758c2ecf20Sopenharmony_ci *type = fwspec->param[1]; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci return 0; 6788c2ecf20Sopenharmony_ci} 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_cistatic unsigned int pm8xxx_child_offset_to_irq(struct gpio_chip *chip, 6818c2ecf20Sopenharmony_ci unsigned int offset) 6828c2ecf20Sopenharmony_ci{ 6838c2ecf20Sopenharmony_ci return offset + PM8XXX_GPIO_PHYSICAL_OFFSET; 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_cistatic int pm8xxx_child_to_parent_hwirq(struct gpio_chip *chip, 6878c2ecf20Sopenharmony_ci unsigned int child_hwirq, 6888c2ecf20Sopenharmony_ci unsigned int child_type, 6898c2ecf20Sopenharmony_ci unsigned int *parent_hwirq, 6908c2ecf20Sopenharmony_ci unsigned int *parent_type) 6918c2ecf20Sopenharmony_ci{ 6928c2ecf20Sopenharmony_ci *parent_hwirq = child_hwirq + 0xc0; 6938c2ecf20Sopenharmony_ci *parent_type = child_type; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci return 0; 6968c2ecf20Sopenharmony_ci} 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_cistatic const struct of_device_id pm8xxx_gpio_of_match[] = { 6998c2ecf20Sopenharmony_ci { .compatible = "qcom,pm8018-gpio", .data = (void *) 6 }, 7008c2ecf20Sopenharmony_ci { .compatible = "qcom,pm8038-gpio", .data = (void *) 12 }, 7018c2ecf20Sopenharmony_ci { .compatible = "qcom,pm8058-gpio", .data = (void *) 44 }, 7028c2ecf20Sopenharmony_ci { .compatible = "qcom,pm8917-gpio", .data = (void *) 38 }, 7038c2ecf20Sopenharmony_ci { .compatible = "qcom,pm8921-gpio", .data = (void *) 44 }, 7048c2ecf20Sopenharmony_ci { }, 7058c2ecf20Sopenharmony_ci}; 7068c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_cistatic int pm8xxx_gpio_probe(struct platform_device *pdev) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin_data; 7118c2ecf20Sopenharmony_ci struct irq_domain *parent_domain; 7128c2ecf20Sopenharmony_ci struct device_node *parent_node; 7138c2ecf20Sopenharmony_ci struct pinctrl_pin_desc *pins; 7148c2ecf20Sopenharmony_ci struct gpio_irq_chip *girq; 7158c2ecf20Sopenharmony_ci struct pm8xxx_gpio *pctrl; 7168c2ecf20Sopenharmony_ci int ret, i; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); 7198c2ecf20Sopenharmony_ci if (!pctrl) 7208c2ecf20Sopenharmony_ci return -ENOMEM; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci pctrl->dev = &pdev->dev; 7238c2ecf20Sopenharmony_ci pctrl->npins = (uintptr_t) device_get_match_data(&pdev->dev); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci pctrl->regmap = dev_get_regmap(pdev->dev.parent, NULL); 7268c2ecf20Sopenharmony_ci if (!pctrl->regmap) { 7278c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "parent regmap unavailable\n"); 7288c2ecf20Sopenharmony_ci return -ENXIO; 7298c2ecf20Sopenharmony_ci } 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci pctrl->desc = pm8xxx_pinctrl_desc; 7328c2ecf20Sopenharmony_ci pctrl->desc.npins = pctrl->npins; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci pins = devm_kcalloc(&pdev->dev, 7358c2ecf20Sopenharmony_ci pctrl->desc.npins, 7368c2ecf20Sopenharmony_ci sizeof(struct pinctrl_pin_desc), 7378c2ecf20Sopenharmony_ci GFP_KERNEL); 7388c2ecf20Sopenharmony_ci if (!pins) 7398c2ecf20Sopenharmony_ci return -ENOMEM; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci pin_data = devm_kcalloc(&pdev->dev, 7428c2ecf20Sopenharmony_ci pctrl->desc.npins, 7438c2ecf20Sopenharmony_ci sizeof(struct pm8xxx_pin_data), 7448c2ecf20Sopenharmony_ci GFP_KERNEL); 7458c2ecf20Sopenharmony_ci if (!pin_data) 7468c2ecf20Sopenharmony_ci return -ENOMEM; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci for (i = 0; i < pctrl->desc.npins; i++) { 7498c2ecf20Sopenharmony_ci pin_data[i].reg = SSBI_REG_ADDR_GPIO(i); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci ret = pm8xxx_pin_populate(pctrl, &pin_data[i]); 7528c2ecf20Sopenharmony_ci if (ret) 7538c2ecf20Sopenharmony_ci return ret; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci pins[i].number = i; 7568c2ecf20Sopenharmony_ci pins[i].name = pm8xxx_groups[i]; 7578c2ecf20Sopenharmony_ci pins[i].drv_data = &pin_data[i]; 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci pctrl->desc.pins = pins; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci pctrl->desc.num_custom_params = ARRAY_SIZE(pm8xxx_gpio_bindings); 7628c2ecf20Sopenharmony_ci pctrl->desc.custom_params = pm8xxx_gpio_bindings; 7638c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 7648c2ecf20Sopenharmony_ci pctrl->desc.custom_conf_items = pm8xxx_conf_items; 7658c2ecf20Sopenharmony_ci#endif 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci pctrl->pctrl = devm_pinctrl_register(&pdev->dev, &pctrl->desc, pctrl); 7688c2ecf20Sopenharmony_ci if (IS_ERR(pctrl->pctrl)) { 7698c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "couldn't register pm8xxx gpio driver\n"); 7708c2ecf20Sopenharmony_ci return PTR_ERR(pctrl->pctrl); 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci pctrl->chip = pm8xxx_gpio_template; 7748c2ecf20Sopenharmony_ci pctrl->chip.base = -1; 7758c2ecf20Sopenharmony_ci pctrl->chip.parent = &pdev->dev; 7768c2ecf20Sopenharmony_ci pctrl->chip.of_node = pdev->dev.of_node; 7778c2ecf20Sopenharmony_ci pctrl->chip.of_gpio_n_cells = 2; 7788c2ecf20Sopenharmony_ci pctrl->chip.label = dev_name(pctrl->dev); 7798c2ecf20Sopenharmony_ci pctrl->chip.ngpio = pctrl->npins; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci parent_node = of_irq_find_parent(pctrl->dev->of_node); 7828c2ecf20Sopenharmony_ci if (!parent_node) 7838c2ecf20Sopenharmony_ci return -ENXIO; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci parent_domain = irq_find_host(parent_node); 7868c2ecf20Sopenharmony_ci of_node_put(parent_node); 7878c2ecf20Sopenharmony_ci if (!parent_domain) 7888c2ecf20Sopenharmony_ci return -ENXIO; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci girq = &pctrl->chip.irq; 7918c2ecf20Sopenharmony_ci girq->chip = &pm8xxx_irq_chip; 7928c2ecf20Sopenharmony_ci girq->default_type = IRQ_TYPE_NONE; 7938c2ecf20Sopenharmony_ci girq->handler = handle_level_irq; 7948c2ecf20Sopenharmony_ci girq->fwnode = of_node_to_fwnode(pctrl->dev->of_node); 7958c2ecf20Sopenharmony_ci girq->parent_domain = parent_domain; 7968c2ecf20Sopenharmony_ci girq->child_to_parent_hwirq = pm8xxx_child_to_parent_hwirq; 7978c2ecf20Sopenharmony_ci girq->populate_parent_alloc_arg = gpiochip_populate_parent_fwspec_twocell; 7988c2ecf20Sopenharmony_ci girq->child_offset_to_irq = pm8xxx_child_offset_to_irq; 7998c2ecf20Sopenharmony_ci girq->child_irq_domain_ops.translate = pm8xxx_domain_translate; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci ret = gpiochip_add_data(&pctrl->chip, pctrl); 8028c2ecf20Sopenharmony_ci if (ret) { 8038c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed register gpiochip\n"); 8048c2ecf20Sopenharmony_ci return ret; 8058c2ecf20Sopenharmony_ci } 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci /* 8088c2ecf20Sopenharmony_ci * For DeviceTree-supported systems, the gpio core checks the 8098c2ecf20Sopenharmony_ci * pinctrl's device node for the "gpio-ranges" property. 8108c2ecf20Sopenharmony_ci * If it is present, it takes care of adding the pin ranges 8118c2ecf20Sopenharmony_ci * for the driver. In this case the driver can skip ahead. 8128c2ecf20Sopenharmony_ci * 8138c2ecf20Sopenharmony_ci * In order to remain compatible with older, existing DeviceTree 8148c2ecf20Sopenharmony_ci * files which don't set the "gpio-ranges" property or systems that 8158c2ecf20Sopenharmony_ci * utilize ACPI the driver has to call gpiochip_add_pin_range(). 8168c2ecf20Sopenharmony_ci */ 8178c2ecf20Sopenharmony_ci if (!of_property_read_bool(pctrl->dev->of_node, "gpio-ranges")) { 8188c2ecf20Sopenharmony_ci ret = gpiochip_add_pin_range(&pctrl->chip, dev_name(pctrl->dev), 8198c2ecf20Sopenharmony_ci 0, 0, pctrl->chip.ngpio); 8208c2ecf20Sopenharmony_ci if (ret) { 8218c2ecf20Sopenharmony_ci dev_err(pctrl->dev, "failed to add pin range\n"); 8228c2ecf20Sopenharmony_ci goto unregister_gpiochip; 8238c2ecf20Sopenharmony_ci } 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, pctrl); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Qualcomm pm8xxx gpio driver probed\n"); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci return 0; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ciunregister_gpiochip: 8338c2ecf20Sopenharmony_ci gpiochip_remove(&pctrl->chip); 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci return ret; 8368c2ecf20Sopenharmony_ci} 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_cistatic int pm8xxx_gpio_remove(struct platform_device *pdev) 8398c2ecf20Sopenharmony_ci{ 8408c2ecf20Sopenharmony_ci struct pm8xxx_gpio *pctrl = platform_get_drvdata(pdev); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci gpiochip_remove(&pctrl->chip); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci return 0; 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_cistatic struct platform_driver pm8xxx_gpio_driver = { 8488c2ecf20Sopenharmony_ci .driver = { 8498c2ecf20Sopenharmony_ci .name = "qcom-ssbi-gpio", 8508c2ecf20Sopenharmony_ci .of_match_table = pm8xxx_gpio_of_match, 8518c2ecf20Sopenharmony_ci }, 8528c2ecf20Sopenharmony_ci .probe = pm8xxx_gpio_probe, 8538c2ecf20Sopenharmony_ci .remove = pm8xxx_gpio_remove, 8548c2ecf20Sopenharmony_ci}; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_cimodule_platform_driver(pm8xxx_gpio_driver); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ciMODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>"); 8598c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm PM8xxx GPIO driver"); 8608c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 861