summaryrefslogtreecommitdiff
path: root/drivers/marvell/icu.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/marvell/icu.c')
-rw-r--r--drivers/marvell/icu.c143
1 files changed, 143 insertions, 0 deletions
diff --git a/drivers/marvell/icu.c b/drivers/marvell/icu.c
new file mode 100644
index 00000000..12e95048
--- /dev/null
+++ b/drivers/marvell/icu.c
@@ -0,0 +1,143 @@
+/*
+* ***************************************************************************
+* Copyright (C) 2016 Marvell International Ltd.
+* ***************************************************************************
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*
+* Redistributions of source code must retain the above copyright notice, this
+* list of conditions and the following disclaimer.
+*
+* Redistributions in binary form must reproduce the above copyright notice,
+* this list of conditions and the following disclaimer in the documentation
+* and/or other materials provided with the distribution.
+*
+* Neither the name of Marvell nor the names of its contributors may be used
+* to endorse or promote products derived from this software without specific
+* prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+* POSSIBILITY OF SUCH DAMAGE.
+*
+***************************************************************************
+*/
+
+#include <plat_def.h>
+#include <mmio.h>
+#include <icu.h>
+
+#define ICU_SET_SPI_AL(x) (0x10 + (0x10 * x))
+#define ICU_SET_SPI_AH(x) (0x14 + (0x10 * x))
+#define ICU_CLR_SPI_AL(x) (0x18 + (0x10 * x))
+#define ICU_CLR_SPI_AH(x) (0x1c + (0x10 * x))
+#define ICU_INT_CFG(x) (0x100 + 4 * x)
+
+#define ICU_INT_ENABLE_OFFSET (24)
+#define ICU_IS_EDGE_OFFSET (28)
+#define ICU_GROUP_OFFSET (29)
+
+enum icu_group {
+ ICU_GRP_NSR = 0,
+ ICU_GRP_SR = 1,
+ ICU_GRP_LPI = 2,
+ ICU_GRP_VLPI = 3,
+ ICU_GRP_SEI = 4,
+ ICU_GRP_REI = 5,
+ ICU_GRP_MAX,
+};
+
+struct icu_msi {
+ enum icu_group group;
+ uintptr_t set_spi_addr;
+ uintptr_t clr_spi_addr;
+};
+
+#define MAX_ICU_IRQS 207
+
+/* Allocate the MSI address per interrupt group,
+ * unsopprted groups get NULL address */
+static struct icu_msi msi_addr[] = {
+ {ICU_GRP_NSR, 0xf03f0040, 0xf03f0048}, /* Non secure interrupts*/
+ {ICU_GRP_SR, 0, 0x0}, /* Secure interrupts */
+ {ICU_GRP_LPI, 0x0, 0x0}, /* LPI interrupts */
+ {ICU_GRP_VLPI, 0x0, 0x0}, /* Virtual LPI interrupts */
+ {ICU_GRP_SEI, 0xf03f0230, 0xf03f0230}, /* System error interrupts */
+ {ICU_GRP_REI, 0xf03f0270, 0xf03f0270}, /* RAM error interrupts */
+};
+
+static void icu_clear_irq(uintptr_t icu_base, int nr)
+{
+ mmio_write_32(icu_base + ICU_INT_CFG(nr), 0);
+}
+
+static void icu_set_irq(uintptr_t icu_base, const struct icu_irq *irq,
+ uint32_t spi_base, enum icu_group group)
+{
+ uint32_t icu_int;
+
+ icu_int = (irq->spi_id + spi_base) | (1 << ICU_INT_ENABLE_OFFSET);
+ icu_int |= irq->is_edge << ICU_IS_EDGE_OFFSET;
+ icu_int |= group << ICU_GROUP_OFFSET;
+
+ mmio_write_32(icu_base + ICU_INT_CFG(irq->icu_id), icu_int);
+}
+
+/*
+ * This function uses 2 spi values to initialize the ICU
+ * spi_base: used to set the base of the SPI id in the MSI message
+ * generated by the ICU. AP806-Z1 required spi_base=64 while
+ * AP806-A0 uses spi_base=0
+ * spi_offset: used to shift the multi instance interrupts between CP-0
+ * and CP-1
+ */
+void icu_init(int cp_index, int spi_base, int spi_offset,
+ const struct icu_config *config)
+{
+ int i;
+ const struct icu_irq *irq;
+ struct icu_msi *msi;
+ uintptr_t icu_base = MVEBU_ICU_REG_BASE(cp_index);
+
+ /* Set the addres for SET_SPI and CLR_SPI registers in AP */
+ msi = msi_addr;
+ for (i = 0; i < ICU_GRP_MAX; i++, msi++) {
+ mmio_write_32(icu_base + ICU_SET_SPI_AL(msi->group), msi->set_spi_addr & 0xFFFFFFFF);
+ mmio_write_32(icu_base + ICU_SET_SPI_AH(msi->group), msi->set_spi_addr >> 32);
+ mmio_write_32(icu_base + ICU_CLR_SPI_AL(msi->group), msi->clr_spi_addr & 0xFFFFFFFF);
+ mmio_write_32(icu_base + ICU_CLR_SPI_AH(msi->group), msi->clr_spi_addr >> 32);
+ }
+
+ /* Mask all ICU interrupts */
+ for (i = 0; i < MAX_ICU_IRQS; i++)
+ icu_clear_irq(icu_base, i);
+
+ /* Configure the ICU interrupt lines */
+ /* Multi instance interrupts use different SPI ID for CP-1*/
+ irq = config->ns_multi.map;
+ for (i = 0; i < config->ns_multi.size; i++, irq++)
+ icu_set_irq(icu_base, irq, spi_base + spi_offset, ICU_GRP_NSR);
+
+ irq = config->ns_single.map;
+ for (i = 0; i < config->ns_single.size; i++, irq++)
+ icu_set_irq(icu_base, irq, spi_base, ICU_GRP_NSR);
+
+ irq = config->sei.map;
+ for (i = 0; i < config->sei.size; i++, irq++)
+ icu_set_irq(icu_base, irq, spi_base, ICU_GRP_SEI);
+
+ irq = config->rei.map;
+ for (i = 0; i < config->rei.size; i++, irq++)
+ icu_set_irq(icu_base, irq, spi_base, ICU_GRP_REI);
+
+ return;
+}