From patchwork Mon Apr 27 22:37:20 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Bresticker X-Patchwork-Id: 6283971 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 ED57C9F326 for ; Mon, 27 Apr 2015 22:44:57 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id A6705202F8 for ; Mon, 27 Apr 2015 22:44:56 +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 5920A202F0 for ; Mon, 27 Apr 2015 22:44:55 +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 1YmriY-0008Pk-SF; Mon, 27 Apr 2015 22:41:34 +0000 Received: from mail-yk0-f202.google.com ([209.85.160.202]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1Ymrfa-0005VC-S7 for linux-arm-kernel@lists.infradead.org; Mon, 27 Apr 2015 22:38:32 +0000 Received: by ykp9 with SMTP id 9so3885481ykp.0 for ; Mon, 27 Apr 2015 15:38:08 -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=NEK2qjFqETWnlCHV+TICIRch1h1TzdM9QNEZOrpqX6I=; b=LOqDit4UzI6qEqc172yL50FEUkuDsL/InBPSydAO/4NJjaQqb9w+LTZosInfEETXqo JMr136ear9qomZMT/mee135b78ziI1sjkyGF9rmWMMkb7HiQx9/1Q6COcVcPj9wZcR3x Zbo81eaIG/OuGJjoAO6d2fjjtOIMqm3RJOP7SSrariiZygvThh9O4OgHCMAeVo0woFsG fDUUGBs0pCls4hYOnYjcjYYPWwdFqrrWifPEbQqAjtD+qluLGV7uS+DxztplxfZ4ibRF SVy+KCGZjoxrrjrSOYrBHUmVDnVpF1WlaxJAyd+BE5Nx17kmuSiD2YKpxR0MDUBrg1AY O9hw== X-Gm-Message-State: ALoCoQm+Zh296vb6TPYV+BFZyb7fOrylSjLnh+yNVakgWt0O1mlfrCRvMuDylaDzE0xz0Z/YVeN7 X-Received: by 10.236.34.136 with SMTP id s8mr24242453yha.36.1430174287922; Mon, 27 Apr 2015 15:38:07 -0700 (PDT) Received: from corpmail-nozzle1-1.hot.corp.google.com ([100.108.1.104]) by gmr-mx.google.com with ESMTPS id r25si1171688yho.3.2015.04.27.15.38.07 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 27 Apr 2015 15:38:07 -0700 (PDT) Received: from abrestic.mtv.corp.google.com ([172.22.65.70]) by corpmail-nozzle1-1.hot.corp.google.com with ESMTP id PV9vDoiK.1; Mon, 27 Apr 2015 15:38:07 -0700 Received: by abrestic.mtv.corp.google.com (Postfix, from userid 137652) id CD19DA49BE; Mon, 27 Apr 2015 15:38:06 -0700 (PDT) From: Andrew Bresticker To: Stephen Warren , Thierry Reding , Alexandre Courbot Subject: [PATCH V7 7/9] mailbox: Add NVIDIA Tegra XUSB mailbox driver Date: Mon, 27 Apr 2015 15:37:20 -0700 Message-Id: <1430174242-29465-8-git-send-email-abrestic@chromium.org> X-Mailer: git-send-email 2.2.0.rc0.207.ga3a616c In-Reply-To: <1430174242-29465-1-git-send-email-abrestic@chromium.org> References: <1430174242-29465-1-git-send-email-abrestic@chromium.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20150427_153831_101602_C67BEDF1 X-CRM114-Status: GOOD ( 23.77 ) X-Spam-Score: -0.7 (/) Cc: devicetree@vger.kernel.org, Jassi Brar , Andrew Bresticker , linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, linux-tegra@vger.kernel.org, linux-arm-kernel@lists.infradead.org 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=-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 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 Cc: Jassi Brar --- Changes from v6: - Access FPCI registers using parent MFD's regmap. Changes from v5: - Poll for TX completion using MBOX_OWNER field. Changes from v4: - Use chan->cl to indicate channel allocation status - Addressed review comments from Thierry 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 | 8 + drivers/mailbox/Makefile | 2 + drivers/mailbox/tegra-xusb-mailbox.c | 275 +++++++++++++++++++++++++++++++++++ include/soc/tegra/xusb.h | 30 ++++ 4 files changed, 315 insertions(+) create mode 100644 drivers/mailbox/tegra-xusb-mailbox.c diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 84b0a2d..37da641 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -60,4 +60,12 @@ config ALTERA_MBOX An implementation of the Altera Mailbox soft core. It is used to send message between processors. Say Y here if you want to use the Altera mailbox support. + +config TEGRA_XUSB_MBOX + tristate "NVIDIA Tegra XUSB Mailbox" + depends on MFD_TEGRA_XUSB + help + Mailbox driver for the XUSB complex found on NVIDIA Tegra124 and + later SoCs. The XUSB mailbox is used to communicate between the + XUSB microcontroller and the host processor. endif diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index b18201e..d77012a 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -11,3 +11,5 @@ obj-$(CONFIG_OMAP2PLUS_MBOX) += omap-mailbox.o obj-$(CONFIG_PCC) += pcc.o obj-$(CONFIG_ALTERA_MBOX) += mailbox-altera.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..f0cac4d --- /dev/null +++ b/drivers/mailbox/tegra-xusb-mailbox.c @@ -0,0 +1,275 @@ +/* + * 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_MBOX_NUM_CHANS 2 /* Host + PHY */ + +#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; + struct tegra_xusb *xusb; + spinlock_t lock; +}; + +static inline u32 mbox_readl(struct tegra_xusb_mbox *mbox, unsigned long offset) +{ + u32 val; + + regmap_read(mbox->xusb->fpci_regs, offset, &val); + + return val; +} + +static inline void mbox_writel(struct tegra_xusb_mbox *mbox, u32 val, + unsigned long offset) +{ + regmap_write(mbox->xusb->fpci_regs, offset, val); +} + +static inline struct tegra_xusb_mbox *to_tegra_mbox(struct mbox_controller *c) +{ + return container_of(c, struct tegra_xusb_mbox, mbox); +} + +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 = to_tegra_mbox(chan->mbox); + struct tegra_xusb_mbox_msg *msg = data; + unsigned long flags; + u32 reg, owner; + + dev_dbg(mbox->mbox.dev, "TX message %#x:%#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) +{ + return 0; +} + +static void tegra_xusb_mbox_shutdown(struct mbox_chan *chan) +{ +} + +static bool tegra_xusb_mbox_last_tx_done(struct mbox_chan *chan) +{ + struct tegra_xusb_mbox *mbox = to_tegra_mbox(chan->mbox); + + return mbox_readl(mbox, XUSB_CFG_ARU_MBOX_OWNER) == MBOX_OWNER_NONE; +} + +static const 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 = p; + struct tegra_xusb_mbox_msg msg; + unsigned 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 %#x:%#x\n", msg.cmd, msg.data); + for (i = 0; i < XUSB_MBOX_NUM_CHANS; i++) { + if (mbox->mbox.chans[i].cl) + 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 = to_tegra_mbox(ctlr); + struct mbox_chan *chan = ERR_PTR(-EINVAL); + unsigned long flags; + unsigned int i; + + /* Pick the first available (virtual) channel. */ + spin_lock_irqsave(&mbox->lock, flags); + for (i = 0; XUSB_MBOX_NUM_CHANS; i++) { + if (!ctlr->chans[i].cl) { + chan = &ctlr->chans[i]; + break; + } + } + spin_unlock_irqrestore(&mbox->lock, flags); + + return chan; +} + +static const 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; + int irq, 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->xusb = dev_get_drvdata(pdev->dev.parent); + + mbox->mbox.dev = &pdev->dev; + mbox->mbox.chans = devm_kcalloc(&pdev->dev, XUSB_MBOX_NUM_CHANS, + sizeof(*mbox->mbox.chans), GFP_KERNEL); + if (!mbox->mbox.chans) + return -ENOMEM; + mbox->mbox.num_chans = XUSB_MBOX_NUM_CHANS; + mbox->mbox.ops = &tegra_xusb_mbox_chan_ops; + mbox->mbox.txdone_poll = true; + mbox->mbox.txpoll_period = 1; + mbox->mbox.of_xlate = tegra_xusb_mbox_of_xlate; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + ret = devm_request_irq(&pdev->dev, 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 = 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 index 9d28d90..d815bc1 100644 --- a/include/soc/tegra/xusb.h +++ b/include/soc/tegra/xusb.h @@ -16,4 +16,34 @@ struct tegra_xusb { struct regmap *fpci_regs; }; +/* 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__ */