blob: 267d6a191b210b4f95757b2987d5b1931a4a875f [file] [log] [blame]
Rick Chen0d389462019-04-02 15:56:39 +08001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2019, Rick Chen <rick@andestech.com>
4 *
5 * U-Boot syscon driver for Andes's Platform Level Interrupt Controller (PLIC).
6 * The PLIC block holds memory-mapped claim and pending registers
7 * associated with software interrupt.
8 */
9
10#include <common.h>
11#include <dm.h>
12#include <dm/device-internal.h>
13#include <dm/lists.h>
14#include <dm/uclass-internal.h>
15#include <regmap.h>
16#include <syscon.h>
17#include <asm/io.h>
18#include <asm/syscon.h>
19#include <cpu.h>
Simon Glass61b29b82020-02-03 07:36:15 -070020#include <linux/err.h>
Rick Chen0d389462019-04-02 15:56:39 +080021
22/* pending register */
Rick Chen43a08322019-11-14 13:52:24 +080023#define PENDING_REG(base, hart) ((ulong)(base) + 0x1000 + ((hart) / 4) * 4)
Rick Chen0d389462019-04-02 15:56:39 +080024/* enable register */
25#define ENABLE_REG(base, hart) ((ulong)(base) + 0x2000 + (hart) * 0x80)
26/* claim register */
27#define CLAIM_REG(base, hart) ((ulong)(base) + 0x200004 + (hart) * 0x1000)
28
29#define ENABLE_HART_IPI (0x80808080)
30#define SEND_IPI_TO_HART(hart) (0x80 >> (hart))
31
32DECLARE_GLOBAL_DATA_PTR;
Rick Chen0d389462019-04-02 15:56:39 +080033
Rick Chend58b0a62019-08-21 11:26:50 +080034static int enable_ipi(int hart)
Rick Chen0d389462019-04-02 15:56:39 +080035{
Rick Chen43a08322019-11-14 13:52:24 +080036 unsigned int en;
Rick Chen0d389462019-04-02 15:56:39 +080037
Rick Chend58b0a62019-08-21 11:26:50 +080038 en = ENABLE_HART_IPI >> hart;
39 writel(en, (void __iomem *)ENABLE_REG(gd->arch.plic, hart));
Rick Chen0d389462019-04-02 15:56:39 +080040
41 return 0;
42}
43
Sean Anderson15943bb2020-09-28 10:52:25 -040044int riscv_init_ipi(void)
Rick Chen0d389462019-04-02 15:56:39 +080045{
Rick Chen0d389462019-04-02 15:56:39 +080046 int ret;
Sean Anderson15943bb2020-09-28 10:52:25 -040047 long *base = syscon_get_first_range(RISCV_SYSCON_PLIC);
48 ofnode node;
49 struct udevice *dev;
Rick Chend58b0a62019-08-21 11:26:50 +080050 u32 reg;
Rick Chen0d389462019-04-02 15:56:39 +080051
Sean Anderson15943bb2020-09-28 10:52:25 -040052 if (IS_ERR(base))
53 return PTR_ERR(base);
54 gd->arch.plic = base;
55
Rick Chen0d389462019-04-02 15:56:39 +080056 ret = uclass_find_first_device(UCLASS_CPU, &dev);
57 if (ret)
58 return ret;
Sean Anderson15943bb2020-09-28 10:52:25 -040059 else if (!dev)
60 return -ENODEV;
Rick Chen0d389462019-04-02 15:56:39 +080061
Sean Anderson15943bb2020-09-28 10:52:25 -040062 ofnode_for_each_subnode(node, dev_ofnode(dev->parent)) {
63 const char *device_type;
Rick Chen0d389462019-04-02 15:56:39 +080064
Sean Anderson15943bb2020-09-28 10:52:25 -040065 device_type = ofnode_read_string(node, "device_type");
66 if (!device_type)
67 continue;
Rick Chend58b0a62019-08-21 11:26:50 +080068
Sean Anderson15943bb2020-09-28 10:52:25 -040069 if (strcmp(device_type, "cpu"))
70 continue;
Rick Chend58b0a62019-08-21 11:26:50 +080071
Sean Anderson15943bb2020-09-28 10:52:25 -040072 /* skip if hart is marked as not available */
73 if (!ofnode_is_available(node))
74 continue;
Rick Chend58b0a62019-08-21 11:26:50 +080075
Sean Anderson15943bb2020-09-28 10:52:25 -040076 /* read hart ID of CPU */
77 ret = ofnode_read_u32(node, "reg", &reg);
78 if (ret == 0)
79 enable_ipi(reg);
Rick Chen0d389462019-04-02 15:56:39 +080080 }
81
Sean Anderson15943bb2020-09-28 10:52:25 -040082 return 0;
Sean Anderson40686c32020-06-24 06:41:18 -040083}
84
Rick Chen0d389462019-04-02 15:56:39 +080085int riscv_send_ipi(int hart)
86{
Sean Anderson40686c32020-06-24 06:41:18 -040087 unsigned int ipi = (SEND_IPI_TO_HART(hart) << (8 * gd->arch.boot_hart));
Rick Chen43a08322019-11-14 13:52:24 +080088
Rick Chen43a08322019-11-14 13:52:24 +080089 writel(ipi, (void __iomem *)PENDING_REG(gd->arch.plic,
90 gd->arch.boot_hart));
Rick Chen0d389462019-04-02 15:56:39 +080091
92 return 0;
93}
94
95int riscv_clear_ipi(int hart)
96{
97 u32 source_id;
98
Rick Chen0d389462019-04-02 15:56:39 +080099 source_id = readl((void __iomem *)CLAIM_REG(gd->arch.plic, hart));
100 writel(source_id, (void __iomem *)CLAIM_REG(gd->arch.plic, hart));
101
102 return 0;
103}
104
Lukas Auer8b3e97b2019-12-08 23:28:50 +0100105int riscv_get_ipi(int hart, int *pending)
106{
Lukas Auer8b3e97b2019-12-08 23:28:50 +0100107 *pending = readl((void __iomem *)PENDING_REG(gd->arch.plic,
108 gd->arch.boot_hart));
109 *pending = !!(*pending & SEND_IPI_TO_HART(hart));
110
111 return 0;
112}
113
Rick Chen0d389462019-04-02 15:56:39 +0800114static const struct udevice_id andes_plic_ids[] = {
115 { .compatible = "riscv,plic1", .data = RISCV_SYSCON_PLIC },
116 { }
117};
118
119U_BOOT_DRIVER(andes_plic) = {
120 .name = "andes_plic",
121 .id = UCLASS_SYSCON,
122 .of_match = andes_plic_ids,
123 .flags = DM_FLAG_PRE_RELOC,
124};