From patchwork Wed Feb 28 02:27:14 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Samuel Holland X-Patchwork-Id: 10246541 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id C7D1760208 for ; Wed, 28 Feb 2018 02:29:40 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B5862289E9 for ; Wed, 28 Feb 2018 02:29:40 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A9F7B28AAF; Wed, 28 Feb 2018 02:29:40 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id CF126289E9 for ; Wed, 28 Feb 2018 02:29:38 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=BRd1ngpqNIcmmy2jgQLflGy+qiWnzpAU7YwQmQCzouo=; b=Xnc/67FrJ21Z6CVf7ZkXIn0C9X 44vlj4x0nnM7/7LAEwmeMFmRPptxhBFC4PGb0VZE88xn9LyCdmQFCDxgvcCYxeHWvWr1oMopKqJQf LrACpwC+XXoDnwOG4bGPF/7bWpA7jYQrwOvGKvJP8qQkIvTx0qiuRAaFdWCJcUdSzShSRk9KvfTAa OZmRrg6EoCZ795eG0YUH1J9z0c0VjUpbcAqGvt5SKFWDjZ8aeGPAMjtNjP5bmmgFjjTTMaH7veZKK Gom7kzvrEJv2o/KvZcsonSYt/DJRYuwa8I+8YVZhe0IBEzg9Kt4d0I5kNVrlA5SBb1fLL+MFJYSIP UVQEmi2g==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.89 #1 (Red Hat Linux)) id 1eqrUn-0002wm-2M; Wed, 28 Feb 2018 02:29:29 +0000 Received: from out2-smtp.messagingengine.com ([66.111.4.26]) by bombadil.infradead.org with esmtps (Exim 4.89 #1 (Red Hat Linux)) id 1eqrSt-0001yt-J6 for linux-arm-kernel@lists.infradead.org; Wed, 28 Feb 2018 02:27:35 +0000 Received: from compute5.internal (compute5.nyi.internal [10.202.2.45]) by mailout.nyi.internal (Postfix) with ESMTP id C0CB221335; Tue, 27 Feb 2018 21:27:19 -0500 (EST) Received: from frontend2 ([10.202.2.161]) by compute5.internal (MEProxy); Tue, 27 Feb 2018 21:27:19 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sholland.org; h= cc:date:from:in-reply-to:message-id:references:subject:to :x-me-sender:x-me-sender:x-sasl-enc; s=fm2; bh=h0hKS6L4SfjmBAuTA k3PLbDcKTkZRA/HJ+FZofpVNqM=; b=ndS1S5fE9QAVogi2wc7qThSzdhTpAKhcq PQ54TEBO/KFqlUuzYKwgSFDIGDBXUXiwfB93P/MxSh0U3GAKhFQ/ebmyXvCt/7Dy BTFJ9KuTzTx9Ci2zhXK8a+3EjcLPLwlUwMES3aE8W124MkIjwWtVd+aySa4nblzF lp/+17NEv0PNKMbsjetHwaO3YXyV0STG5hcxiyvgNAFNDO3nE3YRHbLUqiTtCR7J xRK53zxwIhaP0vBQf8YQH2/7lMnnPz73Yi/EMVBTRl13RJ2SMOBi4F1C1T1/Kr4D OdixHDF2NY7xkP9rCZvtgOVBP146XPhcWUCzs0+zZuvCndtuETHkQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:date:from:in-reply-to:message-id :references:subject:to:x-me-sender:x-me-sender:x-sasl-enc; s= fm2; bh=h0hKS6L4SfjmBAuTAk3PLbDcKTkZRA/HJ+FZofpVNqM=; b=bMfNM7Gu oMbqZ9W4eCgLexPoeyT0ywv7hOOVKkgLT7YdLkVv14vIgb1lgne2u4JLfl/mEb3n ZLVegmidNGsr18Z3Xnfzp+x82sfL4wydVkQr3bf36VBR/aKEiBDyVliqmJknb5Yv fL5pwk4pqnoTjbXk/z0OC0sLzihw60gBq3R/xBB3ubihZdPHmQdaaMCGkLJcwTx0 fQnkmzSetkWz1OqyOJsuSDZoGHoXBBBUPYF6AqI6wVVVt9lPPEqOEBH9FZJSDlx5 XRWzl1Ev/V4P6c99kztmOe/Pct3wcEqwYeMCtOdAbV3Cf48YnYQ9uM+vWxDyU4HZ DdYzIsNfsnvz7Q== X-ME-Sender: Received: from sodium.col.sholland.net (unknown [99.198.199.144]) by mail.messagingengine.com (Postfix) with ESMTPA id AC27F248C8; Tue, 27 Feb 2018 21:27:18 -0500 (EST) From: Samuel Holland To: Maxime Ripard , Chen-Yu Tsai , Jassi Brar , Rob Herring Subject: [PATCH 3/3] mailbox: sunxi-msgbox: Add a new mailbox driver Date: Tue, 27 Feb 2018 20:27:14 -0600 Message-Id: <20180228022714.30068-4-samuel@sholland.org> X-Mailer: git-send-email 2.13.6 In-Reply-To: <20180228022714.30068-1-samuel@sholland.org> References: <20180228022714.30068-1-samuel@sholland.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20180227_182731_930959_B74A945A X-CRM114-Status: GOOD ( 22.77 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: devicetree@vger.kernel.org, Samuel Holland , linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Andre Przywara MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP Recent Allwinner sunxi SoCs contain a "hardware message box" used for communication between the ARM CPUs and the ARISC management processor (the platform's equivalent of the ARM SCP). Add a driver for it, so it can be used for SCPI or other communication protocols. The hardware contains 8 unidirectional 4-message-deep FIFOs. To fit the mailbox framework API, this driver combines them into 4 bidirectional pairs of channels, and only allows one message in each channel to be queued at a time. Signed-off-by: Samuel Holland --- drivers/mailbox/Kconfig | 7 + drivers/mailbox/Makefile | 2 + drivers/mailbox/sunxi-msgbox.c | 323 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 332 insertions(+) create mode 100644 drivers/mailbox/sunxi-msgbox.c diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index ba2f1525f4ee..d6951b52498f 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -171,4 +171,11 @@ config BCM_FLEXRM_MBOX Mailbox implementation of the Broadcom FlexRM ring manager, which provides access to various offload engines on Broadcom SoCs. Say Y here if you want to use the Broadcom FlexRM. + +config SUNXI_MSGBOX + bool "Allwinner sunxi Message Box" + depends on ARCH_SUNXI + help + Mailbox driver for the hardware message box used on Allwinner sunxi + SoCs to communicate with the SCP. endif diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 4896f8dcae95..37a3e8f6a0eb 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -36,3 +36,5 @@ obj-$(CONFIG_BCM_FLEXRM_MBOX) += bcm-flexrm-mailbox.o obj-$(CONFIG_QCOM_APCS_IPC) += qcom-apcs-ipc-mailbox.o obj-$(CONFIG_TEGRA_HSP_MBOX) += tegra-hsp.o + +obj-$(CONFIG_SUNXI_MSGBOX) += sunxi-msgbox.o diff --git a/drivers/mailbox/sunxi-msgbox.c b/drivers/mailbox/sunxi-msgbox.c new file mode 100644 index 000000000000..16ff7aacfc55 --- /dev/null +++ b/drivers/mailbox/sunxi-msgbox.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2017 Samuel Holland + * + * This driver was based on drivers/mailbox/bcm2835-mailbox.c and + * drivers/mailbox/rockchip-mailbox.c. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The message box hardware provides 8 unidirectional channels. As the mailbox + * framework expects them to be bidirectional, create virtual channels out of + * pairs of opposite-direction hardware channels. The first channel in each pair + * is set up for AP->SCP communication, and the second channel is set up for + * SCP->AP transmission. + */ +#define NUM_CHANS 4 + +/* These macros take a virtual channel number. */ +#define CTRL_REG(n) (0x0000 + 0x4 * ((n) / 2)) +#define CTRL_MASK(n) (0x1111 << 16 * ((n) % 2)) +#define CTRL_SET(n) (0x0110 << 16 * ((n) % 2)) + +#define IRQ_EN_REG 0x0060 +#define IRQ_STATUS_REG 0x0070 +#define RX_IRQ(n) BIT(2 + 4 * (n)) +#define TX_IRQ(n) BIT(1 + 4 * (n)) + +#define REMOTE_IRQ_EN_REG 0x0040 +#define REMOTE_IRQ_STATUS_REG 0x0050 +#define REMOTE_RX_IRQ(n) BIT(0 + 4 * (n)) +#define REMOTE_TX_IRQ(n) BIT(3 + 4 * (n)) + +#define RX_FIFO_STATUS_REG(n) (0x0104 + 0x8 * (n)) +#define TX_FIFO_STATUS_REG(n) (0x0100 + 0x8 * (n)) +#define FIFO_STATUS_MASK BIT(0) + +#define RX_MSG_STATUS_REG(n) (0x0144 + 0x8 * (n)) +#define TX_MSG_STATUS_REG(n) (0x0140 + 0x8 * (n)) +#define MSG_STATUS_MASK GENMASK(2, 0) + +#define RX_MSG_DATA_REG(n) (0x0184 + 0x8 * (n)) +#define TX_MSG_DATA_REG(n) (0x0180 + 0x8 * (n)) + +struct sunxi_msgbox { + struct mbox_controller controller; + spinlock_t lock; + void __iomem *regs; +}; + +static bool sunxi_msgbox_last_tx_done(struct mbox_chan *chan); +static bool sunxi_msgbox_peek_data(struct mbox_chan *chan); + +static inline int channel_number(struct mbox_chan *chan) +{ + return chan - chan->mbox->chans; +} + +static inline struct sunxi_msgbox *channel_to_msgbox(struct mbox_chan *chan) +{ + return container_of(chan->mbox, struct sunxi_msgbox, controller); +} + +static irqreturn_t sunxi_msgbox_irq(int irq, void *dev_id) +{ + struct mbox_chan *chan; + struct sunxi_msgbox *mbox = dev_id; + int n; + uint32_t msg, reg; + + reg = readl(mbox->regs + IRQ_STATUS_REG); + for (n = 0; n < NUM_CHANS; ++n) { + if (!(reg & RX_IRQ(n))) + continue; + chan = &mbox->controller.chans[n]; + while (sunxi_msgbox_peek_data(chan)) { + msg = readl(mbox->regs + RX_MSG_DATA_REG(n)); + dev_dbg(mbox->controller.dev, + "Received 0x%08x on channel %d\n", msg, n); + mbox_chan_received_data(chan, &msg); + } + /* Clear the pending interrupt once the FIFO is empty. */ + writel(RX_IRQ(n), mbox->regs + IRQ_STATUS_REG); + } + + return IRQ_HANDLED; +} + +static int sunxi_msgbox_send_data(struct mbox_chan *chan, void *data) +{ + struct sunxi_msgbox *mbox = channel_to_msgbox(chan); + int n = channel_number(chan); + uint32_t msg = *(uint32_t *)data; + + if (!sunxi_msgbox_last_tx_done(chan)) { + dev_dbg(mbox->controller.dev, + "Busy sending 0x%08x on channel %d\n", msg, n); + return -EBUSY; + } + writel(msg, mbox->regs + TX_MSG_DATA_REG(n)); + dev_dbg(mbox->controller.dev, + "Sent 0x%08x on channel %d\n", msg, n); + + return 0; +} + +static int sunxi_msgbox_startup(struct mbox_chan *chan) +{ + struct sunxi_msgbox *mbox = channel_to_msgbox(chan); + int n = channel_number(chan); + uint32_t reg; + + /* Ensure FIFO directions are set properly. */ + spin_lock(&mbox->lock); + reg = readl(mbox->regs + CTRL_REG(n)); + writel((reg & ~CTRL_MASK(n)) | CTRL_SET(n), mbox->regs + CTRL_REG(n)); + spin_unlock(&mbox->lock); + + /* Clear existing messages in the receive FIFO. */ + while (sunxi_msgbox_peek_data(chan)) + readl(mbox->regs + RX_MSG_DATA_REG(n)); + + /* Clear and enable the receive interrupt. */ + spin_lock(&mbox->lock); + reg = readl(mbox->regs + IRQ_STATUS_REG); + writel(reg | RX_IRQ(n), mbox->regs + IRQ_STATUS_REG); + reg = readl(mbox->regs + IRQ_EN_REG); + writel(reg | RX_IRQ(n), mbox->regs + IRQ_EN_REG); + spin_unlock(&mbox->lock); + + dev_dbg(mbox->controller.dev, "Startup channel %d\n", n); + + return 0; +} + +static void sunxi_msgbox_shutdown(struct mbox_chan *chan) +{ + struct sunxi_msgbox *mbox = channel_to_msgbox(chan); + int n = channel_number(chan); + uint32_t reg; + + /* Disable the receive interrupt. */ + spin_lock(&mbox->lock); + reg = readl(mbox->regs + IRQ_EN_REG); + writel(reg & ~RX_IRQ(n), mbox->regs + IRQ_EN_REG); + spin_unlock(&mbox->lock); + + dev_dbg(mbox->controller.dev, "Shutdown channel %d\n", n); +} + +static bool sunxi_msgbox_last_tx_done(struct mbox_chan *chan) +{ + struct sunxi_msgbox *mbox = channel_to_msgbox(chan); + int n = channel_number(chan); + + /* + * The message box hardware allows us to snoop on the other user's IRQ + * statuses. Consider a message to be acknowledged when the reception + * IRQ for that channel is cleared. As the hardware only allows clearing + * the IRQ for a channel when the FIFO is empty, this still ensures that + * the message has actually been read. Compared to checking the number + * of messages in the FIFO, it also gives the receiver an opportunity to + * perform minimal message handling (such as recording extra information + * from a shared memory buffer) before acknowledging a message. + */ + return !(readl(mbox->regs + REMOTE_IRQ_STATUS_REG) & REMOTE_RX_IRQ(n)); +} + +static bool sunxi_msgbox_peek_data(struct mbox_chan *chan) +{ + struct sunxi_msgbox *mbox = channel_to_msgbox(chan); + int n = channel_number(chan); + + return (readl(mbox->regs + RX_MSG_STATUS_REG(n)) & MSG_STATUS_MASK) > 0; +} + +static const struct mbox_chan_ops sunxi_msgbox_chan_ops = { + .send_data = sunxi_msgbox_send_data, + .startup = sunxi_msgbox_startup, + .shutdown = sunxi_msgbox_shutdown, + .last_tx_done = sunxi_msgbox_last_tx_done, + .peek_data = sunxi_msgbox_peek_data, +}; + +static struct mbox_chan *sunxi_msgbox_index_xlate(struct mbox_controller *mbox, + const struct of_phandle_args *sp) +{ + if (sp->args_count != 2) + return NULL; + /* Enforce this driver's assumed physical-to-virtual channel mapping. */ + if ((sp->args[0] % 2) || (sp->args[1] != sp->args[0] + 1) || + (sp->args[0] > (NUM_CHANS - 1) * 2)) + return NULL; + + return &mbox->chans[sp->args[0] / 2]; +} + +static int sunxi_msgbox_probe(struct platform_device *pdev) +{ + struct clk *clk; + struct device *dev = &pdev->dev; + struct mbox_chan *chans; + struct reset_control *rst; + struct resource *res; + struct sunxi_msgbox *mbox; + int ret; + + mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); + if (!mbox) + return -ENOMEM; + + chans = devm_kcalloc(dev, NUM_CHANS, sizeof(*chans), GFP_KERNEL); + if (!chans) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + mbox->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mbox->regs)) + return PTR_ERR(mbox->regs); + + clk = devm_clk_get(dev, "bus"); + if (IS_ERR(clk)) { + dev_err(dev, "Failed to get bus clock\n"); + return PTR_ERR(clk); + } + + rst = devm_reset_control_get(dev, "bus"); + if (IS_ERR(rst)) { + dev_err(dev, "Failed to get bus reset\n"); + return PTR_ERR(rst); + } + + /* + * The failure path should not disable the clock or assert the reset, + * because the PSCI implementation in firmware relies on this device + * being functional. Claiming the clock in this driver is required to + * prevent Linux from turning it off. + */ + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(dev, "Failed to enable bus clock: %d\n", ret); + return ret; + } + + ret = reset_control_deassert(rst); + if (ret) { + dev_err(dev, "Failed to deassert bus reset: %d\n", ret); + return ret; + } + + /* Disable all interrupts. */ + writel(0, mbox->regs + IRQ_EN_REG); + + ret = devm_request_irq(dev, irq_of_parse_and_map(dev->of_node, 0), + sunxi_msgbox_irq, 0, dev_name(dev), mbox); + if (ret) { + dev_err(dev, "Failed to register IRQ handler: %d\n", ret); + return ret; + } + + mbox->controller.dev = dev; + mbox->controller.ops = &sunxi_msgbox_chan_ops; + mbox->controller.chans = chans; + mbox->controller.num_chans = NUM_CHANS; + mbox->controller.txdone_irq = false; + mbox->controller.txdone_poll = true; + mbox->controller.txpoll_period = 5; + mbox->controller.of_xlate = sunxi_msgbox_index_xlate; + + spin_lock_init(&mbox->lock); + platform_set_drvdata(pdev, mbox); + + ret = mbox_controller_register(&mbox->controller); + if (ret) + dev_err(dev, "Failed to register mailbox: %d\n", ret); + + return ret; +} + +static int sunxi_msgbox_remove(struct platform_device *pdev) +{ + struct sunxi_msgbox *mbox = platform_get_drvdata(pdev); + mbox_controller_unregister(&mbox->controller); + + return 0; +} + +static const struct of_device_id sunxi_msgbox_of_match[] = { + { .compatible = "allwinner,sunxi-msgbox", }, + {}, +}; +MODULE_DEVICE_TABLE(of, sunxi_msgbox_of_match); + +static struct platform_driver sunxi_msgbox_driver = { + .driver = { + .name = "sunxi-msgbox", + .of_match_table = sunxi_msgbox_of_match, + }, + .probe = sunxi_msgbox_probe, + .remove = sunxi_msgbox_remove, +}; +module_platform_driver(sunxi_msgbox_driver); + +MODULE_AUTHOR("Samuel Holland "); +MODULE_DESCRIPTION("Allwinner sunxi Message Box"); +MODULE_LICENSE("GPL v2");