1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * APM X-Gene SLIMpro MailBox Driver
4  *
5  * Copyright (c) 2015, Applied Micro Circuits Corporation
6  * Author: Feng Kan fkan@apm.com
7  */
8 #include <linux/acpi.h>
9 #include <linux/delay.h>
10 #include <linux/interrupt.h>
11 #include <linux/io.h>
12 #include <linux/mailbox_controller.h>
13 #include <linux/module.h>
14 #include <linux/of.h>
15 #include <linux/platform_device.h>
16 #include <linux/spinlock.h>
17 
18 #define MBOX_CON_NAME			"slimpro-mbox"
19 #define MBOX_REG_SET_OFFSET		0x1000
20 #define MBOX_CNT			8
21 #define MBOX_STATUS_AVAIL_MASK		BIT(16)
22 #define MBOX_STATUS_ACK_MASK		BIT(0)
23 
24 /* Configuration and Status Registers */
25 #define REG_DB_IN		0x00
26 #define REG_DB_DIN0		0x04
27 #define REG_DB_DIN1		0x08
28 #define REG_DB_OUT		0x10
29 #define REG_DB_DOUT0		0x14
30 #define REG_DB_DOUT1		0x18
31 #define REG_DB_STAT		0x20
32 #define REG_DB_STATMASK		0x24
33 
34 /**
35  * X-Gene SlimPRO mailbox channel information
36  *
37  * @dev:	Device to which it is attached
38  * @chan:	Pointer to mailbox communication channel
39  * @reg:	Base address to access channel registers
40  * @irq:	Interrupt number of the channel
41  * @rx_msg:	Received message storage
42  */
43 struct slimpro_mbox_chan {
44 	struct device		*dev;
45 	struct mbox_chan	*chan;
46 	void __iomem		*reg;
47 	int			irq;
48 	u32			rx_msg[3];
49 };
50 
51 /**
52  * X-Gene SlimPRO Mailbox controller data
53  *
54  * X-Gene SlimPRO Mailbox controller has 8 communication channels.
55  * Each channel has a separate IRQ number assigned to it.
56  *
57  * @mb_ctrl:	Representation of the communication channel controller
58  * @mc:		Array of SlimPRO mailbox channels of the controller
59  * @chans:	Array of mailbox communication channels
60  *
61  */
62 struct slimpro_mbox {
63 	struct mbox_controller		mb_ctrl;
64 	struct slimpro_mbox_chan	mc[MBOX_CNT];
65 	struct mbox_chan		chans[MBOX_CNT];
66 };
67 
68 static void mb_chan_send_msg(struct slimpro_mbox_chan *mb_chan, u32 *msg)
69 {
70 	writel(msg[1], mb_chan->reg + REG_DB_DOUT0);
71 	writel(msg[2], mb_chan->reg + REG_DB_DOUT1);
72 	writel(msg[0], mb_chan->reg + REG_DB_OUT);
73 }
74 
75 static void mb_chan_recv_msg(struct slimpro_mbox_chan *mb_chan)
76 {
77 	mb_chan->rx_msg[1] = readl(mb_chan->reg + REG_DB_DIN0);
78 	mb_chan->rx_msg[2] = readl(mb_chan->reg + REG_DB_DIN1);
79 	mb_chan->rx_msg[0] = readl(mb_chan->reg + REG_DB_IN);
80 }
81 
82 static int mb_chan_status_ack(struct slimpro_mbox_chan *mb_chan)
83 {
84 	u32 val = readl(mb_chan->reg + REG_DB_STAT);
85 
86 	if (val & MBOX_STATUS_ACK_MASK) {
87 		writel(MBOX_STATUS_ACK_MASK, mb_chan->reg + REG_DB_STAT);
88 		return 1;
89 	}
90 	return 0;
91 }
92 
93 static int mb_chan_status_avail(struct slimpro_mbox_chan *mb_chan)
94 {
95 	u32 val = readl(mb_chan->reg + REG_DB_STAT);
96 
97 	if (val & MBOX_STATUS_AVAIL_MASK) {
98 		mb_chan_recv_msg(mb_chan);
99 		writel(MBOX_STATUS_AVAIL_MASK, mb_chan->reg + REG_DB_STAT);
100 		return 1;
101 	}
102 	return 0;
103 }
104 
105 static irqreturn_t slimpro_mbox_irq(int irq, void *id)
106 {
107 	struct slimpro_mbox_chan *mb_chan = id;
108 
109 	if (mb_chan_status_ack(mb_chan))
110 		mbox_chan_txdone(mb_chan->chan, 0);
111 
112 	if (mb_chan_status_avail(mb_chan))
113 		mbox_chan_received_data(mb_chan->chan, mb_chan->rx_msg);
114 
115 	return IRQ_HANDLED;
116 }
117 
118 static int slimpro_mbox_send_data(struct mbox_chan *chan, void *msg)
119 {
120 	struct slimpro_mbox_chan *mb_chan = chan->con_priv;
121 
122 	mb_chan_send_msg(mb_chan, msg);
123 	return 0;
124 }
125 
126 static int slimpro_mbox_startup(struct mbox_chan *chan)
127 {
128 	struct slimpro_mbox_chan *mb_chan = chan->con_priv;
129 	int rc;
130 	u32 val;
131 
132 	rc = devm_request_irq(mb_chan->dev, mb_chan->irq, slimpro_mbox_irq, 0,
133 			      MBOX_CON_NAME, mb_chan);
134 	if (unlikely(rc)) {
135 		dev_err(mb_chan->dev, "failed to register mailbox interrupt %d\n",
136 			mb_chan->irq);
137 		return rc;
138 	}
139 
140 	/* Enable HW interrupt */
141 	writel(MBOX_STATUS_ACK_MASK | MBOX_STATUS_AVAIL_MASK,
142 	       mb_chan->reg + REG_DB_STAT);
143 	/* Unmask doorbell status interrupt */
144 	val = readl(mb_chan->reg + REG_DB_STATMASK);
145 	val &= ~(MBOX_STATUS_ACK_MASK | MBOX_STATUS_AVAIL_MASK);
146 	writel(val, mb_chan->reg + REG_DB_STATMASK);
147 
148 	return 0;
149 }
150 
151 static void slimpro_mbox_shutdown(struct mbox_chan *chan)
152 {
153 	struct slimpro_mbox_chan *mb_chan = chan->con_priv;
154 	u32 val;
155 
156 	/* Mask doorbell status interrupt */
157 	val = readl(mb_chan->reg + REG_DB_STATMASK);
158 	val |= (MBOX_STATUS_ACK_MASK | MBOX_STATUS_AVAIL_MASK);
159 	writel(val, mb_chan->reg + REG_DB_STATMASK);
160 
161 	devm_free_irq(mb_chan->dev, mb_chan->irq, mb_chan);
162 }
163 
164 static const struct mbox_chan_ops slimpro_mbox_ops = {
165 	.send_data = slimpro_mbox_send_data,
166 	.startup = slimpro_mbox_startup,
167 	.shutdown = slimpro_mbox_shutdown,
168 };
169 
170 static int slimpro_mbox_probe(struct platform_device *pdev)
171 {
172 	struct slimpro_mbox *ctx;
173 	void __iomem *mb_base;
174 	int rc;
175 	int i;
176 
177 	ctx = devm_kzalloc(&pdev->dev, sizeof(struct slimpro_mbox), GFP_KERNEL);
178 	if (!ctx)
179 		return -ENOMEM;
180 
181 	platform_set_drvdata(pdev, ctx);
182 
183 	mb_base = devm_platform_ioremap_resource(pdev, 0);
184 	if (IS_ERR(mb_base))
185 		return PTR_ERR(mb_base);
186 
187 	/* Setup mailbox links */
188 	for (i = 0; i < MBOX_CNT; i++) {
189 		ctx->mc[i].irq = platform_get_irq(pdev, i);
190 		if (ctx->mc[i].irq < 0) {
191 			if (i == 0) {
192 				dev_err(&pdev->dev, "no available IRQ\n");
193 				return -EINVAL;
194 			}
195 			dev_info(&pdev->dev, "no IRQ for channel %d\n", i);
196 			break;
197 		}
198 
199 		ctx->mc[i].dev = &pdev->dev;
200 		ctx->mc[i].reg = mb_base + i * MBOX_REG_SET_OFFSET;
201 		ctx->mc[i].chan = &ctx->chans[i];
202 		ctx->chans[i].con_priv = &ctx->mc[i];
203 	}
204 
205 	/* Setup mailbox controller */
206 	ctx->mb_ctrl.dev = &pdev->dev;
207 	ctx->mb_ctrl.chans = ctx->chans;
208 	ctx->mb_ctrl.txdone_irq = true;
209 	ctx->mb_ctrl.ops = &slimpro_mbox_ops;
210 	ctx->mb_ctrl.num_chans = i;
211 
212 	rc = devm_mbox_controller_register(&pdev->dev, &ctx->mb_ctrl);
213 	if (rc) {
214 		dev_err(&pdev->dev,
215 			"APM X-Gene SLIMpro MailBox register failed:%d\n", rc);
216 		return rc;
217 	}
218 
219 	dev_info(&pdev->dev, "APM X-Gene SLIMpro MailBox registered\n");
220 	return 0;
221 }
222 
223 static const struct of_device_id slimpro_of_match[] = {
224 	{.compatible = "apm,xgene-slimpro-mbox" },
225 	{ },
226 };
227 MODULE_DEVICE_TABLE(of, slimpro_of_match);
228 
229 #ifdef CONFIG_ACPI
230 static const struct acpi_device_id slimpro_acpi_ids[] = {
231 	{"APMC0D01", 0},
232 	{}
233 };
234 MODULE_DEVICE_TABLE(acpi, slimpro_acpi_ids);
235 #endif
236 
237 static struct platform_driver slimpro_mbox_driver = {
238 	.probe	= slimpro_mbox_probe,
239 	.driver	= {
240 		.name = "xgene-slimpro-mbox",
241 		.of_match_table = of_match_ptr(slimpro_of_match),
242 		.acpi_match_table = ACPI_PTR(slimpro_acpi_ids)
243 	},
244 };
245 
246 static int __init slimpro_mbox_init(void)
247 {
248 	return platform_driver_register(&slimpro_mbox_driver);
249 }
250 
251 static void __exit slimpro_mbox_exit(void)
252 {
253 	platform_driver_unregister(&slimpro_mbox_driver);
254 }
255 
256 subsys_initcall(slimpro_mbox_init);
257 module_exit(slimpro_mbox_exit);
258 
259 MODULE_DESCRIPTION("APM X-Gene SLIMpro Mailbox Driver");
260 MODULE_LICENSE("GPL");
261