Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 48 additions & 12 deletions drivers/irqchip/irq-ts71xxweim.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@
#include <linux/of_device.h>
#include <linux/seq_file.h>

#define TSWEIM_IRQ_ACK 0x20
#define TSWEIM_IRQ_STATUS 0x24
#define TSWEIM_IRQ_POLARITY 0x28
#define TSWEIM_ACK_MODE 0x2C
#define TSWEIM_IRQ_MASK 0x48
#define TSWEIM_NUM_FPGA_IRQ 32

struct tsweim_intc {
void __iomem *syscon;
struct irq_domain *irqdomain;
struct platform_device *pdev;
raw_spinlock_t lock;
u32 mask;
};

Expand Down Expand Up @@ -72,7 +75,8 @@ static struct irq_chip tsweim_intc_chip = {
.irq_print_chip = tsweim_intc_print_chip,
};

static void tsweim_irq_handler(struct irq_desc *desc)
/* The legacy IRQ handler would "auto clear on read" */
static void tsweim_irq_legacy_handler(struct irq_desc *desc)
{
struct irq_chip *chip = irq_desc_get_chip(desc);
struct tsweim_intc *priv = irq_desc_get_handler_data(desc);
Expand All @@ -95,13 +99,38 @@ static void tsweim_irq_handler(struct irq_desc *desc)
chained_irq_exit(chip, desc);
}

static irqreturn_t tsweim_irq_handler(int irq, void *data)
{
struct tsweim_intc *priv = (struct tsweim_intc *)data;
unsigned long lock_flags;
unsigned long status;
int i;

status = readl(priv->syscon + TSWEIM_IRQ_STATUS);
writel(status, priv->syscon + TSWEIM_IRQ_ACK);

for_each_set_bit(i, &status, 32) {
raw_spin_lock_irqsave(&priv->lock, lock_flags);
generic_handle_domain_irq(priv->irqdomain, i);
raw_spin_unlock_irqrestore(&priv->lock,
lock_flags);
}

return IRQ_HANDLED;
}

static int tsweim_enable_ack(struct tsweim_intc *priv)
{
writel(0x1, priv->syscon + TSWEIM_ACK_MODE);
return (readl(priv->syscon + TSWEIM_ACK_MODE) & 0x1);
}

static int tsweim_intc_irqdomain_map(struct irq_domain *d,
unsigned int irq, irq_hw_number_t hwirq)
{
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);
irq_clear_status_flags(irq, IRQ_NOPROBE);

return 0;
}
Expand All @@ -116,6 +145,7 @@ static int tsweim_intc_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct tsweim_intc *priv;
int irq = 0;
int ret = 0;

irq = platform_get_irq(pdev, 0);
if (irq < 0)
Expand All @@ -134,24 +164,30 @@ static int tsweim_intc_probe(struct platform_device *pdev)
if (of_property_read_bool(dev->of_node, "ts,haspolarity"))
tsweim_intc_chip.irq_set_type = tsweim_intc_set_type;

raw_spin_lock_init(&priv->lock);
platform_set_drvdata(pdev, priv);

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;
}

if (devm_request_irq(dev, irq, no_action, IRQF_NO_THREAD,
dev_name(dev), NULL)) {
irq_domain_remove(priv->irqdomain);
return -ENOENT;
if (tsweim_enable_ack(priv)) {
ret = request_irq(irq, tsweim_irq_handler, 0,
dev_name(dev), priv);
} else {
pr_info("ACK is unsupported, IRQs will be buggy\n");
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_legacy_handler, priv);
}

irq_set_chained_handler_and_data(irq, tsweim_irq_handler, priv);

platform_set_drvdata(pdev, priv);

return 0;
return ret;
}

static int tsweim_intc_remove(struct platform_device *pdev)
Expand Down