From patchwork Wed Sep 17 19:59:23 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Bresticker X-Patchwork-Id: 390546 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id B9F5B14013F for ; Thu, 18 Sep 2014 06:04:37 +1000 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756450AbaIQUD4 (ORCPT ); Wed, 17 Sep 2014 16:03:56 -0400 Received: from mail-pa0-f74.google.com ([209.85.220.74]:45651 "EHLO mail-pa0-f74.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756784AbaIQT7f (ORCPT ); Wed, 17 Sep 2014 15:59:35 -0400 Received: by mail-pa0-f74.google.com with SMTP id lj1so623178pab.1 for ; Wed, 17 Sep 2014 12:59:35 -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=92DrfOgjVS6hLinIfzrDGxD/+wXiiGvR1fv89V3oJGM=; b=SlqZfC7hVz/MZlkNP23HNZoqINZBUZNNBPMWYYsXXJAaAV4BxFCJ/Tdc6ZTHbab6OX CX8/SytHRlfYc7BcdCO0xv57KEul5FGmhgi7g/KQNfLBeZL14Q7je4RHNIHoW7v3wTQ3 eu2EbFx497fh0+jG7aAy8sx5Otfzt3inEv6YZEZ0P7oHHG38qAxbh3k+JT/BIdqdGx2W U/dcOE0j9knKingbVfqooEFTWa/398j3zg9yjGFg7wBmDdlp4F+puZSq8ikIgTSKZefU hQ6CdrXBQkQeTyW8JG2n9RTekfAlaVmhvOQB01mhoNtdwlDIM3S7niIIiMVnQPQF0nXg p4Fw== X-Gm-Message-State: ALoCoQkDTIn5oldgorQIEYQDYPSnsM6V9ABTyBWzj87NczaznfN9ZluY3+/aVI2rhJ31IEDxIlKB X-Received: by 10.70.37.72 with SMTP id w8mr404407pdj.0.1410983974912; Wed, 17 Sep 2014 12:59:34 -0700 (PDT) Received: from corpmail-nozzle1-1.hot.corp.google.com ([100.108.1.104]) by gmr-mx.google.com with ESMTPS id n24si871662yha.6.2014.09.17.12.59.33 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 17 Sep 2014 12:59:34 -0700 (PDT) Received: from abrestic.mtv.corp.google.com ([172.22.65.70]) by corpmail-nozzle1-1.hot.corp.google.com with ESMTP id rfwTqjEz.1; Wed, 17 Sep 2014 12:59:34 -0700 Received: by abrestic.mtv.corp.google.com (Postfix, from userid 137652) id EFDD8220BD2; Wed, 17 Sep 2014 12:59:32 -0700 (PDT) From: Andrew Bresticker To: Stephen Warren , Thierry Reding , linux-tegra@vger.kernel.org Cc: Andrew Bresticker , Rob Herring , Pawel Moll , Mark Rutland , Ian Campbell , Kumar Gala , Russell King , Jassi Brar , Linus Walleij , Greg Kroah-Hartman , Mathias Nyman , Grant Likely , Alan Stern , Arnd Bergmann , Kishon Vijay Abraham I , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-usb@vger.kernel.org Subject: [PATCH v4 2/9] mailbox: Add NVIDIA Tegra XUSB mailbox driver Date: Wed, 17 Sep 2014 12:59:23 -0700 Message-Id: <1410983970-7043-3-git-send-email-abrestic@chromium.org> X-Mailer: git-send-email 2.1.0.rc2.206.gedb03e5 In-Reply-To: <1410983970-7043-1-git-send-email-abrestic@chromium.org> References: <1410983970-7043-1-git-send-email-abrestic@chromium.org> Sender: linux-tegra-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-tegra@vger.kernel.org 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 Reviewed-by: Stephen Warren --- No changes from v3. 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__ */