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-mpp.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "../core.h" 2562306a36Sopenharmony_ci#include "../pinctrl-utils.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* MPP registers */ 2862306a36Sopenharmony_ci#define SSBI_REG_ADDR_MPP_BASE 0x50 2962306a36Sopenharmony_ci#define SSBI_REG_ADDR_MPP(n) (SSBI_REG_ADDR_MPP_BASE + n) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* MPP Type: type */ 3262306a36Sopenharmony_ci#define PM8XXX_MPP_TYPE_D_INPUT 0 3362306a36Sopenharmony_ci#define PM8XXX_MPP_TYPE_D_OUTPUT 1 3462306a36Sopenharmony_ci#define PM8XXX_MPP_TYPE_D_BI_DIR 2 3562306a36Sopenharmony_ci#define PM8XXX_MPP_TYPE_A_INPUT 3 3662306a36Sopenharmony_ci#define PM8XXX_MPP_TYPE_A_OUTPUT 4 3762306a36Sopenharmony_ci#define PM8XXX_MPP_TYPE_SINK 5 3862306a36Sopenharmony_ci#define PM8XXX_MPP_TYPE_DTEST_SINK 6 3962306a36Sopenharmony_ci#define PM8XXX_MPP_TYPE_DTEST_OUTPUT 7 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Digital Input: control */ 4262306a36Sopenharmony_ci#define PM8XXX_MPP_DIN_TO_INT 0 4362306a36Sopenharmony_ci#define PM8XXX_MPP_DIN_TO_DBUS1 1 4462306a36Sopenharmony_ci#define PM8XXX_MPP_DIN_TO_DBUS2 2 4562306a36Sopenharmony_ci#define PM8XXX_MPP_DIN_TO_DBUS3 3 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* Digital Output: control */ 4862306a36Sopenharmony_ci#define PM8XXX_MPP_DOUT_CTRL_LOW 0 4962306a36Sopenharmony_ci#define PM8XXX_MPP_DOUT_CTRL_HIGH 1 5062306a36Sopenharmony_ci#define PM8XXX_MPP_DOUT_CTRL_MPP 2 5162306a36Sopenharmony_ci#define PM8XXX_MPP_DOUT_CTRL_INV_MPP 3 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* Bidirectional: control */ 5462306a36Sopenharmony_ci#define PM8XXX_MPP_BI_PULLUP_1KOHM 0 5562306a36Sopenharmony_ci#define PM8XXX_MPP_BI_PULLUP_OPEN 1 5662306a36Sopenharmony_ci#define PM8XXX_MPP_BI_PULLUP_10KOHM 2 5762306a36Sopenharmony_ci#define PM8XXX_MPP_BI_PULLUP_30KOHM 3 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* Analog Output: control */ 6062306a36Sopenharmony_ci#define PM8XXX_MPP_AOUT_CTRL_DISABLE 0 6162306a36Sopenharmony_ci#define PM8XXX_MPP_AOUT_CTRL_ENABLE 1 6262306a36Sopenharmony_ci#define PM8XXX_MPP_AOUT_CTRL_MPP_HIGH_EN 2 6362306a36Sopenharmony_ci#define PM8XXX_MPP_AOUT_CTRL_MPP_LOW_EN 3 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* Current Sink: control */ 6662306a36Sopenharmony_ci#define PM8XXX_MPP_CS_CTRL_DISABLE 0 6762306a36Sopenharmony_ci#define PM8XXX_MPP_CS_CTRL_ENABLE 1 6862306a36Sopenharmony_ci#define PM8XXX_MPP_CS_CTRL_MPP_HIGH_EN 2 6962306a36Sopenharmony_ci#define PM8XXX_MPP_CS_CTRL_MPP_LOW_EN 3 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* DTEST Current Sink: control */ 7262306a36Sopenharmony_ci#define PM8XXX_MPP_DTEST_CS_CTRL_EN1 0 7362306a36Sopenharmony_ci#define PM8XXX_MPP_DTEST_CS_CTRL_EN2 1 7462306a36Sopenharmony_ci#define PM8XXX_MPP_DTEST_CS_CTRL_EN3 2 7562306a36Sopenharmony_ci#define PM8XXX_MPP_DTEST_CS_CTRL_EN4 3 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/* DTEST Digital Output: control */ 7862306a36Sopenharmony_ci#define PM8XXX_MPP_DTEST_DBUS1 0 7962306a36Sopenharmony_ci#define PM8XXX_MPP_DTEST_DBUS2 1 8062306a36Sopenharmony_ci#define PM8XXX_MPP_DTEST_DBUS3 2 8162306a36Sopenharmony_ci#define PM8XXX_MPP_DTEST_DBUS4 3 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* custom pinconf parameters */ 8462306a36Sopenharmony_ci#define PM8XXX_CONFIG_AMUX (PIN_CONFIG_END + 1) 8562306a36Sopenharmony_ci#define PM8XXX_CONFIG_DTEST_SELECTOR (PIN_CONFIG_END + 2) 8662306a36Sopenharmony_ci#define PM8XXX_CONFIG_ALEVEL (PIN_CONFIG_END + 3) 8762306a36Sopenharmony_ci#define PM8XXX_CONFIG_PAIRED (PIN_CONFIG_END + 4) 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/** 9062306a36Sopenharmony_ci * struct pm8xxx_pin_data - dynamic configuration for a pin 9162306a36Sopenharmony_ci * @reg: address of the control register 9262306a36Sopenharmony_ci * @mode: operating mode for the pin (digital, analog or current sink) 9362306a36Sopenharmony_ci * @input: pin is input 9462306a36Sopenharmony_ci * @output: pin is output 9562306a36Sopenharmony_ci * @high_z: pin is floating 9662306a36Sopenharmony_ci * @paired: mpp operates in paired mode 9762306a36Sopenharmony_ci * @output_value: logical output value of the mpp 9862306a36Sopenharmony_ci * @power_source: selected power source 9962306a36Sopenharmony_ci * @dtest: DTEST route selector 10062306a36Sopenharmony_ci * @amux: input muxing in analog mode 10162306a36Sopenharmony_ci * @aout_level: selector of the output in analog mode 10262306a36Sopenharmony_ci * @drive_strength: drive strength of the current sink 10362306a36Sopenharmony_ci * @pullup: pull up value, when in digital bidirectional mode 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_cistruct pm8xxx_pin_data { 10662306a36Sopenharmony_ci unsigned reg; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci u8 mode; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci bool input; 11162306a36Sopenharmony_ci bool output; 11262306a36Sopenharmony_ci bool high_z; 11362306a36Sopenharmony_ci bool paired; 11462306a36Sopenharmony_ci bool output_value; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci u8 power_source; 11762306a36Sopenharmony_ci u8 dtest; 11862306a36Sopenharmony_ci u8 amux; 11962306a36Sopenharmony_ci u8 aout_level; 12062306a36Sopenharmony_ci u8 drive_strength; 12162306a36Sopenharmony_ci unsigned pullup; 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistruct pm8xxx_mpp { 12562306a36Sopenharmony_ci struct device *dev; 12662306a36Sopenharmony_ci struct regmap *regmap; 12762306a36Sopenharmony_ci struct pinctrl_dev *pctrl; 12862306a36Sopenharmony_ci struct gpio_chip chip; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci struct pinctrl_desc desc; 13162306a36Sopenharmony_ci unsigned npins; 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic const struct pinconf_generic_params pm8xxx_mpp_bindings[] = { 13562306a36Sopenharmony_ci {"qcom,amux-route", PM8XXX_CONFIG_AMUX, 0}, 13662306a36Sopenharmony_ci {"qcom,analog-level", PM8XXX_CONFIG_ALEVEL, 0}, 13762306a36Sopenharmony_ci {"qcom,dtest", PM8XXX_CONFIG_DTEST_SELECTOR, 0}, 13862306a36Sopenharmony_ci {"qcom,paired", PM8XXX_CONFIG_PAIRED, 0}, 13962306a36Sopenharmony_ci}; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 14262306a36Sopenharmony_cistatic const struct pin_config_item pm8xxx_conf_items[] = { 14362306a36Sopenharmony_ci PCONFDUMP(PM8XXX_CONFIG_AMUX, "analog mux", NULL, true), 14462306a36Sopenharmony_ci PCONFDUMP(PM8XXX_CONFIG_ALEVEL, "analog level", NULL, true), 14562306a36Sopenharmony_ci PCONFDUMP(PM8XXX_CONFIG_DTEST_SELECTOR, "dtest", NULL, true), 14662306a36Sopenharmony_ci PCONFDUMP(PM8XXX_CONFIG_PAIRED, "paired", NULL, false), 14762306a36Sopenharmony_ci}; 14862306a36Sopenharmony_ci#endif 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci#define PM8XXX_MAX_MPPS 12 15162306a36Sopenharmony_ci#define PM8XXX_MPP_PHYSICAL_OFFSET 1 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic const char * const pm8xxx_groups[PM8XXX_MAX_MPPS] = { 15462306a36Sopenharmony_ci "mpp1", "mpp2", "mpp3", "mpp4", "mpp5", "mpp6", "mpp7", "mpp8", 15562306a36Sopenharmony_ci "mpp9", "mpp10", "mpp11", "mpp12", 15662306a36Sopenharmony_ci}; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci#define PM8XXX_MPP_DIGITAL 0 15962306a36Sopenharmony_ci#define PM8XXX_MPP_ANALOG 1 16062306a36Sopenharmony_ci#define PM8XXX_MPP_SINK 2 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic const char * const pm8xxx_mpp_functions[] = { 16362306a36Sopenharmony_ci "digital", "analog", "sink", 16462306a36Sopenharmony_ci}; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic int pm8xxx_mpp_update(struct pm8xxx_mpp *pctrl, 16762306a36Sopenharmony_ci struct pm8xxx_pin_data *pin) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci unsigned level; 17062306a36Sopenharmony_ci unsigned ctrl; 17162306a36Sopenharmony_ci unsigned type; 17262306a36Sopenharmony_ci int ret; 17362306a36Sopenharmony_ci u8 val; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci switch (pin->mode) { 17662306a36Sopenharmony_ci case PM8XXX_MPP_DIGITAL: 17762306a36Sopenharmony_ci if (pin->dtest) { 17862306a36Sopenharmony_ci type = PM8XXX_MPP_TYPE_DTEST_OUTPUT; 17962306a36Sopenharmony_ci ctrl = pin->dtest - 1; 18062306a36Sopenharmony_ci } else if (pin->input && pin->output) { 18162306a36Sopenharmony_ci type = PM8XXX_MPP_TYPE_D_BI_DIR; 18262306a36Sopenharmony_ci if (pin->high_z) 18362306a36Sopenharmony_ci ctrl = PM8XXX_MPP_BI_PULLUP_OPEN; 18462306a36Sopenharmony_ci else if (pin->pullup == 600) 18562306a36Sopenharmony_ci ctrl = PM8XXX_MPP_BI_PULLUP_1KOHM; 18662306a36Sopenharmony_ci else if (pin->pullup == 10000) 18762306a36Sopenharmony_ci ctrl = PM8XXX_MPP_BI_PULLUP_10KOHM; 18862306a36Sopenharmony_ci else 18962306a36Sopenharmony_ci ctrl = PM8XXX_MPP_BI_PULLUP_30KOHM; 19062306a36Sopenharmony_ci } else if (pin->input) { 19162306a36Sopenharmony_ci type = PM8XXX_MPP_TYPE_D_INPUT; 19262306a36Sopenharmony_ci if (pin->dtest) 19362306a36Sopenharmony_ci ctrl = pin->dtest; 19462306a36Sopenharmony_ci else 19562306a36Sopenharmony_ci ctrl = PM8XXX_MPP_DIN_TO_INT; 19662306a36Sopenharmony_ci } else { 19762306a36Sopenharmony_ci type = PM8XXX_MPP_TYPE_D_OUTPUT; 19862306a36Sopenharmony_ci ctrl = !!pin->output_value; 19962306a36Sopenharmony_ci if (pin->paired) 20062306a36Sopenharmony_ci ctrl |= BIT(1); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci level = pin->power_source; 20462306a36Sopenharmony_ci break; 20562306a36Sopenharmony_ci case PM8XXX_MPP_ANALOG: 20662306a36Sopenharmony_ci if (pin->output) { 20762306a36Sopenharmony_ci type = PM8XXX_MPP_TYPE_A_OUTPUT; 20862306a36Sopenharmony_ci level = pin->aout_level; 20962306a36Sopenharmony_ci ctrl = pin->output_value; 21062306a36Sopenharmony_ci if (pin->paired) 21162306a36Sopenharmony_ci ctrl |= BIT(1); 21262306a36Sopenharmony_ci } else { 21362306a36Sopenharmony_ci type = PM8XXX_MPP_TYPE_A_INPUT; 21462306a36Sopenharmony_ci level = pin->amux; 21562306a36Sopenharmony_ci ctrl = 0; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci break; 21862306a36Sopenharmony_ci case PM8XXX_MPP_SINK: 21962306a36Sopenharmony_ci level = (pin->drive_strength / 5) - 1; 22062306a36Sopenharmony_ci if (pin->dtest) { 22162306a36Sopenharmony_ci type = PM8XXX_MPP_TYPE_DTEST_SINK; 22262306a36Sopenharmony_ci ctrl = pin->dtest - 1; 22362306a36Sopenharmony_ci } else { 22462306a36Sopenharmony_ci type = PM8XXX_MPP_TYPE_SINK; 22562306a36Sopenharmony_ci ctrl = pin->output_value; 22662306a36Sopenharmony_ci if (pin->paired) 22762306a36Sopenharmony_ci ctrl |= BIT(1); 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci break; 23062306a36Sopenharmony_ci default: 23162306a36Sopenharmony_ci return -EINVAL; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci val = type << 5 | level << 2 | ctrl; 23562306a36Sopenharmony_ci ret = regmap_write(pctrl->regmap, pin->reg, val); 23662306a36Sopenharmony_ci if (ret) 23762306a36Sopenharmony_ci dev_err(pctrl->dev, "failed to write register\n"); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return ret; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic int pm8xxx_get_groups_count(struct pinctrl_dev *pctldev) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct pm8xxx_mpp *pctrl = pinctrl_dev_get_drvdata(pctldev); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci return pctrl->npins; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic const char *pm8xxx_get_group_name(struct pinctrl_dev *pctldev, 25062306a36Sopenharmony_ci unsigned group) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci return pm8xxx_groups[group]; 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic int pm8xxx_get_group_pins(struct pinctrl_dev *pctldev, 25762306a36Sopenharmony_ci unsigned group, 25862306a36Sopenharmony_ci const unsigned **pins, 25962306a36Sopenharmony_ci unsigned *num_pins) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct pm8xxx_mpp *pctrl = pinctrl_dev_get_drvdata(pctldev); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci *pins = &pctrl->desc.pins[group].number; 26462306a36Sopenharmony_ci *num_pins = 1; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci return 0; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic const struct pinctrl_ops pm8xxx_pinctrl_ops = { 27062306a36Sopenharmony_ci .get_groups_count = pm8xxx_get_groups_count, 27162306a36Sopenharmony_ci .get_group_name = pm8xxx_get_group_name, 27262306a36Sopenharmony_ci .get_group_pins = pm8xxx_get_group_pins, 27362306a36Sopenharmony_ci .dt_node_to_map = pinconf_generic_dt_node_to_map_group, 27462306a36Sopenharmony_ci .dt_free_map = pinctrl_utils_free_map, 27562306a36Sopenharmony_ci}; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic int pm8xxx_get_functions_count(struct pinctrl_dev *pctldev) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci return ARRAY_SIZE(pm8xxx_mpp_functions); 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic const char *pm8xxx_get_function_name(struct pinctrl_dev *pctldev, 28362306a36Sopenharmony_ci unsigned function) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci return pm8xxx_mpp_functions[function]; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic int pm8xxx_get_function_groups(struct pinctrl_dev *pctldev, 28962306a36Sopenharmony_ci unsigned function, 29062306a36Sopenharmony_ci const char * const **groups, 29162306a36Sopenharmony_ci unsigned * const num_groups) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci struct pm8xxx_mpp *pctrl = pinctrl_dev_get_drvdata(pctldev); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci *groups = pm8xxx_groups; 29662306a36Sopenharmony_ci *num_groups = pctrl->npins; 29762306a36Sopenharmony_ci return 0; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic int pm8xxx_pinmux_set_mux(struct pinctrl_dev *pctldev, 30162306a36Sopenharmony_ci unsigned function, 30262306a36Sopenharmony_ci unsigned group) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct pm8xxx_mpp *pctrl = pinctrl_dev_get_drvdata(pctldev); 30562306a36Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[group].drv_data; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci pin->mode = function; 30862306a36Sopenharmony_ci pm8xxx_mpp_update(pctrl, pin); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci return 0; 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic const struct pinmux_ops pm8xxx_pinmux_ops = { 31462306a36Sopenharmony_ci .get_functions_count = pm8xxx_get_functions_count, 31562306a36Sopenharmony_ci .get_function_name = pm8xxx_get_function_name, 31662306a36Sopenharmony_ci .get_function_groups = pm8xxx_get_function_groups, 31762306a36Sopenharmony_ci .set_mux = pm8xxx_pinmux_set_mux, 31862306a36Sopenharmony_ci}; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic int pm8xxx_pin_config_get(struct pinctrl_dev *pctldev, 32162306a36Sopenharmony_ci unsigned int offset, 32262306a36Sopenharmony_ci unsigned long *config) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci struct pm8xxx_mpp *pctrl = pinctrl_dev_get_drvdata(pctldev); 32562306a36Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 32662306a36Sopenharmony_ci unsigned param = pinconf_to_config_param(*config); 32762306a36Sopenharmony_ci unsigned arg; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci switch (param) { 33062306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 33162306a36Sopenharmony_ci arg = pin->pullup; 33262306a36Sopenharmony_ci break; 33362306a36Sopenharmony_ci case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: 33462306a36Sopenharmony_ci arg = pin->high_z; 33562306a36Sopenharmony_ci break; 33662306a36Sopenharmony_ci case PIN_CONFIG_INPUT_ENABLE: 33762306a36Sopenharmony_ci arg = pin->input; 33862306a36Sopenharmony_ci break; 33962306a36Sopenharmony_ci case PIN_CONFIG_OUTPUT: 34062306a36Sopenharmony_ci arg = pin->output_value; 34162306a36Sopenharmony_ci break; 34262306a36Sopenharmony_ci case PIN_CONFIG_POWER_SOURCE: 34362306a36Sopenharmony_ci arg = pin->power_source; 34462306a36Sopenharmony_ci break; 34562306a36Sopenharmony_ci case PIN_CONFIG_DRIVE_STRENGTH: 34662306a36Sopenharmony_ci arg = pin->drive_strength; 34762306a36Sopenharmony_ci break; 34862306a36Sopenharmony_ci case PM8XXX_CONFIG_DTEST_SELECTOR: 34962306a36Sopenharmony_ci arg = pin->dtest; 35062306a36Sopenharmony_ci break; 35162306a36Sopenharmony_ci case PM8XXX_CONFIG_AMUX: 35262306a36Sopenharmony_ci arg = pin->amux; 35362306a36Sopenharmony_ci break; 35462306a36Sopenharmony_ci case PM8XXX_CONFIG_ALEVEL: 35562306a36Sopenharmony_ci arg = pin->aout_level; 35662306a36Sopenharmony_ci break; 35762306a36Sopenharmony_ci case PM8XXX_CONFIG_PAIRED: 35862306a36Sopenharmony_ci arg = pin->paired; 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci default: 36162306a36Sopenharmony_ci return -EINVAL; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci *config = pinconf_to_config_packed(param, arg); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci return 0; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic int pm8xxx_pin_config_set(struct pinctrl_dev *pctldev, 37062306a36Sopenharmony_ci unsigned int offset, 37162306a36Sopenharmony_ci unsigned long *configs, 37262306a36Sopenharmony_ci unsigned num_configs) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci struct pm8xxx_mpp *pctrl = pinctrl_dev_get_drvdata(pctldev); 37562306a36Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 37662306a36Sopenharmony_ci unsigned param; 37762306a36Sopenharmony_ci unsigned arg; 37862306a36Sopenharmony_ci unsigned i; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci for (i = 0; i < num_configs; i++) { 38162306a36Sopenharmony_ci param = pinconf_to_config_param(configs[i]); 38262306a36Sopenharmony_ci arg = pinconf_to_config_argument(configs[i]); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci switch (param) { 38562306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 38662306a36Sopenharmony_ci pin->pullup = arg; 38762306a36Sopenharmony_ci break; 38862306a36Sopenharmony_ci case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: 38962306a36Sopenharmony_ci pin->high_z = true; 39062306a36Sopenharmony_ci break; 39162306a36Sopenharmony_ci case PIN_CONFIG_INPUT_ENABLE: 39262306a36Sopenharmony_ci pin->input = true; 39362306a36Sopenharmony_ci break; 39462306a36Sopenharmony_ci case PIN_CONFIG_OUTPUT: 39562306a36Sopenharmony_ci pin->output = true; 39662306a36Sopenharmony_ci pin->output_value = !!arg; 39762306a36Sopenharmony_ci break; 39862306a36Sopenharmony_ci case PIN_CONFIG_POWER_SOURCE: 39962306a36Sopenharmony_ci pin->power_source = arg; 40062306a36Sopenharmony_ci break; 40162306a36Sopenharmony_ci case PIN_CONFIG_DRIVE_STRENGTH: 40262306a36Sopenharmony_ci pin->drive_strength = arg; 40362306a36Sopenharmony_ci break; 40462306a36Sopenharmony_ci case PM8XXX_CONFIG_DTEST_SELECTOR: 40562306a36Sopenharmony_ci pin->dtest = arg; 40662306a36Sopenharmony_ci break; 40762306a36Sopenharmony_ci case PM8XXX_CONFIG_AMUX: 40862306a36Sopenharmony_ci pin->amux = arg; 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci case PM8XXX_CONFIG_ALEVEL: 41162306a36Sopenharmony_ci pin->aout_level = arg; 41262306a36Sopenharmony_ci break; 41362306a36Sopenharmony_ci case PM8XXX_CONFIG_PAIRED: 41462306a36Sopenharmony_ci pin->paired = !!arg; 41562306a36Sopenharmony_ci break; 41662306a36Sopenharmony_ci default: 41762306a36Sopenharmony_ci dev_err(pctrl->dev, 41862306a36Sopenharmony_ci "unsupported config parameter: %x\n", 41962306a36Sopenharmony_ci param); 42062306a36Sopenharmony_ci return -EINVAL; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci pm8xxx_mpp_update(pctrl, pin); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci return 0; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic const struct pinconf_ops pm8xxx_pinconf_ops = { 43062306a36Sopenharmony_ci .is_generic = true, 43162306a36Sopenharmony_ci .pin_config_group_get = pm8xxx_pin_config_get, 43262306a36Sopenharmony_ci .pin_config_group_set = pm8xxx_pin_config_set, 43362306a36Sopenharmony_ci}; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic const struct pinctrl_desc pm8xxx_pinctrl_desc = { 43662306a36Sopenharmony_ci .name = "pm8xxx_mpp", 43762306a36Sopenharmony_ci .pctlops = &pm8xxx_pinctrl_ops, 43862306a36Sopenharmony_ci .pmxops = &pm8xxx_pinmux_ops, 43962306a36Sopenharmony_ci .confops = &pm8xxx_pinconf_ops, 44062306a36Sopenharmony_ci .owner = THIS_MODULE, 44162306a36Sopenharmony_ci}; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic int pm8xxx_mpp_direction_input(struct gpio_chip *chip, 44462306a36Sopenharmony_ci unsigned offset) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci struct pm8xxx_mpp *pctrl = gpiochip_get_data(chip); 44762306a36Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci switch (pin->mode) { 45062306a36Sopenharmony_ci case PM8XXX_MPP_DIGITAL: 45162306a36Sopenharmony_ci pin->input = true; 45262306a36Sopenharmony_ci break; 45362306a36Sopenharmony_ci case PM8XXX_MPP_ANALOG: 45462306a36Sopenharmony_ci pin->input = true; 45562306a36Sopenharmony_ci pin->output = true; 45662306a36Sopenharmony_ci break; 45762306a36Sopenharmony_ci case PM8XXX_MPP_SINK: 45862306a36Sopenharmony_ci return -EINVAL; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci pm8xxx_mpp_update(pctrl, pin); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic int pm8xxx_mpp_direction_output(struct gpio_chip *chip, 46762306a36Sopenharmony_ci unsigned offset, 46862306a36Sopenharmony_ci int value) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci struct pm8xxx_mpp *pctrl = gpiochip_get_data(chip); 47162306a36Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci switch (pin->mode) { 47462306a36Sopenharmony_ci case PM8XXX_MPP_DIGITAL: 47562306a36Sopenharmony_ci pin->output = true; 47662306a36Sopenharmony_ci break; 47762306a36Sopenharmony_ci case PM8XXX_MPP_ANALOG: 47862306a36Sopenharmony_ci pin->input = false; 47962306a36Sopenharmony_ci pin->output = true; 48062306a36Sopenharmony_ci break; 48162306a36Sopenharmony_ci case PM8XXX_MPP_SINK: 48262306a36Sopenharmony_ci pin->input = false; 48362306a36Sopenharmony_ci pin->output = true; 48462306a36Sopenharmony_ci break; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci pm8xxx_mpp_update(pctrl, pin); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci return 0; 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic int pm8xxx_mpp_get(struct gpio_chip *chip, unsigned offset) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct pm8xxx_mpp *pctrl = gpiochip_get_data(chip); 49562306a36Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 49662306a36Sopenharmony_ci bool state; 49762306a36Sopenharmony_ci int ret, irq; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (!pin->input) 50062306a36Sopenharmony_ci return !!pin->output_value; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci irq = chip->to_irq(chip, offset); 50362306a36Sopenharmony_ci if (irq < 0) 50462306a36Sopenharmony_ci return irq; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci ret = irq_get_irqchip_state(irq, IRQCHIP_STATE_LINE_LEVEL, &state); 50762306a36Sopenharmony_ci if (!ret) 50862306a36Sopenharmony_ci ret = !!state; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci return ret; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic void pm8xxx_mpp_set(struct gpio_chip *chip, unsigned offset, int value) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci struct pm8xxx_mpp *pctrl = gpiochip_get_data(chip); 51662306a36Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci pin->output_value = !!value; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci pm8xxx_mpp_update(pctrl, pin); 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic int pm8xxx_mpp_of_xlate(struct gpio_chip *chip, 52462306a36Sopenharmony_ci const struct of_phandle_args *gpio_desc, 52562306a36Sopenharmony_ci u32 *flags) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci if (chip->of_gpio_n_cells < 2) 52862306a36Sopenharmony_ci return -EINVAL; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (flags) 53162306a36Sopenharmony_ci *flags = gpio_desc->args[1]; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci return gpio_desc->args[0] - PM8XXX_MPP_PHYSICAL_OFFSET; 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic void pm8xxx_mpp_dbg_show_one(struct seq_file *s, 54062306a36Sopenharmony_ci struct pinctrl_dev *pctldev, 54162306a36Sopenharmony_ci struct gpio_chip *chip, 54262306a36Sopenharmony_ci unsigned offset, 54362306a36Sopenharmony_ci unsigned gpio) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci struct pm8xxx_mpp *pctrl = gpiochip_get_data(chip); 54662306a36Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci static const char * const aout_lvls[] = { 54962306a36Sopenharmony_ci "1v25", "1v25_2", "0v625", "0v3125", "mpp", "abus1", "abus2", 55062306a36Sopenharmony_ci "abus3" 55162306a36Sopenharmony_ci }; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci static const char * const amuxs[] = { 55462306a36Sopenharmony_ci "amux5", "amux6", "amux7", "amux8", "amux9", "abus1", "abus2", 55562306a36Sopenharmony_ci "abus3", 55662306a36Sopenharmony_ci }; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci seq_printf(s, " mpp%-2d:", offset + PM8XXX_MPP_PHYSICAL_OFFSET); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci switch (pin->mode) { 56162306a36Sopenharmony_ci case PM8XXX_MPP_DIGITAL: 56262306a36Sopenharmony_ci seq_puts(s, " digital "); 56362306a36Sopenharmony_ci if (pin->dtest) { 56462306a36Sopenharmony_ci seq_printf(s, "dtest%d\n", pin->dtest); 56562306a36Sopenharmony_ci } else if (pin->input && pin->output) { 56662306a36Sopenharmony_ci if (pin->high_z) 56762306a36Sopenharmony_ci seq_puts(s, "bi-dir high-z"); 56862306a36Sopenharmony_ci else 56962306a36Sopenharmony_ci seq_printf(s, "bi-dir %dOhm", pin->pullup); 57062306a36Sopenharmony_ci } else if (pin->input) { 57162306a36Sopenharmony_ci if (pin->dtest) 57262306a36Sopenharmony_ci seq_printf(s, "in dtest%d", pin->dtest); 57362306a36Sopenharmony_ci else 57462306a36Sopenharmony_ci seq_puts(s, "in gpio"); 57562306a36Sopenharmony_ci } else if (pin->output) { 57662306a36Sopenharmony_ci seq_puts(s, "out "); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (!pin->paired) { 57962306a36Sopenharmony_ci seq_puts(s, pin->output_value ? 58062306a36Sopenharmony_ci "high" : "low"); 58162306a36Sopenharmony_ci } else { 58262306a36Sopenharmony_ci seq_puts(s, pin->output_value ? 58362306a36Sopenharmony_ci "inverted" : "follow"); 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci break; 58762306a36Sopenharmony_ci case PM8XXX_MPP_ANALOG: 58862306a36Sopenharmony_ci seq_puts(s, " analog "); 58962306a36Sopenharmony_ci if (pin->output) { 59062306a36Sopenharmony_ci seq_printf(s, "out %s ", aout_lvls[pin->aout_level]); 59162306a36Sopenharmony_ci if (!pin->paired) { 59262306a36Sopenharmony_ci seq_puts(s, pin->output_value ? 59362306a36Sopenharmony_ci "high" : "low"); 59462306a36Sopenharmony_ci } else { 59562306a36Sopenharmony_ci seq_puts(s, pin->output_value ? 59662306a36Sopenharmony_ci "inverted" : "follow"); 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci } else { 59962306a36Sopenharmony_ci seq_printf(s, "input mux %s", amuxs[pin->amux]); 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci break; 60262306a36Sopenharmony_ci case PM8XXX_MPP_SINK: 60362306a36Sopenharmony_ci seq_printf(s, " sink %dmA ", pin->drive_strength); 60462306a36Sopenharmony_ci if (pin->dtest) { 60562306a36Sopenharmony_ci seq_printf(s, "dtest%d", pin->dtest); 60662306a36Sopenharmony_ci } else { 60762306a36Sopenharmony_ci if (!pin->paired) { 60862306a36Sopenharmony_ci seq_puts(s, pin->output_value ? 60962306a36Sopenharmony_ci "high" : "low"); 61062306a36Sopenharmony_ci } else { 61162306a36Sopenharmony_ci seq_puts(s, pin->output_value ? 61262306a36Sopenharmony_ci "inverted" : "follow"); 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci break; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic void pm8xxx_mpp_dbg_show(struct seq_file *s, struct gpio_chip *chip) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci unsigned gpio = chip->base; 62262306a36Sopenharmony_ci unsigned i; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci for (i = 0; i < chip->ngpio; i++, gpio++) { 62562306a36Sopenharmony_ci pm8xxx_mpp_dbg_show_one(s, NULL, chip, i, gpio); 62662306a36Sopenharmony_ci seq_puts(s, "\n"); 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci#else 63162306a36Sopenharmony_ci#define pm8xxx_mpp_dbg_show NULL 63262306a36Sopenharmony_ci#endif 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_cistatic const struct gpio_chip pm8xxx_mpp_template = { 63562306a36Sopenharmony_ci .direction_input = pm8xxx_mpp_direction_input, 63662306a36Sopenharmony_ci .direction_output = pm8xxx_mpp_direction_output, 63762306a36Sopenharmony_ci .get = pm8xxx_mpp_get, 63862306a36Sopenharmony_ci .set = pm8xxx_mpp_set, 63962306a36Sopenharmony_ci .of_xlate = pm8xxx_mpp_of_xlate, 64062306a36Sopenharmony_ci .dbg_show = pm8xxx_mpp_dbg_show, 64162306a36Sopenharmony_ci .owner = THIS_MODULE, 64262306a36Sopenharmony_ci}; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cistatic int pm8xxx_pin_populate(struct pm8xxx_mpp *pctrl, 64562306a36Sopenharmony_ci struct pm8xxx_pin_data *pin) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci unsigned int val; 64862306a36Sopenharmony_ci unsigned level; 64962306a36Sopenharmony_ci unsigned ctrl; 65062306a36Sopenharmony_ci unsigned type; 65162306a36Sopenharmony_ci int ret; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci ret = regmap_read(pctrl->regmap, pin->reg, &val); 65462306a36Sopenharmony_ci if (ret) { 65562306a36Sopenharmony_ci dev_err(pctrl->dev, "failed to read register\n"); 65662306a36Sopenharmony_ci return ret; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci type = (val >> 5) & 7; 66062306a36Sopenharmony_ci level = (val >> 2) & 7; 66162306a36Sopenharmony_ci ctrl = (val) & 3; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci switch (type) { 66462306a36Sopenharmony_ci case PM8XXX_MPP_TYPE_D_INPUT: 66562306a36Sopenharmony_ci pin->mode = PM8XXX_MPP_DIGITAL; 66662306a36Sopenharmony_ci pin->input = true; 66762306a36Sopenharmony_ci pin->power_source = level; 66862306a36Sopenharmony_ci pin->dtest = ctrl; 66962306a36Sopenharmony_ci break; 67062306a36Sopenharmony_ci case PM8XXX_MPP_TYPE_D_OUTPUT: 67162306a36Sopenharmony_ci pin->mode = PM8XXX_MPP_DIGITAL; 67262306a36Sopenharmony_ci pin->output = true; 67362306a36Sopenharmony_ci pin->power_source = level; 67462306a36Sopenharmony_ci pin->output_value = !!(ctrl & BIT(0)); 67562306a36Sopenharmony_ci pin->paired = !!(ctrl & BIT(1)); 67662306a36Sopenharmony_ci break; 67762306a36Sopenharmony_ci case PM8XXX_MPP_TYPE_D_BI_DIR: 67862306a36Sopenharmony_ci pin->mode = PM8XXX_MPP_DIGITAL; 67962306a36Sopenharmony_ci pin->input = true; 68062306a36Sopenharmony_ci pin->output = true; 68162306a36Sopenharmony_ci pin->power_source = level; 68262306a36Sopenharmony_ci switch (ctrl) { 68362306a36Sopenharmony_ci case PM8XXX_MPP_BI_PULLUP_1KOHM: 68462306a36Sopenharmony_ci pin->pullup = 600; 68562306a36Sopenharmony_ci break; 68662306a36Sopenharmony_ci case PM8XXX_MPP_BI_PULLUP_OPEN: 68762306a36Sopenharmony_ci pin->high_z = true; 68862306a36Sopenharmony_ci break; 68962306a36Sopenharmony_ci case PM8XXX_MPP_BI_PULLUP_10KOHM: 69062306a36Sopenharmony_ci pin->pullup = 10000; 69162306a36Sopenharmony_ci break; 69262306a36Sopenharmony_ci case PM8XXX_MPP_BI_PULLUP_30KOHM: 69362306a36Sopenharmony_ci pin->pullup = 30000; 69462306a36Sopenharmony_ci break; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci break; 69762306a36Sopenharmony_ci case PM8XXX_MPP_TYPE_A_INPUT: 69862306a36Sopenharmony_ci pin->mode = PM8XXX_MPP_ANALOG; 69962306a36Sopenharmony_ci pin->input = true; 70062306a36Sopenharmony_ci pin->amux = level; 70162306a36Sopenharmony_ci break; 70262306a36Sopenharmony_ci case PM8XXX_MPP_TYPE_A_OUTPUT: 70362306a36Sopenharmony_ci pin->mode = PM8XXX_MPP_ANALOG; 70462306a36Sopenharmony_ci pin->output = true; 70562306a36Sopenharmony_ci pin->aout_level = level; 70662306a36Sopenharmony_ci pin->output_value = !!(ctrl & BIT(0)); 70762306a36Sopenharmony_ci pin->paired = !!(ctrl & BIT(1)); 70862306a36Sopenharmony_ci break; 70962306a36Sopenharmony_ci case PM8XXX_MPP_TYPE_SINK: 71062306a36Sopenharmony_ci pin->mode = PM8XXX_MPP_SINK; 71162306a36Sopenharmony_ci pin->drive_strength = 5 * (level + 1); 71262306a36Sopenharmony_ci pin->output_value = !!(ctrl & BIT(0)); 71362306a36Sopenharmony_ci pin->paired = !!(ctrl & BIT(1)); 71462306a36Sopenharmony_ci break; 71562306a36Sopenharmony_ci case PM8XXX_MPP_TYPE_DTEST_SINK: 71662306a36Sopenharmony_ci pin->mode = PM8XXX_MPP_SINK; 71762306a36Sopenharmony_ci pin->dtest = ctrl + 1; 71862306a36Sopenharmony_ci pin->drive_strength = 5 * (level + 1); 71962306a36Sopenharmony_ci break; 72062306a36Sopenharmony_ci case PM8XXX_MPP_TYPE_DTEST_OUTPUT: 72162306a36Sopenharmony_ci pin->mode = PM8XXX_MPP_DIGITAL; 72262306a36Sopenharmony_ci pin->power_source = level; 72362306a36Sopenharmony_ci if (ctrl >= 1) 72462306a36Sopenharmony_ci pin->dtest = ctrl; 72562306a36Sopenharmony_ci break; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci return 0; 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic int pm8xxx_mpp_domain_translate(struct irq_domain *domain, 73262306a36Sopenharmony_ci struct irq_fwspec *fwspec, 73362306a36Sopenharmony_ci unsigned long *hwirq, 73462306a36Sopenharmony_ci unsigned int *type) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci struct pm8xxx_mpp *pctrl = container_of(domain->host_data, 73762306a36Sopenharmony_ci struct pm8xxx_mpp, chip); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if (fwspec->param_count != 2 || 74062306a36Sopenharmony_ci fwspec->param[0] < PM8XXX_MPP_PHYSICAL_OFFSET || 74162306a36Sopenharmony_ci fwspec->param[0] > pctrl->chip.ngpio) 74262306a36Sopenharmony_ci return -EINVAL; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci *hwirq = fwspec->param[0] - PM8XXX_MPP_PHYSICAL_OFFSET; 74562306a36Sopenharmony_ci *type = fwspec->param[1]; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci return 0; 74862306a36Sopenharmony_ci} 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_cistatic unsigned int pm8xxx_mpp_child_offset_to_irq(struct gpio_chip *chip, 75162306a36Sopenharmony_ci unsigned int offset) 75262306a36Sopenharmony_ci{ 75362306a36Sopenharmony_ci return offset + PM8XXX_MPP_PHYSICAL_OFFSET; 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cistatic int pm8821_mpp_child_to_parent_hwirq(struct gpio_chip *chip, 75762306a36Sopenharmony_ci unsigned int child_hwirq, 75862306a36Sopenharmony_ci unsigned int child_type, 75962306a36Sopenharmony_ci unsigned int *parent_hwirq, 76062306a36Sopenharmony_ci unsigned int *parent_type) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci *parent_hwirq = child_hwirq + 24; 76362306a36Sopenharmony_ci *parent_type = child_type; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci return 0; 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic int pm8xxx_mpp_child_to_parent_hwirq(struct gpio_chip *chip, 76962306a36Sopenharmony_ci unsigned int child_hwirq, 77062306a36Sopenharmony_ci unsigned int child_type, 77162306a36Sopenharmony_ci unsigned int *parent_hwirq, 77262306a36Sopenharmony_ci unsigned int *parent_type) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci *parent_hwirq = child_hwirq + 0x80; 77562306a36Sopenharmony_ci *parent_type = child_type; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci return 0; 77862306a36Sopenharmony_ci} 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cistatic void pm8xxx_mpp_irq_disable(struct irq_data *d) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci gpiochip_disable_irq(gc, irqd_to_hwirq(d)); 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cistatic void pm8xxx_mpp_irq_enable(struct irq_data *d) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci gpiochip_enable_irq(gc, irqd_to_hwirq(d)); 79262306a36Sopenharmony_ci} 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cistatic const struct irq_chip pm8xxx_mpp_irq_chip = { 79562306a36Sopenharmony_ci .name = "ssbi-mpp", 79662306a36Sopenharmony_ci .irq_mask_ack = irq_chip_mask_ack_parent, 79762306a36Sopenharmony_ci .irq_unmask = irq_chip_unmask_parent, 79862306a36Sopenharmony_ci .irq_disable = pm8xxx_mpp_irq_disable, 79962306a36Sopenharmony_ci .irq_enable = pm8xxx_mpp_irq_enable, 80062306a36Sopenharmony_ci .irq_set_type = irq_chip_set_type_parent, 80162306a36Sopenharmony_ci .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE | 80262306a36Sopenharmony_ci IRQCHIP_IMMUTABLE, 80362306a36Sopenharmony_ci GPIOCHIP_IRQ_RESOURCE_HELPERS, 80462306a36Sopenharmony_ci}; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cistatic const struct of_device_id pm8xxx_mpp_of_match[] = { 80762306a36Sopenharmony_ci { .compatible = "qcom,pm8018-mpp", .data = (void *) 6 }, 80862306a36Sopenharmony_ci { .compatible = "qcom,pm8038-mpp", .data = (void *) 6 }, 80962306a36Sopenharmony_ci { .compatible = "qcom,pm8058-mpp", .data = (void *) 12 }, 81062306a36Sopenharmony_ci { .compatible = "qcom,pm8821-mpp", .data = (void *) 4 }, 81162306a36Sopenharmony_ci { .compatible = "qcom,pm8917-mpp", .data = (void *) 10 }, 81262306a36Sopenharmony_ci { .compatible = "qcom,pm8921-mpp", .data = (void *) 12 }, 81362306a36Sopenharmony_ci { }, 81462306a36Sopenharmony_ci}; 81562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, pm8xxx_mpp_of_match); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_cistatic int pm8xxx_mpp_probe(struct platform_device *pdev) 81862306a36Sopenharmony_ci{ 81962306a36Sopenharmony_ci struct pm8xxx_pin_data *pin_data; 82062306a36Sopenharmony_ci struct irq_domain *parent_domain; 82162306a36Sopenharmony_ci struct device_node *parent_node; 82262306a36Sopenharmony_ci struct pinctrl_pin_desc *pins; 82362306a36Sopenharmony_ci struct gpio_irq_chip *girq; 82462306a36Sopenharmony_ci struct pm8xxx_mpp *pctrl; 82562306a36Sopenharmony_ci int ret; 82662306a36Sopenharmony_ci int i; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); 82962306a36Sopenharmony_ci if (!pctrl) 83062306a36Sopenharmony_ci return -ENOMEM; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci pctrl->dev = &pdev->dev; 83362306a36Sopenharmony_ci pctrl->npins = (uintptr_t) device_get_match_data(&pdev->dev); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci pctrl->regmap = dev_get_regmap(pdev->dev.parent, NULL); 83662306a36Sopenharmony_ci if (!pctrl->regmap) { 83762306a36Sopenharmony_ci dev_err(&pdev->dev, "parent regmap unavailable\n"); 83862306a36Sopenharmony_ci return -ENXIO; 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci pctrl->desc = pm8xxx_pinctrl_desc; 84262306a36Sopenharmony_ci pctrl->desc.npins = pctrl->npins; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci pins = devm_kcalloc(&pdev->dev, 84562306a36Sopenharmony_ci pctrl->desc.npins, 84662306a36Sopenharmony_ci sizeof(struct pinctrl_pin_desc), 84762306a36Sopenharmony_ci GFP_KERNEL); 84862306a36Sopenharmony_ci if (!pins) 84962306a36Sopenharmony_ci return -ENOMEM; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci pin_data = devm_kcalloc(&pdev->dev, 85262306a36Sopenharmony_ci pctrl->desc.npins, 85362306a36Sopenharmony_ci sizeof(struct pm8xxx_pin_data), 85462306a36Sopenharmony_ci GFP_KERNEL); 85562306a36Sopenharmony_ci if (!pin_data) 85662306a36Sopenharmony_ci return -ENOMEM; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci for (i = 0; i < pctrl->desc.npins; i++) { 85962306a36Sopenharmony_ci pin_data[i].reg = SSBI_REG_ADDR_MPP(i); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci ret = pm8xxx_pin_populate(pctrl, &pin_data[i]); 86262306a36Sopenharmony_ci if (ret) 86362306a36Sopenharmony_ci return ret; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci pins[i].number = i; 86662306a36Sopenharmony_ci pins[i].name = pm8xxx_groups[i]; 86762306a36Sopenharmony_ci pins[i].drv_data = &pin_data[i]; 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci pctrl->desc.pins = pins; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci pctrl->desc.num_custom_params = ARRAY_SIZE(pm8xxx_mpp_bindings); 87262306a36Sopenharmony_ci pctrl->desc.custom_params = pm8xxx_mpp_bindings; 87362306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 87462306a36Sopenharmony_ci pctrl->desc.custom_conf_items = pm8xxx_conf_items; 87562306a36Sopenharmony_ci#endif 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci pctrl->pctrl = devm_pinctrl_register(&pdev->dev, &pctrl->desc, pctrl); 87862306a36Sopenharmony_ci if (IS_ERR(pctrl->pctrl)) { 87962306a36Sopenharmony_ci dev_err(&pdev->dev, "couldn't register pm8xxx mpp driver\n"); 88062306a36Sopenharmony_ci return PTR_ERR(pctrl->pctrl); 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci pctrl->chip = pm8xxx_mpp_template; 88462306a36Sopenharmony_ci pctrl->chip.base = -1; 88562306a36Sopenharmony_ci pctrl->chip.parent = &pdev->dev; 88662306a36Sopenharmony_ci pctrl->chip.of_gpio_n_cells = 2; 88762306a36Sopenharmony_ci pctrl->chip.label = dev_name(pctrl->dev); 88862306a36Sopenharmony_ci pctrl->chip.ngpio = pctrl->npins; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci parent_node = of_irq_find_parent(pctrl->dev->of_node); 89162306a36Sopenharmony_ci if (!parent_node) 89262306a36Sopenharmony_ci return -ENXIO; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci parent_domain = irq_find_host(parent_node); 89562306a36Sopenharmony_ci of_node_put(parent_node); 89662306a36Sopenharmony_ci if (!parent_domain) 89762306a36Sopenharmony_ci return -ENXIO; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci girq = &pctrl->chip.irq; 90062306a36Sopenharmony_ci gpio_irq_chip_set_chip(girq, &pm8xxx_mpp_irq_chip); 90162306a36Sopenharmony_ci girq->default_type = IRQ_TYPE_NONE; 90262306a36Sopenharmony_ci girq->handler = handle_level_irq; 90362306a36Sopenharmony_ci girq->fwnode = dev_fwnode(pctrl->dev); 90462306a36Sopenharmony_ci girq->parent_domain = parent_domain; 90562306a36Sopenharmony_ci if (of_device_is_compatible(pdev->dev.of_node, "qcom,pm8821-mpp")) 90662306a36Sopenharmony_ci girq->child_to_parent_hwirq = pm8821_mpp_child_to_parent_hwirq; 90762306a36Sopenharmony_ci else 90862306a36Sopenharmony_ci girq->child_to_parent_hwirq = pm8xxx_mpp_child_to_parent_hwirq; 90962306a36Sopenharmony_ci girq->populate_parent_alloc_arg = gpiochip_populate_parent_fwspec_twocell; 91062306a36Sopenharmony_ci girq->child_offset_to_irq = pm8xxx_mpp_child_offset_to_irq; 91162306a36Sopenharmony_ci girq->child_irq_domain_ops.translate = pm8xxx_mpp_domain_translate; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci ret = gpiochip_add_data(&pctrl->chip, pctrl); 91462306a36Sopenharmony_ci if (ret) { 91562306a36Sopenharmony_ci dev_err(&pdev->dev, "failed register gpiochip\n"); 91662306a36Sopenharmony_ci return ret; 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci ret = gpiochip_add_pin_range(&pctrl->chip, 92062306a36Sopenharmony_ci dev_name(pctrl->dev), 92162306a36Sopenharmony_ci 0, 0, pctrl->chip.ngpio); 92262306a36Sopenharmony_ci if (ret) { 92362306a36Sopenharmony_ci dev_err(pctrl->dev, "failed to add pin range\n"); 92462306a36Sopenharmony_ci goto unregister_gpiochip; 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci platform_set_drvdata(pdev, pctrl); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Qualcomm pm8xxx mpp driver probed\n"); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci return 0; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ciunregister_gpiochip: 93462306a36Sopenharmony_ci gpiochip_remove(&pctrl->chip); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci return ret; 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_cistatic int pm8xxx_mpp_remove(struct platform_device *pdev) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci struct pm8xxx_mpp *pctrl = platform_get_drvdata(pdev); 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci gpiochip_remove(&pctrl->chip); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci return 0; 94662306a36Sopenharmony_ci} 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_cistatic struct platform_driver pm8xxx_mpp_driver = { 94962306a36Sopenharmony_ci .driver = { 95062306a36Sopenharmony_ci .name = "qcom-ssbi-mpp", 95162306a36Sopenharmony_ci .of_match_table = pm8xxx_mpp_of_match, 95262306a36Sopenharmony_ci }, 95362306a36Sopenharmony_ci .probe = pm8xxx_mpp_probe, 95462306a36Sopenharmony_ci .remove = pm8xxx_mpp_remove, 95562306a36Sopenharmony_ci}; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_cimodule_platform_driver(pm8xxx_mpp_driver); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ciMODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>"); 96062306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm PM8xxx MPP driver"); 96162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 962