// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2019, Rick Chen * * U-Boot syscon driver for Andes' PLICSW * The PLICSW block is an Andes-specific design for software interrupts, * contains memory-mapped priority, enable, claim and pending registers * similar to RISC-V PLIC. */ #include #include #include #include #include #include #include #include #include #include #include /* pending register */ #define PENDING_REG(base) ((ulong)(base) + 0x1000) /* enable register */ #define ENABLE_REG(base, hart) ((ulong)(base) + 0x2000 + (hart) * 0x80) /* claim register */ #define CLAIM_REG(base, hart) ((ulong)(base) + 0x200004 + (hart) * 0x1000) /* priority register */ #define PRIORITY_REG(base) ((ulong)(base) + PLICSW_PRIORITY_BASE) /* Bit 0 of PLIC-SW pending array is hardwired to zero, so we start from bit 1 */ #define FIRST_AVAILABLE_BIT 0x2 #define SEND_IPI_TO_HART(hart) (FIRST_AVAILABLE_BIT << (hart)) #define PLICSW_PRIORITY_BASE 0x4 #define PLICSW_INTERRUPT_PER_HART 0x1 DECLARE_GLOBAL_DATA_PTR; static int enable_ipi(int hart) { unsigned int en; en = FIRST_AVAILABLE_BIT << hart; writel(en, (void __iomem *)ENABLE_REG(gd->arch.plicsw, hart)); return 0; } static void init_priority_ipi(int hart_num) { uint32_t *priority = (void *)PRIORITY_REG(gd->arch.plicsw); for (int i = 0; i < hart_num * PLICSW_INTERRUPT_PER_HART; i++) { writel(1, &priority[i]); } return; } int riscv_init_ipi(void) { int ret; int hart_num = 0; long *base = syscon_get_first_range(RISCV_SYSCON_PLICSW); ofnode node; struct udevice *dev; u32 reg; if (IS_ERR(base)) return PTR_ERR(base); gd->arch.plicsw = base; ret = uclass_find_first_device(UCLASS_CPU, &dev); if (ret) return ret; if (!dev) return -ENODEV; ofnode_for_each_subnode(node, dev_ofnode(dev->parent)) { const char *device_type; device_type = ofnode_read_string(node, "device_type"); if (!device_type) continue; if (strcmp(device_type, "cpu")) continue; /* skip if hart is marked as not available */ if (!ofnode_is_enabled(node)) continue; /* read hart ID of CPU */ ret = ofnode_read_u32(node, "reg", ®); if (ret == 0) enable_ipi(reg); hart_num++; } init_priority_ipi(hart_num); return 0; } int riscv_send_ipi(int hart) { unsigned int ipi = SEND_IPI_TO_HART(hart); writel(ipi, (void __iomem *)PENDING_REG(gd->arch.plicsw)); return 0; } int riscv_clear_ipi(int hart) { u32 source_id; source_id = readl((void __iomem *)CLAIM_REG(gd->arch.plicsw, hart)); writel(source_id, (void __iomem *)CLAIM_REG(gd->arch.plicsw, hart)); return 0; } int riscv_get_ipi(int hart, int *pending) { unsigned int ipi = SEND_IPI_TO_HART(hart); *pending = readl((void __iomem *)PENDING_REG(gd->arch.plicsw)); *pending = !!(*pending & ipi); return 0; } static const struct udevice_id andes_plicsw_ids[] = { { .compatible = "andestech,plicsw", .data = RISCV_SYSCON_PLICSW }, { } }; U_BOOT_DRIVER(andes_plicsw) = { .name = "andes_plicsw", .id = UCLASS_SYSCON, .of_match = andes_plicsw_ids, .flags = DM_FLAG_PRE_RELOC, };