18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/of.h> 98c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 108c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf-generic.h> 118c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf.h> 128c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinmux.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/regmap.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/types.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <dt-bindings/pinctrl/qcom,pmic-mpp.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "../core.h" 218c2ecf20Sopenharmony_ci#include "../pinctrl-utils.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define PMIC_MPP_ADDRESS_RANGE 0x100 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* 268c2ecf20Sopenharmony_ci * Pull Up Values - it indicates whether a pull-up should be 278c2ecf20Sopenharmony_ci * applied for bidirectional mode only. The hardware ignores the 288c2ecf20Sopenharmony_ci * configuration when operating in other modes. 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci#define PMIC_MPP_PULL_UP_0P6KOHM 0 318c2ecf20Sopenharmony_ci#define PMIC_MPP_PULL_UP_10KOHM 1 328c2ecf20Sopenharmony_ci#define PMIC_MPP_PULL_UP_30KOHM 2 338c2ecf20Sopenharmony_ci#define PMIC_MPP_PULL_UP_OPEN 3 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* type registers base address bases */ 368c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_TYPE 0x4 378c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_SUBTYPE 0x5 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* mpp peripheral type and subtype values */ 408c2ecf20Sopenharmony_ci#define PMIC_MPP_TYPE 0x11 418c2ecf20Sopenharmony_ci#define PMIC_MPP_SUBTYPE_4CH_NO_ANA_OUT 0x3 428c2ecf20Sopenharmony_ci#define PMIC_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT 0x4 438c2ecf20Sopenharmony_ci#define PMIC_MPP_SUBTYPE_4CH_NO_SINK 0x5 448c2ecf20Sopenharmony_ci#define PMIC_MPP_SUBTYPE_ULT_4CH_NO_SINK 0x6 458c2ecf20Sopenharmony_ci#define PMIC_MPP_SUBTYPE_4CH_FULL_FUNC 0x7 468c2ecf20Sopenharmony_ci#define PMIC_MPP_SUBTYPE_8CH_FULL_FUNC 0xf 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_RT_STS 0x10 498c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_RT_STS_VAL_MASK 0x1 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* control register base address bases */ 528c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_MODE_CTL 0x40 538c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_DIG_VIN_CTL 0x41 548c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_DIG_PULL_CTL 0x42 558c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_DIG_IN_CTL 0x43 568c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_EN_CTL 0x46 578c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_AOUT_CTL 0x48 588c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_AIN_CTL 0x4a 598c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_SINK_CTL 0x4c 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* PMIC_MPP_REG_MODE_CTL */ 628c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_MODE_VALUE_MASK 0x1 638c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_MODE_FUNCTION_SHIFT 1 648c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_MODE_FUNCTION_MASK 0x7 658c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_MODE_DIR_SHIFT 4 668c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_MODE_DIR_MASK 0x7 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* PMIC_MPP_REG_DIG_VIN_CTL */ 698c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_VIN_SHIFT 0 708c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_VIN_MASK 0x7 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* PMIC_MPP_REG_DIG_PULL_CTL */ 738c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_PULL_SHIFT 0 748c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_PULL_MASK 0x7 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* PMIC_MPP_REG_EN_CTL */ 778c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_MASTER_EN_SHIFT 7 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* PMIC_MPP_REG_AIN_CTL */ 808c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_AIN_ROUTE_SHIFT 0 818c2ecf20Sopenharmony_ci#define PMIC_MPP_REG_AIN_ROUTE_MASK 0x7 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci#define PMIC_MPP_MODE_DIGITAL_INPUT 0 848c2ecf20Sopenharmony_ci#define PMIC_MPP_MODE_DIGITAL_OUTPUT 1 858c2ecf20Sopenharmony_ci#define PMIC_MPP_MODE_DIGITAL_BIDIR 2 868c2ecf20Sopenharmony_ci#define PMIC_MPP_MODE_ANALOG_BIDIR 3 878c2ecf20Sopenharmony_ci#define PMIC_MPP_MODE_ANALOG_INPUT 4 888c2ecf20Sopenharmony_ci#define PMIC_MPP_MODE_ANALOG_OUTPUT 5 898c2ecf20Sopenharmony_ci#define PMIC_MPP_MODE_CURRENT_SINK 6 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#define PMIC_MPP_SELECTOR_NORMAL 0 928c2ecf20Sopenharmony_ci#define PMIC_MPP_SELECTOR_PAIRED 1 938c2ecf20Sopenharmony_ci#define PMIC_MPP_SELECTOR_DTEST_FIRST 4 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#define PMIC_MPP_PHYSICAL_OFFSET 1 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* Qualcomm specific pin configurations */ 988c2ecf20Sopenharmony_ci#define PMIC_MPP_CONF_AMUX_ROUTE (PIN_CONFIG_END + 1) 998c2ecf20Sopenharmony_ci#define PMIC_MPP_CONF_ANALOG_LEVEL (PIN_CONFIG_END + 2) 1008c2ecf20Sopenharmony_ci#define PMIC_MPP_CONF_DTEST_SELECTOR (PIN_CONFIG_END + 3) 1018c2ecf20Sopenharmony_ci#define PMIC_MPP_CONF_PAIRED (PIN_CONFIG_END + 4) 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/** 1048c2ecf20Sopenharmony_ci * struct pmic_mpp_pad - keep current MPP settings 1058c2ecf20Sopenharmony_ci * @base: Address base in SPMI device. 1068c2ecf20Sopenharmony_ci * @irq: IRQ number which this MPP generate. 1078c2ecf20Sopenharmony_ci * @is_enabled: Set to false when MPP should be put in high Z state. 1088c2ecf20Sopenharmony_ci * @out_value: Cached pin output value. 1098c2ecf20Sopenharmony_ci * @output_enabled: Set to true if MPP output logic is enabled. 1108c2ecf20Sopenharmony_ci * @input_enabled: Set to true if MPP input buffer logic is enabled. 1118c2ecf20Sopenharmony_ci * @paired: Pin operates in paired mode 1128c2ecf20Sopenharmony_ci * @has_pullup: Pin has support to configure pullup 1138c2ecf20Sopenharmony_ci * @num_sources: Number of power-sources supported by this MPP. 1148c2ecf20Sopenharmony_ci * @power_source: Current power-source used. 1158c2ecf20Sopenharmony_ci * @amux_input: Set the source for analog input. 1168c2ecf20Sopenharmony_ci * @aout_level: Analog output level 1178c2ecf20Sopenharmony_ci * @pullup: Pullup resistor value. Valid in Bidirectional mode only. 1188c2ecf20Sopenharmony_ci * @function: See pmic_mpp_functions[]. 1198c2ecf20Sopenharmony_ci * @drive_strength: Amount of current in sink mode 1208c2ecf20Sopenharmony_ci * @dtest: DTEST route selector 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_cistruct pmic_mpp_pad { 1238c2ecf20Sopenharmony_ci u16 base; 1248c2ecf20Sopenharmony_ci int irq; 1258c2ecf20Sopenharmony_ci bool is_enabled; 1268c2ecf20Sopenharmony_ci bool out_value; 1278c2ecf20Sopenharmony_ci bool output_enabled; 1288c2ecf20Sopenharmony_ci bool input_enabled; 1298c2ecf20Sopenharmony_ci bool paired; 1308c2ecf20Sopenharmony_ci bool has_pullup; 1318c2ecf20Sopenharmony_ci unsigned int num_sources; 1328c2ecf20Sopenharmony_ci unsigned int power_source; 1338c2ecf20Sopenharmony_ci unsigned int amux_input; 1348c2ecf20Sopenharmony_ci unsigned int aout_level; 1358c2ecf20Sopenharmony_ci unsigned int pullup; 1368c2ecf20Sopenharmony_ci unsigned int function; 1378c2ecf20Sopenharmony_ci unsigned int drive_strength; 1388c2ecf20Sopenharmony_ci unsigned int dtest; 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistruct pmic_mpp_state { 1428c2ecf20Sopenharmony_ci struct device *dev; 1438c2ecf20Sopenharmony_ci struct regmap *map; 1448c2ecf20Sopenharmony_ci struct pinctrl_dev *ctrl; 1458c2ecf20Sopenharmony_ci struct gpio_chip chip; 1468c2ecf20Sopenharmony_ci}; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic const struct pinconf_generic_params pmic_mpp_bindings[] = { 1498c2ecf20Sopenharmony_ci {"qcom,amux-route", PMIC_MPP_CONF_AMUX_ROUTE, 0}, 1508c2ecf20Sopenharmony_ci {"qcom,analog-level", PMIC_MPP_CONF_ANALOG_LEVEL, 0}, 1518c2ecf20Sopenharmony_ci {"qcom,dtest", PMIC_MPP_CONF_DTEST_SELECTOR, 0}, 1528c2ecf20Sopenharmony_ci {"qcom,paired", PMIC_MPP_CONF_PAIRED, 0}, 1538c2ecf20Sopenharmony_ci}; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 1568c2ecf20Sopenharmony_cistatic const struct pin_config_item pmic_conf_items[] = { 1578c2ecf20Sopenharmony_ci PCONFDUMP(PMIC_MPP_CONF_AMUX_ROUTE, "analog mux", NULL, true), 1588c2ecf20Sopenharmony_ci PCONFDUMP(PMIC_MPP_CONF_ANALOG_LEVEL, "analog level", NULL, true), 1598c2ecf20Sopenharmony_ci PCONFDUMP(PMIC_MPP_CONF_DTEST_SELECTOR, "dtest", NULL, true), 1608c2ecf20Sopenharmony_ci PCONFDUMP(PMIC_MPP_CONF_PAIRED, "paired", NULL, false), 1618c2ecf20Sopenharmony_ci}; 1628c2ecf20Sopenharmony_ci#endif 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic const char *const pmic_mpp_groups[] = { 1658c2ecf20Sopenharmony_ci "mpp1", "mpp2", "mpp3", "mpp4", "mpp5", "mpp6", "mpp7", "mpp8", 1668c2ecf20Sopenharmony_ci}; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci#define PMIC_MPP_DIGITAL 0 1698c2ecf20Sopenharmony_ci#define PMIC_MPP_ANALOG 1 1708c2ecf20Sopenharmony_ci#define PMIC_MPP_SINK 2 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic const char *const pmic_mpp_functions[] = { 1738c2ecf20Sopenharmony_ci "digital", "analog", "sink" 1748c2ecf20Sopenharmony_ci}; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic int pmic_mpp_read(struct pmic_mpp_state *state, 1778c2ecf20Sopenharmony_ci struct pmic_mpp_pad *pad, unsigned int addr) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci unsigned int val; 1808c2ecf20Sopenharmony_ci int ret; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci ret = regmap_read(state->map, pad->base + addr, &val); 1838c2ecf20Sopenharmony_ci if (ret < 0) 1848c2ecf20Sopenharmony_ci dev_err(state->dev, "read 0x%x failed\n", addr); 1858c2ecf20Sopenharmony_ci else 1868c2ecf20Sopenharmony_ci ret = val; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return ret; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic int pmic_mpp_write(struct pmic_mpp_state *state, 1928c2ecf20Sopenharmony_ci struct pmic_mpp_pad *pad, unsigned int addr, 1938c2ecf20Sopenharmony_ci unsigned int val) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci int ret; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci ret = regmap_write(state->map, pad->base + addr, val); 1988c2ecf20Sopenharmony_ci if (ret < 0) 1998c2ecf20Sopenharmony_ci dev_err(state->dev, "write 0x%x failed\n", addr); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return ret; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int pmic_mpp_get_groups_count(struct pinctrl_dev *pctldev) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci /* Every PIN is a group */ 2078c2ecf20Sopenharmony_ci return pctldev->desc->npins; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic const char *pmic_mpp_get_group_name(struct pinctrl_dev *pctldev, 2118c2ecf20Sopenharmony_ci unsigned pin) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci return pctldev->desc->pins[pin].name; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic int pmic_mpp_get_group_pins(struct pinctrl_dev *pctldev, 2178c2ecf20Sopenharmony_ci unsigned pin, 2188c2ecf20Sopenharmony_ci const unsigned **pins, unsigned *num_pins) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci *pins = &pctldev->desc->pins[pin].number; 2218c2ecf20Sopenharmony_ci *num_pins = 1; 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic const struct pinctrl_ops pmic_mpp_pinctrl_ops = { 2268c2ecf20Sopenharmony_ci .get_groups_count = pmic_mpp_get_groups_count, 2278c2ecf20Sopenharmony_ci .get_group_name = pmic_mpp_get_group_name, 2288c2ecf20Sopenharmony_ci .get_group_pins = pmic_mpp_get_group_pins, 2298c2ecf20Sopenharmony_ci .dt_node_to_map = pinconf_generic_dt_node_to_map_group, 2308c2ecf20Sopenharmony_ci .dt_free_map = pinctrl_utils_free_map, 2318c2ecf20Sopenharmony_ci}; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic int pmic_mpp_get_functions_count(struct pinctrl_dev *pctldev) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci return ARRAY_SIZE(pmic_mpp_functions); 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic const char *pmic_mpp_get_function_name(struct pinctrl_dev *pctldev, 2398c2ecf20Sopenharmony_ci unsigned function) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci return pmic_mpp_functions[function]; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic int pmic_mpp_get_function_groups(struct pinctrl_dev *pctldev, 2458c2ecf20Sopenharmony_ci unsigned function, 2468c2ecf20Sopenharmony_ci const char *const **groups, 2478c2ecf20Sopenharmony_ci unsigned *const num_qgroups) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci *groups = pmic_mpp_groups; 2508c2ecf20Sopenharmony_ci *num_qgroups = pctldev->desc->npins; 2518c2ecf20Sopenharmony_ci return 0; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic int pmic_mpp_write_mode_ctl(struct pmic_mpp_state *state, 2558c2ecf20Sopenharmony_ci struct pmic_mpp_pad *pad) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci unsigned int mode; 2588c2ecf20Sopenharmony_ci unsigned int sel; 2598c2ecf20Sopenharmony_ci unsigned int val; 2608c2ecf20Sopenharmony_ci unsigned int en; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci switch (pad->function) { 2638c2ecf20Sopenharmony_ci case PMIC_MPP_ANALOG: 2648c2ecf20Sopenharmony_ci if (pad->input_enabled && pad->output_enabled) 2658c2ecf20Sopenharmony_ci mode = PMIC_MPP_MODE_ANALOG_BIDIR; 2668c2ecf20Sopenharmony_ci else if (pad->input_enabled) 2678c2ecf20Sopenharmony_ci mode = PMIC_MPP_MODE_ANALOG_INPUT; 2688c2ecf20Sopenharmony_ci else 2698c2ecf20Sopenharmony_ci mode = PMIC_MPP_MODE_ANALOG_OUTPUT; 2708c2ecf20Sopenharmony_ci break; 2718c2ecf20Sopenharmony_ci case PMIC_MPP_DIGITAL: 2728c2ecf20Sopenharmony_ci if (pad->input_enabled && pad->output_enabled) 2738c2ecf20Sopenharmony_ci mode = PMIC_MPP_MODE_DIGITAL_BIDIR; 2748c2ecf20Sopenharmony_ci else if (pad->input_enabled) 2758c2ecf20Sopenharmony_ci mode = PMIC_MPP_MODE_DIGITAL_INPUT; 2768c2ecf20Sopenharmony_ci else 2778c2ecf20Sopenharmony_ci mode = PMIC_MPP_MODE_DIGITAL_OUTPUT; 2788c2ecf20Sopenharmony_ci break; 2798c2ecf20Sopenharmony_ci case PMIC_MPP_SINK: 2808c2ecf20Sopenharmony_ci default: 2818c2ecf20Sopenharmony_ci mode = PMIC_MPP_MODE_CURRENT_SINK; 2828c2ecf20Sopenharmony_ci break; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (pad->dtest) 2868c2ecf20Sopenharmony_ci sel = PMIC_MPP_SELECTOR_DTEST_FIRST + pad->dtest - 1; 2878c2ecf20Sopenharmony_ci else if (pad->paired) 2888c2ecf20Sopenharmony_ci sel = PMIC_MPP_SELECTOR_PAIRED; 2898c2ecf20Sopenharmony_ci else 2908c2ecf20Sopenharmony_ci sel = PMIC_MPP_SELECTOR_NORMAL; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci en = !!pad->out_value; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci val = mode << PMIC_MPP_REG_MODE_DIR_SHIFT | 2958c2ecf20Sopenharmony_ci sel << PMIC_MPP_REG_MODE_FUNCTION_SHIFT | 2968c2ecf20Sopenharmony_ci en; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci return pmic_mpp_write(state, pad, PMIC_MPP_REG_MODE_CTL, val); 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic int pmic_mpp_set_mux(struct pinctrl_dev *pctldev, unsigned function, 3028c2ecf20Sopenharmony_ci unsigned pin) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct pmic_mpp_state *state = pinctrl_dev_get_drvdata(pctldev); 3058c2ecf20Sopenharmony_ci struct pmic_mpp_pad *pad; 3068c2ecf20Sopenharmony_ci unsigned int val; 3078c2ecf20Sopenharmony_ci int ret; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci pad = pctldev->desc->pins[pin].drv_data; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci pad->function = function; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci ret = pmic_mpp_write_mode_ctl(state, pad); 3148c2ecf20Sopenharmony_ci if (ret < 0) 3158c2ecf20Sopenharmony_ci return ret; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci val = pad->is_enabled << PMIC_MPP_REG_MASTER_EN_SHIFT; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci return pmic_mpp_write(state, pad, PMIC_MPP_REG_EN_CTL, val); 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic const struct pinmux_ops pmic_mpp_pinmux_ops = { 3238c2ecf20Sopenharmony_ci .get_functions_count = pmic_mpp_get_functions_count, 3248c2ecf20Sopenharmony_ci .get_function_name = pmic_mpp_get_function_name, 3258c2ecf20Sopenharmony_ci .get_function_groups = pmic_mpp_get_function_groups, 3268c2ecf20Sopenharmony_ci .set_mux = pmic_mpp_set_mux, 3278c2ecf20Sopenharmony_ci}; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic int pmic_mpp_config_get(struct pinctrl_dev *pctldev, 3308c2ecf20Sopenharmony_ci unsigned int pin, unsigned long *config) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci unsigned param = pinconf_to_config_param(*config); 3338c2ecf20Sopenharmony_ci struct pmic_mpp_pad *pad; 3348c2ecf20Sopenharmony_ci unsigned arg = 0; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci pad = pctldev->desc->pins[pin].drv_data; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci switch (param) { 3398c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_DISABLE: 3408c2ecf20Sopenharmony_ci if (pad->pullup != PMIC_MPP_PULL_UP_OPEN) 3418c2ecf20Sopenharmony_ci return -EINVAL; 3428c2ecf20Sopenharmony_ci arg = 1; 3438c2ecf20Sopenharmony_ci break; 3448c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 3458c2ecf20Sopenharmony_ci switch (pad->pullup) { 3468c2ecf20Sopenharmony_ci case PMIC_MPP_PULL_UP_0P6KOHM: 3478c2ecf20Sopenharmony_ci arg = 600; 3488c2ecf20Sopenharmony_ci break; 3498c2ecf20Sopenharmony_ci case PMIC_MPP_PULL_UP_10KOHM: 3508c2ecf20Sopenharmony_ci arg = 10000; 3518c2ecf20Sopenharmony_ci break; 3528c2ecf20Sopenharmony_ci case PMIC_MPP_PULL_UP_30KOHM: 3538c2ecf20Sopenharmony_ci arg = 30000; 3548c2ecf20Sopenharmony_ci break; 3558c2ecf20Sopenharmony_ci default: 3568c2ecf20Sopenharmony_ci return -EINVAL; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci break; 3598c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: 3608c2ecf20Sopenharmony_ci if (pad->is_enabled) 3618c2ecf20Sopenharmony_ci return -EINVAL; 3628c2ecf20Sopenharmony_ci arg = 1; 3638c2ecf20Sopenharmony_ci break; 3648c2ecf20Sopenharmony_ci case PIN_CONFIG_POWER_SOURCE: 3658c2ecf20Sopenharmony_ci arg = pad->power_source; 3668c2ecf20Sopenharmony_ci break; 3678c2ecf20Sopenharmony_ci case PIN_CONFIG_INPUT_ENABLE: 3688c2ecf20Sopenharmony_ci if (!pad->input_enabled) 3698c2ecf20Sopenharmony_ci return -EINVAL; 3708c2ecf20Sopenharmony_ci arg = 1; 3718c2ecf20Sopenharmony_ci break; 3728c2ecf20Sopenharmony_ci case PIN_CONFIG_OUTPUT: 3738c2ecf20Sopenharmony_ci arg = pad->out_value; 3748c2ecf20Sopenharmony_ci break; 3758c2ecf20Sopenharmony_ci case PMIC_MPP_CONF_DTEST_SELECTOR: 3768c2ecf20Sopenharmony_ci arg = pad->dtest; 3778c2ecf20Sopenharmony_ci break; 3788c2ecf20Sopenharmony_ci case PMIC_MPP_CONF_AMUX_ROUTE: 3798c2ecf20Sopenharmony_ci arg = pad->amux_input; 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci case PMIC_MPP_CONF_PAIRED: 3828c2ecf20Sopenharmony_ci if (!pad->paired) 3838c2ecf20Sopenharmony_ci return -EINVAL; 3848c2ecf20Sopenharmony_ci arg = 1; 3858c2ecf20Sopenharmony_ci break; 3868c2ecf20Sopenharmony_ci case PIN_CONFIG_DRIVE_STRENGTH: 3878c2ecf20Sopenharmony_ci arg = pad->drive_strength; 3888c2ecf20Sopenharmony_ci break; 3898c2ecf20Sopenharmony_ci case PMIC_MPP_CONF_ANALOG_LEVEL: 3908c2ecf20Sopenharmony_ci arg = pad->aout_level; 3918c2ecf20Sopenharmony_ci break; 3928c2ecf20Sopenharmony_ci default: 3938c2ecf20Sopenharmony_ci return -EINVAL; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* Convert register value to pinconf value */ 3978c2ecf20Sopenharmony_ci *config = pinconf_to_config_packed(param, arg); 3988c2ecf20Sopenharmony_ci return 0; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic int pmic_mpp_config_set(struct pinctrl_dev *pctldev, unsigned int pin, 4028c2ecf20Sopenharmony_ci unsigned long *configs, unsigned nconfs) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct pmic_mpp_state *state = pinctrl_dev_get_drvdata(pctldev); 4058c2ecf20Sopenharmony_ci struct pmic_mpp_pad *pad; 4068c2ecf20Sopenharmony_ci unsigned param, arg; 4078c2ecf20Sopenharmony_ci unsigned int val; 4088c2ecf20Sopenharmony_ci int i, ret; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci pad = pctldev->desc->pins[pin].drv_data; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci /* Make it possible to enable the pin, by not setting high impedance */ 4138c2ecf20Sopenharmony_ci pad->is_enabled = true; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci for (i = 0; i < nconfs; i++) { 4168c2ecf20Sopenharmony_ci param = pinconf_to_config_param(configs[i]); 4178c2ecf20Sopenharmony_ci arg = pinconf_to_config_argument(configs[i]); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci switch (param) { 4208c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_DISABLE: 4218c2ecf20Sopenharmony_ci pad->pullup = PMIC_MPP_PULL_UP_OPEN; 4228c2ecf20Sopenharmony_ci break; 4238c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 4248c2ecf20Sopenharmony_ci switch (arg) { 4258c2ecf20Sopenharmony_ci case 600: 4268c2ecf20Sopenharmony_ci pad->pullup = PMIC_MPP_PULL_UP_0P6KOHM; 4278c2ecf20Sopenharmony_ci break; 4288c2ecf20Sopenharmony_ci case 10000: 4298c2ecf20Sopenharmony_ci pad->pullup = PMIC_MPP_PULL_UP_10KOHM; 4308c2ecf20Sopenharmony_ci break; 4318c2ecf20Sopenharmony_ci case 30000: 4328c2ecf20Sopenharmony_ci pad->pullup = PMIC_MPP_PULL_UP_30KOHM; 4338c2ecf20Sopenharmony_ci break; 4348c2ecf20Sopenharmony_ci default: 4358c2ecf20Sopenharmony_ci return -EINVAL; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci break; 4388c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: 4398c2ecf20Sopenharmony_ci pad->is_enabled = false; 4408c2ecf20Sopenharmony_ci break; 4418c2ecf20Sopenharmony_ci case PIN_CONFIG_POWER_SOURCE: 4428c2ecf20Sopenharmony_ci if (arg >= pad->num_sources) 4438c2ecf20Sopenharmony_ci return -EINVAL; 4448c2ecf20Sopenharmony_ci pad->power_source = arg; 4458c2ecf20Sopenharmony_ci break; 4468c2ecf20Sopenharmony_ci case PIN_CONFIG_INPUT_ENABLE: 4478c2ecf20Sopenharmony_ci pad->input_enabled = arg ? true : false; 4488c2ecf20Sopenharmony_ci break; 4498c2ecf20Sopenharmony_ci case PIN_CONFIG_OUTPUT: 4508c2ecf20Sopenharmony_ci pad->output_enabled = true; 4518c2ecf20Sopenharmony_ci pad->out_value = arg; 4528c2ecf20Sopenharmony_ci break; 4538c2ecf20Sopenharmony_ci case PMIC_MPP_CONF_DTEST_SELECTOR: 4548c2ecf20Sopenharmony_ci pad->dtest = arg; 4558c2ecf20Sopenharmony_ci break; 4568c2ecf20Sopenharmony_ci case PIN_CONFIG_DRIVE_STRENGTH: 4578c2ecf20Sopenharmony_ci pad->drive_strength = arg; 4588c2ecf20Sopenharmony_ci break; 4598c2ecf20Sopenharmony_ci case PMIC_MPP_CONF_AMUX_ROUTE: 4608c2ecf20Sopenharmony_ci if (arg >= PMIC_MPP_AMUX_ROUTE_ABUS4) 4618c2ecf20Sopenharmony_ci return -EINVAL; 4628c2ecf20Sopenharmony_ci pad->amux_input = arg; 4638c2ecf20Sopenharmony_ci break; 4648c2ecf20Sopenharmony_ci case PMIC_MPP_CONF_ANALOG_LEVEL: 4658c2ecf20Sopenharmony_ci pad->aout_level = arg; 4668c2ecf20Sopenharmony_ci break; 4678c2ecf20Sopenharmony_ci case PMIC_MPP_CONF_PAIRED: 4688c2ecf20Sopenharmony_ci pad->paired = !!arg; 4698c2ecf20Sopenharmony_ci break; 4708c2ecf20Sopenharmony_ci default: 4718c2ecf20Sopenharmony_ci return -EINVAL; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci val = pad->power_source << PMIC_MPP_REG_VIN_SHIFT; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_DIG_VIN_CTL, val); 4788c2ecf20Sopenharmony_ci if (ret < 0) 4798c2ecf20Sopenharmony_ci return ret; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (pad->has_pullup) { 4828c2ecf20Sopenharmony_ci val = pad->pullup << PMIC_MPP_REG_PULL_SHIFT; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_DIG_PULL_CTL, 4858c2ecf20Sopenharmony_ci val); 4868c2ecf20Sopenharmony_ci if (ret < 0) 4878c2ecf20Sopenharmony_ci return ret; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci val = pad->amux_input & PMIC_MPP_REG_AIN_ROUTE_MASK; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_AIN_CTL, val); 4938c2ecf20Sopenharmony_ci if (ret < 0) 4948c2ecf20Sopenharmony_ci return ret; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_AOUT_CTL, pad->aout_level); 4978c2ecf20Sopenharmony_ci if (ret < 0) 4988c2ecf20Sopenharmony_ci return ret; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci ret = pmic_mpp_write_mode_ctl(state, pad); 5018c2ecf20Sopenharmony_ci if (ret < 0) 5028c2ecf20Sopenharmony_ci return ret; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_SINK_CTL, pad->drive_strength); 5058c2ecf20Sopenharmony_ci if (ret < 0) 5068c2ecf20Sopenharmony_ci return ret; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci val = pad->is_enabled << PMIC_MPP_REG_MASTER_EN_SHIFT; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return pmic_mpp_write(state, pad, PMIC_MPP_REG_EN_CTL, val); 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic void pmic_mpp_config_dbg_show(struct pinctrl_dev *pctldev, 5148c2ecf20Sopenharmony_ci struct seq_file *s, unsigned pin) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci struct pmic_mpp_state *state = pinctrl_dev_get_drvdata(pctldev); 5178c2ecf20Sopenharmony_ci struct pmic_mpp_pad *pad; 5188c2ecf20Sopenharmony_ci int ret; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci static const char *const biases[] = { 5218c2ecf20Sopenharmony_ci "0.6kOhm", "10kOhm", "30kOhm", "Disabled" 5228c2ecf20Sopenharmony_ci }; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci pad = pctldev->desc->pins[pin].drv_data; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci seq_printf(s, " mpp%-2d:", pin + PMIC_MPP_PHYSICAL_OFFSET); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (!pad->is_enabled) { 5298c2ecf20Sopenharmony_ci seq_puts(s, " ---"); 5308c2ecf20Sopenharmony_ci } else { 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (pad->input_enabled) { 5338c2ecf20Sopenharmony_ci ret = pmic_mpp_read(state, pad, PMIC_MPP_REG_RT_STS); 5348c2ecf20Sopenharmony_ci if (ret < 0) 5358c2ecf20Sopenharmony_ci return; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci ret &= PMIC_MPP_REG_RT_STS_VAL_MASK; 5388c2ecf20Sopenharmony_ci pad->out_value = ret; 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci seq_printf(s, " %-4s", pad->output_enabled ? "out" : "in"); 5428c2ecf20Sopenharmony_ci seq_printf(s, " %-7s", pmic_mpp_functions[pad->function]); 5438c2ecf20Sopenharmony_ci seq_printf(s, " vin-%d", pad->power_source); 5448c2ecf20Sopenharmony_ci seq_printf(s, " %d", pad->aout_level); 5458c2ecf20Sopenharmony_ci if (pad->has_pullup) 5468c2ecf20Sopenharmony_ci seq_printf(s, " %-8s", biases[pad->pullup]); 5478c2ecf20Sopenharmony_ci seq_printf(s, " %-4s", pad->out_value ? "high" : "low"); 5488c2ecf20Sopenharmony_ci if (pad->dtest) 5498c2ecf20Sopenharmony_ci seq_printf(s, " dtest%d", pad->dtest); 5508c2ecf20Sopenharmony_ci if (pad->paired) 5518c2ecf20Sopenharmony_ci seq_puts(s, " paired"); 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic const struct pinconf_ops pmic_mpp_pinconf_ops = { 5568c2ecf20Sopenharmony_ci .is_generic = true, 5578c2ecf20Sopenharmony_ci .pin_config_group_get = pmic_mpp_config_get, 5588c2ecf20Sopenharmony_ci .pin_config_group_set = pmic_mpp_config_set, 5598c2ecf20Sopenharmony_ci .pin_config_group_dbg_show = pmic_mpp_config_dbg_show, 5608c2ecf20Sopenharmony_ci}; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_cistatic int pmic_mpp_direction_input(struct gpio_chip *chip, unsigned pin) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci struct pmic_mpp_state *state = gpiochip_get_data(chip); 5658c2ecf20Sopenharmony_ci unsigned long config; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci return pmic_mpp_config_set(state->ctrl, pin, &config, 1); 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_cistatic int pmic_mpp_direction_output(struct gpio_chip *chip, 5738c2ecf20Sopenharmony_ci unsigned pin, int val) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci struct pmic_mpp_state *state = gpiochip_get_data(chip); 5768c2ecf20Sopenharmony_ci unsigned long config; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci return pmic_mpp_config_set(state->ctrl, pin, &config, 1); 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic int pmic_mpp_get(struct gpio_chip *chip, unsigned pin) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci struct pmic_mpp_state *state = gpiochip_get_data(chip); 5868c2ecf20Sopenharmony_ci struct pmic_mpp_pad *pad; 5878c2ecf20Sopenharmony_ci int ret; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci pad = state->ctrl->desc->pins[pin].drv_data; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci if (pad->input_enabled) { 5928c2ecf20Sopenharmony_ci ret = pmic_mpp_read(state, pad, PMIC_MPP_REG_RT_STS); 5938c2ecf20Sopenharmony_ci if (ret < 0) 5948c2ecf20Sopenharmony_ci return ret; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci pad->out_value = ret & PMIC_MPP_REG_RT_STS_VAL_MASK; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci return !!pad->out_value; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic void pmic_mpp_set(struct gpio_chip *chip, unsigned pin, int value) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci struct pmic_mpp_state *state = gpiochip_get_data(chip); 6058c2ecf20Sopenharmony_ci unsigned long config; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci pmic_mpp_config_set(state->ctrl, pin, &config, 1); 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_cistatic int pmic_mpp_of_xlate(struct gpio_chip *chip, 6138c2ecf20Sopenharmony_ci const struct of_phandle_args *gpio_desc, 6148c2ecf20Sopenharmony_ci u32 *flags) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci if (chip->of_gpio_n_cells < 2) 6178c2ecf20Sopenharmony_ci return -EINVAL; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci if (flags) 6208c2ecf20Sopenharmony_ci *flags = gpio_desc->args[1]; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci return gpio_desc->args[0] - PMIC_MPP_PHYSICAL_OFFSET; 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic int pmic_mpp_to_irq(struct gpio_chip *chip, unsigned pin) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci struct pmic_mpp_state *state = gpiochip_get_data(chip); 6288c2ecf20Sopenharmony_ci struct pmic_mpp_pad *pad; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci pad = state->ctrl->desc->pins[pin].drv_data; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci return pad->irq; 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_cistatic void pmic_mpp_dbg_show(struct seq_file *s, struct gpio_chip *chip) 6368c2ecf20Sopenharmony_ci{ 6378c2ecf20Sopenharmony_ci struct pmic_mpp_state *state = gpiochip_get_data(chip); 6388c2ecf20Sopenharmony_ci unsigned i; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci for (i = 0; i < chip->ngpio; i++) { 6418c2ecf20Sopenharmony_ci pmic_mpp_config_dbg_show(state->ctrl, s, i); 6428c2ecf20Sopenharmony_ci seq_puts(s, "\n"); 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_cistatic const struct gpio_chip pmic_mpp_gpio_template = { 6478c2ecf20Sopenharmony_ci .direction_input = pmic_mpp_direction_input, 6488c2ecf20Sopenharmony_ci .direction_output = pmic_mpp_direction_output, 6498c2ecf20Sopenharmony_ci .get = pmic_mpp_get, 6508c2ecf20Sopenharmony_ci .set = pmic_mpp_set, 6518c2ecf20Sopenharmony_ci .request = gpiochip_generic_request, 6528c2ecf20Sopenharmony_ci .free = gpiochip_generic_free, 6538c2ecf20Sopenharmony_ci .of_xlate = pmic_mpp_of_xlate, 6548c2ecf20Sopenharmony_ci .to_irq = pmic_mpp_to_irq, 6558c2ecf20Sopenharmony_ci .dbg_show = pmic_mpp_dbg_show, 6568c2ecf20Sopenharmony_ci}; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistatic int pmic_mpp_populate(struct pmic_mpp_state *state, 6598c2ecf20Sopenharmony_ci struct pmic_mpp_pad *pad) 6608c2ecf20Sopenharmony_ci{ 6618c2ecf20Sopenharmony_ci int type, subtype, val, dir; 6628c2ecf20Sopenharmony_ci unsigned int sel; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci type = pmic_mpp_read(state, pad, PMIC_MPP_REG_TYPE); 6658c2ecf20Sopenharmony_ci if (type < 0) 6668c2ecf20Sopenharmony_ci return type; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci if (type != PMIC_MPP_TYPE) { 6698c2ecf20Sopenharmony_ci dev_err(state->dev, "incorrect block type 0x%x at 0x%x\n", 6708c2ecf20Sopenharmony_ci type, pad->base); 6718c2ecf20Sopenharmony_ci return -ENODEV; 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci subtype = pmic_mpp_read(state, pad, PMIC_MPP_REG_SUBTYPE); 6758c2ecf20Sopenharmony_ci if (subtype < 0) 6768c2ecf20Sopenharmony_ci return subtype; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci switch (subtype) { 6798c2ecf20Sopenharmony_ci case PMIC_MPP_SUBTYPE_4CH_NO_ANA_OUT: 6808c2ecf20Sopenharmony_ci case PMIC_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT: 6818c2ecf20Sopenharmony_ci case PMIC_MPP_SUBTYPE_4CH_NO_SINK: 6828c2ecf20Sopenharmony_ci case PMIC_MPP_SUBTYPE_ULT_4CH_NO_SINK: 6838c2ecf20Sopenharmony_ci case PMIC_MPP_SUBTYPE_4CH_FULL_FUNC: 6848c2ecf20Sopenharmony_ci pad->num_sources = 4; 6858c2ecf20Sopenharmony_ci break; 6868c2ecf20Sopenharmony_ci case PMIC_MPP_SUBTYPE_8CH_FULL_FUNC: 6878c2ecf20Sopenharmony_ci pad->num_sources = 8; 6888c2ecf20Sopenharmony_ci break; 6898c2ecf20Sopenharmony_ci default: 6908c2ecf20Sopenharmony_ci dev_err(state->dev, "unknown MPP type 0x%x at 0x%x\n", 6918c2ecf20Sopenharmony_ci subtype, pad->base); 6928c2ecf20Sopenharmony_ci return -ENODEV; 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci val = pmic_mpp_read(state, pad, PMIC_MPP_REG_MODE_CTL); 6968c2ecf20Sopenharmony_ci if (val < 0) 6978c2ecf20Sopenharmony_ci return val; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci pad->out_value = val & PMIC_MPP_REG_MODE_VALUE_MASK; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci dir = val >> PMIC_MPP_REG_MODE_DIR_SHIFT; 7028c2ecf20Sopenharmony_ci dir &= PMIC_MPP_REG_MODE_DIR_MASK; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci switch (dir) { 7058c2ecf20Sopenharmony_ci case PMIC_MPP_MODE_DIGITAL_INPUT: 7068c2ecf20Sopenharmony_ci pad->input_enabled = true; 7078c2ecf20Sopenharmony_ci pad->output_enabled = false; 7088c2ecf20Sopenharmony_ci pad->function = PMIC_MPP_DIGITAL; 7098c2ecf20Sopenharmony_ci break; 7108c2ecf20Sopenharmony_ci case PMIC_MPP_MODE_DIGITAL_OUTPUT: 7118c2ecf20Sopenharmony_ci pad->input_enabled = false; 7128c2ecf20Sopenharmony_ci pad->output_enabled = true; 7138c2ecf20Sopenharmony_ci pad->function = PMIC_MPP_DIGITAL; 7148c2ecf20Sopenharmony_ci break; 7158c2ecf20Sopenharmony_ci case PMIC_MPP_MODE_DIGITAL_BIDIR: 7168c2ecf20Sopenharmony_ci pad->input_enabled = true; 7178c2ecf20Sopenharmony_ci pad->output_enabled = true; 7188c2ecf20Sopenharmony_ci pad->function = PMIC_MPP_DIGITAL; 7198c2ecf20Sopenharmony_ci break; 7208c2ecf20Sopenharmony_ci case PMIC_MPP_MODE_ANALOG_BIDIR: 7218c2ecf20Sopenharmony_ci pad->input_enabled = true; 7228c2ecf20Sopenharmony_ci pad->output_enabled = true; 7238c2ecf20Sopenharmony_ci pad->function = PMIC_MPP_ANALOG; 7248c2ecf20Sopenharmony_ci break; 7258c2ecf20Sopenharmony_ci case PMIC_MPP_MODE_ANALOG_INPUT: 7268c2ecf20Sopenharmony_ci pad->input_enabled = true; 7278c2ecf20Sopenharmony_ci pad->output_enabled = false; 7288c2ecf20Sopenharmony_ci pad->function = PMIC_MPP_ANALOG; 7298c2ecf20Sopenharmony_ci break; 7308c2ecf20Sopenharmony_ci case PMIC_MPP_MODE_ANALOG_OUTPUT: 7318c2ecf20Sopenharmony_ci pad->input_enabled = false; 7328c2ecf20Sopenharmony_ci pad->output_enabled = true; 7338c2ecf20Sopenharmony_ci pad->function = PMIC_MPP_ANALOG; 7348c2ecf20Sopenharmony_ci break; 7358c2ecf20Sopenharmony_ci case PMIC_MPP_MODE_CURRENT_SINK: 7368c2ecf20Sopenharmony_ci pad->input_enabled = false; 7378c2ecf20Sopenharmony_ci pad->output_enabled = true; 7388c2ecf20Sopenharmony_ci pad->function = PMIC_MPP_SINK; 7398c2ecf20Sopenharmony_ci break; 7408c2ecf20Sopenharmony_ci default: 7418c2ecf20Sopenharmony_ci dev_err(state->dev, "unknown MPP direction\n"); 7428c2ecf20Sopenharmony_ci return -ENODEV; 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci sel = val >> PMIC_MPP_REG_MODE_FUNCTION_SHIFT; 7468c2ecf20Sopenharmony_ci sel &= PMIC_MPP_REG_MODE_FUNCTION_MASK; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci if (sel >= PMIC_MPP_SELECTOR_DTEST_FIRST) 7498c2ecf20Sopenharmony_ci pad->dtest = sel + 1; 7508c2ecf20Sopenharmony_ci else if (sel == PMIC_MPP_SELECTOR_PAIRED) 7518c2ecf20Sopenharmony_ci pad->paired = true; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci val = pmic_mpp_read(state, pad, PMIC_MPP_REG_DIG_VIN_CTL); 7548c2ecf20Sopenharmony_ci if (val < 0) 7558c2ecf20Sopenharmony_ci return val; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci pad->power_source = val >> PMIC_MPP_REG_VIN_SHIFT; 7588c2ecf20Sopenharmony_ci pad->power_source &= PMIC_MPP_REG_VIN_MASK; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci if (subtype != PMIC_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT && 7618c2ecf20Sopenharmony_ci subtype != PMIC_MPP_SUBTYPE_ULT_4CH_NO_SINK) { 7628c2ecf20Sopenharmony_ci val = pmic_mpp_read(state, pad, PMIC_MPP_REG_DIG_PULL_CTL); 7638c2ecf20Sopenharmony_ci if (val < 0) 7648c2ecf20Sopenharmony_ci return val; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci pad->pullup = val >> PMIC_MPP_REG_PULL_SHIFT; 7678c2ecf20Sopenharmony_ci pad->pullup &= PMIC_MPP_REG_PULL_MASK; 7688c2ecf20Sopenharmony_ci pad->has_pullup = true; 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci val = pmic_mpp_read(state, pad, PMIC_MPP_REG_AIN_CTL); 7728c2ecf20Sopenharmony_ci if (val < 0) 7738c2ecf20Sopenharmony_ci return val; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci pad->amux_input = val >> PMIC_MPP_REG_AIN_ROUTE_SHIFT; 7768c2ecf20Sopenharmony_ci pad->amux_input &= PMIC_MPP_REG_AIN_ROUTE_MASK; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci val = pmic_mpp_read(state, pad, PMIC_MPP_REG_SINK_CTL); 7798c2ecf20Sopenharmony_ci if (val < 0) 7808c2ecf20Sopenharmony_ci return val; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci pad->drive_strength = val; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci val = pmic_mpp_read(state, pad, PMIC_MPP_REG_AOUT_CTL); 7858c2ecf20Sopenharmony_ci if (val < 0) 7868c2ecf20Sopenharmony_ci return val; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci pad->aout_level = val; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci val = pmic_mpp_read(state, pad, PMIC_MPP_REG_EN_CTL); 7918c2ecf20Sopenharmony_ci if (val < 0) 7928c2ecf20Sopenharmony_ci return val; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci pad->is_enabled = !!val; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci return 0; 7978c2ecf20Sopenharmony_ci} 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_cistatic int pmic_mpp_probe(struct platform_device *pdev) 8008c2ecf20Sopenharmony_ci{ 8018c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 8028c2ecf20Sopenharmony_ci struct pinctrl_pin_desc *pindesc; 8038c2ecf20Sopenharmony_ci struct pinctrl_desc *pctrldesc; 8048c2ecf20Sopenharmony_ci struct pmic_mpp_pad *pad, *pads; 8058c2ecf20Sopenharmony_ci struct pmic_mpp_state *state; 8068c2ecf20Sopenharmony_ci int ret, npins, i; 8078c2ecf20Sopenharmony_ci u32 reg; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci ret = of_property_read_u32(dev->of_node, "reg", ®); 8108c2ecf20Sopenharmony_ci if (ret < 0) { 8118c2ecf20Sopenharmony_ci dev_err(dev, "missing base address"); 8128c2ecf20Sopenharmony_ci return ret; 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci npins = platform_irq_count(pdev); 8168c2ecf20Sopenharmony_ci if (!npins) 8178c2ecf20Sopenharmony_ci return -EINVAL; 8188c2ecf20Sopenharmony_ci if (npins < 0) 8198c2ecf20Sopenharmony_ci return npins; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci BUG_ON(npins > ARRAY_SIZE(pmic_mpp_groups)); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); 8248c2ecf20Sopenharmony_ci if (!state) 8258c2ecf20Sopenharmony_ci return -ENOMEM; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, state); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci state->dev = &pdev->dev; 8308c2ecf20Sopenharmony_ci state->map = dev_get_regmap(dev->parent, NULL); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL); 8338c2ecf20Sopenharmony_ci if (!pindesc) 8348c2ecf20Sopenharmony_ci return -ENOMEM; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL); 8378c2ecf20Sopenharmony_ci if (!pads) 8388c2ecf20Sopenharmony_ci return -ENOMEM; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL); 8418c2ecf20Sopenharmony_ci if (!pctrldesc) 8428c2ecf20Sopenharmony_ci return -ENOMEM; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci pctrldesc->pctlops = &pmic_mpp_pinctrl_ops; 8458c2ecf20Sopenharmony_ci pctrldesc->pmxops = &pmic_mpp_pinmux_ops; 8468c2ecf20Sopenharmony_ci pctrldesc->confops = &pmic_mpp_pinconf_ops; 8478c2ecf20Sopenharmony_ci pctrldesc->owner = THIS_MODULE; 8488c2ecf20Sopenharmony_ci pctrldesc->name = dev_name(dev); 8498c2ecf20Sopenharmony_ci pctrldesc->pins = pindesc; 8508c2ecf20Sopenharmony_ci pctrldesc->npins = npins; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci pctrldesc->num_custom_params = ARRAY_SIZE(pmic_mpp_bindings); 8538c2ecf20Sopenharmony_ci pctrldesc->custom_params = pmic_mpp_bindings; 8548c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 8558c2ecf20Sopenharmony_ci pctrldesc->custom_conf_items = pmic_conf_items; 8568c2ecf20Sopenharmony_ci#endif 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci for (i = 0; i < npins; i++, pindesc++) { 8598c2ecf20Sopenharmony_ci pad = &pads[i]; 8608c2ecf20Sopenharmony_ci pindesc->drv_data = pad; 8618c2ecf20Sopenharmony_ci pindesc->number = i; 8628c2ecf20Sopenharmony_ci pindesc->name = pmic_mpp_groups[i]; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci pad->irq = platform_get_irq(pdev, i); 8658c2ecf20Sopenharmony_ci if (pad->irq < 0) 8668c2ecf20Sopenharmony_ci return pad->irq; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci pad->base = reg + i * PMIC_MPP_ADDRESS_RANGE; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci ret = pmic_mpp_populate(state, pad); 8718c2ecf20Sopenharmony_ci if (ret < 0) 8728c2ecf20Sopenharmony_ci return ret; 8738c2ecf20Sopenharmony_ci } 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci state->chip = pmic_mpp_gpio_template; 8768c2ecf20Sopenharmony_ci state->chip.parent = dev; 8778c2ecf20Sopenharmony_ci state->chip.base = -1; 8788c2ecf20Sopenharmony_ci state->chip.ngpio = npins; 8798c2ecf20Sopenharmony_ci state->chip.label = dev_name(dev); 8808c2ecf20Sopenharmony_ci state->chip.of_gpio_n_cells = 2; 8818c2ecf20Sopenharmony_ci state->chip.can_sleep = false; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci state->ctrl = devm_pinctrl_register(dev, pctrldesc, state); 8848c2ecf20Sopenharmony_ci if (IS_ERR(state->ctrl)) 8858c2ecf20Sopenharmony_ci return PTR_ERR(state->ctrl); 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci ret = gpiochip_add_data(&state->chip, state); 8888c2ecf20Sopenharmony_ci if (ret) { 8898c2ecf20Sopenharmony_ci dev_err(state->dev, "can't add gpio chip\n"); 8908c2ecf20Sopenharmony_ci return ret; 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, npins); 8948c2ecf20Sopenharmony_ci if (ret) { 8958c2ecf20Sopenharmony_ci dev_err(dev, "failed to add pin range\n"); 8968c2ecf20Sopenharmony_ci goto err_range; 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci return 0; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_cierr_range: 9028c2ecf20Sopenharmony_ci gpiochip_remove(&state->chip); 9038c2ecf20Sopenharmony_ci return ret; 9048c2ecf20Sopenharmony_ci} 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_cistatic int pmic_mpp_remove(struct platform_device *pdev) 9078c2ecf20Sopenharmony_ci{ 9088c2ecf20Sopenharmony_ci struct pmic_mpp_state *state = platform_get_drvdata(pdev); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci gpiochip_remove(&state->chip); 9118c2ecf20Sopenharmony_ci return 0; 9128c2ecf20Sopenharmony_ci} 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_cistatic const struct of_device_id pmic_mpp_of_match[] = { 9158c2ecf20Sopenharmony_ci { .compatible = "qcom,pm8841-mpp" }, /* 4 MPP's */ 9168c2ecf20Sopenharmony_ci { .compatible = "qcom,pm8916-mpp" }, /* 4 MPP's */ 9178c2ecf20Sopenharmony_ci { .compatible = "qcom,pm8941-mpp" }, /* 8 MPP's */ 9188c2ecf20Sopenharmony_ci { .compatible = "qcom,pm8950-mpp" }, /* 4 MPP's */ 9198c2ecf20Sopenharmony_ci { .compatible = "qcom,pmi8950-mpp" }, /* 4 MPP's */ 9208c2ecf20Sopenharmony_ci { .compatible = "qcom,pm8994-mpp" }, /* 8 MPP's */ 9218c2ecf20Sopenharmony_ci { .compatible = "qcom,pma8084-mpp" }, /* 8 MPP's */ 9228c2ecf20Sopenharmony_ci { .compatible = "qcom,spmi-mpp" }, /* Generic */ 9238c2ecf20Sopenharmony_ci { }, 9248c2ecf20Sopenharmony_ci}; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, pmic_mpp_of_match); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_cistatic struct platform_driver pmic_mpp_driver = { 9298c2ecf20Sopenharmony_ci .driver = { 9308c2ecf20Sopenharmony_ci .name = "qcom-spmi-mpp", 9318c2ecf20Sopenharmony_ci .of_match_table = pmic_mpp_of_match, 9328c2ecf20Sopenharmony_ci }, 9338c2ecf20Sopenharmony_ci .probe = pmic_mpp_probe, 9348c2ecf20Sopenharmony_ci .remove = pmic_mpp_remove, 9358c2ecf20Sopenharmony_ci}; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_cimodule_platform_driver(pmic_mpp_driver); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>"); 9408c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm SPMI PMIC MPP pin control driver"); 9418c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:qcom-spmi-mpp"); 9428c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 943