xref: /linux/drivers/mailbox/qcom-cpucp-mbox.c (revision 7db28abbea0f7dc1ec4fdfdc149db5fbd9e4c994)
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 
50 static inline int channel_number(struct mbox_chan *chan)
51 {
52 	return chan - chan->mbox->chans;
53 }
54 
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 
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 
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 
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 
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 
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 
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