Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2019, Rick Chen <rick@andestech.com> * * 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 <dm.h> #include <asm/global_data.h> #include <dm/device-internal.h> #include <dm/lists.h> #include <dm/uclass-internal.h> #include <regmap.h> #include <syscon.h> #include <asm/io.h> #include <asm/syscon.h> #include <cpu.h> #include <linux/err.h> /* pending register */ #define PENDING_REG(base, hart) ((ulong)(base) + 0x1000 + 4 * (((hart) + 1) / 32)) /* enable register */ #define ENABLE_REG(base, hart) ((ulong)(base) + 0x2000 + (hart) * 0x80 + 4 * (((hart) + 1) / 32)) /* 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 PLICSW_PRIORITY_BASE 0x4 DECLARE_GLOBAL_DATA_PTR; static int enable_ipi(int hart) { u32 enable_bit = (hart + 1) % 32; writel(BIT(enable_bit), (void __iomem *)ENABLE_REG(gd->arch.plicsw, hart)); return 0; } static void init_priority_ipi(int hart_num) { u32 *priority = (void *)PRIORITY_REG(gd->arch.plicsw); for (int i = 0; i < hart_num; 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) { u32 interrupt_id = hart + 1; u32 pending_bit = interrupt_id % 32; writel(BIT(pending_bit), (void __iomem *)PENDING_REG(gd->arch.plicsw, hart)); 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) { u32 interrupt_id = hart + 1; u32 pending_bit = interrupt_id % 32; *pending = readl((void __iomem *)PENDING_REG(gd->arch.plicsw, hart)); *pending = !!(*pending & BIT(pending_bit)); 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, }; |