summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBraden Kell <bradenkell@google.com>2018-06-14 12:50:47 -0700
committerBraden Kell <bradenkell@google.com>2018-06-15 01:47:50 +0000
commit3ee242e4fe5ef4f1da7f1a1b44d804db242ddd67 (patch)
tree3638e9e90ad2537b0724e486ff9552a455e7cbec
parent97230d4da4b7cb0ee775faf861965686c26fbd09 (diff)
downloadpi-v4.4-3ee242e4fe5ef4f1da7f1a1b44d804db242ddd67.tar.gz
Patch aux clock driver to add interrupt controller
The auxiliary devices (UART1, SPI1, SPI2) share an IRQ status register. This patch allows the auxiliaries to be used at the same time by exposing an interrupt controller instead of having each device try to reserve the status register. A follow-up change will make the necessary device tree modifications to enable interrupt sharing. Author: Phil Elwell <pelwell@users.noreply.github.com> Patch: https://github.com/raspberrypi/linux/commit/ecb6033f19bcdd44cfee7891700dda085c61148f Bug: 110147922 Test: RPi boots, serial console works Change-Id: Ia512b19bdc702e1b7b1b93855f03205942171bbb
-rw-r--r--drivers/clk/bcm/clk-bcm2835-aux.c120
1 files changed, 120 insertions, 0 deletions
diff --git a/drivers/clk/bcm/clk-bcm2835-aux.c b/drivers/clk/bcm/clk-bcm2835-aux.c
index 3a177ade6e6c..925e38625137 100644
--- a/drivers/clk/bcm/clk-bcm2835-aux.c
+++ b/drivers/clk/bcm/clk-bcm2835-aux.c
@@ -17,17 +17,107 @@
#include <linux/clk/bcm2835.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/of_irq.h>
#include <dt-bindings/clock/bcm2835-aux.h>
#define BCM2835_AUXIRQ 0x00
#define BCM2835_AUXENB 0x04
+#define BCM2835_AUXIRQ_NUM_IRQS 3
+
+#define BCM2835_AUXIRQ_UART_IRQ 0
+#define BCM2835_AUXIRQ_SPI1_IRQ 1
+#define BCM2835_AUXIRQ_SPI2_IRQ 2
+
+#define BCM2835_AUXIRQ_UART_MASK 0x01
+#define BCM2835_AUXIRQ_SPI1_MASK 0x02
+#define BCM2835_AUXIRQ_SPI2_MASK 0x04
+
+#define BCM2835_AUXIRQ_ALL_MASK \
+ (BCM2835_AUXIRQ_UART_MASK | \
+ BCM2835_AUXIRQ_SPI1_MASK | \
+ BCM2835_AUXIRQ_SPI2_MASK)
+
+struct auxirq_state {
+ void __iomem *status;
+ u32 enables;
+ struct irq_domain *domain;
+ struct regmap *local_regmap;
+};
+
+static struct auxirq_state auxirq __read_mostly;
+
+static irqreturn_t bcm2835_auxirq_handler(int irq, void *dev_id)
+{
+ u32 stat = readl_relaxed(auxirq.status);
+ u32 masked = stat & auxirq.enables;
+
+ if (masked & BCM2835_AUXIRQ_UART_MASK)
+ generic_handle_irq(irq_linear_revmap(auxirq.domain,
+ BCM2835_AUXIRQ_UART_IRQ));
+
+ if (masked & BCM2835_AUXIRQ_SPI1_MASK)
+ generic_handle_irq(irq_linear_revmap(auxirq.domain,
+ BCM2835_AUXIRQ_SPI1_IRQ));
+
+ if (masked & BCM2835_AUXIRQ_SPI2_MASK)
+ generic_handle_irq(irq_linear_revmap(auxirq.domain,
+ BCM2835_AUXIRQ_SPI2_IRQ));
+
+ return (masked & BCM2835_AUXIRQ_ALL_MASK) ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int bcm2835_auxirq_xlate(struct irq_domain *d,
+ struct device_node *ctrlr,
+ const u32 *intspec, unsigned int intsize,
+ unsigned long *out_hwirq,
+ unsigned int *out_type)
+{
+ if (WARN_ON(intsize != 1))
+ return -EINVAL;
+
+ if (WARN_ON(intspec[0] >= BCM2835_AUXIRQ_NUM_IRQS))
+ return -EINVAL;
+
+ *out_hwirq = intspec[0];
+ *out_type = IRQ_TYPE_NONE;
+ return 0;
+}
+
+static void bcm2835_auxirq_mask(struct irq_data *data)
+{
+ irq_hw_number_t hwirq = irqd_to_hwirq(data);
+
+ auxirq.enables &= ~(1 << hwirq);
+}
+
+static void bcm2835_auxirq_unmask(struct irq_data *data)
+{
+ irq_hw_number_t hwirq = irqd_to_hwirq(data);
+
+ auxirq.enables |= (1 << hwirq);
+}
+
+static struct irq_chip bcm2835_auxirq_chip = {
+ .name = "bcm2835-auxirq",
+ .irq_mask = bcm2835_auxirq_mask,
+ .irq_unmask = bcm2835_auxirq_unmask,
+};
+
+static const struct irq_domain_ops bcm2835_auxirq_ops = {
+ .xlate = bcm2835_auxirq_xlate//irq_domain_xlate_onecell
+};
+
static int bcm2835_aux_clk_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
struct clk_onecell_data *onecell;
const char *parent;
struct clk *parent_clk;
+ int parent_irq;
struct resource *res;
void __iomem *reg, *gate;
@@ -41,6 +131,36 @@ static int bcm2835_aux_clk_probe(struct platform_device *pdev)
if (IS_ERR(reg))
return PTR_ERR(reg);
+ parent_irq = irq_of_parse_and_map(node, 0);
+ if (parent_irq) {
+ int ret;
+ int i;
+
+ /* Manage the AUX irq as well */
+ auxirq.status = reg + BCM2835_AUXIRQ;
+ auxirq.domain = irq_domain_add_linear(node,
+ BCM2835_AUXIRQ_NUM_IRQS,
+ &bcm2835_auxirq_ops,
+ NULL);
+ if (!auxirq.domain)
+ return -ENXIO;
+
+ for (i = 0; i < BCM2835_AUXIRQ_NUM_IRQS; i++) {
+ unsigned int irq = irq_create_mapping(auxirq.domain, i);
+
+ if (irq == 0)
+ return -ENXIO;
+
+ irq_set_chip_and_handler(irq, &bcm2835_auxirq_chip,
+ handle_level_irq);
+ }
+
+ ret = devm_request_irq(dev, parent_irq, bcm2835_auxirq_handler,
+ 0, "bcm2835-auxirq", NULL);
+ if (ret)
+ return ret;
+ }
+
onecell = devm_kmalloc(dev, sizeof(*onecell), GFP_KERNEL);
if (!onecell)
return -ENOMEM;