/* * *************************************************************************** * 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 #include #include #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; }