1 /*
2  * Copyright (c) 2022 Andes Technology Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/drivers/mbox.h>
8 
9 #define LOG_LEVEL CONFIG_MBOX_LOG_LEVEL
10 #include <zephyr/logging/log.h>
11 #include <zephyr/irq.h>
12 #include <zephyr/spinlock.h>
13 LOG_MODULE_REGISTER(mbox_andes_plic_sw);
14 
15 #define DT_DRV_COMPAT	andestech_plic_sw
16 
17 #define IRQ_REG(n) (n >> 5)
18 #define PLIC_BASE(dev) \
19 	((const struct mbox_andes_conf * const)(dev)->config)->base
20 
21 #define REG_PRIORITY(dev, irq) \
22 	(PLIC_BASE(dev) + 0x0 + (irq << 2))
23 #define REG_PENDING(dev, irq) \
24 	(PLIC_BASE(dev) + 0x1000 + (IRQ_REG(irq) << 2))
25 #define REG_ENABLE(dev, hart, irq) \
26 	(PLIC_BASE(dev) + 0x2000 + (hart << 7) + IRQ_REG(irq))
27 #define REG_CLAIM(dev, hart) \
28 	(PLIC_BASE(dev) + 0x200004 + (hart << 12))
29 
30 #define IPI_NUM DT_INST_PROP(0, channel_max)
31 
32 static struct mbox_andes_data {
33 	mbox_callback_t cb[IPI_NUM];
34 	void *user_data[IPI_NUM];
35 	uint32_t enabled_channel[CONFIG_MP_MAX_NUM_CPUS];
36 #ifdef CONFIG_SCHED_IPI_SUPPORTED
37 	uint32_t reg_cb_channel;
38 	uint32_t ipi_channel;
39 #endif
40 } andes_mbox_data;
41 
42 static struct mbox_andes_conf {
43 	uint32_t base;
44 	uint32_t channel_max;
45 } andes_mbox_conf = {
46 	.base = DT_INST_REG_ADDR(0),
47 	.channel_max = IPI_NUM,
48 };
49 
50 static struct k_spinlock mbox_syn;
51 
plic_sw_irq_set_pending(const struct device * dev,uint32_t irq)52 static void plic_sw_irq_set_pending(const struct device *dev, uint32_t irq)
53 {
54 	uint32_t pend;
55 	k_spinlock_key_t key = k_spin_lock(&mbox_syn);
56 
57 	pend = sys_read32(REG_PENDING(dev, irq));
58 	pend |= BIT(irq);
59 	sys_write32(pend, REG_PENDING(dev, irq));
60 
61 	k_spin_unlock(&mbox_syn, key);
62 }
63 
is_channel_valid(const struct device * dev,uint32_t ch)64 static inline bool is_channel_valid(const struct device *dev, uint32_t ch)
65 {
66 	const struct mbox_andes_conf *conf = dev->config;
67 
68 	return (ch <= conf->channel_max);
69 }
70 
mbox_andes_send(const struct device * dev,uint32_t ch,const struct mbox_msg * msg)71 static int mbox_andes_send(const struct device *dev, uint32_t ch,
72 			   const struct mbox_msg *msg)
73 {
74 	if (msg) {
75 		LOG_WRN("Sending data not supported");
76 	}
77 
78 	if (!is_channel_valid(dev, ch)) {
79 		return -EINVAL;
80 	}
81 
82 	/* Send IPI by triggering the pending register of PLIC SW. */
83 	plic_sw_irq_set_pending(dev, ch + 1);
84 
85 	return 0;
86 }
87 
mbox_andes_register_callback(const struct device * dev,uint32_t ch,mbox_callback_t cb,void * user_data)88 static int mbox_andes_register_callback(const struct device *dev, uint32_t ch,
89 					mbox_callback_t cb, void *user_data)
90 {
91 	struct mbox_andes_data *data = dev->data;
92 	const struct mbox_andes_conf *conf = dev->config;
93 	int ret = 0;
94 
95 	k_spinlock_key_t key = k_spin_lock(&mbox_syn);
96 
97 	if (ch > conf->channel_max) {
98 		ret = -EINVAL;
99 		goto out;
100 	}
101 
102 #ifdef CONFIG_SCHED_IPI_SUPPORTED
103 	if (ch & data->ipi_channel & data->reg_cb_channel) {
104 		ret = -EALREADY;
105 		goto out;
106 	}
107 
108 	data->reg_cb_channel |= BIT(ch);
109 #endif
110 	data->cb[ch] = cb;
111 	data->user_data[ch] = user_data;
112 
113 out:
114 	k_spin_unlock(&mbox_syn, key);
115 
116 	return 0;
117 }
118 
mbox_andes_mtu_get(const struct device * dev)119 static int mbox_andes_mtu_get(const struct device *dev)
120 {
121 	/* We only support signalling */
122 	return 0;
123 }
124 
mbox_andes_max_channels_get(const struct device * dev)125 static uint32_t mbox_andes_max_channels_get(const struct device *dev)
126 {
127 	const struct mbox_andes_conf *conf = dev->config;
128 
129 	return conf->channel_max;
130 }
131 
mbox_andes_set_enabled(const struct device * dev,uint32_t ch,bool enable)132 static int mbox_andes_set_enabled(const struct device *dev, uint32_t ch,
133 				  bool enable)
134 {
135 	uint32_t en, is_enabled_ch, hartid, cpu_id, irq;
136 	struct mbox_andes_data *data = dev->data;
137 	int ret = 0;
138 
139 	k_spinlock_key_t key = k_spin_lock(&mbox_syn);
140 
141 	if (!is_channel_valid(dev, ch)) {
142 		ret = -EINVAL;
143 		goto out;
144 	}
145 
146 	irq = ch + 1;
147 	hartid = arch_proc_id();
148 	cpu_id = _current_cpu->id;
149 
150 	is_enabled_ch = data->enabled_channel[cpu_id] & BIT(ch);
151 
152 	if ((!enable && !is_enabled_ch) || (enable && is_enabled_ch)) {
153 		ret = -EALREADY;
154 		goto out;
155 	}
156 
157 	if (enable && !(data->cb[ch])) {
158 		LOG_WRN("Enabling channel without a registered callback\n");
159 	}
160 
161 	en = sys_read32(REG_ENABLE(dev, hartid, irq));
162 
163 	if (enable) {
164 		data->enabled_channel[cpu_id] |= BIT(ch);
165 		sys_write32(1, REG_PRIORITY(dev, irq));
166 		en |= BIT(irq);
167 	} else {
168 		data->enabled_channel[cpu_id] &= ~BIT(ch);
169 		en &= ~BIT(irq);
170 	}
171 
172 	sys_write32(en, REG_ENABLE(dev, hartid, irq));
173 out:
174 	k_spin_unlock(&mbox_syn, key);
175 
176 	return ret;
177 }
178 
andes_plic_sw_irq_handler(const struct device * dev)179 static void andes_plic_sw_irq_handler(const struct device *dev)
180 {
181 	struct mbox_andes_data *data = dev->data;
182 	uint32_t irq, ch, hartid;
183 
184 	hartid = arch_proc_id();
185 
186 	/* PLIC claim: Get the SW IRQ number generating the interrupt. */
187 	irq = sys_read32(REG_CLAIM(dev, hartid));
188 	ch = irq - 1;
189 
190 	if (irq) {
191 		sys_write32(irq, REG_CLAIM(dev, hartid));
192 
193 		if (data->cb[ch]) {
194 			/* Only one MAILBOX, id is unused and set to 0 */
195 			data->cb[ch](dev, ch, data->user_data[ch], NULL);
196 		}
197 	}
198 }
199 
mbox_andes_init(const struct device * dev)200 static int mbox_andes_init(const struct device *dev)
201 {
202 	/* Setup IRQ handler for PLIC SW driver */
203 	IRQ_CONNECT(RISCV_IRQ_MSOFT, 1,
204 		    andes_plic_sw_irq_handler, DEVICE_DT_INST_GET(0), 0);
205 
206 #ifndef CONFIG_SMP
207 	irq_enable(RISCV_IRQ_MSOFT);
208 #endif
209 	return 0;
210 }
211 
212 static const struct mbox_driver_api mbox_andes_driver_api = {
213 	.send = mbox_andes_send,
214 	.register_callback = mbox_andes_register_callback,
215 	.mtu_get = mbox_andes_mtu_get,
216 	.max_channels_get = mbox_andes_max_channels_get,
217 	.set_enabled = mbox_andes_set_enabled,
218 };
219 
220 DEVICE_DT_INST_DEFINE(0, mbox_andes_init, NULL, &andes_mbox_data,
221 		      &andes_mbox_conf, PRE_KERNEL_1, CONFIG_MBOX_INIT_PRIORITY,
222 		      &mbox_andes_driver_api);
223