xref: /linux/drivers/mailbox/qcom-cpucp-mbox.c (revision 23d66dbab84e8518943563df2ced14aaab28b77a)
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 		unsigned long flags;
67 
68 		/* Provide mutual exclusion with changes to chan->cl */
69 		spin_lock_irqsave(&chan->lock, flags);
70 		if (chan->cl)
71 			mbox_chan_received_data(chan, &val);
72 		writeq(BIT(i), cpucp->rx_base + APSS_CPUCP_RX_MBOX_CLEAR);
73 		spin_unlock_irqrestore(&chan->lock, flags);
74 	}
75 
76 	return IRQ_HANDLED;
77 }
78 
79 static int qcom_cpucp_mbox_startup(struct mbox_chan *chan)
80 {
81 	struct qcom_cpucp_mbox *cpucp = container_of(chan->mbox, struct qcom_cpucp_mbox, mbox);
82 	unsigned long chan_id = channel_number(chan);
83 	u64 val;
84 
85 	val = readq(cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN);
86 	val |= BIT(chan_id);
87 	writeq(val, cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN);
88 
89 	return 0;
90 }
91 
92 static void qcom_cpucp_mbox_shutdown(struct mbox_chan *chan)
93 {
94 	struct qcom_cpucp_mbox *cpucp = container_of(chan->mbox, struct qcom_cpucp_mbox, mbox);
95 	unsigned long chan_id = channel_number(chan);
96 	u64 val;
97 
98 	val = readq(cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN);
99 	val &= ~BIT(chan_id);
100 	writeq(val, cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN);
101 }
102 
103 static int qcom_cpucp_mbox_send_data(struct mbox_chan *chan, void *data)
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 	u32 *val = data;
108 
109 	writel(*val, cpucp->tx_base + APSS_CPUCP_TX_MBOX_CMD(chan_id) + APSS_CPUCP_MBOX_CMD_OFF);
110 
111 	return 0;
112 }
113 
114 static const struct mbox_chan_ops qcom_cpucp_mbox_chan_ops = {
115 	.startup = qcom_cpucp_mbox_startup,
116 	.send_data = qcom_cpucp_mbox_send_data,
117 	.shutdown = qcom_cpucp_mbox_shutdown
118 };
119 
120 static int qcom_cpucp_mbox_probe(struct platform_device *pdev)
121 {
122 	const struct qcom_cpucp_mbox_data *data;
123 	struct device *dev = &pdev->dev;
124 	struct qcom_cpucp_mbox *cpucp;
125 	struct mbox_controller *mbox;
126 	int irq, ret;
127 
128 	data = of_device_get_match_data(dev);
129 	if (!data)
130 		return dev_err_probe(dev, -EINVAL, "No match data found\n");
131 
132 	cpucp = devm_kzalloc(dev, sizeof(*cpucp), GFP_KERNEL);
133 	if (!cpucp)
134 		return -ENOMEM;
135 
136 	cpucp->chans = devm_kcalloc(dev, data->num_chans, sizeof(*cpucp->chans), GFP_KERNEL);
137 	if (!cpucp->chans)
138 		return -ENOMEM;
139 
140 	cpucp->rx_base = devm_of_iomap(dev, dev->of_node, 0, NULL);
141 	if (IS_ERR(cpucp->rx_base))
142 		return PTR_ERR(cpucp->rx_base);
143 
144 	cpucp->tx_base = devm_of_iomap(dev, dev->of_node, 1, NULL);
145 	if (IS_ERR(cpucp->tx_base))
146 		return PTR_ERR(cpucp->tx_base);
147 
148 	writeq(0, cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN);
149 	writeq(0, cpucp->rx_base + APSS_CPUCP_RX_MBOX_CLEAR);
150 	writeq(0, cpucp->rx_base + APSS_CPUCP_RX_MBOX_MAP);
151 
152 	irq = platform_get_irq(pdev, 0);
153 	if (irq < 0)
154 		return irq;
155 
156 	ret = devm_request_irq(dev, irq, qcom_cpucp_mbox_irq_fn,
157 			       IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND, "apss_cpucp_mbox", cpucp);
158 	if (ret < 0)
159 		return dev_err_probe(dev, ret, "Failed to register irq: %d\n", irq);
160 
161 	writeq(APSS_CPUCP_RX_MBOX_CMD_MASK, cpucp->rx_base + APSS_CPUCP_RX_MBOX_MAP);
162 
163 	mbox = &cpucp->mbox;
164 	mbox->dev = dev;
165 	mbox->num_chans = data->num_chans;
166 	mbox->chans = cpucp->chans;
167 	mbox->ops = &qcom_cpucp_mbox_chan_ops;
168 
169 	ret = devm_mbox_controller_register(dev, mbox);
170 	if (ret)
171 		return dev_err_probe(dev, ret, "Failed to create mailbox\n");
172 
173 	return 0;
174 }
175 
176 static const struct qcom_cpucp_mbox_data qcom_x1e80100_mbox_data = {
177 	.num_chans = 3,
178 };
179 
180 static const struct qcom_cpucp_mbox_data qcom_nord_mbox_data = {
181 	.num_chans = 16,
182 };
183 
184 static const struct of_device_id qcom_cpucp_mbox_of_match[] = {
185 	{ .compatible = "qcom,nord-cpucp-mbox", .data = &qcom_nord_mbox_data },
186 	{ .compatible = "qcom,x1e80100-cpucp-mbox", .data = &qcom_x1e80100_mbox_data },
187 	{}
188 };
189 MODULE_DEVICE_TABLE(of, qcom_cpucp_mbox_of_match);
190 
191 static struct platform_driver qcom_cpucp_mbox_driver = {
192 	.probe = qcom_cpucp_mbox_probe,
193 	.driver = {
194 		.name = "qcom_cpucp_mbox",
195 		.of_match_table = qcom_cpucp_mbox_of_match,
196 	},
197 };
198 
199 static int __init qcom_cpucp_mbox_init(void)
200 {
201 	return platform_driver_register(&qcom_cpucp_mbox_driver);
202 }
203 core_initcall(qcom_cpucp_mbox_init);
204 
205 static void __exit qcom_cpucp_mbox_exit(void)
206 {
207 	platform_driver_unregister(&qcom_cpucp_mbox_driver);
208 }
209 module_exit(qcom_cpucp_mbox_exit);
210 
211 MODULE_DESCRIPTION("QTI CPUCP MBOX Driver");
212 MODULE_LICENSE("GPL");
213