xref: /linux/drivers/pinctrl/pinctrl-stmfx.c (revision 0227b49b50276657243e54f5609e65c4f0eaaf4d)
11490d9f8SAmelie Delaunay // SPDX-License-Identifier: GPL-2.0
21490d9f8SAmelie Delaunay /*
31490d9f8SAmelie Delaunay  * Driver for STMicroelectronics Multi-Function eXpander (STMFX) GPIO expander
41490d9f8SAmelie Delaunay  *
51490d9f8SAmelie Delaunay  * Copyright (C) 2019 STMicroelectronics
61490d9f8SAmelie Delaunay  * Author(s): Amelie Delaunay <amelie.delaunay@st.com>.
71490d9f8SAmelie Delaunay  */
81490d9f8SAmelie Delaunay #include <linux/gpio/driver.h>
91490d9f8SAmelie Delaunay #include <linux/interrupt.h>
101490d9f8SAmelie Delaunay #include <linux/mfd/stmfx.h>
111490d9f8SAmelie Delaunay #include <linux/module.h>
121490d9f8SAmelie Delaunay #include <linux/platform_device.h>
138f27fb48SAndy Shevchenko #include <linux/seq_file.h>
14f33254dbSAndy Shevchenko #include <linux/string_choices.h>
158f27fb48SAndy Shevchenko 
161490d9f8SAmelie Delaunay #include <linux/pinctrl/pinconf.h>
171490d9f8SAmelie Delaunay #include <linux/pinctrl/pinmux.h>
181490d9f8SAmelie Delaunay 
191490d9f8SAmelie Delaunay #include "core.h"
201490d9f8SAmelie Delaunay #include "pinctrl-utils.h"
211490d9f8SAmelie Delaunay 
221490d9f8SAmelie Delaunay /* GPIOs expander */
231490d9f8SAmelie Delaunay /* GPIO_STATE1 0x10, GPIO_STATE2 0x11, GPIO_STATE3 0x12 */
241490d9f8SAmelie Delaunay #define STMFX_REG_GPIO_STATE		STMFX_REG_GPIO_STATE1 /* R */
251490d9f8SAmelie Delaunay /* GPIO_DIR1 0x60, GPIO_DIR2 0x61, GPIO_DIR3 0x63 */
261490d9f8SAmelie Delaunay #define STMFX_REG_GPIO_DIR		STMFX_REG_GPIO_DIR1 /* RW */
271490d9f8SAmelie Delaunay /* GPIO_TYPE1 0x64, GPIO_TYPE2 0x65, GPIO_TYPE3 0x66 */
281490d9f8SAmelie Delaunay #define STMFX_REG_GPIO_TYPE		STMFX_REG_GPIO_TYPE1 /* RW */
291490d9f8SAmelie Delaunay /* GPIO_PUPD1 0x68, GPIO_PUPD2 0x69, GPIO_PUPD3 0x6A */
301490d9f8SAmelie Delaunay #define STMFX_REG_GPIO_PUPD		STMFX_REG_GPIO_PUPD1 /* RW */
311490d9f8SAmelie Delaunay /* GPO_SET1 0x6C, GPO_SET2 0x6D, GPO_SET3 0x6E */
321490d9f8SAmelie Delaunay #define STMFX_REG_GPO_SET		STMFX_REG_GPO_SET1 /* RW */
331490d9f8SAmelie Delaunay /* GPO_CLR1 0x70, GPO_CLR2 0x71, GPO_CLR3 0x72 */
341490d9f8SAmelie Delaunay #define STMFX_REG_GPO_CLR		STMFX_REG_GPO_CLR1 /* RW */
351490d9f8SAmelie Delaunay /* IRQ_GPI_SRC1 0x48, IRQ_GPI_SRC2 0x49, IRQ_GPI_SRC3 0x4A */
361490d9f8SAmelie Delaunay #define STMFX_REG_IRQ_GPI_SRC		STMFX_REG_IRQ_GPI_SRC1 /* RW */
371490d9f8SAmelie Delaunay /* IRQ_GPI_EVT1 0x4C, IRQ_GPI_EVT2 0x4D, IRQ_GPI_EVT3 0x4E */
381490d9f8SAmelie Delaunay #define STMFX_REG_IRQ_GPI_EVT		STMFX_REG_IRQ_GPI_EVT1 /* RW */
391490d9f8SAmelie Delaunay /* IRQ_GPI_TYPE1 0x50, IRQ_GPI_TYPE2 0x51, IRQ_GPI_TYPE3 0x52 */
401490d9f8SAmelie Delaunay #define STMFX_REG_IRQ_GPI_TYPE		STMFX_REG_IRQ_GPI_TYPE1 /* RW */
411490d9f8SAmelie Delaunay /* IRQ_GPI_PENDING1 0x0C, IRQ_GPI_PENDING2 0x0D, IRQ_GPI_PENDING3 0x0E*/
421490d9f8SAmelie Delaunay #define STMFX_REG_IRQ_GPI_PENDING	STMFX_REG_IRQ_GPI_PENDING1 /* R */
431490d9f8SAmelie Delaunay /* IRQ_GPI_ACK1 0x54, IRQ_GPI_ACK2 0x55, IRQ_GPI_ACK3 0x56 */
441490d9f8SAmelie Delaunay #define STMFX_REG_IRQ_GPI_ACK		STMFX_REG_IRQ_GPI_ACK1 /* RW */
451490d9f8SAmelie Delaunay 
461490d9f8SAmelie Delaunay #define NR_GPIO_REGS			3
471490d9f8SAmelie Delaunay #define NR_GPIOS_PER_REG		8
481490d9f8SAmelie Delaunay #define get_reg(offset)			((offset) / NR_GPIOS_PER_REG)
491490d9f8SAmelie Delaunay #define get_shift(offset)		((offset) % NR_GPIOS_PER_REG)
501490d9f8SAmelie Delaunay #define get_mask(offset)		(BIT(get_shift(offset)))
511490d9f8SAmelie Delaunay 
521490d9f8SAmelie Delaunay /*
531490d9f8SAmelie Delaunay  * STMFX pinctrl can have up to 24 pins if STMFX other functions are not used.
541490d9f8SAmelie Delaunay  * Pins availability is managed thanks to gpio-ranges property.
551490d9f8SAmelie Delaunay  */
561490d9f8SAmelie Delaunay static const struct pinctrl_pin_desc stmfx_pins[] = {
571490d9f8SAmelie Delaunay 	PINCTRL_PIN(0, "gpio0"),
581490d9f8SAmelie Delaunay 	PINCTRL_PIN(1, "gpio1"),
591490d9f8SAmelie Delaunay 	PINCTRL_PIN(2, "gpio2"),
601490d9f8SAmelie Delaunay 	PINCTRL_PIN(3, "gpio3"),
611490d9f8SAmelie Delaunay 	PINCTRL_PIN(4, "gpio4"),
621490d9f8SAmelie Delaunay 	PINCTRL_PIN(5, "gpio5"),
631490d9f8SAmelie Delaunay 	PINCTRL_PIN(6, "gpio6"),
641490d9f8SAmelie Delaunay 	PINCTRL_PIN(7, "gpio7"),
651490d9f8SAmelie Delaunay 	PINCTRL_PIN(8, "gpio8"),
661490d9f8SAmelie Delaunay 	PINCTRL_PIN(9, "gpio9"),
671490d9f8SAmelie Delaunay 	PINCTRL_PIN(10, "gpio10"),
681490d9f8SAmelie Delaunay 	PINCTRL_PIN(11, "gpio11"),
691490d9f8SAmelie Delaunay 	PINCTRL_PIN(12, "gpio12"),
701490d9f8SAmelie Delaunay 	PINCTRL_PIN(13, "gpio13"),
711490d9f8SAmelie Delaunay 	PINCTRL_PIN(14, "gpio14"),
721490d9f8SAmelie Delaunay 	PINCTRL_PIN(15, "gpio15"),
731490d9f8SAmelie Delaunay 	PINCTRL_PIN(16, "agpio0"),
741490d9f8SAmelie Delaunay 	PINCTRL_PIN(17, "agpio1"),
751490d9f8SAmelie Delaunay 	PINCTRL_PIN(18, "agpio2"),
761490d9f8SAmelie Delaunay 	PINCTRL_PIN(19, "agpio3"),
771490d9f8SAmelie Delaunay 	PINCTRL_PIN(20, "agpio4"),
781490d9f8SAmelie Delaunay 	PINCTRL_PIN(21, "agpio5"),
791490d9f8SAmelie Delaunay 	PINCTRL_PIN(22, "agpio6"),
801490d9f8SAmelie Delaunay 	PINCTRL_PIN(23, "agpio7"),
811490d9f8SAmelie Delaunay };
821490d9f8SAmelie Delaunay 
831490d9f8SAmelie Delaunay struct stmfx_pinctrl {
841490d9f8SAmelie Delaunay 	struct device *dev;
851490d9f8SAmelie Delaunay 	struct stmfx *stmfx;
861490d9f8SAmelie Delaunay 	struct pinctrl_dev *pctl_dev;
871490d9f8SAmelie Delaunay 	struct pinctrl_desc pctl_desc;
881490d9f8SAmelie Delaunay 	struct gpio_chip gpio_chip;
891490d9f8SAmelie Delaunay 	struct mutex lock; /* IRQ bus lock */
901490d9f8SAmelie Delaunay 	unsigned long gpio_valid_mask;
911490d9f8SAmelie Delaunay 	/* Cache of IRQ_GPI_* registers for bus_lock */
921490d9f8SAmelie Delaunay 	u8 irq_gpi_src[NR_GPIO_REGS];
931490d9f8SAmelie Delaunay 	u8 irq_gpi_type[NR_GPIO_REGS];
941490d9f8SAmelie Delaunay 	u8 irq_gpi_evt[NR_GPIO_REGS];
951490d9f8SAmelie Delaunay 	u8 irq_toggle_edge[NR_GPIO_REGS];
961490d9f8SAmelie Delaunay #ifdef CONFIG_PM
971490d9f8SAmelie Delaunay 	/* Backup of GPIO_* registers for suspend/resume */
981490d9f8SAmelie Delaunay 	u8 bkp_gpio_state[NR_GPIO_REGS];
991490d9f8SAmelie Delaunay 	u8 bkp_gpio_dir[NR_GPIO_REGS];
1001490d9f8SAmelie Delaunay 	u8 bkp_gpio_type[NR_GPIO_REGS];
1011490d9f8SAmelie Delaunay 	u8 bkp_gpio_pupd[NR_GPIO_REGS];
1021490d9f8SAmelie Delaunay #endif
1031490d9f8SAmelie Delaunay };
1041490d9f8SAmelie Delaunay 
stmfx_gpio_get(struct gpio_chip * gc,unsigned int offset)1051490d9f8SAmelie Delaunay static int stmfx_gpio_get(struct gpio_chip *gc, unsigned int offset)
1061490d9f8SAmelie Delaunay {
1071490d9f8SAmelie Delaunay 	struct stmfx_pinctrl *pctl = gpiochip_get_data(gc);
1081490d9f8SAmelie Delaunay 	u32 reg = STMFX_REG_GPIO_STATE + get_reg(offset);
1091490d9f8SAmelie Delaunay 	u32 mask = get_mask(offset);
1101490d9f8SAmelie Delaunay 	u32 value;
1111490d9f8SAmelie Delaunay 	int ret;
1121490d9f8SAmelie Delaunay 
1131490d9f8SAmelie Delaunay 	ret = regmap_read(pctl->stmfx->map, reg, &value);
1141490d9f8SAmelie Delaunay 
1151490d9f8SAmelie Delaunay 	return ret ? ret : !!(value & mask);
1161490d9f8SAmelie Delaunay }
1171490d9f8SAmelie Delaunay 
stmfx_gpio_set(struct gpio_chip * gc,unsigned int offset,int value)1188657c6eeSBartosz Golaszewski static int stmfx_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
1191490d9f8SAmelie Delaunay {
1201490d9f8SAmelie Delaunay 	struct stmfx_pinctrl *pctl = gpiochip_get_data(gc);
1211490d9f8SAmelie Delaunay 	u32 reg = value ? STMFX_REG_GPO_SET : STMFX_REG_GPO_CLR;
1221490d9f8SAmelie Delaunay 	u32 mask = get_mask(offset);
1231490d9f8SAmelie Delaunay 
1248657c6eeSBartosz Golaszewski 	return regmap_write_bits(pctl->stmfx->map, reg + get_reg(offset),
1251490d9f8SAmelie Delaunay 				 mask, mask);
1261490d9f8SAmelie Delaunay }
1271490d9f8SAmelie Delaunay 
stmfx_gpio_get_direction(struct gpio_chip * gc,unsigned int offset)1281490d9f8SAmelie Delaunay static int stmfx_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
1291490d9f8SAmelie Delaunay {
1301490d9f8SAmelie Delaunay 	struct stmfx_pinctrl *pctl = gpiochip_get_data(gc);
1311490d9f8SAmelie Delaunay 	u32 reg = STMFX_REG_GPIO_DIR + get_reg(offset);
1321490d9f8SAmelie Delaunay 	u32 mask = get_mask(offset);
1331490d9f8SAmelie Delaunay 	u32 val;
1341490d9f8SAmelie Delaunay 	int ret;
1351490d9f8SAmelie Delaunay 
1361490d9f8SAmelie Delaunay 	ret = regmap_read(pctl->stmfx->map, reg, &val);
1371490d9f8SAmelie Delaunay 	/*
1381490d9f8SAmelie Delaunay 	 * On stmfx, gpio pins direction is (0)input, (1)output.
1391490d9f8SAmelie Delaunay 	 */
1403c827873SMatti Vaittinen 	if (ret)
1413c827873SMatti Vaittinen 		return ret;
1421490d9f8SAmelie Delaunay 
1433c827873SMatti Vaittinen 	if (val & mask)
1443c827873SMatti Vaittinen 		return GPIO_LINE_DIRECTION_OUT;
1453c827873SMatti Vaittinen 
1463c827873SMatti Vaittinen 	return GPIO_LINE_DIRECTION_IN;
1471490d9f8SAmelie Delaunay }
1481490d9f8SAmelie Delaunay 
stmfx_gpio_direction_input(struct gpio_chip * gc,unsigned int offset)1491490d9f8SAmelie Delaunay static int stmfx_gpio_direction_input(struct gpio_chip *gc, unsigned int offset)
1501490d9f8SAmelie Delaunay {
1511490d9f8SAmelie Delaunay 	struct stmfx_pinctrl *pctl = gpiochip_get_data(gc);
1521490d9f8SAmelie Delaunay 	u32 reg = STMFX_REG_GPIO_DIR + get_reg(offset);
1531490d9f8SAmelie Delaunay 	u32 mask = get_mask(offset);
1541490d9f8SAmelie Delaunay 
1551490d9f8SAmelie Delaunay 	return regmap_write_bits(pctl->stmfx->map, reg, mask, 0);
1561490d9f8SAmelie Delaunay }
1571490d9f8SAmelie Delaunay 
stmfx_gpio_direction_output(struct gpio_chip * gc,unsigned int offset,int value)1581490d9f8SAmelie Delaunay static int stmfx_gpio_direction_output(struct gpio_chip *gc,
1591490d9f8SAmelie Delaunay 				       unsigned int offset, int value)
1601490d9f8SAmelie Delaunay {
1611490d9f8SAmelie Delaunay 	struct stmfx_pinctrl *pctl = gpiochip_get_data(gc);
1621490d9f8SAmelie Delaunay 	u32 reg = STMFX_REG_GPIO_DIR + get_reg(offset);
1631490d9f8SAmelie Delaunay 	u32 mask = get_mask(offset);
1648657c6eeSBartosz Golaszewski 	int ret;
1651490d9f8SAmelie Delaunay 
1668657c6eeSBartosz Golaszewski 	ret = stmfx_gpio_set(gc, offset, value);
1678657c6eeSBartosz Golaszewski 	if (ret)
1688657c6eeSBartosz Golaszewski 		return ret;
1691490d9f8SAmelie Delaunay 
1701490d9f8SAmelie Delaunay 	return regmap_write_bits(pctl->stmfx->map, reg, mask, mask);
1711490d9f8SAmelie Delaunay }
1721490d9f8SAmelie Delaunay 
stmfx_pinconf_get_pupd(struct stmfx_pinctrl * pctl,unsigned int offset)1731490d9f8SAmelie Delaunay static int stmfx_pinconf_get_pupd(struct stmfx_pinctrl *pctl,
1741490d9f8SAmelie Delaunay 				  unsigned int offset)
1751490d9f8SAmelie Delaunay {
1761490d9f8SAmelie Delaunay 	u32 reg = STMFX_REG_GPIO_PUPD + get_reg(offset);
1771490d9f8SAmelie Delaunay 	u32 pupd, mask = get_mask(offset);
1781490d9f8SAmelie Delaunay 	int ret;
1791490d9f8SAmelie Delaunay 
1801490d9f8SAmelie Delaunay 	ret = regmap_read(pctl->stmfx->map, reg, &pupd);
1811490d9f8SAmelie Delaunay 	if (ret)
1821490d9f8SAmelie Delaunay 		return ret;
1831490d9f8SAmelie Delaunay 
1841490d9f8SAmelie Delaunay 	return !!(pupd & mask);
1851490d9f8SAmelie Delaunay }
1861490d9f8SAmelie Delaunay 
stmfx_pinconf_set_pupd(struct stmfx_pinctrl * pctl,unsigned int offset,u32 pupd)1871490d9f8SAmelie Delaunay static int stmfx_pinconf_set_pupd(struct stmfx_pinctrl *pctl,
1881490d9f8SAmelie Delaunay 				  unsigned int offset, u32 pupd)
1891490d9f8SAmelie Delaunay {
1901490d9f8SAmelie Delaunay 	u32 reg = STMFX_REG_GPIO_PUPD + get_reg(offset);
1911490d9f8SAmelie Delaunay 	u32 mask = get_mask(offset);
1921490d9f8SAmelie Delaunay 
1931490d9f8SAmelie Delaunay 	return regmap_write_bits(pctl->stmfx->map, reg, mask, pupd ? mask : 0);
1941490d9f8SAmelie Delaunay }
1951490d9f8SAmelie Delaunay 
stmfx_pinconf_get_type(struct stmfx_pinctrl * pctl,unsigned int offset)1961490d9f8SAmelie Delaunay static int stmfx_pinconf_get_type(struct stmfx_pinctrl *pctl,
1971490d9f8SAmelie Delaunay 				  unsigned int offset)
1981490d9f8SAmelie Delaunay {
1991490d9f8SAmelie Delaunay 	u32 reg = STMFX_REG_GPIO_TYPE + get_reg(offset);
2001490d9f8SAmelie Delaunay 	u32 type, mask = get_mask(offset);
2011490d9f8SAmelie Delaunay 	int ret;
2021490d9f8SAmelie Delaunay 
2031490d9f8SAmelie Delaunay 	ret = regmap_read(pctl->stmfx->map, reg, &type);
2041490d9f8SAmelie Delaunay 	if (ret)
2051490d9f8SAmelie Delaunay 		return ret;
2061490d9f8SAmelie Delaunay 
2071490d9f8SAmelie Delaunay 	return !!(type & mask);
2081490d9f8SAmelie Delaunay }
2091490d9f8SAmelie Delaunay 
stmfx_pinconf_set_type(struct stmfx_pinctrl * pctl,unsigned int offset,u32 type)2101490d9f8SAmelie Delaunay static int stmfx_pinconf_set_type(struct stmfx_pinctrl *pctl,
2111490d9f8SAmelie Delaunay 				  unsigned int offset, u32 type)
2121490d9f8SAmelie Delaunay {
2131490d9f8SAmelie Delaunay 	u32 reg = STMFX_REG_GPIO_TYPE + get_reg(offset);
2141490d9f8SAmelie Delaunay 	u32 mask = get_mask(offset);
2151490d9f8SAmelie Delaunay 
2161490d9f8SAmelie Delaunay 	return regmap_write_bits(pctl->stmfx->map, reg, mask, type ? mask : 0);
2171490d9f8SAmelie Delaunay }
2181490d9f8SAmelie Delaunay 
stmfx_pinconf_get(struct pinctrl_dev * pctldev,unsigned int pin,unsigned long * config)2191490d9f8SAmelie Delaunay static int stmfx_pinconf_get(struct pinctrl_dev *pctldev,
2201490d9f8SAmelie Delaunay 			     unsigned int pin, unsigned long *config)
2211490d9f8SAmelie Delaunay {
2221490d9f8SAmelie Delaunay 	struct stmfx_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
2231490d9f8SAmelie Delaunay 	u32 param = pinconf_to_config_param(*config);
2241490d9f8SAmelie Delaunay 	struct pinctrl_gpio_range *range;
2251490d9f8SAmelie Delaunay 	u32 arg = 0;
22628a85386SLee Jones 	int ret, dir, type, pupd;
2271490d9f8SAmelie Delaunay 
2281490d9f8SAmelie Delaunay 	range = pinctrl_find_gpio_range_from_pin_nolock(pctldev, pin);
2291490d9f8SAmelie Delaunay 	if (!range)
2301490d9f8SAmelie Delaunay 		return -EINVAL;
2311490d9f8SAmelie Delaunay 
2321490d9f8SAmelie Delaunay 	dir = stmfx_gpio_get_direction(&pctl->gpio_chip, pin);
2331490d9f8SAmelie Delaunay 	if (dir < 0)
2341490d9f8SAmelie Delaunay 		return dir;
2353c827873SMatti Vaittinen 
2363c827873SMatti Vaittinen 	/*
2373c827873SMatti Vaittinen 	 * Currently the gpiolib IN is 1 and OUT is 0 but let's not count
2383c827873SMatti Vaittinen 	 * on it just to be on the safe side also in the future :)
2393c827873SMatti Vaittinen 	 */
2403c827873SMatti Vaittinen 	dir = (dir == GPIO_LINE_DIRECTION_IN) ? 1 : 0;
2413c827873SMatti Vaittinen 
2421490d9f8SAmelie Delaunay 	type = stmfx_pinconf_get_type(pctl, pin);
2431490d9f8SAmelie Delaunay 	if (type < 0)
2441490d9f8SAmelie Delaunay 		return type;
2451490d9f8SAmelie Delaunay 	pupd = stmfx_pinconf_get_pupd(pctl, pin);
2461490d9f8SAmelie Delaunay 	if (pupd < 0)
2471490d9f8SAmelie Delaunay 		return pupd;
2481490d9f8SAmelie Delaunay 
2491490d9f8SAmelie Delaunay 	switch (param) {
2501490d9f8SAmelie Delaunay 	case PIN_CONFIG_BIAS_DISABLE:
2511490d9f8SAmelie Delaunay 		if ((!dir && (!type || !pupd)) || (dir && !type))
2521490d9f8SAmelie Delaunay 			arg = 1;
2531490d9f8SAmelie Delaunay 		break;
2541490d9f8SAmelie Delaunay 	case PIN_CONFIG_BIAS_PULL_DOWN:
2551490d9f8SAmelie Delaunay 		if (dir && type && !pupd)
2561490d9f8SAmelie Delaunay 			arg = 1;
2571490d9f8SAmelie Delaunay 		break;
2581490d9f8SAmelie Delaunay 	case PIN_CONFIG_BIAS_PULL_UP:
2591490d9f8SAmelie Delaunay 		if (type && pupd)
2601490d9f8SAmelie Delaunay 			arg = 1;
2611490d9f8SAmelie Delaunay 		break;
2621490d9f8SAmelie Delaunay 	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
2631490d9f8SAmelie Delaunay 		if ((!dir && type) || (dir && !type))
2641490d9f8SAmelie Delaunay 			arg = 1;
2651490d9f8SAmelie Delaunay 		break;
2661490d9f8SAmelie Delaunay 	case PIN_CONFIG_DRIVE_PUSH_PULL:
2671490d9f8SAmelie Delaunay 		if ((!dir && !type) || (dir && type))
2681490d9f8SAmelie Delaunay 			arg = 1;
2691490d9f8SAmelie Delaunay 		break;
2701490d9f8SAmelie Delaunay 	case PIN_CONFIG_OUTPUT:
2711490d9f8SAmelie Delaunay 		if (dir)
2721490d9f8SAmelie Delaunay 			return -EINVAL;
2731490d9f8SAmelie Delaunay 
2741490d9f8SAmelie Delaunay 		ret = stmfx_gpio_get(&pctl->gpio_chip, pin);
2751490d9f8SAmelie Delaunay 		if (ret < 0)
2761490d9f8SAmelie Delaunay 			return ret;
2771490d9f8SAmelie Delaunay 
2781490d9f8SAmelie Delaunay 		arg = ret;
2791490d9f8SAmelie Delaunay 		break;
2801490d9f8SAmelie Delaunay 	default:
2811490d9f8SAmelie Delaunay 		return -ENOTSUPP;
2821490d9f8SAmelie Delaunay 	}
2831490d9f8SAmelie Delaunay 
2841490d9f8SAmelie Delaunay 	*config = pinconf_to_config_packed(param, arg);
2851490d9f8SAmelie Delaunay 
2861490d9f8SAmelie Delaunay 	return 0;
2871490d9f8SAmelie Delaunay }
2881490d9f8SAmelie Delaunay 
stmfx_pinconf_set(struct pinctrl_dev * pctldev,unsigned int pin,unsigned long * configs,unsigned int num_configs)2891490d9f8SAmelie Delaunay static int stmfx_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
2901490d9f8SAmelie Delaunay 			     unsigned long *configs, unsigned int num_configs)
2911490d9f8SAmelie Delaunay {
2921490d9f8SAmelie Delaunay 	struct stmfx_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
2931490d9f8SAmelie Delaunay 	struct pinctrl_gpio_range *range;
2941490d9f8SAmelie Delaunay 	enum pin_config_param param;
2951490d9f8SAmelie Delaunay 	u32 arg;
29636126f53SAmelie Delaunay 	int i, ret;
2971490d9f8SAmelie Delaunay 
2981490d9f8SAmelie Delaunay 	range = pinctrl_find_gpio_range_from_pin_nolock(pctldev, pin);
2991490d9f8SAmelie Delaunay 	if (!range) {
3001490d9f8SAmelie Delaunay 		dev_err(pctldev->dev, "pin %d is not available\n", pin);
3011490d9f8SAmelie Delaunay 		return -EINVAL;
3021490d9f8SAmelie Delaunay 	}
3031490d9f8SAmelie Delaunay 
3041490d9f8SAmelie Delaunay 	for (i = 0; i < num_configs; i++) {
3051490d9f8SAmelie Delaunay 		param = pinconf_to_config_param(configs[i]);
3061490d9f8SAmelie Delaunay 		arg = pinconf_to_config_argument(configs[i]);
3071490d9f8SAmelie Delaunay 
3081490d9f8SAmelie Delaunay 		switch (param) {
3091490d9f8SAmelie Delaunay 		case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
3101490d9f8SAmelie Delaunay 		case PIN_CONFIG_BIAS_DISABLE:
311a502b343SAlexandre Torgue 		case PIN_CONFIG_DRIVE_PUSH_PULL:
312a502b343SAlexandre Torgue 			ret = stmfx_pinconf_set_type(pctl, pin, 0);
313a502b343SAlexandre Torgue 			if (ret)
314a502b343SAlexandre Torgue 				return ret;
315a502b343SAlexandre Torgue 			break;
3161490d9f8SAmelie Delaunay 		case PIN_CONFIG_BIAS_PULL_DOWN:
317a502b343SAlexandre Torgue 			ret = stmfx_pinconf_set_type(pctl, pin, 1);
318a502b343SAlexandre Torgue 			if (ret)
319a502b343SAlexandre Torgue 				return ret;
3201490d9f8SAmelie Delaunay 			ret = stmfx_pinconf_set_pupd(pctl, pin, 0);
3211490d9f8SAmelie Delaunay 			if (ret)
3221490d9f8SAmelie Delaunay 				return ret;
3231490d9f8SAmelie Delaunay 			break;
3241490d9f8SAmelie Delaunay 		case PIN_CONFIG_BIAS_PULL_UP:
325a502b343SAlexandre Torgue 			ret = stmfx_pinconf_set_type(pctl, pin, 1);
326a502b343SAlexandre Torgue 			if (ret)
327a502b343SAlexandre Torgue 				return ret;
3281490d9f8SAmelie Delaunay 			ret = stmfx_pinconf_set_pupd(pctl, pin, 1);
3291490d9f8SAmelie Delaunay 			if (ret)
3301490d9f8SAmelie Delaunay 				return ret;
3311490d9f8SAmelie Delaunay 			break;
3321490d9f8SAmelie Delaunay 		case PIN_CONFIG_DRIVE_OPEN_DRAIN:
3331490d9f8SAmelie Delaunay 			ret = stmfx_pinconf_set_type(pctl, pin, 1);
3341490d9f8SAmelie Delaunay 			if (ret)
3351490d9f8SAmelie Delaunay 				return ret;
3361490d9f8SAmelie Delaunay 			break;
3371490d9f8SAmelie Delaunay 		case PIN_CONFIG_OUTPUT:
3381490d9f8SAmelie Delaunay 			ret = stmfx_gpio_direction_output(&pctl->gpio_chip,
3391490d9f8SAmelie Delaunay 							  pin, arg);
3401490d9f8SAmelie Delaunay 			if (ret)
3411490d9f8SAmelie Delaunay 				return ret;
3421490d9f8SAmelie Delaunay 			break;
3431490d9f8SAmelie Delaunay 		default:
3441490d9f8SAmelie Delaunay 			return -ENOTSUPP;
3451490d9f8SAmelie Delaunay 		}
3461490d9f8SAmelie Delaunay 	}
3471490d9f8SAmelie Delaunay 
3481490d9f8SAmelie Delaunay 	return 0;
3491490d9f8SAmelie Delaunay }
3501490d9f8SAmelie Delaunay 
stmfx_pinconf_dbg_show(struct pinctrl_dev * pctldev,struct seq_file * s,unsigned int offset)3511490d9f8SAmelie Delaunay static void stmfx_pinconf_dbg_show(struct pinctrl_dev *pctldev,
3521490d9f8SAmelie Delaunay 				   struct seq_file *s, unsigned int offset)
3531490d9f8SAmelie Delaunay {
3541490d9f8SAmelie Delaunay 	struct stmfx_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
3551490d9f8SAmelie Delaunay 	struct pinctrl_gpio_range *range;
3561490d9f8SAmelie Delaunay 	int dir, type, pupd, val;
3571490d9f8SAmelie Delaunay 
3581490d9f8SAmelie Delaunay 	range = pinctrl_find_gpio_range_from_pin_nolock(pctldev, offset);
3591490d9f8SAmelie Delaunay 	if (!range)
3601490d9f8SAmelie Delaunay 		return;
3611490d9f8SAmelie Delaunay 
3621490d9f8SAmelie Delaunay 	dir = stmfx_gpio_get_direction(&pctl->gpio_chip, offset);
3631490d9f8SAmelie Delaunay 	if (dir < 0)
3641490d9f8SAmelie Delaunay 		return;
3651490d9f8SAmelie Delaunay 	type = stmfx_pinconf_get_type(pctl, offset);
3661490d9f8SAmelie Delaunay 	if (type < 0)
3671490d9f8SAmelie Delaunay 		return;
3681490d9f8SAmelie Delaunay 	pupd = stmfx_pinconf_get_pupd(pctl, offset);
3691490d9f8SAmelie Delaunay 	if (pupd < 0)
3701490d9f8SAmelie Delaunay 		return;
3711490d9f8SAmelie Delaunay 	val = stmfx_gpio_get(&pctl->gpio_chip, offset);
3721490d9f8SAmelie Delaunay 	if (val < 0)
3731490d9f8SAmelie Delaunay 		return;
3741490d9f8SAmelie Delaunay 
3753c827873SMatti Vaittinen 	if (dir == GPIO_LINE_DIRECTION_OUT) {
376f33254dbSAndy Shevchenko 		seq_printf(s, "output %s ", str_high_low(val));
3771490d9f8SAmelie Delaunay 		if (type)
3781490d9f8SAmelie Delaunay 			seq_printf(s, "open drain %s internal pull-up ",
3791490d9f8SAmelie Delaunay 				   pupd ? "with" : "without");
3801490d9f8SAmelie Delaunay 		else
3811490d9f8SAmelie Delaunay 			seq_puts(s, "push pull no pull ");
3821490d9f8SAmelie Delaunay 	} else {
383f33254dbSAndy Shevchenko 		seq_printf(s, "input %s ", str_high_low(val));
3841490d9f8SAmelie Delaunay 		if (type)
3851490d9f8SAmelie Delaunay 			seq_printf(s, "with internal pull-%s ",
3863f36bffaSKrzysztof Kozlowski 				   str_up_down(pupd));
3871490d9f8SAmelie Delaunay 		else
3881490d9f8SAmelie Delaunay 			seq_printf(s, "%s ", pupd ? "floating" : "analog");
3891490d9f8SAmelie Delaunay 	}
3901490d9f8SAmelie Delaunay }
3911490d9f8SAmelie Delaunay 
3921490d9f8SAmelie Delaunay static const struct pinconf_ops stmfx_pinconf_ops = {
3931490d9f8SAmelie Delaunay 	.pin_config_get		= stmfx_pinconf_get,
3941490d9f8SAmelie Delaunay 	.pin_config_set		= stmfx_pinconf_set,
3951490d9f8SAmelie Delaunay 	.pin_config_dbg_show	= stmfx_pinconf_dbg_show,
3961490d9f8SAmelie Delaunay };
3971490d9f8SAmelie Delaunay 
stmfx_pinctrl_get_groups_count(struct pinctrl_dev * pctldev)3981490d9f8SAmelie Delaunay static int stmfx_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
3991490d9f8SAmelie Delaunay {
4001490d9f8SAmelie Delaunay 	return 0;
4011490d9f8SAmelie Delaunay }
4021490d9f8SAmelie Delaunay 
stmfx_pinctrl_get_group_name(struct pinctrl_dev * pctldev,unsigned int selector)4031490d9f8SAmelie Delaunay static const char *stmfx_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
4041490d9f8SAmelie Delaunay 						unsigned int selector)
4051490d9f8SAmelie Delaunay {
4061490d9f8SAmelie Delaunay 	return NULL;
4071490d9f8SAmelie Delaunay }
4081490d9f8SAmelie Delaunay 
stmfx_pinctrl_get_group_pins(struct pinctrl_dev * pctldev,unsigned int selector,const unsigned int ** pins,unsigned int * num_pins)4091490d9f8SAmelie Delaunay static int stmfx_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
4101490d9f8SAmelie Delaunay 					unsigned int selector,
4111490d9f8SAmelie Delaunay 					const unsigned int **pins,
4121490d9f8SAmelie Delaunay 					unsigned int *num_pins)
4131490d9f8SAmelie Delaunay {
4141490d9f8SAmelie Delaunay 	return -ENOTSUPP;
4151490d9f8SAmelie Delaunay }
4161490d9f8SAmelie Delaunay 
4171490d9f8SAmelie Delaunay static const struct pinctrl_ops stmfx_pinctrl_ops = {
4181490d9f8SAmelie Delaunay 	.get_groups_count = stmfx_pinctrl_get_groups_count,
4191490d9f8SAmelie Delaunay 	.get_group_name = stmfx_pinctrl_get_group_name,
4201490d9f8SAmelie Delaunay 	.get_group_pins = stmfx_pinctrl_get_group_pins,
4211490d9f8SAmelie Delaunay 	.dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
4221490d9f8SAmelie Delaunay 	.dt_free_map = pinctrl_utils_free_map,
4231490d9f8SAmelie Delaunay };
4241490d9f8SAmelie Delaunay 
stmfx_pinctrl_irq_mask(struct irq_data * data)4251490d9f8SAmelie Delaunay static void stmfx_pinctrl_irq_mask(struct irq_data *data)
4261490d9f8SAmelie Delaunay {
4271490d9f8SAmelie Delaunay 	struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data);
4281490d9f8SAmelie Delaunay 	struct stmfx_pinctrl *pctl = gpiochip_get_data(gpio_chip);
4291490d9f8SAmelie Delaunay 	u32 reg = get_reg(data->hwirq);
4301490d9f8SAmelie Delaunay 	u32 mask = get_mask(data->hwirq);
4311490d9f8SAmelie Delaunay 
4321490d9f8SAmelie Delaunay 	pctl->irq_gpi_src[reg] &= ~mask;
4337341944cSLinus Walleij 	gpiochip_disable_irq(gpio_chip, irqd_to_hwirq(data));
4341490d9f8SAmelie Delaunay }
4351490d9f8SAmelie Delaunay 
stmfx_pinctrl_irq_unmask(struct irq_data * data)4361490d9f8SAmelie Delaunay static void stmfx_pinctrl_irq_unmask(struct irq_data *data)
4371490d9f8SAmelie Delaunay {
4381490d9f8SAmelie Delaunay 	struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data);
4391490d9f8SAmelie Delaunay 	struct stmfx_pinctrl *pctl = gpiochip_get_data(gpio_chip);
4401490d9f8SAmelie Delaunay 	u32 reg = get_reg(data->hwirq);
4411490d9f8SAmelie Delaunay 	u32 mask = get_mask(data->hwirq);
4421490d9f8SAmelie Delaunay 
4437341944cSLinus Walleij 	gpiochip_enable_irq(gpio_chip, irqd_to_hwirq(data));
4441490d9f8SAmelie Delaunay 	pctl->irq_gpi_src[reg] |= mask;
4451490d9f8SAmelie Delaunay }
4461490d9f8SAmelie Delaunay 
stmfx_pinctrl_irq_set_type(struct irq_data * data,unsigned int type)4471490d9f8SAmelie Delaunay static int stmfx_pinctrl_irq_set_type(struct irq_data *data, unsigned int type)
4481490d9f8SAmelie Delaunay {
4491490d9f8SAmelie Delaunay 	struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data);
4501490d9f8SAmelie Delaunay 	struct stmfx_pinctrl *pctl = gpiochip_get_data(gpio_chip);
4511490d9f8SAmelie Delaunay 	u32 reg = get_reg(data->hwirq);
4521490d9f8SAmelie Delaunay 	u32 mask = get_mask(data->hwirq);
4531490d9f8SAmelie Delaunay 
454d17ed797SLee Jones 	if (type == IRQ_TYPE_NONE)
4551490d9f8SAmelie Delaunay 		return -EINVAL;
4561490d9f8SAmelie Delaunay 
4571490d9f8SAmelie Delaunay 	if (type & IRQ_TYPE_EDGE_BOTH) {
4581490d9f8SAmelie Delaunay 		pctl->irq_gpi_evt[reg] |= mask;
4591490d9f8SAmelie Delaunay 		irq_set_handler_locked(data, handle_edge_irq);
4601490d9f8SAmelie Delaunay 	} else {
4611490d9f8SAmelie Delaunay 		pctl->irq_gpi_evt[reg] &= ~mask;
4621490d9f8SAmelie Delaunay 		irq_set_handler_locked(data, handle_level_irq);
4631490d9f8SAmelie Delaunay 	}
4641490d9f8SAmelie Delaunay 
4651490d9f8SAmelie Delaunay 	if ((type & IRQ_TYPE_EDGE_RISING) || (type & IRQ_TYPE_LEVEL_HIGH))
4661490d9f8SAmelie Delaunay 		pctl->irq_gpi_type[reg] |= mask;
4671490d9f8SAmelie Delaunay 	else
4681490d9f8SAmelie Delaunay 		pctl->irq_gpi_type[reg] &= ~mask;
4691490d9f8SAmelie Delaunay 
4701490d9f8SAmelie Delaunay 	/*
4711490d9f8SAmelie Delaunay 	 * In case of (type & IRQ_TYPE_EDGE_BOTH), we need to know current
4721490d9f8SAmelie Delaunay 	 * GPIO value to set the right edge trigger. But in atomic context
4731490d9f8SAmelie Delaunay 	 * here we can't access registers over I2C. That's why (type &
4741490d9f8SAmelie Delaunay 	 * IRQ_TYPE_EDGE_BOTH) will be managed in .irq_sync_unlock.
4751490d9f8SAmelie Delaunay 	 */
4761490d9f8SAmelie Delaunay 
4771490d9f8SAmelie Delaunay 	if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
4781490d9f8SAmelie Delaunay 		pctl->irq_toggle_edge[reg] |= mask;
4791490d9f8SAmelie Delaunay 	else
4801490d9f8SAmelie Delaunay 		pctl->irq_toggle_edge[reg] &= mask;
4811490d9f8SAmelie Delaunay 
4821490d9f8SAmelie Delaunay 	return 0;
4831490d9f8SAmelie Delaunay }
4841490d9f8SAmelie Delaunay 
stmfx_pinctrl_irq_bus_lock(struct irq_data * data)4851490d9f8SAmelie Delaunay static void stmfx_pinctrl_irq_bus_lock(struct irq_data *data)
4861490d9f8SAmelie Delaunay {
4871490d9f8SAmelie Delaunay 	struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data);
4881490d9f8SAmelie Delaunay 	struct stmfx_pinctrl *pctl = gpiochip_get_data(gpio_chip);
4891490d9f8SAmelie Delaunay 
4901490d9f8SAmelie Delaunay 	mutex_lock(&pctl->lock);
4911490d9f8SAmelie Delaunay }
4921490d9f8SAmelie Delaunay 
stmfx_pinctrl_irq_bus_sync_unlock(struct irq_data * data)4931490d9f8SAmelie Delaunay static void stmfx_pinctrl_irq_bus_sync_unlock(struct irq_data *data)
4941490d9f8SAmelie Delaunay {
4951490d9f8SAmelie Delaunay 	struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data);
4961490d9f8SAmelie Delaunay 	struct stmfx_pinctrl *pctl = gpiochip_get_data(gpio_chip);
4971490d9f8SAmelie Delaunay 	u32 reg = get_reg(data->hwirq);
4981490d9f8SAmelie Delaunay 	u32 mask = get_mask(data->hwirq);
4991490d9f8SAmelie Delaunay 
5001490d9f8SAmelie Delaunay 	/*
5011490d9f8SAmelie Delaunay 	 * In case of IRQ_TYPE_EDGE_BOTH), read the current GPIO value
5021490d9f8SAmelie Delaunay 	 * (this couldn't be done in .irq_set_type because of atomic context)
5031490d9f8SAmelie Delaunay 	 * to set the right irq trigger type.
5041490d9f8SAmelie Delaunay 	 */
5051490d9f8SAmelie Delaunay 	if (pctl->irq_toggle_edge[reg] & mask) {
5061490d9f8SAmelie Delaunay 		if (stmfx_gpio_get(gpio_chip, data->hwirq))
5071490d9f8SAmelie Delaunay 			pctl->irq_gpi_type[reg] &= ~mask;
5081490d9f8SAmelie Delaunay 		else
5091490d9f8SAmelie Delaunay 			pctl->irq_gpi_type[reg] |= mask;
5101490d9f8SAmelie Delaunay 	}
5111490d9f8SAmelie Delaunay 
5121490d9f8SAmelie Delaunay 	regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_EVT,
5131490d9f8SAmelie Delaunay 			  pctl->irq_gpi_evt, NR_GPIO_REGS);
5141490d9f8SAmelie Delaunay 	regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_TYPE,
5151490d9f8SAmelie Delaunay 			  pctl->irq_gpi_type, NR_GPIO_REGS);
5161490d9f8SAmelie Delaunay 	regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_SRC,
5171490d9f8SAmelie Delaunay 			  pctl->irq_gpi_src, NR_GPIO_REGS);
5181490d9f8SAmelie Delaunay 
5191490d9f8SAmelie Delaunay 	mutex_unlock(&pctl->lock);
5201490d9f8SAmelie Delaunay }
5211490d9f8SAmelie Delaunay 
stmfx_gpio_irq_request_resources(struct irq_data * data)522f086d1feSAmelie Delaunay static int stmfx_gpio_irq_request_resources(struct irq_data *data)
523f086d1feSAmelie Delaunay {
524f086d1feSAmelie Delaunay 	struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data);
525f086d1feSAmelie Delaunay 	int ret;
526f086d1feSAmelie Delaunay 
527f086d1feSAmelie Delaunay 	ret = stmfx_gpio_direction_input(gpio_chip, data->hwirq);
528f086d1feSAmelie Delaunay 	if (ret)
529f086d1feSAmelie Delaunay 		return ret;
530f086d1feSAmelie Delaunay 
531f086d1feSAmelie Delaunay 	return gpiochip_reqres_irq(gpio_chip, data->hwirq);
532f086d1feSAmelie Delaunay }
533f086d1feSAmelie Delaunay 
stmfx_gpio_irq_release_resources(struct irq_data * data)534f086d1feSAmelie Delaunay static void stmfx_gpio_irq_release_resources(struct irq_data *data)
535f086d1feSAmelie Delaunay {
536f086d1feSAmelie Delaunay 	struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data);
537f086d1feSAmelie Delaunay 
538f086d1feSAmelie Delaunay 	return gpiochip_relres_irq(gpio_chip, data->hwirq);
539f086d1feSAmelie Delaunay }
540f086d1feSAmelie Delaunay 
stmfx_pinctrl_irq_toggle_trigger(struct stmfx_pinctrl * pctl,unsigned int offset)5411490d9f8SAmelie Delaunay static void stmfx_pinctrl_irq_toggle_trigger(struct stmfx_pinctrl *pctl,
5421490d9f8SAmelie Delaunay 					     unsigned int offset)
5431490d9f8SAmelie Delaunay {
5441490d9f8SAmelie Delaunay 	u32 reg = get_reg(offset);
5451490d9f8SAmelie Delaunay 	u32 mask = get_mask(offset);
5461490d9f8SAmelie Delaunay 	int val;
5471490d9f8SAmelie Delaunay 
5481490d9f8SAmelie Delaunay 	if (!(pctl->irq_toggle_edge[reg] & mask))
5491490d9f8SAmelie Delaunay 		return;
5501490d9f8SAmelie Delaunay 
5511490d9f8SAmelie Delaunay 	val = stmfx_gpio_get(&pctl->gpio_chip, offset);
5521490d9f8SAmelie Delaunay 	if (val < 0)
5531490d9f8SAmelie Delaunay 		return;
5541490d9f8SAmelie Delaunay 
5551490d9f8SAmelie Delaunay 	if (val) {
5561490d9f8SAmelie Delaunay 		pctl->irq_gpi_type[reg] &= mask;
5571490d9f8SAmelie Delaunay 		regmap_write_bits(pctl->stmfx->map,
5581490d9f8SAmelie Delaunay 				  STMFX_REG_IRQ_GPI_TYPE + reg,
5591490d9f8SAmelie Delaunay 				  mask, 0);
5601490d9f8SAmelie Delaunay 
5611490d9f8SAmelie Delaunay 	} else {
5621490d9f8SAmelie Delaunay 		pctl->irq_gpi_type[reg] |= mask;
5631490d9f8SAmelie Delaunay 		regmap_write_bits(pctl->stmfx->map,
5641490d9f8SAmelie Delaunay 				  STMFX_REG_IRQ_GPI_TYPE + reg,
5651490d9f8SAmelie Delaunay 				  mask, mask);
5661490d9f8SAmelie Delaunay 	}
5671490d9f8SAmelie Delaunay }
5681490d9f8SAmelie Delaunay 
stmfx_pinctrl_irq_thread_fn(int irq,void * dev_id)5691490d9f8SAmelie Delaunay static irqreturn_t stmfx_pinctrl_irq_thread_fn(int irq, void *dev_id)
5701490d9f8SAmelie Delaunay {
5711490d9f8SAmelie Delaunay 	struct stmfx_pinctrl *pctl = (struct stmfx_pinctrl *)dev_id;
5721490d9f8SAmelie Delaunay 	struct gpio_chip *gc = &pctl->gpio_chip;
5731490d9f8SAmelie Delaunay 	u8 pending[NR_GPIO_REGS];
5741490d9f8SAmelie Delaunay 	u8 src[NR_GPIO_REGS] = {0, 0, 0};
5751490d9f8SAmelie Delaunay 	unsigned long n, status;
5761b73e588SMarc Zyngier 	int i, ret;
5771490d9f8SAmelie Delaunay 
5781490d9f8SAmelie Delaunay 	ret = regmap_bulk_read(pctl->stmfx->map, STMFX_REG_IRQ_GPI_PENDING,
5791490d9f8SAmelie Delaunay 			       &pending, NR_GPIO_REGS);
5801490d9f8SAmelie Delaunay 	if (ret)
5811490d9f8SAmelie Delaunay 		return IRQ_NONE;
5821490d9f8SAmelie Delaunay 
5831490d9f8SAmelie Delaunay 	regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_SRC,
5841490d9f8SAmelie Delaunay 			  src, NR_GPIO_REGS);
5851490d9f8SAmelie Delaunay 
5861b73e588SMarc Zyngier 	BUILD_BUG_ON(NR_GPIO_REGS > sizeof(status));
5871b73e588SMarc Zyngier 	for (i = 0, status = 0; i < NR_GPIO_REGS; i++)
5881b73e588SMarc Zyngier 		status |= (unsigned long)pending[i] << (i * 8);
5891490d9f8SAmelie Delaunay 	for_each_set_bit(n, &status, gc->ngpio) {
5901490d9f8SAmelie Delaunay 		handle_nested_irq(irq_find_mapping(gc->irq.domain, n));
5911490d9f8SAmelie Delaunay 		stmfx_pinctrl_irq_toggle_trigger(pctl, n);
5921490d9f8SAmelie Delaunay 	}
5931490d9f8SAmelie Delaunay 
5941490d9f8SAmelie Delaunay 	regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_SRC,
5951490d9f8SAmelie Delaunay 			  pctl->irq_gpi_src, NR_GPIO_REGS);
5961490d9f8SAmelie Delaunay 
5971490d9f8SAmelie Delaunay 	return IRQ_HANDLED;
5981490d9f8SAmelie Delaunay }
5991490d9f8SAmelie Delaunay 
stmfx_pinctrl_irq_print_chip(struct irq_data * d,struct seq_file * p)6007341944cSLinus Walleij static void stmfx_pinctrl_irq_print_chip(struct irq_data *d, struct seq_file *p)
6017341944cSLinus Walleij {
6027341944cSLinus Walleij 	struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(d);
6037341944cSLinus Walleij 	struct stmfx_pinctrl *pctl = gpiochip_get_data(gpio_chip);
6047341944cSLinus Walleij 
605acfeb6deSDavid Wang 	seq_puts(p, dev_name(pctl->dev));
6067341944cSLinus Walleij }
6077341944cSLinus Walleij 
6087341944cSLinus Walleij static const struct irq_chip stmfx_pinctrl_irq_chip = {
6097341944cSLinus Walleij 	.irq_mask = stmfx_pinctrl_irq_mask,
6107341944cSLinus Walleij 	.irq_unmask = stmfx_pinctrl_irq_unmask,
6117341944cSLinus Walleij 	.irq_set_type = stmfx_pinctrl_irq_set_type,
6127341944cSLinus Walleij 	.irq_bus_lock = stmfx_pinctrl_irq_bus_lock,
6137341944cSLinus Walleij 	.irq_bus_sync_unlock = stmfx_pinctrl_irq_bus_sync_unlock,
6147341944cSLinus Walleij 	.irq_request_resources = stmfx_gpio_irq_request_resources,
6157341944cSLinus Walleij 	.irq_release_resources = stmfx_gpio_irq_release_resources,
6167341944cSLinus Walleij 	.irq_print_chip = stmfx_pinctrl_irq_print_chip,
6177341944cSLinus Walleij 	.flags = IRQCHIP_IMMUTABLE,
6187341944cSLinus Walleij };
6197341944cSLinus Walleij 
stmfx_pinctrl_gpio_function_enable(struct stmfx_pinctrl * pctl)6201490d9f8SAmelie Delaunay static int stmfx_pinctrl_gpio_function_enable(struct stmfx_pinctrl *pctl)
6211490d9f8SAmelie Delaunay {
6221490d9f8SAmelie Delaunay 	struct pinctrl_gpio_range *gpio_range;
6231490d9f8SAmelie Delaunay 	struct pinctrl_dev *pctl_dev = pctl->pctl_dev;
6241490d9f8SAmelie Delaunay 	u32 func = STMFX_FUNC_GPIO;
6251490d9f8SAmelie Delaunay 
6261490d9f8SAmelie Delaunay 	pctl->gpio_valid_mask = GENMASK(15, 0);
6271490d9f8SAmelie Delaunay 
6281490d9f8SAmelie Delaunay 	gpio_range = pinctrl_find_gpio_range_from_pin(pctl_dev, 16);
6291490d9f8SAmelie Delaunay 	if (gpio_range) {
6301490d9f8SAmelie Delaunay 		func |= STMFX_FUNC_ALTGPIO_LOW;
6311490d9f8SAmelie Delaunay 		pctl->gpio_valid_mask |= GENMASK(19, 16);
6321490d9f8SAmelie Delaunay 	}
6331490d9f8SAmelie Delaunay 
6341490d9f8SAmelie Delaunay 	gpio_range = pinctrl_find_gpio_range_from_pin(pctl_dev, 20);
6351490d9f8SAmelie Delaunay 	if (gpio_range) {
6361490d9f8SAmelie Delaunay 		func |= STMFX_FUNC_ALTGPIO_HIGH;
6371490d9f8SAmelie Delaunay 		pctl->gpio_valid_mask |= GENMASK(23, 20);
6381490d9f8SAmelie Delaunay 	}
6391490d9f8SAmelie Delaunay 
6401490d9f8SAmelie Delaunay 	return stmfx_function_enable(pctl->stmfx, func);
6411490d9f8SAmelie Delaunay }
6421490d9f8SAmelie Delaunay 
stmfx_pinctrl_probe(struct platform_device * pdev)6431490d9f8SAmelie Delaunay static int stmfx_pinctrl_probe(struct platform_device *pdev)
6441490d9f8SAmelie Delaunay {
6451490d9f8SAmelie Delaunay 	struct stmfx *stmfx = dev_get_drvdata(pdev->dev.parent);
6461490d9f8SAmelie Delaunay 	struct device_node *np = pdev->dev.of_node;
6471490d9f8SAmelie Delaunay 	struct stmfx_pinctrl *pctl;
6481de39b64SLinus Walleij 	struct gpio_irq_chip *girq;
6491490d9f8SAmelie Delaunay 	int irq, ret;
6501490d9f8SAmelie Delaunay 
6511490d9f8SAmelie Delaunay 	pctl = devm_kzalloc(stmfx->dev, sizeof(*pctl), GFP_KERNEL);
6521490d9f8SAmelie Delaunay 	if (!pctl)
6531490d9f8SAmelie Delaunay 		return -ENOMEM;
6541490d9f8SAmelie Delaunay 
6551490d9f8SAmelie Delaunay 	platform_set_drvdata(pdev, pctl);
6561490d9f8SAmelie Delaunay 
6571490d9f8SAmelie Delaunay 	pctl->dev = &pdev->dev;
6581490d9f8SAmelie Delaunay 	pctl->stmfx = stmfx;
6591490d9f8SAmelie Delaunay 
660e0e8fbf8SRob Herring 	if (!of_property_present(np, "gpio-ranges")) {
6611490d9f8SAmelie Delaunay 		dev_err(pctl->dev, "missing required gpio-ranges property\n");
6621490d9f8SAmelie Delaunay 		return -EINVAL;
6631490d9f8SAmelie Delaunay 	}
6641490d9f8SAmelie Delaunay 
6651490d9f8SAmelie Delaunay 	irq = platform_get_irq(pdev, 0);
666fc8a2041SRuan Jinjie 	if (irq < 0)
667fc8a2041SRuan Jinjie 		return irq;
6681490d9f8SAmelie Delaunay 
6691490d9f8SAmelie Delaunay 	mutex_init(&pctl->lock);
6701490d9f8SAmelie Delaunay 
6711490d9f8SAmelie Delaunay 	/* Register pin controller */
6721490d9f8SAmelie Delaunay 	pctl->pctl_desc.name = "stmfx-pinctrl";
6731490d9f8SAmelie Delaunay 	pctl->pctl_desc.pctlops = &stmfx_pinctrl_ops;
6741490d9f8SAmelie Delaunay 	pctl->pctl_desc.confops = &stmfx_pinconf_ops;
6751490d9f8SAmelie Delaunay 	pctl->pctl_desc.pins = stmfx_pins;
6761490d9f8SAmelie Delaunay 	pctl->pctl_desc.npins = ARRAY_SIZE(stmfx_pins);
6771490d9f8SAmelie Delaunay 	pctl->pctl_desc.owner = THIS_MODULE;
678c6045b4eSBenjamin Gaignard 	pctl->pctl_desc.link_consumers = true;
6791490d9f8SAmelie Delaunay 
6801490d9f8SAmelie Delaunay 	ret = devm_pinctrl_register_and_init(pctl->dev, &pctl->pctl_desc,
6811490d9f8SAmelie Delaunay 					     pctl, &pctl->pctl_dev);
6821490d9f8SAmelie Delaunay 	if (ret) {
6831490d9f8SAmelie Delaunay 		dev_err(pctl->dev, "pinctrl registration failed\n");
6841490d9f8SAmelie Delaunay 		return ret;
6851490d9f8SAmelie Delaunay 	}
6861490d9f8SAmelie Delaunay 
6871490d9f8SAmelie Delaunay 	ret = pinctrl_enable(pctl->pctl_dev);
6881490d9f8SAmelie Delaunay 	if (ret) {
6891490d9f8SAmelie Delaunay 		dev_err(pctl->dev, "pinctrl enable failed\n");
6901490d9f8SAmelie Delaunay 		return ret;
6911490d9f8SAmelie Delaunay 	}
6921490d9f8SAmelie Delaunay 
6931490d9f8SAmelie Delaunay 	/* Register gpio controller */
6941490d9f8SAmelie Delaunay 	pctl->gpio_chip.label = "stmfx-gpio";
6951490d9f8SAmelie Delaunay 	pctl->gpio_chip.parent = pctl->dev;
6961490d9f8SAmelie Delaunay 	pctl->gpio_chip.get_direction = stmfx_gpio_get_direction;
6971490d9f8SAmelie Delaunay 	pctl->gpio_chip.direction_input = stmfx_gpio_direction_input;
6981490d9f8SAmelie Delaunay 	pctl->gpio_chip.direction_output = stmfx_gpio_direction_output;
6991490d9f8SAmelie Delaunay 	pctl->gpio_chip.get = stmfx_gpio_get;
700*d9d87d90SBartosz Golaszewski 	pctl->gpio_chip.set = stmfx_gpio_set;
7011490d9f8SAmelie Delaunay 	pctl->gpio_chip.set_config = gpiochip_generic_config;
7021490d9f8SAmelie Delaunay 	pctl->gpio_chip.base = -1;
7031490d9f8SAmelie Delaunay 	pctl->gpio_chip.ngpio = pctl->pctl_desc.npins;
7041490d9f8SAmelie Delaunay 	pctl->gpio_chip.can_sleep = true;
7051490d9f8SAmelie Delaunay 
7061de39b64SLinus Walleij 	girq = &pctl->gpio_chip.irq;
7077341944cSLinus Walleij 	gpio_irq_chip_set_chip(girq, &stmfx_pinctrl_irq_chip);
7081de39b64SLinus Walleij 	/* This will let us handle the parent IRQ in the driver */
7091de39b64SLinus Walleij 	girq->parent_handler = NULL;
7101de39b64SLinus Walleij 	girq->num_parents = 0;
7111de39b64SLinus Walleij 	girq->parents = NULL;
7121de39b64SLinus Walleij 	girq->default_type = IRQ_TYPE_NONE;
7131de39b64SLinus Walleij 	girq->handler = handle_bad_irq;
7141de39b64SLinus Walleij 	girq->threaded = true;
7151de39b64SLinus Walleij 
7161490d9f8SAmelie Delaunay 	ret = devm_gpiochip_add_data(pctl->dev, &pctl->gpio_chip, pctl);
7171490d9f8SAmelie Delaunay 	if (ret) {
7181490d9f8SAmelie Delaunay 		dev_err(pctl->dev, "gpio_chip registration failed\n");
7191490d9f8SAmelie Delaunay 		return ret;
7201490d9f8SAmelie Delaunay 	}
7211490d9f8SAmelie Delaunay 
7221490d9f8SAmelie Delaunay 	ret = stmfx_pinctrl_gpio_function_enable(pctl);
7231490d9f8SAmelie Delaunay 	if (ret)
7241490d9f8SAmelie Delaunay 		return ret;
7251490d9f8SAmelie Delaunay 
7261490d9f8SAmelie Delaunay 	ret = devm_request_threaded_irq(pctl->dev, irq, NULL,
7271490d9f8SAmelie Delaunay 					stmfx_pinctrl_irq_thread_fn,
7281490d9f8SAmelie Delaunay 					IRQF_ONESHOT,
7297341944cSLinus Walleij 					dev_name(pctl->dev), pctl);
7301490d9f8SAmelie Delaunay 	if (ret) {
7311490d9f8SAmelie Delaunay 		dev_err(pctl->dev, "cannot request irq%d\n", irq);
7321490d9f8SAmelie Delaunay 		return ret;
7331490d9f8SAmelie Delaunay 	}
7341490d9f8SAmelie Delaunay 
7351490d9f8SAmelie Delaunay 	dev_info(pctl->dev,
7361490d9f8SAmelie Delaunay 		 "%ld GPIOs available\n", hweight_long(pctl->gpio_valid_mask));
7371490d9f8SAmelie Delaunay 
7381490d9f8SAmelie Delaunay 	return 0;
7391490d9f8SAmelie Delaunay }
7401490d9f8SAmelie Delaunay 
stmfx_pinctrl_remove(struct platform_device * pdev)74186bc4c7fSUwe Kleine-König static void stmfx_pinctrl_remove(struct platform_device *pdev)
7421490d9f8SAmelie Delaunay {
7432fd215b8SAmelie Delaunay 	struct stmfx *stmfx = dev_get_drvdata(pdev->dev.parent);
74435df4b75SUwe Kleine-König 	int ret;
7451490d9f8SAmelie Delaunay 
74635df4b75SUwe Kleine-König 	ret = stmfx_function_disable(stmfx,
7471490d9f8SAmelie Delaunay 				     STMFX_FUNC_GPIO |
7481490d9f8SAmelie Delaunay 				     STMFX_FUNC_ALTGPIO_LOW |
7491490d9f8SAmelie Delaunay 				     STMFX_FUNC_ALTGPIO_HIGH);
75035df4b75SUwe Kleine-König 	if (ret)
75135df4b75SUwe Kleine-König 		dev_err(&pdev->dev, "Failed to disable pins (%pe)\n",
75235df4b75SUwe Kleine-König 			ERR_PTR(ret));
7531490d9f8SAmelie Delaunay }
7541490d9f8SAmelie Delaunay 
7551490d9f8SAmelie Delaunay #ifdef CONFIG_PM_SLEEP
stmfx_pinctrl_backup_regs(struct stmfx_pinctrl * pctl)7561490d9f8SAmelie Delaunay static int stmfx_pinctrl_backup_regs(struct stmfx_pinctrl *pctl)
7571490d9f8SAmelie Delaunay {
7581490d9f8SAmelie Delaunay 	int ret;
7591490d9f8SAmelie Delaunay 
7601490d9f8SAmelie Delaunay 	ret = regmap_bulk_read(pctl->stmfx->map, STMFX_REG_GPIO_STATE,
7611490d9f8SAmelie Delaunay 			       &pctl->bkp_gpio_state, NR_GPIO_REGS);
7621490d9f8SAmelie Delaunay 	if (ret)
7631490d9f8SAmelie Delaunay 		return ret;
7641490d9f8SAmelie Delaunay 	ret = regmap_bulk_read(pctl->stmfx->map, STMFX_REG_GPIO_DIR,
7651490d9f8SAmelie Delaunay 			       &pctl->bkp_gpio_dir, NR_GPIO_REGS);
7661490d9f8SAmelie Delaunay 	if (ret)
7671490d9f8SAmelie Delaunay 		return ret;
7681490d9f8SAmelie Delaunay 	ret = regmap_bulk_read(pctl->stmfx->map, STMFX_REG_GPIO_TYPE,
7691490d9f8SAmelie Delaunay 			       &pctl->bkp_gpio_type, NR_GPIO_REGS);
7701490d9f8SAmelie Delaunay 	if (ret)
7711490d9f8SAmelie Delaunay 		return ret;
7721490d9f8SAmelie Delaunay 	ret = regmap_bulk_read(pctl->stmfx->map, STMFX_REG_GPIO_PUPD,
7731490d9f8SAmelie Delaunay 			       &pctl->bkp_gpio_pupd, NR_GPIO_REGS);
7741490d9f8SAmelie Delaunay 	if (ret)
7751490d9f8SAmelie Delaunay 		return ret;
7761490d9f8SAmelie Delaunay 
7771490d9f8SAmelie Delaunay 	return 0;
7781490d9f8SAmelie Delaunay }
7791490d9f8SAmelie Delaunay 
stmfx_pinctrl_restore_regs(struct stmfx_pinctrl * pctl)7801490d9f8SAmelie Delaunay static int stmfx_pinctrl_restore_regs(struct stmfx_pinctrl *pctl)
7811490d9f8SAmelie Delaunay {
7821490d9f8SAmelie Delaunay 	int ret;
7831490d9f8SAmelie Delaunay 
7841490d9f8SAmelie Delaunay 	ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_GPIO_DIR,
7851490d9f8SAmelie Delaunay 				pctl->bkp_gpio_dir, NR_GPIO_REGS);
7861490d9f8SAmelie Delaunay 	if (ret)
7871490d9f8SAmelie Delaunay 		return ret;
7881490d9f8SAmelie Delaunay 	ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_GPIO_TYPE,
7891490d9f8SAmelie Delaunay 				pctl->bkp_gpio_type, NR_GPIO_REGS);
7901490d9f8SAmelie Delaunay 	if (ret)
7911490d9f8SAmelie Delaunay 		return ret;
7921490d9f8SAmelie Delaunay 	ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_GPIO_PUPD,
7931490d9f8SAmelie Delaunay 				pctl->bkp_gpio_pupd, NR_GPIO_REGS);
7941490d9f8SAmelie Delaunay 	if (ret)
7951490d9f8SAmelie Delaunay 		return ret;
7961490d9f8SAmelie Delaunay 	ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_GPO_SET,
7971490d9f8SAmelie Delaunay 				pctl->bkp_gpio_state, NR_GPIO_REGS);
7981490d9f8SAmelie Delaunay 	if (ret)
7991490d9f8SAmelie Delaunay 		return ret;
8001490d9f8SAmelie Delaunay 	ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_EVT,
8011490d9f8SAmelie Delaunay 				pctl->irq_gpi_evt, NR_GPIO_REGS);
8021490d9f8SAmelie Delaunay 	if (ret)
8031490d9f8SAmelie Delaunay 		return ret;
8041490d9f8SAmelie Delaunay 	ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_TYPE,
8051490d9f8SAmelie Delaunay 				pctl->irq_gpi_type, NR_GPIO_REGS);
8061490d9f8SAmelie Delaunay 	if (ret)
8071490d9f8SAmelie Delaunay 		return ret;
8081490d9f8SAmelie Delaunay 	ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_SRC,
8091490d9f8SAmelie Delaunay 				pctl->irq_gpi_src, NR_GPIO_REGS);
8101490d9f8SAmelie Delaunay 	if (ret)
8111490d9f8SAmelie Delaunay 		return ret;
8121490d9f8SAmelie Delaunay 
8131490d9f8SAmelie Delaunay 	return 0;
8141490d9f8SAmelie Delaunay }
8151490d9f8SAmelie Delaunay 
stmfx_pinctrl_suspend(struct device * dev)8161490d9f8SAmelie Delaunay static int stmfx_pinctrl_suspend(struct device *dev)
8171490d9f8SAmelie Delaunay {
8181490d9f8SAmelie Delaunay 	struct stmfx_pinctrl *pctl = dev_get_drvdata(dev);
8191490d9f8SAmelie Delaunay 	int ret;
8201490d9f8SAmelie Delaunay 
8211490d9f8SAmelie Delaunay 	ret = stmfx_pinctrl_backup_regs(pctl);
8221490d9f8SAmelie Delaunay 	if (ret) {
8231490d9f8SAmelie Delaunay 		dev_err(pctl->dev, "registers backup failure\n");
8241490d9f8SAmelie Delaunay 		return ret;
8251490d9f8SAmelie Delaunay 	}
8261490d9f8SAmelie Delaunay 
8271490d9f8SAmelie Delaunay 	return 0;
8281490d9f8SAmelie Delaunay }
8291490d9f8SAmelie Delaunay 
stmfx_pinctrl_resume(struct device * dev)8301490d9f8SAmelie Delaunay static int stmfx_pinctrl_resume(struct device *dev)
8311490d9f8SAmelie Delaunay {
8321490d9f8SAmelie Delaunay 	struct stmfx_pinctrl *pctl = dev_get_drvdata(dev);
8331490d9f8SAmelie Delaunay 	int ret;
8341490d9f8SAmelie Delaunay 
8351490d9f8SAmelie Delaunay 	ret = stmfx_pinctrl_restore_regs(pctl);
8361490d9f8SAmelie Delaunay 	if (ret) {
8371490d9f8SAmelie Delaunay 		dev_err(pctl->dev, "registers restoration failure\n");
8381490d9f8SAmelie Delaunay 		return ret;
8391490d9f8SAmelie Delaunay 	}
8401490d9f8SAmelie Delaunay 
8411490d9f8SAmelie Delaunay 	return 0;
8421490d9f8SAmelie Delaunay }
8431490d9f8SAmelie Delaunay #endif
8441490d9f8SAmelie Delaunay 
8451490d9f8SAmelie Delaunay static SIMPLE_DEV_PM_OPS(stmfx_pinctrl_dev_pm_ops,
8461490d9f8SAmelie Delaunay 			 stmfx_pinctrl_suspend, stmfx_pinctrl_resume);
8471490d9f8SAmelie Delaunay 
8481490d9f8SAmelie Delaunay static const struct of_device_id stmfx_pinctrl_of_match[] = {
8491490d9f8SAmelie Delaunay 	{ .compatible = "st,stmfx-0300-pinctrl", },
8501490d9f8SAmelie Delaunay 	{},
8511490d9f8SAmelie Delaunay };
8521490d9f8SAmelie Delaunay MODULE_DEVICE_TABLE(of, stmfx_pinctrl_of_match);
8531490d9f8SAmelie Delaunay 
8541490d9f8SAmelie Delaunay static struct platform_driver stmfx_pinctrl_driver = {
8551490d9f8SAmelie Delaunay 	.driver = {
8561490d9f8SAmelie Delaunay 		.name = "stmfx-pinctrl",
8571490d9f8SAmelie Delaunay 		.of_match_table = stmfx_pinctrl_of_match,
8581490d9f8SAmelie Delaunay 		.pm = &stmfx_pinctrl_dev_pm_ops,
8591490d9f8SAmelie Delaunay 	},
8601490d9f8SAmelie Delaunay 	.probe = stmfx_pinctrl_probe,
8611a075b1dSUwe Kleine-König 	.remove = stmfx_pinctrl_remove,
8621490d9f8SAmelie Delaunay };
8631490d9f8SAmelie Delaunay module_platform_driver(stmfx_pinctrl_driver);
8641490d9f8SAmelie Delaunay 
8651490d9f8SAmelie Delaunay MODULE_DESCRIPTION("STMFX pinctrl/GPIO driver");
8661490d9f8SAmelie Delaunay MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");
8671490d9f8SAmelie Delaunay MODULE_LICENSE("GPL v2");
868