• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 commnunication channels.
55  * Each channel has a separate IRQ number assgined to it.
56  *
57  * @mb_ctrl:	Representation of the commnunication 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 
mb_chan_send_msg(struct slimpro_mbox_chan * mb_chan,u32 * msg)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 
mb_chan_recv_msg(struct slimpro_mbox_chan * mb_chan)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 
mb_chan_status_ack(struct slimpro_mbox_chan * mb_chan)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 
mb_chan_status_avail(struct slimpro_mbox_chan * mb_chan)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 
slimpro_mbox_irq(int irq,void * id)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 
slimpro_mbox_send_data(struct mbox_chan * chan,void * msg)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 
slimpro_mbox_startup(struct mbox_chan * chan)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 
slimpro_mbox_shutdown(struct mbox_chan * chan)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 
slimpro_mbox_probe(struct platform_device * pdev)170 static int slimpro_mbox_probe(struct platform_device *pdev)
171 {
172 	struct slimpro_mbox *ctx;
173 	struct resource *regs;
174 	void __iomem *mb_base;
175 	int rc;
176 	int i;
177 
178 	ctx = devm_kzalloc(&pdev->dev, sizeof(struct slimpro_mbox), GFP_KERNEL);
179 	if (!ctx)
180 		return -ENOMEM;
181 
182 	platform_set_drvdata(pdev, ctx);
183 
184 	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
185 	mb_base = devm_ioremap_resource(&pdev->dev, regs);
186 	if (IS_ERR(mb_base))
187 		return PTR_ERR(mb_base);
188 
189 	/* Setup mailbox links */
190 	for (i = 0; i < MBOX_CNT; i++) {
191 		ctx->mc[i].irq = platform_get_irq(pdev, i);
192 		if (ctx->mc[i].irq < 0) {
193 			if (i == 0) {
194 				dev_err(&pdev->dev, "no available IRQ\n");
195 				return -EINVAL;
196 			}
197 			dev_info(&pdev->dev, "no IRQ for channel %d\n", i);
198 			break;
199 		}
200 
201 		ctx->mc[i].dev = &pdev->dev;
202 		ctx->mc[i].reg = mb_base + i * MBOX_REG_SET_OFFSET;
203 		ctx->mc[i].chan = &ctx->chans[i];
204 		ctx->chans[i].con_priv = &ctx->mc[i];
205 	}
206 
207 	/* Setup mailbox controller */
208 	ctx->mb_ctrl.dev = &pdev->dev;
209 	ctx->mb_ctrl.chans = ctx->chans;
210 	ctx->mb_ctrl.txdone_irq = true;
211 	ctx->mb_ctrl.ops = &slimpro_mbox_ops;
212 	ctx->mb_ctrl.num_chans = i;
213 
214 	rc = devm_mbox_controller_register(&pdev->dev, &ctx->mb_ctrl);
215 	if (rc) {
216 		dev_err(&pdev->dev,
217 			"APM X-Gene SLIMpro MailBox register failed:%d\n", rc);
218 		return rc;
219 	}
220 
221 	dev_info(&pdev->dev, "APM X-Gene SLIMpro MailBox registered\n");
222 	return 0;
223 }
224 
225 static const struct of_device_id slimpro_of_match[] = {
226 	{.compatible = "apm,xgene-slimpro-mbox" },
227 	{ },
228 };
229 MODULE_DEVICE_TABLE(of, slimpro_of_match);
230 
231 #ifdef CONFIG_ACPI
232 static const struct acpi_device_id slimpro_acpi_ids[] = {
233 	{"APMC0D01", 0},
234 	{}
235 };
236 MODULE_DEVICE_TABLE(acpi, slimpro_acpi_ids);
237 #endif
238 
239 static struct platform_driver slimpro_mbox_driver = {
240 	.probe	= slimpro_mbox_probe,
241 	.driver	= {
242 		.name = "xgene-slimpro-mbox",
243 		.of_match_table = of_match_ptr(slimpro_of_match),
244 		.acpi_match_table = ACPI_PTR(slimpro_acpi_ids)
245 	},
246 };
247 
slimpro_mbox_init(void)248 static int __init slimpro_mbox_init(void)
249 {
250 	return platform_driver_register(&slimpro_mbox_driver);
251 }
252 
slimpro_mbox_exit(void)253 static void __exit slimpro_mbox_exit(void)
254 {
255 	platform_driver_unregister(&slimpro_mbox_driver);
256 }
257 
258 subsys_initcall(slimpro_mbox_init);
259 module_exit(slimpro_mbox_exit);
260 
261 MODULE_DESCRIPTION("APM X-Gene SLIMpro Mailbox Driver");
262 MODULE_LICENSE("GPL");
263