162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2015, Sony Mobile Communications AB. 462306a36Sopenharmony_ci * Copyright (c) 2013, The Linux Foundation. All rights reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/gpio/driver.h> 862306a36Sopenharmony_ci#include <linux/interrupt.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/of.h> 1162306a36Sopenharmony_ci#include <linux/of_irq.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/regmap.h> 1462306a36Sopenharmony_ci#include <linux/seq_file.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/pinctrl/pinconf-generic.h> 1862306a36Sopenharmony_ci#include <linux/pinctrl/pinconf.h> 1962306a36Sopenharmony_ci#include <linux/pinctrl/pinctrl.h> 2062306a36Sopenharmony_ci#include <linux/pinctrl/pinmux.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <dt-bindings/pinctrl/qcom,pmic-gpio.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "../core.h" 2562306a36Sopenharmony_ci#include "../pinctrl-utils.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* mode */ 2862306a36Sopenharmony_ci#define PM8XXX_GPIO_MODE_ENABLED BIT(0) 2962306a36Sopenharmony_ci#define PM8XXX_GPIO_MODE_INPUT 0 3062306a36Sopenharmony_ci#define PM8XXX_GPIO_MODE_OUTPUT 2 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* output buffer */ 3362306a36Sopenharmony_ci#define PM8XXX_GPIO_PUSH_PULL 0 3462306a36Sopenharmony_ci#define PM8XXX_GPIO_OPEN_DRAIN 1 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* bias */ 3762306a36Sopenharmony_ci#define PM8XXX_GPIO_BIAS_PU_30 0 3862306a36Sopenharmony_ci#define PM8XXX_GPIO_BIAS_PU_1P5 1 3962306a36Sopenharmony_ci#define PM8XXX_GPIO_BIAS_PU_31P5 2 4062306a36Sopenharmony_ci#define PM8XXX_GPIO_BIAS_PU_1P5_30 3 4162306a36Sopenharmony_ci#define PM8XXX_GPIO_BIAS_PD 4 4262306a36Sopenharmony_ci#define PM8XXX_GPIO_BIAS_NP 5 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* GPIO registers */ 4562306a36Sopenharmony_ci#define SSBI_REG_ADDR_GPIO_BASE 0x150 4662306a36Sopenharmony_ci#define SSBI_REG_ADDR_GPIO(n) (SSBI_REG_ADDR_GPIO_BASE + n) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define PM8XXX_BANK_WRITE BIT(7) 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define PM8XXX_MAX_GPIOS 44 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define PM8XXX_GPIO_PHYSICAL_OFFSET 1 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* custom pinconf parameters */ 5562306a36Sopenharmony_ci#define PM8XXX_QCOM_DRIVE_STRENGH (PIN_CONFIG_END + 1) 5662306a36Sopenharmony_ci#define PM8XXX_QCOM_PULL_UP_STRENGTH (PIN_CONFIG_END + 2) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/** 5962306a36Sopenharmony_ci * struct pm8xxx_pin_data - dynamic configuration for a pin 6062306a36Sopenharmony_ci * @reg: address of the control register 6162306a36Sopenharmony_ci * @power_source: logical selected voltage source, mapping in static data 6262306a36Sopenharmony_ci * is used translate to register values 6362306a36Sopenharmony_ci * @mode: operating mode for the pin (input/output) 6462306a36Sopenharmony_ci * @open_drain: output buffer configured as open-drain (vs push-pull) 6562306a36Sopenharmony_ci * @output_value: configured output value 6662306a36Sopenharmony_ci * @bias: register view of configured bias 6762306a36Sopenharmony_ci * @pull_up_strength: placeholder for selected pull up strength 6862306a36Sopenharmony_ci * only used to configure bias when pull up is selected 6962306a36Sopenharmony_ci * @output_strength: selector of output-strength 7062306a36Sopenharmony_ci * @disable: pin disabled / configured as tristate 7162306a36Sopenharmony_ci * @function: pinmux selector 7262306a36Sopenharmony_ci * @inverted: pin logic is inverted 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_cistruct pm8xxx_pin_data { 7562306a36Sopenharmony_ci unsigned reg; 7662306a36Sopenharmony_ci u8 power_source; 7762306a36Sopenharmony_ci u8 mode; 7862306a36Sopenharmony_ci bool open_drain; 7962306a36Sopenharmony_ci bool output_value; 8062306a36Sopenharmony_ci u8 bias; 8162306a36Sopenharmony_ci u8 pull_up_strength; 8262306a36Sopenharmony_ci u8 output_strength; 8362306a36Sopenharmony_ci bool disable; 8462306a36Sopenharmony_ci u8 function; 8562306a36Sopenharmony_ci bool inverted; 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistruct pm8xxx_gpio { 8962306a36Sopenharmony_ci struct device *dev; 9062306a36Sopenharmony_ci struct regmap *regmap; 9162306a36Sopenharmony_ci struct pinctrl_dev *pctrl; 9262306a36Sopenharmony_ci struct gpio_chip chip; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci struct pinctrl_desc desc; 9562306a36Sopenharmony_ci unsigned npins; 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic const struct pinconf_generic_params pm8xxx_gpio_bindings[] = { 9962306a36Sopenharmony_ci {"qcom,drive-strength", PM8XXX_QCOM_DRIVE_STRENGH, 0}, 10062306a36Sopenharmony_ci {"qcom,pull-up-strength", PM8XXX_QCOM_PULL_UP_STRENGTH, 0}, 10162306a36Sopenharmony_ci}; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 10462306a36Sopenharmony_cistatic const struct pin_config_item pm8xxx_conf_items[ARRAY_SIZE(pm8xxx_gpio_bindings)] = { 10562306a36Sopenharmony_ci PCONFDUMP(PM8XXX_QCOM_DRIVE_STRENGH, "drive-strength", NULL, true), 10662306a36Sopenharmony_ci PCONFDUMP(PM8XXX_QCOM_PULL_UP_STRENGTH, "pull up strength", NULL, true), 10762306a36Sopenharmony_ci}; 10862306a36Sopenharmony_ci#endif 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic const char * const pm8xxx_groups[PM8XXX_MAX_GPIOS] = { 11162306a36Sopenharmony_ci "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", "gpio8", 11262306a36Sopenharmony_ci "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", "gpio15", 11362306a36Sopenharmony_ci "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22", 11462306a36Sopenharmony_ci "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29", 11562306a36Sopenharmony_ci "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36", 11662306a36Sopenharmony_ci "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", "gpio43", 11762306a36Sopenharmony_ci "gpio44", 11862306a36Sopenharmony_ci}; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic const char * const pm8xxx_gpio_functions[] = { 12162306a36Sopenharmony_ci PMIC_GPIO_FUNC_NORMAL, PMIC_GPIO_FUNC_PAIRED, 12262306a36Sopenharmony_ci PMIC_GPIO_FUNC_FUNC1, PMIC_GPIO_FUNC_FUNC2, 12362306a36Sopenharmony_ci PMIC_GPIO_FUNC_DTEST1, PMIC_GPIO_FUNC_DTEST2, 12462306a36Sopenharmony_ci PMIC_GPIO_FUNC_DTEST3, PMIC_GPIO_FUNC_DTEST4, 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic int pm8xxx_read_bank(struct pm8xxx_gpio *pctrl, 12862306a36Sopenharmony_ci struct pm8xxx_pin_data *pin, int bank) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci unsigned int val = bank << 4; 13162306a36Sopenharmony_ci int ret; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci ret = regmap_write(pctrl->regmap, pin->reg, val); 13462306a36Sopenharmony_ci if (ret) { 13562306a36Sopenharmony_ci dev_err(pctrl->dev, "failed to select bank %d\n", bank); 13662306a36Sopenharmony_ci return ret; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci ret = regmap_read(pctrl->regmap, pin->reg, &val); 14062306a36Sopenharmony_ci if (ret) { 14162306a36Sopenharmony_ci dev_err(pctrl->dev, "failed to read register %d\n", bank); 14262306a36Sopenharmony_ci return ret; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return val; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic int pm8xxx_write_bank(struct pm8xxx_gpio *pctrl, 14962306a36Sopenharmony_ci struct pm8xxx_pin_data *pin, 15062306a36Sopenharmony_ci int bank, 15162306a36Sopenharmony_ci u8 val) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci int ret; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci val |= PM8XXX_BANK_WRITE; 15662306a36Sopenharmony_ci val |= bank << 4; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci ret = regmap_write(pctrl->regmap, pin->reg, val); 15962306a36Sopenharmony_ci if (ret) 16062306a36Sopenharmony_ci dev_err(pctrl->dev, "failed to write register\n"); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci return ret; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int pm8xxx_get_groups_count(struct pinctrl_dev *pctldev) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci return pctrl->npins; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic const char *pm8xxx_get_group_name(struct pinctrl_dev *pctldev, 17362306a36Sopenharmony_ci unsigned group) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci return pm8xxx_groups[group]; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic int pm8xxx_get_group_pins(struct pinctrl_dev *pctldev, 18062306a36Sopenharmony_ci unsigned group, 18162306a36Sopenharmony_ci const unsigned **pins, 18262306a36Sopenharmony_ci unsigned *num_pins) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci *pins = &pctrl->desc.pins[group].number; 18762306a36Sopenharmony_ci *num_pins = 1; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return 0; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic const struct pinctrl_ops pm8xxx_pinctrl_ops = { 19362306a36Sopenharmony_ci .get_groups_count = pm8xxx_get_groups_count, 19462306a36Sopenharmony_ci .get_group_name = pm8xxx_get_group_name, 19562306a36Sopenharmony_ci .get_group_pins = pm8xxx_get_group_pins, 19662306a36Sopenharmony_ci .dt_node_to_map = pinconf_generic_dt_node_to_map_group, 19762306a36Sopenharmony_ci .dt_free_map = pinctrl_utils_free_map, 19862306a36Sopenharmony_ci}; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic int pm8xxx_get_functions_count(struct pinctrl_dev *pctldev) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci return ARRAY_SIZE(pm8xxx_gpio_functions); 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic const char *pm8xxx_get_function_name(struct pinctrl_dev *pctldev, 20662306a36Sopenharmony_ci unsigned function) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci return pm8xxx_gpio_functions[function]; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic int pm8xxx_get_function_groups(struct pinctrl_dev *pctldev, 21262306a36Sopenharmony_ci unsigned function, 21362306a36Sopenharmony_ci const char * const **groups, 21462306a36Sopenharmony_ci unsigned * const num_groups) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci *groups = pm8xxx_groups; 21962306a36Sopenharmony_ci *num_groups = pctrl->npins; 22062306a36Sopenharmony_ci return 0; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic int pm8xxx_pinmux_set_mux(struct pinctrl_dev *pctldev, 22462306a36Sopenharmony_ci unsigned function, 22562306a36Sopenharmony_ci unsigned group) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev); 22862306a36Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[group].drv_data; 22962306a36Sopenharmony_ci u8 val; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci pin->function = function; 23262306a36Sopenharmony_ci val = pin->function << 1; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci pm8xxx_write_bank(pctrl, pin, 4, val); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return 0; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic const struct pinmux_ops pm8xxx_pinmux_ops = { 24062306a36Sopenharmony_ci .get_functions_count = pm8xxx_get_functions_count, 24162306a36Sopenharmony_ci .get_function_name = pm8xxx_get_function_name, 24262306a36Sopenharmony_ci .get_function_groups = pm8xxx_get_function_groups, 24362306a36Sopenharmony_ci .set_mux = pm8xxx_pinmux_set_mux, 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic int pm8xxx_pin_config_get(struct pinctrl_dev *pctldev, 24762306a36Sopenharmony_ci unsigned int offset, 24862306a36Sopenharmony_ci unsigned long *config) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev); 25162306a36Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 25262306a36Sopenharmony_ci unsigned param = pinconf_to_config_param(*config); 25362306a36Sopenharmony_ci unsigned arg; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci switch (param) { 25662306a36Sopenharmony_ci case PIN_CONFIG_BIAS_DISABLE: 25762306a36Sopenharmony_ci if (pin->bias != PM8XXX_GPIO_BIAS_NP) 25862306a36Sopenharmony_ci return -EINVAL; 25962306a36Sopenharmony_ci arg = 1; 26062306a36Sopenharmony_ci break; 26162306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_DOWN: 26262306a36Sopenharmony_ci if (pin->bias != PM8XXX_GPIO_BIAS_PD) 26362306a36Sopenharmony_ci return -EINVAL; 26462306a36Sopenharmony_ci arg = 1; 26562306a36Sopenharmony_ci break; 26662306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 26762306a36Sopenharmony_ci if (pin->bias > PM8XXX_GPIO_BIAS_PU_1P5_30) 26862306a36Sopenharmony_ci return -EINVAL; 26962306a36Sopenharmony_ci arg = 1; 27062306a36Sopenharmony_ci break; 27162306a36Sopenharmony_ci case PM8XXX_QCOM_PULL_UP_STRENGTH: 27262306a36Sopenharmony_ci arg = pin->pull_up_strength; 27362306a36Sopenharmony_ci break; 27462306a36Sopenharmony_ci case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: 27562306a36Sopenharmony_ci if (!pin->disable) 27662306a36Sopenharmony_ci return -EINVAL; 27762306a36Sopenharmony_ci arg = 1; 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci case PIN_CONFIG_INPUT_ENABLE: 28062306a36Sopenharmony_ci if (pin->mode != PM8XXX_GPIO_MODE_INPUT) 28162306a36Sopenharmony_ci return -EINVAL; 28262306a36Sopenharmony_ci arg = 1; 28362306a36Sopenharmony_ci break; 28462306a36Sopenharmony_ci case PIN_CONFIG_OUTPUT: 28562306a36Sopenharmony_ci if (pin->mode & PM8XXX_GPIO_MODE_OUTPUT) 28662306a36Sopenharmony_ci arg = pin->output_value; 28762306a36Sopenharmony_ci else 28862306a36Sopenharmony_ci arg = 0; 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci case PIN_CONFIG_POWER_SOURCE: 29162306a36Sopenharmony_ci arg = pin->power_source; 29262306a36Sopenharmony_ci break; 29362306a36Sopenharmony_ci case PM8XXX_QCOM_DRIVE_STRENGH: 29462306a36Sopenharmony_ci arg = pin->output_strength; 29562306a36Sopenharmony_ci break; 29662306a36Sopenharmony_ci case PIN_CONFIG_DRIVE_PUSH_PULL: 29762306a36Sopenharmony_ci if (pin->open_drain) 29862306a36Sopenharmony_ci return -EINVAL; 29962306a36Sopenharmony_ci arg = 1; 30062306a36Sopenharmony_ci break; 30162306a36Sopenharmony_ci case PIN_CONFIG_DRIVE_OPEN_DRAIN: 30262306a36Sopenharmony_ci if (!pin->open_drain) 30362306a36Sopenharmony_ci return -EINVAL; 30462306a36Sopenharmony_ci arg = 1; 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci default: 30762306a36Sopenharmony_ci return -EINVAL; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci *config = pinconf_to_config_packed(param, arg); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return 0; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic int pm8xxx_pin_config_set(struct pinctrl_dev *pctldev, 31662306a36Sopenharmony_ci unsigned int offset, 31762306a36Sopenharmony_ci unsigned long *configs, 31862306a36Sopenharmony_ci unsigned num_configs) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev); 32162306a36Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 32262306a36Sopenharmony_ci unsigned param; 32362306a36Sopenharmony_ci unsigned arg; 32462306a36Sopenharmony_ci unsigned i; 32562306a36Sopenharmony_ci u8 banks = 0; 32662306a36Sopenharmony_ci u8 val; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci for (i = 0; i < num_configs; i++) { 32962306a36Sopenharmony_ci param = pinconf_to_config_param(configs[i]); 33062306a36Sopenharmony_ci arg = pinconf_to_config_argument(configs[i]); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci switch (param) { 33362306a36Sopenharmony_ci case PIN_CONFIG_BIAS_DISABLE: 33462306a36Sopenharmony_ci pin->bias = PM8XXX_GPIO_BIAS_NP; 33562306a36Sopenharmony_ci banks |= BIT(2); 33662306a36Sopenharmony_ci pin->disable = 0; 33762306a36Sopenharmony_ci banks |= BIT(3); 33862306a36Sopenharmony_ci break; 33962306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_DOWN: 34062306a36Sopenharmony_ci pin->bias = PM8XXX_GPIO_BIAS_PD; 34162306a36Sopenharmony_ci banks |= BIT(2); 34262306a36Sopenharmony_ci pin->disable = 0; 34362306a36Sopenharmony_ci banks |= BIT(3); 34462306a36Sopenharmony_ci break; 34562306a36Sopenharmony_ci case PM8XXX_QCOM_PULL_UP_STRENGTH: 34662306a36Sopenharmony_ci if (arg > PM8XXX_GPIO_BIAS_PU_1P5_30) { 34762306a36Sopenharmony_ci dev_err(pctrl->dev, "invalid pull-up strength\n"); 34862306a36Sopenharmony_ci return -EINVAL; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci pin->pull_up_strength = arg; 35162306a36Sopenharmony_ci fallthrough; 35262306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 35362306a36Sopenharmony_ci pin->bias = pin->pull_up_strength; 35462306a36Sopenharmony_ci banks |= BIT(2); 35562306a36Sopenharmony_ci pin->disable = 0; 35662306a36Sopenharmony_ci banks |= BIT(3); 35762306a36Sopenharmony_ci break; 35862306a36Sopenharmony_ci case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: 35962306a36Sopenharmony_ci pin->disable = 1; 36062306a36Sopenharmony_ci banks |= BIT(3); 36162306a36Sopenharmony_ci break; 36262306a36Sopenharmony_ci case PIN_CONFIG_INPUT_ENABLE: 36362306a36Sopenharmony_ci pin->mode = PM8XXX_GPIO_MODE_INPUT; 36462306a36Sopenharmony_ci banks |= BIT(0) | BIT(1); 36562306a36Sopenharmony_ci break; 36662306a36Sopenharmony_ci case PIN_CONFIG_OUTPUT: 36762306a36Sopenharmony_ci pin->mode = PM8XXX_GPIO_MODE_OUTPUT; 36862306a36Sopenharmony_ci pin->output_value = !!arg; 36962306a36Sopenharmony_ci banks |= BIT(0) | BIT(1); 37062306a36Sopenharmony_ci break; 37162306a36Sopenharmony_ci case PIN_CONFIG_POWER_SOURCE: 37262306a36Sopenharmony_ci pin->power_source = arg; 37362306a36Sopenharmony_ci banks |= BIT(0); 37462306a36Sopenharmony_ci break; 37562306a36Sopenharmony_ci case PM8XXX_QCOM_DRIVE_STRENGH: 37662306a36Sopenharmony_ci if (arg > PMIC_GPIO_STRENGTH_LOW) { 37762306a36Sopenharmony_ci dev_err(pctrl->dev, "invalid drive strength\n"); 37862306a36Sopenharmony_ci return -EINVAL; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci pin->output_strength = arg; 38162306a36Sopenharmony_ci banks |= BIT(3); 38262306a36Sopenharmony_ci break; 38362306a36Sopenharmony_ci case PIN_CONFIG_DRIVE_PUSH_PULL: 38462306a36Sopenharmony_ci pin->open_drain = 0; 38562306a36Sopenharmony_ci banks |= BIT(1); 38662306a36Sopenharmony_ci break; 38762306a36Sopenharmony_ci case PIN_CONFIG_DRIVE_OPEN_DRAIN: 38862306a36Sopenharmony_ci pin->open_drain = 1; 38962306a36Sopenharmony_ci banks |= BIT(1); 39062306a36Sopenharmony_ci break; 39162306a36Sopenharmony_ci default: 39262306a36Sopenharmony_ci dev_err(pctrl->dev, 39362306a36Sopenharmony_ci "unsupported config parameter: %x\n", 39462306a36Sopenharmony_ci param); 39562306a36Sopenharmony_ci return -EINVAL; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (banks & BIT(0)) { 40062306a36Sopenharmony_ci val = pin->power_source << 1; 40162306a36Sopenharmony_ci val |= PM8XXX_GPIO_MODE_ENABLED; 40262306a36Sopenharmony_ci pm8xxx_write_bank(pctrl, pin, 0, val); 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (banks & BIT(1)) { 40662306a36Sopenharmony_ci val = pin->mode << 2; 40762306a36Sopenharmony_ci val |= pin->open_drain << 1; 40862306a36Sopenharmony_ci val |= pin->output_value; 40962306a36Sopenharmony_ci pm8xxx_write_bank(pctrl, pin, 1, val); 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (banks & BIT(2)) { 41362306a36Sopenharmony_ci val = pin->bias << 1; 41462306a36Sopenharmony_ci pm8xxx_write_bank(pctrl, pin, 2, val); 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (banks & BIT(3)) { 41862306a36Sopenharmony_ci val = pin->output_strength << 2; 41962306a36Sopenharmony_ci val |= pin->disable; 42062306a36Sopenharmony_ci pm8xxx_write_bank(pctrl, pin, 3, val); 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (banks & BIT(4)) { 42462306a36Sopenharmony_ci val = pin->function << 1; 42562306a36Sopenharmony_ci pm8xxx_write_bank(pctrl, pin, 4, val); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (banks & BIT(5)) { 42962306a36Sopenharmony_ci val = 0; 43062306a36Sopenharmony_ci if (!pin->inverted) 43162306a36Sopenharmony_ci val |= BIT(3); 43262306a36Sopenharmony_ci pm8xxx_write_bank(pctrl, pin, 5, val); 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci return 0; 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistatic const struct pinconf_ops pm8xxx_pinconf_ops = { 43962306a36Sopenharmony_ci .is_generic = true, 44062306a36Sopenharmony_ci .pin_config_group_get = pm8xxx_pin_config_get, 44162306a36Sopenharmony_ci .pin_config_group_set = pm8xxx_pin_config_set, 44262306a36Sopenharmony_ci}; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic const struct pinctrl_desc pm8xxx_pinctrl_desc = { 44562306a36Sopenharmony_ci .name = "pm8xxx_gpio", 44662306a36Sopenharmony_ci .pctlops = &pm8xxx_pinctrl_ops, 44762306a36Sopenharmony_ci .pmxops = &pm8xxx_pinmux_ops, 44862306a36Sopenharmony_ci .confops = &pm8xxx_pinconf_ops, 44962306a36Sopenharmony_ci .owner = THIS_MODULE, 45062306a36Sopenharmony_ci}; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic int pm8xxx_gpio_direction_input(struct gpio_chip *chip, 45362306a36Sopenharmony_ci unsigned offset) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip); 45662306a36Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 45762306a36Sopenharmony_ci u8 val; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci pin->mode = PM8XXX_GPIO_MODE_INPUT; 46062306a36Sopenharmony_ci val = pin->mode << 2; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci pm8xxx_write_bank(pctrl, pin, 1, val); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci return 0; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic int pm8xxx_gpio_direction_output(struct gpio_chip *chip, 46862306a36Sopenharmony_ci unsigned offset, 46962306a36Sopenharmony_ci int value) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip); 47262306a36Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 47362306a36Sopenharmony_ci u8 val; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci pin->mode = PM8XXX_GPIO_MODE_OUTPUT; 47662306a36Sopenharmony_ci pin->output_value = !!value; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci val = pin->mode << 2; 47962306a36Sopenharmony_ci val |= pin->open_drain << 1; 48062306a36Sopenharmony_ci val |= pin->output_value; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci pm8xxx_write_bank(pctrl, pin, 1, val); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci return 0; 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic int pm8xxx_gpio_get(struct gpio_chip *chip, unsigned offset) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip); 49062306a36Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 49162306a36Sopenharmony_ci int ret, irq; 49262306a36Sopenharmony_ci bool state; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (pin->mode == PM8XXX_GPIO_MODE_OUTPUT) 49562306a36Sopenharmony_ci return pin->output_value; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci irq = chip->to_irq(chip, offset); 49862306a36Sopenharmony_ci if (irq >= 0) { 49962306a36Sopenharmony_ci ret = irq_get_irqchip_state(irq, IRQCHIP_STATE_LINE_LEVEL, 50062306a36Sopenharmony_ci &state); 50162306a36Sopenharmony_ci if (!ret) 50262306a36Sopenharmony_ci ret = !!state; 50362306a36Sopenharmony_ci } else 50462306a36Sopenharmony_ci ret = -EINVAL; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci return ret; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic void pm8xxx_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip); 51262306a36Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 51362306a36Sopenharmony_ci u8 val; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci pin->output_value = !!value; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci val = pin->mode << 2; 51862306a36Sopenharmony_ci val |= pin->open_drain << 1; 51962306a36Sopenharmony_ci val |= pin->output_value; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci pm8xxx_write_bank(pctrl, pin, 1, val); 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic int pm8xxx_gpio_of_xlate(struct gpio_chip *chip, 52562306a36Sopenharmony_ci const struct of_phandle_args *gpio_desc, 52662306a36Sopenharmony_ci u32 *flags) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci if (chip->of_gpio_n_cells < 2) 52962306a36Sopenharmony_ci return -EINVAL; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (flags) 53262306a36Sopenharmony_ci *flags = gpio_desc->args[1]; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci return gpio_desc->args[0] - PM8XXX_GPIO_PHYSICAL_OFFSET; 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic void pm8xxx_gpio_dbg_show_one(struct seq_file *s, 54162306a36Sopenharmony_ci struct pinctrl_dev *pctldev, 54262306a36Sopenharmony_ci struct gpio_chip *chip, 54362306a36Sopenharmony_ci unsigned offset, 54462306a36Sopenharmony_ci unsigned gpio) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip); 54762306a36Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci static const char * const modes[] = { 55062306a36Sopenharmony_ci "in", "both", "out", "off" 55162306a36Sopenharmony_ci }; 55262306a36Sopenharmony_ci static const char * const biases[] = { 55362306a36Sopenharmony_ci "pull-up 30uA", "pull-up 1.5uA", "pull-up 31.5uA", 55462306a36Sopenharmony_ci "pull-up 1.5uA + 30uA boost", "pull-down 10uA", "no pull" 55562306a36Sopenharmony_ci }; 55662306a36Sopenharmony_ci static const char * const buffer_types[] = { 55762306a36Sopenharmony_ci "push-pull", "open-drain" 55862306a36Sopenharmony_ci }; 55962306a36Sopenharmony_ci static const char * const strengths[] = { 56062306a36Sopenharmony_ci "no", "high", "medium", "low" 56162306a36Sopenharmony_ci }; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci seq_printf(s, " gpio%-2d:", offset + PM8XXX_GPIO_PHYSICAL_OFFSET); 56462306a36Sopenharmony_ci if (pin->disable) { 56562306a36Sopenharmony_ci seq_puts(s, " ---"); 56662306a36Sopenharmony_ci } else { 56762306a36Sopenharmony_ci seq_printf(s, " %-4s", modes[pin->mode]); 56862306a36Sopenharmony_ci seq_printf(s, " %-7s", pm8xxx_gpio_functions[pin->function]); 56962306a36Sopenharmony_ci seq_printf(s, " VIN%d", pin->power_source); 57062306a36Sopenharmony_ci seq_printf(s, " %-27s", biases[pin->bias]); 57162306a36Sopenharmony_ci seq_printf(s, " %-10s", buffer_types[pin->open_drain]); 57262306a36Sopenharmony_ci seq_printf(s, " %-4s", pin->output_value ? "high" : "low"); 57362306a36Sopenharmony_ci seq_printf(s, " %-7s", strengths[pin->output_strength]); 57462306a36Sopenharmony_ci if (pin->inverted) 57562306a36Sopenharmony_ci seq_puts(s, " inverted"); 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistatic void pm8xxx_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci unsigned gpio = chip->base; 58262306a36Sopenharmony_ci unsigned i; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci for (i = 0; i < chip->ngpio; i++, gpio++) { 58562306a36Sopenharmony_ci pm8xxx_gpio_dbg_show_one(s, NULL, chip, i, gpio); 58662306a36Sopenharmony_ci seq_puts(s, "\n"); 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci#else 59162306a36Sopenharmony_ci#define pm8xxx_gpio_dbg_show NULL 59262306a36Sopenharmony_ci#endif 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_cistatic const struct gpio_chip pm8xxx_gpio_template = { 59562306a36Sopenharmony_ci .direction_input = pm8xxx_gpio_direction_input, 59662306a36Sopenharmony_ci .direction_output = pm8xxx_gpio_direction_output, 59762306a36Sopenharmony_ci .get = pm8xxx_gpio_get, 59862306a36Sopenharmony_ci .set = pm8xxx_gpio_set, 59962306a36Sopenharmony_ci .of_xlate = pm8xxx_gpio_of_xlate, 60062306a36Sopenharmony_ci .dbg_show = pm8xxx_gpio_dbg_show, 60162306a36Sopenharmony_ci .owner = THIS_MODULE, 60262306a36Sopenharmony_ci}; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic int pm8xxx_pin_populate(struct pm8xxx_gpio *pctrl, 60562306a36Sopenharmony_ci struct pm8xxx_pin_data *pin) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci int val; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci val = pm8xxx_read_bank(pctrl, pin, 0); 61062306a36Sopenharmony_ci if (val < 0) 61162306a36Sopenharmony_ci return val; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci pin->power_source = (val >> 1) & 0x7; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci val = pm8xxx_read_bank(pctrl, pin, 1); 61662306a36Sopenharmony_ci if (val < 0) 61762306a36Sopenharmony_ci return val; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci pin->mode = (val >> 2) & 0x3; 62062306a36Sopenharmony_ci pin->open_drain = !!(val & BIT(1)); 62162306a36Sopenharmony_ci pin->output_value = val & BIT(0); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci val = pm8xxx_read_bank(pctrl, pin, 2); 62462306a36Sopenharmony_ci if (val < 0) 62562306a36Sopenharmony_ci return val; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci pin->bias = (val >> 1) & 0x7; 62862306a36Sopenharmony_ci if (pin->bias <= PM8XXX_GPIO_BIAS_PU_1P5_30) 62962306a36Sopenharmony_ci pin->pull_up_strength = pin->bias; 63062306a36Sopenharmony_ci else 63162306a36Sopenharmony_ci pin->pull_up_strength = PM8XXX_GPIO_BIAS_PU_30; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci val = pm8xxx_read_bank(pctrl, pin, 3); 63462306a36Sopenharmony_ci if (val < 0) 63562306a36Sopenharmony_ci return val; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci pin->output_strength = (val >> 2) & 0x3; 63862306a36Sopenharmony_ci pin->disable = val & BIT(0); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci val = pm8xxx_read_bank(pctrl, pin, 4); 64162306a36Sopenharmony_ci if (val < 0) 64262306a36Sopenharmony_ci return val; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci pin->function = (val >> 1) & 0x7; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci val = pm8xxx_read_bank(pctrl, pin, 5); 64762306a36Sopenharmony_ci if (val < 0) 64862306a36Sopenharmony_ci return val; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci pin->inverted = !(val & BIT(3)); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci return 0; 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cistatic void pm8xxx_irq_disable(struct irq_data *d) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci gpiochip_disable_irq(gc, irqd_to_hwirq(d)); 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cistatic void pm8xxx_irq_enable(struct irq_data *d) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci gpiochip_enable_irq(gc, irqd_to_hwirq(d)); 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic const struct irq_chip pm8xxx_irq_chip = { 67062306a36Sopenharmony_ci .name = "ssbi-gpio", 67162306a36Sopenharmony_ci .irq_mask_ack = irq_chip_mask_ack_parent, 67262306a36Sopenharmony_ci .irq_unmask = irq_chip_unmask_parent, 67362306a36Sopenharmony_ci .irq_disable = pm8xxx_irq_disable, 67462306a36Sopenharmony_ci .irq_enable = pm8xxx_irq_enable, 67562306a36Sopenharmony_ci .irq_set_type = irq_chip_set_type_parent, 67662306a36Sopenharmony_ci .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE | 67762306a36Sopenharmony_ci IRQCHIP_IMMUTABLE, 67862306a36Sopenharmony_ci GPIOCHIP_IRQ_RESOURCE_HELPERS, 67962306a36Sopenharmony_ci}; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic int pm8xxx_domain_translate(struct irq_domain *domain, 68262306a36Sopenharmony_ci struct irq_fwspec *fwspec, 68362306a36Sopenharmony_ci unsigned long *hwirq, 68462306a36Sopenharmony_ci unsigned int *type) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci struct pm8xxx_gpio *pctrl = container_of(domain->host_data, 68762306a36Sopenharmony_ci struct pm8xxx_gpio, chip); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (fwspec->param_count != 2 || fwspec->param[0] < 1 || 69062306a36Sopenharmony_ci fwspec->param[0] > pctrl->chip.ngpio) 69162306a36Sopenharmony_ci return -EINVAL; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci *hwirq = fwspec->param[0] - PM8XXX_GPIO_PHYSICAL_OFFSET; 69462306a36Sopenharmony_ci *type = fwspec->param[1]; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci return 0; 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic unsigned int pm8xxx_child_offset_to_irq(struct gpio_chip *chip, 70062306a36Sopenharmony_ci unsigned int offset) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci return offset + PM8XXX_GPIO_PHYSICAL_OFFSET; 70362306a36Sopenharmony_ci} 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_cistatic int pm8xxx_child_to_parent_hwirq(struct gpio_chip *chip, 70662306a36Sopenharmony_ci unsigned int child_hwirq, 70762306a36Sopenharmony_ci unsigned int child_type, 70862306a36Sopenharmony_ci unsigned int *parent_hwirq, 70962306a36Sopenharmony_ci unsigned int *parent_type) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci *parent_hwirq = child_hwirq + 0xc0; 71262306a36Sopenharmony_ci *parent_type = child_type; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci return 0; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic const struct of_device_id pm8xxx_gpio_of_match[] = { 71862306a36Sopenharmony_ci { .compatible = "qcom,pm8018-gpio", .data = (void *) 6 }, 71962306a36Sopenharmony_ci { .compatible = "qcom,pm8038-gpio", .data = (void *) 12 }, 72062306a36Sopenharmony_ci { .compatible = "qcom,pm8058-gpio", .data = (void *) 44 }, 72162306a36Sopenharmony_ci { .compatible = "qcom,pm8917-gpio", .data = (void *) 38 }, 72262306a36Sopenharmony_ci { .compatible = "qcom,pm8921-gpio", .data = (void *) 44 }, 72362306a36Sopenharmony_ci { }, 72462306a36Sopenharmony_ci}; 72562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic int pm8xxx_gpio_probe(struct platform_device *pdev) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci struct pm8xxx_pin_data *pin_data; 73062306a36Sopenharmony_ci struct irq_domain *parent_domain; 73162306a36Sopenharmony_ci struct device_node *parent_node; 73262306a36Sopenharmony_ci struct pinctrl_pin_desc *pins; 73362306a36Sopenharmony_ci struct gpio_irq_chip *girq; 73462306a36Sopenharmony_ci struct pm8xxx_gpio *pctrl; 73562306a36Sopenharmony_ci int ret, i; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); 73862306a36Sopenharmony_ci if (!pctrl) 73962306a36Sopenharmony_ci return -ENOMEM; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci pctrl->dev = &pdev->dev; 74262306a36Sopenharmony_ci pctrl->npins = (uintptr_t) device_get_match_data(&pdev->dev); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci pctrl->regmap = dev_get_regmap(pdev->dev.parent, NULL); 74562306a36Sopenharmony_ci if (!pctrl->regmap) { 74662306a36Sopenharmony_ci dev_err(&pdev->dev, "parent regmap unavailable\n"); 74762306a36Sopenharmony_ci return -ENXIO; 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci pctrl->desc = pm8xxx_pinctrl_desc; 75162306a36Sopenharmony_ci pctrl->desc.npins = pctrl->npins; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci pins = devm_kcalloc(&pdev->dev, 75462306a36Sopenharmony_ci pctrl->desc.npins, 75562306a36Sopenharmony_ci sizeof(struct pinctrl_pin_desc), 75662306a36Sopenharmony_ci GFP_KERNEL); 75762306a36Sopenharmony_ci if (!pins) 75862306a36Sopenharmony_ci return -ENOMEM; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci pin_data = devm_kcalloc(&pdev->dev, 76162306a36Sopenharmony_ci pctrl->desc.npins, 76262306a36Sopenharmony_ci sizeof(struct pm8xxx_pin_data), 76362306a36Sopenharmony_ci GFP_KERNEL); 76462306a36Sopenharmony_ci if (!pin_data) 76562306a36Sopenharmony_ci return -ENOMEM; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci for (i = 0; i < pctrl->desc.npins; i++) { 76862306a36Sopenharmony_ci pin_data[i].reg = SSBI_REG_ADDR_GPIO(i); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci ret = pm8xxx_pin_populate(pctrl, &pin_data[i]); 77162306a36Sopenharmony_ci if (ret) 77262306a36Sopenharmony_ci return ret; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci pins[i].number = i; 77562306a36Sopenharmony_ci pins[i].name = pm8xxx_groups[i]; 77662306a36Sopenharmony_ci pins[i].drv_data = &pin_data[i]; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci pctrl->desc.pins = pins; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci pctrl->desc.num_custom_params = ARRAY_SIZE(pm8xxx_gpio_bindings); 78162306a36Sopenharmony_ci pctrl->desc.custom_params = pm8xxx_gpio_bindings; 78262306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 78362306a36Sopenharmony_ci pctrl->desc.custom_conf_items = pm8xxx_conf_items; 78462306a36Sopenharmony_ci#endif 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci pctrl->pctrl = devm_pinctrl_register(&pdev->dev, &pctrl->desc, pctrl); 78762306a36Sopenharmony_ci if (IS_ERR(pctrl->pctrl)) { 78862306a36Sopenharmony_ci dev_err(&pdev->dev, "couldn't register pm8xxx gpio driver\n"); 78962306a36Sopenharmony_ci return PTR_ERR(pctrl->pctrl); 79062306a36Sopenharmony_ci } 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci pctrl->chip = pm8xxx_gpio_template; 79362306a36Sopenharmony_ci pctrl->chip.base = -1; 79462306a36Sopenharmony_ci pctrl->chip.parent = &pdev->dev; 79562306a36Sopenharmony_ci pctrl->chip.of_gpio_n_cells = 2; 79662306a36Sopenharmony_ci pctrl->chip.label = dev_name(pctrl->dev); 79762306a36Sopenharmony_ci pctrl->chip.ngpio = pctrl->npins; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci parent_node = of_irq_find_parent(pctrl->dev->of_node); 80062306a36Sopenharmony_ci if (!parent_node) 80162306a36Sopenharmony_ci return -ENXIO; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci parent_domain = irq_find_host(parent_node); 80462306a36Sopenharmony_ci of_node_put(parent_node); 80562306a36Sopenharmony_ci if (!parent_domain) 80662306a36Sopenharmony_ci return -ENXIO; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci girq = &pctrl->chip.irq; 80962306a36Sopenharmony_ci gpio_irq_chip_set_chip(girq, &pm8xxx_irq_chip); 81062306a36Sopenharmony_ci girq->default_type = IRQ_TYPE_NONE; 81162306a36Sopenharmony_ci girq->handler = handle_level_irq; 81262306a36Sopenharmony_ci girq->fwnode = dev_fwnode(pctrl->dev); 81362306a36Sopenharmony_ci girq->parent_domain = parent_domain; 81462306a36Sopenharmony_ci girq->child_to_parent_hwirq = pm8xxx_child_to_parent_hwirq; 81562306a36Sopenharmony_ci girq->populate_parent_alloc_arg = gpiochip_populate_parent_fwspec_twocell; 81662306a36Sopenharmony_ci girq->child_offset_to_irq = pm8xxx_child_offset_to_irq; 81762306a36Sopenharmony_ci girq->child_irq_domain_ops.translate = pm8xxx_domain_translate; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci ret = gpiochip_add_data(&pctrl->chip, pctrl); 82062306a36Sopenharmony_ci if (ret) { 82162306a36Sopenharmony_ci dev_err(&pdev->dev, "failed register gpiochip\n"); 82262306a36Sopenharmony_ci return ret; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci /* 82662306a36Sopenharmony_ci * For DeviceTree-supported systems, the gpio core checks the 82762306a36Sopenharmony_ci * pinctrl's device node for the "gpio-ranges" property. 82862306a36Sopenharmony_ci * If it is present, it takes care of adding the pin ranges 82962306a36Sopenharmony_ci * for the driver. In this case the driver can skip ahead. 83062306a36Sopenharmony_ci * 83162306a36Sopenharmony_ci * In order to remain compatible with older, existing DeviceTree 83262306a36Sopenharmony_ci * files which don't set the "gpio-ranges" property or systems that 83362306a36Sopenharmony_ci * utilize ACPI the driver has to call gpiochip_add_pin_range(). 83462306a36Sopenharmony_ci */ 83562306a36Sopenharmony_ci if (!of_property_read_bool(pctrl->dev->of_node, "gpio-ranges")) { 83662306a36Sopenharmony_ci ret = gpiochip_add_pin_range(&pctrl->chip, dev_name(pctrl->dev), 83762306a36Sopenharmony_ci 0, 0, pctrl->chip.ngpio); 83862306a36Sopenharmony_ci if (ret) { 83962306a36Sopenharmony_ci dev_err(pctrl->dev, "failed to add pin range\n"); 84062306a36Sopenharmony_ci goto unregister_gpiochip; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci platform_set_drvdata(pdev, pctrl); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Qualcomm pm8xxx gpio driver probed\n"); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci return 0; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ciunregister_gpiochip: 85162306a36Sopenharmony_ci gpiochip_remove(&pctrl->chip); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci return ret; 85462306a36Sopenharmony_ci} 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_cistatic int pm8xxx_gpio_remove(struct platform_device *pdev) 85762306a36Sopenharmony_ci{ 85862306a36Sopenharmony_ci struct pm8xxx_gpio *pctrl = platform_get_drvdata(pdev); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci gpiochip_remove(&pctrl->chip); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci return 0; 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic struct platform_driver pm8xxx_gpio_driver = { 86662306a36Sopenharmony_ci .driver = { 86762306a36Sopenharmony_ci .name = "qcom-ssbi-gpio", 86862306a36Sopenharmony_ci .of_match_table = pm8xxx_gpio_of_match, 86962306a36Sopenharmony_ci }, 87062306a36Sopenharmony_ci .probe = pm8xxx_gpio_probe, 87162306a36Sopenharmony_ci .remove = pm8xxx_gpio_remove, 87262306a36Sopenharmony_ci}; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cimodule_platform_driver(pm8xxx_gpio_driver); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ciMODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>"); 87762306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm PM8xxx GPIO driver"); 87862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 879