123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Copyright (C) 2022, Liuw <lw312886@alibaba-inc.com>
- *
- * U-Boot syscon driver for Thead's Platform Level Interrupt Controller (PLIC)
- */
- #include <common.h>
- #include <dm.h>
- #include <dm/device-internal.h>
- #include <dm/lists.h>
- #include <dm/uclass-internal.h>
- #include <regmap.h>
- #include <syscon.h>
- #include <asm/csr.h>
- #include <asm/io.h>
- #include <asm/syscon.h>
- #include <asm/global_data.h>
- #include <cpu.h>
- #include <linux/err.h>
- #include <asm/arch-thead/light-plic.h>
- DECLARE_GLOBAL_DATA_PTR;
- #define PLIC_BASE_GET(void) \
- do { \
- long *ret; \
- \
- if (!gd->arch.plic) { \
- ret = syscon_get_first_range(RISCV_SYSCON_PLIC); \
- gd->arch.plic = ret; \
- } \
- } while (0)
- irq_handler_t irq_table[MAX_IRQ_NUM];
- void __iomem *plic_base = NULL;
- void external_interrupt(struct pt_regs *regs)
- {
- void __iomem *claim;
- irq_handler_t handler;
- u32 irq_num;
- debug("[%s,%d]\n", __func__, __LINE__);
- if (!plic_base)
- return;
- debug("[%s,%d]\n", __func__, __LINE__);
- claim = PLIC_CLAIM_REG(plic_base, 0);
- while ((irq_num = readl(claim))) {
- if (irq_num >= MAX_IRQ_NUM)
- debug("Cannot find irq:%d\n", irq_num);
- else {
- handler = irq_table[irq_num];
- if (handler)
- handler();
- writel(irq_num, claim);
- }
- }
- debug("[%s,%d]\n", __func__, __LINE__);
- }
- static void plic_toggle(void __iomem *enable_base, int hwirq, int enable)
- {
- u32 __iomem *reg = enable_base + (hwirq / 32) * sizeof(u32);
- u32 hwirq_mask = 1 << (hwirq % 32);
- if (enable)
- writel(readl(reg) | hwirq_mask, reg);
- else
- writel(readl(reg) & ~hwirq_mask, reg);
- debug("[%s,%d][0x%lx] = 0x%x\n", __func__, __LINE__,
- (unsigned long)reg, readl(reg));
- }
- static void plic_set_threshold(void __iomem *thre_base, u32 threshold)
- {
- writel(threshold, thre_base);
- debug("[%s,%d][0x%lx] = 0x%x\n", __func__, __LINE__,
- (unsigned long)thre_base, readl(thre_base));
- }
- static void plic_set_irq_priority(void __iomem *prio_base, int prio)
- {
- writel(prio, prio_base);
- }
- int irq_handler_register(int irq, irq_handler_t handler)
- {
- if (irq < 0 || irq >= MAX_IRQ_NUM) {
- debug("invalid irq number to register\n");
- return -EINVAL;
- }
- irq_table[irq] = handler;
- return 0;
- }
- void arch_local_irq_enable(void)
- {
- csr_set(CSR_MIE, MIE_MEIE);
- csr_set(CSR_MSTATUS, SR_MIE);
- }
- void arch_local_irq_disable(void)
- {
- csr_clear(CSR_MIE, MIE_MEIE);
- csr_clear(CSR_MSTATUS, SR_MIE);
- }
- void irq_priority_set(int irq_id)
- {
- plic_set_irq_priority(PLIC_PRIO_REG(gd->arch.plic, irq_id), 4);
- }
- void irq_enable(int hwirq)
- {
- plic_toggle(PLIC_ENABLE_REG(gd->arch.plic, 0), hwirq, 1);
- }
- void irq_disable(int hwirq)
- {
- plic_toggle(PLIC_ENABLE_REG(gd->arch.plic, 0), hwirq, 0);
- }
- int plic_init()
- {
- PLIC_BASE_GET();
- if (IS_ERR(gd->arch.plic))
- return PTR_ERR(gd->arch.plic);
- plic_base = gd->arch.plic;
- debug("THEAD PLIC BASE: 0x%lx\n", (unsigned long)gd->arch.plic);
- plic_set_threshold(PLIC_THRESHOLD_REG(gd->arch.plic, 0), 0);
- arch_local_irq_enable(); //enale the global interrupt
- return 0;
- }
- static const struct udevice_id thead_plic_ids[] = {
- { .compatible = "riscv,plic0", .data = RISCV_SYSCON_PLIC},
- { }
- };
- U_BOOT_DRIVER(thead_plic) = {
- .name = "thead_light_plic",
- .id = UCLASS_SYSCON,
- .of_match = thead_plic_ids,
- };
|