diff options
author | Michael Ellerman <mpe@ellerman.id.au> | 2015-06-05 18:03:57 +1000 |
---|---|---|
committer | Michael Ellerman <mpe@ellerman.id.au> | 2015-06-05 18:03:57 +1000 |
commit | 571d3820fb5bf246883846076a41f721e890c53a (patch) | |
tree | 50fae91c0601edda7f94bf6b61ce9d44e09372e4 | |
parent | 26052da7cfba68ee2972f39e002a4b40e242102b (diff) | |
parent | 4c0f74bf82e8295ce711da81774178769f6f8215 (diff) | |
download | linux-next-571d3820fb5bf246883846076a41f721e890c53a.tar.gz |
Merge remote-tracking branch 'mailbox/mailbox-for-next'
-rw-r--r-- | Documentation/devicetree/bindings/mailbox/brcm,bcm2835-mbox.txt | 26 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/mailbox/nvidia,tegra124-xusb-mbox.txt | 30 | ||||
-rw-r--r-- | drivers/mailbox/Kconfig | 17 | ||||
-rw-r--r-- | drivers/mailbox/Makefile | 4 | ||||
-rw-r--r-- | drivers/mailbox/arm_mhu.c | 2 | ||||
-rw-r--r-- | drivers/mailbox/bcm2835-mailbox.c | 216 | ||||
-rw-r--r-- | drivers/mailbox/mailbox-altera.c | 2 | ||||
-rw-r--r-- | drivers/mailbox/mailbox.c | 11 | ||||
-rw-r--r-- | drivers/mailbox/omap-mailbox.c | 8 | ||||
-rw-r--r-- | drivers/mailbox/pcc.c | 2 | ||||
-rw-r--r-- | drivers/mailbox/tegra-xusb-mailbox.c | 290 | ||||
-rw-r--r-- | include/linux/mailbox_controller.h | 2 | ||||
-rw-r--r-- | include/soc/tegra/xusb.h | 43 |
13 files changed, 642 insertions, 11 deletions
diff --git a/Documentation/devicetree/bindings/mailbox/brcm,bcm2835-mbox.txt b/Documentation/devicetree/bindings/mailbox/brcm,bcm2835-mbox.txt new file mode 100644 index 00000000000000..e893615ef63570 --- /dev/null +++ b/Documentation/devicetree/bindings/mailbox/brcm,bcm2835-mbox.txt @@ -0,0 +1,26 @@ +Broadcom BCM2835 VideoCore mailbox IPC + +Required properties: + +- compatible: Should be "brcm,bcm2835-mbox" +- reg: Specifies base physical address and size of the registers +- interrupts: The interrupt number + See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt +- #mbox-cells: Specifies the number of cells needed to encode a mailbox + channel. The value shall be 0, since there is only one + mailbox channel implemented by the device. + +Example: + +mailbox: mailbox@7e00b800 { + compatible = "brcm,bcm2835-mbox"; + reg = <0x7e00b880 0x40>; + interrupts = <0 1>; + #mbox-cells = <0>; +}; + +firmware: firmware { + compatible = "raspberrypi,firmware"; + mboxes = <&mailbox>; + #power-domain-cells = <1>; +}; diff --git a/Documentation/devicetree/bindings/mailbox/nvidia,tegra124-xusb-mbox.txt b/Documentation/devicetree/bindings/mailbox/nvidia,tegra124-xusb-mbox.txt new file mode 100644 index 00000000000000..9d89afadc07066 --- /dev/null +++ b/Documentation/devicetree/bindings/mailbox/nvidia,tegra124-xusb-mbox.txt @@ -0,0 +1,30 @@ +NVIDIA Tegra XUSB mailbox +========================= + +The Tegra XUSB mailbox is used by the Tegra xHCI controller's firmware to +communicate requests to the host and PHY drivers. + +Refer to ./mailbox.txt for generic information about mailbox device-tree +bindings. + +Required properties: +-------------------- + - compatible: For Tegra124, must contain "nvidia,tegra124-xusb-mbox". + Otherwise, must contain '"nvidia,<chip>-xusb-mbox", + "nvidia,tegra124-xusb-mbox"' where <chip> is tegra132. + - #mbox-cells: Should be 0. There is only one physical channel. + +Example: +-------- + mailbox { + compatible = "nvidia,tegra124-xusb-mbox"; + + #mbox-cells = <0>; + }; + + usb-host { + ... + mboxes = <&xusb_mbox>; + mbox-names = "xusb"; + ... + }; diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 84b0a2d74d60b7..76c11942ea2378 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -56,8 +56,25 @@ config PCC config ALTERA_MBOX tristate "Altera Mailbox" + depends on HAS_IOMEM help 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 BCM2835_MBOX + tristate "BCM2835 Mailbox" + depends on ARCH_BCM2835 + help + An implementation of the BCM2385 Mailbox. It is used to invoke + the services of the Videocore. Say Y here if you want to use the + BCM2835 Mailbox. + +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 b18201e97e2988..1dc9f9acafdbda 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -11,3 +11,7 @@ obj-$(CONFIG_OMAP2PLUS_MBOX) += omap-mailbox.o obj-$(CONFIG_PCC) += pcc.o obj-$(CONFIG_ALTERA_MBOX) += mailbox-altera.o + +obj-$(CONFIG_BCM2835_MBOX) += bcm2835-mailbox.o + +obj-$(CONFIG_TEGRA_XUSB_MBOX) += tegra-xusb-mailbox.o diff --git a/drivers/mailbox/arm_mhu.c b/drivers/mailbox/arm_mhu.c index ac693c63535769..d9e99f981aa98e 100644 --- a/drivers/mailbox/arm_mhu.c +++ b/drivers/mailbox/arm_mhu.c @@ -110,7 +110,7 @@ static void mhu_shutdown(struct mbox_chan *chan) free_irq(mlink->irq, chan); } -static struct mbox_chan_ops mhu_ops = { +static const struct mbox_chan_ops mhu_ops = { .send_data = mhu_send_data, .startup = mhu_startup, .shutdown = mhu_shutdown, diff --git a/drivers/mailbox/bcm2835-mailbox.c b/drivers/mailbox/bcm2835-mailbox.c new file mode 100644 index 00000000000000..4b13268529f94e --- /dev/null +++ b/drivers/mailbox/bcm2835-mailbox.c @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2010,2015 Broadcom + * Copyright (C) 2013-2014 Lubomir Rintel + * Copyright (C) 2013 Craig McGeachie + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This device provides a mechanism for writing to the mailboxes, + * that are shared between the ARM and the VideoCore processor + * + * Parts of the driver are based on: + * - arch/arm/mach-bcm2708/vcio.c file written by Gray Girling that was + * obtained from branch "rpi-3.6.y" of git://github.com/raspberrypi/ + * linux.git + * - drivers/mailbox/bcm2835-ipc.c by Lubomir Rintel at + * https://github.com/hackerspace/rpi-linux/blob/lr-raspberry-pi/drivers/ + * mailbox/bcm2835-ipc.c + * - documentation available on the following web site: + * https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface + */ + +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/mailbox_controller.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> + +/* Mailboxes */ +#define ARM_0_MAIL0 0x00 +#define ARM_0_MAIL1 0x20 + +/* + * Mailbox registers. We basically only support mailbox 0 & 1. We + * deliver to the VC in mailbox 1, it delivers to us in mailbox 0. See + * BCM2835-ARM-Peripherals.pdf section 1.3 for an explanation about + * the placement of memory barriers. + */ +#define MAIL0_RD (ARM_0_MAIL0 + 0x00) +#define MAIL0_POL (ARM_0_MAIL0 + 0x10) +#define MAIL0_STA (ARM_0_MAIL0 + 0x18) +#define MAIL0_CNF (ARM_0_MAIL0 + 0x1C) +#define MAIL1_WRT (ARM_0_MAIL1 + 0x00) + +/* Status register: FIFO state. */ +#define ARM_MS_FULL BIT(31) +#define ARM_MS_EMPTY BIT(30) + +/* Configuration register: Enable interrupts. */ +#define ARM_MC_IHAVEDATAIRQEN BIT(0) + +struct bcm2835_mbox { + void __iomem *regs; + spinlock_t lock; + struct mbox_controller controller; +}; + +static struct bcm2835_mbox *bcm2835_link_mbox(struct mbox_chan *link) +{ + return container_of(link->mbox, struct bcm2835_mbox, controller); +} + +static irqreturn_t bcm2835_mbox_irq(int irq, void *dev_id) +{ + struct bcm2835_mbox *mbox = dev_id; + struct device *dev = mbox->controller.dev; + struct mbox_chan *link = &mbox->controller.chans[0]; + + while (!(readl(mbox->regs + MAIL0_STA) & ARM_MS_EMPTY)) { + u32 msg = readl(mbox->regs + MAIL0_RD); + dev_dbg(dev, "Reply 0x%08X\n", msg); + mbox_chan_received_data(link, &msg); + } + return IRQ_HANDLED; +} + +static int bcm2835_send_data(struct mbox_chan *link, void *data) +{ + struct bcm2835_mbox *mbox = bcm2835_link_mbox(link); + u32 msg = *(u32 *)data; + + spin_lock(&mbox->lock); + writel(msg, mbox->regs + MAIL1_WRT); + dev_dbg(mbox->controller.dev, "Request 0x%08X\n", msg); + spin_unlock(&mbox->lock); + return 0; +} + +static int bcm2835_startup(struct mbox_chan *link) +{ + struct bcm2835_mbox *mbox = bcm2835_link_mbox(link); + + /* Enable the interrupt on data reception */ + writel(ARM_MC_IHAVEDATAIRQEN, mbox->regs + MAIL0_CNF); + + return 0; +} + +static void bcm2835_shutdown(struct mbox_chan *link) +{ + struct bcm2835_mbox *mbox = bcm2835_link_mbox(link); + + writel(0, mbox->regs + MAIL0_CNF); +} + +static bool bcm2835_last_tx_done(struct mbox_chan *link) +{ + struct bcm2835_mbox *mbox = bcm2835_link_mbox(link); + bool ret; + + spin_lock(&mbox->lock); + ret = !(readl(mbox->regs + MAIL0_STA) & ARM_MS_FULL); + spin_unlock(&mbox->lock); + return ret; +} + +static const struct mbox_chan_ops bcm2835_mbox_chan_ops = { + .send_data = bcm2835_send_data, + .startup = bcm2835_startup, + .shutdown = bcm2835_shutdown, + .last_tx_done = bcm2835_last_tx_done +}; + +static struct mbox_chan *bcm2835_mbox_index_xlate(struct mbox_controller *mbox, + const struct of_phandle_args *sp) +{ + if (sp->args_count != 0) + return NULL; + + return &mbox->chans[0]; +} + +static int bcm2835_mbox_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int ret = 0; + struct resource *iomem; + struct bcm2835_mbox *mbox; + + mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); + if (mbox == NULL) + return -ENOMEM; + spin_lock_init(&mbox->lock); + + ret = devm_request_irq(dev, irq_of_parse_and_map(dev->of_node, 0), + bcm2835_mbox_irq, 0, dev_name(dev), mbox); + if (ret) { + dev_err(dev, "Failed to register a mailbox IRQ handler: %d\n", + ret); + return -ENODEV; + } + + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mbox->regs = devm_ioremap_resource(&pdev->dev, iomem); + if (IS_ERR(mbox->regs)) { + ret = PTR_ERR(mbox->regs); + dev_err(&pdev->dev, "Failed to remap mailbox regs: %d\n", ret); + return ret; + } + + mbox->controller.txdone_poll = true; + mbox->controller.txpoll_period = 5; + mbox->controller.ops = &bcm2835_mbox_chan_ops; + mbox->controller.of_xlate = &bcm2835_mbox_index_xlate; + mbox->controller.dev = dev; + mbox->controller.num_chans = 1; + mbox->controller.chans = devm_kzalloc(dev, + sizeof(*mbox->controller.chans), GFP_KERNEL); + if (!mbox->controller.chans) + return -ENOMEM; + + ret = mbox_controller_register(&mbox->controller); + if (ret) + return ret; + + platform_set_drvdata(pdev, mbox); + dev_info(dev, "mailbox enabled\n"); + + return ret; +} + +static int bcm2835_mbox_remove(struct platform_device *pdev) +{ + struct bcm2835_mbox *mbox = platform_get_drvdata(pdev); + mbox_controller_unregister(&mbox->controller); + return 0; +} + +static const struct of_device_id bcm2835_mbox_of_match[] = { + { .compatible = "brcm,bcm2835-mbox", }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm2835_mbox_of_match); + +static struct platform_driver bcm2835_mbox_driver = { + .driver = { + .name = "bcm2835-mbox", + .owner = THIS_MODULE, + .of_match_table = bcm2835_mbox_of_match, + }, + .probe = bcm2835_mbox_probe, + .remove = bcm2835_mbox_remove, +}; +module_platform_driver(bcm2835_mbox_driver); + +MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>"); +MODULE_DESCRIPTION("BCM2835 mailbox IPC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mailbox/mailbox-altera.c b/drivers/mailbox/mailbox-altera.c index a266265677d3eb..bb682c926b0a04 100644 --- a/drivers/mailbox/mailbox-altera.c +++ b/drivers/mailbox/mailbox-altera.c @@ -285,7 +285,7 @@ static void altera_mbox_shutdown(struct mbox_chan *chan) } } -static struct mbox_chan_ops altera_mbox_ops = { +static const struct mbox_chan_ops altera_mbox_ops = { .send_data = altera_mbox_send_data, .startup = altera_mbox_startup, .shutdown = altera_mbox_shutdown, diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c index 19b491d2964f31..c3c42d42d017c6 100644 --- a/drivers/mailbox/mailbox.c +++ b/drivers/mailbox/mailbox.c @@ -318,7 +318,7 @@ struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index) return ERR_PTR(-ENODEV); } - chan = NULL; + chan = ERR_PTR(-EPROBE_DEFER); list_for_each_entry(mbox, &mbox_cons, node) if (mbox->dev->of_node == spec.np) { chan = mbox->of_xlate(mbox, &spec); @@ -327,7 +327,12 @@ struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index) of_node_put(spec.np); - if (!chan || chan->cl || !try_module_get(mbox->dev->driver->owner)) { + if (IS_ERR(chan)) { + mutex_unlock(&con_mutex); + return chan; + } + + if (chan->cl || !try_module_get(mbox->dev->driver->owner)) { dev_dbg(dev, "%s: mailbox not free\n", __func__); mutex_unlock(&con_mutex); return ERR_PTR(-EBUSY); @@ -390,7 +395,7 @@ of_mbox_index_xlate(struct mbox_controller *mbox, int ind = sp->args[0]; if (ind >= mbox->num_chans) - return NULL; + return ERR_PTR(-EINVAL); return &mbox->chans[ind]; } diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c index 0f332c178b07a0..a3dbfd9c647994 100644 --- a/drivers/mailbox/omap-mailbox.c +++ b/drivers/mailbox/omap-mailbox.c @@ -604,7 +604,7 @@ static int omap_mbox_chan_send_data(struct mbox_chan *chan, void *data) return ret; } -static struct mbox_chan_ops omap_mbox_chan_ops = { +static const struct mbox_chan_ops omap_mbox_chan_ops = { .startup = omap_mbox_chan_startup, .send_data = omap_mbox_chan_send_data, .shutdown = omap_mbox_chan_shutdown, @@ -639,18 +639,18 @@ static struct mbox_chan *omap_mbox_of_xlate(struct mbox_controller *controller, mdev = container_of(controller, struct omap_mbox_device, controller); if (WARN_ON(!mdev)) - return NULL; + return ERR_PTR(-EINVAL); node = of_find_node_by_phandle(phandle); if (!node) { pr_err("%s: could not find node phandle 0x%x\n", __func__, phandle); - return NULL; + return ERR_PTR(-ENODEV); } mbox = omap_mbox_device_find(mdev, node->name); of_node_put(node); - return mbox ? mbox->chan : NULL; + return mbox ? mbox->chan : ERR_PTR(-ENOENT); } static int omap_mbox_probe(struct platform_device *pdev) diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c index 7e91d68a3ac3d0..26d121d1d501b7 100644 --- a/drivers/mailbox/pcc.c +++ b/drivers/mailbox/pcc.c @@ -198,7 +198,7 @@ static int pcc_send_data(struct mbox_chan *chan, void *data) return 0; } -static struct mbox_chan_ops pcc_chan_ops = { +static const struct mbox_chan_ops pcc_chan_ops = { .send_data = pcc_send_data, }; diff --git a/drivers/mailbox/tegra-xusb-mailbox.c b/drivers/mailbox/tegra-xusb-mailbox.c new file mode 100644 index 00000000000000..4e2477dd78819d --- /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 <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mailbox_controller.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#include <soc/tegra/xusb.h> + +#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 regmap *fpci_regs; + spinlock_t lock; + int irq; +}; + +static inline u32 mbox_readl(struct tegra_xusb_mbox *mbox, unsigned long offset) +{ + u32 val; + + regmap_read(mbox->fpci_regs, offset, &val); + + return val; +} + +static inline void mbox_writel(struct tegra_xusb_mbox *mbox, u32 val, + unsigned long offset) +{ + regmap_write(mbox->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 bool mbox_cmd_requires_ack(enum tegra_xusb_mbox_cmd cmd) +{ + switch (cmd) { + case MBOX_CMD_SET_BW: + case MBOX_CMD_ACK: + case MBOX_CMD_NAK: + return false; + default: + return true; + } +} + +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; + + dev_dbg(mbox->mbox.dev, "TX message %#x:%#x\n", msg->cmd, msg->data); + + spin_lock_irqsave(&mbox->lock, flags); + /* + * Acquire the mailbox. The firmware still owns the mailbox for + * ACK/NAK messages. + */ + if (!(msg->cmd == MBOX_CMD_ACK || msg->cmd == MBOX_CMD_NAK)) { + 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, MBOX_OWNER_SW, XUSB_CFG_ARU_MBOX_OWNER); + if (mbox_readl(mbox, XUSB_CFG_ARU_MBOX_OWNER) != + MBOX_OWNER_SW) { + 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); + + reg = mbox_readl(mbox, XUSB_CFG_ARU_MBOX_CMD); + reg &= ~MBOX_DEST_SMI; + mbox_writel(mbox, reg, XUSB_CFG_ARU_MBOX_CMD); + + /* Clear mailbox owner if no ACK/NAK is required. */ + if (!mbox_cmd_requires_ack(msg.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 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->fpci_regs = 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; + + 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); + + synchronize_irq(mbox->irq); + devm_free_irq(&pdev->dev, mbox->irq, mbox); + 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 <abrestic@chromium.org>"); +MODULE_DESCRIPTION("NVIDIA Tegra XUSB mailbox driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mailbox_controller.h b/include/linux/mailbox_controller.h index d4cf96f07cfc42..68c42454439be3 100644 --- a/include/linux/mailbox_controller.h +++ b/include/linux/mailbox_controller.h @@ -72,7 +72,7 @@ struct mbox_chan_ops { */ struct mbox_controller { struct device *dev; - struct mbox_chan_ops *ops; + const struct mbox_chan_ops *ops; struct mbox_chan *chans; int num_chans; bool txdone_irq; diff --git a/include/soc/tegra/xusb.h b/include/soc/tegra/xusb.h new file mode 100644 index 00000000000000..5ce5e12e99fc32 --- /dev/null +++ b/include/soc/tegra/xusb.h @@ -0,0 +1,43 @@ +/* + * 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__ + +/* 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__ */ |