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