162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/gpio/driver.h>
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <linux/of.h>
962306a36Sopenharmony_ci#include <linux/of_irq.h>
1062306a36Sopenharmony_ci#include <linux/platform_device.h>
1162306a36Sopenharmony_ci#include <linux/regmap.h>
1262306a36Sopenharmony_ci#include <linux/seq_file.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/types.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/pinctrl/pinconf-generic.h>
1762306a36Sopenharmony_ci#include <linux/pinctrl/pinconf.h>
1862306a36Sopenharmony_ci#include <linux/pinctrl/pinmux.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <dt-bindings/pinctrl/qcom,pmic-mpp.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "../core.h"
2362306a36Sopenharmony_ci#include "../pinctrl-utils.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define PMIC_MPP_ADDRESS_RANGE			0x100
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/*
2862306a36Sopenharmony_ci * Pull Up Values - it indicates whether a pull-up should be
2962306a36Sopenharmony_ci * applied for bidirectional mode only. The hardware ignores the
3062306a36Sopenharmony_ci * configuration when operating in other modes.
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ci#define PMIC_MPP_PULL_UP_0P6KOHM		0
3362306a36Sopenharmony_ci#define PMIC_MPP_PULL_UP_10KOHM			1
3462306a36Sopenharmony_ci#define PMIC_MPP_PULL_UP_30KOHM			2
3562306a36Sopenharmony_ci#define PMIC_MPP_PULL_UP_OPEN			3
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/* type registers base address bases */
3862306a36Sopenharmony_ci#define PMIC_MPP_REG_TYPE			0x4
3962306a36Sopenharmony_ci#define PMIC_MPP_REG_SUBTYPE			0x5
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/* mpp peripheral type and subtype values */
4262306a36Sopenharmony_ci#define PMIC_MPP_TYPE				0x11
4362306a36Sopenharmony_ci#define PMIC_MPP_SUBTYPE_4CH_NO_ANA_OUT		0x3
4462306a36Sopenharmony_ci#define PMIC_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT	0x4
4562306a36Sopenharmony_ci#define PMIC_MPP_SUBTYPE_4CH_NO_SINK		0x5
4662306a36Sopenharmony_ci#define PMIC_MPP_SUBTYPE_ULT_4CH_NO_SINK	0x6
4762306a36Sopenharmony_ci#define PMIC_MPP_SUBTYPE_4CH_FULL_FUNC		0x7
4862306a36Sopenharmony_ci#define PMIC_MPP_SUBTYPE_8CH_FULL_FUNC		0xf
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#define PMIC_MPP_REG_RT_STS			0x10
5162306a36Sopenharmony_ci#define PMIC_MPP_REG_RT_STS_VAL_MASK		0x1
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/* control register base address bases */
5462306a36Sopenharmony_ci#define PMIC_MPP_REG_MODE_CTL			0x40
5562306a36Sopenharmony_ci#define PMIC_MPP_REG_DIG_VIN_CTL		0x41
5662306a36Sopenharmony_ci#define PMIC_MPP_REG_DIG_PULL_CTL		0x42
5762306a36Sopenharmony_ci#define PMIC_MPP_REG_DIG_IN_CTL			0x43
5862306a36Sopenharmony_ci#define PMIC_MPP_REG_EN_CTL			0x46
5962306a36Sopenharmony_ci#define PMIC_MPP_REG_AOUT_CTL			0x48
6062306a36Sopenharmony_ci#define PMIC_MPP_REG_AIN_CTL			0x4a
6162306a36Sopenharmony_ci#define PMIC_MPP_REG_SINK_CTL			0x4c
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/* PMIC_MPP_REG_MODE_CTL */
6462306a36Sopenharmony_ci#define PMIC_MPP_REG_MODE_VALUE_MASK		0x1
6562306a36Sopenharmony_ci#define PMIC_MPP_REG_MODE_FUNCTION_SHIFT	1
6662306a36Sopenharmony_ci#define PMIC_MPP_REG_MODE_FUNCTION_MASK		0x7
6762306a36Sopenharmony_ci#define PMIC_MPP_REG_MODE_DIR_SHIFT		4
6862306a36Sopenharmony_ci#define PMIC_MPP_REG_MODE_DIR_MASK		0x7
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/* PMIC_MPP_REG_DIG_VIN_CTL */
7162306a36Sopenharmony_ci#define PMIC_MPP_REG_VIN_SHIFT			0
7262306a36Sopenharmony_ci#define PMIC_MPP_REG_VIN_MASK			0x7
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/* PMIC_MPP_REG_DIG_PULL_CTL */
7562306a36Sopenharmony_ci#define PMIC_MPP_REG_PULL_SHIFT			0
7662306a36Sopenharmony_ci#define PMIC_MPP_REG_PULL_MASK			0x7
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/* PMIC_MPP_REG_EN_CTL */
7962306a36Sopenharmony_ci#define PMIC_MPP_REG_MASTER_EN_SHIFT		7
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci/* PMIC_MPP_REG_AIN_CTL */
8262306a36Sopenharmony_ci#define PMIC_MPP_REG_AIN_ROUTE_SHIFT		0
8362306a36Sopenharmony_ci#define PMIC_MPP_REG_AIN_ROUTE_MASK		0x7
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci#define PMIC_MPP_MODE_DIGITAL_INPUT		0
8662306a36Sopenharmony_ci#define PMIC_MPP_MODE_DIGITAL_OUTPUT		1
8762306a36Sopenharmony_ci#define PMIC_MPP_MODE_DIGITAL_BIDIR		2
8862306a36Sopenharmony_ci#define PMIC_MPP_MODE_ANALOG_BIDIR		3
8962306a36Sopenharmony_ci#define PMIC_MPP_MODE_ANALOG_INPUT		4
9062306a36Sopenharmony_ci#define PMIC_MPP_MODE_ANALOG_OUTPUT		5
9162306a36Sopenharmony_ci#define PMIC_MPP_MODE_CURRENT_SINK		6
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define PMIC_MPP_SELECTOR_NORMAL		0
9462306a36Sopenharmony_ci#define PMIC_MPP_SELECTOR_PAIRED		1
9562306a36Sopenharmony_ci#define PMIC_MPP_SELECTOR_DTEST_FIRST		4
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci#define PMIC_MPP_PHYSICAL_OFFSET		1
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/* Qualcomm specific pin configurations */
10062306a36Sopenharmony_ci#define PMIC_MPP_CONF_AMUX_ROUTE		(PIN_CONFIG_END + 1)
10162306a36Sopenharmony_ci#define PMIC_MPP_CONF_ANALOG_LEVEL		(PIN_CONFIG_END + 2)
10262306a36Sopenharmony_ci#define PMIC_MPP_CONF_DTEST_SELECTOR		(PIN_CONFIG_END + 3)
10362306a36Sopenharmony_ci#define PMIC_MPP_CONF_PAIRED			(PIN_CONFIG_END + 4)
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/**
10662306a36Sopenharmony_ci * struct pmic_mpp_pad - keep current MPP settings
10762306a36Sopenharmony_ci * @base: Address base in SPMI device.
10862306a36Sopenharmony_ci * @is_enabled: Set to false when MPP should be put in high Z state.
10962306a36Sopenharmony_ci * @out_value: Cached pin output value.
11062306a36Sopenharmony_ci * @output_enabled: Set to true if MPP output logic is enabled.
11162306a36Sopenharmony_ci * @input_enabled: Set to true if MPP input buffer logic is enabled.
11262306a36Sopenharmony_ci * @paired: Pin operates in paired mode
11362306a36Sopenharmony_ci * @has_pullup: Pin has support to configure pullup
11462306a36Sopenharmony_ci * @num_sources: Number of power-sources supported by this MPP.
11562306a36Sopenharmony_ci * @power_source: Current power-source used.
11662306a36Sopenharmony_ci * @amux_input: Set the source for analog input.
11762306a36Sopenharmony_ci * @aout_level: Analog output level
11862306a36Sopenharmony_ci * @pullup: Pullup resistor value. Valid in Bidirectional mode only.
11962306a36Sopenharmony_ci * @function: See pmic_mpp_functions[].
12062306a36Sopenharmony_ci * @drive_strength: Amount of current in sink mode
12162306a36Sopenharmony_ci * @dtest: DTEST route selector
12262306a36Sopenharmony_ci */
12362306a36Sopenharmony_cistruct pmic_mpp_pad {
12462306a36Sopenharmony_ci	u16		base;
12562306a36Sopenharmony_ci	bool		is_enabled;
12662306a36Sopenharmony_ci	bool		out_value;
12762306a36Sopenharmony_ci	bool		output_enabled;
12862306a36Sopenharmony_ci	bool		input_enabled;
12962306a36Sopenharmony_ci	bool		paired;
13062306a36Sopenharmony_ci	bool		has_pullup;
13162306a36Sopenharmony_ci	unsigned int	num_sources;
13262306a36Sopenharmony_ci	unsigned int	power_source;
13362306a36Sopenharmony_ci	unsigned int	amux_input;
13462306a36Sopenharmony_ci	unsigned int	aout_level;
13562306a36Sopenharmony_ci	unsigned int	pullup;
13662306a36Sopenharmony_ci	unsigned int	function;
13762306a36Sopenharmony_ci	unsigned int	drive_strength;
13862306a36Sopenharmony_ci	unsigned int	dtest;
13962306a36Sopenharmony_ci};
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistruct pmic_mpp_state {
14262306a36Sopenharmony_ci	struct device	*dev;
14362306a36Sopenharmony_ci	struct regmap	*map;
14462306a36Sopenharmony_ci	struct pinctrl_dev *ctrl;
14562306a36Sopenharmony_ci	struct gpio_chip chip;
14662306a36Sopenharmony_ci};
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic const struct pinconf_generic_params pmic_mpp_bindings[] = {
14962306a36Sopenharmony_ci	{"qcom,amux-route",	PMIC_MPP_CONF_AMUX_ROUTE,	0},
15062306a36Sopenharmony_ci	{"qcom,analog-level",	PMIC_MPP_CONF_ANALOG_LEVEL,	0},
15162306a36Sopenharmony_ci	{"qcom,dtest",		PMIC_MPP_CONF_DTEST_SELECTOR,	0},
15262306a36Sopenharmony_ci	{"qcom,paired",		PMIC_MPP_CONF_PAIRED,		0},
15362306a36Sopenharmony_ci};
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
15662306a36Sopenharmony_cistatic const struct pin_config_item pmic_conf_items[] = {
15762306a36Sopenharmony_ci	PCONFDUMP(PMIC_MPP_CONF_AMUX_ROUTE, "analog mux", NULL, true),
15862306a36Sopenharmony_ci	PCONFDUMP(PMIC_MPP_CONF_ANALOG_LEVEL, "analog level", NULL, true),
15962306a36Sopenharmony_ci	PCONFDUMP(PMIC_MPP_CONF_DTEST_SELECTOR, "dtest", NULL, true),
16062306a36Sopenharmony_ci	PCONFDUMP(PMIC_MPP_CONF_PAIRED, "paired", NULL, false),
16162306a36Sopenharmony_ci};
16262306a36Sopenharmony_ci#endif
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic const char *const pmic_mpp_groups[] = {
16562306a36Sopenharmony_ci	"mpp1", "mpp2", "mpp3", "mpp4", "mpp5", "mpp6", "mpp7", "mpp8",
16662306a36Sopenharmony_ci};
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci#define PMIC_MPP_DIGITAL	0
16962306a36Sopenharmony_ci#define PMIC_MPP_ANALOG		1
17062306a36Sopenharmony_ci#define PMIC_MPP_SINK		2
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic const char *const pmic_mpp_functions[] = {
17362306a36Sopenharmony_ci	"digital", "analog", "sink"
17462306a36Sopenharmony_ci};
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic int pmic_mpp_read(struct pmic_mpp_state *state,
17762306a36Sopenharmony_ci			 struct pmic_mpp_pad *pad, unsigned int addr)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	unsigned int val;
18062306a36Sopenharmony_ci	int ret;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	ret = regmap_read(state->map, pad->base + addr, &val);
18362306a36Sopenharmony_ci	if (ret < 0)
18462306a36Sopenharmony_ci		dev_err(state->dev, "read 0x%x failed\n", addr);
18562306a36Sopenharmony_ci	else
18662306a36Sopenharmony_ci		ret = val;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	return ret;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic int pmic_mpp_write(struct pmic_mpp_state *state,
19262306a36Sopenharmony_ci			  struct pmic_mpp_pad *pad, unsigned int addr,
19362306a36Sopenharmony_ci			  unsigned int val)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	int ret;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	ret = regmap_write(state->map, pad->base + addr, val);
19862306a36Sopenharmony_ci	if (ret < 0)
19962306a36Sopenharmony_ci		dev_err(state->dev, "write 0x%x failed\n", addr);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	return ret;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic int pmic_mpp_get_groups_count(struct pinctrl_dev *pctldev)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	/* Every PIN is a group */
20762306a36Sopenharmony_ci	return pctldev->desc->npins;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic const char *pmic_mpp_get_group_name(struct pinctrl_dev *pctldev,
21162306a36Sopenharmony_ci					   unsigned pin)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	return pctldev->desc->pins[pin].name;
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic int pmic_mpp_get_group_pins(struct pinctrl_dev *pctldev,
21762306a36Sopenharmony_ci				   unsigned pin,
21862306a36Sopenharmony_ci				   const unsigned **pins, unsigned *num_pins)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	*pins = &pctldev->desc->pins[pin].number;
22162306a36Sopenharmony_ci	*num_pins = 1;
22262306a36Sopenharmony_ci	return 0;
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic const struct pinctrl_ops pmic_mpp_pinctrl_ops = {
22662306a36Sopenharmony_ci	.get_groups_count	= pmic_mpp_get_groups_count,
22762306a36Sopenharmony_ci	.get_group_name		= pmic_mpp_get_group_name,
22862306a36Sopenharmony_ci	.get_group_pins		= pmic_mpp_get_group_pins,
22962306a36Sopenharmony_ci	.dt_node_to_map		= pinconf_generic_dt_node_to_map_group,
23062306a36Sopenharmony_ci	.dt_free_map		= pinctrl_utils_free_map,
23162306a36Sopenharmony_ci};
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic int pmic_mpp_get_functions_count(struct pinctrl_dev *pctldev)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	return ARRAY_SIZE(pmic_mpp_functions);
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic const char *pmic_mpp_get_function_name(struct pinctrl_dev *pctldev,
23962306a36Sopenharmony_ci					      unsigned function)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	return pmic_mpp_functions[function];
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic int pmic_mpp_get_function_groups(struct pinctrl_dev *pctldev,
24562306a36Sopenharmony_ci					unsigned function,
24662306a36Sopenharmony_ci					const char *const **groups,
24762306a36Sopenharmony_ci					unsigned *const num_qgroups)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	*groups = pmic_mpp_groups;
25062306a36Sopenharmony_ci	*num_qgroups = pctldev->desc->npins;
25162306a36Sopenharmony_ci	return 0;
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic int pmic_mpp_write_mode_ctl(struct pmic_mpp_state *state,
25562306a36Sopenharmony_ci				   struct pmic_mpp_pad *pad)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	unsigned int mode;
25862306a36Sopenharmony_ci	unsigned int sel;
25962306a36Sopenharmony_ci	unsigned int val;
26062306a36Sopenharmony_ci	unsigned int en;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	switch (pad->function) {
26362306a36Sopenharmony_ci	case PMIC_MPP_ANALOG:
26462306a36Sopenharmony_ci		if (pad->input_enabled && pad->output_enabled)
26562306a36Sopenharmony_ci			mode = PMIC_MPP_MODE_ANALOG_BIDIR;
26662306a36Sopenharmony_ci		else if (pad->input_enabled)
26762306a36Sopenharmony_ci			mode = PMIC_MPP_MODE_ANALOG_INPUT;
26862306a36Sopenharmony_ci		else
26962306a36Sopenharmony_ci			mode = PMIC_MPP_MODE_ANALOG_OUTPUT;
27062306a36Sopenharmony_ci		break;
27162306a36Sopenharmony_ci	case PMIC_MPP_DIGITAL:
27262306a36Sopenharmony_ci		if (pad->input_enabled && pad->output_enabled)
27362306a36Sopenharmony_ci			mode = PMIC_MPP_MODE_DIGITAL_BIDIR;
27462306a36Sopenharmony_ci		else if (pad->input_enabled)
27562306a36Sopenharmony_ci			mode = PMIC_MPP_MODE_DIGITAL_INPUT;
27662306a36Sopenharmony_ci		else
27762306a36Sopenharmony_ci			mode = PMIC_MPP_MODE_DIGITAL_OUTPUT;
27862306a36Sopenharmony_ci		break;
27962306a36Sopenharmony_ci	case PMIC_MPP_SINK:
28062306a36Sopenharmony_ci	default:
28162306a36Sopenharmony_ci		mode = PMIC_MPP_MODE_CURRENT_SINK;
28262306a36Sopenharmony_ci		break;
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if (pad->dtest)
28662306a36Sopenharmony_ci		sel = PMIC_MPP_SELECTOR_DTEST_FIRST + pad->dtest - 1;
28762306a36Sopenharmony_ci	else if (pad->paired)
28862306a36Sopenharmony_ci		sel = PMIC_MPP_SELECTOR_PAIRED;
28962306a36Sopenharmony_ci	else
29062306a36Sopenharmony_ci		sel = PMIC_MPP_SELECTOR_NORMAL;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	en = !!pad->out_value;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	val = mode << PMIC_MPP_REG_MODE_DIR_SHIFT |
29562306a36Sopenharmony_ci	      sel << PMIC_MPP_REG_MODE_FUNCTION_SHIFT |
29662306a36Sopenharmony_ci	      en;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	return pmic_mpp_write(state, pad, PMIC_MPP_REG_MODE_CTL, val);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic int pmic_mpp_set_mux(struct pinctrl_dev *pctldev, unsigned function,
30262306a36Sopenharmony_ci				unsigned pin)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	struct pmic_mpp_state *state = pinctrl_dev_get_drvdata(pctldev);
30562306a36Sopenharmony_ci	struct pmic_mpp_pad *pad;
30662306a36Sopenharmony_ci	unsigned int val;
30762306a36Sopenharmony_ci	int ret;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	pad = pctldev->desc->pins[pin].drv_data;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	pad->function = function;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	ret = pmic_mpp_write_mode_ctl(state, pad);
31462306a36Sopenharmony_ci	if (ret < 0)
31562306a36Sopenharmony_ci		return ret;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	val = pad->is_enabled << PMIC_MPP_REG_MASTER_EN_SHIFT;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	return pmic_mpp_write(state, pad, PMIC_MPP_REG_EN_CTL, val);
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic const struct pinmux_ops pmic_mpp_pinmux_ops = {
32362306a36Sopenharmony_ci	.get_functions_count	= pmic_mpp_get_functions_count,
32462306a36Sopenharmony_ci	.get_function_name	= pmic_mpp_get_function_name,
32562306a36Sopenharmony_ci	.get_function_groups	= pmic_mpp_get_function_groups,
32662306a36Sopenharmony_ci	.set_mux		= pmic_mpp_set_mux,
32762306a36Sopenharmony_ci};
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic int pmic_mpp_config_get(struct pinctrl_dev *pctldev,
33062306a36Sopenharmony_ci			       unsigned int pin, unsigned long *config)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	unsigned param = pinconf_to_config_param(*config);
33362306a36Sopenharmony_ci	struct pmic_mpp_pad *pad;
33462306a36Sopenharmony_ci	unsigned arg = 0;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	pad = pctldev->desc->pins[pin].drv_data;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	switch (param) {
33962306a36Sopenharmony_ci	case PIN_CONFIG_BIAS_DISABLE:
34062306a36Sopenharmony_ci		if (pad->pullup != PMIC_MPP_PULL_UP_OPEN)
34162306a36Sopenharmony_ci			return -EINVAL;
34262306a36Sopenharmony_ci		arg = 1;
34362306a36Sopenharmony_ci		break;
34462306a36Sopenharmony_ci	case PIN_CONFIG_BIAS_PULL_UP:
34562306a36Sopenharmony_ci		switch (pad->pullup) {
34662306a36Sopenharmony_ci		case PMIC_MPP_PULL_UP_0P6KOHM:
34762306a36Sopenharmony_ci			arg = 600;
34862306a36Sopenharmony_ci			break;
34962306a36Sopenharmony_ci		case PMIC_MPP_PULL_UP_10KOHM:
35062306a36Sopenharmony_ci			arg = 10000;
35162306a36Sopenharmony_ci			break;
35262306a36Sopenharmony_ci		case PMIC_MPP_PULL_UP_30KOHM:
35362306a36Sopenharmony_ci			arg = 30000;
35462306a36Sopenharmony_ci			break;
35562306a36Sopenharmony_ci		default:
35662306a36Sopenharmony_ci			return -EINVAL;
35762306a36Sopenharmony_ci		}
35862306a36Sopenharmony_ci		break;
35962306a36Sopenharmony_ci	case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
36062306a36Sopenharmony_ci		if (pad->is_enabled)
36162306a36Sopenharmony_ci			return -EINVAL;
36262306a36Sopenharmony_ci		arg = 1;
36362306a36Sopenharmony_ci		break;
36462306a36Sopenharmony_ci	case PIN_CONFIG_POWER_SOURCE:
36562306a36Sopenharmony_ci		arg = pad->power_source;
36662306a36Sopenharmony_ci		break;
36762306a36Sopenharmony_ci	case PIN_CONFIG_INPUT_ENABLE:
36862306a36Sopenharmony_ci		if (!pad->input_enabled)
36962306a36Sopenharmony_ci			return -EINVAL;
37062306a36Sopenharmony_ci		arg = 1;
37162306a36Sopenharmony_ci		break;
37262306a36Sopenharmony_ci	case PIN_CONFIG_OUTPUT:
37362306a36Sopenharmony_ci		arg = pad->out_value;
37462306a36Sopenharmony_ci		break;
37562306a36Sopenharmony_ci	case PMIC_MPP_CONF_DTEST_SELECTOR:
37662306a36Sopenharmony_ci		arg = pad->dtest;
37762306a36Sopenharmony_ci		break;
37862306a36Sopenharmony_ci	case PMIC_MPP_CONF_AMUX_ROUTE:
37962306a36Sopenharmony_ci		arg = pad->amux_input;
38062306a36Sopenharmony_ci		break;
38162306a36Sopenharmony_ci	case PMIC_MPP_CONF_PAIRED:
38262306a36Sopenharmony_ci		if (!pad->paired)
38362306a36Sopenharmony_ci			return -EINVAL;
38462306a36Sopenharmony_ci		arg = 1;
38562306a36Sopenharmony_ci		break;
38662306a36Sopenharmony_ci	case PIN_CONFIG_DRIVE_STRENGTH:
38762306a36Sopenharmony_ci		arg = pad->drive_strength;
38862306a36Sopenharmony_ci		break;
38962306a36Sopenharmony_ci	case PMIC_MPP_CONF_ANALOG_LEVEL:
39062306a36Sopenharmony_ci		arg = pad->aout_level;
39162306a36Sopenharmony_ci		break;
39262306a36Sopenharmony_ci	default:
39362306a36Sopenharmony_ci		return -EINVAL;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	/* Convert register value to pinconf value */
39762306a36Sopenharmony_ci	*config = pinconf_to_config_packed(param, arg);
39862306a36Sopenharmony_ci	return 0;
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic int pmic_mpp_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
40262306a36Sopenharmony_ci			       unsigned long *configs, unsigned nconfs)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	struct pmic_mpp_state *state = pinctrl_dev_get_drvdata(pctldev);
40562306a36Sopenharmony_ci	struct pmic_mpp_pad *pad;
40662306a36Sopenharmony_ci	unsigned param, arg;
40762306a36Sopenharmony_ci	unsigned int val;
40862306a36Sopenharmony_ci	int i, ret;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	pad = pctldev->desc->pins[pin].drv_data;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	/* Make it possible to enable the pin, by not setting high impedance */
41362306a36Sopenharmony_ci	pad->is_enabled = true;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	for (i = 0; i < nconfs; i++) {
41662306a36Sopenharmony_ci		param = pinconf_to_config_param(configs[i]);
41762306a36Sopenharmony_ci		arg = pinconf_to_config_argument(configs[i]);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		switch (param) {
42062306a36Sopenharmony_ci		case PIN_CONFIG_BIAS_DISABLE:
42162306a36Sopenharmony_ci			pad->pullup = PMIC_MPP_PULL_UP_OPEN;
42262306a36Sopenharmony_ci			break;
42362306a36Sopenharmony_ci		case PIN_CONFIG_BIAS_PULL_UP:
42462306a36Sopenharmony_ci			switch (arg) {
42562306a36Sopenharmony_ci			case 600:
42662306a36Sopenharmony_ci				pad->pullup = PMIC_MPP_PULL_UP_0P6KOHM;
42762306a36Sopenharmony_ci				break;
42862306a36Sopenharmony_ci			case 10000:
42962306a36Sopenharmony_ci				pad->pullup = PMIC_MPP_PULL_UP_10KOHM;
43062306a36Sopenharmony_ci				break;
43162306a36Sopenharmony_ci			case 30000:
43262306a36Sopenharmony_ci				pad->pullup = PMIC_MPP_PULL_UP_30KOHM;
43362306a36Sopenharmony_ci				break;
43462306a36Sopenharmony_ci			default:
43562306a36Sopenharmony_ci				return -EINVAL;
43662306a36Sopenharmony_ci			}
43762306a36Sopenharmony_ci			break;
43862306a36Sopenharmony_ci		case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
43962306a36Sopenharmony_ci			pad->is_enabled = false;
44062306a36Sopenharmony_ci			break;
44162306a36Sopenharmony_ci		case PIN_CONFIG_POWER_SOURCE:
44262306a36Sopenharmony_ci			if (arg >= pad->num_sources)
44362306a36Sopenharmony_ci				return -EINVAL;
44462306a36Sopenharmony_ci			pad->power_source = arg;
44562306a36Sopenharmony_ci			break;
44662306a36Sopenharmony_ci		case PIN_CONFIG_INPUT_ENABLE:
44762306a36Sopenharmony_ci			pad->input_enabled = arg ? true : false;
44862306a36Sopenharmony_ci			break;
44962306a36Sopenharmony_ci		case PIN_CONFIG_OUTPUT:
45062306a36Sopenharmony_ci			pad->output_enabled = true;
45162306a36Sopenharmony_ci			pad->out_value = arg;
45262306a36Sopenharmony_ci			break;
45362306a36Sopenharmony_ci		case PMIC_MPP_CONF_DTEST_SELECTOR:
45462306a36Sopenharmony_ci			pad->dtest = arg;
45562306a36Sopenharmony_ci			break;
45662306a36Sopenharmony_ci		case PIN_CONFIG_DRIVE_STRENGTH:
45762306a36Sopenharmony_ci			pad->drive_strength = arg;
45862306a36Sopenharmony_ci			break;
45962306a36Sopenharmony_ci		case PMIC_MPP_CONF_AMUX_ROUTE:
46062306a36Sopenharmony_ci			if (arg >= PMIC_MPP_AMUX_ROUTE_ABUS4)
46162306a36Sopenharmony_ci				return -EINVAL;
46262306a36Sopenharmony_ci			pad->amux_input = arg;
46362306a36Sopenharmony_ci			break;
46462306a36Sopenharmony_ci		case PMIC_MPP_CONF_ANALOG_LEVEL:
46562306a36Sopenharmony_ci			pad->aout_level = arg;
46662306a36Sopenharmony_ci			break;
46762306a36Sopenharmony_ci		case PMIC_MPP_CONF_PAIRED:
46862306a36Sopenharmony_ci			pad->paired = !!arg;
46962306a36Sopenharmony_ci			break;
47062306a36Sopenharmony_ci		default:
47162306a36Sopenharmony_ci			return -EINVAL;
47262306a36Sopenharmony_ci		}
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	val = pad->power_source << PMIC_MPP_REG_VIN_SHIFT;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_DIG_VIN_CTL, val);
47862306a36Sopenharmony_ci	if (ret < 0)
47962306a36Sopenharmony_ci		return ret;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	if (pad->has_pullup) {
48262306a36Sopenharmony_ci		val = pad->pullup << PMIC_MPP_REG_PULL_SHIFT;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci		ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_DIG_PULL_CTL,
48562306a36Sopenharmony_ci				     val);
48662306a36Sopenharmony_ci		if (ret < 0)
48762306a36Sopenharmony_ci			return ret;
48862306a36Sopenharmony_ci	}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	val = pad->amux_input & PMIC_MPP_REG_AIN_ROUTE_MASK;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_AIN_CTL, val);
49362306a36Sopenharmony_ci	if (ret < 0)
49462306a36Sopenharmony_ci		return ret;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_AOUT_CTL, pad->aout_level);
49762306a36Sopenharmony_ci	if (ret < 0)
49862306a36Sopenharmony_ci		return ret;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	ret = pmic_mpp_write_mode_ctl(state, pad);
50162306a36Sopenharmony_ci	if (ret < 0)
50262306a36Sopenharmony_ci		return ret;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_SINK_CTL, pad->drive_strength);
50562306a36Sopenharmony_ci	if (ret < 0)
50662306a36Sopenharmony_ci		return ret;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	val = pad->is_enabled << PMIC_MPP_REG_MASTER_EN_SHIFT;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	return pmic_mpp_write(state, pad, PMIC_MPP_REG_EN_CTL, val);
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_cistatic void pmic_mpp_config_dbg_show(struct pinctrl_dev *pctldev,
51462306a36Sopenharmony_ci				     struct seq_file *s, unsigned pin)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	struct pmic_mpp_state *state = pinctrl_dev_get_drvdata(pctldev);
51762306a36Sopenharmony_ci	struct pmic_mpp_pad *pad;
51862306a36Sopenharmony_ci	int ret;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	static const char *const biases[] = {
52162306a36Sopenharmony_ci		"0.6kOhm", "10kOhm", "30kOhm", "Disabled"
52262306a36Sopenharmony_ci	};
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	pad = pctldev->desc->pins[pin].drv_data;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	seq_printf(s, " mpp%-2d:", pin + PMIC_MPP_PHYSICAL_OFFSET);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	if (!pad->is_enabled) {
52962306a36Sopenharmony_ci		seq_puts(s, " ---");
53062306a36Sopenharmony_ci	} else {
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci		if (pad->input_enabled) {
53362306a36Sopenharmony_ci			ret = pmic_mpp_read(state, pad, PMIC_MPP_REG_RT_STS);
53462306a36Sopenharmony_ci			if (ret < 0)
53562306a36Sopenharmony_ci				return;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci			ret &= PMIC_MPP_REG_RT_STS_VAL_MASK;
53862306a36Sopenharmony_ci			pad->out_value = ret;
53962306a36Sopenharmony_ci		}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci		seq_printf(s, " %-4s", pad->output_enabled ? "out" : "in");
54262306a36Sopenharmony_ci		seq_printf(s, " %-7s", pmic_mpp_functions[pad->function]);
54362306a36Sopenharmony_ci		seq_printf(s, " vin-%d", pad->power_source);
54462306a36Sopenharmony_ci		seq_printf(s, " %d", pad->aout_level);
54562306a36Sopenharmony_ci		if (pad->has_pullup)
54662306a36Sopenharmony_ci			seq_printf(s, " %-8s", biases[pad->pullup]);
54762306a36Sopenharmony_ci		seq_printf(s, " %-4s", pad->out_value ? "high" : "low");
54862306a36Sopenharmony_ci		if (pad->dtest)
54962306a36Sopenharmony_ci			seq_printf(s, " dtest%d", pad->dtest);
55062306a36Sopenharmony_ci		if (pad->paired)
55162306a36Sopenharmony_ci			seq_puts(s, " paired");
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_cistatic const struct pinconf_ops pmic_mpp_pinconf_ops = {
55662306a36Sopenharmony_ci	.is_generic = true,
55762306a36Sopenharmony_ci	.pin_config_group_get		= pmic_mpp_config_get,
55862306a36Sopenharmony_ci	.pin_config_group_set		= pmic_mpp_config_set,
55962306a36Sopenharmony_ci	.pin_config_group_dbg_show	= pmic_mpp_config_dbg_show,
56062306a36Sopenharmony_ci};
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic int pmic_mpp_direction_input(struct gpio_chip *chip, unsigned pin)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	struct pmic_mpp_state *state = gpiochip_get_data(chip);
56562306a36Sopenharmony_ci	unsigned long config;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	return pmic_mpp_config_set(state->ctrl, pin, &config, 1);
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic int pmic_mpp_direction_output(struct gpio_chip *chip,
57362306a36Sopenharmony_ci				     unsigned pin, int val)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	struct pmic_mpp_state *state = gpiochip_get_data(chip);
57662306a36Sopenharmony_ci	unsigned long config;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	return pmic_mpp_config_set(state->ctrl, pin, &config, 1);
58162306a36Sopenharmony_ci}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_cistatic int pmic_mpp_get(struct gpio_chip *chip, unsigned pin)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	struct pmic_mpp_state *state = gpiochip_get_data(chip);
58662306a36Sopenharmony_ci	struct pmic_mpp_pad *pad;
58762306a36Sopenharmony_ci	int ret;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	pad = state->ctrl->desc->pins[pin].drv_data;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	if (pad->input_enabled) {
59262306a36Sopenharmony_ci		ret = pmic_mpp_read(state, pad, PMIC_MPP_REG_RT_STS);
59362306a36Sopenharmony_ci		if (ret < 0)
59462306a36Sopenharmony_ci			return ret;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci		pad->out_value = ret & PMIC_MPP_REG_RT_STS_VAL_MASK;
59762306a36Sopenharmony_ci	}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	return !!pad->out_value;
60062306a36Sopenharmony_ci}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_cistatic void pmic_mpp_set(struct gpio_chip *chip, unsigned pin, int value)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	struct pmic_mpp_state *state = gpiochip_get_data(chip);
60562306a36Sopenharmony_ci	unsigned long config;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	pmic_mpp_config_set(state->ctrl, pin, &config, 1);
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic int pmic_mpp_of_xlate(struct gpio_chip *chip,
61362306a36Sopenharmony_ci			     const struct of_phandle_args *gpio_desc,
61462306a36Sopenharmony_ci			     u32 *flags)
61562306a36Sopenharmony_ci{
61662306a36Sopenharmony_ci	if (chip->of_gpio_n_cells < 2)
61762306a36Sopenharmony_ci		return -EINVAL;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	if (flags)
62062306a36Sopenharmony_ci		*flags = gpio_desc->args[1];
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	return gpio_desc->args[0] - PMIC_MPP_PHYSICAL_OFFSET;
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic void pmic_mpp_dbg_show(struct seq_file *s, struct gpio_chip *chip)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci	struct pmic_mpp_state *state = gpiochip_get_data(chip);
62862306a36Sopenharmony_ci	unsigned i;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	for (i = 0; i < chip->ngpio; i++) {
63162306a36Sopenharmony_ci		pmic_mpp_config_dbg_show(state->ctrl, s, i);
63262306a36Sopenharmony_ci		seq_puts(s, "\n");
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_cistatic const struct gpio_chip pmic_mpp_gpio_template = {
63762306a36Sopenharmony_ci	.direction_input	= pmic_mpp_direction_input,
63862306a36Sopenharmony_ci	.direction_output	= pmic_mpp_direction_output,
63962306a36Sopenharmony_ci	.get			= pmic_mpp_get,
64062306a36Sopenharmony_ci	.set			= pmic_mpp_set,
64162306a36Sopenharmony_ci	.request		= gpiochip_generic_request,
64262306a36Sopenharmony_ci	.free			= gpiochip_generic_free,
64362306a36Sopenharmony_ci	.of_xlate		= pmic_mpp_of_xlate,
64462306a36Sopenharmony_ci	.dbg_show		= pmic_mpp_dbg_show,
64562306a36Sopenharmony_ci};
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic int pmic_mpp_populate(struct pmic_mpp_state *state,
64862306a36Sopenharmony_ci			     struct pmic_mpp_pad *pad)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	int type, subtype, val, dir;
65162306a36Sopenharmony_ci	unsigned int sel;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	type = pmic_mpp_read(state, pad, PMIC_MPP_REG_TYPE);
65462306a36Sopenharmony_ci	if (type < 0)
65562306a36Sopenharmony_ci		return type;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	if (type != PMIC_MPP_TYPE) {
65862306a36Sopenharmony_ci		dev_err(state->dev, "incorrect block type 0x%x at 0x%x\n",
65962306a36Sopenharmony_ci			type, pad->base);
66062306a36Sopenharmony_ci		return -ENODEV;
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	subtype = pmic_mpp_read(state, pad, PMIC_MPP_REG_SUBTYPE);
66462306a36Sopenharmony_ci	if (subtype < 0)
66562306a36Sopenharmony_ci		return subtype;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	switch (subtype) {
66862306a36Sopenharmony_ci	case PMIC_MPP_SUBTYPE_4CH_NO_ANA_OUT:
66962306a36Sopenharmony_ci	case PMIC_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT:
67062306a36Sopenharmony_ci	case PMIC_MPP_SUBTYPE_4CH_NO_SINK:
67162306a36Sopenharmony_ci	case PMIC_MPP_SUBTYPE_ULT_4CH_NO_SINK:
67262306a36Sopenharmony_ci	case PMIC_MPP_SUBTYPE_4CH_FULL_FUNC:
67362306a36Sopenharmony_ci		pad->num_sources = 4;
67462306a36Sopenharmony_ci		break;
67562306a36Sopenharmony_ci	case PMIC_MPP_SUBTYPE_8CH_FULL_FUNC:
67662306a36Sopenharmony_ci		pad->num_sources = 8;
67762306a36Sopenharmony_ci		break;
67862306a36Sopenharmony_ci	default:
67962306a36Sopenharmony_ci		dev_err(state->dev, "unknown MPP type 0x%x at 0x%x\n",
68062306a36Sopenharmony_ci			subtype, pad->base);
68162306a36Sopenharmony_ci		return -ENODEV;
68262306a36Sopenharmony_ci	}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_MODE_CTL);
68562306a36Sopenharmony_ci	if (val < 0)
68662306a36Sopenharmony_ci		return val;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	pad->out_value = val & PMIC_MPP_REG_MODE_VALUE_MASK;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	dir = val >> PMIC_MPP_REG_MODE_DIR_SHIFT;
69162306a36Sopenharmony_ci	dir &= PMIC_MPP_REG_MODE_DIR_MASK;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	switch (dir) {
69462306a36Sopenharmony_ci	case PMIC_MPP_MODE_DIGITAL_INPUT:
69562306a36Sopenharmony_ci		pad->input_enabled = true;
69662306a36Sopenharmony_ci		pad->output_enabled = false;
69762306a36Sopenharmony_ci		pad->function = PMIC_MPP_DIGITAL;
69862306a36Sopenharmony_ci		break;
69962306a36Sopenharmony_ci	case PMIC_MPP_MODE_DIGITAL_OUTPUT:
70062306a36Sopenharmony_ci		pad->input_enabled = false;
70162306a36Sopenharmony_ci		pad->output_enabled = true;
70262306a36Sopenharmony_ci		pad->function = PMIC_MPP_DIGITAL;
70362306a36Sopenharmony_ci		break;
70462306a36Sopenharmony_ci	case PMIC_MPP_MODE_DIGITAL_BIDIR:
70562306a36Sopenharmony_ci		pad->input_enabled = true;
70662306a36Sopenharmony_ci		pad->output_enabled = true;
70762306a36Sopenharmony_ci		pad->function = PMIC_MPP_DIGITAL;
70862306a36Sopenharmony_ci		break;
70962306a36Sopenharmony_ci	case PMIC_MPP_MODE_ANALOG_BIDIR:
71062306a36Sopenharmony_ci		pad->input_enabled = true;
71162306a36Sopenharmony_ci		pad->output_enabled = true;
71262306a36Sopenharmony_ci		pad->function = PMIC_MPP_ANALOG;
71362306a36Sopenharmony_ci		break;
71462306a36Sopenharmony_ci	case PMIC_MPP_MODE_ANALOG_INPUT:
71562306a36Sopenharmony_ci		pad->input_enabled = true;
71662306a36Sopenharmony_ci		pad->output_enabled = false;
71762306a36Sopenharmony_ci		pad->function = PMIC_MPP_ANALOG;
71862306a36Sopenharmony_ci		break;
71962306a36Sopenharmony_ci	case PMIC_MPP_MODE_ANALOG_OUTPUT:
72062306a36Sopenharmony_ci		pad->input_enabled = false;
72162306a36Sopenharmony_ci		pad->output_enabled = true;
72262306a36Sopenharmony_ci		pad->function = PMIC_MPP_ANALOG;
72362306a36Sopenharmony_ci		break;
72462306a36Sopenharmony_ci	case PMIC_MPP_MODE_CURRENT_SINK:
72562306a36Sopenharmony_ci		pad->input_enabled = false;
72662306a36Sopenharmony_ci		pad->output_enabled = true;
72762306a36Sopenharmony_ci		pad->function = PMIC_MPP_SINK;
72862306a36Sopenharmony_ci		break;
72962306a36Sopenharmony_ci	default:
73062306a36Sopenharmony_ci		dev_err(state->dev, "unknown MPP direction\n");
73162306a36Sopenharmony_ci		return -ENODEV;
73262306a36Sopenharmony_ci	}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	sel = val >> PMIC_MPP_REG_MODE_FUNCTION_SHIFT;
73562306a36Sopenharmony_ci	sel &= PMIC_MPP_REG_MODE_FUNCTION_MASK;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	if (sel >= PMIC_MPP_SELECTOR_DTEST_FIRST)
73862306a36Sopenharmony_ci		pad->dtest = sel + 1;
73962306a36Sopenharmony_ci	else if (sel == PMIC_MPP_SELECTOR_PAIRED)
74062306a36Sopenharmony_ci		pad->paired = true;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_DIG_VIN_CTL);
74362306a36Sopenharmony_ci	if (val < 0)
74462306a36Sopenharmony_ci		return val;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	pad->power_source = val >> PMIC_MPP_REG_VIN_SHIFT;
74762306a36Sopenharmony_ci	pad->power_source &= PMIC_MPP_REG_VIN_MASK;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	if (subtype != PMIC_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT &&
75062306a36Sopenharmony_ci	    subtype != PMIC_MPP_SUBTYPE_ULT_4CH_NO_SINK) {
75162306a36Sopenharmony_ci		val = pmic_mpp_read(state, pad, PMIC_MPP_REG_DIG_PULL_CTL);
75262306a36Sopenharmony_ci		if (val < 0)
75362306a36Sopenharmony_ci			return val;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci		pad->pullup = val >> PMIC_MPP_REG_PULL_SHIFT;
75662306a36Sopenharmony_ci		pad->pullup &= PMIC_MPP_REG_PULL_MASK;
75762306a36Sopenharmony_ci		pad->has_pullup = true;
75862306a36Sopenharmony_ci	}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_AIN_CTL);
76162306a36Sopenharmony_ci	if (val < 0)
76262306a36Sopenharmony_ci		return val;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	pad->amux_input = val >> PMIC_MPP_REG_AIN_ROUTE_SHIFT;
76562306a36Sopenharmony_ci	pad->amux_input &= PMIC_MPP_REG_AIN_ROUTE_MASK;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_SINK_CTL);
76862306a36Sopenharmony_ci	if (val < 0)
76962306a36Sopenharmony_ci		return val;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	pad->drive_strength = val;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_AOUT_CTL);
77462306a36Sopenharmony_ci	if (val < 0)
77562306a36Sopenharmony_ci		return val;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	pad->aout_level = val;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_EN_CTL);
78062306a36Sopenharmony_ci	if (val < 0)
78162306a36Sopenharmony_ci		return val;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	pad->is_enabled = !!val;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	return 0;
78662306a36Sopenharmony_ci}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_cistatic int pmic_mpp_domain_translate(struct irq_domain *domain,
78962306a36Sopenharmony_ci				      struct irq_fwspec *fwspec,
79062306a36Sopenharmony_ci				      unsigned long *hwirq,
79162306a36Sopenharmony_ci				      unsigned int *type)
79262306a36Sopenharmony_ci{
79362306a36Sopenharmony_ci	struct pmic_mpp_state *state = container_of(domain->host_data,
79462306a36Sopenharmony_ci						     struct pmic_mpp_state,
79562306a36Sopenharmony_ci						     chip);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	if (fwspec->param_count != 2 ||
79862306a36Sopenharmony_ci	    fwspec->param[0] < 1 || fwspec->param[0] > state->chip.ngpio)
79962306a36Sopenharmony_ci		return -EINVAL;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	*hwirq = fwspec->param[0] - PMIC_MPP_PHYSICAL_OFFSET;
80262306a36Sopenharmony_ci	*type = fwspec->param[1];
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	return 0;
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_cistatic unsigned int pmic_mpp_child_offset_to_irq(struct gpio_chip *chip,
80862306a36Sopenharmony_ci						  unsigned int offset)
80962306a36Sopenharmony_ci{
81062306a36Sopenharmony_ci	return offset + PMIC_MPP_PHYSICAL_OFFSET;
81162306a36Sopenharmony_ci}
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_cistatic int pmic_mpp_child_to_parent_hwirq(struct gpio_chip *chip,
81462306a36Sopenharmony_ci					   unsigned int child_hwirq,
81562306a36Sopenharmony_ci					   unsigned int child_type,
81662306a36Sopenharmony_ci					   unsigned int *parent_hwirq,
81762306a36Sopenharmony_ci					   unsigned int *parent_type)
81862306a36Sopenharmony_ci{
81962306a36Sopenharmony_ci	*parent_hwirq = child_hwirq + 0xc0;
82062306a36Sopenharmony_ci	*parent_type = child_type;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	return 0;
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_cistatic void pmic_mpp_irq_mask(struct irq_data *d)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	irq_chip_mask_parent(d);
83062306a36Sopenharmony_ci	gpiochip_disable_irq(gc, irqd_to_hwirq(d));
83162306a36Sopenharmony_ci}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_cistatic void pmic_mpp_irq_unmask(struct irq_data *d)
83462306a36Sopenharmony_ci{
83562306a36Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	gpiochip_enable_irq(gc, irqd_to_hwirq(d));
83862306a36Sopenharmony_ci	irq_chip_unmask_parent(d);
83962306a36Sopenharmony_ci}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_cistatic const struct irq_chip pmic_mpp_irq_chip = {
84262306a36Sopenharmony_ci	.name = "spmi-mpp",
84362306a36Sopenharmony_ci	.irq_ack = irq_chip_ack_parent,
84462306a36Sopenharmony_ci	.irq_mask = pmic_mpp_irq_mask,
84562306a36Sopenharmony_ci	.irq_unmask = pmic_mpp_irq_unmask,
84662306a36Sopenharmony_ci	.irq_set_type = irq_chip_set_type_parent,
84762306a36Sopenharmony_ci	.irq_set_wake = irq_chip_set_wake_parent,
84862306a36Sopenharmony_ci	.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_IMMUTABLE,
84962306a36Sopenharmony_ci	GPIOCHIP_IRQ_RESOURCE_HELPERS,
85062306a36Sopenharmony_ci};
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_cistatic int pmic_mpp_probe(struct platform_device *pdev)
85362306a36Sopenharmony_ci{
85462306a36Sopenharmony_ci	struct irq_domain *parent_domain;
85562306a36Sopenharmony_ci	struct device_node *parent_node;
85662306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
85762306a36Sopenharmony_ci	struct pinctrl_pin_desc *pindesc;
85862306a36Sopenharmony_ci	struct pinctrl_desc *pctrldesc;
85962306a36Sopenharmony_ci	struct pmic_mpp_pad *pad, *pads;
86062306a36Sopenharmony_ci	struct pmic_mpp_state *state;
86162306a36Sopenharmony_ci	struct gpio_irq_chip *girq;
86262306a36Sopenharmony_ci	int ret, npins, i;
86362306a36Sopenharmony_ci	u32 reg;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	ret = of_property_read_u32(dev->of_node, "reg", &reg);
86662306a36Sopenharmony_ci	if (ret < 0) {
86762306a36Sopenharmony_ci		dev_err(dev, "missing base address");
86862306a36Sopenharmony_ci		return ret;
86962306a36Sopenharmony_ci	}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	npins = (uintptr_t) device_get_match_data(&pdev->dev);
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	BUG_ON(npins > ARRAY_SIZE(pmic_mpp_groups));
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
87662306a36Sopenharmony_ci	if (!state)
87762306a36Sopenharmony_ci		return -ENOMEM;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	platform_set_drvdata(pdev, state);
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	state->dev = &pdev->dev;
88262306a36Sopenharmony_ci	state->map = dev_get_regmap(dev->parent, NULL);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
88562306a36Sopenharmony_ci	if (!pindesc)
88662306a36Sopenharmony_ci		return -ENOMEM;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
88962306a36Sopenharmony_ci	if (!pads)
89062306a36Sopenharmony_ci		return -ENOMEM;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL);
89362306a36Sopenharmony_ci	if (!pctrldesc)
89462306a36Sopenharmony_ci		return -ENOMEM;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	pctrldesc->pctlops = &pmic_mpp_pinctrl_ops;
89762306a36Sopenharmony_ci	pctrldesc->pmxops = &pmic_mpp_pinmux_ops;
89862306a36Sopenharmony_ci	pctrldesc->confops = &pmic_mpp_pinconf_ops;
89962306a36Sopenharmony_ci	pctrldesc->owner = THIS_MODULE;
90062306a36Sopenharmony_ci	pctrldesc->name = dev_name(dev);
90162306a36Sopenharmony_ci	pctrldesc->pins = pindesc;
90262306a36Sopenharmony_ci	pctrldesc->npins = npins;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	pctrldesc->num_custom_params = ARRAY_SIZE(pmic_mpp_bindings);
90562306a36Sopenharmony_ci	pctrldesc->custom_params = pmic_mpp_bindings;
90662306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
90762306a36Sopenharmony_ci	pctrldesc->custom_conf_items = pmic_conf_items;
90862306a36Sopenharmony_ci#endif
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	for (i = 0; i < npins; i++, pindesc++) {
91162306a36Sopenharmony_ci		pad = &pads[i];
91262306a36Sopenharmony_ci		pindesc->drv_data = pad;
91362306a36Sopenharmony_ci		pindesc->number = i;
91462306a36Sopenharmony_ci		pindesc->name = pmic_mpp_groups[i];
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci		pad->base = reg + i * PMIC_MPP_ADDRESS_RANGE;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci		ret = pmic_mpp_populate(state, pad);
91962306a36Sopenharmony_ci		if (ret < 0)
92062306a36Sopenharmony_ci			return ret;
92162306a36Sopenharmony_ci	}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	state->chip = pmic_mpp_gpio_template;
92462306a36Sopenharmony_ci	state->chip.parent = dev;
92562306a36Sopenharmony_ci	state->chip.base = -1;
92662306a36Sopenharmony_ci	state->chip.ngpio = npins;
92762306a36Sopenharmony_ci	state->chip.label = dev_name(dev);
92862306a36Sopenharmony_ci	state->chip.of_gpio_n_cells = 2;
92962306a36Sopenharmony_ci	state->chip.can_sleep = false;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	state->ctrl = devm_pinctrl_register(dev, pctrldesc, state);
93262306a36Sopenharmony_ci	if (IS_ERR(state->ctrl))
93362306a36Sopenharmony_ci		return PTR_ERR(state->ctrl);
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	parent_node = of_irq_find_parent(state->dev->of_node);
93662306a36Sopenharmony_ci	if (!parent_node)
93762306a36Sopenharmony_ci		return -ENXIO;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	parent_domain = irq_find_host(parent_node);
94062306a36Sopenharmony_ci	of_node_put(parent_node);
94162306a36Sopenharmony_ci	if (!parent_domain)
94262306a36Sopenharmony_ci		return -ENXIO;
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	girq = &state->chip.irq;
94562306a36Sopenharmony_ci	gpio_irq_chip_set_chip(girq, &pmic_mpp_irq_chip);
94662306a36Sopenharmony_ci	girq->default_type = IRQ_TYPE_NONE;
94762306a36Sopenharmony_ci	girq->handler = handle_level_irq;
94862306a36Sopenharmony_ci	girq->fwnode = dev_fwnode(state->dev);
94962306a36Sopenharmony_ci	girq->parent_domain = parent_domain;
95062306a36Sopenharmony_ci	girq->child_to_parent_hwirq = pmic_mpp_child_to_parent_hwirq;
95162306a36Sopenharmony_ci	girq->populate_parent_alloc_arg = gpiochip_populate_parent_fwspec_fourcell;
95262306a36Sopenharmony_ci	girq->child_offset_to_irq = pmic_mpp_child_offset_to_irq;
95362306a36Sopenharmony_ci	girq->child_irq_domain_ops.translate = pmic_mpp_domain_translate;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	ret = gpiochip_add_data(&state->chip, state);
95662306a36Sopenharmony_ci	if (ret) {
95762306a36Sopenharmony_ci		dev_err(state->dev, "can't add gpio chip\n");
95862306a36Sopenharmony_ci		return ret;
95962306a36Sopenharmony_ci	}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, npins);
96262306a36Sopenharmony_ci	if (ret) {
96362306a36Sopenharmony_ci		dev_err(dev, "failed to add pin range\n");
96462306a36Sopenharmony_ci		goto err_range;
96562306a36Sopenharmony_ci	}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	return 0;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_cierr_range:
97062306a36Sopenharmony_ci	gpiochip_remove(&state->chip);
97162306a36Sopenharmony_ci	return ret;
97262306a36Sopenharmony_ci}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_cistatic int pmic_mpp_remove(struct platform_device *pdev)
97562306a36Sopenharmony_ci{
97662306a36Sopenharmony_ci	struct pmic_mpp_state *state = platform_get_drvdata(pdev);
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	gpiochip_remove(&state->chip);
97962306a36Sopenharmony_ci	return 0;
98062306a36Sopenharmony_ci}
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_cistatic const struct of_device_id pmic_mpp_of_match[] = {
98362306a36Sopenharmony_ci	{ .compatible = "qcom,pm8019-mpp", .data = (void *) 6 },
98462306a36Sopenharmony_ci	{ .compatible = "qcom,pm8226-mpp", .data = (void *) 8 },
98562306a36Sopenharmony_ci	{ .compatible = "qcom,pm8841-mpp", .data = (void *) 4 },
98662306a36Sopenharmony_ci	{ .compatible = "qcom,pm8916-mpp", .data = (void *) 4 },
98762306a36Sopenharmony_ci	{ .compatible = "qcom,pm8941-mpp", .data = (void *) 8 },
98862306a36Sopenharmony_ci	{ .compatible = "qcom,pm8950-mpp", .data = (void *) 4 },
98962306a36Sopenharmony_ci	{ .compatible = "qcom,pmi8950-mpp", .data = (void *) 4 },
99062306a36Sopenharmony_ci	{ .compatible = "qcom,pm8994-mpp", .data = (void *) 8 },
99162306a36Sopenharmony_ci	{ .compatible = "qcom,pma8084-mpp", .data = (void *) 8 },
99262306a36Sopenharmony_ci	{ .compatible = "qcom,pmi8994-mpp", .data = (void *) 4 },
99362306a36Sopenharmony_ci	{ },
99462306a36Sopenharmony_ci};
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, pmic_mpp_of_match);
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_cistatic struct platform_driver pmic_mpp_driver = {
99962306a36Sopenharmony_ci	.driver = {
100062306a36Sopenharmony_ci		   .name = "qcom-spmi-mpp",
100162306a36Sopenharmony_ci		   .of_match_table = pmic_mpp_of_match,
100262306a36Sopenharmony_ci	},
100362306a36Sopenharmony_ci	.probe	= pmic_mpp_probe,
100462306a36Sopenharmony_ci	.remove = pmic_mpp_remove,
100562306a36Sopenharmony_ci};
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_cimodule_platform_driver(pmic_mpp_driver);
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ciMODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
101062306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm SPMI PMIC MPP pin control driver");
101162306a36Sopenharmony_ciMODULE_ALIAS("platform:qcom-spmi-mpp");
101262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
1013