822 lines · c
1// SPDX-License-Identifier: GPL-2.02//3// Copyright (c) 2018 MediaTek Inc.4 5#include <linux/bitops.h>6#include <linux/clk.h>7#include <linux/clk-provider.h>8#include <linux/dma-mapping.h>9#include <linux/errno.h>10#include <linux/interrupt.h>11#include <linux/io.h>12#include <linux/iopoll.h>13#include <linux/kernel.h>14#include <linux/module.h>15#include <linux/platform_device.h>16#include <linux/pm_runtime.h>17#include <linux/mailbox_controller.h>18#include <linux/mailbox/mtk-cmdq-mailbox.h>19#include <linux/of.h>20 21#define CMDQ_MBOX_AUTOSUSPEND_DELAY_MS 10022 23#define CMDQ_OP_CODE_MASK (0xff << CMDQ_OP_CODE_SHIFT)24#define CMDQ_NUM_CMD(t) (t->cmd_buf_size / CMDQ_INST_SIZE)25 26#define CMDQ_CURR_IRQ_STATUS 0x1027#define CMDQ_SYNC_TOKEN_UPDATE 0x6828#define CMDQ_THR_SLOT_CYCLES 0x3029#define CMDQ_THR_BASE 0x10030#define CMDQ_THR_SIZE 0x8031#define CMDQ_THR_WARM_RESET 0x0032#define CMDQ_THR_ENABLE_TASK 0x0433#define CMDQ_THR_SUSPEND_TASK 0x0834#define CMDQ_THR_CURR_STATUS 0x0c35#define CMDQ_THR_IRQ_STATUS 0x1036#define CMDQ_THR_IRQ_ENABLE 0x1437#define CMDQ_THR_CURR_ADDR 0x2038#define CMDQ_THR_END_ADDR 0x2439#define CMDQ_THR_WAIT_TOKEN 0x3040#define CMDQ_THR_PRIORITY 0x4041 42#define GCE_GCTL_VALUE 0x4843#define GCE_CTRL_BY_SW GENMASK(2, 0)44#define GCE_DDR_EN GENMASK(18, 16)45 46#define CMDQ_THR_ACTIVE_SLOT_CYCLES 0x320047#define CMDQ_THR_ENABLED 0x148#define CMDQ_THR_DISABLED 0x049#define CMDQ_THR_SUSPEND 0x150#define CMDQ_THR_RESUME 0x051#define CMDQ_THR_STATUS_SUSPENDED BIT(1)52#define CMDQ_THR_DO_WARM_RESET BIT(0)53#define CMDQ_THR_IRQ_DONE 0x154#define CMDQ_THR_IRQ_ERROR 0x1255#define CMDQ_THR_IRQ_EN (CMDQ_THR_IRQ_ERROR | CMDQ_THR_IRQ_DONE)56#define CMDQ_THR_IS_WAITING BIT(31)57 58#define CMDQ_JUMP_BY_OFFSET 0x1000000059#define CMDQ_JUMP_BY_PA 0x1000000160 61struct cmdq_thread {62 struct mbox_chan *chan;63 void __iomem *base;64 struct list_head task_busy_list;65 u32 priority;66};67 68struct cmdq_task {69 struct cmdq *cmdq;70 struct list_head list_entry;71 dma_addr_t pa_base;72 struct cmdq_thread *thread;73 struct cmdq_pkt *pkt; /* the packet sent from mailbox client */74};75 76struct cmdq {77 struct mbox_controller mbox;78 void __iomem *base;79 int irq;80 u32 irq_mask;81 const struct gce_plat *pdata;82 struct cmdq_thread *thread;83 struct clk_bulk_data *clocks;84 bool suspended;85};86 87struct gce_plat {88 u32 thread_nr;89 u8 shift;90 bool control_by_sw;91 bool sw_ddr_en;92 u32 gce_num;93};94 95static void cmdq_sw_ddr_enable(struct cmdq *cmdq, bool enable)96{97 WARN_ON(clk_bulk_enable(cmdq->pdata->gce_num, cmdq->clocks));98 99 if (enable)100 writel(GCE_DDR_EN | GCE_CTRL_BY_SW, cmdq->base + GCE_GCTL_VALUE);101 else102 writel(GCE_CTRL_BY_SW, cmdq->base + GCE_GCTL_VALUE);103 104 clk_bulk_disable(cmdq->pdata->gce_num, cmdq->clocks);105}106 107u8 cmdq_get_shift_pa(struct mbox_chan *chan)108{109 struct cmdq *cmdq = container_of(chan->mbox, struct cmdq, mbox);110 111 return cmdq->pdata->shift;112}113EXPORT_SYMBOL(cmdq_get_shift_pa);114 115static int cmdq_thread_suspend(struct cmdq *cmdq, struct cmdq_thread *thread)116{117 u32 status;118 119 writel(CMDQ_THR_SUSPEND, thread->base + CMDQ_THR_SUSPEND_TASK);120 121 /* If already disabled, treat as suspended successful. */122 if (!(readl(thread->base + CMDQ_THR_ENABLE_TASK) & CMDQ_THR_ENABLED))123 return 0;124 125 if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_CURR_STATUS,126 status, status & CMDQ_THR_STATUS_SUSPENDED, 0, 10)) {127 dev_err(cmdq->mbox.dev, "suspend GCE thread 0x%x failed\n",128 (u32)(thread->base - cmdq->base));129 return -EFAULT;130 }131 132 return 0;133}134 135static void cmdq_thread_resume(struct cmdq_thread *thread)136{137 writel(CMDQ_THR_RESUME, thread->base + CMDQ_THR_SUSPEND_TASK);138}139 140static void cmdq_init(struct cmdq *cmdq)141{142 int i;143 u32 gctl_regval = 0;144 145 WARN_ON(clk_bulk_enable(cmdq->pdata->gce_num, cmdq->clocks));146 if (cmdq->pdata->control_by_sw)147 gctl_regval = GCE_CTRL_BY_SW;148 if (cmdq->pdata->sw_ddr_en)149 gctl_regval |= GCE_DDR_EN;150 151 if (gctl_regval)152 writel(gctl_regval, cmdq->base + GCE_GCTL_VALUE);153 154 writel(CMDQ_THR_ACTIVE_SLOT_CYCLES, cmdq->base + CMDQ_THR_SLOT_CYCLES);155 for (i = 0; i <= CMDQ_MAX_EVENT; i++)156 writel(i, cmdq->base + CMDQ_SYNC_TOKEN_UPDATE);157 clk_bulk_disable(cmdq->pdata->gce_num, cmdq->clocks);158}159 160static int cmdq_thread_reset(struct cmdq *cmdq, struct cmdq_thread *thread)161{162 u32 warm_reset;163 164 writel(CMDQ_THR_DO_WARM_RESET, thread->base + CMDQ_THR_WARM_RESET);165 if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_WARM_RESET,166 warm_reset, !(warm_reset & CMDQ_THR_DO_WARM_RESET),167 0, 10)) {168 dev_err(cmdq->mbox.dev, "reset GCE thread 0x%x failed\n",169 (u32)(thread->base - cmdq->base));170 return -EFAULT;171 }172 173 return 0;174}175 176static void cmdq_thread_disable(struct cmdq *cmdq, struct cmdq_thread *thread)177{178 cmdq_thread_reset(cmdq, thread);179 writel(CMDQ_THR_DISABLED, thread->base + CMDQ_THR_ENABLE_TASK);180}181 182/* notify GCE to re-fetch commands by setting GCE thread PC */183static void cmdq_thread_invalidate_fetched_data(struct cmdq_thread *thread)184{185 writel(readl(thread->base + CMDQ_THR_CURR_ADDR),186 thread->base + CMDQ_THR_CURR_ADDR);187}188 189static void cmdq_task_insert_into_thread(struct cmdq_task *task)190{191 struct device *dev = task->cmdq->mbox.dev;192 struct cmdq_thread *thread = task->thread;193 struct cmdq_task *prev_task = list_last_entry(194 &thread->task_busy_list, typeof(*task), list_entry);195 u64 *prev_task_base = prev_task->pkt->va_base;196 197 /* let previous task jump to this task */198 dma_sync_single_for_cpu(dev, prev_task->pa_base,199 prev_task->pkt->cmd_buf_size, DMA_TO_DEVICE);200 prev_task_base[CMDQ_NUM_CMD(prev_task->pkt) - 1] =201 (u64)CMDQ_JUMP_BY_PA << 32 |202 (task->pa_base >> task->cmdq->pdata->shift);203 dma_sync_single_for_device(dev, prev_task->pa_base,204 prev_task->pkt->cmd_buf_size, DMA_TO_DEVICE);205 206 cmdq_thread_invalidate_fetched_data(thread);207}208 209static bool cmdq_thread_is_in_wfe(struct cmdq_thread *thread)210{211 return readl(thread->base + CMDQ_THR_WAIT_TOKEN) & CMDQ_THR_IS_WAITING;212}213 214static void cmdq_task_exec_done(struct cmdq_task *task, int sta)215{216 struct cmdq_cb_data data;217 218 data.sta = sta;219 data.pkt = task->pkt;220 mbox_chan_received_data(task->thread->chan, &data);221 222 list_del(&task->list_entry);223}224 225static void cmdq_task_handle_error(struct cmdq_task *task)226{227 struct cmdq_thread *thread = task->thread;228 struct cmdq_task *next_task;229 struct cmdq *cmdq = task->cmdq;230 231 dev_err(cmdq->mbox.dev, "task 0x%p error\n", task);232 WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);233 next_task = list_first_entry_or_null(&thread->task_busy_list,234 struct cmdq_task, list_entry);235 if (next_task)236 writel(next_task->pa_base >> cmdq->pdata->shift,237 thread->base + CMDQ_THR_CURR_ADDR);238 cmdq_thread_resume(thread);239}240 241static void cmdq_thread_irq_handler(struct cmdq *cmdq,242 struct cmdq_thread *thread)243{244 struct cmdq_task *task, *tmp, *curr_task = NULL;245 u32 curr_pa, irq_flag, task_end_pa;246 bool err;247 248 irq_flag = readl(thread->base + CMDQ_THR_IRQ_STATUS);249 writel(~irq_flag, thread->base + CMDQ_THR_IRQ_STATUS);250 251 /*252 * When ISR call this function, another CPU core could run253 * "release task" right before we acquire the spin lock, and thus254 * reset / disable this GCE thread, so we need to check the enable255 * bit of this GCE thread.256 */257 if (!(readl(thread->base + CMDQ_THR_ENABLE_TASK) & CMDQ_THR_ENABLED))258 return;259 260 if (irq_flag & CMDQ_THR_IRQ_ERROR)261 err = true;262 else if (irq_flag & CMDQ_THR_IRQ_DONE)263 err = false;264 else265 return;266 267 curr_pa = readl(thread->base + CMDQ_THR_CURR_ADDR) << cmdq->pdata->shift;268 269 list_for_each_entry_safe(task, tmp, &thread->task_busy_list,270 list_entry) {271 task_end_pa = task->pa_base + task->pkt->cmd_buf_size;272 if (curr_pa >= task->pa_base && curr_pa < task_end_pa)273 curr_task = task;274 275 if (!curr_task || curr_pa == task_end_pa - CMDQ_INST_SIZE) {276 cmdq_task_exec_done(task, 0);277 kfree(task);278 } else if (err) {279 cmdq_task_exec_done(task, -ENOEXEC);280 cmdq_task_handle_error(curr_task);281 kfree(task);282 }283 284 if (curr_task)285 break;286 }287 288 if (list_empty(&thread->task_busy_list))289 cmdq_thread_disable(cmdq, thread);290}291 292static irqreturn_t cmdq_irq_handler(int irq, void *dev)293{294 struct cmdq *cmdq = dev;295 unsigned long irq_status, flags = 0L;296 int bit;297 298 irq_status = readl(cmdq->base + CMDQ_CURR_IRQ_STATUS) & cmdq->irq_mask;299 if (!(irq_status ^ cmdq->irq_mask))300 return IRQ_NONE;301 302 for_each_clear_bit(bit, &irq_status, cmdq->pdata->thread_nr) {303 struct cmdq_thread *thread = &cmdq->thread[bit];304 305 spin_lock_irqsave(&thread->chan->lock, flags);306 cmdq_thread_irq_handler(cmdq, thread);307 spin_unlock_irqrestore(&thread->chan->lock, flags);308 }309 310 pm_runtime_mark_last_busy(cmdq->mbox.dev);311 312 return IRQ_HANDLED;313}314 315static int cmdq_runtime_resume(struct device *dev)316{317 struct cmdq *cmdq = dev_get_drvdata(dev);318 319 return clk_bulk_enable(cmdq->pdata->gce_num, cmdq->clocks);320}321 322static int cmdq_runtime_suspend(struct device *dev)323{324 struct cmdq *cmdq = dev_get_drvdata(dev);325 326 clk_bulk_disable(cmdq->pdata->gce_num, cmdq->clocks);327 return 0;328}329 330static int cmdq_suspend(struct device *dev)331{332 struct cmdq *cmdq = dev_get_drvdata(dev);333 struct cmdq_thread *thread;334 int i;335 bool task_running = false;336 337 cmdq->suspended = true;338 339 for (i = 0; i < cmdq->pdata->thread_nr; i++) {340 thread = &cmdq->thread[i];341 if (!list_empty(&thread->task_busy_list)) {342 task_running = true;343 break;344 }345 }346 347 if (task_running)348 dev_warn(dev, "exist running task(s) in suspend\n");349 350 if (cmdq->pdata->sw_ddr_en)351 cmdq_sw_ddr_enable(cmdq, false);352 353 return pm_runtime_force_suspend(dev);354}355 356static int cmdq_resume(struct device *dev)357{358 struct cmdq *cmdq = dev_get_drvdata(dev);359 360 WARN_ON(pm_runtime_force_resume(dev));361 cmdq->suspended = false;362 363 if (cmdq->pdata->sw_ddr_en)364 cmdq_sw_ddr_enable(cmdq, true);365 366 return 0;367}368 369static void cmdq_remove(struct platform_device *pdev)370{371 struct cmdq *cmdq = platform_get_drvdata(pdev);372 373 if (cmdq->pdata->sw_ddr_en)374 cmdq_sw_ddr_enable(cmdq, false);375 376 if (!IS_ENABLED(CONFIG_PM))377 cmdq_runtime_suspend(&pdev->dev);378 379 clk_bulk_unprepare(cmdq->pdata->gce_num, cmdq->clocks);380}381 382static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data)383{384 struct cmdq_pkt *pkt = (struct cmdq_pkt *)data;385 struct cmdq_thread *thread = (struct cmdq_thread *)chan->con_priv;386 struct cmdq *cmdq = dev_get_drvdata(chan->mbox->dev);387 struct cmdq_task *task;388 unsigned long curr_pa, end_pa;389 int ret;390 391 /* Client should not flush new tasks if suspended. */392 WARN_ON(cmdq->suspended);393 394 ret = pm_runtime_get_sync(cmdq->mbox.dev);395 if (ret < 0)396 return ret;397 398 task = kzalloc(sizeof(*task), GFP_ATOMIC);399 if (!task) {400 pm_runtime_put_autosuspend(cmdq->mbox.dev);401 return -ENOMEM;402 }403 404 task->cmdq = cmdq;405 INIT_LIST_HEAD(&task->list_entry);406 task->pa_base = pkt->pa_base;407 task->thread = thread;408 task->pkt = pkt;409 410 if (list_empty(&thread->task_busy_list)) {411 /*412 * The thread reset will clear thread related register to 0,413 * including pc, end, priority, irq, suspend and enable. Thus414 * set CMDQ_THR_ENABLED to CMDQ_THR_ENABLE_TASK will enable415 * thread and make it running.416 */417 WARN_ON(cmdq_thread_reset(cmdq, thread) < 0);418 419 writel(task->pa_base >> cmdq->pdata->shift,420 thread->base + CMDQ_THR_CURR_ADDR);421 writel((task->pa_base + pkt->cmd_buf_size) >> cmdq->pdata->shift,422 thread->base + CMDQ_THR_END_ADDR);423 424 writel(thread->priority, thread->base + CMDQ_THR_PRIORITY);425 writel(CMDQ_THR_IRQ_EN, thread->base + CMDQ_THR_IRQ_ENABLE);426 writel(CMDQ_THR_ENABLED, thread->base + CMDQ_THR_ENABLE_TASK);427 } else {428 WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);429 curr_pa = readl(thread->base + CMDQ_THR_CURR_ADDR) <<430 cmdq->pdata->shift;431 end_pa = readl(thread->base + CMDQ_THR_END_ADDR) <<432 cmdq->pdata->shift;433 /* check boundary */434 if (curr_pa == end_pa - CMDQ_INST_SIZE ||435 curr_pa == end_pa) {436 /* set to this task directly */437 writel(task->pa_base >> cmdq->pdata->shift,438 thread->base + CMDQ_THR_CURR_ADDR);439 } else {440 cmdq_task_insert_into_thread(task);441 smp_mb(); /* modify jump before enable thread */442 }443 writel((task->pa_base + pkt->cmd_buf_size) >> cmdq->pdata->shift,444 thread->base + CMDQ_THR_END_ADDR);445 cmdq_thread_resume(thread);446 }447 list_move_tail(&task->list_entry, &thread->task_busy_list);448 449 pm_runtime_mark_last_busy(cmdq->mbox.dev);450 pm_runtime_put_autosuspend(cmdq->mbox.dev);451 452 return 0;453}454 455static int cmdq_mbox_startup(struct mbox_chan *chan)456{457 return 0;458}459 460static void cmdq_mbox_shutdown(struct mbox_chan *chan)461{462 struct cmdq_thread *thread = (struct cmdq_thread *)chan->con_priv;463 struct cmdq *cmdq = dev_get_drvdata(chan->mbox->dev);464 struct cmdq_task *task, *tmp;465 unsigned long flags;466 467 WARN_ON(pm_runtime_get_sync(cmdq->mbox.dev) < 0);468 469 spin_lock_irqsave(&thread->chan->lock, flags);470 if (list_empty(&thread->task_busy_list))471 goto done;472 473 WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);474 475 /* make sure executed tasks have success callback */476 cmdq_thread_irq_handler(cmdq, thread);477 if (list_empty(&thread->task_busy_list))478 goto done;479 480 list_for_each_entry_safe(task, tmp, &thread->task_busy_list,481 list_entry) {482 cmdq_task_exec_done(task, -ECONNABORTED);483 kfree(task);484 }485 486 cmdq_thread_disable(cmdq, thread);487 488done:489 /*490 * The thread->task_busy_list empty means thread already disable. The491 * cmdq_mbox_send_data() always reset thread which clear disable and492 * suspend statue when first pkt send to channel, so there is no need493 * to do any operation here, only unlock and leave.494 */495 spin_unlock_irqrestore(&thread->chan->lock, flags);496 497 pm_runtime_mark_last_busy(cmdq->mbox.dev);498 pm_runtime_put_autosuspend(cmdq->mbox.dev);499}500 501static int cmdq_mbox_flush(struct mbox_chan *chan, unsigned long timeout)502{503 struct cmdq_thread *thread = (struct cmdq_thread *)chan->con_priv;504 struct cmdq_cb_data data;505 struct cmdq *cmdq = dev_get_drvdata(chan->mbox->dev);506 struct cmdq_task *task, *tmp;507 unsigned long flags;508 u32 enable;509 int ret;510 511 ret = pm_runtime_get_sync(cmdq->mbox.dev);512 if (ret < 0)513 return ret;514 515 spin_lock_irqsave(&thread->chan->lock, flags);516 if (list_empty(&thread->task_busy_list))517 goto out;518 519 WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);520 if (!cmdq_thread_is_in_wfe(thread))521 goto wait;522 523 list_for_each_entry_safe(task, tmp, &thread->task_busy_list,524 list_entry) {525 data.sta = -ECONNABORTED;526 data.pkt = task->pkt;527 mbox_chan_received_data(task->thread->chan, &data);528 list_del(&task->list_entry);529 kfree(task);530 }531 532 cmdq_thread_resume(thread);533 cmdq_thread_disable(cmdq, thread);534 535out:536 spin_unlock_irqrestore(&thread->chan->lock, flags);537 pm_runtime_mark_last_busy(cmdq->mbox.dev);538 pm_runtime_put_autosuspend(cmdq->mbox.dev);539 540 return 0;541 542wait:543 cmdq_thread_resume(thread);544 spin_unlock_irqrestore(&thread->chan->lock, flags);545 if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_ENABLE_TASK,546 enable, enable == 0, 1, timeout)) {547 dev_err(cmdq->mbox.dev, "Fail to wait GCE thread 0x%x done\n",548 (u32)(thread->base - cmdq->base));549 550 return -EFAULT;551 }552 pm_runtime_mark_last_busy(cmdq->mbox.dev);553 pm_runtime_put_autosuspend(cmdq->mbox.dev);554 return 0;555}556 557static const struct mbox_chan_ops cmdq_mbox_chan_ops = {558 .send_data = cmdq_mbox_send_data,559 .startup = cmdq_mbox_startup,560 .shutdown = cmdq_mbox_shutdown,561 .flush = cmdq_mbox_flush,562};563 564static struct mbox_chan *cmdq_xlate(struct mbox_controller *mbox,565 const struct of_phandle_args *sp)566{567 int ind = sp->args[0];568 struct cmdq_thread *thread;569 570 if (ind >= mbox->num_chans)571 return ERR_PTR(-EINVAL);572 573 thread = (struct cmdq_thread *)mbox->chans[ind].con_priv;574 thread->priority = sp->args[1];575 thread->chan = &mbox->chans[ind];576 577 return &mbox->chans[ind];578}579 580static int cmdq_get_clocks(struct device *dev, struct cmdq *cmdq)581{582 static const char * const gce_name = "gce";583 struct device_node *node, *parent = dev->of_node->parent;584 struct clk_bulk_data *clks;585 586 cmdq->clocks = devm_kcalloc(dev, cmdq->pdata->gce_num,587 sizeof(cmdq->clocks), GFP_KERNEL);588 if (!cmdq->clocks)589 return -ENOMEM;590 591 if (cmdq->pdata->gce_num == 1) {592 clks = &cmdq->clocks[0];593 594 clks->id = gce_name;595 clks->clk = devm_clk_get(dev, NULL);596 if (IS_ERR(clks->clk))597 return dev_err_probe(dev, PTR_ERR(clks->clk),598 "failed to get gce clock\n");599 600 return 0;601 }602 603 /*604 * If there is more than one GCE, get the clocks for the others too,605 * as the clock of the main GCE must be enabled for additional IPs606 * to be reachable.607 */608 for_each_child_of_node(parent, node) {609 int alias_id = of_alias_get_id(node, gce_name);610 611 if (alias_id < 0 || alias_id >= cmdq->pdata->gce_num)612 continue;613 614 clks = &cmdq->clocks[alias_id];615 616 clks->id = devm_kasprintf(dev, GFP_KERNEL, "gce%d", alias_id);617 if (!clks->id) {618 of_node_put(node);619 return -ENOMEM;620 }621 622 clks->clk = of_clk_get(node, 0);623 if (IS_ERR(clks->clk)) {624 of_node_put(node);625 return dev_err_probe(dev, PTR_ERR(clks->clk),626 "failed to get gce%d clock\n", alias_id);627 }628 }629 630 return 0;631}632 633static int cmdq_probe(struct platform_device *pdev)634{635 struct device *dev = &pdev->dev;636 struct cmdq *cmdq;637 int err, i;638 639 cmdq = devm_kzalloc(dev, sizeof(*cmdq), GFP_KERNEL);640 if (!cmdq)641 return -ENOMEM;642 643 cmdq->base = devm_platform_ioremap_resource(pdev, 0);644 if (IS_ERR(cmdq->base))645 return PTR_ERR(cmdq->base);646 647 cmdq->irq = platform_get_irq(pdev, 0);648 if (cmdq->irq < 0)649 return cmdq->irq;650 651 cmdq->pdata = device_get_match_data(dev);652 if (!cmdq->pdata) {653 dev_err(dev, "failed to get match data\n");654 return -EINVAL;655 }656 657 cmdq->irq_mask = GENMASK(cmdq->pdata->thread_nr - 1, 0);658 659 dev_dbg(dev, "cmdq device: addr:0x%p, va:0x%p, irq:%d\n",660 dev, cmdq->base, cmdq->irq);661 662 err = cmdq_get_clocks(dev, cmdq);663 if (err)664 return err;665 666 cmdq->mbox.dev = dev;667 cmdq->mbox.chans = devm_kcalloc(dev, cmdq->pdata->thread_nr,668 sizeof(*cmdq->mbox.chans), GFP_KERNEL);669 if (!cmdq->mbox.chans)670 return -ENOMEM;671 672 cmdq->mbox.num_chans = cmdq->pdata->thread_nr;673 cmdq->mbox.ops = &cmdq_mbox_chan_ops;674 cmdq->mbox.of_xlate = cmdq_xlate;675 676 /* make use of TXDONE_BY_ACK */677 cmdq->mbox.txdone_irq = false;678 cmdq->mbox.txdone_poll = false;679 680 cmdq->thread = devm_kcalloc(dev, cmdq->pdata->thread_nr,681 sizeof(*cmdq->thread), GFP_KERNEL);682 if (!cmdq->thread)683 return -ENOMEM;684 685 for (i = 0; i < cmdq->pdata->thread_nr; i++) {686 cmdq->thread[i].base = cmdq->base + CMDQ_THR_BASE +687 CMDQ_THR_SIZE * i;688 INIT_LIST_HEAD(&cmdq->thread[i].task_busy_list);689 cmdq->mbox.chans[i].con_priv = (void *)&cmdq->thread[i];690 }691 692 platform_set_drvdata(pdev, cmdq);693 694 WARN_ON(clk_bulk_prepare(cmdq->pdata->gce_num, cmdq->clocks));695 696 cmdq_init(cmdq);697 698 err = devm_request_irq(dev, cmdq->irq, cmdq_irq_handler, IRQF_SHARED,699 "mtk_cmdq", cmdq);700 if (err < 0) {701 dev_err(dev, "failed to register ISR (%d)\n", err);702 return err;703 }704 705 /* If Runtime PM is not available enable the clocks now. */706 if (!IS_ENABLED(CONFIG_PM)) {707 err = cmdq_runtime_resume(dev);708 if (err)709 return err;710 }711 712 err = devm_pm_runtime_enable(dev);713 if (err)714 return err;715 716 pm_runtime_set_autosuspend_delay(dev, CMDQ_MBOX_AUTOSUSPEND_DELAY_MS);717 pm_runtime_use_autosuspend(dev);718 719 err = devm_mbox_controller_register(dev, &cmdq->mbox);720 if (err < 0) {721 dev_err(dev, "failed to register mailbox: %d\n", err);722 return err;723 }724 725 return 0;726}727 728static const struct dev_pm_ops cmdq_pm_ops = {729 .suspend = cmdq_suspend,730 .resume = cmdq_resume,731 SET_RUNTIME_PM_OPS(cmdq_runtime_suspend,732 cmdq_runtime_resume, NULL)733};734 735static const struct gce_plat gce_plat_mt6779 = {736 .thread_nr = 24,737 .shift = 3,738 .control_by_sw = false,739 .gce_num = 1740};741 742static const struct gce_plat gce_plat_mt8173 = {743 .thread_nr = 16,744 .shift = 0,745 .control_by_sw = false,746 .gce_num = 1747};748 749static const struct gce_plat gce_plat_mt8183 = {750 .thread_nr = 24,751 .shift = 0,752 .control_by_sw = false,753 .gce_num = 1754};755 756static const struct gce_plat gce_plat_mt8186 = {757 .thread_nr = 24,758 .shift = 3,759 .control_by_sw = true,760 .sw_ddr_en = true,761 .gce_num = 1762};763 764static const struct gce_plat gce_plat_mt8188 = {765 .thread_nr = 32,766 .shift = 3,767 .control_by_sw = true,768 .gce_num = 2769};770 771static const struct gce_plat gce_plat_mt8192 = {772 .thread_nr = 24,773 .shift = 3,774 .control_by_sw = true,775 .gce_num = 1776};777 778static const struct gce_plat gce_plat_mt8195 = {779 .thread_nr = 24,780 .shift = 3,781 .control_by_sw = true,782 .gce_num = 2783};784 785static const struct of_device_id cmdq_of_ids[] = {786 {.compatible = "mediatek,mt6779-gce", .data = (void *)&gce_plat_mt6779},787 {.compatible = "mediatek,mt8173-gce", .data = (void *)&gce_plat_mt8173},788 {.compatible = "mediatek,mt8183-gce", .data = (void *)&gce_plat_mt8183},789 {.compatible = "mediatek,mt8186-gce", .data = (void *)&gce_plat_mt8186},790 {.compatible = "mediatek,mt8188-gce", .data = (void *)&gce_plat_mt8188},791 {.compatible = "mediatek,mt8192-gce", .data = (void *)&gce_plat_mt8192},792 {.compatible = "mediatek,mt8195-gce", .data = (void *)&gce_plat_mt8195},793 {}794};795MODULE_DEVICE_TABLE(of, cmdq_of_ids);796 797static struct platform_driver cmdq_drv = {798 .probe = cmdq_probe,799 .remove_new = cmdq_remove,800 .driver = {801 .name = "mtk_cmdq",802 .pm = &cmdq_pm_ops,803 .of_match_table = cmdq_of_ids,804 }805};806 807static int __init cmdq_drv_init(void)808{809 return platform_driver_register(&cmdq_drv);810}811 812static void __exit cmdq_drv_exit(void)813{814 platform_driver_unregister(&cmdq_drv);815}816 817subsys_initcall(cmdq_drv_init);818module_exit(cmdq_drv_exit);819 820MODULE_DESCRIPTION("Mediatek Command Queue(CMDQ) Mailbox driver");821MODULE_LICENSE("GPL v2");822