]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - rpmsg/rpmsg.git/commitdiff
irqchip/pruss-intc: Add support for ICSSG INTC on K3 AM65x SoCs
authorSuman Anna <s-anna@ti.com>
Thu, 21 Feb 2019 02:06:33 +0000 (20:06 -0600)
committerSuman Anna <s-anna@ti.com>
Mon, 25 Feb 2019 19:15:11 +0000 (13:15 -0600)
The K3 AM65x SoCs have the next generation of the PRU-ICSS IP, commonly
called ICSSG. The PRUSS INTC present within the ICSSG supports more
System Events (160 vs 64), more Interrupt Channels and Host Interrupts
(20 vs 10) compared to the current generation PRUSS INTC instances.

Enhance the PRUSS INTC driver to add support for this ICSSG INTC
instance. This support is adding using SoC-specific data and updating
the code to use this data instead of the current hard-coded macros.
The INTC config structure is updated to use the higher events and
channels on all SoCs, while limiting the actual processing to only
the relevant number of events/channels/interrupts.

Signed-off-by: Suman Anna <s-anna@ti.com>
drivers/irqchip/irq-pruss-intc.c
include/linux/pruss_driver.h

index 022b4cfa811a87e1b7a3188aa2dac2f10cac14b9..45b2b69e1440aa6d893892a5aca91b0ead0b6c98 100644 (file)
 #define PRU_INTC_HIEISR                0x0034
 #define PRU_INTC_HIDISR                0x0038
 #define PRU_INTC_GPIR          0x0080
+#define PRU_INTC_SRSR(x)       (0x0200 + (x) * 4)
 #define PRU_INTC_SRSR0         0x0200
 #define PRU_INTC_SRSR1         0x0204
+#define PRU_INTC_SECR(x)       (0x0280 + (x) * 4)
 #define PRU_INTC_SECR0         0x0280
 #define PRU_INTC_SECR1         0x0284
+#define PRU_INTC_ESR(x)                (0x0300 + (x) * 4)
 #define PRU_INTC_ESR0          0x0300
 #define PRU_INTC_ESR1          0x0304
+#define PRU_INTC_ECR(x)                (0x0380 + (x) * 4)
 #define PRU_INTC_ECR0          0x0380
 #define PRU_INTC_ECR1          0x0384
 #define PRU_INTC_CMR(x)                (0x0400 + (x) * 4)
 #define PRU_INTC_HMR(x)                (0x0800 + (x) * 4)
 #define PRU_INTC_HIPIR(x)      (0x0900 + (x) * 4)
-#define PRU_INTC_SIPR0         0x0D00
-#define PRU_INTC_SIPR1         0x0D04
-#define PRU_INTC_SITR0         0x0D80
-#define PRU_INTC_SITR1         0x0D84
+#define PRU_INTC_SIPR(x)       (0x0D00 + (x) * 4)
+#define PRU_INTC_SITR(x)       (0x0D80 + (x) * 4)
 #define PRU_INTC_HINLR(x)      (0x1100 + (x) * 4)
 #define PRU_INTC_HIER          0x1500
 
@@ -61,9 +63,13 @@ static const char * const irq_names[] = {
 
 /**
  * struct pruss_intc_match_data - match data to handle SoC variations
+ * @num_system_events: number of input system events handled by the PRUSS INTC
+ * @num_host_intrs: number of host interrupts supported by the PRUSS INTC
  * @no_host7_intr: flag denoting the absence of host7 interrupt into MPU
  */
 struct pruss_intc_match_data {
+       u8 num_system_events;
+       u8 num_host_intrs;
        bool no_host7_intr;
 };
 
@@ -74,6 +80,7 @@ struct pruss_intc_match_data {
  * @base: base virtual address of INTC register space
  * @irqchip: irq chip for this interrupt controller
  * @domain: irq domain for this interrupt controller
+ * @data: cached PRUSS INTC IP configuration data
  * @config_map: stored INTC configuration mapping data
  * @lock: mutex to serialize access to INTC
  * @host_mask: indicate which HOST IRQs are enabled
@@ -84,6 +91,7 @@ struct pruss_intc {
        void __iomem *base;
        struct irq_chip *irqchip;
        struct irq_domain *domain;
+       const struct pruss_intc_match_data *data;
        struct pruss_intc_config config_map;
        struct mutex lock; /* PRUSS INTC lock */
        u32 host_mask;
@@ -106,7 +114,7 @@ static int pruss_intc_check_write(struct pruss_intc *intc, unsigned int reg,
        if (!intc)
                return -EINVAL;
 
-       if (sysevent >= MAX_PRU_SYS_EVENTS)
+       if (sysevent >= intc->data->num_system_events)
                return -EINVAL;
 
        pruss_intc_write_reg(intc, reg, sysevent);
@@ -157,16 +165,24 @@ int pruss_intc_configure(struct pruss *pruss,
 {
        struct device *dev = pruss->dev;
        struct pruss_intc *intc = to_pruss_intc(pruss);
-       int i, idx, ret;
+       int i, idx;
        s8 ch, host;
-       u64 sysevt_mask = 0;
+       u32 num_events = intc->data->num_system_events;
+       u32 num_intrs = intc->data->num_host_intrs;
+       u32 num_regs = DIV_ROUND_UP(num_events, 32);
+       u32 *sysevt_mask = NULL;
        u32 ch_mask = 0;
        u32 host_mask = 0;
+       int ret = 0;
        u32 val;
 
        if (!intc)
                return -EINVAL;
 
+       sysevt_mask = kcalloc(num_regs, sizeof(*sysevt_mask), GFP_KERNEL);
+       if (!sysevt_mask)
+               return -ENOMEM;
+
        mutex_lock(&intc->lock);
 
        /*
@@ -174,7 +190,7 @@ int pruss_intc_configure(struct pruss *pruss,
         * for 4 events, with each event occupying the lower nibble in
         * a register byte address in little-endian fashion
         */
-       for (i = 0; i < ARRAY_SIZE(intc_config->sysev_to_ch); i++) {
+       for (i = 0; i < num_events; i++) {
                ch = intc_config->sysev_to_ch[i];
                if (ch < 0)
                        continue;
@@ -193,7 +209,7 @@ int pruss_intc_configure(struct pruss *pruss,
                val = pruss_intc_read_reg(intc, PRU_INTC_CMR(idx));
                val |= ch << ((i & 3) * 8);
                pruss_intc_write_reg(intc, PRU_INTC_CMR(idx), val);
-               sysevt_mask |= BIT_ULL(i);
+               sysevt_mask[i / 32] |= BIT(i % 32);
                ch_mask |= BIT(ch);
 
                dev_dbg(dev, "SYSEV%d -> CH%d (CMR%d 0x%08x)\n", i, ch, idx,
@@ -205,7 +221,7 @@ int pruss_intc_configure(struct pruss *pruss,
         * 4 channels, with each channel occupying the lower nibble in
         * a register byte address in little-endian fashion
         */
-       for (i = 0; i < ARRAY_SIZE(intc_config->ch_to_host); i++) {
+       for (i = 0; i < num_intrs; i++) {
                host = intc_config->ch_to_host[i];
                if (host < 0)
                        continue;
@@ -241,17 +257,25 @@ int pruss_intc_configure(struct pruss *pruss,
                        pruss_intc_read_reg(intc, PRU_INTC_HMR(idx)));
        }
 
-       dev_info(dev, "configured system_events = 0x%016llx intr_channels = 0x%08x host_intr = 0x%08x\n",
-                sysevt_mask, ch_mask, host_mask);
+       if (num_events == MAX_PRU_SYS_EVENTS) {
+               dev_info(dev, "configured system_events[63-0] = 0x%08x.%08x",
+                        sysevt_mask[1], sysevt_mask[0]);
+       } else if (num_events == MAX_PRU_SYS_EVENTS_K3) {
+               dev_info(dev, "configured system_events[159-0] = 0x%08x.%08x.%08x.%08x.%08x",
+                        sysevt_mask[4], sysevt_mask[3],  sysevt_mask[2],
+                        sysevt_mask[1],  sysevt_mask[0]);
+       }
+       dev_info(dev, "configured intr_channels = 0x%08x host_intr = 0x%08x\n",
+                ch_mask, host_mask);
 
        /* enable system events, writing 0 has no-effect */
-       pruss_intc_write_reg(intc, PRU_INTC_ESR0, lower_32_bits(sysevt_mask));
-       pruss_intc_write_reg(intc, PRU_INTC_SECR0, lower_32_bits(sysevt_mask));
-       pruss_intc_write_reg(intc, PRU_INTC_ESR1, upper_32_bits(sysevt_mask));
-       pruss_intc_write_reg(intc, PRU_INTC_SECR1, upper_32_bits(sysevt_mask));
+       for (i = 0; i < num_regs; i++) {
+               pruss_intc_write_reg(intc, PRU_INTC_ESR(i), sysevt_mask[i]);
+               pruss_intc_write_reg(intc, PRU_INTC_SECR(i), sysevt_mask[i]);
+       }
 
        /* enable host interrupts */
-       for (i = 0; i < MAX_PRU_HOST_INT; i++) {
+       for (i = 0; i < num_intrs; i++) {
                if (host_mask & BIT(i))
                        pruss_intc_write_reg(intc, PRU_INTC_HIEISR, i);
        }
@@ -261,11 +285,9 @@ int pruss_intc_configure(struct pruss *pruss,
 
        intc->host_mask |= host_mask;
 
-       mutex_unlock(&intc->lock);
-       return 0;
-
 unlock:
        mutex_unlock(&intc->lock);
+       kfree(sysevt_mask);
        return ret;
 }
 EXPORT_SYMBOL_GPL(pruss_intc_configure);
@@ -286,25 +308,32 @@ int pruss_intc_unconfigure(struct pruss *pruss,
        struct pruss_intc *intc = to_pruss_intc(pruss);
        int i;
        s8 ch, host;
-       u64 sysevt_mask = 0;
+       u32 num_events = intc->data->num_system_events;
+       u32 num_intrs = intc->data->num_host_intrs;
+       u32 num_regs = DIV_ROUND_UP(num_events, 32);
+       u32 *sysevt_mask = NULL;
        u32 host_mask = 0;
 
        if (!intc)
                return -EINVAL;
 
+       sysevt_mask = kcalloc(num_regs, sizeof(*sysevt_mask), GFP_KERNEL);
+       if (!sysevt_mask)
+               return -ENOMEM;
+
        mutex_lock(&intc->lock);
 
-       for (i = 0; i < ARRAY_SIZE(intc_config->sysev_to_ch); i++) {
+       for (i = 0; i < num_events; i++) {
                ch = intc_config->sysev_to_ch[i];
                if (ch < 0)
                        continue;
 
                /* mark sysevent free in global map */
                intc->config_map.sysev_to_ch[i] = -1;
-               sysevt_mask |= BIT_ULL(i);
+               sysevt_mask[i / 32] |= BIT(i % 32);
        }
 
-       for (i = 0; i < ARRAY_SIZE(intc_config->ch_to_host); i++) {
+       for (i = 0; i < num_intrs; i++) {
                host = intc_config->ch_to_host[i];
                if (host < 0)
                        continue;
@@ -314,15 +343,22 @@ int pruss_intc_unconfigure(struct pruss *pruss,
                host_mask |= BIT(host);
        }
 
-       dev_info(dev, "unconfigured system_events = 0x%016llx host_intr = 0x%08x\n",
-                sysevt_mask, host_mask);
+       if (num_events == MAX_PRU_SYS_EVENTS) {
+               dev_info(dev, "unconfigured system_events[63-0] = 0x%08x.%08x",
+                        sysevt_mask[1], sysevt_mask[0]);
+       } else if (num_events == MAX_PRU_SYS_EVENTS_K3) {
+               dev_info(dev, "unconfigured system_events[159-0] = 0x%08x.%08x.%08x.%08x.%08x",
+                        sysevt_mask[4], sysevt_mask[3],  sysevt_mask[2],
+                        sysevt_mask[1],  sysevt_mask[0]);
+       }
+       dev_info(dev, "unconfigured host_intr = 0x%08x\n", host_mask);
 
-       /* disable system events, writing 0 has no-effect */
-       pruss_intc_write_reg(intc, PRU_INTC_ECR0, lower_32_bits(sysevt_mask));
-       pruss_intc_write_reg(intc, PRU_INTC_ECR1, upper_32_bits(sysevt_mask));
-       /* clear any pending status */
-       pruss_intc_write_reg(intc, PRU_INTC_SECR0, lower_32_bits(sysevt_mask));
-       pruss_intc_write_reg(intc, PRU_INTC_SECR1, upper_32_bits(sysevt_mask));
+       for (i = 0; i < num_regs; i++) {
+               /* disable system events, writing 0 has no-effect */
+               pruss_intc_write_reg(intc, PRU_INTC_ECR(i), sysevt_mask[i]);
+               /* clear any pending status */
+               pruss_intc_write_reg(intc, PRU_INTC_SECR(i), sysevt_mask[i]);
+       }
 
        /* disable host interrupts */
        for (i = 0; i < MAX_PRU_HOST_INT; i++) {
@@ -332,6 +368,7 @@ int pruss_intc_unconfigure(struct pruss *pruss,
 
        intc->host_mask &= ~host_mask;
        mutex_unlock(&intc->lock);
+       kfree(sysevt_mask);
 
        return 0;
 }
@@ -340,21 +377,26 @@ EXPORT_SYMBOL_GPL(pruss_intc_unconfigure);
 static void pruss_intc_init(struct pruss_intc *intc)
 {
        int i;
+       int num_chnl_map_regs = DIV_ROUND_UP(intc->data->num_system_events, 4);
+       int num_host_intr_regs = DIV_ROUND_UP(intc->data->num_host_intrs, 4);
+       int num_event_type_regs =
+                       DIV_ROUND_UP(intc->data->num_system_events, 32);
 
-       /* configure polarity to active high for all system interrupts */
-       pruss_intc_write_reg(intc, PRU_INTC_SIPR0, 0xffffffff);
-       pruss_intc_write_reg(intc, PRU_INTC_SIPR1, 0xffffffff);
-
-       /* configure type to pulse interrupt for all system interrupts */
-       pruss_intc_write_reg(intc, PRU_INTC_SITR0, 0);
-       pruss_intc_write_reg(intc, PRU_INTC_SITR1, 0);
+       /*
+        * configure polarity (SIPR register) to active high and
+        * type (SITR register) to pulse interrupt for all system events
+        */
+       for (i = 0; i < num_event_type_regs; i++) {
+               pruss_intc_write_reg(intc, PRU_INTC_SIPR(i), 0xffffffff);
+               pruss_intc_write_reg(intc, PRU_INTC_SITR(i), 0);
+       }
 
-       /* clear all 16 interrupt channel map registers */
-       for (i = 0; i < 16; i++)
+       /* clear all interrupt channel map registers, 4 events per register */
+       for (i = 0; i < num_chnl_map_regs; i++)
                pruss_intc_write_reg(intc, PRU_INTC_CMR(i), 0);
 
-       /* clear all 3 host interrupt map registers */
-       for (i = 0; i < 3; i++)
+       /* clear all host interrupt map registers, 4 channels per register */
+       for (i = 0; i < num_host_intr_regs; i++)
                pruss_intc_write_reg(intc, PRU_INTC_HMR(i), 0);
 }
 
@@ -509,13 +551,19 @@ static int pruss_intc_probe(struct platform_device *pdev)
        int i, irq;
        const struct pruss_intc_match_data *data;
        bool skip_host7;
+       u8 max_system_events;
 
        data = of_device_get_match_data(dev);
-       skip_host7 = data ? data->no_host7_intr : false;
+       if (!data)
+               return -ENODEV;
+
+       skip_host7 = data->no_host7_intr;
+       max_system_events = data->num_system_events;
 
        intc = devm_kzalloc(dev, sizeof(*intc), GFP_KERNEL);
        if (!intc)
                return -ENOMEM;
+       intc->data = data;
        platform_set_drvdata(pdev, intc);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -552,8 +600,7 @@ static int pruss_intc_probe(struct platform_device *pdev)
        irqchip->name = dev_name(dev);
        intc->irqchip = irqchip;
 
-       /* always 64 events */
-       intc->domain = irq_domain_add_linear(dev->of_node, MAX_PRU_SYS_EVENTS,
+       intc->domain = irq_domain_add_linear(dev->of_node, max_system_events,
                                             &pruss_intc_irq_domain_ops, intc);
        if (!intc->domain)
                return -ENOMEM;
@@ -584,10 +631,11 @@ fail_irq:
 static int pruss_intc_remove(struct platform_device *pdev)
 {
        struct pruss_intc *intc = platform_get_drvdata(pdev);
+       u8 max_system_events = intc->data->num_system_events;
        unsigned int hwirq;
 
        if (intc->domain) {
-               for (hwirq = 0; hwirq < MAX_PRU_SYS_EVENTS; hwirq++)
+               for (hwirq = 0; hwirq < max_system_events; hwirq++)
                        irq_dispose_mapping(irq_find_mapping(intc->domain,
                                                             hwirq));
                irq_domain_remove(intc->domain);
@@ -596,30 +644,44 @@ static int pruss_intc_remove(struct platform_device *pdev)
        return 0;
 }
 
-static const struct pruss_intc_match_data am437x_pruss_intc_data = {
-       .no_host7_intr = true,
+static const struct pruss_intc_match_data am335x_am57xx_pruss_intc_data = {
+       .num_system_events = 64,
+       .num_host_intrs = 10,
+       .no_host7_intr = false,
 };
 
-static const struct pruss_intc_match_data k2g_pruss_intc_data = {
+static const struct pruss_intc_match_data am437x_k2g_pruss_intc_data = {
+       .num_system_events = 64,
+       .num_host_intrs = 10,
        .no_host7_intr = true,
 };
 
+static const struct pruss_intc_match_data am65x_icssg_intc_data = {
+       .num_system_events = 160,
+       .num_host_intrs = 20,
+       .no_host7_intr = false,
+};
+
 static const struct of_device_id pruss_intc_of_match[] = {
        {
                .compatible = "ti,am3356-pruss-intc",
-               .data = NULL,
+               .data = &am335x_am57xx_pruss_intc_data,
        },
        {
                .compatible = "ti,am4376-pruss-intc",
-               .data = &am437x_pruss_intc_data,
+               .data = &am437x_k2g_pruss_intc_data,
        },
        {
                .compatible = "ti,am5728-pruss-intc",
-               .data = NULL,
+               .data = &am335x_am57xx_pruss_intc_data,
        },
        {
                .compatible = "ti,k2g-pruss-intc",
-               .data = &k2g_pruss_intc_data,
+               .data = &am437x_k2g_pruss_intc_data,
+       },
+       {
+               .compatible = "ti,am654-icssg-intc",
+               .data = &am65x_icssg_intc_data,
        },
        { /* sentinel */ },
 };
index 8a691dad6455dc6cae110831f425f6f896ed2256..0a6138aeba8dd2d3e50082a22b472a3d31d918f7 100644 (file)
 
 /* maximum number of system events */
 #define MAX_PRU_SYS_EVENTS     64
+#define MAX_PRU_SYS_EVENTS_K3  160
 
 /* maximum number of interrupt channels */
 #define MAX_PRU_CHANNELS       10
+#define MAX_PRU_CHANNELS_K3    20
 
 /* minimum starting host interrupt number for MPU */
 #define MIN_PRU_HOST_INT       2
 
 /* maximum number of host interrupts */
 #define MAX_PRU_HOST_INT       10
+#define MAX_PRU_HOST_INT_K3    20
 
 /**
  * struct pruss_intc_config - INTC configuration info
@@ -29,8 +32,8 @@
  * @ch_to_host: interrupt channel to host interrupt information
  */
 struct pruss_intc_config {
-       s8 sysev_to_ch[MAX_PRU_SYS_EVENTS];
-       s8 ch_to_host[MAX_PRU_CHANNELS];
+       s8 sysev_to_ch[MAX_PRU_SYS_EVENTS_K3];
+       s8 ch_to_host[MAX_PRU_CHANNELS_K3];
 };
 
 /**