diff options
Diffstat (limited to 'target/linux/oxnas/files/drivers/irqchip/irq-rps.c')
-rw-r--r-- | target/linux/oxnas/files/drivers/irqchip/irq-rps.c | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/target/linux/oxnas/files/drivers/irqchip/irq-rps.c b/target/linux/oxnas/files/drivers/irqchip/irq-rps.c new file mode 100644 index 0000000..48b5d68 --- /dev/null +++ b/target/linux/oxnas/files/drivers/irqchip/irq-rps.c @@ -0,0 +1,163 @@ +#include <linux/irqdomain.h> +#include <linux/irq.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/version.h> + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0) +# include "irqchip.h" +#else +# include <linux/irqchip.h> +#endif + +struct rps_chip_data { + void __iomem *base; + struct irq_chip chip; + struct irq_domain *domain; +} rps_data; + +enum { + RPS_IRQ_BASE = 64, + RPS_IRQ_COUNT = 32, + PRS_HWIRQ_BASE = 0, + + RPS_STATUS = 0, + RPS_RAW_STATUS = 4, + RPS_UNMASK = 8, + RPS_MASK = 0xc, +}; + +/* + * Routines to acknowledge, disable and enable interrupts + */ +static void rps_mask_irq(struct irq_data *d) +{ + struct rps_chip_data *chip_data = irq_data_get_irq_chip_data(d); + u32 mask = BIT(d->hwirq); + + iowrite32(mask, chip_data->base + RPS_MASK); +} + +static void rps_unmask_irq(struct irq_data *d) +{ + struct rps_chip_data *chip_data = irq_data_get_irq_chip_data(d); + u32 mask = BIT(d->hwirq); + + iowrite32(mask, chip_data->base + RPS_UNMASK); +} + +static struct irq_chip rps_chip = { + .name = "RPS", + .irq_mask = rps_mask_irq, + .irq_unmask = rps_unmask_irq, +}; + +static int rps_irq_domain_xlate(struct irq_domain *d, + struct device_node *controller, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, + unsigned int *out_type) +{ + if (d->of_node != controller) + return -EINVAL; + if (intsize < 1) + return -EINVAL; + + *out_hwirq = intspec[0]; + /* Honestly I do not know the type */ + *out_type = IRQ_TYPE_LEVEL_HIGH; + + return 0; +} + +static int rps_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + irq_set_chip_and_handler(irq, &rps_chip, handle_level_irq); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0) + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); +#else + irq_set_probe(irq); +#endif + irq_set_chip_data(irq, d->host_data); + return 0; +} + +const struct irq_domain_ops rps_irq_domain_ops = { + .map = rps_irq_domain_map, + .xlate = rps_irq_domain_xlate, +}; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0) +static void rps_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) +#else +static void rps_handle_cascade_irq(struct irq_desc *desc) +#endif +{ + struct rps_chip_data *chip_data = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned int cascade_irq, rps_irq; + u32 status; + + chained_irq_enter(chip, desc); + + status = ioread32(chip_data->base + RPS_STATUS); + rps_irq = __ffs(status); + cascade_irq = irq_find_mapping(chip_data->domain, rps_irq); + + if (unlikely(rps_irq >= RPS_IRQ_COUNT)) +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0) + handle_bad_irq(cascade_irq, desc); +#else + handle_bad_irq(desc); +#endif + else + generic_handle_irq(cascade_irq); + + chained_irq_exit(chip, desc); +} + +#ifdef CONFIG_OF +int __init rps_of_init(struct device_node *node, struct device_node *parent) +{ + void __iomem *rps_base; + int irq_start = RPS_IRQ_BASE; + int irq_base; + int irq; + + if (WARN_ON(!node)) + return -ENODEV; + + rps_base = of_iomap(node, 0); + WARN(!rps_base, "unable to map rps registers\n"); + rps_data.base = rps_base; + + irq_base = irq_alloc_descs(irq_start, 0, RPS_IRQ_COUNT, numa_node_id()); + if (IS_ERR_VALUE(irq_base)) { + WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n", + irq_start); + irq_base = irq_start; + } + + rps_data.domain = irq_domain_add_legacy(node, RPS_IRQ_COUNT, irq_base, + PRS_HWIRQ_BASE, &rps_irq_domain_ops, &rps_data); + + if (WARN_ON(!rps_data.domain)) + return -ENOMEM; + + if (parent) { + irq = irq_of_parse_and_map(node, 0); + if (irq_set_handler_data(irq, &rps_data) != 0) + BUG(); + irq_set_chained_handler(irq, rps_handle_cascade_irq); + } + return 0; + +} + +IRQCHIP_DECLARE(nas782x, "plxtech,nas782x-rps", rps_of_init); +#endif |