1/*
2 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7#pragma once
8/* tell the kernel we have the set trigger feature */
9#define HAVE_SET_TRIGGER 1
10
11
12#include <plat/machine/devices_gen.h>
13#include <arch/model/smp.h>
14
15/* The memory map is based on the PLIC section in
16 * https://static.dev.sifive.com/U54-MC-RVCoreIP.pdf
17 */
18
19#define PLIC_PPTR_BASE          PLIC_PPTR
20
21
22#define PLIC_HART_ID (CONFIG_FIRST_HART_ID)
23
24#define PLIC_PRIO               0x0
25#define PLIC_PRIO_PER_ID        0x4
26
27#define PLIC_PENDING            0x1000
28#define PLIC_EN                 0x2000
29#define PLIC_EN_PER_HART        0x100
30#define PLIC_EN_PER_CONTEXT     0x80
31
32
33#define PLIC_THRES              0x200000
34#define PLIC_SVC_CONTEXT        1
35#define PLIC_THRES_PER_HART     0x2000
36#define PLIC_THRES_PER_CONTEXT  0x1000
37#define PLIC_THRES_CLAIM        0x4
38
39#define PLIC_NUM_INTERRUPTS PLIC_MAX_IRQ
40
41#if defined(CONFIG_PLAT_HIFIVE) || defined(CONFIG_PLAT_POLARFIRE)
42
43/* SiFive U54-MC has 5 cores, and the first core does not
44 * have supervisor mode. Therefore, we need to compensate
45 * for the addresses.
46 */
47#define PLAT_PLIC_THRES_ADJUST(x) ((x) - PLIC_THRES_PER_CONTEXT)
48#define PLAT_PLIC_EN_ADJUST(x)    ((x) - PLIC_EN_PER_CONTEXT)
49
50#else
51
52#define PLAT_PLIC_THRES_ADJUST(x)   (x)
53#define PLAT_PLIC_EN_ADJUST(x)      (x)
54
55#endif
56
57static inline void write_sie(word_t value)
58{
59    asm volatile("csrw sie,  %0" :: "r"(value));
60}
61
62static inline word_t read_sie(void)
63{
64    word_t temp;
65    asm volatile("csrr %0, sie" : "=r"(temp));
66    return temp;
67}
68
69static inline uint32_t readl(uint64_t addr)
70{
71    return *((volatile uint32_t *)(addr));
72}
73
74static inline void writel(uint32_t val, uint64_t addr)
75{
76    *((volatile uint32_t *)(addr)) = val;
77}
78
79static inline word_t plic_enable_offset(word_t hart_id, word_t context_id)
80{
81    word_t addr = PLAT_PLIC_EN_ADJUST(PLIC_EN + hart_id * PLIC_EN_PER_HART + context_id * PLIC_EN_PER_CONTEXT);
82    return addr;
83}
84
85
86static inline word_t plic_thres_offset(word_t hart_id, word_t context_id)
87{
88    word_t addr = PLAT_PLIC_THRES_ADJUST(PLIC_THRES + hart_id * PLIC_THRES_PER_HART + context_id * PLIC_THRES_PER_CONTEXT);
89    return addr;
90}
91
92static inline word_t plic_claim_offset(word_t hart_id, word_t context_id)
93{
94    word_t addr = plic_thres_offset(hart_id, context_id) + PLIC_THRES_CLAIM;
95    return addr;
96}
97
98static inline bool_t plic_pending_interrupt(word_t interrupt)
99{
100    word_t addr = PLIC_PPTR_BASE + PLIC_PENDING + (interrupt / 32) * 4;
101    word_t bit = interrupt % 32;
102    if (readl(addr) & BIT(bit)) {
103        return true;
104    } else {
105        return false;
106    }
107}
108
109static inline word_t get_hart_id(void)
110{
111#ifdef ENABLE_SMP_SUPPORT
112    return cpuIndexToID(getCurrentCPUIndex());
113#else
114    return CONFIG_FIRST_HART_ID;
115#endif
116}
117
118static inline irq_t plic_get_claim(void)
119{
120    /* Read the claim register for our HART interrupt context */
121    word_t hart_id = get_hart_id();
122    return readl(PLIC_PPTR_BASE + plic_claim_offset(hart_id, PLIC_SVC_CONTEXT));
123}
124
125static inline void plic_complete_claim(irq_t irq)
126{
127    /* Complete the IRQ claim by writing back to the claim register. */
128    word_t hart_id = get_hart_id();
129    writel(irq, PLIC_PPTR_BASE + plic_claim_offset(hart_id, PLIC_SVC_CONTEXT));
130}
131
132static inline void plic_mask_irq(bool_t disable, irq_t irq)
133{
134    uint64_t addr = 0;
135    uint32_t val = 0;
136    uint32_t bit = 0;
137
138    word_t hart_id = get_hart_id();
139    addr = PLIC_PPTR_BASE + plic_enable_offset(hart_id, PLIC_SVC_CONTEXT) + (irq / 32) * 4;
140    bit = irq % 32;
141
142    val = readl(addr);
143    if (disable) {
144        val &= ~BIT(bit);
145    } else {
146        val |= BIT(bit);
147    }
148    writel(val, addr);
149}
150
151static inline void plic_init_hart(void)
152{
153
154    word_t hart_id = get_hart_id();
155
156    for (int i = 1; i <= PLIC_NUM_INTERRUPTS; i++) {
157        /* Disable interrupts */
158        plic_mask_irq(true, i);
159    }
160
161    /* Set threshold to zero */
162    writel(0, (PLIC_PPTR_BASE + plic_thres_offset(hart_id, PLIC_SVC_CONTEXT)));
163}
164
165static inline void plic_init_controller(void)
166{
167
168    for (int i = 1; i <= PLIC_NUM_INTERRUPTS; i++) {
169        /* Clear all pending bits */
170        if (plic_pending_interrupt(i)) {
171            readl(PLIC_PPTR_BASE + plic_claim_offset(PLIC_HART_ID, PLIC_SVC_CONTEXT));
172            writel(i, PLIC_PPTR_BASE + plic_claim_offset(PLIC_HART_ID, PLIC_SVC_CONTEXT));
173        }
174    }
175
176    /* Set the priorities of all interrupts to 1 */
177    for (int i = 1; i <= PLIC_MAX_IRQ + 1; i++) {
178        writel(2, PLIC_PPTR_BASE + PLIC_PRIO + PLIC_PRIO_PER_ID * i);
179    }
180
181}
182
183
184/*
185 * Provide a dummy definition of set trigger as the Hifive platform currently
186 * has all global interrupt positive-level triggered.
187 */
188static inline void plic_irq_set_trigger(irq_t irq, bool_t edge_triggered)
189{
190}
191