diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-ts7100.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ul-ts7100.dtsi index 67a2b6a19795..33f74c84b061 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ul-ts7100.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-ts7100.dtsi @@ -416,36 +416,36 @@ fpga_syscon: bus@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>; fpga_gpio0: gpio@50004010 { compatible = "technologic,ts71xxweim-gpio"; - reg = <0 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>; }; fpga_gpio1: gpio@50004040 { compatible = "technologic,ts71xxweim-gpio"; - reg = <0 0x40 0x08>; + reg = <0x40 0x08>; gpio-controller; #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <1>; }; fpga_gpio2: 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 { @@ -453,7 +453,7 @@ interrupt-controller; #interrupt-cells = <1>; - reg = <0 0x00 0x50>; + reg = <0x00 0x50>; interrupt-parent = <&gpio5>; interrupts = <1 IRQ_TYPE_LEVEL_HIGH>; @@ -472,34 +472,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: flash@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"; @@ -508,7 +481,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 { @@ -528,6 +501,49 @@ 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>; + }; }; - }; + + 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"; + }; + }; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-ts7250v3.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ul-ts7250v3.dtsi index f275e46513aa..99e73fe1afed 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ul-ts7250v3.dtsi +++ b/arch/arm/boot/dts/nxp/imx/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 { diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 42aa7c4c414f..b646684b77a7 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -891,6 +891,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 e6b812d8948d..2c04ef5b22b2 100644 --- a/drivers/gpio/gpio-ts71xxweim.c +++ b/drivers/gpio/gpio-ts71xxweim.c @@ -6,10 +6,14 @@ #include #include +#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. @@ -23,9 +27,24 @@ #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; + spinlock_t lock; }; static inline struct tsweim_gpio_priv *to_gpio_tsweim(struct gpio_chip *chip) @@ -41,7 +60,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; } @@ -50,16 +69,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; } @@ -72,7 +96,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)); } @@ -85,23 +109,184 @@ static int tsweim_gpio_set(struct gpio_chip *chip, unsigned int offset, return -EINVAL; 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); return 0; } -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_domain_irq(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); + + gpiochip_disable_irq(gc, hwirq); +} + +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; + + gpiochip_enable_irq(gc, d->hwirq); + + 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 = { + .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, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + static const struct of_device_id tsweim_gpio_of_match_table[] = { { .compatible = "technologic,ts71xxweim-gpio", }, {}, @@ -112,35 +297,72 @@ static int tsweim_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct tsweim_gpio_priv *priv; - void __iomem *membase; struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - pr_err("Can't get device address\n"); - return -EFAULT; - } - - membase = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); - if (IS_ERR(membase)) { - pr_err("Could not map resource\n"); - return -ENOMEM; - } + 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; - priv->syscon = membase; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) + return -EFAULT; + + priv->base = devm_ioremap(dev, res->start, resource_size(res)); + if (!priv->base) + return -ENOMEM; - priv->gpio_chip = template_chip; + 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; - pdev->dev.platform_data = &priv; - return devm_gpiochip_add_data(&pdev->dev, &priv->gpio_chip, &priv); + 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; + + ack_mode_en = readl(priv->syscon + TSWEIM_IRQ_ACK_MODE) & TSWEIM_IRQ_ACK_MODE_EN; + irq = platform_get_irq_optional(pdev, 0); + if (irq < 0 || !ack_mode_en) { + return devm_gpiochip_add_data(dev, &priv->gpio_chip, priv); + } + + 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; + gpio_irq_chip_set_chip(girq, &tsweim_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(dev, &priv->gpio_chip, priv); } static struct platform_driver tsweim_gpio_driver = { diff --git a/drivers/irqchip/irq-ts71xxweim.c b/drivers/irqchip/irq-ts71xxweim.c index 26e02278383d..3c6496af6974 100644 --- a/drivers/irqchip/irq-ts71xxweim.c +++ b/drivers/irqchip/irq-ts71xxweim.c @@ -8,11 +8,14 @@ #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 @@ -20,23 +23,33 @@ struct tsweim_intc { void __iomem *syscon; struct irq_domain *irqdomain; struct platform_device *pdev; + raw_spinlock_t lock; u32 mask; + bool ack_mode_en; }; static void tsweim_intc_mask(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); } static void tsweim_intc_unmask(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); } static void tsweim_intc_print_chip(struct irq_data *d, struct seq_file *p) @@ -46,11 +59,44 @@ static void tsweim_intc_print_chip(struct irq_data *d, struct seq_file *p) 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_get_irq_chip_data(d); - uint32_t polarity = readl(priv->syscon + TSWEIM_IRQ_POLARITY); - uint32_t bit = BIT_MASK(d->hwirq); + 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: @@ -60,11 +106,14 @@ 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; } @@ -72,26 +121,27 @@ 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_domain_irq(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_domain_irq(priv->irqdomain, irq); } chained_irq_exit(chip, desc); @@ -118,6 +168,7 @@ static int tsweim_intc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct tsweim_intc *priv; int irq = 0; + bool ack_mode_en; irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -133,6 +184,10 @@ static int tsweim_intc_probe(struct platform_device *pdev) priv->pdev = pdev; + raw_spin_lock_init(&priv->lock); + + priv->mask = readl(priv->syscon + TSWEIM_IRQ_MASK); + if (of_property_read_bool(dev->of_node, "ts,haspolarity")) tsweim_intc_chip.irq_set_type = tsweim_intc_set_type; @@ -153,12 +208,21 @@ static int tsweim_intc_probe(struct platform_device *pdev) 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 void 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;