162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Microchip PolarFire SoC (MPFS) system controller/mailbox controller driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2020-2022 Microchip Corporation. All rights reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Conor Dooley <conor.dooley@microchip.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/err.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 1862306a36Sopenharmony_ci#include <linux/platform_device.h> 1962306a36Sopenharmony_ci#include <linux/mailbox_controller.h> 2062306a36Sopenharmony_ci#include <soc/microchip/mpfs.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define SERVICES_CR_OFFSET 0x50u 2362306a36Sopenharmony_ci#define SERVICES_SR_OFFSET 0x54u 2462306a36Sopenharmony_ci#define MAILBOX_REG_OFFSET 0x800u 2562306a36Sopenharmony_ci#define MSS_SYS_MAILBOX_DATA_OFFSET 0u 2662306a36Sopenharmony_ci#define SCB_MASK_WIDTH 16u 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* SCBCTRL service control register */ 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define SCB_CTRL_REQ (0) 3162306a36Sopenharmony_ci#define SCB_CTRL_REQ_MASK BIT(SCB_CTRL_REQ) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define SCB_CTRL_BUSY (1) 3462306a36Sopenharmony_ci#define SCB_CTRL_BUSY_MASK BIT(SCB_CTRL_BUSY) 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define SCB_CTRL_ABORT (2) 3762306a36Sopenharmony_ci#define SCB_CTRL_ABORT_MASK BIT(SCB_CTRL_ABORT) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define SCB_CTRL_NOTIFY (3) 4062306a36Sopenharmony_ci#define SCB_CTRL_NOTIFY_MASK BIT(SCB_CTRL_NOTIFY) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define SCB_CTRL_POS (16) 4362306a36Sopenharmony_ci#define SCB_CTRL_MASK GENMASK(SCB_CTRL_POS + SCB_MASK_WIDTH - 1, SCB_CTRL_POS) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* SCBCTRL service status register */ 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define SCB_STATUS_REQ (0) 4862306a36Sopenharmony_ci#define SCB_STATUS_REQ_MASK BIT(SCB_STATUS_REQ) 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define SCB_STATUS_BUSY (1) 5162306a36Sopenharmony_ci#define SCB_STATUS_BUSY_MASK BIT(SCB_STATUS_BUSY) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define SCB_STATUS_ABORT (2) 5462306a36Sopenharmony_ci#define SCB_STATUS_ABORT_MASK BIT(SCB_STATUS_ABORT) 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define SCB_STATUS_NOTIFY (3) 5762306a36Sopenharmony_ci#define SCB_STATUS_NOTIFY_MASK BIT(SCB_STATUS_NOTIFY) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define SCB_STATUS_POS (16) 6062306a36Sopenharmony_ci#define SCB_STATUS_MASK GENMASK(SCB_STATUS_POS + SCB_MASK_WIDTH - 1, SCB_STATUS_POS) 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistruct mpfs_mbox { 6362306a36Sopenharmony_ci struct mbox_controller controller; 6462306a36Sopenharmony_ci struct device *dev; 6562306a36Sopenharmony_ci int irq; 6662306a36Sopenharmony_ci void __iomem *ctrl_base; 6762306a36Sopenharmony_ci void __iomem *mbox_base; 6862306a36Sopenharmony_ci void __iomem *int_reg; 6962306a36Sopenharmony_ci struct mbox_chan chans[1]; 7062306a36Sopenharmony_ci struct mpfs_mss_response *response; 7162306a36Sopenharmony_ci u16 resp_offset; 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic bool mpfs_mbox_busy(struct mpfs_mbox *mbox) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci u32 status; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci status = readl_relaxed(mbox->ctrl_base + SERVICES_SR_OFFSET); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return status & SCB_STATUS_BUSY_MASK; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic bool mpfs_mbox_last_tx_done(struct mbox_chan *chan) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv; 8662306a36Sopenharmony_ci struct mpfs_mss_response *response = mbox->response; 8762306a36Sopenharmony_ci u32 val; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (mpfs_mbox_busy(mbox)) 9062306a36Sopenharmony_ci return false; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* 9362306a36Sopenharmony_ci * The service status is stored in bits 31:16 of the SERVICES_SR 9462306a36Sopenharmony_ci * register & is only valid when the system controller is not busy. 9562306a36Sopenharmony_ci * Failed services are intended to generated interrupts, but in reality 9662306a36Sopenharmony_ci * this does not happen, so the status must be checked here. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci val = readl_relaxed(mbox->ctrl_base + SERVICES_SR_OFFSET); 9962306a36Sopenharmony_ci response->resp_status = (val & SCB_STATUS_MASK) >> SCB_STATUS_POS; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return true; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int mpfs_mbox_send_data(struct mbox_chan *chan, void *data) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv; 10762306a36Sopenharmony_ci struct mpfs_mss_msg *msg = data; 10862306a36Sopenharmony_ci u32 tx_trigger; 10962306a36Sopenharmony_ci u16 opt_sel; 11062306a36Sopenharmony_ci u32 val = 0u; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci mbox->response = msg->response; 11362306a36Sopenharmony_ci mbox->resp_offset = msg->resp_offset; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (mpfs_mbox_busy(mbox)) 11662306a36Sopenharmony_ci return -EBUSY; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (msg->cmd_data_size) { 11962306a36Sopenharmony_ci u32 index; 12062306a36Sopenharmony_ci u8 extra_bits = msg->cmd_data_size & 3; 12162306a36Sopenharmony_ci u32 *word_buf = (u32 *)msg->cmd_data; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci for (index = 0; index < (msg->cmd_data_size / 4); index++) 12462306a36Sopenharmony_ci writel_relaxed(word_buf[index], 12562306a36Sopenharmony_ci mbox->mbox_base + msg->mbox_offset + index * 0x4); 12662306a36Sopenharmony_ci if (extra_bits) { 12762306a36Sopenharmony_ci u8 i; 12862306a36Sopenharmony_ci u8 byte_off = ALIGN_DOWN(msg->cmd_data_size, 4); 12962306a36Sopenharmony_ci u8 *byte_buf = msg->cmd_data + byte_off; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci val = readl_relaxed(mbox->mbox_base + msg->mbox_offset + index * 0x4); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci for (i = 0u; i < extra_bits; i++) { 13462306a36Sopenharmony_ci val &= ~(0xffu << (i * 8u)); 13562306a36Sopenharmony_ci val |= (byte_buf[i] << (i * 8u)); 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci writel_relaxed(val, mbox->mbox_base + msg->mbox_offset + index * 0x4); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci opt_sel = ((msg->mbox_offset << 7u) | (msg->cmd_opcode & 0x7fu)); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci tx_trigger = (opt_sel << SCB_CTRL_POS) & SCB_CTRL_MASK; 14562306a36Sopenharmony_ci tx_trigger |= SCB_CTRL_REQ_MASK | SCB_STATUS_NOTIFY_MASK; 14662306a36Sopenharmony_ci writel_relaxed(tx_trigger, mbox->ctrl_base + SERVICES_CR_OFFSET); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic void mpfs_mbox_rx_data(struct mbox_chan *chan) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv; 15462306a36Sopenharmony_ci struct mpfs_mss_response *response = mbox->response; 15562306a36Sopenharmony_ci u16 num_words = ALIGN((response->resp_size), (4)) / 4U; 15662306a36Sopenharmony_ci u32 i; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (!response->resp_msg) { 15962306a36Sopenharmony_ci dev_err(mbox->dev, "failed to assign memory for response %d\n", -ENOMEM); 16062306a36Sopenharmony_ci return; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* 16462306a36Sopenharmony_ci * We should *never* get an interrupt while the controller is 16562306a36Sopenharmony_ci * still in the busy state. If we do, something has gone badly 16662306a36Sopenharmony_ci * wrong & the content of the mailbox would not be valid. 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_ci if (mpfs_mbox_busy(mbox)) { 16962306a36Sopenharmony_ci dev_err(mbox->dev, "got an interrupt but system controller is busy\n"); 17062306a36Sopenharmony_ci response->resp_status = 0xDEAD; 17162306a36Sopenharmony_ci return; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci for (i = 0; i < num_words; i++) { 17562306a36Sopenharmony_ci response->resp_msg[i] = 17662306a36Sopenharmony_ci readl_relaxed(mbox->mbox_base 17762306a36Sopenharmony_ci + mbox->resp_offset + i * 0x4); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci mbox_chan_received_data(chan, response); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic irqreturn_t mpfs_mbox_inbox_isr(int irq, void *data) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct mbox_chan *chan = data; 18662306a36Sopenharmony_ci struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci writel_relaxed(0, mbox->int_reg); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci mpfs_mbox_rx_data(chan); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return IRQ_HANDLED; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic int mpfs_mbox_startup(struct mbox_chan *chan) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv; 19862306a36Sopenharmony_ci int ret = 0; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (!mbox) 20162306a36Sopenharmony_ci return -EINVAL; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci ret = devm_request_irq(mbox->dev, mbox->irq, mpfs_mbox_inbox_isr, 0, "mpfs-mailbox", chan); 20462306a36Sopenharmony_ci if (ret) 20562306a36Sopenharmony_ci dev_err(mbox->dev, "failed to register mailbox interrupt:%d\n", ret); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return ret; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic void mpfs_mbox_shutdown(struct mbox_chan *chan) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci devm_free_irq(mbox->dev, mbox->irq, chan); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic const struct mbox_chan_ops mpfs_mbox_ops = { 21862306a36Sopenharmony_ci .send_data = mpfs_mbox_send_data, 21962306a36Sopenharmony_ci .startup = mpfs_mbox_startup, 22062306a36Sopenharmony_ci .shutdown = mpfs_mbox_shutdown, 22162306a36Sopenharmony_ci .last_tx_done = mpfs_mbox_last_tx_done, 22262306a36Sopenharmony_ci}; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic int mpfs_mbox_probe(struct platform_device *pdev) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct mpfs_mbox *mbox; 22762306a36Sopenharmony_ci struct resource *regs; 22862306a36Sopenharmony_ci int ret; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox), GFP_KERNEL); 23162306a36Sopenharmony_ci if (!mbox) 23262306a36Sopenharmony_ci return -ENOMEM; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci mbox->ctrl_base = devm_platform_get_and_ioremap_resource(pdev, 0, ®s); 23562306a36Sopenharmony_ci if (IS_ERR(mbox->ctrl_base)) 23662306a36Sopenharmony_ci return PTR_ERR(mbox->ctrl_base); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci mbox->int_reg = devm_platform_get_and_ioremap_resource(pdev, 1, ®s); 23962306a36Sopenharmony_ci if (IS_ERR(mbox->int_reg)) 24062306a36Sopenharmony_ci return PTR_ERR(mbox->int_reg); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci mbox->mbox_base = devm_platform_get_and_ioremap_resource(pdev, 2, ®s); 24362306a36Sopenharmony_ci if (IS_ERR(mbox->mbox_base)) // account for the old dt-binding w/ 2 regs 24462306a36Sopenharmony_ci mbox->mbox_base = mbox->ctrl_base + MAILBOX_REG_OFFSET; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci mbox->irq = platform_get_irq(pdev, 0); 24762306a36Sopenharmony_ci if (mbox->irq < 0) 24862306a36Sopenharmony_ci return mbox->irq; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci mbox->dev = &pdev->dev; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci mbox->chans[0].con_priv = mbox; 25362306a36Sopenharmony_ci mbox->controller.dev = mbox->dev; 25462306a36Sopenharmony_ci mbox->controller.num_chans = 1; 25562306a36Sopenharmony_ci mbox->controller.chans = mbox->chans; 25662306a36Sopenharmony_ci mbox->controller.ops = &mpfs_mbox_ops; 25762306a36Sopenharmony_ci mbox->controller.txdone_poll = true; 25862306a36Sopenharmony_ci mbox->controller.txpoll_period = 10u; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci ret = devm_mbox_controller_register(&pdev->dev, &mbox->controller); 26162306a36Sopenharmony_ci if (ret) { 26262306a36Sopenharmony_ci dev_err(&pdev->dev, "Registering MPFS mailbox controller failed\n"); 26362306a36Sopenharmony_ci return ret; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci dev_info(&pdev->dev, "Registered MPFS mailbox controller driver\n"); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci return 0; 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic const struct of_device_id mpfs_mbox_of_match[] = { 27162306a36Sopenharmony_ci {.compatible = "microchip,mpfs-mailbox", }, 27262306a36Sopenharmony_ci {}, 27362306a36Sopenharmony_ci}; 27462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mpfs_mbox_of_match); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic struct platform_driver mpfs_mbox_driver = { 27762306a36Sopenharmony_ci .driver = { 27862306a36Sopenharmony_ci .name = "mpfs-mailbox", 27962306a36Sopenharmony_ci .of_match_table = mpfs_mbox_of_match, 28062306a36Sopenharmony_ci }, 28162306a36Sopenharmony_ci .probe = mpfs_mbox_probe, 28262306a36Sopenharmony_ci}; 28362306a36Sopenharmony_cimodule_platform_driver(mpfs_mbox_driver); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 28662306a36Sopenharmony_ciMODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>"); 28762306a36Sopenharmony_ciMODULE_DESCRIPTION("MPFS mailbox controller driver"); 288