From c4ed5ca2c3d6c8829a1f95455e049ac29f18b484 Mon Sep 17 00:00:00 2001 From: Mark Featherston Date: Fri, 27 Mar 2026 14:50:46 -0700 Subject: [PATCH 1/7] ARM: dts: ts7100: Simplify syscon to 1 address cell --- arch/arm/boot/dts/imx6ul-ts7100.dtsi | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/arch/arm/boot/dts/imx6ul-ts7100.dtsi b/arch/arm/boot/dts/imx6ul-ts7100.dtsi index 66baa37de004..5db505502272 100644 --- a/arch/arm/boot/dts/imx6ul-ts7100.dtsi +++ b/arch/arm/boot/dts/imx6ul-ts7100.dtsi @@ -411,14 +411,14 @@ syscon: syscon@50004000 { compatible = "simple-bus"; - #address-cells = <0x2>; + #address-cells = <0x1>; #size-cells = <0x1>; reg = <0x4000 0x58>; - ranges = <0 0 0x4000 0x58>; + ranges = <0 0x4000 0x100>; gpio6: gpio@50004010 { compatible = "technologic,ts71xxweim-gpio"; - reg = <0 0x10 0x08>; + reg = <0x10 0x08>; gpio-controller; #gpio-cells = <2>; interrupt-controller; @@ -427,28 +427,24 @@ gpio7: gpio@50004040 { compatible = "technologic,ts71xxweim-gpio"; - reg = <0 0x40 0x08>; + reg = <0x40 0x08>; gpio-controller; #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <1>; }; gpio8: gpio@50004050 { compatible = "technologic,ts71xxweim-gpio"; - reg = <0 0x50 0x08>; + reg = <0x50 0x08>; gpio-controller; #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <1>; }; fpga_intc: interrupt-controller@50004024 { compatible = "technologic,ts71xxweim-intc"; + reg = <0x00 0x50>; interrupt-controller; #interrupt-cells = <1>; - reg = <0 0x00 0x50>; interrupt-parent = <&gpio5>; interrupts = <1 IRQ_TYPE_LEVEL_HIGH>; From 38d5e739532bf42015286c3678d3a0b5c8eabdcb Mon Sep 17 00:00:00 2001 From: Mark Featherston Date: Fri, 27 Mar 2026 15:20:34 -0700 Subject: [PATCH 2/7] ARM: dts: ts7100: Added IRQ support for first GPIO bank --- arch/arm/boot/dts/imx6ul-ts7100.dtsi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx6ul-ts7100.dtsi b/arch/arm/boot/dts/imx6ul-ts7100.dtsi index 5db505502272..6f82274cfef6 100644 --- a/arch/arm/boot/dts/imx6ul-ts7100.dtsi +++ b/arch/arm/boot/dts/imx6ul-ts7100.dtsi @@ -418,9 +418,13 @@ gpio6: gpio@50004010 { compatible = "technologic,ts71xxweim-gpio"; - reg = <0x10 0x08>; + reg = <0x10 0x08>, <0x80 0x10>; gpio-controller; #gpio-cells = <2>; + + interrupt-parent = <&fpga_intc>; + interrupts = <21 IRQ_TYPE_LEVEL_HIGH>; + interrupt-controller; #interrupt-cells = <1>; }; From 6b9f423a01fc00e763f443c6311d63460168270a Mon Sep 17 00:00:00 2001 From: Mark Featherston Date: Fri, 27 Mar 2026 15:25:21 -0700 Subject: [PATCH 3/7] irqchip: irq-ts71xxweim: Added Ack support fixes --- drivers/irqchip/irq-ts71xxweim.c | 155 ++++++++++++++++++++++--------- 1 file changed, 112 insertions(+), 43 deletions(-) diff --git a/drivers/irqchip/irq-ts71xxweim.c b/drivers/irqchip/irq-ts71xxweim.c index a08ac179ae78..7fda80d5a017 100644 --- a/drivers/irqchip/irq-ts71xxweim.c +++ b/drivers/irqchip/irq-ts71xxweim.c @@ -2,49 +2,99 @@ #include #include +#include #include #include #include #include +#include #define TSWEIM_IRQ_STATUS 0x24 #define TSWEIM_IRQ_POLARITY 0x28 +#define TSWEIM_IRQ_ACK_MODE 0x2C +#define TSWEIM_IRQ_ACK 0x30 +#define TSWEIM_IRQ_ACK_MODE_EN BIT(0) #define TSWEIM_IRQ_MASK 0x48 #define TSWEIM_NUM_FPGA_IRQ 32 struct tsweim_intc { void __iomem *syscon; struct irq_domain *irqdomain; - struct irq_chip chip; + struct platform_device *pdev; + raw_spinlock_t lock; u32 mask; + bool ack_mode_en; }; -static struct tsweim_intc *irq_data_to_priv(struct irq_data *data) -{ - return data->domain->host_data; -} - static void tsweim_intc_mask(struct irq_data *d) { - struct tsweim_intc *priv = irq_data_to_priv(d); + struct tsweim_intc *priv = irq_data_get_irq_chip_data(d); + + raw_spin_lock(&priv->lock); priv->mask = readl(priv->syscon + TSWEIM_IRQ_MASK) & ~BIT(d->hwirq); writel(priv->mask, priv->syscon + TSWEIM_IRQ_MASK); + + raw_spin_unlock(&priv->lock); } static void tsweim_intc_unmask(struct irq_data *d) { - struct tsweim_intc *priv = irq_data_to_priv(d); + struct tsweim_intc *priv = irq_data_get_irq_chip_data(d); + + raw_spin_lock(&priv->lock); priv->mask = readl(priv->syscon + TSWEIM_IRQ_MASK) | BIT(d->hwirq); writel(priv->mask, priv->syscon + TSWEIM_IRQ_MASK); + + raw_spin_unlock(&priv->lock); +} + +static void tsweim_intc_print_chip(struct irq_data *d, struct seq_file *p) +{ + struct tsweim_intc *priv = irq_data_get_irq_chip_data(d); + + seq_printf(p, "%s", dev_name(&priv->pdev->dev)); +} + +static void tsweim_intc_irq_ack(struct irq_data *d) +{ + struct tsweim_intc *priv = irq_data_get_irq_chip_data(d); + + if (priv->ack_mode_en) { + writel(BIT(d->hwirq), priv->syscon + TSWEIM_IRQ_ACK); + } else { + readl(priv->syscon + TSWEIM_IRQ_STATUS); + } +} + +static void tsweim_intc_irq_mask_ack(struct irq_data *d) +{ + struct tsweim_intc *priv = irq_data_get_irq_chip_data(d); + + raw_spin_lock(&priv->lock); + + priv->mask = readl(priv->syscon + TSWEIM_IRQ_MASK) & ~BIT(d->hwirq); + writel(priv->mask, priv->syscon + TSWEIM_IRQ_MASK); + + raw_spin_unlock(&priv->lock); + + if (priv->ack_mode_en) { + writel(BIT(d->hwirq), priv->syscon + TSWEIM_IRQ_ACK); + } else { + readl(priv->syscon + TSWEIM_IRQ_STATUS); + } } static int tsweim_intc_set_type(struct irq_data *d, unsigned int flow_type) { - struct tsweim_intc *priv = irq_data_to_priv(d); - uint32_t polarity = readl(priv->syscon + TSWEIM_IRQ_POLARITY); - uint32_t bit = BIT_MASK(d->hwirq); + struct tsweim_intc *priv = irq_data_get_irq_chip_data(d); + u32 polarity, bit; + + raw_spin_lock(&priv->lock); + + polarity = readl(priv->syscon + TSWEIM_IRQ_POLARITY); + bit = BIT(d->hwirq); switch (flow_type) { case IRQ_TYPE_LEVEL_LOW: @@ -54,34 +104,42 @@ static int tsweim_intc_set_type(struct irq_data *d, unsigned int flow_type) polarity &= ~bit; break; default: + raw_spin_unlock(&priv->lock); return -EINVAL; } writel(polarity, priv->syscon + TSWEIM_IRQ_POLARITY); + raw_spin_unlock(&priv->lock); + return 0; } +static struct irq_chip tsweim_intc_chip = { + .irq_mask = tsweim_intc_mask, + .irq_unmask = tsweim_intc_unmask, + .irq_print_chip = tsweim_intc_print_chip, + .irq_ack = tsweim_intc_irq_ack, + .irq_mask_ack = tsweim_intc_irq_mask_ack, +}; + static void tsweim_irq_handler(struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); struct tsweim_intc *priv = irq_desc_get_handler_data(desc); - unsigned int irq; - unsigned int status; + unsigned long irq, status; chained_irq_enter(chip, desc); - while ((status = - (priv->mask & readl(priv->syscon + TSWEIM_IRQ_STATUS)))) { - irq = 0; - do { - if (status & 1) { - generic_handle_irq(irq_linear_revmap( - priv->irqdomain, irq)); - } - status >>= 1; - irq++; - } while (status); + status = readl(priv->syscon + TSWEIM_IRQ_STATUS); + + /* before the ack mode functionality the mask was not applied in hardware + * requiring the mask to be applied in software */ + if (!priv->ack_mode_en) + status &= priv->mask; + + for_each_set_bit(irq, &status, TSWEIM_NUM_FPGA_IRQ) { + generic_handle_irq(irq_linear_revmap(priv->irqdomain, irq)); } chained_irq_exit(chip, desc); @@ -90,9 +148,8 @@ static void tsweim_irq_handler(struct irq_desc *desc) static int tsweim_intc_irqdomain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq) { - struct tsweim_intc *priv = d->host_data; - - irq_set_chip_and_handler(irq, &priv->chip, handle_level_irq); + irq_set_chip_and_handler(irq, &tsweim_intc_chip, handle_level_irq); + irq_set_chip_data(irq, d->host_data); irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE); irq_set_status_flags(irq, IRQ_LEVEL); @@ -108,8 +165,12 @@ static int tsweim_intc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct tsweim_intc *priv; - struct resource *irq = 0; - struct irq_chip *chip; + int irq = 0; + bool ack_mode_en; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; priv = devm_kzalloc(dev, sizeof(struct tsweim_intc), GFP_KERNEL); if (!priv) @@ -119,39 +180,47 @@ static int tsweim_intc_probe(struct platform_device *pdev) if (IS_ERR(priv->syscon)) return PTR_ERR(priv->syscon); - irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (irq == NULL) { - pr_err("Can't get interrupt\n"); - return -EFAULT; - } + priv->pdev = pdev; - chip = &priv->chip; - chip->name = dev->of_node->name; - chip->irq_mask = tsweim_intc_mask; - chip->irq_unmask = tsweim_intc_unmask; + raw_spin_lock_init(&priv->lock); + + priv->mask = readl(priv->syscon + TSWEIM_IRQ_MASK); if (of_property_read_bool(dev->of_node, "ts,haspolarity")) - chip->irq_set_type = tsweim_intc_set_type; + tsweim_intc_chip.irq_set_type = tsweim_intc_set_type; priv->irqdomain = irq_domain_add_linear(dev->of_node, TSWEIM_NUM_FPGA_IRQ, &tsweim_intc_irqdomain_ops, priv); - if (!priv->irqdomain) { pr_err("unable to add irq domain\n"); return -ENOMEM; } - irq_set_handler_data(irq->start, priv); - irq_set_chained_handler(irq->start, tsweim_irq_handler); + if (devm_request_irq(dev, irq, no_action, IRQF_NO_THREAD, + dev_name(dev), NULL)) { + irq_domain_remove(priv->irqdomain); + return -ENOENT; + } + + irq_set_chained_handler_and_data(irq, tsweim_irq_handler, priv); platform_set_drvdata(pdev, priv); + writel(TSWEIM_IRQ_ACK_MODE_EN, priv->syscon + TSWEIM_IRQ_ACK_MODE); + ack_mode_en = readl(priv->syscon + TSWEIM_IRQ_ACK_MODE) & TSWEIM_IRQ_ACK_MODE_EN; + if (ack_mode_en) { + dev_info(dev, "ACK mode enabled\n"); + priv->ack_mode_en = true; + } else { + dev_warn(dev, "Ack mode not supported\n"); + } + return 0; } static int tsweim_intc_remove(struct platform_device *pdev) { - struct tsweim_intc *priv = dev_get_platdata(&pdev->dev); + struct tsweim_intc *priv = platform_get_drvdata(pdev); if (priv->irqdomain) { int i, irq; From 06c7c16097169d273e79d865a89a51ad0f565eef Mon Sep 17 00:00:00 2001 From: Mark Featherston Date: Fri, 27 Mar 2026 16:14:29 -0700 Subject: [PATCH 4/7] gpio: ts71xxweim: Added IRQ support --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-ts71xxweim.c | 276 +++++++++++++++++++++++++++++---- 2 files changed, 248 insertions(+), 29 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 07568dd299dc..28b859f25133 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -733,6 +733,7 @@ config GPIO_LOONGSON1 config GPIO_TS71XXWEIM bool "embeddedTS WEIM FPGA GPIO" depends on IMX_WEIM + select GPIOLIB_IRQCHIP help Say yes here to enable the GPIO driver for embeddedTS's FPGA core connected to the i.MX6UL WEIM bus. diff --git a/drivers/gpio/gpio-ts71xxweim.c b/drivers/gpio/gpio-ts71xxweim.c index 7e70bf8e5f25..4fd73f040989 100644 --- a/drivers/gpio/gpio-ts71xxweim.c +++ b/drivers/gpio/gpio-ts71xxweim.c @@ -5,9 +5,13 @@ */ #include +#include #include +#include +#include #include #include +#include /* Most that this driver can currently support in a single bank is 16. This is * due simply to how the FPGA used for these devices is structured. @@ -21,9 +25,25 @@ #define TSWEIM_CLR_REG 0x04 #define TSWEIM_EN_CLR_REG 0x06 +/* The IRQ registers are from a second register range */ +#define TSWEIM_GPIO_IRQ_ACK 0x0 +#define TSWEIM_GPIO_IRQ_ACK_MASK 0x2 +#define TSWEIM_GPIO_IRQ_EDGE_LEVEL 0x4 +#define TSWEIM_GPIO_IRQ_EDGE_SEL 0x6 +#define TSWEIM_GPIO_IRQ_POLARITY 0x8 +#define TSWEIM_GPIO_IRQ_MASK 0xA +#define TSWEIM_GPIO_IRQ_PENDING 0xC + +#define TSWEIM_IRQ_ACK_MODE 0x2C +#define TSWEIM_IRQ_ACK_MODE_EN BIT(0) + struct tsweim_gpio_priv { - void __iomem *syscon; + void __iomem *syscon; + void __iomem *base; + void __iomem *irqbase; struct gpio_chip gpio_chip; + struct irq_chip irq_chip; + spinlock_t lock; }; static inline struct tsweim_gpio_priv *to_gpio_tsweim(struct gpio_chip *chip) @@ -39,7 +59,7 @@ static int tsweim_gpio_direction_input(struct gpio_chip *chip, if (!(offset < priv->gpio_chip.ngpio)) return -EINVAL; - writew((1 << offset), priv->syscon + TSWEIM_EN_CLR_REG); + writew((1 << offset), priv->base + TSWEIM_EN_CLR_REG); return 0; } @@ -48,16 +68,21 @@ static int tsweim_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { struct tsweim_gpio_priv *priv = to_gpio_tsweim(chip); + unsigned long flags; if (!(offset < priv->gpio_chip.ngpio)) return -EINVAL; + spin_lock_irqsave(&priv->lock, flags); + if (value) - writew((1 << offset), priv->syscon + TSWEIM_SET_REG); + writew((1 << offset), priv->base + TSWEIM_SET_REG); else - writew((1 << offset), priv->syscon + TSWEIM_CLR_REG); + writew((1 << offset), priv->base + TSWEIM_CLR_REG); - writew((1 << offset), priv->syscon + TSWEIM_EN_SET_REG); + writew((1 << offset), priv->base + TSWEIM_EN_SET_REG); + + spin_unlock_irqrestore(&priv->lock, flags); return 0; } @@ -70,7 +95,7 @@ static int tsweim_gpio_get(struct gpio_chip *chip, unsigned int offset) if (!(offset < priv->gpio_chip.ngpio)) return -EINVAL; - reg = readw(priv->syscon + TSWEIM_GET_REG); + reg = readw(priv->base + TSWEIM_GET_REG); return !!(reg & (1 << offset)); } @@ -83,21 +108,176 @@ static void tsweim_gpio_set(struct gpio_chip *chip, unsigned int offset, return; if (value) - writew((1 << offset), priv->syscon + TSWEIM_SET_REG); + writew((1 << offset), priv->base + TSWEIM_SET_REG); else - writew((1 << offset), priv->syscon + TSWEIM_CLR_REG); + writew((1 << offset), priv->base + TSWEIM_CLR_REG); } -static const struct gpio_chip template_chip = { +static int tsweim_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct tsweim_gpio_priv *priv = to_gpio_tsweim(chip); + u16 en_reg; + + if (!(offset < priv->gpio_chip.ngpio)) + return -EINVAL; + + en_reg = readw(priv->base + TSWEIM_EN_SET_REG); + + return (en_reg & (1 << offset)) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; +} + +static const struct gpio_chip tsweim_gpio_chip = { .owner = THIS_MODULE, .direction_input = tsweim_gpio_direction_input, .direction_output = tsweim_gpio_direction_output, + .get_direction = tsweim_gpio_get_direction, .get = tsweim_gpio_get, .set = tsweim_gpio_set, .base = -1, .can_sleep = false, }; +static void gpio_tsweim_irq_handler(struct irq_desc *desc) +{ + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + struct tsweim_gpio_priv *priv = gpiochip_get_data(gc); + struct irq_chip *irq_chip = irq_desc_get_chip(desc); + unsigned long pending, bit; + + chained_irq_enter(irq_chip, desc); + + pending = readw(priv->irqbase + TSWEIM_GPIO_IRQ_PENDING); + + for_each_set_bit(bit, &pending, 16) { + generic_handle_irq(irq_linear_revmap(gc->irq.domain, bit)); + } + + chained_irq_exit(irq_chip, desc); +} + +static void gpio_tsweim_irq_ack(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + struct tsweim_gpio_priv *priv = gpiochip_get_data(gc); + unsigned long flags; + u16 reg; + + spin_lock_irqsave(&priv->lock, flags); + + reg = readw(priv->irqbase + TSWEIM_GPIO_IRQ_ACK) | BIT(hwirq); + writew(reg, priv->irqbase + TSWEIM_GPIO_IRQ_ACK); + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void gpio_tsweim_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + struct tsweim_gpio_priv *priv = gpiochip_get_data(gc); + unsigned long flags; + u16 mask; + + spin_lock_irqsave(&priv->lock, flags); + + mask = readw(priv->irqbase + TSWEIM_GPIO_IRQ_MASK) | BIT(hwirq); + writew(mask, priv->irqbase + TSWEIM_GPIO_IRQ_MASK); + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void gpio_tsweim_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + struct tsweim_gpio_priv *priv = gpiochip_get_data(gc); + unsigned long flags; + u16 mask; + + spin_lock_irqsave(&priv->lock, flags); + + mask = readw(priv->irqbase + TSWEIM_GPIO_IRQ_MASK) & ~BIT(hwirq); + writew(mask, priv->irqbase + TSWEIM_GPIO_IRQ_MASK); + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static int gpio_tsweim_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct tsweim_gpio_priv *priv = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + u16 polarity, edge, edge_sel; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + + polarity = readw(priv->irqbase + TSWEIM_GPIO_IRQ_POLARITY); + edge = readw(priv->irqbase + TSWEIM_GPIO_IRQ_EDGE_LEVEL); + edge_sel = readw(priv->irqbase + TSWEIM_GPIO_IRQ_EDGE_SEL); + + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_LEVEL_HIGH: + edge &= ~BIT(hwirq); + edge_sel &= ~BIT(hwirq); + polarity |= BIT(hwirq); + irq_set_handler_locked(d, handle_level_irq); + break; + case IRQ_TYPE_LEVEL_LOW: + edge &= ~BIT(hwirq); + edge_sel &= ~BIT(hwirq); + polarity &= ~BIT(hwirq); + irq_set_handler_locked(d, handle_level_irq); + break; + case IRQ_TYPE_EDGE_RISING: + edge |= BIT(hwirq); + edge_sel &= ~BIT(hwirq); + polarity |= BIT(hwirq); + irq_set_handler_locked(d, handle_edge_irq); + break; + case IRQ_TYPE_EDGE_FALLING: + edge |= BIT(hwirq); + edge_sel &= ~BIT(hwirq); + polarity &= ~BIT(hwirq); + irq_set_handler_locked(d, handle_edge_irq); + break; + case IRQ_TYPE_EDGE_BOTH: + edge |= BIT(hwirq); + edge_sel |= BIT(hwirq); + polarity &= ~BIT(hwirq); + irq_set_handler_locked(d, handle_edge_irq); + break; + default: + spin_unlock_irqrestore(&priv->lock, flags); + return -EINVAL; + } + + writew(polarity, priv->irqbase + TSWEIM_GPIO_IRQ_POLARITY); + writew(edge, priv->irqbase + TSWEIM_GPIO_IRQ_EDGE_LEVEL); + writew(edge_sel, priv->irqbase + TSWEIM_GPIO_IRQ_EDGE_SEL); + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static void gpio_tsweim_irq_print_chip(struct irq_data *data, struct seq_file *p) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); + + seq_printf(p, dev_name(gc->parent)); +} + +static const struct irq_chip tsweim_irq_chip_template = { + .name = "tsweim_gpio_irq", + .irq_ack = gpio_tsweim_irq_ack, + .irq_mask = gpio_tsweim_irq_mask, + .irq_unmask = gpio_tsweim_irq_unmask, + .irq_set_type = gpio_tsweim_irq_set_type, + .irq_print_chip = gpio_tsweim_irq_print_chip, +}; + static const struct of_device_id tsweim_gpio_of_match_table[] = { { .compatible = "technologic,ts71xxweim-gpio", }, {}, @@ -108,37 +288,75 @@ static int tsweim_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct tsweim_gpio_priv *priv; - u32 ngpio; - int base; - void __iomem *membase; struct resource *res; + struct gpio_irq_chip *girq; + struct device_node *syscon_of_node; + int irq, ret; + bool ack_mode_en; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - pr_err("Can't get device address\n"); + if (res == NULL) return -EFAULT; - } - membase = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); - if (IS_ERR(membase)) { - pr_err("Could not map resource\n"); + priv->base = devm_ioremap(dev, res->start, resource_size(res)); + if (!priv->base) return -ENOMEM; - } - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) + priv->gpio_chip = tsweim_gpio_chip; + priv->gpio_chip.label = dev_name(dev); + priv->gpio_chip.ngpio = TSWEIM_NR_DIO; + priv->gpio_chip.parent = dev; + + spin_lock_init(&priv->lock); + + syscon_of_node = of_get_parent(pdev->dev.of_node); + if (!syscon_of_node) + return -EINVAL; + + ret = of_address_to_resource(syscon_of_node, 0, res); + of_node_put(syscon_of_node); + + if (ret) + return ret; + + priv->syscon = devm_ioremap(dev, res->start, resource_size(res)); + if (!priv->syscon) return -ENOMEM; - priv->syscon = membase; + ack_mode_en = readl(priv->syscon + TSWEIM_IRQ_ACK_MODE) & TSWEIM_IRQ_ACK_MODE_EN; + irq = platform_get_irq_optional(pdev, 0); + if (irq == -EPROBE_DEFER) + return irq; + if (irq < 0 || !ack_mode_en) { + return devm_gpiochip_add_data(dev, &priv->gpio_chip, priv); + } - priv->gpio_chip = template_chip; - priv->gpio_chip.label = dev_name(dev); - priv->gpio_chip.ngpio = TSWEIM_NR_DIO; - pdev->dev.platform_data = &priv; - priv->gpio_chip.of_node = pdev->dev.of_node; + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) + return -EFAULT; + + priv->irqbase = devm_ioremap(dev, res->start, resource_size(res)); + if (!priv->irqbase) + return -ENOMEM; + + girq = &priv->gpio_chip.irq; + priv->irq_chip = tsweim_irq_chip_template; + girq->chip = &priv->irq_chip; + girq->parent_handler = gpio_tsweim_irq_handler; + girq->num_parents = 1; + girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents), + GFP_KERNEL); + if (!girq->parents) + return -ENOMEM; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_bad_irq; + girq->parents[0] = irq; - return devm_gpiochip_add_data(&pdev->dev, &priv->gpio_chip, &priv); + return devm_gpiochip_add_data(dev, &priv->gpio_chip, priv); } static struct platform_driver tsweim_gpio_driver = { From 1f82fddf36c732c0c59a6f62977bee5fb10644b6 Mon Sep 17 00:00:00 2001 From: Mark Featherston Date: Fri, 27 Mar 2026 16:09:16 -0700 Subject: [PATCH 5/7] ARM: dts: ts7100: Migrated to a single SPI controller --- arch/arm/boot/dts/imx6ul-ts7100.dtsi | 46 ++++++++++------------------ 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/arch/arm/boot/dts/imx6ul-ts7100.dtsi b/arch/arm/boot/dts/imx6ul-ts7100.dtsi index 6f82274cfef6..0bf957555601 100644 --- a/arch/arm/boot/dts/imx6ul-ts7100.dtsi +++ b/arch/arm/boot/dts/imx6ul-ts7100.dtsi @@ -467,34 +467,7 @@ interrupts = <0>; }; - opencores_spi0: spi@50000100 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "opencores,spi-oc"; - reg = <0x100 32>; - interrupt-parent = <&fpga_intc>; - interrupts = <9>; - clocks = <&fpga_clk_weim_bclk>; - clock-names = "spi-oc-clk"; - num-cs = <2>; - - spifram: eeprom@0 { - compatible = "atmel,at25"; - reg = <0>; - spi-max-frequency = <20000000>; - size = <0x800>; - address-width = <16>; - pagesize = <64>; - }; - - spisplash: spi@1 { - compatible = "jedec,spi-nor"; - reg = <1>; - spi-max-frequency = <20000000>; - }; - }; - - opencores_spi1: spi@50000120 { + opencores_spi0: spi@50000120 { #address-cells = <1>; #size-cells = <0>; compatible = "opencores,spi-oc"; @@ -503,7 +476,7 @@ interrupts = <10>; clocks = <&fpga_clk_weim_bclk>; clock-names = "spi-oc-clk"; - num-cs = <1>; + num-cs = <3>; /* Touch screen SPI interface */ touch_spi: touch_spi@0 { @@ -523,6 +496,21 @@ ti,pressure-min = /bits/ 16 <300>; linux,wakeup; }; + + spifram: eeprom@1 { + compatible = "atmel,at25"; + reg = <1>; + spi-max-frequency = <20000000>; + size = <0x800>; + address-width = <16>; + pagesize = <64>; + }; + + spisplash: spi@2 { + compatible = "jedec,spi-nor"; + reg = <2>; + spi-max-frequency = <20000000>; + }; }; }; }; From c0b4673d442e4ff0a9895661a0ded71e7f60b5a1 Mon Sep 17 00:00:00 2001 From: Mark Featherston Date: Fri, 27 Mar 2026 16:10:00 -0700 Subject: [PATCH 6/7] ARM: dts: ts7100: Added PWM controllers --- arch/arm/boot/dts/imx6ul-ts7100.dtsi | 30 +++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx6ul-ts7100.dtsi b/arch/arm/boot/dts/imx6ul-ts7100.dtsi index 0bf957555601..1313b7a9eefc 100644 --- a/arch/arm/boot/dts/imx6ul-ts7100.dtsi +++ b/arch/arm/boot/dts/imx6ul-ts7100.dtsi @@ -512,5 +512,33 @@ spi-max-frequency = <20000000>; }; }; - }; + + fpga_pwm0: pwm@180 { + compatible = "technologic,pwm"; + reg = <0x180 8>; + clocks = <&fpga_clk_weim_bclk>; + clock-names = "PWM-INPUT-CLK"; + }; + + fpga_pwm1: pwm@190 { + compatible = "technologic,pwm"; + reg = <0x190 8>; + clocks = <&fpga_clk_weim_bclk>; + clock-names = "PWM-INPUT-CLK"; + }; + + fpga_pwm2: pwm@1a0 { + compatible = "technologic,pwm"; + reg = <0x1a0 8>; + clocks = <&fpga_clk_weim_bclk>; + clock-names = "PWM-INPUT-CLK"; + }; + + fpga_pwm3: pwm@1b0 { + compatible = "technologic,pwm"; + reg = <0x1b0 8>; + clocks = <&fpga_clk_weim_bclk>; + clock-names = "PWM-INPUT-CLK"; + }; + }; }; From 4e522d0408e1e5198d57b1ca94636c6fbc1634ff Mon Sep 17 00:00:00 2001 From: Mark Featherston Date: Fri, 27 Mar 2026 16:14:07 -0700 Subject: [PATCH 7/7] ARM: dts: ts7250v3: Added IRQ capable GPIO --- arch/arm/boot/dts/imx6ul-ts7250v3.dtsi | 41 +++++++++++++++++++++----- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/arch/arm/boot/dts/imx6ul-ts7250v3.dtsi b/arch/arm/boot/dts/imx6ul-ts7250v3.dtsi index 7e3f99b35d11..89ce2db57d3f 100644 --- a/arch/arm/boot/dts/imx6ul-ts7250v3.dtsi +++ b/arch/arm/boot/dts/imx6ul-ts7250v3.dtsi @@ -428,9 +428,13 @@ fpga_bank0: fpga_gpio@10 { compatible = "technologic,ts71xxweim-gpio"; - reg = <0x10 0x08>; + reg = <0x10 0x08>, <0x80 0x10>; gpio-controller; #gpio-cells = <2>; + + interrupt-parent = <&fpga_intc>; + interrupts = <21 IRQ_TYPE_LEVEL_HIGH>; + interrupt-controller; #interrupt-cells = <1>; @@ -444,9 +448,13 @@ fpga_bank1: fpga_gpio@40 { compatible = "technologic,ts71xxweim-gpio"; - reg = <0x40 0x08>; + reg = <0x40 0x08>, <0x90 0x10>; gpio-controller; #gpio-cells = <2>; + + interrupt-parent = <&fpga_intc>; + interrupts = <22 IRQ_TYPE_LEVEL_HIGH>; + interrupt-controller; #interrupt-cells = <1>; @@ -460,9 +468,13 @@ fpga_bank2: fpga_gpio@54 { compatible = "technologic,ts71xxweim-gpio"; - reg = <0x54 0x08>; + reg = <0x54 0x08>, <0xa0 0x10>; gpio-controller; #gpio-cells = <2>; + + interrupt-parent = <&fpga_intc>; + interrupts = <23 IRQ_TYPE_LEVEL_HIGH>; + interrupt-controller; #interrupt-cells = <1>; @@ -478,9 +490,13 @@ fpga_bank3: fpga_gpio@5c { compatible = "technologic,ts71xxweim-gpio"; - reg = <0x5c 0x08>; + reg = <0x5c 0x08>, <0xb0 0x10>; gpio-controller; #gpio-cells = <2>; + + interrupt-parent = <&fpga_intc>; + interrupts = <24 IRQ_TYPE_LEVEL_HIGH>; + interrupt-controller; #interrupt-cells = <1>; @@ -494,9 +510,13 @@ fpga_bank4: fpga_gpio@64 { compatible = "technologic,ts71xxweim-gpio"; - reg = <0x64 0x08>; + reg = <0x64 0x08>, <0xc0 0x10>; gpio-controller; #gpio-cells = <2>; + + interrupt-parent = <&fpga_intc>; + interrupts = <25 IRQ_TYPE_LEVEL_HIGH>; + interrupt-controller; #interrupt-cells = <1>; @@ -512,17 +532,22 @@ fpga_bank5: fpga_gpio@6c { compatible = "technologic,ts71xxweim-gpio"; - reg = <0x6c 0x08>; + reg = <0x6c 0x08>, <0xd0 0x10>; gpio-controller; #gpio-cells = <2>; + + interrupt-parent = <&fpga_intc>; + interrupts = <26 IRQ_TYPE_LEVEL_HIGH>; + interrupt-controller; #interrupt-cells = <1>; gpio-line-names = "ISA_ADD_16", "ISA_ADD_17", "ISA_ADD_18", "ISA_ADD_19", "ISA_IOR", "ISA_IOW", "ISA_MEMR", "ISA_MEMW", - "ISA_CN_D01", "ISA_CN_D02", "", "", "", - "", "", ""; + "ISA_CN_D01", "ISA_CN_D02", "ISA_IRQ3", + "ISA_IRQ5", "ISA_IRQ6", "ISA_IRQ7", + "ISA_IRQ9", ""; }; pc104bus: fpgaisa@50 {