GNU Linux-libre 5.19-rc6-gnu
[releases.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 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");