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/spinlock.h>
12 #include <zephyr/drivers/interrupt_controller/riscv_plic.h>
13 LOG_MODULE_REGISTER(mbox_andes_plic_sw);
14
15 #define DT_DRV_COMPAT andestech_mbox_plic_sw
16
17 struct mbox_plic_data {
18 mbox_callback_t *cb;
19 void **user_data;
20 struct k_spinlock lock;
21 };
22
23 struct mbox_plic_conf {
24 uint32_t channel_max;
25 const uint32_t *irq_sources;
26 };
27
is_channel_valid(const struct device * dev,uint32_t ch)28 static inline bool is_channel_valid(const struct device *dev, uint32_t ch)
29 {
30 const struct mbox_plic_conf *conf = dev->config;
31
32 return (ch <= conf->channel_max) && conf->irq_sources[ch];
33 }
34
mbox_plic_send(const struct device * dev,uint32_t ch,const struct mbox_msg * msg)35 static int mbox_plic_send(const struct device *dev, uint32_t ch, const struct mbox_msg *msg)
36 {
37 const struct mbox_plic_conf *conf = dev->config;
38
39 if (msg) {
40 LOG_WRN("Transfer mode is not supported");
41 }
42
43 if (!is_channel_valid(dev, ch)) {
44 return -EINVAL;
45 }
46
47 /* Send the MBOX signal by setting the Pending bit register in the PLIC. */
48 riscv_plic_irq_set_pending(conf->irq_sources[ch]);
49
50 return 0;
51 }
52
mbox_plic_register_callback(const struct device * dev,uint32_t ch,mbox_callback_t cb,void * user_data)53 static int mbox_plic_register_callback(const struct device *dev, uint32_t ch, mbox_callback_t cb,
54 void *user_data)
55 {
56 struct mbox_plic_data *data = dev->data;
57
58 if (!is_channel_valid(dev, ch)) {
59 return -EINVAL;
60 }
61
62 k_spinlock_key_t key = k_spin_lock(&data->lock);
63
64 data->cb[ch] = cb;
65 data->user_data[ch] = user_data;
66
67 k_spin_unlock(&data->lock, key);
68
69 return 0;
70 }
71
mbox_plic_mtu_get(const struct device * dev)72 static int mbox_plic_mtu_get(const struct device *dev)
73 {
74 /* MBOX PLIC only support signalling mode */
75 return -ENOTSUP;
76 }
77
mbox_plic_max_channels_get(const struct device * dev)78 static uint32_t mbox_plic_max_channels_get(const struct device *dev)
79 {
80 const struct mbox_plic_conf *conf = dev->config;
81
82 return conf->channel_max;
83 }
84
mbox_plic_set_enabled(const struct device * dev,uint32_t ch,bool enable)85 static int mbox_plic_set_enabled(const struct device *dev, uint32_t ch, bool enable)
86 {
87 struct mbox_plic_data *data = dev->data;
88 const struct mbox_plic_conf *conf = dev->config;
89
90 if (!is_channel_valid(dev, ch)) {
91 return -EINVAL;
92 }
93
94 if (enable && !(data->cb[ch])) {
95 LOG_WRN("Enabling channel without a registered callback\n");
96 }
97
98 if (enable) {
99 riscv_plic_irq_enable(conf->irq_sources[ch]);
100 } else {
101 riscv_plic_irq_disable(conf->irq_sources[ch]);
102 }
103
104 return 0;
105 }
106
107 static DEVICE_API(mbox, mbox_plic_driver_api) = {
108 .send = mbox_plic_send,
109 .register_callback = mbox_plic_register_callback,
110 .mtu_get = mbox_plic_mtu_get,
111 .max_channels_get = mbox_plic_max_channels_get,
112 .set_enabled = mbox_plic_set_enabled,
113 };
114
115 #define MBOX_PLIC_ISR_FUNCTION_IDX(node, prop, idx, n) \
116 static void mbox_plic_irq_handler##n##_##idx(const struct device *dev) \
117 { \
118 struct mbox_plic_data *data = dev->data; \
119 const uint32_t irq = DT_IRQ_BY_IDX(node, idx, irq); \
120 if (data->cb[irq]) { \
121 data->cb[irq](dev, irq, data->user_data[irq], NULL); \
122 } \
123 }
124 #define MBOX_PLIC_ISR_FUNCTION(n) \
125 DT_INST_FOREACH_PROP_ELEM_SEP_VARGS(n, interrupt_names, MBOX_PLIC_ISR_FUNCTION_IDX, (), n)
126 #define MBOX_PLIC_IRQ_CONNECT_IDX(node, prop, idx, n) \
127 IRQ_CONNECT(DT_IRQN_BY_IDX(node, idx), 1, mbox_plic_irq_handler##n##_##idx, \
128 DEVICE_DT_INST_GET(n), 0)
129 #define MBOX_PLIC_IRQ_CONNECT(n) \
130 DT_INST_FOREACH_PROP_ELEM_SEP_VARGS(n, interrupt_names, MBOX_PLIC_IRQ_CONNECT_IDX, (;), n)
131 #define MBOX_PLIC_INIT_FUNCTION(n) \
132 static int mbox_plic_init##n(const struct device *dev) \
133 { \
134 MBOX_PLIC_IRQ_CONNECT(n); \
135 return 0; \
136 }
137 #define MBOX_PLIC_IRQ_SOURCE_IDX(node, prop, idx) \
138 [DT_IRQ_BY_IDX(node, idx, irq)] = DT_IRQN_BY_IDX(node, idx)
139 #define MBOX_PLIC_IRQ_SOURCE(n) \
140 static const unsigned int irq_sources##n[] = {DT_INST_FOREACH_PROP_ELEM_SEP( \
141 n, interrupt_names, MBOX_PLIC_IRQ_SOURCE_IDX, (,))};
142 #define MBOX_PLIC_DEVICE_INIT(n) \
143 MBOX_PLIC_ISR_FUNCTION(n) \
144 MBOX_PLIC_INIT_FUNCTION(n) \
145 MBOX_PLIC_IRQ_SOURCE(n) \
146 static mbox_callback_t mbox_callback##n[ARRAY_SIZE(irq_sources##n)]; \
147 static void *user_data##n[ARRAY_SIZE(irq_sources##n)]; \
148 static struct mbox_plic_data mbox_plic_data##n = { \
149 .cb = mbox_callback##n, \
150 .user_data = user_data##n, \
151 }; \
152 static const struct mbox_plic_conf mbox_plic_conf##n = { \
153 .channel_max = ARRAY_SIZE(irq_sources##n), \
154 .irq_sources = irq_sources##n, \
155 }; \
156 DEVICE_DT_INST_DEFINE(n, &mbox_plic_init##n, NULL, &mbox_plic_data##n, &mbox_plic_conf##n, \
157 PRE_KERNEL_2, CONFIG_MBOX_INIT_PRIORITY, &mbox_plic_driver_api);
158
159 DT_INST_FOREACH_STATUS_OKAY(MBOX_PLIC_DEVICE_INIT)
160