1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
4 */
5
6 #include <linux/bitops.h>
7 #include <linux/interrupt.h>
8 #include <linux/io.h>
9 #include <linux/irq.h>
10 #include <linux/irqdomain.h>
11 #include <linux/mailbox_controller.h>
12 #include <linux/module.h>
13 #include <linux/platform_device.h>
14
15 #define APSS_CPUCP_MBOX_CMD_OFF 0x4
16
17 /* Tx Registers */
18 #define APSS_CPUCP_TX_MBOX_CMD(i) (0x100 + ((i) * 8))
19
20 /* Rx Registers */
21 #define APSS_CPUCP_RX_MBOX_CMD(i) (0x100 + ((i) * 8))
22 #define APSS_CPUCP_RX_MBOX_MAP 0x4000
23 #define APSS_CPUCP_RX_MBOX_STAT 0x4400
24 #define APSS_CPUCP_RX_MBOX_CLEAR 0x4800
25 #define APSS_CPUCP_RX_MBOX_EN 0x4c00
26 #define APSS_CPUCP_RX_MBOX_CMD_MASK GENMASK_ULL(63, 0)
27
28 /**
29 * struct qcom_cpucp_mbox_data - Per-hardware mailbox configuration data
30 * @num_chans: Number of IPC channels supported by this hardware
31 */
32 struct qcom_cpucp_mbox_data {
33 int num_chans;
34 };
35
36 /**
37 * struct qcom_cpucp_mbox - Holder for the mailbox driver
38 * @chans: The mailbox channel
39 * @mbox: The mailbox controller
40 * @tx_base: Base address of the CPUCP tx registers
41 * @rx_base: Base address of the CPUCP rx registers
42 */
43 struct qcom_cpucp_mbox {
44 struct mbox_chan *chans;
45 struct mbox_controller mbox;
46 void __iomem *tx_base;
47 void __iomem *rx_base;
48 };
49
channel_number(struct mbox_chan * chan)50 static inline int channel_number(struct mbox_chan *chan)
51 {
52 return chan - chan->mbox->chans;
53 }
54
qcom_cpucp_mbox_irq_fn(int irq,void * data)55 static irqreturn_t qcom_cpucp_mbox_irq_fn(int irq, void *data)
56 {
57 struct qcom_cpucp_mbox *cpucp = data;
58 u64 status;
59 int i;
60
61 status = readq(cpucp->rx_base + APSS_CPUCP_RX_MBOX_STAT);
62
63 for_each_set_bit(i, (unsigned long *)&status, cpucp->mbox.num_chans) {
64 u32 val = readl(cpucp->rx_base + APSS_CPUCP_RX_MBOX_CMD(i) + APSS_CPUCP_MBOX_CMD_OFF);
65 struct mbox_chan *chan = &cpucp->chans[i];
66 struct mbox_client *cl;
67 unsigned long flags;
68
69 /*
70 * Provide mutual exclusion with changes to chan->cl.
71 * Save cl locally and clear the HW interrupt inside the lock,
72 * then invoke mbox_chan_received_data() outside the lock to
73 * avoid a PREEMPT_RT self-deadlock: mbox_chan_received_data()
74 * can call back into mbox_send_message() via scmi_rx_callback()
75 * -> mailbox_clear_channel(), which re-acquires chan->lock
76 * (converted to an rt_spinlock under PREEMPT_RT).
77 */
78 spin_lock_irqsave(&chan->lock, flags);
79 cl = chan->cl;
80 writeq(BIT(i), cpucp->rx_base + APSS_CPUCP_RX_MBOX_CLEAR);
81 spin_unlock_irqrestore(&chan->lock, flags);
82
83 if (cl)
84 mbox_chan_received_data(chan, &val);
85 }
86
87 return IRQ_HANDLED;
88 }
89
qcom_cpucp_mbox_startup(struct mbox_chan * chan)90 static int qcom_cpucp_mbox_startup(struct mbox_chan *chan)
91 {
92 struct qcom_cpucp_mbox *cpucp = container_of(chan->mbox, struct qcom_cpucp_mbox, mbox);
93 unsigned long chan_id = channel_number(chan);
94 u64 val;
95
96 val = readq(cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN);
97 val |= BIT(chan_id);
98 writeq(val, cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN);
99
100 return 0;
101 }
102
qcom_cpucp_mbox_shutdown(struct mbox_chan * chan)103 static void qcom_cpucp_mbox_shutdown(struct mbox_chan *chan)
104 {
105 struct qcom_cpucp_mbox *cpucp = container_of(chan->mbox, struct qcom_cpucp_mbox, mbox);
106 unsigned long chan_id = channel_number(chan);
107 u64 val;
108
109 val = readq(cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN);
110 val &= ~BIT(chan_id);
111 writeq(val, cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN);
112 }
113
qcom_cpucp_mbox_send_data(struct mbox_chan * chan,void * data)114 static int qcom_cpucp_mbox_send_data(struct mbox_chan *chan, void *data)
115 {
116 struct qcom_cpucp_mbox *cpucp = container_of(chan->mbox, struct qcom_cpucp_mbox, mbox);
117 unsigned long chan_id = channel_number(chan);
118 u32 *val = data;
119
120 /*
121 * mailbox_clear_channel() calls mbox_send_message() with NULL data to
122 * signal the remote side that the channel has been cleared. Nothing
123 * needs to be written to the TX register in that case, so just return.
124 */
125 if (!val)
126 return 0;
127
128 writel(*val, cpucp->tx_base + APSS_CPUCP_TX_MBOX_CMD(chan_id) + APSS_CPUCP_MBOX_CMD_OFF);
129
130 return 0;
131 }
132
133 static const struct mbox_chan_ops qcom_cpucp_mbox_chan_ops = {
134 .startup = qcom_cpucp_mbox_startup,
135 .send_data = qcom_cpucp_mbox_send_data,
136 .shutdown = qcom_cpucp_mbox_shutdown
137 };
138
qcom_cpucp_mbox_probe(struct platform_device * pdev)139 static int qcom_cpucp_mbox_probe(struct platform_device *pdev)
140 {
141 const struct qcom_cpucp_mbox_data *data;
142 struct device *dev = &pdev->dev;
143 struct qcom_cpucp_mbox *cpucp;
144 struct mbox_controller *mbox;
145 int irq, ret;
146
147 data = of_device_get_match_data(dev);
148 if (!data)
149 return dev_err_probe(dev, -EINVAL, "No match data found\n");
150
151 cpucp = devm_kzalloc(dev, sizeof(*cpucp), GFP_KERNEL);
152 if (!cpucp)
153 return -ENOMEM;
154
155 cpucp->chans = devm_kcalloc(dev, data->num_chans, sizeof(*cpucp->chans), GFP_KERNEL);
156 if (!cpucp->chans)
157 return -ENOMEM;
158
159 cpucp->rx_base = devm_of_iomap(dev, dev->of_node, 0, NULL);
160 if (IS_ERR(cpucp->rx_base))
161 return PTR_ERR(cpucp->rx_base);
162
163 cpucp->tx_base = devm_of_iomap(dev, dev->of_node, 1, NULL);
164 if (IS_ERR(cpucp->tx_base))
165 return PTR_ERR(cpucp->tx_base);
166
167 writeq(0, cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN);
168 writeq(0, cpucp->rx_base + APSS_CPUCP_RX_MBOX_CLEAR);
169 writeq(0, cpucp->rx_base + APSS_CPUCP_RX_MBOX_MAP);
170
171 irq = platform_get_irq(pdev, 0);
172 if (irq < 0)
173 return irq;
174
175 ret = devm_request_irq(dev, irq, qcom_cpucp_mbox_irq_fn,
176 IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND, "apss_cpucp_mbox", cpucp);
177 if (ret < 0)
178 return ret;
179
180 writeq(APSS_CPUCP_RX_MBOX_CMD_MASK, cpucp->rx_base + APSS_CPUCP_RX_MBOX_MAP);
181
182 mbox = &cpucp->mbox;
183 mbox->dev = dev;
184 mbox->num_chans = data->num_chans;
185 mbox->chans = cpucp->chans;
186 mbox->ops = &qcom_cpucp_mbox_chan_ops;
187
188 ret = devm_mbox_controller_register(dev, mbox);
189 if (ret)
190 return dev_err_probe(dev, ret, "Failed to create mailbox\n");
191
192 return 0;
193 }
194
195 static const struct qcom_cpucp_mbox_data qcom_x1e80100_mbox_data = {
196 .num_chans = 3,
197 };
198
199 static const struct qcom_cpucp_mbox_data qcom_nord_mbox_data = {
200 .num_chans = 16,
201 };
202
203 static const struct of_device_id qcom_cpucp_mbox_of_match[] = {
204 { .compatible = "qcom,nord-cpucp-mbox", .data = &qcom_nord_mbox_data },
205 { .compatible = "qcom,x1e80100-cpucp-mbox", .data = &qcom_x1e80100_mbox_data },
206 {}
207 };
208 MODULE_DEVICE_TABLE(of, qcom_cpucp_mbox_of_match);
209
210 static struct platform_driver qcom_cpucp_mbox_driver = {
211 .probe = qcom_cpucp_mbox_probe,
212 .driver = {
213 .name = "qcom_cpucp_mbox",
214 .of_match_table = qcom_cpucp_mbox_of_match,
215 },
216 };
217
qcom_cpucp_mbox_init(void)218 static int __init qcom_cpucp_mbox_init(void)
219 {
220 return platform_driver_register(&qcom_cpucp_mbox_driver);
221 }
222 core_initcall(qcom_cpucp_mbox_init);
223
qcom_cpucp_mbox_exit(void)224 static void __exit qcom_cpucp_mbox_exit(void)
225 {
226 platform_driver_unregister(&qcom_cpucp_mbox_driver);
227 }
228 module_exit(qcom_cpucp_mbox_exit);
229
230 MODULE_DESCRIPTION("QTI CPUCP MBOX Driver");
231 MODULE_LICENSE("GPL");
232