diff options
Diffstat (limited to 'drivers/marvell/icu.c')
-rw-r--r-- | drivers/marvell/icu.c | 143 |
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; +} |