1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright (C) 2019, Rick Chen <rick@andestech.com> 4 * 5 * U-Boot syscon driver for Andes' PLICSW 6 * The PLICSW block is an Andes-specific design for software interrupts, 7 * contains memory-mapped priority, enable, claim and pending registers 8 * similar to RISC-V PLIC. 9 */ 10 11#include <dm.h> 12#include <asm/global_data.h> 13#include <dm/device-internal.h> 14#include <dm/lists.h> 15#include <dm/uclass-internal.h> 16#include <regmap.h> 17#include <syscon.h> 18#include <asm/io.h> 19#include <asm/syscon.h> 20#include <cpu.h> 21#include <linux/err.h> 22 23/* pending register */ 24#define PENDING_REG(base, hart) ((ulong)(base) + 0x1000 + 4 * (((hart) + 1) / 32)) 25/* enable register */ 26#define ENABLE_REG(base, hart) ((ulong)(base) + 0x2000 + (hart) * 0x80 + 4 * (((hart) + 1) / 32)) 27/* claim register */ 28#define CLAIM_REG(base, hart) ((ulong)(base) + 0x200004 + (hart) * 0x1000) 29/* priority register */ 30#define PRIORITY_REG(base) ((ulong)(base) + PLICSW_PRIORITY_BASE) 31 32/* Bit 0 of PLIC-SW pending array is hardwired to zero, so we start from bit 1 */ 33#define PLICSW_PRIORITY_BASE 0x4 34 35DECLARE_GLOBAL_DATA_PTR; 36 37static int enable_ipi(int hart) 38{ 39 u32 enable_bit = (hart + 1) % 32; 40 41 writel(BIT(enable_bit), (void __iomem *)ENABLE_REG(gd->arch.plicsw, hart)); 42 43 return 0; 44} 45 46static void init_priority_ipi(int hart_num) 47{ 48 u32 *priority = (void *)PRIORITY_REG(gd->arch.plicsw); 49 50 for (int i = 0; i < hart_num; i++) 51 writel(1, &priority[i]); 52 53 return; 54} 55 56int riscv_init_ipi(void) 57{ 58 int ret; 59 int hart_num = 0; 60 long *base = syscon_get_first_range(RISCV_SYSCON_PLICSW); 61 ofnode node; 62 struct udevice *dev; 63 u32 reg; 64 65 if (IS_ERR(base)) 66 return PTR_ERR(base); 67 gd->arch.plicsw = base; 68 69 ret = uclass_find_first_device(UCLASS_CPU, &dev); 70 if (ret) 71 return ret; 72 if (!dev) 73 return -ENODEV; 74 75 ofnode_for_each_subnode(node, dev_ofnode(dev->parent)) { 76 const char *device_type; 77 78 device_type = ofnode_read_string(node, "device_type"); 79 if (!device_type) 80 continue; 81 82 if (strcmp(device_type, "cpu")) 83 continue; 84 85 /* skip if hart is marked as not available */ 86 if (!ofnode_is_enabled(node)) 87 continue; 88 89 /* read hart ID of CPU */ 90 ret = ofnode_read_u32(node, "reg", ®); 91 if (ret == 0) 92 enable_ipi(reg); 93 hart_num++; 94 } 95 96 init_priority_ipi(hart_num); 97 return 0; 98} 99 100int riscv_send_ipi(int hart) 101{ 102 u32 interrupt_id = hart + 1; 103 u32 pending_bit = interrupt_id % 32; 104 105 writel(BIT(pending_bit), (void __iomem *)PENDING_REG(gd->arch.plicsw, hart)); 106 107 return 0; 108} 109 110int riscv_clear_ipi(int hart) 111{ 112 u32 source_id; 113 114 source_id = readl((void __iomem *)CLAIM_REG(gd->arch.plicsw, hart)); 115 writel(source_id, (void __iomem *)CLAIM_REG(gd->arch.plicsw, hart)); 116 117 return 0; 118} 119 120int riscv_get_ipi(int hart, int *pending) 121{ 122 u32 interrupt_id = hart + 1; 123 u32 pending_bit = interrupt_id % 32; 124 125 *pending = readl((void __iomem *)PENDING_REG(gd->arch.plicsw, hart)); 126 *pending = !!(*pending & BIT(pending_bit)); 127 128 return 0; 129} 130 131static const struct udevice_id andes_plicsw_ids[] = { 132 { .compatible = "andestech,plicsw", .data = RISCV_SYSCON_PLICSW }, 133 { } 134}; 135 136U_BOOT_DRIVER(andes_plicsw) = { 137 .name = "andes_plicsw", 138 .id = UCLASS_SYSCON, 139 .of_match = andes_plicsw_ids, 140 .flags = DM_FLAG_PRE_RELOC, 141}; 142