diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-02-11 10:53:39 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-02-11 10:53:39 -0800 |
| commit | d70178215211a7c73ecabeb55eeb0f8ef002bcab (patch) | |
| tree | b3943a90930022fb5f36a14d48ccc2742ace3dc3 /drivers/gpio | |
| parent | 893ace4df0f96b8ad066651453e0519d4ffe35ca (diff) | |
| parent | af9b4a56f0000fb11057e204ddfb05d72ba4dba0 (diff) | |
Merge tag 'gpio-updates-for-v7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux
Pull gpio updates from Bartosz Golaszewski:
"There are two new drivers and some changes to GPIO core but mostly
just GPIO driver updates across a wide array of files, adding support
for new models as well as various refactoring changes. Nothing
controversial and everything has spent a good measure of time in
linux-next.
GPIOLIB core:
- shrink the GPIO bus driver stub code
- rework software node support for "undefined" software nodes
- provide and use devm_fwnode_gpiod_get_optional()
- only compile the OF quirk for MT2701 when needed
New drivers:
- add the GPIO driver for ROHM bd72720
- add the gpio-line-mux driver providing 1-to-many mapping for a
single real GPIO
Driver changes:
- refactor gpio-pca9570: use lock guard, add missing headers, use
devres consistently
- add support for a new model (G7 Aspeed sgpiom) to the aspeed-sgpio
driver along with some prerequisite refactoring
- use device_get_match_data() where applicable and save some lines
- add support for more models to gpio-cadence
- add the compatible property to reset-gpio and use it in shared GPIO
management
- drop unnecessary use of irqd_get_trigger_type() in gpio-max77759
- add support for a new variant to gpio-pca953x
- extend build coverage with COMPILE_TEST for more drivers
- constify configfs structures in gpio-sim and gpio-virtuser
- add support for the K3 SoC to gpio-spacemit
- implement the missing .get_direction() callback in gpio-max77620
- add support for Tegra264 to gpio-tegra186
- drop unneeded MODULE_ALIAS() from gpio-menz127
DT bindings:
- document support for the opencores GPIO controller in gpio-mmio
- document new variants for gpio-pca953x
Documentation:
- extensively describe interrupt source detection for gpio-pca953x
and add more models to the list of supported variants"
* tag 'gpio-updates-for-v7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: (59 commits)
gpio: tegra186: Add support for Tegra264
dt-bindings: gpio: Add Tegra264 support
gpio: spacemit-k1: Use PDR for pin direction, not SDR/CDR
gpio: max77620: Implement .get_direction() callback
gpio: aspeed-sgpio: Support G7 Aspeed sgpiom controller
dt-bindings: gpio: aspeed,sgpio: Support ast2700
gpio: aspeed-sgpio: Convert IRQ functions to use llops callbacks
gpio: aspeed-sgpio: Create llops to handle hardware access
gpio: aspeed-sgpio: Remove unused bank name field
gpio: aspeed-sgpio: Change the macro to support deferred probe
regulator: bd71815: switch to devm_fwnode_gpiod_get_optional
gpiolib: introduce devm_fwnode_gpiod_get_optional() wrapper
gpio: mmio: Add compatible for opencores GPIO
dt-bindings: gpio-mmio: Correct opencores GPIO
gpio: pca9570: use lock guards
gpio: pca9570: Don't use "proxy" headers
gpio: pca9570: Use devm_mutex_init() for mutex initialization
MAINTAINERS: Add ROHM BD72720 PMIC
power: supply: bd71828-power: Support ROHM BD72720
power: supply: bd71828: Support wider register addresses
...
Diffstat (limited to 'drivers/gpio')
| -rw-r--r-- | drivers/gpio/Kconfig | 24 | ||||
| -rw-r--r-- | drivers/gpio/Makefile | 2 | ||||
| -rw-r--r-- | drivers/gpio/gpio-aggregator.c | 8 | ||||
| -rw-r--r-- | drivers/gpio/gpio-aspeed-sgpio.c | 362 | ||||
| -rw-r--r-- | drivers/gpio/gpio-aspeed.c | 7 | ||||
| -rw-r--r-- | drivers/gpio/gpio-bd72720.c | 281 | ||||
| -rw-r--r-- | drivers/gpio/gpio-cadence.c | 76 | ||||
| -rw-r--r-- | drivers/gpio/gpio-creg-snps.c | 4 | ||||
| -rw-r--r-- | drivers/gpio/gpio-line-mux.c | 116 | ||||
| -rw-r--r-- | drivers/gpio/gpio-max77620.c | 19 | ||||
| -rw-r--r-- | drivers/gpio/gpio-max77759.c | 10 | ||||
| -rw-r--r-- | drivers/gpio/gpio-menz127.c | 1 | ||||
| -rw-r--r-- | drivers/gpio/gpio-mmio.c | 1 | ||||
| -rw-r--r-- | drivers/gpio/gpio-pca953x.c | 6 | ||||
| -rw-r--r-- | drivers/gpio/gpio-pca9570.c | 19 | ||||
| -rw-r--r-- | drivers/gpio/gpio-realtek-otto.c | 5 | ||||
| -rw-r--r-- | drivers/gpio/gpio-sim.c | 16 | ||||
| -rw-r--r-- | drivers/gpio/gpio-spacemit-k1.c | 168 | ||||
| -rw-r--r-- | drivers/gpio/gpio-tegra186.c | 90 | ||||
| -rw-r--r-- | drivers/gpio/gpio-virtuser.c | 8 | ||||
| -rw-r--r-- | drivers/gpio/gpio-zynq.c | 12 | ||||
| -rw-r--r-- | drivers/gpio/gpiolib-of.c | 4 | ||||
| -rw-r--r-- | drivers/gpio/gpiolib-shared.c | 7 | ||||
| -rw-r--r-- | drivers/gpio/gpiolib-swnode.c | 9 | ||||
| -rw-r--r-- | drivers/gpio/gpiolib.c | 30 |
25 files changed, 999 insertions, 286 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index bd185482a7fd..b45fb799e36c 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -600,7 +600,7 @@ config GPIO_RDA config GPIO_REALTEK_OTTO tristate "Realtek Otto GPIO support" - depends on MACH_REALTEK_RTL + depends on MACH_REALTEK_RTL || COMPILE_TEST default MACH_REALTEK_RTL select GPIO_GENERIC select GPIOLIB_IRQCHIP @@ -1193,11 +1193,11 @@ config GPIO_PCA953X 8 bits: max7310, max7315, pca6107, pca9534, pca9538, pca9554, pca9556, pca9557, pca9574, tca6408, tca9554, xra1202, - pcal6408, pcal9554b, tca9538 + pcal6408, pcal9554b, tca9538, tcal6408 16 bits: max7312, max7313, pca9535, pca9539, pca9555, pca9575, tca6416, pca6416, pcal6416, pcal9535, pcal9555a, max7318, - tca9539 + tca9539, tcal6416 18 bits: tca6418 @@ -1317,6 +1317,15 @@ config GPIO_BD71828 This driver can also be built as a module. If so, the module will be called gpio-bd71828. +config GPIO_BD72720 + tristate "ROHM BD72720 and BD73900 PMIC GPIO support" + depends on MFD_ROHM_BD71828 + help + Support for GPIO on ROHM BD72720 and BD73900 PMICs. There are two + pins which can be configured to GPI or GPO, and three pins which can + be configured to GPO on the ROHM PMIC. The pin configuration is done + on OTP at manufacturing. + config GPIO_BD9571MWV tristate "ROHM BD9571 GPIO support" depends on MFD_BD9571MWV @@ -1994,6 +2003,15 @@ config GPIO_LATCH Say yes here to enable a driver for GPIO multiplexers based on latches connected to other GPIOs. +config GPIO_LINE_MUX + tristate "GPIO line mux driver" + depends on OF_GPIO + select MULTIPLEXER + help + Say Y here to support the GPIO line mux, which can provide virtual + GPIOs backed by a shared real GPIO and a multiplexer in a 1-to-many + fashion. + config GPIO_MOCKUP tristate "GPIO Testing Driver (DEPRECATED)" select IRQ_SIM diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 2421a8fd3733..c05f7d795c43 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o obj-$(CONFIG_GPIO_BCM_XGS_IPROC) += gpio-xgs-iproc.o obj-$(CONFIG_GPIO_BD71815) += gpio-bd71815.o obj-$(CONFIG_GPIO_BD71828) += gpio-bd71828.o +obj-$(CONFIG_GPIO_BD72720) += gpio-bd72720.o obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o obj-$(CONFIG_GPIO_BLZP1600) += gpio-blzp1600.o obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o @@ -90,6 +91,7 @@ obj-$(CONFIG_GPIO_IXP4XX) += gpio-ixp4xx.o obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o obj-$(CONFIG_GPIO_KEMPLD) += gpio-kempld.o obj-$(CONFIG_GPIO_LATCH) += gpio-latch.o +obj-$(CONFIG_GPIO_LINE_MUX) += gpio-line-mux.o obj-$(CONFIG_GPIO_LJCA) += gpio-ljca.o obj-$(CONFIG_GPIO_LOGICVC) += gpio-logicvc.o obj-$(CONFIG_GPIO_LOONGSON1) += gpio-loongson1.o diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index 416f265d09d0..a4cd32674a96 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -1226,7 +1226,7 @@ gpio_aggregator_line_release(struct config_item *item) kfree(line); } -static struct configfs_item_operations gpio_aggregator_line_item_ops = { +static const struct configfs_item_operations gpio_aggregator_line_item_ops = { .release = gpio_aggregator_line_release, }; @@ -1247,7 +1247,7 @@ static void gpio_aggregator_device_release(struct config_item *item) gpio_aggregator_free(aggr); } -static struct configfs_item_operations gpio_aggregator_device_item_ops = { +static const struct configfs_item_operations gpio_aggregator_device_item_ops = { .release = gpio_aggregator_device_release, }; @@ -1292,7 +1292,7 @@ gpio_aggregator_device_make_group(struct config_group *group, const char *name) return &line->group; } -static struct configfs_group_operations gpio_aggregator_device_group_ops = { +static const struct configfs_group_operations gpio_aggregator_device_group_ops = { .make_group = gpio_aggregator_device_make_group, }; @@ -1328,7 +1328,7 @@ gpio_aggregator_make_group(struct config_group *group, const char *name) return &aggr->group; } -static struct configfs_group_operations gpio_aggregator_group_ops = { +static const struct configfs_group_operations gpio_aggregator_group_ops = { .make_group = gpio_aggregator_make_group, }; diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c index 7622f9e9f54a..4225261f61c8 100644 --- a/drivers/gpio/gpio-aspeed-sgpio.c +++ b/drivers/gpio/gpio-aspeed-sgpio.c @@ -19,7 +19,31 @@ #include <linux/spinlock.h> #include <linux/string.h> -#define ASPEED_SGPIO_CTRL 0x54 +#define SGPIO_G7_IRQ_STS_BASE 0x40 +#define SGPIO_G7_IRQ_STS_OFFSET(x) (SGPIO_G7_IRQ_STS_BASE + (x) * 0x4) +#define SGPIO_G7_CTRL_REG_BASE 0x80 +#define SGPIO_G7_CTRL_REG_OFFSET(x) (SGPIO_G7_CTRL_REG_BASE + (x) * 0x4) +#define SGPIO_G7_OUT_DATA BIT(0) +#define SGPIO_G7_PARALLEL_OUT_DATA BIT(1) +#define SGPIO_G7_IRQ_EN BIT(2) +#define SGPIO_G7_IRQ_TYPE0 BIT(3) +#define SGPIO_G7_IRQ_TYPE1 BIT(4) +#define SGPIO_G7_IRQ_TYPE2 BIT(5) +#define SGPIO_G7_RST_TOLERANCE BIT(6) +#define SGPIO_G7_INPUT_MASK BIT(9) +#define SGPIO_G7_HW_BYPASS_EN BIT(10) +#define SGPIO_G7_HW_IN_SEL BIT(11) +#define SGPIO_G7_IRQ_STS BIT(12) +#define SGPIO_G7_IN_DATA BIT(13) +#define SGPIO_G7_PARALLEL_IN_DATA BIT(14) +#define SGPIO_G7_SERIAL_OUT_SEL GENMASK(17, 16) +#define SGPIO_G7_PARALLEL_OUT_SEL GENMASK(19, 18) +#define SELECT_FROM_CSR 0 +#define SELECT_FROM_PARALLEL_IN 1 +#define SELECT_FROM_SERIAL_IN 2 + +#define ASPEED_SGPIO_G4_CFG_OFFSET 0x54 +#define ASPEED_SGPIO_G7_CFG_OFFSET 0x0 #define ASPEED_SGPIO_CLK_DIV_MASK GENMASK(31, 16) #define ASPEED_SGPIO_ENABLE BIT(0) @@ -27,6 +51,8 @@ struct aspeed_sgpio_pdata { const u32 pin_mask; + const struct aspeed_sgpio_llops *llops; + const u32 cfg_offset; }; struct aspeed_sgpio { @@ -36,6 +62,7 @@ struct aspeed_sgpio { raw_spinlock_t lock; void __iomem *base; int irq; + const struct aspeed_sgpio_pdata *pdata; }; struct aspeed_sgpio_bank { @@ -43,7 +70,6 @@ struct aspeed_sgpio_bank { u16 rdata_reg; u16 irq_regs; u16 tolerance_regs; - const char names[4][3]; }; /* @@ -59,28 +85,24 @@ static const struct aspeed_sgpio_bank aspeed_sgpio_banks[] = { .rdata_reg = 0x0070, .irq_regs = 0x0004, .tolerance_regs = 0x0018, - .names = { "A", "B", "C", "D" }, }, { .val_regs = 0x001C, .rdata_reg = 0x0074, .irq_regs = 0x0020, .tolerance_regs = 0x0034, - .names = { "E", "F", "G", "H" }, }, { .val_regs = 0x0038, .rdata_reg = 0x0078, .irq_regs = 0x003C, .tolerance_regs = 0x0050, - .names = { "I", "J", "K", "L" }, }, { .val_regs = 0x0090, .rdata_reg = 0x007C, .irq_regs = 0x0094, .tolerance_regs = 0x00A8, - .names = { "M", "N", "O", "P" }, }, }; @@ -95,6 +117,15 @@ enum aspeed_sgpio_reg { reg_tolerance, }; +struct aspeed_sgpio_llops { + void (*reg_bit_set)(struct aspeed_sgpio *gpio, unsigned int offset, + const enum aspeed_sgpio_reg reg, bool val); + bool (*reg_bit_get)(struct aspeed_sgpio *gpio, unsigned int offset, + const enum aspeed_sgpio_reg reg); + int (*reg_bank_get)(struct aspeed_sgpio *gpio, unsigned int offset, + const enum aspeed_sgpio_reg reg); +}; + #define GPIO_VAL_VALUE 0x00 #define GPIO_IRQ_ENABLE 0x00 #define GPIO_IRQ_TYPE0 0x04 @@ -102,9 +133,9 @@ enum aspeed_sgpio_reg { #define GPIO_IRQ_TYPE2 0x0C #define GPIO_IRQ_STATUS 0x10 -static void __iomem *bank_reg(struct aspeed_sgpio *gpio, - const struct aspeed_sgpio_bank *bank, - const enum aspeed_sgpio_reg reg) +static void __iomem *aspeed_sgpio_g4_bank_reg(struct aspeed_sgpio *gpio, + const struct aspeed_sgpio_bank *bank, + const enum aspeed_sgpio_reg reg) { switch (reg) { case reg_val: @@ -129,6 +160,30 @@ static void __iomem *bank_reg(struct aspeed_sgpio *gpio, } } +static u32 aspeed_sgpio_g7_reg_mask(const enum aspeed_sgpio_reg reg) +{ + switch (reg) { + case reg_val: + case reg_rdata: + return SGPIO_G7_OUT_DATA; + case reg_irq_enable: + return SGPIO_G7_IRQ_EN; + case reg_irq_type0: + return SGPIO_G7_IRQ_TYPE0; + case reg_irq_type1: + return SGPIO_G7_IRQ_TYPE1; + case reg_irq_type2: + return SGPIO_G7_IRQ_TYPE2; + case reg_irq_status: + return SGPIO_G7_IRQ_STS; + case reg_tolerance: + return SGPIO_G7_RST_TOLERANCE; + default: + WARN_ON_ONCE(1); + return 0; + } +} + #define GPIO_BANK(x) ((x) >> 6) #define GPIO_OFFSET(x) ((x) & GENMASK(5, 0)) #define GPIO_BIT(x) BIT(GPIO_OFFSET(x) >> 1) @@ -170,14 +225,13 @@ static bool aspeed_sgpio_is_input(unsigned int offset) static int aspeed_sgpio_get(struct gpio_chip *gc, unsigned int offset) { struct aspeed_sgpio *gpio = gpiochip_get_data(gc); - const struct aspeed_sgpio_bank *bank = to_bank(offset); enum aspeed_sgpio_reg reg; int rc = 0; guard(raw_spinlock_irqsave)(&gpio->lock); reg = aspeed_sgpio_is_input(offset) ? reg_val : reg_rdata; - rc = !!(ioread32(bank_reg(gpio, bank, reg)) & GPIO_BIT(offset)); + rc = gpio->pdata->llops->reg_bit_get(gpio, offset, reg); return rc; } @@ -185,26 +239,11 @@ static int aspeed_sgpio_get(struct gpio_chip *gc, unsigned int offset) static int sgpio_set_value(struct gpio_chip *gc, unsigned int offset, int val) { struct aspeed_sgpio *gpio = gpiochip_get_data(gc); - const struct aspeed_sgpio_bank *bank = to_bank(offset); - void __iomem *addr_r, *addr_w; - u32 reg = 0; if (aspeed_sgpio_is_input(offset)) return -EINVAL; - /* Since this is an output, read the cached value from rdata, then - * update val. */ - addr_r = bank_reg(gpio, bank, reg_rdata); - addr_w = bank_reg(gpio, bank, reg_val); - - reg = ioread32(addr_r); - - if (val) - reg |= GPIO_BIT(offset); - else - reg &= ~GPIO_BIT(offset); - - iowrite32(reg, addr_w); + gpio->pdata->llops->reg_bit_set(gpio, offset, reg_val, val); return 0; } @@ -243,69 +282,34 @@ static int aspeed_sgpio_get_direction(struct gpio_chip *gc, unsigned int offset) return !!aspeed_sgpio_is_input(offset); } -static void irqd_to_aspeed_sgpio_data(struct irq_data *d, - struct aspeed_sgpio **gpio, - const struct aspeed_sgpio_bank **bank, - u32 *bit, int *offset) -{ - struct aspeed_sgpio *internal; - - *offset = irqd_to_hwirq(d); - internal = irq_data_get_irq_chip_data(d); - WARN_ON(!internal); - - *gpio = internal; - *bank = to_bank(*offset); - *bit = GPIO_BIT(*offset); -} static void aspeed_sgpio_irq_ack(struct irq_data *d) { - const struct aspeed_sgpio_bank *bank; - struct aspeed_sgpio *gpio; - void __iomem *status_addr; - int offset; - u32 bit; - - irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset); - - status_addr = bank_reg(gpio, bank, reg_irq_status); + struct aspeed_sgpio *gpio = irq_data_get_irq_chip_data(d); + int offset = irqd_to_hwirq(d); guard(raw_spinlock_irqsave)(&gpio->lock); - iowrite32(bit, status_addr); + gpio->pdata->llops->reg_bit_set(gpio, offset, reg_irq_status, 1); } static void aspeed_sgpio_irq_set_mask(struct irq_data *d, bool set) { - const struct aspeed_sgpio_bank *bank; - struct aspeed_sgpio *gpio; - u32 reg, bit; - void __iomem *addr; - int offset; - - irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset); - addr = bank_reg(gpio, bank, reg_irq_enable); + struct aspeed_sgpio *gpio = irq_data_get_irq_chip_data(d); + int offset = irqd_to_hwirq(d); /* Unmasking the IRQ */ if (set) - gpiochip_enable_irq(&gpio->chip, irqd_to_hwirq(d)); - - scoped_guard(raw_spinlock_irqsave, &gpio->lock) { - reg = ioread32(addr); - if (set) - reg |= bit; - else - reg &= ~bit; - - iowrite32(reg, addr); + gpiochip_enable_irq(&gpio->chip, offset); + scoped_guard(raw_spinlock_irqsave, &gpio->lock) + { + gpio->pdata->llops->reg_bit_set(gpio, offset, reg_irq_enable, + set); } /* Masking the IRQ */ if (!set) - gpiochip_disable_irq(&gpio->chip, irqd_to_hwirq(d)); - - + gpiochip_disable_irq(&gpio->chip, offset); } static void aspeed_sgpio_irq_mask(struct irq_data *d) @@ -323,30 +327,25 @@ static int aspeed_sgpio_set_type(struct irq_data *d, unsigned int type) u32 type0 = 0; u32 type1 = 0; u32 type2 = 0; - u32 bit, reg; - const struct aspeed_sgpio_bank *bank; irq_flow_handler_t handler; - struct aspeed_sgpio *gpio; - void __iomem *addr; - int offset; - - irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset); + struct aspeed_sgpio *gpio = irq_data_get_irq_chip_data(d); + int offset = irqd_to_hwirq(d); switch (type & IRQ_TYPE_SENSE_MASK) { case IRQ_TYPE_EDGE_BOTH: - type2 |= bit; + type2 = 1; fallthrough; case IRQ_TYPE_EDGE_RISING: - type0 |= bit; + type0 = 1; fallthrough; case IRQ_TYPE_EDGE_FALLING: handler = handle_edge_irq; break; case IRQ_TYPE_LEVEL_HIGH: - type0 |= bit; + type0 = 1; fallthrough; case IRQ_TYPE_LEVEL_LOW: - type1 |= bit; + type1 = 1; handler = handle_level_irq; break; default: @@ -354,20 +353,9 @@ static int aspeed_sgpio_set_type(struct irq_data *d, unsigned int type) } scoped_guard(raw_spinlock_irqsave, &gpio->lock) { - addr = bank_reg(gpio, bank, reg_irq_type0); - reg = ioread32(addr); - reg = (reg & ~bit) | type0; - iowrite32(reg, addr); - - addr = bank_reg(gpio, bank, reg_irq_type1); - reg = ioread32(addr); - reg = (reg & ~bit) | type1; - iowrite32(reg, addr); - - addr = bank_reg(gpio, bank, reg_irq_type2); - reg = ioread32(addr); - reg = (reg & ~bit) | type2; - iowrite32(reg, addr); + gpio->pdata->llops->reg_bit_set(gpio, offset, reg_irq_type0, type0); + gpio->pdata->llops->reg_bit_set(gpio, offset, reg_irq_type1, type1); + gpio->pdata->llops->reg_bit_set(gpio, offset, reg_irq_type2, type2); } irq_set_handler_locked(d, handler); @@ -380,15 +368,14 @@ static void aspeed_sgpio_irq_handler(struct irq_desc *desc) struct gpio_chip *gc = irq_desc_get_handler_data(desc); struct irq_chip *ic = irq_desc_get_chip(desc); struct aspeed_sgpio *data = gpiochip_get_data(gc); - unsigned int i, p; + unsigned int i, p, banks; unsigned long reg; chained_irq_enter(ic, desc); - for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) { - const struct aspeed_sgpio_bank *bank = &aspeed_sgpio_banks[i]; - - reg = ioread32(bank_reg(data, bank, reg_irq_status)); + banks = DIV_ROUND_UP(gc->ngpio, 64); + for (i = 0; i < banks; i++) { + reg = data->pdata->llops->reg_bank_get(data, i << 6, reg_irq_status); for_each_set_bit(p, ®, 32) generic_handle_domain_irq(gc->irq.domain, (i * 32 + p) * 2); @@ -399,12 +386,8 @@ static void aspeed_sgpio_irq_handler(struct irq_desc *desc) static void aspeed_sgpio_irq_print_chip(struct irq_data *d, struct seq_file *p) { - const struct aspeed_sgpio_bank *bank; - struct aspeed_sgpio *gpio; - u32 bit; - int offset; + struct aspeed_sgpio *gpio = irq_data_get_irq_chip_data(d); - irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset); seq_puts(p, dev_name(gpio->dev)); } @@ -422,7 +405,6 @@ static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio, struct platform_device *pdev) { int rc, i; - const struct aspeed_sgpio_bank *bank; struct gpio_irq_chip *irq; rc = platform_get_irq(pdev, 0); @@ -432,12 +414,11 @@ static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio, gpio->irq = rc; /* Disable IRQ and clear Interrupt status registers for all SGPIO Pins. */ - for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) { - bank = &aspeed_sgpio_banks[i]; + for (i = 0; i < gpio->chip.ngpio; i += 2) { /* disable irq enable bits */ - iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_enable)); + gpio->pdata->llops->reg_bit_set(gpio, i, reg_irq_enable, 0); /* clear status bits */ - iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_status)); + gpio->pdata->llops->reg_bit_set(gpio, i, reg_irq_status, 1); } irq = &gpio->chip.irq; @@ -451,42 +432,91 @@ static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio, irq->num_parents = 1; /* Apply default IRQ settings */ - for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) { - bank = &aspeed_sgpio_banks[i]; + for (i = 0; i < gpio->chip.ngpio; i += 2) { /* set falling or level-low irq */ - iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type0)); + gpio->pdata->llops->reg_bit_set(gpio, i, reg_irq_type0, 0); /* trigger type is edge */ - iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type1)); + gpio->pdata->llops->reg_bit_set(gpio, i, reg_irq_type1, 0); /* single edge trigger */ - iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type2)); + gpio->pdata->llops->reg_bit_set(gpio, i, reg_irq_type2, 0); } return 0; } +static void aspeed_sgpio_g4_reg_bit_set(struct aspeed_sgpio *gpio, unsigned int offset, + const enum aspeed_sgpio_reg reg, bool val) +{ + const struct aspeed_sgpio_bank *bank = to_bank(offset); + void __iomem *addr = aspeed_sgpio_g4_bank_reg(gpio, bank, reg); + u32 temp; + + if (reg == reg_val) { + /* Since this is an output, read the cached value from rdata, then update val. */ + addr = aspeed_sgpio_g4_bank_reg(gpio, bank, reg_rdata); + temp = ioread32(addr); + if (val) + temp |= GPIO_BIT(offset); + else + temp &= ~GPIO_BIT(offset); + + addr = aspeed_sgpio_g4_bank_reg(gpio, bank, reg_val); + iowrite32(temp, addr); + } else if (reg == reg_irq_status) { + if (val) + iowrite32(GPIO_BIT(offset), addr); + } else { + /* When setting other registers, we read from the register itself */ + temp = ioread32(addr); + if (val) + temp |= GPIO_BIT(offset); + else + temp &= ~GPIO_BIT(offset); + iowrite32(temp, addr); + } +} + +static bool aspeed_sgpio_g4_reg_bit_get(struct aspeed_sgpio *gpio, unsigned int offset, + const enum aspeed_sgpio_reg reg) +{ + const struct aspeed_sgpio_bank *bank = to_bank(offset); + void __iomem *addr = aspeed_sgpio_g4_bank_reg(gpio, bank, reg); + + return !!(ioread32(addr) & GPIO_BIT(offset)); +} + +static int aspeed_sgpio_g4_reg_bank_get(struct aspeed_sgpio *gpio, unsigned int offset, + const enum aspeed_sgpio_reg reg) +{ + const struct aspeed_sgpio_bank *bank = to_bank(offset); + void __iomem *addr = aspeed_sgpio_g4_bank_reg(gpio, bank, reg); + + if (reg == reg_irq_status) + return ioread32(addr); + else + return -EOPNOTSUPP; +} + +static const struct aspeed_sgpio_llops aspeed_sgpio_g4_llops = { + .reg_bit_set = aspeed_sgpio_g4_reg_bit_set, + .reg_bit_get = aspeed_sgpio_g4_reg_bit_get, + .reg_bank_get = aspeed_sgpio_g4_reg_bank_get, +}; + static const struct aspeed_sgpio_pdata ast2400_sgpio_pdata = { .pin_mask = GENMASK(9, 6), + .llops = &aspeed_sgpio_g4_llops, + .cfg_offset = ASPEED_SGPIO_G4_CFG_OFFSET, }; static int aspeed_sgpio_reset_tolerance(struct gpio_chip *chip, unsigned int offset, bool enable) { struct aspeed_sgpio *gpio = gpiochip_get_data(chip); - void __iomem *reg; - u32 val; - - reg = bank_reg(gpio, to_bank(offset), reg_tolerance); guard(raw_spinlock_irqsave)(&gpio->lock); - val = readl(reg); - - if (enable) - val |= GPIO_BIT(offset); - else - val &= ~GPIO_BIT(offset); - - writel(val, reg); + gpio->pdata->llops->reg_bit_set(gpio, offset, reg_tolerance, enable); return 0; } @@ -505,21 +535,77 @@ static int aspeed_sgpio_set_config(struct gpio_chip *chip, unsigned int offset, static const struct aspeed_sgpio_pdata ast2600_sgpiom_pdata = { .pin_mask = GENMASK(10, 6), + .llops = &aspeed_sgpio_g4_llops, + .cfg_offset = ASPEED_SGPIO_G4_CFG_OFFSET, +}; + +static void aspeed_sgpio_g7_reg_bit_set(struct aspeed_sgpio *gpio, unsigned int offset, + const enum aspeed_sgpio_reg reg, bool val) +{ + u32 mask = aspeed_sgpio_g7_reg_mask(reg); + void __iomem *addr = gpio->base + SGPIO_G7_CTRL_REG_OFFSET(offset >> 1); + u32 write_val; + + if (mask) { + write_val = (ioread32(addr) & ~(mask)) | field_prep(mask, val); + iowrite32(write_val, addr); + } +} + +static bool aspeed_sgpio_g7_reg_bit_get(struct aspeed_sgpio *gpio, unsigned int offset, + const enum aspeed_sgpio_reg reg) +{ + u32 mask = aspeed_sgpio_g7_reg_mask(reg); + void __iomem *addr; + + addr = gpio->base + SGPIO_G7_CTRL_REG_OFFSET(offset >> 1); + if (reg == reg_val) + mask = SGPIO_G7_IN_DATA; + + if (mask) + return field_get(mask, ioread32(addr)); + else + return 0; +} + +static int aspeed_sgpio_g7_reg_bank_get(struct aspeed_sgpio *gpio, unsigned int offset, + const enum aspeed_sgpio_reg reg) +{ + void __iomem *addr; + + if (reg == reg_irq_status) { + addr = gpio->base + SGPIO_G7_IRQ_STS_OFFSET(offset >> 6); + return ioread32(addr); + } else { + return -EOPNOTSUPP; + } +} + +static const struct aspeed_sgpio_llops aspeed_sgpio_g7_llops = { + .reg_bit_set = aspeed_sgpio_g7_reg_bit_set, + .reg_bit_get = aspeed_sgpio_g7_reg_bit_get, + .reg_bank_get = aspeed_sgpio_g7_reg_bank_get, +}; + +static const struct aspeed_sgpio_pdata ast2700_sgpiom_pdata = { + .pin_mask = GENMASK(11, 6), + .llops = &aspeed_sgpio_g7_llops, + .cfg_offset = ASPEED_SGPIO_G7_CFG_OFFSET, }; static const struct of_device_id aspeed_sgpio_of_table[] = { { .compatible = "aspeed,ast2400-sgpio", .data = &ast2400_sgpio_pdata, }, { .compatible = "aspeed,ast2500-sgpio", .data = &ast2400_sgpio_pdata, }, { .compatible = "aspeed,ast2600-sgpiom", .data = &ast2600_sgpiom_pdata, }, + { .compatible = "aspeed,ast2700-sgpiom", .data = &ast2700_sgpiom_pdata, }, {} }; MODULE_DEVICE_TABLE(of, aspeed_sgpio_of_table); -static int __init aspeed_sgpio_probe(struct platform_device *pdev) +static int aspeed_sgpio_probe(struct platform_device *pdev) { u32 nr_gpios, sgpio_freq, sgpio_clk_div, gpio_cnt_regval, pin_mask; - const struct aspeed_sgpio_pdata *pdata; struct aspeed_sgpio *gpio; unsigned long apb_freq; int rc; @@ -534,12 +620,11 @@ static int __init aspeed_sgpio_probe(struct platform_device *pdev) gpio->dev = &pdev->dev; - pdata = device_get_match_data(&pdev->dev); - if (!pdata) + gpio->pdata = device_get_match_data(&pdev->dev); + if (!gpio->pdata) return -EINVAL; - pin_mask = pdata->pin_mask; - + pin_mask = gpio->pdata->pin_mask; rc = device_property_read_u32(&pdev->dev, "ngpios", &nr_gpios); if (rc < 0) { dev_err(&pdev->dev, "Could not read ngpios property\n"); @@ -583,7 +668,7 @@ static int __init aspeed_sgpio_probe(struct platform_device *pdev) gpio_cnt_regval = ((nr_gpios / 8) << ASPEED_SGPIO_PINS_SHIFT) & pin_mask; iowrite32(FIELD_PREP(ASPEED_SGPIO_CLK_DIV_MASK, sgpio_clk_div) | gpio_cnt_regval | - ASPEED_SGPIO_ENABLE, gpio->base + ASPEED_SGPIO_CTRL); + ASPEED_SGPIO_ENABLE, gpio->base + gpio->pdata->cfg_offset); raw_spin_lock_init(&gpio->lock); @@ -611,11 +696,12 @@ static int __init aspeed_sgpio_probe(struct platform_device *pdev) } static struct platform_driver aspeed_sgpio_driver = { + .probe = aspeed_sgpio_probe, .driver = { .name = KBUILD_MODNAME, .of_match_table = aspeed_sgpio_of_table, }, }; -module_platform_driver_probe(aspeed_sgpio_driver, aspeed_sgpio_probe); +module_platform_driver(aspeed_sgpio_driver); MODULE_DESCRIPTION("Aspeed Serial GPIO Driver"); diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index cbdf781994dc..9115e56a1626 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -1302,7 +1302,6 @@ MODULE_DEVICE_TABLE(of, aspeed_gpio_of_table); static int aspeed_gpio_probe(struct platform_device *pdev) { - const struct of_device_id *gpio_id; struct gpio_irq_chip *girq; struct aspeed_gpio *gpio; int rc, irq, i, banks, err; @@ -1320,8 +1319,8 @@ static int aspeed_gpio_probe(struct platform_device *pdev) raw_spin_lock_init(&gpio->lock); - gpio_id = of_match_node(aspeed_gpio_of_table, pdev->dev.of_node); - if (!gpio_id) + gpio->config = device_get_match_data(&pdev->dev); + if (!gpio->config) return -EINVAL; gpio->clk = devm_clk_get_enabled(&pdev->dev, NULL); @@ -1331,8 +1330,6 @@ static int aspeed_gpio_probe(struct platform_device *pdev) gpio->clk = NULL; } - gpio->config = gpio_id->data; - if (!gpio->config->llops->reg_bit_set || !gpio->config->llops->reg_bit_get || !gpio->config->llops->reg_bank_get) return -EINVAL; diff --git a/drivers/gpio/gpio-bd72720.c b/drivers/gpio/gpio-bd72720.c new file mode 100644 index 000000000000..6549dbf4c7ad --- /dev/null +++ b/drivers/gpio/gpio-bd72720.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Support to GPIOs on ROHM BD72720 and BD79300 + * Copyright 2025 ROHM Semiconductors. + * Author: Matti Vaittinen <mazziesaccount@gmail.com> + */ + +#include <linux/gpio/driver.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/mfd/rohm-bd72720.h> + +#define BD72720_GPIO_OPEN_DRAIN 0 +#define BD72720_GPIO_CMOS BIT(1) +#define BD72720_INT_GPIO1_IN_SRC 4 +/* + * The BD72720 has several "one time programmable" (OTP) configurations which + * can be set at manufacturing phase. A set of these options allow using pins + * as GPIO. The OTP configuration can't be read at run-time, so drivers rely on + * device-tree to advertise the correct options. + * + * Both DVS[0,1] pins can be configured to be used for: + * - OTP0: regulator RUN state control + * - OTP1: GPI + * - OTP2: GPO + * - OTP3: Power sequencer output + * Data-sheet also states that these PINs can always be used for IRQ but the + * driver limits this by allowing them to be used for IRQs with OTP1 only. + * + * Pins GPIO_EXTEN0 (GPIO3), GPIO_EXTEN1 (GPIO4), GPIO_FAULT_B (GPIO5) have OTP + * options for a specific (non GPIO) purposes, but also an option to configure + * them to be used as a GPO. + * + * OTP settings can be separately configured for each pin. + * + * DT properties: + * "rohm,pin-dvs0" and "rohm,pin-dvs1" can be set to one of the values: + * "dvs-input", "gpi", "gpo". + * + * "rohm,pin-exten0", "rohm,pin-exten1" and "rohm,pin-fault_b" can be set to: + * "gpo" + */ + +enum bd72720_gpio_state { + BD72720_PIN_UNKNOWN, + BD72720_PIN_GPI, + BD72720_PIN_GPO, +}; + +enum { + BD72720_GPIO1, + BD72720_GPIO2, + BD72720_GPIO3, + BD72720_GPIO4, + BD72720_GPIO5, + BD72720_GPIO_EPDEN, + BD72720_NUM_GPIOS +}; + +struct bd72720_gpio { + /* chip.parent points the MFD which provides DT node and regmap */ + struct gpio_chip chip; + /* dev points to the platform device for devm and prints */ + struct device *dev; + struct regmap *regmap; + int gpio_is_input; +}; + +static int bd72720gpi_get(struct bd72720_gpio *bdgpio, unsigned int reg_offset) +{ + int ret, val, shift; + + ret = regmap_read(bdgpio->regmap, BD72720_REG_INT_ETC1_SRC, &val); + if (ret) + return ret; + + shift = BD72720_INT_GPIO1_IN_SRC + reg_offset; + + return (val >> shift) & 1; +} + +static int bd72720gpo_get(struct bd72720_gpio *bdgpio, + unsigned int offset) +{ + const int regs[] = { BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL, + BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL, + BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL }; + int ret, val; + + ret = regmap_read(bdgpio->regmap, regs[offset], &val); + if (ret) + return ret; + + return val & BD72720_GPIO_HIGH; +} + +static int bd72720gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct bd72720_gpio *bdgpio = gpiochip_get_data(chip); + + if (BIT(offset) & bdgpio->gpio_is_input) + return bd72720gpi_get(bdgpio, offset); + + return bd72720gpo_get(bdgpio, offset); +} + +static int bd72720gpo_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct bd72720_gpio *bdgpio = gpiochip_get_data(chip); + const int regs[] = { BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL, + BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL, + BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL }; + + if (BIT(offset) & bdgpio->gpio_is_input) { + dev_dbg(bdgpio->dev, "pin %d not output.\n", offset); + return -EINVAL; + } + + if (value) + return regmap_set_bits(bdgpio->regmap, regs[offset], + BD72720_GPIO_HIGH); + + return regmap_clear_bits(bdgpio->regmap, regs[offset], + BD72720_GPIO_HIGH); +} + +static int bd72720_gpio_set_config(struct gpio_chip *chip, unsigned int offset, + unsigned long config) +{ + struct bd72720_gpio *bdgpio = gpiochip_get_data(chip); + const int regs[] = { BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL, + BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL, + BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL }; + + /* + * We can only set the output mode, which makes sense only when output + * OTP configuration is used. + */ + if (BIT(offset) & bdgpio->gpio_is_input) + return -ENOTSUPP; + + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + return regmap_update_bits(bdgpio->regmap, + regs[offset], + BD72720_GPIO_DRIVE_MASK, + BD72720_GPIO_OPEN_DRAIN); + case PIN_CONFIG_DRIVE_PUSH_PULL: + return regmap_update_bits(bdgpio->regmap, + regs[offset], + BD72720_GPIO_DRIVE_MASK, + BD72720_GPIO_CMOS); + default: + break; + } + + return -ENOTSUPP; +} + +static int bd72720gpo_direction_get(struct gpio_chip *chip, + unsigned int offset) +{ + struct bd72720_gpio *bdgpio = gpiochip_get_data(chip); + + if (BIT(offset) & bdgpio->gpio_is_input) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; +} + +static int bd72720_valid_mask(struct gpio_chip *gc, + unsigned long *valid_mask, + unsigned int ngpios) +{ + static const char * const properties[] = { + "rohm,pin-dvs0", "rohm,pin-dvs1", "rohm,pin-exten0", + "rohm,pin-exten1", "rohm,pin-fault_b" + }; + struct bd72720_gpio *g = gpiochip_get_data(gc); + const char *val; + int i, ret; + + *valid_mask = BIT(BD72720_GPIO_EPDEN); + + if (!gc->parent) + return 0; + + for (i = 0; i < ARRAY_SIZE(properties); i++) { + ret = fwnode_property_read_string(dev_fwnode(gc->parent), + properties[i], &val); + + if (ret) { + if (ret == -EINVAL) + continue; + + dev_err(g->dev, "pin %d (%s), bad configuration\n", i, + properties[i]); + + return ret; + } + + if (strcmp(val, "gpi") == 0) { + if (i != BD72720_GPIO1 && i != BD72720_GPIO2) { + dev_warn(g->dev, + "pin %d (%s) does not support INPUT mode", + i, properties[i]); + continue; + } + + *valid_mask |= BIT(i); + g->gpio_is_input |= BIT(i); + } else if (strcmp(val, "gpo") == 0) { + *valid_mask |= BIT(i); + } + } + + return 0; +} + +/* Template for GPIO chip */ +static const struct gpio_chip bd72720gpo_chip = { + .label = "bd72720", + .owner = THIS_MODULE, + .get = bd72720gpio_get, + .get_direction = bd72720gpo_direction_get, + .set = bd72720gpo_set, + .set_config = bd72720_gpio_set_config, + .init_valid_mask = bd72720_valid_mask, + .can_sleep = true, + .ngpio = BD72720_NUM_GPIOS, + .base = -1, +}; + +static int gpo_bd72720_probe(struct platform_device *pdev) +{ + struct bd72720_gpio *g; + struct device *parent, *dev; + + /* + * Bind devm lifetime to this platform device => use dev for devm. + * also the prints should originate from this device. + */ + dev = &pdev->dev; + /* The device-tree and regmap come from MFD => use parent for that */ + parent = dev->parent; + + g = devm_kzalloc(dev, sizeof(*g), GFP_KERNEL); + if (!g) + return -ENOMEM; + + g->chip = bd72720gpo_chip; + g->dev = dev; + g->chip.parent = parent; + g->regmap = dev_get_regmap(parent, NULL); + + return devm_gpiochip_add_data(dev, &g->chip, g); +} + +static const struct platform_device_id bd72720_gpio_id[] = { + { "bd72720-gpio" }, + { }, +}; +MODULE_DEVICE_TABLE(platform, bd72720_gpio_id); + +static struct platform_driver gpo_bd72720_driver = { + .driver = { + .name = "bd72720-gpio", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .probe = gpo_bd72720_probe, + .id_table = bd72720_gpio_id, +}; +module_platform_driver(gpo_bd72720_driver); + +MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); +MODULE_DESCRIPTION("GPIO interface for BD72720 and BD73900"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-cadence.c b/drivers/gpio/gpio-cadence.c index b75734ca22dd..d7790fc35c22 100644 --- a/drivers/gpio/gpio-cadence.c +++ b/drivers/gpio/gpio-cadence.c @@ -2,6 +2,7 @@ /* * Copyright 2017-2018 Cadence + * Copyright (C) 2025 Axiado Corporation. * * Authors: * Jan Kotas <jank@cadence.com> @@ -31,10 +32,23 @@ #define CDNS_GPIO_IRQ_VALUE 0x28 #define CDNS_GPIO_IRQ_ANY_EDGE 0x2c +struct cdns_gpio_quirks { + bool skip_init; +}; + struct cdns_gpio_chip { struct gpio_generic_chip gen_gc; void __iomem *regs; u32 bypass_orig; + const struct cdns_gpio_quirks *quirks; +}; + +static const struct cdns_gpio_quirks cdns_default_quirks = { + .skip_init = false, +}; + +static const struct cdns_gpio_quirks ax3000_gpio_quirks = { + .skip_init = true, }; static int cdns_gpio_request(struct gpio_chip *chip, unsigned int offset) @@ -84,6 +98,7 @@ static int cdns_gpio_irq_set_type(struct irq_data *d, unsigned int type) struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip); u32 int_value; u32 int_type; + u32 int_any; u32 mask = BIT(d->hwirq); int ret = 0; @@ -91,24 +106,35 @@ static int cdns_gpio_irq_set_type(struct irq_data *d, unsigned int type) int_value = ioread32(cgpio->regs + CDNS_GPIO_IRQ_VALUE) & ~mask; int_type = ioread32(cgpio->regs + CDNS_GPIO_IRQ_TYPE) & ~mask; - /* - * The GPIO controller doesn't have an ACK register. - * All interrupt statuses are cleared on a status register read. - * Don't support edge interrupts for now. + * Interrupt polarity and trigger behaviour is configured like this: + * + * (type, value) + * (0, 0) = Falling edge triggered + * (0, 1) = Rising edge triggered + * (1, 0) = Low level triggered + * (1, 1) = High level triggered */ + int_any = ioread32(cgpio->regs + CDNS_GPIO_IRQ_ANY_EDGE) & ~mask; if (type == IRQ_TYPE_LEVEL_HIGH) { int_type |= mask; int_value |= mask; } else if (type == IRQ_TYPE_LEVEL_LOW) { int_type |= mask; + } else if (type == IRQ_TYPE_EDGE_RISING) { + int_value |= mask; + } else if (type == IRQ_TYPE_EDGE_FALLING) { + /* edge trigger, int_value remains cleared for falling */ + } else if (type == IRQ_TYPE_EDGE_BOTH) { + int_any |= mask; } else { return -EINVAL; } iowrite32(int_value, cgpio->regs + CDNS_GPIO_IRQ_VALUE); iowrite32(int_type, cgpio->regs + CDNS_GPIO_IRQ_TYPE); + iowrite32(int_any, cgpio->regs + CDNS_GPIO_IRQ_ANY_EDGE); return ret; } @@ -141,6 +167,19 @@ static const struct irq_chip cdns_gpio_irqchip = { GPIOCHIP_IRQ_RESOURCE_HELPERS, }; +static const struct of_device_id cdns_of_ids[] = { + { + .compatible = "axiado,ax3000-gpio", + .data = &ax3000_gpio_quirks + }, + { + .compatible = "cdns,gpio-r1p02", + .data = &cdns_default_quirks + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, cdns_of_ids); + static int cdns_gpio_probe(struct platform_device *pdev) { struct gpio_generic_chip_config config = { }; @@ -165,6 +204,10 @@ static int cdns_gpio_probe(struct platform_device *pdev) return -EINVAL; } + cgpio->quirks = device_get_match_data(&pdev->dev); + if (!cgpio->quirks) + cgpio->quirks = &cdns_default_quirks; + /* * Set all pins as inputs by default, otherwise: * gpiochip_lock_as_irq: @@ -173,8 +216,15 @@ static int cdns_gpio_probe(struct platform_device *pdev) * so it needs to be changed before gpio_generic_chip_init() is called. */ dir_prev = ioread32(cgpio->regs + CDNS_GPIO_DIRECTION_MODE); - iowrite32(GENMASK(num_gpios - 1, 0), - cgpio->regs + CDNS_GPIO_DIRECTION_MODE); + + /* + * The AX3000 platform performs the required configuration at boot time + * before Linux boots, so this quirk disables pinmux initialization. + */ + if (!cgpio->quirks->skip_init) { + iowrite32(GENMASK(num_gpios - 1, 0), + cgpio->regs + CDNS_GPIO_DIRECTION_MODE); + } config.dev = &pdev->dev; config.sz = 4; @@ -240,9 +290,11 @@ static int cdns_gpio_probe(struct platform_device *pdev) /* * Enable gpio outputs, ignored for input direction */ - iowrite32(GENMASK(num_gpios - 1, 0), - cgpio->regs + CDNS_GPIO_OUTPUT_EN); - iowrite32(0, cgpio->regs + CDNS_GPIO_BYPASS_MODE); + if (!cgpio->quirks->skip_init) { + iowrite32(GENMASK(num_gpios - 1, 0), + cgpio->regs + CDNS_GPIO_OUTPUT_EN); + iowrite32(0, cgpio->regs + CDNS_GPIO_BYPASS_MODE); + } platform_set_drvdata(pdev, cgpio); return 0; @@ -260,12 +312,6 @@ static void cdns_gpio_remove(struct platform_device *pdev) iowrite32(cgpio->bypass_orig, cgpio->regs + CDNS_GPIO_BYPASS_MODE); } -static const struct of_device_id cdns_of_ids[] = { - { .compatible = "cdns,gpio-r1p02" }, - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(of, cdns_of_ids); - static struct platform_driver cdns_gpio_driver = { .driver = { .name = "cdns-gpio", diff --git a/drivers/gpio/gpio-creg-snps.c b/drivers/gpio/gpio-creg-snps.c index f8ea961fa1de..157ab90f5ba8 100644 --- a/drivers/gpio/gpio-creg-snps.c +++ b/drivers/gpio/gpio-creg-snps.c @@ -134,7 +134,6 @@ static const struct of_device_id creg_gpio_ids[] = { static int creg_gpio_probe(struct platform_device *pdev) { - const struct of_device_id *match; struct device *dev = &pdev->dev; struct creg_gpio *hcg; u32 ngpios; @@ -148,8 +147,7 @@ static int creg_gpio_probe(struct platform_device *pdev) if (IS_ERR(hcg->regs)) return PTR_ERR(hcg->regs); - match = of_match_node(creg_gpio_ids, pdev->dev.of_node); - hcg->layout = match->data; + hcg->layout = device_get_match_data(dev); if (!hcg->layout) return -EINVAL; diff --git a/drivers/gpio/gpio-line-mux.c b/drivers/gpio/gpio-line-mux.c new file mode 100644 index 000000000000..62548fbd3ca0 --- /dev/null +++ b/drivers/gpio/gpio-line-mux.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * GPIO line mux which acts as virtual gpiochip and provides a 1-to-many + * mapping between virtual GPIOs and a real GPIO + multiplexer. + * + * Copyright (c) 2025 Jonas Jelonek <jelonek.jonas@gmail.com> + */ + +#include <linux/gpio/consumer.h> +#include <linux/gpio/driver.h> +#include <linux/mod_devicetable.h> +#include <linux/mutex.h> +#include <linux/mux/consumer.h> +#include <linux/platform_device.h> + +#define MUX_SELECT_DELAY_US 100 + +struct gpio_lmux { + struct gpio_chip gc; + struct mux_control *mux; + struct gpio_desc *muxed_gpio; + + u32 num_gpio_mux_states; + unsigned int gpio_mux_states[] __counted_by(num_gpio_mux_states); +}; + +static int gpio_lmux_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct gpio_lmux *glm = gpiochip_get_data(gc); + int ret; + + ret = mux_control_select_delay(glm->mux, glm->gpio_mux_states[offset], + MUX_SELECT_DELAY_US); + if (ret < 0) + return ret; + + ret = gpiod_get_raw_value_cansleep(glm->muxed_gpio); + mux_control_deselect(glm->mux); + return ret; +} + +static int gpio_lmux_gpio_get_direction(struct gpio_chip *gc, + unsigned int offset) +{ + return GPIO_LINE_DIRECTION_IN; +} + +static int gpio_lmux_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct gpio_lmux *glm; + unsigned int ngpio; + size_t size; + int ret; + + ngpio = device_property_count_u32(dev, "gpio-line-mux-states"); + if (!ngpio) + return -EINVAL; + + size = struct_size(glm, gpio_mux_states, ngpio); + glm = devm_kzalloc(dev, size, GFP_KERNEL); + if (!glm) + return -ENOMEM; + + glm->gc.base = -1; + glm->gc.can_sleep = true; + glm->gc.fwnode = dev_fwnode(dev); + glm->gc.label = dev_name(dev); + glm->gc.ngpio = ngpio; + glm->gc.owner = THIS_MODULE; + glm->gc.parent = dev; + + glm->gc.get = gpio_lmux_gpio_get; + glm->gc.get_direction = gpio_lmux_gpio_get_direction; + + glm->mux = devm_mux_control_get(dev, NULL); + if (IS_ERR(glm->mux)) + return dev_err_probe(dev, PTR_ERR(glm->mux), + "could not get mux controller\n"); + + glm->muxed_gpio = devm_gpiod_get(dev, "muxed", GPIOD_IN); + if (IS_ERR(glm->muxed_gpio)) + return dev_err_probe(dev, PTR_ERR(glm->muxed_gpio), + "could not get muxed-gpio\n"); + + glm->num_gpio_mux_states = ngpio; + ret = device_property_read_u32_array(dev, "gpio-line-mux-states", + &glm->gpio_mux_states[0], ngpio); + if (ret) + return dev_err_probe(dev, ret, "could not get mux states\n"); + + ret = devm_gpiochip_add_data(dev, &glm->gc, glm); + if (ret) + return dev_err_probe(dev, ret, "failed to add gpiochip\n"); + + return 0; +} + +static const struct of_device_id gpio_lmux_of_match[] = { + { .compatible = "gpio-line-mux" }, + { } +}; +MODULE_DEVICE_TABLE(of, gpio_lmux_of_match); + +static struct platform_driver gpio_lmux_driver = { + .driver = { + .name = "gpio-line-mux", + .of_match_table = gpio_lmux_of_match, + }, + .probe = gpio_lmux_probe, +}; +module_platform_driver(gpio_lmux_driver); + +MODULE_AUTHOR("Jonas Jelonek <jelonek.jonas@gmail.com>"); +MODULE_DESCRIPTION("GPIO line mux driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index 02eca400b307..e6c85411c695 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -132,6 +132,24 @@ static const struct irq_chip max77620_gpio_irqchip = { GPIOCHIP_IRQ_RESOURCE_HELPERS, }; +static int max77620_gpio_get_dir(struct gpio_chip *gc, unsigned int offset) +{ + struct max77620_gpio *mgpio = gpiochip_get_data(gc); + unsigned int val; + int ret; + + ret = regmap_read(mgpio->rmap, GPIO_REG_ADDR(offset), &val); + if (ret < 0) { + dev_err(mgpio->dev, "CNFG_GPIOx read failed: %d\n", ret); + return ret; + } + + if (val & MAX77620_CNFG_GPIO_DIR_MASK) + return GPIO_LINE_DIRECTION_IN; + else + return GPIO_LINE_DIRECTION_OUT; +} + static int max77620_gpio_dir_input(struct gpio_chip *gc, unsigned int offset) { struct max77620_gpio *mgpio = gpiochip_get_data(gc); @@ -308,6 +326,7 @@ static int max77620_gpio_probe(struct platform_device *pdev) mgpio->gpio_chip.label = pdev->name; mgpio->gpio_chip.parent = pdev->dev.parent; + mgpio->gpio_chip.get_direction = max77620_gpio_get_dir; mgpio->gpio_chip.direction_input = max77620_gpio_dir_input; mgpio->gpio_chip.get = max77620_gpio_get; mgpio->gpio_chip.direction_output = max77620_gpio_dir_output; diff --git a/drivers/gpio/gpio-max77759.c b/drivers/gpio/gpio-max77759.c index 5e48eb03e7b3..3bf9f23d1532 100644 --- a/drivers/gpio/gpio-max77759.c +++ b/drivers/gpio/gpio-max77759.c @@ -435,8 +435,6 @@ static int max77759_gpio_probe(struct platform_device *pdev) int irq; struct gpio_irq_chip *girq; int ret; - unsigned long irq_flags; - struct irq_data *irqd; chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); if (!chip) @@ -486,13 +484,9 @@ static int max77759_gpio_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, ret, "Failed to add GPIO chip\n"); - irq_flags = IRQF_ONESHOT | IRQF_SHARED; - irqd = irq_get_irq_data(irq); - if (irqd) - irq_flags |= irqd_get_trigger_type(irqd); - ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, - max77759_gpio_irqhandler, irq_flags, + max77759_gpio_irqhandler, + IRQF_ONESHOT | IRQF_SHARED, dev_name(&pdev->dev), chip); if (ret < 0) return dev_err_probe(&pdev->dev, ret, diff --git a/drivers/gpio/gpio-menz127.c b/drivers/gpio/gpio-menz127.c index 52b13c6ae496..f51e7517f551 100644 --- a/drivers/gpio/gpio-menz127.c +++ b/drivers/gpio/gpio-menz127.c @@ -223,5 +223,4 @@ module_mcb_driver(men_z127_driver); MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>"); MODULE_DESCRIPTION("MEN GPIO Controller"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("mcb:16z127"); MODULE_IMPORT_NS("MCB"); diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index 5daf962b0323..edbcaad57d00 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -724,6 +724,7 @@ static const struct of_device_id gpio_mmio_of_match[] = { { .compatible = "wd,mbl-gpio" }, { .compatible = "ni,169445-nand-gpio" }, { .compatible = "intel,ixp4xx-expansion-bus-mmio-gpio" }, + { .compatible = "opencores,gpio" }, { } }; MODULE_DEVICE_TABLE(of, gpio_mmio_of_match); diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index f93a3dbb2daa..52e96cc5f67b 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -126,6 +126,9 @@ static const struct i2c_device_id pca953x_id[] = { { "tca9539", 16 | PCA953X_TYPE | PCA_INT, }, { "tca9554", 8 | PCA953X_TYPE | PCA_INT, }, { "xra1202", 8 | PCA953X_TYPE }, + + { "tcal6408", 8 | PCA953X_TYPE | PCA_LATCH_INT, }, + { "tcal6416", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, { } }; MODULE_DEVICE_TABLE(i2c, pca953x_id); @@ -1469,6 +1472,9 @@ static const struct of_device_id pca953x_dt_ids[] = { { .compatible = "ti,tca9538", .data = OF_953X( 8, PCA_INT), }, { .compatible = "ti,tca9539", .data = OF_953X(16, PCA_INT), }, + { .compatible = "ti,tcal6408", .data = OF_953X( 8, PCA_LATCH_INT), }, + { .compatible = "ti,tcal6416", .data = OF_953X(16, PCA_LATCH_INT), }, + { .compatible = "onnn,cat9554", .data = OF_953X( 8, PCA_INT), }, { .compatible = "onnn,pca9654", .data = OF_953X( 8, PCA_INT), }, { .compatible = "onnn,pca9655", .data = OF_953X(16, PCA_INT), }, diff --git a/drivers/gpio/gpio-pca9570.c b/drivers/gpio/gpio-pca9570.c index c5a1287079a0..4a368803fb03 100644 --- a/drivers/gpio/gpio-pca9570.c +++ b/drivers/gpio/gpio-pca9570.c @@ -9,11 +9,16 @@ * Andrew F. Davis <afd@ti.com> */ +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/device/devres.h> +#include <linux/errno.h> #include <linux/gpio/driver.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/property.h> +#include <linux/types.h> #define SLG7XL45106_GPO_REG 0xDB @@ -94,7 +99,7 @@ static int pca9570_set(struct gpio_chip *chip, unsigned int offset, int value) u8 buffer; int ret; - mutex_lock(&gpio->lock); + guard(mutex)(&gpio->lock); buffer = gpio->out; if (value) @@ -104,18 +109,18 @@ static int pca9570_set(struct gpio_chip *chip, unsigned int offset, int value) ret = pca9570_write(gpio, buffer); if (ret) - goto out; + return ret; gpio->out = buffer; -out: - mutex_unlock(&gpio->lock); - return ret; + return 0; } static int pca9570_probe(struct i2c_client *client) { + struct device *dev = &client->dev; struct pca9570 *gpio; + int ret; gpio = devm_kzalloc(&client->dev, sizeof(*gpio), GFP_KERNEL); if (!gpio) @@ -132,7 +137,9 @@ static int pca9570_probe(struct i2c_client *client) gpio->chip.ngpio = gpio->chip_data->ngpio; gpio->chip.can_sleep = true; - mutex_init(&gpio->lock); + ret = devm_mutex_init(dev, &gpio->lock); + if (ret) + return ret; /* Read the current output level */ pca9570_read(gpio, &gpio->out); diff --git a/drivers/gpio/gpio-realtek-otto.c b/drivers/gpio/gpio-realtek-otto.c index de527f4fc6c2..4cf91528f547 100644 --- a/drivers/gpio/gpio-realtek-otto.c +++ b/drivers/gpio/gpio-realtek-otto.c @@ -359,8 +359,7 @@ static int realtek_gpio_probe(struct platform_device *pdev) { struct gpio_generic_chip_config config; struct device *dev = &pdev->dev; - unsigned long gen_gc_flags; - unsigned int dev_flags; + unsigned long gen_gc_flags, dev_flags; struct gpio_irq_chip *girq; struct realtek_gpio_ctrl *ctrl; struct resource *res; @@ -372,7 +371,7 @@ static int realtek_gpio_probe(struct platform_device *pdev) if (!ctrl) return -ENOMEM; - dev_flags = (unsigned int) device_get_match_data(dev); + dev_flags = (uintptr_t)device_get_match_data(dev); ngpios = REALTEK_GPIO_MAX; device_property_read_u32(dev, "ngpios", &ngpios); diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index a83f5238427c..437b4500f56b 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -1384,7 +1384,7 @@ static void gpio_sim_hog_config_item_release(struct config_item *item) kfree(hog); } -static struct configfs_item_operations gpio_sim_hog_config_item_ops = { +static const struct configfs_item_operations gpio_sim_hog_config_item_ops = { .release = gpio_sim_hog_config_item_release, }; @@ -1433,11 +1433,11 @@ static void gpio_sim_line_config_group_release(struct config_item *item) kfree(line); } -static struct configfs_item_operations gpio_sim_line_config_item_ops = { +static const struct configfs_item_operations gpio_sim_line_config_item_ops = { .release = gpio_sim_line_config_group_release, }; -static struct configfs_group_operations gpio_sim_line_config_group_ops = { +static const struct configfs_group_operations gpio_sim_line_config_group_ops = { .make_item = gpio_sim_line_config_make_hog_item, }; @@ -1494,11 +1494,11 @@ static void gpio_sim_bank_config_group_release(struct config_item *item) kfree(bank); } -static struct configfs_item_operations gpio_sim_bank_config_item_ops = { +static const struct configfs_item_operations gpio_sim_bank_config_item_ops = { .release = gpio_sim_bank_config_group_release, }; -static struct configfs_group_operations gpio_sim_bank_config_group_ops = { +static const struct configfs_group_operations gpio_sim_bank_config_group_ops = { .make_group = gpio_sim_bank_config_make_line_group, }; @@ -1549,11 +1549,11 @@ static void gpio_sim_device_config_group_release(struct config_item *item) kfree(dev); } -static struct configfs_item_operations gpio_sim_device_config_item_ops = { +static const struct configfs_item_operations gpio_sim_device_config_item_ops = { .release = gpio_sim_device_config_group_release, }; -static struct configfs_group_operations gpio_sim_device_config_group_ops = { +static const struct configfs_group_operations gpio_sim_device_config_group_ops = { .make_group = gpio_sim_device_config_make_bank_group, }; @@ -1589,7 +1589,7 @@ gpio_sim_config_make_device_group(struct config_group *group, const char *name) return &no_free_ptr(dev)->group; } -static struct configfs_group_operations gpio_sim_config_group_ops = { +static const struct configfs_group_operations gpio_sim_config_group_ops = { .make_group = gpio_sim_config_make_device_group, }; diff --git a/drivers/gpio/gpio-spacemit-k1.c b/drivers/gpio/gpio-spacemit-k1.c index eb66a15c002f..dbd2e81094b9 100644 --- a/drivers/gpio/gpio-spacemit-k1.c +++ b/drivers/gpio/gpio-spacemit-k1.c @@ -15,29 +15,37 @@ #include <linux/platform_device.h> #include <linux/seq_file.h> -/* register offset */ -#define SPACEMIT_GPLR 0x00 /* port level - R */ -#define SPACEMIT_GPDR 0x0c /* port direction - R/W */ -#define SPACEMIT_GPSR 0x18 /* port set - W */ -#define SPACEMIT_GPCR 0x24 /* port clear - W */ -#define SPACEMIT_GRER 0x30 /* port rising edge R/W */ -#define SPACEMIT_GFER 0x3c /* port falling edge R/W */ -#define SPACEMIT_GEDR 0x48 /* edge detect status - R/W1C */ -#define SPACEMIT_GSDR 0x54 /* (set) direction - W */ -#define SPACEMIT_GCDR 0x60 /* (clear) direction - W */ -#define SPACEMIT_GSRER 0x6c /* (set) rising edge detect enable - W */ -#define SPACEMIT_GCRER 0x78 /* (clear) rising edge detect enable - W */ -#define SPACEMIT_GSFER 0x84 /* (set) falling edge detect enable - W */ -#define SPACEMIT_GCFER 0x90 /* (clear) falling edge detect enable - W */ -#define SPACEMIT_GAPMASK 0x9c /* interrupt mask , 0 disable, 1 enable - R/W */ - #define SPACEMIT_NR_BANKS 4 #define SPACEMIT_NR_GPIOS_PER_BANK 32 #define to_spacemit_gpio_bank(x) container_of((x), struct spacemit_gpio_bank, gc) +#define to_spacemit_gpio_regs(gb) ((gb)->sg->data->offsets) + +enum spacemit_gpio_registers { + SPACEMIT_GPLR, /* port level - R */ + SPACEMIT_GPDR, /* port direction - R/W */ + SPACEMIT_GPSR, /* port set - W */ + SPACEMIT_GPCR, /* port clear - W */ + SPACEMIT_GRER, /* port rising edge R/W */ + SPACEMIT_GFER, /* port falling edge R/W */ + SPACEMIT_GEDR, /* edge detect status - R/W1C */ + SPACEMIT_GSDR, /* (set) direction - W */ + SPACEMIT_GCDR, /* (clear) direction - W */ + SPACEMIT_GSRER, /* (set) rising edge detect enable - W */ + SPACEMIT_GCRER, /* (clear) rising edge detect enable - W */ + SPACEMIT_GSFER, /* (set) falling edge detect enable - W */ + SPACEMIT_GCFER, /* (clear) falling edge detect enable - W */ + SPACEMIT_GAPMASK, /* interrupt mask , 0 disable, 1 enable - R/W */ + SPACEMIT_GCPMASK, /* interrupt mask for K3 */ +}; struct spacemit_gpio; +struct spacemit_gpio_data { + const unsigned int *offsets; + u32 bank_offsets[SPACEMIT_NR_BANKS]; +}; + struct spacemit_gpio_bank { struct gpio_generic_chip chip; struct spacemit_gpio *sg; @@ -49,9 +57,22 @@ struct spacemit_gpio_bank { struct spacemit_gpio { struct device *dev; + const struct spacemit_gpio_data *data; struct spacemit_gpio_bank sgb[SPACEMIT_NR_BANKS]; }; +static u32 spacemit_gpio_read(struct spacemit_gpio_bank *gb, + enum spacemit_gpio_registers reg) +{ + return readl(gb->base + to_spacemit_gpio_regs(gb)[reg]); +} + +static void spacemit_gpio_write(struct spacemit_gpio_bank *gb, + enum spacemit_gpio_registers reg, u32 val) +{ + writel(val, gb->base + to_spacemit_gpio_regs(gb)[reg]); +} + static u32 spacemit_gpio_bank_index(struct spacemit_gpio_bank *gb) { return (u32)(gb - gb->sg->sgb); @@ -63,10 +84,10 @@ static irqreturn_t spacemit_gpio_irq_handler(int irq, void *dev_id) unsigned long pending; u32 n, gedr; - gedr = readl(gb->base + SPACEMIT_GEDR); + gedr = spacemit_gpio_read(gb, SPACEMIT_GEDR); if (!gedr) return IRQ_NONE; - writel(gedr, gb->base + SPACEMIT_GEDR); + spacemit_gpio_write(gb, SPACEMIT_GEDR, gedr); pending = gedr & gb->irq_mask; if (!pending) @@ -82,7 +103,7 @@ static void spacemit_gpio_irq_ack(struct irq_data *d) { struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d); - writel(BIT(irqd_to_hwirq(d)), gb->base + SPACEMIT_GEDR); + spacemit_gpio_write(gb, SPACEMIT_GEDR, BIT(irqd_to_hwirq(d))); } static void spacemit_gpio_irq_mask(struct irq_data *d) @@ -91,13 +112,13 @@ static void spacemit_gpio_irq_mask(struct irq_data *d) u32 bit = BIT(irqd_to_hwirq(d)); gb->irq_mask &= ~bit; - writel(gb->irq_mask, gb->base + SPACEMIT_GAPMASK); + spacemit_gpio_write(gb, SPACEMIT_GAPMASK, gb->irq_mask); if (bit & gb->irq_rising_edge) - writel(bit, gb->base + SPACEMIT_GCRER); + spacemit_gpio_write(gb, SPACEMIT_GCRER, bit); if (bit & gb->irq_falling_edge) - writel(bit, gb->base + SPACEMIT_GCFER); + spacemit_gpio_write(gb, SPACEMIT_GCFER, bit); } static void spacemit_gpio_irq_unmask(struct irq_data *d) @@ -108,12 +129,12 @@ static void spacemit_gpio_irq_unmask(struct irq_data *d) gb->irq_mask |= bit; if (bit & gb->irq_rising_edge) - writel(bit, gb->base + SPACEMIT_GSRER); + spacemit_gpio_write(gb, SPACEMIT_GSRER, bit); if (bit & gb->irq_falling_edge) - writel(bit, gb->base + SPACEMIT_GSFER); + spacemit_gpio_write(gb, SPACEMIT_GSFER, bit); - writel(gb->irq_mask, gb->base + SPACEMIT_GAPMASK); + spacemit_gpio_write(gb, SPACEMIT_GAPMASK, gb->irq_mask); } static int spacemit_gpio_irq_set_type(struct irq_data *d, unsigned int type) @@ -123,18 +144,18 @@ static int spacemit_gpio_irq_set_type(struct irq_data *d, unsigned int type) if (type & IRQ_TYPE_EDGE_RISING) { gb->irq_rising_edge |= bit; - writel(bit, gb->base + SPACEMIT_GSRER); + spacemit_gpio_write(gb, SPACEMIT_GSRER, bit); } else { gb->irq_rising_edge &= ~bit; - writel(bit, gb->base + SPACEMIT_GCRER); + spacemit_gpio_write(gb, SPACEMIT_GCRER, bit); } if (type & IRQ_TYPE_EDGE_FALLING) { gb->irq_falling_edge |= bit; - writel(bit, gb->base + SPACEMIT_GSFER); + spacemit_gpio_write(gb, SPACEMIT_GSFER, bit); } else { gb->irq_falling_edge &= ~bit; - writel(bit, gb->base + SPACEMIT_GCFER); + spacemit_gpio_write(gb, SPACEMIT_GCFER, bit); } return 0; @@ -178,16 +199,16 @@ static int spacemit_gpio_add_bank(struct spacemit_gpio *sg, struct gpio_chip *gc = &gb->chip.gc; struct device *dev = sg->dev; struct gpio_irq_chip *girq; - void __iomem *dat, *set, *clr, *dirin, *dirout; - int ret, bank_base[] = { 0x0, 0x4, 0x8, 0x100 }; + void __iomem *dat, *set, *clr, *dirout; + int ret; - gb->base = regs + bank_base[index]; + gb->base = regs + sg->data->bank_offsets[index]; + gb->sg = sg; - dat = gb->base + SPACEMIT_GPLR; - set = gb->base + SPACEMIT_GPSR; - clr = gb->base + SPACEMIT_GPCR; - dirin = gb->base + SPACEMIT_GCDR; - dirout = gb->base + SPACEMIT_GSDR; + dat = gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GPLR]; + set = gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GPSR]; + clr = gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GPCR]; + dirout = gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GPDR]; config = (struct gpio_generic_chip_config) { .dev = dev, @@ -196,9 +217,7 @@ static int spacemit_gpio_add_bank(struct spacemit_gpio *sg, .set = set, .clr = clr, .dirout = dirout, - .dirin = dirin, - .flags = GPIO_GENERIC_UNREADABLE_REG_SET | - GPIO_GENERIC_UNREADABLE_REG_DIR, + .flags = GPIO_GENERIC_UNREADABLE_REG_SET, }; /* This registers 32 GPIO lines per bank */ @@ -206,8 +225,6 @@ static int spacemit_gpio_add_bank(struct spacemit_gpio *sg, if (ret) return dev_err_probe(dev, ret, "failed to init gpio chip\n"); - gb->sg = sg; - gc->label = dev_name(dev); gc->request = gpiochip_generic_request; gc->free = gpiochip_generic_free; @@ -223,13 +240,13 @@ static int spacemit_gpio_add_bank(struct spacemit_gpio *sg, gpio_irq_chip_set_chip(girq, &spacemit_gpio_chip); /* Disable Interrupt */ - writel(0, gb->base + SPACEMIT_GAPMASK); + spacemit_gpio_write(gb, SPACEMIT_GAPMASK, 0); /* Disable Edge Detection Settings */ - writel(0x0, gb->base + SPACEMIT_GRER); - writel(0x0, gb->base + SPACEMIT_GFER); + spacemit_gpio_write(gb, SPACEMIT_GRER, 0x0); + spacemit_gpio_write(gb, SPACEMIT_GFER, 0x0); /* Clear Interrupt */ - writel(0xffffffff, gb->base + SPACEMIT_GCRER); - writel(0xffffffff, gb->base + SPACEMIT_GCFER); + spacemit_gpio_write(gb, SPACEMIT_GCRER, 0xffffffff); + spacemit_gpio_write(gb, SPACEMIT_GCFER, 0xffffffff); ret = devm_request_threaded_irq(dev, irq, NULL, spacemit_gpio_irq_handler, @@ -260,6 +277,10 @@ static int spacemit_gpio_probe(struct platform_device *pdev) if (!sg) return -ENOMEM; + sg->data = of_device_get_match_data(dev); + if (!sg->data) + return dev_err_probe(dev, -EINVAL, "No available compatible data."); + regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) return PTR_ERR(regs); @@ -287,8 +308,55 @@ static int spacemit_gpio_probe(struct platform_device *pdev) return 0; } +static const unsigned int spacemit_gpio_k1_offsets[] = { + [SPACEMIT_GPLR] = 0x00, + [SPACEMIT_GPDR] = 0x0c, + [SPACEMIT_GPSR] = 0x18, + [SPACEMIT_GPCR] = 0x24, + [SPACEMIT_GRER] = 0x30, + [SPACEMIT_GFER] = 0x3c, + [SPACEMIT_GEDR] = 0x48, + [SPACEMIT_GSDR] = 0x54, + [SPACEMIT_GCDR] = 0x60, + [SPACEMIT_GSRER] = 0x6c, + [SPACEMIT_GCRER] = 0x78, + [SPACEMIT_GSFER] = 0x84, + [SPACEMIT_GCFER] = 0x90, + [SPACEMIT_GAPMASK] = 0x9c, + [SPACEMIT_GCPMASK] = 0xA8, +}; + +static const unsigned int spacemit_gpio_k3_offsets[] = { + [SPACEMIT_GPLR] = 0x0, + [SPACEMIT_GPDR] = 0x4, + [SPACEMIT_GPSR] = 0x8, + [SPACEMIT_GPCR] = 0xc, + [SPACEMIT_GRER] = 0x10, + [SPACEMIT_GFER] = 0x14, + [SPACEMIT_GEDR] = 0x18, + [SPACEMIT_GSDR] = 0x1c, + [SPACEMIT_GCDR] = 0x20, + [SPACEMIT_GSRER] = 0x24, + [SPACEMIT_GCRER] = 0x28, + [SPACEMIT_GSFER] = 0x2c, + [SPACEMIT_GCFER] = 0x30, + [SPACEMIT_GAPMASK] = 0x34, + [SPACEMIT_GCPMASK] = 0x38, +}; + +static const struct spacemit_gpio_data k1_gpio_data = { + .offsets = spacemit_gpio_k1_offsets, + .bank_offsets = { 0x0, 0x4, 0x8, 0x100 }, +}; + +static const struct spacemit_gpio_data k3_gpio_data = { + .offsets = spacemit_gpio_k3_offsets, + .bank_offsets = { 0x0, 0x40, 0x80, 0x100 }, +}; + static const struct of_device_id spacemit_gpio_dt_ids[] = { - { .compatible = "spacemit,k1-gpio" }, + { .compatible = "spacemit,k1-gpio", .data = &k1_gpio_data }, + { .compatible = "spacemit,k3-gpio", .data = &k3_gpio_data }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, spacemit_gpio_dt_ids); @@ -296,12 +364,12 @@ MODULE_DEVICE_TABLE(of, spacemit_gpio_dt_ids); static struct platform_driver spacemit_gpio_driver = { .probe = spacemit_gpio_probe, .driver = { - .name = "k1-gpio", + .name = "spacemit-gpio", .of_match_table = spacemit_gpio_dt_ids, }, }; module_platform_driver(spacemit_gpio_driver); MODULE_AUTHOR("Yixun Lan <dlan@gentoo.org>"); -MODULE_DESCRIPTION("GPIO driver for SpacemiT K1 SoC"); +MODULE_DESCRIPTION("GPIO driver for SpacemiT K1/K3 SoC"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index b1498b59a921..9c874f07be75 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2016-2025 NVIDIA Corporation + * Copyright (c) 2016-2026 NVIDIA Corporation * * Author: Thierry Reding <treding@nvidia.com> * Dipen Patel <dpatel@nvidia.com> @@ -21,6 +21,7 @@ #include <dt-bindings/gpio/tegra234-gpio.h> #include <dt-bindings/gpio/tegra241-gpio.h> #include <dt-bindings/gpio/tegra256-gpio.h> +#include <dt-bindings/gpio/nvidia,tegra264-gpio.h> /* security registers */ #define TEGRA186_GPIO_CTL_SCR 0x0c @@ -1001,7 +1002,9 @@ static int tegra186_gpio_probe(struct platform_device *pdev) if (gpio->soc->num_irqs_per_bank > 1) tegra186_gpio_init_route_mapping(gpio); - np = of_find_matching_node(NULL, tegra186_pmc_of_match); + np = of_parse_phandle(pdev->dev.of_node, "wakeup-parent", 0); + if (!np) + np = of_find_matching_node(NULL, tegra186_pmc_of_match); if (np) { if (of_device_is_available(np)) { irq->parent_domain = irq_find_host(np); @@ -1277,6 +1280,80 @@ static const struct tegra_gpio_soc tegra241_aon_soc = { .has_vm_support = false, }; +#define TEGRA264_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA264_MAIN, _name, _bank, _port, _pins) + +static const struct tegra_gpio_port tegra264_main_ports[] = { + TEGRA264_MAIN_GPIO_PORT(F, 3, 0, 8), + TEGRA264_MAIN_GPIO_PORT(G, 3, 1, 5), + TEGRA264_MAIN_GPIO_PORT(H, 1, 0, 8), + TEGRA264_MAIN_GPIO_PORT(J, 1, 1, 8), + TEGRA264_MAIN_GPIO_PORT(K, 1, 2, 8), + TEGRA264_MAIN_GPIO_PORT(L, 1, 3, 8), + TEGRA264_MAIN_GPIO_PORT(M, 1, 4, 6), + TEGRA264_MAIN_GPIO_PORT(P, 2, 0, 8), + TEGRA264_MAIN_GPIO_PORT(Q, 2, 1, 8), + TEGRA264_MAIN_GPIO_PORT(R, 2, 2, 8), + TEGRA264_MAIN_GPIO_PORT(S, 2, 3, 2), + TEGRA264_MAIN_GPIO_PORT(T, 0, 0, 7), + TEGRA264_MAIN_GPIO_PORT(U, 0, 1, 8), + TEGRA264_MAIN_GPIO_PORT(V, 0, 2, 8), + TEGRA264_MAIN_GPIO_PORT(W, 0, 3, 8), + TEGRA264_MAIN_GPIO_PORT(X, 0, 7, 6), + TEGRA264_MAIN_GPIO_PORT(Y, 0, 5, 8), + TEGRA264_MAIN_GPIO_PORT(Z, 0, 6, 8), + TEGRA264_MAIN_GPIO_PORT(AL, 0, 4, 3), +}; + +static const struct tegra_gpio_soc tegra264_main_soc = { + .num_ports = ARRAY_SIZE(tegra264_main_ports), + .ports = tegra264_main_ports, + .name = "tegra264-gpio", + .instance = 0, + .num_irqs_per_bank = 8, + .has_vm_support = true, +}; + +#define TEGRA264_AON_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA264_AON, _name, _bank, _port, _pins) + +static const struct tegra_gpio_port tegra264_aon_ports[] = { + TEGRA264_AON_GPIO_PORT(AA, 0, 0, 8), + TEGRA264_AON_GPIO_PORT(BB, 0, 1, 2), + TEGRA264_AON_GPIO_PORT(CC, 0, 2, 8), + TEGRA264_AON_GPIO_PORT(DD, 0, 3, 8), + TEGRA264_AON_GPIO_PORT(EE, 0, 4, 4) +}; + +static const struct tegra_gpio_soc tegra264_aon_soc = { + .num_ports = ARRAY_SIZE(tegra264_aon_ports), + .ports = tegra264_aon_ports, + .name = "tegra264-gpio-aon", + .instance = 1, + .num_irqs_per_bank = 8, + .has_vm_support = true, +}; + +#define TEGRA264_UPHY_GPIO_PORT(_name, _bank, _port, _pins) \ + TEGRA_GPIO_PORT(TEGRA264_UPHY, _name, _bank, _port, _pins) + +static const struct tegra_gpio_port tegra264_uphy_ports[] = { + TEGRA264_UPHY_GPIO_PORT(A, 0, 0, 6), + TEGRA264_UPHY_GPIO_PORT(B, 0, 1, 8), + TEGRA264_UPHY_GPIO_PORT(C, 0, 2, 3), + TEGRA264_UPHY_GPIO_PORT(D, 1, 0, 8), + TEGRA264_UPHY_GPIO_PORT(E, 1, 1, 4), +}; + +static const struct tegra_gpio_soc tegra264_uphy_soc = { + .num_ports = ARRAY_SIZE(tegra264_uphy_ports), + .ports = tegra264_uphy_ports, + .name = "tegra264-gpio-uphy", + .instance = 2, + .num_irqs_per_bank = 8, + .has_vm_support = true, +}; + #define TEGRA256_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ TEGRA_GPIO_PORT(TEGRA256_MAIN, _name, _bank, _port, _pins) @@ -1369,6 +1446,15 @@ static const struct of_device_id tegra186_gpio_of_match[] = { .compatible = "nvidia,tegra256-gpio", .data = &tegra256_main_soc }, { + .compatible = "nvidia,tegra264-gpio", + .data = &tegra264_main_soc + }, { + .compatible = "nvidia,tegra264-gpio-aon", + .data = &tegra264_aon_soc + }, { + .compatible = "nvidia,tegra264-gpio-uphy", + .data = &tegra264_uphy_soc + }, { /* sentinel */ } }; diff --git a/drivers/gpio/gpio-virtuser.c b/drivers/gpio/gpio-virtuser.c index 098e67d70ffa..b6c515e7a876 100644 --- a/drivers/gpio/gpio-virtuser.c +++ b/drivers/gpio/gpio-virtuser.c @@ -1631,7 +1631,7 @@ static void gpio_virtuser_lookup_config_group_release(struct config_item *item) kfree(lookup); } -static struct configfs_item_operations gpio_virtuser_lookup_config_item_ops = { +static const struct configfs_item_operations gpio_virtuser_lookup_config_item_ops = { .release = gpio_virtuser_lookup_config_group_release, }; @@ -1692,11 +1692,11 @@ static void gpio_virtuser_device_config_group_release(struct config_item *item) kfree(dev); } -static struct configfs_item_operations gpio_virtuser_device_config_item_ops = { +static const struct configfs_item_operations gpio_virtuser_device_config_item_ops = { .release = gpio_virtuser_device_config_group_release, }; -static struct configfs_group_operations gpio_virtuser_device_config_group_ops = { +static const struct configfs_group_operations gpio_virtuser_device_config_group_ops = { .make_group = gpio_virtuser_make_lookup_group, }; @@ -1729,7 +1729,7 @@ gpio_virtuser_config_make_device_group(struct config_group *group, return &no_free_ptr(dev)->group; } -static struct configfs_group_operations gpio_virtuser_config_group_ops = { +static const struct configfs_group_operations gpio_virtuser_config_group_ops = { .make_group = gpio_virtuser_config_make_device_group, }; diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 97780c57ab56..571e366624d2 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -903,18 +903,16 @@ static int zynq_gpio_probe(struct platform_device *pdev) struct zynq_gpio *gpio; struct gpio_chip *chip; struct gpio_irq_chip *girq; - const struct of_device_id *match; gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); if (!gpio) return -ENOMEM; - match = of_match_node(zynq_gpio_of_match, pdev->dev.of_node); - if (!match) { - dev_err(&pdev->dev, "of_match_node() failed\n"); - return -EINVAL; - } - gpio->p_data = match->data; + gpio->p_data = device_get_match_data(&pdev->dev); + if (!gpio->p_data) + return dev_err_probe(&pdev->dev, -EINVAL, + "device_get_match_data() failed\n"); + platform_set_drvdata(pdev, gpio); gpio->base_addr = devm_platform_ioremap_resource(pdev, 0); diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 8657379e9165..ef1ac68b94b7 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -634,6 +634,7 @@ static struct gpio_desc *of_find_gpio_rename(struct device_node *np, return ERR_PTR(-ENOENT); } +#if IS_ENABLED(CONFIG_SND_SOC_MT2701_CS42448) static struct gpio_desc *of_find_mt2701_gpio(struct device_node *np, const char *con_id, unsigned int idx, @@ -665,6 +666,7 @@ static struct gpio_desc *of_find_mt2701_gpio(struct device_node *np, return desc; } +#endif /* * Trigger sources are special, they allow us to use any GPIO as a LED trigger @@ -699,7 +701,9 @@ typedef struct gpio_desc *(*of_find_gpio_quirk)(struct device_node *np, enum of_gpio_flags *of_flags); static const of_find_gpio_quirk of_find_gpio_quirks[] = { of_find_gpio_rename, +#if IS_ENABLED(CONFIG_SND_SOC_MT2701_CS42448) of_find_mt2701_gpio, +#endif of_find_trigger_gpio, NULL }; diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c index 9e6544203439..b3525d1f06a4 100644 --- a/drivers/gpio/gpiolib-shared.c +++ b/drivers/gpio/gpiolib-shared.c @@ -455,12 +455,7 @@ int gpio_shared_add_proxy_lookup(struct device *consumer, const char *con_id, list_for_each_entry(ref, &entry->refs, list) { guard(mutex)(&ref->lock); - /* - * FIXME: use device_is_compatible() once the reset-gpio - * drivers gains a compatible string which it currently - * does not have. - */ - if (!ref->fwnode && strstarts(dev_name(consumer), "reset.gpio.")) { + if (!ref->fwnode && device_is_compatible(consumer, "reset-gpio")) { if (!gpio_shared_dev_is_reset_gpio(consumer, entry, ref)) continue; } else if (!device_match_fwnode(consumer, ref->fwnode)) { diff --git a/drivers/gpio/gpiolib-swnode.c b/drivers/gpio/gpiolib-swnode.c index b44f35d68459..21478b45c127 100644 --- a/drivers/gpio/gpiolib-swnode.c +++ b/drivers/gpio/gpiolib-swnode.c @@ -18,19 +18,18 @@ #include <linux/gpio/consumer.h> #include <linux/gpio/driver.h> +#include <linux/gpio/property.h> #include "gpiolib.h" #include "gpiolib-swnode.h" -#define GPIOLIB_SWNODE_UNDEFINED_NAME "swnode-gpio-undefined" - static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode) { const struct software_node *gdev_node; struct gpio_device *gdev; gdev_node = to_software_node(fwnode); - if (!gdev_node || !gdev_node->name) + if (!gdev_node) goto fwnode_lookup; /* @@ -38,7 +37,7 @@ static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode) * primarily used as a key for internal chip selects in SPI bindings. */ if (IS_ENABLED(CONFIG_GPIO_SWNODE_UNDEFINED) && - !strcmp(gdev_node->name, GPIOLIB_SWNODE_UNDEFINED_NAME)) + gdev_node == &swnode_gpio_undefined) return ERR_PTR(-ENOENT); fwnode_lookup: @@ -140,7 +139,7 @@ int swnode_gpio_count(const struct fwnode_handle *fwnode, const char *con_id) * a key for internal chip selects in SPI bindings. */ const struct software_node swnode_gpio_undefined = { - .name = GPIOLIB_SWNODE_UNDEFINED_NAME, + .name = "swnode-gpio-undefined", }; EXPORT_SYMBOL_NS_GPL(swnode_gpio_undefined, "GPIO_SWNODE"); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 1578cf3a8c74..c52200eaaaff 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -5261,27 +5261,21 @@ void gpiod_put_array(struct gpio_descs *descs) } EXPORT_SYMBOL_GPL(gpiod_put_array); -static int gpio_stub_drv_probe(struct device *dev) -{ - /* - * The DT node of some GPIO chips have a "compatible" property, but - * never have a struct device added and probed by a driver to register - * the GPIO chip with gpiolib. In such cases, fw_devlink=on will cause - * the consumers of the GPIO chip to get probe deferred forever because - * they will be waiting for a device associated with the GPIO chip - * firmware node to get added and bound to a driver. - * - * To allow these consumers to probe, we associate the struct - * gpio_device of the GPIO chip with the firmware node and then simply - * bind it to this stub driver. - */ - return 0; -} - +/* + * The DT node of some GPIO chips have a "compatible" property, but + * never have a struct device added and probed by a driver to register + * the GPIO chip with gpiolib. In such cases, fw_devlink=on will cause + * the consumers of the GPIO chip to get probe deferred forever because + * they will be waiting for a device associated with the GPIO chip + * firmware node to get added and bound to a driver. + * + * To allow these consumers to probe, we associate the struct + * gpio_device of the GPIO chip with the firmware node and then simply + * bind it to this stub driver. + */ static struct device_driver gpio_stub_drv = { .name = "gpio_stub_drv", .bus = &gpio_bus_type, - .probe = gpio_stub_drv_probe, }; static int __init gpiolib_dev_init(void) |
