Linux-libre 5.7.6-gnu
[librecmc/linux-libre.git] / drivers / mailbox / mailbox-xgene-slimpro.c
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
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         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
248 static int __init slimpro_mbox_init(void)
249 {
250         return platform_driver_register(&slimpro_mbox_driver);
251 }
252
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");