162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2015, Fuzhou Rockchip Electronics Co., Ltd 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/clk.h> 762306a36Sopenharmony_ci#include <linux/interrupt.h> 862306a36Sopenharmony_ci#include <linux/io.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/mailbox_controller.h> 1162306a36Sopenharmony_ci#include <linux/of.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define MAILBOX_A2B_INTEN 0x00 1662306a36Sopenharmony_ci#define MAILBOX_A2B_STATUS 0x04 1762306a36Sopenharmony_ci#define MAILBOX_A2B_CMD(x) (0x08 + (x) * 8) 1862306a36Sopenharmony_ci#define MAILBOX_A2B_DAT(x) (0x0c + (x) * 8) 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define MAILBOX_B2A_INTEN 0x28 2162306a36Sopenharmony_ci#define MAILBOX_B2A_STATUS 0x2C 2262306a36Sopenharmony_ci#define MAILBOX_B2A_CMD(x) (0x30 + (x) * 8) 2362306a36Sopenharmony_ci#define MAILBOX_B2A_DAT(x) (0x34 + (x) * 8) 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct rockchip_mbox_msg { 2662306a36Sopenharmony_ci u32 cmd; 2762306a36Sopenharmony_ci int rx_size; 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct rockchip_mbox_data { 3162306a36Sopenharmony_ci int num_chans; 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistruct rockchip_mbox_chan { 3562306a36Sopenharmony_ci int idx; 3662306a36Sopenharmony_ci int irq; 3762306a36Sopenharmony_ci struct rockchip_mbox_msg *msg; 3862306a36Sopenharmony_ci struct rockchip_mbox *mb; 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistruct rockchip_mbox { 4262306a36Sopenharmony_ci struct mbox_controller mbox; 4362306a36Sopenharmony_ci struct clk *pclk; 4462306a36Sopenharmony_ci void __iomem *mbox_base; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* The maximum size of buf for each channel */ 4762306a36Sopenharmony_ci u32 buf_size; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci struct rockchip_mbox_chan *chans; 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic int rockchip_mbox_send_data(struct mbox_chan *chan, void *data) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct rockchip_mbox *mb = dev_get_drvdata(chan->mbox->dev); 5562306a36Sopenharmony_ci struct rockchip_mbox_msg *msg = data; 5662306a36Sopenharmony_ci struct rockchip_mbox_chan *chans = mb->chans; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (!msg) 5962306a36Sopenharmony_ci return -EINVAL; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (msg->rx_size > mb->buf_size) { 6262306a36Sopenharmony_ci dev_err(mb->mbox.dev, "Transmit size over buf size(%d)\n", 6362306a36Sopenharmony_ci mb->buf_size); 6462306a36Sopenharmony_ci return -EINVAL; 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci dev_dbg(mb->mbox.dev, "Chan[%d]: A2B message, cmd 0x%08x\n", 6862306a36Sopenharmony_ci chans->idx, msg->cmd); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci mb->chans[chans->idx].msg = msg; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci writel_relaxed(msg->cmd, mb->mbox_base + MAILBOX_A2B_CMD(chans->idx)); 7362306a36Sopenharmony_ci writel_relaxed(msg->rx_size, mb->mbox_base + 7462306a36Sopenharmony_ci MAILBOX_A2B_DAT(chans->idx)); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return 0; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic int rockchip_mbox_startup(struct mbox_chan *chan) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct rockchip_mbox *mb = dev_get_drvdata(chan->mbox->dev); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* Enable all B2A interrupts */ 8462306a36Sopenharmony_ci writel_relaxed((1 << mb->mbox.num_chans) - 1, 8562306a36Sopenharmony_ci mb->mbox_base + MAILBOX_B2A_INTEN); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci return 0; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic void rockchip_mbox_shutdown(struct mbox_chan *chan) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct rockchip_mbox *mb = dev_get_drvdata(chan->mbox->dev); 9362306a36Sopenharmony_ci struct rockchip_mbox_chan *chans = mb->chans; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* Disable all B2A interrupts */ 9662306a36Sopenharmony_ci writel_relaxed(0, mb->mbox_base + MAILBOX_B2A_INTEN); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci mb->chans[chans->idx].msg = NULL; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic const struct mbox_chan_ops rockchip_mbox_chan_ops = { 10262306a36Sopenharmony_ci .send_data = rockchip_mbox_send_data, 10362306a36Sopenharmony_ci .startup = rockchip_mbox_startup, 10462306a36Sopenharmony_ci .shutdown = rockchip_mbox_shutdown, 10562306a36Sopenharmony_ci}; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic irqreturn_t rockchip_mbox_irq(int irq, void *dev_id) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci int idx; 11062306a36Sopenharmony_ci struct rockchip_mbox *mb = (struct rockchip_mbox *)dev_id; 11162306a36Sopenharmony_ci u32 status = readl_relaxed(mb->mbox_base + MAILBOX_B2A_STATUS); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci for (idx = 0; idx < mb->mbox.num_chans; idx++) { 11462306a36Sopenharmony_ci if ((status & (1 << idx)) && (irq == mb->chans[idx].irq)) { 11562306a36Sopenharmony_ci /* Clear mbox interrupt */ 11662306a36Sopenharmony_ci writel_relaxed(1 << idx, 11762306a36Sopenharmony_ci mb->mbox_base + MAILBOX_B2A_STATUS); 11862306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return IRQ_NONE; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic irqreturn_t rockchip_mbox_isr(int irq, void *dev_id) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci int idx; 12862306a36Sopenharmony_ci struct rockchip_mbox_msg *msg = NULL; 12962306a36Sopenharmony_ci struct rockchip_mbox *mb = (struct rockchip_mbox *)dev_id; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci for (idx = 0; idx < mb->mbox.num_chans; idx++) { 13262306a36Sopenharmony_ci if (irq != mb->chans[idx].irq) 13362306a36Sopenharmony_ci continue; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci msg = mb->chans[idx].msg; 13662306a36Sopenharmony_ci if (!msg) { 13762306a36Sopenharmony_ci dev_err(mb->mbox.dev, 13862306a36Sopenharmony_ci "Chan[%d]: B2A message is NULL\n", idx); 13962306a36Sopenharmony_ci break; /* spurious */ 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci mbox_chan_received_data(&mb->mbox.chans[idx], msg); 14362306a36Sopenharmony_ci mb->chans[idx].msg = NULL; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci dev_dbg(mb->mbox.dev, "Chan[%d]: B2A message, cmd 0x%08x\n", 14662306a36Sopenharmony_ci idx, msg->cmd); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci break; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return IRQ_HANDLED; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic const struct rockchip_mbox_data rk3368_drv_data = { 15562306a36Sopenharmony_ci .num_chans = 4, 15662306a36Sopenharmony_ci}; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic const struct of_device_id rockchip_mbox_of_match[] = { 15962306a36Sopenharmony_ci { .compatible = "rockchip,rk3368-mailbox", .data = &rk3368_drv_data}, 16062306a36Sopenharmony_ci { }, 16162306a36Sopenharmony_ci}; 16262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, rockchp_mbox_of_match); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic int rockchip_mbox_probe(struct platform_device *pdev) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct rockchip_mbox *mb; 16762306a36Sopenharmony_ci const struct rockchip_mbox_data *drv_data; 16862306a36Sopenharmony_ci struct resource *res; 16962306a36Sopenharmony_ci int ret, irq, i; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (!pdev->dev.of_node) 17262306a36Sopenharmony_ci return -ENODEV; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci drv_data = (const struct rockchip_mbox_data *) device_get_match_data(&pdev->dev); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci mb = devm_kzalloc(&pdev->dev, sizeof(*mb), GFP_KERNEL); 17762306a36Sopenharmony_ci if (!mb) 17862306a36Sopenharmony_ci return -ENOMEM; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci mb->chans = devm_kcalloc(&pdev->dev, drv_data->num_chans, 18162306a36Sopenharmony_ci sizeof(*mb->chans), GFP_KERNEL); 18262306a36Sopenharmony_ci if (!mb->chans) 18362306a36Sopenharmony_ci return -ENOMEM; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci mb->mbox.chans = devm_kcalloc(&pdev->dev, drv_data->num_chans, 18662306a36Sopenharmony_ci sizeof(*mb->mbox.chans), GFP_KERNEL); 18762306a36Sopenharmony_ci if (!mb->mbox.chans) 18862306a36Sopenharmony_ci return -ENOMEM; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci platform_set_drvdata(pdev, mb); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci mb->mbox.dev = &pdev->dev; 19362306a36Sopenharmony_ci mb->mbox.num_chans = drv_data->num_chans; 19462306a36Sopenharmony_ci mb->mbox.ops = &rockchip_mbox_chan_ops; 19562306a36Sopenharmony_ci mb->mbox.txdone_irq = true; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci mb->mbox_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 19862306a36Sopenharmony_ci if (IS_ERR(mb->mbox_base)) 19962306a36Sopenharmony_ci return PTR_ERR(mb->mbox_base); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* Each channel has two buffers for A2B and B2A */ 20262306a36Sopenharmony_ci mb->buf_size = (size_t)resource_size(res) / (drv_data->num_chans * 2); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci mb->pclk = devm_clk_get(&pdev->dev, "pclk_mailbox"); 20562306a36Sopenharmony_ci if (IS_ERR(mb->pclk)) { 20662306a36Sopenharmony_ci ret = PTR_ERR(mb->pclk); 20762306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get pclk_mailbox clock: %d\n", 20862306a36Sopenharmony_ci ret); 20962306a36Sopenharmony_ci return ret; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci ret = clk_prepare_enable(mb->pclk); 21362306a36Sopenharmony_ci if (ret) { 21462306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to enable pclk: %d\n", ret); 21562306a36Sopenharmony_ci return ret; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci for (i = 0; i < mb->mbox.num_chans; i++) { 21962306a36Sopenharmony_ci irq = platform_get_irq(pdev, i); 22062306a36Sopenharmony_ci if (irq < 0) 22162306a36Sopenharmony_ci return irq; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, irq, 22462306a36Sopenharmony_ci rockchip_mbox_irq, 22562306a36Sopenharmony_ci rockchip_mbox_isr, IRQF_ONESHOT, 22662306a36Sopenharmony_ci dev_name(&pdev->dev), mb); 22762306a36Sopenharmony_ci if (ret < 0) 22862306a36Sopenharmony_ci return ret; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci mb->chans[i].idx = i; 23162306a36Sopenharmony_ci mb->chans[i].irq = irq; 23262306a36Sopenharmony_ci mb->chans[i].mb = mb; 23362306a36Sopenharmony_ci mb->chans[i].msg = NULL; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci ret = devm_mbox_controller_register(&pdev->dev, &mb->mbox); 23762306a36Sopenharmony_ci if (ret < 0) 23862306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to register mailbox: %d\n", ret); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci return ret; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic struct platform_driver rockchip_mbox_driver = { 24462306a36Sopenharmony_ci .probe = rockchip_mbox_probe, 24562306a36Sopenharmony_ci .driver = { 24662306a36Sopenharmony_ci .name = "rockchip-mailbox", 24762306a36Sopenharmony_ci .of_match_table = rockchip_mbox_of_match, 24862306a36Sopenharmony_ci }, 24962306a36Sopenharmony_ci}; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cimodule_platform_driver(rockchip_mbox_driver); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ciMODULE_DESCRIPTION("Rockchip mailbox: communicate between CPU cores and MCU"); 25462306a36Sopenharmony_ciMODULE_AUTHOR("Addy Ke <addy.ke@rock-chips.com>"); 25562306a36Sopenharmony_ciMODULE_AUTHOR("Caesar Wang <wxt@rock-chips.com>"); 256