2 * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
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.
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
14 #include <linux/interrupt.h>
16 #include <linux/mailbox_controller.h>
18 #include <linux/of_device.h>
19 #include <linux/platform_device.h>
20 #include <linux/slab.h>
22 #include <dt-bindings/mailbox/tegra186-hsp.h>
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
32 #define HSP_DB_TRIGGER 0x0
33 #define HSP_DB_ENABLE 0x4
34 #define HSP_DB_RAW 0x8
35 #define HSP_DB_PENDING 0xc
37 #define HSP_DB_CCPLEX 1
41 struct tegra_hsp_channel
;
44 struct tegra_hsp_channel
{
45 struct tegra_hsp
*hsp
;
46 struct mbox_chan
*chan
;
50 struct tegra_hsp_doorbell
{
51 struct tegra_hsp_channel channel
;
52 struct list_head list
;
58 struct tegra_hsp_db_map
{
64 struct tegra_hsp_soc
{
65 const struct tegra_hsp_db_map
*map
;
69 const struct tegra_hsp_soc
*soc
;
70 struct mbox_controller mbox
;
80 struct list_head doorbells
;
83 static inline struct tegra_hsp
*
84 to_tegra_hsp(struct mbox_controller
*mbox
)
86 return container_of(mbox
, struct tegra_hsp
, mbox
);
89 static inline u32
tegra_hsp_readl(struct tegra_hsp
*hsp
, unsigned int offset
)
91 return readl(hsp
->regs
+ offset
);
94 static inline void tegra_hsp_writel(struct tegra_hsp
*hsp
, u32 value
,
97 writel(value
, hsp
->regs
+ offset
);
100 static inline u32
tegra_hsp_channel_readl(struct tegra_hsp_channel
*channel
,
103 return readl(channel
->regs
+ offset
);
106 static inline void tegra_hsp_channel_writel(struct tegra_hsp_channel
*channel
,
107 u32 value
, unsigned int offset
)
109 writel(value
, channel
->regs
+ offset
);
112 static bool tegra_hsp_doorbell_can_ring(struct tegra_hsp_doorbell
*db
)
116 value
= tegra_hsp_channel_readl(&db
->channel
, HSP_DB_ENABLE
);
118 return (value
& BIT(TEGRA_HSP_DB_MASTER_CCPLEX
)) != 0;
121 static struct tegra_hsp_doorbell
*
122 __tegra_hsp_doorbell_get(struct tegra_hsp
*hsp
, unsigned int master
)
124 struct tegra_hsp_doorbell
*entry
;
126 list_for_each_entry(entry
, &hsp
->doorbells
, list
)
127 if (entry
->master
== master
)
133 static struct tegra_hsp_doorbell
*
134 tegra_hsp_doorbell_get(struct tegra_hsp
*hsp
, unsigned int master
)
136 struct tegra_hsp_doorbell
*db
;
139 spin_lock_irqsave(&hsp
->lock
, flags
);
140 db
= __tegra_hsp_doorbell_get(hsp
, master
);
141 spin_unlock_irqrestore(&hsp
->lock
, flags
);
146 static irqreturn_t
tegra_hsp_doorbell_irq(int irq
, void *data
)
148 struct tegra_hsp
*hsp
= data
;
149 struct tegra_hsp_doorbell
*db
;
150 unsigned long master
, value
;
152 db
= tegra_hsp_doorbell_get(hsp
, TEGRA_HSP_DB_MASTER_CCPLEX
);
156 value
= tegra_hsp_channel_readl(&db
->channel
, HSP_DB_PENDING
);
157 tegra_hsp_channel_writel(&db
->channel
, value
, HSP_DB_PENDING
);
159 spin_lock(&hsp
->lock
);
161 for_each_set_bit(master
, &value
, hsp
->mbox
.num_chans
) {
162 struct tegra_hsp_doorbell
*db
;
164 db
= __tegra_hsp_doorbell_get(hsp
, master
);
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.
170 * In that case, db->channel.chan will still be NULL here and
171 * cause a crash if not properly guarded.
173 * It remains to be seen if ignoring the doorbell in that case
174 * is the correct solution.
176 if (db
&& db
->channel
.chan
)
177 mbox_chan_received_data(db
->channel
.chan
, NULL
);
180 spin_unlock(&hsp
->lock
);
185 static struct tegra_hsp_channel
*
186 tegra_hsp_doorbell_create(struct tegra_hsp
*hsp
, const char *name
,
187 unsigned int master
, unsigned int index
)
189 struct tegra_hsp_doorbell
*db
;
193 db
= kzalloc(sizeof(*db
), GFP_KERNEL
);
195 return ERR_PTR(-ENOMEM
);
197 offset
= (1 + (hsp
->num_sm
/ 2) + hsp
->num_ss
+ hsp
->num_as
) << 16;
198 offset
+= index
* 0x100;
200 db
->channel
.regs
= hsp
->regs
+ offset
;
201 db
->channel
.hsp
= hsp
;
203 db
->name
= kstrdup_const(name
, GFP_KERNEL
);
207 spin_lock_irqsave(&hsp
->lock
, flags
);
208 list_add_tail(&db
->list
, &hsp
->doorbells
);
209 spin_unlock_irqrestore(&hsp
->lock
, flags
);
214 static void __tegra_hsp_doorbell_destroy(struct tegra_hsp_doorbell
*db
)
217 kfree_const(db
->name
);
221 static int tegra_hsp_doorbell_send_data(struct mbox_chan
*chan
, void *data
)
223 struct tegra_hsp_doorbell
*db
= chan
->con_priv
;
225 tegra_hsp_channel_writel(&db
->channel
, 1, HSP_DB_TRIGGER
);
230 static int tegra_hsp_doorbell_startup(struct mbox_chan
*chan
)
232 struct tegra_hsp_doorbell
*db
= chan
->con_priv
;
233 struct tegra_hsp
*hsp
= db
->channel
.hsp
;
234 struct tegra_hsp_doorbell
*ccplex
;
238 if (db
->master
>= hsp
->mbox
.num_chans
) {
239 dev_err(hsp
->mbox
.dev
,
240 "invalid master ID %u for HSP channel\n",
245 ccplex
= tegra_hsp_doorbell_get(hsp
, TEGRA_HSP_DB_MASTER_CCPLEX
);
249 if (!tegra_hsp_doorbell_can_ring(db
))
252 spin_lock_irqsave(&hsp
->lock
, flags
);
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
);
258 spin_unlock_irqrestore(&hsp
->lock
, flags
);
263 static void tegra_hsp_doorbell_shutdown(struct mbox_chan
*chan
)
265 struct tegra_hsp_doorbell
*db
= chan
->con_priv
;
266 struct tegra_hsp
*hsp
= db
->channel
.hsp
;
267 struct tegra_hsp_doorbell
*ccplex
;
271 ccplex
= tegra_hsp_doorbell_get(hsp
, TEGRA_HSP_DB_MASTER_CCPLEX
);
275 spin_lock_irqsave(&hsp
->lock
, flags
);
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
);
281 spin_unlock_irqrestore(&hsp
->lock
, flags
);
284 static 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
,
290 static struct mbox_chan
*of_tegra_hsp_xlate(struct mbox_controller
*mbox
,
291 const struct of_phandle_args
*args
)
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
;
303 case TEGRA_HSP_MBOX_TYPE_DB
:
304 db
= tegra_hsp_doorbell_get(hsp
, master
);
306 channel
= &db
->channel
;
315 return ERR_CAST(channel
);
317 spin_lock_irqsave(&hsp
->lock
, flags
);
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
;
330 spin_unlock_irqrestore(&hsp
->lock
, flags
);
332 return chan
?: ERR_PTR(-EBUSY
);
335 static void tegra_hsp_remove_doorbells(struct tegra_hsp
*hsp
)
337 struct tegra_hsp_doorbell
*db
, *tmp
;
340 spin_lock_irqsave(&hsp
->lock
, flags
);
342 list_for_each_entry_safe(db
, tmp
, &hsp
->doorbells
, list
)
343 __tegra_hsp_doorbell_destroy(db
);
345 spin_unlock_irqrestore(&hsp
->lock
, flags
);
348 static int tegra_hsp_add_doorbells(struct tegra_hsp
*hsp
)
350 const struct tegra_hsp_db_map
*map
= hsp
->soc
->map
;
351 struct tegra_hsp_channel
*channel
;
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
);
367 static int tegra_hsp_probe(struct platform_device
*pdev
)
369 struct tegra_hsp
*hsp
;
370 struct resource
*res
;
374 hsp
= devm_kzalloc(&pdev
->dev
, sizeof(*hsp
), GFP_KERNEL
);
378 hsp
->soc
= of_device_get_match_data(&pdev
->dev
);
379 INIT_LIST_HEAD(&hsp
->doorbells
);
380 spin_lock_init(&hsp
->lock
);
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
);
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
;
394 err
= platform_get_irq_byname(pdev
, "doorbell");
396 dev_err(&pdev
->dev
, "failed to get doorbell IRQ: %d\n", err
);
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
;
409 hsp
->mbox
.chans
= devm_kcalloc(&pdev
->dev
, hsp
->mbox
.num_chans
,
410 sizeof(*hsp
->mbox
.chans
),
412 if (!hsp
->mbox
.chans
)
415 err
= tegra_hsp_add_doorbells(hsp
);
417 dev_err(&pdev
->dev
, "failed to add doorbells: %d\n", err
);
421 platform_set_drvdata(pdev
, hsp
);
423 err
= mbox_controller_register(&hsp
->mbox
);
425 dev_err(&pdev
->dev
, "failed to register mailbox: %d\n", err
);
426 tegra_hsp_remove_doorbells(hsp
);
430 err
= devm_request_irq(&pdev
->dev
, hsp
->irq
, tegra_hsp_doorbell_irq
,
431 IRQF_NO_SUSPEND
, dev_name(&pdev
->dev
), hsp
);
433 dev_err(&pdev
->dev
, "failed to request IRQ#%u: %d\n",
441 static int tegra_hsp_remove(struct platform_device
*pdev
)
443 struct tegra_hsp
*hsp
= platform_get_drvdata(pdev
);
445 mbox_controller_unregister(&hsp
->mbox
);
446 tegra_hsp_remove_doorbells(hsp
);
451 static 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
, },
457 static const struct tegra_hsp_soc tegra186_hsp_soc
= {
458 .map
= tegra186_hsp_db_map
,
461 static const struct of_device_id tegra_hsp_match
[] = {
462 { .compatible
= "nvidia,tegra186-hsp", .data
= &tegra186_hsp_soc
},
466 static struct platform_driver tegra_hsp_driver
= {
469 .of_match_table
= tegra_hsp_match
,
471 .probe
= tegra_hsp_probe
,
472 .remove
= tegra_hsp_remove
,
475 static int __init
tegra_hsp_init(void)
477 return platform_driver_register(&tegra_hsp_driver
);
479 core_initcall(tegra_hsp_init
);