162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for STMicroelectronics Multi-Function eXpander (STMFX) GPIO expander 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2019 STMicroelectronics 662306a36Sopenharmony_ci * Author(s): Amelie Delaunay <amelie.delaunay@st.com>. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/gpio/driver.h> 962306a36Sopenharmony_ci#include <linux/interrupt.h> 1062306a36Sopenharmony_ci#include <linux/mfd/stmfx.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/seq_file.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/pinctrl/pinconf.h> 1662306a36Sopenharmony_ci#include <linux/pinctrl/pinmux.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "core.h" 1962306a36Sopenharmony_ci#include "pinctrl-utils.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* GPIOs expander */ 2262306a36Sopenharmony_ci/* GPIO_STATE1 0x10, GPIO_STATE2 0x11, GPIO_STATE3 0x12 */ 2362306a36Sopenharmony_ci#define STMFX_REG_GPIO_STATE STMFX_REG_GPIO_STATE1 /* R */ 2462306a36Sopenharmony_ci/* GPIO_DIR1 0x60, GPIO_DIR2 0x61, GPIO_DIR3 0x63 */ 2562306a36Sopenharmony_ci#define STMFX_REG_GPIO_DIR STMFX_REG_GPIO_DIR1 /* RW */ 2662306a36Sopenharmony_ci/* GPIO_TYPE1 0x64, GPIO_TYPE2 0x65, GPIO_TYPE3 0x66 */ 2762306a36Sopenharmony_ci#define STMFX_REG_GPIO_TYPE STMFX_REG_GPIO_TYPE1 /* RW */ 2862306a36Sopenharmony_ci/* GPIO_PUPD1 0x68, GPIO_PUPD2 0x69, GPIO_PUPD3 0x6A */ 2962306a36Sopenharmony_ci#define STMFX_REG_GPIO_PUPD STMFX_REG_GPIO_PUPD1 /* RW */ 3062306a36Sopenharmony_ci/* GPO_SET1 0x6C, GPO_SET2 0x6D, GPO_SET3 0x6E */ 3162306a36Sopenharmony_ci#define STMFX_REG_GPO_SET STMFX_REG_GPO_SET1 /* RW */ 3262306a36Sopenharmony_ci/* GPO_CLR1 0x70, GPO_CLR2 0x71, GPO_CLR3 0x72 */ 3362306a36Sopenharmony_ci#define STMFX_REG_GPO_CLR STMFX_REG_GPO_CLR1 /* RW */ 3462306a36Sopenharmony_ci/* IRQ_GPI_SRC1 0x48, IRQ_GPI_SRC2 0x49, IRQ_GPI_SRC3 0x4A */ 3562306a36Sopenharmony_ci#define STMFX_REG_IRQ_GPI_SRC STMFX_REG_IRQ_GPI_SRC1 /* RW */ 3662306a36Sopenharmony_ci/* IRQ_GPI_EVT1 0x4C, IRQ_GPI_EVT2 0x4D, IRQ_GPI_EVT3 0x4E */ 3762306a36Sopenharmony_ci#define STMFX_REG_IRQ_GPI_EVT STMFX_REG_IRQ_GPI_EVT1 /* RW */ 3862306a36Sopenharmony_ci/* IRQ_GPI_TYPE1 0x50, IRQ_GPI_TYPE2 0x51, IRQ_GPI_TYPE3 0x52 */ 3962306a36Sopenharmony_ci#define STMFX_REG_IRQ_GPI_TYPE STMFX_REG_IRQ_GPI_TYPE1 /* RW */ 4062306a36Sopenharmony_ci/* IRQ_GPI_PENDING1 0x0C, IRQ_GPI_PENDING2 0x0D, IRQ_GPI_PENDING3 0x0E*/ 4162306a36Sopenharmony_ci#define STMFX_REG_IRQ_GPI_PENDING STMFX_REG_IRQ_GPI_PENDING1 /* R */ 4262306a36Sopenharmony_ci/* IRQ_GPI_ACK1 0x54, IRQ_GPI_ACK2 0x55, IRQ_GPI_ACK3 0x56 */ 4362306a36Sopenharmony_ci#define STMFX_REG_IRQ_GPI_ACK STMFX_REG_IRQ_GPI_ACK1 /* RW */ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define NR_GPIO_REGS 3 4662306a36Sopenharmony_ci#define NR_GPIOS_PER_REG 8 4762306a36Sopenharmony_ci#define get_reg(offset) ((offset) / NR_GPIOS_PER_REG) 4862306a36Sopenharmony_ci#define get_shift(offset) ((offset) % NR_GPIOS_PER_REG) 4962306a36Sopenharmony_ci#define get_mask(offset) (BIT(get_shift(offset))) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* 5262306a36Sopenharmony_ci * STMFX pinctrl can have up to 24 pins if STMFX other functions are not used. 5362306a36Sopenharmony_ci * Pins availability is managed thanks to gpio-ranges property. 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_cistatic const struct pinctrl_pin_desc stmfx_pins[] = { 5662306a36Sopenharmony_ci PINCTRL_PIN(0, "gpio0"), 5762306a36Sopenharmony_ci PINCTRL_PIN(1, "gpio1"), 5862306a36Sopenharmony_ci PINCTRL_PIN(2, "gpio2"), 5962306a36Sopenharmony_ci PINCTRL_PIN(3, "gpio3"), 6062306a36Sopenharmony_ci PINCTRL_PIN(4, "gpio4"), 6162306a36Sopenharmony_ci PINCTRL_PIN(5, "gpio5"), 6262306a36Sopenharmony_ci PINCTRL_PIN(6, "gpio6"), 6362306a36Sopenharmony_ci PINCTRL_PIN(7, "gpio7"), 6462306a36Sopenharmony_ci PINCTRL_PIN(8, "gpio8"), 6562306a36Sopenharmony_ci PINCTRL_PIN(9, "gpio9"), 6662306a36Sopenharmony_ci PINCTRL_PIN(10, "gpio10"), 6762306a36Sopenharmony_ci PINCTRL_PIN(11, "gpio11"), 6862306a36Sopenharmony_ci PINCTRL_PIN(12, "gpio12"), 6962306a36Sopenharmony_ci PINCTRL_PIN(13, "gpio13"), 7062306a36Sopenharmony_ci PINCTRL_PIN(14, "gpio14"), 7162306a36Sopenharmony_ci PINCTRL_PIN(15, "gpio15"), 7262306a36Sopenharmony_ci PINCTRL_PIN(16, "agpio0"), 7362306a36Sopenharmony_ci PINCTRL_PIN(17, "agpio1"), 7462306a36Sopenharmony_ci PINCTRL_PIN(18, "agpio2"), 7562306a36Sopenharmony_ci PINCTRL_PIN(19, "agpio3"), 7662306a36Sopenharmony_ci PINCTRL_PIN(20, "agpio4"), 7762306a36Sopenharmony_ci PINCTRL_PIN(21, "agpio5"), 7862306a36Sopenharmony_ci PINCTRL_PIN(22, "agpio6"), 7962306a36Sopenharmony_ci PINCTRL_PIN(23, "agpio7"), 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistruct stmfx_pinctrl { 8362306a36Sopenharmony_ci struct device *dev; 8462306a36Sopenharmony_ci struct stmfx *stmfx; 8562306a36Sopenharmony_ci struct pinctrl_dev *pctl_dev; 8662306a36Sopenharmony_ci struct pinctrl_desc pctl_desc; 8762306a36Sopenharmony_ci struct gpio_chip gpio_chip; 8862306a36Sopenharmony_ci struct mutex lock; /* IRQ bus lock */ 8962306a36Sopenharmony_ci unsigned long gpio_valid_mask; 9062306a36Sopenharmony_ci /* Cache of IRQ_GPI_* registers for bus_lock */ 9162306a36Sopenharmony_ci u8 irq_gpi_src[NR_GPIO_REGS]; 9262306a36Sopenharmony_ci u8 irq_gpi_type[NR_GPIO_REGS]; 9362306a36Sopenharmony_ci u8 irq_gpi_evt[NR_GPIO_REGS]; 9462306a36Sopenharmony_ci u8 irq_toggle_edge[NR_GPIO_REGS]; 9562306a36Sopenharmony_ci#ifdef CONFIG_PM 9662306a36Sopenharmony_ci /* Backup of GPIO_* registers for suspend/resume */ 9762306a36Sopenharmony_ci u8 bkp_gpio_state[NR_GPIO_REGS]; 9862306a36Sopenharmony_ci u8 bkp_gpio_dir[NR_GPIO_REGS]; 9962306a36Sopenharmony_ci u8 bkp_gpio_type[NR_GPIO_REGS]; 10062306a36Sopenharmony_ci u8 bkp_gpio_pupd[NR_GPIO_REGS]; 10162306a36Sopenharmony_ci#endif 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int stmfx_gpio_get(struct gpio_chip *gc, unsigned int offset) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct stmfx_pinctrl *pctl = gpiochip_get_data(gc); 10762306a36Sopenharmony_ci u32 reg = STMFX_REG_GPIO_STATE + get_reg(offset); 10862306a36Sopenharmony_ci u32 mask = get_mask(offset); 10962306a36Sopenharmony_ci u32 value; 11062306a36Sopenharmony_ci int ret; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci ret = regmap_read(pctl->stmfx->map, reg, &value); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return ret ? ret : !!(value & mask); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void stmfx_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct stmfx_pinctrl *pctl = gpiochip_get_data(gc); 12062306a36Sopenharmony_ci u32 reg = value ? STMFX_REG_GPO_SET : STMFX_REG_GPO_CLR; 12162306a36Sopenharmony_ci u32 mask = get_mask(offset); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci regmap_write_bits(pctl->stmfx->map, reg + get_reg(offset), 12462306a36Sopenharmony_ci mask, mask); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic int stmfx_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct stmfx_pinctrl *pctl = gpiochip_get_data(gc); 13062306a36Sopenharmony_ci u32 reg = STMFX_REG_GPIO_DIR + get_reg(offset); 13162306a36Sopenharmony_ci u32 mask = get_mask(offset); 13262306a36Sopenharmony_ci u32 val; 13362306a36Sopenharmony_ci int ret; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci ret = regmap_read(pctl->stmfx->map, reg, &val); 13662306a36Sopenharmony_ci /* 13762306a36Sopenharmony_ci * On stmfx, gpio pins direction is (0)input, (1)output. 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_ci if (ret) 14062306a36Sopenharmony_ci return ret; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (val & mask) 14362306a36Sopenharmony_ci return GPIO_LINE_DIRECTION_OUT; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return GPIO_LINE_DIRECTION_IN; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic int stmfx_gpio_direction_input(struct gpio_chip *gc, unsigned int offset) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct stmfx_pinctrl *pctl = gpiochip_get_data(gc); 15162306a36Sopenharmony_ci u32 reg = STMFX_REG_GPIO_DIR + get_reg(offset); 15262306a36Sopenharmony_ci u32 mask = get_mask(offset); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return regmap_write_bits(pctl->stmfx->map, reg, mask, 0); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int stmfx_gpio_direction_output(struct gpio_chip *gc, 15862306a36Sopenharmony_ci unsigned int offset, int value) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct stmfx_pinctrl *pctl = gpiochip_get_data(gc); 16162306a36Sopenharmony_ci u32 reg = STMFX_REG_GPIO_DIR + get_reg(offset); 16262306a36Sopenharmony_ci u32 mask = get_mask(offset); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci stmfx_gpio_set(gc, offset, value); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return regmap_write_bits(pctl->stmfx->map, reg, mask, mask); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int stmfx_pinconf_get_pupd(struct stmfx_pinctrl *pctl, 17062306a36Sopenharmony_ci unsigned int offset) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci u32 reg = STMFX_REG_GPIO_PUPD + get_reg(offset); 17362306a36Sopenharmony_ci u32 pupd, mask = get_mask(offset); 17462306a36Sopenharmony_ci int ret; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci ret = regmap_read(pctl->stmfx->map, reg, &pupd); 17762306a36Sopenharmony_ci if (ret) 17862306a36Sopenharmony_ci return ret; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci return !!(pupd & mask); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic int stmfx_pinconf_set_pupd(struct stmfx_pinctrl *pctl, 18462306a36Sopenharmony_ci unsigned int offset, u32 pupd) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci u32 reg = STMFX_REG_GPIO_PUPD + get_reg(offset); 18762306a36Sopenharmony_ci u32 mask = get_mask(offset); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return regmap_write_bits(pctl->stmfx->map, reg, mask, pupd ? mask : 0); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic int stmfx_pinconf_get_type(struct stmfx_pinctrl *pctl, 19362306a36Sopenharmony_ci unsigned int offset) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci u32 reg = STMFX_REG_GPIO_TYPE + get_reg(offset); 19662306a36Sopenharmony_ci u32 type, mask = get_mask(offset); 19762306a36Sopenharmony_ci int ret; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci ret = regmap_read(pctl->stmfx->map, reg, &type); 20062306a36Sopenharmony_ci if (ret) 20162306a36Sopenharmony_ci return ret; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return !!(type & mask); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic int stmfx_pinconf_set_type(struct stmfx_pinctrl *pctl, 20762306a36Sopenharmony_ci unsigned int offset, u32 type) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci u32 reg = STMFX_REG_GPIO_TYPE + get_reg(offset); 21062306a36Sopenharmony_ci u32 mask = get_mask(offset); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return regmap_write_bits(pctl->stmfx->map, reg, mask, type ? mask : 0); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic int stmfx_pinconf_get(struct pinctrl_dev *pctldev, 21662306a36Sopenharmony_ci unsigned int pin, unsigned long *config) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci struct stmfx_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); 21962306a36Sopenharmony_ci u32 param = pinconf_to_config_param(*config); 22062306a36Sopenharmony_ci struct pinctrl_gpio_range *range; 22162306a36Sopenharmony_ci u32 arg = 0; 22262306a36Sopenharmony_ci int ret, dir, type, pupd; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci range = pinctrl_find_gpio_range_from_pin_nolock(pctldev, pin); 22562306a36Sopenharmony_ci if (!range) 22662306a36Sopenharmony_ci return -EINVAL; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci dir = stmfx_gpio_get_direction(&pctl->gpio_chip, pin); 22962306a36Sopenharmony_ci if (dir < 0) 23062306a36Sopenharmony_ci return dir; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci /* 23362306a36Sopenharmony_ci * Currently the gpiolib IN is 1 and OUT is 0 but let's not count 23462306a36Sopenharmony_ci * on it just to be on the safe side also in the future :) 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_ci dir = (dir == GPIO_LINE_DIRECTION_IN) ? 1 : 0; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci type = stmfx_pinconf_get_type(pctl, pin); 23962306a36Sopenharmony_ci if (type < 0) 24062306a36Sopenharmony_ci return type; 24162306a36Sopenharmony_ci pupd = stmfx_pinconf_get_pupd(pctl, pin); 24262306a36Sopenharmony_ci if (pupd < 0) 24362306a36Sopenharmony_ci return pupd; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci switch (param) { 24662306a36Sopenharmony_ci case PIN_CONFIG_BIAS_DISABLE: 24762306a36Sopenharmony_ci if ((!dir && (!type || !pupd)) || (dir && !type)) 24862306a36Sopenharmony_ci arg = 1; 24962306a36Sopenharmony_ci break; 25062306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_DOWN: 25162306a36Sopenharmony_ci if (dir && type && !pupd) 25262306a36Sopenharmony_ci arg = 1; 25362306a36Sopenharmony_ci break; 25462306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 25562306a36Sopenharmony_ci if (type && pupd) 25662306a36Sopenharmony_ci arg = 1; 25762306a36Sopenharmony_ci break; 25862306a36Sopenharmony_ci case PIN_CONFIG_DRIVE_OPEN_DRAIN: 25962306a36Sopenharmony_ci if ((!dir && type) || (dir && !type)) 26062306a36Sopenharmony_ci arg = 1; 26162306a36Sopenharmony_ci break; 26262306a36Sopenharmony_ci case PIN_CONFIG_DRIVE_PUSH_PULL: 26362306a36Sopenharmony_ci if ((!dir && !type) || (dir && type)) 26462306a36Sopenharmony_ci arg = 1; 26562306a36Sopenharmony_ci break; 26662306a36Sopenharmony_ci case PIN_CONFIG_OUTPUT: 26762306a36Sopenharmony_ci if (dir) 26862306a36Sopenharmony_ci return -EINVAL; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci ret = stmfx_gpio_get(&pctl->gpio_chip, pin); 27162306a36Sopenharmony_ci if (ret < 0) 27262306a36Sopenharmony_ci return ret; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci arg = ret; 27562306a36Sopenharmony_ci break; 27662306a36Sopenharmony_ci default: 27762306a36Sopenharmony_ci return -ENOTSUPP; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci *config = pinconf_to_config_packed(param, arg); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return 0; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic int stmfx_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, 28662306a36Sopenharmony_ci unsigned long *configs, unsigned int num_configs) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci struct stmfx_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); 28962306a36Sopenharmony_ci struct pinctrl_gpio_range *range; 29062306a36Sopenharmony_ci enum pin_config_param param; 29162306a36Sopenharmony_ci u32 arg; 29262306a36Sopenharmony_ci int i, ret; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci range = pinctrl_find_gpio_range_from_pin_nolock(pctldev, pin); 29562306a36Sopenharmony_ci if (!range) { 29662306a36Sopenharmony_ci dev_err(pctldev->dev, "pin %d is not available\n", pin); 29762306a36Sopenharmony_ci return -EINVAL; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci for (i = 0; i < num_configs; i++) { 30162306a36Sopenharmony_ci param = pinconf_to_config_param(configs[i]); 30262306a36Sopenharmony_ci arg = pinconf_to_config_argument(configs[i]); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci switch (param) { 30562306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: 30662306a36Sopenharmony_ci case PIN_CONFIG_BIAS_DISABLE: 30762306a36Sopenharmony_ci case PIN_CONFIG_DRIVE_PUSH_PULL: 30862306a36Sopenharmony_ci ret = stmfx_pinconf_set_type(pctl, pin, 0); 30962306a36Sopenharmony_ci if (ret) 31062306a36Sopenharmony_ci return ret; 31162306a36Sopenharmony_ci break; 31262306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_DOWN: 31362306a36Sopenharmony_ci ret = stmfx_pinconf_set_type(pctl, pin, 1); 31462306a36Sopenharmony_ci if (ret) 31562306a36Sopenharmony_ci return ret; 31662306a36Sopenharmony_ci ret = stmfx_pinconf_set_pupd(pctl, pin, 0); 31762306a36Sopenharmony_ci if (ret) 31862306a36Sopenharmony_ci return ret; 31962306a36Sopenharmony_ci break; 32062306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 32162306a36Sopenharmony_ci ret = stmfx_pinconf_set_type(pctl, pin, 1); 32262306a36Sopenharmony_ci if (ret) 32362306a36Sopenharmony_ci return ret; 32462306a36Sopenharmony_ci ret = stmfx_pinconf_set_pupd(pctl, pin, 1); 32562306a36Sopenharmony_ci if (ret) 32662306a36Sopenharmony_ci return ret; 32762306a36Sopenharmony_ci break; 32862306a36Sopenharmony_ci case PIN_CONFIG_DRIVE_OPEN_DRAIN: 32962306a36Sopenharmony_ci ret = stmfx_pinconf_set_type(pctl, pin, 1); 33062306a36Sopenharmony_ci if (ret) 33162306a36Sopenharmony_ci return ret; 33262306a36Sopenharmony_ci break; 33362306a36Sopenharmony_ci case PIN_CONFIG_OUTPUT: 33462306a36Sopenharmony_ci ret = stmfx_gpio_direction_output(&pctl->gpio_chip, 33562306a36Sopenharmony_ci pin, arg); 33662306a36Sopenharmony_ci if (ret) 33762306a36Sopenharmony_ci return ret; 33862306a36Sopenharmony_ci break; 33962306a36Sopenharmony_ci default: 34062306a36Sopenharmony_ci return -ENOTSUPP; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci return 0; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic void stmfx_pinconf_dbg_show(struct pinctrl_dev *pctldev, 34862306a36Sopenharmony_ci struct seq_file *s, unsigned int offset) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci struct stmfx_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); 35162306a36Sopenharmony_ci struct pinctrl_gpio_range *range; 35262306a36Sopenharmony_ci int dir, type, pupd, val; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci range = pinctrl_find_gpio_range_from_pin_nolock(pctldev, offset); 35562306a36Sopenharmony_ci if (!range) 35662306a36Sopenharmony_ci return; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci dir = stmfx_gpio_get_direction(&pctl->gpio_chip, offset); 35962306a36Sopenharmony_ci if (dir < 0) 36062306a36Sopenharmony_ci return; 36162306a36Sopenharmony_ci type = stmfx_pinconf_get_type(pctl, offset); 36262306a36Sopenharmony_ci if (type < 0) 36362306a36Sopenharmony_ci return; 36462306a36Sopenharmony_ci pupd = stmfx_pinconf_get_pupd(pctl, offset); 36562306a36Sopenharmony_ci if (pupd < 0) 36662306a36Sopenharmony_ci return; 36762306a36Sopenharmony_ci val = stmfx_gpio_get(&pctl->gpio_chip, offset); 36862306a36Sopenharmony_ci if (val < 0) 36962306a36Sopenharmony_ci return; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (dir == GPIO_LINE_DIRECTION_OUT) { 37262306a36Sopenharmony_ci seq_printf(s, "output %s ", val ? "high" : "low"); 37362306a36Sopenharmony_ci if (type) 37462306a36Sopenharmony_ci seq_printf(s, "open drain %s internal pull-up ", 37562306a36Sopenharmony_ci pupd ? "with" : "without"); 37662306a36Sopenharmony_ci else 37762306a36Sopenharmony_ci seq_puts(s, "push pull no pull "); 37862306a36Sopenharmony_ci } else { 37962306a36Sopenharmony_ci seq_printf(s, "input %s ", val ? "high" : "low"); 38062306a36Sopenharmony_ci if (type) 38162306a36Sopenharmony_ci seq_printf(s, "with internal pull-%s ", 38262306a36Sopenharmony_ci pupd ? "up" : "down"); 38362306a36Sopenharmony_ci else 38462306a36Sopenharmony_ci seq_printf(s, "%s ", pupd ? "floating" : "analog"); 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic const struct pinconf_ops stmfx_pinconf_ops = { 38962306a36Sopenharmony_ci .pin_config_get = stmfx_pinconf_get, 39062306a36Sopenharmony_ci .pin_config_set = stmfx_pinconf_set, 39162306a36Sopenharmony_ci .pin_config_dbg_show = stmfx_pinconf_dbg_show, 39262306a36Sopenharmony_ci}; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic int stmfx_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci return 0; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic const char *stmfx_pinctrl_get_group_name(struct pinctrl_dev *pctldev, 40062306a36Sopenharmony_ci unsigned int selector) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci return NULL; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic int stmfx_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, 40662306a36Sopenharmony_ci unsigned int selector, 40762306a36Sopenharmony_ci const unsigned int **pins, 40862306a36Sopenharmony_ci unsigned int *num_pins) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci return -ENOTSUPP; 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic const struct pinctrl_ops stmfx_pinctrl_ops = { 41462306a36Sopenharmony_ci .get_groups_count = stmfx_pinctrl_get_groups_count, 41562306a36Sopenharmony_ci .get_group_name = stmfx_pinctrl_get_group_name, 41662306a36Sopenharmony_ci .get_group_pins = stmfx_pinctrl_get_group_pins, 41762306a36Sopenharmony_ci .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, 41862306a36Sopenharmony_ci .dt_free_map = pinctrl_utils_free_map, 41962306a36Sopenharmony_ci}; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic void stmfx_pinctrl_irq_mask(struct irq_data *data) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data); 42462306a36Sopenharmony_ci struct stmfx_pinctrl *pctl = gpiochip_get_data(gpio_chip); 42562306a36Sopenharmony_ci u32 reg = get_reg(data->hwirq); 42662306a36Sopenharmony_ci u32 mask = get_mask(data->hwirq); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci pctl->irq_gpi_src[reg] &= ~mask; 42962306a36Sopenharmony_ci gpiochip_disable_irq(gpio_chip, irqd_to_hwirq(data)); 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic void stmfx_pinctrl_irq_unmask(struct irq_data *data) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data); 43562306a36Sopenharmony_ci struct stmfx_pinctrl *pctl = gpiochip_get_data(gpio_chip); 43662306a36Sopenharmony_ci u32 reg = get_reg(data->hwirq); 43762306a36Sopenharmony_ci u32 mask = get_mask(data->hwirq); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci gpiochip_enable_irq(gpio_chip, irqd_to_hwirq(data)); 44062306a36Sopenharmony_ci pctl->irq_gpi_src[reg] |= mask; 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic int stmfx_pinctrl_irq_set_type(struct irq_data *data, unsigned int type) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data); 44662306a36Sopenharmony_ci struct stmfx_pinctrl *pctl = gpiochip_get_data(gpio_chip); 44762306a36Sopenharmony_ci u32 reg = get_reg(data->hwirq); 44862306a36Sopenharmony_ci u32 mask = get_mask(data->hwirq); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (type == IRQ_TYPE_NONE) 45162306a36Sopenharmony_ci return -EINVAL; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (type & IRQ_TYPE_EDGE_BOTH) { 45462306a36Sopenharmony_ci pctl->irq_gpi_evt[reg] |= mask; 45562306a36Sopenharmony_ci irq_set_handler_locked(data, handle_edge_irq); 45662306a36Sopenharmony_ci } else { 45762306a36Sopenharmony_ci pctl->irq_gpi_evt[reg] &= ~mask; 45862306a36Sopenharmony_ci irq_set_handler_locked(data, handle_level_irq); 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if ((type & IRQ_TYPE_EDGE_RISING) || (type & IRQ_TYPE_LEVEL_HIGH)) 46262306a36Sopenharmony_ci pctl->irq_gpi_type[reg] |= mask; 46362306a36Sopenharmony_ci else 46462306a36Sopenharmony_ci pctl->irq_gpi_type[reg] &= ~mask; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci /* 46762306a36Sopenharmony_ci * In case of (type & IRQ_TYPE_EDGE_BOTH), we need to know current 46862306a36Sopenharmony_ci * GPIO value to set the right edge trigger. But in atomic context 46962306a36Sopenharmony_ci * here we can't access registers over I2C. That's why (type & 47062306a36Sopenharmony_ci * IRQ_TYPE_EDGE_BOTH) will be managed in .irq_sync_unlock. 47162306a36Sopenharmony_ci */ 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) 47462306a36Sopenharmony_ci pctl->irq_toggle_edge[reg] |= mask; 47562306a36Sopenharmony_ci else 47662306a36Sopenharmony_ci pctl->irq_toggle_edge[reg] &= mask; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci return 0; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic void stmfx_pinctrl_irq_bus_lock(struct irq_data *data) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data); 48462306a36Sopenharmony_ci struct stmfx_pinctrl *pctl = gpiochip_get_data(gpio_chip); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci mutex_lock(&pctl->lock); 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic void stmfx_pinctrl_irq_bus_sync_unlock(struct irq_data *data) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data); 49262306a36Sopenharmony_ci struct stmfx_pinctrl *pctl = gpiochip_get_data(gpio_chip); 49362306a36Sopenharmony_ci u32 reg = get_reg(data->hwirq); 49462306a36Sopenharmony_ci u32 mask = get_mask(data->hwirq); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* 49762306a36Sopenharmony_ci * In case of IRQ_TYPE_EDGE_BOTH), read the current GPIO value 49862306a36Sopenharmony_ci * (this couldn't be done in .irq_set_type because of atomic context) 49962306a36Sopenharmony_ci * to set the right irq trigger type. 50062306a36Sopenharmony_ci */ 50162306a36Sopenharmony_ci if (pctl->irq_toggle_edge[reg] & mask) { 50262306a36Sopenharmony_ci if (stmfx_gpio_get(gpio_chip, data->hwirq)) 50362306a36Sopenharmony_ci pctl->irq_gpi_type[reg] &= ~mask; 50462306a36Sopenharmony_ci else 50562306a36Sopenharmony_ci pctl->irq_gpi_type[reg] |= mask; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_EVT, 50962306a36Sopenharmony_ci pctl->irq_gpi_evt, NR_GPIO_REGS); 51062306a36Sopenharmony_ci regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_TYPE, 51162306a36Sopenharmony_ci pctl->irq_gpi_type, NR_GPIO_REGS); 51262306a36Sopenharmony_ci regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_SRC, 51362306a36Sopenharmony_ci pctl->irq_gpi_src, NR_GPIO_REGS); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci mutex_unlock(&pctl->lock); 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cistatic int stmfx_gpio_irq_request_resources(struct irq_data *data) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data); 52162306a36Sopenharmony_ci int ret; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci ret = stmfx_gpio_direction_input(gpio_chip, data->hwirq); 52462306a36Sopenharmony_ci if (ret) 52562306a36Sopenharmony_ci return ret; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci return gpiochip_reqres_irq(gpio_chip, data->hwirq); 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistatic void stmfx_gpio_irq_release_resources(struct irq_data *data) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci return gpiochip_relres_irq(gpio_chip, data->hwirq); 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cistatic void stmfx_pinctrl_irq_toggle_trigger(struct stmfx_pinctrl *pctl, 53862306a36Sopenharmony_ci unsigned int offset) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci u32 reg = get_reg(offset); 54162306a36Sopenharmony_ci u32 mask = get_mask(offset); 54262306a36Sopenharmony_ci int val; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (!(pctl->irq_toggle_edge[reg] & mask)) 54562306a36Sopenharmony_ci return; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci val = stmfx_gpio_get(&pctl->gpio_chip, offset); 54862306a36Sopenharmony_ci if (val < 0) 54962306a36Sopenharmony_ci return; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (val) { 55262306a36Sopenharmony_ci pctl->irq_gpi_type[reg] &= mask; 55362306a36Sopenharmony_ci regmap_write_bits(pctl->stmfx->map, 55462306a36Sopenharmony_ci STMFX_REG_IRQ_GPI_TYPE + reg, 55562306a36Sopenharmony_ci mask, 0); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci } else { 55862306a36Sopenharmony_ci pctl->irq_gpi_type[reg] |= mask; 55962306a36Sopenharmony_ci regmap_write_bits(pctl->stmfx->map, 56062306a36Sopenharmony_ci STMFX_REG_IRQ_GPI_TYPE + reg, 56162306a36Sopenharmony_ci mask, mask); 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_cistatic irqreturn_t stmfx_pinctrl_irq_thread_fn(int irq, void *dev_id) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci struct stmfx_pinctrl *pctl = (struct stmfx_pinctrl *)dev_id; 56862306a36Sopenharmony_ci struct gpio_chip *gc = &pctl->gpio_chip; 56962306a36Sopenharmony_ci u8 pending[NR_GPIO_REGS]; 57062306a36Sopenharmony_ci u8 src[NR_GPIO_REGS] = {0, 0, 0}; 57162306a36Sopenharmony_ci unsigned long n, status; 57262306a36Sopenharmony_ci int i, ret; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci ret = regmap_bulk_read(pctl->stmfx->map, STMFX_REG_IRQ_GPI_PENDING, 57562306a36Sopenharmony_ci &pending, NR_GPIO_REGS); 57662306a36Sopenharmony_ci if (ret) 57762306a36Sopenharmony_ci return IRQ_NONE; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_SRC, 58062306a36Sopenharmony_ci src, NR_GPIO_REGS); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci BUILD_BUG_ON(NR_GPIO_REGS > sizeof(status)); 58362306a36Sopenharmony_ci for (i = 0, status = 0; i < NR_GPIO_REGS; i++) 58462306a36Sopenharmony_ci status |= (unsigned long)pending[i] << (i * 8); 58562306a36Sopenharmony_ci for_each_set_bit(n, &status, gc->ngpio) { 58662306a36Sopenharmony_ci handle_nested_irq(irq_find_mapping(gc->irq.domain, n)); 58762306a36Sopenharmony_ci stmfx_pinctrl_irq_toggle_trigger(pctl, n); 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_SRC, 59162306a36Sopenharmony_ci pctl->irq_gpi_src, NR_GPIO_REGS); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci return IRQ_HANDLED; 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic void stmfx_pinctrl_irq_print_chip(struct irq_data *d, struct seq_file *p) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(d); 59962306a36Sopenharmony_ci struct stmfx_pinctrl *pctl = gpiochip_get_data(gpio_chip); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci seq_printf(p, dev_name(pctl->dev)); 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic const struct irq_chip stmfx_pinctrl_irq_chip = { 60562306a36Sopenharmony_ci .irq_mask = stmfx_pinctrl_irq_mask, 60662306a36Sopenharmony_ci .irq_unmask = stmfx_pinctrl_irq_unmask, 60762306a36Sopenharmony_ci .irq_set_type = stmfx_pinctrl_irq_set_type, 60862306a36Sopenharmony_ci .irq_bus_lock = stmfx_pinctrl_irq_bus_lock, 60962306a36Sopenharmony_ci .irq_bus_sync_unlock = stmfx_pinctrl_irq_bus_sync_unlock, 61062306a36Sopenharmony_ci .irq_request_resources = stmfx_gpio_irq_request_resources, 61162306a36Sopenharmony_ci .irq_release_resources = stmfx_gpio_irq_release_resources, 61262306a36Sopenharmony_ci .irq_print_chip = stmfx_pinctrl_irq_print_chip, 61362306a36Sopenharmony_ci .flags = IRQCHIP_IMMUTABLE, 61462306a36Sopenharmony_ci}; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic int stmfx_pinctrl_gpio_function_enable(struct stmfx_pinctrl *pctl) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci struct pinctrl_gpio_range *gpio_range; 61962306a36Sopenharmony_ci struct pinctrl_dev *pctl_dev = pctl->pctl_dev; 62062306a36Sopenharmony_ci u32 func = STMFX_FUNC_GPIO; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci pctl->gpio_valid_mask = GENMASK(15, 0); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci gpio_range = pinctrl_find_gpio_range_from_pin(pctl_dev, 16); 62562306a36Sopenharmony_ci if (gpio_range) { 62662306a36Sopenharmony_ci func |= STMFX_FUNC_ALTGPIO_LOW; 62762306a36Sopenharmony_ci pctl->gpio_valid_mask |= GENMASK(19, 16); 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci gpio_range = pinctrl_find_gpio_range_from_pin(pctl_dev, 20); 63162306a36Sopenharmony_ci if (gpio_range) { 63262306a36Sopenharmony_ci func |= STMFX_FUNC_ALTGPIO_HIGH; 63362306a36Sopenharmony_ci pctl->gpio_valid_mask |= GENMASK(23, 20); 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci return stmfx_function_enable(pctl->stmfx, func); 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_cistatic int stmfx_pinctrl_probe(struct platform_device *pdev) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci struct stmfx *stmfx = dev_get_drvdata(pdev->dev.parent); 64262306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 64362306a36Sopenharmony_ci struct stmfx_pinctrl *pctl; 64462306a36Sopenharmony_ci struct gpio_irq_chip *girq; 64562306a36Sopenharmony_ci int irq, ret; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci pctl = devm_kzalloc(stmfx->dev, sizeof(*pctl), GFP_KERNEL); 64862306a36Sopenharmony_ci if (!pctl) 64962306a36Sopenharmony_ci return -ENOMEM; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci platform_set_drvdata(pdev, pctl); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci pctl->dev = &pdev->dev; 65462306a36Sopenharmony_ci pctl->stmfx = stmfx; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (!of_property_present(np, "gpio-ranges")) { 65762306a36Sopenharmony_ci dev_err(pctl->dev, "missing required gpio-ranges property\n"); 65862306a36Sopenharmony_ci return -EINVAL; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 66262306a36Sopenharmony_ci if (irq < 0) 66362306a36Sopenharmony_ci return irq; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci mutex_init(&pctl->lock); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci /* Register pin controller */ 66862306a36Sopenharmony_ci pctl->pctl_desc.name = "stmfx-pinctrl"; 66962306a36Sopenharmony_ci pctl->pctl_desc.pctlops = &stmfx_pinctrl_ops; 67062306a36Sopenharmony_ci pctl->pctl_desc.confops = &stmfx_pinconf_ops; 67162306a36Sopenharmony_ci pctl->pctl_desc.pins = stmfx_pins; 67262306a36Sopenharmony_ci pctl->pctl_desc.npins = ARRAY_SIZE(stmfx_pins); 67362306a36Sopenharmony_ci pctl->pctl_desc.owner = THIS_MODULE; 67462306a36Sopenharmony_ci pctl->pctl_desc.link_consumers = true; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci ret = devm_pinctrl_register_and_init(pctl->dev, &pctl->pctl_desc, 67762306a36Sopenharmony_ci pctl, &pctl->pctl_dev); 67862306a36Sopenharmony_ci if (ret) { 67962306a36Sopenharmony_ci dev_err(pctl->dev, "pinctrl registration failed\n"); 68062306a36Sopenharmony_ci return ret; 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci ret = pinctrl_enable(pctl->pctl_dev); 68462306a36Sopenharmony_ci if (ret) { 68562306a36Sopenharmony_ci dev_err(pctl->dev, "pinctrl enable failed\n"); 68662306a36Sopenharmony_ci return ret; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci /* Register gpio controller */ 69062306a36Sopenharmony_ci pctl->gpio_chip.label = "stmfx-gpio"; 69162306a36Sopenharmony_ci pctl->gpio_chip.parent = pctl->dev; 69262306a36Sopenharmony_ci pctl->gpio_chip.get_direction = stmfx_gpio_get_direction; 69362306a36Sopenharmony_ci pctl->gpio_chip.direction_input = stmfx_gpio_direction_input; 69462306a36Sopenharmony_ci pctl->gpio_chip.direction_output = stmfx_gpio_direction_output; 69562306a36Sopenharmony_ci pctl->gpio_chip.get = stmfx_gpio_get; 69662306a36Sopenharmony_ci pctl->gpio_chip.set = stmfx_gpio_set; 69762306a36Sopenharmony_ci pctl->gpio_chip.set_config = gpiochip_generic_config; 69862306a36Sopenharmony_ci pctl->gpio_chip.base = -1; 69962306a36Sopenharmony_ci pctl->gpio_chip.ngpio = pctl->pctl_desc.npins; 70062306a36Sopenharmony_ci pctl->gpio_chip.can_sleep = true; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci girq = &pctl->gpio_chip.irq; 70362306a36Sopenharmony_ci gpio_irq_chip_set_chip(girq, &stmfx_pinctrl_irq_chip); 70462306a36Sopenharmony_ci /* This will let us handle the parent IRQ in the driver */ 70562306a36Sopenharmony_ci girq->parent_handler = NULL; 70662306a36Sopenharmony_ci girq->num_parents = 0; 70762306a36Sopenharmony_ci girq->parents = NULL; 70862306a36Sopenharmony_ci girq->default_type = IRQ_TYPE_NONE; 70962306a36Sopenharmony_ci girq->handler = handle_bad_irq; 71062306a36Sopenharmony_ci girq->threaded = true; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci ret = devm_gpiochip_add_data(pctl->dev, &pctl->gpio_chip, pctl); 71362306a36Sopenharmony_ci if (ret) { 71462306a36Sopenharmony_ci dev_err(pctl->dev, "gpio_chip registration failed\n"); 71562306a36Sopenharmony_ci return ret; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci ret = stmfx_pinctrl_gpio_function_enable(pctl); 71962306a36Sopenharmony_ci if (ret) 72062306a36Sopenharmony_ci return ret; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci ret = devm_request_threaded_irq(pctl->dev, irq, NULL, 72362306a36Sopenharmony_ci stmfx_pinctrl_irq_thread_fn, 72462306a36Sopenharmony_ci IRQF_ONESHOT, 72562306a36Sopenharmony_ci dev_name(pctl->dev), pctl); 72662306a36Sopenharmony_ci if (ret) { 72762306a36Sopenharmony_ci dev_err(pctl->dev, "cannot request irq%d\n", irq); 72862306a36Sopenharmony_ci return ret; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci dev_info(pctl->dev, 73262306a36Sopenharmony_ci "%ld GPIOs available\n", hweight_long(pctl->gpio_valid_mask)); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci return 0; 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_cistatic int stmfx_pinctrl_remove(struct platform_device *pdev) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci struct stmfx *stmfx = dev_get_drvdata(pdev->dev.parent); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci return stmfx_function_disable(stmfx, 74262306a36Sopenharmony_ci STMFX_FUNC_GPIO | 74362306a36Sopenharmony_ci STMFX_FUNC_ALTGPIO_LOW | 74462306a36Sopenharmony_ci STMFX_FUNC_ALTGPIO_HIGH); 74562306a36Sopenharmony_ci} 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 74862306a36Sopenharmony_cistatic int stmfx_pinctrl_backup_regs(struct stmfx_pinctrl *pctl) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci int ret; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci ret = regmap_bulk_read(pctl->stmfx->map, STMFX_REG_GPIO_STATE, 75362306a36Sopenharmony_ci &pctl->bkp_gpio_state, NR_GPIO_REGS); 75462306a36Sopenharmony_ci if (ret) 75562306a36Sopenharmony_ci return ret; 75662306a36Sopenharmony_ci ret = regmap_bulk_read(pctl->stmfx->map, STMFX_REG_GPIO_DIR, 75762306a36Sopenharmony_ci &pctl->bkp_gpio_dir, NR_GPIO_REGS); 75862306a36Sopenharmony_ci if (ret) 75962306a36Sopenharmony_ci return ret; 76062306a36Sopenharmony_ci ret = regmap_bulk_read(pctl->stmfx->map, STMFX_REG_GPIO_TYPE, 76162306a36Sopenharmony_ci &pctl->bkp_gpio_type, NR_GPIO_REGS); 76262306a36Sopenharmony_ci if (ret) 76362306a36Sopenharmony_ci return ret; 76462306a36Sopenharmony_ci ret = regmap_bulk_read(pctl->stmfx->map, STMFX_REG_GPIO_PUPD, 76562306a36Sopenharmony_ci &pctl->bkp_gpio_pupd, NR_GPIO_REGS); 76662306a36Sopenharmony_ci if (ret) 76762306a36Sopenharmony_ci return ret; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci return 0; 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cistatic int stmfx_pinctrl_restore_regs(struct stmfx_pinctrl *pctl) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci int ret; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_GPIO_DIR, 77762306a36Sopenharmony_ci pctl->bkp_gpio_dir, NR_GPIO_REGS); 77862306a36Sopenharmony_ci if (ret) 77962306a36Sopenharmony_ci return ret; 78062306a36Sopenharmony_ci ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_GPIO_TYPE, 78162306a36Sopenharmony_ci pctl->bkp_gpio_type, NR_GPIO_REGS); 78262306a36Sopenharmony_ci if (ret) 78362306a36Sopenharmony_ci return ret; 78462306a36Sopenharmony_ci ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_GPIO_PUPD, 78562306a36Sopenharmony_ci pctl->bkp_gpio_pupd, NR_GPIO_REGS); 78662306a36Sopenharmony_ci if (ret) 78762306a36Sopenharmony_ci return ret; 78862306a36Sopenharmony_ci ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_GPO_SET, 78962306a36Sopenharmony_ci pctl->bkp_gpio_state, NR_GPIO_REGS); 79062306a36Sopenharmony_ci if (ret) 79162306a36Sopenharmony_ci return ret; 79262306a36Sopenharmony_ci ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_EVT, 79362306a36Sopenharmony_ci pctl->irq_gpi_evt, NR_GPIO_REGS); 79462306a36Sopenharmony_ci if (ret) 79562306a36Sopenharmony_ci return ret; 79662306a36Sopenharmony_ci ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_TYPE, 79762306a36Sopenharmony_ci pctl->irq_gpi_type, NR_GPIO_REGS); 79862306a36Sopenharmony_ci if (ret) 79962306a36Sopenharmony_ci return ret; 80062306a36Sopenharmony_ci ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_SRC, 80162306a36Sopenharmony_ci pctl->irq_gpi_src, NR_GPIO_REGS); 80262306a36Sopenharmony_ci if (ret) 80362306a36Sopenharmony_ci return ret; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci return 0; 80662306a36Sopenharmony_ci} 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_cistatic int stmfx_pinctrl_suspend(struct device *dev) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci struct stmfx_pinctrl *pctl = dev_get_drvdata(dev); 81162306a36Sopenharmony_ci int ret; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci ret = stmfx_pinctrl_backup_regs(pctl); 81462306a36Sopenharmony_ci if (ret) { 81562306a36Sopenharmony_ci dev_err(pctl->dev, "registers backup failure\n"); 81662306a36Sopenharmony_ci return ret; 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci return 0; 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_cistatic int stmfx_pinctrl_resume(struct device *dev) 82362306a36Sopenharmony_ci{ 82462306a36Sopenharmony_ci struct stmfx_pinctrl *pctl = dev_get_drvdata(dev); 82562306a36Sopenharmony_ci int ret; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci ret = stmfx_pinctrl_restore_regs(pctl); 82862306a36Sopenharmony_ci if (ret) { 82962306a36Sopenharmony_ci dev_err(pctl->dev, "registers restoration failure\n"); 83062306a36Sopenharmony_ci return ret; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci return 0; 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ci#endif 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(stmfx_pinctrl_dev_pm_ops, 83862306a36Sopenharmony_ci stmfx_pinctrl_suspend, stmfx_pinctrl_resume); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_cistatic const struct of_device_id stmfx_pinctrl_of_match[] = { 84162306a36Sopenharmony_ci { .compatible = "st,stmfx-0300-pinctrl", }, 84262306a36Sopenharmony_ci {}, 84362306a36Sopenharmony_ci}; 84462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, stmfx_pinctrl_of_match); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_cistatic struct platform_driver stmfx_pinctrl_driver = { 84762306a36Sopenharmony_ci .driver = { 84862306a36Sopenharmony_ci .name = "stmfx-pinctrl", 84962306a36Sopenharmony_ci .of_match_table = stmfx_pinctrl_of_match, 85062306a36Sopenharmony_ci .pm = &stmfx_pinctrl_dev_pm_ops, 85162306a36Sopenharmony_ci }, 85262306a36Sopenharmony_ci .probe = stmfx_pinctrl_probe, 85362306a36Sopenharmony_ci .remove = stmfx_pinctrl_remove, 85462306a36Sopenharmony_ci}; 85562306a36Sopenharmony_cimodule_platform_driver(stmfx_pinctrl_driver); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ciMODULE_DESCRIPTION("STMFX pinctrl/GPIO driver"); 85862306a36Sopenharmony_ciMODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>"); 85962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 860