123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576 |
- /*
- * Copyright (c) 2006-2022, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2013-07-20 Bernard first version
- * 2014-04-03 Grissiom many enhancements
- * 2018-11-22 Jesven add rt_hw_ipi_send()
- * add rt_hw_ipi_handler_install()
- * 2022-08-24 GuEe-GUI add pic support
- * 2022-11-07 GuEe-GUI add v2m support
- */
- #include <rthw.h>
- #include <rtthread.h>
- #include <rtdevice.h>
- #define DBG_TAG "pic.gicv2"
- #define DBG_LVL DBG_INFO
- #include <rtdbg.h>
- #include <cpuport.h>
- #include <ioremap.h>
- #include "pic-gicv2.h"
- #include "pic-gic-common.h"
- #define GIC_CPU_IMAX 8
- #define raw_to_gicv2(raw) rt_container_of(raw, struct gicv2, parent)
- static rt_bool_t needs_rmw_access = RT_FALSE;
- static int _gicv2_nr = 0, _init_cpu_id = 0;
- static struct gicv2 _gicv2_list[RT_PIC_ARM_GIC_MAX_NR] = {};
- static rt_bool_t _gicv2_eoi_mode_ns = RT_FALSE;
- static rt_uint8_t _gicv2_cpumask_map[GIC_CPU_IMAX] =
- {
- [0 ... GIC_CPU_IMAX - 1] = 0xff,
- };
- static rt_uint8_t gicv2_cpumask_map(struct gicv2 *gic)
- {
- rt_uint32_t mask, i;
- for (i = mask = 0; i < 32; i += 4)
- {
- mask = HWREG32(gic->dist_base + GIC_DIST_TARGET + i);
- mask |= mask >> 16;
- mask |= mask >> 8;
- if (mask)
- {
- break;
- }
- }
- return mask;
- }
- static void gicv2_dist_init(struct gicv2 *gic)
- {
- void *base = gic->dist_base;
- rt_uint32_t i;
- rt_uint32_t cpumask = gicv2_cpumask_map(gic);
- gic->max_irq = HWREG32(base + GIC_DIST_TYPE) & 0x1f;
- gic->max_irq = (gic->max_irq + 1) * 32;
- /*
- * The GIC only supports up to 1020 interrupt sources.
- * Limit this to either the architected maximum, or the
- * platform maximum.
- */
- if (gic->max_irq > 1020)
- {
- gic->max_irq = 1020;
- }
- LOG_D("Max irq = %d", gic->max_irq);
- HWREG32(base + GIC_DIST_CTRL) = GICD_DISABLE;
- /* Set all global (unused) interrupts to this CPU only. */
- cpumask |= cpumask << 8;
- cpumask |= cpumask << 16;
- for (i = 32; i < gic->max_irq; i += 4)
- {
- HWREG32(base + GIC_DIST_TARGET + i * 4 / 4) = cpumask;
- }
- gic_common_dist_config(base, gic->max_irq, RT_NULL, RT_NULL);
- HWREG32(base + GIC_DIST_CTRL) = GICD_ENABLE;
- }
- static void gicv2_cpu_init(struct gicv2 *gic)
- {
- rt_uint32_t cpumask;
- void *base = gic->cpu_base;
- rt_uint32_t config = GICC_ENABLE;
- int cpu_id = _init_cpu_id = rt_hw_cpu_id();
- cpumask = gicv2_cpumask_map(gic);
- _gicv2_cpumask_map[cpu_id] = cpumask;
- /*
- * Clear our mask from the other map entries in case they're
- * still undefined.
- */
- for (int i = 0; i < RT_ARRAY_SIZE(_gicv2_cpumask_map); ++i)
- {
- if (i != cpu_id)
- {
- _gicv2_cpumask_map[i] &= ~cpumask;
- }
- }
- gic_common_cpu_config(gic->dist_base, 32, RT_NULL, RT_NULL);
- HWREG32(base + GIC_CPU_PRIMASK) = GICC_INT_PRI_THRESHOLD;
- HWREG32(base + GIC_CPU_BINPOINT) = 0x7;
- #ifdef ARCH_SUPPORT_HYP
- _gicv2_eoi_mode_ns = RT_TRUE;
- #endif
- if (_gicv2_eoi_mode_ns)
- {
- config |= GIC_CPU_CTRL_EOI_MODE_NS;
- }
- HWREG32(base + GIC_CPU_CTRL) = config;
- }
- static rt_err_t gicv2_irq_init(struct rt_pic *pic)
- {
- gicv2_cpu_init(rt_container_of(pic, struct gicv2, parent));
- return RT_EOK;
- }
- static void gicv2_irq_ack(struct rt_pic_irq *pirq)
- {
- int hwirq = pirq->hwirq;
- struct gicv2 *gic = raw_to_gicv2(pirq->pic);
- if (!_gicv2_eoi_mode_ns)
- {
- HWREG32(gic->dist_base + GIC_DIST_PENDING_CLEAR + hwirq / 32 * 4) = 1U << (hwirq % 32);
- }
- HWREG32(gic->cpu_base + GIC_CPU_EOI) = hwirq;
- }
- static void gicv2_irq_mask(struct rt_pic_irq *pirq)
- {
- int hwirq = pirq->hwirq;
- struct gicv2 *gic = raw_to_gicv2(pirq->pic);
- HWREG32(gic->dist_base + GIC_DIST_ENABLE_CLEAR + hwirq / 32 * 4) = 1U << (hwirq % 32);
- }
- static void gicv2_irq_unmask(struct rt_pic_irq *pirq)
- {
- int hwirq = pirq->hwirq;
- struct gicv2 *gic = raw_to_gicv2(pirq->pic);
- HWREG32(gic->dist_base + GIC_DIST_ENABLE_SET + hwirq / 32 * 4) = 1U << (hwirq % 32);
- }
- static void gicv2_irq_eoi(struct rt_pic_irq *pirq)
- {
- struct gicv2 *gic = raw_to_gicv2(pirq->pic);
- if (_gicv2_eoi_mode_ns)
- {
- HWREG32(gic->cpu_base + GIC_CPU_DIR) = pirq->hwirq;
- }
- }
- static rt_err_t gicv2_irq_set_priority(struct rt_pic_irq *pirq, rt_uint32_t priority)
- {
- rt_uint32_t mask;
- int hwirq = pirq->hwirq;
- struct gicv2 *gic = raw_to_gicv2(pirq->pic);
- mask = HWREG32(gic->dist_base + GIC_DIST_PRI + hwirq / 4 * 4);
- mask &= ~(0xffU << ((hwirq % 4) * 8));
- mask |= ((priority & 0xffU) << ((hwirq % 4) * 8));
- HWREG32(gic->dist_base + GIC_DIST_PRI + hwirq / 4 * 4) = mask;
- return RT_EOK;
- }
- static rt_err_t gicv2_irq_set_affinity(struct rt_pic_irq *pirq, rt_bitmap_t *affinity)
- {
- int hwirq = pirq->hwirq;
- struct gicv2 *gic = raw_to_gicv2(pirq->pic);
- rt_uint32_t target_list = ((rt_uint8_t *)affinity)[gic - &_gicv2_list[0]];
- rt_uint8_t valb = _gicv2_cpumask_map[__rt_ffs(target_list) - 1];
- void *io_addr = gic->dist_base + GIC_DIST_TARGET + hwirq;
- if (needs_rmw_access)
- {
- /* RMW write byte */
- rt_uint32_t val;
- rt_ubase_t level;
- rt_ubase_t offset = (rt_ubase_t)io_addr & 3UL, shift = offset * 8;
- static struct rt_spinlock rmw_lock = {};
- level = rt_spin_lock_irqsave(&rmw_lock);
- io_addr -= offset;
- val = HWREG32(io_addr);
- val &= ~RT_GENMASK(shift + 7, shift);
- val |= valb << shift;
- HWREG32(io_addr) = val;
- rt_spin_unlock_irqrestore(&rmw_lock, level);
- }
- else
- {
- HWREG8(io_addr) = valb;
- }
- return RT_EOK;
- }
- static rt_err_t gicv2_irq_set_triger_mode(struct rt_pic_irq *pirq, rt_uint32_t mode)
- {
- rt_err_t err = RT_EOK;
- int hwirq = pirq->hwirq;
- struct gicv2 *gic = raw_to_gicv2(pirq->pic);
- if (hwirq >= GIC_SGI_NR)
- {
- err = gic_common_configure_irq(gic->dist_base + GIC_DIST_CONFIG, pirq->hwirq, mode, RT_NULL, RT_NULL);
- }
- else
- {
- err = -RT_ENOSYS;
- }
- return err;
- }
- static void gicv2_irq_send_ipi(struct rt_pic_irq *pirq, rt_bitmap_t *cpumask)
- {
- struct gicv2 *gic;
- int sgi = pirq->hwirq;
- rt_uint8_t *target_list = (rt_uint8_t *)cpumask;
- for (int i = 0; i < _gicv2_nr; ++i)
- {
- if (*target_list)
- {
- gic = &_gicv2_list[i];
- HWREG32(gic->dist_base + GIC_DIST_SOFTINT) = ((*target_list & 0xffU) << 16) | (sgi & 0xf);
- rt_hw_dsb();
- }
- ++target_list;
- }
- }
- static int gicv2_irq_map(struct rt_pic *pic, int hwirq, rt_uint32_t mode)
- {
- int irq, irq_index = hwirq - GIC_SGI_NR;
- struct rt_pic_irq *pirq = rt_pic_find_irq(pic, irq_index);
- if (pirq && hwirq >= GIC_SGI_NR)
- {
- pirq->mode = mode;
- pirq->priority = GICD_INT_DEF_PRI;
- rt_bitmap_set_bit(pirq->affinity, _init_cpu_id);
- irq = rt_pic_config_irq(pic, irq_index, hwirq);
- if (irq >= 0 && mode != RT_IRQ_MODE_LEVEL_HIGH)
- {
- gicv2_irq_set_triger_mode(pirq, mode);
- }
- }
- else
- {
- irq = -1;
- }
- return irq;
- }
- static rt_err_t gicv2_irq_parse(struct rt_pic *pic, struct rt_ofw_cell_args *args, struct rt_pic_irq *out_pirq)
- {
- rt_err_t err = RT_EOK;
- if (args->args_count == 3)
- {
- out_pirq->mode = args->args[2] & RT_IRQ_MODE_MASK;
- switch (args->args[0])
- {
- case 0:
- /* SPI */
- out_pirq->hwirq = args->args[1] + 32;
- break;
- case 1:
- /* PPI */
- out_pirq->hwirq = args->args[1] + 16;
- break;
- default:
- err = -RT_ENOSYS;
- break;
- }
- }
- else
- {
- err = -RT_EINVAL;
- }
- return err;
- }
- static struct rt_pic_ops gicv2_ops =
- {
- .name = "GICv2",
- .irq_init = gicv2_irq_init,
- .irq_ack = gicv2_irq_ack,
- .irq_mask = gicv2_irq_mask,
- .irq_unmask = gicv2_irq_unmask,
- .irq_eoi = gicv2_irq_eoi,
- .irq_set_priority = gicv2_irq_set_priority,
- .irq_set_affinity = gicv2_irq_set_affinity,
- .irq_set_triger_mode = gicv2_irq_set_triger_mode,
- .irq_send_ipi = gicv2_irq_send_ipi,
- .irq_map = gicv2_irq_map,
- .irq_parse = gicv2_irq_parse,
- };
- static rt_bool_t gicv2_handler(void *data)
- {
- rt_bool_t res = RT_FALSE;
- int hwirq;
- struct gicv2 *gic = data;
- hwirq = HWREG32(gic->cpu_base + GIC_CPU_INTACK) & 0x3ffUL;
- if (!(hwirq >= 1020 && hwirq <= 1023))
- {
- struct rt_pic_irq *pirq;
- if (hwirq < GIC_SGI_NR)
- {
- rt_hw_rmb();
- pirq = rt_pic_find_ipi(&gic->parent, hwirq);
- }
- else
- {
- pirq = rt_pic_find_irq(&gic->parent, hwirq - GIC_SGI_NR);
- }
- gicv2_irq_ack(pirq);
- rt_pic_handle_isr(pirq);
- gicv2_irq_eoi(pirq);
- res = RT_TRUE;
- }
- return res;
- }
- static rt_err_t gicv2_enable_rmw_access(void *data)
- {
- if (rt_ofw_machine_is_compatible("renesas,emev2"))
- {
- needs_rmw_access = RT_TRUE;
- return RT_EOK;
- }
- return -RT_EINVAL;
- }
- static const struct gic_quirk _gicv2_quirks[] =
- {
- {
- .desc = "GICv2: Broken byte access",
- .compatible = "arm,pl390",
- .init = gicv2_enable_rmw_access,
- },
- { /* sentinel */ }
- };
- static rt_err_t gicv2_iomap_init(struct gicv2 *gic, rt_uint64_t *regs)
- {
- rt_err_t err = RT_EOK;
- int idx;
- const char *name[] =
- {
- "Distributor",
- "CPU interfaces",
- "Virtual interface control",
- "Virtual CPU interface",
- };
- do {
- /* GICD->GICC->GICH->GICV */
- gic->dist_size = regs[1];
- gic->dist_base = rt_ioremap((void *)regs[0], gic->dist_size);
- if (!gic->dist_base)
- {
- idx = 0;
- err = -RT_ERROR;
- break;
- }
- gic->cpu_size = regs[3];
- gic->cpu_base = rt_ioremap((void *)regs[2], gic->cpu_size);
- if (!gic->cpu_base)
- {
- idx = 1;
- err = -RT_ERROR;
- break;
- }
- /* ArchRev[4:7] */
- gic->version = HWREG32(gic->dist_base + GIC_DIST_ICPIDR2) >> 4;
- #ifdef ARCH_SUPPORT_HYP
- if (gic->version == 1)
- {
- break;
- }
- gic->hyp_size = regs[5];
- gic->hyp_base = rt_ioremap((void *)regs[4], gic->hyp_size);
- if (!gic->hyp_base)
- {
- idx = 2;
- err = -RT_ERROR;
- break;
- }
- gic->vcpu_size = regs[7];
- gic->vcpu_base = rt_ioremap((void *)regs[6], gic->vcpu_size);
- if (!gic->vcpu_base)
- {
- idx = 3;
- err = -RT_ERROR;
- break;
- }
- #endif /* ARCH_SUPPORT_HYP */
- } while (0);
- if (err)
- {
- RT_UNUSED(idx);
- RT_UNUSED(name);
- LOG_E("gic[%d] %s IO[%p, %p] map fail", _gicv2_nr, name[idx], regs[idx * 2], regs[idx * 2 + 1]);
- }
- return err;
- }
- static void gicv2_init(struct gicv2 *gic)
- {
- gicv2_dist_init(gic);
- gic->parent.priv_data = gic;
- gic->parent.ops = &gicv2_ops;
- rt_pic_linear_irq(&gic->parent, gic->max_irq + 1 - GIC_SGI_NR);
- gic_common_sgi_config(gic->dist_base, &gic->parent, _gicv2_nr * GIC_SGI_NR);
- rt_pic_add_traps(gicv2_handler, gic);
- rt_pic_user_extends(&gic->parent);
- }
- static void gicv2_init_fail(struct gicv2 *gic)
- {
- if (gic->dist_base)
- {
- rt_iounmap(gic->dist_base);
- }
- if (gic->cpu_base)
- {
- rt_iounmap(gic->cpu_base);
- }
- if (gic->hyp_base)
- {
- rt_iounmap(gic->hyp_base);
- }
- if (gic->vcpu_base)
- {
- rt_iounmap(gic->vcpu_base);
- }
- rt_memset(gic, 0, sizeof(*gic));
- }
- static rt_err_t gicv2_ofw_init(struct rt_ofw_node *np, const struct rt_ofw_node_id *id)
- {
- rt_err_t err = RT_EOK;
- struct gicv2 *gic = RT_NULL;
- do {
- rt_uint64_t regs[8];
- if (_gicv2_nr >= RT_PIC_ARM_GIC_MAX_NR)
- {
- LOG_W("GICv2/v1 table is full");
- err = -RT_EFULL;
- break;
- }
- gic = &_gicv2_list[_gicv2_nr];
- rt_ofw_get_address_array(np, RT_ARRAY_SIZE(regs), regs);
- if ((err = gicv2_iomap_init(gic, regs)))
- {
- break;
- }
- if (gic->version != 1 && gic->version != 2)
- {
- LOG_E("Version = %d is not support", gic->version);
- err = -RT_EINVAL;
- break;
- }
- gic_common_init_quirk_ofw(np, _gicv2_quirks, gic);
- gicv2_init(gic);
- rt_ofw_data(np) = &gic->parent;
- if (gic->version == 2)
- {
- #ifdef RT_PIC_ARM_GIC_V2M
- gicv2m_ofw_probe(np, id);
- #endif
- }
- ++_gicv2_nr;
- } while (0);
- if (err && gic)
- {
- gicv2_init_fail(gic);
- }
- return err;
- }
- static const struct rt_ofw_node_id gicv2_ofw_ids[] =
- {
- { .compatible = "arm,gic-400" },
- { .compatible = "arm,arm11mp-gic" },
- { .compatible = "arm,arm1176jzf-devchip-gic" },
- { .compatible = "arm,cortex-a15-gic" },
- { .compatible = "arm,cortex-a9-gic" },
- { .compatible = "arm,cortex-a7-gic" },
- { .compatible = "qcom,msm-8660-qgic" },
- { .compatible = "qcom,msm-qgic2" },
- { .compatible = "arm,pl390" },
- { /* sentinel */ }
- };
- RT_PIC_OFW_DECLARE(gicv2, gicv2_ofw_ids, gicv2_ofw_init);
|