| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190 |
- From 576623d706613feec2392ef16b84e23a46200552 Mon Sep 17 00:00:00 2001
- From: AngeloGioacchino Del Regno <[email protected]>
- 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 <[email protected]>
- Signed-off-by: David Bauer <[email protected]>
- Link: https://lore.kernel.org/r/[email protected]
- Acked-by: Krzysztof Kozlowski <[email protected]>
- Signed-off-by: Linus Walleij <[email protected]>
- Link: https://lore.kernel.org/r/[email protected]
- ---
- 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
- + * <[email protected]>
- + */
- +
- +#include <linux/bitfield.h>
- +#include <linux/gpio/consumer.h>
- +#include <linux/gpio/driver.h>
- +#include <linux/i2c.h>
- +#include <linux/init.h>
- +#include <linux/interrupt.h>
- +#include <linux/irq.h>
- +#include <linux/mutex.h>
- +#include <linux/module.h>
- +#include <linux/pinctrl/pinconf.h>
- +#include <linux/pinctrl/pinctrl.h>
- +#include <linux/pinctrl/pinmux.h>
- +#include <linux/pinctrl/pinconf-generic.h>
- +#include <linux/property.h>
- +#include <linux/regmap.h>
- +#include <linux/regulator/consumer.h>
- +#include <linux/slab.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 -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 <[email protected]>");
- +MODULE_LICENSE("GPL v2");
|