From patchwork Tue Sep 2 21:34:54 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Bresticker X-Patchwork-Id: 4828571 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 360BEC0338 for ; Tue, 2 Sep 2014 21:38:27 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id D92B0201C0 for ; Tue, 2 Sep 2014 21:38:25 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 90D202017D for ; Tue, 2 Sep 2014 21:38:24 +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 1XOvjx-0005xS-Ca; Tue, 02 Sep 2014 21:35:49 +0000 Received: from mail-ie0-f202.google.com ([209.85.223.202]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1XOvjZ-0005md-Ek for linux-arm-kernel@lists.infradead.org; Tue, 02 Sep 2014 21:35:31 +0000 Received: by mail-ie0-f202.google.com with SMTP id rl12so1304640iec.1 for ; Tue, 02 Sep 2014 14:35:05 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=hbrE3AQfVviMchxU7cWxBptvPLQV22LcfWNx9LR45Lo=; b=cOJAwVNz1zSzRBJYPMG0Z7yo0s/rc51s80V3whykLUsm38xW629vYXFwulS+OXqcB3 PcYEUD8+ePt9wOi/ysh4AKy/EGpVjRAUhM5cgmCogtrLHTzhRC5k8JNK9lKBkVDm03gT AxPwc86tGydB+u4vwSyEXHZFD07kbKr18Qq99tOaA3bYZLxFZzki/mCYco+nFSZ+mhyM hCKvvisQNSMPUynKgXdN0BI87rJw6aMv6m9PJMV9sYa0ohJUXwdTKrGO1UI3ZK47wkoJ qg29bxKEeH9j1KHfRBPk49gD+JGi7ojnroeYNx3x76ggfBRGqnkQiNWwLV+GcBTdr4BF 4Fcw== X-Gm-Message-State: ALoCoQkSkxnuACPttXw6SK57Ls5bO+Im2VaGY3p5SY9T4OFrgKNKN2fTtQWiQy3RO/IdUadvopLq X-Received: by 10.182.56.200 with SMTP id c8mr21511991obq.7.1409693705623; Tue, 02 Sep 2014 14:35:05 -0700 (PDT) Received: from corp2gmr1-1.hot.corp.google.com (corp2gmr1-1.hot.corp.google.com [172.24.189.92]) by gmr-mx.google.com with ESMTPS id m14si715020yhm.7.2014.09.02.14.35.05 for (version=TLSv1.1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 02 Sep 2014 14:35:05 -0700 (PDT) Received: from abrestic.mtv.corp.google.com (abrestic.mtv.corp.google.com [172.22.65.70]) by corp2gmr1-1.hot.corp.google.com (Postfix) with ESMTP id 466A031C202; Tue, 2 Sep 2014 14:35:05 -0700 (PDT) Received: by abrestic.mtv.corp.google.com (Postfix, from userid 137652) id 098C3220570; Tue, 2 Sep 2014 14:35:04 -0700 (PDT) From: Andrew Bresticker To: Stephen Warren , Thierry Reding , linux-tegra@vger.kernel.org Subject: [PATCH v3 2/9] mailbox: Add NVIDIA Tegra XUSB mailbox driver Date: Tue, 2 Sep 2014 14:34:54 -0700 Message-Id: <1409693701-16520-3-git-send-email-abrestic@chromium.org> X-Mailer: git-send-email 2.1.0.rc2.206.gedb03e5 In-Reply-To: <1409693701-16520-1-git-send-email-abrestic@chromium.org> References: <1409693701-16520-1-git-send-email-abrestic@chromium.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140902_143525_584306_6A7F385A X-CRM114-Status: GOOD ( 24.91 ) X-Spam-Score: -3.1 (---) Cc: Mark Rutland , devicetree@vger.kernel.org, linux-usb@vger.kernel.org, Russell King , Mathias Nyman , Pawel Moll , Ian Campbell , Andrew Bresticker , Greg Kroah-Hartman , Linus Walleij , Jassi Brar , linux-kernel@vger.kernel.org, Kishon Vijay Abraham I , Rob Herring , Alan Stern , linux-arm-kernel@lists.infradead.org, Kumar Gala , Grant Likely , Arnd Bergmann X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , 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=-3.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE, 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 The Tegra xHCI controller's firmware communicates requests to the host processor through a mailbox interface. While there is only a single physical channel, messages sent by the controller can be divided into two groups: those intended for the PHY driver and those intended for the host-controller driver. The requesting driver is assigned one of two virtual channels when the single physical channel is requested. All incoming messages are sent to both virtual channels. Signed-off-by: Andrew Bresticker --- Jassi: I've handled the sharing by making the channels in the Tegra driver 'virtual' channels. Having the mailbox core handle channel sharing would be a much more invasive change, but let me know if that's what you'd prefer. Changes from v2: - Fixed mailbox IRQ vs. channel alloc/free race. - Renamed defines to match TRM. - Dropped channel specifier and instead allocated virtual channels as they were requested. - Removed MODULE_ALIAS. Changes from v1: - Converted to common mailbox framework. - Removed useless polling sequences in TX path. - Moved xusb include from linux/ to soc/tegra/ --- drivers/mailbox/Kconfig | 3 + drivers/mailbox/Makefile | 2 + drivers/mailbox/tegra-xusb-mailbox.c | 290 +++++++++++++++++++++++++++++++++++ include/soc/tegra/xusb.h | 46 ++++++ 4 files changed, 341 insertions(+) create mode 100644 drivers/mailbox/tegra-xusb-mailbox.c create mode 100644 include/soc/tegra/xusb.h diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 9fd9c67..97369c2 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -33,4 +33,7 @@ config OMAP_MBOX_KFIFO_SIZE Specify the default size of mailbox's kfifo buffers (bytes). This can also be changed at runtime (via the mbox_kfifo_size module parameter). + +config TEGRA_XUSB_MBOX + def_bool y if ARCH_TEGRA endif diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 94ed7ce..7f0af9c 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -5,3 +5,5 @@ obj-$(CONFIG_MAILBOX) += mailbox.o obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o obj-$(CONFIG_OMAP2PLUS_MBOX) += omap-mailbox.o + +obj-$(CONFIG_TEGRA_XUSB_MBOX) += tegra-xusb-mailbox.o diff --git a/drivers/mailbox/tegra-xusb-mailbox.c b/drivers/mailbox/tegra-xusb-mailbox.c new file mode 100644 index 0000000..2d87b8a --- /dev/null +++ b/drivers/mailbox/tegra-xusb-mailbox.c @@ -0,0 +1,290 @@ +/* + * NVIDIA Tegra XUSB mailbox driver + * + * Copyright (C) 2014 NVIDIA Corporation + * Copyright (C) 2014 Google, Inc. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define XUSB_CFG_ARU_MBOX_CMD 0xe4 +#define MBOX_DEST_FALC BIT(27) +#define MBOX_DEST_PME BIT(28) +#define MBOX_DEST_SMI BIT(29) +#define MBOX_DEST_XHCI BIT(30) +#define MBOX_INT_EN BIT(31) +#define XUSB_CFG_ARU_MBOX_DATA_IN 0xe8 +#define CMD_DATA_SHIFT 0 +#define CMD_DATA_MASK 0xffffff +#define CMD_TYPE_SHIFT 24 +#define CMD_TYPE_MASK 0xff +#define XUSB_CFG_ARU_MBOX_DATA_OUT 0xec +#define XUSB_CFG_ARU_MBOX_OWNER 0xf0 +#define MBOX_OWNER_NONE 0 +#define MBOX_OWNER_FW 1 +#define MBOX_OWNER_SW 2 +#define XUSB_CFG_ARU_SMI_INTR 0x428 +#define MBOX_SMI_INTR_FW_HANG BIT(1) +#define MBOX_SMI_INTR_EN BIT(3) + +struct tegra_xusb_mbox { + struct mbox_controller mbox; + int irq; + void __iomem *regs; + spinlock_t lock; + bool vchan_allocated[TEGRA_XUSB_MBOX_NUM_CHANS]; +}; + +static inline u32 mbox_readl(struct tegra_xusb_mbox *mbox, unsigned long offset) +{ + return readl(mbox->regs + offset); +} + +static inline void mbox_writel(struct tegra_xusb_mbox *mbox, u32 val, + unsigned long offset) +{ + writel(val, mbox->regs + offset); +} + +static inline u32 mbox_pack_msg(struct tegra_xusb_mbox_msg *msg) +{ + u32 val; + + val = (msg->cmd & CMD_TYPE_MASK) << CMD_TYPE_SHIFT; + val |= (msg->data & CMD_DATA_MASK) << CMD_DATA_SHIFT; + + return val; +} + +static inline void mbox_unpack_msg(u32 val, struct tegra_xusb_mbox_msg *msg) +{ + msg->cmd = (val >> CMD_TYPE_SHIFT) & CMD_TYPE_MASK; + msg->data = (val >> CMD_DATA_SHIFT) & CMD_DATA_MASK; +} + +static int tegra_xusb_mbox_send_data(struct mbox_chan *chan, void *data) +{ + struct tegra_xusb_mbox *mbox = dev_get_drvdata(chan->mbox->dev); + struct tegra_xusb_mbox_msg *msg = data; + unsigned long flags; + u32 reg, owner; + + dev_dbg(mbox->mbox.dev, "TX message 0x%x:0x%x\n", msg->cmd, msg->data); + + /* ACK/NAK must be sent with the controller as the mailbox owner */ + if (msg->cmd == MBOX_CMD_ACK || msg->cmd == MBOX_CMD_NAK) + owner = MBOX_OWNER_FW; + else + owner = MBOX_OWNER_SW; + + spin_lock_irqsave(&mbox->lock, flags); + + /* Acquire mailbox */ + if (mbox_readl(mbox, XUSB_CFG_ARU_MBOX_OWNER) != MBOX_OWNER_NONE) { + dev_err(mbox->mbox.dev, "Mailbox not idle\n"); + goto busy; + } + mbox_writel(mbox, owner, XUSB_CFG_ARU_MBOX_OWNER); + if (mbox_readl(mbox, XUSB_CFG_ARU_MBOX_OWNER) != owner) { + dev_err(mbox->mbox.dev, "Failed to acquire mailbox"); + goto busy; + } + + mbox_writel(mbox, mbox_pack_msg(msg), XUSB_CFG_ARU_MBOX_DATA_IN); + reg = mbox_readl(mbox, XUSB_CFG_ARU_MBOX_CMD); + reg |= MBOX_INT_EN | MBOX_DEST_FALC; + mbox_writel(mbox, reg, XUSB_CFG_ARU_MBOX_CMD); + + spin_unlock_irqrestore(&mbox->lock, flags); + + return 0; +busy: + spin_unlock_irqrestore(&mbox->lock, flags); + return -EBUSY; +} + +static int tegra_xusb_mbox_startup(struct mbox_chan *chan) +{ + struct tegra_xusb_mbox *mbox = dev_get_drvdata(chan->mbox->dev); + int idx = chan - mbox->mbox.chans; + unsigned long flags; + + spin_lock_irqsave(&mbox->lock, flags); + mbox->vchan_allocated[idx] = true; + spin_unlock_irqrestore(&mbox->lock, flags); + + return 0; +} + +static void tegra_xusb_mbox_shutdown(struct mbox_chan *chan) +{ + struct tegra_xusb_mbox *mbox = dev_get_drvdata(chan->mbox->dev); + int idx = chan - mbox->mbox.chans; + unsigned long flags; + + spin_lock_irqsave(&mbox->lock, flags); + mbox->vchan_allocated[idx] = false; + spin_unlock_irqrestore(&mbox->lock, flags); +} + +static bool tegra_xusb_mbox_last_tx_done(struct mbox_chan *chan) +{ + /* + * Transmissions are assumed to be completed as soon as they are + * written to the mailbox. + */ + return true; +} + +static struct mbox_chan_ops tegra_xusb_mbox_chan_ops = { + .send_data = tegra_xusb_mbox_send_data, + .startup = tegra_xusb_mbox_startup, + .shutdown = tegra_xusb_mbox_shutdown, + .last_tx_done = tegra_xusb_mbox_last_tx_done, +}; + +static irqreturn_t tegra_xusb_mbox_irq(int irq, void *p) +{ + struct tegra_xusb_mbox *mbox = (struct tegra_xusb_mbox *)p; + struct tegra_xusb_mbox_msg msg; + int i; + u32 reg; + + spin_lock(&mbox->lock); + + /* Clear mbox interrupts */ + reg = mbox_readl(mbox, XUSB_CFG_ARU_SMI_INTR); + if (reg & MBOX_SMI_INTR_FW_HANG) + dev_err(mbox->mbox.dev, "Controller firmware hang\n"); + mbox_writel(mbox, reg, XUSB_CFG_ARU_SMI_INTR); + + reg = mbox_readl(mbox, XUSB_CFG_ARU_MBOX_DATA_OUT); + mbox_unpack_msg(reg, &msg); + + /* + * Set the mailbox back to idle. The recipient of the message is + * responsible for sending an ACK/NAK, if necessary. + */ + reg = mbox_readl(mbox, XUSB_CFG_ARU_MBOX_CMD); + reg &= ~MBOX_DEST_SMI; + mbox_writel(mbox, reg, XUSB_CFG_ARU_MBOX_CMD); + mbox_writel(mbox, MBOX_OWNER_NONE, XUSB_CFG_ARU_MBOX_OWNER); + + dev_dbg(mbox->mbox.dev, "RX message 0x%x:0x%x\n", msg.cmd, msg.data); + for (i = 0; i < ARRAY_SIZE(mbox->vchan_allocated); i++) { + if (mbox->vchan_allocated[i]) + mbox_chan_received_data(&mbox->mbox.chans[i], &msg); + } + + spin_unlock(&mbox->lock); + + return IRQ_HANDLED; +} + +static struct mbox_chan *tegra_xusb_mbox_of_xlate(struct mbox_controller *ctlr, + const struct of_phandle_args *sp) +{ + struct tegra_xusb_mbox *mbox = dev_get_drvdata(ctlr->dev); + struct mbox_chan *chan = NULL; + unsigned long flags; + int i; + + /* Pick the first available (virtual) channel. */ + spin_lock_irqsave(&mbox->lock, flags); + for (i = 0; i < ARRAY_SIZE(mbox->vchan_allocated); i++) { + if (!mbox->vchan_allocated[i]) { + chan = &ctlr->chans[i]; + break; + } + } + spin_unlock_irqrestore(&mbox->lock, flags); + + return chan; +} + +static struct of_device_id tegra_xusb_mbox_of_match[] = { + { .compatible = "nvidia,tegra124-xusb-mbox" }, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra_xusb_mbox_of_match); + +static int tegra_xusb_mbox_probe(struct platform_device *pdev) +{ + struct tegra_xusb_mbox *mbox; + struct resource *res; + int ret; + + mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox), GFP_KERNEL); + if (!mbox) + return -ENOMEM; + platform_set_drvdata(pdev, mbox); + spin_lock_init(&mbox->lock); + + mbox->mbox.dev = &pdev->dev; + mbox->mbox.chans = devm_kcalloc(&pdev->dev, TEGRA_XUSB_MBOX_NUM_CHANS, + sizeof(*mbox->mbox.chans), GFP_KERNEL); + if (!mbox->mbox.chans) + return -ENOMEM; + mbox->mbox.num_chans = TEGRA_XUSB_MBOX_NUM_CHANS; + mbox->mbox.ops = &tegra_xusb_mbox_chan_ops; + mbox->mbox.txdone_poll = true; + mbox->mbox.txpoll_period = 0; /* no need to actually poll */ + mbox->mbox.of_xlate = tegra_xusb_mbox_of_xlate; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + mbox->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!mbox->regs) + return -ENOMEM; + + mbox->irq = platform_get_irq(pdev, 0); + if (mbox->irq < 0) + return mbox->irq; + ret = devm_request_irq(&pdev->dev, mbox->irq, tegra_xusb_mbox_irq, 0, + dev_name(&pdev->dev), mbox); + if (ret < 0) + return ret; + + ret = mbox_controller_register(&mbox->mbox); + if (ret < 0) + dev_err(&pdev->dev, "failed to register mailbox: %d\n", ret); + + return ret; +} + +static int tegra_xusb_mbox_remove(struct platform_device *pdev) +{ + struct tegra_xusb_mbox *mbox = platform_get_drvdata(pdev); + + mbox_controller_unregister(&mbox->mbox); + + return 0; +} + +static struct platform_driver tegra_xusb_mbox_driver = { + .probe = tegra_xusb_mbox_probe, + .remove = tegra_xusb_mbox_remove, + .driver = { + .name = "tegra-xusb-mbox", + .of_match_table = of_match_ptr(tegra_xusb_mbox_of_match), + }, +}; +module_platform_driver(tegra_xusb_mbox_driver); + +MODULE_AUTHOR("Andrew Bresticker "); +MODULE_DESCRIPTION("NVIDIA Tegra XUSB mailbox driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/soc/tegra/xusb.h b/include/soc/tegra/xusb.h new file mode 100644 index 0000000..cfe211d --- /dev/null +++ b/include/soc/tegra/xusb.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014 NVIDIA Corporation + * Copyright (C) 2014 Google, Inc. + * + * 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. + */ + +#ifndef __SOC_TEGRA_XUSB_H__ +#define __SOC_TEGRA_XUSB_H__ + +/* Two virtual channels: host + phy */ +#define TEGRA_XUSB_MBOX_NUM_CHANS 2 + +/* Command requests from the firmware */ +enum tegra_xusb_mbox_cmd { + MBOX_CMD_MSG_ENABLED = 1, + MBOX_CMD_INC_FALC_CLOCK, + MBOX_CMD_DEC_FALC_CLOCK, + MBOX_CMD_INC_SSPI_CLOCK, + MBOX_CMD_DEC_SSPI_CLOCK, + MBOX_CMD_SET_BW, /* no ACK/NAK required */ + MBOX_CMD_SET_SS_PWR_GATING, + MBOX_CMD_SET_SS_PWR_UNGATING, + MBOX_CMD_SAVE_DFE_CTLE_CTX, + MBOX_CMD_AIRPLANE_MODE_ENABLED, /* unused */ + MBOX_CMD_AIRPLANE_MODE_DISABLED, /* unused */ + MBOX_CMD_START_HSIC_IDLE, + MBOX_CMD_STOP_HSIC_IDLE, + MBOX_CMD_DBC_WAKE_STACK, /* unused */ + MBOX_CMD_HSIC_PRETEND_CONNECT, + + MBOX_CMD_MAX, + + /* Response message to above commands */ + MBOX_CMD_ACK = 128, + MBOX_CMD_NAK +}; + +struct tegra_xusb_mbox_msg { + u32 cmd; + u32 data; +}; + +#endif /* __SOC_TEGRA_XUSB_H__ */