18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2015, Sony Mobile Communications AB. 48c2ecf20Sopenharmony_ci * Copyright (c) 2013, The Linux Foundation. All rights reserved. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 98c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinctrl.h> 108c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinmux.h> 118c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf.h> 128c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf-generic.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/regmap.h> 158c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/of_device.h> 188c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <dt-bindings/pinctrl/qcom,pmic-mpp.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "../core.h" 238c2ecf20Sopenharmony_ci#include "../pinctrl-utils.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* MPP registers */ 268c2ecf20Sopenharmony_ci#define SSBI_REG_ADDR_MPP_BASE 0x50 278c2ecf20Sopenharmony_ci#define SSBI_REG_ADDR_MPP(n) (SSBI_REG_ADDR_MPP_BASE + n) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* MPP Type: type */ 308c2ecf20Sopenharmony_ci#define PM8XXX_MPP_TYPE_D_INPUT 0 318c2ecf20Sopenharmony_ci#define PM8XXX_MPP_TYPE_D_OUTPUT 1 328c2ecf20Sopenharmony_ci#define PM8XXX_MPP_TYPE_D_BI_DIR 2 338c2ecf20Sopenharmony_ci#define PM8XXX_MPP_TYPE_A_INPUT 3 348c2ecf20Sopenharmony_ci#define PM8XXX_MPP_TYPE_A_OUTPUT 4 358c2ecf20Sopenharmony_ci#define PM8XXX_MPP_TYPE_SINK 5 368c2ecf20Sopenharmony_ci#define PM8XXX_MPP_TYPE_DTEST_SINK 6 378c2ecf20Sopenharmony_ci#define PM8XXX_MPP_TYPE_DTEST_OUTPUT 7 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* Digital Input: control */ 408c2ecf20Sopenharmony_ci#define PM8XXX_MPP_DIN_TO_INT 0 418c2ecf20Sopenharmony_ci#define PM8XXX_MPP_DIN_TO_DBUS1 1 428c2ecf20Sopenharmony_ci#define PM8XXX_MPP_DIN_TO_DBUS2 2 438c2ecf20Sopenharmony_ci#define PM8XXX_MPP_DIN_TO_DBUS3 3 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* Digital Output: control */ 468c2ecf20Sopenharmony_ci#define PM8XXX_MPP_DOUT_CTRL_LOW 0 478c2ecf20Sopenharmony_ci#define PM8XXX_MPP_DOUT_CTRL_HIGH 1 488c2ecf20Sopenharmony_ci#define PM8XXX_MPP_DOUT_CTRL_MPP 2 498c2ecf20Sopenharmony_ci#define PM8XXX_MPP_DOUT_CTRL_INV_MPP 3 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* Bidirectional: control */ 528c2ecf20Sopenharmony_ci#define PM8XXX_MPP_BI_PULLUP_1KOHM 0 538c2ecf20Sopenharmony_ci#define PM8XXX_MPP_BI_PULLUP_OPEN 1 548c2ecf20Sopenharmony_ci#define PM8XXX_MPP_BI_PULLUP_10KOHM 2 558c2ecf20Sopenharmony_ci#define PM8XXX_MPP_BI_PULLUP_30KOHM 3 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* Analog Output: control */ 588c2ecf20Sopenharmony_ci#define PM8XXX_MPP_AOUT_CTRL_DISABLE 0 598c2ecf20Sopenharmony_ci#define PM8XXX_MPP_AOUT_CTRL_ENABLE 1 608c2ecf20Sopenharmony_ci#define PM8XXX_MPP_AOUT_CTRL_MPP_HIGH_EN 2 618c2ecf20Sopenharmony_ci#define PM8XXX_MPP_AOUT_CTRL_MPP_LOW_EN 3 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* Current Sink: control */ 648c2ecf20Sopenharmony_ci#define PM8XXX_MPP_CS_CTRL_DISABLE 0 658c2ecf20Sopenharmony_ci#define PM8XXX_MPP_CS_CTRL_ENABLE 1 668c2ecf20Sopenharmony_ci#define PM8XXX_MPP_CS_CTRL_MPP_HIGH_EN 2 678c2ecf20Sopenharmony_ci#define PM8XXX_MPP_CS_CTRL_MPP_LOW_EN 3 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* DTEST Current Sink: control */ 708c2ecf20Sopenharmony_ci#define PM8XXX_MPP_DTEST_CS_CTRL_EN1 0 718c2ecf20Sopenharmony_ci#define PM8XXX_MPP_DTEST_CS_CTRL_EN2 1 728c2ecf20Sopenharmony_ci#define PM8XXX_MPP_DTEST_CS_CTRL_EN3 2 738c2ecf20Sopenharmony_ci#define PM8XXX_MPP_DTEST_CS_CTRL_EN4 3 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* DTEST Digital Output: control */ 768c2ecf20Sopenharmony_ci#define PM8XXX_MPP_DTEST_DBUS1 0 778c2ecf20Sopenharmony_ci#define PM8XXX_MPP_DTEST_DBUS2 1 788c2ecf20Sopenharmony_ci#define PM8XXX_MPP_DTEST_DBUS3 2 798c2ecf20Sopenharmony_ci#define PM8XXX_MPP_DTEST_DBUS4 3 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* custom pinconf parameters */ 828c2ecf20Sopenharmony_ci#define PM8XXX_CONFIG_AMUX (PIN_CONFIG_END + 1) 838c2ecf20Sopenharmony_ci#define PM8XXX_CONFIG_DTEST_SELECTOR (PIN_CONFIG_END + 2) 848c2ecf20Sopenharmony_ci#define PM8XXX_CONFIG_ALEVEL (PIN_CONFIG_END + 3) 858c2ecf20Sopenharmony_ci#define PM8XXX_CONFIG_PAIRED (PIN_CONFIG_END + 4) 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/** 888c2ecf20Sopenharmony_ci * struct pm8xxx_pin_data - dynamic configuration for a pin 898c2ecf20Sopenharmony_ci * @reg: address of the control register 908c2ecf20Sopenharmony_ci * @irq: IRQ from the PMIC interrupt controller 918c2ecf20Sopenharmony_ci * @mode: operating mode for the pin (digital, analog or current sink) 928c2ecf20Sopenharmony_ci * @input: pin is input 938c2ecf20Sopenharmony_ci * @output: pin is output 948c2ecf20Sopenharmony_ci * @high_z: pin is floating 958c2ecf20Sopenharmony_ci * @paired: mpp operates in paired mode 968c2ecf20Sopenharmony_ci * @output_value: logical output value of the mpp 978c2ecf20Sopenharmony_ci * @power_source: selected power source 988c2ecf20Sopenharmony_ci * @dtest: DTEST route selector 998c2ecf20Sopenharmony_ci * @amux: input muxing in analog mode 1008c2ecf20Sopenharmony_ci * @aout_level: selector of the output in analog mode 1018c2ecf20Sopenharmony_ci * @drive_strength: drive strength of the current sink 1028c2ecf20Sopenharmony_ci * @pullup: pull up value, when in digital bidirectional mode 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_cistruct pm8xxx_pin_data { 1058c2ecf20Sopenharmony_ci unsigned reg; 1068c2ecf20Sopenharmony_ci int irq; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci u8 mode; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci bool input; 1118c2ecf20Sopenharmony_ci bool output; 1128c2ecf20Sopenharmony_ci bool high_z; 1138c2ecf20Sopenharmony_ci bool paired; 1148c2ecf20Sopenharmony_ci bool output_value; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci u8 power_source; 1178c2ecf20Sopenharmony_ci u8 dtest; 1188c2ecf20Sopenharmony_ci u8 amux; 1198c2ecf20Sopenharmony_ci u8 aout_level; 1208c2ecf20Sopenharmony_ci u8 drive_strength; 1218c2ecf20Sopenharmony_ci unsigned pullup; 1228c2ecf20Sopenharmony_ci}; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistruct pm8xxx_mpp { 1258c2ecf20Sopenharmony_ci struct device *dev; 1268c2ecf20Sopenharmony_ci struct regmap *regmap; 1278c2ecf20Sopenharmony_ci struct pinctrl_dev *pctrl; 1288c2ecf20Sopenharmony_ci struct gpio_chip chip; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci struct pinctrl_desc desc; 1318c2ecf20Sopenharmony_ci unsigned npins; 1328c2ecf20Sopenharmony_ci}; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic const struct pinconf_generic_params pm8xxx_mpp_bindings[] = { 1358c2ecf20Sopenharmony_ci {"qcom,amux-route", PM8XXX_CONFIG_AMUX, 0}, 1368c2ecf20Sopenharmony_ci {"qcom,analog-level", PM8XXX_CONFIG_ALEVEL, 0}, 1378c2ecf20Sopenharmony_ci {"qcom,dtest", PM8XXX_CONFIG_DTEST_SELECTOR, 0}, 1388c2ecf20Sopenharmony_ci {"qcom,paired", PM8XXX_CONFIG_PAIRED, 0}, 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 1428c2ecf20Sopenharmony_cistatic const struct pin_config_item pm8xxx_conf_items[] = { 1438c2ecf20Sopenharmony_ci PCONFDUMP(PM8XXX_CONFIG_AMUX, "analog mux", NULL, true), 1448c2ecf20Sopenharmony_ci PCONFDUMP(PM8XXX_CONFIG_ALEVEL, "analog level", NULL, true), 1458c2ecf20Sopenharmony_ci PCONFDUMP(PM8XXX_CONFIG_DTEST_SELECTOR, "dtest", NULL, true), 1468c2ecf20Sopenharmony_ci PCONFDUMP(PM8XXX_CONFIG_PAIRED, "paired", NULL, false), 1478c2ecf20Sopenharmony_ci}; 1488c2ecf20Sopenharmony_ci#endif 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci#define PM8XXX_MAX_MPPS 12 1518c2ecf20Sopenharmony_cistatic const char * const pm8xxx_groups[PM8XXX_MAX_MPPS] = { 1528c2ecf20Sopenharmony_ci "mpp1", "mpp2", "mpp3", "mpp4", "mpp5", "mpp6", "mpp7", "mpp8", 1538c2ecf20Sopenharmony_ci "mpp9", "mpp10", "mpp11", "mpp12", 1548c2ecf20Sopenharmony_ci}; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci#define PM8XXX_MPP_DIGITAL 0 1578c2ecf20Sopenharmony_ci#define PM8XXX_MPP_ANALOG 1 1588c2ecf20Sopenharmony_ci#define PM8XXX_MPP_SINK 2 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic const char * const pm8xxx_mpp_functions[] = { 1618c2ecf20Sopenharmony_ci "digital", "analog", "sink", 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic int pm8xxx_mpp_update(struct pm8xxx_mpp *pctrl, 1658c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci unsigned level; 1688c2ecf20Sopenharmony_ci unsigned ctrl; 1698c2ecf20Sopenharmony_ci unsigned type; 1708c2ecf20Sopenharmony_ci int ret; 1718c2ecf20Sopenharmony_ci u8 val; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci switch (pin->mode) { 1748c2ecf20Sopenharmony_ci case PM8XXX_MPP_DIGITAL: 1758c2ecf20Sopenharmony_ci if (pin->dtest) { 1768c2ecf20Sopenharmony_ci type = PM8XXX_MPP_TYPE_DTEST_OUTPUT; 1778c2ecf20Sopenharmony_ci ctrl = pin->dtest - 1; 1788c2ecf20Sopenharmony_ci } else if (pin->input && pin->output) { 1798c2ecf20Sopenharmony_ci type = PM8XXX_MPP_TYPE_D_BI_DIR; 1808c2ecf20Sopenharmony_ci if (pin->high_z) 1818c2ecf20Sopenharmony_ci ctrl = PM8XXX_MPP_BI_PULLUP_OPEN; 1828c2ecf20Sopenharmony_ci else if (pin->pullup == 600) 1838c2ecf20Sopenharmony_ci ctrl = PM8XXX_MPP_BI_PULLUP_1KOHM; 1848c2ecf20Sopenharmony_ci else if (pin->pullup == 10000) 1858c2ecf20Sopenharmony_ci ctrl = PM8XXX_MPP_BI_PULLUP_10KOHM; 1868c2ecf20Sopenharmony_ci else 1878c2ecf20Sopenharmony_ci ctrl = PM8XXX_MPP_BI_PULLUP_30KOHM; 1888c2ecf20Sopenharmony_ci } else if (pin->input) { 1898c2ecf20Sopenharmony_ci type = PM8XXX_MPP_TYPE_D_INPUT; 1908c2ecf20Sopenharmony_ci if (pin->dtest) 1918c2ecf20Sopenharmony_ci ctrl = pin->dtest; 1928c2ecf20Sopenharmony_ci else 1938c2ecf20Sopenharmony_ci ctrl = PM8XXX_MPP_DIN_TO_INT; 1948c2ecf20Sopenharmony_ci } else { 1958c2ecf20Sopenharmony_ci type = PM8XXX_MPP_TYPE_D_OUTPUT; 1968c2ecf20Sopenharmony_ci ctrl = !!pin->output_value; 1978c2ecf20Sopenharmony_ci if (pin->paired) 1988c2ecf20Sopenharmony_ci ctrl |= BIT(1); 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci level = pin->power_source; 2028c2ecf20Sopenharmony_ci break; 2038c2ecf20Sopenharmony_ci case PM8XXX_MPP_ANALOG: 2048c2ecf20Sopenharmony_ci if (pin->output) { 2058c2ecf20Sopenharmony_ci type = PM8XXX_MPP_TYPE_A_OUTPUT; 2068c2ecf20Sopenharmony_ci level = pin->aout_level; 2078c2ecf20Sopenharmony_ci ctrl = pin->output_value; 2088c2ecf20Sopenharmony_ci if (pin->paired) 2098c2ecf20Sopenharmony_ci ctrl |= BIT(1); 2108c2ecf20Sopenharmony_ci } else { 2118c2ecf20Sopenharmony_ci type = PM8XXX_MPP_TYPE_A_INPUT; 2128c2ecf20Sopenharmony_ci level = pin->amux; 2138c2ecf20Sopenharmony_ci ctrl = 0; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci break; 2168c2ecf20Sopenharmony_ci case PM8XXX_MPP_SINK: 2178c2ecf20Sopenharmony_ci level = (pin->drive_strength / 5) - 1; 2188c2ecf20Sopenharmony_ci if (pin->dtest) { 2198c2ecf20Sopenharmony_ci type = PM8XXX_MPP_TYPE_DTEST_SINK; 2208c2ecf20Sopenharmony_ci ctrl = pin->dtest - 1; 2218c2ecf20Sopenharmony_ci } else { 2228c2ecf20Sopenharmony_ci type = PM8XXX_MPP_TYPE_SINK; 2238c2ecf20Sopenharmony_ci ctrl = pin->output_value; 2248c2ecf20Sopenharmony_ci if (pin->paired) 2258c2ecf20Sopenharmony_ci ctrl |= BIT(1); 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci break; 2288c2ecf20Sopenharmony_ci default: 2298c2ecf20Sopenharmony_ci return -EINVAL; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci val = type << 5 | level << 2 | ctrl; 2338c2ecf20Sopenharmony_ci ret = regmap_write(pctrl->regmap, pin->reg, val); 2348c2ecf20Sopenharmony_ci if (ret) 2358c2ecf20Sopenharmony_ci dev_err(pctrl->dev, "failed to write register\n"); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci return ret; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic int pm8xxx_get_groups_count(struct pinctrl_dev *pctldev) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci struct pm8xxx_mpp *pctrl = pinctrl_dev_get_drvdata(pctldev); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return pctrl->npins; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic const char *pm8xxx_get_group_name(struct pinctrl_dev *pctldev, 2488c2ecf20Sopenharmony_ci unsigned group) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci return pm8xxx_groups[group]; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic int pm8xxx_get_group_pins(struct pinctrl_dev *pctldev, 2558c2ecf20Sopenharmony_ci unsigned group, 2568c2ecf20Sopenharmony_ci const unsigned **pins, 2578c2ecf20Sopenharmony_ci unsigned *num_pins) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci struct pm8xxx_mpp *pctrl = pinctrl_dev_get_drvdata(pctldev); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci *pins = &pctrl->desc.pins[group].number; 2628c2ecf20Sopenharmony_ci *num_pins = 1; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return 0; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic const struct pinctrl_ops pm8xxx_pinctrl_ops = { 2688c2ecf20Sopenharmony_ci .get_groups_count = pm8xxx_get_groups_count, 2698c2ecf20Sopenharmony_ci .get_group_name = pm8xxx_get_group_name, 2708c2ecf20Sopenharmony_ci .get_group_pins = pm8xxx_get_group_pins, 2718c2ecf20Sopenharmony_ci .dt_node_to_map = pinconf_generic_dt_node_to_map_group, 2728c2ecf20Sopenharmony_ci .dt_free_map = pinctrl_utils_free_map, 2738c2ecf20Sopenharmony_ci}; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic int pm8xxx_get_functions_count(struct pinctrl_dev *pctldev) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci return ARRAY_SIZE(pm8xxx_mpp_functions); 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic const char *pm8xxx_get_function_name(struct pinctrl_dev *pctldev, 2818c2ecf20Sopenharmony_ci unsigned function) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci return pm8xxx_mpp_functions[function]; 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic int pm8xxx_get_function_groups(struct pinctrl_dev *pctldev, 2878c2ecf20Sopenharmony_ci unsigned function, 2888c2ecf20Sopenharmony_ci const char * const **groups, 2898c2ecf20Sopenharmony_ci unsigned * const num_groups) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci struct pm8xxx_mpp *pctrl = pinctrl_dev_get_drvdata(pctldev); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci *groups = pm8xxx_groups; 2948c2ecf20Sopenharmony_ci *num_groups = pctrl->npins; 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int pm8xxx_pinmux_set_mux(struct pinctrl_dev *pctldev, 2998c2ecf20Sopenharmony_ci unsigned function, 3008c2ecf20Sopenharmony_ci unsigned group) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct pm8xxx_mpp *pctrl = pinctrl_dev_get_drvdata(pctldev); 3038c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[group].drv_data; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci pin->mode = function; 3068c2ecf20Sopenharmony_ci pm8xxx_mpp_update(pctrl, pin); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci return 0; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic const struct pinmux_ops pm8xxx_pinmux_ops = { 3128c2ecf20Sopenharmony_ci .get_functions_count = pm8xxx_get_functions_count, 3138c2ecf20Sopenharmony_ci .get_function_name = pm8xxx_get_function_name, 3148c2ecf20Sopenharmony_ci .get_function_groups = pm8xxx_get_function_groups, 3158c2ecf20Sopenharmony_ci .set_mux = pm8xxx_pinmux_set_mux, 3168c2ecf20Sopenharmony_ci}; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic int pm8xxx_pin_config_get(struct pinctrl_dev *pctldev, 3198c2ecf20Sopenharmony_ci unsigned int offset, 3208c2ecf20Sopenharmony_ci unsigned long *config) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct pm8xxx_mpp *pctrl = pinctrl_dev_get_drvdata(pctldev); 3238c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 3248c2ecf20Sopenharmony_ci unsigned param = pinconf_to_config_param(*config); 3258c2ecf20Sopenharmony_ci unsigned arg; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci switch (param) { 3288c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 3298c2ecf20Sopenharmony_ci arg = pin->pullup; 3308c2ecf20Sopenharmony_ci break; 3318c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: 3328c2ecf20Sopenharmony_ci arg = pin->high_z; 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci case PIN_CONFIG_INPUT_ENABLE: 3358c2ecf20Sopenharmony_ci arg = pin->input; 3368c2ecf20Sopenharmony_ci break; 3378c2ecf20Sopenharmony_ci case PIN_CONFIG_OUTPUT: 3388c2ecf20Sopenharmony_ci arg = pin->output_value; 3398c2ecf20Sopenharmony_ci break; 3408c2ecf20Sopenharmony_ci case PIN_CONFIG_POWER_SOURCE: 3418c2ecf20Sopenharmony_ci arg = pin->power_source; 3428c2ecf20Sopenharmony_ci break; 3438c2ecf20Sopenharmony_ci case PIN_CONFIG_DRIVE_STRENGTH: 3448c2ecf20Sopenharmony_ci arg = pin->drive_strength; 3458c2ecf20Sopenharmony_ci break; 3468c2ecf20Sopenharmony_ci case PM8XXX_CONFIG_DTEST_SELECTOR: 3478c2ecf20Sopenharmony_ci arg = pin->dtest; 3488c2ecf20Sopenharmony_ci break; 3498c2ecf20Sopenharmony_ci case PM8XXX_CONFIG_AMUX: 3508c2ecf20Sopenharmony_ci arg = pin->amux; 3518c2ecf20Sopenharmony_ci break; 3528c2ecf20Sopenharmony_ci case PM8XXX_CONFIG_ALEVEL: 3538c2ecf20Sopenharmony_ci arg = pin->aout_level; 3548c2ecf20Sopenharmony_ci break; 3558c2ecf20Sopenharmony_ci case PM8XXX_CONFIG_PAIRED: 3568c2ecf20Sopenharmony_ci arg = pin->paired; 3578c2ecf20Sopenharmony_ci break; 3588c2ecf20Sopenharmony_ci default: 3598c2ecf20Sopenharmony_ci return -EINVAL; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci *config = pinconf_to_config_packed(param, arg); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci return 0; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic int pm8xxx_pin_config_set(struct pinctrl_dev *pctldev, 3688c2ecf20Sopenharmony_ci unsigned int offset, 3698c2ecf20Sopenharmony_ci unsigned long *configs, 3708c2ecf20Sopenharmony_ci unsigned num_configs) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct pm8xxx_mpp *pctrl = pinctrl_dev_get_drvdata(pctldev); 3738c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 3748c2ecf20Sopenharmony_ci unsigned param; 3758c2ecf20Sopenharmony_ci unsigned arg; 3768c2ecf20Sopenharmony_ci unsigned i; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci for (i = 0; i < num_configs; i++) { 3798c2ecf20Sopenharmony_ci param = pinconf_to_config_param(configs[i]); 3808c2ecf20Sopenharmony_ci arg = pinconf_to_config_argument(configs[i]); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci switch (param) { 3838c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 3848c2ecf20Sopenharmony_ci pin->pullup = arg; 3858c2ecf20Sopenharmony_ci break; 3868c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: 3878c2ecf20Sopenharmony_ci pin->high_z = true; 3888c2ecf20Sopenharmony_ci break; 3898c2ecf20Sopenharmony_ci case PIN_CONFIG_INPUT_ENABLE: 3908c2ecf20Sopenharmony_ci pin->input = true; 3918c2ecf20Sopenharmony_ci break; 3928c2ecf20Sopenharmony_ci case PIN_CONFIG_OUTPUT: 3938c2ecf20Sopenharmony_ci pin->output = true; 3948c2ecf20Sopenharmony_ci pin->output_value = !!arg; 3958c2ecf20Sopenharmony_ci break; 3968c2ecf20Sopenharmony_ci case PIN_CONFIG_POWER_SOURCE: 3978c2ecf20Sopenharmony_ci pin->power_source = arg; 3988c2ecf20Sopenharmony_ci break; 3998c2ecf20Sopenharmony_ci case PIN_CONFIG_DRIVE_STRENGTH: 4008c2ecf20Sopenharmony_ci pin->drive_strength = arg; 4018c2ecf20Sopenharmony_ci break; 4028c2ecf20Sopenharmony_ci case PM8XXX_CONFIG_DTEST_SELECTOR: 4038c2ecf20Sopenharmony_ci pin->dtest = arg; 4048c2ecf20Sopenharmony_ci break; 4058c2ecf20Sopenharmony_ci case PM8XXX_CONFIG_AMUX: 4068c2ecf20Sopenharmony_ci pin->amux = arg; 4078c2ecf20Sopenharmony_ci break; 4088c2ecf20Sopenharmony_ci case PM8XXX_CONFIG_ALEVEL: 4098c2ecf20Sopenharmony_ci pin->aout_level = arg; 4108c2ecf20Sopenharmony_ci break; 4118c2ecf20Sopenharmony_ci case PM8XXX_CONFIG_PAIRED: 4128c2ecf20Sopenharmony_ci pin->paired = !!arg; 4138c2ecf20Sopenharmony_ci break; 4148c2ecf20Sopenharmony_ci default: 4158c2ecf20Sopenharmony_ci dev_err(pctrl->dev, 4168c2ecf20Sopenharmony_ci "unsupported config parameter: %x\n", 4178c2ecf20Sopenharmony_ci param); 4188c2ecf20Sopenharmony_ci return -EINVAL; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci pm8xxx_mpp_update(pctrl, pin); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci return 0; 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic const struct pinconf_ops pm8xxx_pinconf_ops = { 4288c2ecf20Sopenharmony_ci .is_generic = true, 4298c2ecf20Sopenharmony_ci .pin_config_group_get = pm8xxx_pin_config_get, 4308c2ecf20Sopenharmony_ci .pin_config_group_set = pm8xxx_pin_config_set, 4318c2ecf20Sopenharmony_ci}; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic const struct pinctrl_desc pm8xxx_pinctrl_desc = { 4348c2ecf20Sopenharmony_ci .name = "pm8xxx_mpp", 4358c2ecf20Sopenharmony_ci .pctlops = &pm8xxx_pinctrl_ops, 4368c2ecf20Sopenharmony_ci .pmxops = &pm8xxx_pinmux_ops, 4378c2ecf20Sopenharmony_ci .confops = &pm8xxx_pinconf_ops, 4388c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4398c2ecf20Sopenharmony_ci}; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic int pm8xxx_mpp_direction_input(struct gpio_chip *chip, 4428c2ecf20Sopenharmony_ci unsigned offset) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct pm8xxx_mpp *pctrl = gpiochip_get_data(chip); 4458c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci switch (pin->mode) { 4488c2ecf20Sopenharmony_ci case PM8XXX_MPP_DIGITAL: 4498c2ecf20Sopenharmony_ci pin->input = true; 4508c2ecf20Sopenharmony_ci break; 4518c2ecf20Sopenharmony_ci case PM8XXX_MPP_ANALOG: 4528c2ecf20Sopenharmony_ci pin->input = true; 4538c2ecf20Sopenharmony_ci pin->output = true; 4548c2ecf20Sopenharmony_ci break; 4558c2ecf20Sopenharmony_ci case PM8XXX_MPP_SINK: 4568c2ecf20Sopenharmony_ci return -EINVAL; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci pm8xxx_mpp_update(pctrl, pin); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci return 0; 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic int pm8xxx_mpp_direction_output(struct gpio_chip *chip, 4658c2ecf20Sopenharmony_ci unsigned offset, 4668c2ecf20Sopenharmony_ci int value) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci struct pm8xxx_mpp *pctrl = gpiochip_get_data(chip); 4698c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci switch (pin->mode) { 4728c2ecf20Sopenharmony_ci case PM8XXX_MPP_DIGITAL: 4738c2ecf20Sopenharmony_ci pin->output = true; 4748c2ecf20Sopenharmony_ci break; 4758c2ecf20Sopenharmony_ci case PM8XXX_MPP_ANALOG: 4768c2ecf20Sopenharmony_ci pin->input = false; 4778c2ecf20Sopenharmony_ci pin->output = true; 4788c2ecf20Sopenharmony_ci break; 4798c2ecf20Sopenharmony_ci case PM8XXX_MPP_SINK: 4808c2ecf20Sopenharmony_ci pin->input = false; 4818c2ecf20Sopenharmony_ci pin->output = true; 4828c2ecf20Sopenharmony_ci break; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci pm8xxx_mpp_update(pctrl, pin); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci return 0; 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic int pm8xxx_mpp_get(struct gpio_chip *chip, unsigned offset) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci struct pm8xxx_mpp *pctrl = gpiochip_get_data(chip); 4938c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 4948c2ecf20Sopenharmony_ci bool state; 4958c2ecf20Sopenharmony_ci int ret; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (!pin->input) 4988c2ecf20Sopenharmony_ci return !!pin->output_value; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci ret = irq_get_irqchip_state(pin->irq, IRQCHIP_STATE_LINE_LEVEL, &state); 5018c2ecf20Sopenharmony_ci if (!ret) 5028c2ecf20Sopenharmony_ci ret = !!state; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci return ret; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic void pm8xxx_mpp_set(struct gpio_chip *chip, unsigned offset, int value) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct pm8xxx_mpp *pctrl = gpiochip_get_data(chip); 5108c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci pin->output_value = !!value; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci pm8xxx_mpp_update(pctrl, pin); 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic int pm8xxx_mpp_of_xlate(struct gpio_chip *chip, 5188c2ecf20Sopenharmony_ci const struct of_phandle_args *gpio_desc, 5198c2ecf20Sopenharmony_ci u32 *flags) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci if (chip->of_gpio_n_cells < 2) 5228c2ecf20Sopenharmony_ci return -EINVAL; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci if (flags) 5258c2ecf20Sopenharmony_ci *flags = gpio_desc->args[1]; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci return gpio_desc->args[0] - 1; 5288c2ecf20Sopenharmony_ci} 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic int pm8xxx_mpp_to_irq(struct gpio_chip *chip, unsigned offset) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci struct pm8xxx_mpp *pctrl = gpiochip_get_data(chip); 5348c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci return pin->irq; 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 5408c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic void pm8xxx_mpp_dbg_show_one(struct seq_file *s, 5438c2ecf20Sopenharmony_ci struct pinctrl_dev *pctldev, 5448c2ecf20Sopenharmony_ci struct gpio_chip *chip, 5458c2ecf20Sopenharmony_ci unsigned offset, 5468c2ecf20Sopenharmony_ci unsigned gpio) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci struct pm8xxx_mpp *pctrl = gpiochip_get_data(chip); 5498c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci static const char * const aout_lvls[] = { 5528c2ecf20Sopenharmony_ci "1v25", "1v25_2", "0v625", "0v3125", "mpp", "abus1", "abus2", 5538c2ecf20Sopenharmony_ci "abus3" 5548c2ecf20Sopenharmony_ci }; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci static const char * const amuxs[] = { 5578c2ecf20Sopenharmony_ci "amux5", "amux6", "amux7", "amux8", "amux9", "abus1", "abus2", 5588c2ecf20Sopenharmony_ci "abus3", 5598c2ecf20Sopenharmony_ci }; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci seq_printf(s, " mpp%-2d:", offset + 1); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci switch (pin->mode) { 5648c2ecf20Sopenharmony_ci case PM8XXX_MPP_DIGITAL: 5658c2ecf20Sopenharmony_ci seq_puts(s, " digital "); 5668c2ecf20Sopenharmony_ci if (pin->dtest) { 5678c2ecf20Sopenharmony_ci seq_printf(s, "dtest%d\n", pin->dtest); 5688c2ecf20Sopenharmony_ci } else if (pin->input && pin->output) { 5698c2ecf20Sopenharmony_ci if (pin->high_z) 5708c2ecf20Sopenharmony_ci seq_puts(s, "bi-dir high-z"); 5718c2ecf20Sopenharmony_ci else 5728c2ecf20Sopenharmony_ci seq_printf(s, "bi-dir %dOhm", pin->pullup); 5738c2ecf20Sopenharmony_ci } else if (pin->input) { 5748c2ecf20Sopenharmony_ci if (pin->dtest) 5758c2ecf20Sopenharmony_ci seq_printf(s, "in dtest%d", pin->dtest); 5768c2ecf20Sopenharmony_ci else 5778c2ecf20Sopenharmony_ci seq_puts(s, "in gpio"); 5788c2ecf20Sopenharmony_ci } else if (pin->output) { 5798c2ecf20Sopenharmony_ci seq_puts(s, "out "); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci if (!pin->paired) { 5828c2ecf20Sopenharmony_ci seq_puts(s, pin->output_value ? 5838c2ecf20Sopenharmony_ci "high" : "low"); 5848c2ecf20Sopenharmony_ci } else { 5858c2ecf20Sopenharmony_ci seq_puts(s, pin->output_value ? 5868c2ecf20Sopenharmony_ci "inverted" : "follow"); 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci break; 5908c2ecf20Sopenharmony_ci case PM8XXX_MPP_ANALOG: 5918c2ecf20Sopenharmony_ci seq_puts(s, " analog "); 5928c2ecf20Sopenharmony_ci if (pin->output) { 5938c2ecf20Sopenharmony_ci seq_printf(s, "out %s ", aout_lvls[pin->aout_level]); 5948c2ecf20Sopenharmony_ci if (!pin->paired) { 5958c2ecf20Sopenharmony_ci seq_puts(s, pin->output_value ? 5968c2ecf20Sopenharmony_ci "high" : "low"); 5978c2ecf20Sopenharmony_ci } else { 5988c2ecf20Sopenharmony_ci seq_puts(s, pin->output_value ? 5998c2ecf20Sopenharmony_ci "inverted" : "follow"); 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci } else { 6028c2ecf20Sopenharmony_ci seq_printf(s, "input mux %s", amuxs[pin->amux]); 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci break; 6058c2ecf20Sopenharmony_ci case PM8XXX_MPP_SINK: 6068c2ecf20Sopenharmony_ci seq_printf(s, " sink %dmA ", pin->drive_strength); 6078c2ecf20Sopenharmony_ci if (pin->dtest) { 6088c2ecf20Sopenharmony_ci seq_printf(s, "dtest%d", pin->dtest); 6098c2ecf20Sopenharmony_ci } else { 6108c2ecf20Sopenharmony_ci if (!pin->paired) { 6118c2ecf20Sopenharmony_ci seq_puts(s, pin->output_value ? 6128c2ecf20Sopenharmony_ci "high" : "low"); 6138c2ecf20Sopenharmony_ci } else { 6148c2ecf20Sopenharmony_ci seq_puts(s, pin->output_value ? 6158c2ecf20Sopenharmony_ci "inverted" : "follow"); 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci break; 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic void pm8xxx_mpp_dbg_show(struct seq_file *s, struct gpio_chip *chip) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci unsigned gpio = chip->base; 6258c2ecf20Sopenharmony_ci unsigned i; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci for (i = 0; i < chip->ngpio; i++, gpio++) { 6288c2ecf20Sopenharmony_ci pm8xxx_mpp_dbg_show_one(s, NULL, chip, i, gpio); 6298c2ecf20Sopenharmony_ci seq_puts(s, "\n"); 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci#else 6348c2ecf20Sopenharmony_ci#define pm8xxx_mpp_dbg_show NULL 6358c2ecf20Sopenharmony_ci#endif 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_cistatic const struct gpio_chip pm8xxx_mpp_template = { 6388c2ecf20Sopenharmony_ci .direction_input = pm8xxx_mpp_direction_input, 6398c2ecf20Sopenharmony_ci .direction_output = pm8xxx_mpp_direction_output, 6408c2ecf20Sopenharmony_ci .get = pm8xxx_mpp_get, 6418c2ecf20Sopenharmony_ci .set = pm8xxx_mpp_set, 6428c2ecf20Sopenharmony_ci .of_xlate = pm8xxx_mpp_of_xlate, 6438c2ecf20Sopenharmony_ci .to_irq = pm8xxx_mpp_to_irq, 6448c2ecf20Sopenharmony_ci .dbg_show = pm8xxx_mpp_dbg_show, 6458c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 6468c2ecf20Sopenharmony_ci}; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_cistatic int pm8xxx_pin_populate(struct pm8xxx_mpp *pctrl, 6498c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci unsigned int val; 6528c2ecf20Sopenharmony_ci unsigned level; 6538c2ecf20Sopenharmony_ci unsigned ctrl; 6548c2ecf20Sopenharmony_ci unsigned type; 6558c2ecf20Sopenharmony_ci int ret; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci ret = regmap_read(pctrl->regmap, pin->reg, &val); 6588c2ecf20Sopenharmony_ci if (ret) { 6598c2ecf20Sopenharmony_ci dev_err(pctrl->dev, "failed to read register\n"); 6608c2ecf20Sopenharmony_ci return ret; 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci type = (val >> 5) & 7; 6648c2ecf20Sopenharmony_ci level = (val >> 2) & 7; 6658c2ecf20Sopenharmony_ci ctrl = (val) & 3; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci switch (type) { 6688c2ecf20Sopenharmony_ci case PM8XXX_MPP_TYPE_D_INPUT: 6698c2ecf20Sopenharmony_ci pin->mode = PM8XXX_MPP_DIGITAL; 6708c2ecf20Sopenharmony_ci pin->input = true; 6718c2ecf20Sopenharmony_ci pin->power_source = level; 6728c2ecf20Sopenharmony_ci pin->dtest = ctrl; 6738c2ecf20Sopenharmony_ci break; 6748c2ecf20Sopenharmony_ci case PM8XXX_MPP_TYPE_D_OUTPUT: 6758c2ecf20Sopenharmony_ci pin->mode = PM8XXX_MPP_DIGITAL; 6768c2ecf20Sopenharmony_ci pin->output = true; 6778c2ecf20Sopenharmony_ci pin->power_source = level; 6788c2ecf20Sopenharmony_ci pin->output_value = !!(ctrl & BIT(0)); 6798c2ecf20Sopenharmony_ci pin->paired = !!(ctrl & BIT(1)); 6808c2ecf20Sopenharmony_ci break; 6818c2ecf20Sopenharmony_ci case PM8XXX_MPP_TYPE_D_BI_DIR: 6828c2ecf20Sopenharmony_ci pin->mode = PM8XXX_MPP_DIGITAL; 6838c2ecf20Sopenharmony_ci pin->input = true; 6848c2ecf20Sopenharmony_ci pin->output = true; 6858c2ecf20Sopenharmony_ci pin->power_source = level; 6868c2ecf20Sopenharmony_ci switch (ctrl) { 6878c2ecf20Sopenharmony_ci case PM8XXX_MPP_BI_PULLUP_1KOHM: 6888c2ecf20Sopenharmony_ci pin->pullup = 600; 6898c2ecf20Sopenharmony_ci break; 6908c2ecf20Sopenharmony_ci case PM8XXX_MPP_BI_PULLUP_OPEN: 6918c2ecf20Sopenharmony_ci pin->high_z = true; 6928c2ecf20Sopenharmony_ci break; 6938c2ecf20Sopenharmony_ci case PM8XXX_MPP_BI_PULLUP_10KOHM: 6948c2ecf20Sopenharmony_ci pin->pullup = 10000; 6958c2ecf20Sopenharmony_ci break; 6968c2ecf20Sopenharmony_ci case PM8XXX_MPP_BI_PULLUP_30KOHM: 6978c2ecf20Sopenharmony_ci pin->pullup = 30000; 6988c2ecf20Sopenharmony_ci break; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci break; 7018c2ecf20Sopenharmony_ci case PM8XXX_MPP_TYPE_A_INPUT: 7028c2ecf20Sopenharmony_ci pin->mode = PM8XXX_MPP_ANALOG; 7038c2ecf20Sopenharmony_ci pin->input = true; 7048c2ecf20Sopenharmony_ci pin->amux = level; 7058c2ecf20Sopenharmony_ci break; 7068c2ecf20Sopenharmony_ci case PM8XXX_MPP_TYPE_A_OUTPUT: 7078c2ecf20Sopenharmony_ci pin->mode = PM8XXX_MPP_ANALOG; 7088c2ecf20Sopenharmony_ci pin->output = true; 7098c2ecf20Sopenharmony_ci pin->aout_level = level; 7108c2ecf20Sopenharmony_ci pin->output_value = !!(ctrl & BIT(0)); 7118c2ecf20Sopenharmony_ci pin->paired = !!(ctrl & BIT(1)); 7128c2ecf20Sopenharmony_ci break; 7138c2ecf20Sopenharmony_ci case PM8XXX_MPP_TYPE_SINK: 7148c2ecf20Sopenharmony_ci pin->mode = PM8XXX_MPP_SINK; 7158c2ecf20Sopenharmony_ci pin->drive_strength = 5 * (level + 1); 7168c2ecf20Sopenharmony_ci pin->output_value = !!(ctrl & BIT(0)); 7178c2ecf20Sopenharmony_ci pin->paired = !!(ctrl & BIT(1)); 7188c2ecf20Sopenharmony_ci break; 7198c2ecf20Sopenharmony_ci case PM8XXX_MPP_TYPE_DTEST_SINK: 7208c2ecf20Sopenharmony_ci pin->mode = PM8XXX_MPP_SINK; 7218c2ecf20Sopenharmony_ci pin->dtest = ctrl + 1; 7228c2ecf20Sopenharmony_ci pin->drive_strength = 5 * (level + 1); 7238c2ecf20Sopenharmony_ci break; 7248c2ecf20Sopenharmony_ci case PM8XXX_MPP_TYPE_DTEST_OUTPUT: 7258c2ecf20Sopenharmony_ci pin->mode = PM8XXX_MPP_DIGITAL; 7268c2ecf20Sopenharmony_ci pin->power_source = level; 7278c2ecf20Sopenharmony_ci if (ctrl >= 1) 7288c2ecf20Sopenharmony_ci pin->dtest = ctrl; 7298c2ecf20Sopenharmony_ci break; 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci return 0; 7338c2ecf20Sopenharmony_ci} 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_cistatic const struct of_device_id pm8xxx_mpp_of_match[] = { 7368c2ecf20Sopenharmony_ci { .compatible = "qcom,pm8018-mpp" }, 7378c2ecf20Sopenharmony_ci { .compatible = "qcom,pm8038-mpp" }, 7388c2ecf20Sopenharmony_ci { .compatible = "qcom,pm8058-mpp" }, 7398c2ecf20Sopenharmony_ci { .compatible = "qcom,pm8917-mpp" }, 7408c2ecf20Sopenharmony_ci { .compatible = "qcom,pm8821-mpp" }, 7418c2ecf20Sopenharmony_ci { .compatible = "qcom,pm8921-mpp" }, 7428c2ecf20Sopenharmony_ci { .compatible = "qcom,ssbi-mpp" }, 7438c2ecf20Sopenharmony_ci { }, 7448c2ecf20Sopenharmony_ci}; 7458c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, pm8xxx_mpp_of_match); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_cistatic int pm8xxx_mpp_probe(struct platform_device *pdev) 7488c2ecf20Sopenharmony_ci{ 7498c2ecf20Sopenharmony_ci struct pm8xxx_pin_data *pin_data; 7508c2ecf20Sopenharmony_ci struct pinctrl_pin_desc *pins; 7518c2ecf20Sopenharmony_ci struct pm8xxx_mpp *pctrl; 7528c2ecf20Sopenharmony_ci int ret; 7538c2ecf20Sopenharmony_ci int i, npins; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); 7568c2ecf20Sopenharmony_ci if (!pctrl) 7578c2ecf20Sopenharmony_ci return -ENOMEM; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci pctrl->dev = &pdev->dev; 7608c2ecf20Sopenharmony_ci npins = platform_irq_count(pdev); 7618c2ecf20Sopenharmony_ci if (!npins) 7628c2ecf20Sopenharmony_ci return -EINVAL; 7638c2ecf20Sopenharmony_ci if (npins < 0) 7648c2ecf20Sopenharmony_ci return npins; 7658c2ecf20Sopenharmony_ci pctrl->npins = npins; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci pctrl->regmap = dev_get_regmap(pdev->dev.parent, NULL); 7688c2ecf20Sopenharmony_ci if (!pctrl->regmap) { 7698c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "parent regmap unavailable\n"); 7708c2ecf20Sopenharmony_ci return -ENXIO; 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci pctrl->desc = pm8xxx_pinctrl_desc; 7748c2ecf20Sopenharmony_ci pctrl->desc.npins = pctrl->npins; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci pins = devm_kcalloc(&pdev->dev, 7778c2ecf20Sopenharmony_ci pctrl->desc.npins, 7788c2ecf20Sopenharmony_ci sizeof(struct pinctrl_pin_desc), 7798c2ecf20Sopenharmony_ci GFP_KERNEL); 7808c2ecf20Sopenharmony_ci if (!pins) 7818c2ecf20Sopenharmony_ci return -ENOMEM; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci pin_data = devm_kcalloc(&pdev->dev, 7848c2ecf20Sopenharmony_ci pctrl->desc.npins, 7858c2ecf20Sopenharmony_ci sizeof(struct pm8xxx_pin_data), 7868c2ecf20Sopenharmony_ci GFP_KERNEL); 7878c2ecf20Sopenharmony_ci if (!pin_data) 7888c2ecf20Sopenharmony_ci return -ENOMEM; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci for (i = 0; i < pctrl->desc.npins; i++) { 7918c2ecf20Sopenharmony_ci pin_data[i].reg = SSBI_REG_ADDR_MPP(i); 7928c2ecf20Sopenharmony_ci pin_data[i].irq = platform_get_irq(pdev, i); 7938c2ecf20Sopenharmony_ci if (pin_data[i].irq < 0) 7948c2ecf20Sopenharmony_ci return pin_data[i].irq; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci ret = pm8xxx_pin_populate(pctrl, &pin_data[i]); 7978c2ecf20Sopenharmony_ci if (ret) 7988c2ecf20Sopenharmony_ci return ret; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci pins[i].number = i; 8018c2ecf20Sopenharmony_ci pins[i].name = pm8xxx_groups[i]; 8028c2ecf20Sopenharmony_ci pins[i].drv_data = &pin_data[i]; 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci pctrl->desc.pins = pins; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci pctrl->desc.num_custom_params = ARRAY_SIZE(pm8xxx_mpp_bindings); 8078c2ecf20Sopenharmony_ci pctrl->desc.custom_params = pm8xxx_mpp_bindings; 8088c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 8098c2ecf20Sopenharmony_ci pctrl->desc.custom_conf_items = pm8xxx_conf_items; 8108c2ecf20Sopenharmony_ci#endif 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci pctrl->pctrl = devm_pinctrl_register(&pdev->dev, &pctrl->desc, pctrl); 8138c2ecf20Sopenharmony_ci if (IS_ERR(pctrl->pctrl)) { 8148c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "couldn't register pm8xxx mpp driver\n"); 8158c2ecf20Sopenharmony_ci return PTR_ERR(pctrl->pctrl); 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci pctrl->chip = pm8xxx_mpp_template; 8198c2ecf20Sopenharmony_ci pctrl->chip.base = -1; 8208c2ecf20Sopenharmony_ci pctrl->chip.parent = &pdev->dev; 8218c2ecf20Sopenharmony_ci pctrl->chip.of_node = pdev->dev.of_node; 8228c2ecf20Sopenharmony_ci pctrl->chip.of_gpio_n_cells = 2; 8238c2ecf20Sopenharmony_ci pctrl->chip.label = dev_name(pctrl->dev); 8248c2ecf20Sopenharmony_ci pctrl->chip.ngpio = pctrl->npins; 8258c2ecf20Sopenharmony_ci ret = gpiochip_add_data(&pctrl->chip, pctrl); 8268c2ecf20Sopenharmony_ci if (ret) { 8278c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed register gpiochip\n"); 8288c2ecf20Sopenharmony_ci return ret; 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci ret = gpiochip_add_pin_range(&pctrl->chip, 8328c2ecf20Sopenharmony_ci dev_name(pctrl->dev), 8338c2ecf20Sopenharmony_ci 0, 0, pctrl->chip.ngpio); 8348c2ecf20Sopenharmony_ci if (ret) { 8358c2ecf20Sopenharmony_ci dev_err(pctrl->dev, "failed to add pin range\n"); 8368c2ecf20Sopenharmony_ci goto unregister_gpiochip; 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, pctrl); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Qualcomm pm8xxx mpp driver probed\n"); 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci return 0; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ciunregister_gpiochip: 8468c2ecf20Sopenharmony_ci gpiochip_remove(&pctrl->chip); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci return ret; 8498c2ecf20Sopenharmony_ci} 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_cistatic int pm8xxx_mpp_remove(struct platform_device *pdev) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci struct pm8xxx_mpp *pctrl = platform_get_drvdata(pdev); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci gpiochip_remove(&pctrl->chip); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci return 0; 8588c2ecf20Sopenharmony_ci} 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_cistatic struct platform_driver pm8xxx_mpp_driver = { 8618c2ecf20Sopenharmony_ci .driver = { 8628c2ecf20Sopenharmony_ci .name = "qcom-ssbi-mpp", 8638c2ecf20Sopenharmony_ci .of_match_table = pm8xxx_mpp_of_match, 8648c2ecf20Sopenharmony_ci }, 8658c2ecf20Sopenharmony_ci .probe = pm8xxx_mpp_probe, 8668c2ecf20Sopenharmony_ci .remove = pm8xxx_mpp_remove, 8678c2ecf20Sopenharmony_ci}; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_cimodule_platform_driver(pm8xxx_mpp_driver); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ciMODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>"); 8728c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm PM8xxx MPP driver"); 8738c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 874