.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
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. |
---|
| 3 | + * Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved. |
---|
12 | 4 | */ |
---|
13 | 5 | |
---|
| 6 | +#include <linux/delay.h> |
---|
14 | 7 | #include <linux/interrupt.h> |
---|
15 | 8 | #include <linux/io.h> |
---|
16 | 9 | #include <linux/mailbox_controller.h> |
---|
17 | 10 | #include <linux/of.h> |
---|
18 | 11 | #include <linux/of_device.h> |
---|
19 | 12 | #include <linux/platform_device.h> |
---|
| 13 | +#include <linux/pm.h> |
---|
20 | 14 | #include <linux/slab.h> |
---|
21 | 15 | |
---|
| 16 | +#include <soc/tegra/fuse.h> |
---|
| 17 | + |
---|
22 | 18 | #include <dt-bindings/mailbox/tegra186-hsp.h> |
---|
| 19 | + |
---|
| 20 | +#include "mailbox.h" |
---|
| 21 | + |
---|
| 22 | +#define HSP_INT_IE(x) (0x100 + ((x) * 4)) |
---|
| 23 | +#define HSP_INT_IV 0x300 |
---|
| 24 | +#define HSP_INT_IR 0x304 |
---|
| 25 | + |
---|
| 26 | +#define HSP_INT_EMPTY_SHIFT 0 |
---|
| 27 | +#define HSP_INT_EMPTY_MASK 0xff |
---|
| 28 | +#define HSP_INT_FULL_SHIFT 8 |
---|
| 29 | +#define HSP_INT_FULL_MASK 0xff |
---|
23 | 30 | |
---|
24 | 31 | #define HSP_INT_DIMENSIONING 0x380 |
---|
25 | 32 | #define HSP_nSM_SHIFT 0 |
---|
.. | .. |
---|
33 | 40 | #define HSP_DB_ENABLE 0x4 |
---|
34 | 41 | #define HSP_DB_RAW 0x8 |
---|
35 | 42 | #define HSP_DB_PENDING 0xc |
---|
| 43 | + |
---|
| 44 | +#define HSP_SM_SHRD_MBOX 0x0 |
---|
| 45 | +#define HSP_SM_SHRD_MBOX_FULL BIT(31) |
---|
| 46 | +#define HSP_SM_SHRD_MBOX_FULL_INT_IE 0x04 |
---|
| 47 | +#define HSP_SM_SHRD_MBOX_EMPTY_INT_IE 0x08 |
---|
36 | 48 | |
---|
37 | 49 | #define HSP_DB_CCPLEX 1 |
---|
38 | 50 | #define HSP_DB_BPMP 3 |
---|
.. | .. |
---|
55 | 67 | unsigned int index; |
---|
56 | 68 | }; |
---|
57 | 69 | |
---|
| 70 | +struct tegra_hsp_mailbox { |
---|
| 71 | + struct tegra_hsp_channel channel; |
---|
| 72 | + unsigned int index; |
---|
| 73 | + bool producer; |
---|
| 74 | +}; |
---|
| 75 | + |
---|
58 | 76 | struct tegra_hsp_db_map { |
---|
59 | 77 | const char *name; |
---|
60 | 78 | unsigned int master; |
---|
.. | .. |
---|
63 | 81 | |
---|
64 | 82 | struct tegra_hsp_soc { |
---|
65 | 83 | const struct tegra_hsp_db_map *map; |
---|
| 84 | + bool has_per_mb_ie; |
---|
66 | 85 | }; |
---|
67 | 86 | |
---|
68 | 87 | struct tegra_hsp { |
---|
| 88 | + struct device *dev; |
---|
69 | 89 | const struct tegra_hsp_soc *soc; |
---|
70 | | - struct mbox_controller mbox; |
---|
| 90 | + struct mbox_controller mbox_db; |
---|
| 91 | + struct mbox_controller mbox_sm; |
---|
71 | 92 | void __iomem *regs; |
---|
72 | | - unsigned int irq; |
---|
| 93 | + unsigned int doorbell_irq; |
---|
| 94 | + unsigned int *shared_irqs; |
---|
| 95 | + unsigned int shared_irq; |
---|
73 | 96 | unsigned int num_sm; |
---|
74 | 97 | unsigned int num_as; |
---|
75 | 98 | unsigned int num_ss; |
---|
.. | .. |
---|
78 | 101 | spinlock_t lock; |
---|
79 | 102 | |
---|
80 | 103 | struct list_head doorbells; |
---|
81 | | -}; |
---|
| 104 | + struct tegra_hsp_mailbox *mailboxes; |
---|
82 | 105 | |
---|
83 | | -static inline struct tegra_hsp * |
---|
84 | | -to_tegra_hsp(struct mbox_controller *mbox) |
---|
85 | | -{ |
---|
86 | | - return container_of(mbox, struct tegra_hsp, mbox); |
---|
87 | | -} |
---|
| 106 | + unsigned long mask; |
---|
| 107 | +}; |
---|
88 | 108 | |
---|
89 | 109 | static inline u32 tegra_hsp_readl(struct tegra_hsp *hsp, unsigned int offset) |
---|
90 | 110 | { |
---|
.. | .. |
---|
158 | 178 | |
---|
159 | 179 | spin_lock(&hsp->lock); |
---|
160 | 180 | |
---|
161 | | - for_each_set_bit(master, &value, hsp->mbox.num_chans) { |
---|
| 181 | + for_each_set_bit(master, &value, hsp->mbox_db.num_chans) { |
---|
162 | 182 | struct tegra_hsp_doorbell *db; |
---|
163 | 183 | |
---|
164 | 184 | db = __tegra_hsp_doorbell_get(hsp, master); |
---|
.. | .. |
---|
182 | 202 | return IRQ_HANDLED; |
---|
183 | 203 | } |
---|
184 | 204 | |
---|
| 205 | +static irqreturn_t tegra_hsp_shared_irq(int irq, void *data) |
---|
| 206 | +{ |
---|
| 207 | + struct tegra_hsp *hsp = data; |
---|
| 208 | + unsigned long bit, mask; |
---|
| 209 | + u32 status, value; |
---|
| 210 | + void *msg; |
---|
| 211 | + |
---|
| 212 | + status = tegra_hsp_readl(hsp, HSP_INT_IR) & hsp->mask; |
---|
| 213 | + |
---|
| 214 | + /* process EMPTY interrupts first */ |
---|
| 215 | + mask = (status >> HSP_INT_EMPTY_SHIFT) & HSP_INT_EMPTY_MASK; |
---|
| 216 | + |
---|
| 217 | + for_each_set_bit(bit, &mask, hsp->num_sm) { |
---|
| 218 | + struct tegra_hsp_mailbox *mb = &hsp->mailboxes[bit]; |
---|
| 219 | + |
---|
| 220 | + if (mb->producer) { |
---|
| 221 | + /* |
---|
| 222 | + * Disable EMPTY interrupts until data is sent with |
---|
| 223 | + * the next message. These interrupts are level- |
---|
| 224 | + * triggered, so if we kept them enabled they would |
---|
| 225 | + * constantly trigger until we next write data into |
---|
| 226 | + * the message. |
---|
| 227 | + */ |
---|
| 228 | + spin_lock(&hsp->lock); |
---|
| 229 | + |
---|
| 230 | + hsp->mask &= ~BIT(HSP_INT_EMPTY_SHIFT + mb->index); |
---|
| 231 | + tegra_hsp_writel(hsp, hsp->mask, |
---|
| 232 | + HSP_INT_IE(hsp->shared_irq)); |
---|
| 233 | + |
---|
| 234 | + spin_unlock(&hsp->lock); |
---|
| 235 | + |
---|
| 236 | + mbox_chan_txdone(mb->channel.chan, 0); |
---|
| 237 | + } |
---|
| 238 | + } |
---|
| 239 | + |
---|
| 240 | + /* process FULL interrupts */ |
---|
| 241 | + mask = (status >> HSP_INT_FULL_SHIFT) & HSP_INT_FULL_MASK; |
---|
| 242 | + |
---|
| 243 | + for_each_set_bit(bit, &mask, hsp->num_sm) { |
---|
| 244 | + struct tegra_hsp_mailbox *mb = &hsp->mailboxes[bit]; |
---|
| 245 | + |
---|
| 246 | + if (!mb->producer) { |
---|
| 247 | + value = tegra_hsp_channel_readl(&mb->channel, |
---|
| 248 | + HSP_SM_SHRD_MBOX); |
---|
| 249 | + value &= ~HSP_SM_SHRD_MBOX_FULL; |
---|
| 250 | + msg = (void *)(unsigned long)value; |
---|
| 251 | + mbox_chan_received_data(mb->channel.chan, msg); |
---|
| 252 | + |
---|
| 253 | + /* |
---|
| 254 | + * Need to clear all bits here since some producers, |
---|
| 255 | + * such as TCU, depend on fields in the register |
---|
| 256 | + * getting cleared by the consumer. |
---|
| 257 | + * |
---|
| 258 | + * The mailbox API doesn't give the consumers a way |
---|
| 259 | + * of doing that explicitly, so we have to make sure |
---|
| 260 | + * we cover all possible cases. |
---|
| 261 | + */ |
---|
| 262 | + tegra_hsp_channel_writel(&mb->channel, 0x0, |
---|
| 263 | + HSP_SM_SHRD_MBOX); |
---|
| 264 | + } |
---|
| 265 | + } |
---|
| 266 | + |
---|
| 267 | + return IRQ_HANDLED; |
---|
| 268 | +} |
---|
| 269 | + |
---|
185 | 270 | static struct tegra_hsp_channel * |
---|
186 | 271 | tegra_hsp_doorbell_create(struct tegra_hsp *hsp, const char *name, |
---|
187 | 272 | unsigned int master, unsigned int index) |
---|
.. | .. |
---|
190 | 275 | unsigned int offset; |
---|
191 | 276 | unsigned long flags; |
---|
192 | 277 | |
---|
193 | | - db = kzalloc(sizeof(*db), GFP_KERNEL); |
---|
| 278 | + db = devm_kzalloc(hsp->dev, sizeof(*db), GFP_KERNEL); |
---|
194 | 279 | if (!db) |
---|
195 | 280 | return ERR_PTR(-ENOMEM); |
---|
196 | 281 | |
---|
197 | | - offset = (1 + (hsp->num_sm / 2) + hsp->num_ss + hsp->num_as) << 16; |
---|
| 282 | + offset = (1 + (hsp->num_sm / 2) + hsp->num_ss + hsp->num_as) * SZ_64K; |
---|
198 | 283 | offset += index * 0x100; |
---|
199 | 284 | |
---|
200 | 285 | db->channel.regs = hsp->regs + offset; |
---|
201 | 286 | db->channel.hsp = hsp; |
---|
202 | 287 | |
---|
203 | | - db->name = kstrdup_const(name, GFP_KERNEL); |
---|
| 288 | + db->name = devm_kstrdup_const(hsp->dev, name, GFP_KERNEL); |
---|
204 | 289 | db->master = master; |
---|
205 | 290 | db->index = index; |
---|
206 | 291 | |
---|
.. | .. |
---|
209 | 294 | spin_unlock_irqrestore(&hsp->lock, flags); |
---|
210 | 295 | |
---|
211 | 296 | return &db->channel; |
---|
212 | | -} |
---|
213 | | - |
---|
214 | | -static 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 | 297 | } |
---|
220 | 298 | |
---|
221 | 299 | static int tegra_hsp_doorbell_send_data(struct mbox_chan *chan, void *data) |
---|
.. | .. |
---|
235 | 313 | unsigned long flags; |
---|
236 | 314 | u32 value; |
---|
237 | 315 | |
---|
238 | | - if (db->master >= hsp->mbox.num_chans) { |
---|
239 | | - dev_err(hsp->mbox.dev, |
---|
| 316 | + if (db->master >= chan->mbox->num_chans) { |
---|
| 317 | + dev_err(chan->mbox->dev, |
---|
240 | 318 | "invalid master ID %u for HSP channel\n", |
---|
241 | 319 | db->master); |
---|
242 | 320 | return -EINVAL; |
---|
.. | .. |
---|
246 | 324 | if (!ccplex) |
---|
247 | 325 | return -ENODEV; |
---|
248 | 326 | |
---|
249 | | - if (!tegra_hsp_doorbell_can_ring(db)) |
---|
| 327 | + /* |
---|
| 328 | + * On simulation platforms the BPMP hasn't had a chance yet to mark |
---|
| 329 | + * the doorbell as ringable by the CCPLEX, so we want to skip extra |
---|
| 330 | + * checks here. |
---|
| 331 | + */ |
---|
| 332 | + if (tegra_is_silicon() && !tegra_hsp_doorbell_can_ring(db)) |
---|
250 | 333 | return -ENODEV; |
---|
251 | 334 | |
---|
252 | 335 | spin_lock_irqsave(&hsp->lock, flags); |
---|
.. | .. |
---|
281 | 364 | spin_unlock_irqrestore(&hsp->lock, flags); |
---|
282 | 365 | } |
---|
283 | 366 | |
---|
284 | | -static const struct mbox_chan_ops tegra_hsp_doorbell_ops = { |
---|
| 367 | +static const struct mbox_chan_ops tegra_hsp_db_ops = { |
---|
285 | 368 | .send_data = tegra_hsp_doorbell_send_data, |
---|
286 | 369 | .startup = tegra_hsp_doorbell_startup, |
---|
287 | 370 | .shutdown = tegra_hsp_doorbell_shutdown, |
---|
288 | 371 | }; |
---|
289 | 372 | |
---|
290 | | -static struct mbox_chan *of_tegra_hsp_xlate(struct mbox_controller *mbox, |
---|
| 373 | +static int tegra_hsp_mailbox_send_data(struct mbox_chan *chan, void *data) |
---|
| 374 | +{ |
---|
| 375 | + struct tegra_hsp_mailbox *mb = chan->con_priv; |
---|
| 376 | + struct tegra_hsp *hsp = mb->channel.hsp; |
---|
| 377 | + unsigned long flags; |
---|
| 378 | + u32 value; |
---|
| 379 | + |
---|
| 380 | + if (WARN_ON(!mb->producer)) |
---|
| 381 | + return -EPERM; |
---|
| 382 | + |
---|
| 383 | + /* copy data and mark mailbox full */ |
---|
| 384 | + value = (u32)(unsigned long)data; |
---|
| 385 | + value |= HSP_SM_SHRD_MBOX_FULL; |
---|
| 386 | + |
---|
| 387 | + tegra_hsp_channel_writel(&mb->channel, value, HSP_SM_SHRD_MBOX); |
---|
| 388 | + |
---|
| 389 | + /* enable EMPTY interrupt for the shared mailbox */ |
---|
| 390 | + spin_lock_irqsave(&hsp->lock, flags); |
---|
| 391 | + |
---|
| 392 | + hsp->mask |= BIT(HSP_INT_EMPTY_SHIFT + mb->index); |
---|
| 393 | + tegra_hsp_writel(hsp, hsp->mask, HSP_INT_IE(hsp->shared_irq)); |
---|
| 394 | + |
---|
| 395 | + spin_unlock_irqrestore(&hsp->lock, flags); |
---|
| 396 | + |
---|
| 397 | + return 0; |
---|
| 398 | +} |
---|
| 399 | + |
---|
| 400 | +static int tegra_hsp_mailbox_flush(struct mbox_chan *chan, |
---|
| 401 | + unsigned long timeout) |
---|
| 402 | +{ |
---|
| 403 | + struct tegra_hsp_mailbox *mb = chan->con_priv; |
---|
| 404 | + struct tegra_hsp_channel *ch = &mb->channel; |
---|
| 405 | + u32 value; |
---|
| 406 | + |
---|
| 407 | + timeout = jiffies + msecs_to_jiffies(timeout); |
---|
| 408 | + |
---|
| 409 | + while (time_before(jiffies, timeout)) { |
---|
| 410 | + value = tegra_hsp_channel_readl(ch, HSP_SM_SHRD_MBOX); |
---|
| 411 | + if ((value & HSP_SM_SHRD_MBOX_FULL) == 0) { |
---|
| 412 | + mbox_chan_txdone(chan, 0); |
---|
| 413 | + |
---|
| 414 | + /* Wait until channel is empty */ |
---|
| 415 | + if (chan->active_req != NULL) |
---|
| 416 | + continue; |
---|
| 417 | + |
---|
| 418 | + return 0; |
---|
| 419 | + } |
---|
| 420 | + |
---|
| 421 | + udelay(1); |
---|
| 422 | + } |
---|
| 423 | + |
---|
| 424 | + return -ETIME; |
---|
| 425 | +} |
---|
| 426 | + |
---|
| 427 | +static int tegra_hsp_mailbox_startup(struct mbox_chan *chan) |
---|
| 428 | +{ |
---|
| 429 | + struct tegra_hsp_mailbox *mb = chan->con_priv; |
---|
| 430 | + struct tegra_hsp_channel *ch = &mb->channel; |
---|
| 431 | + struct tegra_hsp *hsp = mb->channel.hsp; |
---|
| 432 | + unsigned long flags; |
---|
| 433 | + |
---|
| 434 | + chan->txdone_method = TXDONE_BY_IRQ; |
---|
| 435 | + |
---|
| 436 | + /* |
---|
| 437 | + * Shared mailboxes start out as consumers by default. FULL and EMPTY |
---|
| 438 | + * interrupts are coalesced at the same shared interrupt. |
---|
| 439 | + * |
---|
| 440 | + * Keep EMPTY interrupts disabled at startup and only enable them when |
---|
| 441 | + * the mailbox is actually full. This is required because the FULL and |
---|
| 442 | + * EMPTY interrupts are level-triggered, so keeping EMPTY interrupts |
---|
| 443 | + * enabled all the time would cause an interrupt storm while mailboxes |
---|
| 444 | + * are idle. |
---|
| 445 | + */ |
---|
| 446 | + |
---|
| 447 | + spin_lock_irqsave(&hsp->lock, flags); |
---|
| 448 | + |
---|
| 449 | + if (mb->producer) |
---|
| 450 | + hsp->mask &= ~BIT(HSP_INT_EMPTY_SHIFT + mb->index); |
---|
| 451 | + else |
---|
| 452 | + hsp->mask |= BIT(HSP_INT_FULL_SHIFT + mb->index); |
---|
| 453 | + |
---|
| 454 | + tegra_hsp_writel(hsp, hsp->mask, HSP_INT_IE(hsp->shared_irq)); |
---|
| 455 | + |
---|
| 456 | + spin_unlock_irqrestore(&hsp->lock, flags); |
---|
| 457 | + |
---|
| 458 | + if (hsp->soc->has_per_mb_ie) { |
---|
| 459 | + if (mb->producer) |
---|
| 460 | + tegra_hsp_channel_writel(ch, 0x0, |
---|
| 461 | + HSP_SM_SHRD_MBOX_EMPTY_INT_IE); |
---|
| 462 | + else |
---|
| 463 | + tegra_hsp_channel_writel(ch, 0x1, |
---|
| 464 | + HSP_SM_SHRD_MBOX_FULL_INT_IE); |
---|
| 465 | + } |
---|
| 466 | + |
---|
| 467 | + return 0; |
---|
| 468 | +} |
---|
| 469 | + |
---|
| 470 | +static void tegra_hsp_mailbox_shutdown(struct mbox_chan *chan) |
---|
| 471 | +{ |
---|
| 472 | + struct tegra_hsp_mailbox *mb = chan->con_priv; |
---|
| 473 | + struct tegra_hsp_channel *ch = &mb->channel; |
---|
| 474 | + struct tegra_hsp *hsp = mb->channel.hsp; |
---|
| 475 | + unsigned long flags; |
---|
| 476 | + |
---|
| 477 | + if (hsp->soc->has_per_mb_ie) { |
---|
| 478 | + if (mb->producer) |
---|
| 479 | + tegra_hsp_channel_writel(ch, 0x0, |
---|
| 480 | + HSP_SM_SHRD_MBOX_EMPTY_INT_IE); |
---|
| 481 | + else |
---|
| 482 | + tegra_hsp_channel_writel(ch, 0x0, |
---|
| 483 | + HSP_SM_SHRD_MBOX_FULL_INT_IE); |
---|
| 484 | + } |
---|
| 485 | + |
---|
| 486 | + spin_lock_irqsave(&hsp->lock, flags); |
---|
| 487 | + |
---|
| 488 | + if (mb->producer) |
---|
| 489 | + hsp->mask &= ~BIT(HSP_INT_EMPTY_SHIFT + mb->index); |
---|
| 490 | + else |
---|
| 491 | + hsp->mask &= ~BIT(HSP_INT_FULL_SHIFT + mb->index); |
---|
| 492 | + |
---|
| 493 | + tegra_hsp_writel(hsp, hsp->mask, HSP_INT_IE(hsp->shared_irq)); |
---|
| 494 | + |
---|
| 495 | + spin_unlock_irqrestore(&hsp->lock, flags); |
---|
| 496 | +} |
---|
| 497 | + |
---|
| 498 | +static const struct mbox_chan_ops tegra_hsp_sm_ops = { |
---|
| 499 | + .send_data = tegra_hsp_mailbox_send_data, |
---|
| 500 | + .flush = tegra_hsp_mailbox_flush, |
---|
| 501 | + .startup = tegra_hsp_mailbox_startup, |
---|
| 502 | + .shutdown = tegra_hsp_mailbox_shutdown, |
---|
| 503 | +}; |
---|
| 504 | + |
---|
| 505 | +static struct mbox_chan *tegra_hsp_db_xlate(struct mbox_controller *mbox, |
---|
291 | 506 | const struct of_phandle_args *args) |
---|
292 | 507 | { |
---|
| 508 | + struct tegra_hsp *hsp = container_of(mbox, struct tegra_hsp, mbox_db); |
---|
| 509 | + unsigned int type = args->args[0], master = args->args[1]; |
---|
293 | 510 | 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 | 511 | struct tegra_hsp_doorbell *db; |
---|
298 | 512 | struct mbox_chan *chan; |
---|
299 | 513 | unsigned long flags; |
---|
300 | 514 | unsigned int i; |
---|
301 | 515 | |
---|
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; |
---|
| 516 | + if (type != TEGRA_HSP_MBOX_TYPE_DB || !hsp->doorbell_irq) |
---|
| 517 | + return ERR_PTR(-ENODEV); |
---|
307 | 518 | |
---|
308 | | - break; |
---|
309 | | - |
---|
310 | | - default: |
---|
311 | | - break; |
---|
312 | | - } |
---|
| 519 | + db = tegra_hsp_doorbell_get(hsp, master); |
---|
| 520 | + if (db) |
---|
| 521 | + channel = &db->channel; |
---|
313 | 522 | |
---|
314 | 523 | if (IS_ERR(channel)) |
---|
315 | 524 | return ERR_CAST(channel); |
---|
316 | 525 | |
---|
317 | 526 | spin_lock_irqsave(&hsp->lock, flags); |
---|
318 | 527 | |
---|
319 | | - for (i = 0; i < hsp->mbox.num_chans; i++) { |
---|
320 | | - chan = &hsp->mbox.chans[i]; |
---|
| 528 | + for (i = 0; i < mbox->num_chans; i++) { |
---|
| 529 | + chan = &mbox->chans[i]; |
---|
321 | 530 | if (!chan->con_priv) { |
---|
322 | | - chan->con_priv = channel; |
---|
323 | 531 | channel->chan = chan; |
---|
| 532 | + chan->con_priv = db; |
---|
324 | 533 | break; |
---|
325 | 534 | } |
---|
326 | 535 | |
---|
.. | .. |
---|
332 | 541 | return chan ?: ERR_PTR(-EBUSY); |
---|
333 | 542 | } |
---|
334 | 543 | |
---|
335 | | -static void tegra_hsp_remove_doorbells(struct tegra_hsp *hsp) |
---|
| 544 | +static struct mbox_chan *tegra_hsp_sm_xlate(struct mbox_controller *mbox, |
---|
| 545 | + const struct of_phandle_args *args) |
---|
336 | 546 | { |
---|
337 | | - struct tegra_hsp_doorbell *db, *tmp; |
---|
338 | | - unsigned long flags; |
---|
| 547 | + struct tegra_hsp *hsp = container_of(mbox, struct tegra_hsp, mbox_sm); |
---|
| 548 | + unsigned int type = args->args[0], index; |
---|
| 549 | + struct tegra_hsp_mailbox *mb; |
---|
339 | 550 | |
---|
340 | | - spin_lock_irqsave(&hsp->lock, flags); |
---|
| 551 | + index = args->args[1] & TEGRA_HSP_SM_MASK; |
---|
341 | 552 | |
---|
342 | | - list_for_each_entry_safe(db, tmp, &hsp->doorbells, list) |
---|
343 | | - __tegra_hsp_doorbell_destroy(db); |
---|
| 553 | + if (type != TEGRA_HSP_MBOX_TYPE_SM || !hsp->shared_irqs || |
---|
| 554 | + index >= hsp->num_sm) |
---|
| 555 | + return ERR_PTR(-ENODEV); |
---|
344 | 556 | |
---|
345 | | - spin_unlock_irqrestore(&hsp->lock, flags); |
---|
| 557 | + mb = &hsp->mailboxes[index]; |
---|
| 558 | + |
---|
| 559 | + if ((args->args[1] & TEGRA_HSP_SM_FLAG_TX) == 0) |
---|
| 560 | + mb->producer = false; |
---|
| 561 | + else |
---|
| 562 | + mb->producer = true; |
---|
| 563 | + |
---|
| 564 | + return mb->channel.chan; |
---|
346 | 565 | } |
---|
347 | 566 | |
---|
348 | 567 | static int tegra_hsp_add_doorbells(struct tegra_hsp *hsp) |
---|
.. | .. |
---|
353 | 572 | while (map->name) { |
---|
354 | 573 | channel = tegra_hsp_doorbell_create(hsp, map->name, |
---|
355 | 574 | map->master, map->index); |
---|
356 | | - if (IS_ERR(channel)) { |
---|
357 | | - tegra_hsp_remove_doorbells(hsp); |
---|
| 575 | + if (IS_ERR(channel)) |
---|
358 | 576 | return PTR_ERR(channel); |
---|
359 | | - } |
---|
360 | 577 | |
---|
361 | 578 | map++; |
---|
| 579 | + } |
---|
| 580 | + |
---|
| 581 | + return 0; |
---|
| 582 | +} |
---|
| 583 | + |
---|
| 584 | +static int tegra_hsp_add_mailboxes(struct tegra_hsp *hsp, struct device *dev) |
---|
| 585 | +{ |
---|
| 586 | + int i; |
---|
| 587 | + |
---|
| 588 | + hsp->mailboxes = devm_kcalloc(dev, hsp->num_sm, sizeof(*hsp->mailboxes), |
---|
| 589 | + GFP_KERNEL); |
---|
| 590 | + if (!hsp->mailboxes) |
---|
| 591 | + return -ENOMEM; |
---|
| 592 | + |
---|
| 593 | + for (i = 0; i < hsp->num_sm; i++) { |
---|
| 594 | + struct tegra_hsp_mailbox *mb = &hsp->mailboxes[i]; |
---|
| 595 | + |
---|
| 596 | + mb->index = i; |
---|
| 597 | + |
---|
| 598 | + mb->channel.hsp = hsp; |
---|
| 599 | + mb->channel.regs = hsp->regs + SZ_64K + i * SZ_32K; |
---|
| 600 | + mb->channel.chan = &hsp->mbox_sm.chans[i]; |
---|
| 601 | + mb->channel.chan->con_priv = mb; |
---|
| 602 | + } |
---|
| 603 | + |
---|
| 604 | + return 0; |
---|
| 605 | +} |
---|
| 606 | + |
---|
| 607 | +static int tegra_hsp_request_shared_irq(struct tegra_hsp *hsp) |
---|
| 608 | +{ |
---|
| 609 | + unsigned int i, irq = 0; |
---|
| 610 | + int err; |
---|
| 611 | + |
---|
| 612 | + for (i = 0; i < hsp->num_si; i++) { |
---|
| 613 | + irq = hsp->shared_irqs[i]; |
---|
| 614 | + if (irq <= 0) |
---|
| 615 | + continue; |
---|
| 616 | + |
---|
| 617 | + err = devm_request_irq(hsp->dev, irq, tegra_hsp_shared_irq, 0, |
---|
| 618 | + dev_name(hsp->dev), hsp); |
---|
| 619 | + if (err < 0) { |
---|
| 620 | + dev_err(hsp->dev, "failed to request interrupt: %d\n", |
---|
| 621 | + err); |
---|
| 622 | + continue; |
---|
| 623 | + } |
---|
| 624 | + |
---|
| 625 | + hsp->shared_irq = i; |
---|
| 626 | + |
---|
| 627 | + /* disable all interrupts */ |
---|
| 628 | + tegra_hsp_writel(hsp, 0, HSP_INT_IE(hsp->shared_irq)); |
---|
| 629 | + |
---|
| 630 | + dev_dbg(hsp->dev, "interrupt requested: %u\n", irq); |
---|
| 631 | + |
---|
| 632 | + break; |
---|
| 633 | + } |
---|
| 634 | + |
---|
| 635 | + if (i == hsp->num_si) { |
---|
| 636 | + dev_err(hsp->dev, "failed to find available interrupt\n"); |
---|
| 637 | + return -ENOENT; |
---|
362 | 638 | } |
---|
363 | 639 | |
---|
364 | 640 | return 0; |
---|
.. | .. |
---|
368 | 644 | { |
---|
369 | 645 | struct tegra_hsp *hsp; |
---|
370 | 646 | struct resource *res; |
---|
| 647 | + unsigned int i; |
---|
371 | 648 | u32 value; |
---|
372 | 649 | int err; |
---|
373 | 650 | |
---|
.. | .. |
---|
375 | 652 | if (!hsp) |
---|
376 | 653 | return -ENOMEM; |
---|
377 | 654 | |
---|
| 655 | + hsp->dev = &pdev->dev; |
---|
378 | 656 | hsp->soc = of_device_get_match_data(&pdev->dev); |
---|
379 | 657 | INIT_LIST_HEAD(&hsp->doorbells); |
---|
380 | 658 | spin_lock_init(&hsp->lock); |
---|
.. | .. |
---|
391 | 669 | hsp->num_db = (value >> HSP_nDB_SHIFT) & HSP_nINT_MASK; |
---|
392 | 670 | hsp->num_si = (value >> HSP_nSI_SHIFT) & HSP_nINT_MASK; |
---|
393 | 671 | |
---|
394 | | - err = platform_get_irq_byname(pdev, "doorbell"); |
---|
| 672 | + err = platform_get_irq_byname_optional(pdev, "doorbell"); |
---|
| 673 | + if (err >= 0) |
---|
| 674 | + hsp->doorbell_irq = err; |
---|
| 675 | + |
---|
| 676 | + if (hsp->num_si > 0) { |
---|
| 677 | + unsigned int count = 0; |
---|
| 678 | + |
---|
| 679 | + hsp->shared_irqs = devm_kcalloc(&pdev->dev, hsp->num_si, |
---|
| 680 | + sizeof(*hsp->shared_irqs), |
---|
| 681 | + GFP_KERNEL); |
---|
| 682 | + if (!hsp->shared_irqs) |
---|
| 683 | + return -ENOMEM; |
---|
| 684 | + |
---|
| 685 | + for (i = 0; i < hsp->num_si; i++) { |
---|
| 686 | + char *name; |
---|
| 687 | + |
---|
| 688 | + name = kasprintf(GFP_KERNEL, "shared%u", i); |
---|
| 689 | + if (!name) |
---|
| 690 | + return -ENOMEM; |
---|
| 691 | + |
---|
| 692 | + err = platform_get_irq_byname_optional(pdev, name); |
---|
| 693 | + if (err >= 0) { |
---|
| 694 | + hsp->shared_irqs[i] = err; |
---|
| 695 | + count++; |
---|
| 696 | + } |
---|
| 697 | + |
---|
| 698 | + kfree(name); |
---|
| 699 | + } |
---|
| 700 | + |
---|
| 701 | + if (count == 0) { |
---|
| 702 | + devm_kfree(&pdev->dev, hsp->shared_irqs); |
---|
| 703 | + hsp->shared_irqs = NULL; |
---|
| 704 | + } |
---|
| 705 | + } |
---|
| 706 | + |
---|
| 707 | + /* setup the doorbell controller */ |
---|
| 708 | + hsp->mbox_db.of_xlate = tegra_hsp_db_xlate; |
---|
| 709 | + hsp->mbox_db.num_chans = 32; |
---|
| 710 | + hsp->mbox_db.dev = &pdev->dev; |
---|
| 711 | + hsp->mbox_db.ops = &tegra_hsp_db_ops; |
---|
| 712 | + |
---|
| 713 | + hsp->mbox_db.chans = devm_kcalloc(&pdev->dev, hsp->mbox_db.num_chans, |
---|
| 714 | + sizeof(*hsp->mbox_db.chans), |
---|
| 715 | + GFP_KERNEL); |
---|
| 716 | + if (!hsp->mbox_db.chans) |
---|
| 717 | + return -ENOMEM; |
---|
| 718 | + |
---|
| 719 | + if (hsp->doorbell_irq) { |
---|
| 720 | + err = tegra_hsp_add_doorbells(hsp); |
---|
| 721 | + if (err < 0) { |
---|
| 722 | + dev_err(&pdev->dev, "failed to add doorbells: %d\n", |
---|
| 723 | + err); |
---|
| 724 | + return err; |
---|
| 725 | + } |
---|
| 726 | + } |
---|
| 727 | + |
---|
| 728 | + err = devm_mbox_controller_register(&pdev->dev, &hsp->mbox_db); |
---|
395 | 729 | if (err < 0) { |
---|
396 | | - dev_err(&pdev->dev, "failed to get doorbell IRQ: %d\n", err); |
---|
| 730 | + dev_err(&pdev->dev, "failed to register doorbell mailbox: %d\n", |
---|
| 731 | + err); |
---|
397 | 732 | return err; |
---|
398 | 733 | } |
---|
399 | 734 | |
---|
400 | | - hsp->irq = err; |
---|
| 735 | + /* setup the shared mailbox controller */ |
---|
| 736 | + hsp->mbox_sm.of_xlate = tegra_hsp_sm_xlate; |
---|
| 737 | + hsp->mbox_sm.num_chans = hsp->num_sm; |
---|
| 738 | + hsp->mbox_sm.dev = &pdev->dev; |
---|
| 739 | + hsp->mbox_sm.ops = &tegra_hsp_sm_ops; |
---|
401 | 740 | |
---|
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) |
---|
| 741 | + hsp->mbox_sm.chans = devm_kcalloc(&pdev->dev, hsp->mbox_sm.num_chans, |
---|
| 742 | + sizeof(*hsp->mbox_sm.chans), |
---|
| 743 | + GFP_KERNEL); |
---|
| 744 | + if (!hsp->mbox_sm.chans) |
---|
413 | 745 | return -ENOMEM; |
---|
414 | 746 | |
---|
415 | | - err = tegra_hsp_add_doorbells(hsp); |
---|
| 747 | + if (hsp->shared_irqs) { |
---|
| 748 | + err = tegra_hsp_add_mailboxes(hsp, &pdev->dev); |
---|
| 749 | + if (err < 0) { |
---|
| 750 | + dev_err(&pdev->dev, "failed to add mailboxes: %d\n", |
---|
| 751 | + err); |
---|
| 752 | + return err; |
---|
| 753 | + } |
---|
| 754 | + } |
---|
| 755 | + |
---|
| 756 | + err = devm_mbox_controller_register(&pdev->dev, &hsp->mbox_sm); |
---|
416 | 757 | if (err < 0) { |
---|
417 | | - dev_err(&pdev->dev, "failed to add doorbells: %d\n", err); |
---|
| 758 | + dev_err(&pdev->dev, "failed to register shared mailbox: %d\n", |
---|
| 759 | + err); |
---|
418 | 760 | return err; |
---|
419 | 761 | } |
---|
420 | 762 | |
---|
421 | 763 | platform_set_drvdata(pdev, hsp); |
---|
422 | 764 | |
---|
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; |
---|
| 765 | + if (hsp->doorbell_irq) { |
---|
| 766 | + err = devm_request_irq(&pdev->dev, hsp->doorbell_irq, |
---|
| 767 | + tegra_hsp_doorbell_irq, IRQF_NO_SUSPEND, |
---|
| 768 | + dev_name(&pdev->dev), hsp); |
---|
| 769 | + if (err < 0) { |
---|
| 770 | + dev_err(&pdev->dev, |
---|
| 771 | + "failed to request doorbell IRQ#%u: %d\n", |
---|
| 772 | + hsp->doorbell_irq, err); |
---|
| 773 | + return err; |
---|
| 774 | + } |
---|
428 | 775 | } |
---|
429 | 776 | |
---|
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; |
---|
| 777 | + if (hsp->shared_irqs) { |
---|
| 778 | + err = tegra_hsp_request_shared_irq(hsp); |
---|
| 779 | + if (err < 0) |
---|
| 780 | + return err; |
---|
436 | 781 | } |
---|
437 | 782 | |
---|
438 | 783 | return 0; |
---|
439 | 784 | } |
---|
440 | 785 | |
---|
441 | | -static int tegra_hsp_remove(struct platform_device *pdev) |
---|
| 786 | +static int __maybe_unused tegra_hsp_resume(struct device *dev) |
---|
442 | 787 | { |
---|
443 | | - struct tegra_hsp *hsp = platform_get_drvdata(pdev); |
---|
| 788 | + struct tegra_hsp *hsp = dev_get_drvdata(dev); |
---|
| 789 | + unsigned int i; |
---|
| 790 | + struct tegra_hsp_doorbell *db; |
---|
444 | 791 | |
---|
445 | | - mbox_controller_unregister(&hsp->mbox); |
---|
446 | | - tegra_hsp_remove_doorbells(hsp); |
---|
| 792 | + list_for_each_entry(db, &hsp->doorbells, list) { |
---|
| 793 | + if (db && db->channel.chan) |
---|
| 794 | + tegra_hsp_doorbell_startup(db->channel.chan); |
---|
| 795 | + } |
---|
| 796 | + |
---|
| 797 | + if (hsp->mailboxes) { |
---|
| 798 | + for (i = 0; i < hsp->num_sm; i++) { |
---|
| 799 | + struct tegra_hsp_mailbox *mb = &hsp->mailboxes[i]; |
---|
| 800 | + |
---|
| 801 | + if (mb->channel.chan->cl) |
---|
| 802 | + tegra_hsp_mailbox_startup(mb->channel.chan); |
---|
| 803 | + } |
---|
| 804 | + } |
---|
447 | 805 | |
---|
448 | 806 | return 0; |
---|
449 | 807 | } |
---|
| 808 | + |
---|
| 809 | +static const struct dev_pm_ops tegra_hsp_pm_ops = { |
---|
| 810 | + .resume_noirq = tegra_hsp_resume, |
---|
| 811 | +}; |
---|
450 | 812 | |
---|
451 | 813 | static const struct tegra_hsp_db_map tegra186_hsp_db_map[] = { |
---|
452 | 814 | { "ccplex", TEGRA_HSP_DB_MASTER_CCPLEX, HSP_DB_CCPLEX, }, |
---|
.. | .. |
---|
456 | 818 | |
---|
457 | 819 | static const struct tegra_hsp_soc tegra186_hsp_soc = { |
---|
458 | 820 | .map = tegra186_hsp_db_map, |
---|
| 821 | + .has_per_mb_ie = false, |
---|
| 822 | +}; |
---|
| 823 | + |
---|
| 824 | +static const struct tegra_hsp_soc tegra194_hsp_soc = { |
---|
| 825 | + .map = tegra186_hsp_db_map, |
---|
| 826 | + .has_per_mb_ie = true, |
---|
459 | 827 | }; |
---|
460 | 828 | |
---|
461 | 829 | static const struct of_device_id tegra_hsp_match[] = { |
---|
462 | 830 | { .compatible = "nvidia,tegra186-hsp", .data = &tegra186_hsp_soc }, |
---|
| 831 | + { .compatible = "nvidia,tegra194-hsp", .data = &tegra194_hsp_soc }, |
---|
463 | 832 | { } |
---|
464 | 833 | }; |
---|
465 | 834 | |
---|
.. | .. |
---|
467 | 836 | .driver = { |
---|
468 | 837 | .name = "tegra-hsp", |
---|
469 | 838 | .of_match_table = tegra_hsp_match, |
---|
| 839 | + .pm = &tegra_hsp_pm_ops, |
---|
470 | 840 | }, |
---|
471 | 841 | .probe = tegra_hsp_probe, |
---|
472 | | - .remove = tegra_hsp_remove, |
---|
473 | 842 | }; |
---|
474 | 843 | |
---|
475 | 844 | static int __init tegra_hsp_init(void) |
---|