Commit 1fc9d96ec63e7e071f22b89c268145d50720d667

Authored by Jason Cooper

Merge branch 'irqchip/keystone' into irqchip/core

Showing 4 changed files Side-by-side Diff

Documentation/devicetree/bindings/interrupt-controller/ti,keystone-irq.txt
  1 +Keystone 2 IRQ controller IP
  2 +
  3 +On Keystone SOCs, DSP cores can send interrupts to ARM
  4 +host using the IRQ controller IP. It provides 28 IRQ signals to ARM.
  5 +The IRQ handler running on HOST OS can identify DSP signal source by
  6 +analyzing SRCCx bits in IPCARx registers. This is one of the component
  7 +used by the IPC mechanism used on Keystone SOCs.
  8 +
  9 +Required Properties:
  10 +- compatible: should be "ti,keystone-irq"
  11 +- ti,syscon-dev : phandle and offset pair. The phandle to syscon used to
  12 + access device control registers and the offset inside
  13 + device control registers range.
  14 +- interrupt-controller : Identifies the node as an interrupt controller
  15 +- #interrupt-cells : Specifies the number of cells needed to encode interrupt
  16 + source should be 1.
  17 +- interrupts: interrupt reference to primary interrupt controller
  18 +
  19 +Please refer to interrupts.txt in this directory for details of the common
  20 +Interrupt Controllers bindings used by client devices.
  21 +
  22 +Example:
  23 + kirq0: keystone_irq0@026202a0 {
  24 + compatible = "ti,keystone-irq";
  25 + ti,syscon-dev = <&devctrl 0x2a0>;
  26 + interrupts = <GIC_SPI 4 IRQ_TYPE_EDGE_RISING>;
  27 + interrupt-controller;
  28 + #interrupt-cells = <1>;
  29 + };
  30 +
  31 + dsp0: dsp0 {
  32 + compatible = "linux,rproc-user";
  33 + ...
  34 + interrupt-parent = <&kirq0>;
  35 + interrupts = <10 2>;
  36 + };
drivers/irqchip/Kconfig
... ... @@ -113,4 +113,11 @@
113 113 The primary irqchip invokes the crossbar's callback which inturn allocates
114 114 a free irq and configures the IP. Thus the peripheral interrupts are
115 115 routed to one of the free irqchip interrupt lines.
  116 +
  117 +config KEYSTONE_IRQ
  118 + tristate "Keystone 2 IRQ controller IP"
  119 + depends on ARCH_KEYSTONE
  120 + help
  121 + Support for Texas Instruments Keystone 2 IRQ controller IP which
  122 + is part of the Keystone 2 IPC mechanism
drivers/irqchip/Makefile
... ... @@ -34,4 +34,5 @@
34 34 obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o
35 35 obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o
36 36 obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o
  37 +obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o
drivers/irqchip/irq-keystone.c
  1 +/*
  2 + * Texas Instruments Keystone IRQ controller IP driver
  3 + *
  4 + * Copyright (C) 2014 Texas Instruments, Inc.
  5 + * Author: Sajesh Kumar Saran <sajesh@ti.com>
  6 + * Grygorii Strashko <grygorii.strashko@ti.com>
  7 + *
  8 + * This program is free software; you can redistribute it and/or
  9 + * modify it under the terms of the GNU General Public License as
  10 + * published by the Free Software Foundation version 2.
  11 + *
  12 + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
  13 + * kind, whether express or implied; without even the implied warranty
  14 + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15 + * GNU General Public License for more details.
  16 + */
  17 +
  18 +#include <linux/irq.h>
  19 +#include <linux/bitops.h>
  20 +#include <linux/module.h>
  21 +#include <linux/moduleparam.h>
  22 +#include <linux/irqdomain.h>
  23 +#include <linux/irqchip/chained_irq.h>
  24 +#include <linux/of.h>
  25 +#include <linux/of_platform.h>
  26 +#include <linux/mfd/syscon.h>
  27 +#include <linux/regmap.h>
  28 +#include "irqchip.h"
  29 +
  30 +
  31 +/* The source ID bits start from 4 to 31 (total 28 bits)*/
  32 +#define BIT_OFS 4
  33 +#define KEYSTONE_N_IRQ (32 - BIT_OFS)
  34 +
  35 +struct keystone_irq_device {
  36 + struct device *dev;
  37 + struct irq_chip chip;
  38 + u32 mask;
  39 + int irq;
  40 + struct irq_domain *irqd;
  41 + struct regmap *devctrl_regs;
  42 + u32 devctrl_offset;
  43 +};
  44 +
  45 +static inline u32 keystone_irq_readl(struct keystone_irq_device *kirq)
  46 +{
  47 + int ret;
  48 + u32 val = 0;
  49 +
  50 + ret = regmap_read(kirq->devctrl_regs, kirq->devctrl_offset, &val);
  51 + if (ret < 0)
  52 + dev_dbg(kirq->dev, "irq read failed ret(%d)\n", ret);
  53 + return val;
  54 +}
  55 +
  56 +static inline void
  57 +keystone_irq_writel(struct keystone_irq_device *kirq, u32 value)
  58 +{
  59 + int ret;
  60 +
  61 + ret = regmap_write(kirq->devctrl_regs, kirq->devctrl_offset, value);
  62 + if (ret < 0)
  63 + dev_dbg(kirq->dev, "irq write failed ret(%d)\n", ret);
  64 +}
  65 +
  66 +static void keystone_irq_setmask(struct irq_data *d)
  67 +{
  68 + struct keystone_irq_device *kirq = irq_data_get_irq_chip_data(d);
  69 +
  70 + kirq->mask |= BIT(d->hwirq);
  71 + dev_dbg(kirq->dev, "mask %lu [%x]\n", d->hwirq, kirq->mask);
  72 +}
  73 +
  74 +static void keystone_irq_unmask(struct irq_data *d)
  75 +{
  76 + struct keystone_irq_device *kirq = irq_data_get_irq_chip_data(d);
  77 +
  78 + kirq->mask &= ~BIT(d->hwirq);
  79 + dev_dbg(kirq->dev, "unmask %lu [%x]\n", d->hwirq, kirq->mask);
  80 +}
  81 +
  82 +static void keystone_irq_ack(struct irq_data *d)
  83 +{
  84 + /* nothing to do here */
  85 +}
  86 +
  87 +static void keystone_irq_handler(unsigned irq, struct irq_desc *desc)
  88 +{
  89 + struct keystone_irq_device *kirq = irq_desc_get_handler_data(desc);
  90 + unsigned long pending;
  91 + int src, virq;
  92 +
  93 + dev_dbg(kirq->dev, "start irq %d\n", irq);
  94 +
  95 + chained_irq_enter(irq_desc_get_chip(desc), desc);
  96 +
  97 + pending = keystone_irq_readl(kirq);
  98 + keystone_irq_writel(kirq, pending);
  99 +
  100 + dev_dbg(kirq->dev, "pending 0x%lx, mask 0x%x\n", pending, kirq->mask);
  101 +
  102 + pending = (pending >> BIT_OFS) & ~kirq->mask;
  103 +
  104 + dev_dbg(kirq->dev, "pending after mask 0x%lx\n", pending);
  105 +
  106 + for (src = 0; src < KEYSTONE_N_IRQ; src++) {
  107 + if (BIT(src) & pending) {
  108 + virq = irq_find_mapping(kirq->irqd, src);
  109 + dev_dbg(kirq->dev, "dispatch bit %d, virq %d\n",
  110 + src, virq);
  111 + if (!virq)
  112 + dev_warn(kirq->dev, "sporious irq detected hwirq %d, virq %d\n",
  113 + src, virq);
  114 + generic_handle_irq(virq);
  115 + }
  116 + }
  117 +
  118 + chained_irq_exit(irq_desc_get_chip(desc), desc);
  119 +
  120 + dev_dbg(kirq->dev, "end irq %d\n", irq);
  121 +}
  122 +
  123 +static int keystone_irq_map(struct irq_domain *h, unsigned int virq,
  124 + irq_hw_number_t hw)
  125 +{
  126 + struct keystone_irq_device *kirq = h->host_data;
  127 +
  128 + irq_set_chip_data(virq, kirq);
  129 + irq_set_chip_and_handler(virq, &kirq->chip, handle_level_irq);
  130 + set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);
  131 + return 0;
  132 +}
  133 +
  134 +static struct irq_domain_ops keystone_irq_ops = {
  135 + .map = keystone_irq_map,
  136 + .xlate = irq_domain_xlate_onecell,
  137 +};
  138 +
  139 +static int keystone_irq_probe(struct platform_device *pdev)
  140 +{
  141 + struct device *dev = &pdev->dev;
  142 + struct device_node *np = dev->of_node;
  143 + struct keystone_irq_device *kirq;
  144 + int ret;
  145 +
  146 + if (np == NULL)
  147 + return -EINVAL;
  148 +
  149 + kirq = devm_kzalloc(dev, sizeof(*kirq), GFP_KERNEL);
  150 + if (!kirq)
  151 + return -ENOMEM;
  152 +
  153 + kirq->devctrl_regs =
  154 + syscon_regmap_lookup_by_phandle(np, "ti,syscon-dev");
  155 + if (IS_ERR(kirq->devctrl_regs))
  156 + return PTR_ERR(kirq->devctrl_regs);
  157 +
  158 + ret = of_property_read_u32_index(np, "ti,syscon-dev", 1,
  159 + &kirq->devctrl_offset);
  160 + if (ret) {
  161 + dev_err(dev, "couldn't read the devctrl_offset offset!\n");
  162 + return ret;
  163 + }
  164 +
  165 + kirq->irq = platform_get_irq(pdev, 0);
  166 + if (kirq->irq < 0) {
  167 + dev_err(dev, "no irq resource %d\n", kirq->irq);
  168 + return kirq->irq;
  169 + }
  170 +
  171 + kirq->dev = dev;
  172 + kirq->mask = ~0x0;
  173 + kirq->chip.name = "keystone-irq";
  174 + kirq->chip.irq_ack = keystone_irq_ack;
  175 + kirq->chip.irq_mask = keystone_irq_setmask;
  176 + kirq->chip.irq_unmask = keystone_irq_unmask;
  177 +
  178 + kirq->irqd = irq_domain_add_linear(np, KEYSTONE_N_IRQ,
  179 + &keystone_irq_ops, kirq);
  180 + if (!kirq->irqd) {
  181 + dev_err(dev, "IRQ domain registration failed\n");
  182 + return -ENODEV;
  183 + }
  184 +
  185 + platform_set_drvdata(pdev, kirq);
  186 +
  187 + irq_set_chained_handler(kirq->irq, keystone_irq_handler);
  188 + irq_set_handler_data(kirq->irq, kirq);
  189 +
  190 + /* clear all source bits */
  191 + keystone_irq_writel(kirq, ~0x0);
  192 +
  193 + dev_info(dev, "irqchip registered, nr_irqs %u\n", KEYSTONE_N_IRQ);
  194 +
  195 + return 0;
  196 +}
  197 +
  198 +static int keystone_irq_remove(struct platform_device *pdev)
  199 +{
  200 + struct keystone_irq_device *kirq = platform_get_drvdata(pdev);
  201 + int hwirq;
  202 +
  203 + for (hwirq = 0; hwirq < KEYSTONE_N_IRQ; hwirq++)
  204 + irq_dispose_mapping(irq_find_mapping(kirq->irqd, hwirq));
  205 +
  206 + irq_domain_remove(kirq->irqd);
  207 + return 0;
  208 +}
  209 +
  210 +static const struct of_device_id keystone_irq_dt_ids[] = {
  211 + { .compatible = "ti,keystone-irq", },
  212 + {},
  213 +};
  214 +MODULE_DEVICE_TABLE(of, keystone_irq_dt_ids);
  215 +
  216 +static struct platform_driver keystone_irq_device_driver = {
  217 + .probe = keystone_irq_probe,
  218 + .remove = keystone_irq_remove,
  219 + .driver = {
  220 + .name = "keystone_irq",
  221 + .owner = THIS_MODULE,
  222 + .of_match_table = of_match_ptr(keystone_irq_dt_ids),
  223 + }
  224 +};
  225 +
  226 +module_platform_driver(keystone_irq_device_driver);
  227 +
  228 +MODULE_AUTHOR("Texas Instruments");
  229 +MODULE_AUTHOR("Sajesh Kumar Saran");
  230 +MODULE_AUTHOR("Grygorii Strashko");
  231 +MODULE_DESCRIPTION("Keystone IRQ chip");
  232 +MODULE_LICENSE("GPL v2");