summaryrefslogtreecommitdiffstats
path: root/drivers/mailbox
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-12-15 19:03:25 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2016-12-15 19:03:25 -0500
commit991688bfc63550b8c7ab9fb1de2feb44e3071d29 (patch)
tree1566341d55e1cc56bf9153bc9accc01976b7c87a /drivers/mailbox
parent482c3e8835e9e9b325aad295c21bd9e965a11006 (diff)
parent2ada9593224ccc0f6a9368778dc55a59b92aff10 (diff)
Merge tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC driver updates from Arnd Bergmann: "Driver updates for ARM SoCs, including a couple of newly added drivers: - A new driver for the power management controller on TI Keystone - Support for the prerelease "SCPI" firmware protocol that ended up being shipped by Amlogic in their GXBB SoC. - A soc_device can now be matched using a glob from inside the kernel, when another driver wants to know the specific chip it is running on and cannot find out from DT, firmware or hardware. - Renesas SoCs now support identification through the soc_device interface, both in user space and kernel. - Renesas r8a7743 and r8a7745 gain support for their system controller - A new checking module for the ARM "PSCI" (not to be confused with "SCPI" mentioned above) firmware interface. - A new driver for the Tegra GMI memory interface - Support for the Tegra firmware interfaces with their power management controllers As usual, the updates for the reset controller framework are merged here, as they tend to touch multiple SoCs as well, including a new driver for the Oxford (now Broadcom) OX820 chip and the Tegra bpmp interface. The existing drivers for Atmel, Qualcomm, NVIDIA, TI Davinci, and Rockchips SoCs see some further updates" * tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (76 commits) misc: sram: remove useless #ifdef drivers: psci: Allow PSCI node to be disabled drivers: psci: PSCI checker module soc: renesas: Identify SoC and register with the SoC bus firmware: qcom: scm: Return PTR_ERR when devm_clk_get fails firmware: qcom: scm: Remove core, iface and bus clocks dependency dt-bindings: firmware: scm: Add MSM8996 DT bindings memory: da8xx-ddrctl: drop the call to of_flat_dt_get_machine_name() bus: da8xx-mstpri: drop the call to of_flat_dt_get_machine_name() ARM: shmobile: Document DT bindings for Product Register soc: renesas: rcar-sysc: add R8A7745 support reset: Add Tegra BPMP reset driver dt-bindings: firmware: Allow child nodes inside the Tegra BPMP dt-bindings: Add power domains to Tegra BPMP firmware firmware: tegra: Add BPMP support firmware: tegra: Add IVC library dt-bindings: firmware: Add bindings for Tegra BPMP mailbox: tegra-hsp: Use after free in tegra_hsp_remove_doorbells() mailbox: Add Tegra HSP driver firmware: arm_scpi: add support for pre-v1.0 SCPI compatible ...
Diffstat (limited to 'drivers/mailbox')
-rw-r--r--drivers/mailbox/Kconfig9
-rw-r--r--drivers/mailbox/Makefile2
-rw-r--r--drivers/mailbox/tegra-hsp.c479
3 files changed, 490 insertions, 0 deletions
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index 11eebfe8a4cb..ceff415f201c 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -124,6 +124,15 @@ config MAILBOX_TEST
124 Test client to help with testing new Controller driver 124 Test client to help with testing new Controller driver
125 implementations. 125 implementations.
126 126
127config TEGRA_HSP_MBOX
128 bool "Tegra HSP (Hardware Synchronization Primitives) Driver"
129 depends on ARCH_TEGRA_186_SOC
130 help
131 The Tegra HSP driver is used for the interprocessor communication
132 between different remote processors and host processors on Tegra186
133 and later SoCs. Say Y here if you want to have this support.
134 If unsure say N.
135
127config XGENE_SLIMPRO_MBOX 136config XGENE_SLIMPRO_MBOX
128 tristate "APM SoC X-Gene SLIMpro Mailbox Controller" 137 tristate "APM SoC X-Gene SLIMpro Mailbox Controller"
129 depends on ARCH_XGENE 138 depends on ARCH_XGENE
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index ace6fed8fea9..7dde4f609ae8 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -29,3 +29,5 @@ obj-$(CONFIG_XGENE_SLIMPRO_MBOX) += mailbox-xgene-slimpro.o
29obj-$(CONFIG_HI6220_MBOX) += hi6220-mailbox.o 29obj-$(CONFIG_HI6220_MBOX) += hi6220-mailbox.o
30 30
31obj-$(CONFIG_BCM_PDC_MBOX) += bcm-pdc-mailbox.o 31obj-$(CONFIG_BCM_PDC_MBOX) += bcm-pdc-mailbox.o
32
33obj-$(CONFIG_TEGRA_HSP_MBOX) += tegra-hsp.o
diff --git a/drivers/mailbox/tegra-hsp.c b/drivers/mailbox/tegra-hsp.c
new file mode 100644
index 000000000000..0cde356c11ab
--- /dev/null
+++ b/drivers/mailbox/tegra-hsp.c
@@ -0,0 +1,479 @@
1/*
2 * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 */
13
14#include <linux/interrupt.h>
15#include <linux/io.h>
16#include <linux/mailbox_controller.h>
17#include <linux/of.h>
18#include <linux/of_device.h>
19#include <linux/platform_device.h>
20#include <linux/slab.h>
21
22#include <dt-bindings/mailbox/tegra186-hsp.h>
23
24#define HSP_INT_DIMENSIONING 0x380
25#define HSP_nSM_SHIFT 0
26#define HSP_nSS_SHIFT 4
27#define HSP_nAS_SHIFT 8
28#define HSP_nDB_SHIFT 12
29#define HSP_nSI_SHIFT 16
30#define HSP_nINT_MASK 0xf
31
32#define HSP_DB_TRIGGER 0x0
33#define HSP_DB_ENABLE 0x4
34#define HSP_DB_RAW 0x8
35#define HSP_DB_PENDING 0xc
36
37#define HSP_DB_CCPLEX 1
38#define HSP_DB_BPMP 3
39#define HSP_DB_MAX 7
40
41struct tegra_hsp_channel;
42struct tegra_hsp;
43
44struct tegra_hsp_channel {
45 struct tegra_hsp *hsp;
46 struct mbox_chan *chan;
47 void __iomem *regs;
48};
49
50struct tegra_hsp_doorbell {
51 struct tegra_hsp_channel channel;
52 struct list_head list;
53 const char *name;
54 unsigned int master;
55 unsigned int index;
56};
57
58struct tegra_hsp_db_map {
59 const char *name;
60 unsigned int master;
61 unsigned int index;
62};
63
64struct tegra_hsp_soc {
65 const struct tegra_hsp_db_map *map;
66};
67
68struct tegra_hsp {
69 const struct tegra_hsp_soc *soc;
70 struct mbox_controller mbox;
71 void __iomem *regs;
72 unsigned int irq;
73 unsigned int num_sm;
74 unsigned int num_as;
75 unsigned int num_ss;
76 unsigned int num_db;
77 unsigned int num_si;
78 spinlock_t lock;
79
80 struct list_head doorbells;
81};
82
83static inline struct tegra_hsp *
84to_tegra_hsp(struct mbox_controller *mbox)
85{
86 return container_of(mbox, struct tegra_hsp, mbox);
87}
88
89static inline u32 tegra_hsp_readl(struct tegra_hsp *hsp, unsigned int offset)
90{
91 return readl(hsp->regs + offset);
92}
93
94static inline void tegra_hsp_writel(struct tegra_hsp *hsp, u32 value,
95 unsigned int offset)
96{
97 writel(value, hsp->regs + offset);
98}
99
100static inline u32 tegra_hsp_channel_readl(struct tegra_hsp_channel *channel,
101 unsigned int offset)
102{
103 return readl(channel->regs + offset);
104}
105
106static inline void tegra_hsp_channel_writel(struct tegra_hsp_channel *channel,
107 u32 value, unsigned int offset)
108{
109 writel(value, channel->regs + offset);
110}
111
112static bool tegra_hsp_doorbell_can_ring(struct tegra_hsp_doorbell *db)
113{
114 u32 value;
115
116 value = tegra_hsp_channel_readl(&db->channel, HSP_DB_ENABLE);
117
118 return (value & BIT(TEGRA_HSP_DB_MASTER_CCPLEX)) != 0;
119}
120
121static struct tegra_hsp_doorbell *
122__tegra_hsp_doorbell_get(struct tegra_hsp *hsp, unsigned int master)
123{
124 struct tegra_hsp_doorbell *entry;
125
126 list_for_each_entry(entry, &hsp->doorbells, list)
127 if (entry->master == master)
128 return entry;
129
130 return NULL;
131}
132
133static struct tegra_hsp_doorbell *
134tegra_hsp_doorbell_get(struct tegra_hsp *hsp, unsigned int master)
135{
136 struct tegra_hsp_doorbell *db;
137 unsigned long flags;
138
139 spin_lock_irqsave(&hsp->lock, flags);
140 db = __tegra_hsp_doorbell_get(hsp, master);
141 spin_unlock_irqrestore(&hsp->lock, flags);
142
143 return db;
144}
145
146static irqreturn_t tegra_hsp_doorbell_irq(int irq, void *data)
147{
148 struct tegra_hsp *hsp = data;
149 struct tegra_hsp_doorbell *db;
150 unsigned long master, value;
151
152 db = tegra_hsp_doorbell_get(hsp, TEGRA_HSP_DB_MASTER_CCPLEX);
153 if (!db)
154 return IRQ_NONE;
155
156 value = tegra_hsp_channel_readl(&db->channel, HSP_DB_PENDING);
157 tegra_hsp_channel_writel(&db->channel, value, HSP_DB_PENDING);
158
159 spin_lock(&hsp->lock);
160
161 for_each_set_bit(master, &value, hsp->mbox.num_chans) {
162 struct tegra_hsp_doorbell *db;
163
164 db = __tegra_hsp_doorbell_get(hsp, master);
165 /*
166 * Depending on the bootloader chain, the CCPLEX doorbell will
167 * have some doorbells enabled, which means that requesting an
168 * interrupt will immediately fire.
169 *
170 * In that case, db->channel.chan will still be NULL here and
171 * cause a crash if not properly guarded.
172 *
173 * It remains to be seen if ignoring the doorbell in that case
174 * is the correct solution.
175 */
176 if (db && db->channel.chan)
177 mbox_chan_received_data(db->channel.chan, NULL);
178 }
179
180 spin_unlock(&hsp->lock);
181
182 return IRQ_HANDLED;
183}
184
185static struct tegra_hsp_channel *
186tegra_hsp_doorbell_create(struct tegra_hsp *hsp, const char *name,
187 unsigned int master, unsigned int index)
188{
189 struct tegra_hsp_doorbell *db;
190 unsigned int offset;
191 unsigned long flags;
192
193 db = kzalloc(sizeof(*db), GFP_KERNEL);
194 if (!db)
195 return ERR_PTR(-ENOMEM);
196
197 offset = (1 + (hsp->num_sm / 2) + hsp->num_ss + hsp->num_as) << 16;
198 offset += index * 0x100;
199
200 db->channel.regs = hsp->regs + offset;
201 db->channel.hsp = hsp;
202
203 db->name = kstrdup_const(name, GFP_KERNEL);
204 db->master = master;
205 db->index = index;
206
207 spin_lock_irqsave(&hsp->lock, flags);
208 list_add_tail(&db->list, &hsp->doorbells);
209 spin_unlock_irqrestore(&hsp->lock, flags);
210
211 return &db->channel;
212}
213
214static void __tegra_hsp_doorbell_destroy(struct tegra_hsp_doorbell *db)
215{
216 list_del(&db->list);
217 kfree_const(db->name);
218 kfree(db);
219}
220
221static int tegra_hsp_doorbell_send_data(struct mbox_chan *chan, void *data)
222{
223 struct tegra_hsp_doorbell *db = chan->con_priv;
224
225 tegra_hsp_channel_writel(&db->channel, 1, HSP_DB_TRIGGER);
226
227 return 0;
228}
229
230static int tegra_hsp_doorbell_startup(struct mbox_chan *chan)
231{
232 struct tegra_hsp_doorbell *db = chan->con_priv;
233 struct tegra_hsp *hsp = db->channel.hsp;
234 struct tegra_hsp_doorbell *ccplex;
235 unsigned long flags;
236 u32 value;
237
238 if (db->master >= hsp->mbox.num_chans) {
239 dev_err(hsp->mbox.dev,
240 "invalid master ID %u for HSP channel\n",
241 db->master);
242 return -EINVAL;
243 }
244
245 ccplex = tegra_hsp_doorbell_get(hsp, TEGRA_HSP_DB_MASTER_CCPLEX);
246 if (!ccplex)
247 return -ENODEV;
248
249 if (!tegra_hsp_doorbell_can_ring(db))
250 return -ENODEV;
251
252 spin_lock_irqsave(&hsp->lock, flags);
253
254 value = tegra_hsp_channel_readl(&ccplex->channel, HSP_DB_ENABLE);
255 value |= BIT(db->master);
256 tegra_hsp_channel_writel(&ccplex->channel, value, HSP_DB_ENABLE);
257
258 spin_unlock_irqrestore(&hsp->lock, flags);
259
260 return 0;
261}
262
263static void tegra_hsp_doorbell_shutdown(struct mbox_chan *chan)
264{
265 struct tegra_hsp_doorbell *db = chan->con_priv;
266 struct tegra_hsp *hsp = db->channel.hsp;
267 struct tegra_hsp_doorbell *ccplex;
268 unsigned long flags;
269 u32 value;
270
271 ccplex = tegra_hsp_doorbell_get(hsp, TEGRA_HSP_DB_MASTER_CCPLEX);
272 if (!ccplex)
273 return;
274
275 spin_lock_irqsave(&hsp->lock, flags);
276
277 value = tegra_hsp_channel_readl(&ccplex->channel, HSP_DB_ENABLE);
278 value &= ~BIT(db->master);
279 tegra_hsp_channel_writel(&ccplex->channel, value, HSP_DB_ENABLE);
280
281 spin_unlock_irqrestore(&hsp->lock, flags);
282}
283
284static const struct mbox_chan_ops tegra_hsp_doorbell_ops = {
285 .send_data = tegra_hsp_doorbell_send_data,
286 .startup = tegra_hsp_doorbell_startup,
287 .shutdown = tegra_hsp_doorbell_shutdown,
288};
289
290static struct mbox_chan *of_tegra_hsp_xlate(struct mbox_controller *mbox,
291 const struct of_phandle_args *args)
292{
293 struct tegra_hsp_channel *channel = ERR_PTR(-ENODEV);
294 struct tegra_hsp *hsp = to_tegra_hsp(mbox);
295 unsigned int type = args->args[0];
296 unsigned int master = args->args[1];
297 struct tegra_hsp_doorbell *db;
298 struct mbox_chan *chan;
299 unsigned long flags;
300 unsigned int i;
301
302 switch (type) {
303 case TEGRA_HSP_MBOX_TYPE_DB:
304 db = tegra_hsp_doorbell_get(hsp, master);
305 if (db)
306 channel = &db->channel;
307
308 break;
309
310 default:
311 break;
312 }
313
314 if (IS_ERR(channel))
315 return ERR_CAST(channel);
316
317 spin_lock_irqsave(&hsp->lock, flags);
318
319 for (i = 0; i < hsp->mbox.num_chans; i++) {
320 chan = &hsp->mbox.chans[i];
321 if (!chan->con_priv) {
322 chan->con_priv = channel;
323 channel->chan = chan;
324 break;
325 }
326
327 chan = NULL;
328 }
329
330 spin_unlock_irqrestore(&hsp->lock, flags);
331
332 return chan ?: ERR_PTR(-EBUSY);
333}
334
335static void tegra_hsp_remove_doorbells(struct tegra_hsp *hsp)
336{
337 struct tegra_hsp_doorbell *db, *tmp;
338 unsigned long flags;
339
340 spin_lock_irqsave(&hsp->lock, flags);
341
342 list_for_each_entry_safe(db, tmp, &hsp->doorbells, list)
343 __tegra_hsp_doorbell_destroy(db);
344
345 spin_unlock_irqrestore(&hsp->lock, flags);
346}
347
348static int tegra_hsp_add_doorbells(struct tegra_hsp *hsp)
349{
350 const struct tegra_hsp_db_map *map = hsp->soc->map;
351 struct tegra_hsp_channel *channel;
352
353 while (map->name) {
354 channel = tegra_hsp_doorbell_create(hsp, map->name,
355 map->master, map->index);
356 if (IS_ERR(channel)) {
357 tegra_hsp_remove_doorbells(hsp);
358 return PTR_ERR(channel);
359 }
360
361 map++;
362 }
363
364 return 0;
365}
366
367static int tegra_hsp_probe(struct platform_device *pdev)
368{
369 struct tegra_hsp *hsp;
370 struct resource *res;
371 u32 value;
372 int err;
373
374 hsp = devm_kzalloc(&pdev->dev, sizeof(*hsp), GFP_KERNEL);
375 if (!hsp)
376 return -ENOMEM;
377
378 hsp->soc = of_device_get_match_data(&pdev->dev);
379 INIT_LIST_HEAD(&hsp->doorbells);
380 spin_lock_init(&hsp->lock);
381
382 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
383 hsp->regs = devm_ioremap_resource(&pdev->dev, res);
384 if (IS_ERR(hsp->regs))
385 return PTR_ERR(hsp->regs);
386
387 value = tegra_hsp_readl(hsp, HSP_INT_DIMENSIONING);
388 hsp->num_sm = (value >> HSP_nSM_SHIFT) & HSP_nINT_MASK;
389 hsp->num_ss = (value >> HSP_nSS_SHIFT) & HSP_nINT_MASK;
390 hsp->num_as = (value >> HSP_nAS_SHIFT) & HSP_nINT_MASK;
391 hsp->num_db = (value >> HSP_nDB_SHIFT) & HSP_nINT_MASK;
392 hsp->num_si = (value >> HSP_nSI_SHIFT) & HSP_nINT_MASK;
393
394 err = platform_get_irq_byname(pdev, "doorbell");
395 if (err < 0) {
396 dev_err(&pdev->dev, "failed to get doorbell IRQ: %d\n", err);
397 return err;
398 }
399
400 hsp->irq = err;
401
402 hsp->mbox.of_xlate = of_tegra_hsp_xlate;
403 hsp->mbox.num_chans = 32;
404 hsp->mbox.dev = &pdev->dev;
405 hsp->mbox.txdone_irq = false;
406 hsp->mbox.txdone_poll = false;
407 hsp->mbox.ops = &tegra_hsp_doorbell_ops;
408
409 hsp->mbox.chans = devm_kcalloc(&pdev->dev, hsp->mbox.num_chans,
410 sizeof(*hsp->mbox.chans),
411 GFP_KERNEL);
412 if (!hsp->mbox.chans)
413 return -ENOMEM;
414
415 err = tegra_hsp_add_doorbells(hsp);
416 if (err < 0) {
417 dev_err(&pdev->dev, "failed to add doorbells: %d\n", err);
418 return err;
419 }
420
421 platform_set_drvdata(pdev, hsp);
422
423 err = mbox_controller_register(&hsp->mbox);
424 if (err) {
425 dev_err(&pdev->dev, "failed to register mailbox: %d\n", err);
426 tegra_hsp_remove_doorbells(hsp);
427 return err;
428 }
429
430 err = devm_request_irq(&pdev->dev, hsp->irq, tegra_hsp_doorbell_irq,
431 IRQF_NO_SUSPEND, dev_name(&pdev->dev), hsp);
432 if (err < 0) {
433 dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n",
434 hsp->irq, err);
435 return err;
436 }
437
438 return 0;
439}
440
441static int tegra_hsp_remove(struct platform_device *pdev)
442{
443 struct tegra_hsp *hsp = platform_get_drvdata(pdev);
444
445 mbox_controller_unregister(&hsp->mbox);
446 tegra_hsp_remove_doorbells(hsp);
447
448 return 0;
449}
450
451static const struct tegra_hsp_db_map tegra186_hsp_db_map[] = {
452 { "ccplex", TEGRA_HSP_DB_MASTER_CCPLEX, HSP_DB_CCPLEX, },
453 { "bpmp", TEGRA_HSP_DB_MASTER_BPMP, HSP_DB_BPMP, },
454 { /* sentinel */ }
455};
456
457static const struct tegra_hsp_soc tegra186_hsp_soc = {
458 .map = tegra186_hsp_db_map,
459};
460
461static const struct of_device_id tegra_hsp_match[] = {
462 { .compatible = "nvidia,tegra186-hsp", .data = &tegra186_hsp_soc },
463 { }
464};
465
466static struct platform_driver tegra_hsp_driver = {
467 .driver = {
468 .name = "tegra-hsp",
469 .of_match_table = tegra_hsp_match,
470 },
471 .probe = tegra_hsp_probe,
472 .remove = tegra_hsp_remove,
473};
474
475static int __init tegra_hsp_init(void)
476{
477 return platform_driver_register(&tegra_hsp_driver);
478}
479core_initcall(tegra_hsp_init);