From patchwork Mon Sep 14 11:06:37 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Caesar Wang X-Patchwork-Id: 7174631 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 05D119F336 for ; Mon, 14 Sep 2015 11:10:35 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id B8C532064E for ; Mon, 14 Sep 2015 11:10:32 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 62EE72065F for ; Mon, 14 Sep 2015 11:10:31 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1ZbRcj-0007nw-9w; Mon, 14 Sep 2015 11:08:37 +0000 Received: from mail-pa0-f49.google.com ([209.85.220.49]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1ZbRcf-0007VA-2i; Mon, 14 Sep 2015 11:08:34 +0000 Received: by pacfv12 with SMTP id fv12so144465916pac.2; Mon, 14 Sep 2015 04:08:11 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=9SnhoZYeLcO9BSOgHI03Xy8CLUjDLpge+sshQE6AV7k=; b=UVRAnbP/9hUBUO3G8MI9cUAu1/M+a7hW7nnr2/lp07/p32Bdz2373cy5l2L4A6jCzW RfXjgV3PwfrJUqcrUoYAga8nryF3yQUp/nUbEJcbihpR8pojq0bXc2tZpxPQXc5CkGyL NG7P4qkUMAj3wKBeVmYn/oiWWi5ARZACDWuf8EXOrnMZg6AD4vQWxAMCRoigm8jdI5Bd aKELQyDmRJ9jB/ReIfT5Wu6U1gFx3yDPcqvvSDU2atrreUazOSWaMLzAsNMawh8mWlEa 9wfCCTERo7p8tHh8gNh1yP90+C3ZvkV0294QCIc2IqJsSMEAN8yULYwpGT7E4mP9ggeN CMrQ== X-Received: by 10.68.192.225 with SMTP id hj1mr33647638pbc.67.1442228891624; Mon, 14 Sep 2015 04:08:11 -0700 (PDT) Received: from localhost.localdomain ([103.47.144.143]) by smtp.gmail.com with ESMTPSA id yk5sm15631928pab.31.2015.09.14.04.07.52 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 14 Sep 2015 04:08:10 -0700 (PDT) From: Caesar Wang To: heiko@sntech.de, jassisinghbrar@gmail.com Subject: [PATCH 2/3] mailbox: rockchip: Add Rockchip mailbox driver Date: Mon, 14 Sep 2015 19:06:37 +0800 Message-Id: <1442228798-15191-3-git-send-email-wxt@rock-chips.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1442228798-15191-1-git-send-email-wxt@rock-chips.com> References: <1442228798-15191-1-git-send-email-wxt@rock-chips.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20150914_040833_182141_BFC737ED X-CRM114-Status: GOOD ( 25.50 ) X-Spam-Score: -2.4 (--) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: mark.rutland@arm.com, devicetree@vger.kernel.org, pawel.moll@arm.com, ijc+devicetree@hellion.org.uk, frank.wang@rock-chips.com, catalin.marinas@arm.com, will.deacon@arm.com, linux-kernel@vger.kernel.org, linux-rockchip@lists.infradead.org, robh+dt@kernel.org, galak@codeaurora.org, olof@lixom.net, linux-arm-kernel@lists.infradead.org, wxt@rock-chips.com MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This driver is found on RK3368 SoCs. The Mailbox module is a simple APB peripheral that allows both the Cortex-A53 MCU system to communicate by writing operation to generate interrupt. The registers are accessible by both CPU via APB interface. The Mailbox has the following main features: @ Support dual-core system: Cortex-A53 and MCU. @ Support APB interface. @ Support four mailbox elements, each element includes one data word, one command word register and one flag bit that can represent one interrupt. @ Four interrupts to Cortex-A53. @ Four interrupts to MCU. @ Provide 32 lock registers for software to use to indicate whether mailbox is occupied. Signed-off-by: Caesar Wang --- drivers/mailbox/Kconfig | 9 ++ drivers/mailbox/Makefile | 2 + drivers/mailbox/rockchip-mailbox.c | 317 +++++++++++++++++++++++++++++++++++++ 3 files changed, 328 insertions(+) create mode 100644 drivers/mailbox/rockchip-mailbox.c diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index bbec500..a548d700 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -43,6 +43,15 @@ config OMAP_MBOX_KFIFO_SIZE This can also be changed at runtime (via the mbox_kfifo_size module parameter). +config ROCKCHIP_MBOX + bool "Rockchip Soc Intergrated Mailbox Support" + depends on ARCH_ROCKCHIP + help + This driver provides support for inter-processor communication + between CPU cores and MCU processor on Some Rockchip SOCs. + Please check it that the Soc you use have Mailbox hardware. + Say Y here if you want to use the Rockchip Mailbox support. + config PCC bool "Platform Communication Channel Driver" depends on ACPI diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 8e6d822..730cb5d 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -8,6 +8,8 @@ obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o obj-$(CONFIG_OMAP2PLUS_MBOX) += omap-mailbox.o +obj-$(CONFIG_ROCKCHIP_MBOX) += rockchip-mailbox.o + obj-$(CONFIG_PCC) += pcc.o obj-$(CONFIG_ALTERA_MBOX) += mailbox-altera.o diff --git a/drivers/mailbox/rockchip-mailbox.c b/drivers/mailbox/rockchip-mailbox.c new file mode 100644 index 0000000..715ab96 --- /dev/null +++ b/drivers/mailbox/rockchip-mailbox.c @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2015, Fuzhou Rockchip Electronics Co., Ltd + * + * Authors: Addy Ke + * Caesar Wang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAILBOX_A2B_INTEN 0x00 +#define MAILBOX_A2B_STATUS 0x04 +#define MAILBOX_A2B_CMD(x) (0x08 + (x) * 8) +#define MAILBOX_A2B_DAT(x) (0x0c + (x) * 8) + +#define MAILBOX_B2A_INTEN 0x28 +#define MAILBOX_B2A_STATUS 0x2C +#define MAILBOX_B2A_CMD(x) (0x30 + (x) * 8) +#define MAILBOX_B2A_DAT(x) (0x34 + (x) * 8) + +#define MAILBOX_ATOMIC_LOCK(x) (0x100 + (x) * 8) + +/* A2B: 0 - 2k */ +#define A2B_BUF(size, idx) ((idx) * (size)) + +/* B2A: 2k - 4k */ +#define B2A_BUF(size, idx) (((idx) + 4) * (size)) + +struct rockchip_mbox_msg { + u32 cmd; + int tx_size; + void *tx_buf; + int rx_size; + void *rx_buf; + void *cl_data; +}; + +struct rockchip_mbox_data { + int num_chans; +}; + +struct rockchip_mbox_chan { + int idx; + int irq; + struct rockchip_mbox_msg *msg; + struct rockchip_mbox *mb; +}; + +struct rockchip_mbox { + struct mbox_controller mbox; + struct clk *pclk; + void __iomem *mbox_base; + + /* The base address of share memory to transfer data */ + void __iomem *buf_base; + + /* The maximum size of buf for each channel */ + u32 buf_size; + + struct rockchip_mbox_chan *chans; +}; + +static inline int chan_to_idx(struct rockchip_mbox *mb, + struct mbox_chan *chan) +{ + return (chan - mb->mbox.chans); +} + +static int rockchip_mbox_send_data(struct mbox_chan *chan, void *data) +{ + struct rockchip_mbox *mb = dev_get_drvdata(chan->mbox->dev); + struct rockchip_mbox_msg *msg = data; + int idx = chan_to_idx(mb, chan); + + if (!msg) + return -EINVAL; + + if ((msg->tx_size > mb->buf_size) || + (msg->rx_size > mb->buf_size)) { + dev_err(mb->mbox.dev, "Transmit size over buf size(%d)\n", + mb->buf_size); + return -EINVAL; + } + + dev_dbg(mb->mbox.dev, "Chan[%d]: A2B message, cmd 0x%08x\n", + idx, msg->cmd); + + mb->chans[idx].msg = msg; + + if (msg->tx_buf) + memcpy(mb->buf_base + A2B_BUF(mb->buf_size, idx), + msg->tx_buf, msg->tx_size); + + writel_relaxed(msg->cmd, mb->mbox_base + MAILBOX_A2B_CMD(idx)); + writel_relaxed(msg->rx_size, mb->mbox_base + MAILBOX_A2B_DAT(idx)); + + return 0; +} + +static int rockchip_mbox_startup(struct mbox_chan *chan) +{ + return 0; +} + +static void rockchip_mbox_shutdown(struct mbox_chan *chan) +{ + struct rockchip_mbox *mb = dev_get_drvdata(chan->mbox->dev); + int idx = chan_to_idx(mb, chan); + + mb->chans[idx].msg = NULL; +} + +static const struct mbox_chan_ops rockchip_mbox_chan_ops = { + .send_data = rockchip_mbox_send_data, + .startup = rockchip_mbox_startup, + .shutdown = rockchip_mbox_shutdown, +}; + +static irqreturn_t rockchip_mbox_irq(int irq, void *dev_id) +{ + int idx; + struct rockchip_mbox *mb = (struct rockchip_mbox *)dev_id; + u32 status = readl_relaxed(mb->mbox_base + MAILBOX_B2A_STATUS); + + for (idx = 0; idx < mb->mbox.num_chans; idx++) { + if ((status & (1 << idx)) && (irq == mb->chans[idx].irq)) { + /* Clear mbox interrupt */ + writel_relaxed(1 << idx, + mb->mbox_base + MAILBOX_B2A_STATUS); + return IRQ_WAKE_THREAD; + } + } + + return IRQ_NONE; +} + +static irqreturn_t rockchip_mbox_isr(int irq, void *dev_id) +{ + int idx; + struct rockchip_mbox_msg *msg = NULL; + struct rockchip_mbox *mb = (struct rockchip_mbox *)dev_id; + + for (idx = 0; idx < mb->mbox.num_chans; idx++) { + if (irq != mb->chans[idx].irq) + continue; + + msg = mb->chans[idx].msg; + if (!msg) { + dev_err(mb->mbox.dev, + "Chan[%d]: B2A message is NULL\n", idx); + break; /* spurious */ + } + + if (msg->rx_buf) + memcpy(msg->rx_buf, + mb->buf_base + B2A_BUF(mb->buf_size, idx), + msg->rx_size); + + mbox_chan_received_data(&mb->mbox.chans[idx], msg); + mb->chans[idx].msg = NULL; + + dev_dbg(mb->mbox.dev, "Chan[%d]: B2A message, cmd 0x%08x\n", + idx, msg->cmd); + + break; + } + + return IRQ_HANDLED; +} + +static const struct rockchip_mbox_data rk3368_drv_data = { + .num_chans = 4, +}; + +static const struct of_device_id rockchip_mbox_of_match[] = { + { .compatible = "rockchip,rk3368-mailbox", .data = &rk3368_drv_data}, + { }, +}; +MODULE_DEVICE_TABLE(of, rockchp_mbox_of_match); + +static int rockchip_mbox_probe(struct platform_device *pdev) +{ + struct rockchip_mbox *mb; + const struct of_device_id *match; + const struct rockchip_mbox_data *drv_data; + struct resource *res; + int ret, irq, i; + + if (!pdev->dev.of_node) + return -ENODEV; + + match = of_match_node(rockchip_mbox_of_match, pdev->dev.of_node); + drv_data = (const struct rockchip_mbox_data *)match->data; + + mb = devm_kzalloc(&pdev->dev, sizeof(*mb), GFP_KERNEL); + if (!mb) + return -ENOMEM; + + mb->chans = devm_kcalloc(&pdev->dev, drv_data->num_chans, + sizeof(*mb->chans), GFP_KERNEL); + if (!mb->chans) + return -ENOMEM; + + mb->mbox.chans = devm_kcalloc(&pdev->dev, drv_data->num_chans, + sizeof(*mb->mbox.chans), GFP_KERNEL); + if (!mb->mbox.chans) + return -ENOMEM; + + platform_set_drvdata(pdev, mb); + + mb->mbox.dev = &pdev->dev; + mb->mbox.num_chans = drv_data->num_chans; + mb->mbox.ops = &rockchip_mbox_chan_ops; + mb->mbox.txdone_irq = true; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + mb->mbox_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mb->mbox_base)) + return PTR_ERR(mb->mbox_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) + return -ENODEV; + + /* Each channel has two buffers for A2B and B2A */ + mb->buf_size = resource_size(res) / (drv_data->num_chans * 2); + mb->buf_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mb->buf_base)) + return PTR_ERR(mb->buf_base); + + mb->pclk = devm_clk_get(&pdev->dev, "pclk_mailbox"); + if (IS_ERR(mb->pclk)) { + ret = PTR_ERR(mb->pclk); + dev_err(&pdev->dev, "failed to get pclk_mailbox clock: %d\n", + ret); + return ret; + } + + ret = clk_prepare_enable(mb->pclk); + if (ret) { + dev_err(&pdev->dev, "failed to enable pclk: %d\n", ret); + return ret; + } + + for (i = 0; i < mb->mbox.num_chans; i++) { + irq = platform_get_irq(pdev, i); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(&pdev->dev, irq, + rockchip_mbox_irq, + rockchip_mbox_isr, IRQF_ONESHOT, + dev_name(&pdev->dev), mb); + if (ret < 0) + return ret; + + mb->chans[i].idx = i; + mb->chans[i].mb = mb; + mb->chans[i].msg = NULL; + mb->chans[i].irq = irq; + } + + /* Enable all B2A interrupts */ + writel_relaxed((1 << mb->mbox.num_chans) - 1, + mb->mbox_base + MAILBOX_B2A_INTEN); + + ret = mbox_controller_register(&mb->mbox); + if (ret < 0) + dev_err(&pdev->dev, "Failed to register mailbox: %d\n", ret); + + return ret; +} + +static int rockchip_mbox_remove(struct platform_device *pdev) +{ + struct rockchip_mbox *mb = platform_get_drvdata(pdev); + + if (!mb) + return -EINVAL; + + mbox_controller_unregister(&mb->mbox); + + return 0; +} + +static struct platform_driver rockchip_mbox_driver = { + .probe = rockchip_mbox_probe, + .remove = rockchip_mbox_remove, + .driver = { + .name = "rockchip-mailbox", + .of_match_table = of_match_ptr(rockchip_mbox_of_match), + }, +}; + +static int __init rockchip_mbox_init(void) +{ + return platform_driver_register(&rockchip_mbox_driver); +} +subsys_initcall(rockchip_mbox_init);