162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Marvell 37xx SoC pinctrl driver
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2017 Marvell
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Gregory CLEMENT <gregory.clement@free-electrons.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This file is licensed under the terms of the GNU General Public
962306a36Sopenharmony_ci * License version 2 or later. This program is licensed "as is"
1062306a36Sopenharmony_ci * without any warranty of any kind, whether express or implied.
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/gpio/driver.h>
1462306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
1562306a36Sopenharmony_ci#include <linux/of.h>
1662306a36Sopenharmony_ci#include <linux/of_irq.h>
1762306a36Sopenharmony_ci#include <linux/pinctrl/pinconf-generic.h>
1862306a36Sopenharmony_ci#include <linux/pinctrl/pinconf.h>
1962306a36Sopenharmony_ci#include <linux/pinctrl/pinctrl.h>
2062306a36Sopenharmony_ci#include <linux/pinctrl/pinmux.h>
2162306a36Sopenharmony_ci#include <linux/platform_device.h>
2262306a36Sopenharmony_ci#include <linux/property.h>
2362306a36Sopenharmony_ci#include <linux/regmap.h>
2462306a36Sopenharmony_ci#include <linux/seq_file.h>
2562306a36Sopenharmony_ci#include <linux/slab.h>
2662306a36Sopenharmony_ci#include <linux/string_helpers.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include "../pinctrl-utils.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define OUTPUT_EN	0x0
3162306a36Sopenharmony_ci#define INPUT_VAL	0x10
3262306a36Sopenharmony_ci#define OUTPUT_VAL	0x18
3362306a36Sopenharmony_ci#define OUTPUT_CTL	0x20
3462306a36Sopenharmony_ci#define SELECTION	0x30
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define IRQ_EN		0x0
3762306a36Sopenharmony_ci#define IRQ_POL		0x08
3862306a36Sopenharmony_ci#define IRQ_STATUS	0x10
3962306a36Sopenharmony_ci#define IRQ_WKUP	0x18
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define NB_FUNCS 3
4262306a36Sopenharmony_ci#define GPIO_PER_REG	32
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/**
4562306a36Sopenharmony_ci * struct armada_37xx_pin_group: represents group of pins of a pinmux function.
4662306a36Sopenharmony_ci * The pins of a pinmux groups are composed of one or two groups of contiguous
4762306a36Sopenharmony_ci * pins.
4862306a36Sopenharmony_ci * @name:	Name of the pin group, used to lookup the group.
4962306a36Sopenharmony_ci * @start_pin:	Index of the first pin of the main range of pins belonging to
5062306a36Sopenharmony_ci *		the group
5162306a36Sopenharmony_ci * @npins:	Number of pins included in the first range
5262306a36Sopenharmony_ci * @reg_mask:	Bit mask matching the group in the selection register
5362306a36Sopenharmony_ci * @val:	Value to write to the registers for a given function
5462306a36Sopenharmony_ci * @extra_pin:	Index of the first pin of the optional second range of pins
5562306a36Sopenharmony_ci *		belonging to the group
5662306a36Sopenharmony_ci * @extra_npins:Number of pins included in the second optional range
5762306a36Sopenharmony_ci * @funcs:	A list of pinmux functions that can be selected for this group.
5862306a36Sopenharmony_ci * @pins:	List of the pins included in the group
5962306a36Sopenharmony_ci */
6062306a36Sopenharmony_cistruct armada_37xx_pin_group {
6162306a36Sopenharmony_ci	const char	*name;
6262306a36Sopenharmony_ci	unsigned int	start_pin;
6362306a36Sopenharmony_ci	unsigned int	npins;
6462306a36Sopenharmony_ci	u32		reg_mask;
6562306a36Sopenharmony_ci	u32		val[NB_FUNCS];
6662306a36Sopenharmony_ci	unsigned int	extra_pin;
6762306a36Sopenharmony_ci	unsigned int	extra_npins;
6862306a36Sopenharmony_ci	const char	*funcs[NB_FUNCS];
6962306a36Sopenharmony_ci	unsigned int	*pins;
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistruct armada_37xx_pin_data {
7362306a36Sopenharmony_ci	u8				nr_pins;
7462306a36Sopenharmony_ci	char				*name;
7562306a36Sopenharmony_ci	struct armada_37xx_pin_group	*groups;
7662306a36Sopenharmony_ci	int				ngroups;
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistruct armada_37xx_pmx_func {
8062306a36Sopenharmony_ci	const char		*name;
8162306a36Sopenharmony_ci	const char		**groups;
8262306a36Sopenharmony_ci	unsigned int		ngroups;
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistruct armada_37xx_pm_state {
8662306a36Sopenharmony_ci	u32 out_en_l;
8762306a36Sopenharmony_ci	u32 out_en_h;
8862306a36Sopenharmony_ci	u32 out_val_l;
8962306a36Sopenharmony_ci	u32 out_val_h;
9062306a36Sopenharmony_ci	u32 irq_en_l;
9162306a36Sopenharmony_ci	u32 irq_en_h;
9262306a36Sopenharmony_ci	u32 irq_pol_l;
9362306a36Sopenharmony_ci	u32 irq_pol_h;
9462306a36Sopenharmony_ci	u32 selection;
9562306a36Sopenharmony_ci};
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistruct armada_37xx_pinctrl {
9862306a36Sopenharmony_ci	struct regmap			*regmap;
9962306a36Sopenharmony_ci	void __iomem			*base;
10062306a36Sopenharmony_ci	const struct armada_37xx_pin_data	*data;
10162306a36Sopenharmony_ci	struct device			*dev;
10262306a36Sopenharmony_ci	struct gpio_chip		gpio_chip;
10362306a36Sopenharmony_ci	raw_spinlock_t			irq_lock;
10462306a36Sopenharmony_ci	struct pinctrl_desc		pctl;
10562306a36Sopenharmony_ci	struct pinctrl_dev		*pctl_dev;
10662306a36Sopenharmony_ci	struct armada_37xx_pin_group	*groups;
10762306a36Sopenharmony_ci	unsigned int			ngroups;
10862306a36Sopenharmony_ci	struct armada_37xx_pmx_func	*funcs;
10962306a36Sopenharmony_ci	unsigned int			nfuncs;
11062306a36Sopenharmony_ci	struct armada_37xx_pm_state	pm;
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci#define PIN_GRP_GPIO_0(_name, _start, _nr)	\
11462306a36Sopenharmony_ci	{					\
11562306a36Sopenharmony_ci		.name = _name,			\
11662306a36Sopenharmony_ci		.start_pin = _start,		\
11762306a36Sopenharmony_ci		.npins = _nr,			\
11862306a36Sopenharmony_ci		.reg_mask = 0,			\
11962306a36Sopenharmony_ci		.val = {0},			\
12062306a36Sopenharmony_ci		.funcs = {"gpio"}		\
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci#define PIN_GRP_GPIO(_name, _start, _nr, _mask, _func1)	\
12462306a36Sopenharmony_ci	{					\
12562306a36Sopenharmony_ci		.name = _name,			\
12662306a36Sopenharmony_ci		.start_pin = _start,		\
12762306a36Sopenharmony_ci		.npins = _nr,			\
12862306a36Sopenharmony_ci		.reg_mask = _mask,		\
12962306a36Sopenharmony_ci		.val = {0, _mask},		\
13062306a36Sopenharmony_ci		.funcs = {_func1, "gpio"}	\
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci#define PIN_GRP_GPIO_2(_name, _start, _nr, _mask, _val1, _val2, _func1)   \
13462306a36Sopenharmony_ci	{					\
13562306a36Sopenharmony_ci		.name = _name,			\
13662306a36Sopenharmony_ci		.start_pin = _start,		\
13762306a36Sopenharmony_ci		.npins = _nr,			\
13862306a36Sopenharmony_ci		.reg_mask = _mask,		\
13962306a36Sopenharmony_ci		.val = {_val1, _val2},		\
14062306a36Sopenharmony_ci		.funcs = {_func1, "gpio"}	\
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci#define PIN_GRP_GPIO_3(_name, _start, _nr, _mask, _v1, _v2, _v3, _f1, _f2) \
14462306a36Sopenharmony_ci	{					\
14562306a36Sopenharmony_ci		.name = _name,			\
14662306a36Sopenharmony_ci		.start_pin = _start,		\
14762306a36Sopenharmony_ci		.npins = _nr,			\
14862306a36Sopenharmony_ci		.reg_mask = _mask,		\
14962306a36Sopenharmony_ci		.val = {_v1, _v2, _v3},	\
15062306a36Sopenharmony_ci		.funcs = {_f1, _f2, "gpio"}	\
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci#define PIN_GRP_EXTRA(_name, _start, _nr, _mask, _v1, _v2, _start2, _nr2, \
15462306a36Sopenharmony_ci		      _f1, _f2)				\
15562306a36Sopenharmony_ci	{						\
15662306a36Sopenharmony_ci		.name = _name,				\
15762306a36Sopenharmony_ci		.start_pin = _start,			\
15862306a36Sopenharmony_ci		.npins = _nr,				\
15962306a36Sopenharmony_ci		.reg_mask = _mask,			\
16062306a36Sopenharmony_ci		.val = {_v1, _v2},			\
16162306a36Sopenharmony_ci		.extra_pin = _start2,			\
16262306a36Sopenharmony_ci		.extra_npins = _nr2,			\
16362306a36Sopenharmony_ci		.funcs = {_f1, _f2}			\
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic struct armada_37xx_pin_group armada_37xx_nb_groups[] = {
16762306a36Sopenharmony_ci	PIN_GRP_GPIO("jtag", 20, 5, BIT(0), "jtag"),
16862306a36Sopenharmony_ci	PIN_GRP_GPIO("sdio0", 8, 3, BIT(1), "sdio"),
16962306a36Sopenharmony_ci	PIN_GRP_GPIO("emmc_nb", 27, 9, BIT(2), "emmc"),
17062306a36Sopenharmony_ci	PIN_GRP_GPIO_3("pwm0", 11, 1, BIT(3) | BIT(20), 0, BIT(20), BIT(3),
17162306a36Sopenharmony_ci		       "pwm", "led"),
17262306a36Sopenharmony_ci	PIN_GRP_GPIO_3("pwm1", 12, 1, BIT(4) | BIT(21), 0, BIT(21), BIT(4),
17362306a36Sopenharmony_ci		       "pwm", "led"),
17462306a36Sopenharmony_ci	PIN_GRP_GPIO_3("pwm2", 13, 1, BIT(5) | BIT(22), 0, BIT(22), BIT(5),
17562306a36Sopenharmony_ci		       "pwm", "led"),
17662306a36Sopenharmony_ci	PIN_GRP_GPIO_3("pwm3", 14, 1, BIT(6) | BIT(23), 0, BIT(23), BIT(6),
17762306a36Sopenharmony_ci		       "pwm", "led"),
17862306a36Sopenharmony_ci	PIN_GRP_GPIO("pmic1", 7, 1, BIT(7), "pmic"),
17962306a36Sopenharmony_ci	PIN_GRP_GPIO("pmic0", 6, 1, BIT(8), "pmic"),
18062306a36Sopenharmony_ci	PIN_GRP_GPIO_0("gpio1_5", 5, 1),
18162306a36Sopenharmony_ci	PIN_GRP_GPIO("i2c2", 2, 2, BIT(9), "i2c"),
18262306a36Sopenharmony_ci	PIN_GRP_GPIO("i2c1", 0, 2, BIT(10), "i2c"),
18362306a36Sopenharmony_ci	PIN_GRP_GPIO("spi_cs1", 17, 1, BIT(12), "spi"),
18462306a36Sopenharmony_ci	PIN_GRP_GPIO_2("spi_cs2", 18, 1, BIT(13) | BIT(19), 0, BIT(13), "spi"),
18562306a36Sopenharmony_ci	PIN_GRP_GPIO_2("spi_cs3", 19, 1, BIT(14) | BIT(19), 0, BIT(14), "spi"),
18662306a36Sopenharmony_ci	PIN_GRP_GPIO("onewire", 4, 1, BIT(16), "onewire"),
18762306a36Sopenharmony_ci	PIN_GRP_GPIO("uart1", 25, 2, BIT(17), "uart"),
18862306a36Sopenharmony_ci	PIN_GRP_GPIO("spi_quad", 15, 2, BIT(18), "spi"),
18962306a36Sopenharmony_ci	PIN_GRP_EXTRA("uart2", 9, 2, BIT(1) | BIT(13) | BIT(14) | BIT(19),
19062306a36Sopenharmony_ci		      BIT(1) | BIT(13) | BIT(14), BIT(1) | BIT(19),
19162306a36Sopenharmony_ci		      18, 2, "gpio", "uart"),
19262306a36Sopenharmony_ci};
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic struct armada_37xx_pin_group armada_37xx_sb_groups[] = {
19562306a36Sopenharmony_ci	PIN_GRP_GPIO("usb32_drvvbus0", 0, 1, BIT(0), "drvbus"),
19662306a36Sopenharmony_ci	PIN_GRP_GPIO("usb2_drvvbus1", 1, 1, BIT(1), "drvbus"),
19762306a36Sopenharmony_ci	PIN_GRP_GPIO_0("gpio2_2", 2, 1),
19862306a36Sopenharmony_ci	PIN_GRP_GPIO("sdio_sb", 24, 6, BIT(2), "sdio"),
19962306a36Sopenharmony_ci	PIN_GRP_GPIO("rgmii", 6, 12, BIT(3), "mii"),
20062306a36Sopenharmony_ci	PIN_GRP_GPIO("smi", 18, 2, BIT(4), "smi"),
20162306a36Sopenharmony_ci	PIN_GRP_GPIO("pcie1", 3, 1, BIT(5), "pcie"), /* this actually controls "pcie1_reset" */
20262306a36Sopenharmony_ci	PIN_GRP_GPIO("pcie1_clkreq", 4, 1, BIT(9), "pcie"),
20362306a36Sopenharmony_ci	PIN_GRP_GPIO("pcie1_wakeup", 5, 1, BIT(10), "pcie"),
20462306a36Sopenharmony_ci	PIN_GRP_GPIO("ptp", 20, 1, BIT(11), "ptp"),
20562306a36Sopenharmony_ci	PIN_GRP_GPIO_3("ptp_clk", 21, 1, BIT(6) | BIT(12), 0, BIT(6), BIT(12),
20662306a36Sopenharmony_ci		       "ptp", "mii"),
20762306a36Sopenharmony_ci	PIN_GRP_GPIO_3("ptp_trig", 22, 1, BIT(7) | BIT(13), 0, BIT(7), BIT(13),
20862306a36Sopenharmony_ci		       "ptp", "mii"),
20962306a36Sopenharmony_ci	PIN_GRP_GPIO_3("mii_col", 23, 1, BIT(8) | BIT(14), 0, BIT(8), BIT(14),
21062306a36Sopenharmony_ci		       "mii", "mii_err"),
21162306a36Sopenharmony_ci};
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic const struct armada_37xx_pin_data armada_37xx_pin_nb = {
21462306a36Sopenharmony_ci	.nr_pins = 36,
21562306a36Sopenharmony_ci	.name = "GPIO1",
21662306a36Sopenharmony_ci	.groups = armada_37xx_nb_groups,
21762306a36Sopenharmony_ci	.ngroups = ARRAY_SIZE(armada_37xx_nb_groups),
21862306a36Sopenharmony_ci};
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic const struct armada_37xx_pin_data armada_37xx_pin_sb = {
22162306a36Sopenharmony_ci	.nr_pins = 30,
22262306a36Sopenharmony_ci	.name = "GPIO2",
22362306a36Sopenharmony_ci	.groups = armada_37xx_sb_groups,
22462306a36Sopenharmony_ci	.ngroups = ARRAY_SIZE(armada_37xx_sb_groups),
22562306a36Sopenharmony_ci};
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic inline void armada_37xx_update_reg(unsigned int *reg,
22862306a36Sopenharmony_ci					  unsigned int *offset)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	/* We never have more than 2 registers */
23162306a36Sopenharmony_ci	if (*offset >= GPIO_PER_REG) {
23262306a36Sopenharmony_ci		*offset -= GPIO_PER_REG;
23362306a36Sopenharmony_ci		*reg += sizeof(u32);
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic struct armada_37xx_pin_group *armada_37xx_find_next_grp_by_pin(
23862306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info, int pin, int *grp)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	while (*grp < info->ngroups) {
24162306a36Sopenharmony_ci		struct armada_37xx_pin_group *group = &info->groups[*grp];
24262306a36Sopenharmony_ci		int j;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci		*grp = *grp + 1;
24562306a36Sopenharmony_ci		for (j = 0; j < (group->npins + group->extra_npins); j++)
24662306a36Sopenharmony_ci			if (group->pins[j] == pin)
24762306a36Sopenharmony_ci				return group;
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci	return NULL;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic int armada_37xx_pin_config_group_get(struct pinctrl_dev *pctldev,
25362306a36Sopenharmony_ci			    unsigned int selector, unsigned long *config)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	return -ENOTSUPP;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic int armada_37xx_pin_config_group_set(struct pinctrl_dev *pctldev,
25962306a36Sopenharmony_ci			    unsigned int selector, unsigned long *configs,
26062306a36Sopenharmony_ci			    unsigned int num_configs)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	return -ENOTSUPP;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic const struct pinconf_ops armada_37xx_pinconf_ops = {
26662306a36Sopenharmony_ci	.is_generic = true,
26762306a36Sopenharmony_ci	.pin_config_group_get = armada_37xx_pin_config_group_get,
26862306a36Sopenharmony_ci	.pin_config_group_set = armada_37xx_pin_config_group_set,
26962306a36Sopenharmony_ci};
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic int armada_37xx_get_groups_count(struct pinctrl_dev *pctldev)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	return info->ngroups;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic const char *armada_37xx_get_group_name(struct pinctrl_dev *pctldev,
27962306a36Sopenharmony_ci					      unsigned int group)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	return info->groups[group].name;
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic int armada_37xx_get_group_pins(struct pinctrl_dev *pctldev,
28762306a36Sopenharmony_ci				      unsigned int selector,
28862306a36Sopenharmony_ci				      const unsigned int **pins,
28962306a36Sopenharmony_ci				      unsigned int *npins)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	if (selector >= info->ngroups)
29462306a36Sopenharmony_ci		return -EINVAL;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	*pins = info->groups[selector].pins;
29762306a36Sopenharmony_ci	*npins = info->groups[selector].npins +
29862306a36Sopenharmony_ci		info->groups[selector].extra_npins;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	return 0;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic const struct pinctrl_ops armada_37xx_pctrl_ops = {
30462306a36Sopenharmony_ci	.get_groups_count	= armada_37xx_get_groups_count,
30562306a36Sopenharmony_ci	.get_group_name		= armada_37xx_get_group_name,
30662306a36Sopenharmony_ci	.get_group_pins		= armada_37xx_get_group_pins,
30762306a36Sopenharmony_ci	.dt_node_to_map		= pinconf_generic_dt_node_to_map_group,
30862306a36Sopenharmony_ci	.dt_free_map		= pinctrl_utils_free_map,
30962306a36Sopenharmony_ci};
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci/*
31262306a36Sopenharmony_ci * Pinmux_ops handling
31362306a36Sopenharmony_ci */
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic int armada_37xx_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	return info->nfuncs;
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic const char *armada_37xx_pmx_get_func_name(struct pinctrl_dev *pctldev,
32362306a36Sopenharmony_ci						 unsigned int selector)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	return info->funcs[selector].name;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic int armada_37xx_pmx_get_groups(struct pinctrl_dev *pctldev,
33162306a36Sopenharmony_ci				      unsigned int selector,
33262306a36Sopenharmony_ci				      const char * const **groups,
33362306a36Sopenharmony_ci				      unsigned int * const num_groups)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	*groups = info->funcs[selector].groups;
33862306a36Sopenharmony_ci	*num_groups = info->funcs[selector].ngroups;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	return 0;
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic int armada_37xx_pmx_set_by_name(struct pinctrl_dev *pctldev,
34462306a36Sopenharmony_ci				       const char *name,
34562306a36Sopenharmony_ci				       struct armada_37xx_pin_group *grp)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
34862306a36Sopenharmony_ci	struct device *dev = info->dev;
34962306a36Sopenharmony_ci	unsigned int reg = SELECTION;
35062306a36Sopenharmony_ci	unsigned int mask = grp->reg_mask;
35162306a36Sopenharmony_ci	int func, val;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	dev_dbg(dev, "enable function %s group %s\n", name, grp->name);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	func = match_string(grp->funcs, NB_FUNCS, name);
35662306a36Sopenharmony_ci	if (func < 0)
35762306a36Sopenharmony_ci		return -ENOTSUPP;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	val = grp->val[func];
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	regmap_update_bits(info->regmap, reg, mask, val);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	return 0;
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic int armada_37xx_pmx_set(struct pinctrl_dev *pctldev,
36762306a36Sopenharmony_ci			       unsigned int selector,
36862306a36Sopenharmony_ci			       unsigned int group)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
37262306a36Sopenharmony_ci	struct armada_37xx_pin_group *grp = &info->groups[group];
37362306a36Sopenharmony_ci	const char *name = info->funcs[selector].name;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	return armada_37xx_pmx_set_by_name(pctldev, name, grp);
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_cistatic inline void armada_37xx_irq_update_reg(unsigned int *reg,
37962306a36Sopenharmony_ci					  struct irq_data *d)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	int offset = irqd_to_hwirq(d);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	armada_37xx_update_reg(reg, &offset);
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic int armada_37xx_gpio_direction_input(struct gpio_chip *chip,
38762306a36Sopenharmony_ci					    unsigned int offset)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = gpiochip_get_data(chip);
39062306a36Sopenharmony_ci	unsigned int reg = OUTPUT_EN;
39162306a36Sopenharmony_ci	unsigned int mask;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	armada_37xx_update_reg(&reg, &offset);
39462306a36Sopenharmony_ci	mask = BIT(offset);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	return regmap_update_bits(info->regmap, reg, mask, 0);
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic int armada_37xx_gpio_get_direction(struct gpio_chip *chip,
40062306a36Sopenharmony_ci					  unsigned int offset)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = gpiochip_get_data(chip);
40362306a36Sopenharmony_ci	unsigned int reg = OUTPUT_EN;
40462306a36Sopenharmony_ci	unsigned int val, mask;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	armada_37xx_update_reg(&reg, &offset);
40762306a36Sopenharmony_ci	mask = BIT(offset);
40862306a36Sopenharmony_ci	regmap_read(info->regmap, reg, &val);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	if (val & mask)
41162306a36Sopenharmony_ci		return GPIO_LINE_DIRECTION_OUT;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	return GPIO_LINE_DIRECTION_IN;
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_cistatic int armada_37xx_gpio_direction_output(struct gpio_chip *chip,
41762306a36Sopenharmony_ci					     unsigned int offset, int value)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = gpiochip_get_data(chip);
42062306a36Sopenharmony_ci	unsigned int reg = OUTPUT_EN;
42162306a36Sopenharmony_ci	unsigned int mask, val, ret;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	armada_37xx_update_reg(&reg, &offset);
42462306a36Sopenharmony_ci	mask = BIT(offset);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	ret = regmap_update_bits(info->regmap, reg, mask, mask);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (ret)
42962306a36Sopenharmony_ci		return ret;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	reg = OUTPUT_VAL;
43262306a36Sopenharmony_ci	val = value ? mask : 0;
43362306a36Sopenharmony_ci	regmap_update_bits(info->regmap, reg, mask, val);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	return 0;
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cistatic int armada_37xx_gpio_get(struct gpio_chip *chip, unsigned int offset)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = gpiochip_get_data(chip);
44162306a36Sopenharmony_ci	unsigned int reg = INPUT_VAL;
44262306a36Sopenharmony_ci	unsigned int val, mask;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	armada_37xx_update_reg(&reg, &offset);
44562306a36Sopenharmony_ci	mask = BIT(offset);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	regmap_read(info->regmap, reg, &val);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	return (val & mask) != 0;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic void armada_37xx_gpio_set(struct gpio_chip *chip, unsigned int offset,
45362306a36Sopenharmony_ci				 int value)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = gpiochip_get_data(chip);
45662306a36Sopenharmony_ci	unsigned int reg = OUTPUT_VAL;
45762306a36Sopenharmony_ci	unsigned int mask, val;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	armada_37xx_update_reg(&reg, &offset);
46062306a36Sopenharmony_ci	mask = BIT(offset);
46162306a36Sopenharmony_ci	val = value ? mask : 0;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	regmap_update_bits(info->regmap, reg, mask, val);
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic int armada_37xx_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
46762306a36Sopenharmony_ci					      struct pinctrl_gpio_range *range,
46862306a36Sopenharmony_ci					      unsigned int offset, bool input)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
47162306a36Sopenharmony_ci	struct gpio_chip *chip = range->gc;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	dev_dbg(info->dev, "gpio_direction for pin %u as %s-%d to %s\n",
47462306a36Sopenharmony_ci		offset, range->name, offset, input ? "input" : "output");
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	if (input)
47762306a36Sopenharmony_ci		armada_37xx_gpio_direction_input(chip, offset);
47862306a36Sopenharmony_ci	else
47962306a36Sopenharmony_ci		armada_37xx_gpio_direction_output(chip, offset, 0);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	return 0;
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic int armada_37xx_gpio_request_enable(struct pinctrl_dev *pctldev,
48562306a36Sopenharmony_ci					   struct pinctrl_gpio_range *range,
48662306a36Sopenharmony_ci					   unsigned int offset)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
48962306a36Sopenharmony_ci	struct armada_37xx_pin_group *group;
49062306a36Sopenharmony_ci	int grp = 0;
49162306a36Sopenharmony_ci	int ret;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	dev_dbg(info->dev, "requesting gpio %d\n", offset);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	while ((group = armada_37xx_find_next_grp_by_pin(info, offset, &grp))) {
49662306a36Sopenharmony_ci		ret = armada_37xx_pmx_set_by_name(pctldev, "gpio", group);
49762306a36Sopenharmony_ci		if (ret)
49862306a36Sopenharmony_ci			return ret;
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	return 0;
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic const struct pinmux_ops armada_37xx_pmx_ops = {
50562306a36Sopenharmony_ci	.get_functions_count	= armada_37xx_pmx_get_funcs_count,
50662306a36Sopenharmony_ci	.get_function_name	= armada_37xx_pmx_get_func_name,
50762306a36Sopenharmony_ci	.get_function_groups	= armada_37xx_pmx_get_groups,
50862306a36Sopenharmony_ci	.set_mux		= armada_37xx_pmx_set,
50962306a36Sopenharmony_ci	.gpio_request_enable	= armada_37xx_gpio_request_enable,
51062306a36Sopenharmony_ci	.gpio_set_direction	= armada_37xx_pmx_gpio_set_direction,
51162306a36Sopenharmony_ci};
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_cistatic const struct gpio_chip armada_37xx_gpiolib_chip = {
51462306a36Sopenharmony_ci	.request = gpiochip_generic_request,
51562306a36Sopenharmony_ci	.free = gpiochip_generic_free,
51662306a36Sopenharmony_ci	.set = armada_37xx_gpio_set,
51762306a36Sopenharmony_ci	.get = armada_37xx_gpio_get,
51862306a36Sopenharmony_ci	.get_direction	= armada_37xx_gpio_get_direction,
51962306a36Sopenharmony_ci	.direction_input = armada_37xx_gpio_direction_input,
52062306a36Sopenharmony_ci	.direction_output = armada_37xx_gpio_direction_output,
52162306a36Sopenharmony_ci	.owner = THIS_MODULE,
52262306a36Sopenharmony_ci};
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic void armada_37xx_irq_ack(struct irq_data *d)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
52762306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = gpiochip_get_data(chip);
52862306a36Sopenharmony_ci	u32 reg = IRQ_STATUS;
52962306a36Sopenharmony_ci	unsigned long flags;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	armada_37xx_irq_update_reg(&reg, d);
53262306a36Sopenharmony_ci	raw_spin_lock_irqsave(&info->irq_lock, flags);
53362306a36Sopenharmony_ci	writel(d->mask, info->base + reg);
53462306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&info->irq_lock, flags);
53562306a36Sopenharmony_ci}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_cistatic void armada_37xx_irq_mask(struct irq_data *d)
53862306a36Sopenharmony_ci{
53962306a36Sopenharmony_ci	struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
54062306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = gpiochip_get_data(chip);
54162306a36Sopenharmony_ci	u32 val, reg = IRQ_EN;
54262306a36Sopenharmony_ci	unsigned long flags;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	armada_37xx_irq_update_reg(&reg, d);
54562306a36Sopenharmony_ci	raw_spin_lock_irqsave(&info->irq_lock, flags);
54662306a36Sopenharmony_ci	val = readl(info->base + reg);
54762306a36Sopenharmony_ci	writel(val & ~d->mask, info->base + reg);
54862306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&info->irq_lock, flags);
54962306a36Sopenharmony_ci	gpiochip_disable_irq(chip, irqd_to_hwirq(d));
55062306a36Sopenharmony_ci}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_cistatic void armada_37xx_irq_unmask(struct irq_data *d)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
55562306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = gpiochip_get_data(chip);
55662306a36Sopenharmony_ci	u32 val, reg = IRQ_EN;
55762306a36Sopenharmony_ci	unsigned long flags;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	gpiochip_enable_irq(chip, irqd_to_hwirq(d));
56062306a36Sopenharmony_ci	armada_37xx_irq_update_reg(&reg, d);
56162306a36Sopenharmony_ci	raw_spin_lock_irqsave(&info->irq_lock, flags);
56262306a36Sopenharmony_ci	val = readl(info->base + reg);
56362306a36Sopenharmony_ci	writel(val | d->mask, info->base + reg);
56462306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&info->irq_lock, flags);
56562306a36Sopenharmony_ci}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cistatic int armada_37xx_irq_set_wake(struct irq_data *d, unsigned int on)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
57062306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = gpiochip_get_data(chip);
57162306a36Sopenharmony_ci	u32 val, reg = IRQ_WKUP;
57262306a36Sopenharmony_ci	unsigned long flags;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	armada_37xx_irq_update_reg(&reg, d);
57562306a36Sopenharmony_ci	raw_spin_lock_irqsave(&info->irq_lock, flags);
57662306a36Sopenharmony_ci	val = readl(info->base + reg);
57762306a36Sopenharmony_ci	if (on)
57862306a36Sopenharmony_ci		val |= (BIT(d->hwirq % GPIO_PER_REG));
57962306a36Sopenharmony_ci	else
58062306a36Sopenharmony_ci		val &= ~(BIT(d->hwirq % GPIO_PER_REG));
58162306a36Sopenharmony_ci	writel(val, info->base + reg);
58262306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&info->irq_lock, flags);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	return 0;
58562306a36Sopenharmony_ci}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_cistatic int armada_37xx_irq_set_type(struct irq_data *d, unsigned int type)
58862306a36Sopenharmony_ci{
58962306a36Sopenharmony_ci	struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
59062306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = gpiochip_get_data(chip);
59162306a36Sopenharmony_ci	u32 val, reg = IRQ_POL;
59262306a36Sopenharmony_ci	unsigned long flags;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	raw_spin_lock_irqsave(&info->irq_lock, flags);
59562306a36Sopenharmony_ci	armada_37xx_irq_update_reg(&reg, d);
59662306a36Sopenharmony_ci	val = readl(info->base + reg);
59762306a36Sopenharmony_ci	switch (type) {
59862306a36Sopenharmony_ci	case IRQ_TYPE_EDGE_RISING:
59962306a36Sopenharmony_ci		val &= ~(BIT(d->hwirq % GPIO_PER_REG));
60062306a36Sopenharmony_ci		break;
60162306a36Sopenharmony_ci	case IRQ_TYPE_EDGE_FALLING:
60262306a36Sopenharmony_ci		val |= (BIT(d->hwirq % GPIO_PER_REG));
60362306a36Sopenharmony_ci		break;
60462306a36Sopenharmony_ci	case IRQ_TYPE_EDGE_BOTH: {
60562306a36Sopenharmony_ci		u32 in_val, in_reg = INPUT_VAL;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci		armada_37xx_irq_update_reg(&in_reg, d);
60862306a36Sopenharmony_ci		regmap_read(info->regmap, in_reg, &in_val);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci		/* Set initial polarity based on current input level. */
61162306a36Sopenharmony_ci		if (in_val & BIT(d->hwirq % GPIO_PER_REG))
61262306a36Sopenharmony_ci			val |= BIT(d->hwirq % GPIO_PER_REG);	/* falling */
61362306a36Sopenharmony_ci		else
61462306a36Sopenharmony_ci			val &= ~(BIT(d->hwirq % GPIO_PER_REG));	/* rising */
61562306a36Sopenharmony_ci		break;
61662306a36Sopenharmony_ci	}
61762306a36Sopenharmony_ci	default:
61862306a36Sopenharmony_ci		raw_spin_unlock_irqrestore(&info->irq_lock, flags);
61962306a36Sopenharmony_ci		return -EINVAL;
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci	writel(val, info->base + reg);
62262306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&info->irq_lock, flags);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	return 0;
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_cistatic int armada_37xx_edge_both_irq_swap_pol(struct armada_37xx_pinctrl *info,
62862306a36Sopenharmony_ci					     u32 pin_idx)
62962306a36Sopenharmony_ci{
63062306a36Sopenharmony_ci	u32 reg_idx = pin_idx / GPIO_PER_REG;
63162306a36Sopenharmony_ci	u32 bit_num = pin_idx % GPIO_PER_REG;
63262306a36Sopenharmony_ci	u32 p, l, ret;
63362306a36Sopenharmony_ci	unsigned long flags;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	regmap_read(info->regmap, INPUT_VAL + 4*reg_idx, &l);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	raw_spin_lock_irqsave(&info->irq_lock, flags);
63862306a36Sopenharmony_ci	p = readl(info->base + IRQ_POL + 4 * reg_idx);
63962306a36Sopenharmony_ci	if ((p ^ l) & (1 << bit_num)) {
64062306a36Sopenharmony_ci		/*
64162306a36Sopenharmony_ci		 * For the gpios which are used for both-edge irqs, when their
64262306a36Sopenharmony_ci		 * interrupts happen, their input levels are changed,
64362306a36Sopenharmony_ci		 * yet their interrupt polarities are kept in old values, we
64462306a36Sopenharmony_ci		 * should synchronize their interrupt polarities; for example,
64562306a36Sopenharmony_ci		 * at first a gpio's input level is low and its interrupt
64662306a36Sopenharmony_ci		 * polarity control is "Detect rising edge", then the gpio has
64762306a36Sopenharmony_ci		 * a interrupt , its level turns to high, we should change its
64862306a36Sopenharmony_ci		 * polarity control to "Detect falling edge" correspondingly.
64962306a36Sopenharmony_ci		 */
65062306a36Sopenharmony_ci		p ^= 1 << bit_num;
65162306a36Sopenharmony_ci		writel(p, info->base + IRQ_POL + 4 * reg_idx);
65262306a36Sopenharmony_ci		ret = 0;
65362306a36Sopenharmony_ci	} else {
65462306a36Sopenharmony_ci		/* Spurious irq */
65562306a36Sopenharmony_ci		ret = -1;
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&info->irq_lock, flags);
65962306a36Sopenharmony_ci	return ret;
66062306a36Sopenharmony_ci}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cistatic void armada_37xx_irq_handler(struct irq_desc *desc)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	struct gpio_chip *gc = irq_desc_get_handler_data(desc);
66562306a36Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
66662306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = gpiochip_get_data(gc);
66762306a36Sopenharmony_ci	struct irq_domain *d = gc->irq.domain;
66862306a36Sopenharmony_ci	int i;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	chained_irq_enter(chip, desc);
67162306a36Sopenharmony_ci	for (i = 0; i <= d->revmap_size / GPIO_PER_REG; i++) {
67262306a36Sopenharmony_ci		u32 status;
67362306a36Sopenharmony_ci		unsigned long flags;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci		raw_spin_lock_irqsave(&info->irq_lock, flags);
67662306a36Sopenharmony_ci		status = readl_relaxed(info->base + IRQ_STATUS + 4 * i);
67762306a36Sopenharmony_ci		/* Manage only the interrupt that was enabled */
67862306a36Sopenharmony_ci		status &= readl_relaxed(info->base + IRQ_EN + 4 * i);
67962306a36Sopenharmony_ci		raw_spin_unlock_irqrestore(&info->irq_lock, flags);
68062306a36Sopenharmony_ci		while (status) {
68162306a36Sopenharmony_ci			u32 hwirq = ffs(status) - 1;
68262306a36Sopenharmony_ci			u32 virq = irq_find_mapping(d, hwirq +
68362306a36Sopenharmony_ci						     i * GPIO_PER_REG);
68462306a36Sopenharmony_ci			u32 t = irq_get_trigger_type(virq);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci			if ((t & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) {
68762306a36Sopenharmony_ci				/* Swap polarity (race with GPIO line) */
68862306a36Sopenharmony_ci				if (armada_37xx_edge_both_irq_swap_pol(info,
68962306a36Sopenharmony_ci					hwirq + i * GPIO_PER_REG)) {
69062306a36Sopenharmony_ci					/*
69162306a36Sopenharmony_ci					 * For spurious irq, which gpio level
69262306a36Sopenharmony_ci					 * is not as expected after incoming
69362306a36Sopenharmony_ci					 * edge, just ack the gpio irq.
69462306a36Sopenharmony_ci					 */
69562306a36Sopenharmony_ci					writel(1 << hwirq,
69662306a36Sopenharmony_ci					       info->base +
69762306a36Sopenharmony_ci					       IRQ_STATUS + 4 * i);
69862306a36Sopenharmony_ci					goto update_status;
69962306a36Sopenharmony_ci				}
70062306a36Sopenharmony_ci			}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci			generic_handle_irq(virq);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ciupdate_status:
70562306a36Sopenharmony_ci			/* Update status in case a new IRQ appears */
70662306a36Sopenharmony_ci			raw_spin_lock_irqsave(&info->irq_lock, flags);
70762306a36Sopenharmony_ci			status = readl_relaxed(info->base +
70862306a36Sopenharmony_ci					       IRQ_STATUS + 4 * i);
70962306a36Sopenharmony_ci			/* Manage only the interrupt that was enabled */
71062306a36Sopenharmony_ci			status &= readl_relaxed(info->base + IRQ_EN + 4 * i);
71162306a36Sopenharmony_ci			raw_spin_unlock_irqrestore(&info->irq_lock, flags);
71262306a36Sopenharmony_ci		}
71362306a36Sopenharmony_ci	}
71462306a36Sopenharmony_ci	chained_irq_exit(chip, desc);
71562306a36Sopenharmony_ci}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_cistatic unsigned int armada_37xx_irq_startup(struct irq_data *d)
71862306a36Sopenharmony_ci{
71962306a36Sopenharmony_ci	/*
72062306a36Sopenharmony_ci	 * The mask field is a "precomputed bitmask for accessing the
72162306a36Sopenharmony_ci	 * chip registers" which was introduced for the generic
72262306a36Sopenharmony_ci	 * irqchip framework. As we don't use this framework, we can
72362306a36Sopenharmony_ci	 * reuse this field for our own usage.
72462306a36Sopenharmony_ci	 */
72562306a36Sopenharmony_ci	d->mask = BIT(d->hwirq % GPIO_PER_REG);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	armada_37xx_irq_unmask(d);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	return 0;
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_cistatic void armada_37xx_irq_print_chip(struct irq_data *d, struct seq_file *p)
73362306a36Sopenharmony_ci{
73462306a36Sopenharmony_ci	struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
73562306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = gpiochip_get_data(chip);
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	seq_printf(p, info->data->name);
73862306a36Sopenharmony_ci}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_cistatic const struct irq_chip armada_37xx_irqchip = {
74162306a36Sopenharmony_ci	.irq_ack = armada_37xx_irq_ack,
74262306a36Sopenharmony_ci	.irq_mask = armada_37xx_irq_mask,
74362306a36Sopenharmony_ci	.irq_unmask = armada_37xx_irq_unmask,
74462306a36Sopenharmony_ci	.irq_set_wake = armada_37xx_irq_set_wake,
74562306a36Sopenharmony_ci	.irq_set_type = armada_37xx_irq_set_type,
74662306a36Sopenharmony_ci	.irq_startup = armada_37xx_irq_startup,
74762306a36Sopenharmony_ci	.irq_print_chip = armada_37xx_irq_print_chip,
74862306a36Sopenharmony_ci	.flags = IRQCHIP_IMMUTABLE,
74962306a36Sopenharmony_ci	GPIOCHIP_IRQ_RESOURCE_HELPERS,
75062306a36Sopenharmony_ci};
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_cistatic int armada_37xx_irqchip_register(struct platform_device *pdev,
75362306a36Sopenharmony_ci					struct armada_37xx_pinctrl *info)
75462306a36Sopenharmony_ci{
75562306a36Sopenharmony_ci	struct gpio_chip *gc = &info->gpio_chip;
75662306a36Sopenharmony_ci	struct gpio_irq_chip *girq = &gc->irq;
75762306a36Sopenharmony_ci	struct device_node *np = to_of_node(gc->fwnode);
75862306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
75962306a36Sopenharmony_ci	unsigned int i, nr_irq_parent;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	raw_spin_lock_init(&info->irq_lock);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	nr_irq_parent = of_irq_count(np);
76462306a36Sopenharmony_ci	if (!nr_irq_parent) {
76562306a36Sopenharmony_ci		dev_err(dev, "invalid or no IRQ\n");
76662306a36Sopenharmony_ci		return 0;
76762306a36Sopenharmony_ci	}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	info->base = devm_platform_ioremap_resource(pdev, 1);
77062306a36Sopenharmony_ci	if (IS_ERR(info->base))
77162306a36Sopenharmony_ci		return PTR_ERR(info->base);
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	gpio_irq_chip_set_chip(girq, &armada_37xx_irqchip);
77462306a36Sopenharmony_ci	girq->parent_handler = armada_37xx_irq_handler;
77562306a36Sopenharmony_ci	/*
77662306a36Sopenharmony_ci	 * Many interrupts are connected to the parent interrupt
77762306a36Sopenharmony_ci	 * controller. But we do not take advantage of this and use
77862306a36Sopenharmony_ci	 * the chained irq with all of them.
77962306a36Sopenharmony_ci	 */
78062306a36Sopenharmony_ci	girq->num_parents = nr_irq_parent;
78162306a36Sopenharmony_ci	girq->parents = devm_kcalloc(dev, nr_irq_parent, sizeof(*girq->parents), GFP_KERNEL);
78262306a36Sopenharmony_ci	if (!girq->parents)
78362306a36Sopenharmony_ci		return -ENOMEM;
78462306a36Sopenharmony_ci	for (i = 0; i < nr_irq_parent; i++) {
78562306a36Sopenharmony_ci		int irq = irq_of_parse_and_map(np, i);
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci		if (!irq)
78862306a36Sopenharmony_ci			continue;
78962306a36Sopenharmony_ci		girq->parents[i] = irq;
79062306a36Sopenharmony_ci	}
79162306a36Sopenharmony_ci	girq->default_type = IRQ_TYPE_NONE;
79262306a36Sopenharmony_ci	girq->handler = handle_edge_irq;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	return 0;
79562306a36Sopenharmony_ci}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_cistatic int armada_37xx_gpiochip_register(struct platform_device *pdev,
79862306a36Sopenharmony_ci					struct armada_37xx_pinctrl *info)
79962306a36Sopenharmony_ci{
80062306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
80162306a36Sopenharmony_ci	struct fwnode_handle *fwnode;
80262306a36Sopenharmony_ci	struct gpio_chip *gc;
80362306a36Sopenharmony_ci	int ret;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	fwnode = gpiochip_node_get_first(dev);
80662306a36Sopenharmony_ci	if (!fwnode)
80762306a36Sopenharmony_ci		return -ENODEV;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	info->gpio_chip = armada_37xx_gpiolib_chip;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	gc = &info->gpio_chip;
81262306a36Sopenharmony_ci	gc->ngpio = info->data->nr_pins;
81362306a36Sopenharmony_ci	gc->parent = dev;
81462306a36Sopenharmony_ci	gc->base = -1;
81562306a36Sopenharmony_ci	gc->fwnode = fwnode;
81662306a36Sopenharmony_ci	gc->label = info->data->name;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	ret = armada_37xx_irqchip_register(pdev, info);
81962306a36Sopenharmony_ci	if (ret)
82062306a36Sopenharmony_ci		return ret;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	return devm_gpiochip_add_data(dev, gc, info);
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci/**
82662306a36Sopenharmony_ci * armada_37xx_add_function() - Add a new function to the list
82762306a36Sopenharmony_ci * @funcs: array of function to add the new one
82862306a36Sopenharmony_ci * @funcsize: size of the remaining space for the function
82962306a36Sopenharmony_ci * @name: name of the function to add
83062306a36Sopenharmony_ci *
83162306a36Sopenharmony_ci * If it is a new function then create it by adding its name else
83262306a36Sopenharmony_ci * increment the number of group associated to this function.
83362306a36Sopenharmony_ci */
83462306a36Sopenharmony_cistatic int armada_37xx_add_function(struct armada_37xx_pmx_func *funcs,
83562306a36Sopenharmony_ci				    int *funcsize, const char *name)
83662306a36Sopenharmony_ci{
83762306a36Sopenharmony_ci	int i = 0;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	if (*funcsize <= 0)
84062306a36Sopenharmony_ci		return -EOVERFLOW;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	while (funcs->ngroups) {
84362306a36Sopenharmony_ci		/* function already there */
84462306a36Sopenharmony_ci		if (strcmp(funcs->name, name) == 0) {
84562306a36Sopenharmony_ci			funcs->ngroups++;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci			return -EEXIST;
84862306a36Sopenharmony_ci		}
84962306a36Sopenharmony_ci		funcs++;
85062306a36Sopenharmony_ci		i++;
85162306a36Sopenharmony_ci	}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	/* append new unique function */
85462306a36Sopenharmony_ci	funcs->name = name;
85562306a36Sopenharmony_ci	funcs->ngroups = 1;
85662306a36Sopenharmony_ci	(*funcsize)--;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	return 0;
85962306a36Sopenharmony_ci}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci/**
86262306a36Sopenharmony_ci * armada_37xx_fill_group() - complete the group array
86362306a36Sopenharmony_ci * @info: info driver instance
86462306a36Sopenharmony_ci *
86562306a36Sopenharmony_ci * Based on the data available from the armada_37xx_pin_group array
86662306a36Sopenharmony_ci * completes the last member of the struct for each function: the list
86762306a36Sopenharmony_ci * of the groups associated to this function.
86862306a36Sopenharmony_ci *
86962306a36Sopenharmony_ci */
87062306a36Sopenharmony_cistatic int armada_37xx_fill_group(struct armada_37xx_pinctrl *info)
87162306a36Sopenharmony_ci{
87262306a36Sopenharmony_ci	int n, num = 0, funcsize = info->data->nr_pins;
87362306a36Sopenharmony_ci	struct device *dev = info->dev;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	for (n = 0; n < info->ngroups; n++) {
87662306a36Sopenharmony_ci		struct armada_37xx_pin_group *grp = &info->groups[n];
87762306a36Sopenharmony_ci		int i, j, f;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci		grp->pins = devm_kcalloc(dev, grp->npins + grp->extra_npins,
88062306a36Sopenharmony_ci					 sizeof(*grp->pins),
88162306a36Sopenharmony_ci					 GFP_KERNEL);
88262306a36Sopenharmony_ci		if (!grp->pins)
88362306a36Sopenharmony_ci			return -ENOMEM;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci		for (i = 0; i < grp->npins; i++)
88662306a36Sopenharmony_ci			grp->pins[i] = grp->start_pin + i;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci		for (j = 0; j < grp->extra_npins; j++)
88962306a36Sopenharmony_ci			grp->pins[i+j] = grp->extra_pin + j;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci		for (f = 0; (f < NB_FUNCS) && grp->funcs[f]; f++) {
89262306a36Sopenharmony_ci			int ret;
89362306a36Sopenharmony_ci			/* check for unique functions and count groups */
89462306a36Sopenharmony_ci			ret = armada_37xx_add_function(info->funcs, &funcsize,
89562306a36Sopenharmony_ci					    grp->funcs[f]);
89662306a36Sopenharmony_ci			if (ret == -EOVERFLOW)
89762306a36Sopenharmony_ci				dev_err(dev, "More functions than pins(%d)\n",
89862306a36Sopenharmony_ci					info->data->nr_pins);
89962306a36Sopenharmony_ci			if (ret < 0)
90062306a36Sopenharmony_ci				continue;
90162306a36Sopenharmony_ci			num++;
90262306a36Sopenharmony_ci		}
90362306a36Sopenharmony_ci	}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	info->nfuncs = num;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	return 0;
90862306a36Sopenharmony_ci}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci/**
91162306a36Sopenharmony_ci * armada_37xx_fill_func() - complete the funcs array
91262306a36Sopenharmony_ci * @info: info driver instance
91362306a36Sopenharmony_ci *
91462306a36Sopenharmony_ci * Based on the data available from the armada_37xx_pin_group array
91562306a36Sopenharmony_ci * completes the last two member of the struct for each group:
91662306a36Sopenharmony_ci * - the list of the pins included in the group
91762306a36Sopenharmony_ci * - the list of pinmux functions that can be selected for this group
91862306a36Sopenharmony_ci *
91962306a36Sopenharmony_ci */
92062306a36Sopenharmony_cistatic int armada_37xx_fill_func(struct armada_37xx_pinctrl *info)
92162306a36Sopenharmony_ci{
92262306a36Sopenharmony_ci	struct armada_37xx_pmx_func *funcs = info->funcs;
92362306a36Sopenharmony_ci	struct device *dev = info->dev;
92462306a36Sopenharmony_ci	int n;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	for (n = 0; n < info->nfuncs; n++) {
92762306a36Sopenharmony_ci		const char *name = funcs[n].name;
92862306a36Sopenharmony_ci		const char **groups;
92962306a36Sopenharmony_ci		int g;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci		funcs[n].groups = devm_kcalloc(dev, funcs[n].ngroups,
93262306a36Sopenharmony_ci					       sizeof(*(funcs[n].groups)),
93362306a36Sopenharmony_ci					       GFP_KERNEL);
93462306a36Sopenharmony_ci		if (!funcs[n].groups)
93562306a36Sopenharmony_ci			return -ENOMEM;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci		groups = funcs[n].groups;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci		for (g = 0; g < info->ngroups; g++) {
94062306a36Sopenharmony_ci			struct armada_37xx_pin_group *gp = &info->groups[g];
94162306a36Sopenharmony_ci			int f;
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci			f = match_string(gp->funcs, NB_FUNCS, name);
94462306a36Sopenharmony_ci			if (f < 0)
94562306a36Sopenharmony_ci				continue;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci			*groups = gp->name;
94862306a36Sopenharmony_ci			groups++;
94962306a36Sopenharmony_ci		}
95062306a36Sopenharmony_ci	}
95162306a36Sopenharmony_ci	return 0;
95262306a36Sopenharmony_ci}
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_cistatic int armada_37xx_pinctrl_register(struct platform_device *pdev,
95562306a36Sopenharmony_ci					struct armada_37xx_pinctrl *info)
95662306a36Sopenharmony_ci{
95762306a36Sopenharmony_ci	const struct armada_37xx_pin_data *pin_data = info->data;
95862306a36Sopenharmony_ci	struct pinctrl_desc *ctrldesc = &info->pctl;
95962306a36Sopenharmony_ci	struct pinctrl_pin_desc *pindesc, *pdesc;
96062306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
96162306a36Sopenharmony_ci	char **pin_names;
96262306a36Sopenharmony_ci	int pin, ret;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	info->groups = pin_data->groups;
96562306a36Sopenharmony_ci	info->ngroups = pin_data->ngroups;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	ctrldesc->name = "armada_37xx-pinctrl";
96862306a36Sopenharmony_ci	ctrldesc->owner = THIS_MODULE;
96962306a36Sopenharmony_ci	ctrldesc->pctlops = &armada_37xx_pctrl_ops;
97062306a36Sopenharmony_ci	ctrldesc->pmxops = &armada_37xx_pmx_ops;
97162306a36Sopenharmony_ci	ctrldesc->confops = &armada_37xx_pinconf_ops;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	pindesc = devm_kcalloc(dev, pin_data->nr_pins, sizeof(*pindesc), GFP_KERNEL);
97462306a36Sopenharmony_ci	if (!pindesc)
97562306a36Sopenharmony_ci		return -ENOMEM;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	ctrldesc->pins = pindesc;
97862306a36Sopenharmony_ci	ctrldesc->npins = pin_data->nr_pins;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	pin_names = devm_kasprintf_strarray(dev, pin_data->name, pin_data->nr_pins);
98162306a36Sopenharmony_ci	if (IS_ERR(pin_names))
98262306a36Sopenharmony_ci		return PTR_ERR(pin_names);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	pdesc = pindesc;
98562306a36Sopenharmony_ci	for (pin = 0; pin < pin_data->nr_pins; pin++) {
98662306a36Sopenharmony_ci		pdesc->number = pin;
98762306a36Sopenharmony_ci		pdesc->name = pin_names[pin];
98862306a36Sopenharmony_ci		pdesc++;
98962306a36Sopenharmony_ci	}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	/*
99262306a36Sopenharmony_ci	 * we allocate functions for number of pins and hope there are
99362306a36Sopenharmony_ci	 * fewer unique functions than pins available
99462306a36Sopenharmony_ci	 */
99562306a36Sopenharmony_ci	info->funcs = devm_kcalloc(dev, pin_data->nr_pins, sizeof(*info->funcs), GFP_KERNEL);
99662306a36Sopenharmony_ci	if (!info->funcs)
99762306a36Sopenharmony_ci		return -ENOMEM;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	ret = armada_37xx_fill_group(info);
100062306a36Sopenharmony_ci	if (ret)
100162306a36Sopenharmony_ci		return ret;
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	ret = armada_37xx_fill_func(info);
100462306a36Sopenharmony_ci	if (ret)
100562306a36Sopenharmony_ci		return ret;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	info->pctl_dev = devm_pinctrl_register(dev, ctrldesc, info);
100862306a36Sopenharmony_ci	if (IS_ERR(info->pctl_dev))
100962306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(info->pctl_dev), "could not register pinctrl driver\n");
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	return 0;
101262306a36Sopenharmony_ci}
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_cistatic int armada_3700_pinctrl_suspend(struct device *dev)
101562306a36Sopenharmony_ci{
101662306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = dev_get_drvdata(dev);
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	/* Save GPIO state */
101962306a36Sopenharmony_ci	regmap_read(info->regmap, OUTPUT_EN, &info->pm.out_en_l);
102062306a36Sopenharmony_ci	regmap_read(info->regmap, OUTPUT_EN + sizeof(u32), &info->pm.out_en_h);
102162306a36Sopenharmony_ci	regmap_read(info->regmap, OUTPUT_VAL, &info->pm.out_val_l);
102262306a36Sopenharmony_ci	regmap_read(info->regmap, OUTPUT_VAL + sizeof(u32),
102362306a36Sopenharmony_ci		    &info->pm.out_val_h);
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	info->pm.irq_en_l = readl(info->base + IRQ_EN);
102662306a36Sopenharmony_ci	info->pm.irq_en_h = readl(info->base + IRQ_EN + sizeof(u32));
102762306a36Sopenharmony_ci	info->pm.irq_pol_l = readl(info->base + IRQ_POL);
102862306a36Sopenharmony_ci	info->pm.irq_pol_h = readl(info->base + IRQ_POL + sizeof(u32));
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	/* Save pinctrl state */
103162306a36Sopenharmony_ci	regmap_read(info->regmap, SELECTION, &info->pm.selection);
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	return 0;
103462306a36Sopenharmony_ci}
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_cistatic int armada_3700_pinctrl_resume(struct device *dev)
103762306a36Sopenharmony_ci{
103862306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info = dev_get_drvdata(dev);
103962306a36Sopenharmony_ci	struct gpio_chip *gc;
104062306a36Sopenharmony_ci	struct irq_domain *d;
104162306a36Sopenharmony_ci	int i;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	/* Restore GPIO state */
104462306a36Sopenharmony_ci	regmap_write(info->regmap, OUTPUT_EN, info->pm.out_en_l);
104562306a36Sopenharmony_ci	regmap_write(info->regmap, OUTPUT_EN + sizeof(u32),
104662306a36Sopenharmony_ci		     info->pm.out_en_h);
104762306a36Sopenharmony_ci	regmap_write(info->regmap, OUTPUT_VAL, info->pm.out_val_l);
104862306a36Sopenharmony_ci	regmap_write(info->regmap, OUTPUT_VAL + sizeof(u32),
104962306a36Sopenharmony_ci		     info->pm.out_val_h);
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	/*
105262306a36Sopenharmony_ci	 * Input levels may change during suspend, which is not monitored at
105362306a36Sopenharmony_ci	 * that time. GPIOs used for both-edge IRQs may not be synchronized
105462306a36Sopenharmony_ci	 * anymore with their polarities (rising/falling edge) and must be
105562306a36Sopenharmony_ci	 * re-configured manually.
105662306a36Sopenharmony_ci	 */
105762306a36Sopenharmony_ci	gc = &info->gpio_chip;
105862306a36Sopenharmony_ci	d = gc->irq.domain;
105962306a36Sopenharmony_ci	for (i = 0; i < gc->ngpio; i++) {
106062306a36Sopenharmony_ci		u32 irq_bit = BIT(i % GPIO_PER_REG);
106162306a36Sopenharmony_ci		u32 mask, *irq_pol, input_reg, virq, type, level;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci		if (i < GPIO_PER_REG) {
106462306a36Sopenharmony_ci			mask = info->pm.irq_en_l;
106562306a36Sopenharmony_ci			irq_pol = &info->pm.irq_pol_l;
106662306a36Sopenharmony_ci			input_reg = INPUT_VAL;
106762306a36Sopenharmony_ci		} else {
106862306a36Sopenharmony_ci			mask = info->pm.irq_en_h;
106962306a36Sopenharmony_ci			irq_pol = &info->pm.irq_pol_h;
107062306a36Sopenharmony_ci			input_reg = INPUT_VAL + sizeof(u32);
107162306a36Sopenharmony_ci		}
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci		if (!(mask & irq_bit))
107462306a36Sopenharmony_ci			continue;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci		virq = irq_find_mapping(d, i);
107762306a36Sopenharmony_ci		type = irq_get_trigger_type(virq);
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci		/*
108062306a36Sopenharmony_ci		 * Synchronize level and polarity for both-edge irqs:
108162306a36Sopenharmony_ci		 *     - a high input level expects a falling edge,
108262306a36Sopenharmony_ci		 *     - a low input level exepects a rising edge.
108362306a36Sopenharmony_ci		 */
108462306a36Sopenharmony_ci		if ((type & IRQ_TYPE_SENSE_MASK) ==
108562306a36Sopenharmony_ci		    IRQ_TYPE_EDGE_BOTH) {
108662306a36Sopenharmony_ci			regmap_read(info->regmap, input_reg, &level);
108762306a36Sopenharmony_ci			if ((*irq_pol ^ level) & irq_bit)
108862306a36Sopenharmony_ci				*irq_pol ^= irq_bit;
108962306a36Sopenharmony_ci		}
109062306a36Sopenharmony_ci	}
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	writel(info->pm.irq_en_l, info->base + IRQ_EN);
109362306a36Sopenharmony_ci	writel(info->pm.irq_en_h, info->base + IRQ_EN + sizeof(u32));
109462306a36Sopenharmony_ci	writel(info->pm.irq_pol_l, info->base + IRQ_POL);
109562306a36Sopenharmony_ci	writel(info->pm.irq_pol_h, info->base + IRQ_POL + sizeof(u32));
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	/* Restore pinctrl state */
109862306a36Sopenharmony_ci	regmap_write(info->regmap, SELECTION, info->pm.selection);
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	return 0;
110162306a36Sopenharmony_ci}
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci/*
110462306a36Sopenharmony_ci * Since pinctrl is an infrastructure module, its resume should be issued prior
110562306a36Sopenharmony_ci * to other IO drivers.
110662306a36Sopenharmony_ci */
110762306a36Sopenharmony_cistatic DEFINE_NOIRQ_DEV_PM_OPS(armada_3700_pinctrl_pm_ops,
110862306a36Sopenharmony_ci			       armada_3700_pinctrl_suspend, armada_3700_pinctrl_resume);
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_cistatic const struct of_device_id armada_37xx_pinctrl_of_match[] = {
111162306a36Sopenharmony_ci	{
111262306a36Sopenharmony_ci		.compatible = "marvell,armada3710-sb-pinctrl",
111362306a36Sopenharmony_ci		.data = &armada_37xx_pin_sb,
111462306a36Sopenharmony_ci	},
111562306a36Sopenharmony_ci	{
111662306a36Sopenharmony_ci		.compatible = "marvell,armada3710-nb-pinctrl",
111762306a36Sopenharmony_ci		.data = &armada_37xx_pin_nb,
111862306a36Sopenharmony_ci	},
111962306a36Sopenharmony_ci	{ },
112062306a36Sopenharmony_ci};
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_cistatic const struct regmap_config armada_37xx_pinctrl_regmap_config = {
112362306a36Sopenharmony_ci	.reg_bits = 32,
112462306a36Sopenharmony_ci	.val_bits = 32,
112562306a36Sopenharmony_ci	.reg_stride = 4,
112662306a36Sopenharmony_ci	.use_raw_spinlock = true,
112762306a36Sopenharmony_ci};
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_cistatic int __init armada_37xx_pinctrl_probe(struct platform_device *pdev)
113062306a36Sopenharmony_ci{
113162306a36Sopenharmony_ci	struct armada_37xx_pinctrl *info;
113262306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
113362306a36Sopenharmony_ci	struct regmap *regmap;
113462306a36Sopenharmony_ci	void __iomem *base;
113562306a36Sopenharmony_ci	int ret;
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
113862306a36Sopenharmony_ci	if (IS_ERR(base)) {
113962306a36Sopenharmony_ci		dev_err(dev, "failed to ioremap base address: %pe\n", base);
114062306a36Sopenharmony_ci		return PTR_ERR(base);
114162306a36Sopenharmony_ci	}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	regmap = devm_regmap_init_mmio(dev, base,
114462306a36Sopenharmony_ci				       &armada_37xx_pinctrl_regmap_config);
114562306a36Sopenharmony_ci	if (IS_ERR(regmap)) {
114662306a36Sopenharmony_ci		dev_err(dev, "failed to create regmap: %pe\n", regmap);
114762306a36Sopenharmony_ci		return PTR_ERR(regmap);
114862306a36Sopenharmony_ci	}
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
115162306a36Sopenharmony_ci	if (!info)
115262306a36Sopenharmony_ci		return -ENOMEM;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	info->dev = dev;
115562306a36Sopenharmony_ci	info->regmap = regmap;
115662306a36Sopenharmony_ci	info->data = of_device_get_match_data(dev);
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	ret = armada_37xx_pinctrl_register(pdev, info);
115962306a36Sopenharmony_ci	if (ret)
116062306a36Sopenharmony_ci		return ret;
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	ret = armada_37xx_gpiochip_register(pdev, info);
116362306a36Sopenharmony_ci	if (ret)
116462306a36Sopenharmony_ci		return ret;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	platform_set_drvdata(pdev, info);
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	return 0;
116962306a36Sopenharmony_ci}
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_cistatic struct platform_driver armada_37xx_pinctrl_driver = {
117262306a36Sopenharmony_ci	.driver = {
117362306a36Sopenharmony_ci		.name = "armada-37xx-pinctrl",
117462306a36Sopenharmony_ci		.of_match_table = armada_37xx_pinctrl_of_match,
117562306a36Sopenharmony_ci		.pm = pm_sleep_ptr(&armada_3700_pinctrl_pm_ops),
117662306a36Sopenharmony_ci	},
117762306a36Sopenharmony_ci};
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_cibuiltin_platform_driver_probe(armada_37xx_pinctrl_driver,
118062306a36Sopenharmony_ci			      armada_37xx_pinctrl_probe);
1181