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