From: Rosen Penev Date: Wed, 21 Aug 2024 16:06:03 +0000 (-0700) Subject: ramips: pinctrl-aw9523: update to current upstream X-Git-Url: http://git.cdn.openwrt.org/?a=commitdiff_plain;h=8e6543fae576829628ddf20076bdf8401bf1cccf;p=openwrt%2Fstaging%2Fblocktrron.git ramips: pinctrl-aw9523: update to current upstream Various cleanups and bugfixes were applied. Signed-off-by: Rosen Penev Link: https://github.com/openwrt/openwrt/pull/16215 Signed-off-by: Robert Marko --- diff --git a/target/linux/ramips/files/drivers/pinctrl/pinctrl-aw9523.c b/target/linux/ramips/files/drivers/pinctrl/pinctrl-aw9523.c deleted file mode 100644 index 8b642e28b1..0000000000 --- a/target/linux/ramips/files/drivers/pinctrl/pinctrl-aw9523.c +++ /dev/null @@ -1,1128 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Awinic AW9523B i2c pin controller driver - * Copyright (c) 2020, AngeloGioacchino Del Regno - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "core.h" -#include "pinconf.h" -#include "pinctrl-utils.h" - -#define AW9523_MAX_FUNCS 2 -#define AW9523_NUM_PORTS 2 -#define AW9523_PINS_PER_PORT 8 - -/* - * HW needs at least 20uS for reset and at least 1-2uS to recover from - * reset, but we have to account for eventual board quirks, if any: - * for this reason, keep reset asserted for 50uS and wait for 20uS - * to recover from the reset. - */ -#define AW9523_HW_RESET_US 50 -#define AW9523_HW_RESET_RECOVERY_US 20 - -/* Port 0: P0_0...P0_7 - Port 1: P1_0...P1_7 */ -#define AW9523_PIN_TO_PORT(pin) (pin >> 3) -#define AW9523_REG_IN_STATE(pin) (0x00 + AW9523_PIN_TO_PORT(pin)) -#define AW9523_REG_OUT_STATE(pin) (0x02 + AW9523_PIN_TO_PORT(pin)) -#define AW9523_REG_CONF_STATE(pin) (0x04 + AW9523_PIN_TO_PORT(pin)) -#define AW9523_REG_INTR_DIS(pin) (0x06 + AW9523_PIN_TO_PORT(pin)) -#define AW9523_REG_CHIPID 0x10 -#define AW9523_VAL_EXPECTED_CHIPID 0x23 - -#define AW9523_REG_GCR 0x11 -#define AW9523_GCR_ISEL_MASK GENMASK(0, 1) -#define AW9523_GCR_GPOMD_MASK BIT(4) - -#define AW9523_REG_PORT_MODE(pin) (0x12 + AW9523_PIN_TO_PORT(pin)) -#define AW9523_REG_SOFT_RESET 0x7f -#define AW9523_VAL_RESET 0x00 - -/* - * struct aw9523_irq - Interrupt controller structure - * @lock: mutex locking for the irq bus - * @irqchip: structure holding irqchip params - * @cached_gpio: stores the previous gpio status for bit comparison - */ -struct aw9523_irq { - struct mutex lock; - struct irq_chip *irqchip; - u16 cached_gpio; -}; - -/* - * struct aw9523_pinmux - Pin mux params - * @name: Name of the mux - * @grps: Groups of the mux - * @num_grps: Number of groups (sizeof array grps) - */ -struct aw9523_pinmux { - const char *name; - const char * const *grps; - const u8 num_grps; -}; - -/* - * struct aw9523 - Main driver structure - * @dev: device handle - * @regmap: regmap handle for current device - * @i2c_lock: Mutex lock for i2c operations - * @reset_gpio: Hardware reset (RSTN) signal GPIO - * @vio_vreg: VCC regulator (Optional) - * @pctl: pinctrl handle for current device - * @gpio: structure holding gpiochip params - * @irq: Interrupt controller structure - */ -struct aw9523 { - struct device *dev; - struct regmap *regmap; - struct mutex i2c_lock; - struct gpio_desc *reset_gpio; - struct regulator *vio_vreg; - struct pinctrl_dev *pctl; - struct gpio_chip gpio; - struct aw9523_irq *irq; -}; - -static const struct pinctrl_pin_desc aw9523_pins[] = { - /* Port 0 */ - PINCTRL_PIN(0, "gpio0"), - PINCTRL_PIN(1, "gpio1"), - PINCTRL_PIN(2, "gpio2"), - PINCTRL_PIN(3, "gpio3"), - PINCTRL_PIN(4, "gpio4"), - PINCTRL_PIN(5, "gpio5"), - PINCTRL_PIN(6, "gpio6"), - PINCTRL_PIN(7, "gpio7"), - - /* Port 1 */ - PINCTRL_PIN(8, "gpio8"), - PINCTRL_PIN(9, "gpio9"), - PINCTRL_PIN(10, "gpio10"), - PINCTRL_PIN(11, "gpio11"), - PINCTRL_PIN(12, "gpio12"), - PINCTRL_PIN(13, "gpio13"), - PINCTRL_PIN(14, "gpio14"), - PINCTRL_PIN(15, "gpio15"), -}; - -static int aw9523_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) -{ - return ARRAY_SIZE(aw9523_pins); -} - -static const char *aw9523_pinctrl_get_group_name(struct pinctrl_dev *pctldev, - unsigned int selector) -{ - return aw9523_pins[selector].name; -} - -static int aw9523_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, - unsigned int selector, - const unsigned int **pins, - unsigned int *num_pins) -{ - *pins = &aw9523_pins[selector].number; - *num_pins = 1; - return 0; -} - -static const struct pinctrl_ops aw9523_pinctrl_ops = { - .get_groups_count = aw9523_pinctrl_get_groups_count, - .get_group_pins = aw9523_pinctrl_get_group_pins, - .get_group_name = aw9523_pinctrl_get_group_name, - .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, - .dt_free_map = pinconf_generic_dt_free_map, -}; - -static const char * const gpio_pwm_groups[] = { - "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", - "gpio6", "gpio7", "gpio8", "gpio9", "gpio10", "gpio11", - "gpio12", "gpio13", "gpio14", "gpio15" -}; - -/* Warning: Do NOT reorder this array */ -static const struct aw9523_pinmux aw9523_pmx[] = { - { - .name = "pwm", - .grps = gpio_pwm_groups, - .num_grps = ARRAY_SIZE(gpio_pwm_groups), - }, - { - .name = "gpio", - .grps = gpio_pwm_groups, - .num_grps = ARRAY_SIZE(gpio_pwm_groups), - }, -}; - -static int aw9523_pmx_get_funcs_count(struct pinctrl_dev *pctl) -{ - return ARRAY_SIZE(aw9523_pmx); -} - -static const char *aw9523_pmx_get_fname(struct pinctrl_dev *pctl, - unsigned int sel) -{ - return aw9523_pmx[sel].name; -} - -static int aw9523_pmx_get_groups(struct pinctrl_dev *pctl, unsigned int sel, - const char * const **groups, - unsigned int * const num_groups) -{ - *groups = aw9523_pmx[sel].grps; - *num_groups = aw9523_pmx[sel].num_grps; - return 0; -} - -static int aw9523_pmx_set_mux(struct pinctrl_dev *pctl, unsigned int fsel, - unsigned int grp) -{ - struct aw9523 *awi = pinctrl_dev_get_drvdata(pctl); - int ret, pin = aw9523_pins[grp].number % AW9523_PINS_PER_PORT; - - if (fsel >= ARRAY_SIZE(aw9523_pmx)) - return -EINVAL; - - /* - * This maps directly to the aw9523_pmx array: programming a - * high bit means "gpio" and a low bit means "pwm". - */ - mutex_lock(&awi->i2c_lock); - ret = regmap_update_bits(awi->regmap, AW9523_REG_PORT_MODE(pin), - BIT(pin), (fsel ? BIT(pin) : 0)); - mutex_unlock(&awi->i2c_lock); - return ret; -} - -static const struct pinmux_ops aw9523_pinmux_ops = { - .get_functions_count = aw9523_pmx_get_funcs_count, - .get_function_name = aw9523_pmx_get_fname, - .get_function_groups = aw9523_pmx_get_groups, - .set_mux = aw9523_pmx_set_mux, -}; - -static int aw9523_pcfg_param_to_reg(enum pin_config_param pcp, int pin, u8 *r) -{ - u8 reg; - - switch (pcp) { - case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: - case PIN_CONFIG_BIAS_PULL_DOWN: - case PIN_CONFIG_BIAS_PULL_UP: - reg = AW9523_REG_IN_STATE(pin); - break; - case PIN_CONFIG_DRIVE_OPEN_DRAIN: - case PIN_CONFIG_DRIVE_PUSH_PULL: - reg = AW9523_REG_GCR; - break; - case PIN_CONFIG_INPUT_ENABLE: - case PIN_CONFIG_OUTPUT_ENABLE: - reg = AW9523_REG_CONF_STATE(pin); - break; - case PIN_CONFIG_OUTPUT: - reg = AW9523_REG_OUT_STATE(pin); - break; - default: - return -ENOTSUPP; - } - *r = reg; - - return 0; -} - -static int aw9523_pconf_get(struct pinctrl_dev *pctldev, unsigned int pin, - unsigned long *config) -{ - struct aw9523 *awi = pinctrl_dev_get_drvdata(pctldev); - enum pin_config_param param = pinconf_to_config_param(*config); - int regbit = pin % AW9523_PINS_PER_PORT; - unsigned int val; - u8 reg; - int rc; - - rc = aw9523_pcfg_param_to_reg(param, pin, ®); - if (rc) - return rc; - - mutex_lock(&awi->i2c_lock); - rc = regmap_read(awi->regmap, reg, &val); - mutex_unlock(&awi->i2c_lock); - if (rc) - return rc; - - switch (param) { - case PIN_CONFIG_BIAS_PULL_UP: - case PIN_CONFIG_INPUT_ENABLE: - case PIN_CONFIG_OUTPUT: - val &= BIT(regbit); - break; - case PIN_CONFIG_BIAS_PULL_DOWN: - case PIN_CONFIG_OUTPUT_ENABLE: - val &= BIT(regbit); - val = !val; - break; - case PIN_CONFIG_DRIVE_OPEN_DRAIN: - if (pin >= AW9523_PINS_PER_PORT) - val = 0; - else - val = !FIELD_GET(AW9523_GCR_GPOMD_MASK, val); - break; - case PIN_CONFIG_DRIVE_PUSH_PULL: - if (pin >= AW9523_PINS_PER_PORT) - val = 1; - else - val = FIELD_GET(AW9523_GCR_GPOMD_MASK, val); - break; - default: - return -ENOTSUPP; - } - if (val < 1) - return -EINVAL; - - *config = pinconf_to_config_packed(param, !!val); - - return rc; -} - -static int aw9523_pconf_set(struct pinctrl_dev *pctldev, unsigned int pin, - unsigned long *configs, unsigned int num_configs) -{ - struct aw9523 *awi = pinctrl_dev_get_drvdata(pctldev); - enum pin_config_param param; - int regbit = pin % AW9523_PINS_PER_PORT; - u32 arg; - u8 reg; - unsigned int mask, val; - int i, rc; - - mutex_lock(&awi->i2c_lock); - for (i = 0; i < num_configs; i++) { - param = pinconf_to_config_param(configs[i]); - arg = pinconf_to_config_argument(configs[i]); - - rc = aw9523_pcfg_param_to_reg(param, pin, ®); - if (rc) - goto end; - - switch (param) { - case PIN_CONFIG_OUTPUT: - /* First, enable pin output */ - rc = regmap_update_bits(awi->regmap, - AW9523_REG_CONF_STATE(pin), - BIT(regbit), 0); - if (rc) - goto end; - - /* Then, fall through to config output level */ - fallthrough; - case PIN_CONFIG_OUTPUT_ENABLE: - arg = !arg; - fallthrough; - case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: - case PIN_CONFIG_BIAS_PULL_DOWN: - case PIN_CONFIG_BIAS_PULL_UP: - case PIN_CONFIG_INPUT_ENABLE: - mask = BIT(regbit); - val = arg ? BIT(regbit) : 0; - break; - case PIN_CONFIG_DRIVE_OPEN_DRAIN: - /* Open-Drain is supported only on port 0 */ - if (pin >= AW9523_PINS_PER_PORT) { - rc = -ENOTSUPP; - goto end; - } - mask = AW9523_GCR_GPOMD_MASK; - val = 0; - break; - case PIN_CONFIG_DRIVE_PUSH_PULL: - /* Port 1 is always Push-Pull */ - if (pin >= AW9523_PINS_PER_PORT) { - mask = 0; - val = 0; - continue; - } - mask = AW9523_GCR_GPOMD_MASK; - val = AW9523_GCR_GPOMD_MASK; - break; - default: - rc = -ENOTSUPP; - goto end; - } - - rc = regmap_update_bits(awi->regmap, reg, mask, val); - if (rc) - goto end; - } -end: - mutex_unlock(&awi->i2c_lock); - return rc; -} - -static const struct pinconf_ops aw9523_pinconf_ops = { - .pin_config_get = aw9523_pconf_get, - .pin_config_set = aw9523_pconf_set, - .is_generic = true, -}; - -/* - * aw9523_get_pin_direction - Get pin direction - * @regmap: Regmap structure - * @pin: gpiolib pin number - * @n: pin index in port register - * - * Return: Pin direction for success or negative number for error - */ -static int aw9523_get_pin_direction(struct regmap *regmap, u8 pin, u8 n) -{ - int val, ret; - - ret = regmap_read(regmap, AW9523_REG_CONF_STATE(pin), &val); - if (ret < 0) - return ret; - - return (val & BIT(n)) == BIT(n); -} - -/* - * aw9523_get_port_state - Get input or output state for entire port - * @regmap: Regmap structure - * @pin: gpiolib pin number - * @regbit: hw pin index, used to retrieve port number - * @state: returned port state - * - * Return: Zero for success or negative number for error - */ -static int aw9523_get_port_state(struct regmap *regmap, u8 pin, - u8 regbit, unsigned int *state) -{ - u8 reg; - int dir; - - dir = aw9523_get_pin_direction(regmap, pin, regbit); - if (dir < 0) - return dir; - - if (dir == GPIO_LINE_DIRECTION_IN) - reg = AW9523_REG_IN_STATE(pin); - else - reg = AW9523_REG_OUT_STATE(pin); - - return regmap_read(regmap, reg, state); -} - -static int aw9523_gpio_irq_type(struct irq_data *d, unsigned int type) -{ - switch (type) { - case IRQ_TYPE_NONE: - case IRQ_TYPE_EDGE_BOTH: - return 0; - default: - return -EINVAL; - }; -} - -/* - * aw9523_irq_mask - Mask interrupt - * @d: irq data - * - * Sets which interrupt to mask in the bitmap; - * The interrupt will be masked when unlocking the irq bus. - */ -static void aw9523_irq_mask(struct irq_data *d) -{ - struct aw9523 *awi = gpiochip_get_data(irq_data_get_irq_chip_data(d)); - unsigned int n = d->hwirq % AW9523_PINS_PER_PORT; - - regmap_update_bits(awi->regmap, - AW9523_REG_INTR_DIS(d->hwirq), - BIT(n), BIT(n)); -} - -/* - * aw9523_irq_unmask - Unmask interrupt - * @d: irq data - * - * Sets which interrupt to unmask in the bitmap; - * The interrupt will be masked when unlocking the irq bus. - */ -static void aw9523_irq_unmask(struct irq_data *d) -{ - struct aw9523 *awi = gpiochip_get_data(irq_data_get_irq_chip_data(d)); - unsigned int n = d->hwirq % AW9523_PINS_PER_PORT; - - regmap_update_bits(awi->regmap, - AW9523_REG_INTR_DIS(d->hwirq), - BIT(n), 0); -} - -static irqreturn_t aw9523_irq_thread_func(int irq, void *dev_id) -{ - struct aw9523 *awi = (struct aw9523 *)dev_id; - unsigned long n, val = 0; - unsigned long changed_gpio; - unsigned int tmp, port_pin, i, ret; - - for (i = 0; i < AW9523_NUM_PORTS; i++) { - port_pin = i * AW9523_PINS_PER_PORT; - ret = regmap_read(awi->regmap, - AW9523_REG_IN_STATE(port_pin), - &tmp); - if (ret) - return ret; - val |= (u8)tmp << (i * 8); - } - - /* Handle GPIO input release interrupt as well */ - changed_gpio = awi->irq->cached_gpio ^ val; - awi->irq->cached_gpio = val; - - /* - * To avoid up to four *slow* i2c reads from any driver hooked - * up to our interrupts, just check for the irq_find_mapping - * result: if the interrupt is not mapped, then we don't want - * to care about it. - */ - for_each_set_bit(n, &changed_gpio, awi->gpio.ngpio) { - tmp = irq_find_mapping(awi->gpio.irq.domain, n); - if (tmp <= 0) - continue; - handle_nested_irq(tmp); - } - - return IRQ_HANDLED; -} - -/* - * aw9523_irq_bus_lock - Grab lock for interrupt operation - * @d: irq data - */ -static void aw9523_irq_bus_lock(struct irq_data *d) -{ - struct aw9523 *awi = gpiochip_get_data(irq_data_get_irq_chip_data(d)); - - mutex_lock(&awi->irq->lock); - regcache_cache_only(awi->regmap, true); -} - -/* - * aw9523_irq_bus_sync_unlock - Synchronize state and unlock - * @d: irq data - * - * Writes the interrupt mask bits (found in the bit map) to the - * hardware, then unlocks the bus. - */ -static void aw9523_irq_bus_sync_unlock(struct irq_data *d) -{ - struct aw9523 *awi = gpiochip_get_data(irq_data_get_irq_chip_data(d)); - - regcache_cache_only(awi->regmap, false); - regcache_sync(awi->regmap); - mutex_unlock(&awi->irq->lock); -} - -static int aw9523_gpio_get_direction(struct gpio_chip *chip, - unsigned int offset) -{ - struct aw9523 *awi = gpiochip_get_data(chip); - u8 regbit = offset % AW9523_PINS_PER_PORT; - int ret; - - mutex_lock(&awi->i2c_lock); - ret = aw9523_get_pin_direction(awi->regmap, offset, regbit); - mutex_unlock(&awi->i2c_lock); - - return ret; -} - -static int aw9523_gpio_get(struct gpio_chip *chip, unsigned int offset) -{ - struct aw9523 *awi = gpiochip_get_data(chip); - u8 regbit = offset % AW9523_PINS_PER_PORT; - unsigned int val; - int ret; - - mutex_lock(&awi->i2c_lock); - ret = aw9523_get_port_state(awi->regmap, offset, regbit, &val); - mutex_unlock(&awi->i2c_lock); - if (ret) - return ret; - - return !!(val & BIT(regbit)); -} - -/** - * _aw9523_gpio_get_multiple - Get I/O state for an entire port - * @regmap: Regmap structure - * @pin: gpiolib pin number - * @regbit: hw pin index, used to retrieve port number - * @state: returned port I/O state - * - * Return: Zero for success or negative number for error - */ -static int _aw9523_gpio_get_multiple(struct aw9523 *awi, u8 regbit, - u8 *state, u8 mask) -{ - u32 dir_in, val; - u8 m; - int ret; - - /* Registers are 8-bits wide */ - ret = regmap_read(awi->regmap, AW9523_REG_CONF_STATE(regbit), &dir_in); - if (ret) - return ret; - *state = 0; - - m = mask & dir_in; - if (m) { - ret = regmap_read(awi->regmap, AW9523_REG_IN_STATE(regbit), - &val); - if (ret) - return ret; - *state |= (u8)val & m; - } - - m = mask & ~dir_in; - if (m) { - ret = regmap_read(awi->regmap, AW9523_REG_OUT_STATE(regbit), - &val); - if (ret) - return ret; - *state |= (u8)val & m; - } - - return 0; -} - -static int aw9523_gpio_get_multiple(struct gpio_chip *chip, - unsigned long *mask, - unsigned long *bits) -{ - struct aw9523 *awi = gpiochip_get_data(chip); - u8 m, state = 0; - int ret; - - mutex_lock(&awi->i2c_lock); - - /* Port 0 (gpio 0-7) */ - m = *mask & U8_MAX; - if (m) { - ret = _aw9523_gpio_get_multiple(awi, 0, &state, m); - if (ret) - goto out; - } - *bits = state; - - /* Port 1 (gpio 8-15) */ - m = (*mask >> 8) & U8_MAX; - if (m) { - ret = _aw9523_gpio_get_multiple(awi, AW9523_PINS_PER_PORT, - &state, m); - if (ret) - goto out; - - *bits |= (state << 8); - } -out: - mutex_unlock(&awi->i2c_lock); - return ret; -} - -static void aw9523_gpio_set_multiple(struct gpio_chip *chip, - unsigned long *mask, - unsigned long *bits) -{ - struct aw9523 *awi = gpiochip_get_data(chip); - u8 mask_lo, mask_hi, bits_lo, bits_hi; - unsigned int reg; - int ret = 0; - - mask_lo = *mask & U8_MAX; - mask_hi = (*mask >> 8) & U8_MAX; - mutex_lock(&awi->i2c_lock); - if (mask_hi) { - reg = AW9523_REG_OUT_STATE(AW9523_PINS_PER_PORT); - bits_hi = (*bits >> 8) & U8_MAX; - - ret = regmap_write_bits(awi->regmap, reg, mask_hi, bits_hi); - if (ret) { - dev_warn(awi->dev, "Cannot write port1 out level\n"); - goto out; - } - } - if (mask_lo) { - reg = AW9523_REG_OUT_STATE(0); - bits_lo = *bits & U8_MAX; - ret = regmap_write_bits(awi->regmap, reg, mask_lo, bits_lo); - if (ret) - dev_warn(awi->dev, "Cannot write port0 out level\n"); - } -out: - mutex_unlock(&awi->i2c_lock); -} - -static void aw9523_gpio_set(struct gpio_chip *chip, - unsigned int offset, int value) -{ - struct aw9523 *awi = gpiochip_get_data(chip); - u8 regbit = offset % AW9523_PINS_PER_PORT; - - mutex_lock(&awi->i2c_lock); - regmap_update_bits(awi->regmap, AW9523_REG_OUT_STATE(offset), - BIT(regbit), value ? BIT(regbit) : 0); - mutex_unlock(&awi->i2c_lock); -} - - -static int aw9523_direction_input(struct gpio_chip *chip, unsigned int offset) -{ - struct aw9523 *awi = gpiochip_get_data(chip); - u8 regbit = offset % AW9523_PINS_PER_PORT; - int ret; - - mutex_lock(&awi->i2c_lock); - ret = regmap_update_bits(awi->regmap, AW9523_REG_CONF_STATE(offset), - BIT(regbit), BIT(regbit)); - mutex_unlock(&awi->i2c_lock); - - return ret; -} - -static int aw9523_direction_output(struct gpio_chip *chip, - unsigned int offset, int value) -{ - struct aw9523 *awi = gpiochip_get_data(chip); - u8 regbit = offset % AW9523_PINS_PER_PORT; - int ret; - - mutex_lock(&awi->i2c_lock); - ret = regmap_update_bits(awi->regmap, AW9523_REG_OUT_STATE(offset), - BIT(regbit), value ? BIT(regbit) : 0); - if (ret) - goto end; - - ret = regmap_update_bits(awi->regmap, AW9523_REG_CONF_STATE(offset), - BIT(regbit), 0); -end: - mutex_unlock(&awi->i2c_lock); - return ret; -} - -static int aw9523_drive_reset_gpio(struct aw9523 *awi) -{ - unsigned int chip_id; - int ret; - - /* - * If the chip is already configured for any reason, then we - * will probably succeed in sending the soft reset signal to - * the hardware through I2C: this operation takes less time - * compared to a full HW reset and it gives the same results. - */ - ret = regmap_write(awi->regmap, AW9523_REG_SOFT_RESET, 0); - if (ret == 0) - goto done; - - dev_dbg(awi->dev, "Cannot execute soft reset: trying hard reset\n"); - ret = gpiod_direction_output(awi->reset_gpio, 0); - if (ret) - return ret; - - /* The reset pulse has to be longer than 20uS due to deglitch */ - usleep_range(AW9523_HW_RESET_US, AW9523_HW_RESET_US + 1); - - ret = gpiod_direction_output(awi->reset_gpio, 1); - if (ret) - return ret; -done: - /* The HW needs at least 1uS to reliably recover after reset */ - usleep_range(AW9523_HW_RESET_RECOVERY_US, - AW9523_HW_RESET_RECOVERY_US + 1); - - /* Check the ChipID */ - ret = regmap_read(awi->regmap, AW9523_REG_CHIPID, &chip_id); - if (ret) { - dev_err(awi->dev, "Cannot read Chip ID: %d\n", ret); - return ret; - } - if (chip_id != AW9523_VAL_EXPECTED_CHIPID) { - dev_err(awi->dev, "Bad ChipID; read 0x%x, expected 0x%x\n", - chip_id, AW9523_VAL_EXPECTED_CHIPID); - return -EINVAL; - } - - return 0; -} - -static int aw9523_hw_reset(struct aw9523 *awi) -{ - int ret, max_retries = 2; - - /* Sometimes the chip needs more than one reset cycle */ - do { - ret = aw9523_drive_reset_gpio(awi); - if (ret == 0) - break; - max_retries--; - } while (max_retries); - - return ret; -} - -static int aw9523_init_gpiochip(struct aw9523 *awi, unsigned int npins) -{ - struct device *dev = awi->dev; - struct gpio_chip *gpiochip = &awi->gpio; - - gpiochip->label = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); - if (!gpiochip->label) - return -ENOMEM; - - gpiochip->base = -1; - gpiochip->ngpio = npins; - gpiochip->get_direction = aw9523_gpio_get_direction; - gpiochip->direction_input = aw9523_direction_input; - gpiochip->direction_output = aw9523_direction_output; - gpiochip->get = aw9523_gpio_get; - gpiochip->get_multiple = aw9523_gpio_get_multiple; - gpiochip->set = aw9523_gpio_set; - gpiochip->set_multiple = aw9523_gpio_set_multiple; - gpiochip->set_config = gpiochip_generic_config; - gpiochip->parent = dev; - gpiochip->fwnode = dev->fwnode; - gpiochip->owner = THIS_MODULE; - gpiochip->can_sleep = true; - - return 0; -} - -static int aw9523_init_irq(struct aw9523 *awi, int irq) -{ - struct device *dev = awi->dev; - struct gpio_irq_chip *gpioirq; - struct irq_chip *irqchip; - int ret; - - if (!device_property_read_bool(dev, "interrupt-controller")) - return 0; - - irqchip = devm_kzalloc(dev, sizeof(*irqchip), GFP_KERNEL); - if (!irqchip) - return -ENOMEM; - - awi->irq = devm_kzalloc(dev, sizeof(*awi->irq), GFP_KERNEL); - if (!awi->irq) - return -ENOMEM; - - irqchip->name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); - if (!irqchip->name) - return -ENOMEM; - - irqchip->irq_mask = aw9523_irq_mask; - irqchip->irq_unmask = aw9523_irq_unmask; - irqchip->irq_bus_lock = aw9523_irq_bus_lock; - irqchip->irq_bus_sync_unlock = aw9523_irq_bus_sync_unlock; - irqchip->irq_set_type = aw9523_gpio_irq_type; - awi->irq->irqchip = irqchip; - mutex_init(&awi->irq->lock); - - ret = devm_request_threaded_irq(dev, irq, NULL, aw9523_irq_thread_func, - IRQF_ONESHOT, dev_name(dev), awi); - if (ret) { - dev_err(dev, "Failed to request irq %d\n", irq); - return ret; - } - - gpioirq = &awi->gpio.irq; - gpioirq->chip = irqchip; - gpioirq->parent_handler = NULL; - gpioirq->num_parents = 0; - gpioirq->parents = NULL; - gpioirq->default_type = IRQ_TYPE_LEVEL_MASK; - gpioirq->handler = handle_simple_irq; - gpioirq->threaded = true; - gpioirq->first = 0; - - return 0; -} - -static bool aw9523_is_reg_hole(unsigned int reg) -{ - return (reg > AW9523_REG_PORT_MODE(AW9523_PINS_PER_PORT) && - reg < AW9523_REG_SOFT_RESET) || - (reg > AW9523_REG_INTR_DIS(AW9523_PINS_PER_PORT) && - reg < AW9523_REG_CHIPID); -} - -static bool aw9523_readable_reg(struct device *dev, unsigned int reg) -{ - /* All available registers (minus holes) can be read */ - return !aw9523_is_reg_hole(reg); -} - -static bool aw9523_volatile_reg(struct device *dev, unsigned int reg) -{ - return aw9523_is_reg_hole(reg) || - reg == AW9523_REG_IN_STATE(0) || - reg == AW9523_REG_IN_STATE(AW9523_PINS_PER_PORT) || - reg == AW9523_REG_CHIPID || - reg == AW9523_REG_SOFT_RESET; -} - -static bool aw9523_writeable_reg(struct device *dev, unsigned int reg) -{ - return !aw9523_is_reg_hole(reg) && reg != AW9523_REG_CHIPID; -} - -static bool aw9523_precious_reg(struct device *dev, unsigned int reg) -{ - /* Reading AW9523_REG_IN_STATE clears interrupt status */ - return aw9523_is_reg_hole(reg) || - reg == AW9523_REG_IN_STATE(0) || - reg == AW9523_REG_IN_STATE(AW9523_PINS_PER_PORT); -} - -static const struct regmap_config aw9523_regmap = { - .reg_bits = 8, - .val_bits = 8, - .reg_stride = 1, - - .precious_reg = aw9523_precious_reg, - .readable_reg = aw9523_readable_reg, - .volatile_reg = aw9523_volatile_reg, - .writeable_reg = aw9523_writeable_reg, - - .cache_type = REGCACHE_FLAT, - .disable_locking = true, - - .num_reg_defaults_raw = AW9523_REG_SOFT_RESET, -}; - -static int aw9523_hw_init(struct aw9523 *awi) -{ - u8 p1_pin = AW9523_PINS_PER_PORT; - unsigned int val; - int ret; - - /* No register caching during initialization */ - regcache_cache_bypass(awi->regmap, true); - - /* Bring up the chip */ - ret = aw9523_hw_reset(awi); - if (ret) { - dev_err(awi->dev, "HW Reset failed: %d\n", ret); - return ret; - } - - /* - * This is the expected chip and it is running: it's time to - * set a safe default configuration in case the user doesn't - * configure (all of the available) pins in this chip. - * P.S.: The writes order doesn't matter. - */ - - /* Set all pins as GPIO */ - ret = regmap_write(awi->regmap, AW9523_REG_PORT_MODE(0), U8_MAX); - if (ret) - return ret; - ret = regmap_write(awi->regmap, AW9523_REG_PORT_MODE(p1_pin), U8_MAX); - if (ret) - return ret; - - /* Set Open-Drain mode on Port 0 (Port 1 is always P-P) */ - ret = regmap_write(awi->regmap, AW9523_REG_GCR, 0); - if (ret) - return ret; - - /* Set all pins as inputs */ - ret = regmap_write(awi->regmap, AW9523_REG_CONF_STATE(0), U8_MAX); - if (ret) - return ret; - ret = regmap_write(awi->regmap, AW9523_REG_CONF_STATE(p1_pin), U8_MAX); - if (ret) - return ret; - - /* Disable all interrupts to avoid unreasoned wakeups */ - ret = regmap_write(awi->regmap, AW9523_REG_INTR_DIS(0), U8_MAX); - if (ret) - return ret; - ret = regmap_write(awi->regmap, AW9523_REG_INTR_DIS(p1_pin), U8_MAX); - if (ret) - return ret; - - /* Clear setup-generated interrupts by performing a port state read */ - ret = aw9523_get_port_state(awi->regmap, 0, 0, &val); - if (ret) - return ret; - ret = aw9523_get_port_state(awi->regmap, p1_pin, 0, &val); - if (ret) - return ret; - - /* Everything went fine: activate and reinitialize register cache */ - regcache_cache_bypass(awi->regmap, false); - return regmap_reinit_cache(awi->regmap, &aw9523_regmap); -} - -static int aw9523_probe(struct i2c_client *client) -{ - struct device *dev = &client->dev; - struct pinctrl_desc *pdesc; - struct aw9523 *awi; - int ret; - - awi = devm_kzalloc(dev, sizeof(*awi), GFP_KERNEL); - if (!awi) - return -ENOMEM; - - i2c_set_clientdata(client, awi); - - awi->dev = dev; - awi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(awi->reset_gpio)) - return PTR_ERR(awi->reset_gpio); - gpiod_set_consumer_name(awi->reset_gpio, "aw9523 reset"); - - awi->regmap = devm_regmap_init_i2c(client, &aw9523_regmap); - if (IS_ERR(awi->regmap)) - return PTR_ERR(awi->regmap); - - awi->vio_vreg = devm_regulator_get_optional(dev, "vio"); - if (IS_ERR(awi->vio_vreg)) { - if (PTR_ERR(awi->vio_vreg) == -EPROBE_DEFER) - return -EPROBE_DEFER; - awi->vio_vreg = NULL; - } else { - ret = regulator_enable(awi->vio_vreg); - if (ret) - return ret; - } - - mutex_init(&awi->i2c_lock); - lockdep_set_subclass(&awi->i2c_lock, - i2c_adapter_depth(client->adapter)); - - pdesc = devm_kzalloc(dev, sizeof(*pdesc), GFP_KERNEL); - if (!pdesc) - return -ENOMEM; - - ret = aw9523_hw_init(awi); - if (ret) - goto err_disable_vregs; - - pdesc->name = dev_name(dev); - pdesc->owner = THIS_MODULE; - pdesc->pctlops = &aw9523_pinctrl_ops; - pdesc->pmxops = &aw9523_pinmux_ops; - pdesc->confops = &aw9523_pinconf_ops; - pdesc->pins = aw9523_pins; - pdesc->npins = ARRAY_SIZE(aw9523_pins); - - ret = aw9523_init_gpiochip(awi, pdesc->npins); - if (ret) - goto err_disable_vregs; - - if (client->irq) { - ret = aw9523_init_irq(awi, client->irq); - if (ret) - goto err_disable_vregs; - } - - awi->pctl = devm_pinctrl_register(dev, pdesc, awi); - if (IS_ERR(awi->pctl)) { - ret = PTR_ERR(awi->pctl); - dev_err(dev, "Cannot register pinctrl: %d", ret); - goto err_disable_vregs; - } - - ret = devm_gpiochip_add_data(dev, &awi->gpio, awi); - if (ret) - goto err_disable_vregs; - - return ret; - -err_disable_vregs: - if (awi->vio_vreg) - regulator_disable(awi->vio_vreg); - mutex_destroy(&awi->i2c_lock); - return ret; -} - -static int aw9523_remove(struct i2c_client *client) -{ - struct aw9523 *awi = i2c_get_clientdata(client); - int ret; - - if (!awi) - return 0; - - /* - * If the chip VIO is connected to a regulator that we can turn - * off, life is easy... otherwise, reinitialize the chip and - * set the pins to hardware defaults before removing the driver - * to leave it in a clean, safe and predictable state. - */ - if (awi->vio_vreg) { - regulator_disable(awi->vio_vreg); - } else { - mutex_lock(&awi->i2c_lock); - ret = aw9523_hw_init(awi); - mutex_unlock(&awi->i2c_lock); - if (ret) - return ret; - } - - mutex_destroy(&awi->i2c_lock); - return 0; -} - -static void aw9523_remove_void(struct i2c_client *client) -{ - aw9523_remove(client); -} - -static const struct i2c_device_id aw9523_i2c_id_table[] = { - { "aw9523_i2c", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, aw9523_i2c_id_table); - -static const struct of_device_id of_aw9523_i2c_match[] = { - { .compatible = "awinic,aw9523-pinctrl", }, -}; -MODULE_DEVICE_TABLE(of, of_aw9523_i2c_match); - -static struct i2c_driver aw9523_driver = { - .driver = { - .name = "aw9523-pinctrl", - .of_match_table = of_aw9523_i2c_match, - }, - .probe = aw9523_probe, - .remove = aw9523_remove_void, - .id_table = aw9523_i2c_id_table, -}; -module_i2c_driver(aw9523_driver); - -MODULE_DESCRIPTION("Awinic AW9523 I2C GPIO Expander driver"); -MODULE_AUTHOR("AngeloGioacchino Del Regno "); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:aw9523"); diff --git a/target/linux/ramips/patches-6.6/805-01-v6.9-pinctrl-Add-driver-for-Awinic-AW9523-B-I2C-GPIO-Expa.patch b/target/linux/ramips/patches-6.6/805-01-v6.9-pinctrl-Add-driver-for-Awinic-AW9523-B-I2C-GPIO-Expa.patch new file mode 100644 index 0000000000..84b4571b48 --- /dev/null +++ b/target/linux/ramips/patches-6.6/805-01-v6.9-pinctrl-Add-driver-for-Awinic-AW9523-B-I2C-GPIO-Expa.patch @@ -0,0 +1,1190 @@ +From 576623d706613feec2392ef16b84e23a46200552 Mon Sep 17 00:00:00 2001 +From: AngeloGioacchino Del Regno +Date: Fri, 1 Mar 2024 14:29:24 +0100 +Subject: [PATCH] pinctrl: Add driver for Awinic AW9523/B I2C GPIO Expander + +The Awinic AW9523(B) is a multi-function I2C gpio expander in a +TQFN-24L package, featuring PWM (max 37mA per pin, or total max +power 3.2Watts) for LED driving capability. + +It has two ports with 8 pins per port (for a total of 16 pins), +configurable as either PWM with 1/256 stepping or GPIO input/output, +1.8V logic input; each GPIO can be configured as input or output +independently from each other. + +This IC also has an internal interrupt controller, which is capable +of generating an interrupt for each GPIO, depending on the +configuration, and will raise an interrupt on the INTN pin to +advertise this to an external interrupt controller. + +Signed-off-by: AngeloGioacchino Del Regno +Signed-off-by: David Bauer +Link: https://lore.kernel.org/r/20210624214458.68716-2-mail@david-bauer.net +Acked-by: Krzysztof Kozlowski +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20240301-awinic-aw9523-v8-1-7ec572f5dfb4@linaro.org +--- + drivers/pinctrl/Kconfig | 18 + + drivers/pinctrl/Makefile | 1 + + drivers/pinctrl/pinctrl-aw9523.c | 1118 ++++++++++++++++++++++++++++++ + 3 files changed, 1137 insertions(+) + create mode 100644 drivers/pinctrl/pinctrl-aw9523.c + +--- a/drivers/pinctrl/Kconfig ++++ b/drivers/pinctrl/Kconfig +@@ -127,6 +127,24 @@ config PINCTRL_AXP209 + selected. + Say Y to enable pinctrl and GPIO support for the AXP209 PMIC. + ++config PINCTRL_AW9523 ++ bool "Awinic AW9523/AW9523B I2C GPIO expander pinctrl driver" ++ depends on OF && I2C ++ select PINMUX ++ select PINCONF ++ select GENERIC_PINCONF ++ select GPIOLIB ++ select GPIOLIB_IRQCHIP ++ select REGMAP ++ select REGMAP_I2C ++ help ++ The Awinic AW9523/AW9523B is a multi-function I2C GPIO ++ expander with PWM functionality. This driver bundles a ++ pinctrl driver to select the function muxing and a GPIO ++ driver to handle GPIO, when the GPIO function is selected. ++ ++ Say yes to enable pinctrl and GPIO support for the AW9523(B). ++ + config PINCTRL_BM1880 + bool "Bitmain BM1880 Pinctrl driver" + depends on OF && (ARCH_BITMAIN || COMPILE_TEST) +--- a/drivers/pinctrl/Makefile ++++ b/drivers/pinctrl/Makefile +@@ -15,6 +15,7 @@ obj-$(CONFIG_PINCTRL_ARTPEC6) += pinctrl + obj-$(CONFIG_PINCTRL_AS3722) += pinctrl-as3722.o + obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o + obj-$(CONFIG_PINCTRL_AT91PIO4) += pinctrl-at91-pio4.o ++obj-$(CONFIG_PINCTRL_AW9523) += pinctrl-aw9523.o + obj-$(CONFIG_PINCTRL_AXP209) += pinctrl-axp209.o + obj-$(CONFIG_PINCTRL_BM1880) += pinctrl-bm1880.o + obj-$(CONFIG_PINCTRL_CY8C95X0) += pinctrl-cy8c95x0.o +--- /dev/null ++++ b/drivers/pinctrl/pinctrl-aw9523.c +@@ -0,0 +1,1118 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Awinic AW9523B i2c pin controller driver ++ * Copyright (c) 2020, AngeloGioacchino Del Regno ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define AW9523_MAX_FUNCS 2 ++#define AW9523_NUM_PORTS 2 ++#define AW9523_PINS_PER_PORT 8 ++ ++/* ++ * HW needs at least 20uS for reset and at least 1-2uS to recover from ++ * reset, but we have to account for eventual board quirks, if any: ++ * for this reason, keep reset asserted for 50uS and wait for 20uS ++ * to recover from the reset. ++ */ ++#define AW9523_HW_RESET_US 50 ++#define AW9523_HW_RESET_RECOVERY_US 20 ++ ++/* Port 0: P0_0...P0_7 - Port 1: P1_0...P1_7 */ ++#define AW9523_PIN_TO_PORT(pin) (pin >> 3) ++#define AW9523_REG_IN_STATE(pin) (0x00 + AW9523_PIN_TO_PORT(pin)) ++#define AW9523_REG_OUT_STATE(pin) (0x02 + AW9523_PIN_TO_PORT(pin)) ++#define AW9523_REG_CONF_STATE(pin) (0x04 + AW9523_PIN_TO_PORT(pin)) ++#define AW9523_REG_INTR_DIS(pin) (0x06 + AW9523_PIN_TO_PORT(pin)) ++#define AW9523_REG_CHIPID 0x10 ++#define AW9523_VAL_EXPECTED_CHIPID 0x23 ++ ++#define AW9523_REG_GCR 0x11 ++#define AW9523_GCR_ISEL_MASK GENMASK(0, 1) ++#define AW9523_GCR_GPOMD_MASK BIT(4) ++ ++#define AW9523_REG_PORT_MODE(pin) (0x12 + AW9523_PIN_TO_PORT(pin)) ++#define AW9523_REG_SOFT_RESET 0x7f ++#define AW9523_VAL_RESET 0x00 ++ ++/* ++ * struct aw9523_irq - Interrupt controller structure ++ * @lock: mutex locking for the irq bus ++ * @irqchip: structure holding irqchip params ++ * @cached_gpio: stores the previous gpio status for bit comparison ++ */ ++struct aw9523_irq { ++ struct mutex lock; ++ struct irq_chip *irqchip; ++ u16 cached_gpio; ++}; ++ ++/* ++ * struct aw9523_pinmux - Pin mux params ++ * @name: Name of the mux ++ * @grps: Groups of the mux ++ * @num_grps: Number of groups (sizeof array grps) ++ */ ++struct aw9523_pinmux { ++ const char *name; ++ const char * const *grps; ++ const u8 num_grps; ++}; ++ ++/* ++ * struct aw9523 - Main driver structure ++ * @dev: device handle ++ * @regmap: regmap handle for current device ++ * @i2c_lock: Mutex lock for i2c operations ++ * @reset_gpio: Hardware reset (RSTN) signal GPIO ++ * @vio_vreg: VCC regulator (Optional) ++ * @pctl: pinctrl handle for current device ++ * @gpio: structure holding gpiochip params ++ * @irq: Interrupt controller structure ++ */ ++struct aw9523 { ++ struct device *dev; ++ struct regmap *regmap; ++ struct mutex i2c_lock; ++ struct gpio_desc *reset_gpio; ++ struct regulator *vio_vreg; ++ struct pinctrl_dev *pctl; ++ struct gpio_chip gpio; ++ struct aw9523_irq *irq; ++}; ++ ++static const struct pinctrl_pin_desc aw9523_pins[] = { ++ /* Port 0 */ ++ PINCTRL_PIN(0, "gpio0"), ++ PINCTRL_PIN(1, "gpio1"), ++ PINCTRL_PIN(2, "gpio2"), ++ PINCTRL_PIN(3, "gpio3"), ++ PINCTRL_PIN(4, "gpio4"), ++ PINCTRL_PIN(5, "gpio5"), ++ PINCTRL_PIN(6, "gpio6"), ++ PINCTRL_PIN(7, "gpio7"), ++ ++ /* Port 1 */ ++ PINCTRL_PIN(8, "gpio8"), ++ PINCTRL_PIN(9, "gpio9"), ++ PINCTRL_PIN(10, "gpio10"), ++ PINCTRL_PIN(11, "gpio11"), ++ PINCTRL_PIN(12, "gpio12"), ++ PINCTRL_PIN(13, "gpio13"), ++ PINCTRL_PIN(14, "gpio14"), ++ PINCTRL_PIN(15, "gpio15"), ++}; ++ ++static int aw9523_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) ++{ ++ return ARRAY_SIZE(aw9523_pins); ++} ++ ++static const char *aw9523_pinctrl_get_group_name(struct pinctrl_dev *pctldev, ++ unsigned int selector) ++{ ++ return aw9523_pins[selector].name; ++} ++ ++static int aw9523_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, ++ unsigned int selector, ++ const unsigned int **pins, ++ unsigned int *num_pins) ++{ ++ *pins = &aw9523_pins[selector].number; ++ *num_pins = 1; ++ return 0; ++} ++ ++static const struct pinctrl_ops aw9523_pinctrl_ops = { ++ .get_groups_count = aw9523_pinctrl_get_groups_count, ++ .get_group_pins = aw9523_pinctrl_get_group_pins, ++ .get_group_name = aw9523_pinctrl_get_group_name, ++ .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, ++ .dt_free_map = pinconf_generic_dt_free_map, ++}; ++ ++static const char * const gpio_pwm_groups[] = { ++ "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", ++ "gpio6", "gpio7", "gpio8", "gpio9", "gpio10", "gpio11", ++ "gpio12", "gpio13", "gpio14", "gpio15" ++}; ++ ++/* Warning: Do NOT reorder this array */ ++static const struct aw9523_pinmux aw9523_pmx[] = { ++ { ++ .name = "pwm", ++ .grps = gpio_pwm_groups, ++ .num_grps = ARRAY_SIZE(gpio_pwm_groups), ++ }, ++ { ++ .name = "gpio", ++ .grps = gpio_pwm_groups, ++ .num_grps = ARRAY_SIZE(gpio_pwm_groups), ++ }, ++}; ++ ++static int aw9523_pmx_get_funcs_count(struct pinctrl_dev *pctl) ++{ ++ return ARRAY_SIZE(aw9523_pmx); ++} ++ ++static const char *aw9523_pmx_get_fname(struct pinctrl_dev *pctl, ++ unsigned int sel) ++{ ++ return aw9523_pmx[sel].name; ++} ++ ++static int aw9523_pmx_get_groups(struct pinctrl_dev *pctl, unsigned int sel, ++ const char * const **groups, ++ unsigned int * const num_groups) ++{ ++ *groups = aw9523_pmx[sel].grps; ++ *num_groups = aw9523_pmx[sel].num_grps; ++ return 0; ++} ++ ++static int aw9523_pmx_set_mux(struct pinctrl_dev *pctl, unsigned int fsel, ++ unsigned int grp) ++{ ++ struct aw9523 *awi = pinctrl_dev_get_drvdata(pctl); ++ int ret, pin = aw9523_pins[grp].number % AW9523_PINS_PER_PORT; ++ ++ if (fsel >= ARRAY_SIZE(aw9523_pmx)) ++ return -EINVAL; ++ ++ /* ++ * This maps directly to the aw9523_pmx array: programming a ++ * high bit means "gpio" and a low bit means "pwm". ++ */ ++ mutex_lock(&awi->i2c_lock); ++ ret = regmap_update_bits(awi->regmap, AW9523_REG_PORT_MODE(pin), ++ BIT(pin), (fsel ? BIT(pin) : 0)); ++ mutex_unlock(&awi->i2c_lock); ++ return ret; ++} ++ ++static const struct pinmux_ops aw9523_pinmux_ops = { ++ .get_functions_count = aw9523_pmx_get_funcs_count, ++ .get_function_name = aw9523_pmx_get_fname, ++ .get_function_groups = aw9523_pmx_get_groups, ++ .set_mux = aw9523_pmx_set_mux, ++}; ++ ++static int aw9523_pcfg_param_to_reg(enum pin_config_param pcp, int pin, u8 *r) ++{ ++ u8 reg; ++ ++ switch (pcp) { ++ case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: ++ case PIN_CONFIG_BIAS_PULL_DOWN: ++ case PIN_CONFIG_BIAS_PULL_UP: ++ reg = AW9523_REG_IN_STATE(pin); ++ break; ++ case PIN_CONFIG_DRIVE_OPEN_DRAIN: ++ case PIN_CONFIG_DRIVE_PUSH_PULL: ++ reg = AW9523_REG_GCR; ++ break; ++ case PIN_CONFIG_INPUT_ENABLE: ++ case PIN_CONFIG_OUTPUT_ENABLE: ++ reg = AW9523_REG_CONF_STATE(pin); ++ break; ++ case PIN_CONFIG_OUTPUT: ++ reg = AW9523_REG_OUT_STATE(pin); ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ *r = reg; ++ ++ return 0; ++} ++ ++static int aw9523_pconf_get(struct pinctrl_dev *pctldev, unsigned int pin, ++ unsigned long *config) ++{ ++ struct aw9523 *awi = pinctrl_dev_get_drvdata(pctldev); ++ enum pin_config_param param = pinconf_to_config_param(*config); ++ int regbit = pin % AW9523_PINS_PER_PORT; ++ unsigned int val; ++ u8 reg; ++ int rc; ++ ++ rc = aw9523_pcfg_param_to_reg(param, pin, ®); ++ if (rc) ++ return rc; ++ ++ mutex_lock(&awi->i2c_lock); ++ rc = regmap_read(awi->regmap, reg, &val); ++ mutex_unlock(&awi->i2c_lock); ++ if (rc) ++ return rc; ++ ++ switch (param) { ++ case PIN_CONFIG_BIAS_PULL_UP: ++ case PIN_CONFIG_INPUT_ENABLE: ++ case PIN_CONFIG_OUTPUT: ++ val &= BIT(regbit); ++ break; ++ case PIN_CONFIG_BIAS_PULL_DOWN: ++ case PIN_CONFIG_OUTPUT_ENABLE: ++ val &= BIT(regbit); ++ val = !val; ++ break; ++ case PIN_CONFIG_DRIVE_OPEN_DRAIN: ++ if (pin >= AW9523_PINS_PER_PORT) ++ val = 0; ++ else ++ val = !FIELD_GET(AW9523_GCR_GPOMD_MASK, val); ++ break; ++ case PIN_CONFIG_DRIVE_PUSH_PULL: ++ if (pin >= AW9523_PINS_PER_PORT) ++ val = 1; ++ else ++ val = FIELD_GET(AW9523_GCR_GPOMD_MASK, val); ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ if (val < 1) ++ return -EINVAL; ++ ++ *config = pinconf_to_config_packed(param, !!val); ++ ++ return rc; ++} ++ ++static int aw9523_pconf_set(struct pinctrl_dev *pctldev, unsigned int pin, ++ unsigned long *configs, unsigned int num_configs) ++{ ++ struct aw9523 *awi = pinctrl_dev_get_drvdata(pctldev); ++ enum pin_config_param param; ++ int regbit = pin % AW9523_PINS_PER_PORT; ++ u32 arg; ++ u8 reg; ++ unsigned int mask, val; ++ int i, rc; ++ ++ mutex_lock(&awi->i2c_lock); ++ for (i = 0; i < num_configs; i++) { ++ param = pinconf_to_config_param(configs[i]); ++ arg = pinconf_to_config_argument(configs[i]); ++ ++ rc = aw9523_pcfg_param_to_reg(param, pin, ®); ++ if (rc) ++ goto end; ++ ++ switch (param) { ++ case PIN_CONFIG_OUTPUT: ++ /* First, enable pin output */ ++ rc = regmap_update_bits(awi->regmap, ++ AW9523_REG_CONF_STATE(pin), ++ BIT(regbit), 0); ++ if (rc) ++ goto end; ++ ++ /* Then, fall through to config output level */ ++ fallthrough; ++ case PIN_CONFIG_OUTPUT_ENABLE: ++ arg = !arg; ++ fallthrough; ++ case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: ++ case PIN_CONFIG_BIAS_PULL_DOWN: ++ case PIN_CONFIG_BIAS_PULL_UP: ++ case PIN_CONFIG_INPUT_ENABLE: ++ mask = BIT(regbit); ++ val = arg ? BIT(regbit) : 0; ++ break; ++ case PIN_CONFIG_DRIVE_OPEN_DRAIN: ++ /* Open-Drain is supported only on port 0 */ ++ if (pin >= AW9523_PINS_PER_PORT) { ++ rc = -EOPNOTSUPP; ++ goto end; ++ } ++ mask = AW9523_GCR_GPOMD_MASK; ++ val = 0; ++ break; ++ case PIN_CONFIG_DRIVE_PUSH_PULL: ++ /* Port 1 is always Push-Pull */ ++ if (pin >= AW9523_PINS_PER_PORT) { ++ mask = 0; ++ val = 0; ++ continue; ++ } ++ mask = AW9523_GCR_GPOMD_MASK; ++ val = AW9523_GCR_GPOMD_MASK; ++ break; ++ default: ++ rc = -EOPNOTSUPP; ++ goto end; ++ } ++ ++ rc = regmap_update_bits(awi->regmap, reg, mask, val); ++ if (rc) ++ goto end; ++ } ++end: ++ mutex_unlock(&awi->i2c_lock); ++ return rc; ++} ++ ++static const struct pinconf_ops aw9523_pinconf_ops = { ++ .pin_config_get = aw9523_pconf_get, ++ .pin_config_set = aw9523_pconf_set, ++ .is_generic = true, ++}; ++ ++/* ++ * aw9523_get_pin_direction - Get pin direction ++ * @regmap: Regmap structure ++ * @pin: gpiolib pin number ++ * @n: pin index in port register ++ * ++ * Return: Pin direction for success or negative number for error ++ */ ++static int aw9523_get_pin_direction(struct regmap *regmap, u8 pin, u8 n) ++{ ++ int ret; ++ ++ ret = regmap_test_bits(regmap, AW9523_REG_CONF_STATE(pin), BIT(n)); ++ if (ret < 0) ++ return ret; ++ ++ return ret ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT; ++} ++ ++/* ++ * aw9523_get_port_state - Get input or output state for entire port ++ * @regmap: Regmap structure ++ * @pin: gpiolib pin number ++ * @regbit: hw pin index, used to retrieve port number ++ * @state: returned port state ++ * ++ * Return: Zero for success or negative number for error ++ */ ++static int aw9523_get_port_state(struct regmap *regmap, u8 pin, ++ u8 regbit, unsigned int *state) ++{ ++ u8 reg; ++ int dir; ++ ++ dir = aw9523_get_pin_direction(regmap, pin, regbit); ++ if (dir < 0) ++ return dir; ++ ++ if (dir == GPIO_LINE_DIRECTION_IN) ++ reg = AW9523_REG_IN_STATE(pin); ++ else ++ reg = AW9523_REG_OUT_STATE(pin); ++ ++ return regmap_read(regmap, reg, state); ++} ++ ++static int aw9523_gpio_irq_type(struct irq_data *d, unsigned int type) ++{ ++ switch (type) { ++ case IRQ_TYPE_NONE: ++ case IRQ_TYPE_EDGE_BOTH: ++ return 0; ++ default: ++ return -EINVAL; ++ }; ++} ++ ++/* ++ * aw9523_irq_mask - Mask interrupt ++ * @d: irq data ++ * ++ * Sets which interrupt to mask in the bitmap; ++ * The interrupt will be masked when unlocking the irq bus. ++ */ ++static void aw9523_irq_mask(struct irq_data *d) ++{ ++ struct aw9523 *awi = gpiochip_get_data(irq_data_get_irq_chip_data(d)); ++ unsigned int n = d->hwirq % AW9523_PINS_PER_PORT; ++ ++ regmap_update_bits(awi->regmap, ++ AW9523_REG_INTR_DIS(d->hwirq), ++ BIT(n), BIT(n)); ++ gpiochip_disable_irq(&awi->gpio, irqd_to_hwirq(d)); ++} ++ ++/* ++ * aw9523_irq_unmask - Unmask interrupt ++ * @d: irq data ++ * ++ * Sets which interrupt to unmask in the bitmap; ++ * The interrupt will be masked when unlocking the irq bus. ++ */ ++static void aw9523_irq_unmask(struct irq_data *d) ++{ ++ struct aw9523 *awi = gpiochip_get_data(irq_data_get_irq_chip_data(d)); ++ unsigned int n = d->hwirq % AW9523_PINS_PER_PORT; ++ ++ gpiochip_enable_irq(&awi->gpio, irqd_to_hwirq(d)); ++ regmap_update_bits(awi->regmap, ++ AW9523_REG_INTR_DIS(d->hwirq), ++ BIT(n), 0); ++} ++ ++static irqreturn_t aw9523_irq_thread_func(int irq, void *dev_id) ++{ ++ struct aw9523 *awi = (struct aw9523 *)dev_id; ++ unsigned long n, val = 0; ++ unsigned long changed_gpio; ++ unsigned int tmp, port_pin, i, ret; ++ ++ for (i = 0; i < AW9523_NUM_PORTS; i++) { ++ port_pin = i * AW9523_PINS_PER_PORT; ++ ret = regmap_read(awi->regmap, ++ AW9523_REG_IN_STATE(port_pin), ++ &tmp); ++ if (ret) ++ return ret; ++ val |= (u8)tmp << (i * 8); ++ } ++ ++ /* Handle GPIO input release interrupt as well */ ++ changed_gpio = awi->irq->cached_gpio ^ val; ++ awi->irq->cached_gpio = val; ++ ++ /* ++ * To avoid up to four *slow* i2c reads from any driver hooked ++ * up to our interrupts, just check for the irq_find_mapping ++ * result: if the interrupt is not mapped, then we don't want ++ * to care about it. ++ */ ++ for_each_set_bit(n, &changed_gpio, awi->gpio.ngpio) { ++ tmp = irq_find_mapping(awi->gpio.irq.domain, n); ++ if (tmp <= 0) ++ continue; ++ handle_nested_irq(tmp); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++/* ++ * aw9523_irq_bus_lock - Grab lock for interrupt operation ++ * @d: irq data ++ */ ++static void aw9523_irq_bus_lock(struct irq_data *d) ++{ ++ struct aw9523 *awi = gpiochip_get_data(irq_data_get_irq_chip_data(d)); ++ ++ mutex_lock(&awi->irq->lock); ++ regcache_cache_only(awi->regmap, true); ++} ++ ++/* ++ * aw9523_irq_bus_sync_unlock - Synchronize state and unlock ++ * @d: irq data ++ * ++ * Writes the interrupt mask bits (found in the bit map) to the ++ * hardware, then unlocks the bus. ++ */ ++static void aw9523_irq_bus_sync_unlock(struct irq_data *d) ++{ ++ struct aw9523 *awi = gpiochip_get_data(irq_data_get_irq_chip_data(d)); ++ ++ regcache_cache_only(awi->regmap, false); ++ regcache_sync(awi->regmap); ++ mutex_unlock(&awi->irq->lock); ++} ++ ++static int aw9523_gpio_get_direction(struct gpio_chip *chip, ++ unsigned int offset) ++{ ++ struct aw9523 *awi = gpiochip_get_data(chip); ++ u8 regbit = offset % AW9523_PINS_PER_PORT; ++ int ret; ++ ++ mutex_lock(&awi->i2c_lock); ++ ret = aw9523_get_pin_direction(awi->regmap, offset, regbit); ++ mutex_unlock(&awi->i2c_lock); ++ ++ return ret; ++} ++ ++static int aw9523_gpio_get(struct gpio_chip *chip, unsigned int offset) ++{ ++ struct aw9523 *awi = gpiochip_get_data(chip); ++ u8 regbit = offset % AW9523_PINS_PER_PORT; ++ unsigned int val; ++ int ret; ++ ++ mutex_lock(&awi->i2c_lock); ++ ret = aw9523_get_port_state(awi->regmap, offset, regbit, &val); ++ mutex_unlock(&awi->i2c_lock); ++ if (ret) ++ return ret; ++ ++ return !!(val & BIT(regbit)); ++} ++ ++/** ++ * _aw9523_gpio_get_multiple - Get I/O state for an entire port ++ * @regmap: Regmap structure ++ * @pin: gpiolib pin number ++ * @regbit: hw pin index, used to retrieve port number ++ * @state: returned port I/O state ++ * ++ * Return: Zero for success or negative number for error ++ */ ++static int _aw9523_gpio_get_multiple(struct aw9523 *awi, u8 regbit, ++ u8 *state, u8 mask) ++{ ++ u32 dir_in, val; ++ u8 m; ++ int ret; ++ ++ /* Registers are 8-bits wide */ ++ ret = regmap_read(awi->regmap, AW9523_REG_CONF_STATE(regbit), &dir_in); ++ if (ret) ++ return ret; ++ *state = 0; ++ ++ m = mask & dir_in; ++ if (m) { ++ ret = regmap_read(awi->regmap, AW9523_REG_IN_STATE(regbit), ++ &val); ++ if (ret) ++ return ret; ++ *state |= (u8)val & m; ++ } ++ ++ m = mask & ~dir_in; ++ if (m) { ++ ret = regmap_read(awi->regmap, AW9523_REG_OUT_STATE(regbit), ++ &val); ++ if (ret) ++ return ret; ++ *state |= (u8)val & m; ++ } ++ ++ return 0; ++} ++ ++static int aw9523_gpio_get_multiple(struct gpio_chip *chip, ++ unsigned long *mask, ++ unsigned long *bits) ++{ ++ struct aw9523 *awi = gpiochip_get_data(chip); ++ u8 m, state = 0; ++ int ret; ++ ++ mutex_lock(&awi->i2c_lock); ++ ++ /* Port 0 (gpio 0-7) */ ++ m = *mask & U8_MAX; ++ if (m) { ++ ret = _aw9523_gpio_get_multiple(awi, 0, &state, m); ++ if (ret) ++ goto out; ++ } ++ *bits = state; ++ ++ /* Port 1 (gpio 8-15) */ ++ m = (*mask >> 8) & U8_MAX; ++ if (m) { ++ ret = _aw9523_gpio_get_multiple(awi, AW9523_PINS_PER_PORT, ++ &state, m); ++ if (ret) ++ goto out; ++ ++ *bits |= (state << 8); ++ } ++out: ++ mutex_unlock(&awi->i2c_lock); ++ return ret; ++} ++ ++static void aw9523_gpio_set_multiple(struct gpio_chip *chip, ++ unsigned long *mask, ++ unsigned long *bits) ++{ ++ struct aw9523 *awi = gpiochip_get_data(chip); ++ u8 mask_lo, mask_hi, bits_lo, bits_hi; ++ unsigned int reg; ++ int ret = 0; ++ ++ mask_lo = *mask & U8_MAX; ++ mask_hi = (*mask >> 8) & U8_MAX; ++ mutex_lock(&awi->i2c_lock); ++ if (mask_hi) { ++ reg = AW9523_REG_OUT_STATE(AW9523_PINS_PER_PORT); ++ bits_hi = (*bits >> 8) & U8_MAX; ++ ++ ret = regmap_write_bits(awi->regmap, reg, mask_hi, bits_hi); ++ if (ret) { ++ dev_warn(awi->dev, "Cannot write port1 out level\n"); ++ goto out; ++ } ++ } ++ if (mask_lo) { ++ reg = AW9523_REG_OUT_STATE(0); ++ bits_lo = *bits & U8_MAX; ++ ret = regmap_write_bits(awi->regmap, reg, mask_lo, bits_lo); ++ if (ret) ++ dev_warn(awi->dev, "Cannot write port0 out level\n"); ++ } ++out: ++ mutex_unlock(&awi->i2c_lock); ++} ++ ++static void aw9523_gpio_set(struct gpio_chip *chip, ++ unsigned int offset, int value) ++{ ++ struct aw9523 *awi = gpiochip_get_data(chip); ++ u8 regbit = offset % AW9523_PINS_PER_PORT; ++ ++ mutex_lock(&awi->i2c_lock); ++ regmap_update_bits(awi->regmap, AW9523_REG_OUT_STATE(offset), ++ BIT(regbit), value ? BIT(regbit) : 0); ++ mutex_unlock(&awi->i2c_lock); ++} ++ ++ ++static int aw9523_direction_input(struct gpio_chip *chip, unsigned int offset) ++{ ++ struct aw9523 *awi = gpiochip_get_data(chip); ++ u8 regbit = offset % AW9523_PINS_PER_PORT; ++ int ret; ++ ++ mutex_lock(&awi->i2c_lock); ++ ret = regmap_update_bits(awi->regmap, AW9523_REG_CONF_STATE(offset), ++ BIT(regbit), BIT(regbit)); ++ mutex_unlock(&awi->i2c_lock); ++ ++ return ret; ++} ++ ++static int aw9523_direction_output(struct gpio_chip *chip, ++ unsigned int offset, int value) ++{ ++ struct aw9523 *awi = gpiochip_get_data(chip); ++ u8 regbit = offset % AW9523_PINS_PER_PORT; ++ int ret; ++ ++ mutex_lock(&awi->i2c_lock); ++ ret = regmap_update_bits(awi->regmap, AW9523_REG_OUT_STATE(offset), ++ BIT(regbit), value ? BIT(regbit) : 0); ++ if (ret) ++ goto end; ++ ++ ret = regmap_update_bits(awi->regmap, AW9523_REG_CONF_STATE(offset), ++ BIT(regbit), 0); ++end: ++ mutex_unlock(&awi->i2c_lock); ++ return ret; ++} ++ ++static int aw9523_drive_reset_gpio(struct aw9523 *awi) ++{ ++ unsigned int chip_id; ++ int ret; ++ ++ /* ++ * If the chip is already configured for any reason, then we ++ * will probably succeed in sending the soft reset signal to ++ * the hardware through I2C: this operation takes less time ++ * compared to a full HW reset and it gives the same results. ++ */ ++ ret = regmap_write(awi->regmap, AW9523_REG_SOFT_RESET, 0); ++ if (ret == 0) ++ goto done; ++ ++ dev_dbg(awi->dev, "Cannot execute soft reset: trying hard reset\n"); ++ ret = gpiod_direction_output(awi->reset_gpio, 0); ++ if (ret) ++ return ret; ++ ++ /* The reset pulse has to be longer than 20uS due to deglitch */ ++ usleep_range(AW9523_HW_RESET_US, AW9523_HW_RESET_US + 1); ++ ++ ret = gpiod_direction_output(awi->reset_gpio, 1); ++ if (ret) ++ return ret; ++done: ++ /* The HW needs at least 1uS to reliably recover after reset */ ++ usleep_range(AW9523_HW_RESET_RECOVERY_US, ++ AW9523_HW_RESET_RECOVERY_US + 1); ++ ++ /* Check the ChipID */ ++ ret = regmap_read(awi->regmap, AW9523_REG_CHIPID, &chip_id); ++ if (ret) { ++ dev_err(awi->dev, "Cannot read Chip ID: %d\n", ret); ++ return ret; ++ } ++ if (chip_id != AW9523_VAL_EXPECTED_CHIPID) { ++ dev_err(awi->dev, "Bad ChipID; read 0x%x, expected 0x%x\n", ++ chip_id, AW9523_VAL_EXPECTED_CHIPID); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int aw9523_hw_reset(struct aw9523 *awi) ++{ ++ int ret, max_retries = 2; ++ ++ /* Sometimes the chip needs more than one reset cycle */ ++ do { ++ ret = aw9523_drive_reset_gpio(awi); ++ if (ret == 0) ++ break; ++ max_retries--; ++ } while (max_retries); ++ ++ return ret; ++} ++ ++static int aw9523_init_gpiochip(struct aw9523 *awi, unsigned int npins) ++{ ++ struct device *dev = awi->dev; ++ struct gpio_chip *gc = &awi->gpio; ++ ++ gc->label = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); ++ if (!gc->label) ++ return -ENOMEM; ++ ++ gc->base = -1; ++ gc->ngpio = npins; ++ gc->get_direction = aw9523_gpio_get_direction; ++ gc->direction_input = aw9523_direction_input; ++ gc->direction_output = aw9523_direction_output; ++ gc->get = aw9523_gpio_get; ++ gc->get_multiple = aw9523_gpio_get_multiple; ++ gc->set = aw9523_gpio_set; ++ gc->set_multiple = aw9523_gpio_set_multiple; ++ gc->set_config = gpiochip_generic_config; ++ gc->parent = dev; ++ gc->owner = THIS_MODULE; ++ gc->can_sleep = false; ++ ++ return 0; ++} ++ ++static const struct irq_chip aw9523_irq_chip = { ++ .name = "aw9523", ++ .irq_mask = aw9523_irq_mask, ++ .irq_unmask = aw9523_irq_unmask, ++ .irq_bus_lock = aw9523_irq_bus_lock, ++ .irq_bus_sync_unlock = aw9523_irq_bus_sync_unlock, ++ .irq_set_type = aw9523_gpio_irq_type, ++ .flags = IRQCHIP_IMMUTABLE, ++ GPIOCHIP_IRQ_RESOURCE_HELPERS, ++}; ++ ++static int aw9523_init_irq(struct aw9523 *awi, int irq) ++{ ++ struct device *dev = awi->dev; ++ struct gpio_irq_chip *girq; ++ struct irq_chip *irqchip; ++ int ret; ++ ++ if (!device_property_read_bool(dev, "interrupt-controller")) ++ return 0; ++ ++ irqchip = devm_kzalloc(dev, sizeof(*irqchip), GFP_KERNEL); ++ if (!irqchip) ++ return -ENOMEM; ++ ++ awi->irq = devm_kzalloc(dev, sizeof(*awi->irq), GFP_KERNEL); ++ if (!awi->irq) ++ return -ENOMEM; ++ ++ awi->irq->irqchip = irqchip; ++ mutex_init(&awi->irq->lock); ++ ++ ret = devm_request_threaded_irq(dev, irq, NULL, aw9523_irq_thread_func, ++ IRQF_ONESHOT, dev_name(dev), awi); ++ if (ret) { ++ dev_err(dev, "Failed to request irq %d\n", irq); ++ return ret; ++ } ++ ++ girq = &awi->gpio.irq; ++ gpio_irq_chip_set_chip(girq, &aw9523_irq_chip); ++ girq->parent_handler = NULL; ++ girq->num_parents = 0; ++ girq->parents = NULL; ++ girq->default_type = IRQ_TYPE_EDGE_BOTH; ++ girq->handler = handle_simple_irq; ++ girq->threaded = true; ++ ++ return 0; ++} ++ ++static bool aw9523_is_reg_hole(unsigned int reg) ++{ ++ return (reg > AW9523_REG_PORT_MODE(AW9523_PINS_PER_PORT) && ++ reg < AW9523_REG_SOFT_RESET) || ++ (reg > AW9523_REG_INTR_DIS(AW9523_PINS_PER_PORT) && ++ reg < AW9523_REG_CHIPID); ++} ++ ++static bool aw9523_readable_reg(struct device *dev, unsigned int reg) ++{ ++ /* All available registers (minus holes) can be read */ ++ return !aw9523_is_reg_hole(reg); ++} ++ ++static bool aw9523_volatile_reg(struct device *dev, unsigned int reg) ++{ ++ return aw9523_is_reg_hole(reg) || ++ reg == AW9523_REG_IN_STATE(0) || ++ reg == AW9523_REG_IN_STATE(AW9523_PINS_PER_PORT) || ++ reg == AW9523_REG_CHIPID || ++ reg == AW9523_REG_SOFT_RESET; ++} ++ ++static bool aw9523_writeable_reg(struct device *dev, unsigned int reg) ++{ ++ return !aw9523_is_reg_hole(reg) && reg != AW9523_REG_CHIPID; ++} ++ ++static bool aw9523_precious_reg(struct device *dev, unsigned int reg) ++{ ++ /* Reading AW9523_REG_IN_STATE clears interrupt status */ ++ return aw9523_is_reg_hole(reg) || ++ reg == AW9523_REG_IN_STATE(0) || ++ reg == AW9523_REG_IN_STATE(AW9523_PINS_PER_PORT); ++} ++ ++static const struct regmap_config aw9523_regmap = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .reg_stride = 1, ++ ++ .precious_reg = aw9523_precious_reg, ++ .readable_reg = aw9523_readable_reg, ++ .volatile_reg = aw9523_volatile_reg, ++ .writeable_reg = aw9523_writeable_reg, ++ ++ .cache_type = REGCACHE_FLAT, ++ .disable_locking = true, ++ ++ .num_reg_defaults_raw = AW9523_REG_SOFT_RESET, ++}; ++ ++static int aw9523_hw_init(struct aw9523 *awi) ++{ ++ u8 p1_pin = AW9523_PINS_PER_PORT; ++ unsigned int val; ++ int ret; ++ ++ /* No register caching during initialization */ ++ regcache_cache_bypass(awi->regmap, true); ++ ++ /* Bring up the chip */ ++ ret = aw9523_hw_reset(awi); ++ if (ret) { ++ dev_err(awi->dev, "HW Reset failed: %d\n", ret); ++ return ret; ++ } ++ ++ /* ++ * This is the expected chip and it is running: it's time to ++ * set a safe default configuration in case the user doesn't ++ * configure (all of the available) pins in this chip. ++ * P.S.: The writes order doesn't matter. ++ */ ++ ++ /* Set all pins as GPIO */ ++ ret = regmap_write(awi->regmap, AW9523_REG_PORT_MODE(0), U8_MAX); ++ if (ret) ++ return ret; ++ ret = regmap_write(awi->regmap, AW9523_REG_PORT_MODE(p1_pin), U8_MAX); ++ if (ret) ++ return ret; ++ ++ /* Set Open-Drain mode on Port 0 (Port 1 is always P-P) */ ++ ret = regmap_write(awi->regmap, AW9523_REG_GCR, 0); ++ if (ret) ++ return ret; ++ ++ /* Set all pins as inputs */ ++ ret = regmap_write(awi->regmap, AW9523_REG_CONF_STATE(0), U8_MAX); ++ if (ret) ++ return ret; ++ ret = regmap_write(awi->regmap, AW9523_REG_CONF_STATE(p1_pin), U8_MAX); ++ if (ret) ++ return ret; ++ ++ /* Disable all interrupts to avoid unreasoned wakeups */ ++ ret = regmap_write(awi->regmap, AW9523_REG_INTR_DIS(0), U8_MAX); ++ if (ret) ++ return ret; ++ ret = regmap_write(awi->regmap, AW9523_REG_INTR_DIS(p1_pin), U8_MAX); ++ if (ret) ++ return ret; ++ ++ /* Clear setup-generated interrupts by performing a port state read */ ++ ret = aw9523_get_port_state(awi->regmap, 0, 0, &val); ++ if (ret) ++ return ret; ++ ret = aw9523_get_port_state(awi->regmap, p1_pin, 0, &val); ++ if (ret) ++ return ret; ++ ++ /* Everything went fine: activate and reinitialize register cache */ ++ regcache_cache_bypass(awi->regmap, false); ++ return regmap_reinit_cache(awi->regmap, &aw9523_regmap); ++} ++ ++static int aw9523_probe(struct i2c_client *client) ++{ ++ struct device *dev = &client->dev; ++ struct pinctrl_desc *pdesc; ++ struct aw9523 *awi; ++ int ret; ++ ++ awi = devm_kzalloc(dev, sizeof(*awi), GFP_KERNEL); ++ if (!awi) ++ return -ENOMEM; ++ ++ i2c_set_clientdata(client, awi); ++ ++ awi->dev = dev; ++ awi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); ++ if (IS_ERR(awi->reset_gpio)) ++ return PTR_ERR(awi->reset_gpio); ++ gpiod_set_consumer_name(awi->reset_gpio, "aw9523 reset"); ++ ++ awi->regmap = devm_regmap_init_i2c(client, &aw9523_regmap); ++ if (IS_ERR(awi->regmap)) ++ return PTR_ERR(awi->regmap); ++ ++ awi->vio_vreg = devm_regulator_get_optional(dev, "vio"); ++ if (IS_ERR(awi->vio_vreg)) { ++ if (PTR_ERR(awi->vio_vreg) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ awi->vio_vreg = NULL; ++ } else { ++ ret = regulator_enable(awi->vio_vreg); ++ if (ret) ++ return ret; ++ } ++ ++ mutex_init(&awi->i2c_lock); ++ lockdep_set_subclass(&awi->i2c_lock, ++ i2c_adapter_depth(client->adapter)); ++ ++ pdesc = devm_kzalloc(dev, sizeof(*pdesc), GFP_KERNEL); ++ if (!pdesc) ++ return -ENOMEM; ++ ++ ret = aw9523_hw_init(awi); ++ if (ret) ++ goto err_disable_vregs; ++ ++ pdesc->name = dev_name(dev); ++ pdesc->owner = THIS_MODULE; ++ pdesc->pctlops = &aw9523_pinctrl_ops; ++ pdesc->pmxops = &aw9523_pinmux_ops; ++ pdesc->confops = &aw9523_pinconf_ops; ++ pdesc->pins = aw9523_pins; ++ pdesc->npins = ARRAY_SIZE(aw9523_pins); ++ ++ ret = aw9523_init_gpiochip(awi, pdesc->npins); ++ if (ret) ++ goto err_disable_vregs; ++ ++ if (client->irq) { ++ ret = aw9523_init_irq(awi, client->irq); ++ if (ret) ++ goto err_disable_vregs; ++ } ++ ++ awi->pctl = devm_pinctrl_register(dev, pdesc, awi); ++ if (IS_ERR(awi->pctl)) { ++ ret = PTR_ERR(awi->pctl); ++ dev_err(dev, "Cannot register pinctrl: %d", ret); ++ goto err_disable_vregs; ++ } ++ ++ ret = devm_gpiochip_add_data(dev, &awi->gpio, awi); ++ if (ret) ++ goto err_disable_vregs; ++ ++ return ret; ++ ++err_disable_vregs: ++ if (awi->vio_vreg) ++ regulator_disable(awi->vio_vreg); ++ mutex_destroy(&awi->i2c_lock); ++ return ret; ++} ++ ++static void aw9523_remove(struct i2c_client *client) ++{ ++ struct aw9523 *awi = i2c_get_clientdata(client); ++ int ret; ++ ++ if (!awi) ++ return; ++ ++ /* ++ * If the chip VIO is connected to a regulator that we can turn ++ * off, life is easy... otherwise, reinitialize the chip and ++ * set the pins to hardware defaults before removing the driver ++ * to leave it in a clean, safe and predictable state. ++ */ ++ if (awi->vio_vreg) { ++ regulator_disable(awi->vio_vreg); ++ } else { ++ mutex_lock(&awi->i2c_lock); ++ ret = aw9523_hw_init(awi); ++ mutex_unlock(&awi->i2c_lock); ++ if (ret) ++ return; ++ } ++ ++ mutex_destroy(&awi->i2c_lock); ++} ++ ++static const struct i2c_device_id aw9523_i2c_id_table[] = { ++ { "aw9523_i2c", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, aw9523_i2c_id_table); ++ ++static const struct of_device_id of_aw9523_i2c_match[] = { ++ { .compatible = "awinic,aw9523-pinctrl", }, ++}; ++MODULE_DEVICE_TABLE(of, of_aw9523_i2c_match); ++ ++static struct i2c_driver aw9523_driver = { ++ .driver = { ++ .name = "aw9523-pinctrl", ++ .of_match_table = of_aw9523_i2c_match, ++ }, ++ .probe = aw9523_probe, ++ .remove = aw9523_remove, ++ .id_table = aw9523_i2c_id_table, ++}; ++module_i2c_driver(aw9523_driver); ++ ++MODULE_DESCRIPTION("Awinic AW9523 I2C GPIO Expander driver"); ++MODULE_AUTHOR("AngeloGioacchino Del Regno "); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/ramips/patches-6.6/805-02-v6.9-pinctrl-aw9523-Add-proper-terminator.patch b/target/linux/ramips/patches-6.6/805-02-v6.9-pinctrl-aw9523-Add-proper-terminator.patch new file mode 100644 index 0000000000..193c797c21 --- /dev/null +++ b/target/linux/ramips/patches-6.6/805-02-v6.9-pinctrl-aw9523-Add-proper-terminator.patch @@ -0,0 +1,26 @@ +From 52279c3d50d964c646692c42a0db87ef7bb451cc Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Wed, 6 Mar 2024 08:54:25 +0100 +Subject: [PATCH] pinctrl: aw9523: Add proper terminator + +The of_device_id array needs to be terminated with a NULL +entry. + +Reported-by: kernel test robot +Closes: https://lore.kernel.org/oe-kbuild-all/202403061147.85XYVsk3-lkp@intel.com/ +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20240306-fix-aw9523-terminator-v1-1-13f90f87a7f6@linaro.org +--- + drivers/pinctrl/pinctrl-aw9523.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/pinctrl/pinctrl-aw9523.c ++++ b/drivers/pinctrl/pinctrl-aw9523.c +@@ -1099,6 +1099,7 @@ MODULE_DEVICE_TABLE(i2c, aw9523_i2c_id_t + + static const struct of_device_id of_aw9523_i2c_match[] = { + { .compatible = "awinic,aw9523-pinctrl", }, ++ { } + }; + MODULE_DEVICE_TABLE(of, of_aw9523_i2c_match); + diff --git a/target/linux/ramips/patches-6.6/805-03-v6.10-pinctrl-aw9523-Destroy-mutex-on-remove.patch b/target/linux/ramips/patches-6.6/805-03-v6.10-pinctrl-aw9523-Destroy-mutex-on-remove.patch new file mode 100644 index 0000000000..97d262451e --- /dev/null +++ b/target/linux/ramips/patches-6.6/805-03-v6.10-pinctrl-aw9523-Destroy-mutex-on-remove.patch @@ -0,0 +1,41 @@ +From e5e8a58023707472e5dbe9bc7b473a8703b401e0 Mon Sep 17 00:00:00 2001 +From: Andy Shevchenko +Date: Fri, 29 Mar 2024 12:55:15 +0200 +Subject: [PATCH] pinctrl: aw9523: Destroy mutex on ->remove() + +If aw9523_hw_init() fails on ->remove() the mutex left alive. +Destroy it in that case as well. While at it, remove never +true check at the beginning of the function. + +Signed-off-by: Andy Shevchenko +Message-ID: <20240329105634.712457-2-andy.shevchenko@gmail.com> +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/pinctrl-aw9523.c | 8 +------- + 1 file changed, 1 insertion(+), 7 deletions(-) + +--- a/drivers/pinctrl/pinctrl-aw9523.c ++++ b/drivers/pinctrl/pinctrl-aw9523.c +@@ -1067,10 +1067,6 @@ err_disable_vregs: + static void aw9523_remove(struct i2c_client *client) + { + struct aw9523 *awi = i2c_get_clientdata(client); +- int ret; +- +- if (!awi) +- return; + + /* + * If the chip VIO is connected to a regulator that we can turn +@@ -1082,10 +1078,8 @@ static void aw9523_remove(struct i2c_cli + regulator_disable(awi->vio_vreg); + } else { + mutex_lock(&awi->i2c_lock); +- ret = aw9523_hw_init(awi); ++ aw9523_hw_init(awi); + mutex_unlock(&awi->i2c_lock); +- if (ret) +- return; + } + + mutex_destroy(&awi->i2c_lock); diff --git a/target/linux/ramips/patches-6.6/805-04-v6.10-pinctrl-aw9523-Use-correct-error-code-for-not-suppor.patch b/target/linux/ramips/patches-6.6/805-04-v6.10-pinctrl-aw9523-Use-correct-error-code-for-not-suppor.patch new file mode 100644 index 0000000000..1217a6ce5e --- /dev/null +++ b/target/linux/ramips/patches-6.6/805-04-v6.10-pinctrl-aw9523-Use-correct-error-code-for-not-suppor.patch @@ -0,0 +1,62 @@ +From f91eafcb18e096108cd19d24ab71a0db5bc12416 Mon Sep 17 00:00:00 2001 +From: Andy Shevchenko +Date: Fri, 29 Mar 2024 12:55:16 +0200 +Subject: [PATCH] pinctrl: aw9523: Use correct error code for not supported + functionality + +The pin control subsystem internally uses ENOTSUPP for the not supported +functionality. The checkpatch is false positive about this error code. + +Signed-off-by: Andy Shevchenko +Message-ID: <20240329105634.712457-3-andy.shevchenko@gmail.com> +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/pinctrl-aw9523.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +--- a/drivers/pinctrl/pinctrl-aw9523.c ++++ b/drivers/pinctrl/pinctrl-aw9523.c +@@ -6,6 +6,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -239,7 +240,7 @@ static int aw9523_pcfg_param_to_reg(enum + reg = AW9523_REG_OUT_STATE(pin); + break; + default: +- return -EOPNOTSUPP; ++ return -ENOTSUPP; + } + *r = reg; + +@@ -290,7 +291,7 @@ static int aw9523_pconf_get(struct pinct + val = FIELD_GET(AW9523_GCR_GPOMD_MASK, val); + break; + default: +- return -EOPNOTSUPP; ++ return -ENOTSUPP; + } + if (val < 1) + return -EINVAL; +@@ -344,7 +345,7 @@ static int aw9523_pconf_set(struct pinct + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + /* Open-Drain is supported only on port 0 */ + if (pin >= AW9523_PINS_PER_PORT) { +- rc = -EOPNOTSUPP; ++ rc = -ENOTSUPP; + goto end; + } + mask = AW9523_GCR_GPOMD_MASK; +@@ -361,7 +362,7 @@ static int aw9523_pconf_set(struct pinct + val = AW9523_GCR_GPOMD_MASK; + break; + default: +- rc = -EOPNOTSUPP; ++ rc = -ENOTSUPP; + goto end; + } + diff --git a/target/linux/ramips/patches-6.6/805-05-v6.10-pinctrl-aw9523-Always-try-both-ports-in-aw9523_gpio_.patch b/target/linux/ramips/patches-6.6/805-05-v6.10-pinctrl-aw9523-Always-try-both-ports-in-aw9523_gpio_.patch new file mode 100644 index 0000000000..c90d4dac32 --- /dev/null +++ b/target/linux/ramips/patches-6.6/805-05-v6.10-pinctrl-aw9523-Always-try-both-ports-in-aw9523_gpio_.patch @@ -0,0 +1,47 @@ +From 091655b9285d837db520381924c689bd5dc5d286 Mon Sep 17 00:00:00 2001 +From: Andy Shevchenko +Date: Fri, 29 Mar 2024 12:55:17 +0200 +Subject: [PATCH] pinctrl: aw9523: Always try both ports in + aw9523_gpio_set_multiple() + +The ports are equivalent from the user's point of view. Don't limit +trying them both if writing to one fails. + +Signed-off-by: Andy Shevchenko +Message-ID: <20240329105634.712457-4-andy.shevchenko@gmail.com> +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/pinctrl-aw9523.c | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +--- a/drivers/pinctrl/pinctrl-aw9523.c ++++ b/drivers/pinctrl/pinctrl-aw9523.c +@@ -653,7 +653,7 @@ static void aw9523_gpio_set_multiple(str + struct aw9523 *awi = gpiochip_get_data(chip); + u8 mask_lo, mask_hi, bits_lo, bits_hi; + unsigned int reg; +- int ret = 0; ++ int ret; + + mask_lo = *mask & U8_MAX; + mask_hi = (*mask >> 8) & U8_MAX; +@@ -663,10 +663,8 @@ static void aw9523_gpio_set_multiple(str + bits_hi = (*bits >> 8) & U8_MAX; + + ret = regmap_write_bits(awi->regmap, reg, mask_hi, bits_hi); +- if (ret) { ++ if (ret) + dev_warn(awi->dev, "Cannot write port1 out level\n"); +- goto out; +- } + } + if (mask_lo) { + reg = AW9523_REG_OUT_STATE(0); +@@ -675,7 +673,6 @@ static void aw9523_gpio_set_multiple(str + if (ret) + dev_warn(awi->dev, "Cannot write port0 out level\n"); + } +-out: + mutex_unlock(&awi->i2c_lock); + } + diff --git a/target/linux/ramips/patches-6.6/805-06-v6.10-pinctrl-aw9523-Make-use-of-struct-pinfunction-and-PI.patch b/target/linux/ramips/patches-6.6/805-06-v6.10-pinctrl-aw9523-Make-use-of-struct-pinfunction-and-PI.patch new file mode 100644 index 0000000000..f300fa0193 --- /dev/null +++ b/target/linux/ramips/patches-6.6/805-06-v6.10-pinctrl-aw9523-Make-use-of-struct-pinfunction-and-PI.patch @@ -0,0 +1,72 @@ +From 418ee9488ff74ab4ada3a539a2840dda9e56f847 Mon Sep 17 00:00:00 2001 +From: Andy Shevchenko +Date: Fri, 29 Mar 2024 12:55:18 +0200 +Subject: [PATCH] pinctrl: aw9523: Make use of struct pinfunction and + PINCTRL_PINFUNCTION() + +Since pin control provides a generic data type and a macro for +the pin function definition, use them in the driver. + +Signed-off-by: Andy Shevchenko +Message-ID: <20240329105634.712457-5-andy.shevchenko@gmail.com> +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/pinctrl-aw9523.c | 32 ++++++-------------------------- + 1 file changed, 6 insertions(+), 26 deletions(-) + +--- a/drivers/pinctrl/pinctrl-aw9523.c ++++ b/drivers/pinctrl/pinctrl-aw9523.c +@@ -67,18 +67,6 @@ struct aw9523_irq { + }; + + /* +- * struct aw9523_pinmux - Pin mux params +- * @name: Name of the mux +- * @grps: Groups of the mux +- * @num_grps: Number of groups (sizeof array grps) +- */ +-struct aw9523_pinmux { +- const char *name; +- const char * const *grps; +- const u8 num_grps; +-}; +- +-/* + * struct aw9523 - Main driver structure + * @dev: device handle + * @regmap: regmap handle for current device +@@ -158,17 +146,9 @@ static const char * const gpio_pwm_group + }; + + /* Warning: Do NOT reorder this array */ +-static const struct aw9523_pinmux aw9523_pmx[] = { +- { +- .name = "pwm", +- .grps = gpio_pwm_groups, +- .num_grps = ARRAY_SIZE(gpio_pwm_groups), +- }, +- { +- .name = "gpio", +- .grps = gpio_pwm_groups, +- .num_grps = ARRAY_SIZE(gpio_pwm_groups), +- }, ++static const struct pinfunction aw9523_pmx[] = { ++ PINCTRL_PINFUNCTION("pwm", gpio_pwm_groups, ARRAY_SIZE(gpio_pwm_groups)), ++ PINCTRL_PINFUNCTION("gpio", gpio_pwm_groups, ARRAY_SIZE(gpio_pwm_groups)), + }; + + static int aw9523_pmx_get_funcs_count(struct pinctrl_dev *pctl) +@@ -184,10 +164,10 @@ static const char *aw9523_pmx_get_fname( + + static int aw9523_pmx_get_groups(struct pinctrl_dev *pctl, unsigned int sel, + const char * const **groups, +- unsigned int * const num_groups) ++ unsigned int * const ngroups) + { +- *groups = aw9523_pmx[sel].grps; +- *num_groups = aw9523_pmx[sel].num_grps; ++ *groups = aw9523_pmx[sel].groups; ++ *ngroups = aw9523_pmx[sel].ngroups; + return 0; + } + diff --git a/target/linux/ramips/patches-6.6/805-07-v6.10-pinctrl-aw9523-Use-temporary-variable-for-HW-IRQ-num.patch b/target/linux/ramips/patches-6.6/805-07-v6.10-pinctrl-aw9523-Use-temporary-variable-for-HW-IRQ-num.patch new file mode 100644 index 0000000000..98a1310ad5 --- /dev/null +++ b/target/linux/ramips/patches-6.6/805-07-v6.10-pinctrl-aw9523-Use-temporary-variable-for-HW-IRQ-num.patch @@ -0,0 +1,50 @@ +From 66413f0468d35adb352c76bc286bf6f6746ba354 Mon Sep 17 00:00:00 2001 +From: Andy Shevchenko +Date: Fri, 29 Mar 2024 12:55:19 +0200 +Subject: [PATCH] pinctrl: aw9523: Use temporary variable for HW IRQ number + +There are two different ways on how to get HW IRQ number in some functions. +Unify that by using temporary variable and irqd_to_hwirq() call. + +Signed-off-by: Andy Shevchenko +Message-ID: <20240329105634.712457-6-andy.shevchenko@gmail.com> +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/pinctrl-aw9523.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +--- a/drivers/pinctrl/pinctrl-aw9523.c ++++ b/drivers/pinctrl/pinctrl-aw9523.c +@@ -428,12 +428,12 @@ static int aw9523_gpio_irq_type(struct i + static void aw9523_irq_mask(struct irq_data *d) + { + struct aw9523 *awi = gpiochip_get_data(irq_data_get_irq_chip_data(d)); +- unsigned int n = d->hwirq % AW9523_PINS_PER_PORT; ++ irq_hw_number_t hwirq = irqd_to_hwirq(d); ++ unsigned int n = hwirq % AW9523_PINS_PER_PORT; + +- regmap_update_bits(awi->regmap, +- AW9523_REG_INTR_DIS(d->hwirq), ++ regmap_update_bits(awi->regmap, AW9523_REG_INTR_DIS(hwirq), + BIT(n), BIT(n)); +- gpiochip_disable_irq(&awi->gpio, irqd_to_hwirq(d)); ++ gpiochip_disable_irq(&awi->gpio, hwirq); + } + + /* +@@ -446,11 +446,11 @@ static void aw9523_irq_mask(struct irq_d + static void aw9523_irq_unmask(struct irq_data *d) + { + struct aw9523 *awi = gpiochip_get_data(irq_data_get_irq_chip_data(d)); +- unsigned int n = d->hwirq % AW9523_PINS_PER_PORT; ++ irq_hw_number_t hwirq = irqd_to_hwirq(d); ++ unsigned int n = hwirq % AW9523_PINS_PER_PORT; + +- gpiochip_enable_irq(&awi->gpio, irqd_to_hwirq(d)); +- regmap_update_bits(awi->regmap, +- AW9523_REG_INTR_DIS(d->hwirq), ++ gpiochip_enable_irq(&awi->gpio, hwirq); ++ regmap_update_bits(awi->regmap, AW9523_REG_INTR_DIS(hwirq), + BIT(n), 0); + } + diff --git a/target/linux/ramips/patches-6.6/805-08-v6.10-pinctrl-aw9523-Get-rid-of-redundant-U8_MAX-pieces.patch b/target/linux/ramips/patches-6.6/805-08-v6.10-pinctrl-aw9523-Get-rid-of-redundant-U8_MAX-pieces.patch new file mode 100644 index 0000000000..a9f86481e3 --- /dev/null +++ b/target/linux/ramips/patches-6.6/805-08-v6.10-pinctrl-aw9523-Get-rid-of-redundant-U8_MAX-pieces.patch @@ -0,0 +1,61 @@ +From 4210ef801a248223a0ea5f47b5446081b4925e10 Mon Sep 17 00:00:00 2001 +From: Andy Shevchenko +Date: Fri, 29 Mar 2024 12:55:20 +0200 +Subject: [PATCH] pinctrl: aw9523: Get rid of redundant ' & U8_MAX' pieces + +When the variable is declared as u8, no need to perform ' & U8_MAX' +as it's implied anyway. + +Signed-off-by: Andy Shevchenko +Message-ID: <20240329105634.712457-7-andy.shevchenko@gmail.com> +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/pinctrl-aw9523.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +--- a/drivers/pinctrl/pinctrl-aw9523.c ++++ b/drivers/pinctrl/pinctrl-aw9523.c +@@ -603,7 +603,7 @@ static int aw9523_gpio_get_multiple(stru + mutex_lock(&awi->i2c_lock); + + /* Port 0 (gpio 0-7) */ +- m = *mask & U8_MAX; ++ m = *mask; + if (m) { + ret = _aw9523_gpio_get_multiple(awi, 0, &state, m); + if (ret) +@@ -612,7 +612,7 @@ static int aw9523_gpio_get_multiple(stru + *bits = state; + + /* Port 1 (gpio 8-15) */ +- m = (*mask >> 8) & U8_MAX; ++ m = *mask >> 8; + if (m) { + ret = _aw9523_gpio_get_multiple(awi, AW9523_PINS_PER_PORT, + &state, m); +@@ -635,20 +635,20 @@ static void aw9523_gpio_set_multiple(str + unsigned int reg; + int ret; + +- mask_lo = *mask & U8_MAX; +- mask_hi = (*mask >> 8) & U8_MAX; ++ mask_lo = *mask; ++ mask_hi = *mask >> 8; ++ bits_lo = *bits; ++ bits_hi = *bits >> 8; ++ + mutex_lock(&awi->i2c_lock); + if (mask_hi) { + reg = AW9523_REG_OUT_STATE(AW9523_PINS_PER_PORT); +- bits_hi = (*bits >> 8) & U8_MAX; +- + ret = regmap_write_bits(awi->regmap, reg, mask_hi, bits_hi); + if (ret) + dev_warn(awi->dev, "Cannot write port1 out level\n"); + } + if (mask_lo) { + reg = AW9523_REG_OUT_STATE(0); +- bits_lo = *bits & U8_MAX; + ret = regmap_write_bits(awi->regmap, reg, mask_lo, bits_lo); + if (ret) + dev_warn(awi->dev, "Cannot write port0 out level\n"); diff --git a/target/linux/ramips/patches-6.6/805-09-v6.10-pinctrl-aw9523-Remove-unused-irqchip-field-in-struct.patch b/target/linux/ramips/patches-6.6/805-09-v6.10-pinctrl-aw9523-Remove-unused-irqchip-field-in-struct.patch new file mode 100644 index 0000000000..b37a425e8d --- /dev/null +++ b/target/linux/ramips/patches-6.6/805-09-v6.10-pinctrl-aw9523-Remove-unused-irqchip-field-in-struct.patch @@ -0,0 +1,52 @@ +From 6bf270863ade776485d1c6bdb8f69d642b0e5f64 Mon Sep 17 00:00:00 2001 +From: Andy Shevchenko +Date: Fri, 29 Mar 2024 12:55:21 +0200 +Subject: [PATCH] pinctrl: aw9523: Remove unused irqchip field in struct + aw9523_irq + +The irqchip field is allocated, assigned but never used. Remove it. + +Signed-off-by: Andy Shevchenko +Message-ID: <20240329105634.712457-8-andy.shevchenko@gmail.com> +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/pinctrl-aw9523.c | 8 -------- + 1 file changed, 8 deletions(-) + +--- a/drivers/pinctrl/pinctrl-aw9523.c ++++ b/drivers/pinctrl/pinctrl-aw9523.c +@@ -57,12 +57,10 @@ + /* + * struct aw9523_irq - Interrupt controller structure + * @lock: mutex locking for the irq bus +- * @irqchip: structure holding irqchip params + * @cached_gpio: stores the previous gpio status for bit comparison + */ + struct aw9523_irq { + struct mutex lock; +- struct irq_chip *irqchip; + u16 cached_gpio; + }; + +@@ -805,21 +803,15 @@ static int aw9523_init_irq(struct aw9523 + { + struct device *dev = awi->dev; + struct gpio_irq_chip *girq; +- struct irq_chip *irqchip; + int ret; + + if (!device_property_read_bool(dev, "interrupt-controller")) + return 0; + +- irqchip = devm_kzalloc(dev, sizeof(*irqchip), GFP_KERNEL); +- if (!irqchip) +- return -ENOMEM; +- + awi->irq = devm_kzalloc(dev, sizeof(*awi->irq), GFP_KERNEL); + if (!awi->irq) + return -ENOMEM; + +- awi->irq->irqchip = irqchip; + mutex_init(&awi->irq->lock); + + ret = devm_request_threaded_irq(dev, irq, NULL, aw9523_irq_thread_func, diff --git a/target/linux/ramips/patches-6.6/805-10-v6.10-pinctrl-aw9523-Make-use-of-dev_err_probe.patch b/target/linux/ramips/patches-6.6/805-10-v6.10-pinctrl-aw9523-Make-use-of-dev_err_probe.patch new file mode 100644 index 0000000000..f1f4e52140 --- /dev/null +++ b/target/linux/ramips/patches-6.6/805-10-v6.10-pinctrl-aw9523-Make-use-of-dev_err_probe.patch @@ -0,0 +1,40 @@ +From c567b00cc3d73f3ce4e92126731545d177262090 Mon Sep 17 00:00:00 2001 +From: Andy Shevchenko +Date: Fri, 29 Mar 2024 12:55:22 +0200 +Subject: [PATCH] pinctrl: aw9523: Make use of dev_err_probe() + +Simplify the error handling in probe function by switching from +dev_err() to dev_err_probe(). + +Signed-off-by: Andy Shevchenko +Message-ID: <20240329105634.712457-9-andy.shevchenko@gmail.com> +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/pinctrl-aw9523.c | 9 +++------ + 1 file changed, 3 insertions(+), 6 deletions(-) + +--- a/drivers/pinctrl/pinctrl-aw9523.c ++++ b/drivers/pinctrl/pinctrl-aw9523.c +@@ -816,10 +816,8 @@ static int aw9523_init_irq(struct aw9523 + + ret = devm_request_threaded_irq(dev, irq, NULL, aw9523_irq_thread_func, + IRQF_ONESHOT, dev_name(dev), awi); +- if (ret) { +- dev_err(dev, "Failed to request irq %d\n", irq); +- return ret; +- } ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to request irq %d\n", irq); + + girq = &awi->gpio.irq; + gpio_irq_chip_set_chip(girq, &aw9523_irq_chip); +@@ -1016,8 +1014,7 @@ static int aw9523_probe(struct i2c_clien + + awi->pctl = devm_pinctrl_register(dev, pdesc, awi); + if (IS_ERR(awi->pctl)) { +- ret = PTR_ERR(awi->pctl); +- dev_err(dev, "Cannot register pinctrl: %d", ret); ++ ret = dev_err_probe(dev, PTR_ERR(awi->pctl), "Cannot register pinctrl"); + goto err_disable_vregs; + } + diff --git a/target/linux/ramips/patches-6.6/805-11-v6.10-pinctrl-aw9523-Sort-headers-and-group-pinctrl.patch b/target/linux/ramips/patches-6.6/805-11-v6.10-pinctrl-aw9523-Sort-headers-and-group-pinctrl.patch new file mode 100644 index 0000000000..4d81c6ae7f --- /dev/null +++ b/target/linux/ramips/patches-6.6/805-11-v6.10-pinctrl-aw9523-Sort-headers-and-group-pinctrl.patch @@ -0,0 +1,41 @@ +From 7b8b9b5450b89d01e4b8f120b903cee85b529231 Mon Sep 17 00:00:00 2001 +From: Andy Shevchenko +Date: Fri, 29 Mar 2024 12:55:23 +0200 +Subject: [PATCH] pinctrl: aw9523: Sort headers and group pinctrl/* + +One header was misplaced and group pinctrl/* ones to show the relation +with the pin control subsystem. + +Signed-off-by: Andy Shevchenko +Message-ID: <20240329105634.712457-10-andy.shevchenko@gmail.com> +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/pinctrl-aw9523.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +--- a/drivers/pinctrl/pinctrl-aw9523.c ++++ b/drivers/pinctrl/pinctrl-aw9523.c +@@ -13,17 +13,18 @@ + #include + #include + #include +-#include + #include +-#include +-#include +-#include +-#include ++#include + #include + #include + #include + #include + ++#include ++#include ++#include ++#include ++ + #define AW9523_MAX_FUNCS 2 + #define AW9523_NUM_PORTS 2 + #define AW9523_PINS_PER_PORT 8 diff --git a/target/linux/ramips/patches-6.6/805-12-v6.10-pinctrl-aw9523-Fix-indentation-in-a-few-places.patch b/target/linux/ramips/patches-6.6/805-12-v6.10-pinctrl-aw9523-Fix-indentation-in-a-few-places.patch new file mode 100644 index 0000000000..3ebdcdfa4a --- /dev/null +++ b/target/linux/ramips/patches-6.6/805-12-v6.10-pinctrl-aw9523-Fix-indentation-in-a-few-places.patch @@ -0,0 +1,62 @@ +From 4aad0ad20f4ea80180a3e58b04b701728541c0f7 Mon Sep 17 00:00:00 2001 +From: Andy Shevchenko +Date: Fri, 29 Mar 2024 12:55:24 +0200 +Subject: [PATCH] pinctrl: aw9523: Fix indentation in a few places + +In the comment, function prototype, and array of strings indentation +is kinda broken. Reindent that. + +Signed-off-by: Andy Shevchenko +Message-ID: <20240329105634.712457-11-andy.shevchenko@gmail.com> +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/pinctrl-aw9523.c | 17 ++++++++--------- + 1 file changed, 8 insertions(+), 9 deletions(-) + +--- a/drivers/pinctrl/pinctrl-aw9523.c ++++ b/drivers/pinctrl/pinctrl-aw9523.c +@@ -1,8 +1,7 @@ + // SPDX-License-Identifier: GPL-2.0-only + /* + * Awinic AW9523B i2c pin controller driver +- * Copyright (c) 2020, AngeloGioacchino Del Regno +- * ++ * Copyright (c) 2020, AngeloGioacchino Del Regno + */ + + #include +@@ -139,9 +138,10 @@ static const struct pinctrl_ops aw9523_p + }; + + static const char * const gpio_pwm_groups[] = { +- "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", +- "gpio6", "gpio7", "gpio8", "gpio9", "gpio10", "gpio11", +- "gpio12", "gpio13", "gpio14", "gpio15" ++ "gpio0", "gpio1", "gpio2", "gpio3", /* 0-3 */ ++ "gpio4", "gpio5", "gpio6", "gpio7", /* 4-7 */ ++ "gpio8", "gpio9", "gpio10", "gpio11", /* 8-11 */ ++ "gpio12", "gpio13", "gpio14", "gpio15", /* 11-15 */ + }; + + /* Warning: Do NOT reorder this array */ +@@ -388,8 +388,8 @@ static int aw9523_get_pin_direction(stru + * + * Return: Zero for success or negative number for error + */ +-static int aw9523_get_port_state(struct regmap *regmap, u8 pin, +- u8 regbit, unsigned int *state) ++static int aw9523_get_port_state(struct regmap *regmap, u8 pin, u8 regbit, ++ unsigned int *state) + { + u8 reg; + int dir; +@@ -984,8 +984,7 @@ static int aw9523_probe(struct i2c_clien + } + + mutex_init(&awi->i2c_lock); +- lockdep_set_subclass(&awi->i2c_lock, +- i2c_adapter_depth(client->adapter)); ++ lockdep_set_subclass(&awi->i2c_lock, i2c_adapter_depth(client->adapter)); + + pdesc = devm_kzalloc(dev, sizeof(*pdesc), GFP_KERNEL); + if (!pdesc) diff --git a/target/linux/ramips/patches-6.6/805-pinctrl-AW9523.patch b/target/linux/ramips/patches-6.6/805-pinctrl-AW9523.patch deleted file mode 100644 index f9fa791fe1..0000000000 --- a/target/linux/ramips/patches-6.6/805-pinctrl-AW9523.patch +++ /dev/null @@ -1,72 +0,0 @@ -From: AngeloGioacchino Del Regno - -To: linus.walleij@linaro.org -Cc: linux-kernel@vger.kernel.org, konrad.dybcio@somainline.org, - marijn.suijten@somainline.org, martin.botka@somainline.org, - phone-devel@vger.kernel.org, linux-gpio@vger.kernel.org, - devicetree@vger.kernel.org, robh+dt@kernel.org, - AngeloGioacchino Del Regno - -Subject: [PATCH v5 1/2] pinctrl: Add driver for Awinic AW9523/B I2C GPIO - Expander -Date: Mon, 25 Jan 2021 19:22:18 +0100 - -The Awinic AW9523(B) is a multi-function I2C gpio expander in a -TQFN-24L package, featuring PWM (max 37mA per pin, or total max -power 3.2Watts) for LED driving capability. - -It has two ports with 8 pins per port (for a total of 16 pins), -configurable as either PWM with 1/256 stepping or GPIO input/output, -1.8V logic input; each GPIO can be configured as input or output -independently from each other. - -This IC also has an internal interrupt controller, which is capable -of generating an interrupt for each GPIO, depending on the -configuration, and will raise an interrupt on the INTN pin to -advertise this to an external interrupt controller. - -Signed-off-by: AngeloGioacchino Del Regno ---- - drivers/pinctrl/Kconfig | 17 + - drivers/pinctrl/Makefile | 1 + - drivers/pinctrl/pinctrl-aw9523.c | 1122 ++++++++++++++++++++++++++++++ - 3 files changed, 1140 insertions(+) - create mode 100644 drivers/pinctrl/pinctrl-aw9523.c - ---- a/drivers/pinctrl/Kconfig -+++ b/drivers/pinctrl/Kconfig -@@ -113,6 +113,24 @@ config PINCTRL_AT91PIO4 - Say Y here to enable the at91 pinctrl/gpio driver for Atmel PIO4 - controller available on sama5d2 SoC. - -+config PINCTRL_AW9523 -+ bool "Awinic AW9523/AW9523B I2C GPIO expander pinctrl driver" -+ depends on OF && I2C -+ select PINMUX -+ select PINCONF -+ select GENERIC_PINCONF -+ select GPIOLIB -+ select GPIOLIB_IRQCHIP -+ select REGMAP -+ select REGMAP_I2C -+ help -+ The Awinic AW9523/AW9523B is a multi-function I2C GPIO -+ expander with PWM functionality. This driver bundles a -+ pinctrl driver to select the function muxing and a GPIO -+ driver to handle GPIO, when the GPIO function is selected. -+ -+ Say yes to enable pinctrl and GPIO support for the AW9523(B). -+ - config PINCTRL_AXP209 - tristate "X-Powers AXP209 PMIC pinctrl and GPIO Support" - depends on MFD_AXP20X ---- a/drivers/pinctrl/Makefile -+++ b/drivers/pinctrl/Makefile -@@ -15,6 +15,7 @@ obj-$(CONFIG_PINCTRL_ARTPEC6) += pinctrl - obj-$(CONFIG_PINCTRL_AS3722) += pinctrl-as3722.o - obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o - obj-$(CONFIG_PINCTRL_AT91PIO4) += pinctrl-at91-pio4.o -+obj-$(CONFIG_PINCTRL_AW9523) += pinctrl-aw9523.o - obj-$(CONFIG_PINCTRL_AXP209) += pinctrl-axp209.o - obj-$(CONFIG_PINCTRL_BM1880) += pinctrl-bm1880.o - obj-$(CONFIG_PINCTRL_CY8C95X0) += pinctrl-cy8c95x0.o