From 03521794966c0123e45b84da5faa7382ad53cc16 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 6 Dec 2019 14:28:49 +0100 Subject: usb: host: ehci-sh: Remove unused platform data support ehci_sh_platdata was never used, remove it. It can be resurrected from git history when needed. This basically reverts commit 3e0c70d050c7ed6d ("usb: ehci-sh: Add PHY init function with platform data"). Signed-off-by: Geert Uytterhoeven Acked-by: Alan Stern Acked-by: Nobuhiro Iwamatsu Link: https://lore.kernel.org/r/20191206132849.29406-1-geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-sh.c | 7 ------- include/linux/platform_data/ehci-sh.h | 16 ---------------- 2 files changed, 23 deletions(-) delete mode 100644 include/linux/platform_data/ehci-sh.h diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index 2afde14dc425..c25c51d26f26 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -8,7 +8,6 @@ */ #include #include -#include struct ehci_sh_priv { struct clk *iclk, *fclk; @@ -76,7 +75,6 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev) { struct resource *res; struct ehci_sh_priv *priv; - struct ehci_sh_platdata *pdata; struct usb_hcd *hcd; int irq, ret; @@ -89,8 +87,6 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev) goto fail_create_hcd; } - pdata = dev_get_platdata(&pdev->dev); - /* initialize hcd */ hcd = usb_create_hcd(&ehci_sh_hc_driver, &pdev->dev, dev_name(&pdev->dev)); @@ -127,9 +123,6 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev) clk_enable(priv->fclk); clk_enable(priv->iclk); - if (pdata && pdata->phy_init) - pdata->phy_init(); - ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret != 0) { dev_err(&pdev->dev, "Failed to add hcd"); diff --git a/include/linux/platform_data/ehci-sh.h b/include/linux/platform_data/ehci-sh.h deleted file mode 100644 index 219bd79dabfc..000000000000 --- a/include/linux/platform_data/ehci-sh.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 - * - * EHCI SuperH driver platform data - * - * Copyright (C) 2012 Nobuhiro Iwamatsu - * Copyright (C) 2012 Renesas Solutions Corp. - */ - -#ifndef __USB_EHCI_SH_H -#define __USB_EHCI_SH_H - -struct ehci_sh_platdata { - void (*phy_init)(void); /* Phy init function */ -}; - -#endif /* __USB_EHCI_SH_H */ -- cgit v1.2.3-59-g8ed1b From 145e6dd8a5c9db70f28326dc89df241823d681ce Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 22 Nov 2019 08:49:04 -0800 Subject: usb: drop comment about 2 uhci drivers Drop the ancient comment about 2 usb/uhci drivers since there are no longer 2 of them. Cc: Johan Hovold Cc: linux-usb@vger.kernel.org Signed-off-by: Randy Dunlap Link: https://lore.kernel.org/r/5f93d906-4f5c-2a78-c1c7-36cf07e94bcc@infradead.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/Kconfig | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index ed4a18b435a0..25d7e0c36d38 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -129,9 +129,6 @@ config USB_SERIAL_DIGI_ACCELEPORT parallel port on the USB 2 appears as a third serial port on Linux. The Digi Acceleport USB 8 is not yet supported by this driver. - This driver works under SMP with the usb-uhci driver. It does not - work under SMP with the uhci driver. - To compile this driver as a module, choose M here: the module will be called digi_acceleport. -- cgit v1.2.3-59-g8ed1b From dbb7a6b48d07b5268ac9dfd6cef8d1bcbc35b303 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Thu, 28 Nov 2019 13:43:58 +0000 Subject: dt-bindings: connector: Improve the english of the initial description The description lacks a few indefinite articles when reading and as a result is a bit clunky to read. Introduce a few indefinite articles to appease the grammar gods. Cc: Andrzej Hajda Cc: Rob Herring Cc: Chanwoo Choi Cc: linux-usb@vger.kernel.org Cc: devicetree@vger.kernel.org Signed-off-by: Bryan O'Donoghue Link: https://lore.kernel.org/r/20191128134358.3880498-3-bryan.odonoghue@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/connector/usb-connector.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/connector/usb-connector.txt b/Documentation/devicetree/bindings/connector/usb-connector.txt index d357987181ee..88578ac1a8a7 100644 --- a/Documentation/devicetree/bindings/connector/usb-connector.txt +++ b/Documentation/devicetree/bindings/connector/usb-connector.txt @@ -1,8 +1,8 @@ USB Connector ============= -USB connector node represents physical USB connector. It should be -a child of USB interface controller. +A USB connector node represents a physical USB connector. It should be +a child of a USB interface controller. Required properties: - compatible: describes type of the connector, must be one of: -- cgit v1.2.3-59-g8ed1b From c763771504d158abefcdb965df632d09f7602e9f Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 6 Dec 2019 15:06:44 +0100 Subject: usb: host: xhci-tegra: Fix "tega" -> "tegra" typo The tegra_xusb_mbox_regs structure was misspelled tega_xusb_mbox_regs. Fortunately this was done consistently so it didn't cause any issues. Reviewed-by: JC Kuo Signed-off-by: Thierry Reding Link: https://lore.kernel.org/r/20191206140653.2085561-2-thierry.reding@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index bf9065438320..aa1c4e5fd750 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -145,7 +145,7 @@ struct tegra_xusb_phy_type { unsigned int num; }; -struct tega_xusb_mbox_regs { +struct tegra_xusb_mbox_regs { u16 cmd; u16 data_in; u16 data_out; @@ -166,7 +166,7 @@ struct tegra_xusb_soc { } usb2, ulpi, hsic, usb3; } ports; - struct tega_xusb_mbox_regs mbox; + struct tegra_xusb_mbox_regs mbox; bool scale_ss_clock; bool has_ipfs; -- cgit v1.2.3-59-g8ed1b From 741d6e5d84f30266694ca23641f1d028b55f7f40 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 6 Dec 2019 15:06:45 +0100 Subject: usb: host: xhci-tegra: Separate firmware request and load Subsequent patches for system suspend/resume support will need to reload the firmware on resume. Since the firmware remains in system memory, the driver doesn't need to reload it from the filesystem. However, the XUSB controller will be reset across suspend/resume, so it needs to load the firmware into its microcontroller on resume. Split the firmware request and the firmware load code into two separate functions so that the driver can reuse the firmware in system memory to reload the microcontroller on resume. Based on work by JC Kuo . Signed-off-by: Thierry Reding Link: https://lore.kernel.org/r/20191206140653.2085561-3-thierry.reding@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index aa1c4e5fd750..5cfd54862670 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -793,17 +793,10 @@ disable_clk: return err; } -static int tegra_xusb_load_firmware(struct tegra_xusb *tegra) +static int tegra_xusb_request_firmware(struct tegra_xusb *tegra) { - unsigned int code_tag_blocks, code_size_blocks, code_blocks; struct tegra_xusb_fw_header *header; - struct device *dev = tegra->dev; const struct firmware *fw; - unsigned long timeout; - time64_t timestamp; - struct tm time; - u64 address; - u32 value; int err; err = request_firmware(&fw, tegra->soc->firmware, tegra->dev); @@ -828,6 +821,24 @@ static int tegra_xusb_load_firmware(struct tegra_xusb *tegra) memcpy(tegra->fw.virt, fw->data, tegra->fw.size); release_firmware(fw); + return 0; +} + +static int tegra_xusb_load_firmware(struct tegra_xusb *tegra) +{ + unsigned int code_tag_blocks, code_size_blocks, code_blocks; + struct tegra_xusb_fw_header *header; + struct xhci_cap_regs __iomem *cap; + struct xhci_op_regs __iomem *op; + struct device *dev = tegra->dev; + unsigned long timeout; + time64_t timestamp; + struct tm time; + u64 address; + u32 value; + + header = (struct tegra_xusb_fw_header *)tegra->fw.virt; + if (csb_readl(tegra, XUSB_CSB_MP_ILOAD_BASE_LO) != 0) { dev_info(dev, "Firmware already loaded, Falcon state %#x\n", csb_readl(tegra, XUSB_FALC_CPUCTL)); @@ -1208,10 +1219,16 @@ static int tegra_xusb_probe(struct platform_device *pdev) goto put_rpm; } + err = tegra_xusb_request_firmware(tegra); + if (err < 0) { + dev_err(&pdev->dev, "failed to request firmware: %d\n", err); + goto disable_phy; + } + err = tegra_xusb_load_firmware(tegra); if (err < 0) { dev_err(&pdev->dev, "failed to load firmware: %d\n", err); - goto put_rpm; + goto free_firmware; } tegra->hcd->regs = tegra->regs; @@ -1221,7 +1238,7 @@ static int tegra_xusb_probe(struct platform_device *pdev) err = usb_add_hcd(tegra->hcd, tegra->xhci_irq, IRQF_SHARED); if (err < 0) { dev_err(&pdev->dev, "failed to add USB HCD: %d\n", err); - goto put_rpm; + goto free_firmware; } device_wakeup_enable(tegra->hcd->self.controller); @@ -1281,6 +1298,9 @@ put_rpm: tegra_xusb_runtime_suspend(&pdev->dev); put_hcd: usb_put_hcd(tegra->hcd); +free_firmware: + dma_free_coherent(&pdev->dev, tegra->fw.size, tegra->fw.virt, + tegra->fw.phys); disable_phy: tegra_xusb_phy_disable(tegra); pm_runtime_disable(&pdev->dev); -- cgit v1.2.3-59-g8ed1b From ec12ac10c9a7e2f1edf15c488e54f4c813cf0f52 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 6 Dec 2019 15:06:46 +0100 Subject: usb: host: xhci-tegra: Avoid a fixed duration sleep Do not use a fixed duration sleep to wait for the DMA controller to become ready. Instead, poll the L2IMEMOP_RESULT register for the VLD flag to determine when the XUSB controller's DMA master is ready. Based on work by JC Kuo . Signed-off-by: Thierry Reding Link: https://lore.kernel.org/r/20191206140653.2085561-4-thierry.reding@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 5cfd54862670..e80fce712fd5 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -101,6 +102,8 @@ #define L2IMEMOP_ACTION_SHIFT 24 #define L2IMEMOP_INVALIDATE_ALL (0x40 << L2IMEMOP_ACTION_SHIFT) #define L2IMEMOP_LOAD_LOCKED_RESULT (0x11 << L2IMEMOP_ACTION_SHIFT) +#define XUSB_CSB_MEMPOOL_L2IMEMOP_RESULT 0x101a18 +#define L2IMEMOP_RESULT_VLD BIT(31) #define XUSB_CSB_MP_APMAP 0x10181c #define APMAP_BOOTPATH BIT(31) @@ -836,6 +839,7 @@ static int tegra_xusb_load_firmware(struct tegra_xusb *tegra) struct tm time; u64 address; u32 value; + int err; header = (struct tegra_xusb_fw_header *)tegra->fw.virt; @@ -893,7 +897,16 @@ static int tegra_xusb_load_firmware(struct tegra_xusb *tegra) csb_writel(tegra, 0, XUSB_FALC_DMACTL); - msleep(50); + /* wait for RESULT_VLD to get set */ +#define tegra_csb_readl(offset) csb_readl(tegra, offset) + err = readx_poll_timeout(tegra_csb_readl, + XUSB_CSB_MEMPOOL_L2IMEMOP_RESULT, value, + value & L2IMEMOP_RESULT_VLD, 100, 10000); + if (err < 0) { + dev_err(dev, "DMA controller not ready %#010x\n", value); + return err; + } +#undef tegra_csb_readl csb_writel(tegra, le32_to_cpu(header->boot_codetag), XUSB_FALC_BOOTVEC); -- cgit v1.2.3-59-g8ed1b From 482ba7a6b42fa87dc8fa7d8c6140a916d0506549 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 6 Dec 2019 15:06:47 +0100 Subject: usb: host: xhci-tegra: Use CNR as firmware ready indicator The Falcon CPU state is a suboptimal indicator for firmware readiness, since the Falcon can be in a running state if the firmware is handling port state changes or running other tasks. Instead, the driver should check the STS_CNR bit to determine whether or not the firmware has been successfully loaded and is ready for XHCI operation. Based on work by JC Kuo . Signed-off-by: Thierry Reding Link: https://lore.kernel.org/r/20191206140653.2085561-5-thierry.reding@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index e80fce712fd5..eda5e1d50828 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -830,10 +830,10 @@ static int tegra_xusb_request_firmware(struct tegra_xusb *tegra) static int tegra_xusb_load_firmware(struct tegra_xusb *tegra) { unsigned int code_tag_blocks, code_size_blocks, code_blocks; + struct xhci_cap_regs __iomem *cap = tegra->regs; struct tegra_xusb_fw_header *header; - struct xhci_cap_regs __iomem *cap; - struct xhci_op_regs __iomem *op; struct device *dev = tegra->dev; + struct xhci_op_regs __iomem *op; unsigned long timeout; time64_t timestamp; struct tm time; @@ -842,6 +842,7 @@ static int tegra_xusb_load_firmware(struct tegra_xusb *tegra) int err; header = (struct tegra_xusb_fw_header *)tegra->fw.virt; + op = tegra->regs + HC_LENGTH(readl(&cap->hc_capbase)); if (csb_readl(tegra, XUSB_CSB_MP_ILOAD_BASE_LO) != 0) { dev_info(dev, "Firmware already loaded, Falcon state %#x\n", @@ -911,21 +912,23 @@ static int tegra_xusb_load_firmware(struct tegra_xusb *tegra) csb_writel(tegra, le32_to_cpu(header->boot_codetag), XUSB_FALC_BOOTVEC); - /* Boot Falcon CPU and wait for it to enter the STOPPED (idle) state. */ - timeout = jiffies + msecs_to_jiffies(5); - + /* Boot Falcon CPU and wait for USBSTS_CNR to get cleared. */ csb_writel(tegra, CPUCTL_STARTCPU, XUSB_FALC_CPUCTL); - while (time_before(jiffies, timeout)) { - if (csb_readl(tegra, XUSB_FALC_CPUCTL) == CPUCTL_STATE_STOPPED) + timeout = jiffies + msecs_to_jiffies(200); + + do { + value = readl(&op->status); + if ((value & STS_CNR) == 0) break; - usleep_range(100, 200); - } + usleep_range(1000, 2000); + } while (time_is_after_jiffies(timeout)); - if (csb_readl(tegra, XUSB_FALC_CPUCTL) != CPUCTL_STATE_STOPPED) { - dev_err(dev, "Falcon failed to start, state: %#x\n", - csb_readl(tegra, XUSB_FALC_CPUCTL)); + value = readl(&op->status); + if (value & STS_CNR) { + value = csb_readl(tegra, XUSB_FALC_CPUCTL); + dev_err(dev, "XHCI controller not read: %#010x\n", value); return -EIO; } -- cgit v1.2.3-59-g8ed1b From 96d8f628f0b35e1c1d93340cd4d2cde1ed3b8d9f Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 6 Dec 2019 15:06:48 +0100 Subject: usb: host: xhci-tegra: Extract firmware enable helper Extract a helper that enables message generation from the firmware. This removes clutter from tegra_xusb_probe() and will also come in useful for subsequent patches that introduce suspend/resume support. Based on work by JC Kuo . Signed-off-by: Thierry Reding Link: https://lore.kernel.org/r/20191206140653.2085561-6-thierry.reding@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index eda5e1d50828..499104c05668 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -993,11 +993,37 @@ static int tegra_xusb_powerdomain_init(struct device *dev, return 0; } -static int tegra_xusb_probe(struct platform_device *pdev) +static int __tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra) { struct tegra_xusb_mbox_msg msg; - struct resource *regs; + int err; + + /* Enable firmware messages from controller. */ + msg.cmd = MBOX_CMD_MSG_ENABLED; + msg.data = 0; + + err = tegra_xusb_mbox_send(tegra, &msg); + if (err < 0) + dev_err(tegra->dev, "failed to enable messages: %d\n", err); + + return err; +} + +static int tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra) +{ + int err; + + mutex_lock(&tegra->lock); + err = __tegra_xusb_enable_firmware_messages(tegra); + mutex_unlock(&tegra->lock); + + return err; +} + +static int tegra_xusb_probe(struct platform_device *pdev) +{ struct tegra_xusb *tegra; + struct resource *regs; struct xhci_hcd *xhci; unsigned int i, j, k; struct phy *phy; @@ -1277,21 +1303,12 @@ static int tegra_xusb_probe(struct platform_device *pdev) goto put_usb3; } - mutex_lock(&tegra->lock); - - /* Enable firmware messages from controller. */ - msg.cmd = MBOX_CMD_MSG_ENABLED; - msg.data = 0; - - err = tegra_xusb_mbox_send(tegra, &msg); + err = tegra_xusb_enable_firmware_messages(tegra); if (err < 0) { dev_err(&pdev->dev, "failed to enable messages: %d\n", err); - mutex_unlock(&tegra->lock); goto remove_usb3; } - mutex_unlock(&tegra->lock); - err = devm_request_threaded_irq(&pdev->dev, tegra->mbox_irq, tegra_xusb_mbox_irq, tegra_xusb_mbox_thread, 0, -- cgit v1.2.3-59-g8ed1b From ecd0fbd12d0fb5ee030d97d5be44766552aba07c Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 6 Dec 2019 15:06:49 +0100 Subject: usb: host: xhci-tegra: Reuse stored register base address The base address of the XUSB controller's registers is already stored in the HCD. Move assignment to the HCD fields to an earlier point so that they can be reused in the tegra_xusb_config() function. This avoids the need to pass the base address as an extra parameter, which in turn comes in handy in subsequent patches that need to call this function from the suspend/resume paths where these values are no longer readily available. Based on work by JC Kuo . Signed-off-by: Thierry Reding Link: https://lore.kernel.org/r/20191206140653.2085561-7-thierry.reding@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 499104c05668..31411f85e742 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -626,9 +626,9 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void *data) return IRQ_HANDLED; } -static void tegra_xusb_config(struct tegra_xusb *tegra, - struct resource *regs) +static void tegra_xusb_config(struct tegra_xusb *tegra) { + u32 regs = tegra->hcd->rsrc_start; u32 value; if (tegra->soc->has_ipfs) { @@ -642,7 +642,7 @@ static void tegra_xusb_config(struct tegra_xusb *tegra, /* Program BAR0 space */ value = fpci_readl(tegra, XUSB_CFG_4); value &= ~(XUSB_BASE_ADDR_MASK << XUSB_BASE_ADDR_SHIFT); - value |= regs->start & (XUSB_BASE_ADDR_MASK << XUSB_BASE_ADDR_SHIFT); + value |= regs & (XUSB_BASE_ADDR_MASK << XUSB_BASE_ADDR_SHIFT); fpci_writel(tegra, value, XUSB_CFG_4); usleep_range(100, 200); @@ -1226,6 +1226,10 @@ static int tegra_xusb_probe(struct platform_device *pdev) goto put_powerdomains; } + tegra->hcd->regs = tegra->regs; + tegra->hcd->rsrc_start = regs->start; + tegra->hcd->rsrc_len = resource_size(regs); + /* * This must happen after usb_create_hcd(), because usb_create_hcd() * will overwrite the drvdata of the device with the hcd it creates. @@ -1249,7 +1253,7 @@ static int tegra_xusb_probe(struct platform_device *pdev) goto disable_phy; } - tegra_xusb_config(tegra, regs); + tegra_xusb_config(tegra); /* * The XUSB Falcon microcontroller can only address 40 bits, so set @@ -1273,10 +1277,6 @@ static int tegra_xusb_probe(struct platform_device *pdev) goto free_firmware; } - tegra->hcd->regs = tegra->regs; - tegra->hcd->rsrc_start = regs->start; - tegra->hcd->rsrc_len = resource_size(regs); - err = usb_add_hcd(tegra->hcd, tegra->xhci_irq, IRQF_SHARED); if (err < 0) { dev_err(&pdev->dev, "failed to add USB HCD: %d\n", err); -- cgit v1.2.3-59-g8ed1b From 17926924be44b21556043a2faccc3b449110bd00 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 6 Dec 2019 15:06:50 +0100 Subject: usb: host: xhci-tegra: Enable runtime PM as late as possible A number of things can currently go wrong after the XUSB controller has been enabled, which means that it might need to be disabled again before it has ever been used. Avoid this by delaying runtime PM enablement until it's really required right before registers are accessed for the first time. Signed-off-by: Thierry Reding Link: https://lore.kernel.org/r/20191206140653.2085561-8-thierry.reding@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 31411f85e742..117e91b8ac6f 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -1242,19 +1242,6 @@ static int tegra_xusb_probe(struct platform_device *pdev) goto put_hcd; } - pm_runtime_enable(&pdev->dev); - if (pm_runtime_enabled(&pdev->dev)) - err = pm_runtime_get_sync(&pdev->dev); - else - err = tegra_xusb_runtime_resume(&pdev->dev); - - if (err < 0) { - dev_err(&pdev->dev, "failed to enable device: %d\n", err); - goto disable_phy; - } - - tegra_xusb_config(tegra); - /* * The XUSB Falcon microcontroller can only address 40 bits, so set * the DMA mask accordingly. @@ -1262,7 +1249,7 @@ static int tegra_xusb_probe(struct platform_device *pdev) err = dma_set_mask_and_coherent(tegra->dev, DMA_BIT_MASK(40)); if (err < 0) { dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err); - goto put_rpm; + goto disable_phy; } err = tegra_xusb_request_firmware(tegra); @@ -1271,16 +1258,30 @@ static int tegra_xusb_probe(struct platform_device *pdev) goto disable_phy; } + pm_runtime_enable(&pdev->dev); + + if (!pm_runtime_enabled(&pdev->dev)) + err = tegra_xusb_runtime_resume(&pdev->dev); + else + err = pm_runtime_get_sync(&pdev->dev); + + if (err < 0) { + dev_err(&pdev->dev, "failed to enable device: %d\n", err); + goto free_firmware; + } + + tegra_xusb_config(tegra); + err = tegra_xusb_load_firmware(tegra); if (err < 0) { dev_err(&pdev->dev, "failed to load firmware: %d\n", err); - goto free_firmware; + goto put_rpm; } err = usb_add_hcd(tegra->hcd, tegra->xhci_irq, IRQF_SHARED); if (err < 0) { dev_err(&pdev->dev, "failed to add USB HCD: %d\n", err); - goto free_firmware; + goto put_rpm; } device_wakeup_enable(tegra->hcd->self.controller); -- cgit v1.2.3-59-g8ed1b From 5c4e8d3781bc00363183b639cf3b603bd16d3994 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 6 Dec 2019 15:06:51 +0100 Subject: usb: host: xhci-tegra: Add support for XUSB context save/restore The XUSB controller contains registers that need to be saved on suspend and restored on resume in addition to the XHCI specific registers. Add support for saving and restoring the XUSB specific context. Based on work by JC Kuo . Signed-off-by: Thierry Reding Link: https://lore.kernel.org/r/20191206140653.2085561-9-thierry.reding@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 102 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 117e91b8ac6f..1b5e4ee313ce 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -155,12 +155,25 @@ struct tegra_xusb_mbox_regs { u16 owner; }; +struct tegra_xusb_context_soc { + struct { + const unsigned int *offsets; + unsigned int num_offsets; + } ipfs; + + struct { + const unsigned int *offsets; + unsigned int num_offsets; + } fpci; +}; + struct tegra_xusb_soc { const char *firmware; const char * const *supply_names; unsigned int num_supplies; const struct tegra_xusb_phy_type *phy_types; unsigned int num_types; + const struct tegra_xusb_context_soc *context; struct { struct { @@ -175,6 +188,11 @@ struct tegra_xusb_soc { bool has_ipfs; }; +struct tegra_xusb_context { + u32 *ipfs; + u32 *fpci; +}; + struct tegra_xusb { struct device *dev; void __iomem *regs; @@ -221,6 +239,8 @@ struct tegra_xusb { void *virt; dma_addr_t phys; } fw; + + struct tegra_xusb_context context; }; static struct hc_driver __read_mostly tegra_xhci_hc_driver; @@ -796,6 +816,37 @@ disable_clk: return err; } +#ifdef CONFIG_PM_SLEEP +static int tegra_xusb_init_context(struct tegra_xusb *tegra) +{ + const struct tegra_xusb_context_soc *soc = tegra->soc->context; + + /* + * Skip support for context save/restore if the SoC doesn't have any + * XUSB specific context that needs to be saved/restored. + */ + if (!soc) + return 0; + + tegra->context.ipfs = devm_kcalloc(tegra->dev, soc->ipfs.num_offsets, + sizeof(u32), GFP_KERNEL); + if (!tegra->context.ipfs) + return -ENOMEM; + + tegra->context.fpci = devm_kcalloc(tegra->dev, soc->ipfs.num_offsets, + sizeof(u32), GFP_KERNEL); + if (!tegra->context.fpci) + return -ENOMEM; + + return 0; +} +#else +static inline int tegra_xusb_init_context(struct tegra_xusb *tegra) +{ + return 0; +} +#endif + static int tegra_xusb_request_firmware(struct tegra_xusb *tegra) { struct tegra_xusb_fw_header *header; @@ -1039,6 +1090,10 @@ static int tegra_xusb_probe(struct platform_device *pdev) mutex_init(&tegra->lock); tegra->dev = &pdev->dev; + err = tegra_xusb_init_context(tegra); + if (err < 0) + return err; + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); tegra->regs = devm_ioremap_resource(&pdev->dev, regs); if (IS_ERR(tegra->regs)) @@ -1382,14 +1437,55 @@ static int tegra_xusb_remove(struct platform_device *pdev) } #ifdef CONFIG_PM_SLEEP +static void tegra_xusb_save_context(struct tegra_xusb *tegra) +{ + const struct tegra_xusb_context_soc *soc = tegra->soc->context; + struct tegra_xusb_context *ctx = &tegra->context; + unsigned int i; + + if (soc && soc->ipfs.num_offsets > 0) { + for (i = 0; i < soc->ipfs.num_offsets; i++) + ctx->ipfs[i] = ipfs_readl(tegra, soc->ipfs.offsets[i]); + } + + if (soc && soc->fpci.num_offsets > 0) { + for (i = 0; i < soc->fpci.num_offsets; i++) + ctx->fpci[i] = fpci_readl(tegra, soc->fpci.offsets[i]); + } +} + +static void tegra_xusb_restore_context(struct tegra_xusb *tegra) +{ + const struct tegra_xusb_context_soc *soc = tegra->soc->context; + struct tegra_xusb_context *ctx = &tegra->context; + unsigned int i; + + if (soc && soc->fpci.num_offsets > 0) { + for (i = 0; i < soc->fpci.num_offsets; i++) + fpci_writel(tegra, ctx->fpci[i], soc->fpci.offsets[i]); + } + + if (soc && soc->ipfs.num_offsets > 0) { + for (i = 0; i < soc->ipfs.num_offsets; i++) + ipfs_writel(tegra, ctx->ipfs[i], soc->ipfs.offsets[i]); + } +} + static int tegra_xusb_suspend(struct device *dev) { struct tegra_xusb *tegra = dev_get_drvdata(dev); struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); bool wakeup = device_may_wakeup(dev); + int err; /* TODO: Powergate controller across suspend/resume. */ - return xhci_suspend(xhci, wakeup); + err = xhci_suspend(xhci, wakeup); + if (err < 0) + return err; + + tegra_xusb_save_context(tegra); + + return 0; } static int tegra_xusb_resume(struct device *dev) @@ -1397,7 +1493,9 @@ static int tegra_xusb_resume(struct device *dev) struct tegra_xusb *tegra = dev_get_drvdata(dev); struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); - return xhci_resume(xhci, 0); + tegra_xusb_restore_context(tegra); + + return xhci_resume(xhci, false); } #endif -- cgit v1.2.3-59-g8ed1b From 9ccae88e572b36a3ede1c2fe67cfd3f2b36e0610 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 6 Dec 2019 15:06:52 +0100 Subject: usb: host: xhci-tegra: Add XUSB controller context Define the offsets of the registers that need to be saved on suspend and restored on resume for the various NVIDIA Tegra generations supported by the XUSB driver. Based on work by JC Kuo . Signed-off-by: Thierry Reding Link: https://lore.kernel.org/r/20191206140653.2085561-10-thierry.reding@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 80 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 11 deletions(-) diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 1b5e4ee313ce..7f6657ad5ce5 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -39,7 +39,15 @@ #define XUSB_CFG_4 0x010 #define XUSB_BASE_ADDR_SHIFT 15 #define XUSB_BASE_ADDR_MASK 0x1ffff +#define XUSB_CFG_16 0x040 +#define XUSB_CFG_24 0x060 +#define XUSB_CFG_AXI_CFG 0x0f8 #define XUSB_CFG_ARU_C11_CSBRANGE 0x41c +#define XUSB_CFG_ARU_CONTEXT 0x43c +#define XUSB_CFG_ARU_CONTEXT_HS_PLS 0x478 +#define XUSB_CFG_ARU_CONTEXT_FS_PLS 0x47c +#define XUSB_CFG_ARU_CONTEXT_HSFS_SPEED 0x480 +#define XUSB_CFG_ARU_CONTEXT_HSFS_PP 0x484 #define XUSB_CFG_CSB_BASE_ADDR 0x800 /* FPCI mailbox registers */ @@ -63,11 +71,20 @@ #define MBOX_SMI_INTR_EN BIT(3) /* IPFS registers */ +#define IPFS_XUSB_HOST_MSI_BAR_SZ_0 0x0c0 +#define IPFS_XUSB_HOST_MSI_AXI_BAR_ST_0 0x0c4 +#define IPFS_XUSB_HOST_MSI_FPCI_BAR_ST_0 0x0c8 +#define IPFS_XUSB_HOST_MSI_VEC0_0 0x100 +#define IPFS_XUSB_HOST_MSI_EN_VEC0_0 0x140 #define IPFS_XUSB_HOST_CONFIGURATION_0 0x180 #define IPFS_EN_FPCI BIT(0) +#define IPFS_XUSB_HOST_FPCI_ERROR_MASKS_0 0x184 #define IPFS_XUSB_HOST_INTR_MASK_0 0x188 #define IPFS_IP_INT_MASK BIT(16) +#define IPFS_XUSB_HOST_INTR_ENABLE_0 0x198 +#define IPFS_XUSB_HOST_UFPCI_CONFIG_0 0x19c #define IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0 0x1bc +#define IPFS_XUSB_HOST_MCCIF_FIFOCTRL_0 0x1dc #define CSB_PAGE_SELECT_MASK 0x7fffff #define CSB_PAGE_SELECT_SHIFT 9 @@ -821,13 +838,6 @@ static int tegra_xusb_init_context(struct tegra_xusb *tegra) { const struct tegra_xusb_context_soc *soc = tegra->soc->context; - /* - * Skip support for context save/restore if the SoC doesn't have any - * XUSB specific context that needs to be saved/restored. - */ - if (!soc) - return 0; - tegra->context.ipfs = devm_kcalloc(tegra->dev, soc->ipfs.num_offsets, sizeof(u32), GFP_KERNEL); if (!tegra->context.ipfs) @@ -1443,12 +1453,12 @@ static void tegra_xusb_save_context(struct tegra_xusb *tegra) struct tegra_xusb_context *ctx = &tegra->context; unsigned int i; - if (soc && soc->ipfs.num_offsets > 0) { + if (soc->ipfs.num_offsets > 0) { for (i = 0; i < soc->ipfs.num_offsets; i++) ctx->ipfs[i] = ipfs_readl(tegra, soc->ipfs.offsets[i]); } - if (soc && soc->fpci.num_offsets > 0) { + if (soc->fpci.num_offsets > 0) { for (i = 0; i < soc->fpci.num_offsets; i++) ctx->fpci[i] = fpci_readl(tegra, soc->fpci.offsets[i]); } @@ -1460,12 +1470,12 @@ static void tegra_xusb_restore_context(struct tegra_xusb *tegra) struct tegra_xusb_context *ctx = &tegra->context; unsigned int i; - if (soc && soc->fpci.num_offsets > 0) { + if (soc->fpci.num_offsets > 0) { for (i = 0; i < soc->fpci.num_offsets; i++) fpci_writel(tegra, ctx->fpci[i], soc->fpci.offsets[i]); } - if (soc && soc->ipfs.num_offsets > 0) { + if (soc->ipfs.num_offsets > 0) { for (i = 0; i < soc->ipfs.num_offsets; i++) ipfs_writel(tegra, ctx->ipfs[i], soc->ipfs.offsets[i]); } @@ -1522,12 +1532,50 @@ static const struct tegra_xusb_phy_type tegra124_phy_types[] = { { .name = "hsic", .num = 2, }, }; +static const unsigned int tegra124_xusb_context_ipfs[] = { + IPFS_XUSB_HOST_MSI_BAR_SZ_0, + IPFS_XUSB_HOST_MSI_BAR_SZ_0, + IPFS_XUSB_HOST_MSI_AXI_BAR_ST_0, + IPFS_XUSB_HOST_MSI_FPCI_BAR_ST_0, + IPFS_XUSB_HOST_MSI_VEC0_0, + IPFS_XUSB_HOST_MSI_EN_VEC0_0, + IPFS_XUSB_HOST_FPCI_ERROR_MASKS_0, + IPFS_XUSB_HOST_INTR_MASK_0, + IPFS_XUSB_HOST_INTR_ENABLE_0, + IPFS_XUSB_HOST_UFPCI_CONFIG_0, + IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0, + IPFS_XUSB_HOST_MCCIF_FIFOCTRL_0, +}; + +static const unsigned int tegra124_xusb_context_fpci[] = { + XUSB_CFG_ARU_CONTEXT_HS_PLS, + XUSB_CFG_ARU_CONTEXT_FS_PLS, + XUSB_CFG_ARU_CONTEXT_HSFS_SPEED, + XUSB_CFG_ARU_CONTEXT_HSFS_PP, + XUSB_CFG_ARU_CONTEXT, + XUSB_CFG_AXI_CFG, + XUSB_CFG_24, + XUSB_CFG_16, +}; + +static const struct tegra_xusb_context_soc tegra124_xusb_context = { + .ipfs = { + .num_offsets = ARRAY_SIZE(tegra124_xusb_context_ipfs), + .offsets = tegra124_xusb_context_ipfs, + }, + .fpci = { + .num_offsets = ARRAY_SIZE(tegra124_xusb_context_fpci), + .offsets = tegra124_xusb_context_fpci, + }, +}; + static const struct tegra_xusb_soc tegra124_soc = { .firmware = "nvidia/tegra124/xusb.bin", .supply_names = tegra124_supply_names, .num_supplies = ARRAY_SIZE(tegra124_supply_names), .phy_types = tegra124_phy_types, .num_types = ARRAY_SIZE(tegra124_phy_types), + .context = &tegra124_xusb_context, .ports = { .usb2 = { .offset = 4, .count = 4, }, .hsic = { .offset = 6, .count = 2, }, @@ -1566,6 +1614,7 @@ static const struct tegra_xusb_soc tegra210_soc = { .num_supplies = ARRAY_SIZE(tegra210_supply_names), .phy_types = tegra210_phy_types, .num_types = ARRAY_SIZE(tegra210_phy_types), + .context = &tegra124_xusb_context, .ports = { .usb2 = { .offset = 4, .count = 4, }, .hsic = { .offset = 8, .count = 1, }, @@ -1591,12 +1640,20 @@ static const struct tegra_xusb_phy_type tegra186_phy_types[] = { { .name = "hsic", .num = 1, }, }; +static const struct tegra_xusb_context_soc tegra186_xusb_context = { + .fpci = { + .num_offsets = ARRAY_SIZE(tegra124_xusb_context_fpci), + .offsets = tegra124_xusb_context_fpci, + }, +}; + static const struct tegra_xusb_soc tegra186_soc = { .firmware = "nvidia/tegra186/xusb.bin", .supply_names = tegra186_supply_names, .num_supplies = ARRAY_SIZE(tegra186_supply_names), .phy_types = tegra186_phy_types, .num_types = ARRAY_SIZE(tegra186_phy_types), + .context = &tegra186_xusb_context, .ports = { .usb3 = { .offset = 0, .count = 3, }, .usb2 = { .offset = 3, .count = 3, }, @@ -1626,6 +1683,7 @@ static const struct tegra_xusb_soc tegra194_soc = { .num_supplies = ARRAY_SIZE(tegra194_supply_names), .phy_types = tegra194_phy_types, .num_types = ARRAY_SIZE(tegra194_phy_types), + .context = &tegra186_xusb_context, .ports = { .usb3 = { .offset = 0, .count = 4, }, .usb2 = { .offset = 4, .count = 4, }, -- cgit v1.2.3-59-g8ed1b From cad0a5c74e7a1760d90a41df8e6151a53a598676 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 6 Dec 2019 15:06:53 +0100 Subject: usb: host: xhci-tegra: Implement basic ELPG support This implements basic engine-level powergate support which allows the XUSB controller to be put into a low power mode on system sleep and get it out of that low power mode again on resume. Based on work by JC Kuo . Signed-off-by: Thierry Reding Link: https://lore.kernel.org/r/20191206140653.2085561-11-thierry.reding@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 127 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 119 insertions(+), 8 deletions(-) diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 7f6657ad5ce5..0b58ef3a7f7f 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -1447,6 +1447,45 @@ static int tegra_xusb_remove(struct platform_device *pdev) } #ifdef CONFIG_PM_SLEEP +static bool xhci_hub_ports_suspended(struct xhci_hub *hub) +{ + struct device *dev = hub->hcd->self.controller; + bool status = true; + unsigned int i; + u32 value; + + for (i = 0; i < hub->num_ports; i++) { + value = readl(hub->ports[i]->addr); + if ((value & PORT_PE) == 0) + continue; + + if ((value & PORT_PLS_MASK) != XDEV_U3) { + dev_info(dev, "%u-%u isn't suspended: %#010x\n", + hub->hcd->self.busnum, i + 1, value); + status = false; + } + } + + return status; +} + +static int tegra_xusb_check_ports(struct tegra_xusb *tegra) +{ + struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); + unsigned long flags; + int err = 0; + + spin_lock_irqsave(&xhci->lock, flags); + + if (!xhci_hub_ports_suspended(&xhci->usb2_rhub) || + !xhci_hub_ports_suspended(&xhci->usb3_rhub)) + err = -EBUSY; + + spin_unlock_irqrestore(&xhci->lock, flags); + + return err; +} + static void tegra_xusb_save_context(struct tegra_xusb *tegra) { const struct tegra_xusb_context_soc *soc = tegra->soc->context; @@ -1481,31 +1520,103 @@ static void tegra_xusb_restore_context(struct tegra_xusb *tegra) } } -static int tegra_xusb_suspend(struct device *dev) +static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool wakeup) { - struct tegra_xusb *tegra = dev_get_drvdata(dev); struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); - bool wakeup = device_may_wakeup(dev); int err; - /* TODO: Powergate controller across suspend/resume. */ + err = tegra_xusb_check_ports(tegra); + if (err < 0) { + dev_err(tegra->dev, "not all ports suspended: %d\n", err); + return err; + } + err = xhci_suspend(xhci, wakeup); - if (err < 0) + if (err < 0) { + dev_err(tegra->dev, "failed to suspend XHCI: %d\n", err); return err; + } tegra_xusb_save_context(tegra); + tegra_xusb_phy_disable(tegra); + tegra_xusb_clk_disable(tegra); return 0; } -static int tegra_xusb_resume(struct device *dev) +static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool wakeup) { - struct tegra_xusb *tegra = dev_get_drvdata(dev); struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); + int err; + err = tegra_xusb_clk_enable(tegra); + if (err < 0) { + dev_err(tegra->dev, "failed to enable clocks: %d\n", err); + return err; + } + + err = tegra_xusb_phy_enable(tegra); + if (err < 0) { + dev_err(tegra->dev, "failed to enable PHYs: %d\n", err); + goto disable_clk; + } + + tegra_xusb_config(tegra); tegra_xusb_restore_context(tegra); - return xhci_resume(xhci, false); + err = tegra_xusb_load_firmware(tegra); + if (err < 0) { + dev_err(tegra->dev, "failed to load firmware: %d\n", err); + goto disable_phy; + } + + err = __tegra_xusb_enable_firmware_messages(tegra); + if (err < 0) { + dev_err(tegra->dev, "failed to enable messages: %d\n", err); + goto disable_phy; + } + + err = xhci_resume(xhci, true); + if (err < 0) { + dev_err(tegra->dev, "failed to resume XHCI: %d\n", err); + goto disable_phy; + } + + return 0; + +disable_phy: + tegra_xusb_phy_disable(tegra); +disable_clk: + tegra_xusb_clk_disable(tegra); + return err; +} + +static int tegra_xusb_suspend(struct device *dev) +{ + struct tegra_xusb *tegra = dev_get_drvdata(dev); + bool wakeup = device_may_wakeup(dev); + int err; + + synchronize_irq(tegra->mbox_irq); + + mutex_lock(&tegra->lock); + err = tegra_xusb_enter_elpg(tegra, wakeup); + mutex_unlock(&tegra->lock); + + return err; +} + +static int tegra_xusb_resume(struct device *dev) +{ + struct tegra_xusb *tegra = dev_get_drvdata(dev); + bool wakeup = device_may_wakeup(dev); + int err; + + mutex_lock(&tegra->lock); + err = tegra_xusb_exit_elpg(tegra, wakeup); + mutex_unlock(&tegra->lock); + + return err; } #endif -- cgit v1.2.3-59-g8ed1b From d27ab1e60970a6a32a0f8b3287f3463e20d68909 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Dec 2019 15:18:21 +0100 Subject: usb: gadget: u_audio: Use managed buffer allocation Clean up the driver with the new managed buffer allocation API. The hw_params and hw_free callbacks became superfluous and dropped. Signed-off-by: Takashi Iwai Link: https://lore.kernel.org/r/20191210141822.18705-2-tiwai@suse.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/u_audio.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index 7ec6a996af26..67c4c85433d1 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -239,18 +239,6 @@ static snd_pcm_uframes_t uac_pcm_pointer(struct snd_pcm_substream *substream) return bytes_to_frames(substream->runtime, prm->hw_ptr); } -static int uac_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - return snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); -} - -static int uac_pcm_hw_free(struct snd_pcm_substream *substream) -{ - return snd_pcm_lib_free_pages(substream); -} - static int uac_pcm_open(struct snd_pcm_substream *substream) { struct snd_uac_chip *uac = snd_pcm_substream_chip(substream); @@ -327,8 +315,6 @@ static const struct snd_pcm_ops uac_pcm_ops = { .open = uac_pcm_open, .close = uac_pcm_null, .ioctl = snd_pcm_lib_ioctl, - .hw_params = uac_pcm_hw_params, - .hw_free = uac_pcm_hw_free, .trigger = uac_pcm_trigger, .pointer = uac_pcm_pointer, .prepare = uac_pcm_null, @@ -584,8 +570,8 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, strlcpy(card->shortname, card_name, sizeof(card->shortname)); sprintf(card->longname, "%s %i", card_name, card->dev->id); - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, - NULL, 0, BUFF_SIZE_MAX); + snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, + NULL, 0, BUFF_SIZE_MAX); err = snd_card_register(card); -- cgit v1.2.3-59-g8ed1b From fcc84698291203f6588598be417b3ac58d2561d3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Dec 2019 15:18:22 +0100 Subject: usb: gadget: u_audio: Drop superfluous ioctl PCM ops PCM core deals the empty ioctl field now as default. Let's kill the redundant lines. Signed-off-by: Takashi Iwai Link: https://lore.kernel.org/r/20191210141822.18705-3-tiwai@suse.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/u_audio.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index 67c4c85433d1..cf4f2358889b 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -314,7 +314,6 @@ static int uac_pcm_null(struct snd_pcm_substream *substream) static const struct snd_pcm_ops uac_pcm_ops = { .open = uac_pcm_open, .close = uac_pcm_null, - .ioctl = snd_pcm_lib_ioctl, .trigger = uac_pcm_trigger, .pointer = uac_pcm_pointer, .prepare = uac_pcm_null, -- cgit v1.2.3-59-g8ed1b From 10e5e6c2496354f0afec82dba459339c421badbf Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 11 Dec 2019 16:38:57 +0900 Subject: usb: gadget: move choice ... endchoice to legacy/Kconfig drivers/usb/gadget/Kconfig includes drivers/usb/gadget/legacy/Kconfig inside the 'choice' block. The current Kconfig allows this, but I'd like to discourage this usage. People tend to mess up the structure without noticing that entire drivers/usb/gadget/legacy/Kconfig is placed in the choice context. In fact, legacy/Kconfig mixes up bool and tristate in the choice, and creates nested choice, etc. This commit does not change the behavior, but it will help people notice how badly this Kconfig file is written. Signed-off-by: Masahiro Yamada Link: https://lore.kernel.org/r/20191211073857.16780-1-masahiroy@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/Kconfig | 28 ---------------------------- drivers/usb/gadget/legacy/Kconfig | 28 ++++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 02ff850278b1..c6db0a0a340c 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -483,34 +483,6 @@ config USB_CONFIGFS_F_TCM Both protocols can work on USB2.0 and USB3.0. UAS utilizes the USB 3.0 feature called streams support. -choice - tristate "USB Gadget precomposed configurations" - default USB_ETH - optional - help - A Linux "Gadget Driver" talks to the USB Peripheral Controller - driver through the abstract "gadget" API. Some other operating - systems call these "client" drivers, of which "class drivers" - are a subset (implementing a USB device class specification). - A gadget driver implements one or more USB functions using - the peripheral hardware. - - Gadget drivers are hardware-neutral, or "platform independent", - except that they sometimes must understand quirks or limitations - of the particular controllers they work with. For example, when - a controller doesn't support alternate configurations or provide - enough of the right types of endpoints, the gadget driver might - not be able work with that controller, or might need to implement - a less common variant of a device class protocol. - - The available choices each represent a single precomposed USB - gadget configuration. In the device model, each option contains - both the device instantiation as a child for a USB gadget - controller, and the relevant drivers for each function declared - by the device. - source "drivers/usb/gadget/legacy/Kconfig" -endchoice - endif # USB_GADGET diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig index 119a4e47681f..6e7e1a9202e6 100644 --- a/drivers/usb/gadget/legacy/Kconfig +++ b/drivers/usb/gadget/legacy/Kconfig @@ -14,6 +14,32 @@ # both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG). # +choice + tristate "USB Gadget precomposed configurations" + default USB_ETH + optional + help + A Linux "Gadget Driver" talks to the USB Peripheral Controller + driver through the abstract "gadget" API. Some other operating + systems call these "client" drivers, of which "class drivers" + are a subset (implementing a USB device class specification). + A gadget driver implements one or more USB functions using + the peripheral hardware. + + Gadget drivers are hardware-neutral, or "platform independent", + except that they sometimes must understand quirks or limitations + of the particular controllers they work with. For example, when + a controller doesn't support alternate configurations or provide + enough of the right types of endpoints, the gadget driver might + not be able work with that controller, or might need to implement + a less common variant of a device class protocol. + + The available choices each represent a single precomposed USB + gadget configuration. In the device model, each option contains + both the device instantiation as a child for a USB gadget + controller, and the relevant drivers for each function declared + by the device. + config USB_ZERO tristate "Gadget Zero (DEVELOPMENT)" select USB_LIBCOMPOSITE @@ -489,3 +515,5 @@ config USB_G_WEBCAM Say "y" to link the driver statically, or "m" to build a dynamically linked module called "g_webcam". + +endchoice -- cgit v1.2.3-59-g8ed1b From 386e5e29d81cd088a1111277a18f13d571a6cea5 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 17 Dec 2019 15:33:37 +0300 Subject: thunderbolt: Make tb_find_port() available to other files We will be needing this when adding initial USB4 support so make it available to other files in the driver as well. We also rename it to tb_switch_find_port() to follow conventions used in switch.c. No functional changes. Signed-off-by: Mika Westerberg Link: https://lore.kernel.org/r/20191217123345.31850-2-mika.westerberg@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/switch.c | 18 ++++++++++++++++++ drivers/thunderbolt/tb.c | 22 ++-------------------- drivers/thunderbolt/tb.h | 2 ++ 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index ca86a8e09c77..9c72521cb298 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -2517,6 +2517,24 @@ struct tb_switch *tb_switch_find_by_route(struct tb *tb, u64 route) return NULL; } +/** + * tb_switch_find_port() - return the first port of @type on @sw or NULL + * @sw: Switch to find the port from + * @type: Port type to look for + */ +struct tb_port *tb_switch_find_port(struct tb_switch *sw, + enum tb_port_type type) +{ + struct tb_port *port; + + tb_switch_for_each_port(sw, port) { + if (port->config.type == type) + return port; + } + + return NULL; +} + void tb_switch_exit(void) { ida_destroy(&nvm_ida); diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index ea8727f769d6..54085f67810a 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -338,24 +338,6 @@ static void tb_free_unplugged_children(struct tb_switch *sw) } } -/** - * tb_find_port() - return the first port of @type on @sw or NULL - * @sw: Switch to find the port from - * @type: Port type to look for - */ -static struct tb_port *tb_find_port(struct tb_switch *sw, - enum tb_port_type type) -{ - struct tb_port *port; - - tb_switch_for_each_port(sw, port) { - if (port->config.type == type) - return port; - } - - return NULL; -} - /** * tb_find_unused_port() - return the first inactive port on @sw * @sw: Switch to find the port on @@ -586,7 +568,7 @@ static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw) struct tb_switch *parent_sw; struct tb_tunnel *tunnel; - up = tb_find_port(sw, TB_TYPE_PCIE_UP); + up = tb_switch_find_port(sw, TB_TYPE_PCIE_UP); if (!up) return 0; @@ -624,7 +606,7 @@ static int tb_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) sw = tb_to_switch(xd->dev.parent); dst_port = tb_port_at(xd->route, sw); - nhi_port = tb_find_port(tb->root_switch, TB_TYPE_NHI); + nhi_port = tb_switch_find_port(tb->root_switch, TB_TYPE_NHI); mutex_lock(&tb->lock); tunnel = tb_tunnel_alloc_dma(tb, nhi_port, dst_port, xd->transmit_ring, diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index ec851f20c571..ade1c7c77db1 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -533,6 +533,8 @@ void tb_switch_suspend(struct tb_switch *sw); int tb_switch_resume(struct tb_switch *sw); int tb_switch_reset(struct tb *tb, u64 route); void tb_sw_set_unplugged(struct tb_switch *sw); +struct tb_port *tb_switch_find_port(struct tb_switch *sw, + enum tb_port_type type); struct tb_switch *tb_switch_find_by_link_depth(struct tb *tb, u8 link, u8 depth); struct tb_switch *tb_switch_find_by_uuid(struct tb *tb, const uuid_t *uuid); -- cgit v1.2.3-59-g8ed1b From 4deb200d34a779aa336ddcd213e39eb6104eb78a Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 17 Dec 2019 15:33:38 +0300 Subject: thunderbolt: Call tb_eeprom_get_drom_offset() from tb_eeprom_read_n() We are going to re-use tb_drom_read() for USB4 DROM reading as well. USB4 has separate router operations for this which does not need the drom_offset. Therefore we move call to tb_eeprom_get_drom_offset() into tb_eeprom_read_n() where it is needed. While there change return -ENOSYS to -ENODEV because the former is only supposed to be used with system calls (invalid syscall nr). Signed-off-by: Mika Westerberg Link: https://lore.kernel.org/r/20191217123345.31850-3-mika.westerberg@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/eeprom.c | 88 ++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c index 8dd7de0cc826..540e0105bcc0 100644 --- a/drivers/thunderbolt/eeprom.c +++ b/drivers/thunderbolt/eeprom.c @@ -130,13 +130,52 @@ static int tb_eeprom_in(struct tb_switch *sw, u8 *val) return 0; } +/** + * tb_eeprom_get_drom_offset - get drom offset within eeprom + */ +static int tb_eeprom_get_drom_offset(struct tb_switch *sw, u16 *offset) +{ + struct tb_cap_plug_events cap; + int res; + + if (!sw->cap_plug_events) { + tb_sw_warn(sw, "no TB_CAP_PLUG_EVENTS, cannot read eeprom\n"); + return -ENODEV; + } + res = tb_sw_read(sw, &cap, TB_CFG_SWITCH, sw->cap_plug_events, + sizeof(cap) / 4); + if (res) + return res; + + if (!cap.eeprom_ctl.present || cap.eeprom_ctl.not_present) { + tb_sw_warn(sw, "no NVM\n"); + return -ENODEV; + } + + if (cap.drom_offset > 0xffff) { + tb_sw_warn(sw, "drom offset is larger than 0xffff: %#x\n", + cap.drom_offset); + return -ENXIO; + } + *offset = cap.drom_offset; + return 0; +} + /** * tb_eeprom_read_n - read count bytes from offset into val */ static int tb_eeprom_read_n(struct tb_switch *sw, u16 offset, u8 *val, size_t count) { + u16 drom_offset; int i, res; + + res = tb_eeprom_get_drom_offset(sw, &drom_offset); + if (res) + return res; + + offset += drom_offset; + res = tb_eeprom_active(sw, true); if (res) return res; @@ -238,36 +277,6 @@ struct tb_drom_entry_port { } __packed; -/** - * tb_eeprom_get_drom_offset - get drom offset within eeprom - */ -static int tb_eeprom_get_drom_offset(struct tb_switch *sw, u16 *offset) -{ - struct tb_cap_plug_events cap; - int res; - if (!sw->cap_plug_events) { - tb_sw_warn(sw, "no TB_CAP_PLUG_EVENTS, cannot read eeprom\n"); - return -ENOSYS; - } - res = tb_sw_read(sw, &cap, TB_CFG_SWITCH, sw->cap_plug_events, - sizeof(cap) / 4); - if (res) - return res; - - if (!cap.eeprom_ctl.present || cap.eeprom_ctl.not_present) { - tb_sw_warn(sw, "no NVM\n"); - return -ENOSYS; - } - - if (cap.drom_offset > 0xffff) { - tb_sw_warn(sw, "drom offset is larger than 0xffff: %#x\n", - cap.drom_offset); - return -ENXIO; - } - *offset = cap.drom_offset; - return 0; -} - /** * tb_drom_read_uid_only - read uid directly from drom * @@ -277,17 +286,11 @@ static int tb_eeprom_get_drom_offset(struct tb_switch *sw, u16 *offset) int tb_drom_read_uid_only(struct tb_switch *sw, u64 *uid) { u8 data[9]; - u16 drom_offset; u8 crc; - int res = tb_eeprom_get_drom_offset(sw, &drom_offset); - if (res) - return res; - - if (drom_offset == 0) - return -ENODEV; + int res; /* read uid */ - res = tb_eeprom_read_n(sw, drom_offset, data, 9); + res = tb_eeprom_read_n(sw, 0, data, 9); if (res) return res; @@ -489,7 +492,6 @@ err_free: */ int tb_drom_read(struct tb_switch *sw) { - u16 drom_offset; u16 size; u32 crc; struct tb_drom_header *header; @@ -517,11 +519,7 @@ int tb_drom_read(struct tb_switch *sw) return 0; } - res = tb_eeprom_get_drom_offset(sw, &drom_offset); - if (res) - return res; - - res = tb_eeprom_read_n(sw, drom_offset + 14, (u8 *) &size, 2); + res = tb_eeprom_read_n(sw, 14, (u8 *) &size, 2); if (res) return res; size &= 0x3ff; @@ -535,7 +533,7 @@ int tb_drom_read(struct tb_switch *sw) sw->drom = kzalloc(size, GFP_KERNEL); if (!sw->drom) return -ENOMEM; - res = tb_eeprom_read_n(sw, drom_offset, sw->drom, size); + res = tb_eeprom_read_n(sw, 0, sw->drom, size); if (res) goto err; -- cgit v1.2.3-59-g8ed1b From 210e9f56e9e12472741b949950f9efcebf350750 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 17 Dec 2019 15:33:39 +0300 Subject: thunderbolt: Populate PG field in hot plug acknowledgment packet USB4 1.0 section 6.4.2.7 specifies a new field (PG) in notification packet that is sent as response of hot plug/unplug events. This field tells whether the acknowledgment is for plug or unplug event. This needs to be set accordingly in order the router to send further hot plug notifications. To make it simpler we fill the field unconditionally. Legacy devices do not look at this field so there should be no problems with them. While there rename tb_cfg_error() to tb_cfg_ack_plug() and update the log message accordingly. The function is only used to ack plug/unplug events. Signed-off-by: Mika Westerberg Link: https://lore.kernel.org/r/20191217123345.31850-4-mika.westerberg@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/ctl.c | 19 +++++++++++++------ drivers/thunderbolt/ctl.h | 3 +-- drivers/thunderbolt/tb.c | 3 +-- drivers/thunderbolt/tb_msgs.h | 6 +++++- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index d97813e80e5f..f77ceae5c7d7 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -708,19 +708,26 @@ void tb_ctl_stop(struct tb_ctl *ctl) /* public interface, commands */ /** - * tb_cfg_error() - send error packet + * tb_cfg_ack_plug() - Ack hot plug/unplug event + * @ctl: Control channel to use + * @route: Router that originated the event + * @port: Port where the hot plug/unplug happened + * @unplug: Ack hot plug or unplug * - * Return: Returns 0 on success or an error code on failure. + * Call this as response for hot plug/unplug event to ack it. + * Returns %0 on success or an error code on failure. */ -int tb_cfg_error(struct tb_ctl *ctl, u64 route, u32 port, - enum tb_cfg_error error) +int tb_cfg_ack_plug(struct tb_ctl *ctl, u64 route, u32 port, bool unplug) { struct cfg_error_pkg pkg = { .header = tb_cfg_make_header(route), .port = port, - .error = error, + .error = TB_CFG_ERROR_ACK_PLUG_EVENT, + .pg = unplug ? TB_CFG_ERROR_PG_HOT_UNPLUG + : TB_CFG_ERROR_PG_HOT_PLUG, }; - tb_ctl_dbg(ctl, "resetting error on %llx:%x.\n", route, port); + tb_ctl_dbg(ctl, "acking hot %splug event on %llx:%x\n", + unplug ? "un" : "", route, port); return tb_ctl_tx(ctl, &pkg, sizeof(pkg), TB_CFG_PKG_ERROR); } diff --git a/drivers/thunderbolt/ctl.h b/drivers/thunderbolt/ctl.h index 2f1a1e111110..97cb03b38953 100644 --- a/drivers/thunderbolt/ctl.h +++ b/drivers/thunderbolt/ctl.h @@ -123,8 +123,7 @@ static inline struct tb_cfg_header tb_cfg_make_header(u64 route) return header; } -int tb_cfg_error(struct tb_ctl *ctl, u64 route, u32 port, - enum tb_cfg_error error); +int tb_cfg_ack_plug(struct tb_ctl *ctl, u64 route, u32 port, bool unplug); struct tb_cfg_result tb_cfg_reset(struct tb_ctl *ctl, u64 route, int timeout_msec); struct tb_cfg_result tb_cfg_read_raw(struct tb_ctl *ctl, void *buffer, diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 54085f67810a..e54d0d89a32d 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -768,8 +768,7 @@ static void tb_handle_event(struct tb *tb, enum tb_cfg_pkg_type type, route = tb_cfg_get_route(&pkg->header); - if (tb_cfg_error(tb->ctl, route, pkg->port, - TB_CFG_ERROR_ACK_PLUG_EVENT)) { + if (tb_cfg_ack_plug(tb->ctl, route, pkg->port, pkg->unplug)) { tb_warn(tb, "could not ack plug event on %llx:%x\n", route, pkg->port); } diff --git a/drivers/thunderbolt/tb_msgs.h b/drivers/thunderbolt/tb_msgs.h index 3705057723b6..fc208c567953 100644 --- a/drivers/thunderbolt/tb_msgs.h +++ b/drivers/thunderbolt/tb_msgs.h @@ -67,9 +67,13 @@ struct cfg_error_pkg { u32 zero1:4; u32 port:6; u32 zero2:2; /* Both should be zero, still they are different fields. */ - u32 zero3:16; + u32 zero3:14; + u32 pg:2; } __packed; +#define TB_CFG_ERROR_PG_HOT_PLUG 0x2 +#define TB_CFG_ERROR_PG_HOT_UNPLUG 0x3 + /* TB_CFG_PKG_EVENT */ struct cfg_event_pkg { struct tb_cfg_header header; -- cgit v1.2.3-59-g8ed1b From b04079837b2094f09e145676eec4b9a56ae8a6aa Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 17 Dec 2019 15:33:40 +0300 Subject: thunderbolt: Add initial support for USB4 USB4 is the public specification based on Thunderbolt 3 protocol. There are some differences in register layouts and flows. In addition to PCIe and DP tunneling, USB4 supports tunneling of USB 3.x. USB4 is also backward compatible with Thunderbolt 3 (and older generations but the spec only talks about 3rd generation). USB4 compliant devices can be identified by checking USB4 version field in router configuration space. This patch adds initial support for USB4 compliant hosts and devices which enables following features provided by the existing functionality in the driver: - PCIe tunneling - Display Port tunneling - Host and device NVM firmware upgrade - P2P networking This brings the USB4 support to the same level that we already have for Thunderbolt 1, 2 and 3 devices. Note the spec talks about host and device "routers" but in the driver we still use term "switch" in most places. Both can be used interchangeably. Co-developed-by: Rajmohan Mani Signed-off-by: Rajmohan Mani Signed-off-by: Mika Westerberg Link: https://lore.kernel.org/r/20191217123345.31850-5-mika.westerberg@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/Makefile | 2 +- drivers/thunderbolt/eeprom.c | 53 +++- drivers/thunderbolt/nhi.c | 3 + drivers/thunderbolt/nhi.h | 2 + drivers/thunderbolt/switch.c | 382 ++++++++++++++++------ drivers/thunderbolt/tb.c | 20 +- drivers/thunderbolt/tb.h | 36 +++ drivers/thunderbolt/tb_regs.h | 36 ++- drivers/thunderbolt/tunnel.c | 11 +- drivers/thunderbolt/usb4.c | 724 ++++++++++++++++++++++++++++++++++++++++++ drivers/thunderbolt/xdomain.c | 6 + 11 files changed, 1158 insertions(+), 117 deletions(-) create mode 100644 drivers/thunderbolt/usb4.c diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile index 001187c577bf..c0b2fd73dfbd 100644 --- a/drivers/thunderbolt/Makefile +++ b/drivers/thunderbolt/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-${CONFIG_THUNDERBOLT} := thunderbolt.o thunderbolt-objs := nhi.o nhi_ops.o ctl.o tb.o switch.o cap.o path.o tunnel.o eeprom.o -thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o lc.o +thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o lc.o usb4.o diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c index 540e0105bcc0..921d164b3f35 100644 --- a/drivers/thunderbolt/eeprom.c +++ b/drivers/thunderbolt/eeprom.c @@ -487,6 +487,37 @@ err_free: return ret; } +static int usb4_copy_host_drom(struct tb_switch *sw, u16 *size) +{ + int ret; + + ret = usb4_switch_drom_read(sw, 14, size, sizeof(*size)); + if (ret) + return ret; + + /* Size includes CRC8 + UID + CRC32 */ + *size += 1 + 8 + 4; + sw->drom = kzalloc(*size, GFP_KERNEL); + if (!sw->drom) + return -ENOMEM; + + ret = usb4_switch_drom_read(sw, 0, sw->drom, *size); + if (ret) { + kfree(sw->drom); + sw->drom = NULL; + } + + return ret; +} + +static int tb_drom_read_n(struct tb_switch *sw, u16 offset, u8 *val, + size_t count) +{ + if (tb_switch_is_usb4(sw)) + return usb4_switch_drom_read(sw, offset, val, count); + return tb_eeprom_read_n(sw, offset, val, count); +} + /** * tb_drom_read - copy drom to sw->drom and parse it */ @@ -512,14 +543,26 @@ int tb_drom_read(struct tb_switch *sw) goto parse; /* - * The root switch contains only a dummy drom (header only, - * no entries). Hardcode the configuration here. + * USB4 hosts may support reading DROM through router + * operations. */ - tb_drom_read_uid_only(sw, &sw->uid); + if (tb_switch_is_usb4(sw)) { + usb4_switch_read_uid(sw, &sw->uid); + if (!usb4_copy_host_drom(sw, &size)) + goto parse; + } else { + /* + * The root switch contains only a dummy drom + * (header only, no entries). Hardcode the + * configuration here. + */ + tb_drom_read_uid_only(sw, &sw->uid); + } + return 0; } - res = tb_eeprom_read_n(sw, 14, (u8 *) &size, 2); + res = tb_drom_read_n(sw, 14, (u8 *) &size, 2); if (res) return res; size &= 0x3ff; @@ -533,7 +576,7 @@ int tb_drom_read(struct tb_switch *sw) sw->drom = kzalloc(size, GFP_KERNEL); if (!sw->drom) return -ENOMEM; - res = tb_eeprom_read_n(sw, 0, sw->drom, size); + res = tb_drom_read_n(sw, 0, sw->drom, size); if (res) goto err; diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 641b21b54460..1be491ecbb45 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -1271,6 +1271,9 @@ static struct pci_device_id nhi_ids[] = { { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICL_NHI1), .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + /* Any USB4 compliant host */ + { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_USB4, ~0) }, + { 0,} }; diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h index b7b973949f8e..5d276ee9b38e 100644 --- a/drivers/thunderbolt/nhi.h +++ b/drivers/thunderbolt/nhi.h @@ -74,4 +74,6 @@ extern const struct tb_nhi_ops icl_nhi_ops; #define PCI_DEVICE_ID_INTEL_ICL_NHI1 0x8a0d #define PCI_DEVICE_ID_INTEL_ICL_NHI0 0x8a17 +#define PCI_CLASS_SERIAL_USB_USB4 0x0c0340 + #endif diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 9c72521cb298..c1d5cd7e0631 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -163,10 +163,12 @@ static int nvm_validate_and_write(struct tb_switch *sw) image_size -= hdr_size; } + if (tb_switch_is_usb4(sw)) + return usb4_switch_nvm_write(sw, 0, buf, image_size); return dma_port_flash_write(sw->dma_port, 0, buf, image_size); } -static int nvm_authenticate_host(struct tb_switch *sw) +static int nvm_authenticate_host_dma_port(struct tb_switch *sw) { int ret = 0; @@ -206,7 +208,7 @@ static int nvm_authenticate_host(struct tb_switch *sw) return ret; } -static int nvm_authenticate_device(struct tb_switch *sw) +static int nvm_authenticate_device_dma_port(struct tb_switch *sw) { int ret, retries = 10; @@ -251,6 +253,78 @@ static int nvm_authenticate_device(struct tb_switch *sw) return -ETIMEDOUT; } +static void nvm_authenticate_start_dma_port(struct tb_switch *sw) +{ + struct pci_dev *root_port; + + /* + * During host router NVM upgrade we should not allow root port to + * go into D3cold because some root ports cannot trigger PME + * itself. To be on the safe side keep the root port in D0 during + * the whole upgrade process. + */ + root_port = pci_find_pcie_root_port(sw->tb->nhi->pdev); + if (root_port) + pm_runtime_get_noresume(&root_port->dev); +} + +static void nvm_authenticate_complete_dma_port(struct tb_switch *sw) +{ + struct pci_dev *root_port; + + root_port = pci_find_pcie_root_port(sw->tb->nhi->pdev); + if (root_port) + pm_runtime_put(&root_port->dev); +} + +static inline bool nvm_readable(struct tb_switch *sw) +{ + if (tb_switch_is_usb4(sw)) { + /* + * USB4 devices must support NVM operations but it is + * optional for hosts. Therefore we query the NVM sector + * size here and if it is supported assume NVM + * operations are implemented. + */ + return usb4_switch_nvm_sector_size(sw) > 0; + } + + /* Thunderbolt 2 and 3 devices support NVM through DMA port */ + return !!sw->dma_port; +} + +static inline bool nvm_upgradeable(struct tb_switch *sw) +{ + if (sw->no_nvm_upgrade) + return false; + return nvm_readable(sw); +} + +static inline int nvm_read(struct tb_switch *sw, unsigned int address, + void *buf, size_t size) +{ + if (tb_switch_is_usb4(sw)) + return usb4_switch_nvm_read(sw, address, buf, size); + return dma_port_flash_read(sw->dma_port, address, buf, size); +} + +static int nvm_authenticate(struct tb_switch *sw) +{ + int ret; + + if (tb_switch_is_usb4(sw)) + return usb4_switch_nvm_authenticate(sw); + + if (!tb_route(sw)) { + nvm_authenticate_start_dma_port(sw); + ret = nvm_authenticate_host_dma_port(sw); + } else { + ret = nvm_authenticate_device_dma_port(sw); + } + + return ret; +} + static int tb_switch_nvm_read(void *priv, unsigned int offset, void *val, size_t bytes) { @@ -264,7 +338,7 @@ static int tb_switch_nvm_read(void *priv, unsigned int offset, void *val, goto out; } - ret = dma_port_flash_read(sw->dma_port, offset, val, bytes); + ret = nvm_read(sw, offset, val, bytes); mutex_unlock(&sw->tb->lock); out: @@ -341,9 +415,21 @@ static int tb_switch_nvm_add(struct tb_switch *sw) u32 val; int ret; - if (!sw->dma_port) + if (!nvm_readable(sw)) return 0; + /* + * The NVM format of non-Intel hardware is not known so + * currently restrict NVM upgrade for Intel hardware. We may + * relax this in the future when we learn other NVM formats. + */ + if (sw->config.vendor_id != PCI_VENDOR_ID_INTEL) { + dev_info(&sw->dev, + "NVM format of vendor %#x is not known, disabling NVM upgrade\n", + sw->config.vendor_id); + return 0; + } + nvm = kzalloc(sizeof(*nvm), GFP_KERNEL); if (!nvm) return -ENOMEM; @@ -358,8 +444,7 @@ static int tb_switch_nvm_add(struct tb_switch *sw) if (!sw->safe_mode) { u32 nvm_size, hdr_size; - ret = dma_port_flash_read(sw->dma_port, NVM_FLASH_SIZE, &val, - sizeof(val)); + ret = nvm_read(sw, NVM_FLASH_SIZE, &val, sizeof(val)); if (ret) goto err_ida; @@ -367,8 +452,7 @@ static int tb_switch_nvm_add(struct tb_switch *sw) nvm_size = (SZ_1M << (val & 7)) / 8; nvm_size = (nvm_size - hdr_size) / 2; - ret = dma_port_flash_read(sw->dma_port, NVM_VERSION, &val, - sizeof(val)); + ret = nvm_read(sw, NVM_VERSION, &val, sizeof(val)); if (ret) goto err_ida; @@ -619,6 +703,24 @@ int tb_port_clear_counter(struct tb_port *port, int counter) return tb_port_write(port, zero, TB_CFG_COUNTERS, 3 * counter, 3); } +/** + * tb_port_unlock() - Unlock downstream port + * @port: Port to unlock + * + * Needed for USB4 but can be called for any CIO/USB4 ports. Makes the + * downstream router accessible for CM. + */ +int tb_port_unlock(struct tb_port *port) +{ + if (tb_switch_is_icm(port->sw)) + return 0; + if (!tb_port_is_null(port)) + return -EINVAL; + if (tb_switch_is_usb4(port->sw)) + return usb4_port_unlock(port); + return 0; +} + /** * tb_init_port() - initialize a port * @@ -650,6 +752,10 @@ static int tb_init_port(struct tb_port *port) port->cap_phy = cap; else tb_port_WARN(port, "non switch port without a PHY\n"); + + cap = tb_port_find_cap(port, TB_PORT_CAP_USB4); + if (cap > 0) + port->cap_usb4 = cap; } else if (port->port != 0) { cap = tb_port_find_cap(port, TB_PORT_CAP_ADAP); if (cap > 0) @@ -1088,20 +1194,38 @@ int tb_dp_port_enable(struct tb_port *port, bool enable) /* switch utility functions */ -static void tb_dump_switch(struct tb *tb, struct tb_regs_switch_header *sw) +static const char *tb_switch_generation_name(const struct tb_switch *sw) +{ + switch (sw->generation) { + case 1: + return "Thunderbolt 1"; + case 2: + return "Thunderbolt 2"; + case 3: + return "Thunderbolt 3"; + case 4: + return "USB4"; + default: + return "Unknown"; + } +} + +static void tb_dump_switch(const struct tb *tb, const struct tb_switch *sw) { - tb_dbg(tb, " Switch: %x:%x (Revision: %d, TB Version: %d)\n", - sw->vendor_id, sw->device_id, sw->revision, - sw->thunderbolt_version); - tb_dbg(tb, " Max Port Number: %d\n", sw->max_port_number); + const struct tb_regs_switch_header *regs = &sw->config; + + tb_dbg(tb, " %s Switch: %x:%x (Revision: %d, TB Version: %d)\n", + tb_switch_generation_name(sw), regs->vendor_id, regs->device_id, + regs->revision, regs->thunderbolt_version); + tb_dbg(tb, " Max Port Number: %d\n", regs->max_port_number); tb_dbg(tb, " Config:\n"); tb_dbg(tb, " Upstream Port Number: %d Depth: %d Route String: %#llx Enabled: %d, PlugEventsDelay: %dms\n", - sw->upstream_port_number, sw->depth, - (((u64) sw->route_hi) << 32) | sw->route_lo, - sw->enabled, sw->plug_events_delay); + regs->upstream_port_number, regs->depth, + (((u64) regs->route_hi) << 32) | regs->route_lo, + regs->enabled, regs->plug_events_delay); tb_dbg(tb, " unknown1: %#x unknown4: %#x\n", - sw->__unknown1, sw->__unknown4); + regs->__unknown1, regs->__unknown4); } /** @@ -1148,6 +1272,10 @@ static int tb_plug_events_active(struct tb_switch *sw, bool active) if (res) return res; + /* Plug events are always enabled in USB4 */ + if (tb_switch_is_usb4(sw)) + return 0; + res = tb_sw_read(sw, &data, TB_CFG_SWITCH, sw->cap_plug_events + 1, 1); if (res) return res; @@ -1359,30 +1487,6 @@ static ssize_t lanes_show(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(rx_lanes, 0444, lanes_show, NULL); static DEVICE_ATTR(tx_lanes, 0444, lanes_show, NULL); -static void nvm_authenticate_start(struct tb_switch *sw) -{ - struct pci_dev *root_port; - - /* - * During host router NVM upgrade we should not allow root port to - * go into D3cold because some root ports cannot trigger PME - * itself. To be on the safe side keep the root port in D0 during - * the whole upgrade process. - */ - root_port = pci_find_pcie_root_port(sw->tb->nhi->pdev); - if (root_port) - pm_runtime_get_noresume(&root_port->dev); -} - -static void nvm_authenticate_complete(struct tb_switch *sw) -{ - struct pci_dev *root_port; - - root_port = pci_find_pcie_root_port(sw->tb->nhi->pdev); - if (root_port) - pm_runtime_put(&root_port->dev); -} - static ssize_t nvm_authenticate_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1431,17 +1535,7 @@ static ssize_t nvm_authenticate_store(struct device *dev, goto exit_unlock; sw->nvm->authenticating = true; - - if (!tb_route(sw)) { - /* - * Keep root port from suspending as long as the - * NVM upgrade process is running. - */ - nvm_authenticate_start(sw); - ret = nvm_authenticate_host(sw); - } else { - ret = nvm_authenticate_device(sw); - } + ret = nvm_authenticate(sw); } exit_unlock: @@ -1556,11 +1650,11 @@ static umode_t switch_attr_is_visible(struct kobject *kobj, return attr->mode; return 0; } else if (attr == &dev_attr_nvm_authenticate.attr) { - if (sw->dma_port && !sw->no_nvm_upgrade) + if (nvm_upgradeable(sw)) return attr->mode; return 0; } else if (attr == &dev_attr_nvm_version.attr) { - if (sw->dma_port) + if (nvm_readable(sw)) return attr->mode; return 0; } else if (attr == &dev_attr_boot.attr) { @@ -1672,6 +1766,9 @@ static int tb_switch_get_generation(struct tb_switch *sw) return 3; default: + if (tb_switch_is_usb4(sw)) + return 4; + /* * For unknown switches assume generation to be 1 to be * on the safe side. @@ -1682,6 +1779,19 @@ static int tb_switch_get_generation(struct tb_switch *sw) } } +static bool tb_switch_exceeds_max_depth(const struct tb_switch *sw, int depth) +{ + int max_depth; + + if (tb_switch_is_usb4(sw) || + (sw->tb->root_switch && tb_switch_is_usb4(sw->tb->root_switch))) + max_depth = USB4_SWITCH_MAX_DEPTH; + else + max_depth = TB_SWITCH_MAX_DEPTH; + + return depth > max_depth; +} + /** * tb_switch_alloc() - allocate a switch * @tb: Pointer to the owning domain @@ -1703,10 +1813,16 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, struct device *parent, int upstream_port; int i, ret, depth; - /* Make sure we do not exceed maximum topology limit */ + /* Unlock the downstream port so we can access the switch below */ + if (route) { + struct tb_switch *parent_sw = tb_to_switch(parent); + struct tb_port *down; + + down = tb_port_at(route, parent_sw); + tb_port_unlock(down); + } + depth = tb_route_length(route); - if (depth > TB_SWITCH_MAX_DEPTH) - return ERR_PTR(-EADDRNOTAVAIL); upstream_port = tb_cfg_get_upstream_port(tb->ctl, route); if (upstream_port < 0) @@ -1721,8 +1837,10 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, struct device *parent, if (ret) goto err_free_sw_ports; + sw->generation = tb_switch_get_generation(sw); + tb_dbg(tb, "current switch config:\n"); - tb_dump_switch(tb, &sw->config); + tb_dump_switch(tb, sw); /* configure switch */ sw->config.upstream_port_number = upstream_port; @@ -1731,6 +1849,10 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, struct device *parent, sw->config.route_lo = lower_32_bits(route); sw->config.enabled = 0; + /* Make sure we do not exceed maximum topology limit */ + if (tb_switch_exceeds_max_depth(sw, depth)) + return ERR_PTR(-EADDRNOTAVAIL); + /* initialize ports */ sw->ports = kcalloc(sw->config.max_port_number + 1, sizeof(*sw->ports), GFP_KERNEL); @@ -1745,14 +1867,9 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, struct device *parent, sw->ports[i].port = i; } - sw->generation = tb_switch_get_generation(sw); - ret = tb_switch_find_vse_cap(sw, TB_VSE_CAP_PLUG_EVENTS); - if (ret < 0) { - tb_sw_warn(sw, "cannot find TB_VSE_CAP_PLUG_EVENTS aborting\n"); - goto err_free_sw_ports; - } - sw->cap_plug_events = ret; + if (ret > 0) + sw->cap_plug_events = ret; ret = tb_switch_find_vse_cap(sw, TB_VSE_CAP_LINK_CONTROLLER); if (ret > 0) @@ -1823,7 +1940,8 @@ tb_switch_alloc_safe_mode(struct tb *tb, struct device *parent, u64 route) * * Call this function before the switch is added to the system. It will * upload configuration to the switch and makes it available for the - * connection manager to use. + * connection manager to use. Can be called to the switch again after + * resume from low power states to re-initialize it. * * Return: %0 in case of success and negative errno in case of failure */ @@ -1834,21 +1952,50 @@ int tb_switch_configure(struct tb_switch *sw) int ret; route = tb_route(sw); - tb_dbg(tb, "initializing Switch at %#llx (depth: %d, up port: %d)\n", - route, tb_route_length(route), sw->config.upstream_port_number); - if (sw->config.vendor_id != PCI_VENDOR_ID_INTEL) - tb_sw_warn(sw, "unknown switch vendor id %#x\n", - sw->config.vendor_id); + tb_dbg(tb, "%s Switch at %#llx (depth: %d, up port: %d)\n", + sw->config.enabled ? "restoring " : "initializing", route, + tb_route_length(route), sw->config.upstream_port_number); sw->config.enabled = 1; - /* upload configuration */ - ret = tb_sw_write(sw, 1 + (u32 *)&sw->config, TB_CFG_SWITCH, 1, 3); - if (ret) - return ret; + if (tb_switch_is_usb4(sw)) { + /* + * For USB4 devices, we need to program the CM version + * accordingly so that it knows to expose all the + * additional capabilities. + */ + sw->config.cmuv = USB4_VERSION_1_0; + + /* Enumerate the switch */ + ret = tb_sw_write(sw, (u32 *)&sw->config + 1, TB_CFG_SWITCH, + ROUTER_CS_1, 4); + if (ret) + return ret; - ret = tb_lc_configure_link(sw); + ret = usb4_switch_setup(sw); + if (ret) + return ret; + + ret = usb4_switch_configure_link(sw); + } else { + if (sw->config.vendor_id != PCI_VENDOR_ID_INTEL) + tb_sw_warn(sw, "unknown switch vendor id %#x\n", + sw->config.vendor_id); + + if (!sw->cap_plug_events) { + tb_sw_warn(sw, "cannot find TB_VSE_CAP_PLUG_EVENTS aborting\n"); + return -ENODEV; + } + + /* Enumerate the switch */ + ret = tb_sw_write(sw, (u32 *)&sw->config + 1, TB_CFG_SWITCH, + ROUTER_CS_1, 3); + if (ret) + return ret; + + ret = tb_lc_configure_link(sw); + } if (ret) return ret; @@ -1857,18 +2004,32 @@ int tb_switch_configure(struct tb_switch *sw) static int tb_switch_set_uuid(struct tb_switch *sw) { + bool uid = false; u32 uuid[4]; int ret; if (sw->uuid) return 0; - /* - * The newer controllers include fused UUID as part of link - * controller specific registers - */ - ret = tb_lc_read_uuid(sw, uuid); - if (ret) { + if (tb_switch_is_usb4(sw)) { + ret = usb4_switch_read_uid(sw, &sw->uid); + if (ret) + return ret; + uid = true; + } else { + /* + * The newer controllers include fused UUID as part of + * link controller specific registers + */ + ret = tb_lc_read_uuid(sw, uuid); + if (ret) { + if (ret != -EINVAL) + return ret; + uid = true; + } + } + + if (uid) { /* * ICM generates UUID based on UID and fills the upper * two words with ones. This is not strictly following @@ -1935,7 +2096,7 @@ static int tb_switch_add_dma_port(struct tb_switch *sw) nvm_get_auth_status(sw, &status); if (status) { if (!tb_route(sw)) - nvm_authenticate_complete(sw); + nvm_authenticate_complete_dma_port(sw); return 0; } @@ -1950,7 +2111,7 @@ static int tb_switch_add_dma_port(struct tb_switch *sw) /* Now we can allow root port to suspend again */ if (!tb_route(sw)) - nvm_authenticate_complete(sw); + nvm_authenticate_complete_dma_port(sw); if (status) { tb_sw_info(sw, "switch flash authentication failed\n"); @@ -2004,6 +2165,8 @@ static bool tb_switch_lane_bonding_possible(struct tb_switch *sw) if (!up->dual_link_port || !up->dual_link_port->remote) return false; + if (tb_switch_is_usb4(sw)) + return usb4_switch_lane_bonding_possible(sw); return tb_lc_lane_bonding_possible(sw); } @@ -2240,7 +2403,11 @@ void tb_switch_remove(struct tb_switch *sw) if (!sw->is_unplugged) tb_plug_events_active(sw, false); - tb_lc_unconfigure_link(sw); + + if (tb_switch_is_usb4(sw)) + usb4_switch_unconfigure_link(sw); + else + tb_lc_unconfigure_link(sw); tb_switch_nvm_remove(sw); @@ -2298,7 +2465,10 @@ int tb_switch_resume(struct tb_switch *sw) return err; } - err = tb_drom_read_uid_only(sw, &uid); + if (tb_switch_is_usb4(sw)) + err = usb4_switch_read_uid(sw, &uid); + else + err = tb_drom_read_uid_only(sw, &uid); if (err) { tb_sw_warn(sw, "uid read failed\n"); return err; @@ -2311,16 +2481,7 @@ int tb_switch_resume(struct tb_switch *sw) } } - /* upload configuration */ - err = tb_sw_write(sw, 1 + (u32 *) &sw->config, TB_CFG_SWITCH, 1, 3); - if (err) - return err; - - err = tb_lc_configure_link(sw); - if (err) - return err; - - err = tb_plug_events_active(sw, true); + err = tb_switch_configure(sw); if (err) return err; @@ -2336,8 +2497,14 @@ int tb_switch_resume(struct tb_switch *sw) tb_sw_set_unplugged(port->remote->sw); else if (port->xdomain) port->xdomain->is_unplugged = true; - } else if (tb_port_has_remote(port)) { - if (tb_switch_resume(port->remote->sw)) { + } else if (tb_port_has_remote(port) || port->xdomain) { + /* + * Always unlock the port so the downstream + * switch/domain is accessible. + */ + if (tb_port_unlock(port)) + tb_port_warn(port, "failed to unlock port\n"); + if (port->remote && tb_switch_resume(port->remote->sw)) { tb_port_warn(port, "lost during suspend, disconnecting\n"); tb_sw_set_unplugged(port->remote->sw); @@ -2361,7 +2528,10 @@ void tb_switch_suspend(struct tb_switch *sw) tb_switch_suspend(port->remote->sw); } - tb_lc_set_sleep(sw); + if (tb_switch_is_usb4(sw)) + usb4_switch_set_sleep(sw); + else + tb_lc_set_sleep(sw); } /** @@ -2374,6 +2544,8 @@ void tb_switch_suspend(struct tb_switch *sw) */ bool tb_switch_query_dp_resource(struct tb_switch *sw, struct tb_port *in) { + if (tb_switch_is_usb4(sw)) + return usb4_switch_query_dp_resource(sw, in); return tb_lc_dp_sink_query(sw, in); } @@ -2388,6 +2560,8 @@ bool tb_switch_query_dp_resource(struct tb_switch *sw, struct tb_port *in) */ int tb_switch_alloc_dp_resource(struct tb_switch *sw, struct tb_port *in) { + if (tb_switch_is_usb4(sw)) + return usb4_switch_alloc_dp_resource(sw, in); return tb_lc_dp_sink_alloc(sw, in); } @@ -2401,10 +2575,16 @@ int tb_switch_alloc_dp_resource(struct tb_switch *sw, struct tb_port *in) */ void tb_switch_dealloc_dp_resource(struct tb_switch *sw, struct tb_port *in) { - if (tb_lc_dp_sink_dealloc(sw, in)) { + int ret; + + if (tb_switch_is_usb4(sw)) + ret = usb4_switch_dealloc_dp_resource(sw, in); + else + ret = tb_lc_dp_sink_dealloc(sw, in); + + if (ret) tb_sw_warn(sw, "failed to de-allocate DP resource for port %d\n", in->port); - } } struct tb_sw_lookup { diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index e54d0d89a32d..6b99dcd1790c 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -365,12 +365,15 @@ static struct tb_port *tb_find_unused_port(struct tb_switch *sw, static struct tb_port *tb_find_pcie_down(struct tb_switch *sw, const struct tb_port *port) { + struct tb_port *down = NULL; + /* * To keep plugging devices consistently in the same PCIe - * hierarchy, do mapping here for root switch downstream PCIe - * ports. + * hierarchy, do mapping here for switch downstream PCIe ports. */ - if (!tb_route(sw)) { + if (tb_switch_is_usb4(sw)) { + down = usb4_switch_map_pcie_down(sw, port); + } else if (!tb_route(sw)) { int phy_port = tb_phy_port_from_link(port->port); int index; @@ -391,12 +394,17 @@ static struct tb_port *tb_find_pcie_down(struct tb_switch *sw, /* Validate the hard-coding */ if (WARN_ON(index > sw->config.max_port_number)) goto out; - if (WARN_ON(!tb_port_is_pcie_down(&sw->ports[index]))) + + down = &sw->ports[index]; + } + + if (down) { + if (WARN_ON(!tb_port_is_pcie_down(down))) goto out; - if (WARN_ON(tb_pci_port_is_enabled(&sw->ports[index]))) + if (WARN_ON(tb_pci_port_is_enabled(down))) goto out; - return &sw->ports[index]; + return down; } out: diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index ade1c7c77db1..0158f0e9858c 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -44,6 +44,7 @@ struct tb_switch_nvm { #define TB_SWITCH_KEY_SIZE 32 #define TB_SWITCH_MAX_DEPTH 6 +#define USB4_SWITCH_MAX_DEPTH 5 /** * struct tb_switch - a thunderbolt switch @@ -129,6 +130,7 @@ struct tb_switch { * @xdomain: Remote host (%NULL if not connected) * @cap_phy: Offset, zero if not found * @cap_adap: Offset of the adapter specific capability (%0 if not present) + * @cap_usb4: Offset to the USB4 port capability (%0 if not present) * @port: Port number on switch * @disabled: Disabled by eeprom * @bonded: true if the port is bonded (two lanes combined as one) @@ -146,6 +148,7 @@ struct tb_port { struct tb_xdomain *xdomain; int cap_phy; int cap_adap; + int cap_usb4; u8 port; bool disabled; bool bonded; @@ -637,6 +640,17 @@ static inline bool tb_switch_is_titan_ridge(const struct tb_switch *sw) } } +/** + * tb_switch_is_usb4() - Is the switch USB4 compliant + * @sw: Switch to check + * + * Returns true if the @sw is USB4 compliant router, false otherwise. + */ +static inline bool tb_switch_is_usb4(const struct tb_switch *sw) +{ + return sw->config.thunderbolt_version == USB4_VERSION_1_0; +} + /** * tb_switch_is_icm() - Is the switch handled by ICM firmware * @sw: Switch to check @@ -662,6 +676,7 @@ int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged); int tb_port_add_nfc_credits(struct tb_port *port, int credits); int tb_port_set_initial_credits(struct tb_port *port, u32 credits); int tb_port_clear_counter(struct tb_port *port, int counter); +int tb_port_unlock(struct tb_port *port); int tb_port_alloc_in_hopid(struct tb_port *port, int hopid, int max_hopid); void tb_port_release_in_hopid(struct tb_port *port, int hopid); int tb_port_alloc_out_hopid(struct tb_port *port, int hopid, int max_hopid); @@ -736,4 +751,25 @@ void tb_xdomain_remove(struct tb_xdomain *xd); struct tb_xdomain *tb_xdomain_find_by_link_depth(struct tb *tb, u8 link, u8 depth); +int usb4_switch_setup(struct tb_switch *sw); +int usb4_switch_read_uid(struct tb_switch *sw, u64 *uid); +int usb4_switch_drom_read(struct tb_switch *sw, unsigned int address, void *buf, + size_t size); +int usb4_switch_configure_link(struct tb_switch *sw); +void usb4_switch_unconfigure_link(struct tb_switch *sw); +bool usb4_switch_lane_bonding_possible(struct tb_switch *sw); +int usb4_switch_set_sleep(struct tb_switch *sw); +int usb4_switch_nvm_sector_size(struct tb_switch *sw); +int usb4_switch_nvm_read(struct tb_switch *sw, unsigned int address, void *buf, + size_t size); +int usb4_switch_nvm_write(struct tb_switch *sw, unsigned int address, + const void *buf, size_t size); +int usb4_switch_nvm_authenticate(struct tb_switch *sw); +bool usb4_switch_query_dp_resource(struct tb_switch *sw, struct tb_port *in); +int usb4_switch_alloc_dp_resource(struct tb_switch *sw, struct tb_port *in); +int usb4_switch_dealloc_dp_resource(struct tb_switch *sw, struct tb_port *in); +struct tb_port *usb4_switch_map_pcie_down(struct tb_switch *sw, + const struct tb_port *port); + +int usb4_port_unlock(struct tb_port *port); #endif diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index 7ee45b73c7f7..47f73f992412 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -41,6 +41,7 @@ enum tb_port_cap { TB_PORT_CAP_TIME1 = 0x03, TB_PORT_CAP_ADAP = 0x04, TB_PORT_CAP_VSE = 0x05, + TB_PORT_CAP_USB4 = 0x06, }; enum tb_port_state { @@ -164,10 +165,36 @@ struct tb_regs_switch_header { * milliseconds. Writing 0x00 is interpreted * as 255ms. */ - u32 __unknown4:16; + u32 cmuv:8; + u32 __unknown4:8; u32 thunderbolt_version:8; } __packed; +/* USB4 version 1.0 */ +#define USB4_VERSION_1_0 0x20 + +#define ROUTER_CS_1 0x01 +#define ROUTER_CS_4 0x04 +#define ROUTER_CS_5 0x05 +#define ROUTER_CS_5_SLP BIT(0) +#define ROUTER_CS_5_C3S BIT(23) +#define ROUTER_CS_5_PTO BIT(24) +#define ROUTER_CS_5_HCO BIT(26) +#define ROUTER_CS_5_CV BIT(31) +#define ROUTER_CS_6 0x06 +#define ROUTER_CS_6_SLPR BIT(0) +#define ROUTER_CS_6_TNS BIT(1) +#define ROUTER_CS_6_HCI BIT(18) +#define ROUTER_CS_6_CR BIT(25) +#define ROUTER_CS_7 0x07 +#define ROUTER_CS_9 0x09 +#define ROUTER_CS_25 0x19 +#define ROUTER_CS_26 0x1a +#define ROUTER_CS_26_STATUS_MASK GENMASK(29, 24) +#define ROUTER_CS_26_STATUS_SHIFT 24 +#define ROUTER_CS_26_ONS BIT(30) +#define ROUTER_CS_26_OV BIT(31) + enum tb_port_type { TB_TYPE_INACTIVE = 0x000000, TB_TYPE_PORT = 0x000001, @@ -216,6 +243,7 @@ struct tb_regs_port_header { #define ADP_CS_4_NFC_BUFFERS_MASK GENMASK(9, 0) #define ADP_CS_4_TOTAL_BUFFERS_MASK GENMASK(29, 20) #define ADP_CS_4_TOTAL_BUFFERS_SHIFT 20 +#define ADP_CS_4_LCK BIT(31) #define ADP_CS_5 0x05 #define ADP_CS_5_LCA_MASK GENMASK(28, 22) #define ADP_CS_5_LCA_SHIFT 22 @@ -237,6 +265,12 @@ struct tb_regs_port_header { #define LANE_ADP_CS_1_CURRENT_WIDTH_MASK GENMASK(25, 20) #define LANE_ADP_CS_1_CURRENT_WIDTH_SHIFT 20 +/* USB4 port registers */ +#define PORT_CS_18 0x12 +#define PORT_CS_18_BE BIT(8) +#define PORT_CS_19 0x13 +#define PORT_CS_19_PC BIT(3) + /* Display Port adapter registers */ #define ADP_DP_CS_0 0x00 #define ADP_DP_CS_0_VIDEO_HOPID_MASK GENMASK(26, 16) diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 0d3463c4e24a..21d266a76b7d 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -243,6 +243,12 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up, return tunnel; } +static bool tb_dp_is_usb4(const struct tb_switch *sw) +{ + /* Titan Ridge DP adapters need the same treatment as USB4 */ + return tb_switch_is_usb4(sw) || tb_switch_is_titan_ridge(sw); +} + static int tb_dp_cm_handshake(struct tb_port *in, struct tb_port *out) { int timeout = 10; @@ -250,8 +256,7 @@ static int tb_dp_cm_handshake(struct tb_port *in, struct tb_port *out) int ret; /* Both ends need to support this */ - if (!tb_switch_is_titan_ridge(in->sw) || - !tb_switch_is_titan_ridge(out->sw)) + if (!tb_dp_is_usb4(in->sw) || !tb_dp_is_usb4(out->sw)) return 0; ret = tb_port_read(out, &val, TB_CFG_PORT, @@ -531,7 +536,7 @@ static int tb_dp_consumed_bandwidth(struct tb_tunnel *tunnel) u32 val, rate = 0, lanes = 0; int ret; - if (tb_switch_is_titan_ridge(sw)) { + if (tb_dp_is_usb4(sw)) { int timeout = 10; /* diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c new file mode 100644 index 000000000000..b84c74346d2b --- /dev/null +++ b/drivers/thunderbolt/usb4.c @@ -0,0 +1,724 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * USB4 specific functionality + * + * Copyright (C) 2019, Intel Corporation + * Authors: Mika Westerberg + * Rajmohan Mani + */ + +#include +#include + +#include "tb.h" + +#define USB4_DATA_DWORDS 16 +#define USB4_DATA_RETRIES 3 + +enum usb4_switch_op { + USB4_SWITCH_OP_QUERY_DP_RESOURCE = 0x10, + USB4_SWITCH_OP_ALLOC_DP_RESOURCE = 0x11, + USB4_SWITCH_OP_DEALLOC_DP_RESOURCE = 0x12, + USB4_SWITCH_OP_NVM_WRITE = 0x20, + USB4_SWITCH_OP_NVM_AUTH = 0x21, + USB4_SWITCH_OP_NVM_READ = 0x22, + USB4_SWITCH_OP_NVM_SET_OFFSET = 0x23, + USB4_SWITCH_OP_DROM_READ = 0x24, + USB4_SWITCH_OP_NVM_SECTOR_SIZE = 0x25, +}; + +#define USB4_NVM_READ_OFFSET_MASK GENMASK(23, 2) +#define USB4_NVM_READ_OFFSET_SHIFT 2 +#define USB4_NVM_READ_LENGTH_MASK GENMASK(27, 24) +#define USB4_NVM_READ_LENGTH_SHIFT 24 + +#define USB4_NVM_SET_OFFSET_MASK USB4_NVM_READ_OFFSET_MASK +#define USB4_NVM_SET_OFFSET_SHIFT USB4_NVM_READ_OFFSET_SHIFT + +#define USB4_DROM_ADDRESS_MASK GENMASK(14, 2) +#define USB4_DROM_ADDRESS_SHIFT 2 +#define USB4_DROM_SIZE_MASK GENMASK(19, 15) +#define USB4_DROM_SIZE_SHIFT 15 + +#define USB4_NVM_SECTOR_SIZE_MASK GENMASK(23, 0) + +typedef int (*read_block_fn)(struct tb_switch *, unsigned int, void *, size_t); +typedef int (*write_block_fn)(struct tb_switch *, const void *, size_t); + +static int usb4_switch_wait_for_bit(struct tb_switch *sw, u32 offset, u32 bit, + u32 value, int timeout_msec) +{ + ktime_t timeout = ktime_add_ms(ktime_get(), timeout_msec); + + do { + u32 val; + int ret; + + ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, offset, 1); + if (ret) + return ret; + + if ((val & bit) == value) + return 0; + + usleep_range(50, 100); + } while (ktime_before(ktime_get(), timeout)); + + return -ETIMEDOUT; +} + +static int usb4_switch_op_read_data(struct tb_switch *sw, void *data, + size_t dwords) +{ + if (dwords > USB4_DATA_DWORDS) + return -EINVAL; + + return tb_sw_read(sw, data, TB_CFG_SWITCH, ROUTER_CS_9, dwords); +} + +static int usb4_switch_op_write_data(struct tb_switch *sw, const void *data, + size_t dwords) +{ + if (dwords > USB4_DATA_DWORDS) + return -EINVAL; + + return tb_sw_write(sw, data, TB_CFG_SWITCH, ROUTER_CS_9, dwords); +} + +static int usb4_switch_op_read_metadata(struct tb_switch *sw, u32 *metadata) +{ + return tb_sw_read(sw, metadata, TB_CFG_SWITCH, ROUTER_CS_25, 1); +} + +static int usb4_switch_op_write_metadata(struct tb_switch *sw, u32 metadata) +{ + return tb_sw_write(sw, &metadata, TB_CFG_SWITCH, ROUTER_CS_25, 1); +} + +static int usb4_switch_do_read_data(struct tb_switch *sw, u16 address, + void *buf, size_t size, read_block_fn read_block) +{ + unsigned int retries = USB4_DATA_RETRIES; + unsigned int offset; + + offset = address & 3; + address = address & ~3; + + do { + size_t nbytes = min_t(size_t, size, USB4_DATA_DWORDS * 4); + unsigned int dwaddress, dwords; + u8 data[USB4_DATA_DWORDS * 4]; + int ret; + + dwaddress = address / 4; + dwords = ALIGN(nbytes, 4) / 4; + + ret = read_block(sw, dwaddress, data, dwords); + if (ret) { + if (ret == -ETIMEDOUT) { + if (retries--) + continue; + ret = -EIO; + } + return ret; + } + + memcpy(buf, data + offset, nbytes); + + size -= nbytes; + address += nbytes; + buf += nbytes; + } while (size > 0); + + return 0; +} + +static int usb4_switch_do_write_data(struct tb_switch *sw, u16 address, + const void *buf, size_t size, write_block_fn write_next_block) +{ + unsigned int retries = USB4_DATA_RETRIES; + unsigned int offset; + + offset = address & 3; + address = address & ~3; + + do { + u32 nbytes = min_t(u32, size, USB4_DATA_DWORDS * 4); + u8 data[USB4_DATA_DWORDS * 4]; + int ret; + + memcpy(data + offset, buf, nbytes); + + ret = write_next_block(sw, data, nbytes / 4); + if (ret) { + if (ret == -ETIMEDOUT) { + if (retries--) + continue; + ret = -EIO; + } + return ret; + } + + size -= nbytes; + address += nbytes; + buf += nbytes; + } while (size > 0); + + return 0; +} + +static int usb4_switch_op(struct tb_switch *sw, u16 opcode, u8 *status) +{ + u32 val; + int ret; + + val = opcode | ROUTER_CS_26_OV; + ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, ROUTER_CS_26, 1); + if (ret) + return ret; + + ret = usb4_switch_wait_for_bit(sw, ROUTER_CS_26, ROUTER_CS_26_OV, 0, 500); + if (ret) + return ret; + + ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, ROUTER_CS_26, 1); + if (val & ROUTER_CS_26_ONS) + return -EOPNOTSUPP; + + *status = (val & ROUTER_CS_26_STATUS_MASK) >> ROUTER_CS_26_STATUS_SHIFT; + return 0; +} + +/** + * usb4_switch_setup() - Additional setup for USB4 device + * @sw: USB4 router to setup + * + * USB4 routers need additional settings in order to enable all the + * tunneling. This function enables USB and PCIe tunneling if it can be + * enabled (e.g the parent switch also supports them). If USB tunneling + * is not available for some reason (like that there is Thunderbolt 3 + * switch upstream) then the internal xHCI controller is enabled + * instead. + */ +int usb4_switch_setup(struct tb_switch *sw) +{ + struct tb_switch *parent; + bool tbt3, xhci; + u32 val = 0; + int ret; + + if (!tb_route(sw)) + return 0; + + ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, ROUTER_CS_6, 1); + if (ret) + return ret; + + xhci = val & ROUTER_CS_6_HCI; + tbt3 = !(val & ROUTER_CS_6_TNS); + + tb_sw_dbg(sw, "TBT3 support: %s, xHCI: %s\n", + tbt3 ? "yes" : "no", xhci ? "yes" : "no"); + + ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, ROUTER_CS_5, 1); + if (ret) + return ret; + + parent = tb_switch_parent(sw); + + /* Only enable PCIe tunneling if the parent router supports it */ + if (tb_switch_find_port(parent, TB_TYPE_PCIE_DOWN)) { + val |= ROUTER_CS_5_PTO; + /* xHCI can be enabled if PCIe tunneling is supported */ + if (xhci & ROUTER_CS_6_HCI) + val |= ROUTER_CS_5_HCO; + } + + /* TBT3 supported by the CM */ + val |= ROUTER_CS_5_C3S; + /* Tunneling configuration is ready now */ + val |= ROUTER_CS_5_CV; + + ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, ROUTER_CS_5, 1); + if (ret) + return ret; + + return usb4_switch_wait_for_bit(sw, ROUTER_CS_6, ROUTER_CS_6_CR, + ROUTER_CS_6_CR, 50); +} + +/** + * usb4_switch_read_uid() - Read UID from USB4 router + * @sw: USB4 router + * + * Reads 64-bit UID from USB4 router config space. + */ +int usb4_switch_read_uid(struct tb_switch *sw, u64 *uid) +{ + return tb_sw_read(sw, uid, TB_CFG_SWITCH, ROUTER_CS_7, 2); +} + +static int usb4_switch_drom_read_block(struct tb_switch *sw, + unsigned int dwaddress, void *buf, + size_t dwords) +{ + u8 status = 0; + u32 metadata; + int ret; + + metadata = (dwords << USB4_DROM_SIZE_SHIFT) & USB4_DROM_SIZE_MASK; + metadata |= (dwaddress << USB4_DROM_ADDRESS_SHIFT) & + USB4_DROM_ADDRESS_MASK; + + ret = usb4_switch_op_write_metadata(sw, metadata); + if (ret) + return ret; + + ret = usb4_switch_op(sw, USB4_SWITCH_OP_DROM_READ, &status); + if (ret) + return ret; + + if (status) + return -EIO; + + return usb4_switch_op_read_data(sw, buf, dwords); +} + +/** + * usb4_switch_drom_read() - Read arbitrary bytes from USB4 router DROM + * @sw: USB4 router + * + * Uses USB4 router operations to read router DROM. For devices this + * should always work but for hosts it may return %-EOPNOTSUPP in which + * case the host router does not have DROM. + */ +int usb4_switch_drom_read(struct tb_switch *sw, unsigned int address, void *buf, + size_t size) +{ + return usb4_switch_do_read_data(sw, address, buf, size, + usb4_switch_drom_read_block); +} + +static int usb4_set_port_configured(struct tb_port *port, bool configured) +{ + int ret; + u32 val; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_usb4 + PORT_CS_19, 1); + if (ret) + return ret; + + if (configured) + val |= PORT_CS_19_PC; + else + val &= ~PORT_CS_19_PC; + + return tb_port_write(port, &val, TB_CFG_PORT, + port->cap_usb4 + PORT_CS_19, 1); +} + +/** + * usb4_switch_configure_link() - Set upstream USB4 link configured + * @sw: USB4 router + * + * Sets the upstream USB4 link to be configured for power management + * purposes. + */ +int usb4_switch_configure_link(struct tb_switch *sw) +{ + struct tb_port *up; + + if (!tb_route(sw)) + return 0; + + up = tb_upstream_port(sw); + return usb4_set_port_configured(up, true); +} + +/** + * usb4_switch_unconfigure_link() - Un-set upstream USB4 link configuration + * @sw: USB4 router + * + * Reverse of usb4_switch_configure_link(). + */ +void usb4_switch_unconfigure_link(struct tb_switch *sw) +{ + struct tb_port *up; + + if (sw->is_unplugged || !tb_route(sw)) + return; + + up = tb_upstream_port(sw); + usb4_set_port_configured(up, false); +} + +/** + * usb4_switch_lane_bonding_possible() - Are conditions met for lane bonding + * @sw: USB4 router + * + * Checks whether conditions are met so that lane bonding can be + * established with the upstream router. Call only for device routers. + */ +bool usb4_switch_lane_bonding_possible(struct tb_switch *sw) +{ + struct tb_port *up; + int ret; + u32 val; + + up = tb_upstream_port(sw); + ret = tb_port_read(up, &val, TB_CFG_PORT, up->cap_usb4 + PORT_CS_18, 1); + if (ret) + return false; + + return !!(val & PORT_CS_18_BE); +} + +/** + * usb4_switch_set_sleep() - Prepare the router to enter sleep + * @sw: USB4 router + * + * Enables wakes and sets sleep bit for the router. Returns when the + * router sleep ready bit has been asserted. + */ +int usb4_switch_set_sleep(struct tb_switch *sw) +{ + int ret; + u32 val; + + /* Set sleep bit and wait for sleep ready to be asserted */ + ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, ROUTER_CS_5, 1); + if (ret) + return ret; + + val |= ROUTER_CS_5_SLP; + + ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, ROUTER_CS_5, 1); + if (ret) + return ret; + + return usb4_switch_wait_for_bit(sw, ROUTER_CS_6, ROUTER_CS_6_SLPR, + ROUTER_CS_6_SLPR, 500); +} + +/** + * usb4_switch_nvm_sector_size() - Return router NVM sector size + * @sw: USB4 router + * + * If the router supports NVM operations this function returns the NVM + * sector size in bytes. If NVM operations are not supported returns + * %-EOPNOTSUPP. + */ +int usb4_switch_nvm_sector_size(struct tb_switch *sw) +{ + u32 metadata; + u8 status; + int ret; + + ret = usb4_switch_op(sw, USB4_SWITCH_OP_NVM_SECTOR_SIZE, &status); + if (ret) + return ret; + + if (status) + return status == 0x2 ? -EOPNOTSUPP : -EIO; + + ret = usb4_switch_op_read_metadata(sw, &metadata); + if (ret) + return ret; + + return metadata & USB4_NVM_SECTOR_SIZE_MASK; +} + +static int usb4_switch_nvm_read_block(struct tb_switch *sw, + unsigned int dwaddress, void *buf, size_t dwords) +{ + u8 status = 0; + u32 metadata; + int ret; + + metadata = (dwords << USB4_NVM_READ_LENGTH_SHIFT) & + USB4_NVM_READ_LENGTH_MASK; + metadata |= (dwaddress << USB4_NVM_READ_OFFSET_SHIFT) & + USB4_NVM_READ_OFFSET_MASK; + + ret = usb4_switch_op_write_metadata(sw, metadata); + if (ret) + return ret; + + ret = usb4_switch_op(sw, USB4_SWITCH_OP_NVM_READ, &status); + if (ret) + return ret; + + if (status) + return -EIO; + + return usb4_switch_op_read_data(sw, buf, dwords); +} + +/** + * usb4_switch_nvm_read() - Read arbitrary bytes from router NVM + * @sw: USB4 router + * @address: Starting address in bytes + * @buf: Read data is placed here + * @size: How many bytes to read + * + * Reads NVM contents of the router. If NVM is not supported returns + * %-EOPNOTSUPP. + */ +int usb4_switch_nvm_read(struct tb_switch *sw, unsigned int address, void *buf, + size_t size) +{ + return usb4_switch_do_read_data(sw, address, buf, size, + usb4_switch_nvm_read_block); +} + +static int usb4_switch_nvm_set_offset(struct tb_switch *sw, + unsigned int address) +{ + u32 metadata, dwaddress; + u8 status = 0; + int ret; + + dwaddress = address / 4; + metadata = (dwaddress << USB4_NVM_SET_OFFSET_SHIFT) & + USB4_NVM_SET_OFFSET_MASK; + + ret = usb4_switch_op_write_metadata(sw, metadata); + if (ret) + return ret; + + ret = usb4_switch_op(sw, USB4_SWITCH_OP_NVM_SET_OFFSET, &status); + if (ret) + return ret; + + return status ? -EIO : 0; +} + +static int usb4_switch_nvm_write_next_block(struct tb_switch *sw, + const void *buf, size_t dwords) +{ + u8 status; + int ret; + + ret = usb4_switch_op_write_data(sw, buf, dwords); + if (ret) + return ret; + + ret = usb4_switch_op(sw, USB4_SWITCH_OP_NVM_WRITE, &status); + if (ret) + return ret; + + return status ? -EIO : 0; +} + +/** + * usb4_switch_nvm_write() - Write to the router NVM + * @sw: USB4 router + * @address: Start address where to write in bytes + * @buf: Pointer to the data to write + * @size: Size of @buf in bytes + * + * Writes @buf to the router NVM using USB4 router operations. If NVM + * write is not supported returns %-EOPNOTSUPP. + */ +int usb4_switch_nvm_write(struct tb_switch *sw, unsigned int address, + const void *buf, size_t size) +{ + int ret; + + ret = usb4_switch_nvm_set_offset(sw, address); + if (ret) + return ret; + + return usb4_switch_do_write_data(sw, address, buf, size, + usb4_switch_nvm_write_next_block); +} + +/** + * usb4_switch_nvm_authenticate() - Authenticate new NVM + * @sw: USB4 router + * + * After the new NVM has been written via usb4_switch_nvm_write(), this + * function triggers NVM authentication process. If the authentication + * is successful the router is power cycled and the new NVM starts + * running. In case of failure returns negative errno. + */ +int usb4_switch_nvm_authenticate(struct tb_switch *sw) +{ + u8 status = 0; + int ret; + + ret = usb4_switch_op(sw, USB4_SWITCH_OP_NVM_AUTH, &status); + if (ret) + return ret; + + switch (status) { + case 0x0: + tb_sw_dbg(sw, "NVM authentication successful\n"); + return 0; + case 0x1: + return -EINVAL; + case 0x2: + return -EAGAIN; + case 0x3: + return -EOPNOTSUPP; + default: + return -EIO; + } +} + +/** + * usb4_switch_query_dp_resource() - Query availability of DP IN resource + * @sw: USB4 router + * @in: DP IN adapter + * + * For DP tunneling this function can be used to query availability of + * DP IN resource. Returns true if the resource is available for DP + * tunneling, false otherwise. + */ +bool usb4_switch_query_dp_resource(struct tb_switch *sw, struct tb_port *in) +{ + u8 status; + int ret; + + ret = usb4_switch_op_write_metadata(sw, in->port); + if (ret) + return false; + + ret = usb4_switch_op(sw, USB4_SWITCH_OP_QUERY_DP_RESOURCE, &status); + /* + * If DP resource allocation is not supported assume it is + * always available. + */ + if (ret == -EOPNOTSUPP) + return true; + else if (ret) + return false; + + return !status; +} + +/** + * usb4_switch_alloc_dp_resource() - Allocate DP IN resource + * @sw: USB4 router + * @in: DP IN adapter + * + * Allocates DP IN resource for DP tunneling using USB4 router + * operations. If the resource was allocated returns %0. Otherwise + * returns negative errno, in particular %-EBUSY if the resource is + * already allocated. + */ +int usb4_switch_alloc_dp_resource(struct tb_switch *sw, struct tb_port *in) +{ + u8 status; + int ret; + + ret = usb4_switch_op_write_metadata(sw, in->port); + if (ret) + return ret; + + ret = usb4_switch_op(sw, USB4_SWITCH_OP_ALLOC_DP_RESOURCE, &status); + if (ret == -EOPNOTSUPP) + return 0; + else if (ret) + return ret; + + return status ? -EBUSY : 0; +} + +/** + * usb4_switch_dealloc_dp_resource() - Releases allocated DP IN resource + * @sw: USB4 router + * @in: DP IN adapter + * + * Releases the previously allocated DP IN resource. + */ +int usb4_switch_dealloc_dp_resource(struct tb_switch *sw, struct tb_port *in) +{ + u8 status; + int ret; + + ret = usb4_switch_op_write_metadata(sw, in->port); + if (ret) + return ret; + + ret = usb4_switch_op(sw, USB4_SWITCH_OP_DEALLOC_DP_RESOURCE, &status); + if (ret == -EOPNOTSUPP) + return 0; + else if (ret) + return ret; + + return status ? -EIO : 0; +} + +static int usb4_port_idx(const struct tb_switch *sw, const struct tb_port *port) +{ + struct tb_port *p; + int usb4_idx = 0; + + /* Assume port is primary */ + tb_switch_for_each_port(sw, p) { + if (!tb_port_is_null(p)) + continue; + if (tb_is_upstream_port(p)) + continue; + if (!p->link_nr) { + if (p == port) + break; + usb4_idx++; + } + } + + return usb4_idx; +} + +/** + * usb4_switch_map_pcie_down() - Map USB4 port to a PCIe downstream adapter + * @sw: USB4 router + * @port: USB4 port + * + * USB4 routers have direct mapping between USB4 ports and PCIe + * downstream adapters where the PCIe topology is extended. This + * function returns the corresponding downstream PCIe adapter or %NULL + * if no such mapping was possible. + */ +struct tb_port *usb4_switch_map_pcie_down(struct tb_switch *sw, + const struct tb_port *port) +{ + int usb4_idx = usb4_port_idx(sw, port); + struct tb_port *p; + int pcie_idx = 0; + + /* Find PCIe down port matching usb4_port */ + tb_switch_for_each_port(sw, p) { + if (!tb_port_is_pcie_down(p)) + continue; + + if (pcie_idx == usb4_idx && !tb_pci_port_is_enabled(p)) + return p; + + pcie_idx++; + } + + return NULL; +} + +/** + * usb4_port_unlock() - Unlock USB4 downstream port + * @port: USB4 port to unlock + * + * Unlocks USB4 downstream port so that the connection manager can + * access the router below this port. + */ +int usb4_port_unlock(struct tb_port *port) +{ + int ret; + u32 val; + + ret = tb_port_read(port, &val, TB_CFG_PORT, ADP_CS_4, 1); + if (ret) + return ret; + + val &= ~ADP_CS_4_LCK; + return tb_port_write(port, &val, TB_CFG_PORT, ADP_CS_4, 1); +} diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 880d784398a3..053f918e00e8 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -1220,7 +1220,13 @@ struct tb_xdomain *tb_xdomain_alloc(struct tb *tb, struct device *parent, u64 route, const uuid_t *local_uuid, const uuid_t *remote_uuid) { + struct tb_switch *parent_sw = tb_to_switch(parent); struct tb_xdomain *xd; + struct tb_port *down; + + /* Make sure the downstream domain is accessible */ + down = tb_port_at(route, parent_sw); + tb_port_unlock(down); xd = kzalloc(sizeof(*xd), GFP_KERNEL); if (!xd) -- cgit v1.2.3-59-g8ed1b From 690ac0d20d4022bb3c7d84e0e3760eb40aa8028d Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 17 Dec 2019 15:33:41 +0300 Subject: thunderbolt: Update Kconfig entries to USB4 Since the driver now supports USB4 which is the standard going forward, update the Kconfig entry to mention this and rename the entry from CONFIG_THUNDERBOLT to CONFIG_USB4 instead to help people to find the correct option if they want to enable USB4. Also do the same for Thunderbolt network driver. Signed-off-by: Mika Westerberg Cc: David S. Miller Link: https://lore.kernel.org/r/20191217123345.31850-6-mika.westerberg@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/Makefile | 2 +- drivers/net/Kconfig | 10 +++++----- drivers/net/Makefile | 2 +- drivers/thunderbolt/Kconfig | 11 ++++++----- drivers/thunderbolt/Makefile | 2 +- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/Makefile b/drivers/Makefile index aaef17cc6512..31cf17dee252 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -171,7 +171,7 @@ obj-$(CONFIG_POWERCAP) += powercap/ obj-$(CONFIG_MCB) += mcb/ obj-$(CONFIG_PERF_EVENTS) += perf/ obj-$(CONFIG_RAS) += ras/ -obj-$(CONFIG_THUNDERBOLT) += thunderbolt/ +obj-$(CONFIG_USB4) += thunderbolt/ obj-$(CONFIG_CORESIGHT) += hwtracing/coresight/ obj-y += hwtracing/intel_th/ obj-$(CONFIG_STM) += hwtracing/stm/ diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index d02f12a5254e..d1c84d47779d 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -489,12 +489,12 @@ config FUJITSU_ES This driver provides support for Extended Socket network device on Extended Partitioning of FUJITSU PRIMEQUEST 2000 E2 series. -config THUNDERBOLT_NET - tristate "Networking over Thunderbolt cable" - depends on THUNDERBOLT && INET +config USB4_NET + tristate "Networking over USB4 and Thunderbolt cables" + depends on USB4 && INET help - Select this if you want to create network between two - computers over a Thunderbolt cable. The driver supports Apple + Select this if you want to create network between two computers + over a USB4 and Thunderbolt cables. The driver supports Apple ThunderboltIP protocol and allows communication with any host supporting the same protocol including Windows and macOS. diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 0d3ba056cda3..29e83e9f545e 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -76,6 +76,6 @@ obj-$(CONFIG_NTB_NETDEV) += ntb_netdev.o obj-$(CONFIG_FUJITSU_ES) += fjes/ thunderbolt-net-y += thunderbolt.o -obj-$(CONFIG_THUNDERBOLT_NET) += thunderbolt-net.o +obj-$(CONFIG_USB4_NET) += thunderbolt-net.o obj-$(CONFIG_NETDEVSIM) += netdevsim/ obj-$(CONFIG_NET_FAILOVER) += net_failover.o diff --git a/drivers/thunderbolt/Kconfig b/drivers/thunderbolt/Kconfig index fd9adca898ff..1eb757e8df3b 100644 --- a/drivers/thunderbolt/Kconfig +++ b/drivers/thunderbolt/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only -menuconfig THUNDERBOLT - tristate "Thunderbolt support" +menuconfig USB4 + tristate "Unified support for USB4 and Thunderbolt" depends on PCI depends on X86 || COMPILE_TEST select APPLE_PROPERTIES if EFI_STUB && X86 @@ -9,9 +9,10 @@ menuconfig THUNDERBOLT select CRYPTO_HASH select NVMEM help - Thunderbolt Controller driver. This driver is required if you - want to hotplug Thunderbolt devices on Apple hardware or on PCs - with Intel Falcon Ridge or newer. + USB4 and Thunderbolt driver. USB4 is the public speficiation + based on Thunderbolt 3 protocol. This driver is required if + you want to hotplug Thunderbolt and USB4 compliant devices on + Apple hardware or on PCs with Intel Falcon Ridge or newer. To compile this driver a module, choose M here. The module will be called thunderbolt. diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile index c0b2fd73dfbd..102e9529ee66 100644 --- a/drivers/thunderbolt/Makefile +++ b/drivers/thunderbolt/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-${CONFIG_THUNDERBOLT} := thunderbolt.o +obj-${CONFIG_USB4} := thunderbolt.o thunderbolt-objs := nhi.o nhi_ops.o ctl.o tb.o switch.o cap.o path.o tunnel.o eeprom.o thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o lc.o usb4.o -- cgit v1.2.3-59-g8ed1b From aa43a9dcf7fcb71f34689bc63cdfb3464d2bebbb Mon Sep 17 00:00:00 2001 From: Rajmohan Mani Date: Tue, 17 Dec 2019 15:33:42 +0300 Subject: thunderbolt: Make tb_switch_find_cap() available to other files We need to find switch capabilities in order to implement TMU support so make it available to other files as well. Signed-off-by: Rajmohan Mani Signed-off-by: Mika Westerberg Link: https://lore.kernel.org/r/20191217123345.31850-7-mika.westerberg@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/cap.c | 11 ++++++++++- drivers/thunderbolt/tb.h | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/thunderbolt/cap.c b/drivers/thunderbolt/cap.c index fdd77bb4628d..19db6cdc5b70 100644 --- a/drivers/thunderbolt/cap.c +++ b/drivers/thunderbolt/cap.c @@ -113,7 +113,16 @@ int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap) return ret; } -static int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap) +/** + * tb_switch_find_cap() - Find switch capability + * @sw Switch to find the capability for + * @cap: Capability to look + * + * Returns offset to start of capability or %-ENOENT if no such + * capability was found. Negative errno is returned if there was an + * error. + */ +int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap) { int offset = sw->config.first_cap_offset; diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 0158f0e9858c..28dd0e0b1579 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -685,6 +685,7 @@ struct tb_port *tb_next_port_on_path(struct tb_port *start, struct tb_port *end, struct tb_port *prev); int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec); +int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap); int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap); bool tb_port_is_enabled(struct tb_port *port); -- cgit v1.2.3-59-g8ed1b From cf29b9afb121494a7aa12dae6eebf81347e0313b Mon Sep 17 00:00:00 2001 From: Rajmohan Mani Date: Tue, 17 Dec 2019 15:33:43 +0300 Subject: thunderbolt: Add support for Time Management Unit Time Management Unit (TMU) is included in each USB4 router. It is used to synchronize time across the USB4 fabric. By default when USB4 router is plugged to the domain, its TMU is turned off. This differs from Thunderbolt (1, 2 and 3) devices whose TMU is by default configured to bi-directional HiFi mode. Since time synchronization is needed for proper Display Port tunneling this means we need to configure the TMU on USB4 compliant devices. The USB4 spec allows some flexibility on how the TMU can be configured. This makes it possible to enable link power management states (CLx) in certain topologies, where for example DP tunneling is not used. TMU can also be re-configured dynamicaly depending on types of tunnels created over the USB4 fabric. In this patch we simply configure the TMU to be in bi-directional HiFi mode. This way we can tunnel any kind of traffic without need to perform complex steps to re-configure the domain dynamically. We can add more fine-grained TMU configuration later on when we start enabling CLx states. Signed-off-by: Rajmohan Mani Co-developed-by: Mika Westerberg Signed-off-by: Mika Westerberg Link: https://lore.kernel.org/r/20191217123345.31850-8-mika.westerberg@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/Makefile | 2 +- drivers/thunderbolt/switch.c | 4 + drivers/thunderbolt/tb.c | 28 +++ drivers/thunderbolt/tb.h | 47 ++++++ drivers/thunderbolt/tb_regs.h | 20 +++ drivers/thunderbolt/tmu.c | 383 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 483 insertions(+), 1 deletion(-) create mode 100644 drivers/thunderbolt/tmu.c diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile index 102e9529ee66..eae28dd45250 100644 --- a/drivers/thunderbolt/Makefile +++ b/drivers/thunderbolt/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-${CONFIG_USB4} := thunderbolt.o thunderbolt-objs := nhi.o nhi_ops.o ctl.o tb.o switch.o cap.o path.o tunnel.o eeprom.o -thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o lc.o usb4.o +thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o lc.o tmu.o usb4.o diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index c1d5cd7e0631..82f45780dc81 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -2338,6 +2338,10 @@ int tb_switch_add(struct tb_switch *sw) ret = tb_switch_update_link_attributes(sw); if (ret) return ret; + + ret = tb_switch_tmu_init(sw); + if (ret) + return ret; } ret = device_add(&sw->dev); diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 6b99dcd1790c..e446624dd3e7 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -158,6 +158,25 @@ static void tb_scan_xdomain(struct tb_port *port) } } +static int tb_enable_tmu(struct tb_switch *sw) +{ + int ret; + + /* If it is already enabled in correct mode, don't touch it */ + if (tb_switch_tmu_is_enabled(sw)) + return 0; + + ret = tb_switch_tmu_disable(sw); + if (ret) + return ret; + + ret = tb_switch_tmu_post_time(sw); + if (ret) + return ret; + + return tb_switch_tmu_enable(sw); +} + static void tb_scan_port(struct tb_port *port); /** @@ -257,6 +276,9 @@ static void tb_scan_port(struct tb_port *port) if (tb_switch_lane_bonding_enable(sw)) tb_sw_warn(sw, "failed to enable lane bonding\n"); + if (tb_enable_tmu(sw)) + tb_sw_warn(sw, "failed to enable TMU\n"); + tb_scan_switch(sw); } @@ -709,6 +731,7 @@ static void tb_handle_hotplug(struct work_struct *work) tb_sw_set_unplugged(port->remote->sw); tb_free_invalid_tunnels(tb); tb_remove_dp_resources(port->remote->sw); + tb_switch_tmu_disable(port->remote->sw); tb_switch_lane_bonding_disable(port->remote->sw); tb_switch_remove(port->remote->sw); port->remote = NULL; @@ -855,6 +878,8 @@ static int tb_start(struct tb *tb) return ret; } + /* Enable TMU if it is off */ + tb_switch_tmu_enable(tb->root_switch); /* Full scan to discover devices added before the driver was loaded. */ tb_scan_switch(tb->root_switch); /* Find out tunnels created by the boot firmware */ @@ -886,6 +911,9 @@ static void tb_restore_children(struct tb_switch *sw) { struct tb_port *port; + if (tb_enable_tmu(sw)) + tb_sw_warn(sw, "failed to restore TMU configuration\n"); + tb_switch_for_each_port(sw, port) { if (!tb_port_has_remote(port)) continue; diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 28dd0e0b1579..63ffb3cbdefe 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -46,6 +46,38 @@ struct tb_switch_nvm { #define TB_SWITCH_MAX_DEPTH 6 #define USB4_SWITCH_MAX_DEPTH 5 +/** + * enum tb_switch_tmu_rate - TMU refresh rate + * @TB_SWITCH_TMU_RATE_OFF: %0 (Disable Time Sync handshake) + * @TB_SWITCH_TMU_RATE_HIFI: %16 us time interval between successive + * transmission of the Delay Request TSNOS + * (Time Sync Notification Ordered Set) on a Link + * @TB_SWITCH_TMU_RATE_NORMAL: %1 ms time interval between successive + * transmission of the Delay Request TSNOS on + * a Link + */ +enum tb_switch_tmu_rate { + TB_SWITCH_TMU_RATE_OFF = 0, + TB_SWITCH_TMU_RATE_HIFI = 16, + TB_SWITCH_TMU_RATE_NORMAL = 1000, +}; + +/** + * struct tb_switch_tmu - Structure holding switch TMU configuration + * @cap: Offset to the TMU capability (%0 if not found) + * @has_ucap: Does the switch support uni-directional mode + * @rate: TMU refresh rate related to upstream switch. In case of root + * switch this holds the domain rate. + * @unidirectional: Is the TMU in uni-directional or bi-directional mode + * related to upstream switch. Don't case for root switch. + */ +struct tb_switch_tmu { + int cap; + bool has_ucap; + enum tb_switch_tmu_rate rate; + bool unidirectional; +}; + /** * struct tb_switch - a thunderbolt switch * @dev: Device for the switch @@ -55,6 +87,7 @@ struct tb_switch_nvm { * mailbox this will hold the pointer to that (%NULL * otherwise). If set it also means the switch has * upgradeable NVM. + * @tmu: The switch TMU configuration * @tb: Pointer to the domain the switch belongs to * @uid: Unique ID of the switch * @uuid: UUID of the switch (or %NULL if not supported) @@ -93,6 +126,7 @@ struct tb_switch { struct tb_regs_switch_header config; struct tb_port *ports; struct tb_dma_port *dma_port; + struct tb_switch_tmu tmu; struct tb *tb; u64 uid; uuid_t *uuid; @@ -129,6 +163,7 @@ struct tb_switch { * @remote: Remote port (%NULL if not connected) * @xdomain: Remote host (%NULL if not connected) * @cap_phy: Offset, zero if not found + * @cap_tmu: Offset of the adapter specific TMU capability (%0 if not present) * @cap_adap: Offset of the adapter specific capability (%0 if not present) * @cap_usb4: Offset to the USB4 port capability (%0 if not present) * @port: Port number on switch @@ -147,6 +182,7 @@ struct tb_port { struct tb_port *remote; struct tb_xdomain *xdomain; int cap_phy; + int cap_tmu; int cap_adap; int cap_usb4; u8 port; @@ -672,6 +708,17 @@ bool tb_switch_query_dp_resource(struct tb_switch *sw, struct tb_port *in); int tb_switch_alloc_dp_resource(struct tb_switch *sw, struct tb_port *in); void tb_switch_dealloc_dp_resource(struct tb_switch *sw, struct tb_port *in); +int tb_switch_tmu_init(struct tb_switch *sw); +int tb_switch_tmu_post_time(struct tb_switch *sw); +int tb_switch_tmu_disable(struct tb_switch *sw); +int tb_switch_tmu_enable(struct tb_switch *sw); + +static inline bool tb_switch_tmu_is_enabled(const struct tb_switch *sw) +{ + return sw->tmu.rate == TB_SWITCH_TMU_RATE_HIFI && + !sw->tmu.unidirectional; +} + int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged); int tb_port_add_nfc_credits(struct tb_port *port, int credits); int tb_port_set_initial_credits(struct tb_port *port, u32 credits); diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index 47f73f992412..ec1a5d1f7c94 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -26,6 +26,7 @@ #define TB_MAX_CONFIG_RW_LENGTH 60 enum tb_switch_cap { + TB_SWITCH_CAP_TMU = 0x03, TB_SWITCH_CAP_VSE = 0x05, }; @@ -195,6 +196,21 @@ struct tb_regs_switch_header { #define ROUTER_CS_26_ONS BIT(30) #define ROUTER_CS_26_OV BIT(31) +/* Router TMU configuration */ +#define TMU_RTR_CS_0 0x00 +#define TMU_RTR_CS_0_TD BIT(27) +#define TMU_RTR_CS_0_UCAP BIT(30) +#define TMU_RTR_CS_1 0x01 +#define TMU_RTR_CS_1_LOCAL_TIME_NS_MASK GENMASK(31, 16) +#define TMU_RTR_CS_1_LOCAL_TIME_NS_SHIFT 16 +#define TMU_RTR_CS_2 0x02 +#define TMU_RTR_CS_3 0x03 +#define TMU_RTR_CS_3_LOCAL_TIME_NS_MASK GENMASK(15, 0) +#define TMU_RTR_CS_3_TS_PACKET_INTERVAL_MASK GENMASK(31, 16) +#define TMU_RTR_CS_3_TS_PACKET_INTERVAL_SHIFT 16 +#define TMU_RTR_CS_22 0x16 +#define TMU_RTR_CS_24 0x18 + enum tb_port_type { TB_TYPE_INACTIVE = 0x000000, TB_TYPE_PORT = 0x000001, @@ -248,6 +264,10 @@ struct tb_regs_port_header { #define ADP_CS_5_LCA_MASK GENMASK(28, 22) #define ADP_CS_5_LCA_SHIFT 22 +/* TMU adapter registers */ +#define TMU_ADP_CS_3 0x03 +#define TMU_ADP_CS_3_UDM BIT(29) + /* Lane adapter registers */ #define LANE_ADP_CS_0 0x00 #define LANE_ADP_CS_0_SUPPORTED_WIDTH_MASK GENMASK(25, 20) diff --git a/drivers/thunderbolt/tmu.c b/drivers/thunderbolt/tmu.c new file mode 100644 index 000000000000..039c42a06000 --- /dev/null +++ b/drivers/thunderbolt/tmu.c @@ -0,0 +1,383 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Thunderbolt Time Management Unit (TMU) support + * + * Copyright (C) 2019, Intel Corporation + * Authors: Mika Westerberg + * Rajmohan Mani + */ + +#include + +#include "tb.h" + +static const char *tb_switch_tmu_mode_name(const struct tb_switch *sw) +{ + bool root_switch = !tb_route(sw); + + switch (sw->tmu.rate) { + case TB_SWITCH_TMU_RATE_OFF: + return "off"; + + case TB_SWITCH_TMU_RATE_HIFI: + /* Root switch does not have upstream directionality */ + if (root_switch) + return "HiFi"; + if (sw->tmu.unidirectional) + return "uni-directional, HiFi"; + return "bi-directional, HiFi"; + + case TB_SWITCH_TMU_RATE_NORMAL: + if (root_switch) + return "normal"; + return "uni-directional, normal"; + + default: + return "unknown"; + } +} + +static bool tb_switch_tmu_ucap_supported(struct tb_switch *sw) +{ + int ret; + u32 val; + + ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, + sw->tmu.cap + TMU_RTR_CS_0, 1); + if (ret) + return false; + + return !!(val & TMU_RTR_CS_0_UCAP); +} + +static int tb_switch_tmu_rate_read(struct tb_switch *sw) +{ + int ret; + u32 val; + + ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, + sw->tmu.cap + TMU_RTR_CS_3, 1); + if (ret) + return ret; + + val >>= TMU_RTR_CS_3_TS_PACKET_INTERVAL_SHIFT; + return val; +} + +static int tb_switch_tmu_rate_write(struct tb_switch *sw, int rate) +{ + int ret; + u32 val; + + ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, + sw->tmu.cap + TMU_RTR_CS_3, 1); + if (ret) + return ret; + + val &= ~TMU_RTR_CS_3_TS_PACKET_INTERVAL_MASK; + val |= rate << TMU_RTR_CS_3_TS_PACKET_INTERVAL_SHIFT; + + return tb_sw_write(sw, &val, TB_CFG_SWITCH, + sw->tmu.cap + TMU_RTR_CS_3, 1); +} + +static int tb_port_tmu_write(struct tb_port *port, u8 offset, u32 mask, + u32 value) +{ + u32 data; + int ret; + + ret = tb_port_read(port, &data, TB_CFG_PORT, port->cap_tmu + offset, 1); + if (ret) + return ret; + + data &= ~mask; + data |= value; + + return tb_port_write(port, &data, TB_CFG_PORT, + port->cap_tmu + offset, 1); +} + +static int tb_port_tmu_set_unidirectional(struct tb_port *port, + bool unidirectional) +{ + u32 val; + + if (!port->sw->tmu.has_ucap) + return 0; + + val = unidirectional ? TMU_ADP_CS_3_UDM : 0; + return tb_port_tmu_write(port, TMU_ADP_CS_3, TMU_ADP_CS_3_UDM, val); +} + +static inline int tb_port_tmu_unidirectional_disable(struct tb_port *port) +{ + return tb_port_tmu_set_unidirectional(port, false); +} + +static bool tb_port_tmu_is_unidirectional(struct tb_port *port) +{ + int ret; + u32 val; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_tmu + TMU_ADP_CS_3, 1); + if (ret) + return false; + + return val & TMU_ADP_CS_3_UDM; +} + +static int tb_switch_tmu_set_time_disruption(struct tb_switch *sw, bool set) +{ + int ret; + u32 val; + + ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, + sw->tmu.cap + TMU_RTR_CS_0, 1); + if (ret) + return ret; + + if (set) + val |= TMU_RTR_CS_0_TD; + else + val &= ~TMU_RTR_CS_0_TD; + + return tb_sw_write(sw, &val, TB_CFG_SWITCH, + sw->tmu.cap + TMU_RTR_CS_0, 1); +} + +/** + * tb_switch_tmu_init() - Initialize switch TMU structures + * @sw: Switch to initialized + * + * This function must be called before other TMU related functions to + * makes the internal structures are filled in correctly. Does not + * change any hardware configuration. + */ +int tb_switch_tmu_init(struct tb_switch *sw) +{ + struct tb_port *port; + int ret; + + if (tb_switch_is_icm(sw)) + return 0; + + ret = tb_switch_find_cap(sw, TB_SWITCH_CAP_TMU); + if (ret > 0) + sw->tmu.cap = ret; + + tb_switch_for_each_port(sw, port) { + int cap; + + cap = tb_port_find_cap(port, TB_PORT_CAP_TIME1); + if (cap > 0) + port->cap_tmu = cap; + } + + ret = tb_switch_tmu_rate_read(sw); + if (ret < 0) + return ret; + + sw->tmu.rate = ret; + + sw->tmu.has_ucap = tb_switch_tmu_ucap_supported(sw); + if (sw->tmu.has_ucap) { + tb_sw_dbg(sw, "TMU: supports uni-directional mode\n"); + + if (tb_route(sw)) { + struct tb_port *up = tb_upstream_port(sw); + + sw->tmu.unidirectional = + tb_port_tmu_is_unidirectional(up); + } + } else { + sw->tmu.unidirectional = false; + } + + tb_sw_dbg(sw, "TMU: current mode: %s\n", tb_switch_tmu_mode_name(sw)); + return 0; +} + +/** + * tb_switch_tmu_post_time() - Update switch local time + * @sw: Switch whose time to update + * + * Updates switch local time using time posting procedure. + */ +int tb_switch_tmu_post_time(struct tb_switch *sw) +{ + unsigned int post_local_time_offset, post_time_offset; + struct tb_switch *root_switch = sw->tb->root_switch; + u64 hi, mid, lo, local_time, post_time; + int i, ret, retries = 100; + u32 gm_local_time[3]; + + if (!tb_route(sw)) + return 0; + + if (!tb_switch_is_usb4(sw)) + return 0; + + /* Need to be able to read the grand master time */ + if (!root_switch->tmu.cap) + return 0; + + ret = tb_sw_read(root_switch, gm_local_time, TB_CFG_SWITCH, + root_switch->tmu.cap + TMU_RTR_CS_1, + ARRAY_SIZE(gm_local_time)); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(gm_local_time); i++) + tb_sw_dbg(root_switch, "local_time[%d]=0x%08x\n", i, + gm_local_time[i]); + + /* Convert to nanoseconds (drop fractional part) */ + hi = gm_local_time[2] & TMU_RTR_CS_3_LOCAL_TIME_NS_MASK; + mid = gm_local_time[1]; + lo = (gm_local_time[0] & TMU_RTR_CS_1_LOCAL_TIME_NS_MASK) >> + TMU_RTR_CS_1_LOCAL_TIME_NS_SHIFT; + local_time = hi << 48 | mid << 16 | lo; + + /* Tell the switch that time sync is disrupted for a while */ + ret = tb_switch_tmu_set_time_disruption(sw, true); + if (ret) + return ret; + + post_local_time_offset = sw->tmu.cap + TMU_RTR_CS_22; + post_time_offset = sw->tmu.cap + TMU_RTR_CS_24; + + /* + * Write the Grandmaster time to the Post Local Time registers + * of the new switch. + */ + ret = tb_sw_write(sw, &local_time, TB_CFG_SWITCH, + post_local_time_offset, 2); + if (ret) + goto out; + + /* + * Have the new switch update its local time (by writing 1 to + * the post_time registers) and wait for the completion of the + * same (post_time register becomes 0). This means the time has + * been converged properly. + */ + post_time = 1; + + ret = tb_sw_write(sw, &post_time, TB_CFG_SWITCH, post_time_offset, 2); + if (ret) + goto out; + + do { + usleep_range(5, 10); + ret = tb_sw_read(sw, &post_time, TB_CFG_SWITCH, + post_time_offset, 2); + if (ret) + goto out; + } while (--retries && post_time); + + if (!retries) { + ret = -ETIMEDOUT; + goto out; + } + + tb_sw_dbg(sw, "TMU: updated local time to %#llx\n", local_time); + +out: + tb_switch_tmu_set_time_disruption(sw, false); + return ret; +} + +/** + * tb_switch_tmu_disable() - Disable TMU of a switch + * @sw: Switch whose TMU to disable + * + * Turns off TMU of @sw if it is enabled. If not enabled does nothing. + */ +int tb_switch_tmu_disable(struct tb_switch *sw) +{ + int ret; + + if (!tb_switch_is_usb4(sw)) + return 0; + + /* Already disabled? */ + if (sw->tmu.rate == TB_SWITCH_TMU_RATE_OFF) + return 0; + + if (sw->tmu.unidirectional) { + struct tb_switch *parent = tb_switch_parent(sw); + struct tb_port *up, *down; + + up = tb_upstream_port(sw); + down = tb_port_at(tb_route(sw), parent); + + /* The switch may be unplugged so ignore any errors */ + tb_port_tmu_unidirectional_disable(up); + ret = tb_port_tmu_unidirectional_disable(down); + if (ret) + return ret; + } + + tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF); + + sw->tmu.unidirectional = false; + sw->tmu.rate = TB_SWITCH_TMU_RATE_OFF; + + tb_sw_dbg(sw, "TMU: disabled\n"); + return 0; +} + +/** + * tb_switch_tmu_enable() - Enable TMU on a switch + * @sw: Switch whose TMU to enable + * + * Enables TMU of a switch to be in bi-directional, HiFi mode. In this mode + * all tunneling should work. + */ +int tb_switch_tmu_enable(struct tb_switch *sw) +{ + int ret; + + if (!tb_switch_is_usb4(sw)) + return 0; + + if (tb_switch_tmu_is_enabled(sw)) + return 0; + + ret = tb_switch_tmu_set_time_disruption(sw, true); + if (ret) + return ret; + + /* Change mode to bi-directional */ + if (tb_route(sw) && sw->tmu.unidirectional) { + struct tb_switch *parent = tb_switch_parent(sw); + struct tb_port *up, *down; + + up = tb_upstream_port(sw); + down = tb_port_at(tb_route(sw), parent); + + ret = tb_port_tmu_unidirectional_disable(down); + if (ret) + return ret; + + ret = tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_HIFI); + if (ret) + return ret; + + ret = tb_port_tmu_unidirectional_disable(up); + if (ret) + return ret; + } else { + ret = tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_HIFI); + if (ret) + return ret; + } + + sw->tmu.unidirectional = false; + sw->tmu.rate = TB_SWITCH_TMU_RATE_HIFI; + tb_sw_dbg(sw, "TMU: mode set to: %s\n", tb_switch_tmu_mode_name(sw)); + + return tb_switch_tmu_set_time_disruption(sw, false); +} -- cgit v1.2.3-59-g8ed1b From e6f818585713efb29d54f732f41291f75046a2c7 Mon Sep 17 00:00:00 2001 From: Rajmohan Mani Date: Tue, 17 Dec 2019 15:33:44 +0300 Subject: thunderbolt: Add support for USB 3.x tunnels USB4 added a capability to tunnel USB 3.x protocol over the USB4 fabric. USB4 device routers may include integrated SuperSpeed HUB or a function or both. USB tunneling follows PCIe so that the tunnel is created between the parent and the child router from USB3 downstream adapter port to USB3 upstream adapter port over a single USB4 link. This adds support for USB 3.x tunneling and also capability to discover existing USB 3.x tunnels (for example created by connection manager in boot firmware). Signed-off-by: Rajmohan Mani Co-developed-by: Mika Westerberg Signed-off-by: Mika Westerberg Link: https://lore.kernel.org/r/20191217123345.31850-9-mika.westerberg@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/switch.c | 35 ++++++++++ drivers/thunderbolt/tb.c | 154 +++++++++++++++++++++++++++++++++------- drivers/thunderbolt/tb.h | 15 ++++ drivers/thunderbolt/tb_regs.h | 9 ++- drivers/thunderbolt/tunnel.c | 158 +++++++++++++++++++++++++++++++++++++++++- drivers/thunderbolt/tunnel.h | 9 +++ drivers/thunderbolt/usb4.c | 42 ++++++++++- 7 files changed, 395 insertions(+), 27 deletions(-) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 82f45780dc81..3454e6154958 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -1042,11 +1042,46 @@ bool tb_port_is_enabled(struct tb_port *port) case TB_TYPE_DP_HDMI_OUT: return tb_dp_port_is_enabled(port); + case TB_TYPE_USB3_UP: + case TB_TYPE_USB3_DOWN: + return tb_usb3_port_is_enabled(port); + default: return false; } } +/** + * tb_usb3_port_is_enabled() - Is the USB3 adapter port enabled + * @port: USB3 adapter port to check + */ +bool tb_usb3_port_is_enabled(struct tb_port *port) +{ + u32 data; + + if (tb_port_read(port, &data, TB_CFG_PORT, + port->cap_adap + ADP_USB3_CS_0, 1)) + return false; + + return !!(data & ADP_USB3_CS_0_PE); +} + +/** + * tb_usb3_port_enable() - Enable USB3 adapter port + * @port: USB3 adapter port to enable + * @enable: Enable/disable the USB3 adapter + */ +int tb_usb3_port_enable(struct tb_port *port, bool enable) +{ + u32 word = enable ? (ADP_USB3_CS_0_PE | ADP_USB3_CS_0_V) + : ADP_USB3_CS_0_V; + + if (!port->cap_adap) + return -ENXIO; + return tb_port_write(port, &word, TB_CFG_PORT, + port->cap_adap + ADP_USB3_CS_0, 1); +} + /** * tb_pci_port_is_enabled() - Is the PCIe adapter port enabled * @port: PCIe port to check diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index e446624dd3e7..107cd232f486 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -111,6 +111,10 @@ static void tb_discover_tunnels(struct tb_switch *sw) tunnel = tb_tunnel_discover_pci(tb, port); break; + case TB_TYPE_USB3_DOWN: + tunnel = tb_tunnel_discover_usb3(tb, port); + break; + default: break; } @@ -177,6 +181,118 @@ static int tb_enable_tmu(struct tb_switch *sw) return tb_switch_tmu_enable(sw); } +/** + * tb_find_unused_port() - return the first inactive port on @sw + * @sw: Switch to find the port on + * @type: Port type to look for + */ +static struct tb_port *tb_find_unused_port(struct tb_switch *sw, + enum tb_port_type type) +{ + struct tb_port *port; + + tb_switch_for_each_port(sw, port) { + if (tb_is_upstream_port(port)) + continue; + if (port->config.type != type) + continue; + if (!port->cap_adap) + continue; + if (tb_port_is_enabled(port)) + continue; + return port; + } + return NULL; +} + +static struct tb_port *tb_find_usb3_down(struct tb_switch *sw, + const struct tb_port *port) +{ + struct tb_port *down; + + down = usb4_switch_map_usb3_down(sw, port); + if (down) { + if (WARN_ON(!tb_port_is_usb3_down(down))) + goto out; + if (WARN_ON(tb_usb3_port_is_enabled(down))) + goto out; + + return down; + } + +out: + return tb_find_unused_port(sw, TB_TYPE_USB3_DOWN); +} + +static int tb_tunnel_usb3(struct tb *tb, struct tb_switch *sw) +{ + struct tb_switch *parent = tb_switch_parent(sw); + struct tb_port *up, *down, *port; + struct tb_cm *tcm = tb_priv(tb); + struct tb_tunnel *tunnel; + + up = tb_switch_find_port(sw, TB_TYPE_USB3_UP); + if (!up) + return 0; + + /* + * Look up available down port. Since we are chaining it should + * be found right above this switch. + */ + port = tb_port_at(tb_route(sw), parent); + down = tb_find_usb3_down(parent, port); + if (!down) + return 0; + + if (tb_route(parent)) { + struct tb_port *parent_up; + /* + * Check first that the parent switch has its upstream USB3 + * port enabled. Otherwise the chain is not complete and + * there is no point setting up a new tunnel. + */ + parent_up = tb_switch_find_port(parent, TB_TYPE_USB3_UP); + if (!parent_up || !tb_port_is_enabled(parent_up)) + return 0; + } + + tunnel = tb_tunnel_alloc_usb3(tb, up, down); + if (!tunnel) + return -ENOMEM; + + if (tb_tunnel_activate(tunnel)) { + tb_port_info(up, + "USB3 tunnel activation failed, aborting\n"); + tb_tunnel_free(tunnel); + return -EIO; + } + + list_add_tail(&tunnel->list, &tcm->tunnel_list); + return 0; +} + +static int tb_create_usb3_tunnels(struct tb_switch *sw) +{ + struct tb_port *port; + int ret; + + if (tb_route(sw)) { + ret = tb_tunnel_usb3(sw->tb, sw); + if (ret) + return ret; + } + + tb_switch_for_each_port(sw, port) { + if (!tb_port_has_remote(port)) + continue; + ret = tb_create_usb3_tunnels(port->remote->sw); + if (ret) + return ret; + } + + return 0; +} + static void tb_scan_port(struct tb_port *port); /** @@ -279,6 +395,15 @@ static void tb_scan_port(struct tb_port *port) if (tb_enable_tmu(sw)) tb_sw_warn(sw, "failed to enable TMU\n"); + /* + * Create USB 3.x tunnels only when the switch is plugged to the + * domain. This is because we scan the domain also during discovery + * and want to discover existing USB 3.x tunnels before we create + * any new. + */ + if (tcm->hotplug_active && tb_tunnel_usb3(sw->tb, sw)) + tb_sw_warn(sw, "USB3 tunnel creation failed\n"); + tb_scan_switch(sw); } @@ -360,30 +485,6 @@ static void tb_free_unplugged_children(struct tb_switch *sw) } } -/** - * tb_find_unused_port() - return the first inactive port on @sw - * @sw: Switch to find the port on - * @type: Port type to look for - */ -static struct tb_port *tb_find_unused_port(struct tb_switch *sw, - enum tb_port_type type) -{ - struct tb_port *port; - - tb_switch_for_each_port(sw, port) { - if (tb_is_upstream_port(port)) - continue; - if (port->config.type != type) - continue; - if (port->cap_adap) - continue; - if (tb_port_is_enabled(port)) - continue; - return port; - } - return NULL; -} - static struct tb_port *tb_find_pcie_down(struct tb_switch *sw, const struct tb_port *port) { @@ -884,6 +985,11 @@ static int tb_start(struct tb *tb) tb_scan_switch(tb->root_switch); /* Find out tunnels created by the boot firmware */ tb_discover_tunnels(tb->root_switch); + /* + * If the boot firmware did not create USB 3.x tunnels create them + * now for the whole topology. + */ + tb_create_usb3_tunnels(tb->root_switch); /* Add DP IN resources for the root switch */ tb_add_dp_resources(tb->root_switch); /* Make the discovered switches available to the userspace */ diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 63ffb3cbdefe..2eb2bcd3cca3 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -432,6 +432,16 @@ static inline bool tb_port_is_dpout(const struct tb_port *port) return port && port->config.type == TB_TYPE_DP_HDMI_OUT; } +static inline bool tb_port_is_usb3_down(const struct tb_port *port) +{ + return port && port->config.type == TB_TYPE_USB3_DOWN; +} + +static inline bool tb_port_is_usb3_up(const struct tb_port *port) +{ + return port && port->config.type == TB_TYPE_USB3_UP; +} + static inline int tb_sw_read(struct tb_switch *sw, void *buffer, enum tb_cfg_space space, u32 offset, u32 length) { @@ -736,6 +746,9 @@ int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap); int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap); bool tb_port_is_enabled(struct tb_port *port); +bool tb_usb3_port_is_enabled(struct tb_port *port); +int tb_usb3_port_enable(struct tb_port *port, bool enable); + bool tb_pci_port_is_enabled(struct tb_port *port); int tb_pci_port_enable(struct tb_port *port, bool enable); @@ -818,6 +831,8 @@ int usb4_switch_alloc_dp_resource(struct tb_switch *sw, struct tb_port *in); int usb4_switch_dealloc_dp_resource(struct tb_switch *sw, struct tb_port *in); struct tb_port *usb4_switch_map_pcie_down(struct tb_switch *sw, const struct tb_port *port); +struct tb_port *usb4_switch_map_usb3_down(struct tb_switch *sw, + const struct tb_port *port); int usb4_port_unlock(struct tb_port *port); #endif diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index ec1a5d1f7c94..c29c5075525a 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -180,6 +180,7 @@ struct tb_regs_switch_header { #define ROUTER_CS_5_SLP BIT(0) #define ROUTER_CS_5_C3S BIT(23) #define ROUTER_CS_5_PTO BIT(24) +#define ROUTER_CS_5_UTO BIT(25) #define ROUTER_CS_5_HCO BIT(26) #define ROUTER_CS_5_CV BIT(31) #define ROUTER_CS_6 0x06 @@ -221,7 +222,8 @@ enum tb_port_type { TB_TYPE_DP_HDMI_OUT = 0x0e0102, TB_TYPE_PCIE_DOWN = 0x100101, TB_TYPE_PCIE_UP = 0x100102, - /* TB_TYPE_USB = 0x200000, lower order bits are not known */ + TB_TYPE_USB3_DOWN = 0x200101, + TB_TYPE_USB3_UP = 0x200102, }; /* Present on every port in TB_CF_PORT at address zero. */ @@ -331,6 +333,11 @@ struct tb_regs_port_header { #define ADP_PCIE_CS_0 0x00 #define ADP_PCIE_CS_0_PE BIT(31) +/* USB adapter registers */ +#define ADP_USB3_CS_0 0x00 +#define ADP_USB3_CS_0_V BIT(30) +#define ADP_USB3_CS_0_PE BIT(31) + /* Hop register from TB_CFG_HOPS. 8 byte per entry. */ struct tb_regs_hop { /* DWORD 0 */ diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 21d266a76b7d..dbe90bcf4ad4 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -19,6 +19,12 @@ #define TB_PCI_PATH_DOWN 0 #define TB_PCI_PATH_UP 1 +/* USB3 adapters use always HopID of 8 for both directions */ +#define TB_USB3_HOPID 8 + +#define TB_USB3_PATH_DOWN 0 +#define TB_USB3_PATH_UP 1 + /* DP adapters use HopID 8 for AUX and 9 for Video */ #define TB_DP_AUX_TX_HOPID 8 #define TB_DP_AUX_RX_HOPID 8 @@ -31,7 +37,7 @@ #define TB_DMA_PATH_OUT 0 #define TB_DMA_PATH_IN 1 -static const char * const tb_tunnel_names[] = { "PCI", "DP", "DMA" }; +static const char * const tb_tunnel_names[] = { "PCI", "DP", "DMA", "USB3" }; #define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...) \ do { \ @@ -848,6 +854,156 @@ struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi, return tunnel; } +static int tb_usb3_activate(struct tb_tunnel *tunnel, bool activate) +{ + int res; + + res = tb_usb3_port_enable(tunnel->src_port, activate); + if (res) + return res; + + if (tb_port_is_usb3_up(tunnel->dst_port)) + return tb_usb3_port_enable(tunnel->dst_port, activate); + + return 0; +} + +static void tb_usb3_init_path(struct tb_path *path) +{ + path->egress_fc_enable = TB_PATH_SOURCE | TB_PATH_INTERNAL; + path->egress_shared_buffer = TB_PATH_NONE; + path->ingress_fc_enable = TB_PATH_ALL; + path->ingress_shared_buffer = TB_PATH_NONE; + path->priority = 3; + path->weight = 3; + path->drop_packages = 0; + path->nfc_credits = 0; + path->hops[0].initial_credits = 7; + path->hops[1].initial_credits = + tb_initial_credits(path->hops[1].in_port->sw); +} + +/** + * tb_tunnel_discover_usb3() - Discover existing USB3 tunnels + * @tb: Pointer to the domain structure + * @down: USB3 downstream adapter + * + * If @down adapter is active, follows the tunnel to the USB3 upstream + * adapter and back. Returns the discovered tunnel or %NULL if there was + * no tunnel. + */ +struct tb_tunnel *tb_tunnel_discover_usb3(struct tb *tb, struct tb_port *down) +{ + struct tb_tunnel *tunnel; + struct tb_path *path; + + if (!tb_usb3_port_is_enabled(down)) + return NULL; + + tunnel = tb_tunnel_alloc(tb, 2, TB_TUNNEL_USB3); + if (!tunnel) + return NULL; + + tunnel->activate = tb_usb3_activate; + tunnel->src_port = down; + + /* + * Discover both paths even if they are not complete. We will + * clean them up by calling tb_tunnel_deactivate() below in that + * case. + */ + path = tb_path_discover(down, TB_USB3_HOPID, NULL, -1, + &tunnel->dst_port, "USB3 Up"); + if (!path) { + /* Just disable the downstream port */ + tb_usb3_port_enable(down, false); + goto err_free; + } + tunnel->paths[TB_USB3_PATH_UP] = path; + tb_usb3_init_path(tunnel->paths[TB_USB3_PATH_UP]); + + path = tb_path_discover(tunnel->dst_port, -1, down, TB_USB3_HOPID, NULL, + "USB3 Down"); + if (!path) + goto err_deactivate; + tunnel->paths[TB_USB3_PATH_DOWN] = path; + tb_usb3_init_path(tunnel->paths[TB_USB3_PATH_DOWN]); + + /* Validate that the tunnel is complete */ + if (!tb_port_is_usb3_up(tunnel->dst_port)) { + tb_port_warn(tunnel->dst_port, + "path does not end on an USB3 adapter, cleaning up\n"); + goto err_deactivate; + } + + if (down != tunnel->src_port) { + tb_tunnel_warn(tunnel, "path is not complete, cleaning up\n"); + goto err_deactivate; + } + + if (!tb_usb3_port_is_enabled(tunnel->dst_port)) { + tb_tunnel_warn(tunnel, + "tunnel is not fully activated, cleaning up\n"); + goto err_deactivate; + } + + tb_tunnel_dbg(tunnel, "discovered\n"); + return tunnel; + +err_deactivate: + tb_tunnel_deactivate(tunnel); +err_free: + tb_tunnel_free(tunnel); + + return NULL; +} + +/** + * tb_tunnel_alloc_usb3() - allocate a USB3 tunnel + * @tb: Pointer to the domain structure + * @up: USB3 upstream adapter port + * @down: USB3 downstream adapter port + * + * Allocate an USB3 tunnel. The ports must be of type @TB_TYPE_USB3_UP and + * @TB_TYPE_USB3_DOWN. + * + * Return: Returns a tb_tunnel on success or %NULL on failure. + */ +struct tb_tunnel *tb_tunnel_alloc_usb3(struct tb *tb, struct tb_port *up, + struct tb_port *down) +{ + struct tb_tunnel *tunnel; + struct tb_path *path; + + tunnel = tb_tunnel_alloc(tb, 2, TB_TUNNEL_USB3); + if (!tunnel) + return NULL; + + tunnel->activate = tb_usb3_activate; + tunnel->src_port = down; + tunnel->dst_port = up; + + path = tb_path_alloc(tb, down, TB_USB3_HOPID, up, TB_USB3_HOPID, 0, + "USB3 Down"); + if (!path) { + tb_tunnel_free(tunnel); + return NULL; + } + tb_usb3_init_path(path); + tunnel->paths[TB_USB3_PATH_DOWN] = path; + + path = tb_path_alloc(tb, up, TB_USB3_HOPID, down, TB_USB3_HOPID, 0, + "USB3 Up"); + if (!path) { + tb_tunnel_free(tunnel); + return NULL; + } + tb_usb3_init_path(path); + tunnel->paths[TB_USB3_PATH_UP] = path; + + return tunnel; +} + /** * tb_tunnel_free() - free a tunnel * @tunnel: Tunnel to be freed diff --git a/drivers/thunderbolt/tunnel.h b/drivers/thunderbolt/tunnel.h index ba888da005f5..3f5ba93225e7 100644 --- a/drivers/thunderbolt/tunnel.h +++ b/drivers/thunderbolt/tunnel.h @@ -15,6 +15,7 @@ enum tb_tunnel_type { TB_TUNNEL_PCI, TB_TUNNEL_DP, TB_TUNNEL_DMA, + TB_TUNNEL_USB3, }; /** @@ -57,6 +58,9 @@ struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi, struct tb_port *dst, int transmit_ring, int transmit_path, int receive_ring, int receive_path); +struct tb_tunnel *tb_tunnel_discover_usb3(struct tb *tb, struct tb_port *down); +struct tb_tunnel *tb_tunnel_alloc_usb3(struct tb *tb, struct tb_port *up, + struct tb_port *down); void tb_tunnel_free(struct tb_tunnel *tunnel); int tb_tunnel_activate(struct tb_tunnel *tunnel); @@ -82,5 +86,10 @@ static inline bool tb_tunnel_is_dma(const struct tb_tunnel *tunnel) return tunnel->type == TB_TUNNEL_DMA; } +static inline bool tb_tunnel_is_usb3(const struct tb_tunnel *tunnel) +{ + return tunnel->type == TB_TUNNEL_USB3; +} + #endif diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index b84c74346d2b..dbe7ecce4505 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -226,10 +226,19 @@ int usb4_switch_setup(struct tb_switch *sw) parent = tb_switch_parent(sw); + if (tb_switch_find_port(parent, TB_TYPE_USB3_DOWN)) { + val |= ROUTER_CS_5_UTO; + xhci = false; + } + /* Only enable PCIe tunneling if the parent router supports it */ if (tb_switch_find_port(parent, TB_TYPE_PCIE_DOWN)) { val |= ROUTER_CS_5_PTO; - /* xHCI can be enabled if PCIe tunneling is supported */ + /* + * xHCI can be enabled if PCIe tunneling is supported + * and the parent does not have any USB3 dowstream + * adapters (so we cannot do USB 3.x tunneling). + */ if (xhci & ROUTER_CS_6_HCI) val |= ROUTER_CS_5_HCO; } @@ -703,6 +712,37 @@ struct tb_port *usb4_switch_map_pcie_down(struct tb_switch *sw, return NULL; } +/** + * usb4_switch_map_usb3_down() - Map USB4 port to a USB3 downstream adapter + * @sw: USB4 router + * @port: USB4 port + * + * USB4 routers have direct mapping between USB4 ports and USB 3.x + * downstream adapters where the USB 3.x topology is extended. This + * function returns the corresponding downstream USB 3.x adapter or + * %NULL if no such mapping was possible. + */ +struct tb_port *usb4_switch_map_usb3_down(struct tb_switch *sw, + const struct tb_port *port) +{ + int usb4_idx = usb4_port_idx(sw, port); + struct tb_port *p; + int usb_idx = 0; + + /* Find USB3 down port matching usb4_port */ + tb_switch_for_each_port(sw, p) { + if (!tb_port_is_usb3_down(p)) + continue; + + if (usb_idx == usb4_idx && !tb_usb3_port_is_enabled(p)) + return p; + + usb_idx++; + } + + return NULL; +} + /** * usb4_port_unlock() - Unlock USB4 downstream port * @port: USB4 port to unlock -- cgit v1.2.3-59-g8ed1b From ea81896dc98f324ff3fb9b1e74b4915a1beb3296 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 17 Dec 2019 15:33:45 +0300 Subject: thunderbolt: Update documentation with the USB4 information Update user's and administrator's guide to mention USB4, how it relates to Thunderbolt and and how it is supported in Linux. While there add the missing SPDX identifier to the document. Signed-off-by: Mika Westerberg Link: https://lore.kernel.org/r/20191217123345.31850-10-mika.westerberg@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- Documentation/admin-guide/thunderbolt.rst | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/Documentation/admin-guide/thunderbolt.rst b/Documentation/admin-guide/thunderbolt.rst index 898ad78f3cc7..10c4f0ce2ad0 100644 --- a/Documentation/admin-guide/thunderbolt.rst +++ b/Documentation/admin-guide/thunderbolt.rst @@ -1,6 +1,28 @@ -============= - Thunderbolt -============= +.. SPDX-License-Identifier: GPL-2.0 + +====================== + USB4 and Thunderbolt +====================== +USB4 is the public specification based on Thunderbolt 3 protocol with +some differences at the register level among other things. Connection +manager is an entity running on the host router (host controller) +responsible for enumerating routers and establishing tunnels. A +connection manager can be implemented either in firmware or software. +Typically PCs come with a firmware connection manager for Thunderbolt 3 +and early USB4 capable systems. Apple systems on the other hand use +software connection manager and the later USB4 compliant devices follow +the suit. + +The Linux Thunderbolt driver supports both and can detect at runtime which +connection manager implementation is to be used. To be on the safe side the +software connection manager in Linux also advertises security level +``user`` which means PCIe tunneling is disabled by default. The +documentation below applies to both implementations with the exception that +the software connection manager only supports ``user`` security level and +is expected to be accompanied with an IOMMU based DMA protection. + +Security levels and how to use them +----------------------------------- The interface presented here is not meant for end users. Instead there should be a userspace tool that handles all the low-level details, keeps a database of the authorized devices and prompts users for new connections. @@ -18,8 +40,6 @@ This will authorize all devices automatically when they appear. However, keep in mind that this bypasses the security levels and makes the system vulnerable to DMA attacks. -Security levels and how to use them ------------------------------------ Starting with Intel Falcon Ridge Thunderbolt controller there are 4 security levels available. Intel Titan Ridge added one more security level (usbonly). The reason for these is the fact that the connected devices can -- cgit v1.2.3-59-g8ed1b From 795e55999b2fefca006002f0632a88773cce376d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 21 Nov 2019 04:19:47 +0100 Subject: phy: hisilicon: Fix Kconfig indentation Adjust indentation from spaces to tab (+optional two spaces) as in coding style with command like: $ sed -e 's/^ /\t/' -i */Kconfig Signed-off-by: Krzysztof Kozlowski Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/hisilicon/Kconfig | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/phy/hisilicon/Kconfig b/drivers/phy/hisilicon/Kconfig index 534e393a09b3..1c73053bcc98 100644 --- a/drivers/phy/hisilicon/Kconfig +++ b/drivers/phy/hisilicon/Kconfig @@ -33,14 +33,14 @@ config PHY_HISTB_COMBPHY If unsure, say N. config PHY_HISI_INNO_USB2 - tristate "HiSilicon INNO USB2 PHY support" - depends on (ARCH_HISI && ARM64) || COMPILE_TEST - select GENERIC_PHY - select MFD_SYSCON - help - Support for INNO USB2 PHY on HiSilicon SoCs. This Phy supports - USB 1.5Mb/s, USB 12Mb/s, USB 480Mb/s speeds. It supports one - USB host port to accept one USB device. + tristate "HiSilicon INNO USB2 PHY support" + depends on (ARCH_HISI && ARM64) || COMPILE_TEST + select GENERIC_PHY + select MFD_SYSCON + help + Support for INNO USB2 PHY on HiSilicon SoCs. This Phy supports + USB 1.5Mb/s, USB 12Mb/s, USB 480Mb/s speeds. It supports one + USB host port to accept one USB device. config PHY_HIX5HD2_SATA tristate "HIX5HD2 SATA PHY Driver" -- cgit v1.2.3-59-g8ed1b From a3a0641599cd48865d67b098d6d88b5ba66ef860 Mon Sep 17 00:00:00 2001 From: Nishad Kamdar Date: Wed, 27 Nov 2019 19:32:33 +0530 Subject: phy: qcom-qmp: Use the correct style for SPDX License Identifier This patch corrects the SPDX License Identifier style in header files related to PHY drivers for Qualcomm platforms. For C header files Documentation/process/license-rules.rst mandates C-like comments (opposed to C source files where C++ style should be used). Changes made by using a script provided by Joe Perches here: https://lkml.org/lkml/2019/2/7/46. Suggested-by: Joe Perches Signed-off-by: Nishad Kamdar Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/qualcomm/phy-qcom-qmp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index ab6ff9b45a32..90f793c2293d 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2017, The Linux Foundation. All rights reserved. */ -- cgit v1.2.3-59-g8ed1b From 24dbe0aaa0030231d4d35886d3552121d208df69 Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Wed, 4 Dec 2019 19:47:59 +0800 Subject: phy: ti-pipe3: make clk operations symmetric in probe and remove The driver calls clk_prepare_enable in probe but the corresponding clk_disable_unprepare() is in ti_pipe3_disable_clocks(). Move clk_disable_unprepare() to remove to make them symmetric. Signed-off-by: Chuhong Yuan Acked-by: Roger Quadros Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/ti/phy-ti-pipe3.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/drivers/phy/ti/phy-ti-pipe3.c b/drivers/phy/ti/phy-ti-pipe3.c index edd6859afba8..a87946589eb7 100644 --- a/drivers/phy/ti/phy-ti-pipe3.c +++ b/drivers/phy/ti/phy-ti-pipe3.c @@ -850,6 +850,12 @@ static int ti_pipe3_probe(struct platform_device *pdev) static int ti_pipe3_remove(struct platform_device *pdev) { + struct ti_pipe3 *phy = platform_get_drvdata(pdev); + + if (phy->mode == PIPE3_MODE_SATA) { + clk_disable_unprepare(phy->refclk); + phy->sata_refclk_enabled = false; + } pm_runtime_disable(&pdev->dev); return 0; @@ -900,18 +906,8 @@ static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy) { if (!IS_ERR(phy->wkupclk)) clk_disable_unprepare(phy->wkupclk); - if (!IS_ERR(phy->refclk)) { + if (!IS_ERR(phy->refclk)) clk_disable_unprepare(phy->refclk); - /* - * SATA refclk needs an additional disable as we left it - * on in probe to avoid Errata i783 - */ - if (phy->sata_refclk_enabled) { - clk_disable_unprepare(phy->refclk); - phy->sata_refclk_enabled = false; - } - } - if (!IS_ERR(phy->div_clk)) clk_disable_unprepare(phy->div_clk); } -- cgit v1.2.3-59-g8ed1b From cf94ca4993e59a52be5639d37517fa26ca8d7322 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Sat, 21 Dec 2019 07:50:07 +0100 Subject: USB: EHCI: ehci-mv: make the PHY optional We may be using a NOP transceiver and those are treated specially by the USB core and return -ENODEV with devm_phy_get(). Signed-off-by: Lubomir Rintel Acked-by: Alan Stern Link: https://lore.kernel.org/r/20191221065008.266445-3-lkundrak@v3.sk Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-mv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 66ec1fdf9fe7..d476a2516bf6 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -116,7 +116,7 @@ static int mv_ehci_probe(struct platform_device *pdev) ehci_mv->set_vbus = pdata->set_vbus; } - ehci_mv->phy = devm_phy_get(&pdev->dev, "usb"); + ehci_mv->phy = devm_phy_optional_get(&pdev->dev, "usb"); if (IS_ERR(ehci_mv->phy)) { retval = PTR_ERR(ehci_mv->phy); if (retval != -EPROBE_DEFER) -- cgit v1.2.3-59-g8ed1b From 92f983520cb82c407a091bbeabb505fc97419d3a Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Sat, 21 Dec 2019 07:50:08 +0100 Subject: USB: EHCI: ehci-mv: drop pxa_ehci_type and some device IDs This is merely a cleanup. None of these is used anywhere. Signed-off-by: Lubomir Rintel Acked-by: Alan Stern Link: https://lore.kernel.org/r/20191221065008.266445-4-lkundrak@v3.sk Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-mv.c | 6 ++---- include/linux/platform_data/mv_usb.h | 8 -------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index d476a2516bf6..57e97903e7eb 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -246,10 +246,8 @@ static int mv_ehci_remove(struct platform_device *pdev) MODULE_ALIAS("mv-ehci"); static const struct platform_device_id ehci_id_table[] = { - {"pxa-u2oehci", PXA_U2OEHCI}, - {"pxa-sph", PXA_SPH}, - {"mmp3-hsic", MMP3_HSIC}, - {"mmp3-fsic", MMP3_FSIC}, + {"pxa-u2oehci", 0}, + {"pxa-sph", 0}, {}, }; diff --git a/include/linux/platform_data/mv_usb.h b/include/linux/platform_data/mv_usb.h index 5376b6d799d5..20d239c02bf3 100644 --- a/include/linux/platform_data/mv_usb.h +++ b/include/linux/platform_data/mv_usb.h @@ -6,14 +6,6 @@ #ifndef __MV_PLATFORM_USB_H #define __MV_PLATFORM_USB_H -enum pxa_ehci_type { - EHCI_UNDEFINED = 0, - PXA_U2OEHCI, /* pxa 168, 9xx */ - PXA_SPH, /* pxa 168, 9xx SPH */ - MMP3_HSIC, /* mmp3 hsic */ - MMP3_FSIC, /* mmp3 fsic */ -}; - enum { MV_USB_MODE_OTG, MV_USB_MODE_HOST, -- cgit v1.2.3-59-g8ed1b From 7b104f890adea9b7ec554a491ac4d89a0a57ce96 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Sat, 21 Dec 2019 07:50:06 +0100 Subject: USB: EHCI: ehci-mv: add HSIC support Some special dance is needed to initialize the HSIC port. Signed-off-by: Lubomir Rintel Acked-by: Alan Stern Link: https://lore.kernel.org/r/20191221065008.266445-2-lkundrak@v3.sk Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-mv.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 57e97903e7eb..91602e349208 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -67,6 +68,8 @@ static int mv_ehci_reset(struct usb_hcd *hcd) { struct device *dev = hcd->self.controller; struct ehci_hcd_mv *ehci_mv = hcd_to_ehci_hcd_mv(hcd); + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + u32 status; int retval; if (ehci_mv == NULL) { @@ -80,6 +83,14 @@ static int mv_ehci_reset(struct usb_hcd *hcd) if (retval) dev_err(dev, "ehci_setup failed %d\n", retval); + if (of_usb_get_phy_mode(dev->of_node) == USBPHY_INTERFACE_MODE_HSIC) { + status = ehci_readl(ehci, &ehci->regs->port_status[0]); + status |= PORT_TEST_FORCE; + ehci_writel(ehci, status, &ehci->regs->port_status[0]); + status &= ~PORT_TEST_FORCE; + ehci_writel(ehci, status, &ehci->regs->port_status[0]); + } + return retval; } -- cgit v1.2.3-59-g8ed1b From 8e1a20096bfbc1fc05f68abcb8cf20fc07dca8a1 Mon Sep 17 00:00:00 2001 From: Xu Wang Date: Fri, 20 Dec 2019 07:19:38 +0000 Subject: usb: cdns3: gadget: Remove unneeded variable ret Remove unneeded variable ret used to store return value,just return 0. Signed-off-by: Xu Wang Acked-by: Roger Quadros Link: https://lore.kernel.org/r/1576826378-4387-1-git-send-email-vulab@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/gadget.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index 4c1e75509303..73b75a35ce19 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -2389,7 +2389,6 @@ static int cdns3_gadget_udc_stop(struct usb_gadget *gadget) struct cdns3_endpoint *priv_ep; u32 bEndpointAddress; struct usb_ep *ep; - int ret = 0; int val; priv_dev->gadget_driver = NULL; @@ -2413,7 +2412,7 @@ static int cdns3_gadget_udc_stop(struct usb_gadget *gadget) writel(0, &priv_dev->regs->usb_ien); writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf); - return ret; + return 0; } static const struct usb_gadget_ops cdns3_gadget_ops = { -- cgit v1.2.3-59-g8ed1b From c4a68b4da65ad388bbe3c3791f60655c4dd81173 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Wed, 18 Dec 2019 21:34:50 +0100 Subject: usb: phy: ab8500-usb: Keep PHY turned on in UART mode AB8505 supports an "UART carkit mode" which makes UART accessible through the USB connector. Upon detection of the UART cable, this mode has to be manually enabled by: 1. Turning on the PHY in peripheral mode 2. Reconfiguring PHY/pins to route UART signals to USB pins At the moment, we do not handle the UART link statuses at all, which means that UART stops working as soon as phy-ab8500-usb is loaded (since we disable the PHY after initialization). Keeping UART working if the cable is inserted before turning on the device is quite simple: In this case, early boot firmware has already set up the necessary PHY/pin configuration. The presence of the UART cable is reported by a special value in the USB link status register. We can check for that value in ab8505_usb_link_status_update() and set the PHY back to peripheral mode to restore UART. (Note: This will result in some minor garbage since we still temporarily disable the PHY during initialization...) Fully implementing this feature is more complicated: For some reason, AB8505 does not update UART link status after bootup. Regular USB cables work fine, but the link status register does not change its state if an UART cable is inserted/removed. It seems likely that the hardware is not actually capable of detecting UART cables autonomously. In addition to the USB link status register, implementations in the vendor kernel also manually measure the ID resistance to detect additional cable types. For UART cables, the USB link status register might simply reflect the PHY configuration instead of the actual link status. Implementing that functionality requires significant additions, so for now just implement the simple case. This allows using UART when inserting the cable before turning on the device. Signed-off-by: Stephan Gerhold Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20191218203450.71037-1-stephan@gerhold.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-ab8500-usb.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 4bb4b1d42f32..20c0f082bf9c 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -108,7 +108,8 @@ enum ab8500_usb_mode { USB_IDLE = 0, USB_PERIPHERAL, USB_HOST, - USB_DEDICATED_CHG + USB_DEDICATED_CHG, + USB_UART }; /* Register USB_LINK_STATUS interrupt */ @@ -393,6 +394,24 @@ static int ab8505_usb_link_status_update(struct ab8500_usb *ab, usb_phy_set_event(&ab->phy, USB_EVENT_CHARGER); break; + /* + * FIXME: For now we rely on the boot firmware to set up the necessary + * PHY/pin configuration for UART mode. + * + * AB8505 does not seem to report any status change for UART cables, + * possibly because it cannot detect them autonomously. + * We may need to measure the ID resistance manually to reliably + * detect UART cables after bootup. + */ + case USB_LINK_SAMSUNG_UART_CBL_PHY_EN_8505: + case USB_LINK_SAMSUNG_UART_CBL_PHY_DISB_8505: + if (ab->mode == USB_IDLE) { + ab->mode = USB_UART; + ab8500_usb_peri_phy_en(ab); + } + + break; + default: break; } @@ -566,6 +585,11 @@ static irqreturn_t ab8500_usb_disconnect_irq(int irq, void *data) ab->vbus_draw = 0; } + if (ab->mode == USB_UART) { + ab8500_usb_peri_phy_dis(ab); + ab->mode = USB_IDLE; + } + if (is_ab8500_2p0(ab->ab8500)) { if (ab->mode == USB_DEDICATED_CHG) { ab8500_usb_wd_linkstatus(ab, -- cgit v1.2.3-59-g8ed1b From 5311f88e07ce83ea529a6a84716cbe32e8db2b6a Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 19 Dec 2019 18:39:54 +0900 Subject: usb: mtk-xhci: Do not explicitly set the DMA mask The mtk-xhci platform glue sets the DMA mask to 32 bits on its own, which was needed before commit fda182d80a0b ("usb: xhci: configure 32-bit DMA if the controller does not support 64-bit DMA"), but now it has no effect, because xhci_gen_setup() sets it up for us according to hardware capabilities. Remove the useless code. Signed-off-by: Tomasz Figa Link: https://lore.kernel.org/r/20191219093954.163417-1-tfiga@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mtk.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index b18a6baef204..bfbdb3ceed29 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -488,11 +488,6 @@ static int xhci_mtk_probe(struct platform_device *pdev) goto disable_clk; } - /* Initialize dma_mask and coherent_dma_mask to 32-bits */ - ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); - if (ret) - goto disable_clk; - hcd = usb_create_hcd(driver, dev, dev_name(dev)); if (!hcd) { ret = -ENOMEM; -- cgit v1.2.3-59-g8ed1b From 71a1fa0df2a3728b8ccb97394be420d1f03df40e Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 30 Dec 2019 16:34:30 +0300 Subject: usb: typec: ucsi: Store the notification mask The driver needs to ignore any Connector Change Events before the Connector Change Indication notifications have actually been enabled. This adds a check to ucsi_connector_change() function to make sure the function does not try to process the event unless the Connector Change notifications have been enabled. It is quite common that the firmware representing the "PPM" (Platform Policy Manager) starts generating Connector Change notifications even when only the Command Completion notifications are enabled. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20191230133431.63445-2-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 15 ++++++++++----- drivers/usb/typec/ucsi/ucsi.h | 3 +++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 4459bc68aa33..672cb61b737f 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -189,7 +189,7 @@ int ucsi_resume(struct ucsi *ucsi) u64 command; /* Restore UCSI notification enable mask after system resume */ - command = UCSI_SET_NOTIFICATION_ENABLE | UCSI_ENABLE_NTFY_ALL; + command = UCSI_SET_NOTIFICATION_ENABLE | ucsi->ntfy; return ucsi_send_command(ucsi, command, NULL, 0); } @@ -589,6 +589,11 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num) { struct ucsi_connector *con = &ucsi->connector[num - 1]; + if (!(ucsi->ntfy & UCSI_ENABLE_NTFY_CONNECTOR_CHANGE)) { + dev_dbg(ucsi->dev, "Bogus connetor change event\n"); + return; + } + if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags)) schedule_work(&con->work); } @@ -656,7 +661,7 @@ static int ucsi_role_cmd(struct ucsi_connector *con, u64 command) ucsi_reset_ppm(con->ucsi); mutex_unlock(&con->ucsi->ppm_lock); - c = UCSI_SET_NOTIFICATION_ENABLE | UCSI_ENABLE_NTFY_ALL; + c = UCSI_SET_NOTIFICATION_ENABLE | con->ucsi->ntfy; ucsi_send_command(con->ucsi, c, NULL, 0); ucsi_reset_connector(con, true); @@ -890,8 +895,8 @@ int ucsi_init(struct ucsi *ucsi) } /* Enable basic notifications */ - command = UCSI_SET_NOTIFICATION_ENABLE; - command |= UCSI_ENABLE_NTFY_CMD_COMPLETE | UCSI_ENABLE_NTFY_ERROR; + ucsi->ntfy = UCSI_ENABLE_NTFY_CMD_COMPLETE | UCSI_ENABLE_NTFY_ERROR; + command = UCSI_SET_NOTIFICATION_ENABLE | ucsi->ntfy; ret = ucsi_run_command(ucsi, command, NULL, 0); if (ret < 0) goto err_reset; @@ -923,7 +928,7 @@ int ucsi_init(struct ucsi *ucsi) } /* Enable all notifications */ - command = UCSI_SET_NOTIFICATION_ENABLE | UCSI_ENABLE_NTFY_ALL; + command = UCSI_SET_NOTIFICATION_ENABLE | ucsi->ntfy; ret = ucsi_run_command(ucsi, command, NULL, 0); if (ret < 0) goto err_unregister; diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 8569bbd3762f..09ba261ea103 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -269,6 +269,9 @@ struct ucsi { /* PPM Communication lock */ struct mutex ppm_lock; + /* The latest "Notification Enable" bits (SET_NOTIFICATION_ENABLE) */ + u64 ntfy; + /* PPM communication flags */ unsigned long flags; #define EVENT_PENDING 0 -- cgit v1.2.3-59-g8ed1b From 170a6726d0e266f2c8f306e3d61715c32f4ee41e Mon Sep 17 00:00:00 2001 From: Ajay Gupta Date: Mon, 30 Dec 2019 16:34:31 +0300 Subject: usb: typec: ucsi: add support for separate DP altmode devices CCGx controller used on NVIDIA GPU card has two separate display altmode for two DP pin assignments. UCSI specification doesn't prohibits using separate display altmode. Current UCSI Type-C framework expects only one display altmode for all DP pin assignment. This patch squashes two separate display altmode into single altmode to support controllers with separate display altmode. We first read all the alternate modes of connector and then run through it to know if there are separate display altmodes. If so, it prepares a new port altmode set after squashing two or more separate altmodes into one. Signed-off-by: Ajay Gupta Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20191230133431.63445-3-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 79 ++++++++++++++++ drivers/usb/typec/ucsi/ucsi.h | 11 +++ drivers/usb/typec/ucsi/ucsi_ccg.c | 191 +++++++++++++++++++++++++++++++++++++- 3 files changed, 279 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 672cb61b737f..466bd8afceea 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -318,6 +318,82 @@ err: return ret; } +static int +ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient) +{ + int max_altmodes = UCSI_MAX_ALTMODES; + struct typec_altmode_desc desc; + struct ucsi_altmode alt; + struct ucsi_altmode orig[UCSI_MAX_ALTMODES]; + struct ucsi_altmode updated[UCSI_MAX_ALTMODES]; + struct ucsi *ucsi = con->ucsi; + bool multi_dp = false; + u64 command; + int ret; + int len; + int i; + int k = 0; + + if (recipient == UCSI_RECIPIENT_CON) + max_altmodes = con->ucsi->cap.num_alt_modes; + + memset(orig, 0, sizeof(orig)); + memset(updated, 0, sizeof(updated)); + + /* First get all the alternate modes */ + for (i = 0; i < max_altmodes; i++) { + memset(&alt, 0, sizeof(alt)); + command = UCSI_GET_ALTERNATE_MODES; + command |= UCSI_GET_ALTMODE_RECIPIENT(recipient); + command |= UCSI_GET_ALTMODE_CONNECTOR_NUMBER(con->num); + command |= UCSI_GET_ALTMODE_OFFSET(i); + len = ucsi_run_command(con->ucsi, command, &alt, sizeof(alt)); + /* + * We are collecting all altmodes first and then registering. + * Some type-C device will return zero length data beyond last + * alternate modes. We should not return if length is zero. + */ + if (len < 0) + return len; + + /* We got all altmodes, now break out and register them */ + if (!len || !alt.svid) + break; + + orig[k].mid = alt.mid; + orig[k].svid = alt.svid; + k++; + } + /* + * Update the original altmode table as some ppms may report + * multiple DP altmodes. + */ + if (recipient == UCSI_RECIPIENT_CON) + multi_dp = ucsi->ops->update_altmodes(ucsi, orig, updated); + + /* now register altmodes */ + for (i = 0; i < max_altmodes; i++) { + memset(&desc, 0, sizeof(desc)); + if (multi_dp && recipient == UCSI_RECIPIENT_CON) { + desc.svid = updated[i].svid; + desc.vdo = updated[i].mid; + } else { + desc.svid = orig[i].svid; + desc.vdo = orig[i].mid; + } + desc.roles = TYPEC_PORT_DRD; + + if (!desc.svid) + return 0; + + ret = ucsi_register_altmode(con, &desc, recipient); + if (ret) + return ret; + } + + return 0; +} + static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient) { int max_altmodes = UCSI_MAX_ALTMODES; @@ -336,6 +412,9 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient) if (recipient == UCSI_RECIPIENT_SOP && con->partner_altmode[0]) return 0; + if (con->ucsi->ops->update_altmodes) + return ucsi_register_altmodes_nvidia(con, recipient); + if (recipient == UCSI_RECIPIENT_CON) max_altmodes = con->ucsi->cap.num_alt_modes; diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 09ba261ea103..9e01a9556603 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -11,6 +11,7 @@ /* -------------------------------------------------------------------------- */ struct ucsi; +struct ucsi_altmode; /* UCSI offsets (Bytes) */ #define UCSI_VERSION 0 @@ -35,6 +36,7 @@ struct ucsi; * @read: Read operation * @sync_write: Blocking write operation * @async_write: Non-blocking write operation + * @update_altmodes: Squashes duplicate DP altmodes * * Read and write routines for UCSI interface. @sync_write must wait for the * Command Completion Event from the PPM before returning, and @async_write must @@ -47,6 +49,8 @@ struct ucsi_operations { const void *val, size_t val_len); int (*async_write)(struct ucsi *ucsi, unsigned int offset, const void *val, size_t val_len); + bool (*update_altmodes)(struct ucsi *ucsi, struct ucsi_altmode *orig, + struct ucsi_altmode *updated); }; struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops); @@ -82,6 +86,7 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num); #define UCSI_GET_ERROR_STATUS 0x13 #define UCSI_CONNECTOR_NUMBER(_num_) ((u64)(_num_) << 16) +#define UCSI_COMMAND(_cmd_) ((_cmd_) & 0xff) /* CONNECTOR_RESET command bits */ #define UCSI_CONNECTOR_RESET_HARD BIT(23) /* Deprecated in v1.1 */ @@ -140,6 +145,12 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num); #define UCSI_ERROR_PPM_POLICY_CONFLICT BIT(11) #define UCSI_ERROR_SWAP_REJECTED BIT(12) +#define UCSI_SET_NEW_CAM_ENTER(x) (((x) >> 23) & 0x1) +#define UCSI_SET_NEW_CAM_GET_AM(x) (((x) >> 24) & 0xff) +#define UCSI_SET_NEW_CAM_AM_MASK (0xff << 24) +#define UCSI_SET_NEW_CAM_SET_AM(x) (((x) & 0xff) << 24) +#define UCSI_CMD_CONNECTOR_MASK (0x7) + /* Data structure filled by PPM in response to GET_CAPABILITY command. */ struct ucsi_capability { u32 attributes; diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index 3370b3fc37b1..a5b8530490db 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "ucsi.h" @@ -173,6 +174,15 @@ struct ccg_resp { u8 length; }; +struct ucsi_ccg_altmode { + u16 svid; + u32 mid; + u8 linked_idx; + u8 active_idx; +#define UCSI_MULTI_DP_INDEX (0xff) + bool checked; +} __packed; + struct ucsi_ccg { struct device *dev; struct ucsi *ucsi; @@ -198,6 +208,11 @@ struct ucsi_ccg { struct work_struct pm_work; struct completion complete; + + u64 last_cmd_sent; + bool has_multiple_dp; + struct ucsi_ccg_altmode orig[UCSI_MAX_ALTMODES]; + struct ucsi_ccg_altmode updated[UCSI_MAX_ALTMODES]; }; static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) @@ -319,12 +334,169 @@ static int ucsi_ccg_init(struct ucsi_ccg *uc) return -ETIMEDOUT; } +static void ucsi_ccg_update_get_current_cam_cmd(struct ucsi_ccg *uc, u8 *data) +{ + u8 cam, new_cam; + + cam = data[0]; + new_cam = uc->orig[cam].linked_idx; + uc->updated[new_cam].active_idx = cam; + data[0] = new_cam; +} + +static bool ucsi_ccg_update_altmodes(struct ucsi *ucsi, + struct ucsi_altmode *orig, + struct ucsi_altmode *updated) +{ + struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi); + struct ucsi_ccg_altmode *alt, *new_alt; + int i, j, k = 0; + bool found = false; + + alt = uc->orig; + new_alt = uc->updated; + memset(uc->updated, 0, sizeof(uc->updated)); + + /* + * Copy original connector altmodes to new structure. + * We need this before second loop since second loop + * checks for duplicate altmodes. + */ + for (i = 0; i < UCSI_MAX_ALTMODES; i++) { + alt[i].svid = orig[i].svid; + alt[i].mid = orig[i].mid; + if (!alt[i].svid) + break; + } + + for (i = 0; i < UCSI_MAX_ALTMODES; i++) { + if (!alt[i].svid) + break; + + /* already checked and considered */ + if (alt[i].checked) + continue; + + if (!DP_CONF_GET_PIN_ASSIGN(alt[i].mid)) { + /* Found Non DP altmode */ + new_alt[k].svid = alt[i].svid; + new_alt[k].mid |= alt[i].mid; + new_alt[k].linked_idx = i; + alt[i].linked_idx = k; + updated[k].svid = new_alt[k].svid; + updated[k].mid = new_alt[k].mid; + k++; + continue; + } + + for (j = i + 1; j < UCSI_MAX_ALTMODES; j++) { + if (alt[i].svid != alt[j].svid || + !DP_CONF_GET_PIN_ASSIGN(alt[j].mid)) { + continue; + } else { + /* Found duplicate DP mode */ + new_alt[k].svid = alt[i].svid; + new_alt[k].mid |= alt[i].mid | alt[j].mid; + new_alt[k].linked_idx = UCSI_MULTI_DP_INDEX; + alt[i].linked_idx = k; + alt[j].linked_idx = k; + alt[j].checked = true; + found = true; + } + } + if (found) { + uc->has_multiple_dp = true; + } else { + /* Didn't find any duplicate DP altmode */ + new_alt[k].svid = alt[i].svid; + new_alt[k].mid |= alt[i].mid; + new_alt[k].linked_idx = i; + alt[i].linked_idx = k; + } + updated[k].svid = new_alt[k].svid; + updated[k].mid = new_alt[k].mid; + k++; + } + return found; +} + +static void ucsi_ccg_update_set_new_cam_cmd(struct ucsi_ccg *uc, + struct ucsi_connector *con, + u64 *cmd) +{ + struct ucsi_ccg_altmode *new_port, *port; + struct typec_altmode *alt = NULL; + u8 new_cam, cam, pin; + bool enter_new_mode; + int i, j, k = 0xff; + + port = uc->orig; + new_cam = UCSI_SET_NEW_CAM_GET_AM(*cmd); + new_port = &uc->updated[new_cam]; + cam = new_port->linked_idx; + enter_new_mode = UCSI_SET_NEW_CAM_ENTER(*cmd); + + /* + * If CAM is UCSI_MULTI_DP_INDEX then this is DP altmode + * with multiple DP mode. Find out CAM for best pin assignment + * among all DP mode. Priorite pin E->D->C after making sure + * the partner supports that pin. + */ + if (cam == UCSI_MULTI_DP_INDEX) { + if (enter_new_mode) { + for (i = 0; con->partner_altmode[i]; i++) { + alt = con->partner_altmode[i]; + if (alt->svid == new_port->svid) + break; + } + /* + * alt will always be non NULL since this is + * UCSI_SET_NEW_CAM command and so there will be + * at least one con->partner_altmode[i] with svid + * matching with new_port->svid. + */ + for (j = 0; port[j].svid; j++) { + pin = DP_CONF_GET_PIN_ASSIGN(port[j].mid); + if (alt && port[j].svid == alt->svid && + (pin & DP_CONF_GET_PIN_ASSIGN(alt->vdo))) { + /* prioritize pin E->D->C */ + if (k == 0xff || (k != 0xff && pin > + DP_CONF_GET_PIN_ASSIGN(port[k].mid)) + ) { + k = j; + } + } + } + cam = k; + new_port->active_idx = cam; + } else { + cam = new_port->active_idx; + } + } + *cmd &= ~UCSI_SET_NEW_CAM_AM_MASK; + *cmd |= UCSI_SET_NEW_CAM_SET_AM(cam); +} + static int ucsi_ccg_read(struct ucsi *ucsi, unsigned int offset, void *val, size_t val_len) { + struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi); + int ret; u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset); - return ccg_read(ucsi_get_drvdata(ucsi), reg, val, val_len); + ret = ccg_read(uc, reg, val, val_len); + if (ret) + return ret; + + if (offset == UCSI_MESSAGE_IN) { + if (UCSI_COMMAND(uc->last_cmd_sent) == UCSI_GET_CURRENT_CAM && + uc->has_multiple_dp) { + ucsi_ccg_update_get_current_cam_cmd(uc, (u8 *)val); + } + uc->last_cmd_sent = 0; + } + + return ret; } static int ucsi_ccg_async_write(struct ucsi *ucsi, unsigned int offset, @@ -339,12 +511,26 @@ static int ucsi_ccg_sync_write(struct ucsi *ucsi, unsigned int offset, const void *val, size_t val_len) { struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi); + struct ucsi_connector *con; + int con_index; int ret; mutex_lock(&uc->lock); pm_runtime_get_sync(uc->dev); set_bit(DEV_CMD_PENDING, &uc->flags); + if (offset == UCSI_CONTROL && val_len == sizeof(uc->last_cmd_sent)) { + uc->last_cmd_sent = *(u64 *)val; + + if (UCSI_COMMAND(uc->last_cmd_sent) == UCSI_SET_NEW_CAM && + uc->has_multiple_dp) { + con_index = (uc->last_cmd_sent >> 16) & + UCSI_CMD_CONNECTOR_MASK; + con = &uc->ucsi->connector[con_index - 1]; + ucsi_ccg_update_set_new_cam_cmd(uc, con, (u64 *)val); + } + } + ret = ucsi_ccg_async_write(ucsi, offset, val, val_len); if (ret) goto err_clear_bit; @@ -363,7 +549,8 @@ err_clear_bit: static const struct ucsi_operations ucsi_ccg_ops = { .read = ucsi_ccg_read, .sync_write = ucsi_ccg_sync_write, - .async_write = ucsi_ccg_async_write + .async_write = ucsi_ccg_async_write, + .update_altmodes = ucsi_ccg_update_altmodes }; static irqreturn_t ccg_irq_handler(int irq, void *data) -- cgit v1.2.3-59-g8ed1b From 88eaaecc44461f9fb147bf7ee6ccc6d4e9fc23e0 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 30 Dec 2019 18:22:14 +0100 Subject: usb: host: Enable compile testing for some of drivers Some of the USB host drivers can be compile tested to increase build coverage. Add 'if' conditional to 'default y' so they will not get enabled by default on all other architectures. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20191230172215.17370-1-krzk@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 54 ++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 8d730180db06..da14a3d16b57 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -186,7 +186,7 @@ config USB_EHCI_FSL config USB_EHCI_MXC tristate "Support for Freescale i.MX on-chip EHCI USB controller" - depends on ARCH_MXC + depends on ARCH_MXC || COMPILE_TEST select USB_EHCI_ROOT_HUB_TT ---help--- Variation of ARC USB block used in some Freescale chips. @@ -210,8 +210,8 @@ config USB_EHCI_HCD_OMAP config USB_EHCI_HCD_ORION tristate "Support for Marvell EBU on-chip EHCI USB controller" - depends on USB_EHCI_HCD && (PLAT_ORION || ARCH_MVEBU) - default y + depends on USB_EHCI_HCD && (PLAT_ORION || ARCH_MVEBU || COMPILE_TEST) + default y if (PLAT_ORION || ARCH_MVEBU) ---help--- Enables support for the on-chip EHCI controller on Marvell's embedded ARM SoCs, including Orion, Kirkwood, Dove, Armada XP, @@ -221,15 +221,15 @@ config USB_EHCI_HCD_ORION config USB_EHCI_HCD_SPEAR tristate "Support for ST SPEAr on-chip EHCI USB controller" - depends on USB_EHCI_HCD && PLAT_SPEAR - default y + depends on USB_EHCI_HCD && (PLAT_SPEAR || COMPILE_TEST) + default y if PLAT_SPEAR ---help--- Enables support for the on-chip EHCI controller on ST SPEAr chips. config USB_EHCI_HCD_STI tristate "Support for ST STiHxxx on-chip EHCI USB controller" - depends on ARCH_STI && OF + depends on (ARCH_STI || COMPILE_TEST) && OF select GENERIC_PHY select USB_EHCI_HCD_PLATFORM help @@ -238,8 +238,8 @@ config USB_EHCI_HCD_STI config USB_EHCI_HCD_AT91 tristate "Support for Atmel on-chip EHCI USB controller" - depends on USB_EHCI_HCD && ARCH_AT91 - default y + depends on USB_EHCI_HCD && (ARCH_AT91 || COMPILE_TEST) + default y if ARCH_AT91 ---help--- Enables support for the on-chip EHCI controller on Atmel chips. @@ -263,20 +263,20 @@ config USB_EHCI_HCD_PPC_OF config USB_EHCI_SH bool "EHCI support for SuperH USB controller" - depends on SUPERH + depends on SUPERH || COMPILE_TEST ---help--- Enables support for the on-chip EHCI controller on the SuperH. If you use the PCI EHCI controller, this option is not necessary. config USB_EHCI_EXYNOS tristate "EHCI support for Samsung S5P/EXYNOS SoC Series" - depends on ARCH_S5PV210 || ARCH_EXYNOS + depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST help Enable support for the Samsung Exynos SOC's on-chip EHCI controller. config USB_EHCI_MV tristate "EHCI support for Marvell PXA/MMP USB controller" - depends on (ARCH_PXA || ARCH_MMP) + depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST select USB_EHCI_ROOT_HUB_TT ---help--- Enables support for Marvell (including PXA and MMP series) on-chip @@ -289,7 +289,7 @@ config USB_EHCI_MV config USB_CNS3XXX_EHCI bool "Cavium CNS3XXX EHCI Module (DEPRECATED)" - depends on ARCH_CNS3XXX + depends on ARCH_CNS3XXX || COMPILE_TEST select USB_EHCI_HCD_PLATFORM ---help--- This option is deprecated now and the driver was removed, use @@ -309,7 +309,7 @@ config USB_EHCI_HCD_PLATFORM config USB_OCTEON_EHCI bool "Octeon on-chip EHCI support (DEPRECATED)" - depends on CAVIUM_OCTEON_SOC + depends on CAVIUM_OCTEON_SOC || COMPILE_TEST select USB_EHCI_BIG_ENDIAN_MMIO if CPU_BIG_ENDIAN select USB_EHCI_HCD_PLATFORM help @@ -410,15 +410,15 @@ config USB_OHCI_HCD_OMAP1 config USB_OHCI_HCD_SPEAR tristate "Support for ST SPEAr on-chip OHCI USB controller" - depends on USB_OHCI_HCD && PLAT_SPEAR - default y + depends on USB_OHCI_HCD && (PLAT_SPEAR || COMPILE_TEST) + default y if PLAT_SPEAR ---help--- Enables support for the on-chip OHCI controller on ST SPEAr chips. config USB_OHCI_HCD_STI tristate "Support for ST STiHxxx on-chip OHCI USB controller" - depends on ARCH_STI && OF + depends on (ARCH_STI || COMPILE_TEST) && OF select GENERIC_PHY select USB_OHCI_HCD_PLATFORM help @@ -427,8 +427,8 @@ config USB_OHCI_HCD_STI config USB_OHCI_HCD_S3C2410 tristate "OHCI support for Samsung S3C24xx/S3C64xx SoC series" - depends on USB_OHCI_HCD && (ARCH_S3C24XX || ARCH_S3C64XX) - default y + depends on USB_OHCI_HCD && (ARCH_S3C24XX || ARCH_S3C64XX || COMPILE_TEST) + default y if (ARCH_S3C24XX || ARCH_S3C64XX) ---help--- Enables support for the on-chip OHCI controller on S3C24xx/S3C64xx chips. @@ -453,17 +453,17 @@ config USB_OHCI_HCD_PXA27X config USB_OHCI_HCD_AT91 tristate "Support for Atmel on-chip OHCI USB controller" - depends on USB_OHCI_HCD && ARCH_AT91 && OF - default y + depends on USB_OHCI_HCD && (ARCH_AT91 || COMPILE_TEST) && OF + default y if ARCH_AT91 ---help--- Enables support for the on-chip OHCI controller on Atmel chips. config USB_OHCI_HCD_OMAP3 tristate "OHCI support for OMAP3 and later chips" - depends on (ARCH_OMAP3 || ARCH_OMAP4 || SOC_OMAP5) + depends on ARCH_OMAP3 || ARCH_OMAP4 || SOC_OMAP5 || COMPILE_TEST select USB_OHCI_HCD_PLATFORM - default y + default y if ARCH_OMAP3 || ARCH_OMAP4 || SOC_OMAP5 help This option is deprecated now and the driver was removed, use USB_OHCI_HCD_PLATFORM instead. @@ -473,10 +473,10 @@ config USB_OHCI_HCD_OMAP3 config USB_OHCI_HCD_DAVINCI tristate "OHCI support for TI DaVinci DA8xx" - depends on ARCH_DAVINCI_DA8XX + depends on ARCH_DAVINCI_DA8XX || COMPILE_TEST depends on USB_OHCI_HCD select PHY_DA8XX_USB - default y + default y if ARCH_DAVINCI_DA8XX help Enables support for the DaVinci DA8xx integrated OHCI controller. This driver cannot currently be a loadable @@ -532,7 +532,7 @@ config USB_OHCI_HCD_SSB config USB_OHCI_SH bool "OHCI support for SuperH USB controller (DEPRECATED)" - depends on SUPERH + depends on SUPERH || COMPILE_TEST select USB_OHCI_HCD_PLATFORM ---help--- This option is deprecated now and the driver was removed, use @@ -543,13 +543,13 @@ config USB_OHCI_SH config USB_OHCI_EXYNOS tristate "OHCI support for Samsung S5P/EXYNOS SoC Series" - depends on ARCH_S5PV210 || ARCH_EXYNOS + depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST help Enable support for the Samsung Exynos SOC's on-chip OHCI controller. config USB_CNS3XXX_OHCI bool "Cavium CNS3XXX OHCI Module (DEPRECATED)" - depends on ARCH_CNS3XXX + depends on ARCH_CNS3XXX || COMPILE_TEST select USB_OHCI_HCD_PLATFORM ---help--- This option is deprecated now and the driver was removed, use -- cgit v1.2.3-59-g8ed1b From 91687c1926bcd6b80d669375d57331b7bf80bf99 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 30 Dec 2019 18:22:15 +0100 Subject: usb: phy: Enable compile testing for some of drivers Some of the USB phy drivers can be compile tested to increase build coverage. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20191230172215.17370-2-krzk@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 24b4f091acb8..ff24fca0a2d9 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -162,7 +162,7 @@ config USB_MXS_PHY config USB_TEGRA_PHY tristate "NVIDIA Tegra USB PHY Driver" - depends on ARCH_TEGRA + depends on ARCH_TEGRA || COMPILE_TEST select USB_COMMON select USB_PHY select USB_ULPI @@ -172,7 +172,7 @@ config USB_TEGRA_PHY config USB_ULPI bool "Generic ULPI Transceiver Driver" - depends on ARM || ARM64 + depends on ARM || ARM64 || COMPILE_TEST select USB_ULPI_VIEWPORT help Enable this to support ULPI connected USB OTG transceivers which -- cgit v1.2.3-59-g8ed1b From 3b31ec1848ec41d9501db3de61805e3ae173f485 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 17 Dec 2019 15:12:41 +0100 Subject: usb: renesas_usbhs: Switch to GPIO descriptor The Renesas USBHS driver includes a bit of surplus headers and uses the old GPIO API so let's switch it to use the GPIO descriptor. I noticed that the enable_gpio inside renesas_usbhs_driver_param isn't really referenced anywhere, and it is also the wrong type (u32) so let's just delete it and use a local variable instead. Cc: Eugeniu Rosca Cc: Veeraiyan Chidambaram Cc: Yoshihiro Shimoda Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20191217141241.57639-1-linus.walleij@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/common.c | 22 +++++++++------------- drivers/usb/renesas_usbhs/rcar2.c | 2 -- include/linux/usb/renesas_usbhs.h | 2 -- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index d438b7871446..3af91b2b8f76 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -8,11 +8,10 @@ */ #include #include -#include +#include #include #include #include -#include #include #include #include @@ -592,7 +591,8 @@ static int usbhs_probe(struct platform_device *pdev) struct usbhs_priv *priv; struct resource *irq_res; struct device *dev = &pdev->dev; - int ret, gpio; + struct gpio_desc *gpiod; + int ret; u32 tmp; /* check device node */ @@ -657,10 +657,9 @@ static int usbhs_probe(struct platform_device *pdev) priv->dparam.pio_dma_border = 64; /* 64byte */ if (!of_property_read_u32(dev_of_node(dev), "renesas,buswait", &tmp)) priv->dparam.buswait_bwait = tmp; - gpio = of_get_named_gpio_flags(dev_of_node(dev), "renesas,enable-gpio", - 0, NULL); - if (gpio > 0) - priv->dparam.enable_gpio = gpio; + gpiod = devm_gpiod_get_optional(dev, "renesas,enable", GPIOD_IN); + if (IS_ERR(gpiod)) + return PTR_ERR(gpiod); /* FIXME */ /* runtime power control ? */ @@ -708,13 +707,10 @@ static int usbhs_probe(struct platform_device *pdev) usbhs_sys_clock_ctrl(priv, 0); /* check GPIO determining if USB function should be enabled */ - if (priv->dparam.enable_gpio) { - gpio_request_one(priv->dparam.enable_gpio, GPIOF_IN, NULL); - ret = !gpio_get_value(priv->dparam.enable_gpio); - gpio_free(priv->dparam.enable_gpio); + if (gpiod) { + ret = !gpiod_get_value(gpiod); if (ret) { - dev_warn(dev, "USB function not selected (GPIO %d)\n", - priv->dparam.enable_gpio); + dev_warn(dev, "USB function not selected (GPIO)\n"); ret = -ENOTSUPP; goto probe_end_mod_exit; } diff --git a/drivers/usb/renesas_usbhs/rcar2.c b/drivers/usb/renesas_usbhs/rcar2.c index 440d213e1749..7f2f06586ea5 100644 --- a/drivers/usb/renesas_usbhs/rcar2.c +++ b/drivers/usb/renesas_usbhs/rcar2.c @@ -6,8 +6,6 @@ * Copyright (C) 2019 Renesas Electronics Corporation */ -#include -#include #include #include "common.h" #include "rcar2.h" diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index 6914475bbc86..d418c55523a7 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -170,8 +170,6 @@ struct renesas_usbhs_driver_param { */ int pio_dma_border; /* default is 64byte */ - u32 enable_gpio; - /* * option: */ -- cgit v1.2.3-59-g8ed1b From c2f59e8180c9311108872dba582b1b0146acb054 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 11 Dec 2019 15:52:08 +0100 Subject: ARM: dts: qcom: Correct USB3503 GPIOs polarity Current USB3503 driver ignores GPIO polarity and always operates as if the GPIO lines were flagged as ACTIVE_HIGH. Fix the polarity for the existing USB3503 chip applications to match the chip specification and common convention for naming the pins. The only pin, which has to be ACTIVE_LOW is the reset pin. The remaining are ACTIVE_HIGH. This change allows later to fix the USB3503 driver to properly use generic GPIO bindings and read polarity from DT. Signed-off-by: Marek Szyprowski Reviewed-by: Linus Walleij Acked-by: Bjorn Andersson Link: https://lore.kernel.org/r/20191211145208.24976-1-m.szyprowski@samsung.com Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/qcom-mdm9615-wp8548-mangoh-green.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom-mdm9615-wp8548-mangoh-green.dts b/arch/arm/boot/dts/qcom-mdm9615-wp8548-mangoh-green.dts index 26160c324802..942e3a2cac35 100644 --- a/arch/arm/boot/dts/qcom-mdm9615-wp8548-mangoh-green.dts +++ b/arch/arm/boot/dts/qcom-mdm9615-wp8548-mangoh-green.dts @@ -143,7 +143,7 @@ compatible = "smsc,usb3503a"; reg = <0x8>; connect-gpios = <&gpioext2 1 GPIO_ACTIVE_HIGH>; - intn-gpios = <&gpioext2 0 GPIO_ACTIVE_LOW>; + intn-gpios = <&gpioext2 0 GPIO_ACTIVE_HIGH>; initial-mode = <1>; }; }; -- cgit v1.2.3-59-g8ed1b From 51d22e855ea3459d4b272e46aff95de0e59e65a7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 11 Dec 2019 15:52:26 +0100 Subject: usb: usb3503: Convert to use GPIO descriptors This converts the USB3503 to pick GPIO descriptors from the device tree instead of iteratively picking out GPIO number references and then referencing these from the global GPIO numberspace. The USB3503 is only used from device tree among the in-tree platforms. If board files would still desire to use it they can provide machine descriptor tables. Make sure to preserve semantics such as the reset delay introduced by Stefan. Cc: Chunfeng Yun Cc: Marek Szyprowski Cc: Stefan Agner Cc: Krzysztof Kozlowski Signed-off-by: Linus Walleij [mszyprow: invert the logic behind reset GPIO line] Signed-off-by: Marek Szyprowski Link: https://lore.kernel.org/r/20191211145226.25074-1-m.szyprowski@samsung.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb3503.c | 94 +++++++++++++---------------------- include/linux/platform_data/usb3503.h | 3 -- 2 files changed, 35 insertions(+), 62 deletions(-) diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index 72f39a9751b5..116bd789e568 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -7,11 +7,10 @@ #include #include -#include +#include #include #include #include -#include #include #include #include @@ -47,19 +46,19 @@ struct usb3503 { struct device *dev; struct clk *clk; u8 port_off_mask; - int gpio_intn; - int gpio_reset; - int gpio_connect; + struct gpio_desc *intn; + struct gpio_desc *reset; + struct gpio_desc *connect; bool secondary_ref_clk; }; static int usb3503_reset(struct usb3503 *hub, int state) { - if (!state && gpio_is_valid(hub->gpio_connect)) - gpio_set_value_cansleep(hub->gpio_connect, 0); + if (!state && hub->connect) + gpiod_set_value_cansleep(hub->connect, 0); - if (gpio_is_valid(hub->gpio_reset)) - gpio_set_value_cansleep(hub->gpio_reset, state); + if (hub->reset) + gpiod_set_value_cansleep(hub->reset, !state); /* Wait T_HUBINIT == 4ms for hub logic to stabilize */ if (state) @@ -115,8 +114,8 @@ static int usb3503_connect(struct usb3503 *hub) } } - if (gpio_is_valid(hub->gpio_connect)) - gpio_set_value_cansleep(hub->gpio_connect, 1); + if (hub->connect) + gpiod_set_value_cansleep(hub->connect, 1); hub->mode = USB3503_MODE_HUB; dev_info(dev, "switched to HUB mode\n"); @@ -163,13 +162,11 @@ static int usb3503_probe(struct usb3503 *hub) int err; u32 mode = USB3503_MODE_HUB; const u32 *property; + enum gpiod_flags flags; int len; if (pdata) { hub->port_off_mask = pdata->port_off_mask; - hub->gpio_intn = pdata->gpio_intn; - hub->gpio_connect = pdata->gpio_connect; - hub->gpio_reset = pdata->gpio_reset; hub->mode = pdata->initial_mode; } else if (np) { u32 rate = 0; @@ -230,59 +227,38 @@ static int usb3503_probe(struct usb3503 *hub) } } - hub->gpio_intn = of_get_named_gpio(np, "intn-gpios", 0); - if (hub->gpio_intn == -EPROBE_DEFER) - return -EPROBE_DEFER; - hub->gpio_connect = of_get_named_gpio(np, "connect-gpios", 0); - if (hub->gpio_connect == -EPROBE_DEFER) - return -EPROBE_DEFER; - hub->gpio_reset = of_get_named_gpio(np, "reset-gpios", 0); - if (hub->gpio_reset == -EPROBE_DEFER) - return -EPROBE_DEFER; of_property_read_u32(np, "initial-mode", &mode); hub->mode = mode; } - if (hub->port_off_mask && !hub->regmap) - dev_err(dev, "Ports disabled with no control interface\n"); - - if (gpio_is_valid(hub->gpio_intn)) { - int val = hub->secondary_ref_clk ? GPIOF_OUT_INIT_LOW : - GPIOF_OUT_INIT_HIGH; - err = devm_gpio_request_one(dev, hub->gpio_intn, val, - "usb3503 intn"); - if (err) { - dev_err(dev, - "unable to request GPIO %d as interrupt pin (%d)\n", - hub->gpio_intn, err); - return err; - } - } - - if (gpio_is_valid(hub->gpio_connect)) { - err = devm_gpio_request_one(dev, hub->gpio_connect, - GPIOF_OUT_INIT_LOW, "usb3503 connect"); - if (err) { - dev_err(dev, - "unable to request GPIO %d as connect pin (%d)\n", - hub->gpio_connect, err); - return err; - } - } - - if (gpio_is_valid(hub->gpio_reset)) { - err = devm_gpio_request_one(dev, hub->gpio_reset, - GPIOF_OUT_INIT_LOW, "usb3503 reset"); + if (hub->secondary_ref_clk) + flags = GPIOD_OUT_LOW; + else + flags = GPIOD_OUT_HIGH; + hub->intn = devm_gpiod_get_optional(dev, "intn", flags); + if (IS_ERR(hub->intn)) + return PTR_ERR(hub->intn); + if (hub->intn) + gpiod_set_consumer_name(hub->intn, "usb3503 intn"); + + hub->connect = devm_gpiod_get_optional(dev, "connect", GPIOD_OUT_LOW); + if (IS_ERR(hub->connect)) + return PTR_ERR(hub->connect); + if (hub->connect) + gpiod_set_consumer_name(hub->connect, "usb3503 connect"); + + hub->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(hub->reset)) + return PTR_ERR(hub->reset); + if (hub->reset) { /* Datasheet defines a hardware reset to be at least 100us */ usleep_range(100, 10000); - if (err) { - dev_err(dev, - "unable to request GPIO %d as reset pin (%d)\n", - hub->gpio_reset, err); - return err; - } + gpiod_set_consumer_name(hub->reset, "usb3503 reset"); } + if (hub->port_off_mask && !hub->regmap) + dev_err(dev, "Ports disabled with no control interface\n"); + usb3503_switch_mode(hub, hub->mode); dev_info(dev, "%s: probed in %s mode\n", __func__, diff --git a/include/linux/platform_data/usb3503.h b/include/linux/platform_data/usb3503.h index e049d51c1353..d01ef97ddf36 100644 --- a/include/linux/platform_data/usb3503.h +++ b/include/linux/platform_data/usb3503.h @@ -17,9 +17,6 @@ enum usb3503_mode { struct usb3503_platform_data { enum usb3503_mode initial_mode; u8 port_off_mask; - int gpio_intn; - int gpio_connect; - int gpio_reset; }; #endif -- cgit v1.2.3-59-g8ed1b From 4e52af1ccaa2d979894d4d059037ff9ec4d26a83 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 31 Dec 2019 08:46:32 +0100 Subject: usb: host: Do not compile test deprecated USB_OCTEON_EHCI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The USB_OCTEON_EHCI is deprecated and only selects proper driver so there is no need to compile test it. Since it selects USB_EHCI_BIG_ENDIAN_MMIO it causes compilation failures on certain big endian architectures (e.g. m68k): In file included from drivers/usb/host/ehci-mxc.c:19:0: drivers/usb/host/ehci.h: In function ‘ehci_readl’: drivers/usb/host/ehci.h:743:3: error: implicit declaration of function ‘readl_be’ [-Werror=implicit-function-declaration] Reported-by: kbuild test robot Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/1577778392-570-1-git-send-email-krzk@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index da14a3d16b57..803023fcb3fe 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -309,7 +309,7 @@ config USB_EHCI_HCD_PLATFORM config USB_OCTEON_EHCI bool "Octeon on-chip EHCI support (DEPRECATED)" - depends on CAVIUM_OCTEON_SOC || COMPILE_TEST + depends on CAVIUM_OCTEON_SOC select USB_EHCI_BIG_ENDIAN_MMIO if CPU_BIG_ENDIAN select USB_EHCI_HCD_PLATFORM help -- cgit v1.2.3-59-g8ed1b From 203b7ee14d3a38f1b8c44dd86ce0313d8fc4107d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 3 Jan 2020 17:47:10 +0100 Subject: phy: Enable compile testing for some of drivers Some of the phy drivers can be compile tested to increase build coverage. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20200103164710.4829-2-krzk@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/phy/allwinner/Kconfig | 3 ++- drivers/phy/broadcom/Kconfig | 4 ++-- drivers/phy/marvell/Kconfig | 8 +++++--- drivers/phy/mediatek/Kconfig | 10 +++++++--- drivers/phy/samsung/Kconfig | 8 ++++---- drivers/phy/ti/Kconfig | 4 ++-- 6 files changed, 22 insertions(+), 15 deletions(-) diff --git a/drivers/phy/allwinner/Kconfig b/drivers/phy/allwinner/Kconfig index 3dab79e9d52b..e760d89d3976 100644 --- a/drivers/phy/allwinner/Kconfig +++ b/drivers/phy/allwinner/Kconfig @@ -48,7 +48,8 @@ config PHY_SUN9I_USB config PHY_SUN50I_USB3 tristate "Allwinner H6 SoC USB3 PHY driver" - depends on ARCH_SUNXI && HAS_IOMEM && OF + depends on ARCH_SUNXI || COMPILE_TEST + depends on HAS_IOMEM && OF depends on RESET_CONTROLLER select GENERIC_PHY help diff --git a/drivers/phy/broadcom/Kconfig b/drivers/phy/broadcom/Kconfig index d3d983c128ea..b29f11c19155 100644 --- a/drivers/phy/broadcom/Kconfig +++ b/drivers/phy/broadcom/Kconfig @@ -50,7 +50,7 @@ config PHY_BCM_NS_USB3 config PHY_NS2_PCIE tristate "Broadcom Northstar2 PCIe PHY driver" - depends on OF && MDIO_BUS_MUX_BCM_IPROC + depends on (OF && MDIO_BUS_MUX_BCM_IPROC) || (COMPILE_TEST && MDIO_BUS) select GENERIC_PHY default ARCH_BCM_IPROC help @@ -83,7 +83,7 @@ config PHY_BRCM_SATA config PHY_BRCM_USB tristate "Broadcom STB USB PHY driver" - depends on ARCH_BRCMSTB + depends on ARCH_BRCMSTB || COMPILE_TEST depends on OF select GENERIC_PHY select SOC_BRCMSTB diff --git a/drivers/phy/marvell/Kconfig b/drivers/phy/marvell/Kconfig index 005e02dd4a91..8f6273c837ec 100644 --- a/drivers/phy/marvell/Kconfig +++ b/drivers/phy/marvell/Kconfig @@ -10,14 +10,16 @@ config ARMADA375_USBCLUSTER_PHY config PHY_BERLIN_SATA tristate "Marvell Berlin SATA PHY driver" - depends on ARCH_BERLIN && HAS_IOMEM && OF + depends on ARCH_BERLIN || COMPILE_TEST + depends on OF && HAS_IOMEM select GENERIC_PHY help Enable this to support the SATA PHY on Marvell Berlin SoCs. config PHY_BERLIN_USB tristate "Marvell Berlin USB PHY Driver" - depends on ARCH_BERLIN && RESET_CONTROLLER && HAS_IOMEM && OF + depends on ARCH_BERLIN || COMPILE_TEST + depends on OF && HAS_IOMEM && RESET_CONTROLLER select GENERIC_PHY help Enable this to support the USB PHY on Marvell Berlin SoCs. @@ -95,7 +97,7 @@ config PHY_PXA_28NM_USB2 config PHY_PXA_USB tristate "Marvell PXA USB PHY Driver" - depends on ARCH_PXA || ARCH_MMP + depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST select GENERIC_PHY help Enable this to support Marvell PXA USB PHY driver for Marvell diff --git a/drivers/phy/mediatek/Kconfig b/drivers/phy/mediatek/Kconfig index 376f5d189da0..81cfea156bcd 100644 --- a/drivers/phy/mediatek/Kconfig +++ b/drivers/phy/mediatek/Kconfig @@ -4,7 +4,9 @@ # config PHY_MTK_TPHY tristate "MediaTek T-PHY Driver" - depends on ARCH_MEDIATEK && OF + depends on ARCH_MEDIATEK && OF + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on OF select GENERIC_PHY help Say 'Y' here to add support for MediaTek T-PHY driver, @@ -16,7 +18,8 @@ config PHY_MTK_TPHY config PHY_MTK_UFS tristate "MediaTek UFS M-PHY driver" - depends on ARCH_MEDIATEK && OF + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on OF select GENERIC_PHY help Support for UFS M-PHY on MediaTek chipsets. @@ -26,7 +29,8 @@ config PHY_MTK_UFS config PHY_MTK_XSPHY tristate "MediaTek XS-PHY Driver" - depends on ARCH_MEDIATEK && OF + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on OF select GENERIC_PHY help Enable this to support the SuperSpeedPlus XS-PHY transceiver for diff --git a/drivers/phy/samsung/Kconfig b/drivers/phy/samsung/Kconfig index 290a6c70f570..349fcb23e5f3 100644 --- a/drivers/phy/samsung/Kconfig +++ b/drivers/phy/samsung/Kconfig @@ -32,7 +32,7 @@ config PHY_EXYNOS_PCIE config PHY_SAMSUNG_USB2 tristate "Samsung USB 2.0 PHY driver" depends on HAS_IOMEM - depends on USB_EHCI_EXYNOS || USB_OHCI_EXYNOS || USB_DWC2 + depends on USB_EHCI_EXYNOS || USB_OHCI_EXYNOS || USB_DWC2 || COMPILE_TEST select GENERIC_PHY select MFD_SYSCON default ARCH_EXYNOS @@ -60,7 +60,7 @@ config PHY_EXYNOS5250_USB2 config PHY_S5PV210_USB2 bool "Support for S5PV210" depends on PHY_SAMSUNG_USB2 - depends on ARCH_S5PV210 + depends on ARCH_S5PV210 || COMPILE_TEST help Enable USB PHY support for S5PV210. This option requires that Samsung USB 2.0 PHY driver is enabled and means that support for this @@ -69,7 +69,7 @@ config PHY_S5PV210_USB2 config PHY_EXYNOS5_USBDRD tristate "Exynos5 SoC series USB DRD PHY driver" - depends on ARCH_EXYNOS && OF + depends on (ARCH_EXYNOS && OF) || COMPILE_TEST depends on HAS_IOMEM depends on USB_DWC3_EXYNOS select GENERIC_PHY @@ -82,7 +82,7 @@ config PHY_EXYNOS5_USBDRD config PHY_EXYNOS5250_SATA tristate "Exynos5250 Sata SerDes/PHY driver" - depends on SOC_EXYNOS5250 + depends on SOC_EXYNOS5250 || COMPILE_TEST depends on HAS_IOMEM depends on OF select GENERIC_PHY diff --git a/drivers/phy/ti/Kconfig b/drivers/phy/ti/Kconfig index 174888609779..e231c0e369c5 100644 --- a/drivers/phy/ti/Kconfig +++ b/drivers/phy/ti/Kconfig @@ -4,7 +4,7 @@ # config PHY_DA8XX_USB tristate "TI DA8xx USB PHY Driver" - depends on ARCH_DAVINCI_DA8XX + depends on ARCH_DAVINCI_DA8XX || COMPILE_TEST select GENERIC_PHY select MFD_SYSCON help @@ -14,7 +14,7 @@ config PHY_DA8XX_USB config PHY_DM816X_USB tristate "TI dm816x USB PHY driver" - depends on ARCH_OMAP2PLUS + depends on ARCH_OMAP2PLUS || COMPILE_TEST depends on USB_SUPPORT select GENERIC_PHY select USB_PHY -- cgit v1.2.3-59-g8ed1b From a00dfd4ddd4871eb87a5216970a338bf381abfbe Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 20 Dec 2019 02:35:25 +0900 Subject: tools: usb: usbip: Get rid of driver name printout in README Driver name is no longer printed out so update the README examples to avoid confusion. Signed-off-by: Magnus Damm Acked-by: Shuah Khan Link: https://lore.kernel.org/r/157677692518.684.15385402529285904844.sendpatchset@octo Signed-off-by: Greg Kroah-Hartman --- tools/usb/usbip/README | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tools/usb/usbip/README b/tools/usb/usbip/README index 7844490fc603..2fc021c0eae1 100644 --- a/tools/usb/usbip/README +++ b/tools/usb/usbip/README @@ -138,28 +138,28 @@ attached to this host. Local USB devices ================= - busid 1-1 (05a9:a511) - 1-1:1.0 -> ov511 + 1-1:1.0 - busid 3-2 (0711:0902) - 3-2:1.0 -> none + 3-2:1.0 - busid 3-3.1 (08bb:2702) - 3-3.1:1.0 -> snd-usb-audio - 3-3.1:1.1 -> snd-usb-audio + 3-3.1:1.0 + 3-3.1:1.1 - busid 3-3.2 (04bb:0206) - 3-3.2:1.0 -> usb-storage + 3-3.2:1.0 - busid 3-3 (0409:0058) - 3-3:1.0 -> hub + 3-3:1.0 - busid 4-1 (046d:08b2) - 4-1:1.0 -> none - 4-1:1.1 -> none - 4-1:1.2 -> none + 4-1:1.0 + 4-1:1.1 + 4-1:1.2 - busid 5-2 (058f:9254) - 5-2:1.0 -> hub + 5-2:1.0 A USB storage device of busid 3-3.2 is now bound to the usb-storage driver. To export this device, we first mark the device as @@ -180,7 +180,7 @@ Mark the device of busid 3-3.2 as exportable: ... - busid 3-3.2 (04bb:0206) - 3-3.2:1.0 -> usbip-host + 3-3.2:1.0 ... --------------------------- -- cgit v1.2.3-59-g8ed1b From 987351e1ea7772cf2f0795e917fb33b2e282e1c1 Mon Sep 17 00:00:00 2001 From: Alexandre Torgue Date: Mon, 4 Nov 2019 15:37:13 +0100 Subject: phy: core: Add consumer device link support In order to enforce suspend/resume ordering, this commit creates link between phy consumers and phy devices. This link avoids to suspend phy before phy consumers. Signed-off-by: Alexandre Torgue [jonathanh@nvidia.com: Fix an abort when of_phy_get() returns error] Signed-off-by: Jonathan Hunter Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-core.c | 49 +++++++++++++++++++++++++++++++++++---- drivers/usb/renesas_usbhs/rcar2.c | 2 +- drivers/usb/renesas_usbhs/rza2.c | 2 +- include/linux/phy/phy.h | 9 +++++-- 4 files changed, 53 insertions(+), 9 deletions(-) diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index b04f4fe85ac2..2eb28cc2d2dc 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -29,7 +29,7 @@ static void devm_phy_release(struct device *dev, void *res) { struct phy *phy = *(struct phy **)res; - phy_put(phy); + phy_put(dev, phy); } static void devm_phy_provider_release(struct device *dev, void *res) @@ -566,12 +566,12 @@ struct phy *of_phy_get(struct device_node *np, const char *con_id) EXPORT_SYMBOL_GPL(of_phy_get); /** - * phy_put() - release the PHY - * @phy: the phy returned by phy_get() + * of_phy_put() - release the PHY + * @phy: the phy returned by of_phy_get() * - * Releases a refcount the caller received from phy_get(). + * Releases a refcount the caller received from of_phy_get(). */ -void phy_put(struct phy *phy) +void of_phy_put(struct phy *phy) { if (!phy || IS_ERR(phy)) return; @@ -584,6 +584,20 @@ void phy_put(struct phy *phy) module_put(phy->ops->owner); put_device(&phy->dev); } +EXPORT_SYMBOL_GPL(of_phy_put); + +/** + * phy_put() - release the PHY + * @dev: device that wants to release this phy + * @phy: the phy returned by phy_get() + * + * Releases a refcount the caller received from phy_get(). + */ +void phy_put(struct device *dev, struct phy *phy) +{ + device_link_remove(dev, &phy->dev); + of_phy_put(phy); +} EXPORT_SYMBOL_GPL(phy_put); /** @@ -651,6 +665,7 @@ struct phy *phy_get(struct device *dev, const char *string) { int index = 0; struct phy *phy; + struct device_link *link; if (string == NULL) { dev_WARN(dev, "missing string\n"); @@ -672,6 +687,13 @@ struct phy *phy_get(struct device *dev, const char *string) get_device(&phy->dev); + link = device_link_add(dev, &phy->dev, DL_FLAG_STATELESS); + if (!link) { + dev_err(dev, "failed to create device link to %s\n", + dev_name(phy->dev.parent)); + return ERR_PTR(-EINVAL); + } + return phy; } EXPORT_SYMBOL_GPL(phy_get); @@ -765,6 +787,7 @@ struct phy *devm_of_phy_get(struct device *dev, struct device_node *np, const char *con_id) { struct phy **ptr, *phy; + struct device_link *link; ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL); if (!ptr) @@ -776,6 +799,14 @@ struct phy *devm_of_phy_get(struct device *dev, struct device_node *np, devres_add(dev, ptr); } else { devres_free(ptr); + return phy; + } + + link = device_link_add(dev, &phy->dev, DL_FLAG_STATELESS); + if (!link) { + dev_err(dev, "failed to create device link to %s\n", + dev_name(phy->dev.parent)); + return ERR_PTR(-EINVAL); } return phy; @@ -798,6 +829,7 @@ struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np, int index) { struct phy **ptr, *phy; + struct device_link *link; ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL); if (!ptr) @@ -819,6 +851,13 @@ struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np, *ptr = phy; devres_add(dev, ptr); + link = device_link_add(dev, &phy->dev, DL_FLAG_STATELESS); + if (!link) { + dev_err(dev, "failed to create device link to %s\n", + dev_name(phy->dev.parent)); + return ERR_PTR(-EINVAL); + } + return phy; } EXPORT_SYMBOL_GPL(devm_of_phy_get_by_index); diff --git a/drivers/usb/renesas_usbhs/rcar2.c b/drivers/usb/renesas_usbhs/rcar2.c index 440d213e1749..791908f8cf73 100644 --- a/drivers/usb/renesas_usbhs/rcar2.c +++ b/drivers/usb/renesas_usbhs/rcar2.c @@ -34,7 +34,7 @@ static int usbhs_rcar2_hardware_exit(struct platform_device *pdev) struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); if (priv->phy) { - phy_put(priv->phy); + phy_put(&pdev->dev, priv->phy); priv->phy = NULL; } diff --git a/drivers/usb/renesas_usbhs/rza2.c b/drivers/usb/renesas_usbhs/rza2.c index 021749594389..3eed3334a17f 100644 --- a/drivers/usb/renesas_usbhs/rza2.c +++ b/drivers/usb/renesas_usbhs/rza2.c @@ -29,7 +29,7 @@ static int usbhs_rza2_hardware_exit(struct platform_device *pdev) { struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); - phy_put(priv->phy); + phy_put(&pdev->dev, priv->phy); priv->phy = NULL; return 0; diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index 56d3a100006a..19eddd64c8f6 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -234,7 +234,8 @@ struct phy *devm_of_phy_get(struct device *dev, struct device_node *np, const char *con_id); struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np, int index); -void phy_put(struct phy *phy); +void of_phy_put(struct phy *phy); +void phy_put(struct device *dev, struct phy *phy); void devm_phy_put(struct device *dev, struct phy *phy); struct phy *of_phy_get(struct device_node *np, const char *con_id); struct phy *of_phy_simple_xlate(struct device *dev, @@ -419,7 +420,11 @@ static inline struct phy *devm_of_phy_get_by_index(struct device *dev, return ERR_PTR(-ENOSYS); } -static inline void phy_put(struct phy *phy) +static inline void of_phy_put(struct phy *phy) +{ +} + +static inline void phy_put(struct device *dev, struct phy *phy) { } -- cgit v1.2.3-59-g8ed1b From a89806c998ee123bb9c0f18526e55afd12c0c0ab Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Tue, 17 Dec 2019 18:36:37 -0700 Subject: phy: qualcomm: Adjust indentation in read_poll_timeout Clang warns: ../drivers/phy/qualcomm/phy-qcom-apq8064-sata.c:83:4: warning: misleading indentation; statement is not part of the previous 'if' [-Wmisleading-indentation] usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50); ^ ../drivers/phy/qualcomm/phy-qcom-apq8064-sata.c:80:3: note: previous statement is here if (readl_relaxed(addr) & mask) ^ 1 warning generated. This warning occurs because there is a space after the tab on this line. Remove it so that the indentation is consistent with the Linux kernel coding style and clang no longer warns. Fixes: 1de990d8a169 ("phy: qcom: Add driver for QCOM APQ8064 SATA PHY") Link: https://github.com/ClangBuiltLinux/linux/issues/816 Signed-off-by: Nathan Chancellor Reviewed-by: Bjorn Andersson Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/qualcomm/phy-qcom-apq8064-sata.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c b/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c index 42bc5150dd92..febe0aef68d4 100644 --- a/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c +++ b/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c @@ -80,7 +80,7 @@ static int read_poll_timeout(void __iomem *addr, u32 mask) if (readl_relaxed(addr) & mask) return 0; - usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50); + usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50); } while (!time_after(jiffies, timeout)); return (readl_relaxed(addr) & mask) ? 0 : -ETIMEDOUT; -- cgit v1.2.3-59-g8ed1b From 6f69e2a330932756b0baf772b54a82b0e33748db Mon Sep 17 00:00:00 2001 From: Ma Feng Date: Thu, 19 Dec 2019 11:24:38 +0800 Subject: phy: lantiq: vrx200-pcie: Remove unneeded semicolon Fixes coccicheck warning: drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c:389:2-3: Unneeded semicolon Fixes: e52a632195bf ("phy: lantiq: vrx200-pcie: add a driver for the Lantiq VRX200 PCIe PHY") Reported-by: Hulk Robot Signed-off-by: Ma Feng Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c b/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c index 6e457967653e..2ff9a48d833e 100644 --- a/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c +++ b/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c @@ -386,7 +386,7 @@ static struct phy *ltq_vrx200_pcie_phy_xlate(struct device *dev, default: dev_err(dev, "invalid PHY mode %u\n", mode); return ERR_PTR(-EINVAL); - }; + } return priv->phy; } -- cgit v1.2.3-59-g8ed1b From cb18b9a92b0baaa3188d67d1371079c1eacb3454 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Mon, 16 Dec 2019 13:24:47 +0100 Subject: dt-bindings: phy: drop #clock-cells from rockchip,px30-dsi-dphy Further review of the dsi components for the px30 revealed that the phy shouldn't expose the pll as clock but instead handle settings via phy parameters. As the phy binding is new and not used anywhere yet, just drop them so they don't get used. Fixes: 3817c7961179 ("dt-bindings: phy: add yaml binding for rockchip,px30-dsi-dphy") Signed-off-by: Heiko Stuebner Signed-off-by: Kishon Vijay Abraham I --- Documentation/devicetree/bindings/phy/rockchip,px30-dsi-dphy.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/rockchip,px30-dsi-dphy.yaml b/Documentation/devicetree/bindings/phy/rockchip,px30-dsi-dphy.yaml index bb0da87bcd84..476c56a1dc8c 100644 --- a/Documentation/devicetree/bindings/phy/rockchip,px30-dsi-dphy.yaml +++ b/Documentation/devicetree/bindings/phy/rockchip,px30-dsi-dphy.yaml @@ -13,9 +13,6 @@ properties: "#phy-cells": const: 0 - "#clock-cells": - const: 0 - compatible: enum: - rockchip,px30-dsi-dphy @@ -49,7 +46,6 @@ properties: required: - "#phy-cells" - - "#clock-cells" - compatible - reg - clocks @@ -66,7 +62,6 @@ examples: reg = <0x0 0xff2e0000 0x0 0x10000>; clocks = <&pmucru 13>, <&cru 12>; clock-names = "ref", "pclk"; - #clock-cells = <0>; resets = <&cru 12>; reset-names = "apb"; #phy-cells = <0>; -- cgit v1.2.3-59-g8ed1b From f0684c1a836770afba7a7097e61935edd69693bf Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Fri, 8 Nov 2019 01:06:40 +0100 Subject: phy/rockchip: inno-dsidphy: generalize parameter handling During review it came to light that exposing the pll clock outside is not the right approach and struct phy_configure_opts_mipi_dphy exists just for that reason to transfer parameters to the phy. So drop the exposed clock and rely on the phy configure options to bring in the correct rate. That way we can also just drop the open coded timing struct and default values function. Fixes: b7535a3bc0ba ("phy/rockchip: Add support for Innosilicon MIPI/LVDS/TTL PHY") Signed-off-by: Heiko Stuebner Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/rockchip/Kconfig | 1 + drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c | 319 +++++++---------------- 2 files changed, 100 insertions(+), 220 deletions(-) diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig index dbd2de4d28b1..0824b9dd5683 100644 --- a/drivers/phy/rockchip/Kconfig +++ b/drivers/phy/rockchip/Kconfig @@ -39,6 +39,7 @@ config PHY_ROCKCHIP_INNO_DSIDPHY tristate "Rockchip Innosilicon MIPI/LVDS/TTL PHY driver" depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF select GENERIC_PHY + select GENERIC_PHY_MIPI_DPHY help Enable this to support the Rockchip MIPI/LVDS/TTL PHY with Innosilicon IP block. diff --git a/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c b/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c index fc729ecd3fe9..a7c6c940a3a8 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -167,31 +168,6 @@ #define DSI_PHY_STATUS 0xb0 #define PHY_LOCK BIT(0) -struct mipi_dphy_timing { - unsigned int clkmiss; - unsigned int clkpost; - unsigned int clkpre; - unsigned int clkprepare; - unsigned int clksettle; - unsigned int clktermen; - unsigned int clktrail; - unsigned int clkzero; - unsigned int dtermen; - unsigned int eot; - unsigned int hsexit; - unsigned int hsprepare; - unsigned int hszero; - unsigned int hssettle; - unsigned int hsskip; - unsigned int hstrail; - unsigned int init; - unsigned int lpx; - unsigned int taget; - unsigned int tago; - unsigned int tasure; - unsigned int wakeup; -}; - struct inno_dsidphy { struct device *dev; struct clk *ref_clk; @@ -201,7 +177,9 @@ struct inno_dsidphy { void __iomem *host_base; struct reset_control *rst; enum phy_mode mode; + struct phy_configure_opts_mipi_dphy dphy_cfg; + struct clk *pll_clk; struct { struct clk_hw hw; u8 prediv; @@ -238,37 +216,79 @@ static void phy_update_bits(struct inno_dsidphy *inno, writel(tmp, inno->phy_base + reg); } -static void mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing, - unsigned long period) +static unsigned long inno_dsidphy_pll_calc_rate(struct inno_dsidphy *inno, + unsigned long rate) { - /* Global Operation Timing Parameters */ - timing->clkmiss = 0; - timing->clkpost = 70000 + 52 * period; - timing->clkpre = 8 * period; - timing->clkprepare = 65000; - timing->clksettle = 95000; - timing->clktermen = 0; - timing->clktrail = 80000; - timing->clkzero = 260000; - timing->dtermen = 0; - timing->eot = 0; - timing->hsexit = 120000; - timing->hsprepare = 65000 + 4 * period; - timing->hszero = 145000 + 6 * period; - timing->hssettle = 85000 + 6 * period; - timing->hsskip = 40000; - timing->hstrail = max(8 * period, 60000 + 4 * period); - timing->init = 100000000; - timing->lpx = 60000; - timing->taget = 5 * timing->lpx; - timing->tago = 4 * timing->lpx; - timing->tasure = 2 * timing->lpx; - timing->wakeup = 1000000000; + unsigned long prate = clk_get_rate(inno->ref_clk); + unsigned long best_freq = 0; + unsigned long fref, fout; + u8 min_prediv, max_prediv; + u8 _prediv, best_prediv = 1; + u16 _fbdiv, best_fbdiv = 1; + u32 min_delta = UINT_MAX; + + /* + * The PLL output frequency can be calculated using a simple formula: + * PLL_Output_Frequency = (FREF / PREDIV * FBDIV) / 2 + * PLL_Output_Frequency: it is equal to DDR-Clock-Frequency * 2 + */ + fref = prate / 2; + if (rate > 1000000000UL) + fout = 1000000000UL; + else + fout = rate; + + /* 5Mhz < Fref / prediv < 40MHz */ + min_prediv = DIV_ROUND_UP(fref, 40000000); + max_prediv = fref / 5000000; + + for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) { + u64 tmp; + u32 delta; + + tmp = (u64)fout * _prediv; + do_div(tmp, fref); + _fbdiv = tmp; + + /* + * The possible settings of feedback divider are + * 12, 13, 14, 16, ~ 511 + */ + if (_fbdiv == 15) + continue; + + if (_fbdiv < 12 || _fbdiv > 511) + continue; + + tmp = (u64)_fbdiv * fref; + do_div(tmp, _prediv); + + delta = abs(fout - tmp); + if (!delta) { + best_prediv = _prediv; + best_fbdiv = _fbdiv; + best_freq = tmp; + break; + } else if (delta < min_delta) { + best_prediv = _prediv; + best_fbdiv = _fbdiv; + best_freq = tmp; + min_delta = delta; + } + } + + if (best_freq) { + inno->pll.prediv = best_prediv; + inno->pll.fbdiv = best_fbdiv; + inno->pll.rate = best_freq; + } + + return best_freq; } static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno) { - struct mipi_dphy_timing gotp; + struct phy_configure_opts_mipi_dphy *cfg = &inno->dphy_cfg; const struct { unsigned long rate; u8 hs_prepare; @@ -288,12 +308,14 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno) { 800000000, 0x21, 0x1f, 0x09, 0x29}, {1000000000, 0x09, 0x20, 0x09, 0x27}, }; - u32 t_txbyteclkhs, t_txclkesc, ui; + u32 t_txbyteclkhs, t_txclkesc; u32 txbyteclkhs, txclkesc, esc_clk_div; u32 hs_exit, clk_post, clk_pre, wakeup, lpx, ta_go, ta_sure, ta_wait; u32 hs_prepare, hs_trail, hs_zero, clk_lane_hs_zero, data_lane_hs_zero; unsigned int i; + inno_dsidphy_pll_calc_rate(inno, cfg->hs_clk_rate); + /* Select MIPI mode */ phy_update_bits(inno, REGISTER_PART_LVDS, 0x03, MODE_ENABLE_MASK, MIPI_MODE_ENABLE); @@ -328,32 +350,27 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno) txclkesc = txbyteclkhs / esc_clk_div; t_txclkesc = div_u64(PSEC_PER_SEC, txclkesc); - ui = div_u64(PSEC_PER_SEC, inno->pll.rate); - - memset(&gotp, 0, sizeof(gotp)); - mipi_dphy_timing_get_default(&gotp, ui); - /* * The value of counter for HS Ths-exit * Ths-exit = Tpin_txbyteclkhs * value */ - hs_exit = DIV_ROUND_UP(gotp.hsexit, t_txbyteclkhs); + hs_exit = DIV_ROUND_UP(cfg->hs_exit, t_txbyteclkhs); /* * The value of counter for HS Tclk-post * Tclk-post = Tpin_txbyteclkhs * value */ - clk_post = DIV_ROUND_UP(gotp.clkpost, t_txbyteclkhs); + clk_post = DIV_ROUND_UP(cfg->clk_post, t_txbyteclkhs); /* * The value of counter for HS Tclk-pre * Tclk-pre = Tpin_txbyteclkhs * value */ - clk_pre = DIV_ROUND_UP(gotp.clkpre, t_txbyteclkhs); + clk_pre = DIV_ROUND_UP(cfg->clk_pre, t_txbyteclkhs); /* * The value of counter for HS Tlpx Time * Tlpx = Tpin_txbyteclkhs * (2 + value) */ - lpx = DIV_ROUND_UP(gotp.lpx, t_txbyteclkhs); + lpx = DIV_ROUND_UP(cfg->lpx, t_txbyteclkhs); if (lpx >= 2) lpx -= 2; @@ -362,19 +379,19 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno) * Tta-go for turnaround * Tta-go = Ttxclkesc * value */ - ta_go = DIV_ROUND_UP(gotp.tago, t_txclkesc); + ta_go = DIV_ROUND_UP(cfg->ta_go, t_txclkesc); /* * The value of counter for HS Tta-sure * Tta-sure for turnaround * Tta-sure = Ttxclkesc * value */ - ta_sure = DIV_ROUND_UP(gotp.tasure, t_txclkesc); + ta_sure = DIV_ROUND_UP(cfg->ta_sure, t_txclkesc); /* * The value of counter for HS Tta-wait * Tta-wait for turnaround * Tta-wait = Ttxclkesc * value */ - ta_wait = DIV_ROUND_UP(gotp.taget, t_txclkesc); + ta_wait = DIV_ROUND_UP(cfg->ta_get, t_txclkesc); for (i = 0; i < ARRAY_SIZE(timings); i++) if (inno->pll.rate <= timings[i].rate) @@ -479,6 +496,7 @@ static int inno_dsidphy_power_on(struct phy *phy) struct inno_dsidphy *inno = phy_get_drvdata(phy); clk_prepare_enable(inno->pclk_phy); + clk_prepare_enable(inno->ref_clk); pm_runtime_get_sync(inno->dev); /* Bandgap power on */ @@ -524,6 +542,7 @@ static int inno_dsidphy_power_off(struct phy *phy) LVDS_PLL_POWER_OFF | LVDS_BANDGAP_POWER_DOWN); pm_runtime_put(inno->dev); + clk_disable_unprepare(inno->ref_clk); clk_disable_unprepare(inno->pclk_phy); return 0; @@ -546,168 +565,32 @@ static int inno_dsidphy_set_mode(struct phy *phy, enum phy_mode mode, return 0; } -static const struct phy_ops inno_dsidphy_ops = { - .set_mode = inno_dsidphy_set_mode, - .power_on = inno_dsidphy_power_on, - .power_off = inno_dsidphy_power_off, - .owner = THIS_MODULE, -}; - -static unsigned long inno_dsidphy_pll_round_rate(struct inno_dsidphy *inno, - unsigned long prate, - unsigned long rate, - u8 *prediv, u16 *fbdiv) -{ - unsigned long best_freq = 0; - unsigned long fref, fout; - u8 min_prediv, max_prediv; - u8 _prediv, best_prediv = 1; - u16 _fbdiv, best_fbdiv = 1; - u32 min_delta = UINT_MAX; - - /* - * The PLL output frequency can be calculated using a simple formula: - * PLL_Output_Frequency = (FREF / PREDIV * FBDIV) / 2 - * PLL_Output_Frequency: it is equal to DDR-Clock-Frequency * 2 - */ - fref = prate / 2; - if (rate > 1000000000UL) - fout = 1000000000UL; - else - fout = rate; - - /* 5Mhz < Fref / prediv < 40MHz */ - min_prediv = DIV_ROUND_UP(fref, 40000000); - max_prediv = fref / 5000000; - - for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) { - u64 tmp; - u32 delta; - - tmp = (u64)fout * _prediv; - do_div(tmp, fref); - _fbdiv = tmp; - - /* - * The possible settings of feedback divider are - * 12, 13, 14, 16, ~ 511 - */ - if (_fbdiv == 15) - continue; - - if (_fbdiv < 12 || _fbdiv > 511) - continue; - - tmp = (u64)_fbdiv * fref; - do_div(tmp, _prediv); - - delta = abs(fout - tmp); - if (!delta) { - best_prediv = _prediv; - best_fbdiv = _fbdiv; - best_freq = tmp; - break; - } else if (delta < min_delta) { - best_prediv = _prediv; - best_fbdiv = _fbdiv; - best_freq = tmp; - min_delta = delta; - } - } - - if (best_freq) { - *prediv = best_prediv; - *fbdiv = best_fbdiv; - } - - return best_freq; -} - -static long inno_dsidphy_pll_clk_round_rate(struct clk_hw *hw, - unsigned long rate, - unsigned long *prate) +static int inno_dsidphy_configure(struct phy *phy, + union phy_configure_opts *opts) { - struct inno_dsidphy *inno = hw_to_inno(hw); - unsigned long fout; - u16 fbdiv = 1; - u8 prediv = 1; - - fout = inno_dsidphy_pll_round_rate(inno, *prate, rate, - &prediv, &fbdiv); - - return fout; -} - -static int inno_dsidphy_pll_clk_set_rate(struct clk_hw *hw, - unsigned long rate, - unsigned long parent_rate) -{ - struct inno_dsidphy *inno = hw_to_inno(hw); - unsigned long fout; - u16 fbdiv = 1; - u8 prediv = 1; + struct inno_dsidphy *inno = phy_get_drvdata(phy); + int ret; - fout = inno_dsidphy_pll_round_rate(inno, parent_rate, rate, - &prediv, &fbdiv); + if (inno->mode != PHY_MODE_MIPI_DPHY) + return -EINVAL; - dev_dbg(inno->dev, "fin=%lu, fout=%lu, prediv=%u, fbdiv=%u\n", - parent_rate, fout, prediv, fbdiv); + ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy); + if (ret) + return ret; - inno->pll.prediv = prediv; - inno->pll.fbdiv = fbdiv; - inno->pll.rate = fout; + memcpy(&inno->dphy_cfg, &opts->mipi_dphy, sizeof(inno->dphy_cfg)); return 0; } -static unsigned long -inno_dsidphy_pll_clk_recalc_rate(struct clk_hw *hw, unsigned long prate) -{ - struct inno_dsidphy *inno = hw_to_inno(hw); - - /* PLL_Output_Frequency = (FREF / PREDIV * FBDIV) / 2 */ - return (prate / inno->pll.prediv * inno->pll.fbdiv) / 2; -} - -static const struct clk_ops inno_dsidphy_pll_clk_ops = { - .round_rate = inno_dsidphy_pll_clk_round_rate, - .set_rate = inno_dsidphy_pll_clk_set_rate, - .recalc_rate = inno_dsidphy_pll_clk_recalc_rate, +static const struct phy_ops inno_dsidphy_ops = { + .configure = inno_dsidphy_configure, + .set_mode = inno_dsidphy_set_mode, + .power_on = inno_dsidphy_power_on, + .power_off = inno_dsidphy_power_off, + .owner = THIS_MODULE, }; -static int inno_dsidphy_pll_register(struct inno_dsidphy *inno) -{ - struct device *dev = inno->dev; - struct clk *clk; - const char *parent_name; - struct clk_init_data init; - int ret; - - parent_name = __clk_get_name(inno->ref_clk); - - init.name = "mipi_dphy_pll"; - ret = of_property_read_string(dev->of_node, "clock-output-names", - &init.name); - if (ret < 0) - dev_dbg(dev, "phy should set clock-output-names property\n"); - - init.ops = &inno_dsidphy_pll_clk_ops; - init.parent_names = &parent_name; - init.num_parents = 1; - init.flags = 0; - - inno->pll.hw.init = &init; - clk = devm_clk_register(dev, &inno->pll.hw); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - dev_err(dev, "failed to register PLL: %d\n", ret); - return ret; - } - - return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, - &inno->pll.hw); -} - static int inno_dsidphy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -764,10 +647,6 @@ static int inno_dsidphy_probe(struct platform_device *pdev) return ret; } - ret = inno_dsidphy_pll_register(inno); - if (ret) - return ret; - pm_runtime_enable(dev); return 0; -- cgit v1.2.3-59-g8ed1b From d0c05c68d669e8fccd50ab64c79154def5221588 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 10 Dec 2019 12:08:51 -0800 Subject: dt-bindings: phy: Document BCM7216 SATA PHY compatible string Define "brcm,bcm7216-sata-phy" as a new compatible string for the Broadcom SATA3 PHY. Signed-off-by: Florian Fainelli Acked-by: Rob Herring Signed-off-by: Kishon Vijay Abraham I --- Documentation/devicetree/bindings/phy/brcm-sata-phy.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/phy/brcm-sata-phy.txt b/Documentation/devicetree/bindings/phy/brcm-sata-phy.txt index b640845fec67..c03ad2198410 100644 --- a/Documentation/devicetree/bindings/phy/brcm-sata-phy.txt +++ b/Documentation/devicetree/bindings/phy/brcm-sata-phy.txt @@ -2,6 +2,7 @@ Required properties: - compatible: should be one or more of + "brcm,bcm7216-sata-phy" "brcm,bcm7425-sata-phy" "brcm,bcm7445-sata-phy" "brcm,iproc-ns2-sata-phy" -- cgit v1.2.3-59-g8ed1b From 978442532e57b84985f236761d557f1d99c67449 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 10 Dec 2019 12:08:52 -0800 Subject: phy: brcm-sata: Implement 7216 initialization sequence 7216 is a 16nm process chip with a slightly different version of the PHY SerdDeS/AFE that requires a specific tuning sequence. Key on the compatible string to perform that initialization. Signed-off-by: Florian Fainelli Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/broadcom/phy-brcm-sata.c | 120 +++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/drivers/phy/broadcom/phy-brcm-sata.c b/drivers/phy/broadcom/phy-brcm-sata.c index 50ac75bbb0c9..4710cfcc3037 100644 --- a/drivers/phy/broadcom/phy-brcm-sata.c +++ b/drivers/phy/broadcom/phy-brcm-sata.c @@ -33,6 +33,7 @@ #define SATA_PHY_CTRL_REG_28NM_SPACE_SIZE 0x8 enum brcm_sata_phy_version { + BRCM_SATA_PHY_STB_16NM, BRCM_SATA_PHY_STB_28NM, BRCM_SATA_PHY_STB_40NM, BRCM_SATA_PHY_IPROC_NS2, @@ -104,10 +105,13 @@ enum sata_phy_regs { PLL1_ACTRL5 = 0x85, PLL1_ACTRL6 = 0x86, PLL1_ACTRL7 = 0x87, + PLL1_ACTRL8 = 0x88, TX_REG_BANK = 0x070, TX_ACTRL0 = 0x80, TX_ACTRL0_TXPOL_FLIP = BIT(6), + TX_ACTRL5 = 0x85, + TX_ACTRL5_SSC_EN = BIT(11), AEQRX_REG_BANK_0 = 0xd0, AEQ_CONTROL1 = 0x81, @@ -116,6 +120,7 @@ enum sata_phy_regs { AEQ_FRC_EQ = 0x83, AEQ_FRC_EQ_FORCE = BIT(0), AEQ_FRC_EQ_FORCE_VAL = BIT(1), + AEQ_RFZ_FRC_VAL = BIT(8), AEQRX_REG_BANK_1 = 0xe0, AEQRX_SLCAL0_CTRL0 = 0x82, AEQRX_SLCAL1_CTRL0 = 0x86, @@ -152,7 +157,28 @@ enum sata_phy_regs { TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK = 0x3ff, RXPMD_REG_BANK = 0x1c0, + RXPMD_RX_CDR_CONTROL1 = 0x81, + RXPMD_RX_PPM_VAL_MASK = 0x1ff, + RXPMD_RXPMD_EN_FRC = BIT(12), + RXPMD_RXPMD_EN_FRC_VAL = BIT(13), + RXPMD_RX_CDR_CDR_PROP_BW = 0x82, + RXPMD_G_CDR_PROP_BW_MASK = 0x7, + RXPMD_G1_CDR_PROP_BW_SHIFT = 0, + RXPMD_G2_CDR_PROP_BW_SHIFT = 3, + RXPMD_G3_CDR_PROB_BW_SHIFT = 6, + RXPMD_RX_CDR_CDR_ACQ_INTEG_BW = 0x83, + RXPMD_G_CDR_ACQ_INT_BW_MASK = 0x7, + RXPMD_G1_CDR_ACQ_INT_BW_SHIFT = 0, + RXPMD_G2_CDR_ACQ_INT_BW_SHIFT = 3, + RXPMD_G3_CDR_ACQ_INT_BW_SHIFT = 6, + RXPMD_RX_CDR_CDR_LOCK_INTEG_BW = 0x84, + RXPMD_G_CDR_LOCK_INT_BW_MASK = 0x7, + RXPMD_G1_CDR_LOCK_INT_BW_SHIFT = 0, + RXPMD_G2_CDR_LOCK_INT_BW_SHIFT = 3, + RXPMD_G3_CDR_LOCK_INT_BW_SHIFT = 6, RXPMD_RX_FREQ_MON_CONTROL1 = 0x87, + RXPMD_MON_CORRECT_EN = BIT(8), + RXPMD_MON_MARGIN_VAL_MASK = 0xff, }; enum sata_phy_ctrl_regs { @@ -166,6 +192,7 @@ static inline void __iomem *brcm_sata_pcb_base(struct brcm_sata_port *port) u32 size = 0; switch (priv->version) { + case BRCM_SATA_PHY_STB_16NM: case BRCM_SATA_PHY_STB_28NM: case BRCM_SATA_PHY_IPROC_NS2: case BRCM_SATA_PHY_DSL_28NM: @@ -287,6 +314,94 @@ static int brcm_stb_sata_init(struct brcm_sata_port *port) return brcm_stb_sata_rxaeq_init(port); } +static int brcm_stb_sata_16nm_ssc_init(struct brcm_sata_port *port) +{ + void __iomem *base = brcm_sata_pcb_base(port); + u32 tmp, value; + + /* Reduce CP tail current to 1/16th of its default value */ + brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL6, 0, 0x141); + + /* Turn off CP tail current boost */ + brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL8, 0, 0xc006); + + /* Set a specific AEQ equalizer value */ + tmp = AEQ_FRC_EQ_FORCE_VAL | AEQ_FRC_EQ_FORCE; + brcm_sata_phy_wr(base, AEQRX_REG_BANK_0, AEQ_FRC_EQ, + ~(tmp | AEQ_RFZ_FRC_VAL | + AEQ_FRC_EQ_VAL_MASK << AEQ_FRC_EQ_VAL_SHIFT), + tmp | 32 << AEQ_FRC_EQ_VAL_SHIFT); + + /* Set RX PPM val center frequency */ + if (port->ssc_en) + value = 0x52; + else + value = 0; + brcm_sata_phy_wr(base, RXPMD_REG_BANK, RXPMD_RX_CDR_CONTROL1, + ~RXPMD_RX_PPM_VAL_MASK, value); + + /* Set proportional loop bandwith Gen1/2/3 */ + tmp = RXPMD_G_CDR_PROP_BW_MASK << RXPMD_G1_CDR_PROP_BW_SHIFT | + RXPMD_G_CDR_PROP_BW_MASK << RXPMD_G2_CDR_PROP_BW_SHIFT | + RXPMD_G_CDR_PROP_BW_MASK << RXPMD_G3_CDR_PROB_BW_SHIFT; + if (port->ssc_en) + value = 2 << RXPMD_G1_CDR_PROP_BW_SHIFT | + 2 << RXPMD_G2_CDR_PROP_BW_SHIFT | + 2 << RXPMD_G3_CDR_PROB_BW_SHIFT; + else + value = 1 << RXPMD_G1_CDR_PROP_BW_SHIFT | + 1 << RXPMD_G2_CDR_PROP_BW_SHIFT | + 1 << RXPMD_G3_CDR_PROB_BW_SHIFT; + brcm_sata_phy_wr(base, RXPMD_REG_BANK, RXPMD_RX_CDR_CDR_PROP_BW, ~tmp, + value); + + /* Set CDR integral loop acquisition bandwidth for Gen1/2/3 */ + tmp = RXPMD_G_CDR_ACQ_INT_BW_MASK << RXPMD_G1_CDR_ACQ_INT_BW_SHIFT | + RXPMD_G_CDR_ACQ_INT_BW_MASK << RXPMD_G2_CDR_ACQ_INT_BW_SHIFT | + RXPMD_G_CDR_ACQ_INT_BW_MASK << RXPMD_G3_CDR_ACQ_INT_BW_SHIFT; + if (port->ssc_en) + value = 1 << RXPMD_G1_CDR_ACQ_INT_BW_SHIFT | + 1 << RXPMD_G2_CDR_ACQ_INT_BW_SHIFT | + 1 << RXPMD_G3_CDR_ACQ_INT_BW_SHIFT; + else + value = 0; + brcm_sata_phy_wr(base, RXPMD_REG_BANK, RXPMD_RX_CDR_CDR_ACQ_INTEG_BW, + ~tmp, value); + + /* Set CDR integral loop locking bandwidth to 1 for Gen 1/2/3 */ + tmp = RXPMD_G_CDR_LOCK_INT_BW_MASK << RXPMD_G1_CDR_LOCK_INT_BW_SHIFT | + RXPMD_G_CDR_LOCK_INT_BW_MASK << RXPMD_G2_CDR_LOCK_INT_BW_SHIFT | + RXPMD_G_CDR_LOCK_INT_BW_MASK << RXPMD_G3_CDR_LOCK_INT_BW_SHIFT; + if (port->ssc_en) + value = 1 << RXPMD_G1_CDR_LOCK_INT_BW_SHIFT | + 1 << RXPMD_G2_CDR_LOCK_INT_BW_SHIFT | + 1 << RXPMD_G3_CDR_LOCK_INT_BW_SHIFT; + else + value = 0; + brcm_sata_phy_wr(base, RXPMD_REG_BANK, RXPMD_RX_CDR_CDR_LOCK_INTEG_BW, + ~tmp, value); + + /* Set no guard band and clamp CDR */ + tmp = RXPMD_MON_CORRECT_EN | RXPMD_MON_MARGIN_VAL_MASK; + if (port->ssc_en) + value = 0x51; + else + value = 0; + brcm_sata_phy_wr(base, RXPMD_REG_BANK, RXPMD_RX_FREQ_MON_CONTROL1, + ~tmp, RXPMD_MON_CORRECT_EN | value); + + /* Turn on/off SSC */ + brcm_sata_phy_wr(base, TX_REG_BANK, TX_ACTRL5, ~TX_ACTRL5_SSC_EN, + port->ssc_en ? TX_ACTRL5_SSC_EN : 0); + + return 0; +} + +static int brcm_stb_sata_16nm_init(struct brcm_sata_port *port) +{ + return brcm_stb_sata_16nm_ssc_init(port); +} + /* NS2 SATA PLL1 defaults were characterized by H/W group */ #define NS2_PLL1_ACTRL2_MAGIC 0x1df8 #define NS2_PLL1_ACTRL3_MAGIC 0x2b00 @@ -544,6 +659,9 @@ static int brcm_sata_phy_init(struct phy *phy) struct brcm_sata_port *port = phy_get_drvdata(phy); switch (port->phy_priv->version) { + case BRCM_SATA_PHY_STB_16NM: + rc = brcm_stb_sata_16nm_init(port); + break; case BRCM_SATA_PHY_STB_28NM: case BRCM_SATA_PHY_STB_40NM: rc = brcm_stb_sata_init(port); @@ -601,6 +719,8 @@ static const struct phy_ops phy_ops = { }; static const struct of_device_id brcm_sata_phy_of_match[] = { + { .compatible = "brcm,bcm7216-sata-phy", + .data = (void *)BRCM_SATA_PHY_STB_16NM }, { .compatible = "brcm,bcm7445-sata-phy", .data = (void *)BRCM_SATA_PHY_STB_28NM }, { .compatible = "brcm,bcm7425-sata-phy", -- cgit v1.2.3-59-g8ed1b From 730430dceeb55957169c5a33df88720db0673a9b Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 23 Dec 2019 20:00:43 +0530 Subject: phy: qcom-qmp: Use register defines We already define register offsets so use them in register layout. Signed-off-by: Vinod Koul Reviewed-by: Manu Gautam Reviewed-by: Can Guo Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/qualcomm/phy-qcom-qmp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 091e20303a14..7f6ff5da7233 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -166,8 +166,8 @@ static const unsigned int sdm845_ufsphy_regs_layout[] = { }; static const unsigned int sm8150_ufsphy_regs_layout[] = { - [QPHY_START_CTRL] = 0x00, - [QPHY_PCS_READY_STATUS] = 0x180, + [QPHY_START_CTRL] = QPHY_V4_PHY_START, + [QPHY_PCS_READY_STATUS] = QPHY_V4_PCS_READY_STATUS, }; static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = { -- cgit v1.2.3-59-g8ed1b From 01240af0138b9fa76a17e12b31a33fbce30c5786 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 23 Dec 2019 20:00:44 +0530 Subject: phy: qcom-qmp: remove duplicate powerdown write We already write to QPHY_POWER_DOWN_CONTROL in qcom_qmp_phy_com_init() before invoking qcom_qmp_phy_configure() so remove the duplicate write. Signed-off-by: Vinod Koul Reviewed-by: Manu Gautam Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/qualcomm/phy-qcom-qmp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 7f6ff5da7233..aece1a9fb9aa 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -885,7 +885,6 @@ static const struct qmp_phy_init_tbl msm8998_usb3_pcs_tbl[] = { }; static const struct qmp_phy_init_tbl sm8150_ufsphy_serdes_tbl[] = { - QMP_PHY_INIT_CFG(QPHY_POWER_DOWN_CONTROL, 0x01), QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0xd9), QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x11), QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_HS_SWITCH_SEL, 0x00), -- cgit v1.2.3-59-g8ed1b From 7d59e8e8fdd0d690c7b6956d591125321a508c5f Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 23 Dec 2019 20:00:45 +0530 Subject: phy: qcom-qmp: remove no_pcs_sw_reset for sm8150 SM8150 QMPY phy for UFS and onwards the PHY_SW_RESET is present in PHY's PCS register so we should not mark no_pcs_sw_reset for sm8150 and onwards Signed-off-by: Vinod Koul Reviewed-by: Manu Gautam Reviewed-by: Can Guo Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/qualcomm/phy-qcom-qmp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index aece1a9fb9aa..dee5616253f5 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -1389,7 +1389,6 @@ static const struct qmp_phy_cfg sm8150_ufsphy_cfg = { .pwrdn_ctrl = SW_PWRDN, .is_dual_lane_phy = true, - .no_pcs_sw_reset = true, }; static void qcom_qmp_phy_configure(void __iomem *base, -- cgit v1.2.3-59-g8ed1b From d0312fdbf3e1dc34bc370b17fee290921cf9b814 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 23 Dec 2019 20:00:46 +0530 Subject: phy: qcom-qmp: Add SW reset register For V4 QMP UFS Phy, we need to assert reset bits, configure the phy and then deassert it, so add the QPHY_SW_RESET register which does this. Signed-off-by: Vinod Koul Reviewed-by: Manu Gautam Reviewed-by: Can Guo Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/qualcomm/phy-qcom-qmp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index dee5616253f5..45c9de4a6f55 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -168,6 +168,7 @@ static const unsigned int sdm845_ufsphy_regs_layout[] = { static const unsigned int sm8150_ufsphy_regs_layout[] = { [QPHY_START_CTRL] = QPHY_V4_PHY_START, [QPHY_PCS_READY_STATUS] = QPHY_V4_PCS_READY_STATUS, + [QPHY_SW_RESET] = QPHY_V4_SW_RESET, }; static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = { -- cgit v1.2.3-59-g8ed1b From dc9aa43c43668481089c48135707ec3f8f5b2e19 Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Fri, 3 Jan 2020 13:17:59 -0500 Subject: phy: usb: EHCI DMA may lose a burst of DMA data for 7255xA0 family When the EHCI controller received a 512 byte USB packet that had to be broken into 2 256 byte bursts across the SCB bus AND there was a following 512 byte USB packet, the second burst of data from the first packet was sometimes being lost. If the burst size was changed to 128 bytes via the EBR_SCB_SIZE field in the USB_CTRL_EBRIDGE register we'd see the 4th 128 byte burst of the first packet being lost. This problem became much worse if other threads were running that accessed memory, like a memcpy test. Setting the EBR_SCB_SIZE to 512, which prevents breaking the EHCI USB packet (max size of 512 bytes) into bursts, fixed the problem. Signed-off-by: Al Cooper Reviewed-by: Florian Fainelli Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/broadcom/phy-brcm-usb-init.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.c b/drivers/phy/broadcom/phy-brcm-usb-init.c index 91b5b09589d6..bd473d12ab28 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init.c @@ -42,6 +42,7 @@ #define USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK 0x80000000 /* option */ #define USB_CTRL_EBRIDGE 0x0c #define USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK 0x00020000 /* option */ +#define USB_CTRL_EBRIDGE_EBR_SCB_SIZE_MASK 0x00000f80 /* option */ #define USB_CTRL_OBRIDGE 0x10 #define USB_CTRL_OBRIDGE_LS_KEEP_ALIVE_MASK 0x08000000 #define USB_CTRL_MDIO 0x14 @@ -176,6 +177,7 @@ static const struct id_to_type id_to_type_table[] = { { 0x33900000, BRCM_FAMILY_3390A0 }, { 0x72500010, BRCM_FAMILY_7250B0 }, { 0x72600000, BRCM_FAMILY_7260A0 }, + { 0x72550000, BRCM_FAMILY_7260A0 }, { 0x72680000, BRCM_FAMILY_7271A0 }, { 0x72710000, BRCM_FAMILY_7271A0 }, { 0x73640000, BRCM_FAMILY_7364A0 }, @@ -948,6 +950,17 @@ void brcm_usb_init_eohci(struct brcm_usb_init_params *params) if (params->selected_family == BRCM_FAMILY_7271A0) /* Enable LS keep alive fix for certain keyboards */ USB_CTRL_SET(ctrl, OBRIDGE, LS_KEEP_ALIVE); + + if (params->family_id == 0x72550000) { + /* + * Make the burst size 512 bytes to fix a hardware bug + * on the 7255a0. See HW7255-24. + */ + reg = brcmusb_readl(USB_CTRL_REG(ctrl, EBRIDGE)); + reg &= ~USB_CTRL_MASK(EBRIDGE, EBR_SCB_SIZE); + reg |= 0x800; + brcmusb_writel(reg, USB_CTRL_REG(ctrl, EBRIDGE)); + } } void brcm_usb_init_xhci(struct brcm_usb_init_params *params) -- cgit v1.2.3-59-g8ed1b From ece5ffd9e15e9c8471e58b581a098032a679d34e Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Fri, 3 Jan 2020 13:18:00 -0500 Subject: phy: usb: Get all drivers that use USB clks using correct enable/disable The BRCM USB Phy, ohci, ehci and xhci drivers all use the USB clocks but not all drivers use the clk_prepare_enable/clk_disable_unprepare versions to enable/disable the clocks. This change gets all drivers using the prepare version. Signed-off-by: Al Cooper Reviewed-by: Florian Fainelli Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/broadcom/phy-brcm-usb.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c index f5c1f2983a1d..217e3702ef4e 100644 --- a/drivers/phy/broadcom/phy-brcm-usb.c +++ b/drivers/phy/broadcom/phy-brcm-usb.c @@ -74,8 +74,8 @@ static int brcm_usb_phy_init(struct phy *gphy) */ mutex_lock(&priv->mutex); if (priv->init_count++ == 0) { - clk_enable(priv->usb_20_clk); - clk_enable(priv->usb_30_clk); + clk_prepare_enable(priv->usb_20_clk); + clk_prepare_enable(priv->usb_30_clk); brcm_usb_init_common(&priv->ini); } mutex_unlock(&priv->mutex); @@ -106,8 +106,8 @@ static int brcm_usb_phy_exit(struct phy *gphy) mutex_lock(&priv->mutex); if (--priv->init_count == 0) { brcm_usb_uninit_common(&priv->ini); - clk_disable(priv->usb_20_clk); - clk_disable(priv->usb_30_clk); + clk_disable_unprepare(priv->usb_20_clk); + clk_disable_unprepare(priv->usb_30_clk); } mutex_unlock(&priv->mutex); phy->inited = false; @@ -360,8 +360,8 @@ static int brcm_usb_phy_probe(struct platform_device *pdev) if (priv->has_eohci) brcm_usb_uninit_eohci(&priv->ini); brcm_usb_uninit_common(&priv->ini); - clk_disable(priv->usb_20_clk); - clk_disable(priv->usb_30_clk); + clk_disable_unprepare(priv->usb_20_clk); + clk_disable_unprepare(priv->usb_30_clk); phy_provider = devm_of_phy_provider_register(dev, brcm_usb_phy_xlate); @@ -381,8 +381,8 @@ static int brcm_usb_phy_suspend(struct device *dev) struct brcm_usb_phy_data *priv = dev_get_drvdata(dev); if (priv->init_count) { - clk_disable(priv->usb_20_clk); - clk_disable(priv->usb_30_clk); + clk_disable_unprepare(priv->usb_20_clk); + clk_disable_unprepare(priv->usb_30_clk); } return 0; } @@ -391,8 +391,8 @@ static int brcm_usb_phy_resume(struct device *dev) { struct brcm_usb_phy_data *priv = dev_get_drvdata(dev); - clk_enable(priv->usb_20_clk); - clk_enable(priv->usb_30_clk); + clk_prepare_enable(priv->usb_20_clk); + clk_prepare_enable(priv->usb_30_clk); brcm_usb_init_ipp(&priv->ini); /* @@ -405,13 +405,13 @@ static int brcm_usb_phy_resume(struct device *dev) brcm_usb_init_eohci(&priv->ini); } else if (priv->has_eohci) { brcm_usb_uninit_eohci(&priv->ini); - clk_disable(priv->usb_20_clk); + clk_disable_unprepare(priv->usb_20_clk); } if (priv->phys[BRCM_USB_PHY_3_0].inited) { brcm_usb_init_xhci(&priv->ini); } else if (priv->has_xhci) { brcm_usb_uninit_xhci(&priv->ini); - clk_disable(priv->usb_30_clk); + clk_disable_unprepare(priv->usb_30_clk); } } else { if (priv->has_xhci) @@ -419,8 +419,8 @@ static int brcm_usb_phy_resume(struct device *dev) if (priv->has_eohci) brcm_usb_uninit_eohci(&priv->ini); brcm_usb_uninit_common(&priv->ini); - clk_disable(priv->usb_20_clk); - clk_disable(priv->usb_30_clk); + clk_disable_unprepare(priv->usb_20_clk); + clk_disable_unprepare(priv->usb_30_clk); } return 0; -- cgit v1.2.3-59-g8ed1b From 6597af4e4835ec0709638d48f73c11b5c624790f Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Fri, 3 Jan 2020 13:18:01 -0500 Subject: phy: usb: Put USB phys into IDDQ on suspend to save power in S2 mode Currently the Phy driver will put the USB phys into the max power saving mode (IDDQ) when there is no corresponding XHCI, EHCI or OHCI client (through rmmod, unbind or if the driver is not builtin). This change will also put the Phys into IDDQ mode on suspend so that S2 will get the additional power savings. Signed-off-by: Al Cooper Reviewed-by: Florian Fainelli Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/broadcom/phy-brcm-usb-init.c | 2 -- drivers/phy/broadcom/phy-brcm-usb.c | 11 +++++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.c b/drivers/phy/broadcom/phy-brcm-usb-init.c index bd473d12ab28..ac7f7995c11f 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init.c @@ -1002,8 +1002,6 @@ void brcm_usb_uninit_common(struct brcm_usb_init_params *params) void brcm_usb_uninit_eohci(struct brcm_usb_init_params *params) { - if (USB_CTRL_MASK_FAMILY(params, USB_PM, USB20_HC_RESETB)) - USB_CTRL_UNSET_FAMILY(params, USB_PM, USB20_HC_RESETB); } void brcm_usb_uninit_xhci(struct brcm_usb_init_params *params) diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c index 217e3702ef4e..634afc803778 100644 --- a/drivers/phy/broadcom/phy-brcm-usb.c +++ b/drivers/phy/broadcom/phy-brcm-usb.c @@ -381,8 +381,15 @@ static int brcm_usb_phy_suspend(struct device *dev) struct brcm_usb_phy_data *priv = dev_get_drvdata(dev); if (priv->init_count) { - clk_disable_unprepare(priv->usb_20_clk); - clk_disable_unprepare(priv->usb_30_clk); + if (priv->phys[BRCM_USB_PHY_3_0].inited) + brcm_usb_uninit_xhci(&priv->ini); + if (priv->phys[BRCM_USB_PHY_2_0].inited) + brcm_usb_uninit_eohci(&priv->ini); + brcm_usb_uninit_common(&priv->ini); + if (priv->phys[BRCM_USB_PHY_3_0].inited) + clk_disable_unprepare(priv->usb_30_clk); + if (priv->phys[BRCM_USB_PHY_2_0].inited) + clk_disable_unprepare(priv->usb_20_clk); } return 0; } -- cgit v1.2.3-59-g8ed1b From f1c0db40a3ade1f1a39e5794d728f2953d817322 Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Fri, 3 Jan 2020 13:18:02 -0500 Subject: phy: usb: Add "wake on" functionality Add the ability to handle USB wake events from USB devices when in S2 mode. Typically there is some additional configuration needed to tell the USB device to generate the wake event when suspended but this varies with the different USB device classes. For example, on USB Ethernet dongles, ethtool should be used to enable the magic packet wake functionality in the dongle. NOTE: This requires that the "power/wakeup" sysfs entry for the USB device generating the wakeup be set to "enabled". This functionality requires a special hardware sideband path that will trigger the AON_PM_L2 interrupt needed to wake the system from S2 even though the USB host controllers are in IDDQ (low power state) and most USB related clocks are shut off. For the sideband signaling to work we need to leave the usbx_freerun clock running, but this clock consumes very little power by design. There's a bug in the XHCI wake hardware so only EHCI/OHCI wake is currently supported. Signed-off-by: Al Cooper Reviewed-by: Florian Fainelli Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/broadcom/phy-brcm-usb-init.c | 17 +++++++++++ drivers/phy/broadcom/phy-brcm-usb-init.h | 1 + drivers/phy/broadcom/phy-brcm-usb.c | 48 ++++++++++++++++++++++++++++++-- 3 files changed, 63 insertions(+), 3 deletions(-) diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.c b/drivers/phy/broadcom/phy-brcm-usb-init.c index ac7f7995c11f..58882c10396a 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init.c @@ -58,6 +58,8 @@ #define USB_CTRL_USB_PM_SOFT_RESET_MASK 0x40000000 /* option */ #define USB_CTRL_USB_PM_USB20_HC_RESETB_MASK 0x30000000 /* option */ #define USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK 0x00300000 /* option */ +#define USB_CTRL_USB_PM_RMTWKUP_EN_MASK 0x00000001 +#define USB_CTRL_USB_PM_STATUS 0x38 #define USB_CTRL_USB30_CTL1 0x60 #define USB_CTRL_USB30_CTL1_PHY3_PLL_SEQ_START_MASK 0x00000010 #define USB_CTRL_USB30_CTL1_PHY3_RESETB_MASK 0x00010000 @@ -855,6 +857,10 @@ void brcm_usb_init_common(struct brcm_usb_init_params *params) u32 reg; void __iomem *ctrl = params->ctrl_regs; + /* Clear any pending wake conditions */ + reg = brcmusb_readl(USB_CTRL_REG(ctrl, USB_PM_STATUS)); + brcmusb_writel(reg, USB_CTRL_REG(ctrl, USB_PM_STATUS)); + /* Take USB out of power down */ if (USB_CTRL_MASK_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN)) { USB_CTRL_UNSET_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN); @@ -1010,6 +1016,17 @@ void brcm_usb_uninit_xhci(struct brcm_usb_init_params *params) USB_CTRL_SET(params->ctrl_regs, USB30_PCTL, PHY3_IDDQ_OVERRIDE); } +void brcm_usb_wake_enable(struct brcm_usb_init_params *params, + int enable) +{ + void __iomem *ctrl = params->ctrl_regs; + + if (enable) + USB_CTRL_SET(ctrl, USB_PM, RMTWKUP_EN); + else + USB_CTRL_UNSET(ctrl, USB_PM, RMTWKUP_EN); +} + void brcm_usb_set_family_map(struct brcm_usb_init_params *params) { int fam; diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.h b/drivers/phy/broadcom/phy-brcm-usb-init.h index f4f4f6d5d258..f473e0c51f0b 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.h +++ b/drivers/phy/broadcom/phy-brcm-usb-init.h @@ -38,5 +38,6 @@ void brcm_usb_init_xhci(struct brcm_usb_init_params *ini); void brcm_usb_uninit_common(struct brcm_usb_init_params *ini); void brcm_usb_uninit_eohci(struct brcm_usb_init_params *ini); void brcm_usb_uninit_xhci(struct brcm_usb_init_params *ini); +void brcm_usb_wake_enable(struct brcm_usb_init_params *params, int enable); #endif /* _USB_BRCM_COMMON_INIT_H */ diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c index 634afc803778..cca04d60f2d2 100644 --- a/drivers/phy/broadcom/phy-brcm-usb.c +++ b/drivers/phy/broadcom/phy-brcm-usb.c @@ -57,11 +57,22 @@ struct brcm_usb_phy_data { bool has_xhci; struct clk *usb_20_clk; struct clk *usb_30_clk; + struct clk *suspend_clk; struct mutex mutex; /* serialize phy init */ int init_count; + int wake_irq; struct brcm_usb_phy phys[BRCM_USB_PHY_ID_MAX]; }; +static irqreturn_t brcm_usb_phy_wake_isr(int irq, void *dev_id) +{ + struct phy *gphy = dev_id; + + pm_wakeup_event(&gphy->dev, 0); + + return IRQ_HANDLED; +} + static int brcm_usb_phy_init(struct phy *gphy) { struct brcm_usb_phy *phy = phy_get_drvdata(gphy); @@ -76,6 +87,7 @@ static int brcm_usb_phy_init(struct phy *gphy) if (priv->init_count++ == 0) { clk_prepare_enable(priv->usb_20_clk); clk_prepare_enable(priv->usb_30_clk); + clk_prepare_enable(priv->suspend_clk); brcm_usb_init_common(&priv->ini); } mutex_unlock(&priv->mutex); @@ -108,6 +120,7 @@ static int brcm_usb_phy_exit(struct phy *gphy) brcm_usb_uninit_common(&priv->ini); clk_disable_unprepare(priv->usb_20_clk); clk_disable_unprepare(priv->usb_30_clk); + clk_disable_unprepare(priv->suspend_clk); } mutex_unlock(&priv->mutex); phy->inited = false; @@ -228,11 +241,12 @@ static const struct attribute_group brcm_usb_phy_group = { .attrs = brcm_usb_phy_attrs, }; -static int brcm_usb_phy_dvr_init(struct device *dev, +static int brcm_usb_phy_dvr_init(struct platform_device *pdev, struct brcm_usb_phy_data *priv, struct device_node *dn) { - struct phy *gphy; + struct device *dev = &pdev->dev; + struct phy *gphy = NULL; int err; priv->usb_20_clk = of_clk_get_by_name(dn, "sw_usb"); @@ -275,6 +289,28 @@ static int brcm_usb_phy_dvr_init(struct device *dev, if (err) return err; } + + priv->suspend_clk = clk_get(dev, "usb0_freerun"); + if (IS_ERR(priv->suspend_clk)) { + dev_err(dev, "Suspend Clock not found in Device Tree\n"); + priv->suspend_clk = NULL; + } + + priv->wake_irq = platform_get_irq_byname(pdev, "wake"); + if (priv->wake_irq < 0) + priv->wake_irq = platform_get_irq_byname(pdev, "wakeup"); + if (priv->wake_irq >= 0) { + err = devm_request_irq(dev, priv->wake_irq, + brcm_usb_phy_wake_isr, 0, + dev_name(dev), gphy); + if (err < 0) + return err; + device_set_wakeup_capable(dev, 1); + } else { + dev_info(dev, + "Wake interrupt missing, system wake not supported\n"); + } + return 0; } @@ -335,7 +371,7 @@ static int brcm_usb_phy_probe(struct platform_device *pdev) if (of_property_read_bool(dn, "brcm,has-eohci")) priv->has_eohci = true; - err = brcm_usb_phy_dvr_init(dev, priv, dn); + err = brcm_usb_phy_dvr_init(pdev, priv, dn); if (err) return err; @@ -386,10 +422,13 @@ static int brcm_usb_phy_suspend(struct device *dev) if (priv->phys[BRCM_USB_PHY_2_0].inited) brcm_usb_uninit_eohci(&priv->ini); brcm_usb_uninit_common(&priv->ini); + brcm_usb_wake_enable(&priv->ini, true); if (priv->phys[BRCM_USB_PHY_3_0].inited) clk_disable_unprepare(priv->usb_30_clk); if (priv->phys[BRCM_USB_PHY_2_0].inited) clk_disable_unprepare(priv->usb_20_clk); + if (priv->wake_irq >= 0) + enable_irq_wake(priv->wake_irq); } return 0; } @@ -400,6 +439,7 @@ static int brcm_usb_phy_resume(struct device *dev) clk_prepare_enable(priv->usb_20_clk); clk_prepare_enable(priv->usb_30_clk); + brcm_usb_wake_enable(&priv->ini, false); brcm_usb_init_ipp(&priv->ini); /* @@ -407,6 +447,8 @@ static int brcm_usb_phy_resume(struct device *dev) * Uninitialize anything that wasn't previously initialized. */ if (priv->init_count) { + if (priv->wake_irq >= 0) + disable_irq_wake(priv->wake_irq); brcm_usb_init_common(&priv->ini); if (priv->phys[BRCM_USB_PHY_2_0].inited) { brcm_usb_init_eohci(&priv->ini); -- cgit v1.2.3-59-g8ed1b From 94583a41047eb9489f576344b8ba9370cf4cbfb7 Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Fri, 3 Jan 2020 13:18:03 -0500 Subject: phy: usb: Restructure in preparation for adding 7216 USB support The driver is being restructured in preparation for adding support for the new Synopsys USB conroller on the 7216. Since all the bugs and work-arounds in previous STB chips are supposed to be fixed, most of the code in phy-brcm-usb-init.c is not needed. Instead of adding more complexity to the already complicated phy-brcm-usb-init.c module, the driver will be restructured to use a vector table to dispatch into different C modules for the different controllers. There was also some general cleanup done including some ipp setup code that was incorrect. Signed-off-by: Al Cooper Reviewed-by: Florian Fainelli Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/broadcom/phy-brcm-usb-init.c | 191 ++++++++++++++----------------- drivers/phy/broadcom/phy-brcm-usb-init.h | 140 +++++++++++++++++++--- drivers/phy/broadcom/phy-brcm-usb.c | 6 +- 3 files changed, 214 insertions(+), 123 deletions(-) diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.c b/drivers/phy/broadcom/phy-brcm-usb-init.c index 58882c10396a..80d6f54d276e 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init.c @@ -129,10 +129,6 @@ enum { USB_CTRL_SELECTOR_COUNT, }; -#define USB_CTRL_REG(base, reg) ((void __iomem *)base + USB_CTRL_##reg) -#define USB_XHCI_EC_REG(base, reg) ((void __iomem *)base + USB_XHCI_EC_##reg) -#define USB_CTRL_MASK(reg, field) \ - USB_CTRL_##reg##_##field##_MASK #define USB_CTRL_MASK_FAMILY(params, reg, field) \ (params->usb_reg_bits_map[USB_CTRL_##reg##_##field##_SELECTOR]) @@ -143,13 +139,6 @@ enum { usb_ctrl_unset_family(params, USB_CTRL_##reg, \ USB_CTRL_##reg##_##field##_SELECTOR) -#define USB_CTRL_SET(base, reg, field) \ - usb_ctrl_set(USB_CTRL_REG(base, reg), \ - USB_CTRL_##reg##_##field##_MASK) -#define USB_CTRL_UNSET(base, reg, field) \ - usb_ctrl_unset(USB_CTRL_REG(base, reg), \ - USB_CTRL_##reg##_##field##_MASK) - #define MDIO_USB2 0 #define MDIO_USB3 BIT(31) @@ -405,26 +394,14 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { }, }; -static inline u32 brcmusb_readl(void __iomem *addr) -{ - return readl(addr); -} - -static inline void brcmusb_writel(u32 val, void __iomem *addr) -{ - writel(val, addr); -} - static inline void usb_ctrl_unset_family(struct brcm_usb_init_params *params, u32 reg_offset, u32 field) { u32 mask; - void __iomem *reg; mask = params->usb_reg_bits_map[field]; - reg = params->ctrl_regs + reg_offset; - brcmusb_writel(brcmusb_readl(reg) & ~mask, reg); + brcm_usb_ctrl_unset(params->ctrl_regs + reg_offset, mask); }; static inline @@ -432,45 +409,27 @@ void usb_ctrl_set_family(struct brcm_usb_init_params *params, u32 reg_offset, u32 field) { u32 mask; - void __iomem *reg; mask = params->usb_reg_bits_map[field]; - reg = params->ctrl_regs + reg_offset; - brcmusb_writel(brcmusb_readl(reg) | mask, reg); + brcm_usb_ctrl_set(params->ctrl_regs + reg_offset, mask); }; -static inline void usb_ctrl_set(void __iomem *reg, u32 field) -{ - u32 value; - - value = brcmusb_readl(reg); - brcmusb_writel(value | field, reg); -} - -static inline void usb_ctrl_unset(void __iomem *reg, u32 field) -{ - u32 value; - - value = brcmusb_readl(reg); - brcmusb_writel(value & ~field, reg); -} - static u32 brcmusb_usb_mdio_read(void __iomem *ctrl_base, u32 reg, int mode) { u32 data; data = (reg << 16) | mode; - brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO)); + brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO)); data |= (1 << 24); - brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO)); + brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO)); data &= ~(1 << 24); /* wait for the 60MHz parallel to serial shifter */ usleep_range(10, 20); - brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO)); + brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO)); /* wait for the 60MHz parallel to serial shifter */ usleep_range(10, 20); - return brcmusb_readl(USB_CTRL_REG(ctrl_base, MDIO2)) & 0xffff; + return brcm_usb_readl(USB_CTRL_REG(ctrl_base, MDIO2)) & 0xffff; } static void brcmusb_usb_mdio_write(void __iomem *ctrl_base, u32 reg, @@ -479,14 +438,14 @@ static void brcmusb_usb_mdio_write(void __iomem *ctrl_base, u32 reg, u32 data; data = (reg << 16) | val | mode; - brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO)); + brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO)); data |= (1 << 25); - brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO)); + brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO)); data &= ~(1 << 25); /* wait for the 60MHz parallel to serial shifter */ usleep_range(10, 20); - brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO)); + brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO)); /* wait for the 60MHz parallel to serial shifter */ usleep_range(10, 20); } @@ -713,12 +672,12 @@ static void brcmusb_usb3_otp_fix(struct brcm_usb_init_params *params) if (params->family_id != 0x74371000 || !xhci_ec_base) return; - brcmusb_writel(0xa20c, USB_XHCI_EC_REG(xhci_ec_base, IRAADR)); - val = brcmusb_readl(USB_XHCI_EC_REG(xhci_ec_base, IRADAT)); + brcm_usb_writel(0xa20c, USB_XHCI_EC_REG(xhci_ec_base, IRAADR)); + val = brcm_usb_readl(USB_XHCI_EC_REG(xhci_ec_base, IRADAT)); /* set cfg_pick_ss_lock */ val |= (1 << 27); - brcmusb_writel(val, USB_XHCI_EC_REG(xhci_ec_base, IRADAT)); + brcm_usb_writel(val, USB_XHCI_EC_REG(xhci_ec_base, IRADAT)); /* Reset USB 3.0 PHY for workaround to take effect */ USB_CTRL_UNSET(params->ctrl_regs, USB30_CTL1, PHY3_RESETB); @@ -751,7 +710,7 @@ static void brcmusb_xhci_soft_reset(struct brcm_usb_init_params *params, * - default chip/rev. * NOTE: The minor rev is always ignored. */ -static enum brcm_family_type brcmusb_get_family_type( +static enum brcm_family_type get_family_type( struct brcm_usb_init_params *params) { int last_type = -1; @@ -779,7 +738,7 @@ static enum brcm_family_type brcmusb_get_family_type( return last_type; } -void brcm_usb_init_ipp(struct brcm_usb_init_params *params) +static void usb_init_ipp(struct brcm_usb_init_params *params) { void __iomem *ctrl = params->ctrl_regs; u32 reg; @@ -795,7 +754,7 @@ void brcm_usb_init_ipp(struct brcm_usb_init_params *params) USB_CTRL_SET_FAMILY(params, USB30_CTL1, USB3_IPP); } - reg = brcmusb_readl(USB_CTRL_REG(ctrl, SETUP)); + reg = brcm_usb_readl(USB_CTRL_REG(ctrl, SETUP)); orig_reg = reg; if (USB_CTRL_MASK_FAMILY(params, SETUP, STRAP_CC_DRD_MODE_ENABLE_SEL)) /* Never use the strap, it's going away. */ @@ -803,8 +762,8 @@ void brcm_usb_init_ipp(struct brcm_usb_init_params *params) SETUP, STRAP_CC_DRD_MODE_ENABLE_SEL)); if (USB_CTRL_MASK_FAMILY(params, SETUP, STRAP_IPP_SEL)) + /* override ipp strap pin (if it exits) */ if (params->ipp != 2) - /* override ipp strap pin (if it exits) */ reg &= ~(USB_CTRL_MASK_FAMILY(params, SETUP, STRAP_IPP_SEL)); @@ -812,54 +771,26 @@ void brcm_usb_init_ipp(struct brcm_usb_init_params *params) reg &= ~(USB_CTRL_MASK(SETUP, IPP) | USB_CTRL_MASK(SETUP, IOC)); if (params->ioc) reg |= USB_CTRL_MASK(SETUP, IOC); - if (params->ipp == 1 && ((reg & USB_CTRL_MASK(SETUP, IPP)) == 0)) + if (params->ipp == 1) reg |= USB_CTRL_MASK(SETUP, IPP); - brcmusb_writel(reg, USB_CTRL_REG(ctrl, SETUP)); + brcm_usb_writel(reg, USB_CTRL_REG(ctrl, SETUP)); /* * If we're changing IPP, make sure power is off long enough * to turn off any connected devices. */ - if (reg != orig_reg) + if ((reg ^ orig_reg) & USB_CTRL_MASK(SETUP, IPP)) msleep(50); } -int brcm_usb_init_get_dual_select(struct brcm_usb_init_params *params) -{ - void __iomem *ctrl = params->ctrl_regs; - u32 reg = 0; - - if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) { - reg = brcmusb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); - reg &= USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, - PORT_MODE); - } - return reg; -} - -void brcm_usb_init_set_dual_select(struct brcm_usb_init_params *params, - int mode) -{ - void __iomem *ctrl = params->ctrl_regs; - u32 reg; - - if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) { - reg = brcmusb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); - reg &= ~USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, - PORT_MODE); - reg |= mode; - brcmusb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); - } -} - -void brcm_usb_init_common(struct brcm_usb_init_params *params) +static void usb_init_common(struct brcm_usb_init_params *params) { u32 reg; void __iomem *ctrl = params->ctrl_regs; /* Clear any pending wake conditions */ - reg = brcmusb_readl(USB_CTRL_REG(ctrl, USB_PM_STATUS)); - brcmusb_writel(reg, USB_CTRL_REG(ctrl, USB_PM_STATUS)); + reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_PM_STATUS)); + brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_PM_STATUS)); /* Take USB out of power down */ if (USB_CTRL_MASK_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN)) { @@ -885,7 +816,7 @@ void brcm_usb_init_common(struct brcm_usb_init_params *params) /* Block auto PLL suspend by USB2 PHY (Sasi) */ USB_CTRL_SET(ctrl, PLL_CTL, PLL_SUSPEND_EN); - reg = brcmusb_readl(USB_CTRL_REG(ctrl, SETUP)); + reg = brcm_usb_readl(USB_CTRL_REG(ctrl, SETUP)); if (params->selected_family == BRCM_FAMILY_7364A0) /* Suppress overcurrent indication from USB30 ports for A0 */ reg |= USB_CTRL_MASK_FAMILY(params, SETUP, OC3_DISABLE); @@ -901,16 +832,16 @@ void brcm_usb_init_common(struct brcm_usb_init_params *params) reg |= USB_CTRL_MASK_FAMILY(params, SETUP, SCB1_EN); if (USB_CTRL_MASK_FAMILY(params, SETUP, SCB2_EN)) reg |= USB_CTRL_MASK_FAMILY(params, SETUP, SCB2_EN); - brcmusb_writel(reg, USB_CTRL_REG(ctrl, SETUP)); + brcm_usb_writel(reg, USB_CTRL_REG(ctrl, SETUP)); brcmusb_memc_fix(params); if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) { - reg = brcmusb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); + reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); reg &= ~USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE); reg |= params->mode; - brcmusb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); + brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); } if (USB_CTRL_MASK_FAMILY(params, USB_PM, BDC_SOFT_RESETB)) { switch (params->mode) { @@ -932,7 +863,7 @@ void brcm_usb_init_common(struct brcm_usb_init_params *params) } } -void brcm_usb_init_eohci(struct brcm_usb_init_params *params) +static void usb_init_eohci(struct brcm_usb_init_params *params) { u32 reg; void __iomem *ctrl = params->ctrl_regs; @@ -948,10 +879,10 @@ void brcm_usb_init_eohci(struct brcm_usb_init_params *params) USB_CTRL_SET(ctrl, EBRIDGE, ESTOP_SCB_REQ); /* Setup the endian bits */ - reg = brcmusb_readl(USB_CTRL_REG(ctrl, SETUP)); + reg = brcm_usb_readl(USB_CTRL_REG(ctrl, SETUP)); reg &= ~USB_CTRL_SETUP_ENDIAN_BITS; reg |= USB_CTRL_MASK_FAMILY(params, SETUP, ENDIAN); - brcmusb_writel(reg, USB_CTRL_REG(ctrl, SETUP)); + brcm_usb_writel(reg, USB_CTRL_REG(ctrl, SETUP)); if (params->selected_family == BRCM_FAMILY_7271A0) /* Enable LS keep alive fix for certain keyboards */ @@ -962,14 +893,14 @@ void brcm_usb_init_eohci(struct brcm_usb_init_params *params) * Make the burst size 512 bytes to fix a hardware bug * on the 7255a0. See HW7255-24. */ - reg = brcmusb_readl(USB_CTRL_REG(ctrl, EBRIDGE)); + reg = brcm_usb_readl(USB_CTRL_REG(ctrl, EBRIDGE)); reg &= ~USB_CTRL_MASK(EBRIDGE, EBR_SCB_SIZE); reg |= 0x800; - brcmusb_writel(reg, USB_CTRL_REG(ctrl, EBRIDGE)); + brcm_usb_writel(reg, USB_CTRL_REG(ctrl, EBRIDGE)); } } -void brcm_usb_init_xhci(struct brcm_usb_init_params *params) +static void usb_init_xhci(struct brcm_usb_init_params *params) { void __iomem *ctrl = params->ctrl_regs; @@ -997,7 +928,7 @@ void brcm_usb_init_xhci(struct brcm_usb_init_params *params) brcmusb_usb3_otp_fix(params); } -void brcm_usb_uninit_common(struct brcm_usb_init_params *params) +static void usb_uninit_common(struct brcm_usb_init_params *params) { if (USB_CTRL_MASK_FAMILY(params, USB_PM, USB_PWRDN)) USB_CTRL_SET_FAMILY(params, USB_PM, USB_PWRDN); @@ -1006,17 +937,47 @@ void brcm_usb_uninit_common(struct brcm_usb_init_params *params) USB_CTRL_SET_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN); } -void brcm_usb_uninit_eohci(struct brcm_usb_init_params *params) +static void usb_uninit_eohci(struct brcm_usb_init_params *params) { } -void brcm_usb_uninit_xhci(struct brcm_usb_init_params *params) +static void usb_uninit_xhci(struct brcm_usb_init_params *params) { brcmusb_xhci_soft_reset(params, 1); USB_CTRL_SET(params->ctrl_regs, USB30_PCTL, PHY3_IDDQ_OVERRIDE); } -void brcm_usb_wake_enable(struct brcm_usb_init_params *params, +static int usb_get_dual_select(struct brcm_usb_init_params *params) +{ + void __iomem *ctrl = params->ctrl_regs; + u32 reg = 0; + + pr_debug("%s\n", __func__); + if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) { + reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); + reg &= USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, + PORT_MODE); + } + return reg; +} + +static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode) +{ + void __iomem *ctrl = params->ctrl_regs; + u32 reg; + + pr_debug("%s\n", __func__); + + if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) { + reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); + reg &= ~USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, + PORT_MODE); + reg |= mode; + brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); + } +} + +static void usb_wake_enable(struct brcm_usb_init_params *params, int enable) { void __iomem *ctrl = params->ctrl_regs; @@ -1027,13 +988,29 @@ void brcm_usb_wake_enable(struct brcm_usb_init_params *params, USB_CTRL_UNSET(ctrl, USB_PM, RMTWKUP_EN); } -void brcm_usb_set_family_map(struct brcm_usb_init_params *params) +static const struct brcm_usb_init_ops bcm7445_ops = { + .init_ipp = usb_init_ipp, + .init_common = usb_init_common, + .init_eohci = usb_init_eohci, + .init_xhci = usb_init_xhci, + .uninit_common = usb_uninit_common, + .uninit_eohci = usb_uninit_eohci, + .uninit_xhci = usb_uninit_xhci, + .get_dual_select = usb_get_dual_select, + .set_dual_select = usb_set_dual_select, + .wake_enable = usb_wake_enable, +}; + +void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params) { int fam; - fam = brcmusb_get_family_type(params); + pr_debug("%s\n", __func__); + + fam = get_family_type(params); params->selected_family = fam; params->usb_reg_bits_map = &usb_reg_bits_map_table[fam][0]; params->family_name = family_names[fam]; + params->ops = &bcm7445_ops; } diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.h b/drivers/phy/broadcom/phy-brcm-usb-init.h index f473e0c51f0b..7701872d1136 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.h +++ b/drivers/phy/broadcom/phy-brcm-usb-init.h @@ -13,6 +13,33 @@ struct brcm_usb_init_params; +#define USB_CTRL_REG(base, reg) ((void __iomem *)base + USB_CTRL_##reg) +#define USB_XHCI_EC_REG(base, reg) ((void __iomem *)base + USB_XHCI_EC_##reg) +#define USB_CTRL_MASK(reg, field) \ + USB_CTRL_##reg##_##field##_MASK +#define USB_CTRL_SET(base, reg, field) \ + brcm_usb_ctrl_set(USB_CTRL_REG(base, reg), \ + USB_CTRL_##reg##_##field##_MASK) +#define USB_CTRL_UNSET(base, reg, field) \ + brcm_usb_ctrl_unset(USB_CTRL_REG(base, reg), \ + USB_CTRL_##reg##_##field##_MASK) + +struct brcm_usb_init_params; + +struct brcm_usb_init_ops { + void (*init_ipp)(struct brcm_usb_init_params *params); + void (*init_common)(struct brcm_usb_init_params *params); + void (*init_eohci)(struct brcm_usb_init_params *params); + void (*init_xhci)(struct brcm_usb_init_params *params); + void (*uninit_common)(struct brcm_usb_init_params *params); + void (*uninit_eohci)(struct brcm_usb_init_params *params); + void (*uninit_xhci)(struct brcm_usb_init_params *params); + int (*get_dual_select)(struct brcm_usb_init_params *params); + void (*set_dual_select)(struct brcm_usb_init_params *params, int mode); + void (*wake_enable)(struct brcm_usb_init_params *params, + int enable); +}; + struct brcm_usb_init_params { void __iomem *ctrl_regs; void __iomem *xhci_ec_regs; @@ -24,20 +51,107 @@ struct brcm_usb_init_params { int selected_family; const char *family_name; const u32 *usb_reg_bits_map; + const struct brcm_usb_init_ops *ops; }; -void brcm_usb_set_family_map(struct brcm_usb_init_params *params); -int brcm_usb_init_get_dual_select(struct brcm_usb_init_params *params); -void brcm_usb_init_set_dual_select(struct brcm_usb_init_params *params, - int mode); - -void brcm_usb_init_ipp(struct brcm_usb_init_params *ini); -void brcm_usb_init_common(struct brcm_usb_init_params *ini); -void brcm_usb_init_eohci(struct brcm_usb_init_params *ini); -void brcm_usb_init_xhci(struct brcm_usb_init_params *ini); -void brcm_usb_uninit_common(struct brcm_usb_init_params *ini); -void brcm_usb_uninit_eohci(struct brcm_usb_init_params *ini); -void brcm_usb_uninit_xhci(struct brcm_usb_init_params *ini); -void brcm_usb_wake_enable(struct brcm_usb_init_params *params, int enable); +void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params); + +static inline u32 brcm_usb_readl(void __iomem *addr) +{ + /* + * MIPS endianness is configured by boot strap, which also reverses all + * bus endianness (i.e., big-endian CPU + big endian bus ==> native + * endian I/O). + * + * Other architectures (e.g., ARM) either do not support big endian, or + * else leave I/O in little endian mode. + */ + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN)) + return __raw_readl(addr); + else + return readl_relaxed(addr); +} + +static inline void brcm_usb_writel(u32 val, void __iomem *addr) +{ + /* See brcmnand_readl() comments */ + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN)) + __raw_writel(val, addr); + else + writel_relaxed(val, addr); +} + +static inline void brcm_usb_ctrl_unset(void __iomem *reg, u32 mask) +{ + brcm_usb_writel(brcm_usb_readl(reg) & ~(mask), reg); +}; + +static inline void brcm_usb_ctrl_set(void __iomem *reg, u32 mask) +{ + brcm_usb_writel(brcm_usb_readl(reg) | (mask), reg); +}; + +static inline void brcm_usb_init_ipp(struct brcm_usb_init_params *ini) +{ + if (ini->ops->init_ipp) + ini->ops->init_ipp(ini); +} + +static inline void brcm_usb_init_common(struct brcm_usb_init_params *ini) +{ + if (ini->ops->init_common) + ini->ops->init_common(ini); +} + +static inline void brcm_usb_init_eohci(struct brcm_usb_init_params *ini) +{ + if (ini->ops->init_eohci) + ini->ops->init_eohci(ini); +} + +static inline void brcm_usb_init_xhci(struct brcm_usb_init_params *ini) +{ + if (ini->ops->init_xhci) + ini->ops->init_xhci(ini); +} + +static inline void brcm_usb_uninit_common(struct brcm_usb_init_params *ini) +{ + if (ini->ops->uninit_common) + ini->ops->uninit_common(ini); +} + +static inline void brcm_usb_uninit_eohci(struct brcm_usb_init_params *ini) +{ + if (ini->ops->uninit_eohci) + ini->ops->uninit_eohci(ini); +} + +static inline void brcm_usb_uninit_xhci(struct brcm_usb_init_params *ini) +{ + if (ini->ops->uninit_xhci) + ini->ops->uninit_xhci(ini); +} + +static inline void brcm_usb_wake_enable(struct brcm_usb_init_params *ini, + int enable) +{ + if (ini->ops->wake_enable) + ini->ops->wake_enable(ini, enable); +} + +static inline int brcm_usb_get_dual_select(struct brcm_usb_init_params *ini) +{ + if (ini->ops->get_dual_select) + return ini->ops->get_dual_select(ini); + return 0; +} + +static inline void brcm_usb_set_dual_select(struct brcm_usb_init_params *ini, + int mode) +{ + if (ini->ops->set_dual_select) + ini->ops->set_dual_select(ini, mode); +} #endif /* _USB_BRCM_COMMON_INIT_H */ diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c index cca04d60f2d2..9d93c5599511 100644 --- a/drivers/phy/broadcom/phy-brcm-usb.c +++ b/drivers/phy/broadcom/phy-brcm-usb.c @@ -207,7 +207,7 @@ static ssize_t dual_select_store(struct device *dev, res = name_to_value(&brcm_dual_mode_to_name[0], ARRAY_SIZE(brcm_dual_mode_to_name), buf, &value); if (!res) { - brcm_usb_init_set_dual_select(&priv->ini, value); + brcm_usb_set_dual_select(&priv->ini, value); res = len; } mutex_unlock(&sysfs_lock); @@ -222,7 +222,7 @@ static ssize_t dual_select_show(struct device *dev, int value; mutex_lock(&sysfs_lock); - value = brcm_usb_init_get_dual_select(&priv->ini); + value = brcm_usb_get_dual_select(&priv->ini); mutex_unlock(&sysfs_lock); return sprintf(buf, "%s\n", value_to_name(&brcm_dual_mode_to_name[0], @@ -331,7 +331,7 @@ static int brcm_usb_phy_probe(struct platform_device *pdev) priv->ini.family_id = brcmstb_get_family_id(); priv->ini.product_id = brcmstb_get_product_id(); - brcm_usb_set_family_map(&priv->ini); + brcm_usb_dvr_init_7445(&priv->ini); dev_dbg(dev, "Best mapping table is for %s\n", priv->ini.family_name); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- cgit v1.2.3-59-g8ed1b From b11df0c9efbbe2b52c5133ca15030f01b43ec6ef Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Fri, 3 Jan 2020 13:18:04 -0500 Subject: dt-bindings: Add Broadcom STB USB PHY binding document Add support for bcm7216 and bcm7211 Signed-off-by: Al Cooper Reviewed-by: Rob Herring Reviewed-by: Florian Fainelli Signed-off-by: Kishon Vijay Abraham I --- .../bindings/phy/brcm,brcmstb-usb-phy.txt | 69 ++++++++++++++++++---- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/brcm,brcmstb-usb-phy.txt b/Documentation/devicetree/bindings/phy/brcm,brcmstb-usb-phy.txt index 24a0d06acd1d..698aacbdcfc4 100644 --- a/Documentation/devicetree/bindings/phy/brcm,brcmstb-usb-phy.txt +++ b/Documentation/devicetree/bindings/phy/brcm,brcmstb-usb-phy.txt @@ -1,30 +1,49 @@ Broadcom STB USB PHY Required properties: - - compatible: brcm,brcmstb-usb-phy - - reg: two offset and length pairs. - The first pair specifies a manditory set of memory mapped - registers used for general control of the PHY. - The second pair specifies optional registers used by some of - the SoCs that support USB 3.x - - #phy-cells: Shall be 1 as it expects one argument for setting - the type of the PHY. Possible values are: - - PHY_TYPE_USB2 for USB1.1/2.0 PHY - - PHY_TYPE_USB3 for USB3.x PHY +- compatible: should be one of + "brcm,brcmstb-usb-phy" + "brcm,bcm7216-usb-phy" + "brcm,bcm7211-usb-phy" + +- reg and reg-names properties requirements are specific to the + compatible string. + "brcm,brcmstb-usb-phy": + - reg: 1 or 2 offset and length pairs. One for the base CTRL registers + and an optional pair for systems with USB 3.x support + - reg-names: not specified + "brcm,bcm7216-usb-phy": + - reg: 3 offset and length pairs for CTRL, XHCI_EC and XHCI_GBL + registers + - reg-names: "ctrl", "xhci_ec", "xhci_gbl" + "brcm,bcm7211-usb-phy": + - reg: 5 offset and length pairs for CTRL, XHCI_EC, XHCI_GBL, + USB_PHY and USB_MDIO registers and an optional pair + for the BDC registers + - reg-names: "ctrl", "xhci_ec", "xhci_gbl", "usb_phy", "usb_mdio", "bdc_ec" + +- #phy-cells: Shall be 1 as it expects one argument for setting + the type of the PHY. Possible values are: + - PHY_TYPE_USB2 for USB1.1/2.0 PHY + - PHY_TYPE_USB3 for USB3.x PHY Optional Properties: - clocks : clock phandles. - clock-names: String, clock name. +- interrupts: wakeup interrupt +- interrupt-names: "wakeup" - brcm,ipp: Boolean, Invert Port Power. Possible values are: 0 (Don't invert), 1 (Invert) - brcm,ioc: Boolean, Invert Over Current detection. Possible values are: 0 (Don't invert), 1 (Invert) -NOTE: one or both of the following two properties must be set -- brcm,has-xhci: Boolean indicating the phy has an XHCI phy. -- brcm,has-eohci: Boolean indicating the phy has an EHCI/OHCI phy. - dr_mode: String, PHY Device mode. Possible values are: "host", "peripheral ", "drd" or "typec-pd" If this property is not defined, the phy will default to "host" mode. +- brcm,syscon-piarbctl: phandle to syscon for handling config registers +NOTE: one or both of the following two properties must be set +- brcm,has-xhci: Boolean indicating the phy has an XHCI phy. +- brcm,has-eohci: Boolean indicating the phy has an EHCI/OHCI phy. + Example: @@ -41,3 +60,27 @@ usbphy_0: usb-phy@f0470200 { clocks = <&usb20>, <&usb30>; clock-names = "sw_usb", "sw_usb3"; }; + +usb-phy@29f0200 { + reg = <0x29f0200 0x200>, + <0x29c0880 0x30>, + <0x29cc100 0x534>, + <0x2808000 0x24>, + <0x2980080 0x8>; + reg-names = "ctrl", + "xhci_ec", + "xhci_gbl", + "usb_phy", + "usb_mdio"; + brcm,ioc = <0x0>; + brcm,ipp = <0x0>; + compatible = "brcm,bcm7211-usb-phy"; + interrupts = <0x30>; + interrupt-parent = <&vpu_intr1_nosec_intc>; + interrupt-names = "wake"; + #phy-cells = <0x1>; + brcm,has-xhci; + syscon-piarbctl = <&syscon_piarbctl>; + clocks = <&scmi_clk 256>; + clock-names = "sw_usb"; +}; -- cgit v1.2.3-59-g8ed1b From 4e5b9c9a73b32d28759225a40d30848393a8f1fd Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Fri, 3 Jan 2020 13:18:05 -0500 Subject: phy: usb: Add support for new Synopsys USB controller on the 7216 The 7216 has the new USB XHCI controller from Synopsys. While this new controller and the PHY are similar to the STB versions, the major differences are: - Many of the registers and fields in the CTRL block have been removed or changed. - A new set of Synopsys control registers, BCHP_USB_XHCI_GBL, were added. - MDIO functionality has been replaced with direct access registers in the BCHP_USB_XHCI_GBL block. - Power up PHY defaults that had to be changed by MDIO in previous chips will now power up with the correct defaults. A new init module was created for this new Synopsys USB controller. A new compatible string was added and the driver will dispatch into one of two init modules based on it. A "reg-names" field was added so the driver can more easily get optional registers. A DT bindings document was also added for this driver. Signed-off-by: Al Cooper Reviewed-by: Florian Fainelli Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/broadcom/Makefile | 2 +- drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c | 171 ++++++++++++++++++++++ drivers/phy/broadcom/phy-brcm-usb-init.h | 2 + drivers/phy/broadcom/phy-brcm-usb.c | 70 ++++++--- 4 files changed, 227 insertions(+), 18 deletions(-) create mode 100644 drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c diff --git a/drivers/phy/broadcom/Makefile b/drivers/phy/broadcom/Makefile index f453c7d3ffff..c78de546135c 100644 --- a/drivers/phy/broadcom/Makefile +++ b/drivers/phy/broadcom/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_PHY_NS2_USB_DRD) += phy-bcm-ns2-usbdrd.o obj-$(CONFIG_PHY_BRCM_SATA) += phy-brcm-sata.o obj-$(CONFIG_PHY_BRCM_USB) += phy-brcm-usb-dvr.o -phy-brcm-usb-dvr-objs := phy-brcm-usb.o phy-brcm-usb-init.o +phy-brcm-usb-dvr-objs := phy-brcm-usb.o phy-brcm-usb-init.o phy-brcm-usb-init-synopsys.o obj-$(CONFIG_PHY_BCM_SR_PCIE) += phy-bcm-sr-pcie.o obj-$(CONFIG_PHY_BCM_SR_USB) += phy-bcm-sr-usb.o diff --git a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c new file mode 100644 index 000000000000..0bd9ccc43323 --- /dev/null +++ b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2018, Broadcom */ + +/* + * This module contains USB PHY initialization for power up and S3 resume + * for newer Synopsys based USB hardware first used on the bcm7216. + */ + +#include +#include + +#include +#include "phy-brcm-usb-init.h" + +/* Register definitions for the USB CTRL block */ +#define USB_CTRL_SETUP 0x00 +#define USB_CTRL_SETUP_STRAP_IPP_SEL_MASK 0x02000000 +#define USB_CTRL_SETUP_SCB2_EN_MASK 0x00008000 +#define USB_CTRL_SETUP_SCB1_EN_MASK 0x00004000 +#define USB_CTRL_SETUP_SOFT_SHUTDOWN_MASK 0x00000200 +#define USB_CTRL_SETUP_IPP_MASK 0x00000020 +#define USB_CTRL_SETUP_IOC_MASK 0x00000010 +#define USB_CTRL_USB_PM 0x04 +#define USB_CTRL_USB_PM_USB_PWRDN_MASK 0x80000000 +#define USB_CTRL_USB_PM_SOFT_RESET_MASK 0x40000000 +#define USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK 0x00800000 +#define USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK 0x00400000 +#define USB_CTRL_USB_PM_STATUS 0x08 +#define USB_CTRL_USB_DEVICE_CTL1 0x10 +#define USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK 0x00000003 + + +static void xhci_soft_reset(struct brcm_usb_init_params *params, + int on_off) +{ + void __iomem *ctrl = params->ctrl_regs; + + /* Assert reset */ + if (on_off) + USB_CTRL_UNSET(ctrl, USB_PM, XHC_SOFT_RESETB); + /* De-assert reset */ + else + USB_CTRL_SET(ctrl, USB_PM, XHC_SOFT_RESETB); +} + +static void usb_init_ipp(struct brcm_usb_init_params *params) +{ + void __iomem *ctrl = params->ctrl_regs; + u32 reg; + u32 orig_reg; + + pr_debug("%s\n", __func__); + + orig_reg = reg = brcm_usb_readl(USB_CTRL_REG(ctrl, SETUP)); + if (params->ipp != 2) + /* override ipp strap pin (if it exits) */ + reg &= ~(USB_CTRL_MASK(SETUP, STRAP_IPP_SEL)); + + /* Override the default OC and PP polarity */ + reg &= ~(USB_CTRL_MASK(SETUP, IPP) | USB_CTRL_MASK(SETUP, IOC)); + if (params->ioc) + reg |= USB_CTRL_MASK(SETUP, IOC); + if (params->ipp == 1) + reg |= USB_CTRL_MASK(SETUP, IPP); + brcm_usb_writel(reg, USB_CTRL_REG(ctrl, SETUP)); + + /* + * If we're changing IPP, make sure power is off long enough + * to turn off any connected devices. + */ + if ((reg ^ orig_reg) & USB_CTRL_MASK(SETUP, IPP)) + msleep(50); +} + +static void usb_init_common(struct brcm_usb_init_params *params) +{ + u32 reg; + void __iomem *ctrl = params->ctrl_regs; + + pr_debug("%s\n", __func__); + + USB_CTRL_UNSET(ctrl, USB_PM, USB_PWRDN); + /* 1 millisecond - for USB clocks to settle down */ + usleep_range(1000, 2000); + + if (USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE)) { + reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); + reg &= ~USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE); + reg |= params->mode; + brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); + } + switch (params->mode) { + case USB_CTLR_MODE_HOST: + USB_CTRL_UNSET(ctrl, USB_PM, BDC_SOFT_RESETB); + break; + default: + USB_CTRL_UNSET(ctrl, USB_PM, BDC_SOFT_RESETB); + USB_CTRL_SET(ctrl, USB_PM, BDC_SOFT_RESETB); + break; + } +} + +static void usb_init_xhci(struct brcm_usb_init_params *params) +{ + pr_debug("%s\n", __func__); + + xhci_soft_reset(params, 0); +} + +static void usb_uninit_common(struct brcm_usb_init_params *params) +{ + void __iomem *ctrl = params->ctrl_regs; + + pr_debug("%s\n", __func__); + + USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN); + +} + +static void usb_uninit_xhci(struct brcm_usb_init_params *params) +{ + + pr_debug("%s\n", __func__); + + xhci_soft_reset(params, 1); +} + +static int usb_get_dual_select(struct brcm_usb_init_params *params) +{ + void __iomem *ctrl = params->ctrl_regs; + u32 reg = 0; + + pr_debug("%s\n", __func__); + + reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); + reg &= USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE); + return reg; +} + +static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode) +{ + void __iomem *ctrl = params->ctrl_regs; + u32 reg; + + pr_debug("%s\n", __func__); + + reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); + reg &= ~USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE); + reg |= mode; + brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); +} + + +static const struct brcm_usb_init_ops bcm7216_ops = { + .init_ipp = usb_init_ipp, + .init_common = usb_init_common, + .init_xhci = usb_init_xhci, + .uninit_common = usb_uninit_common, + .uninit_xhci = usb_uninit_xhci, + .get_dual_select = usb_get_dual_select, + .set_dual_select = usb_set_dual_select, +}; + +void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params) +{ + + pr_debug("%s\n", __func__); + + params->family_name = "7216"; + params->ops = &bcm7216_ops; +} diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.h b/drivers/phy/broadcom/phy-brcm-usb-init.h index 7701872d1136..db6851c55335 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.h +++ b/drivers/phy/broadcom/phy-brcm-usb-init.h @@ -43,6 +43,7 @@ struct brcm_usb_init_ops { struct brcm_usb_init_params { void __iomem *ctrl_regs; void __iomem *xhci_ec_regs; + void __iomem *xhci_gbl_regs; int ioc; int ipp; int mode; @@ -55,6 +56,7 @@ struct brcm_usb_init_params { }; void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params); +void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params); static inline u32 brcm_usb_readl(void __iomem *addr) { diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c index 9d93c5599511..64379ede480e 100644 --- a/drivers/phy/broadcom/phy-brcm-usb.c +++ b/drivers/phy/broadcom/phy-brcm-usb.c @@ -241,6 +241,15 @@ static const struct attribute_group brcm_usb_phy_group = { .attrs = brcm_usb_phy_attrs, }; +static const struct of_device_id brcm_usb_dt_ids[] = { + { + .compatible = "brcm,bcm7216-usb-phy", + .data = &brcm_usb_dvr_init_7216, + }, + { .compatible = "brcm,brcmstb-usb-phy" }, + { /* sentinel */ } +}; + static int brcm_usb_phy_dvr_init(struct platform_device *pdev, struct brcm_usb_phy_data *priv, struct device_node *dn) @@ -316,13 +325,16 @@ static int brcm_usb_phy_dvr_init(struct platform_device *pdev, static int brcm_usb_phy_probe(struct platform_device *pdev) { - struct resource *res; + struct resource *res_ctrl; + struct resource *res_xhciec = NULL; + struct resource *res_xhcigbl = NULL; struct device *dev = &pdev->dev; struct brcm_usb_phy_data *priv; struct phy_provider *phy_provider; struct device_node *dn = pdev->dev.of_node; int err; const char *mode; + const struct of_device_id *match; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -331,30 +343,59 @@ static int brcm_usb_phy_probe(struct platform_device *pdev) priv->ini.family_id = brcmstb_get_family_id(); priv->ini.product_id = brcmstb_get_product_id(); - brcm_usb_dvr_init_7445(&priv->ini); + + match = of_match_node(brcm_usb_dt_ids, dev->of_node); + if (match && match->data) { + void (*dvr_init)(struct brcm_usb_init_params *params); + + dvr_init = match->data; + (*dvr_init)(&priv->ini); + } else { + brcm_usb_dvr_init_7445(&priv->ini); + } + dev_dbg(dev, "Best mapping table is for %s\n", priv->ini.family_name); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "can't get USB_CTRL base address\n"); - return -EINVAL; + + /* Newer DT node has reg-names. xhci_ec and xhci_gbl are optional. */ + res_ctrl = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl"); + if (res_ctrl != NULL) { + res_xhciec = platform_get_resource_byname(pdev, + IORESOURCE_MEM, + "xhci_ec"); + res_xhcigbl = platform_get_resource_byname(pdev, + IORESOURCE_MEM, + "xhci_gbl"); + } else { + /* Older DT node without reg-names, use index */ + res_ctrl = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res_ctrl == NULL) { + dev_err(dev, "can't get CTRL base address\n"); + return -EINVAL; + } + res_xhciec = platform_get_resource(pdev, IORESOURCE_MEM, 1); } - priv->ini.ctrl_regs = devm_ioremap_resource(dev, res); + priv->ini.ctrl_regs = devm_ioremap_resource(dev, res_ctrl); if (IS_ERR(priv->ini.ctrl_regs)) { dev_err(dev, "can't map CTRL register space\n"); return -EINVAL; } - - /* The XHCI EC registers are optional */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (res) { + if (res_xhciec) { priv->ini.xhci_ec_regs = - devm_ioremap_resource(dev, res); + devm_ioremap_resource(dev, res_xhciec); if (IS_ERR(priv->ini.xhci_ec_regs)) { dev_err(dev, "can't map XHCI EC register space\n"); return -EINVAL; } } + if (res_xhcigbl) { + priv->ini.xhci_gbl_regs = + devm_ioremap_resource(dev, res_xhcigbl); + if (IS_ERR(priv->ini.xhci_gbl_regs)) { + dev_err(dev, "can't map XHCI Global register space\n"); + return -EINVAL; + } + } of_property_read_u32(dn, "brcm,ipp", &priv->ini.ipp); of_property_read_u32(dn, "brcm,ioc", &priv->ini.ioc); @@ -480,11 +521,6 @@ static const struct dev_pm_ops brcm_usb_phy_pm_ops = { SET_LATE_SYSTEM_SLEEP_PM_OPS(brcm_usb_phy_suspend, brcm_usb_phy_resume) }; -static const struct of_device_id brcm_usb_dt_ids[] = { - { .compatible = "brcm,brcmstb-usb-phy" }, - { /* sentinel */ } -}; - MODULE_DEVICE_TABLE(of, brcm_usb_dt_ids); static struct platform_driver brcm_usb_driver = { -- cgit v1.2.3-59-g8ed1b From 9d5f51dcdb646c2ed21649d379fbb703994f1ec9 Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Fri, 3 Jan 2020 13:18:06 -0500 Subject: phy: usb: Add support for new Synopsys USB controller on the 7211b0 The 7211b0 has added the STB XHCI Synopsys controller and it will be used instead of the RPi based DWC USB controller. The new Synopsys XHCI controller core is the same one that is used on the 7216, but because of the way the STB USB PHY is used on both the A0 and B0, some of the PHY control is different. Signed-off-by: Al Cooper Reviewed-by: Florian Fainelli Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c | 163 +++++++++++++++++++++- drivers/phy/broadcom/phy-brcm-usb-init.c | 31 ++-- drivers/phy/broadcom/phy-brcm-usb-init.h | 17 ++- drivers/phy/broadcom/phy-brcm-usb.c | 162 ++++++++++++++------- 4 files changed, 295 insertions(+), 78 deletions(-) diff --git a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c index 0bd9ccc43323..ee49cbdb55bb 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c @@ -12,10 +12,33 @@ #include #include "phy-brcm-usb-init.h" +#define PHY_LOCK_TIMEOUT_MS 200 + +/* Register definitions for syscon piarbctl registers */ +#define PIARBCTL_CAM 0x00 +#define PIARBCTL_SPLITTER 0x04 +#define PIARBCTL_MISC 0x08 +#define PIARBCTL_MISC_SECURE_MASK 0x80000000 +#define PIARBCTL_MISC_USB_SELECT_MASK 0x40000000 +#define PIARBCTL_MISC_USB_4G_SDRAM_MASK 0x20000000 +#define PIARBCTL_MISC_USB_PRIORITY_MASK 0x000f0000 +#define PIARBCTL_MISC_USB_MEM_PAGE_MASK 0x0000f000 +#define PIARBCTL_MISC_CAM1_MEM_PAGE_MASK 0x00000f00 +#define PIARBCTL_MISC_CAM0_MEM_PAGE_MASK 0x000000f0 +#define PIARBCTL_MISC_SATA_PRIORITY_MASK 0x0000000f +#define PIARBCTL_USB_M_ASB_CTRL 0x10 + +#define PIARBCTL_MISC_USB_ONLY_MASK \ + (PIARBCTL_MISC_USB_SELECT_MASK | \ + PIARBCTL_MISC_USB_4G_SDRAM_MASK | \ + PIARBCTL_MISC_USB_PRIORITY_MASK | \ + PIARBCTL_MISC_USB_MEM_PAGE_MASK) + /* Register definitions for the USB CTRL block */ #define USB_CTRL_SETUP 0x00 #define USB_CTRL_SETUP_STRAP_IPP_SEL_MASK 0x02000000 #define USB_CTRL_SETUP_SCB2_EN_MASK 0x00008000 +#define USB_CTRL_SETUP_tca_drv_sel_MASK 0x01000000 #define USB_CTRL_SETUP_SCB1_EN_MASK 0x00004000 #define USB_CTRL_SETUP_SOFT_SHUTDOWN_MASK 0x00000200 #define USB_CTRL_SETUP_IPP_MASK 0x00000020 @@ -29,11 +52,73 @@ #define USB_CTRL_USB_DEVICE_CTL1 0x10 #define USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK 0x00000003 +/* Register definitions for the USB_PHY block in 7211b0 */ +#define USB_PHY_PLL_LDO_CTL 0x08 +#define USB_PHY_PLL_LDO_CTL_AFE_CORERDY_MASK 0x00000004 +#define USB_PHY_UTMI_CTL_1 0x04 +#define USB_PHY_UTMI_CTL_1_PHY_MODE_MASK 0x0000000c +#define USB_PHY_UTMI_CTL_1_PHY_MODE_SHIFT 2 +#define USB_PHY_STATUS 0x20 +#define USB_PHY_STATUS_pll_lock_MASK 0x00000001 + +/* Register definitions for the MDIO registers in the DWC2 block of + * the 7211b0. + * NOTE: The PHY's MDIO registers are only accessible through the + * legacy DesignWare USB controller even though it's not being used. + */ +#define USB_GMDIOCSR 0 +#define USB_GMDIOGEN 4 + + +static void usb_mdio_write_7211b0(struct brcm_usb_init_params *params, + uint8_t addr, uint16_t data) +{ + void __iomem *usb_mdio = params->regs[BRCM_REGS_USB_MDIO]; + + addr &= 0x1f; /* 5-bit address */ + brcm_usb_writel(0xffffffff, usb_mdio + USB_GMDIOGEN); + while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31)) + ; + brcm_usb_writel(0x59020000 | (addr << 18) | data, + usb_mdio + USB_GMDIOGEN); + while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31)) + ; + brcm_usb_writel(0x00000000, usb_mdio + USB_GMDIOGEN); + while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31)) + ; +} + +static uint16_t __maybe_unused usb_mdio_read_7211b0( + struct brcm_usb_init_params *params, uint8_t addr) +{ + void __iomem *usb_mdio = params->regs[BRCM_REGS_USB_MDIO]; + + addr &= 0x1f; /* 5-bit address */ + brcm_usb_writel(0xffffffff, usb_mdio + USB_GMDIOGEN); + while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31)) + ; + brcm_usb_writel(0x69020000 | (addr << 18), usb_mdio + USB_GMDIOGEN); + while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31)) + ; + brcm_usb_writel(0x00000000, usb_mdio + USB_GMDIOGEN); + while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31)) + ; + return brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & 0xffff; +} + +static void usb2_eye_fix_7211b0(struct brcm_usb_init_params *params) +{ + /* select bank */ + usb_mdio_write_7211b0(params, 0x1f, 0x80a0); + + /* Set the eye */ + usb_mdio_write_7211b0(params, 0x0a, 0xc6a0); +} static void xhci_soft_reset(struct brcm_usb_init_params *params, int on_off) { - void __iomem *ctrl = params->ctrl_regs; + void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; /* Assert reset */ if (on_off) @@ -45,7 +130,7 @@ static void xhci_soft_reset(struct brcm_usb_init_params *params, static void usb_init_ipp(struct brcm_usb_init_params *params) { - void __iomem *ctrl = params->ctrl_regs; + void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; u32 reg; u32 orig_reg; @@ -72,10 +157,18 @@ static void usb_init_ipp(struct brcm_usb_init_params *params) msleep(50); } +static void syscon_piarbctl_init(struct regmap *rmap) +{ + /* Switch from legacy USB OTG controller to new STB USB controller */ + regmap_update_bits(rmap, PIARBCTL_MISC, PIARBCTL_MISC_USB_ONLY_MASK, + PIARBCTL_MISC_USB_SELECT_MASK | + PIARBCTL_MISC_USB_4G_SDRAM_MASK); +} + static void usb_init_common(struct brcm_usb_init_params *params) { u32 reg; - void __iomem *ctrl = params->ctrl_regs; + void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; pr_debug("%s\n", __func__); @@ -100,6 +193,45 @@ static void usb_init_common(struct brcm_usb_init_params *params) } } +static void usb_init_common_7211b0(struct brcm_usb_init_params *params) +{ + void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; + void __iomem *usb_phy = params->regs[BRCM_REGS_USB_PHY]; + int timeout_ms = PHY_LOCK_TIMEOUT_MS; + u32 reg; + + if (params->syscon_piarbctl) + syscon_piarbctl_init(params->syscon_piarbctl); + + /* Init the PHY */ + reg = brcm_usb_readl(usb_phy + USB_PHY_PLL_LDO_CTL); + reg |= USB_PHY_PLL_LDO_CTL_AFE_CORERDY_MASK; + brcm_usb_writel(reg, usb_phy + USB_PHY_PLL_LDO_CTL); + + /* wait for lock */ + while (timeout_ms-- > 0) { + reg = brcm_usb_readl(usb_phy + USB_PHY_STATUS); + if (reg & USB_PHY_STATUS_pll_lock_MASK) + break; + usleep_range(1000, 2000); + } + + /* Set the PHY_MODE */ + reg = brcm_usb_readl(usb_phy + USB_PHY_UTMI_CTL_1); + reg &= ~USB_PHY_UTMI_CTL_1_PHY_MODE_MASK; + reg |= params->mode << USB_PHY_UTMI_CTL_1_PHY_MODE_SHIFT; + brcm_usb_writel(reg, usb_phy + USB_PHY_UTMI_CTL_1); + + /* Fix the incorrect default */ + reg = brcm_usb_readl(ctrl + USB_CTRL_SETUP); + reg &= ~USB_CTRL_SETUP_tca_drv_sel_MASK; + brcm_usb_writel(reg, ctrl + USB_CTRL_SETUP); + + usb_init_common(params); + + usb2_eye_fix_7211b0(params); +} + static void usb_init_xhci(struct brcm_usb_init_params *params) { pr_debug("%s\n", __func__); @@ -109,7 +241,7 @@ static void usb_init_xhci(struct brcm_usb_init_params *params) static void usb_uninit_common(struct brcm_usb_init_params *params) { - void __iomem *ctrl = params->ctrl_regs; + void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; pr_debug("%s\n", __func__); @@ -127,7 +259,7 @@ static void usb_uninit_xhci(struct brcm_usb_init_params *params) static int usb_get_dual_select(struct brcm_usb_init_params *params) { - void __iomem *ctrl = params->ctrl_regs; + void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; u32 reg = 0; pr_debug("%s\n", __func__); @@ -139,7 +271,7 @@ static int usb_get_dual_select(struct brcm_usb_init_params *params) static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode) { - void __iomem *ctrl = params->ctrl_regs; + void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; u32 reg; pr_debug("%s\n", __func__); @@ -161,6 +293,16 @@ static const struct brcm_usb_init_ops bcm7216_ops = { .set_dual_select = usb_set_dual_select, }; +static const struct brcm_usb_init_ops bcm7211b0_ops = { + .init_ipp = usb_init_ipp, + .init_common = usb_init_common_7211b0, + .init_xhci = usb_init_xhci, + .uninit_common = usb_uninit_common, + .uninit_xhci = usb_uninit_xhci, + .get_dual_select = usb_get_dual_select, + .set_dual_select = usb_set_dual_select, +}; + void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params) { @@ -169,3 +311,12 @@ void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params) params->family_name = "7216"; params->ops = &bcm7216_ops; } + +void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params) +{ + + pr_debug("%s\n", __func__); + + params->family_name = "7211"; + params->ops = &bcm7211b0_ops; +} diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.c b/drivers/phy/broadcom/phy-brcm-usb-init.c index 80d6f54d276e..17acc3c1051b 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init.c @@ -401,7 +401,7 @@ void usb_ctrl_unset_family(struct brcm_usb_init_params *params, u32 mask; mask = params->usb_reg_bits_map[field]; - brcm_usb_ctrl_unset(params->ctrl_regs + reg_offset, mask); + brcm_usb_ctrl_unset(params->regs[BRCM_REGS_CTRL] + reg_offset, mask); }; static inline @@ -411,7 +411,7 @@ void usb_ctrl_set_family(struct brcm_usb_init_params *params, u32 mask; mask = params->usb_reg_bits_map[field]; - brcm_usb_ctrl_set(params->ctrl_regs + reg_offset, mask); + brcm_usb_ctrl_set(params->regs[BRCM_REGS_CTRL] + reg_offset, mask); }; static u32 brcmusb_usb_mdio_read(void __iomem *ctrl_base, u32 reg, int mode) @@ -544,7 +544,7 @@ static void brcmusb_usb3_pll_54mhz(struct brcm_usb_init_params *params) { u32 ofs; int ii; - void __iomem *ctrl_base = params->ctrl_regs; + void __iomem *ctrl_base = params->regs[BRCM_REGS_CTRL]; /* * On newer B53 based SoC's, the reference clock for the @@ -625,7 +625,7 @@ static void brcmusb_usb3_ssc_enable(void __iomem *ctrl_base) static void brcmusb_usb3_phy_workarounds(struct brcm_usb_init_params *params) { - void __iomem *ctrl_base = params->ctrl_regs; + void __iomem *ctrl_base = params->regs[BRCM_REGS_CTRL]; brcmusb_usb3_pll_fix(ctrl_base); brcmusb_usb3_pll_54mhz(params); @@ -667,7 +667,7 @@ static void brcmusb_memc_fix(struct brcm_usb_init_params *params) static void brcmusb_usb3_otp_fix(struct brcm_usb_init_params *params) { - void __iomem *xhci_ec_base = params->xhci_ec_regs; + void __iomem *xhci_ec_base = params->regs[BRCM_REGS_XHCI_EC]; u32 val; if (params->family_id != 0x74371000 || !xhci_ec_base) @@ -680,8 +680,8 @@ static void brcmusb_usb3_otp_fix(struct brcm_usb_init_params *params) brcm_usb_writel(val, USB_XHCI_EC_REG(xhci_ec_base, IRADAT)); /* Reset USB 3.0 PHY for workaround to take effect */ - USB_CTRL_UNSET(params->ctrl_regs, USB30_CTL1, PHY3_RESETB); - USB_CTRL_SET(params->ctrl_regs, USB30_CTL1, PHY3_RESETB); + USB_CTRL_UNSET(params->regs[BRCM_REGS_CTRL], USB30_CTL1, PHY3_RESETB); + USB_CTRL_SET(params->regs[BRCM_REGS_CTRL], USB30_CTL1, PHY3_RESETB); } static void brcmusb_xhci_soft_reset(struct brcm_usb_init_params *params, @@ -740,7 +740,7 @@ static enum brcm_family_type get_family_type( static void usb_init_ipp(struct brcm_usb_init_params *params) { - void __iomem *ctrl = params->ctrl_regs; + void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; u32 reg; u32 orig_reg; @@ -786,7 +786,7 @@ static void usb_init_ipp(struct brcm_usb_init_params *params) static void usb_init_common(struct brcm_usb_init_params *params) { u32 reg; - void __iomem *ctrl = params->ctrl_regs; + void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; /* Clear any pending wake conditions */ reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_PM_STATUS)); @@ -866,7 +866,7 @@ static void usb_init_common(struct brcm_usb_init_params *params) static void usb_init_eohci(struct brcm_usb_init_params *params) { u32 reg; - void __iomem *ctrl = params->ctrl_regs; + void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; if (USB_CTRL_MASK_FAMILY(params, USB_PM, USB20_HC_RESETB)) USB_CTRL_SET_FAMILY(params, USB_PM, USB20_HC_RESETB); @@ -902,7 +902,7 @@ static void usb_init_eohci(struct brcm_usb_init_params *params) static void usb_init_xhci(struct brcm_usb_init_params *params) { - void __iomem *ctrl = params->ctrl_regs; + void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; USB_CTRL_UNSET(ctrl, USB30_PCTL, PHY3_IDDQ_OVERRIDE); /* 1 millisecond - for USB clocks to settle down */ @@ -944,12 +944,13 @@ static void usb_uninit_eohci(struct brcm_usb_init_params *params) static void usb_uninit_xhci(struct brcm_usb_init_params *params) { brcmusb_xhci_soft_reset(params, 1); - USB_CTRL_SET(params->ctrl_regs, USB30_PCTL, PHY3_IDDQ_OVERRIDE); + USB_CTRL_SET(params->regs[BRCM_REGS_CTRL], USB30_PCTL, + PHY3_IDDQ_OVERRIDE); } static int usb_get_dual_select(struct brcm_usb_init_params *params) { - void __iomem *ctrl = params->ctrl_regs; + void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; u32 reg = 0; pr_debug("%s\n", __func__); @@ -963,7 +964,7 @@ static int usb_get_dual_select(struct brcm_usb_init_params *params) static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode) { - void __iomem *ctrl = params->ctrl_regs; + void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; u32 reg; pr_debug("%s\n", __func__); @@ -980,7 +981,7 @@ static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode) static void usb_wake_enable(struct brcm_usb_init_params *params, int enable) { - void __iomem *ctrl = params->ctrl_regs; + void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; if (enable) USB_CTRL_SET(ctrl, USB_PM, RMTWKUP_EN); diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.h b/drivers/phy/broadcom/phy-brcm-usb-init.h index db6851c55335..2ea81daf295e 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.h +++ b/drivers/phy/broadcom/phy-brcm-usb-init.h @@ -6,12 +6,21 @@ #ifndef _USB_BRCM_COMMON_INIT_H #define _USB_BRCM_COMMON_INIT_H +#include + #define USB_CTLR_MODE_HOST 0 #define USB_CTLR_MODE_DEVICE 1 #define USB_CTLR_MODE_DRD 2 #define USB_CTLR_MODE_TYPEC_PD 3 -struct brcm_usb_init_params; +enum brcmusb_reg_sel { + BRCM_REGS_CTRL = 0, + BRCM_REGS_XHCI_EC, + BRCM_REGS_XHCI_GBL, + BRCM_REGS_USB_PHY, + BRCM_REGS_USB_MDIO, + BRCM_REGS_MAX +}; #define USB_CTRL_REG(base, reg) ((void __iomem *)base + USB_CTRL_##reg) #define USB_XHCI_EC_REG(base, reg) ((void __iomem *)base + USB_XHCI_EC_##reg) @@ -41,9 +50,7 @@ struct brcm_usb_init_ops { }; struct brcm_usb_init_params { - void __iomem *ctrl_regs; - void __iomem *xhci_ec_regs; - void __iomem *xhci_gbl_regs; + void __iomem *regs[BRCM_REGS_MAX]; int ioc; int ipp; int mode; @@ -53,10 +60,12 @@ struct brcm_usb_init_params { const char *family_name; const u32 *usb_reg_bits_map; const struct brcm_usb_init_ops *ops; + struct regmap *syscon_piarbctl; }; void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params); void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params); +void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params); static inline u32 brcm_usb_readl(void __iomem *addr) { diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c index 64379ede480e..5f7bfa09494d 100644 --- a/drivers/phy/broadcom/phy-brcm-usb.c +++ b/drivers/phy/broadcom/phy-brcm-usb.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "phy-brcm-usb-init.h" @@ -32,6 +33,11 @@ struct value_to_name_map { const char *name; }; +struct match_chip_info { + void *init_func; + u8 required_regs[BRCM_REGS_MAX + 1]; +}; + static struct value_to_name_map brcm_dr_mode_to_name[] = { { USB_CTLR_MODE_HOST, "host" }, { USB_CTLR_MODE_DEVICE, "peripheral" }, @@ -64,6 +70,10 @@ struct brcm_usb_phy_data { struct brcm_usb_phy phys[BRCM_USB_PHY_ID_MAX]; }; +static s8 *node_reg_names[BRCM_REGS_MAX] = { + "crtl", "xhci_ec", "xhci_gbl", "usb_phy", "usb_mdio" +}; + static irqreturn_t brcm_usb_phy_wake_isr(int irq, void *dev_id) { struct phy *gphy = dev_id; @@ -241,15 +251,86 @@ static const struct attribute_group brcm_usb_phy_group = { .attrs = brcm_usb_phy_attrs, }; +static struct match_chip_info chip_info_7216 = { + .init_func = &brcm_usb_dvr_init_7216, + .required_regs = { + BRCM_REGS_CTRL, + BRCM_REGS_XHCI_EC, + BRCM_REGS_XHCI_GBL, + -1, + }, +}; + +static struct match_chip_info chip_info_7211b0 = { + .init_func = &brcm_usb_dvr_init_7211b0, + .required_regs = { + BRCM_REGS_CTRL, + BRCM_REGS_XHCI_EC, + BRCM_REGS_XHCI_GBL, + BRCM_REGS_USB_PHY, + BRCM_REGS_USB_MDIO, + -1, + }, +}; + +static struct match_chip_info chip_info_7445 = { + .init_func = &brcm_usb_dvr_init_7445, + .required_regs = { + BRCM_REGS_CTRL, + BRCM_REGS_XHCI_EC, + -1, + }, +}; + static const struct of_device_id brcm_usb_dt_ids[] = { { .compatible = "brcm,bcm7216-usb-phy", - .data = &brcm_usb_dvr_init_7216, + .data = &chip_info_7216, + }, + { + .compatible = "brcm,bcm7211-usb-phy", + .data = &chip_info_7211b0, + }, + { + .compatible = "brcm,brcmstb-usb-phy", + .data = &chip_info_7445, }, - { .compatible = "brcm,brcmstb-usb-phy" }, { /* sentinel */ } }; +static int brcm_usb_get_regs(struct platform_device *pdev, + enum brcmusb_reg_sel regs, + struct brcm_usb_init_params *ini) +{ + struct resource *res; + + /* Older DT nodes have ctrl and optional xhci_ec by index only */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + node_reg_names[regs]); + if (res == NULL) { + if (regs == BRCM_REGS_CTRL) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + } else if (regs == BRCM_REGS_XHCI_EC) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + /* XHCI_EC registers are optional */ + if (res == NULL) + return 0; + } + if (res == NULL) { + dev_err(&pdev->dev, "can't get %s base address\n", + node_reg_names[regs]); + return 1; + } + } + ini->regs[regs] = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ini->regs[regs])) { + dev_err(&pdev->dev, "can't map %s register space\n", + node_reg_names[regs]); + return 1; + } + return 0; +} + static int brcm_usb_phy_dvr_init(struct platform_device *pdev, struct brcm_usb_phy_data *priv, struct device_node *dn) @@ -325,9 +406,6 @@ static int brcm_usb_phy_dvr_init(struct platform_device *pdev, static int brcm_usb_phy_probe(struct platform_device *pdev) { - struct resource *res_ctrl; - struct resource *res_xhciec = NULL; - struct resource *res_xhcigbl = NULL; struct device *dev = &pdev->dev; struct brcm_usb_phy_data *priv; struct phy_provider *phy_provider; @@ -335,6 +413,10 @@ static int brcm_usb_phy_probe(struct platform_device *pdev) int err; const char *mode; const struct of_device_id *match; + void (*dvr_init)(struct brcm_usb_init_params *params); + const struct match_chip_info *info; + struct regmap *rmap; + int x; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -345,58 +427,13 @@ static int brcm_usb_phy_probe(struct platform_device *pdev) priv->ini.product_id = brcmstb_get_product_id(); match = of_match_node(brcm_usb_dt_ids, dev->of_node); - if (match && match->data) { - void (*dvr_init)(struct brcm_usb_init_params *params); - - dvr_init = match->data; - (*dvr_init)(&priv->ini); - } else { - brcm_usb_dvr_init_7445(&priv->ini); - } + info = match->data; + dvr_init = info->init_func; + (*dvr_init)(&priv->ini); dev_dbg(dev, "Best mapping table is for %s\n", priv->ini.family_name); - /* Newer DT node has reg-names. xhci_ec and xhci_gbl are optional. */ - res_ctrl = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl"); - if (res_ctrl != NULL) { - res_xhciec = platform_get_resource_byname(pdev, - IORESOURCE_MEM, - "xhci_ec"); - res_xhcigbl = platform_get_resource_byname(pdev, - IORESOURCE_MEM, - "xhci_gbl"); - } else { - /* Older DT node without reg-names, use index */ - res_ctrl = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res_ctrl == NULL) { - dev_err(dev, "can't get CTRL base address\n"); - return -EINVAL; - } - res_xhciec = platform_get_resource(pdev, IORESOURCE_MEM, 1); - } - priv->ini.ctrl_regs = devm_ioremap_resource(dev, res_ctrl); - if (IS_ERR(priv->ini.ctrl_regs)) { - dev_err(dev, "can't map CTRL register space\n"); - return -EINVAL; - } - if (res_xhciec) { - priv->ini.xhci_ec_regs = - devm_ioremap_resource(dev, res_xhciec); - if (IS_ERR(priv->ini.xhci_ec_regs)) { - dev_err(dev, "can't map XHCI EC register space\n"); - return -EINVAL; - } - } - if (res_xhcigbl) { - priv->ini.xhci_gbl_regs = - devm_ioremap_resource(dev, res_xhcigbl); - if (IS_ERR(priv->ini.xhci_gbl_regs)) { - dev_err(dev, "can't map XHCI Global register space\n"); - return -EINVAL; - } - } - of_property_read_u32(dn, "brcm,ipp", &priv->ini.ipp); of_property_read_u32(dn, "brcm,ioc", &priv->ini.ioc); @@ -412,6 +449,16 @@ static int brcm_usb_phy_probe(struct platform_device *pdev) if (of_property_read_bool(dn, "brcm,has-eohci")) priv->has_eohci = true; + for (x = 0; x < BRCM_REGS_MAX; x++) { + if (info->required_regs[x] >= BRCM_REGS_MAX) + break; + + err = brcm_usb_get_regs(pdev, info->required_regs[x], + &priv->ini); + if (err) + return -EINVAL; + } + err = brcm_usb_phy_dvr_init(pdev, priv, dn); if (err) return err; @@ -431,6 +478,15 @@ static int brcm_usb_phy_probe(struct platform_device *pdev) if (err) dev_warn(dev, "Error creating sysfs attributes\n"); + /* Get piarbctl syscon if it exists */ + rmap = syscon_regmap_lookup_by_phandle(dev->of_node, + "syscon-piarbctl"); + if (IS_ERR(rmap)) + rmap = syscon_regmap_lookup_by_phandle(dev->of_node, + "brcm,syscon-piarbctl"); + if (!IS_ERR(rmap)) + priv->ini.syscon_piarbctl = rmap; + /* start with everything off */ if (priv->has_xhci) brcm_usb_uninit_xhci(&priv->ini); -- cgit v1.2.3-59-g8ed1b From 89927fe0061aaa69b39e95ed793d2c61903b7895 Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Fri, 3 Jan 2020 13:18:07 -0500 Subject: phy: usb: fix driver to defer on clk_get defer Handle defer on clk_get because the new SCMI clock driver comes up after this driver. Signed-off-by: Al Cooper Reviewed-by: Florian Fainelli Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/broadcom/phy-brcm-usb.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c index 5f7bfa09494d..c82d7ec15334 100644 --- a/drivers/phy/broadcom/phy-brcm-usb.c +++ b/drivers/phy/broadcom/phy-brcm-usb.c @@ -341,6 +341,8 @@ static int brcm_usb_phy_dvr_init(struct platform_device *pdev, priv->usb_20_clk = of_clk_get_by_name(dn, "sw_usb"); if (IS_ERR(priv->usb_20_clk)) { + if (PTR_ERR(priv->usb_20_clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; dev_info(dev, "Clock not found in Device Tree\n"); priv->usb_20_clk = NULL; } @@ -371,6 +373,8 @@ static int brcm_usb_phy_dvr_init(struct platform_device *pdev, priv->usb_30_clk = of_clk_get_by_name(dn, "sw_usb3"); if (IS_ERR(priv->usb_30_clk)) { + if (PTR_ERR(priv->usb_30_clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; dev_info(dev, "USB3.0 clock not found in Device Tree\n"); priv->usb_30_clk = NULL; @@ -382,6 +386,8 @@ static int brcm_usb_phy_dvr_init(struct platform_device *pdev, priv->suspend_clk = clk_get(dev, "usb0_freerun"); if (IS_ERR(priv->suspend_clk)) { + if (PTR_ERR(priv->suspend_clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; dev_err(dev, "Suspend Clock not found in Device Tree\n"); priv->suspend_clk = NULL; } -- cgit v1.2.3-59-g8ed1b From fc430aea02068150d053ef24bc424db3dd1357d4 Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Fri, 3 Jan 2020 13:18:08 -0500 Subject: phy: usb: PHY's MDIO registers not accessible without device installed When there is no device connected and FSM is enabled, the XHCI puts the PHY into suspend mode. When the PHY is put into suspend mode the USB LDO powers down the PHY. This causes the MDIO to be inaccessible and its registers reset to default. The fix is to disable FSM. Signed-off-by: Al Cooper Reviewed-by: Florian Fainelli Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c index ee49cbdb55bb..ce4226ac552e 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c @@ -56,6 +56,7 @@ #define USB_PHY_PLL_LDO_CTL 0x08 #define USB_PHY_PLL_LDO_CTL_AFE_CORERDY_MASK 0x00000004 #define USB_PHY_UTMI_CTL_1 0x04 +#define USB_PHY_UTMI_CTL_1_POWER_UP_FSM_EN_MASK 0x00000800 #define USB_PHY_UTMI_CTL_1_PHY_MODE_MASK 0x0000000c #define USB_PHY_UTMI_CTL_1_PHY_MODE_SHIFT 2 #define USB_PHY_STATUS 0x20 @@ -229,6 +230,14 @@ static void usb_init_common_7211b0(struct brcm_usb_init_params *params) usb_init_common(params); + /* + * Disable FSM, otherwise the PHY will auto suspend when no + * device is connected and will be reset on resume. + */ + reg = brcm_usb_readl(usb_phy + USB_PHY_UTMI_CTL_1); + reg &= ~USB_PHY_UTMI_CTL_1_POWER_UP_FSM_EN_MASK; + brcm_usb_writel(reg, usb_phy + USB_PHY_UTMI_CTL_1); + usb2_eye_fix_7211b0(params); } -- cgit v1.2.3-59-g8ed1b From bed63b636fedf47dbab899a5193ec5ec4539f6fc Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Fri, 3 Jan 2020 13:18:09 -0500 Subject: phy: usb: bdc: Fix occasional failure with BDC on 7211 The BDC "Read Transaction Size" needs to be changed from 1024 bytes to 256 bytes to prevent occasional transaction failures. Signed-off-by: Al Cooper Reviewed-by: Florian Fainelli Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c | 18 ++++++++++++++++++ drivers/phy/broadcom/phy-brcm-usb-init.h | 1 + drivers/phy/broadcom/phy-brcm-usb.c | 23 +++++++++++++++++++---- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c index ce4226ac552e..60969827a67a 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c @@ -70,6 +70,11 @@ #define USB_GMDIOCSR 0 #define USB_GMDIOGEN 4 +/* Register definitions for the BDC EC block in 7211b0 */ +#define BDC_EC_AXIRDA 0x0c +#define BDC_EC_AXIRDA_RTS_MASK 0xf0000000 +#define BDC_EC_AXIRDA_RTS_SHIFT 28 + static void usb_mdio_write_7211b0(struct brcm_usb_init_params *params, uint8_t addr, uint16_t data) @@ -198,6 +203,7 @@ static void usb_init_common_7211b0(struct brcm_usb_init_params *params) { void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; void __iomem *usb_phy = params->regs[BRCM_REGS_USB_PHY]; + void __iomem *bdc_ec = params->regs[BRCM_REGS_BDC_EC]; int timeout_ms = PHY_LOCK_TIMEOUT_MS; u32 reg; @@ -230,6 +236,18 @@ static void usb_init_common_7211b0(struct brcm_usb_init_params *params) usb_init_common(params); + /* + * The BDC controller will get occasional failures with + * the default "Read Transaction Size" of 6 (1024 bytes). + * Set it to 4 (256 bytes). + */ + if ((params->mode != USB_CTLR_MODE_HOST) && bdc_ec) { + reg = brcm_usb_readl(bdc_ec + BDC_EC_AXIRDA); + reg &= ~BDC_EC_AXIRDA_RTS_MASK; + reg |= (0x4 << BDC_EC_AXIRDA_RTS_SHIFT); + brcm_usb_writel(reg, bdc_ec + BDC_EC_AXIRDA); + } + /* * Disable FSM, otherwise the PHY will auto suspend when no * device is connected and will be reset on resume. diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.h b/drivers/phy/broadcom/phy-brcm-usb-init.h index 2ea81daf295e..4cdd9cc1c5a3 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.h +++ b/drivers/phy/broadcom/phy-brcm-usb-init.h @@ -19,6 +19,7 @@ enum brcmusb_reg_sel { BRCM_REGS_XHCI_GBL, BRCM_REGS_USB_PHY, BRCM_REGS_USB_MDIO, + BRCM_REGS_BDC_EC, BRCM_REGS_MAX }; diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c index c82d7ec15334..cc5763ace3ad 100644 --- a/drivers/phy/broadcom/phy-brcm-usb.c +++ b/drivers/phy/broadcom/phy-brcm-usb.c @@ -36,6 +36,7 @@ struct value_to_name_map { struct match_chip_info { void *init_func; u8 required_regs[BRCM_REGS_MAX + 1]; + u8 optional_reg; }; static struct value_to_name_map brcm_dr_mode_to_name[] = { @@ -71,7 +72,7 @@ struct brcm_usb_phy_data { }; static s8 *node_reg_names[BRCM_REGS_MAX] = { - "crtl", "xhci_ec", "xhci_gbl", "usb_phy", "usb_mdio" + "crtl", "xhci_ec", "xhci_gbl", "usb_phy", "usb_mdio", "bdc_ec" }; static irqreturn_t brcm_usb_phy_wake_isr(int irq, void *dev_id) @@ -271,6 +272,7 @@ static struct match_chip_info chip_info_7211b0 = { BRCM_REGS_USB_MDIO, -1, }, + .optional_reg = BRCM_REGS_BDC_EC, }; static struct match_chip_info chip_info_7445 = { @@ -300,7 +302,8 @@ static const struct of_device_id brcm_usb_dt_ids[] = { static int brcm_usb_get_regs(struct platform_device *pdev, enum brcmusb_reg_sel regs, - struct brcm_usb_init_params *ini) + struct brcm_usb_init_params *ini, + bool optional) { struct resource *res; @@ -317,7 +320,13 @@ static int brcm_usb_get_regs(struct platform_device *pdev, return 0; } if (res == NULL) { - dev_err(&pdev->dev, "can't get %s base address\n", + if (optional) { + dev_dbg(&pdev->dev, + "Optional reg %s not found\n", + node_reg_names[regs]); + return 0; + } + dev_err(&pdev->dev, "can't get %s base addr\n", node_reg_names[regs]); return 1; } @@ -460,7 +469,13 @@ static int brcm_usb_phy_probe(struct platform_device *pdev) break; err = brcm_usb_get_regs(pdev, info->required_regs[x], - &priv->ini); + &priv->ini, false); + if (err) + return -EINVAL; + } + if (info->optional_reg) { + err = brcm_usb_get_regs(pdev, info->optional_reg, + &priv->ini, true); if (err) return -EINVAL; } -- cgit v1.2.3-59-g8ed1b From 5dfe1cec580829faa49842672a25481b104c26ef Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Fri, 3 Jan 2020 13:18:10 -0500 Subject: phy: usb: USB driver is crashing during S3 resume on 7216 This is a result of the USB 2.0 clocks not being disabled/enabled during suspend/resume on XHCI only systems. Signed-off-by: Al Cooper Reviewed-by: Florian Fainelli Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/broadcom/phy-brcm-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c index cc5763ace3ad..1ab44f54244b 100644 --- a/drivers/phy/broadcom/phy-brcm-usb.c +++ b/drivers/phy/broadcom/phy-brcm-usb.c @@ -543,7 +543,7 @@ static int brcm_usb_phy_suspend(struct device *dev) brcm_usb_wake_enable(&priv->ini, true); if (priv->phys[BRCM_USB_PHY_3_0].inited) clk_disable_unprepare(priv->usb_30_clk); - if (priv->phys[BRCM_USB_PHY_2_0].inited) + if (priv->phys[BRCM_USB_PHY_2_0].inited || !priv->has_eohci) clk_disable_unprepare(priv->usb_20_clk); if (priv->wake_irq >= 0) enable_irq_wake(priv->wake_irq); -- cgit v1.2.3-59-g8ed1b From b0c0b66c0b432d3f3a1ae5849298ba9c7f1810c5 Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Fri, 3 Jan 2020 13:18:11 -0500 Subject: phy: usb: Add support for wake and USB low power mode for 7211 S2/S5 Add support for 7211 USB wake. Disable all possible 7211 USB logic for S2/S5 if USB wake is not enabled. On the 7211, the XHCI wake signal was not connected properly and only goes to the USB1_USB1_CTRL_TP_DIAG1 diagonstic register. The workaround is to have VPU code running that polls for the proper bit in the DIAG register and to wake the system when the bit is asserted. Signed-off-by: Al Cooper Reviewed-by: Florian Fainelli Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c | 77 +++++++++++++++++++++-- drivers/phy/broadcom/phy-brcm-usb-init.c | 26 ++++---- drivers/phy/broadcom/phy-brcm-usb-init.h | 11 +--- drivers/phy/broadcom/phy-brcm-usb.c | 25 +++++--- 4 files changed, 105 insertions(+), 34 deletions(-) diff --git a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c index 60969827a67a..456dc4a100c2 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c @@ -26,7 +26,6 @@ #define PIARBCTL_MISC_CAM1_MEM_PAGE_MASK 0x00000f00 #define PIARBCTL_MISC_CAM0_MEM_PAGE_MASK 0x000000f0 #define PIARBCTL_MISC_SATA_PRIORITY_MASK 0x0000000f -#define PIARBCTL_USB_M_ASB_CTRL 0x10 #define PIARBCTL_MISC_USB_ONLY_MASK \ (PIARBCTL_MISC_USB_SELECT_MASK | \ @@ -51,14 +50,27 @@ #define USB_CTRL_USB_PM_STATUS 0x08 #define USB_CTRL_USB_DEVICE_CTL1 0x10 #define USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK 0x00000003 +#define USB_CTRL_TEST_PORT_CTL 0x30 +#define USB_CTRL_TEST_PORT_CTL_TPOUT_SEL_MASK 0x000000ff +#define USB_CTRL_TEST_PORT_CTL_TPOUT_SEL_PME_GEN_MASK 0x0000002e +#define USB_CTRL_TP_DIAG1 0x34 +#define USB_CTLR_TP_DIAG1_wake_MASK 0x00000002 +#define USB_CTRL_CTLR_CSHCR 0x50 +#define USB_CTRL_CTLR_CSHCR_ctl_pme_en_MASK 0x00040000 /* Register definitions for the USB_PHY block in 7211b0 */ +#define USB_PHY_PLL_CTL 0x00 +#define USB_PHY_PLL_CTL_PLL_RESETB_MASK 0x40000000 #define USB_PHY_PLL_LDO_CTL 0x08 #define USB_PHY_PLL_LDO_CTL_AFE_CORERDY_MASK 0x00000004 +#define USB_PHY_PLL_LDO_CTL_AFE_LDO_PWRDWNB_MASK 0x00000002 +#define USB_PHY_PLL_LDO_CTL_AFE_BG_PWRDWNB_MASK 0x00000001 #define USB_PHY_UTMI_CTL_1 0x04 #define USB_PHY_UTMI_CTL_1_POWER_UP_FSM_EN_MASK 0x00000800 #define USB_PHY_UTMI_CTL_1_PHY_MODE_MASK 0x0000000c #define USB_PHY_UTMI_CTL_1_PHY_MODE_SHIFT 2 +#define USB_PHY_IDDQ 0x1c +#define USB_PHY_IDDQ_phy_iddq_MASK 0x00000001 #define USB_PHY_STATUS 0x20 #define USB_PHY_STATUS_pll_lock_MASK 0x00000001 @@ -199,6 +211,17 @@ static void usb_init_common(struct brcm_usb_init_params *params) } } +static void usb_wake_enable_7211b0(struct brcm_usb_init_params *params, + bool enable) +{ + void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; + + if (enable) + USB_CTRL_SET(ctrl, CTLR_CSHCR, ctl_pme_en); + else + USB_CTRL_UNSET(ctrl, CTLR_CSHCR, ctl_pme_en); +} + static void usb_init_common_7211b0(struct brcm_usb_init_params *params) { void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; @@ -210,9 +233,27 @@ static void usb_init_common_7211b0(struct brcm_usb_init_params *params) if (params->syscon_piarbctl) syscon_piarbctl_init(params->syscon_piarbctl); + USB_CTRL_UNSET(ctrl, USB_PM, USB_PWRDN); + + usb_wake_enable_7211b0(params, false); + if (!params->wake_enabled) { + + /* undo possible suspend settings */ + brcm_usb_writel(0, usb_phy + USB_PHY_IDDQ); + reg = brcm_usb_readl(usb_phy + USB_PHY_PLL_CTL); + reg |= USB_PHY_PLL_CTL_PLL_RESETB_MASK; + brcm_usb_writel(reg, usb_phy + USB_PHY_PLL_CTL); + + /* temporarily enable FSM so PHY comes up properly */ + reg = brcm_usb_readl(usb_phy + USB_PHY_UTMI_CTL_1); + reg |= USB_PHY_UTMI_CTL_1_POWER_UP_FSM_EN_MASK; + brcm_usb_writel(reg, usb_phy + USB_PHY_UTMI_CTL_1); + } + /* Init the PHY */ - reg = brcm_usb_readl(usb_phy + USB_PHY_PLL_LDO_CTL); - reg |= USB_PHY_PLL_LDO_CTL_AFE_CORERDY_MASK; + reg = USB_PHY_PLL_LDO_CTL_AFE_CORERDY_MASK | + USB_PHY_PLL_LDO_CTL_AFE_LDO_PWRDWNB_MASK | + USB_PHY_PLL_LDO_CTL_AFE_BG_PWRDWNB_MASK; brcm_usb_writel(reg, usb_phy + USB_PHY_PLL_LDO_CTL); /* wait for lock */ @@ -276,12 +317,36 @@ static void usb_uninit_common(struct brcm_usb_init_params *params) } +static void usb_uninit_common_7211b0(struct brcm_usb_init_params *params) +{ + void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; + void __iomem *usb_phy = params->regs[BRCM_REGS_USB_PHY]; + u32 reg; + + pr_debug("%s\n", __func__); + + if (params->wake_enabled) { + USB_CTRL_SET(ctrl, TEST_PORT_CTL, TPOUT_SEL_PME_GEN); + usb_wake_enable_7211b0(params, true); + } else { + USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN); + brcm_usb_writel(0, usb_phy + USB_PHY_PLL_LDO_CTL); + reg = brcm_usb_readl(usb_phy + USB_PHY_PLL_CTL); + reg &= ~USB_PHY_PLL_CTL_PLL_RESETB_MASK; + brcm_usb_writel(reg, usb_phy + USB_PHY_PLL_CTL); + brcm_usb_writel(USB_PHY_IDDQ_phy_iddq_MASK, + usb_phy + USB_PHY_IDDQ); + } + +} + static void usb_uninit_xhci(struct brcm_usb_init_params *params) { pr_debug("%s\n", __func__); - xhci_soft_reset(params, 1); + if (!params->wake_enabled) + xhci_soft_reset(params, 1); } static int usb_get_dual_select(struct brcm_usb_init_params *params) @@ -309,7 +374,6 @@ static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode) brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); } - static const struct brcm_usb_init_ops bcm7216_ops = { .init_ipp = usb_init_ipp, .init_common = usb_init_common, @@ -324,7 +388,7 @@ static const struct brcm_usb_init_ops bcm7211b0_ops = { .init_ipp = usb_init_ipp, .init_common = usb_init_common_7211b0, .init_xhci = usb_init_xhci, - .uninit_common = usb_uninit_common, + .uninit_common = usb_uninit_common_7211b0, .uninit_xhci = usb_uninit_xhci, .get_dual_select = usb_get_dual_select, .set_dual_select = usb_set_dual_select, @@ -346,4 +410,5 @@ void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params) params->family_name = "7211"; params->ops = &bcm7211b0_ops; + params->suspend_with_clocks = true; } diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.c b/drivers/phy/broadcom/phy-brcm-usb-init.c index 17acc3c1051b..9391ab42a12b 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init.c @@ -783,12 +783,24 @@ static void usb_init_ipp(struct brcm_usb_init_params *params) msleep(50); } +static void usb_wake_enable(struct brcm_usb_init_params *params, + bool enable) +{ + void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; + + if (enable) + USB_CTRL_SET(ctrl, USB_PM, RMTWKUP_EN); + else + USB_CTRL_UNSET(ctrl, USB_PM, RMTWKUP_EN); +} + static void usb_init_common(struct brcm_usb_init_params *params) { u32 reg; void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; /* Clear any pending wake conditions */ + usb_wake_enable(params, false); reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_PM_STATUS)); brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_PM_STATUS)); @@ -935,6 +947,8 @@ static void usb_uninit_common(struct brcm_usb_init_params *params) if (USB_CTRL_MASK_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN)) USB_CTRL_SET_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN); + if (params->wake_enabled) + usb_wake_enable(params, true); } static void usb_uninit_eohci(struct brcm_usb_init_params *params) @@ -978,17 +992,6 @@ static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode) } } -static void usb_wake_enable(struct brcm_usb_init_params *params, - int enable) -{ - void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; - - if (enable) - USB_CTRL_SET(ctrl, USB_PM, RMTWKUP_EN); - else - USB_CTRL_UNSET(ctrl, USB_PM, RMTWKUP_EN); -} - static const struct brcm_usb_init_ops bcm7445_ops = { .init_ipp = usb_init_ipp, .init_common = usb_init_common, @@ -999,7 +1002,6 @@ static const struct brcm_usb_init_ops bcm7445_ops = { .uninit_xhci = usb_uninit_xhci, .get_dual_select = usb_get_dual_select, .set_dual_select = usb_set_dual_select, - .wake_enable = usb_wake_enable, }; void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params) diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.h b/drivers/phy/broadcom/phy-brcm-usb-init.h index 4cdd9cc1c5a3..899b9eb43fad 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.h +++ b/drivers/phy/broadcom/phy-brcm-usb-init.h @@ -46,8 +46,6 @@ struct brcm_usb_init_ops { void (*uninit_xhci)(struct brcm_usb_init_params *params); int (*get_dual_select)(struct brcm_usb_init_params *params); void (*set_dual_select)(struct brcm_usb_init_params *params, int mode); - void (*wake_enable)(struct brcm_usb_init_params *params, - int enable); }; struct brcm_usb_init_params { @@ -62,6 +60,8 @@ struct brcm_usb_init_params { const u32 *usb_reg_bits_map; const struct brcm_usb_init_ops *ops; struct regmap *syscon_piarbctl; + bool wake_enabled; + bool suspend_with_clocks; }; void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params); @@ -145,13 +145,6 @@ static inline void brcm_usb_uninit_xhci(struct brcm_usb_init_params *ini) ini->ops->uninit_xhci(ini); } -static inline void brcm_usb_wake_enable(struct brcm_usb_init_params *ini, - int enable) -{ - if (ini->ops->wake_enable) - ini->ops->wake_enable(ini, enable); -} - static inline int brcm_usb_get_dual_select(struct brcm_usb_init_params *ini) { if (ini->ops->get_dual_select) diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c index 1ab44f54244b..491bbd46c5b3 100644 --- a/drivers/phy/broadcom/phy-brcm-usb.c +++ b/drivers/phy/broadcom/phy-brcm-usb.c @@ -535,16 +535,26 @@ static int brcm_usb_phy_suspend(struct device *dev) struct brcm_usb_phy_data *priv = dev_get_drvdata(dev); if (priv->init_count) { + priv->ini.wake_enabled = device_may_wakeup(dev); if (priv->phys[BRCM_USB_PHY_3_0].inited) brcm_usb_uninit_xhci(&priv->ini); if (priv->phys[BRCM_USB_PHY_2_0].inited) brcm_usb_uninit_eohci(&priv->ini); brcm_usb_uninit_common(&priv->ini); - brcm_usb_wake_enable(&priv->ini, true); - if (priv->phys[BRCM_USB_PHY_3_0].inited) - clk_disable_unprepare(priv->usb_30_clk); - if (priv->phys[BRCM_USB_PHY_2_0].inited || !priv->has_eohci) - clk_disable_unprepare(priv->usb_20_clk); + + /* + * Handle the clocks unless needed for wake. This has + * to work for both older XHCI->3.0-clks, EOHCI->2.0-clks + * and newer XHCI->2.0-clks/3.0-clks. + */ + + if (!priv->ini.suspend_with_clocks) { + if (priv->phys[BRCM_USB_PHY_3_0].inited) + clk_disable_unprepare(priv->usb_30_clk); + if (priv->phys[BRCM_USB_PHY_2_0].inited || + !priv->has_eohci) + clk_disable_unprepare(priv->usb_20_clk); + } if (priv->wake_irq >= 0) enable_irq_wake(priv->wake_irq); } @@ -557,7 +567,6 @@ static int brcm_usb_phy_resume(struct device *dev) clk_prepare_enable(priv->usb_20_clk); clk_prepare_enable(priv->usb_30_clk); - brcm_usb_wake_enable(&priv->ini, false); brcm_usb_init_ipp(&priv->ini); /* @@ -579,6 +588,8 @@ static int brcm_usb_phy_resume(struct device *dev) } else if (priv->has_xhci) { brcm_usb_uninit_xhci(&priv->ini); clk_disable_unprepare(priv->usb_30_clk); + if (!priv->has_eohci) + clk_disable_unprepare(priv->usb_20_clk); } } else { if (priv->has_xhci) @@ -589,7 +600,7 @@ static int brcm_usb_phy_resume(struct device *dev) clk_disable_unprepare(priv->usb_20_clk); clk_disable_unprepare(priv->usb_30_clk); } - + priv->ini.wake_enabled = false; return 0; } #endif /* CONFIG_PM_SLEEP */ -- cgit v1.2.3-59-g8ed1b From 56d34730c1a220d5015b672088e95f99e8b83b1f Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 16 Dec 2019 15:26:59 +0530 Subject: dt-bindings: phy: Sierra: Add bindings for Sierra in TI's J721E Add DT binding documentation for Sierra PHY IP used in TI's J721E SoC. Signed-off-by: Kishon Vijay Abraham I Reviewed-by: Rob Herring --- .../devicetree/bindings/phy/phy-cadence-sierra.txt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/phy-cadence-sierra.txt b/Documentation/devicetree/bindings/phy/phy-cadence-sierra.txt index 6e1b47bfce43..03f5939d3d19 100644 --- a/Documentation/devicetree/bindings/phy/phy-cadence-sierra.txt +++ b/Documentation/devicetree/bindings/phy/phy-cadence-sierra.txt @@ -2,21 +2,24 @@ Cadence Sierra PHY ----------------------- Required properties: -- compatible: cdns,sierra-phy-t0 -- clocks: Must contain an entry in clock-names. - See ../clocks/clock-bindings.txt for details. -- clock-names: Must be "phy_clk" +- compatible: Must be "cdns,sierra-phy-t0" for Sierra in Cadence platform + Must be "ti,sierra-phy-t0" for Sierra in TI's J721E SoC. - resets: Must contain an entry for each in reset-names. See ../reset/reset.txt for details. - reset-names: Must include "sierra_reset" and "sierra_apb". "sierra_reset" must control the reset line to the PHY. "sierra_apb" must control the reset line to the APB PHY - interface. + interface ("sierra_apb" is optional). - reg: register range for the PHY. - #address-cells: Must be 1 - #size-cells: Must be 0 Optional properties: +- clocks: Must contain an entry in clock-names. + See ../clocks/clock-bindings.txt for details. +- clock-names: Must contain "cmn_refclk_dig_div" and + "cmn_refclk1_dig_div" for configuring the frequency of + the clock to the lanes. "phy_clk" is deprecated. - cdns,autoconf: A boolean property whose presence indicates that the PHY registers will be configured by hardware. If not present, all sub-node optional properties must be -- cgit v1.2.3-59-g8ed1b From 372428db44cf8666ff07fa349d305c1be35cb7d4 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 16 Dec 2019 15:27:00 +0530 Subject: phy: cadence: Sierra: Make "phy_clk" and "sierra_apb" optional resources Certain platforms like TI J721E using Cadence Sierra Serdes doesn't provide explicit phy_clk and reset (APB reset) control. Make them optional here. Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/cadence/phy-cadence-sierra.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index de10402f2931..bed68c25682f 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -193,7 +193,7 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) platform_set_drvdata(pdev, sp); - sp->clk = devm_clk_get(dev, "phy_clk"); + sp->clk = devm_clk_get_optional(dev, "phy_clk"); if (IS_ERR(sp->clk)) { dev_err(dev, "failed to get clock phy_clk\n"); return PTR_ERR(sp->clk); @@ -205,7 +205,7 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) return PTR_ERR(sp->phy_rst); } - sp->apb_rst = devm_reset_control_get(dev, "sierra_apb"); + sp->apb_rst = devm_reset_control_get_optional(dev, "sierra_apb"); if (IS_ERR(sp->apb_rst)) { dev_err(dev, "failed to get apb reset\n"); return PTR_ERR(sp->apb_rst); -- cgit v1.2.3-59-g8ed1b From 380f57083c12936d6189fcda9e954ffcb821ec74 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 16 Dec 2019 15:27:01 +0530 Subject: phy: cadence: Sierra: Use "regmap" for read and write to Sierra registers Use "regmap" for read and write to Sierra registers. This is in perparation for adding SERDES_16G support present in TI's J721E SoC. Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/cadence/phy-cadence-sierra.c | 291 +++++++++++++++++++++++++------ 1 file changed, 237 insertions(+), 54 deletions(-) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index bed68c25682f..c60809f615af 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -22,49 +22,63 @@ #include /* PHY register offsets */ -#define SIERRA_PHY_PLL_CFG (0xc00e << 2) -#define SIERRA_DET_STANDEC_A (0x4000 << 2) -#define SIERRA_DET_STANDEC_B (0x4001 << 2) -#define SIERRA_DET_STANDEC_C (0x4002 << 2) -#define SIERRA_DET_STANDEC_D (0x4003 << 2) -#define SIERRA_DET_STANDEC_E (0x4004 << 2) -#define SIERRA_PSM_LANECAL (0x4008 << 2) -#define SIERRA_PSM_DIAG (0x4015 << 2) -#define SIERRA_PSC_TX_A0 (0x4028 << 2) -#define SIERRA_PSC_TX_A1 (0x4029 << 2) -#define SIERRA_PSC_TX_A2 (0x402A << 2) -#define SIERRA_PSC_TX_A3 (0x402B << 2) -#define SIERRA_PSC_RX_A0 (0x4030 << 2) -#define SIERRA_PSC_RX_A1 (0x4031 << 2) -#define SIERRA_PSC_RX_A2 (0x4032 << 2) -#define SIERRA_PSC_RX_A3 (0x4033 << 2) -#define SIERRA_PLLCTRL_SUBRATE (0x403A << 2) -#define SIERRA_PLLCTRL_GEN_D (0x403E << 2) -#define SIERRA_DRVCTRL_ATTEN (0x406A << 2) -#define SIERRA_CLKPATHCTRL_TMR (0x4081 << 2) -#define SIERRA_RX_CREQ_FLTR_A_MODE1 (0x4087 << 2) -#define SIERRA_RX_CREQ_FLTR_A_MODE0 (0x4088 << 2) -#define SIERRA_CREQ_CCLKDET_MODE01 (0x408E << 2) -#define SIERRA_RX_CTLE_MAINTENANCE (0x4091 << 2) -#define SIERRA_CREQ_FSMCLK_SEL (0x4092 << 2) -#define SIERRA_CTLELUT_CTRL (0x4098 << 2) -#define SIERRA_DFE_ECMP_RATESEL (0x40C0 << 2) -#define SIERRA_DFE_SMP_RATESEL (0x40C1 << 2) -#define SIERRA_DEQ_VGATUNE_CTRL (0x40E1 << 2) -#define SIERRA_TMRVAL_MODE3 (0x416E << 2) -#define SIERRA_TMRVAL_MODE2 (0x416F << 2) -#define SIERRA_TMRVAL_MODE1 (0x4170 << 2) -#define SIERRA_TMRVAL_MODE0 (0x4171 << 2) -#define SIERRA_PICNT_MODE1 (0x4174 << 2) -#define SIERRA_CPI_OUTBUF_RATESEL (0x417C << 2) -#define SIERRA_LFPSFILT_NS (0x418A << 2) -#define SIERRA_LFPSFILT_RD (0x418B << 2) -#define SIERRA_LFPSFILT_MP (0x418C << 2) -#define SIERRA_SDFILT_H2L_A (0x4191 << 2) +#define SIERRA_COMMON_CDB_OFFSET 0x0 +#define SIERRA_MACRO_ID_REG 0x0 + +#define SIERRA_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \ + ((0x4000 << (block_offset)) + \ + (((ln) << 9) << (reg_offset))) +#define SIERRA_DET_STANDEC_A 0x000 +#define SIERRA_DET_STANDEC_B 0x001 +#define SIERRA_DET_STANDEC_C 0x002 +#define SIERRA_DET_STANDEC_D 0x003 +#define SIERRA_DET_STANDEC_E 0x004 +#define SIERRA_PSM_LANECAL 0x008 +#define SIERRA_PSM_DIAG 0x015 +#define SIERRA_PSC_TX_A0 0x028 +#define SIERRA_PSC_TX_A1 0x029 +#define SIERRA_PSC_TX_A2 0x02A +#define SIERRA_PSC_TX_A3 0x02B +#define SIERRA_PSC_RX_A0 0x030 +#define SIERRA_PSC_RX_A1 0x031 +#define SIERRA_PSC_RX_A2 0x032 +#define SIERRA_PSC_RX_A3 0x033 +#define SIERRA_PLLCTRL_SUBRATE 0x03A +#define SIERRA_PLLCTRL_GEN_D 0x03E +#define SIERRA_DRVCTRL_ATTEN 0x06A +#define SIERRA_CLKPATHCTRL_TMR 0x081 +#define SIERRA_RX_CREQ_FLTR_A_MODE1 0x087 +#define SIERRA_RX_CREQ_FLTR_A_MODE0 0x088 +#define SIERRA_CREQ_CCLKDET_MODE01 0x08E +#define SIERRA_RX_CTLE_MAINTENANCE 0x091 +#define SIERRA_CREQ_FSMCLK_SEL 0x092 +#define SIERRA_CTLELUT_CTRL 0x098 +#define SIERRA_DFE_ECMP_RATESEL 0x0C0 +#define SIERRA_DFE_SMP_RATESEL 0x0C1 +#define SIERRA_DEQ_VGATUNE_CTRL 0x0E1 +#define SIERRA_TMRVAL_MODE3 0x16E +#define SIERRA_TMRVAL_MODE2 0x16F +#define SIERRA_TMRVAL_MODE1 0x170 +#define SIERRA_TMRVAL_MODE0 0x171 +#define SIERRA_PICNT_MODE1 0x174 +#define SIERRA_CPI_OUTBUF_RATESEL 0x17C +#define SIERRA_LFPSFILT_NS 0x18A +#define SIERRA_LFPSFILT_RD 0x18B +#define SIERRA_LFPSFILT_MP 0x18C +#define SIERRA_SDFILT_H2L_A 0x191 + +#define SIERRA_PHY_CONFIG_CTRL_OFFSET(block_offset) \ + (0xc000 << (block_offset)) +#define SIERRA_PHY_PLL_CFG 0xe #define SIERRA_MACRO_ID 0x00007364 #define SIERRA_MAX_LANES 4 +static const struct reg_field macro_id_type = + REG_FIELD(SIERRA_MACRO_ID_REG, 0, 15); +static const struct reg_field phy_pll_cfg_1 = + REG_FIELD(SIERRA_PHY_PLL_CFG, 1, 1); + struct cdns_sierra_inst { struct phy *phy; u32 phy_type; @@ -80,28 +94,93 @@ struct cdns_reg_pairs { struct cdns_sierra_data { u32 id_value; + u8 block_offset_shift; + u8 reg_offset_shift; u32 pcie_regs; u32 usb_regs; struct cdns_reg_pairs *pcie_vals; struct cdns_reg_pairs *usb_vals; }; -struct cdns_sierra_phy { +struct cdns_regmap_cdb_context { struct device *dev; void __iomem *base; + u8 reg_offset_shift; +}; + +struct cdns_sierra_phy { + struct device *dev; + struct regmap *regmap; struct cdns_sierra_data *init_data; struct cdns_sierra_inst phys[SIERRA_MAX_LANES]; struct reset_control *phy_rst; struct reset_control *apb_rst; + struct regmap *regmap_lane_cdb[SIERRA_MAX_LANES]; + struct regmap *regmap_phy_config_ctrl; + struct regmap *regmap_common_cdb; + struct regmap_field *macro_id_type; + struct regmap_field *phy_pll_cfg_1; struct clk *clk; int nsubnodes; bool autoconf; }; +static int cdns_regmap_write(void *context, unsigned int reg, unsigned int val) +{ + struct cdns_regmap_cdb_context *ctx = context; + u32 offset = reg << ctx->reg_offset_shift; + + writew(val, ctx->base + offset); + + return 0; +} + +static int cdns_regmap_read(void *context, unsigned int reg, unsigned int *val) +{ + struct cdns_regmap_cdb_context *ctx = context; + u32 offset = reg << ctx->reg_offset_shift; + + *val = readw(ctx->base + offset); + return 0; +} + +#define SIERRA_LANE_CDB_REGMAP_CONF(n) \ +{ \ + .name = "sierra_lane" n "_cdb", \ + .reg_stride = 1, \ + .fast_io = true, \ + .reg_write = cdns_regmap_write, \ + .reg_read = cdns_regmap_read, \ +} + +static struct regmap_config cdns_sierra_lane_cdb_config[] = { + SIERRA_LANE_CDB_REGMAP_CONF("0"), + SIERRA_LANE_CDB_REGMAP_CONF("1"), + SIERRA_LANE_CDB_REGMAP_CONF("2"), + SIERRA_LANE_CDB_REGMAP_CONF("3"), +}; + +static struct regmap_config cdns_sierra_common_cdb_config = { + .name = "sierra_common_cdb", + .reg_stride = 1, + .fast_io = true, + .reg_write = cdns_regmap_write, + .reg_read = cdns_regmap_read, +}; + +static struct regmap_config cdns_sierra_phy_config_ctrl_config = { + .name = "sierra_phy_config_ctrl", + .reg_stride = 1, + .fast_io = true, + .reg_write = cdns_regmap_write, + .reg_read = cdns_regmap_read, +}; + static void cdns_sierra_phy_init(struct phy *gphy) { struct cdns_sierra_inst *ins = phy_get_drvdata(gphy); struct cdns_sierra_phy *phy = dev_get_drvdata(gphy->dev.parent); + struct regmap *regmap = phy->regmap; int i, j; struct cdns_reg_pairs *vals; u32 num_regs; @@ -115,10 +194,12 @@ static void cdns_sierra_phy_init(struct phy *gphy) } else { return; } - for (i = 0; i < ins->num_lanes; i++) - for (j = 0; j < num_regs ; j++) - writel(vals[j].val, phy->base + - vals[j].off + (i + ins->mlane) * 0x800); + for (i = 0; i < ins->num_lanes; i++) { + for (j = 0; j < num_regs ; j++) { + regmap = phy->regmap_lane_cdb[i + ins->mlane]; + regmap_write(regmap, vals[j].off, vals[j].val); + } + } } static int cdns_sierra_phy_on(struct phy *gphy) @@ -159,37 +240,136 @@ static int cdns_sierra_get_optional(struct cdns_sierra_inst *inst, static const struct of_device_id cdns_sierra_id_table[]; +static struct regmap *cdns_regmap_init(struct device *dev, void __iomem *base, + u32 block_offset, u8 reg_offset_shift, + const struct regmap_config *config) +{ + struct cdns_regmap_cdb_context *ctx; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return ERR_PTR(-ENOMEM); + + ctx->dev = dev; + ctx->base = base + block_offset; + ctx->reg_offset_shift = reg_offset_shift; + + return devm_regmap_init(dev, NULL, ctx, config); +} + +static int cdns_regfield_init(struct cdns_sierra_phy *sp) +{ + struct device *dev = sp->dev; + struct regmap_field *field; + struct regmap *regmap; + + regmap = sp->regmap_common_cdb; + field = devm_regmap_field_alloc(dev, regmap, macro_id_type); + if (IS_ERR(field)) { + dev_err(dev, "MACRO_ID_TYPE reg field init failed\n"); + return PTR_ERR(field); + } + sp->macro_id_type = field; + + regmap = sp->regmap_phy_config_ctrl; + field = devm_regmap_field_alloc(dev, regmap, phy_pll_cfg_1); + if (IS_ERR(field)) { + dev_err(dev, "PHY_PLL_CFG_1 reg field init failed\n"); + return PTR_ERR(field); + } + sp->phy_pll_cfg_1 = field; + + return 0; +} + +static int cdns_regmap_init_blocks(struct cdns_sierra_phy *sp, + void __iomem *base, u8 block_offset_shift, + u8 reg_offset_shift) +{ + struct device *dev = sp->dev; + struct regmap *regmap; + u32 block_offset; + int i; + + for (i = 0; i < SIERRA_MAX_LANES; i++) { + block_offset = SIERRA_LANE_CDB_OFFSET(i, block_offset_shift, + reg_offset_shift); + regmap = cdns_regmap_init(dev, base, block_offset, + reg_offset_shift, + &cdns_sierra_lane_cdb_config[i]); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to init lane CDB regmap\n"); + return PTR_ERR(regmap); + } + sp->regmap_lane_cdb[i] = regmap; + } + + regmap = cdns_regmap_init(dev, base, SIERRA_COMMON_CDB_OFFSET, + reg_offset_shift, + &cdns_sierra_common_cdb_config); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to init common CDB regmap\n"); + return PTR_ERR(regmap); + } + sp->regmap_common_cdb = regmap; + + block_offset = SIERRA_PHY_CONFIG_CTRL_OFFSET(block_offset_shift); + regmap = cdns_regmap_init(dev, base, block_offset, reg_offset_shift, + &cdns_sierra_phy_config_ctrl_config); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to init PHY config and control regmap\n"); + return PTR_ERR(regmap); + } + sp->regmap_phy_config_ctrl = regmap; + + return 0; +} + static int cdns_sierra_phy_probe(struct platform_device *pdev) { struct cdns_sierra_phy *sp; struct phy_provider *phy_provider; struct device *dev = &pdev->dev; const struct of_device_id *match; + struct cdns_sierra_data *data; + unsigned int id_value; struct resource *res; int i, ret, node = 0; + void __iomem *base; struct device_node *dn = dev->of_node, *child; if (of_get_child_count(dn) == 0) return -ENODEV; + /* Get init data for this PHY */ + match = of_match_device(cdns_sierra_id_table, dev); + if (!match) + return -EINVAL; + + data = (struct cdns_sierra_data *)match->data; + sp = devm_kzalloc(dev, sizeof(*sp), GFP_KERNEL); if (!sp) return -ENOMEM; dev_set_drvdata(dev, sp); sp->dev = dev; + sp->init_data = data; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - sp->base = devm_ioremap_resource(dev, res); - if (IS_ERR(sp->base)) { + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) { dev_err(dev, "missing \"reg\"\n"); - return PTR_ERR(sp->base); + return PTR_ERR(base); } - /* Get init data for this PHY */ - match = of_match_device(cdns_sierra_id_table, dev); - if (!match) - return -EINVAL; - sp->init_data = (struct cdns_sierra_data *)match->data; + ret = cdns_regmap_init_blocks(sp, base, data->block_offset_shift, + data->reg_offset_shift); + if (ret) + return ret; + + ret = cdns_regfield_init(sp); + if (ret) + return ret; platform_set_drvdata(pdev, sp); @@ -219,7 +399,8 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) reset_control_deassert(sp->apb_rst); /* Check that PHY is present */ - if (sp->init_data->id_value != readl(sp->base)) { + regmap_field_read(sp->macro_id_type, &id_value); + if (sp->init_data->id_value != id_value) { ret = -EINVAL; goto clk_disable; } @@ -267,7 +448,7 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) /* If more than one subnode, configure the PHY as multilink */ if (!sp->autoconf && sp->nsubnodes > 1) - writel(2, sp->base + SIERRA_PHY_PLL_CFG); + regmap_field_write(sp->phy_pll_cfg_1, 0x1); pm_runtime_enable(dev); phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); @@ -364,6 +545,8 @@ static struct cdns_reg_pairs cdns_pcie_regs[] = { static const struct cdns_sierra_data cdns_map_sierra = { SIERRA_MACRO_ID, + 0x2, + 0x2, ARRAY_SIZE(cdns_pcie_regs), ARRAY_SIZE(cdns_usb_regs), cdns_pcie_regs, -- cgit v1.2.3-59-g8ed1b From 367da978713b4efa1c1689935c5c5d839e778c67 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 16 Dec 2019 15:27:02 +0530 Subject: phy: cadence: Sierra: Add support for SERDES_16G used in J721E SoC SERDES_16G in TI's J721E SoC uses Cadence Sierra PHY. Add support to use Cadence Sierra driver in J721E SoC. Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/cadence/phy-cadence-sierra.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index c60809f615af..d3b0dac2db77 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -553,11 +553,25 @@ static const struct cdns_sierra_data cdns_map_sierra = { cdns_usb_regs }; +static const struct cdns_sierra_data cdns_ti_map_sierra = { + SIERRA_MACRO_ID, + 0x0, + 0x1, + ARRAY_SIZE(cdns_pcie_regs), + ARRAY_SIZE(cdns_usb_regs), + cdns_pcie_regs, + cdns_usb_regs +}; + static const struct of_device_id cdns_sierra_id_table[] = { { .compatible = "cdns,sierra-phy-t0", .data = &cdns_map_sierra, }, + { + .compatible = "ti,sierra-phy-t0", + .data = &cdns_ti_map_sierra, + }, {} }; MODULE_DEVICE_TABLE(of, cdns_sierra_id_table); -- cgit v1.2.3-59-g8ed1b From cedcc2e2ea39c4b47c417461a64eec27dedd335b Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 16 Dec 2019 15:27:03 +0530 Subject: phy: cadence: Sierra: Make cdns_sierra_phy_init() as phy_ops Instead of invoking cdns_sierra_phy_init() from probe, add it in phy_ops so that it's initialized when the PHY consumer invokes phy_init() Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/cadence/phy-cadence-sierra.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index d3b0dac2db77..bc2ae260359c 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -176,7 +176,7 @@ static struct regmap_config cdns_sierra_phy_config_ctrl_config = { .reg_read = cdns_regmap_read, }; -static void cdns_sierra_phy_init(struct phy *gphy) +static int cdns_sierra_phy_init(struct phy *gphy) { struct cdns_sierra_inst *ins = phy_get_drvdata(gphy); struct cdns_sierra_phy *phy = dev_get_drvdata(gphy->dev.parent); @@ -185,6 +185,10 @@ static void cdns_sierra_phy_init(struct phy *gphy) struct cdns_reg_pairs *vals; u32 num_regs; + /* Initialise the PHY registers, unless auto configured */ + if (phy->autoconf) + return 0; + if (ins->phy_type == PHY_TYPE_PCIE) { num_regs = phy->init_data->pcie_regs; vals = phy->init_data->pcie_vals; @@ -192,7 +196,7 @@ static void cdns_sierra_phy_init(struct phy *gphy) num_regs = phy->init_data->usb_regs; vals = phy->init_data->usb_vals; } else { - return; + return -EINVAL; } for (i = 0; i < ins->num_lanes; i++) { for (j = 0; j < num_regs ; j++) { @@ -200,6 +204,8 @@ static void cdns_sierra_phy_init(struct phy *gphy) regmap_write(regmap, vals[j].off, vals[j].val); } } + + return 0; } static int cdns_sierra_phy_on(struct phy *gphy) @@ -218,6 +224,7 @@ static int cdns_sierra_phy_off(struct phy *gphy) } static const struct phy_ops ops = { + .init = cdns_sierra_phy_init, .power_on = cdns_sierra_phy_on, .power_off = cdns_sierra_phy_off, .owner = THIS_MODULE, @@ -438,10 +445,6 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) sp->phys[node].phy = gphy; phy_set_drvdata(gphy, &sp->phys[node]); - /* Initialise the PHY registers, unless auto configured */ - if (!sp->autoconf) - cdns_sierra_phy_init(gphy); - node++; } sp->nsubnodes = node; -- cgit v1.2.3-59-g8ed1b From aead5fd6026d4006e494167b07a44254af8b43a9 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 16 Dec 2019 15:27:04 +0530 Subject: phy: cadence: Sierra: Modify register macro names to be in sync with Sierra user guide No functional change. Modify register offset macro names to be in sync with Sierra user guide. Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/cadence/phy-cadence-sierra.c | 167 ++++++++++++++++--------------- 1 file changed, 84 insertions(+), 83 deletions(-) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index bc2ae260359c..d490e1641cf9 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -22,57 +22,58 @@ #include /* PHY register offsets */ -#define SIERRA_COMMON_CDB_OFFSET 0x0 -#define SIERRA_MACRO_ID_REG 0x0 +#define SIERRA_COMMON_CDB_OFFSET 0x0 +#define SIERRA_MACRO_ID_REG 0x0 #define SIERRA_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \ ((0x4000 << (block_offset)) + \ (((ln) << 9) << (reg_offset))) -#define SIERRA_DET_STANDEC_A 0x000 -#define SIERRA_DET_STANDEC_B 0x001 -#define SIERRA_DET_STANDEC_C 0x002 -#define SIERRA_DET_STANDEC_D 0x003 -#define SIERRA_DET_STANDEC_E 0x004 -#define SIERRA_PSM_LANECAL 0x008 -#define SIERRA_PSM_DIAG 0x015 -#define SIERRA_PSC_TX_A0 0x028 -#define SIERRA_PSC_TX_A1 0x029 -#define SIERRA_PSC_TX_A2 0x02A -#define SIERRA_PSC_TX_A3 0x02B -#define SIERRA_PSC_RX_A0 0x030 -#define SIERRA_PSC_RX_A1 0x031 -#define SIERRA_PSC_RX_A2 0x032 -#define SIERRA_PSC_RX_A3 0x033 -#define SIERRA_PLLCTRL_SUBRATE 0x03A -#define SIERRA_PLLCTRL_GEN_D 0x03E -#define SIERRA_DRVCTRL_ATTEN 0x06A -#define SIERRA_CLKPATHCTRL_TMR 0x081 -#define SIERRA_RX_CREQ_FLTR_A_MODE1 0x087 -#define SIERRA_RX_CREQ_FLTR_A_MODE0 0x088 -#define SIERRA_CREQ_CCLKDET_MODE01 0x08E -#define SIERRA_RX_CTLE_MAINTENANCE 0x091 -#define SIERRA_CREQ_FSMCLK_SEL 0x092 -#define SIERRA_CTLELUT_CTRL 0x098 -#define SIERRA_DFE_ECMP_RATESEL 0x0C0 -#define SIERRA_DFE_SMP_RATESEL 0x0C1 -#define SIERRA_DEQ_VGATUNE_CTRL 0x0E1 -#define SIERRA_TMRVAL_MODE3 0x16E -#define SIERRA_TMRVAL_MODE2 0x16F -#define SIERRA_TMRVAL_MODE1 0x170 -#define SIERRA_TMRVAL_MODE0 0x171 -#define SIERRA_PICNT_MODE1 0x174 -#define SIERRA_CPI_OUTBUF_RATESEL 0x17C -#define SIERRA_LFPSFILT_NS 0x18A -#define SIERRA_LFPSFILT_RD 0x18B -#define SIERRA_LFPSFILT_MP 0x18C -#define SIERRA_SDFILT_H2L_A 0x191 + +#define SIERRA_DET_STANDEC_A_PREG 0x000 +#define SIERRA_DET_STANDEC_B_PREG 0x001 +#define SIERRA_DET_STANDEC_C_PREG 0x002 +#define SIERRA_DET_STANDEC_D_PREG 0x003 +#define SIERRA_DET_STANDEC_E_PREG 0x004 +#define SIERRA_PSM_LANECAL_PREG 0x008 +#define SIERRA_PSM_DIAG_PREG 0x015 +#define SIERRA_PSC_TX_A0_PREG 0x028 +#define SIERRA_PSC_TX_A1_PREG 0x029 +#define SIERRA_PSC_TX_A2_PREG 0x02A +#define SIERRA_PSC_TX_A3_PREG 0x02B +#define SIERRA_PSC_RX_A0_PREG 0x030 +#define SIERRA_PSC_RX_A1_PREG 0x031 +#define SIERRA_PSC_RX_A2_PREG 0x032 +#define SIERRA_PSC_RX_A3_PREG 0x033 +#define SIERRA_PLLCTRL_SUBRATE_PREG 0x03A +#define SIERRA_PLLCTRL_GEN_D_PREG 0x03E +#define SIERRA_DRVCTRL_ATTEN_PREG 0x06A +#define SIERRA_CLKPATHCTRL_TMR_PREG 0x081 +#define SIERRA_RX_CREQ_FLTR_A_MODE1_PREG 0x087 +#define SIERRA_RX_CREQ_FLTR_A_MODE0_PREG 0x088 +#define SIERRA_CREQ_CCLKDET_MODE01_PREG 0x08E +#define SIERRA_RX_CTLE_MAINTENANCE_PREG 0x091 +#define SIERRA_CREQ_FSMCLK_SEL_PREG 0x092 +#define SIERRA_CTLELUT_CTRL_PREG 0x098 +#define SIERRA_DFE_ECMP_RATESEL_PREG 0x0C0 +#define SIERRA_DFE_SMP_RATESEL_PREG 0x0C1 +#define SIERRA_DEQ_VGATUNE_CTRL_PREG 0x0E1 +#define SIERRA_TMRVAL_MODE3_PREG 0x16E +#define SIERRA_TMRVAL_MODE2_PREG 0x16F +#define SIERRA_TMRVAL_MODE1_PREG 0x170 +#define SIERRA_TMRVAL_MODE0_PREG 0x171 +#define SIERRA_PICNT_MODE1_PREG 0x174 +#define SIERRA_CPI_OUTBUF_RATESEL_PREG 0x17C +#define SIERRA_LFPSFILT_NS_PREG 0x18A +#define SIERRA_LFPSFILT_RD_PREG 0x18B +#define SIERRA_LFPSFILT_MP_PREG 0x18C +#define SIERRA_SDFILT_H2L_A_PREG 0x191 #define SIERRA_PHY_CONFIG_CTRL_OFFSET(block_offset) \ (0xc000 << (block_offset)) -#define SIERRA_PHY_PLL_CFG 0xe +#define SIERRA_PHY_PLL_CFG 0xe -#define SIERRA_MACRO_ID 0x00007364 -#define SIERRA_MAX_LANES 4 +#define SIERRA_MACRO_ID 0x00007364 +#define SIERRA_MAX_LANES 4 static const struct reg_field macro_id_type = REG_FIELD(SIERRA_MACRO_ID_REG, 0, 15); @@ -496,42 +497,42 @@ static struct cdns_reg_pairs cdns_usb_regs[] = { * These values are specific to this specific hardware * configuration. */ - {0xFE0A, SIERRA_DET_STANDEC_A}, - {0x000F, SIERRA_DET_STANDEC_B}, - {0x55A5, SIERRA_DET_STANDEC_C}, - {0x69AD, SIERRA_DET_STANDEC_D}, - {0x0241, SIERRA_DET_STANDEC_E}, - {0x0110, SIERRA_PSM_LANECAL}, - {0xCF00, SIERRA_PSM_DIAG}, - {0x001F, SIERRA_PSC_TX_A0}, - {0x0007, SIERRA_PSC_TX_A1}, - {0x0003, SIERRA_PSC_TX_A2}, - {0x0003, SIERRA_PSC_TX_A3}, - {0x0FFF, SIERRA_PSC_RX_A0}, - {0x0003, SIERRA_PSC_RX_A1}, - {0x0003, SIERRA_PSC_RX_A2}, - {0x0001, SIERRA_PSC_RX_A3}, - {0x0001, SIERRA_PLLCTRL_SUBRATE}, - {0x0406, SIERRA_PLLCTRL_GEN_D}, - {0x0000, SIERRA_DRVCTRL_ATTEN}, - {0x823E, SIERRA_CLKPATHCTRL_TMR}, - {0x078F, SIERRA_RX_CREQ_FLTR_A_MODE1}, - {0x078F, SIERRA_RX_CREQ_FLTR_A_MODE0}, - {0x7B3C, SIERRA_CREQ_CCLKDET_MODE01}, - {0x023C, SIERRA_RX_CTLE_MAINTENANCE}, - {0x3232, SIERRA_CREQ_FSMCLK_SEL}, - {0x8452, SIERRA_CTLELUT_CTRL}, - {0x4121, SIERRA_DFE_ECMP_RATESEL}, - {0x4121, SIERRA_DFE_SMP_RATESEL}, - {0x9999, SIERRA_DEQ_VGATUNE_CTRL}, - {0x0330, SIERRA_TMRVAL_MODE0}, - {0x01FF, SIERRA_PICNT_MODE1}, - {0x0009, SIERRA_CPI_OUTBUF_RATESEL}, - {0x000F, SIERRA_LFPSFILT_NS}, - {0x0009, SIERRA_LFPSFILT_RD}, - {0x0001, SIERRA_LFPSFILT_MP}, - {0x8013, SIERRA_SDFILT_H2L_A}, - {0x0400, SIERRA_TMRVAL_MODE1}, + {0xFE0A, SIERRA_DET_STANDEC_A_PREG}, + {0x000F, SIERRA_DET_STANDEC_B_PREG}, + {0x55A5, SIERRA_DET_STANDEC_C_PREG}, + {0x69AD, SIERRA_DET_STANDEC_D_PREG}, + {0x0241, SIERRA_DET_STANDEC_E_PREG}, + {0x0110, SIERRA_PSM_LANECAL_PREG}, + {0xCF00, SIERRA_PSM_DIAG_PREG}, + {0x001F, SIERRA_PSC_TX_A0_PREG}, + {0x0007, SIERRA_PSC_TX_A1_PREG}, + {0x0003, SIERRA_PSC_TX_A2_PREG}, + {0x0003, SIERRA_PSC_TX_A3_PREG}, + {0x0FFF, SIERRA_PSC_RX_A0_PREG}, + {0x0003, SIERRA_PSC_RX_A1_PREG}, + {0x0003, SIERRA_PSC_RX_A2_PREG}, + {0x0001, SIERRA_PSC_RX_A3_PREG}, + {0x0001, SIERRA_PLLCTRL_SUBRATE_PREG}, + {0x0406, SIERRA_PLLCTRL_GEN_D_PREG}, + {0x0000, SIERRA_DRVCTRL_ATTEN_PREG}, + {0x823E, SIERRA_CLKPATHCTRL_TMR_PREG}, + {0x078F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG}, + {0x078F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, + {0x7B3C, SIERRA_CREQ_CCLKDET_MODE01_PREG}, + {0x023C, SIERRA_RX_CTLE_MAINTENANCE_PREG}, + {0x3232, SIERRA_CREQ_FSMCLK_SEL_PREG}, + {0x8452, SIERRA_CTLELUT_CTRL_PREG}, + {0x4121, SIERRA_DFE_ECMP_RATESEL_PREG}, + {0x4121, SIERRA_DFE_SMP_RATESEL_PREG}, + {0x9999, SIERRA_DEQ_VGATUNE_CTRL_PREG}, + {0x0330, SIERRA_TMRVAL_MODE0_PREG}, + {0x01FF, SIERRA_PICNT_MODE1_PREG}, + {0x0009, SIERRA_CPI_OUTBUF_RATESEL_PREG}, + {0x000F, SIERRA_LFPSFILT_NS_PREG}, + {0x0009, SIERRA_LFPSFILT_RD_PREG}, + {0x0001, SIERRA_LFPSFILT_MP_PREG}, + {0x8013, SIERRA_SDFILT_H2L_A_PREG}, + {0x0400, SIERRA_TMRVAL_MODE1_PREG}, }; static struct cdns_reg_pairs cdns_pcie_regs[] = { @@ -540,10 +541,10 @@ static struct cdns_reg_pairs cdns_pcie_regs[] = { * These values are specific to this specific hardware * configuration. */ - {0x891f, SIERRA_DET_STANDEC_D}, - {0x0053, SIERRA_DET_STANDEC_E}, - {0x0400, SIERRA_TMRVAL_MODE2}, - {0x0200, SIERRA_TMRVAL_MODE3}, + {0x891f, SIERRA_DET_STANDEC_D_PREG}, + {0x0053, SIERRA_DET_STANDEC_E_PREG}, + {0x0400, SIERRA_TMRVAL_MODE2_PREG}, + {0x0200, SIERRA_TMRVAL_MODE3_PREG}, }; static const struct cdns_sierra_data cdns_map_sierra = { -- cgit v1.2.3-59-g8ed1b From 871002d788817eb4cd0cd03101d284c3db06ed74 Mon Sep 17 00:00:00 2001 From: Anil Varughese Date: Mon, 16 Dec 2019 15:27:05 +0530 Subject: phy: cadence: Sierra: Configure both lane cdb and common cdb registers for external SSC The existing configuration done in Cadence Sierra driver is only for reference and is not used in any platforms. Remove them and configure both lane cdb and common cdb registers to be used with external SSC configuration. This is validated in TI J721E platform. Signed-off-by: Anil Varughese Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/cadence/phy-cadence-sierra.c | 350 ++++++++++++++++++++++--------- 1 file changed, 254 insertions(+), 96 deletions(-) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index d490e1641cf9..fdca3bd178c6 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -22,58 +22,125 @@ #include /* PHY register offsets */ -#define SIERRA_COMMON_CDB_OFFSET 0x0 -#define SIERRA_MACRO_ID_REG 0x0 +#define SIERRA_COMMON_CDB_OFFSET 0x0 +#define SIERRA_MACRO_ID_REG 0x0 +#define SIERRA_CMN_PLLLC_MODE_PREG 0x48 +#define SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG 0x49 +#define SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG 0x4A +#define SIERRA_CMN_PLLLC_LOCK_CNTSTART_PREG 0x4B +#define SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG 0x4F +#define SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG 0x50 +#define SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG 0x62 #define SIERRA_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \ ((0x4000 << (block_offset)) + \ (((ln) << 9) << (reg_offset))) -#define SIERRA_DET_STANDEC_A_PREG 0x000 -#define SIERRA_DET_STANDEC_B_PREG 0x001 -#define SIERRA_DET_STANDEC_C_PREG 0x002 -#define SIERRA_DET_STANDEC_D_PREG 0x003 -#define SIERRA_DET_STANDEC_E_PREG 0x004 -#define SIERRA_PSM_LANECAL_PREG 0x008 -#define SIERRA_PSM_DIAG_PREG 0x015 -#define SIERRA_PSC_TX_A0_PREG 0x028 -#define SIERRA_PSC_TX_A1_PREG 0x029 -#define SIERRA_PSC_TX_A2_PREG 0x02A -#define SIERRA_PSC_TX_A3_PREG 0x02B -#define SIERRA_PSC_RX_A0_PREG 0x030 -#define SIERRA_PSC_RX_A1_PREG 0x031 -#define SIERRA_PSC_RX_A2_PREG 0x032 -#define SIERRA_PSC_RX_A3_PREG 0x033 -#define SIERRA_PLLCTRL_SUBRATE_PREG 0x03A -#define SIERRA_PLLCTRL_GEN_D_PREG 0x03E -#define SIERRA_DRVCTRL_ATTEN_PREG 0x06A -#define SIERRA_CLKPATHCTRL_TMR_PREG 0x081 -#define SIERRA_RX_CREQ_FLTR_A_MODE1_PREG 0x087 -#define SIERRA_RX_CREQ_FLTR_A_MODE0_PREG 0x088 -#define SIERRA_CREQ_CCLKDET_MODE01_PREG 0x08E -#define SIERRA_RX_CTLE_MAINTENANCE_PREG 0x091 -#define SIERRA_CREQ_FSMCLK_SEL_PREG 0x092 -#define SIERRA_CTLELUT_CTRL_PREG 0x098 -#define SIERRA_DFE_ECMP_RATESEL_PREG 0x0C0 -#define SIERRA_DFE_SMP_RATESEL_PREG 0x0C1 -#define SIERRA_DEQ_VGATUNE_CTRL_PREG 0x0E1 -#define SIERRA_TMRVAL_MODE3_PREG 0x16E -#define SIERRA_TMRVAL_MODE2_PREG 0x16F -#define SIERRA_TMRVAL_MODE1_PREG 0x170 -#define SIERRA_TMRVAL_MODE0_PREG 0x171 -#define SIERRA_PICNT_MODE1_PREG 0x174 -#define SIERRA_CPI_OUTBUF_RATESEL_PREG 0x17C -#define SIERRA_LFPSFILT_NS_PREG 0x18A -#define SIERRA_LFPSFILT_RD_PREG 0x18B -#define SIERRA_LFPSFILT_MP_PREG 0x18C -#define SIERRA_SDFILT_H2L_A_PREG 0x191 +#define SIERRA_DET_STANDEC_A_PREG 0x000 +#define SIERRA_DET_STANDEC_B_PREG 0x001 +#define SIERRA_DET_STANDEC_C_PREG 0x002 +#define SIERRA_DET_STANDEC_D_PREG 0x003 +#define SIERRA_DET_STANDEC_E_PREG 0x004 +#define SIERRA_PSM_LANECAL_DLY_A1_RESETS_PREG 0x008 +#define SIERRA_PSM_A0IN_TMR_PREG 0x009 +#define SIERRA_PSM_DIAG_PREG 0x015 +#define SIERRA_PSC_TX_A0_PREG 0x028 +#define SIERRA_PSC_TX_A1_PREG 0x029 +#define SIERRA_PSC_TX_A2_PREG 0x02A +#define SIERRA_PSC_TX_A3_PREG 0x02B +#define SIERRA_PSC_RX_A0_PREG 0x030 +#define SIERRA_PSC_RX_A1_PREG 0x031 +#define SIERRA_PSC_RX_A2_PREG 0x032 +#define SIERRA_PSC_RX_A3_PREG 0x033 +#define SIERRA_PLLCTRL_SUBRATE_PREG 0x03A +#define SIERRA_PLLCTRL_GEN_D_PREG 0x03E +#define SIERRA_PLLCTRL_CPGAIN_MODE_PREG 0x03F +#define SIERRA_CLKPATH_BIASTRIM_PREG 0x04B +#define SIERRA_DFE_BIASTRIM_PREG 0x04C +#define SIERRA_DRVCTRL_ATTEN_PREG 0x06A +#define SIERRA_CLKPATHCTRL_TMR_PREG 0x081 +#define SIERRA_RX_CREQ_FLTR_A_MODE3_PREG 0x085 +#define SIERRA_RX_CREQ_FLTR_A_MODE2_PREG 0x086 +#define SIERRA_RX_CREQ_FLTR_A_MODE1_PREG 0x087 +#define SIERRA_RX_CREQ_FLTR_A_MODE0_PREG 0x088 +#define SIERRA_CREQ_CCLKDET_MODE01_PREG 0x08E +#define SIERRA_RX_CTLE_MAINTENANCE_PREG 0x091 +#define SIERRA_CREQ_FSMCLK_SEL_PREG 0x092 +#define SIERRA_CREQ_EQ_CTRL_PREG 0x093 +#define SIERRA_CREQ_SPARE_PREG 0x096 +#define SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG 0x097 +#define SIERRA_CTLELUT_CTRL_PREG 0x098 +#define SIERRA_DFE_ECMP_RATESEL_PREG 0x0C0 +#define SIERRA_DFE_SMP_RATESEL_PREG 0x0C1 +#define SIERRA_DEQ_PHALIGN_CTRL 0x0C4 +#define SIERRA_DEQ_CONCUR_CTRL1_PREG 0x0C8 +#define SIERRA_DEQ_CONCUR_CTRL2_PREG 0x0C9 +#define SIERRA_DEQ_EPIPWR_CTRL2_PREG 0x0CD +#define SIERRA_DEQ_FAST_MAINT_CYCLES_PREG 0x0CE +#define SIERRA_DEQ_ERRCMP_CTRL_PREG 0x0D0 +#define SIERRA_DEQ_OFFSET_CTRL_PREG 0x0D8 +#define SIERRA_DEQ_GAIN_CTRL_PREG 0x0E0 +#define SIERRA_DEQ_VGATUNE_CTRL_PREG 0x0E1 +#define SIERRA_DEQ_GLUT0 0x0E8 +#define SIERRA_DEQ_GLUT1 0x0E9 +#define SIERRA_DEQ_GLUT2 0x0EA +#define SIERRA_DEQ_GLUT3 0x0EB +#define SIERRA_DEQ_GLUT4 0x0EC +#define SIERRA_DEQ_GLUT5 0x0ED +#define SIERRA_DEQ_GLUT6 0x0EE +#define SIERRA_DEQ_GLUT7 0x0EF +#define SIERRA_DEQ_GLUT8 0x0F0 +#define SIERRA_DEQ_GLUT9 0x0F1 +#define SIERRA_DEQ_GLUT10 0x0F2 +#define SIERRA_DEQ_GLUT11 0x0F3 +#define SIERRA_DEQ_GLUT12 0x0F4 +#define SIERRA_DEQ_GLUT13 0x0F5 +#define SIERRA_DEQ_GLUT14 0x0F6 +#define SIERRA_DEQ_GLUT15 0x0F7 +#define SIERRA_DEQ_GLUT16 0x0F8 +#define SIERRA_DEQ_ALUT0 0x108 +#define SIERRA_DEQ_ALUT1 0x109 +#define SIERRA_DEQ_ALUT2 0x10A +#define SIERRA_DEQ_ALUT3 0x10B +#define SIERRA_DEQ_ALUT4 0x10C +#define SIERRA_DEQ_ALUT5 0x10D +#define SIERRA_DEQ_ALUT6 0x10E +#define SIERRA_DEQ_ALUT7 0x10F +#define SIERRA_DEQ_ALUT8 0x110 +#define SIERRA_DEQ_ALUT9 0x111 +#define SIERRA_DEQ_ALUT10 0x112 +#define SIERRA_DEQ_ALUT11 0x113 +#define SIERRA_DEQ_ALUT12 0x114 +#define SIERRA_DEQ_ALUT13 0x115 +#define SIERRA_DEQ_DFETAP_CTRL_PREG 0x128 +#define SIERRA_DFE_EN_1010_IGNORE_PREG 0x134 +#define SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG 0x150 +#define SIERRA_DEQ_TAU_CTRL2_PREG 0x151 +#define SIERRA_DEQ_PICTRL_PREG 0x161 +#define SIERRA_CPICAL_TMRVAL_MODE1_PREG 0x170 +#define SIERRA_CPICAL_TMRVAL_MODE0_PREG 0x171 +#define SIERRA_CPICAL_PICNT_MODE1_PREG 0x174 +#define SIERRA_CPI_OUTBUF_RATESEL_PREG 0x17C +#define SIERRA_CPICAL_RES_STARTCODE_MODE23_PREG 0x183 +#define SIERRA_LFPSDET_SUPPORT_PREG 0x188 +#define SIERRA_LFPSFILT_NS_PREG 0x18A +#define SIERRA_LFPSFILT_RD_PREG 0x18B +#define SIERRA_LFPSFILT_MP_PREG 0x18C +#define SIERRA_SIGDET_SUPPORT_PREG 0x190 +#define SIERRA_SDFILT_H2L_A_PREG 0x191 +#define SIERRA_SDFILT_L2H_PREG 0x193 +#define SIERRA_RXBUFFER_CTLECTRL_PREG 0x19E +#define SIERRA_RXBUFFER_RCDFECTRL_PREG 0x19F +#define SIERRA_RXBUFFER_DFECTRL_PREG 0x1A0 +#define SIERRA_DEQ_TAU_CTRL1_FAST_MAINT_PREG 0x14F +#define SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG 0x150 #define SIERRA_PHY_CONFIG_CTRL_OFFSET(block_offset) \ (0xc000 << (block_offset)) -#define SIERRA_PHY_PLL_CFG 0xe +#define SIERRA_PHY_PLL_CFG 0xe -#define SIERRA_MACRO_ID 0x00007364 -#define SIERRA_MAX_LANES 4 +#define SIERRA_MACRO_ID 0x00007364 +#define SIERRA_MAX_LANES 4 static const struct reg_field macro_id_type = REG_FIELD(SIERRA_MACRO_ID_REG, 0, 15); @@ -97,10 +164,14 @@ struct cdns_sierra_data { u32 id_value; u8 block_offset_shift; u8 reg_offset_shift; - u32 pcie_regs; - u32 usb_regs; - struct cdns_reg_pairs *pcie_vals; - struct cdns_reg_pairs *usb_vals; + u32 pcie_cmn_regs; + u32 pcie_ln_regs; + u32 usb_cmn_regs; + u32 usb_ln_regs; + struct cdns_reg_pairs *pcie_cmn_vals; + struct cdns_reg_pairs *pcie_ln_vals; + struct cdns_reg_pairs *usb_cmn_vals; + struct cdns_reg_pairs *usb_ln_vals; }; struct cdns_regmap_cdb_context { @@ -183,26 +254,35 @@ static int cdns_sierra_phy_init(struct phy *gphy) struct cdns_sierra_phy *phy = dev_get_drvdata(gphy->dev.parent); struct regmap *regmap = phy->regmap; int i, j; - struct cdns_reg_pairs *vals; - u32 num_regs; + struct cdns_reg_pairs *cmn_vals, *ln_vals; + u32 num_cmn_regs, num_ln_regs; /* Initialise the PHY registers, unless auto configured */ if (phy->autoconf) return 0; if (ins->phy_type == PHY_TYPE_PCIE) { - num_regs = phy->init_data->pcie_regs; - vals = phy->init_data->pcie_vals; + num_cmn_regs = phy->init_data->pcie_cmn_regs; + num_ln_regs = phy->init_data->pcie_ln_regs; + cmn_vals = phy->init_data->pcie_cmn_vals; + ln_vals = phy->init_data->pcie_ln_vals; } else if (ins->phy_type == PHY_TYPE_USB3) { - num_regs = phy->init_data->usb_regs; - vals = phy->init_data->usb_vals; + num_cmn_regs = phy->init_data->usb_cmn_regs; + num_ln_regs = phy->init_data->usb_ln_regs; + cmn_vals = phy->init_data->usb_cmn_vals; + ln_vals = phy->init_data->usb_ln_vals; } else { return -EINVAL; } + + regmap = phy->regmap_common_cdb; + for (j = 0; j < num_cmn_regs ; j++) + regmap_write(regmap, cmn_vals[j].off, cmn_vals[j].val); + for (i = 0; i < ins->num_lanes; i++) { - for (j = 0; j < num_regs ; j++) { + for (j = 0; j < num_ln_regs ; j++) { regmap = phy->regmap_lane_cdb[i + ins->mlane]; - regmap_write(regmap, vals[j].off, vals[j].val); + regmap_write(regmap, ln_vals[j].off, ln_vals[j].val); } } @@ -491,80 +571,158 @@ static int cdns_sierra_phy_remove(struct platform_device *pdev) return 0; } -static struct cdns_reg_pairs cdns_usb_regs[] = { - /* - * Write USB configuration parameters to the PHY. - * These values are specific to this specific hardware - * configuration. - */ +/* refclk100MHz_32b_PCIe_cmn_pll_ext_ssc */ +static struct cdns_reg_pairs cdns_pcie_cmn_regs_ext_ssc[] = { + {0x2106, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG}, + {0x2106, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG}, + {0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG}, + {0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG}, + {0x1B1B, SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG} +}; + +/* refclk100MHz_32b_PCIe_ln_ext_ssc */ +static struct cdns_reg_pairs cdns_pcie_ln_regs_ext_ssc[] = { + {0x813E, SIERRA_CLKPATHCTRL_TMR_PREG}, + {0x8047, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, + {0x033C, SIERRA_RX_CTLE_MAINTENANCE_PREG}, + {0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG} +}; + +/* refclk100MHz_20b_USB_cmn_pll_ext_ssc */ +static struct cdns_reg_pairs cdns_usb_cmn_regs_ext_ssc[] = { + {0x2085, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG}, + {0x2085, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG}, + {0x0000, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG}, + {0x0000, SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG} +}; + +/* refclk100MHz_20b_USB_ln_ext_ssc */ +static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = { {0xFE0A, SIERRA_DET_STANDEC_A_PREG}, {0x000F, SIERRA_DET_STANDEC_B_PREG}, - {0x55A5, SIERRA_DET_STANDEC_C_PREG}, - {0x69AD, SIERRA_DET_STANDEC_D_PREG}, + {0x00A5, SIERRA_DET_STANDEC_C_PREG}, + {0x69ad, SIERRA_DET_STANDEC_D_PREG}, {0x0241, SIERRA_DET_STANDEC_E_PREG}, - {0x0110, SIERRA_PSM_LANECAL_PREG}, + {0x0010, SIERRA_PSM_LANECAL_DLY_A1_RESETS_PREG}, + {0x0014, SIERRA_PSM_A0IN_TMR_PREG}, {0xCF00, SIERRA_PSM_DIAG_PREG}, {0x001F, SIERRA_PSC_TX_A0_PREG}, {0x0007, SIERRA_PSC_TX_A1_PREG}, {0x0003, SIERRA_PSC_TX_A2_PREG}, {0x0003, SIERRA_PSC_TX_A3_PREG}, {0x0FFF, SIERRA_PSC_RX_A0_PREG}, - {0x0003, SIERRA_PSC_RX_A1_PREG}, + {0x0619, SIERRA_PSC_RX_A1_PREG}, {0x0003, SIERRA_PSC_RX_A2_PREG}, {0x0001, SIERRA_PSC_RX_A3_PREG}, {0x0001, SIERRA_PLLCTRL_SUBRATE_PREG}, {0x0406, SIERRA_PLLCTRL_GEN_D_PREG}, + {0x5233, SIERRA_PLLCTRL_CPGAIN_MODE_PREG}, + {0x00CA, SIERRA_CLKPATH_BIASTRIM_PREG}, + {0x2512, SIERRA_DFE_BIASTRIM_PREG}, {0x0000, SIERRA_DRVCTRL_ATTEN_PREG}, - {0x823E, SIERRA_CLKPATHCTRL_TMR_PREG}, - {0x078F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG}, - {0x078F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, + {0x873E, SIERRA_CLKPATHCTRL_TMR_PREG}, + {0x03CF, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG}, + {0x01CE, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, {0x7B3C, SIERRA_CREQ_CCLKDET_MODE01_PREG}, - {0x023C, SIERRA_RX_CTLE_MAINTENANCE_PREG}, + {0x033F, SIERRA_RX_CTLE_MAINTENANCE_PREG}, {0x3232, SIERRA_CREQ_FSMCLK_SEL_PREG}, - {0x8452, SIERRA_CTLELUT_CTRL_PREG}, - {0x4121, SIERRA_DFE_ECMP_RATESEL_PREG}, - {0x4121, SIERRA_DFE_SMP_RATESEL_PREG}, - {0x9999, SIERRA_DEQ_VGATUNE_CTRL_PREG}, - {0x0330, SIERRA_TMRVAL_MODE0_PREG}, - {0x01FF, SIERRA_PICNT_MODE1_PREG}, + {0x0000, SIERRA_CREQ_EQ_CTRL_PREG}, + {0x8000, SIERRA_CREQ_SPARE_PREG}, + {0xCC44, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG}, + {0x8453, SIERRA_CTLELUT_CTRL_PREG}, + {0x4110, SIERRA_DFE_ECMP_RATESEL_PREG}, + {0x4110, SIERRA_DFE_SMP_RATESEL_PREG}, + {0x0002, SIERRA_DEQ_PHALIGN_CTRL}, + {0x3200, SIERRA_DEQ_CONCUR_CTRL1_PREG}, + {0x5064, SIERRA_DEQ_CONCUR_CTRL2_PREG}, + {0x0030, SIERRA_DEQ_EPIPWR_CTRL2_PREG}, + {0x0048, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG}, + {0x5A5A, SIERRA_DEQ_ERRCMP_CTRL_PREG}, + {0x02F5, SIERRA_DEQ_OFFSET_CTRL_PREG}, + {0x02F5, SIERRA_DEQ_GAIN_CTRL_PREG}, + {0x9A8A, SIERRA_DEQ_VGATUNE_CTRL_PREG}, + {0x0014, SIERRA_DEQ_GLUT0}, + {0x0014, SIERRA_DEQ_GLUT1}, + {0x0014, SIERRA_DEQ_GLUT2}, + {0x0014, SIERRA_DEQ_GLUT3}, + {0x0014, SIERRA_DEQ_GLUT4}, + {0x0014, SIERRA_DEQ_GLUT5}, + {0x0014, SIERRA_DEQ_GLUT6}, + {0x0014, SIERRA_DEQ_GLUT7}, + {0x0014, SIERRA_DEQ_GLUT8}, + {0x0014, SIERRA_DEQ_GLUT9}, + {0x0014, SIERRA_DEQ_GLUT10}, + {0x0014, SIERRA_DEQ_GLUT11}, + {0x0014, SIERRA_DEQ_GLUT12}, + {0x0014, SIERRA_DEQ_GLUT13}, + {0x0014, SIERRA_DEQ_GLUT14}, + {0x0014, SIERRA_DEQ_GLUT15}, + {0x0014, SIERRA_DEQ_GLUT16}, + {0x0BAE, SIERRA_DEQ_ALUT0}, + {0x0AEB, SIERRA_DEQ_ALUT1}, + {0x0A28, SIERRA_DEQ_ALUT2}, + {0x0965, SIERRA_DEQ_ALUT3}, + {0x08A2, SIERRA_DEQ_ALUT4}, + {0x07DF, SIERRA_DEQ_ALUT5}, + {0x071C, SIERRA_DEQ_ALUT6}, + {0x0659, SIERRA_DEQ_ALUT7}, + {0x0596, SIERRA_DEQ_ALUT8}, + {0x0514, SIERRA_DEQ_ALUT9}, + {0x0492, SIERRA_DEQ_ALUT10}, + {0x0410, SIERRA_DEQ_ALUT11}, + {0x038E, SIERRA_DEQ_ALUT12}, + {0x030C, SIERRA_DEQ_ALUT13}, + {0x03F4, SIERRA_DEQ_DFETAP_CTRL_PREG}, + {0x0001, SIERRA_DFE_EN_1010_IGNORE_PREG}, + {0x3C01, SIERRA_DEQ_TAU_CTRL1_FAST_MAINT_PREG}, + {0x3C40, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, + {0x1C08, SIERRA_DEQ_TAU_CTRL2_PREG}, + {0x0033, SIERRA_DEQ_PICTRL_PREG}, + {0x0400, SIERRA_CPICAL_TMRVAL_MODE1_PREG}, + {0x0330, SIERRA_CPICAL_TMRVAL_MODE0_PREG}, + {0x01FF, SIERRA_CPICAL_PICNT_MODE1_PREG}, {0x0009, SIERRA_CPI_OUTBUF_RATESEL_PREG}, + {0x3232, SIERRA_CPICAL_RES_STARTCODE_MODE23_PREG}, + {0x0005, SIERRA_LFPSDET_SUPPORT_PREG}, {0x000F, SIERRA_LFPSFILT_NS_PREG}, {0x0009, SIERRA_LFPSFILT_RD_PREG}, {0x0001, SIERRA_LFPSFILT_MP_PREG}, {0x8013, SIERRA_SDFILT_H2L_A_PREG}, - {0x0400, SIERRA_TMRVAL_MODE1_PREG}, -}; - -static struct cdns_reg_pairs cdns_pcie_regs[] = { - /* - * Write PCIe configuration parameters to the PHY. - * These values are specific to this specific hardware - * configuration. - */ - {0x891f, SIERRA_DET_STANDEC_D_PREG}, - {0x0053, SIERRA_DET_STANDEC_E_PREG}, - {0x0400, SIERRA_TMRVAL_MODE2_PREG}, - {0x0200, SIERRA_TMRVAL_MODE3_PREG}, + {0x8009, SIERRA_SDFILT_L2H_PREG}, + {0x0024, SIERRA_RXBUFFER_CTLECTRL_PREG}, + {0x0020, SIERRA_RXBUFFER_RCDFECTRL_PREG}, + {0x4243, SIERRA_RXBUFFER_DFECTRL_PREG} }; static const struct cdns_sierra_data cdns_map_sierra = { SIERRA_MACRO_ID, 0x2, 0x2, - ARRAY_SIZE(cdns_pcie_regs), - ARRAY_SIZE(cdns_usb_regs), - cdns_pcie_regs, - cdns_usb_regs + ARRAY_SIZE(cdns_pcie_cmn_regs_ext_ssc), + ARRAY_SIZE(cdns_pcie_ln_regs_ext_ssc), + ARRAY_SIZE(cdns_usb_cmn_regs_ext_ssc), + ARRAY_SIZE(cdns_usb_ln_regs_ext_ssc), + cdns_pcie_cmn_regs_ext_ssc, + cdns_pcie_ln_regs_ext_ssc, + cdns_usb_cmn_regs_ext_ssc, + cdns_usb_ln_regs_ext_ssc, }; static const struct cdns_sierra_data cdns_ti_map_sierra = { SIERRA_MACRO_ID, 0x0, 0x1, - ARRAY_SIZE(cdns_pcie_regs), - ARRAY_SIZE(cdns_usb_regs), - cdns_pcie_regs, - cdns_usb_regs + ARRAY_SIZE(cdns_pcie_cmn_regs_ext_ssc), + ARRAY_SIZE(cdns_pcie_ln_regs_ext_ssc), + ARRAY_SIZE(cdns_usb_cmn_regs_ext_ssc), + ARRAY_SIZE(cdns_usb_ln_regs_ext_ssc), + cdns_pcie_cmn_regs_ext_ssc, + cdns_pcie_ln_regs_ext_ssc, + cdns_usb_cmn_regs_ext_ssc, + cdns_usb_ln_regs_ext_ssc, }; static const struct of_device_id cdns_sierra_id_table[] = { -- cgit v1.2.3-59-g8ed1b From b872936f5757412ec11039ffe895e1b9249d6b68 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 16 Dec 2019 15:27:06 +0530 Subject: phy: cadence: Sierra: Get reset control "array" for each link A link may have multiple lanes each with a separate reset. Get reset control "array" in order to reset all the lanes associated with the link. Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/cadence/phy-cadence-sierra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index fdca3bd178c6..497c83827670 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -499,7 +499,7 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) struct phy *gphy; sp->phys[node].lnk_rst = - of_reset_control_get_exclusive_by_index(child, 0); + of_reset_control_array_get_exclusive(child); if (IS_ERR(sp->phys[node].lnk_rst)) { dev_err(dev, "failed to get reset %s\n", -- cgit v1.2.3-59-g8ed1b From adc4bd6f6545bedc5547c76c2bf52257a8fffa97 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 16 Dec 2019 15:27:07 +0530 Subject: phy: cadence: Sierra: Check for PLL lock during PHY power on Check for PLL lock during PHY power on. Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/cadence/phy-cadence-sierra.c | 33 +++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index 497c83827670..62bff4b043f0 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -55,6 +55,7 @@ #define SIERRA_PLLCTRL_SUBRATE_PREG 0x03A #define SIERRA_PLLCTRL_GEN_D_PREG 0x03E #define SIERRA_PLLCTRL_CPGAIN_MODE_PREG 0x03F +#define SIERRA_PLLCTRL_STATUS_PREG 0x044 #define SIERRA_CLKPATH_BIASTRIM_PREG 0x04B #define SIERRA_DFE_BIASTRIM_PREG 0x04C #define SIERRA_DRVCTRL_ATTEN_PREG 0x06A @@ -141,11 +142,14 @@ #define SIERRA_MACRO_ID 0x00007364 #define SIERRA_MAX_LANES 4 +#define PLL_LOCK_TIME 100000 static const struct reg_field macro_id_type = REG_FIELD(SIERRA_MACRO_ID_REG, 0, 15); static const struct reg_field phy_pll_cfg_1 = REG_FIELD(SIERRA_PHY_PLL_CFG, 1, 1); +static const struct reg_field pllctrl_lock = + REG_FIELD(SIERRA_PLLCTRL_STATUS_PREG, 0, 0); struct cdns_sierra_inst { struct phy *phy; @@ -192,6 +196,7 @@ struct cdns_sierra_phy { struct regmap *regmap_common_cdb; struct regmap_field *macro_id_type; struct regmap_field *phy_pll_cfg_1; + struct regmap_field *pllctrl_lock[SIERRA_MAX_LANES]; struct clk *clk; int nsubnodes; bool autoconf; @@ -291,10 +296,25 @@ static int cdns_sierra_phy_init(struct phy *gphy) static int cdns_sierra_phy_on(struct phy *gphy) { + struct cdns_sierra_phy *sp = dev_get_drvdata(gphy->dev.parent); struct cdns_sierra_inst *ins = phy_get_drvdata(gphy); + struct device *dev = sp->dev; + u32 val; + int ret; /* Take the PHY lane group out of reset */ - return reset_control_deassert(ins->lnk_rst); + ret = reset_control_deassert(ins->lnk_rst); + if (ret) { + dev_err(dev, "Failed to take the PHY lane out of reset\n"); + return ret; + } + + ret = regmap_field_read_poll_timeout(sp->pllctrl_lock[ins->mlane], + val, val, 1000, PLL_LOCK_TIME); + if (ret < 0) + dev_err(dev, "PLL lock of lane failed\n"); + + return ret; } static int cdns_sierra_phy_off(struct phy *gphy) @@ -350,6 +370,7 @@ static int cdns_regfield_init(struct cdns_sierra_phy *sp) struct device *dev = sp->dev; struct regmap_field *field; struct regmap *regmap; + int i; regmap = sp->regmap_common_cdb; field = devm_regmap_field_alloc(dev, regmap, macro_id_type); @@ -367,6 +388,16 @@ static int cdns_regfield_init(struct cdns_sierra_phy *sp) } sp->phy_pll_cfg_1 = field; + for (i = 0; i < SIERRA_MAX_LANES; i++) { + regmap = sp->regmap_lane_cdb[i]; + field = devm_regmap_field_alloc(dev, regmap, pllctrl_lock); + if (IS_ERR(field)) { + dev_err(dev, "P%d_ENABLE reg field init failed\n", i); + return PTR_ERR(field); + } + sp->pllctrl_lock[i] = field; + } + return 0; } -- cgit v1.2.3-59-g8ed1b From a43f72ae136a816a3cceab8957dd3aa301263281 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 16 Dec 2019 15:27:08 +0530 Subject: phy: cadence: Sierra: Change MAX_LANES of Sierra to 16 Sierra SERDES IP supports upto 16 lanes (though not all of it will be enabled in a platform). Allow Sierra driver to support a maximum of upto 16 lanes. Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/cadence/phy-cadence-sierra.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index 62bff4b043f0..665a6dbc7816 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -141,7 +141,7 @@ #define SIERRA_PHY_PLL_CFG 0xe #define SIERRA_MACRO_ID 0x00007364 -#define SIERRA_MAX_LANES 4 +#define SIERRA_MAX_LANES 16 #define PLL_LOCK_TIME 100000 static const struct reg_field macro_id_type = @@ -199,6 +199,7 @@ struct cdns_sierra_phy { struct regmap_field *pllctrl_lock[SIERRA_MAX_LANES]; struct clk *clk; int nsubnodes; + u32 num_lanes; bool autoconf; }; @@ -235,6 +236,18 @@ static struct regmap_config cdns_sierra_lane_cdb_config[] = { SIERRA_LANE_CDB_REGMAP_CONF("1"), SIERRA_LANE_CDB_REGMAP_CONF("2"), SIERRA_LANE_CDB_REGMAP_CONF("3"), + SIERRA_LANE_CDB_REGMAP_CONF("4"), + SIERRA_LANE_CDB_REGMAP_CONF("5"), + SIERRA_LANE_CDB_REGMAP_CONF("6"), + SIERRA_LANE_CDB_REGMAP_CONF("7"), + SIERRA_LANE_CDB_REGMAP_CONF("8"), + SIERRA_LANE_CDB_REGMAP_CONF("9"), + SIERRA_LANE_CDB_REGMAP_CONF("10"), + SIERRA_LANE_CDB_REGMAP_CONF("11"), + SIERRA_LANE_CDB_REGMAP_CONF("12"), + SIERRA_LANE_CDB_REGMAP_CONF("13"), + SIERRA_LANE_CDB_REGMAP_CONF("14"), + SIERRA_LANE_CDB_REGMAP_CONF("15"), }; static struct regmap_config cdns_sierra_common_cdb_config = { @@ -548,6 +561,8 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) } } + sp->num_lanes += sp->phys[node].num_lanes; + gphy = devm_phy_create(dev, child, &ops); if (IS_ERR(gphy)) { @@ -561,6 +576,11 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) } sp->nsubnodes = node; + if (sp->num_lanes > SIERRA_MAX_LANES) { + dev_err(dev, "Invalid lane configuration\n"); + goto put_child2; + } + /* If more than one subnode, configure the PHY as multilink */ if (!sp->autoconf && sp->nsubnodes > 1) regmap_field_write(sp->phy_pll_cfg_1, 0x1); -- cgit v1.2.3-59-g8ed1b From 6825cfc94825c3170feef946e926f1551a8a25c9 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 16 Dec 2019 15:27:09 +0530 Subject: phy: cadence: Sierra: Set cmn_refclk_dig_div/cmn_refclk1_dig_div frequency to 25MHz Set cmn_refclk_dig_div/cmn_refclk1_dig_div frequency to 25MHz as specified in "Common Module Clock Configurations" of the Cadence Sierra 16FFC Multi-Protocol PHY PMA Specification. It is set to 25MHz since the only user of Cadence Sierra SERDES, TI J721E SoC provides input clock frequency of 100MHz. For other frequencies, cmn_refclk_dig_div/cmn_refclk1_dig_div should be configured based on the "Common Module Clock Configurations". Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/cadence/phy-cadence-sierra.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index 665a6dbc7816..82466d0e9b38 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -198,6 +198,8 @@ struct cdns_sierra_phy { struct regmap_field *phy_pll_cfg_1; struct regmap_field *pllctrl_lock[SIERRA_MAX_LANES]; struct clk *clk; + struct clk *cmn_refclk_dig_div; + struct clk *cmn_refclk1_dig_div; int nsubnodes; u32 num_lanes; bool autoconf; @@ -279,6 +281,8 @@ static int cdns_sierra_phy_init(struct phy *gphy) if (phy->autoconf) return 0; + clk_set_rate(phy->cmn_refclk_dig_div, 25000000); + clk_set_rate(phy->cmn_refclk1_dig_div, 25000000); if (ins->phy_type == PHY_TYPE_PCIE) { num_cmn_regs = phy->init_data->pcie_cmn_regs; num_ln_regs = phy->init_data->pcie_ln_regs; @@ -468,6 +472,7 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) struct resource *res; int i, ret, node = 0; void __iomem *base; + struct clk *clk; struct device_node *dn = dev->of_node, *child; if (of_get_child_count(dn) == 0) @@ -523,6 +528,22 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) return PTR_ERR(sp->apb_rst); } + clk = devm_clk_get_optional(dev, "cmn_refclk_dig_div"); + if (IS_ERR(clk)) { + dev_err(dev, "cmn_refclk_dig_div clock not found\n"); + ret = PTR_ERR(clk); + return ret; + } + sp->cmn_refclk_dig_div = clk; + + clk = devm_clk_get_optional(dev, "cmn_refclk1_dig_div"); + if (IS_ERR(clk)) { + dev_err(dev, "cmn_refclk1_dig_div clock not found\n"); + ret = PTR_ERR(clk); + return ret; + } + sp->cmn_refclk1_dig_div = clk; + ret = clk_prepare_enable(sp->clk); if (ret) return ret; -- cgit v1.2.3-59-g8ed1b From 748e3456b240061fcbcea663d28040bf426c9594 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 16 Dec 2019 15:27:10 +0530 Subject: phy: cadence: Sierra: Use correct dev pointer in cdns_sierra_phy_remove() commit 44d30d622821d3b ("phy: cadence: Add driver for Sierra PHY"), incorrectly used parent device pointer to get driver data. Fix it here. Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/cadence/phy-cadence-sierra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index 82466d0e9b38..eb87f1a0a596 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -625,7 +625,7 @@ clk_disable: static int cdns_sierra_phy_remove(struct platform_device *pdev) { - struct cdns_sierra_phy *phy = dev_get_drvdata(pdev->dev.parent); + struct cdns_sierra_phy *phy = platform_get_drvdata(pdev); int i; reset_control_assert(phy->phy_rst); -- cgit v1.2.3-59-g8ed1b From c7a7ac84afeaf310a0ee477fc114e1e291a37c43 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 8 Jan 2020 15:53:16 +0300 Subject: thunderbolt: Fix xhci check in usb4_switch_setup() The code tried to check whether xhci variable has ROUTER_CS_6_HCI bit set but since xhci type is bool and it already holds true or false based on that very bit, fix the check to use the variable directly. Reported-by: Dan Carpenter Fixes: b04079837b20 ("thunderbolt: Add initial support for USB4") Signed-off-by: Mika Westerberg Link: https://lore.kernel.org/r/20200108125317.36444-2-mika.westerberg@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/usb4.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index dbe7ecce4505..b341fc60c4ba 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -239,7 +239,7 @@ int usb4_switch_setup(struct tb_switch *sw) * and the parent does not have any USB3 dowstream * adapters (so we cannot do USB 3.x tunneling). */ - if (xhci & ROUTER_CS_6_HCI) + if (xhci) val |= ROUTER_CS_5_HCO; } -- cgit v1.2.3-59-g8ed1b From 7dad8e6f04990478a6a860b19367e479b2377205 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 8 Jan 2020 15:53:17 +0300 Subject: MAINTAINERS: Use linux-usb mailing list for Thunderbolt and USB4 patches Now that Thunderbolt public specification is called USB4 and is coming from USB IF it makes sense to use linux-usb as mailing list for patches touching this driver. Suggested-by: Dan Carpenter Signed-off-by: Mika Westerberg Link: https://lore.kernel.org/r/20200108125317.36444-3-mika.westerberg@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index ffa3371bc750..c2ffeb8087d9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16371,6 +16371,7 @@ M: Andreas Noever M: Michael Jamet M: Mika Westerberg M: Yehezkel Bernat +L: linux-usb@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt.git S: Maintained F: Documentation/admin-guide/thunderbolt.rst -- cgit v1.2.3-59-g8ed1b From 2d686c738a2e6a90a87aefc392224a26befd1e55 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 7 Jan 2020 22:24:50 -0800 Subject: usb: typec: fix non-kernel-doc comments Use "/*" for non-kernel-doc comments instead of "/**", which is intended to be used only for kernel-doc notation. Signed-off-by: Randy Dunlap Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/88821011-2128-a8dd-68b8-c5ae8f43271f@infradead.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/bus.c | 2 +- drivers/usb/typec/mux.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c index 74cb3c2ecb34..7e94e8c46ab3 100644 --- a/drivers/usb/typec/bus.c +++ b/drivers/usb/typec/bus.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * Bus for USB Type-C Alternate Modes * * Copyright (C) 2018 Intel Corporation diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index 57907f26f681..5baf0f416c73 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * USB Type-C Multiplexer/DeMultiplexer Switch support * * Copyright (C) 2018 Intel Corporation -- cgit v1.2.3-59-g8ed1b From 9521e47e9ab8401eb42e8ef5c2c9a46ac7dd8876 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 8 Jan 2020 16:13:47 +0300 Subject: usb: typec: ucsi: Actually enable all the interface notifications The notification mask was not updated properly before all the notifications were enabled in ucsi_init(). Fixes: 71a1fa0df2a3 ("usb: typec: ucsi: Store the notification mask") Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200108131347.43217-3-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 466bd8afceea..59c8ccdc68ac 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1007,6 +1007,7 @@ int ucsi_init(struct ucsi *ucsi) } /* Enable all notifications */ + ucsi->ntfy = UCSI_ENABLE_NTFY_ALL; command = UCSI_SET_NOTIFICATION_ENABLE | ucsi->ntfy; ret = ucsi_run_command(ucsi, command, NULL, 0); if (ret < 0) -- cgit v1.2.3-59-g8ed1b From 17da9b8e5ab89ad815671a95db1b53d835093060 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 7 Jan 2020 22:43:46 +0100 Subject: usb: host: oxu210hp-hcd: fix gcc warning gcc -O3 warns about correct code: inlined from 'oxu_hub_control.constprop' at drivers/usb/host/oxu210hp-hcd.c:3652:3: include/linux/string.h:411:9: error: argument 1 null where non-null expected [-Werror=nonnull] return __builtin_memset(p, c, size); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/usb/host/oxu210hp-hcd.c: In function 'oxu_hub_control.constprop': include/linux/string.h:411:9: note: in a call to built-in function '__builtin_memset' Expand the code slightly to let gcc better understand it and not warn any more. Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20200107214354.1008937-1-arnd@arndb.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/oxu210hp-hcd.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index fe09b8626329..120666a0d590 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -2783,11 +2783,15 @@ static void ehci_port_power(struct oxu_hcd *oxu, int is_on) return; oxu_dbg(oxu, "...power%s ports...\n", is_on ? "up" : "down"); - for (port = HCS_N_PORTS(oxu->hcs_params); port > 0; ) - (void) oxu_hub_control(oxu_to_hcd(oxu), - is_on ? SetPortFeature : ClearPortFeature, - USB_PORT_FEAT_POWER, - port--, NULL, 0); + for (port = HCS_N_PORTS(oxu->hcs_params); port > 0; ) { + if (is_on) + oxu_hub_control(oxu_to_hcd(oxu), SetPortFeature, + USB_PORT_FEAT_POWER, port--, NULL, 0); + else + oxu_hub_control(oxu_to_hcd(oxu), ClearPortFeature, + USB_PORT_FEAT_POWER, port--, NULL, 0); + } + msleep(20); } -- cgit v1.2.3-59-g8ed1b From 0e84f2fd0d261ace1bde76b3c0ed6eee02126e85 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 1 Jan 2020 08:43:22 +0100 Subject: usb: gadget: udc: atmel: constify copied structure The usba_gadget_template structure is only copied into another structure, so make it const. The opportunity for this change was found using Coccinelle. Signed-off-by: Julia Lawall Acked-by: Cristian Birsan Link: https://lore.kernel.org/r/1577864614-5543-5-git-send-email-Julia.Lawall@inria.fr Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/atmel_usba_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 8a42768e3213..6e0432141c40 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -1122,7 +1122,7 @@ static struct usb_endpoint_descriptor usba_ep0_desc = { .bInterval = 1, }; -static struct usb_gadget usba_gadget_template = { +static const struct usb_gadget usba_gadget_template = { .ops = &usba_udc_ops, .max_speed = USB_SPEED_HIGH, .name = "atmel_usba_udc", -- cgit v1.2.3-59-g8ed1b From 7b7ad03f49a5c6be19ef1c3d4090651b5892e6e0 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 1 Jan 2020 18:49:41 +0100 Subject: USB: omap_udc: use resource_size Use resource_size rather than a verbose computation on the end and start fields. The semantic patch that makes these changes is as follows: (http://coccinelle.lip6.fr/) @@ struct resource ptr; @@ - (ptr.end - ptr.start + 1) + resource_size(&ptr) Signed-off-by: Julia Lawall Link: https://lore.kernel.org/r/1577900990-8588-2-git-send-email-Julia.Lawall@inria.fr Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/omap_udc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c index f36f0730afab..bd12417996db 100644 --- a/drivers/usb/gadget/udc/omap_udc.c +++ b/drivers/usb/gadget/udc/omap_udc.c @@ -2757,7 +2757,7 @@ static int omap_udc_probe(struct platform_device *pdev) /* NOTE: "knows" the order of the resources! */ if (!request_mem_region(pdev->resource[0].start, - pdev->resource[0].end - pdev->resource[0].start + 1, + resource_size(&pdev->resource[0]), driver_name)) { DBG("request_mem_region failed\n"); return -EBUSY; @@ -2934,7 +2934,7 @@ cleanup0: } release_mem_region(pdev->resource[0].start, - pdev->resource[0].end - pdev->resource[0].start + 1); + resource_size(&pdev->resource[0])); return status; } @@ -2950,7 +2950,7 @@ static int omap_udc_remove(struct platform_device *pdev) wait_for_completion(&done); release_mem_region(pdev->resource[0].start, - pdev->resource[0].end - pdev->resource[0].start + 1); + resource_size(&pdev->resource[0])); return 0; } -- cgit v1.2.3-59-g8ed1b From 60826786fcdb328a222e41ea13e0e63b23ad9daa Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 3 Jan 2020 17:40:31 +0100 Subject: usb: ehci-mv: Fix missing iomem in cast Fix missing __iomem in cast to struct ehci_caps. This fixes the Sparse warning visible on x86_64 compile test: drivers/usb/host/ehci-mv.c:167:23: warning: cast removes address space '' of expression drivers/usb/host/ehci-mv.c:167:20: warning: incorrect type in assignment (different address spaces) drivers/usb/host/ehci-mv.c:167:20: expected struct ehci_caps [noderef] *caps drivers/usb/host/ehci-mv.c:167:20: got struct ehci_caps * Reported-by: kbuild test robot Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20200103164031.4089-1-krzk@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-mv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 91602e349208..bd4f6ef534d9 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -175,7 +175,7 @@ static int mv_ehci_probe(struct platform_device *pdev) } ehci = hcd_to_ehci(hcd); - ehci->caps = (struct ehci_caps *) ehci_mv->cap_regs; + ehci->caps = (struct ehci_caps __iomem *) ehci_mv->cap_regs; if (ehci_mv->mode == MV_USB_MODE_OTG) { ehci_mv->otg = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); -- cgit v1.2.3-59-g8ed1b From 497210f27b8ccc38c03c679237a4fc766fbc3aba Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 6 Jan 2020 11:11:24 +0000 Subject: usb: typec: ucsi: fix spelling mistake "connetor" -> "connector" There is a spelling mistake in a dev_dbg message. Fix it. Signed-off-by: Colin Ian King Acked-by: Heikki Krogerus Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200106111124.28100-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 59c8ccdc68ac..d5a6aac86327 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -669,7 +669,7 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num) struct ucsi_connector *con = &ucsi->connector[num - 1]; if (!(ucsi->ntfy & UCSI_ENABLE_NTFY_CONNECTOR_CHANGE)) { - dev_dbg(ucsi->dev, "Bogus connetor change event\n"); + dev_dbg(ucsi->dev, "Bogus connector change event\n"); return; } -- cgit v1.2.3-59-g8ed1b From cf2f58fb88d9494ceff4516ad4bddc54bf56f426 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 30 Dec 2019 17:25:57 +0300 Subject: usb: typec: Block mode entry if the port has the mode disabled Originally the port drivers were expected to check does the connector have the mode enabled or disabled when the alt mode drivers attempted to enter the mode, but since typec_altmode_enter() puts the connector into USB Safe State before calling the port driver, it really has to do the check on its own, and before changing the state. Otherwise the connector may be left in USB Safe State if the port driver does not move it back to normal USB operation when the mode is disabled. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20191230142611.24921-2-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/bus.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c index 7e94e8c46ab3..8f6d8933d72e 100644 --- a/drivers/usb/typec/bus.c +++ b/drivers/usb/typec/bus.c @@ -101,6 +101,9 @@ int typec_altmode_enter(struct typec_altmode *adev) if (!pdev->ops || !pdev->ops->enter) return -EOPNOTSUPP; + if (is_typec_port(pdev->dev.parent) && !pdev->active) + return -EPERM; + /* Moving to USB Safe State */ ret = typec_altmode_set_state(adev, TYPEC_STATE_SAFE); if (ret) -- cgit v1.2.3-59-g8ed1b From 8face9aa57c8335b2d698a70bcfaaaa46dd36b93 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 30 Dec 2019 17:25:58 +0300 Subject: usb: typec: Add parameter for the VDO to typec_altmode_enter() Enter Mode Command may contain one VDO. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20191230142611.24921-3-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/altmodes/displayport.c | 5 +++-- drivers/usb/typec/bus.c | 8 +++++--- drivers/usb/typec/tcpm/tcpm.c | 6 +++--- drivers/usb/typec/ucsi/displayport.c | 2 +- include/linux/usb/typec_altmode.h | 4 ++-- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index 4092248a5936..0edfb89e04a8 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -188,7 +188,7 @@ static void dp_altmode_work(struct work_struct *work) switch (dp->state) { case DP_STATE_ENTER: - ret = typec_altmode_enter(dp->alt); + ret = typec_altmode_enter(dp->alt, NULL); if (ret) dev_err(&dp->alt->dev, "failed to enter mode\n"); break; @@ -306,7 +306,8 @@ err_unlock: static int dp_altmode_activate(struct typec_altmode *alt, int activate) { - return activate ? typec_altmode_enter(alt) : typec_altmode_exit(alt); + return activate ? typec_altmode_enter(alt, NULL) : + typec_altmode_exit(alt); } static const struct typec_altmode_ops dp_altmode_ops = { diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c index 8f6d8933d72e..e78c8a68c745 100644 --- a/drivers/usb/typec/bus.c +++ b/drivers/usb/typec/bus.c @@ -84,12 +84,14 @@ EXPORT_SYMBOL_GPL(typec_altmode_notify); /** * typec_altmode_enter - Enter Mode * @adev: The alternate mode + * @vdo: VDO for the Enter Mode command * * The alternate mode drivers use this function to enter mode. The port drivers * use this to inform the alternate mode drivers that the partner has initiated - * Enter Mode command. + * Enter Mode command. If the alternate mode does not require VDO, @vdo must be + * NULL. */ -int typec_altmode_enter(struct typec_altmode *adev) +int typec_altmode_enter(struct typec_altmode *adev, u32 *vdo) { struct altmode *partner = to_altmode(adev)->partner; struct typec_altmode *pdev = &partner->adev; @@ -110,7 +112,7 @@ int typec_altmode_enter(struct typec_altmode *adev) return ret; /* Enter Mode */ - return pdev->ops->enter(pdev); + return pdev->ops->enter(pdev, vdo); } EXPORT_SYMBOL_GPL(typec_altmode_enter); diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 56fc356bc55c..f3087ef8265c 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -1475,16 +1475,16 @@ static int tcpm_validate_caps(struct tcpm_port *port, const u32 *pdo, return 0; } -static int tcpm_altmode_enter(struct typec_altmode *altmode) +static int tcpm_altmode_enter(struct typec_altmode *altmode, u32 *vdo) { struct tcpm_port *port = typec_altmode_get_drvdata(altmode); u32 header; mutex_lock(&port->lock); - header = VDO(altmode->svid, 1, CMD_ENTER_MODE); + header = VDO(altmode->svid, vdo ? 2 : 1, CMD_ENTER_MODE); header |= VDO_OPOS(altmode->mode); - tcpm_queue_vdm(port, header, NULL, 0); + tcpm_queue_vdm(port, header, vdo, vdo ? 1 : 0); mod_delayed_work(port->wq, &port->vdm_state_machine, 0); mutex_unlock(&port->lock); diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c index d4d5189edfb8..0f1273ae086c 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -45,7 +45,7 @@ struct ucsi_dp { * -EOPNOTSUPP. */ -static int ucsi_displayport_enter(struct typec_altmode *alt) +static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo) { struct ucsi_dp *dp = typec_altmode_get_drvdata(alt); struct ucsi *ucsi = dp->con->ucsi; diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h index 9a88c74a1d0d..fc57fd88004f 100644 --- a/include/linux/usb/typec_altmode.h +++ b/include/linux/usb/typec_altmode.h @@ -55,7 +55,7 @@ static inline void *typec_altmode_get_drvdata(struct typec_altmode *altmode) * @activate: User callback for Enter/Exit Mode */ struct typec_altmode_ops { - int (*enter)(struct typec_altmode *altmode); + int (*enter)(struct typec_altmode *altmode, u32 *vdo); int (*exit)(struct typec_altmode *altmode); void (*attention)(struct typec_altmode *altmode, u32 vdo); int (*vdm)(struct typec_altmode *altmode, const u32 hdr, @@ -65,7 +65,7 @@ struct typec_altmode_ops { int (*activate)(struct typec_altmode *altmode, int activate); }; -int typec_altmode_enter(struct typec_altmode *altmode); +int typec_altmode_enter(struct typec_altmode *altmode, u32 *vdo); int typec_altmode_exit(struct typec_altmode *altmode); void typec_altmode_attention(struct typec_altmode *altmode, u32 vdo); int typec_altmode_vdm(struct typec_altmode *altmode, -- cgit v1.2.3-59-g8ed1b From b66b40ee7d0d376a725fde2b6d951a37cb3062c6 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 30 Dec 2019 17:25:59 +0300 Subject: usb: typec: More API for cable handling Thunderbolt 3, and probable USB4 too, will need to be able to get details about the cables. Adding typec_cable_get() function that the alternate mode drivers can use to gain access to gain access to the cable. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20191230142611.24921-4-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/usb/typec.h | 4 ++++ 2 files changed, 50 insertions(+) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 91d62276b56f..08923637cd88 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -834,6 +834,52 @@ static const struct device_type typec_cable_dev_type = { .release = typec_cable_release, }; +static int cable_match(struct device *dev, void *data) +{ + return is_typec_cable(dev); +} + +/** + * typec_cable_get - Get a reference to the USB Type-C cable + * @port: The USB Type-C Port the cable is connected to + * + * The caller must decrement the reference count with typec_cable_put() after + * use. + */ +struct typec_cable *typec_cable_get(struct typec_port *port) +{ + struct device *dev; + + dev = device_find_child(&port->dev, NULL, cable_match); + if (!dev) + return NULL; + + return to_typec_cable(dev); +} +EXPORT_SYMBOL_GPL(typec_cable_get); + +/** + * typec_cable_get - Decrement the reference count on USB Type-C cable + * @cable: The USB Type-C cable + */ +void typec_cable_put(struct typec_cable *cable) +{ + put_device(&cable->dev); +} +EXPORT_SYMBOL_GPL(typec_cable_put); + +/** + * typec_cable_is_active - Check is the USB Type-C cable active or passive + * @cable: The USB Type-C Cable + * + * Return 1 if the cable is active or 0 if it's passive. + */ +int typec_cable_is_active(struct typec_cable *cable) +{ + return cable->active; +} +EXPORT_SYMBOL_GPL(typec_cable_is_active); + /** * typec_cable_set_identity - Report result from Discover Identity command * @cable: The cable updated identity values diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index 0f52723a11bd..d95ea0d398b8 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -230,6 +230,10 @@ struct typec_cable *typec_register_cable(struct typec_port *port, struct typec_cable_desc *desc); void typec_unregister_cable(struct typec_cable *cable); +struct typec_cable *typec_cable_get(struct typec_port *port); +void typec_cable_put(struct typec_cable *cable); +int typec_cable_is_active(struct typec_cable *cable); + struct typec_plug *typec_register_plug(struct typec_cable *cable, struct typec_plug_desc *desc); void typec_unregister_plug(struct typec_plug *plug); -- cgit v1.2.3-59-g8ed1b From 7823905de0c6c319995d710bb67f761681dc7e4e Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 30 Dec 2019 17:26:04 +0300 Subject: usb: pd: Add definitions for the Enter_USB message Version 2.0 of the USB Power Delivery Specification R3.0 defines a new message called Enter_USB, which is made with USB4 in mind. Enter_USB message is in practice the same as the Enter Mode command (used when entering alternate modes) that just needs to be used when entering USB4 mode. The message does also support entering USB 2.0 or USB 3.2 mode instead of USB4 mode, but it is only required with USB4. I.e. with USB2 and USB3 Enter_USB message is optional. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20191230142611.24921-9-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/pd.h | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h index 145c38e351c2..a665d7f21142 100644 --- a/include/linux/usb/pd.h +++ b/include/linux/usb/pd.h @@ -45,7 +45,8 @@ enum pd_data_msg_type { PD_DATA_BATT_STATUS = 5, PD_DATA_ALERT = 6, PD_DATA_GET_COUNTRY_INFO = 7, - /* 8-14 Reserved */ + PD_DATA_ENTER_USB = 8, + /* 9-14 Reserved */ PD_DATA_VENDOR_DEF = 15, /* 16-31 Reserved */ }; @@ -418,6 +419,36 @@ static inline unsigned int rdo_max_power(u32 rdo) return ((rdo >> RDO_BATT_MAX_PWR_SHIFT) & RDO_PWR_MASK) * 250; } +/* Enter_USB Data Object */ +#define EUDO_USB_MODE_MASK GENMASK(30, 28) +#define EUDO_USB_MODE_SHIFT 28 +#define EUDO_USB_MODE_USB2 0 +#define EUDO_USB_MODE_USB3 1 +#define EUDO_USB_MODE_USB4 2 +#define EUDO_USB4_DRD BIT(26) +#define EUDO_USB3_DRD BIT(25) +#define EUDO_CABLE_SPEED_MASK GENMASK(23, 21) +#define EUDO_CABLE_SPEED_SHIFT 21 +#define EUDO_CABLE_SPEED_USB2 0 +#define EUDO_CABLE_SPEED_USB3_GEN1 1 +#define EUDO_CABLE_SPEED_USB4_GEN2 2 +#define EUDO_CABLE_SPEED_USB4_GEN3 3 +#define EUDO_CABLE_TYPE_MASK GENMASK(20, 19) +#define EUDO_CABLE_TYPE_SHIFT 19 +#define EUDO_CABLE_TYPE_PASSIVE 0 +#define EUDO_CABLE_TYPE_RE_TIMER 1 +#define EUDO_CABLE_TYPE_RE_DRIVER 2 +#define EUDO_CABLE_TYPE_OPTICAL 3 +#define EUDO_CABLE_CURRENT_MASK GENMASK(18, 17) +#define EUDO_CABLE_CURRENT_SHIFT 17 +#define EUDO_CABLE_CURRENT_NOTSUPP 0 +#define EUDO_CABLE_CURRENT_3A 2 +#define EUDO_CABLE_CURRENT_5A 3 +#define EUDO_PCIE_SUPPORT BIT(16) +#define EUDO_DP_SUPPORT BIT(15) +#define EUDO_TBT_SUPPORT BIT(14) +#define EUDO_HOST_PRESENT BIT(13) + /* USB PD timers and counters */ #define PD_T_NO_RESPONSE 5000 /* 4.5 - 5.5 seconds */ #define PD_T_DB_DETECT 10000 /* 10 - 15 seconds */ -- cgit v1.2.3-59-g8ed1b From d48ece0bce2d733c1c831d64751c0acaee1e8dc9 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 30 Dec 2019 17:26:05 +0300 Subject: usb: pd: Add definition for DFP and UFP1 VDOs The latest version of the USB Power Delivery Specification R3.0 added UFP and DFP product types for the Discover Identity message. Both types can be used for example for checking the USB capability of the partner, which means the USB modes (USB 2.0, USB 3.0 and USB4) that the partner device supports. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20191230142611.24921-10-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/pd_vdo.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/include/linux/usb/pd_vdo.h b/include/linux/usb/pd_vdo.h index 781f4e98dd23..35b8e15efaa0 100644 --- a/include/linux/usb/pd_vdo.h +++ b/include/linux/usb/pd_vdo.h @@ -139,6 +139,38 @@ #define VDO_PRODUCT(pid, bcd) (((pid) & 0xffff) << 16 | ((bcd) & 0xffff)) #define PD_PRODUCT_PID(vdo) (((vdo) >> 16) & 0xffff) +/* + * UFP VDO1 + * -------- + * <31:29> :: UFP VDO version + * <28> :: Reserved + * <27:24> :: Device capability + * <23:6> :: Reserved + * <5:3> :: Alternate modes + * <2:0> :: USB highest speed + */ +#define PD_VDO1_UFP_DEVCAP(vdo) (((vdo) & GENMASK(27, 24)) >> 24) + +#define DEV_USB2_CAPABLE BIT(0) +#define DEV_USB2_BILLBOARD BIT(1) +#define DEV_USB3_CAPABLE BIT(2) +#define DEV_USB4_CAPABLE BIT(3) + +/* + * DFP VDO + * -------- + * <31:29> :: DFP VDO version + * <28:27> :: Reserved + * <26:24> :: Host capability + * <23:5> :: Reserved + * <4:0> :: Port number + */ +#define PD_VDO_DFP_HOSTCAP(vdo) (((vdo) & GENMASK(26, 24)) >> 24) + +#define HOST_USB2_CAPABLE BIT(0) +#define HOST_USB3_CAPABLE BIT(1) +#define HOST_USB4_CAPABLE BIT(2) + /* * Cable VDO * --------- -- cgit v1.2.3-59-g8ed1b From f6c56ca91b92088cf2f4075c1aa7c57ea89f7327 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 30 Dec 2019 17:26:06 +0300 Subject: usb: typec: Add the Product Type VDOs to struct usb_pd_identity Discover Identity command response has also 3 product type specific VDOs on top of ID Header VDO, Cert Stat VDO and Product VDO. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20191230142611.24921-11-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/typec.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index d95ea0d398b8..47595e96070e 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -74,6 +74,7 @@ enum typec_orientation { * @id_header: ID Header VDO * @cert_stat: Cert Stat VDO * @product: Product VDO + * @vdo: Product Type Specific VDOs * * USB power delivery Discover Identity command response data. * @@ -84,6 +85,7 @@ struct usb_pd_identity { u32 id_header; u32 cert_stat; u32 product; + u32 vdo[3]; }; int typec_partner_set_identity(struct typec_partner *partner); -- cgit v1.2.3-59-g8ed1b From 0ac53493296801b01c326507af3db093b95f6fb0 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 30 Dec 2019 17:26:07 +0300 Subject: usb: typec: Add definitions for the latest specification releases Adding definitions for USB Type-C Specification Release 1.3, 1.4 and 2.0. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20191230142611.24921-12-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/typec.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index 47595e96070e..c358b3fd05c9 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -9,6 +9,9 @@ #define USB_TYPEC_REV_1_0 0x100 /* 1.0 */ #define USB_TYPEC_REV_1_1 0x110 /* 1.1 */ #define USB_TYPEC_REV_1_2 0x120 /* 1.2 */ +#define USB_TYPEC_REV_1_3 0x130 /* 1.3 */ +#define USB_TYPEC_REV_1_4 0x140 /* 1.4 */ +#define USB_TYPEC_REV_2_0 0x200 /* 2.0 */ struct typec_partner; struct typec_cable; -- cgit v1.2.3-59-g8ed1b From 87e3daa005cfba19433b5429bfbca9b848925507 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 30 Dec 2019 17:26:08 +0300 Subject: usb: typec: Give the mux drivers all the details regarding the port state Passing all the details that the alternate mode drivers provide to the mux drivers during mode changes. The mux drivers will in practice need to be able to make decisions on their own. It is not enough that they get only the requested port state. With the Thunderbolt 3 alternate mode for example the mux driver will need to consider also the capabilities of the cable before configuring the mux. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20191230142611.24921-13-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/bus.c | 29 ++++++++++++++++++++--------- drivers/usb/typec/class.c | 6 +++++- drivers/usb/typec/mux/pi3usb30532.c | 5 +++-- include/linux/usb/typec_mux.h | 10 +++++++++- 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c index e78c8a68c745..2e45eb479386 100644 --- a/drivers/usb/typec/bus.c +++ b/drivers/usb/typec/bus.c @@ -10,12 +10,23 @@ #include "bus.h" -static inline int typec_altmode_set_mux(struct altmode *alt, u8 state) +static inline int +typec_altmode_set_mux(struct altmode *alt, unsigned long conf, void *data) { - return alt->mux ? alt->mux->set(alt->mux, state) : 0; + struct typec_mux_state state; + + if (!alt->mux) + return 0; + + state.alt = &alt->adev; + state.mode = conf; + state.data = data; + + return alt->mux->set(alt->mux, &state); } -static int typec_altmode_set_state(struct typec_altmode *adev, int state) +static int typec_altmode_set_state(struct typec_altmode *adev, + unsigned long conf, void *data) { bool is_port = is_typec_port(adev->dev.parent); struct altmode *port_altmode; @@ -23,11 +34,11 @@ static int typec_altmode_set_state(struct typec_altmode *adev, int state) port_altmode = is_port ? to_altmode(adev) : to_altmode(adev)->partner; - ret = typec_altmode_set_mux(port_altmode, state); + ret = typec_altmode_set_mux(port_altmode, conf, data); if (ret) return ret; - blocking_notifier_call_chain(&port_altmode->nh, state, NULL); + blocking_notifier_call_chain(&port_altmode->nh, conf, NULL); return 0; } @@ -67,7 +78,7 @@ int typec_altmode_notify(struct typec_altmode *adev, is_port = is_typec_port(adev->dev.parent); partner = altmode->partner; - ret = typec_altmode_set_mux(is_port ? altmode : partner, (u8)conf); + ret = typec_altmode_set_mux(is_port ? altmode : partner, conf, data); if (ret) return ret; @@ -107,7 +118,7 @@ int typec_altmode_enter(struct typec_altmode *adev, u32 *vdo) return -EPERM; /* Moving to USB Safe State */ - ret = typec_altmode_set_state(adev, TYPEC_STATE_SAFE); + ret = typec_altmode_set_state(adev, TYPEC_STATE_SAFE, NULL); if (ret) return ret; @@ -135,7 +146,7 @@ int typec_altmode_exit(struct typec_altmode *adev) return -EOPNOTSUPP; /* Moving to USB Safe State */ - ret = typec_altmode_set_state(adev, TYPEC_STATE_SAFE); + ret = typec_altmode_set_state(adev, TYPEC_STATE_SAFE, NULL); if (ret) return ret; @@ -388,7 +399,7 @@ static int typec_remove(struct device *dev) drv->remove(to_typec_altmode(dev)); if (adev->active) { - WARN_ON(typec_altmode_set_state(adev, TYPEC_STATE_SAFE)); + WARN_ON(typec_altmode_set_state(adev, TYPEC_STATE_SAFE, NULL)); typec_altmode_update_active(adev, false); } diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 08923637cd88..7c44e930602f 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -1529,7 +1529,11 @@ EXPORT_SYMBOL_GPL(typec_get_orientation); */ int typec_set_mode(struct typec_port *port, int mode) { - return port->mux ? port->mux->set(port->mux, mode) : 0; + struct typec_mux_state state = { }; + + state.mode = mode; + + return port->mux ? port->mux->set(port->mux, &state) : 0; } EXPORT_SYMBOL_GPL(typec_set_mode); diff --git a/drivers/usb/typec/mux/pi3usb30532.c b/drivers/usb/typec/mux/pi3usb30532.c index 5585b109095b..46457c133d2b 100644 --- a/drivers/usb/typec/mux/pi3usb30532.c +++ b/drivers/usb/typec/mux/pi3usb30532.c @@ -73,7 +73,8 @@ static int pi3usb30532_sw_set(struct typec_switch *sw, return ret; } -static int pi3usb30532_mux_set(struct typec_mux *mux, int state) +static int +pi3usb30532_mux_set(struct typec_mux *mux, struct typec_mux_state *state) { struct pi3usb30532 *pi = typec_mux_get_drvdata(mux); u8 new_conf; @@ -82,7 +83,7 @@ static int pi3usb30532_mux_set(struct typec_mux *mux, int state) mutex_lock(&pi->lock); new_conf = pi->conf; - switch (state) { + switch (state->mode) { case TYPEC_STATE_SAFE: new_conf = (new_conf & PI3USB30532_CONF_SWAP) | PI3USB30532_CONF_OPEN; diff --git a/include/linux/usb/typec_mux.h b/include/linux/usb/typec_mux.h index 873ace5b0cf8..be7292c0be5e 100644 --- a/include/linux/usb/typec_mux.h +++ b/include/linux/usb/typec_mux.h @@ -8,6 +8,7 @@ struct device; struct typec_mux; struct typec_switch; +struct typec_altmode; struct fwnode_handle; typedef int (*typec_switch_set_fn_t)(struct typec_switch *sw, @@ -29,7 +30,14 @@ void typec_switch_unregister(struct typec_switch *sw); void typec_switch_set_drvdata(struct typec_switch *sw, void *data); void *typec_switch_get_drvdata(struct typec_switch *sw); -typedef int (*typec_mux_set_fn_t)(struct typec_mux *mux, int state); +struct typec_mux_state { + struct typec_altmode *alt; + unsigned long mode; + void *data; +}; + +typedef int (*typec_mux_set_fn_t)(struct typec_mux *mux, + struct typec_mux_state *state); struct typec_mux_desc { struct fwnode_handle *fwnode; -- cgit v1.2.3-59-g8ed1b From 0f37a607091c30c2270d9065e8808a7d6ff34646 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 30 Dec 2019 17:26:09 +0300 Subject: usb: typec: Provide definitions for the USB modes Defining the USB modes from the latest USB Power Delivery Specification - USB 2.0, USB 3.2 and USB4 - as special modal states just like the Accessory Modes. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20191230142611.24921-14-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/typec_altmode.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h index fc57fd88004f..923ff3af0628 100644 --- a/include/linux/usb/typec_altmode.h +++ b/include/linux/usb/typec_altmode.h @@ -101,6 +101,22 @@ enum { TYPEC_MODE_DEBUG, /* Debug Accessory */ }; +/* + * USB4 also requires that the pins on the connector are repurposed, just like + * Alternate Modes. USB4 mode is however not entered with the Enter Mode Command + * like the Alternate Modes are, but instead with a special Enter_USB Message. + * The Enter_USB Message can also be used for setting to connector to operate in + * USB 3.2 or in USB 2.0 mode instead of USB4. + * + * The Enter_USB specific "USB Modes" are also supplied here as special modal + * state values, just like the Accessory Modes. + */ +enum { + TYPEC_MODE_USB2 = TYPEC_MODE_DEBUG, /* USB 2.0 mode */ + TYPEC_MODE_USB3, /* USB 3.2 mode */ + TYPEC_MODE_USB4 /* USB4 mode */ +}; + #define TYPEC_MODAL_STATE(_state_) ((_state_) + TYPEC_STATE_MODAL) struct typec_altmode *typec_altmode_get_plug(struct typec_altmode *altmode, -- cgit v1.2.3-59-g8ed1b From dea7b202bd9c03a2523c4c908f117d271a2b2d10 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 4 Jan 2020 16:20:55 +0100 Subject: usb: exynos: Rename Samsung and Exynos to lowercase Fix up inconsistent usage of upper and lowercase letters in "Samsung" and "Exynos" names. "SAMSUNG" and "EXYNOS" are not abbreviations but regular trademarked names. Therefore they should be written with lowercase letters starting with capital letter. The lowercase "Exynos" name is promoted by its manufacturer Samsung Electronics Co., Ltd., in advertisement materials and on website. Although advertisement materials usually use uppercase "SAMSUNG", the lowercase version is used in all legal aspects (e.g. on Wikipedia and in privacy/legal statements on https://www.samsung.com/semiconductor/privacy-global/). Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20200104152107.11407-9-krzk@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-exynos.c | 4 ++-- drivers/usb/host/Kconfig | 4 ++-- drivers/usb/host/ehci-exynos.c | 4 ++-- drivers/usb/host/ohci-exynos.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index c1e9ea621f41..90bb022737da 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /** - * dwc3-exynos.c - Samsung EXYNOS DWC3 Specific Glue layer + * dwc3-exynos.c - Samsung Exynos DWC3 Specific Glue layer * * Copyright (c) 2012 Samsung Electronics Co., Ltd. * http://www.samsung.com @@ -255,4 +255,4 @@ module_platform_driver(dwc3_exynos_driver); MODULE_AUTHOR("Anton Tikhomirov "); MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("DesignWare USB3 EXYNOS Glue Layer"); +MODULE_DESCRIPTION("DesignWare USB3 Exynos Glue Layer"); diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 803023fcb3fe..55bdfdf11e4c 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -269,7 +269,7 @@ config USB_EHCI_SH If you use the PCI EHCI controller, this option is not necessary. config USB_EHCI_EXYNOS - tristate "EHCI support for Samsung S5P/EXYNOS SoC Series" + tristate "EHCI support for Samsung S5P/Exynos SoC Series" depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST help Enable support for the Samsung Exynos SOC's on-chip EHCI controller. @@ -542,7 +542,7 @@ config USB_OHCI_SH If you use the PCI OHCI controller, this option is not necessary. config USB_OHCI_EXYNOS - tristate "OHCI support for Samsung S5P/EXYNOS SoC Series" + tristate "OHCI support for Samsung S5P/Exynos SoC Series" depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST help Enable support for the Samsung Exynos SOC's on-chip OHCI controller. diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c index 01debfd03d4a..a4e9abcbdc4f 100644 --- a/drivers/usb/host/ehci-exynos.c +++ b/drivers/usb/host/ehci-exynos.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * SAMSUNG EXYNOS USB HOST EHCI Controller + * Samsung Exynos USB HOST EHCI Controller * * Copyright (C) 2011 Samsung Electronics Co.Ltd * Author: Jingoo Han @@ -21,7 +21,7 @@ #include "ehci.h" -#define DRIVER_DESC "EHCI EXYNOS driver" +#define DRIVER_DESC "EHCI Exynos driver" #define EHCI_INSNREG00(base) (base + 0x90) #define EHCI_INSNREG00_ENA_INCR16 (0x1 << 25) diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index d5ce98e205c7..bd40e597f256 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -19,7 +19,7 @@ #include "ohci.h" -#define DRIVER_DESC "OHCI EXYNOS driver" +#define DRIVER_DESC "OHCI Exynos driver" static const char hcd_name[] = "ohci-exynos"; static struct hc_driver __read_mostly exynos_ohci_hc_driver; -- cgit v1.2.3-59-g8ed1b From 0bb207acd37bd087d8eb424aacb23fb1ecdc232b Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 6 Jan 2020 04:33:57 +0300 Subject: dt-binding: usb: ci-hdrc-usb2: Document NVIDIA Tegra support NVIDIA Tegra SoCs use ChipIdea silicon IP for the USB controllers. Acked-by: Peter Chen Acked-by: Thierry Reding Acked-by: Rob Herring Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20200106013416.9604-2-digetx@gmail.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt index cfc9f40ab641..51376cbe5f3d 100644 --- a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt +++ b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt @@ -15,6 +15,10 @@ Required properties: "qcom,ci-hdrc" "chipidea,usb2" "xlnx,zynq-usb-2.20a" + "nvidia,tegra20-udc" + "nvidia,tegra30-udc" + "nvidia,tegra114-udc" + "nvidia,tegra124-udc" - reg: base address and length of the registers - interrupts: interrupt for the USB controller -- cgit v1.2.3-59-g8ed1b From 62a7f62891247e83b6a17cedae8f8ae15cff2c5e Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 6 Jan 2020 04:33:58 +0300 Subject: usb: host: ehci-tegra: Correct teardown order of driver's removal I found that PHY's enable refcounting was broken and after fixing it I also found that machine started to hang after EHCI driver module removal. Turned out that the teardown order is incorrect because HCD must be unregistered *before* PHY's disabling. Note that it is also not correct to assert the shared reset during of driver's removal because PHY takes care of resetting shared pads and thus it's better to remove that part from the EHCI driver. Signed-off-by: Dmitry Osipenko Acked-by: Alan Stern Link: https://lore.kernel.org/r/20200106013416.9604-3-digetx@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-tegra.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 4d2cdec4cb78..32483bef046b 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -521,16 +521,10 @@ static int tegra_ehci_remove(struct platform_device *pdev) struct tegra_ehci_hcd *tegra = (struct tegra_ehci_hcd *)hcd_to_ehci(hcd)->priv; + usb_remove_hcd(hcd); otg_set_host(hcd->usb_phy->otg, NULL); - usb_phy_shutdown(hcd->usb_phy); - usb_remove_hcd(hcd); - - reset_control_assert(tegra->rst); - udelay(1); - clk_disable_unprepare(tegra->clk); - usb_put_hcd(hcd); return 0; -- cgit v1.2.3-59-g8ed1b From 28d190ac437c675763f03b17407ea16c1dd8c613 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 6 Jan 2020 04:33:59 +0300 Subject: usb: phy: tegra: Clean up ulpi_phy_power_off Firstly, the PHY's clock needs to unprepared to keep prepare count balanced. Secondly, downstream code suggests that reset is synchronous and thus it should be asserted before disabling clock. Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20200106013416.9604-4-digetx@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-tegra-usb.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index ea7ef1dc0b42..99acfde4ab8d 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -757,8 +757,19 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy) static int ulpi_phy_power_off(struct tegra_usb_phy *phy) { - clk_disable(phy->clk); - return gpio_direction_output(phy->reset_gpio, 0); + int err; + + err = gpio_direction_output(phy->reset_gpio, 0); + if (err) { + dev_err(phy->u_phy.dev, "reset GPIO not asserted: %d\n", err); + return err; + } + + usleep_range(5000, 6000); + + clk_disable_unprepare(phy->clk); + + return 0; } static void tegra_usb_phy_close(struct tegra_usb_phy *phy) -- cgit v1.2.3-59-g8ed1b From 18bd8bff69f7fbc53903dba4a1c234a30a8fcbde Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 6 Jan 2020 04:34:00 +0300 Subject: usb: phy: tegra: Keep track of power on-off state The PHY driver should keep track of the enable state, otherwise enable refcount is screwed if USB driver tries to enable PHY when it is already enabled. This will be the case for ChipIdea and Tegra EHCI drivers once PHY driver will gain support for the init/shutdown callbacks. Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20200106013416.9604-5-digetx@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-tegra-usb.c | 30 ++++++++++++++++++++++++++---- include/linux/usb/tegra_usb_phy.h | 1 + 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 99acfde4ab8d..88466c7f13e6 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -785,18 +785,40 @@ static void tegra_usb_phy_close(struct tegra_usb_phy *phy) static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy) { + int err; + + if (phy->powered_on) + return 0; + if (phy->is_ulpi_phy) - return ulpi_phy_power_on(phy); + err = ulpi_phy_power_on(phy); else - return utmi_phy_power_on(phy); + err = utmi_phy_power_on(phy); + if (err) + return err; + + phy->powered_on = true; + + return 0; } static int tegra_usb_phy_power_off(struct tegra_usb_phy *phy) { + int err; + + if (!phy->powered_on) + return 0; + if (phy->is_ulpi_phy) - return ulpi_phy_power_off(phy); + err = ulpi_phy_power_off(phy); else - return utmi_phy_power_off(phy); + err = utmi_phy_power_off(phy); + if (err) + return err; + + phy->powered_on = false; + + return 0; } static int tegra_usb_phy_suspend(struct usb_phy *x, int suspend) diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index 0c5c3ea8b2d7..3ae73bdc6245 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -78,6 +78,7 @@ struct tegra_usb_phy { bool is_ulpi_phy; int reset_gpio; struct reset_control *pad_rst; + bool powered_on; }; void tegra_usb_phy_preresume(struct usb_phy *phy); -- cgit v1.2.3-59-g8ed1b From 5dcdafdd30b1babde143ee7086d3de79396d023b Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 6 Jan 2020 04:34:01 +0300 Subject: usb: phy: tegra: Hook up init/shutdown callbacks Generic PHY provides init/shutdown callbacks which allow USB-host drivers to abstract PHY's hardware management in a common way. This change allows to remove Tegra-specific PHY handling from the ChipIdea driver. Note that ChipIdea's driver shall be changed at the same time because it turns PHY ON without the PHY's initialization and this doesn't work now, resulting in a NULL dereference of phy->freq because it's set during of the PHY's initialization. Acked-by: Peter Chen Acked-by: Thierry Reding Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20200106013416.9604-6-digetx@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-tegra-usb.c | 180 ++++++++++++++++++++++++---------------- 1 file changed, 110 insertions(+), 70 deletions(-) diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 88466c7f13e6..664259d74658 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -238,23 +238,6 @@ static int utmip_pad_open(struct tegra_usb_phy *phy) { int ret; - phy->pad_clk = devm_clk_get(phy->u_phy.dev, "utmi-pads"); - if (IS_ERR(phy->pad_clk)) { - ret = PTR_ERR(phy->pad_clk); - dev_err(phy->u_phy.dev, - "Failed to get UTMIP pad clock: %d\n", ret); - return ret; - } - - phy->pad_rst = devm_reset_control_get_optional_shared( - phy->u_phy.dev, "utmi-pads"); - if (IS_ERR(phy->pad_rst)) { - ret = PTR_ERR(phy->pad_rst); - dev_err(phy->u_phy.dev, - "Failed to get UTMI-pads reset: %d\n", ret); - return ret; - } - ret = clk_prepare_enable(phy->pad_clk); if (ret) { dev_err(phy->u_phy.dev, @@ -772,17 +755,6 @@ static int ulpi_phy_power_off(struct tegra_usb_phy *phy) return 0; } -static void tegra_usb_phy_close(struct tegra_usb_phy *phy) -{ - if (!IS_ERR(phy->vbus)) - regulator_disable(phy->vbus); - - if (!phy->is_ulpi_phy) - utmip_pad_close(phy); - - clk_disable_unprepare(phy->pll_u); -} - static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy) { int err; @@ -821,9 +793,34 @@ static int tegra_usb_phy_power_off(struct tegra_usb_phy *phy) return 0; } -static int tegra_usb_phy_suspend(struct usb_phy *x, int suspend) +static void tegra_usb_phy_shutdown(struct usb_phy *u_phy) +{ + struct tegra_usb_phy *phy = container_of(u_phy, struct tegra_usb_phy, + u_phy); + + if (WARN_ON(!phy->freq)) + return; + + tegra_usb_phy_power_off(phy); + + if (!phy->is_ulpi_phy) + utmip_pad_close(phy); + + if (!IS_ERR(phy->vbus)) + regulator_disable(phy->vbus); + + clk_disable_unprepare(phy->pll_u); + + phy->freq = NULL; +} + +static int tegra_usb_phy_set_suspend(struct usb_phy *x, int suspend) { struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); + + if (WARN_ON(!phy->freq)) + return -EINVAL; + if (suspend) return tegra_usb_phy_power_off(phy); else @@ -834,53 +831,27 @@ static int ulpi_open(struct tegra_usb_phy *phy) { int err; - phy->clk = devm_clk_get(phy->u_phy.dev, "ulpi-link"); - if (IS_ERR(phy->clk)) { - err = PTR_ERR(phy->clk); - dev_err(phy->u_phy.dev, "Failed to get ULPI clock: %d\n", err); - return err; - } - - err = devm_gpio_request(phy->u_phy.dev, phy->reset_gpio, - "ulpi_phy_reset_b"); - if (err < 0) { - dev_err(phy->u_phy.dev, "Request failed for GPIO %d: %d\n", - phy->reset_gpio, err); - return err; - } - err = gpio_direction_output(phy->reset_gpio, 0); if (err < 0) { dev_err(phy->u_phy.dev, - "GPIO %d direction not set to output: %d\n", + "ULPI reset GPIO %d direction not asserted: %d\n", phy->reset_gpio, err); return err; } - phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0); - if (!phy->ulpi) { - dev_err(phy->u_phy.dev, "Failed to create ULPI OTG\n"); - err = -ENOMEM; - return err; - } - - phy->ulpi->io_priv = phy->regs + ULPI_VIEWPORT; return 0; } -static int tegra_usb_phy_init(struct tegra_usb_phy *phy) +static int tegra_usb_phy_init(struct usb_phy *u_phy) { + struct tegra_usb_phy *phy = container_of(u_phy, struct tegra_usb_phy, + u_phy); unsigned long parent_rate; int i; int err; - phy->pll_u = devm_clk_get(phy->u_phy.dev, "pll_u"); - if (IS_ERR(phy->pll_u)) { - err = PTR_ERR(phy->pll_u); - dev_err(phy->u_phy.dev, - "Failed to get pll_u clock: %d\n", err); - return err; - } + if (WARN_ON(phy->freq)) + return 0; err = clk_prepare_enable(phy->pll_u); if (err) @@ -917,10 +888,20 @@ static int tegra_usb_phy_init(struct tegra_usb_phy *phy) if (err < 0) goto fail; + err = tegra_usb_phy_power_on(phy); + if (err) + goto close_phy; + return 0; +close_phy: + if (!phy->is_ulpi_phy) + utmip_pad_close(phy); fail: clk_disable_unprepare(phy->pll_u); + + phy->freq = NULL; + return err; } @@ -1167,22 +1148,77 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) tegra_phy->vbus = ERR_PTR(-ENODEV); } - tegra_phy->u_phy.dev = &pdev->dev; - err = tegra_usb_phy_init(tegra_phy); - if (err < 0) + tegra_phy->pll_u = devm_clk_get(&pdev->dev, "pll_u"); + err = PTR_ERR_OR_ZERO(tegra_phy->pll_u); + if (err) { + dev_err(&pdev->dev, "Failed to get pll_u clock: %d\n", err); return err; + } + + if (tegra_phy->is_ulpi_phy) { + tegra_phy->clk = devm_clk_get(&pdev->dev, "ulpi-link"); + err = PTR_ERR_OR_ZERO(tegra_phy->clk); + if (err) { + dev_err(&pdev->dev, + "Failed to get ULPI clock: %d\n", err); + return err; + } - tegra_phy->u_phy.set_suspend = tegra_usb_phy_suspend; + err = devm_gpio_request(&pdev->dev, tegra_phy->reset_gpio, + "ulpi_phy_reset_b"); + if (err < 0) { + dev_err(&pdev->dev, "Request failed for GPIO %d: %d\n", + tegra_phy->reset_gpio, err); + return err; + } + + tegra_phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0); + if (!tegra_phy->ulpi) { + dev_err(&pdev->dev, "Failed to create ULPI OTG\n"); + err = -ENOMEM; + return err; + } + + tegra_phy->ulpi->io_priv = tegra_phy->regs + ULPI_VIEWPORT; + } else { + tegra_phy->pad_clk = devm_clk_get(&pdev->dev, "utmi-pads"); + err = PTR_ERR_OR_ZERO(tegra_phy->pad_clk); + if (err) { + dev_err(&pdev->dev, + "Failed to get UTMIP pad clock: %d\n", err); + return err; + } + + tegra_phy->pad_rst = devm_reset_control_get_optional_shared( + &pdev->dev, "utmi-pads"); + err = PTR_ERR_OR_ZERO(tegra_phy->pad_rst); + if (err) { + dev_err(&pdev->dev, + "Failed to get UTMI-pads reset: %d\n", err); + return err; + } + } + + tegra_phy->u_phy.dev = &pdev->dev; + tegra_phy->u_phy.init = tegra_usb_phy_init; + tegra_phy->u_phy.shutdown = tegra_usb_phy_shutdown; + tegra_phy->u_phy.set_suspend = tegra_usb_phy_set_suspend; platform_set_drvdata(pdev, tegra_phy); err = usb_add_phy_dev(&tegra_phy->u_phy); - if (err < 0) { - tegra_usb_phy_close(tegra_phy); - return err; - } + if (err < 0) + goto free_ulpi; return 0; + +free_ulpi: + if (tegra_phy->ulpi) { + kfree(tegra_phy->ulpi->otg); + kfree(tegra_phy->ulpi); + } + + return err; } static int tegra_usb_phy_remove(struct platform_device *pdev) @@ -1190,7 +1226,11 @@ static int tegra_usb_phy_remove(struct platform_device *pdev) struct tegra_usb_phy *tegra_phy = platform_get_drvdata(pdev); usb_remove_phy(&tegra_phy->u_phy); - tegra_usb_phy_close(tegra_phy); + + if (tegra_phy->ulpi) { + kfree(tegra_phy->ulpi->otg); + kfree(tegra_phy->ulpi); + } return 0; } -- cgit v1.2.3-59-g8ed1b From 545592e8eb6f20341c8260ddd32a7407f8bfa5f2 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 6 Jan 2020 04:34:02 +0300 Subject: usb: phy: tegra: Perform general clean up of the code This patch fixes few dozens of legit checkpatch warnings, adds missed handling of potential error-cases and prettifies code where makes sense. All these clean-up changes are quite minor and do not fix any real problems. Acked-by: Thierry Reding Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20200106013416.9604-7-digetx@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-tegra-usb.c | 485 +++++++++++++++++++++------------------- 1 file changed, 254 insertions(+), 231 deletions(-) diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 664259d74658..c045f44ea964 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -28,35 +28,35 @@ #include #include -#define ULPI_VIEWPORT 0x170 +#define ULPI_VIEWPORT 0x170 /* PORTSC PTS/PHCD bits, Tegra20 only */ -#define TEGRA_USB_PORTSC1 0x184 -#define TEGRA_USB_PORTSC1_PTS(x) (((x) & 0x3) << 30) -#define TEGRA_USB_PORTSC1_PHCD (1 << 23) +#define TEGRA_USB_PORTSC1 0x184 +#define TEGRA_USB_PORTSC1_PTS(x) (((x) & 0x3) << 30) +#define TEGRA_USB_PORTSC1_PHCD BIT(23) /* HOSTPC1 PTS/PHCD bits, Tegra30 and above */ -#define TEGRA_USB_HOSTPC1_DEVLC 0x1b4 -#define TEGRA_USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29) -#define TEGRA_USB_HOSTPC1_DEVLC_PHCD (1 << 22) +#define TEGRA_USB_HOSTPC1_DEVLC 0x1b4 +#define TEGRA_USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29) +#define TEGRA_USB_HOSTPC1_DEVLC_PHCD BIT(22) /* Bits of PORTSC1, which will get cleared by writing 1 into them */ #define TEGRA_PORTSC1_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) -#define USB_SUSP_CTRL 0x400 -#define USB_WAKE_ON_CNNT_EN_DEV (1 << 3) -#define USB_WAKE_ON_DISCON_EN_DEV (1 << 4) -#define USB_SUSP_CLR (1 << 5) -#define USB_PHY_CLK_VALID (1 << 7) -#define UTMIP_RESET (1 << 11) -#define UHSIC_RESET (1 << 11) -#define UTMIP_PHY_ENABLE (1 << 12) -#define ULPI_PHY_ENABLE (1 << 13) -#define USB_SUSP_SET (1 << 14) -#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16) - -#define USB1_LEGACY_CTRL 0x410 -#define USB1_NO_LEGACY_MODE (1 << 0) +#define USB_SUSP_CTRL 0x400 +#define USB_WAKE_ON_CNNT_EN_DEV BIT(3) +#define USB_WAKE_ON_DISCON_EN_DEV BIT(4) +#define USB_SUSP_CLR BIT(5) +#define USB_PHY_CLK_VALID BIT(7) +#define UTMIP_RESET BIT(11) +#define UHSIC_RESET BIT(11) +#define UTMIP_PHY_ENABLE BIT(12) +#define ULPI_PHY_ENABLE BIT(13) +#define USB_SUSP_SET BIT(14) +#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16) + +#define USB1_LEGACY_CTRL 0x410 +#define USB1_NO_LEGACY_MODE BIT(0) #define USB1_VBUS_SENSE_CTL_MASK (3 << 1) #define USB1_VBUS_SENSE_CTL_VBUS_WAKEUP (0 << 1) #define USB1_VBUS_SENSE_CTL_AB_SESS_VLD_OR_VBUS_WAKEUP \ @@ -64,94 +64,94 @@ #define USB1_VBUS_SENSE_CTL_AB_SESS_VLD (2 << 1) #define USB1_VBUS_SENSE_CTL_A_SESS_VLD (3 << 1) -#define ULPI_TIMING_CTRL_0 0x424 -#define ULPI_OUTPUT_PINMUX_BYP (1 << 10) -#define ULPI_CLKOUT_PINMUX_BYP (1 << 11) +#define ULPI_TIMING_CTRL_0 0x424 +#define ULPI_OUTPUT_PINMUX_BYP BIT(10) +#define ULPI_CLKOUT_PINMUX_BYP BIT(11) -#define ULPI_TIMING_CTRL_1 0x428 -#define ULPI_DATA_TRIMMER_LOAD (1 << 0) -#define ULPI_DATA_TRIMMER_SEL(x) (((x) & 0x7) << 1) -#define ULPI_STPDIRNXT_TRIMMER_LOAD (1 << 16) -#define ULPI_STPDIRNXT_TRIMMER_SEL(x) (((x) & 0x7) << 17) -#define ULPI_DIR_TRIMMER_LOAD (1 << 24) -#define ULPI_DIR_TRIMMER_SEL(x) (((x) & 0x7) << 25) +#define ULPI_TIMING_CTRL_1 0x428 +#define ULPI_DATA_TRIMMER_LOAD BIT(0) +#define ULPI_DATA_TRIMMER_SEL(x) (((x) & 0x7) << 1) +#define ULPI_STPDIRNXT_TRIMMER_LOAD BIT(16) +#define ULPI_STPDIRNXT_TRIMMER_SEL(x) (((x) & 0x7) << 17) +#define ULPI_DIR_TRIMMER_LOAD BIT(24) +#define ULPI_DIR_TRIMMER_SEL(x) (((x) & 0x7) << 25) -#define UTMIP_PLL_CFG1 0x804 +#define UTMIP_PLL_CFG1 0x804 #define UTMIP_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0) #define UTMIP_PLLU_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 27) -#define UTMIP_XCVR_CFG0 0x808 +#define UTMIP_XCVR_CFG0 0x808 #define UTMIP_XCVR_SETUP(x) (((x) & 0xf) << 0) #define UTMIP_XCVR_SETUP_MSB(x) ((((x) & 0x70) >> 4) << 22) #define UTMIP_XCVR_LSRSLEW(x) (((x) & 0x3) << 8) #define UTMIP_XCVR_LSFSLEW(x) (((x) & 0x3) << 10) -#define UTMIP_FORCE_PD_POWERDOWN (1 << 14) -#define UTMIP_FORCE_PD2_POWERDOWN (1 << 16) -#define UTMIP_FORCE_PDZI_POWERDOWN (1 << 18) -#define UTMIP_XCVR_LSBIAS_SEL (1 << 21) +#define UTMIP_FORCE_PD_POWERDOWN BIT(14) +#define UTMIP_FORCE_PD2_POWERDOWN BIT(16) +#define UTMIP_FORCE_PDZI_POWERDOWN BIT(18) +#define UTMIP_XCVR_LSBIAS_SEL BIT(21) #define UTMIP_XCVR_HSSLEW(x) (((x) & 0x3) << 4) #define UTMIP_XCVR_HSSLEW_MSB(x) ((((x) & 0x1fc) >> 2) << 25) -#define UTMIP_BIAS_CFG0 0x80c -#define UTMIP_OTGPD (1 << 11) -#define UTMIP_BIASPD (1 << 10) -#define UTMIP_HSSQUELCH_LEVEL(x) (((x) & 0x3) << 0) -#define UTMIP_HSDISCON_LEVEL(x) (((x) & 0x3) << 2) -#define UTMIP_HSDISCON_LEVEL_MSB(x) ((((x) & 0x4) >> 2) << 24) +#define UTMIP_BIAS_CFG0 0x80c +#define UTMIP_OTGPD BIT(11) +#define UTMIP_BIASPD BIT(10) +#define UTMIP_HSSQUELCH_LEVEL(x) (((x) & 0x3) << 0) +#define UTMIP_HSDISCON_LEVEL(x) (((x) & 0x3) << 2) +#define UTMIP_HSDISCON_LEVEL_MSB(x) ((((x) & 0x4) >> 2) << 24) -#define UTMIP_HSRX_CFG0 0x810 -#define UTMIP_ELASTIC_LIMIT(x) (((x) & 0x1f) << 10) -#define UTMIP_IDLE_WAIT(x) (((x) & 0x1f) << 15) +#define UTMIP_HSRX_CFG0 0x810 +#define UTMIP_ELASTIC_LIMIT(x) (((x) & 0x1f) << 10) +#define UTMIP_IDLE_WAIT(x) (((x) & 0x1f) << 15) -#define UTMIP_HSRX_CFG1 0x814 -#define UTMIP_HS_SYNC_START_DLY(x) (((x) & 0x1f) << 1) +#define UTMIP_HSRX_CFG1 0x814 +#define UTMIP_HS_SYNC_START_DLY(x) (((x) & 0x1f) << 1) -#define UTMIP_TX_CFG0 0x820 -#define UTMIP_FS_PREABMLE_J (1 << 19) -#define UTMIP_HS_DISCON_DISABLE (1 << 8) +#define UTMIP_TX_CFG0 0x820 +#define UTMIP_FS_PREABMLE_J BIT(19) +#define UTMIP_HS_DISCON_DISABLE BIT(8) -#define UTMIP_MISC_CFG0 0x824 -#define UTMIP_DPDM_OBSERVE (1 << 26) -#define UTMIP_DPDM_OBSERVE_SEL(x) (((x) & 0xf) << 27) -#define UTMIP_DPDM_OBSERVE_SEL_FS_J UTMIP_DPDM_OBSERVE_SEL(0xf) -#define UTMIP_DPDM_OBSERVE_SEL_FS_K UTMIP_DPDM_OBSERVE_SEL(0xe) -#define UTMIP_DPDM_OBSERVE_SEL_FS_SE1 UTMIP_DPDM_OBSERVE_SEL(0xd) -#define UTMIP_DPDM_OBSERVE_SEL_FS_SE0 UTMIP_DPDM_OBSERVE_SEL(0xc) -#define UTMIP_SUSPEND_EXIT_ON_EDGE (1 << 22) +#define UTMIP_MISC_CFG0 0x824 +#define UTMIP_DPDM_OBSERVE BIT(26) +#define UTMIP_DPDM_OBSERVE_SEL(x) (((x) & 0xf) << 27) +#define UTMIP_DPDM_OBSERVE_SEL_FS_J UTMIP_DPDM_OBSERVE_SEL(0xf) +#define UTMIP_DPDM_OBSERVE_SEL_FS_K UTMIP_DPDM_OBSERVE_SEL(0xe) +#define UTMIP_DPDM_OBSERVE_SEL_FS_SE1 UTMIP_DPDM_OBSERVE_SEL(0xd) +#define UTMIP_DPDM_OBSERVE_SEL_FS_SE0 UTMIP_DPDM_OBSERVE_SEL(0xc) +#define UTMIP_SUSPEND_EXIT_ON_EDGE BIT(22) -#define UTMIP_MISC_CFG1 0x828 -#define UTMIP_PLL_ACTIVE_DLY_COUNT(x) (((x) & 0x1f) << 18) -#define UTMIP_PLLU_STABLE_COUNT(x) (((x) & 0xfff) << 6) +#define UTMIP_MISC_CFG1 0x828 +#define UTMIP_PLL_ACTIVE_DLY_COUNT(x) (((x) & 0x1f) << 18) +#define UTMIP_PLLU_STABLE_COUNT(x) (((x) & 0xfff) << 6) -#define UTMIP_DEBOUNCE_CFG0 0x82c -#define UTMIP_BIAS_DEBOUNCE_A(x) (((x) & 0xffff) << 0) +#define UTMIP_DEBOUNCE_CFG0 0x82c +#define UTMIP_BIAS_DEBOUNCE_A(x) (((x) & 0xffff) << 0) -#define UTMIP_BAT_CHRG_CFG0 0x830 -#define UTMIP_PD_CHRG (1 << 0) +#define UTMIP_BAT_CHRG_CFG0 0x830 +#define UTMIP_PD_CHRG BIT(0) -#define UTMIP_SPARE_CFG0 0x834 -#define FUSE_SETUP_SEL (1 << 3) +#define UTMIP_SPARE_CFG0 0x834 +#define FUSE_SETUP_SEL BIT(3) -#define UTMIP_XCVR_CFG1 0x838 -#define UTMIP_FORCE_PDDISC_POWERDOWN (1 << 0) -#define UTMIP_FORCE_PDCHRP_POWERDOWN (1 << 2) -#define UTMIP_FORCE_PDDR_POWERDOWN (1 << 4) -#define UTMIP_XCVR_TERM_RANGE_ADJ(x) (((x) & 0xf) << 18) +#define UTMIP_XCVR_CFG1 0x838 +#define UTMIP_FORCE_PDDISC_POWERDOWN BIT(0) +#define UTMIP_FORCE_PDCHRP_POWERDOWN BIT(2) +#define UTMIP_FORCE_PDDR_POWERDOWN BIT(4) +#define UTMIP_XCVR_TERM_RANGE_ADJ(x) (((x) & 0xf) << 18) -#define UTMIP_BIAS_CFG1 0x83c -#define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3) +#define UTMIP_BIAS_CFG1 0x83c +#define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3) /* For Tegra30 and above only, the address is different in Tegra20 */ -#define USB_USBMODE 0x1f8 -#define USB_USBMODE_MASK (3 << 0) -#define USB_USBMODE_HOST (3 << 0) -#define USB_USBMODE_DEVICE (2 << 0) +#define USB_USBMODE 0x1f8 +#define USB_USBMODE_MASK (3 << 0) +#define USB_USBMODE_HOST (3 << 0) +#define USB_USBMODE_DEVICE (2 << 0) static DEFINE_SPINLOCK(utmip_pad_lock); -static int utmip_pad_count; +static unsigned int utmip_pad_count; struct tegra_xtal_freq { - int freq; + unsigned int freq; u8 enable_delay; u8 stable_count; u8 active_delay; @@ -194,6 +194,11 @@ static const struct tegra_xtal_freq tegra_freq_table[] = { }, }; +static inline struct tegra_usb_phy *to_tegra_usb_phy(struct usb_phy *u_phy) +{ + return container_of(u_phy, struct tegra_usb_phy, u_phy); +} + static void set_pts(struct tegra_usb_phy *phy, u8 pts_val) { void __iomem *base = phy->regs; @@ -298,13 +303,16 @@ static int utmip_pad_close(struct tegra_usb_phy *phy) return ret; } -static void utmip_pad_power_on(struct tegra_usb_phy *phy) +static int utmip_pad_power_on(struct tegra_usb_phy *phy) { - unsigned long val, flags; - void __iomem *base = phy->pad_regs; struct tegra_utmip_config *config = phy->config; + void __iomem *base = phy->pad_regs; + unsigned long val, flags; + int err; - clk_prepare_enable(phy->pad_clk); + err = clk_prepare_enable(phy->pad_clk); + if (err) + return err; spin_lock_irqsave(&utmip_pad_lock, flags); @@ -327,19 +335,24 @@ static void utmip_pad_power_on(struct tegra_usb_phy *phy) spin_unlock_irqrestore(&utmip_pad_lock, flags); clk_disable_unprepare(phy->pad_clk); + + return 0; } static int utmip_pad_power_off(struct tegra_usb_phy *phy) { - unsigned long val, flags; void __iomem *base = phy->pad_regs; + unsigned long val, flags; + int err; if (!utmip_pad_count) { dev_err(phy->u_phy.dev, "UTMIP pad already powered off\n"); return -EINVAL; } - clk_prepare_enable(phy->pad_clk); + err = clk_prepare_enable(phy->pad_clk); + if (err) + return err; spin_lock_irqsave(&utmip_pad_lock, flags); @@ -366,8 +379,8 @@ static int utmi_wait_register(void __iomem *reg, u32 mask, u32 result) static void utmi_phy_clk_disable(struct tegra_usb_phy *phy) { - unsigned long val; void __iomem *base = phy->regs; + unsigned long val; /* * The USB driver may have already initiated the phy clock @@ -382,23 +395,24 @@ static void utmi_phy_clk_disable(struct tegra_usb_phy *phy) val |= USB_SUSP_SET; writel(val, base + USB_SUSP_CTRL); - udelay(10); + usleep_range(10, 100); val = readl(base + USB_SUSP_CTRL); val &= ~USB_SUSP_SET; writel(val, base + USB_SUSP_CTRL); - } else + } else { set_phcd(phy, true); + } - if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) < 0) + if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0)) dev_err(phy->u_phy.dev, "Timeout waiting for PHY to stabilize on disable\n"); } static void utmi_phy_clk_enable(struct tegra_usb_phy *phy) { - unsigned long val; void __iomem *base = phy->regs; + unsigned long val; /* * The USB driver may have already initiated the phy clock @@ -414,25 +428,27 @@ static void utmi_phy_clk_enable(struct tegra_usb_phy *phy) val |= USB_SUSP_CLR; writel(val, base + USB_SUSP_CTRL); - udelay(10); + usleep_range(10, 100); val = readl(base + USB_SUSP_CTRL); val &= ~USB_SUSP_CLR; writel(val, base + USB_SUSP_CTRL); - } else + } else { set_phcd(phy, false); + } if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, - USB_PHY_CLK_VALID)) + USB_PHY_CLK_VALID)) dev_err(phy->u_phy.dev, "Timeout waiting for PHY to stabilize on enable\n"); } static int utmi_phy_power_on(struct tegra_usb_phy *phy) { - unsigned long val; - void __iomem *base = phy->regs; struct tegra_utmip_config *config = phy->config; + void __iomem *base = phy->regs; + unsigned long val; + int err; val = readl(base + USB_SUSP_CTRL); val |= UTMIP_RESET; @@ -498,7 +514,9 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) writel(val, base + UTMIP_BAT_CHRG_CFG0); } - utmip_pad_power_on(phy); + err = utmip_pad_power_on(phy); + if (err) + return err; val = readl(base + UTMIP_XCVR_CFG0); val &= ~(UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN | @@ -579,8 +597,8 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) static int utmi_phy_power_off(struct tegra_usb_phy *phy) { - unsigned long val; void __iomem *base = phy->regs; + unsigned long val; utmi_phy_clk_disable(phy); @@ -614,8 +632,8 @@ static int utmi_phy_power_off(struct tegra_usb_phy *phy) static void utmi_phy_preresume(struct tegra_usb_phy *phy) { - unsigned long val; void __iomem *base = phy->regs; + unsigned long val; val = readl(base + UTMIP_TX_CFG0); val |= UTMIP_HS_DISCON_DISABLE; @@ -624,8 +642,8 @@ static void utmi_phy_preresume(struct tegra_usb_phy *phy) static void utmi_phy_postresume(struct tegra_usb_phy *phy) { - unsigned long val; void __iomem *base = phy->regs; + unsigned long val; val = readl(base + UTMIP_TX_CFG0); val &= ~UTMIP_HS_DISCON_DISABLE; @@ -635,8 +653,8 @@ static void utmi_phy_postresume(struct tegra_usb_phy *phy) static void utmi_phy_restore_start(struct tegra_usb_phy *phy, enum tegra_usb_phy_port_speed port_speed) { - unsigned long val; void __iomem *base = phy->regs; + unsigned long val; val = readl(base + UTMIP_MISC_CFG0); val &= ~UTMIP_DPDM_OBSERVE_SEL(~0); @@ -645,47 +663,52 @@ static void utmi_phy_restore_start(struct tegra_usb_phy *phy, else val |= UTMIP_DPDM_OBSERVE_SEL_FS_J; writel(val, base + UTMIP_MISC_CFG0); - udelay(1); + usleep_range(1, 10); val = readl(base + UTMIP_MISC_CFG0); val |= UTMIP_DPDM_OBSERVE; writel(val, base + UTMIP_MISC_CFG0); - udelay(10); + usleep_range(10, 100); } static void utmi_phy_restore_end(struct tegra_usb_phy *phy) { - unsigned long val; void __iomem *base = phy->regs; + unsigned long val; val = readl(base + UTMIP_MISC_CFG0); val &= ~UTMIP_DPDM_OBSERVE; writel(val, base + UTMIP_MISC_CFG0); - udelay(10); + usleep_range(10, 100); } static int ulpi_phy_power_on(struct tegra_usb_phy *phy) { - int ret; - unsigned long val; void __iomem *base = phy->regs; + unsigned long val; + int err; - ret = gpio_direction_output(phy->reset_gpio, 0); - if (ret < 0) { + err = gpio_direction_output(phy->reset_gpio, 0); + if (err) { dev_err(phy->u_phy.dev, "GPIO %d not set to 0: %d\n", - phy->reset_gpio, ret); - return ret; + phy->reset_gpio, err); + return err; } - msleep(5); - ret = gpio_direction_output(phy->reset_gpio, 1); - if (ret < 0) { + + err = clk_prepare_enable(phy->clk); + if (err) + return err; + + usleep_range(5000, 6000); + + err = gpio_direction_output(phy->reset_gpio, 1); + if (err) { dev_err(phy->u_phy.dev, "GPIO %d not set to 1: %d\n", - phy->reset_gpio, ret); - return ret; + phy->reset_gpio, err); + goto disable_clk; } - clk_prepare_enable(phy->clk); - msleep(1); + usleep_range(1000, 2000); val = readl(base + USB_SUSP_CTRL); val |= UHSIC_RESET; @@ -706,7 +729,7 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy) val |= ULPI_STPDIRNXT_TRIMMER_SEL(4); val |= ULPI_DIR_TRIMMER_SEL(4); writel(val, base + ULPI_TIMING_CTRL_1); - udelay(10); + usleep_range(10, 100); val |= ULPI_DATA_TRIMMER_LOAD; val |= ULPI_STPDIRNXT_TRIMMER_LOAD; @@ -714,28 +737,33 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy) writel(val, base + ULPI_TIMING_CTRL_1); /* Fix VbusInvalid due to floating VBUS */ - ret = usb_phy_io_write(phy->ulpi, 0x40, 0x08); - if (ret) { - dev_err(phy->u_phy.dev, "ULPI write failed: %d\n", ret); - return ret; + err = usb_phy_io_write(phy->ulpi, 0x40, 0x08); + if (err) { + dev_err(phy->u_phy.dev, "ULPI write failed: %d\n", err); + goto disable_clk; } - ret = usb_phy_io_write(phy->ulpi, 0x80, 0x0B); - if (ret) { - dev_err(phy->u_phy.dev, "ULPI write failed: %d\n", ret); - return ret; + err = usb_phy_io_write(phy->ulpi, 0x80, 0x0B); + if (err) { + dev_err(phy->u_phy.dev, "ULPI write failed: %d\n", err); + goto disable_clk; } val = readl(base + USB_SUSP_CTRL); val |= USB_SUSP_CLR; writel(val, base + USB_SUSP_CTRL); - udelay(100); + usleep_range(100, 1000); val = readl(base + USB_SUSP_CTRL); val &= ~USB_SUSP_CLR; writel(val, base + USB_SUSP_CTRL); return 0; + +disable_clk: + clk_disable_unprepare(phy->clk); + + return err; } static int ulpi_phy_power_off(struct tegra_usb_phy *phy) @@ -795,8 +823,7 @@ static int tegra_usb_phy_power_off(struct tegra_usb_phy *phy) static void tegra_usb_phy_shutdown(struct usb_phy *u_phy) { - struct tegra_usb_phy *phy = container_of(u_phy, struct tegra_usb_phy, - u_phy); + struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy); if (WARN_ON(!phy->freq)) return; @@ -814,9 +841,9 @@ static void tegra_usb_phy_shutdown(struct usb_phy *u_phy) phy->freq = NULL; } -static int tegra_usb_phy_set_suspend(struct usb_phy *x, int suspend) +static int tegra_usb_phy_set_suspend(struct usb_phy *u_phy, int suspend) { - struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); + struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy); if (WARN_ON(!phy->freq)) return -EINVAL; @@ -832,7 +859,7 @@ static int ulpi_open(struct tegra_usb_phy *phy) int err; err = gpio_direction_output(phy->reset_gpio, 0); - if (err < 0) { + if (err) { dev_err(phy->u_phy.dev, "ULPI reset GPIO %d direction not asserted: %d\n", phy->reset_gpio, err); @@ -844,10 +871,9 @@ static int ulpi_open(struct tegra_usb_phy *phy) static int tegra_usb_phy_init(struct usb_phy *u_phy) { - struct tegra_usb_phy *phy = container_of(u_phy, struct tegra_usb_phy, - u_phy); + struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy); unsigned long parent_rate; - int i; + unsigned int i; int err; if (WARN_ON(phy->freq)) @@ -885,7 +911,7 @@ static int tegra_usb_phy_init(struct usb_phy *u_phy) err = ulpi_open(phy); else err = utmip_pad_open(phy); - if (err < 0) + if (err) goto fail; err = tegra_usb_phy_power_on(phy); @@ -905,37 +931,37 @@ fail: return err; } -void tegra_usb_phy_preresume(struct usb_phy *x) +void tegra_usb_phy_preresume(struct usb_phy *u_phy) { - struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); + struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy); if (!phy->is_ulpi_phy) utmi_phy_preresume(phy); } EXPORT_SYMBOL_GPL(tegra_usb_phy_preresume); -void tegra_usb_phy_postresume(struct usb_phy *x) +void tegra_usb_phy_postresume(struct usb_phy *u_phy) { - struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); + struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy); if (!phy->is_ulpi_phy) utmi_phy_postresume(phy); } EXPORT_SYMBOL_GPL(tegra_usb_phy_postresume); -void tegra_ehci_phy_restore_start(struct usb_phy *x, - enum tegra_usb_phy_port_speed port_speed) +void tegra_ehci_phy_restore_start(struct usb_phy *u_phy, + enum tegra_usb_phy_port_speed port_speed) { - struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); + struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy); if (!phy->is_ulpi_phy) utmi_phy_restore_start(phy, port_speed); } EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_start); -void tegra_ehci_phy_restore_end(struct usb_phy *x) +void tegra_ehci_phy_restore_end(struct usb_phy *u_phy) { - struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); + struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy); if (!phy->is_ulpi_phy) utmi_phy_restore_end(phy); @@ -946,21 +972,25 @@ static int read_utmi_param(struct platform_device *pdev, const char *param, u8 *dest) { u32 value; - int err = of_property_read_u32(pdev->dev.of_node, param, &value); - *dest = (u8)value; - if (err < 0) + int err; + + err = of_property_read_u32(pdev->dev.of_node, param, &value); + if (err) dev_err(&pdev->dev, "Failed to read USB UTMI parameter %s: %d\n", param, err); + else + *dest = value; + return err; } static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy, struct platform_device *pdev) { + struct tegra_utmip_config *config; struct resource *res; int err; - struct tegra_utmip_config *config; tegra_phy->is_ulpi_phy = false; @@ -971,7 +1001,7 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy, } tegra_phy->pad_regs = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); + resource_size(res)); if (!tegra_phy->pad_regs) { dev_err(&pdev->dev, "Failed to remap UTMI pad regs\n"); return -ENOMEM; @@ -985,49 +1015,49 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy, config = tegra_phy->config; err = read_utmi_param(pdev, "nvidia,hssync-start-delay", - &config->hssync_start_delay); - if (err < 0) + &config->hssync_start_delay); + if (err) return err; err = read_utmi_param(pdev, "nvidia,elastic-limit", - &config->elastic_limit); - if (err < 0) + &config->elastic_limit); + if (err) return err; err = read_utmi_param(pdev, "nvidia,idle-wait-delay", - &config->idle_wait_delay); - if (err < 0) + &config->idle_wait_delay); + if (err) return err; err = read_utmi_param(pdev, "nvidia,term-range-adj", - &config->term_range_adj); - if (err < 0) + &config->term_range_adj); + if (err) return err; err = read_utmi_param(pdev, "nvidia,xcvr-lsfslew", - &config->xcvr_lsfslew); - if (err < 0) + &config->xcvr_lsfslew); + if (err) return err; err = read_utmi_param(pdev, "nvidia,xcvr-lsrslew", - &config->xcvr_lsrslew); - if (err < 0) + &config->xcvr_lsrslew); + if (err) return err; if (tegra_phy->soc_config->requires_extra_tuning_parameters) { err = read_utmi_param(pdev, "nvidia,xcvr-hsslew", - &config->xcvr_hsslew); - if (err < 0) + &config->xcvr_hsslew); + if (err) return err; err = read_utmi_param(pdev, "nvidia,hssquelch-level", - &config->hssquelch_level); - if (err < 0) + &config->hssquelch_level); + if (err) return err; err = read_utmi_param(pdev, "nvidia,hsdiscon-level", - &config->hsdiscon_level); - if (err < 0) + &config->hsdiscon_level); + if (err) return err; } @@ -1036,8 +1066,8 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy, if (!config->xcvr_setup_use_fuses) { err = read_utmi_param(pdev, "nvidia,xcvr-setup", - &config->xcvr_setup); - if (err < 0) + &config->xcvr_setup); + if (err) return err; } @@ -1067,23 +1097,18 @@ MODULE_DEVICE_TABLE(of, tegra_usb_phy_id_table); static int tegra_usb_phy_probe(struct platform_device *pdev) { - const struct of_device_id *match; - struct resource *res; - struct tegra_usb_phy *tegra_phy = NULL; struct device_node *np = pdev->dev.of_node; + struct tegra_usb_phy *tegra_phy; enum usb_phy_interface phy_type; + struct reset_control *reset; + struct resource *res; int err; tegra_phy = devm_kzalloc(&pdev->dev, sizeof(*tegra_phy), GFP_KERNEL); if (!tegra_phy) return -ENOMEM; - match = of_match_device(tegra_usb_phy_id_table, &pdev->dev); - if (!match) { - dev_err(&pdev->dev, "Error: No device match found\n"); - return -ENODEV; - } - tegra_phy->soc_config = match->data; + tegra_phy->soc_config = of_device_get_match_data(&pdev->dev); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { @@ -1092,7 +1117,7 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) } tegra_phy->regs = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); + resource_size(res)); if (!tegra_phy->regs) { dev_err(&pdev->dev, "Failed to remap I/O memory\n"); return -ENOMEM; @@ -1101,33 +1126,6 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) tegra_phy->is_legacy_phy = of_property_read_bool(np, "nvidia,has-legacy-mode"); - phy_type = of_usb_get_phy_mode(np); - switch (phy_type) { - case USBPHY_INTERFACE_MODE_UTMI: - err = utmi_phy_probe(tegra_phy, pdev); - if (err < 0) - return err; - break; - - case USBPHY_INTERFACE_MODE_ULPI: - tegra_phy->is_ulpi_phy = true; - - tegra_phy->reset_gpio = - of_get_named_gpio(np, "nvidia,phy-reset-gpio", 0); - if (!gpio_is_valid(tegra_phy->reset_gpio)) { - dev_err(&pdev->dev, - "Invalid GPIO: %d\n", tegra_phy->reset_gpio); - return tegra_phy->reset_gpio; - } - tegra_phy->config = NULL; - break; - - default: - dev_err(&pdev->dev, "phy_type %u is invalid or unsupported\n", - phy_type); - return -EINVAL; - } - if (of_find_property(np, "dr_mode", NULL)) tegra_phy->mode = usb_get_dr_mode(&pdev->dev); else @@ -1155,7 +1153,44 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) return err; } - if (tegra_phy->is_ulpi_phy) { + phy_type = of_usb_get_phy_mode(np); + switch (phy_type) { + case USBPHY_INTERFACE_MODE_UTMI: + err = utmi_phy_probe(tegra_phy, pdev); + if (err) + return err; + + tegra_phy->pad_clk = devm_clk_get(&pdev->dev, "utmi-pads"); + err = PTR_ERR_OR_ZERO(tegra_phy->pad_clk); + if (err) { + dev_err(&pdev->dev, + "Failed to get UTMIP pad clock: %d\n", err); + return err; + } + + reset = devm_reset_control_get_optional_shared(&pdev->dev, + "utmi-pads"); + err = PTR_ERR_OR_ZERO(reset); + if (err) { + dev_err(&pdev->dev, + "Failed to get UTMI-pads reset: %d\n", err); + return err; + } + tegra_phy->pad_rst = reset; + break; + + case USBPHY_INTERFACE_MODE_ULPI: + tegra_phy->is_ulpi_phy = true; + + tegra_phy->reset_gpio = + of_get_named_gpio(np, "nvidia,phy-reset-gpio", 0); + + if (!gpio_is_valid(tegra_phy->reset_gpio)) { + dev_err(&pdev->dev, + "Invalid GPIO: %d\n", tegra_phy->reset_gpio); + return tegra_phy->reset_gpio; + } + tegra_phy->clk = devm_clk_get(&pdev->dev, "ulpi-link"); err = PTR_ERR_OR_ZERO(tegra_phy->clk); if (err) { @@ -1165,8 +1200,8 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) } err = devm_gpio_request(&pdev->dev, tegra_phy->reset_gpio, - "ulpi_phy_reset_b"); - if (err < 0) { + "ulpi_phy_reset_b"); + if (err) { dev_err(&pdev->dev, "Request failed for GPIO %d: %d\n", tegra_phy->reset_gpio, err); return err; @@ -1175,28 +1210,16 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) tegra_phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0); if (!tegra_phy->ulpi) { dev_err(&pdev->dev, "Failed to create ULPI OTG\n"); - err = -ENOMEM; - return err; + return -ENOMEM; } tegra_phy->ulpi->io_priv = tegra_phy->regs + ULPI_VIEWPORT; - } else { - tegra_phy->pad_clk = devm_clk_get(&pdev->dev, "utmi-pads"); - err = PTR_ERR_OR_ZERO(tegra_phy->pad_clk); - if (err) { - dev_err(&pdev->dev, - "Failed to get UTMIP pad clock: %d\n", err); - return err; - } + break; - tegra_phy->pad_rst = devm_reset_control_get_optional_shared( - &pdev->dev, "utmi-pads"); - err = PTR_ERR_OR_ZERO(tegra_phy->pad_rst); - if (err) { - dev_err(&pdev->dev, - "Failed to get UTMI-pads reset: %d\n", err); - return err; - } + default: + dev_err(&pdev->dev, "phy_type %u is invalid or unsupported\n", + phy_type); + return -EINVAL; } tegra_phy->u_phy.dev = &pdev->dev; @@ -1207,7 +1230,7 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) platform_set_drvdata(pdev, tegra_phy); err = usb_add_phy_dev(&tegra_phy->u_phy); - if (err < 0) + if (err) goto free_ulpi; return 0; -- cgit v1.2.3-59-g8ed1b From 5bb69850ad41e8ce0b820d3b9e74d573f3bf1a2c Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 6 Jan 2020 04:34:03 +0300 Subject: usb: phy: tegra: Clean up included headers Add "spinlock.h", which was included indirectly, and sort includes in alphabet order. Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20200106013416.9604-8-digetx@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-tegra-usb.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index c045f44ea964..aca1413db0ed 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -9,24 +9,26 @@ * Venu Byravarasu */ -#include #include -#include #include #include -#include -#include -#include #include +#include +#include #include #include #include -#include -#include -#include +#include +#include +#include +#include + +#include + #include +#include #include -#include +#include #define ULPI_VIEWPORT 0x170 -- cgit v1.2.3-59-g8ed1b From b07e5f863f4352339438ec05fda6f86c3d3a3584 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 6 Jan 2020 04:34:04 +0300 Subject: usb: phy: tegra: Use relaxed versions of readl/writel There is nothing to synchronize in regards to memory stores, thus all readl/writel occurrences in the code could be replaced with a relaxed versions, for consistency. Acked-by: Thierry Reding Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20200106013416.9604-9-digetx@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-tegra-usb.c | 195 ++++++++++++++++++++-------------------- 1 file changed, 98 insertions(+), 97 deletions(-) diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index aca1413db0ed..268b9d9ce57e 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -207,15 +207,16 @@ static void set_pts(struct tegra_usb_phy *phy, u8 pts_val) unsigned long val; if (phy->soc_config->has_hostpc) { - val = readl(base + TEGRA_USB_HOSTPC1_DEVLC); + val = readl_relaxed(base + TEGRA_USB_HOSTPC1_DEVLC); val &= ~TEGRA_USB_HOSTPC1_DEVLC_PTS(~0); val |= TEGRA_USB_HOSTPC1_DEVLC_PTS(pts_val); - writel(val, base + TEGRA_USB_HOSTPC1_DEVLC); + writel_relaxed(val, base + TEGRA_USB_HOSTPC1_DEVLC); } else { - val = readl(base + TEGRA_USB_PORTSC1) & ~TEGRA_PORTSC1_RWC_BITS; + val = readl_relaxed(base + TEGRA_USB_PORTSC1); + val &= ~TEGRA_PORTSC1_RWC_BITS; val &= ~TEGRA_USB_PORTSC1_PTS(~0); val |= TEGRA_USB_PORTSC1_PTS(pts_val); - writel(val, base + TEGRA_USB_PORTSC1); + writel_relaxed(val, base + TEGRA_USB_PORTSC1); } } @@ -225,19 +226,19 @@ static void set_phcd(struct tegra_usb_phy *phy, bool enable) unsigned long val; if (phy->soc_config->has_hostpc) { - val = readl(base + TEGRA_USB_HOSTPC1_DEVLC); + val = readl_relaxed(base + TEGRA_USB_HOSTPC1_DEVLC); if (enable) val |= TEGRA_USB_HOSTPC1_DEVLC_PHCD; else val &= ~TEGRA_USB_HOSTPC1_DEVLC_PHCD; - writel(val, base + TEGRA_USB_HOSTPC1_DEVLC); + writel_relaxed(val, base + TEGRA_USB_HOSTPC1_DEVLC); } else { - val = readl(base + TEGRA_USB_PORTSC1) & ~PORT_RWC_BITS; + val = readl_relaxed(base + TEGRA_USB_PORTSC1) & ~PORT_RWC_BITS; if (enable) val |= TEGRA_USB_PORTSC1_PHCD; else val &= ~TEGRA_USB_PORTSC1_PHCD; - writel(val, base + TEGRA_USB_PORTSC1); + writel_relaxed(val, base + TEGRA_USB_PORTSC1); } } @@ -319,7 +320,7 @@ static int utmip_pad_power_on(struct tegra_usb_phy *phy) spin_lock_irqsave(&utmip_pad_lock, flags); if (utmip_pad_count++ == 0) { - val = readl(base + UTMIP_BIAS_CFG0); + val = readl_relaxed(base + UTMIP_BIAS_CFG0); val &= ~(UTMIP_OTGPD | UTMIP_BIASPD); if (phy->soc_config->requires_extra_tuning_parameters) { @@ -331,7 +332,7 @@ static int utmip_pad_power_on(struct tegra_usb_phy *phy) val |= UTMIP_HSDISCON_LEVEL(config->hsdiscon_level); val |= UTMIP_HSDISCON_LEVEL_MSB(config->hsdiscon_level); } - writel(val, base + UTMIP_BIAS_CFG0); + writel_relaxed(val, base + UTMIP_BIAS_CFG0); } spin_unlock_irqrestore(&utmip_pad_lock, flags); @@ -359,9 +360,9 @@ static int utmip_pad_power_off(struct tegra_usb_phy *phy) spin_lock_irqsave(&utmip_pad_lock, flags); if (--utmip_pad_count == 0) { - val = readl(base + UTMIP_BIAS_CFG0); + val = readl_relaxed(base + UTMIP_BIAS_CFG0); val |= UTMIP_OTGPD | UTMIP_BIASPD; - writel(val, base + UTMIP_BIAS_CFG0); + writel_relaxed(val, base + UTMIP_BIAS_CFG0); } spin_unlock_irqrestore(&utmip_pad_lock, flags); @@ -375,8 +376,8 @@ static int utmi_wait_register(void __iomem *reg, u32 mask, u32 result) { u32 tmp; - return readl_poll_timeout(reg, tmp, (tmp & mask) == result, - 2000, 6000); + return readl_relaxed_poll_timeout(reg, tmp, (tmp & mask) == result, + 2000, 6000); } static void utmi_phy_clk_disable(struct tegra_usb_phy *phy) @@ -393,15 +394,15 @@ static void utmi_phy_clk_disable(struct tegra_usb_phy *phy) return; if (phy->is_legacy_phy) { - val = readl(base + USB_SUSP_CTRL); + val = readl_relaxed(base + USB_SUSP_CTRL); val |= USB_SUSP_SET; - writel(val, base + USB_SUSP_CTRL); + writel_relaxed(val, base + USB_SUSP_CTRL); usleep_range(10, 100); - val = readl(base + USB_SUSP_CTRL); + val = readl_relaxed(base + USB_SUSP_CTRL); val &= ~USB_SUSP_SET; - writel(val, base + USB_SUSP_CTRL); + writel_relaxed(val, base + USB_SUSP_CTRL); } else { set_phcd(phy, true); } @@ -426,15 +427,15 @@ static void utmi_phy_clk_enable(struct tegra_usb_phy *phy) return; if (phy->is_legacy_phy) { - val = readl(base + USB_SUSP_CTRL); + val = readl_relaxed(base + USB_SUSP_CTRL); val |= USB_SUSP_CLR; - writel(val, base + USB_SUSP_CTRL); + writel_relaxed(val, base + USB_SUSP_CTRL); usleep_range(10, 100); - val = readl(base + USB_SUSP_CTRL); + val = readl_relaxed(base + USB_SUSP_CTRL); val &= ~USB_SUSP_CLR; - writel(val, base + USB_SUSP_CTRL); + writel_relaxed(val, base + USB_SUSP_CTRL); } else { set_phcd(phy, false); } @@ -452,75 +453,75 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) unsigned long val; int err; - val = readl(base + USB_SUSP_CTRL); + val = readl_relaxed(base + USB_SUSP_CTRL); val |= UTMIP_RESET; - writel(val, base + USB_SUSP_CTRL); + writel_relaxed(val, base + USB_SUSP_CTRL); if (phy->is_legacy_phy) { - val = readl(base + USB1_LEGACY_CTRL); + val = readl_relaxed(base + USB1_LEGACY_CTRL); val |= USB1_NO_LEGACY_MODE; - writel(val, base + USB1_LEGACY_CTRL); + writel_relaxed(val, base + USB1_LEGACY_CTRL); } - val = readl(base + UTMIP_TX_CFG0); + val = readl_relaxed(base + UTMIP_TX_CFG0); val |= UTMIP_FS_PREABMLE_J; - writel(val, base + UTMIP_TX_CFG0); + writel_relaxed(val, base + UTMIP_TX_CFG0); - val = readl(base + UTMIP_HSRX_CFG0); + val = readl_relaxed(base + UTMIP_HSRX_CFG0); val &= ~(UTMIP_IDLE_WAIT(~0) | UTMIP_ELASTIC_LIMIT(~0)); val |= UTMIP_IDLE_WAIT(config->idle_wait_delay); val |= UTMIP_ELASTIC_LIMIT(config->elastic_limit); - writel(val, base + UTMIP_HSRX_CFG0); + writel_relaxed(val, base + UTMIP_HSRX_CFG0); - val = readl(base + UTMIP_HSRX_CFG1); + val = readl_relaxed(base + UTMIP_HSRX_CFG1); val &= ~UTMIP_HS_SYNC_START_DLY(~0); val |= UTMIP_HS_SYNC_START_DLY(config->hssync_start_delay); - writel(val, base + UTMIP_HSRX_CFG1); + writel_relaxed(val, base + UTMIP_HSRX_CFG1); - val = readl(base + UTMIP_DEBOUNCE_CFG0); + val = readl_relaxed(base + UTMIP_DEBOUNCE_CFG0); val &= ~UTMIP_BIAS_DEBOUNCE_A(~0); val |= UTMIP_BIAS_DEBOUNCE_A(phy->freq->debounce); - writel(val, base + UTMIP_DEBOUNCE_CFG0); + writel_relaxed(val, base + UTMIP_DEBOUNCE_CFG0); - val = readl(base + UTMIP_MISC_CFG0); + val = readl_relaxed(base + UTMIP_MISC_CFG0); val &= ~UTMIP_SUSPEND_EXIT_ON_EDGE; - writel(val, base + UTMIP_MISC_CFG0); + writel_relaxed(val, base + UTMIP_MISC_CFG0); if (!phy->soc_config->utmi_pll_config_in_car_module) { - val = readl(base + UTMIP_MISC_CFG1); + val = readl_relaxed(base + UTMIP_MISC_CFG1); val &= ~(UTMIP_PLL_ACTIVE_DLY_COUNT(~0) | UTMIP_PLLU_STABLE_COUNT(~0)); val |= UTMIP_PLL_ACTIVE_DLY_COUNT(phy->freq->active_delay) | UTMIP_PLLU_STABLE_COUNT(phy->freq->stable_count); - writel(val, base + UTMIP_MISC_CFG1); + writel_relaxed(val, base + UTMIP_MISC_CFG1); - val = readl(base + UTMIP_PLL_CFG1); + val = readl_relaxed(base + UTMIP_PLL_CFG1); val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) | UTMIP_PLLU_ENABLE_DLY_COUNT(~0)); val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) | UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay); - writel(val, base + UTMIP_PLL_CFG1); + writel_relaxed(val, base + UTMIP_PLL_CFG1); } if (phy->mode == USB_DR_MODE_PERIPHERAL) { - val = readl(base + USB_SUSP_CTRL); + val = readl_relaxed(base + USB_SUSP_CTRL); val &= ~(USB_WAKE_ON_CNNT_EN_DEV | USB_WAKE_ON_DISCON_EN_DEV); - writel(val, base + USB_SUSP_CTRL); + writel_relaxed(val, base + USB_SUSP_CTRL); - val = readl(base + UTMIP_BAT_CHRG_CFG0); + val = readl_relaxed(base + UTMIP_BAT_CHRG_CFG0); val &= ~UTMIP_PD_CHRG; - writel(val, base + UTMIP_BAT_CHRG_CFG0); + writel_relaxed(val, base + UTMIP_BAT_CHRG_CFG0); } else { - val = readl(base + UTMIP_BAT_CHRG_CFG0); + val = readl_relaxed(base + UTMIP_BAT_CHRG_CFG0); val |= UTMIP_PD_CHRG; - writel(val, base + UTMIP_BAT_CHRG_CFG0); + writel_relaxed(val, base + UTMIP_BAT_CHRG_CFG0); } err = utmip_pad_power_on(phy); if (err) return err; - val = readl(base + UTMIP_XCVR_CFG0); + val = readl_relaxed(base + UTMIP_XCVR_CFG0); val &= ~(UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN | UTMIP_FORCE_PDZI_POWERDOWN | UTMIP_XCVR_LSBIAS_SEL | UTMIP_XCVR_SETUP(~0) | UTMIP_XCVR_SETUP_MSB(~0) | @@ -538,57 +539,57 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) val |= UTMIP_XCVR_HSSLEW(config->xcvr_hsslew); val |= UTMIP_XCVR_HSSLEW_MSB(config->xcvr_hsslew); } - writel(val, base + UTMIP_XCVR_CFG0); + writel_relaxed(val, base + UTMIP_XCVR_CFG0); - val = readl(base + UTMIP_XCVR_CFG1); + val = readl_relaxed(base + UTMIP_XCVR_CFG1); val &= ~(UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN | UTMIP_FORCE_PDDR_POWERDOWN | UTMIP_XCVR_TERM_RANGE_ADJ(~0)); val |= UTMIP_XCVR_TERM_RANGE_ADJ(config->term_range_adj); - writel(val, base + UTMIP_XCVR_CFG1); + writel_relaxed(val, base + UTMIP_XCVR_CFG1); - val = readl(base + UTMIP_BIAS_CFG1); + val = readl_relaxed(base + UTMIP_BIAS_CFG1); val &= ~UTMIP_BIAS_PDTRK_COUNT(~0); val |= UTMIP_BIAS_PDTRK_COUNT(0x5); - writel(val, base + UTMIP_BIAS_CFG1); + writel_relaxed(val, base + UTMIP_BIAS_CFG1); - val = readl(base + UTMIP_SPARE_CFG0); + val = readl_relaxed(base + UTMIP_SPARE_CFG0); if (config->xcvr_setup_use_fuses) val |= FUSE_SETUP_SEL; else val &= ~FUSE_SETUP_SEL; - writel(val, base + UTMIP_SPARE_CFG0); + writel_relaxed(val, base + UTMIP_SPARE_CFG0); if (!phy->is_legacy_phy) { - val = readl(base + USB_SUSP_CTRL); + val = readl_relaxed(base + USB_SUSP_CTRL); val |= UTMIP_PHY_ENABLE; - writel(val, base + USB_SUSP_CTRL); + writel_relaxed(val, base + USB_SUSP_CTRL); } - val = readl(base + USB_SUSP_CTRL); + val = readl_relaxed(base + USB_SUSP_CTRL); val &= ~UTMIP_RESET; - writel(val, base + USB_SUSP_CTRL); + writel_relaxed(val, base + USB_SUSP_CTRL); if (phy->is_legacy_phy) { - val = readl(base + USB1_LEGACY_CTRL); + val = readl_relaxed(base + USB1_LEGACY_CTRL); val &= ~USB1_VBUS_SENSE_CTL_MASK; val |= USB1_VBUS_SENSE_CTL_A_SESS_VLD; - writel(val, base + USB1_LEGACY_CTRL); + writel_relaxed(val, base + USB1_LEGACY_CTRL); - val = readl(base + USB_SUSP_CTRL); + val = readl_relaxed(base + USB_SUSP_CTRL); val &= ~USB_SUSP_SET; - writel(val, base + USB_SUSP_CTRL); + writel_relaxed(val, base + USB_SUSP_CTRL); } utmi_phy_clk_enable(phy); if (phy->soc_config->requires_usbmode_setup) { - val = readl(base + USB_USBMODE); + val = readl_relaxed(base + USB_USBMODE); val &= ~USB_USBMODE_MASK; if (phy->mode == USB_DR_MODE_HOST) val |= USB_USBMODE_HOST; else val |= USB_USBMODE_DEVICE; - writel(val, base + USB_USBMODE); + writel_relaxed(val, base + USB_USBMODE); } if (!phy->is_legacy_phy) @@ -605,29 +606,29 @@ static int utmi_phy_power_off(struct tegra_usb_phy *phy) utmi_phy_clk_disable(phy); if (phy->mode == USB_DR_MODE_PERIPHERAL) { - val = readl(base + USB_SUSP_CTRL); + val = readl_relaxed(base + USB_SUSP_CTRL); val &= ~USB_WAKEUP_DEBOUNCE_COUNT(~0); val |= USB_WAKE_ON_CNNT_EN_DEV | USB_WAKEUP_DEBOUNCE_COUNT(5); - writel(val, base + USB_SUSP_CTRL); + writel_relaxed(val, base + USB_SUSP_CTRL); } - val = readl(base + USB_SUSP_CTRL); + val = readl_relaxed(base + USB_SUSP_CTRL); val |= UTMIP_RESET; - writel(val, base + USB_SUSP_CTRL); + writel_relaxed(val, base + USB_SUSP_CTRL); - val = readl(base + UTMIP_BAT_CHRG_CFG0); + val = readl_relaxed(base + UTMIP_BAT_CHRG_CFG0); val |= UTMIP_PD_CHRG; - writel(val, base + UTMIP_BAT_CHRG_CFG0); + writel_relaxed(val, base + UTMIP_BAT_CHRG_CFG0); - val = readl(base + UTMIP_XCVR_CFG0); + val = readl_relaxed(base + UTMIP_XCVR_CFG0); val |= UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN | UTMIP_FORCE_PDZI_POWERDOWN; - writel(val, base + UTMIP_XCVR_CFG0); + writel_relaxed(val, base + UTMIP_XCVR_CFG0); - val = readl(base + UTMIP_XCVR_CFG1); + val = readl_relaxed(base + UTMIP_XCVR_CFG1); val |= UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN | UTMIP_FORCE_PDDR_POWERDOWN; - writel(val, base + UTMIP_XCVR_CFG1); + writel_relaxed(val, base + UTMIP_XCVR_CFG1); return utmip_pad_power_off(phy); } @@ -637,9 +638,9 @@ static void utmi_phy_preresume(struct tegra_usb_phy *phy) void __iomem *base = phy->regs; unsigned long val; - val = readl(base + UTMIP_TX_CFG0); + val = readl_relaxed(base + UTMIP_TX_CFG0); val |= UTMIP_HS_DISCON_DISABLE; - writel(val, base + UTMIP_TX_CFG0); + writel_relaxed(val, base + UTMIP_TX_CFG0); } static void utmi_phy_postresume(struct tegra_usb_phy *phy) @@ -647,9 +648,9 @@ static void utmi_phy_postresume(struct tegra_usb_phy *phy) void __iomem *base = phy->regs; unsigned long val; - val = readl(base + UTMIP_TX_CFG0); + val = readl_relaxed(base + UTMIP_TX_CFG0); val &= ~UTMIP_HS_DISCON_DISABLE; - writel(val, base + UTMIP_TX_CFG0); + writel_relaxed(val, base + UTMIP_TX_CFG0); } static void utmi_phy_restore_start(struct tegra_usb_phy *phy, @@ -658,18 +659,18 @@ static void utmi_phy_restore_start(struct tegra_usb_phy *phy, void __iomem *base = phy->regs; unsigned long val; - val = readl(base + UTMIP_MISC_CFG0); + val = readl_relaxed(base + UTMIP_MISC_CFG0); val &= ~UTMIP_DPDM_OBSERVE_SEL(~0); if (port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) val |= UTMIP_DPDM_OBSERVE_SEL_FS_K; else val |= UTMIP_DPDM_OBSERVE_SEL_FS_J; - writel(val, base + UTMIP_MISC_CFG0); + writel_relaxed(val, base + UTMIP_MISC_CFG0); usleep_range(1, 10); - val = readl(base + UTMIP_MISC_CFG0); + val = readl_relaxed(base + UTMIP_MISC_CFG0); val |= UTMIP_DPDM_OBSERVE; - writel(val, base + UTMIP_MISC_CFG0); + writel_relaxed(val, base + UTMIP_MISC_CFG0); usleep_range(10, 100); } @@ -678,9 +679,9 @@ static void utmi_phy_restore_end(struct tegra_usb_phy *phy) void __iomem *base = phy->regs; unsigned long val; - val = readl(base + UTMIP_MISC_CFG0); + val = readl_relaxed(base + UTMIP_MISC_CFG0); val &= ~UTMIP_DPDM_OBSERVE; - writel(val, base + UTMIP_MISC_CFG0); + writel_relaxed(val, base + UTMIP_MISC_CFG0); usleep_range(10, 100); } @@ -712,31 +713,31 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy) usleep_range(1000, 2000); - val = readl(base + USB_SUSP_CTRL); + val = readl_relaxed(base + USB_SUSP_CTRL); val |= UHSIC_RESET; - writel(val, base + USB_SUSP_CTRL); + writel_relaxed(val, base + USB_SUSP_CTRL); - val = readl(base + ULPI_TIMING_CTRL_0); + val = readl_relaxed(base + ULPI_TIMING_CTRL_0); val |= ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP; - writel(val, base + ULPI_TIMING_CTRL_0); + writel_relaxed(val, base + ULPI_TIMING_CTRL_0); - val = readl(base + USB_SUSP_CTRL); + val = readl_relaxed(base + USB_SUSP_CTRL); val |= ULPI_PHY_ENABLE; - writel(val, base + USB_SUSP_CTRL); + writel_relaxed(val, base + USB_SUSP_CTRL); val = 0; - writel(val, base + ULPI_TIMING_CTRL_1); + writel_relaxed(val, base + ULPI_TIMING_CTRL_1); val |= ULPI_DATA_TRIMMER_SEL(4); val |= ULPI_STPDIRNXT_TRIMMER_SEL(4); val |= ULPI_DIR_TRIMMER_SEL(4); - writel(val, base + ULPI_TIMING_CTRL_1); + writel_relaxed(val, base + ULPI_TIMING_CTRL_1); usleep_range(10, 100); val |= ULPI_DATA_TRIMMER_LOAD; val |= ULPI_STPDIRNXT_TRIMMER_LOAD; val |= ULPI_DIR_TRIMMER_LOAD; - writel(val, base + ULPI_TIMING_CTRL_1); + writel_relaxed(val, base + ULPI_TIMING_CTRL_1); /* Fix VbusInvalid due to floating VBUS */ err = usb_phy_io_write(phy->ulpi, 0x40, 0x08); @@ -751,14 +752,14 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy) goto disable_clk; } - val = readl(base + USB_SUSP_CTRL); + val = readl_relaxed(base + USB_SUSP_CTRL); val |= USB_SUSP_CLR; - writel(val, base + USB_SUSP_CTRL); + writel_relaxed(val, base + USB_SUSP_CTRL); usleep_range(100, 1000); - val = readl(base + USB_SUSP_CTRL); + val = readl_relaxed(base + USB_SUSP_CTRL); val &= ~USB_SUSP_CLR; - writel(val, base + USB_SUSP_CTRL); + writel_relaxed(val, base + USB_SUSP_CTRL); return 0; -- cgit v1.2.3-59-g8ed1b From 9df3adca0b4a96250db5826168fc022b2923272f Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 6 Jan 2020 04:34:05 +0300 Subject: usb: phy: tegra: Use generic stub for a missing VBUS regulator Regulator core provides dummy regulator if device-tree doesn't define VBUS regulator. Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20200106013416.9604-10-digetx@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-tegra-usb.c | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 268b9d9ce57e..5b9f031619c5 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -836,9 +836,7 @@ static void tegra_usb_phy_shutdown(struct usb_phy *u_phy) if (!phy->is_ulpi_phy) utmip_pad_close(phy); - if (!IS_ERR(phy->vbus)) - regulator_disable(phy->vbus); - + regulator_disable(phy->vbus); clk_disable_unprepare(phy->pll_u); phy->freq = NULL; @@ -900,14 +898,11 @@ static int tegra_usb_phy_init(struct usb_phy *u_phy) goto fail; } - if (!IS_ERR(phy->vbus)) { - err = regulator_enable(phy->vbus); - if (err) { - dev_err(phy->u_phy.dev, - "Failed to enable USB VBUS regulator: %d\n", - err); - goto fail; - } + err = regulator_enable(phy->vbus); + if (err) { + dev_err(phy->u_phy.dev, + "Failed to enable USB VBUS regulator: %d\n", err); + goto fail; } if (phy->is_ulpi_phy) @@ -1140,14 +1135,9 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) } /* On some boards, the VBUS regulator doesn't need to be controlled */ - if (of_find_property(np, "vbus-supply", NULL)) { - tegra_phy->vbus = devm_regulator_get(&pdev->dev, "vbus"); - if (IS_ERR(tegra_phy->vbus)) - return PTR_ERR(tegra_phy->vbus); - } else { - dev_notice(&pdev->dev, "no vbus regulator"); - tegra_phy->vbus = ERR_PTR(-ENODEV); - } + tegra_phy->vbus = devm_regulator_get(&pdev->dev, "vbus"); + if (IS_ERR(tegra_phy->vbus)) + return PTR_ERR(tegra_phy->vbus); tegra_phy->pll_u = devm_clk_get(&pdev->dev, "pll_u"); err = PTR_ERR_OR_ZERO(tegra_phy->pll_u); -- cgit v1.2.3-59-g8ed1b From dea75ee6c98474c966bb12164cdebc1daddcd86b Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 6 Jan 2020 04:34:06 +0300 Subject: usb: ulpi: Add resource-managed variant of otg_ulpi_create() Now drivers (like NVIDIA Tegra USB PHY for example) will be able to benefit from the resource-managed variant, making driver's code a bit cleaner. Suggested-by: Thierry Reding Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20200106013416.9604-11-digetx@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-ulpi.c | 48 +++++++++++++++++++++++++++++++++++++--------- include/linux/usb/ulpi.h | 11 +++++++++++ 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/drivers/usb/phy/phy-ulpi.c b/drivers/usb/phy/phy-ulpi.c index a43c49369a60..e683a37e3a7a 100644 --- a/drivers/usb/phy/phy-ulpi.c +++ b/drivers/usb/phy/phy-ulpi.c @@ -240,6 +240,21 @@ static int ulpi_set_vbus(struct usb_otg *otg, bool on) return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL); } +static void otg_ulpi_init(struct usb_phy *phy, struct usb_otg *otg, + struct usb_phy_io_ops *ops, + unsigned int flags) +{ + phy->label = "ULPI"; + phy->flags = flags; + phy->io_ops = ops; + phy->otg = otg; + phy->init = ulpi_init; + + otg->usb_phy = phy; + otg->set_host = ulpi_set_host; + otg->set_vbus = ulpi_set_vbus; +} + struct usb_phy * otg_ulpi_create(struct usb_phy_io_ops *ops, unsigned int flags) @@ -257,17 +272,32 @@ otg_ulpi_create(struct usb_phy_io_ops *ops, return NULL; } - phy->label = "ULPI"; - phy->flags = flags; - phy->io_ops = ops; - phy->otg = otg; - phy->init = ulpi_init; - - otg->usb_phy = phy; - otg->set_host = ulpi_set_host; - otg->set_vbus = ulpi_set_vbus; + otg_ulpi_init(phy, otg, ops, flags); return phy; } EXPORT_SYMBOL_GPL(otg_ulpi_create); +struct usb_phy * +devm_otg_ulpi_create(struct device *dev, + struct usb_phy_io_ops *ops, + unsigned int flags) +{ + struct usb_phy *phy; + struct usb_otg *otg; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return NULL; + + otg = devm_kzalloc(dev, sizeof(*otg), GFP_KERNEL); + if (!otg) { + devm_kfree(dev, phy); + return NULL; + } + + otg_ulpi_init(phy, otg, ops, flags); + + return phy; +} +EXPORT_SYMBOL_GPL(devm_otg_ulpi_create); diff --git a/include/linux/usb/ulpi.h b/include/linux/usb/ulpi.h index c515765adab7..36c2982780ad 100644 --- a/include/linux/usb/ulpi.h +++ b/include/linux/usb/ulpi.h @@ -55,12 +55,23 @@ #if IS_ENABLED(CONFIG_USB_ULPI) struct usb_phy *otg_ulpi_create(struct usb_phy_io_ops *ops, unsigned int flags); + +struct usb_phy *devm_otg_ulpi_create(struct device *dev, + struct usb_phy_io_ops *ops, + unsigned int flags); #else static inline struct usb_phy *otg_ulpi_create(struct usb_phy_io_ops *ops, unsigned int flags) { return NULL; } + +static inline struct usb_phy *devm_otg_ulpi_create(struct device *dev, + struct usb_phy_io_ops *ops, + unsigned int flags) +{ + return NULL; +} #endif #ifdef CONFIG_USB_ULPI_VIEWPORT -- cgit v1.2.3-59-g8ed1b From 875417471e9c0c3bb311a007d99390d9f5339b81 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 6 Jan 2020 04:34:07 +0300 Subject: usb: phy: tegra: Use devm_otg_ulpi_create() The resource-managed variant removes the necessity for the driver to care about freeing ULPI resources. Suggested-by: Thierry Reding Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20200106013416.9604-12-digetx@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-tegra-usb.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 5b9f031619c5..9adbcdf8d3a1 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -1100,6 +1100,7 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) enum usb_phy_interface phy_type; struct reset_control *reset; struct resource *res; + struct usb_phy *phy; int err; tegra_phy = devm_kzalloc(&pdev->dev, sizeof(*tegra_phy), GFP_KERNEL); @@ -1200,12 +1201,14 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) return err; } - tegra_phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0); - if (!tegra_phy->ulpi) { + phy = devm_otg_ulpi_create(&pdev->dev, + &ulpi_viewport_access_ops, 0); + if (!phy) { dev_err(&pdev->dev, "Failed to create ULPI OTG\n"); return -ENOMEM; } + tegra_phy->ulpi = phy; tegra_phy->ulpi->io_priv = tegra_phy->regs + ULPI_VIEWPORT; break; @@ -1224,17 +1227,9 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) err = usb_add_phy_dev(&tegra_phy->u_phy); if (err) - goto free_ulpi; + return err; return 0; - -free_ulpi: - if (tegra_phy->ulpi) { - kfree(tegra_phy->ulpi->otg); - kfree(tegra_phy->ulpi); - } - - return err; } static int tegra_usb_phy_remove(struct platform_device *pdev) @@ -1243,11 +1238,6 @@ static int tegra_usb_phy_remove(struct platform_device *pdev) usb_remove_phy(&tegra_phy->u_phy); - if (tegra_phy->ulpi) { - kfree(tegra_phy->ulpi->otg); - kfree(tegra_phy->ulpi); - } - return 0; } -- cgit v1.2.3-59-g8ed1b From 01d6ea31db65257cc6d121b957516940a65e92fe Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 6 Jan 2020 04:34:08 +0300 Subject: usb: phy: tegra: Use u32 for hardware register variables There is a mix of u32/ULONG usage in the driver's code. Let's switch to u32 uniformly, for consistency. Suggested-by: Thierry Reding Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20200106013416.9604-13-digetx@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-tegra-usb.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 9adbcdf8d3a1..f9acbab477cf 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -204,7 +204,7 @@ static inline struct tegra_usb_phy *to_tegra_usb_phy(struct usb_phy *u_phy) static void set_pts(struct tegra_usb_phy *phy, u8 pts_val) { void __iomem *base = phy->regs; - unsigned long val; + u32 val; if (phy->soc_config->has_hostpc) { val = readl_relaxed(base + TEGRA_USB_HOSTPC1_DEVLC); @@ -223,7 +223,7 @@ static void set_pts(struct tegra_usb_phy *phy, u8 pts_val) static void set_phcd(struct tegra_usb_phy *phy, bool enable) { void __iomem *base = phy->regs; - unsigned long val; + u32 val; if (phy->soc_config->has_hostpc) { val = readl_relaxed(base + TEGRA_USB_HOSTPC1_DEVLC); @@ -310,7 +310,8 @@ static int utmip_pad_power_on(struct tegra_usb_phy *phy) { struct tegra_utmip_config *config = phy->config; void __iomem *base = phy->pad_regs; - unsigned long val, flags; + unsigned long flags; + u32 val; int err; err = clk_prepare_enable(phy->pad_clk); @@ -345,7 +346,8 @@ static int utmip_pad_power_on(struct tegra_usb_phy *phy) static int utmip_pad_power_off(struct tegra_usb_phy *phy) { void __iomem *base = phy->pad_regs; - unsigned long val, flags; + unsigned long flags; + u32 val; int err; if (!utmip_pad_count) { @@ -383,7 +385,7 @@ static int utmi_wait_register(void __iomem *reg, u32 mask, u32 result) static void utmi_phy_clk_disable(struct tegra_usb_phy *phy) { void __iomem *base = phy->regs; - unsigned long val; + u32 val; /* * The USB driver may have already initiated the phy clock @@ -415,7 +417,7 @@ static void utmi_phy_clk_disable(struct tegra_usb_phy *phy) static void utmi_phy_clk_enable(struct tegra_usb_phy *phy) { void __iomem *base = phy->regs; - unsigned long val; + u32 val; /* * The USB driver may have already initiated the phy clock @@ -450,7 +452,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) { struct tegra_utmip_config *config = phy->config; void __iomem *base = phy->regs; - unsigned long val; + u32 val; int err; val = readl_relaxed(base + USB_SUSP_CTRL); @@ -601,7 +603,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) static int utmi_phy_power_off(struct tegra_usb_phy *phy) { void __iomem *base = phy->regs; - unsigned long val; + u32 val; utmi_phy_clk_disable(phy); @@ -636,7 +638,7 @@ static int utmi_phy_power_off(struct tegra_usb_phy *phy) static void utmi_phy_preresume(struct tegra_usb_phy *phy) { void __iomem *base = phy->regs; - unsigned long val; + u32 val; val = readl_relaxed(base + UTMIP_TX_CFG0); val |= UTMIP_HS_DISCON_DISABLE; @@ -646,7 +648,7 @@ static void utmi_phy_preresume(struct tegra_usb_phy *phy) static void utmi_phy_postresume(struct tegra_usb_phy *phy) { void __iomem *base = phy->regs; - unsigned long val; + u32 val; val = readl_relaxed(base + UTMIP_TX_CFG0); val &= ~UTMIP_HS_DISCON_DISABLE; @@ -657,7 +659,7 @@ static void utmi_phy_restore_start(struct tegra_usb_phy *phy, enum tegra_usb_phy_port_speed port_speed) { void __iomem *base = phy->regs; - unsigned long val; + u32 val; val = readl_relaxed(base + UTMIP_MISC_CFG0); val &= ~UTMIP_DPDM_OBSERVE_SEL(~0); @@ -677,7 +679,7 @@ static void utmi_phy_restore_start(struct tegra_usb_phy *phy, static void utmi_phy_restore_end(struct tegra_usb_phy *phy) { void __iomem *base = phy->regs; - unsigned long val; + u32 val; val = readl_relaxed(base + UTMIP_MISC_CFG0); val &= ~UTMIP_DPDM_OBSERVE; @@ -688,7 +690,7 @@ static void utmi_phy_restore_end(struct tegra_usb_phy *phy) static int ulpi_phy_power_on(struct tegra_usb_phy *phy) { void __iomem *base = phy->regs; - unsigned long val; + u32 val; int err; err = gpio_direction_output(phy->reset_gpio, 0); -- cgit v1.2.3-59-g8ed1b From 06e60e5038fa432900ffa956307459a1aabee1db Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 6 Jan 2020 04:34:09 +0300 Subject: usb: phy: tegra: Use device-tree notion of reset-GPIO's active-state It is much more intuitive if reset is treated as asserted when GPIO value is set to 1. All NVIDIA Tegra device-trees are properly specifying active state of the reset-GPIO since 2013, let's clean up that part of the code. Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20200106013416.9604-14-digetx@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-tegra-usb.c | 70 +++++++++------------------------------ include/linux/usb/tegra_usb_phy.h | 3 +- 2 files changed, 18 insertions(+), 55 deletions(-) diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index f9acbab477cf..c431968d0433 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -693,12 +693,7 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy) u32 val; int err; - err = gpio_direction_output(phy->reset_gpio, 0); - if (err) { - dev_err(phy->u_phy.dev, "GPIO %d not set to 0: %d\n", - phy->reset_gpio, err); - return err; - } + gpiod_set_value_cansleep(phy->reset_gpio, 1); err = clk_prepare_enable(phy->clk); if (err) @@ -706,12 +701,7 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy) usleep_range(5000, 6000); - err = gpio_direction_output(phy->reset_gpio, 1); - if (err) { - dev_err(phy->u_phy.dev, "GPIO %d not set to 1: %d\n", - phy->reset_gpio, err); - goto disable_clk; - } + gpiod_set_value_cansleep(phy->reset_gpio, 0); usleep_range(1000, 2000); @@ -773,16 +763,8 @@ disable_clk: static int ulpi_phy_power_off(struct tegra_usb_phy *phy) { - int err; - - err = gpio_direction_output(phy->reset_gpio, 0); - if (err) { - dev_err(phy->u_phy.dev, "reset GPIO not asserted: %d\n", err); - return err; - } - + gpiod_set_value_cansleep(phy->reset_gpio, 1); usleep_range(5000, 6000); - clk_disable_unprepare(phy->clk); return 0; @@ -857,21 +839,6 @@ static int tegra_usb_phy_set_suspend(struct usb_phy *u_phy, int suspend) return tegra_usb_phy_power_on(phy); } -static int ulpi_open(struct tegra_usb_phy *phy) -{ - int err; - - err = gpio_direction_output(phy->reset_gpio, 0); - if (err) { - dev_err(phy->u_phy.dev, - "ULPI reset GPIO %d direction not asserted: %d\n", - phy->reset_gpio, err); - return err; - } - - return 0; -} - static int tegra_usb_phy_init(struct usb_phy *u_phy) { struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy); @@ -907,12 +874,11 @@ static int tegra_usb_phy_init(struct usb_phy *u_phy) goto fail; } - if (phy->is_ulpi_phy) - err = ulpi_open(phy); - else + if (!phy->is_ulpi_phy) { err = utmip_pad_open(phy); - if (err) - goto fail; + if (err) + goto fail; + } err = tegra_usb_phy_power_on(phy); if (err) @@ -1101,6 +1067,7 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) struct tegra_usb_phy *tegra_phy; enum usb_phy_interface phy_type; struct reset_control *reset; + struct gpio_desc *gpiod; struct resource *res; struct usb_phy *phy; int err; @@ -1178,15 +1145,6 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) case USBPHY_INTERFACE_MODE_ULPI: tegra_phy->is_ulpi_phy = true; - tegra_phy->reset_gpio = - of_get_named_gpio(np, "nvidia,phy-reset-gpio", 0); - - if (!gpio_is_valid(tegra_phy->reset_gpio)) { - dev_err(&pdev->dev, - "Invalid GPIO: %d\n", tegra_phy->reset_gpio); - return tegra_phy->reset_gpio; - } - tegra_phy->clk = devm_clk_get(&pdev->dev, "ulpi-link"); err = PTR_ERR_OR_ZERO(tegra_phy->clk); if (err) { @@ -1195,13 +1153,17 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) return err; } - err = devm_gpio_request(&pdev->dev, tegra_phy->reset_gpio, - "ulpi_phy_reset_b"); + gpiod = devm_gpiod_get_from_of_node(&pdev->dev, np, + "nvidia,phy-reset-gpio", + 0, GPIOD_OUT_HIGH, + "ulpi_phy_reset_b"); + err = PTR_ERR_OR_ZERO(gpiod); if (err) { - dev_err(&pdev->dev, "Request failed for GPIO %d: %d\n", - tegra_phy->reset_gpio, err); + dev_err(&pdev->dev, + "Request failed for reset GPIO: %d\n", err); return err; } + tegra_phy->reset_gpio = gpiod; phy = devm_otg_ulpi_create(&pdev->dev, &ulpi_viewport_access_ops, 0); diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index 3ae73bdc6245..c29d1b4c9381 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -17,6 +17,7 @@ #define __TEGRA_USB_PHY_H #include +#include #include #include @@ -76,7 +77,7 @@ struct tegra_usb_phy { struct usb_phy u_phy; bool is_legacy_phy; bool is_ulpi_phy; - int reset_gpio; + struct gpio_desc *reset_gpio; struct reset_control *pad_rst; bool powered_on; }; -- cgit v1.2.3-59-g8ed1b From aecc5af3ec1d6c5fb3d6c3e73276a7e53777166a Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 6 Jan 2020 04:34:10 +0300 Subject: usb: phy: tegra: Disable VBUS regulator on tegra_usb_phy_init failure VBUS regulator should be turned off in a case of error. Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20200106013416.9604-15-digetx@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-tegra-usb.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index c431968d0433..90b42e963a1e 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -864,20 +864,20 @@ static int tegra_usb_phy_init(struct usb_phy *u_phy) dev_err(phy->u_phy.dev, "Invalid pll_u parent rate %ld\n", parent_rate); err = -EINVAL; - goto fail; + goto disable_clk; } err = regulator_enable(phy->vbus); if (err) { dev_err(phy->u_phy.dev, "Failed to enable USB VBUS regulator: %d\n", err); - goto fail; + goto disable_clk; } if (!phy->is_ulpi_phy) { err = utmip_pad_open(phy); if (err) - goto fail; + goto disable_vbus; } err = tegra_usb_phy_power_on(phy); @@ -889,7 +889,11 @@ static int tegra_usb_phy_init(struct usb_phy *u_phy) close_phy: if (!phy->is_ulpi_phy) utmip_pad_close(phy); -fail: + +disable_vbus: + regulator_disable(phy->vbus); + +disable_clk: clk_disable_unprepare(phy->pll_u); phy->freq = NULL; -- cgit v1.2.3-59-g8ed1b From 92bd2ef26c5d804a21ada9d4e2d353e3e2e215ab Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 6 Jan 2020 04:34:11 +0300 Subject: usb: phy: tegra: Move utmip_pad_count checking under lock It's unlikely that two drivers could manage PHY's state simultaneously in practice, nevertheless the utmip_pad_count checking should be under lock, for consistency. Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20200106013416.9604-16-digetx@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-tegra-usb.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 90b42e963a1e..1b9667b0aa11 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -348,30 +348,31 @@ static int utmip_pad_power_off(struct tegra_usb_phy *phy) void __iomem *base = phy->pad_regs; unsigned long flags; u32 val; - int err; + int ret; + + ret = clk_prepare_enable(phy->pad_clk); + if (ret) + return ret; + + spin_lock_irqsave(&utmip_pad_lock, flags); if (!utmip_pad_count) { dev_err(phy->u_phy.dev, "UTMIP pad already powered off\n"); - return -EINVAL; + ret = -EINVAL; + goto ulock; } - err = clk_prepare_enable(phy->pad_clk); - if (err) - return err; - - spin_lock_irqsave(&utmip_pad_lock, flags); - if (--utmip_pad_count == 0) { val = readl_relaxed(base + UTMIP_BIAS_CFG0); val |= UTMIP_OTGPD | UTMIP_BIASPD; writel_relaxed(val, base + UTMIP_BIAS_CFG0); } - +ulock: spin_unlock_irqrestore(&utmip_pad_lock, flags); clk_disable_unprepare(phy->pad_clk); - return 0; + return ret; } static int utmi_wait_register(void __iomem *reg, u32 mask, u32 result) -- cgit v1.2.3-59-g8ed1b From f1f0c7516708370de234ef1490da1298168e32ac Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 6 Jan 2020 04:34:12 +0300 Subject: usb: phy: tegra: Keep CPU interrupts enabled There is no good reason for disabling of CPU interrupts in order to protect the utmip_pad_count modification. Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20200106013416.9604-17-digetx@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-tegra-usb.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 1b9667b0aa11..037e8eee737d 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -310,7 +310,6 @@ static int utmip_pad_power_on(struct tegra_usb_phy *phy) { struct tegra_utmip_config *config = phy->config; void __iomem *base = phy->pad_regs; - unsigned long flags; u32 val; int err; @@ -318,7 +317,7 @@ static int utmip_pad_power_on(struct tegra_usb_phy *phy) if (err) return err; - spin_lock_irqsave(&utmip_pad_lock, flags); + spin_lock(&utmip_pad_lock); if (utmip_pad_count++ == 0) { val = readl_relaxed(base + UTMIP_BIAS_CFG0); @@ -336,7 +335,7 @@ static int utmip_pad_power_on(struct tegra_usb_phy *phy) writel_relaxed(val, base + UTMIP_BIAS_CFG0); } - spin_unlock_irqrestore(&utmip_pad_lock, flags); + spin_unlock(&utmip_pad_lock); clk_disable_unprepare(phy->pad_clk); @@ -346,7 +345,6 @@ static int utmip_pad_power_on(struct tegra_usb_phy *phy) static int utmip_pad_power_off(struct tegra_usb_phy *phy) { void __iomem *base = phy->pad_regs; - unsigned long flags; u32 val; int ret; @@ -354,7 +352,7 @@ static int utmip_pad_power_off(struct tegra_usb_phy *phy) if (ret) return ret; - spin_lock_irqsave(&utmip_pad_lock, flags); + spin_lock(&utmip_pad_lock); if (!utmip_pad_count) { dev_err(phy->u_phy.dev, "UTMIP pad already powered off\n"); @@ -368,7 +366,7 @@ static int utmip_pad_power_off(struct tegra_usb_phy *phy) writel_relaxed(val, base + UTMIP_BIAS_CFG0); } ulock: - spin_unlock_irqrestore(&utmip_pad_lock, flags); + spin_unlock(&utmip_pad_lock); clk_disable_unprepare(phy->pad_clk); -- cgit v1.2.3-59-g8ed1b From 7ac85f4a644435532229eda63c4a08d96b362057 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 6 Jan 2020 04:34:13 +0300 Subject: usb: chipidea: tegra: Stop managing PHY's power Tegra's USB PHY driver now provides generic PHY init/shutdown callbacks and thus the custom PHY management could be removed from Tegra-specific part of the ChipIdea driver. Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20200106013416.9604-18-digetx@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci_hdrc_tegra.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c index 0c9911d44ee5..7455df0ede49 100644 --- a/drivers/usb/chipidea/ci_hdrc_tegra.c +++ b/drivers/usb/chipidea/ci_hdrc_tegra.c @@ -83,13 +83,6 @@ static int tegra_udc_probe(struct platform_device *pdev) return err; } - /* - * Tegra's USB PHY driver doesn't implement optional phy_init() - * hook, so we have to power on UDC controller before ChipIdea - * driver initialization kicks in. - */ - usb_phy_set_suspend(udc->phy, 0); - /* setup and register ChipIdea HDRC device */ udc->data.name = "tegra-udc"; udc->data.flags = soc->flags; @@ -109,7 +102,6 @@ static int tegra_udc_probe(struct platform_device *pdev) return 0; fail_power_off: - usb_phy_set_suspend(udc->phy, 1); clk_disable_unprepare(udc->clk); return err; } @@ -119,7 +111,6 @@ static int tegra_udc_remove(struct platform_device *pdev) struct tegra_udc *udc = platform_get_drvdata(pdev); ci_hdrc_remove_device(udc->dev); - usb_phy_set_suspend(udc->phy, 1); clk_disable_unprepare(udc->clk); return 0; -- cgit v1.2.3-59-g8ed1b From 7d999a7d096bb60e18ee19e107b360ade6419cc6 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 6 Jan 2020 04:34:14 +0300 Subject: usb: chipidea: tegra: Add USB_TEGRA_PHY to driver's dependencies Add build dependency on USB_TEGRA_PHY since UDC driver isn't usable without the PHY. Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20200106013416.9604-19-digetx@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig index ae850b3fddf2..d53db520e209 100644 --- a/drivers/usb/chipidea/Kconfig +++ b/drivers/usb/chipidea/Kconfig @@ -7,6 +7,7 @@ config USB_CHIPIDEA select RESET_CONTROLLER select USB_ULPI_BUS select USB_ROLE_SWITCH + select USB_TEGRA_PHY if ARCH_TEGRA help Say Y here if your system has a dual role high speed USB controller based on ChipIdea silicon IP. It supports: -- cgit v1.2.3-59-g8ed1b From 32806e7cb0238207ffa9f5306c474fd0e8a3c9de Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 6 Jan 2020 04:34:15 +0300 Subject: usb: host: ehci-tegra: Stop managing PHY's power There is no need to use usb_phy_set_suspend during of driver's probe because now PHY driver enables hardware during of PHY's initialization. Signed-off-by: Dmitry Osipenko Acked-by: Alan Stern Link: https://lore.kernel.org/r/20200106013416.9604-20-digetx@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-tegra.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 32483bef046b..1eb94205a5ac 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -480,12 +480,6 @@ static int tegra_ehci_probe(struct platform_device *pdev) } u_phy->otg->host = hcd_to_bus(hcd); - err = usb_phy_set_suspend(hcd->usb_phy, 0); - if (err) { - dev_err(&pdev->dev, "Failed to power on the phy\n"); - goto cleanup_phy; - } - irq = platform_get_irq(pdev, 0); if (!irq) { dev_err(&pdev->dev, "Failed to get IRQ\n"); -- cgit v1.2.3-59-g8ed1b From bc57ecbd72fca37557677a30e717e95abe075ae8 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 6 Jan 2020 04:34:16 +0300 Subject: usb: host: ehci-tegra: Remove unused fields from tegra_ehci_hcd There are few stale fields in tegra_ehci_hcd structure, let's remove them. Signed-off-by: Dmitry Osipenko Acked-by: Alan Stern Link: https://lore.kernel.org/r/20200106013416.9604-21-digetx@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-tegra.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 1eb94205a5ac..d6433f206c17 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -42,12 +42,10 @@ struct tegra_ehci_soc_config { }; struct tegra_ehci_hcd { - struct tegra_usb_phy *phy; struct clk *clk; struct reset_control *rst; int port_resuming; bool needs_double_reset; - enum tegra_usb_phy_port_speed port_speed; }; static int tegra_reset_usb_controller(struct platform_device *pdev) -- cgit v1.2.3-59-g8ed1b From 585c91f40d201bc564d4e76b83c05b3b5363fe7e Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Wed, 8 Jan 2020 18:24:16 -0700 Subject: usbip: Fix unsafe unaligned pointer usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix unsafe unaligned pointer usage in usbip network interfaces. usbip tool build fails with new gcc -Werror=address-of-packed-member checks. usbip_network.c: In function ‘usbip_net_pack_usb_device’: usbip_network.c:79:32: error: taking address of packed member of ‘struct usbip_usb_device’ may result in an unaligned pointer value [-Werror=address-of-packed-member] 79 | usbip_net_pack_uint32_t(pack, &udev->busnum); Fix with minor changes to pass by value instead of by address. Signed-off-by: Shuah Khan Link: https://lore.kernel.org/r/20200109012416.2875-1-skhan@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- tools/usb/usbip/src/usbip_network.c | 40 ++++++++++++++++++++++--------------- tools/usb/usbip/src/usbip_network.h | 12 +++-------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/tools/usb/usbip/src/usbip_network.c b/tools/usb/usbip/src/usbip_network.c index d595d72693fb..ed4dc8c14269 100644 --- a/tools/usb/usbip/src/usbip_network.c +++ b/tools/usb/usbip/src/usbip_network.c @@ -50,39 +50,39 @@ void usbip_setup_port_number(char *arg) info("using port %d (\"%s\")", usbip_port, usbip_port_string); } -void usbip_net_pack_uint32_t(int pack, uint32_t *num) +uint32_t usbip_net_pack_uint32_t(int pack, uint32_t num) { uint32_t i; if (pack) - i = htonl(*num); + i = htonl(num); else - i = ntohl(*num); + i = ntohl(num); - *num = i; + return i; } -void usbip_net_pack_uint16_t(int pack, uint16_t *num) +uint16_t usbip_net_pack_uint16_t(int pack, uint16_t num) { uint16_t i; if (pack) - i = htons(*num); + i = htons(num); else - i = ntohs(*num); + i = ntohs(num); - *num = i; + return i; } void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev) { - usbip_net_pack_uint32_t(pack, &udev->busnum); - usbip_net_pack_uint32_t(pack, &udev->devnum); - usbip_net_pack_uint32_t(pack, &udev->speed); + udev->busnum = usbip_net_pack_uint32_t(pack, udev->busnum); + udev->devnum = usbip_net_pack_uint32_t(pack, udev->devnum); + udev->speed = usbip_net_pack_uint32_t(pack, udev->speed); - usbip_net_pack_uint16_t(pack, &udev->idVendor); - usbip_net_pack_uint16_t(pack, &udev->idProduct); - usbip_net_pack_uint16_t(pack, &udev->bcdDevice); + udev->idVendor = usbip_net_pack_uint16_t(pack, udev->idVendor); + udev->idProduct = usbip_net_pack_uint16_t(pack, udev->idProduct); + udev->bcdDevice = usbip_net_pack_uint16_t(pack, udev->bcdDevice); } void usbip_net_pack_usb_interface(int pack __attribute__((unused)), @@ -129,6 +129,14 @@ ssize_t usbip_net_send(int sockfd, void *buff, size_t bufflen) return usbip_net_xmit(sockfd, buff, bufflen, 1); } +static inline void usbip_net_pack_op_common(int pack, + struct op_common *op_common) +{ + op_common->version = usbip_net_pack_uint16_t(pack, op_common->version); + op_common->code = usbip_net_pack_uint16_t(pack, op_common->code); + op_common->status = usbip_net_pack_uint32_t(pack, op_common->status); +} + int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status) { struct op_common op_common; @@ -140,7 +148,7 @@ int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status) op_common.code = code; op_common.status = status; - PACK_OP_COMMON(1, &op_common); + usbip_net_pack_op_common(1, &op_common); rc = usbip_net_send(sockfd, &op_common, sizeof(op_common)); if (rc < 0) { @@ -164,7 +172,7 @@ int usbip_net_recv_op_common(int sockfd, uint16_t *code, int *status) goto err; } - PACK_OP_COMMON(0, &op_common); + usbip_net_pack_op_common(0, &op_common); if (op_common.version != USBIP_VERSION) { err("USBIP Kernel and tool version mismatch: %d %d:", diff --git a/tools/usb/usbip/src/usbip_network.h b/tools/usb/usbip/src/usbip_network.h index 555215eae43e..83b4c5344f72 100644 --- a/tools/usb/usbip/src/usbip_network.h +++ b/tools/usb/usbip/src/usbip_network.h @@ -32,12 +32,6 @@ struct op_common { } __attribute__((packed)); -#define PACK_OP_COMMON(pack, op_common) do {\ - usbip_net_pack_uint16_t(pack, &(op_common)->version);\ - usbip_net_pack_uint16_t(pack, &(op_common)->code);\ - usbip_net_pack_uint32_t(pack, &(op_common)->status);\ -} while (0) - /* ---------------------------------------------------------------------- */ /* Dummy Code */ #define OP_UNSPEC 0x00 @@ -163,11 +157,11 @@ struct op_devlist_reply_extra { } while (0) #define PACK_OP_DEVLIST_REPLY(pack, reply) do {\ - usbip_net_pack_uint32_t(pack, &(reply)->ndev);\ + (reply)->ndev = usbip_net_pack_uint32_t(pack, (reply)->ndev);\ } while (0) -void usbip_net_pack_uint32_t(int pack, uint32_t *num); -void usbip_net_pack_uint16_t(int pack, uint16_t *num); +uint32_t usbip_net_pack_uint32_t(int pack, uint32_t num); +uint16_t usbip_net_pack_uint16_t(int pack, uint16_t num); void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev); void usbip_net_pack_usb_interface(int pack, struct usbip_usb_interface *uinf); -- cgit v1.2.3-59-g8ed1b From ad044f01c2cc482f9fc5670a1c448638650b0aac Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 2 Jan 2020 15:26:31 +0530 Subject: dt-bindings: phy: Document WIZ (SERDES wrapper) bindings Add DT binding documentation for WIZ (SERDES wrapper). WIZ is *NOT* a PHY but a wrapper used to configure some of the input signals to the SERDES. It is used with both Sierra(16G) and Torrent(10G) serdes. Signed-off-by: Kishon Vijay Abraham I [jsarha@ti.com: Add separate compatible for Sierra(16G) and Torrent(10G) SERDES] Signed-off-by: Jyri Sarha Reviewed-by: Rob Herring --- .../devicetree/bindings/phy/ti,phy-j721e-wiz.yaml | 204 +++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml diff --git a/Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml b/Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml new file mode 100644 index 000000000000..ebc8f403b4bf --- /dev/null +++ b/Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml @@ -0,0 +1,204 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/phy/ti,phy-j721e-wiz.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: TI J721E WIZ (SERDES Wrapper) + +maintainers: + - Kishon Vijay Abraham I + +properties: + compatible: + enum: + - ti,j721e-wiz-16g + - ti,j721e-wiz-10g + + power-domains: + maxItems: 1 + + clocks: + maxItems: 3 + description: clock-specifier to represent input to the WIZ + + clock-names: + items: + - const: fck + - const: core_ref_clk + - const: ext_ref_clk + + num-lanes: + minimum: 1 + maximum: 4 + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + + "#reset-cells": + const: 1 + + ranges: true + + assigned-clocks: + maxItems: 2 + + assigned-clock-parents: + maxItems: 2 + +patternProperties: + "^pll[0|1]-refclk$": + type: object + description: | + WIZ node should have subnodes for each of the PLLs present in + the SERDES. + properties: + clocks: + maxItems: 2 + description: Phandle to clock nodes representing the two inputs to PLL. + + "#clock-cells": + const: 0 + + assigned-clocks: + maxItems: 1 + + assigned-clock-parents: + maxItems: 1 + + required: + - clocks + - "#clock-cells" + - assigned-clocks + - assigned-clock-parents + + "^cmn-refclk1?-dig-div$": + type: object + description: + WIZ node should have subnodes for each of the PMA common refclock + provided by the SERDES. + properties: + clocks: + maxItems: 1 + description: Phandle to the clock node representing the input to the + divider clock. + + "#clock-cells": + const: 0 + + required: + - clocks + - "#clock-cells" + + "^refclk-dig$": + type: object + description: | + WIZ node should have subnode for refclk_dig to select the reference + clock source for the reference clock used in the PHY and PMA digital + logic. + properties: + clocks: + maxItems: 4 + description: Phandle to four clock nodes representing the inputs to + refclk_dig + + "#clock-cells": + const: 0 + + assigned-clocks: + maxItems: 1 + + assigned-clock-parents: + maxItems: 1 + + required: + - clocks + - "#clock-cells" + - assigned-clocks + - assigned-clock-parents + + "^serdes@[0-9a-f]+$": + type: object + description: | + WIZ node should have '1' subnode for the SERDES. It could be either + Sierra SERDES or Torrent SERDES. Sierra SERDES should follow the + bindings specified in + Documentation/devicetree/bindings/phy/phy-cadence-sierra.txt + Torrent SERDES should follow the bindings specified in + Documentation/devicetree/bindings/phy/phy-cadence-dp.txt + +required: + - compatible + - power-domains + - clocks + - clock-names + - num-lanes + - "#address-cells" + - "#size-cells" + - "#reset-cells" + - ranges + +examples: + - | + #include + + wiz@5000000 { + compatible = "ti,j721e-wiz-16g"; + #address-cells = <1>; + #size-cells = <1>; + power-domains = <&k3_pds 292 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 292 5>, <&k3_clks 292 11>, <&dummy_cmn_refclk>; + clock-names = "fck", "core_ref_clk", "ext_ref_clk"; + assigned-clocks = <&k3_clks 292 11>, <&k3_clks 292 0>; + assigned-clock-parents = <&k3_clks 292 15>, <&k3_clks 292 4>; + num-lanes = <2>; + #reset-cells = <1>; + ranges = <0x5000000 0x5000000 0x10000>; + + pll0-refclk { + clocks = <&k3_clks 293 13>, <&dummy_cmn_refclk>; + #clock-cells = <0>; + assigned-clocks = <&wiz1_pll0_refclk>; + assigned-clock-parents = <&k3_clks 293 13>; + }; + + pll1-refclk { + clocks = <&k3_clks 293 0>, <&dummy_cmn_refclk1>; + #clock-cells = <0>; + assigned-clocks = <&wiz1_pll1_refclk>; + assigned-clock-parents = <&k3_clks 293 0>; + }; + + cmn-refclk-dig-div { + clocks = <&wiz1_refclk_dig>; + #clock-cells = <0>; + }; + + cmn-refclk1-dig-div { + clocks = <&wiz1_pll1_refclk>; + #clock-cells = <0>; + }; + + refclk-dig { + clocks = <&k3_clks 292 11>, <&k3_clks 292 0>, <&dummy_cmn_refclk>, <&dummy_cmn_refclk1>; + #clock-cells = <0>; + assigned-clocks = <&wiz0_refclk_dig>; + assigned-clock-parents = <&k3_clks 292 11>; + }; + + serdes@5000000 { + compatible = "cdns,ti,sierra-phy-t0"; + reg-names = "serdes"; + reg = <0x5000000 0x10000>; + #address-cells = <1>; + #size-cells = <0>; + resets = <&serdes_wiz0 0>; + reset-names = "sierra_reset"; + clocks = <&wiz0_cmn_refclk_dig_div>, <&wiz0_cmn_refclk1_dig_div>; + clock-names = "cmn_refclk_dig_div", "cmn_refclk1_dig_div"; + }; + }; -- cgit v1.2.3-59-g8ed1b From 091876cc355d6739e393efa4b3d07f451a6a035c Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 16 Dec 2019 15:27:12 +0530 Subject: phy: ti: j721e-wiz: Add support for WIZ module present in TI J721E SoC Add support for WIZ module present in TI's J721E SoC. WIZ is a SERDES wrapper used to configure some of the input signals to the SERDES. It is used with both Sierra(16G) and Torrent(10G) SERDES. This driver configures three clock selects (pll0, pll1, dig), two divider clocks and supports resets for each of the lanes. [jsarha@ti.com: Add support for Torrent(10G) SERDES wrapper] Signed-off-by: Jyri Sarha Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/ti/Kconfig | 15 + drivers/phy/ti/Makefile | 1 + drivers/phy/ti/phy-j721e-wiz.c | 898 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 914 insertions(+) create mode 100644 drivers/phy/ti/phy-j721e-wiz.c diff --git a/drivers/phy/ti/Kconfig b/drivers/phy/ti/Kconfig index 174888609779..50f6b829cad0 100644 --- a/drivers/phy/ti/Kconfig +++ b/drivers/phy/ti/Kconfig @@ -33,6 +33,21 @@ config PHY_AM654_SERDES This option enables support for TI AM654 SerDes PHY used for PCIe. +config PHY_J721E_WIZ + tristate "TI J721E WIZ (SERDES Wrapper) support" + depends on OF && ARCH_K3 || COMPILE_TEST + depends on COMMON_CLK + select GENERIC_PHY + select MULTIPLEXER + select REGMAP_MMIO + select MUX_MMIO + help + This option enables support for WIZ module present in TI's J721E + SoC. WIZ is a serdes wrapper used to configure some of the input + signals to the SERDES (Sierra/Torrent). This driver configures + three clock selects (pll0, pll1, dig) and resets for each of the + lanes. + config OMAP_CONTROL_PHY tristate "OMAP CONTROL PHY Driver" depends on ARCH_OMAP2PLUS || COMPILE_TEST diff --git a/drivers/phy/ti/Makefile b/drivers/phy/ti/Makefile index bff901eb0ecc..dcba2571c9bd 100644 --- a/drivers/phy/ti/Makefile +++ b/drivers/phy/ti/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o obj-$(CONFIG_PHY_AM654_SERDES) += phy-am654-serdes.o obj-$(CONFIG_PHY_TI_GMII_SEL) += phy-gmii-sel.o +obj-$(CONFIG_PHY_J721E_WIZ) += phy-j721e-wiz.o diff --git a/drivers/phy/ti/phy-j721e-wiz.c b/drivers/phy/ti/phy-j721e-wiz.c new file mode 100644 index 000000000000..b86ebdd68302 --- /dev/null +++ b/drivers/phy/ti/phy-j721e-wiz.c @@ -0,0 +1,898 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * Wrapper driver for SERDES used in J721E + * + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Kishon Vijay Abraham I + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WIZ_SERDES_CTRL 0x404 +#define WIZ_SERDES_TOP_CTRL 0x408 +#define WIZ_SERDES_RST 0x40c +#define WIZ_LANECTL(n) (0x480 + (0x40 * (n))) + +#define WIZ_MAX_LANES 4 +#define WIZ_MUX_NUM_CLOCKS 3 +#define WIZ_DIV_NUM_CLOCKS_16G 2 +#define WIZ_DIV_NUM_CLOCKS_10G 1 + +enum wiz_lane_standard_mode { + LANE_MODE_GEN1, + LANE_MODE_GEN2, + LANE_MODE_GEN3, + LANE_MODE_GEN4, +}; + +enum wiz_refclk_mux_sel { + PLL0_REFCLK, + PLL1_REFCLK, + REFCLK_DIG, +}; + +enum wiz_refclk_div_sel { + CMN_REFCLK_DIG_DIV, + CMN_REFCLK1_DIG_DIV, +}; + +static const struct reg_field por_en = REG_FIELD(WIZ_SERDES_CTRL, 31, 31); +static const struct reg_field phy_reset_n = REG_FIELD(WIZ_SERDES_RST, 31, 31); +static const struct reg_field pll1_refclk_mux_sel = + REG_FIELD(WIZ_SERDES_RST, 29, 29); +static const struct reg_field pll0_refclk_mux_sel = + REG_FIELD(WIZ_SERDES_RST, 28, 28); +static const struct reg_field refclk_dig_sel_16g = + REG_FIELD(WIZ_SERDES_RST, 24, 25); +static const struct reg_field refclk_dig_sel_10g = + REG_FIELD(WIZ_SERDES_RST, 24, 24); +static const struct reg_field pma_cmn_refclk_int_mode = + REG_FIELD(WIZ_SERDES_TOP_CTRL, 28, 29); +static const struct reg_field pma_cmn_refclk_mode = + REG_FIELD(WIZ_SERDES_TOP_CTRL, 30, 31); +static const struct reg_field pma_cmn_refclk_dig_div = + REG_FIELD(WIZ_SERDES_TOP_CTRL, 26, 27); +static const struct reg_field pma_cmn_refclk1_dig_div = + REG_FIELD(WIZ_SERDES_TOP_CTRL, 24, 25); + +static const struct reg_field p_enable[WIZ_MAX_LANES] = { + REG_FIELD(WIZ_LANECTL(0), 30, 31), + REG_FIELD(WIZ_LANECTL(1), 30, 31), + REG_FIELD(WIZ_LANECTL(2), 30, 31), + REG_FIELD(WIZ_LANECTL(3), 30, 31), +}; + +static const struct reg_field p_align[WIZ_MAX_LANES] = { + REG_FIELD(WIZ_LANECTL(0), 29, 29), + REG_FIELD(WIZ_LANECTL(1), 29, 29), + REG_FIELD(WIZ_LANECTL(2), 29, 29), + REG_FIELD(WIZ_LANECTL(3), 29, 29), +}; + +static const struct reg_field p_raw_auto_start[WIZ_MAX_LANES] = { + REG_FIELD(WIZ_LANECTL(0), 28, 28), + REG_FIELD(WIZ_LANECTL(1), 28, 28), + REG_FIELD(WIZ_LANECTL(2), 28, 28), + REG_FIELD(WIZ_LANECTL(3), 28, 28), +}; + +static const struct reg_field p_standard_mode[WIZ_MAX_LANES] = { + REG_FIELD(WIZ_LANECTL(0), 24, 25), + REG_FIELD(WIZ_LANECTL(1), 24, 25), + REG_FIELD(WIZ_LANECTL(2), 24, 25), + REG_FIELD(WIZ_LANECTL(3), 24, 25), +}; + +struct wiz_clk_mux { + struct clk_hw hw; + struct regmap_field *field; + u32 *table; + struct clk_init_data clk_data; +}; + +#define to_wiz_clk_mux(_hw) container_of(_hw, struct wiz_clk_mux, hw) + +struct wiz_clk_divider { + struct clk_hw hw; + struct regmap_field *field; + struct clk_div_table *table; + struct clk_init_data clk_data; +}; + +#define to_wiz_clk_div(_hw) container_of(_hw, struct wiz_clk_divider, hw) + +struct wiz_clk_mux_sel { + struct regmap_field *field; + u32 table[4]; + const char *node_name; +}; + +struct wiz_clk_div_sel { + struct regmap_field *field; + struct clk_div_table *table; + const char *node_name; +}; + +static struct wiz_clk_mux_sel clk_mux_sel_16g[] = { + { + /* + * Mux value to be configured for each of the input clocks + * in the order populated in device tree + */ + .table = { 1, 0 }, + .node_name = "pll0-refclk", + }, + { + .table = { 1, 0 }, + .node_name = "pll1-refclk", + }, + { + .table = { 1, 3, 0, 2 }, + .node_name = "refclk-dig", + }, +}; + +static struct wiz_clk_mux_sel clk_mux_sel_10g[] = { + { + /* + * Mux value to be configured for each of the input clocks + * in the order populated in device tree + */ + .table = { 1, 0 }, + .node_name = "pll0-refclk", + }, + { + .table = { 1, 0 }, + .node_name = "pll1-refclk", + }, + { + .table = { 1, 0 }, + .node_name = "refclk-dig", + }, +}; + +static struct clk_div_table clk_div_table[] = { + { .val = 0, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 2, .div = 4, }, + { .val = 3, .div = 8, }, +}; + +static struct wiz_clk_div_sel clk_div_sel[] = { + { + .table = clk_div_table, + .node_name = "cmn-refclk-dig-div", + }, + { + .table = clk_div_table, + .node_name = "cmn-refclk1-dig-div", + }, +}; + +enum wiz_type { + J721E_WIZ_16G, + J721E_WIZ_10G, +}; + +struct wiz { + struct regmap *regmap; + enum wiz_type type; + struct wiz_clk_mux_sel *clk_mux_sel; + struct wiz_clk_div_sel *clk_div_sel; + unsigned int clk_div_sel_num; + struct regmap_field *por_en; + struct regmap_field *phy_reset_n; + struct regmap_field *p_enable[WIZ_MAX_LANES]; + struct regmap_field *p_align[WIZ_MAX_LANES]; + struct regmap_field *p_raw_auto_start[WIZ_MAX_LANES]; + struct regmap_field *p_standard_mode[WIZ_MAX_LANES]; + struct regmap_field *pma_cmn_refclk_int_mode; + struct regmap_field *pma_cmn_refclk_mode; + struct regmap_field *pma_cmn_refclk_dig_div; + struct regmap_field *pma_cmn_refclk1_dig_div; + + struct device *dev; + u32 num_lanes; + struct platform_device *serdes_pdev; + struct reset_controller_dev wiz_phy_reset_dev; +}; + +static int wiz_reset(struct wiz *wiz) +{ + int ret; + + ret = regmap_field_write(wiz->por_en, 0x1); + if (ret) + return ret; + + mdelay(1); + + ret = regmap_field_write(wiz->por_en, 0x0); + if (ret) + return ret; + + return 0; +} + +static int wiz_mode_select(struct wiz *wiz) +{ + u32 num_lanes = wiz->num_lanes; + int ret; + int i; + + for (i = 0; i < num_lanes; i++) { + ret = regmap_field_write(wiz->p_standard_mode[i], + LANE_MODE_GEN4); + if (ret) + return ret; + } + + return 0; +} + +static int wiz_init_raw_interface(struct wiz *wiz, bool enable) +{ + u32 num_lanes = wiz->num_lanes; + int i; + int ret; + + for (i = 0; i < num_lanes; i++) { + ret = regmap_field_write(wiz->p_align[i], enable); + if (ret) + return ret; + + ret = regmap_field_write(wiz->p_raw_auto_start[i], enable); + if (ret) + return ret; + } + + return 0; +} + +static int wiz_init(struct wiz *wiz) +{ + struct device *dev = wiz->dev; + int ret; + + ret = wiz_reset(wiz); + if (ret) { + dev_err(dev, "WIZ reset failed\n"); + return ret; + } + + ret = wiz_mode_select(wiz); + if (ret) { + dev_err(dev, "WIZ mode select failed\n"); + return ret; + } + + ret = wiz_init_raw_interface(wiz, true); + if (ret) { + dev_err(dev, "WIZ interface initialization failed\n"); + return ret; + } + + return 0; +} + +static int wiz_regfield_init(struct wiz *wiz) +{ + struct wiz_clk_mux_sel *clk_mux_sel; + struct wiz_clk_div_sel *clk_div_sel; + struct regmap *regmap = wiz->regmap; + int num_lanes = wiz->num_lanes; + struct device *dev = wiz->dev; + int i; + + wiz->por_en = devm_regmap_field_alloc(dev, regmap, por_en); + if (IS_ERR(wiz->por_en)) { + dev_err(dev, "POR_EN reg field init failed\n"); + return PTR_ERR(wiz->por_en); + } + + wiz->phy_reset_n = devm_regmap_field_alloc(dev, regmap, + phy_reset_n); + if (IS_ERR(wiz->phy_reset_n)) { + dev_err(dev, "PHY_RESET_N reg field init failed\n"); + return PTR_ERR(wiz->phy_reset_n); + } + + wiz->pma_cmn_refclk_int_mode = + devm_regmap_field_alloc(dev, regmap, pma_cmn_refclk_int_mode); + if (IS_ERR(wiz->pma_cmn_refclk_int_mode)) { + dev_err(dev, "PMA_CMN_REFCLK_INT_MODE reg field init failed\n"); + return PTR_ERR(wiz->pma_cmn_refclk_int_mode); + } + + wiz->pma_cmn_refclk_mode = + devm_regmap_field_alloc(dev, regmap, pma_cmn_refclk_mode); + if (IS_ERR(wiz->pma_cmn_refclk_mode)) { + dev_err(dev, "PMA_CMN_REFCLK_MODE reg field init failed\n"); + return PTR_ERR(wiz->pma_cmn_refclk_mode); + } + + clk_div_sel = &wiz->clk_div_sel[CMN_REFCLK_DIG_DIV]; + clk_div_sel->field = devm_regmap_field_alloc(dev, regmap, + pma_cmn_refclk_dig_div); + if (IS_ERR(clk_div_sel->field)) { + dev_err(dev, "PMA_CMN_REFCLK_DIG_DIV reg field init failed\n"); + return PTR_ERR(clk_div_sel->field); + } + + if (wiz->type == J721E_WIZ_16G) { + clk_div_sel = &wiz->clk_div_sel[CMN_REFCLK1_DIG_DIV]; + clk_div_sel->field = + devm_regmap_field_alloc(dev, regmap, + pma_cmn_refclk1_dig_div); + if (IS_ERR(clk_div_sel->field)) { + dev_err(dev, "PMA_CMN_REFCLK1_DIG_DIV reg field init failed\n"); + return PTR_ERR(clk_div_sel->field); + } + } + + clk_mux_sel = &wiz->clk_mux_sel[PLL0_REFCLK]; + clk_mux_sel->field = devm_regmap_field_alloc(dev, regmap, + pll0_refclk_mux_sel); + if (IS_ERR(clk_mux_sel->field)) { + dev_err(dev, "PLL0_REFCLK_SEL reg field init failed\n"); + return PTR_ERR(clk_mux_sel->field); + } + + clk_mux_sel = &wiz->clk_mux_sel[PLL1_REFCLK]; + clk_mux_sel->field = devm_regmap_field_alloc(dev, regmap, + pll1_refclk_mux_sel); + if (IS_ERR(clk_mux_sel->field)) { + dev_err(dev, "PLL1_REFCLK_SEL reg field init failed\n"); + return PTR_ERR(clk_mux_sel->field); + } + + clk_mux_sel = &wiz->clk_mux_sel[REFCLK_DIG]; + if (wiz->type == J721E_WIZ_10G) + clk_mux_sel->field = + devm_regmap_field_alloc(dev, regmap, + refclk_dig_sel_10g); + else + clk_mux_sel->field = + devm_regmap_field_alloc(dev, regmap, + refclk_dig_sel_16g); + + if (IS_ERR(clk_mux_sel->field)) { + dev_err(dev, "REFCLK_DIG_SEL reg field init failed\n"); + return PTR_ERR(clk_mux_sel->field); + } + + for (i = 0; i < num_lanes; i++) { + wiz->p_enable[i] = devm_regmap_field_alloc(dev, regmap, + p_enable[i]); + if (IS_ERR(wiz->p_enable[i])) { + dev_err(dev, "P%d_ENABLE reg field init failed\n", i); + return PTR_ERR(wiz->p_enable[i]); + } + + wiz->p_align[i] = devm_regmap_field_alloc(dev, regmap, + p_align[i]); + if (IS_ERR(wiz->p_align[i])) { + dev_err(dev, "P%d_ALIGN reg field init failed\n", i); + return PTR_ERR(wiz->p_align[i]); + } + + wiz->p_raw_auto_start[i] = + devm_regmap_field_alloc(dev, regmap, p_raw_auto_start[i]); + if (IS_ERR(wiz->p_raw_auto_start[i])) { + dev_err(dev, "P%d_RAW_AUTO_START reg field init fail\n", + i); + return PTR_ERR(wiz->p_raw_auto_start[i]); + } + + wiz->p_standard_mode[i] = + devm_regmap_field_alloc(dev, regmap, p_standard_mode[i]); + if (IS_ERR(wiz->p_standard_mode[i])) { + dev_err(dev, "P%d_STANDARD_MODE reg field init fail\n", + i); + return PTR_ERR(wiz->p_standard_mode[i]); + } + } + + return 0; +} + +static u8 wiz_clk_mux_get_parent(struct clk_hw *hw) +{ + struct wiz_clk_mux *mux = to_wiz_clk_mux(hw); + struct regmap_field *field = mux->field; + unsigned int val; + + regmap_field_read(field, &val); + return clk_mux_val_to_index(hw, mux->table, 0, val); +} + +static int wiz_clk_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct wiz_clk_mux *mux = to_wiz_clk_mux(hw); + struct regmap_field *field = mux->field; + int val; + + val = mux->table[index]; + return regmap_field_write(field, val); +} + +static const struct clk_ops wiz_clk_mux_ops = { + .set_parent = wiz_clk_mux_set_parent, + .get_parent = wiz_clk_mux_get_parent, +}; + +static int wiz_mux_clk_register(struct wiz *wiz, struct device_node *node, + struct regmap_field *field, u32 *table) +{ + struct device *dev = wiz->dev; + struct clk_init_data *init; + const char **parent_names; + unsigned int num_parents; + struct wiz_clk_mux *mux; + char clk_name[100]; + struct clk *clk; + int ret; + + mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); + if (!mux) + return -ENOMEM; + + num_parents = of_clk_get_parent_count(node); + if (num_parents < 2) { + dev_err(dev, "SERDES clock must have parents\n"); + return -EINVAL; + } + + parent_names = devm_kzalloc(dev, (sizeof(char *) * num_parents), + GFP_KERNEL); + if (!parent_names) + return -ENOMEM; + + of_clk_parent_fill(node, parent_names, num_parents); + + snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), + node->name); + + init = &mux->clk_data; + + init->ops = &wiz_clk_mux_ops; + init->flags = CLK_SET_RATE_NO_REPARENT; + init->parent_names = parent_names; + init->num_parents = num_parents; + init->name = clk_name; + + mux->field = field; + mux->table = table; + mux->hw.init = init; + + clk = devm_clk_register(dev, &mux->hw); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (ret) + dev_err(dev, "Failed to add clock provider: %s\n", clk_name); + + return ret; +} + +static unsigned long wiz_clk_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct wiz_clk_divider *div = to_wiz_clk_div(hw); + struct regmap_field *field = div->field; + int val; + + regmap_field_read(field, &val); + + return divider_recalc_rate(hw, parent_rate, val, div->table, 0x0, 2); +} + +static long wiz_clk_div_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct wiz_clk_divider *div = to_wiz_clk_div(hw); + + return divider_round_rate(hw, rate, prate, div->table, 2, 0x0); +} + +static int wiz_clk_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct wiz_clk_divider *div = to_wiz_clk_div(hw); + struct regmap_field *field = div->field; + int val; + + val = divider_get_val(rate, parent_rate, div->table, 2, 0x0); + if (val < 0) + return val; + + return regmap_field_write(field, val); +} + +static const struct clk_ops wiz_clk_div_ops = { + .recalc_rate = wiz_clk_div_recalc_rate, + .round_rate = wiz_clk_div_round_rate, + .set_rate = wiz_clk_div_set_rate, +}; + +static int wiz_div_clk_register(struct wiz *wiz, struct device_node *node, + struct regmap_field *field, + struct clk_div_table *table) +{ + struct device *dev = wiz->dev; + struct wiz_clk_divider *div; + struct clk_init_data *init; + const char **parent_names; + char clk_name[100]; + struct clk *clk; + int ret; + + div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL); + if (!div) + return -ENOMEM; + + snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), + node->name); + + parent_names = devm_kzalloc(dev, sizeof(char *), GFP_KERNEL); + if (!parent_names) + return -ENOMEM; + + of_clk_parent_fill(node, parent_names, 1); + + init = &div->clk_data; + + init->ops = &wiz_clk_div_ops; + init->flags = 0; + init->parent_names = parent_names; + init->num_parents = 1; + init->name = clk_name; + + div->field = field; + div->table = table; + div->hw.init = init; + + clk = devm_clk_register(dev, &div->hw); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (ret) + dev_err(dev, "Failed to add clock provider: %s\n", clk_name); + + return ret; +} + +static void wiz_clock_cleanup(struct wiz *wiz, struct device_node *node) +{ + struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel; + struct device_node *clk_node; + int i; + + for (i = 0; i < WIZ_MUX_NUM_CLOCKS; i++) { + clk_node = of_get_child_by_name(node, clk_mux_sel[i].node_name); + of_clk_del_provider(clk_node); + of_node_put(clk_node); + } +} + +static int wiz_clock_init(struct wiz *wiz, struct device_node *node) +{ + struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel; + struct device *dev = wiz->dev; + struct device_node *clk_node; + const char *node_name; + unsigned long rate; + struct clk *clk; + int ret; + int i; + + clk = devm_clk_get(dev, "core_ref_clk"); + if (IS_ERR(clk)) { + dev_err(dev, "core_ref_clk clock not found\n"); + ret = PTR_ERR(clk); + return ret; + } + + rate = clk_get_rate(clk); + if (rate >= 100000000) + regmap_field_write(wiz->pma_cmn_refclk_int_mode, 0x1); + else + regmap_field_write(wiz->pma_cmn_refclk_int_mode, 0x3); + + clk = devm_clk_get(dev, "ext_ref_clk"); + if (IS_ERR(clk)) { + dev_err(dev, "ext_ref_clk clock not found\n"); + ret = PTR_ERR(clk); + return ret; + } + + rate = clk_get_rate(clk); + if (rate >= 100000000) + regmap_field_write(wiz->pma_cmn_refclk_mode, 0x0); + else + regmap_field_write(wiz->pma_cmn_refclk_mode, 0x2); + + for (i = 0; i < WIZ_MUX_NUM_CLOCKS; i++) { + node_name = clk_mux_sel[i].node_name; + clk_node = of_get_child_by_name(node, node_name); + if (!clk_node) { + dev_err(dev, "Unable to get %s node\n", node_name); + ret = -EINVAL; + goto err; + } + + ret = wiz_mux_clk_register(wiz, clk_node, clk_mux_sel[i].field, + clk_mux_sel[i].table); + if (ret) { + dev_err(dev, "Failed to register %s clock\n", + node_name); + of_node_put(clk_node); + goto err; + } + + of_node_put(clk_node); + } + + for (i = 0; i < wiz->clk_div_sel_num; i++) { + node_name = clk_div_sel[i].node_name; + clk_node = of_get_child_by_name(node, node_name); + if (!clk_node) { + dev_err(dev, "Unable to get %s node\n", node_name); + ret = -EINVAL; + goto err; + } + + ret = wiz_div_clk_register(wiz, clk_node, clk_div_sel[i].field, + clk_div_sel[i].table); + if (ret) { + dev_err(dev, "Failed to register %s clock\n", + node_name); + of_node_put(clk_node); + goto err; + } + + of_node_put(clk_node); + } + + return 0; +err: + wiz_clock_cleanup(wiz, node); + + return ret; +} + +static int wiz_phy_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct device *dev = rcdev->dev; + struct wiz *wiz = dev_get_drvdata(dev); + int ret = 0; + + if (id == 0) { + ret = regmap_field_write(wiz->phy_reset_n, false); + return ret; + } + + ret = regmap_field_write(wiz->p_enable[id - 1], false); + return ret; +} + +static int wiz_phy_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct device *dev = rcdev->dev; + struct wiz *wiz = dev_get_drvdata(dev); + int ret; + + if (id == 0) { + ret = regmap_field_write(wiz->phy_reset_n, true); + return ret; + } + + ret = regmap_field_write(wiz->p_enable[id - 1], true); + return ret; +} + +static const struct reset_control_ops wiz_phy_reset_ops = { + .assert = wiz_phy_reset_assert, + .deassert = wiz_phy_reset_deassert, +}; + +static struct regmap_config wiz_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .fast_io = true, +}; + +static const struct of_device_id wiz_id_table[] = { + { + .compatible = "ti,j721e-wiz-16g", .data = (void *)J721E_WIZ_16G + }, + { + .compatible = "ti,j721e-wiz-10g", .data = (void *)J721E_WIZ_10G + }, + {} +}; +MODULE_DEVICE_TABLE(of, wiz_id_table); + +static int wiz_probe(struct platform_device *pdev) +{ + struct reset_controller_dev *phy_reset_dev; + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct platform_device *serdes_pdev; + struct device_node *child_node; + struct regmap *regmap; + struct resource res; + void __iomem *base; + struct wiz *wiz; + u32 num_lanes; + int ret; + + wiz = devm_kzalloc(dev, sizeof(*wiz), GFP_KERNEL); + if (!wiz) + return -ENOMEM; + + wiz->type = (enum wiz_type)of_device_get_match_data(dev); + + child_node = of_get_child_by_name(node, "serdes"); + if (!child_node) { + dev_err(dev, "Failed to get SERDES child DT node\n"); + return -ENODEV; + } + + ret = of_address_to_resource(child_node, 0, &res); + if (ret) { + dev_err(dev, "Failed to get memory resource\n"); + goto err_addr_to_resource; + } + + base = devm_ioremap(dev, res.start, resource_size(&res)); + if (IS_ERR(base)) + goto err_addr_to_resource; + + regmap = devm_regmap_init_mmio(dev, base, &wiz_regmap_config); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to initialize regmap\n"); + ret = PTR_ERR(regmap); + goto err_addr_to_resource; + } + + ret = of_property_read_u32(node, "num-lanes", &num_lanes); + if (ret) { + dev_err(dev, "Failed to read num-lanes property\n"); + goto err_addr_to_resource; + } + + if (num_lanes > WIZ_MAX_LANES) { + dev_err(dev, "Cannot support %d lanes\n", num_lanes); + goto err_addr_to_resource; + } + + wiz->dev = dev; + wiz->regmap = regmap; + wiz->num_lanes = num_lanes; + if (wiz->type == J721E_WIZ_10G) + wiz->clk_mux_sel = clk_mux_sel_10g; + else + wiz->clk_mux_sel = clk_mux_sel_16g; + + wiz->clk_div_sel = clk_div_sel; + + if (wiz->type == J721E_WIZ_10G) + wiz->clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_10G; + else + wiz->clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_16G; + + platform_set_drvdata(pdev, wiz); + + ret = wiz_regfield_init(wiz); + if (ret) { + dev_err(dev, "Failed to initialize regfields\n"); + goto err_addr_to_resource; + } + + phy_reset_dev = &wiz->wiz_phy_reset_dev; + phy_reset_dev->dev = dev; + phy_reset_dev->ops = &wiz_phy_reset_ops, + phy_reset_dev->owner = THIS_MODULE, + phy_reset_dev->of_node = node; + /* Reset for each of the lane and one for the entire SERDES */ + phy_reset_dev->nr_resets = num_lanes + 1; + + ret = devm_reset_controller_register(dev, phy_reset_dev); + if (ret < 0) { + dev_warn(dev, "Failed to register reset controller\n"); + goto err_addr_to_resource; + } + + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "pm_runtime_get_sync failed\n"); + goto err_get_sync; + } + + ret = wiz_clock_init(wiz, node); + if (ret < 0) { + dev_warn(dev, "Failed to initialize clocks\n"); + goto err_get_sync; + } + + serdes_pdev = of_platform_device_create(child_node, NULL, dev); + if (!serdes_pdev) { + dev_WARN(dev, "Unable to create SERDES platform device\n"); + goto err_pdev_create; + } + wiz->serdes_pdev = serdes_pdev; + + ret = wiz_init(wiz); + if (ret) { + dev_err(dev, "WIZ initialization failed\n"); + goto err_wiz_init; + } + + of_node_put(child_node); + return 0; + +err_wiz_init: + of_platform_device_destroy(&serdes_pdev->dev, NULL); + +err_pdev_create: + wiz_clock_cleanup(wiz, node); + +err_get_sync: + pm_runtime_put(dev); + pm_runtime_disable(dev); + +err_addr_to_resource: + of_node_put(child_node); + + return ret; +} + +static int wiz_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct platform_device *serdes_pdev; + struct wiz *wiz; + + wiz = dev_get_drvdata(dev); + serdes_pdev = wiz->serdes_pdev; + + of_platform_device_destroy(&serdes_pdev->dev, NULL); + wiz_clock_cleanup(wiz, node); + pm_runtime_put(dev); + pm_runtime_disable(dev); + + return 0; +} + +static struct platform_driver wiz_driver = { + .probe = wiz_probe, + .remove = wiz_remove, + .driver = { + .name = "wiz", + .of_match_table = wiz_id_table, + }, +}; +module_platform_driver(wiz_driver); + +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("TI J721E WIZ driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 5bc999108025a82a2862b2e7e0af00e34643d270 Mon Sep 17 00:00:00 2001 From: Ramuthevar Vadivel Murugan Date: Tue, 17 Dec 2019 09:56:57 +0800 Subject: dt-bindings: phy: intel-emmc-phy: Add YAML schema for LGM eMMC PHY Add a YAML schema to use the host controller driver with the eMMC PHY on Intel's Lightning Mountain SoC. Signed-off-by: Ramuthevar Vadivel Murugan Reviewed-by: Rob Herring Signed-off-by: Kishon Vijay Abraham I --- .../bindings/phy/intel,lgm-emmc-phy.yaml | 56 ++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/intel,lgm-emmc-phy.yaml diff --git a/Documentation/devicetree/bindings/phy/intel,lgm-emmc-phy.yaml b/Documentation/devicetree/bindings/phy/intel,lgm-emmc-phy.yaml new file mode 100644 index 000000000000..ff7959c21af0 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/intel,lgm-emmc-phy.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/intel,lgm-emmc-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Intel Lightning Mountain(LGM) eMMC PHY Device Tree Bindings + +maintainers: + - Ramuthevar Vadivel Murugan + +description: |+ + Bindings for eMMC PHY on Intel's Lightning Mountain SoC, syscon + node is used to reference the base address of eMMC phy registers. + + The eMMC PHY node should be the child of a syscon node with the + required property: + + - compatible: Should be one of the following: + "intel,lgm-syscon", "syscon" + - reg: + maxItems: 1 + +properties: + compatible: + const: intel,lgm-emmc-phy + + "#phy-cells": + const: 0 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + +required: + - "#phy-cells" + - compatible + - reg + - clocks + +examples: + - | + sysconf: chiptop@e0200000 { + compatible = "intel,lgm-syscon", "syscon"; + reg = <0xe0200000 0x100>; + + emmc-phy: emmc-phy@a8 { + compatible = "intel,lgm-emmc-phy"; + reg = <0x00a8 0x10>; + clocks = <&emmc>; + #phy-cells = <0>; + }; + }; +... -- cgit v1.2.3-59-g8ed1b From 9227942383307f97fa6992416f73af4a23ef972c Mon Sep 17 00:00:00 2001 From: Ramuthevar Vadivel Murugan Date: Tue, 17 Dec 2019 09:56:58 +0800 Subject: phy: intel-lgm-emmc: Add support for eMMC PHY Add support for eMMC PHY on Intel's Lightning Mountain SoC. Signed-off-by: Ramuthevar Vadivel Murugan Reviewed-by: Andy Shevchenko Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/Kconfig | 1 + drivers/phy/Makefile | 1 + drivers/phy/intel/Kconfig | 9 ++ drivers/phy/intel/Makefile | 2 + drivers/phy/intel/phy-intel-emmc.c | 283 +++++++++++++++++++++++++++++++++++++ 5 files changed, 296 insertions(+) create mode 100644 drivers/phy/intel/Kconfig create mode 100644 drivers/phy/intel/Makefile create mode 100644 drivers/phy/intel/phy-intel-emmc.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 0263db2ac874..b3ed94b98d9b 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -69,5 +69,6 @@ source "drivers/phy/socionext/Kconfig" source "drivers/phy/st/Kconfig" source "drivers/phy/tegra/Kconfig" source "drivers/phy/ti/Kconfig" +source "drivers/phy/intel/Kconfig" endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index c96a1afc95bd..310c149a9df5 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -18,6 +18,7 @@ obj-y += broadcom/ \ cadence/ \ freescale/ \ hisilicon/ \ + intel/ \ lantiq/ \ marvell/ \ motorola/ \ diff --git a/drivers/phy/intel/Kconfig b/drivers/phy/intel/Kconfig new file mode 100644 index 000000000000..4ea6a8897cd7 --- /dev/null +++ b/drivers/phy/intel/Kconfig @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Phy drivers for Intel Lightning Mountain(LGM) platform +# +config PHY_INTEL_EMMC + tristate "Intel EMMC PHY driver" + select GENERIC_PHY + help + Enable this to support the Intel EMMC PHY diff --git a/drivers/phy/intel/Makefile b/drivers/phy/intel/Makefile new file mode 100644 index 000000000000..6b876a75599d --- /dev/null +++ b/drivers/phy/intel/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PHY_INTEL_EMMC) += phy-intel-emmc.o diff --git a/drivers/phy/intel/phy-intel-emmc.c b/drivers/phy/intel/phy-intel-emmc.c new file mode 100644 index 000000000000..1a358e7fd236 --- /dev/null +++ b/drivers/phy/intel/phy-intel-emmc.c @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel eMMC PHY driver + * Copyright (C) 2019 Intel, Corp. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* eMMC phy register definitions */ +#define EMMC_PHYCTRL0_REG 0xa8 +#define DR_TY_MASK GENMASK(30, 28) +#define DR_TY_SHIFT(x) (((x) << 28) & DR_TY_MASK) +#define OTAPDLYENA BIT(14) +#define OTAPDLYSEL_MASK GENMASK(13, 10) +#define OTAPDLYSEL_SHIFT(x) (((x) << 10) & OTAPDLYSEL_MASK) + +#define EMMC_PHYCTRL1_REG 0xac +#define PDB_MASK BIT(0) +#define PDB_SHIFT(x) (((x) << 0) & PDB_MASK) +#define ENDLL_MASK BIT(7) +#define ENDLL_SHIFT(x) (((x) << 7) & ENDLL_MASK) + +#define EMMC_PHYCTRL2_REG 0xb0 +#define FRQSEL_25M 0 +#define FRQSEL_50M 1 +#define FRQSEL_100M 2 +#define FRQSEL_150M 3 +#define FRQSEL_MASK GENMASK(24, 22) +#define FRQSEL_SHIFT(x) (((x) << 22) & FRQSEL_MASK) + +#define EMMC_PHYSTAT_REG 0xbc +#define CALDONE_MASK BIT(9) +#define DLLRDY_MASK BIT(8) +#define IS_CALDONE(x) ((x) & CALDONE_MASK) +#define IS_DLLRDY(x) ((x) & DLLRDY_MASK) + +struct intel_emmc_phy { + struct regmap *syscfg; + struct clk *emmcclk; +}; + +static int intel_emmc_phy_power(struct phy *phy, bool on_off) +{ + struct intel_emmc_phy *priv = phy_get_drvdata(phy); + unsigned int caldone; + unsigned int dllrdy; + unsigned int freqsel; + unsigned long rate; + int ret, quot; + + /* + * Keep phyctrl_pdb and phyctrl_endll low to allow + * initialization of CALIO state M/C DFFs + */ + ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL1_REG, PDB_MASK, + PDB_SHIFT(0)); + if (ret) { + dev_err(&phy->dev, "CALIO power down bar failed: %d\n", ret); + return ret; + } + + /* Already finish power_off above */ + if (!on_off) + return 0; + + rate = clk_get_rate(priv->emmcclk); + quot = DIV_ROUND_CLOSEST(rate, 50000000); + if (quot > FRQSEL_150M) + dev_warn(&phy->dev, "Unsupported rate: %lu\n", rate); + freqsel = clamp_t(int, quot, FRQSEL_25M, FRQSEL_150M); + + /* + * According to the user manual, calpad calibration + * cycle takes more than 2us without the minimal recommended + * value, so we may need a little margin here + */ + udelay(5); + + ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL1_REG, PDB_MASK, + PDB_SHIFT(1)); + if (ret) { + dev_err(&phy->dev, "CALIO power down bar failed: %d\n", ret); + return ret; + } + + /* + * According to the user manual, it asks driver to wait 5us for + * calpad busy trimming. However it is documented that this value is + * PVT(A.K.A process,voltage and temperature) relevant, so some + * failure cases are found which indicates we should be more tolerant + * to calpad busy trimming. + */ + ret = regmap_read_poll_timeout(priv->syscfg, EMMC_PHYSTAT_REG, + caldone, IS_CALDONE(caldone), + 0, 50); + if (ret) { + dev_err(&phy->dev, "caldone failed, ret=%d\n", ret); + return ret; + } + + /* Set the frequency of the DLL operation */ + ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL2_REG, FRQSEL_MASK, + FRQSEL_SHIFT(freqsel)); + if (ret) { + dev_err(&phy->dev, "set the frequency of dll failed:%d\n", ret); + return ret; + } + + /* Turn on the DLL */ + ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL1_REG, ENDLL_MASK, + ENDLL_SHIFT(1)); + if (ret) { + dev_err(&phy->dev, "turn on the dll failed: %d\n", ret); + return ret; + } + + /* + * After enabling analog DLL circuits docs say that we need 10.2 us if + * our source clock is at 50 MHz and that lock time scales linearly + * with clock speed. If we are powering on the PHY and the card clock + * is super slow (like 100 kHZ) this could take as long as 5.1 ms as + * per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms + * Hopefully we won't be running at 100 kHz, but we should still make + * sure we wait long enough. + * + * NOTE: There appear to be corner cases where the DLL seems to take + * extra long to lock for reasons that aren't understood. In some + * extreme cases we've seen it take up to over 10ms (!). We'll be + * generous and give it 50ms. + */ + ret = regmap_read_poll_timeout(priv->syscfg, + EMMC_PHYSTAT_REG, + dllrdy, IS_DLLRDY(dllrdy), + 0, 50 * USEC_PER_MSEC); + if (ret) { + dev_err(&phy->dev, "dllrdy failed. ret=%d\n", ret); + return ret; + } + + return 0; +} + +static int intel_emmc_phy_init(struct phy *phy) +{ + struct intel_emmc_phy *priv = phy_get_drvdata(phy); + + /* + * We purposely get the clock here and not in probe to avoid the + * circular dependency problem. We expect: + * - PHY driver to probe + * - SDHCI driver to start probe + * - SDHCI driver to register it's clock + * - SDHCI driver to get the PHY + * - SDHCI driver to init the PHY + * + * The clock is optional, so upon any error just return it like + * any other error to user. + * + */ + priv->emmcclk = clk_get_optional(&phy->dev, "emmcclk"); + if (IS_ERR(priv->emmcclk)) { + dev_err(&phy->dev, "ERROR: getting emmcclk\n"); + return PTR_ERR(priv->emmcclk); + } + + return 0; +} + +static int intel_emmc_phy_exit(struct phy *phy) +{ + struct intel_emmc_phy *priv = phy_get_drvdata(phy); + + clk_put(priv->emmcclk); + + return 0; +} + +static int intel_emmc_phy_power_on(struct phy *phy) +{ + struct intel_emmc_phy *priv = phy_get_drvdata(phy); + int ret; + + /* Drive impedance: 50 Ohm */ + ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL0_REG, DR_TY_MASK, + DR_TY_SHIFT(6)); + if (ret) { + dev_err(&phy->dev, "ERROR set drive-impednce-50ohm: %d\n", ret); + return ret; + } + + /* Output tap delay: disable */ + ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL0_REG, OTAPDLYENA, + 0); + if (ret) { + dev_err(&phy->dev, "ERROR Set output tap delay : %d\n", ret); + return ret; + } + + /* Output tap delay */ + ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL0_REG, + OTAPDLYSEL_MASK, OTAPDLYSEL_SHIFT(4)); + if (ret) { + dev_err(&phy->dev, "ERROR: output tap dly select: %d\n", ret); + return ret; + } + + /* Power up eMMC phy analog blocks */ + return intel_emmc_phy_power(phy, true); +} + +static int intel_emmc_phy_power_off(struct phy *phy) +{ + /* Power down eMMC phy analog blocks */ + return intel_emmc_phy_power(phy, false); +} + +static const struct phy_ops ops = { + .init = intel_emmc_phy_init, + .exit = intel_emmc_phy_exit, + .power_on = intel_emmc_phy_power_on, + .power_off = intel_emmc_phy_power_off, + .owner = THIS_MODULE, +}; + +static int intel_emmc_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct intel_emmc_phy *priv; + struct phy *generic_phy; + struct phy_provider *phy_provider; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* Get eMMC phy (accessed via chiptop) regmap */ + priv->syscfg = syscon_regmap_lookup_by_phandle(np, "intel,syscon"); + if (IS_ERR(priv->syscfg)) { + dev_err(dev, "failed to find syscon\n"); + return PTR_ERR(priv->syscfg); + } + + generic_phy = devm_phy_create(dev, np, &ops); + if (IS_ERR(generic_phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(generic_phy); + } + + phy_set_drvdata(generic_phy, priv); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id intel_emmc_phy_dt_ids[] = { + { .compatible = "intel,lgm-emmc-phy" }, + {} +}; + +MODULE_DEVICE_TABLE(of, intel_emmc_phy_dt_ids); + +static struct platform_driver intel_emmc_driver = { + .probe = intel_emmc_phy_probe, + .driver = { + .name = "intel-emmc-phy", + .of_match_table = intel_emmc_phy_dt_ids, + }, +}; + +module_platform_driver(intel_emmc_driver); + +MODULE_AUTHOR("Peter Harliman Liem "); +MODULE_DESCRIPTION("Intel eMMC PHY driver"); -- cgit v1.2.3-59-g8ed1b From 4cb6eea22621e14e29c91d18d5c66f0b01470071 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 3 Jan 2020 17:47:09 +0100 Subject: phy: mediatek: Fix Kconfig indentation Adjust indentation from spaces to tab (+optional two spaces) as in coding style. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Stanley Chu Acked-by: Florian Fainelli Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/mediatek/Kconfig | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/phy/mediatek/Kconfig b/drivers/phy/mediatek/Kconfig index 376f5d189da0..7d19134c6b7c 100644 --- a/drivers/phy/mediatek/Kconfig +++ b/drivers/phy/mediatek/Kconfig @@ -3,12 +3,12 @@ # Phy drivers for Mediatek devices # config PHY_MTK_TPHY - tristate "MediaTek T-PHY Driver" - depends on ARCH_MEDIATEK && OF - select GENERIC_PHY - help - Say 'Y' here to add support for MediaTek T-PHY driver, - it supports multiple usb2.0, usb3.0 ports, PCIe and + tristate "MediaTek T-PHY Driver" + depends on ARCH_MEDIATEK && OF + select GENERIC_PHY + help + Say 'Y' here to add support for MediaTek T-PHY driver, + it supports multiple usb2.0, usb3.0 ports, PCIe and SATA, and meanwhile supports two version T-PHY which have different banks layout, the T-PHY with shared banks between multi-ports is first version, otherwise is second veriosn, @@ -25,10 +25,10 @@ config PHY_MTK_UFS specified M-PHYs. config PHY_MTK_XSPHY - tristate "MediaTek XS-PHY Driver" - depends on ARCH_MEDIATEK && OF - select GENERIC_PHY - help + tristate "MediaTek XS-PHY Driver" + depends on ARCH_MEDIATEK && OF + select GENERIC_PHY + help Enable this to support the SuperSpeedPlus XS-PHY transceiver for USB3.1 GEN2 controllers on MediaTek chips. The driver supports multiple USB2.0, USB3.1 GEN2 ports. -- cgit v1.2.3-59-g8ed1b From e7b4aaf051d581a30bea1f55d775a627b0ad3106 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 3 Jan 2020 17:47:10 +0100 Subject: phy: Enable compile testing for some of drivers Some of the phy drivers can be compile tested to increase build coverage. Signed-off-by: Krzysztof Kozlowski Acked-by: Florian Fainelli Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/allwinner/Kconfig | 3 ++- drivers/phy/broadcom/Kconfig | 4 ++-- drivers/phy/marvell/Kconfig | 8 +++++--- drivers/phy/mediatek/Kconfig | 9 ++++++--- drivers/phy/samsung/Kconfig | 8 ++++---- drivers/phy/ti/Kconfig | 4 ++-- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/drivers/phy/allwinner/Kconfig b/drivers/phy/allwinner/Kconfig index 3dab79e9d52b..e760d89d3976 100644 --- a/drivers/phy/allwinner/Kconfig +++ b/drivers/phy/allwinner/Kconfig @@ -48,7 +48,8 @@ config PHY_SUN9I_USB config PHY_SUN50I_USB3 tristate "Allwinner H6 SoC USB3 PHY driver" - depends on ARCH_SUNXI && HAS_IOMEM && OF + depends on ARCH_SUNXI || COMPILE_TEST + depends on HAS_IOMEM && OF depends on RESET_CONTROLLER select GENERIC_PHY help diff --git a/drivers/phy/broadcom/Kconfig b/drivers/phy/broadcom/Kconfig index d3d983c128ea..b29f11c19155 100644 --- a/drivers/phy/broadcom/Kconfig +++ b/drivers/phy/broadcom/Kconfig @@ -50,7 +50,7 @@ config PHY_BCM_NS_USB3 config PHY_NS2_PCIE tristate "Broadcom Northstar2 PCIe PHY driver" - depends on OF && MDIO_BUS_MUX_BCM_IPROC + depends on (OF && MDIO_BUS_MUX_BCM_IPROC) || (COMPILE_TEST && MDIO_BUS) select GENERIC_PHY default ARCH_BCM_IPROC help @@ -83,7 +83,7 @@ config PHY_BRCM_SATA config PHY_BRCM_USB tristate "Broadcom STB USB PHY driver" - depends on ARCH_BRCMSTB + depends on ARCH_BRCMSTB || COMPILE_TEST depends on OF select GENERIC_PHY select SOC_BRCMSTB diff --git a/drivers/phy/marvell/Kconfig b/drivers/phy/marvell/Kconfig index 005e02dd4a91..8f6273c837ec 100644 --- a/drivers/phy/marvell/Kconfig +++ b/drivers/phy/marvell/Kconfig @@ -10,14 +10,16 @@ config ARMADA375_USBCLUSTER_PHY config PHY_BERLIN_SATA tristate "Marvell Berlin SATA PHY driver" - depends on ARCH_BERLIN && HAS_IOMEM && OF + depends on ARCH_BERLIN || COMPILE_TEST + depends on OF && HAS_IOMEM select GENERIC_PHY help Enable this to support the SATA PHY on Marvell Berlin SoCs. config PHY_BERLIN_USB tristate "Marvell Berlin USB PHY Driver" - depends on ARCH_BERLIN && RESET_CONTROLLER && HAS_IOMEM && OF + depends on ARCH_BERLIN || COMPILE_TEST + depends on OF && HAS_IOMEM && RESET_CONTROLLER select GENERIC_PHY help Enable this to support the USB PHY on Marvell Berlin SoCs. @@ -95,7 +97,7 @@ config PHY_PXA_28NM_USB2 config PHY_PXA_USB tristate "Marvell PXA USB PHY Driver" - depends on ARCH_PXA || ARCH_MMP + depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST select GENERIC_PHY help Enable this to support Marvell PXA USB PHY driver for Marvell diff --git a/drivers/phy/mediatek/Kconfig b/drivers/phy/mediatek/Kconfig index 7d19134c6b7c..dee757c957f2 100644 --- a/drivers/phy/mediatek/Kconfig +++ b/drivers/phy/mediatek/Kconfig @@ -4,7 +4,8 @@ # config PHY_MTK_TPHY tristate "MediaTek T-PHY Driver" - depends on ARCH_MEDIATEK && OF + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on OF select GENERIC_PHY help Say 'Y' here to add support for MediaTek T-PHY driver, @@ -16,7 +17,8 @@ config PHY_MTK_TPHY config PHY_MTK_UFS tristate "MediaTek UFS M-PHY driver" - depends on ARCH_MEDIATEK && OF + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on OF select GENERIC_PHY help Support for UFS M-PHY on MediaTek chipsets. @@ -26,7 +28,8 @@ config PHY_MTK_UFS config PHY_MTK_XSPHY tristate "MediaTek XS-PHY Driver" - depends on ARCH_MEDIATEK && OF + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on OF select GENERIC_PHY help Enable this to support the SuperSpeedPlus XS-PHY transceiver for diff --git a/drivers/phy/samsung/Kconfig b/drivers/phy/samsung/Kconfig index 290a6c70f570..349fcb23e5f3 100644 --- a/drivers/phy/samsung/Kconfig +++ b/drivers/phy/samsung/Kconfig @@ -32,7 +32,7 @@ config PHY_EXYNOS_PCIE config PHY_SAMSUNG_USB2 tristate "Samsung USB 2.0 PHY driver" depends on HAS_IOMEM - depends on USB_EHCI_EXYNOS || USB_OHCI_EXYNOS || USB_DWC2 + depends on USB_EHCI_EXYNOS || USB_OHCI_EXYNOS || USB_DWC2 || COMPILE_TEST select GENERIC_PHY select MFD_SYSCON default ARCH_EXYNOS @@ -60,7 +60,7 @@ config PHY_EXYNOS5250_USB2 config PHY_S5PV210_USB2 bool "Support for S5PV210" depends on PHY_SAMSUNG_USB2 - depends on ARCH_S5PV210 + depends on ARCH_S5PV210 || COMPILE_TEST help Enable USB PHY support for S5PV210. This option requires that Samsung USB 2.0 PHY driver is enabled and means that support for this @@ -69,7 +69,7 @@ config PHY_S5PV210_USB2 config PHY_EXYNOS5_USBDRD tristate "Exynos5 SoC series USB DRD PHY driver" - depends on ARCH_EXYNOS && OF + depends on (ARCH_EXYNOS && OF) || COMPILE_TEST depends on HAS_IOMEM depends on USB_DWC3_EXYNOS select GENERIC_PHY @@ -82,7 +82,7 @@ config PHY_EXYNOS5_USBDRD config PHY_EXYNOS5250_SATA tristate "Exynos5250 Sata SerDes/PHY driver" - depends on SOC_EXYNOS5250 + depends on SOC_EXYNOS5250 || COMPILE_TEST depends on HAS_IOMEM depends on OF select GENERIC_PHY diff --git a/drivers/phy/ti/Kconfig b/drivers/phy/ti/Kconfig index 50f6b829cad0..3a1d3887c99c 100644 --- a/drivers/phy/ti/Kconfig +++ b/drivers/phy/ti/Kconfig @@ -4,7 +4,7 @@ # config PHY_DA8XX_USB tristate "TI DA8xx USB PHY Driver" - depends on ARCH_DAVINCI_DA8XX + depends on ARCH_DAVINCI_DA8XX || COMPILE_TEST select GENERIC_PHY select MFD_SYSCON help @@ -14,7 +14,7 @@ config PHY_DA8XX_USB config PHY_DM816X_USB tristate "TI dm816x USB PHY driver" - depends on ARCH_OMAP2PLUS + depends on ARCH_OMAP2PLUS || COMPILE_TEST depends on USB_SUPPORT select GENERIC_PHY select USB_PHY -- cgit v1.2.3-59-g8ed1b From 42d068472ddf532f3ca2bcdd06d1ca6b53f57e5e Mon Sep 17 00:00:00 2001 From: Yuti Amonkar Date: Mon, 6 Jan 2020 13:22:40 +0100 Subject: phy: Add DisplayPort configuration options Allow DisplayPort PHYs to be configured through the generic functions through a custom structure added to the generic union. The configuration structure is used for reconfiguration of DisplayPort PHYs during link training operation. The parameters added here are the ones defined in the DisplayPort spec v1.4 which include link rate, number of lanes, voltage swing and pre-emphasis. Add the DisplayPort phy mode to the generic phy_mode enum. Signed-off-by: Yuti Amonkar Reviewed-by: Maxime Ripard Reviewed-by: Jyri Sarha Signed-off-by: Kishon Vijay Abraham I --- include/linux/phy/phy-dp.h | 95 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/phy/phy.h | 5 +++ 2 files changed, 100 insertions(+) create mode 100644 include/linux/phy/phy-dp.h diff --git a/include/linux/phy/phy-dp.h b/include/linux/phy/phy-dp.h new file mode 100644 index 000000000000..18cad23642cd --- /dev/null +++ b/include/linux/phy/phy-dp.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 Cadence Design Systems Inc. + */ + +#ifndef __PHY_DP_H_ +#define __PHY_DP_H_ + +#include + +/** + * struct phy_configure_opts_dp - DisplayPort PHY configuration set + * + * This structure is used to represent the configuration state of a + * DisplayPort phy. + */ +struct phy_configure_opts_dp { + /** + * @link_rate: + * + * Link Rate, in Mb/s, of the main link. + * + * Allowed values: 1620, 2160, 2430, 2700, 3240, 4320, 5400, 8100 Mb/s + */ + unsigned int link_rate; + + /** + * @lanes: + * + * Number of active, consecutive, data lanes, starting from + * lane 0, used for the transmissions on main link. + * + * Allowed values: 1, 2, 4 + */ + unsigned int lanes; + + /** + * @voltage: + * + * Voltage swing levels, as specified by DisplayPort specification, + * to be used by particular lanes. One value per lane. + * voltage[0] is for lane 0, voltage[1] is for lane 1, etc. + * + * Maximum value: 3 + */ + unsigned int voltage[4]; + + /** + * @pre: + * + * Pre-emphasis levels, as specified by DisplayPort specification, to be + * used by particular lanes. One value per lane. + * + * Maximum value: 3 + */ + unsigned int pre[4]; + + /** + * @ssc: + * + * Flag indicating, whether or not to enable spread-spectrum clocking. + * + */ + u8 ssc : 1; + + /** + * @set_rate: + * + * Flag indicating, whether or not reconfigure link rate and SSC to + * requested values. + * + */ + u8 set_rate : 1; + + /** + * @set_lanes: + * + * Flag indicating, whether or not reconfigure lane count to + * requested value. + * + */ + u8 set_lanes : 1; + + /** + * @set_voltages: + * + * Flag indicating, whether or not reconfigure voltage swing + * and pre-emphasis to requested values. Only lanes specified + * by "lanes" parameter will be affected. + * + */ + u8 set_voltages : 1; +}; + +#endif /* __PHY_DP_H_ */ diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index 19eddd64c8f6..bcee8eba62b3 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -16,6 +16,7 @@ #include #include +#include #include struct phy; @@ -40,6 +41,7 @@ enum phy_mode { PHY_MODE_MIPI_DPHY, PHY_MODE_SATA, PHY_MODE_LVDS, + PHY_MODE_DP }; /** @@ -47,9 +49,12 @@ enum phy_mode { * * @mipi_dphy: Configuration set applicable for phys supporting * the MIPI_DPHY phy mode. + * @dp: Configuration set applicable for phys supporting + * the DisplayPort protocol. */ union phy_configure_opts { struct phy_configure_opts_mipi_dphy mipi_dphy; + struct phy_configure_opts_dp dp; }; /** -- cgit v1.2.3-59-g8ed1b From 80f96fb186a3134a886d696c0a1ecc1962f36c89 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 8 Jan 2020 11:59:36 +0530 Subject: phy: cadence: Sierra: remove redundant initialization of pointer regmap The pointer regmap is being initialized with a value that is never read and it is being updated later with a new value from phy->regmap_common_cdb. The initialization is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/cadence/phy-cadence-sierra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index eb87f1a0a596..ecfb1f9de2e3 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -272,7 +272,7 @@ static int cdns_sierra_phy_init(struct phy *gphy) { struct cdns_sierra_inst *ins = phy_get_drvdata(gphy); struct cdns_sierra_phy *phy = dev_get_drvdata(gphy->dev.parent); - struct regmap *regmap = phy->regmap; + struct regmap *regmap; int i, j; struct cdns_reg_pairs *cmn_vals, *ln_vals; u32 num_cmn_regs, num_ln_regs; -- cgit v1.2.3-59-g8ed1b From 7904e15b4d31a5515a882c3a87dfc898c4749fed Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 6 Jan 2020 15:06:20 +0200 Subject: phy: cadence: Sierra: add phy_reset hook Some platforms e.g. J721e need lane swap register to be programmed before reset is deasserted. This patch ensures that we propagate the phy_reset back to the reset controller driver. Signed-off-by: Roger Quadros Signed-off-by: Sekhar Nori Reviewed-by: Jyri Sarha Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/cadence/phy-cadence-sierra.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index ecfb1f9de2e3..a5c08e5bd2bf 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -341,10 +341,20 @@ static int cdns_sierra_phy_off(struct phy *gphy) return reset_control_assert(ins->lnk_rst); } +static int cdns_sierra_phy_reset(struct phy *gphy) +{ + struct cdns_sierra_phy *sp = dev_get_drvdata(gphy->dev.parent); + + reset_control_assert(sp->phy_rst); + reset_control_deassert(sp->phy_rst); + return 0; +}; + static const struct phy_ops ops = { .init = cdns_sierra_phy_init, .power_on = cdns_sierra_phy_on, .power_off = cdns_sierra_phy_off, + .reset = cdns_sierra_phy_reset, .owner = THIS_MODULE, }; -- cgit v1.2.3-59-g8ed1b From 6385cbe9c567cb85ba40b6af09ad2f506e71158d Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 6 Jan 2020 15:06:21 +0200 Subject: dt-bindings: phy: ti,phy-j721e-wiz: Add Type-C dir GPIO This is an optional GPIO, if specified will be used to swap lane 0 and lane 1 based on GPIO status. This is required to achieve plug flip support for USB Type-C. Type-C companions typically need some time after the cable is plugged before and before they reflect the correct status of Type-C plug orientation on the DIR line. Type-C Spec specifies CC attachment debounce time (tCCDebounce) of 100 ms (min) to 200 ms (max). Allow the DT node to specify the time (in ms) that we need to wait before sampling the DIR line. Signed-off-by: Roger Quadros Cc: Rob Herring Reviewed-by: Rob Herring Signed-off-by: Kishon Vijay Abraham I --- .../devicetree/bindings/phy/ti,phy-j721e-wiz.yaml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml b/Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml index ebc8f403b4bf..452cee1aed32 100644 --- a/Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml +++ b/Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml @@ -50,6 +50,23 @@ properties: assigned-clock-parents: maxItems: 2 + typec-dir-gpios: + maxItems: 1 + description: + GPIO to signal Type-C cable orientation for lane swap. + If GPIO is active, lane 0 and lane 1 of SERDES will be swapped to + achieve the funtionality of an external type-C plug flip mux. + + typec-dir-debounce-ms: + minimum: 100 + maximum: 1000 + default: 100 + description: + Number of milliseconds to wait before sampling typec-dir-gpio. + If not specified, the default debounce of 100ms will be used. + Type-C spec states minimum CC pin debounce of 100 ms and maximum + of 200 ms. However, some solutions might need more than 200 ms. + patternProperties: "^pll[0|1]-refclk$": type: object -- cgit v1.2.3-59-g8ed1b From c9f9eba06629cd813c21df3327a1013ad092e988 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 6 Jan 2020 15:06:22 +0200 Subject: phy: ti: j721e-wiz: Manage typec-gpio-dir Based on this GPIO state we need to configure LN10 bit to swap lane0 and lane1 if required (flipped connector). Type-C companions typically need some time after the cable is plugged before and before they reflect the correct status of Type-C plug orientation on the DIR line. Type-C Spec specifies CC attachment debounce time (tCCDebounce) of 100 ms (min) to 200 ms (max). Use the DT property to figure out if we need to add delay or not before sampling the Type-C DIR line. Signed-off-by: Roger Quadros Signed-off-by: Sekhar Nori Reviewed-by: Jyri Sarha Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/ti/phy-j721e-wiz.c | 61 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/drivers/phy/ti/phy-j721e-wiz.c b/drivers/phy/ti/phy-j721e-wiz.c index b86ebdd68302..b5f6019e5c7d 100644 --- a/drivers/phy/ti/phy-j721e-wiz.c +++ b/drivers/phy/ti/phy-j721e-wiz.c @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include #include @@ -22,6 +24,7 @@ #define WIZ_SERDES_CTRL 0x404 #define WIZ_SERDES_TOP_CTRL 0x408 #define WIZ_SERDES_RST 0x40c +#define WIZ_SERDES_TYPEC 0x410 #define WIZ_LANECTL(n) (0x480 + (0x40 * (n))) #define WIZ_MAX_LANES 4 @@ -29,6 +32,8 @@ #define WIZ_DIV_NUM_CLOCKS_16G 2 #define WIZ_DIV_NUM_CLOCKS_10G 1 +#define WIZ_SERDES_TYPEC_LN10_SWAP BIT(30) + enum wiz_lane_standard_mode { LANE_MODE_GEN1, LANE_MODE_GEN2, @@ -94,6 +99,9 @@ static const struct reg_field p_standard_mode[WIZ_MAX_LANES] = { REG_FIELD(WIZ_LANECTL(3), 24, 25), }; +static const struct reg_field typec_ln10_swap = + REG_FIELD(WIZ_SERDES_TYPEC, 30, 30); + struct wiz_clk_mux { struct clk_hw hw; struct regmap_field *field; @@ -185,6 +193,9 @@ enum wiz_type { J721E_WIZ_10G, }; +#define WIZ_TYPEC_DIR_DEBOUNCE_MIN 100 /* ms */ +#define WIZ_TYPEC_DIR_DEBOUNCE_MAX 1000 + struct wiz { struct regmap *regmap; enum wiz_type type; @@ -201,11 +212,14 @@ struct wiz { struct regmap_field *pma_cmn_refclk_mode; struct regmap_field *pma_cmn_refclk_dig_div; struct regmap_field *pma_cmn_refclk1_dig_div; + struct regmap_field *typec_ln10_swap; struct device *dev; u32 num_lanes; struct platform_device *serdes_pdev; struct reset_controller_dev wiz_phy_reset_dev; + struct gpio_desc *gpio_typec_dir; + int typec_dir_delay; }; static int wiz_reset(struct wiz *wiz) @@ -404,6 +418,13 @@ static int wiz_regfield_init(struct wiz *wiz) } } + wiz->typec_ln10_swap = devm_regmap_field_alloc(dev, regmap, + typec_ln10_swap); + if (IS_ERR(wiz->typec_ln10_swap)) { + dev_err(dev, "LN10_SWAP reg field init failed\n"); + return PTR_ERR(wiz->typec_ln10_swap); + } + return 0; } @@ -697,6 +718,17 @@ static int wiz_phy_reset_deassert(struct reset_controller_dev *rcdev, struct wiz *wiz = dev_get_drvdata(dev); int ret; + /* if typec-dir gpio was specified, set LN10 SWAP bit based on that */ + if (id == 0 && wiz->gpio_typec_dir) { + if (wiz->typec_dir_delay) + msleep_interruptible(wiz->typec_dir_delay); + + if (gpiod_get_value_cansleep(wiz->gpio_typec_dir)) + regmap_field_write(wiz->typec_ln10_swap, 1); + else + regmap_field_write(wiz->typec_ln10_swap, 0); + } + if (id == 0) { ret = regmap_field_write(wiz->phy_reset_n, true); return ret; @@ -783,6 +815,35 @@ static int wiz_probe(struct platform_device *pdev) goto err_addr_to_resource; } + wiz->gpio_typec_dir = devm_gpiod_get_optional(dev, "typec-dir", + GPIOD_IN); + if (IS_ERR(wiz->gpio_typec_dir)) { + ret = PTR_ERR(wiz->gpio_typec_dir); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to request typec-dir gpio: %d\n", + ret); + goto err_addr_to_resource; + } + + if (wiz->gpio_typec_dir) { + ret = of_property_read_u32(node, "typec-dir-debounce-ms", + &wiz->typec_dir_delay); + if (ret && ret != -EINVAL) { + dev_err(dev, "Invalid typec-dir-debounce property\n"); + goto err_addr_to_resource; + } + + /* use min. debounce from Type-C spec if not provided in DT */ + if (ret == -EINVAL) + wiz->typec_dir_delay = WIZ_TYPEC_DIR_DEBOUNCE_MIN; + + if (wiz->typec_dir_delay < WIZ_TYPEC_DIR_DEBOUNCE_MIN || + wiz->typec_dir_delay > WIZ_TYPEC_DIR_DEBOUNCE_MAX) { + dev_err(dev, "Invalid typec-dir-debounce property\n"); + goto err_addr_to_resource; + } + } + wiz->dev = dev; wiz->regmap = regmap; wiz->num_lanes = num_lanes; -- cgit v1.2.3-59-g8ed1b From b109c13a533b8cc2dab92d8668f5c112cc5ae4fc Mon Sep 17 00:00:00 2001 From: Ramuthevar Vadivel Murugan Date: Tue, 7 Jan 2020 14:06:06 +0800 Subject: phy: intel-lgm-emmc: Fix warning by adding missing MODULE_LICENSE commit 95f1061f715e ("phy: intel-lgm-emmc: Add support for eMMC PHY") introduces the below warning WARNING: modpost: missing MODULE_LICENSE() in drivers/phy/intel/phy-intel-emmc.o Fix it by adding missing MODULE_LICENSE. Signed-off-by: Ramuthevar Vadivel Murugan Reported-by: Stephen Rothwell Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/intel/phy-intel-emmc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/phy/intel/phy-intel-emmc.c b/drivers/phy/intel/phy-intel-emmc.c index 1a358e7fd236..703aeb122541 100644 --- a/drivers/phy/intel/phy-intel-emmc.c +++ b/drivers/phy/intel/phy-intel-emmc.c @@ -281,3 +281,4 @@ module_platform_driver(intel_emmc_driver); MODULE_AUTHOR("Peter Harliman Liem "); MODULE_DESCRIPTION("Intel eMMC PHY driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From b66d1ac82918cb5860e684153706058b3330df00 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 3 Jan 2020 16:28:24 +0100 Subject: dt-bindings: usb: Convert Allwinner A80 USB PHY controller to a schema The Allwinner A80 SoCs have a USB PHY controller that is used by Linux, with a matching Device Tree binding. Now that we have the DT validation in place, let's convert the device tree bindings for that controller over to a YAML schemas. Reviewed-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard Reviewed-by: Rob Herring Signed-off-by: Kishon Vijay Abraham I --- .../bindings/phy/allwinner,sun9i-a80-usb-phy.yaml | 135 +++++++++++++++++++++ .../devicetree/bindings/phy/sun9i-usb-phy.txt | 37 ------ 2 files changed, 135 insertions(+), 37 deletions(-) create mode 100644 Documentation/devicetree/bindings/phy/allwinner,sun9i-a80-usb-phy.yaml delete mode 100644 Documentation/devicetree/bindings/phy/sun9i-usb-phy.txt diff --git a/Documentation/devicetree/bindings/phy/allwinner,sun9i-a80-usb-phy.yaml b/Documentation/devicetree/bindings/phy/allwinner,sun9i-a80-usb-phy.yaml new file mode 100644 index 000000000000..ded7d6f0a119 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/allwinner,sun9i-a80-usb-phy.yaml @@ -0,0 +1,135 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/allwinner,sun9i-a80-usb-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Allwinner A80 USB PHY Device Tree Bindings + +maintainers: + - Chen-Yu Tsai + - Maxime Ripard + +properties: + "#phy-cells": + const: 0 + + compatible: + const: allwinner,sun9i-a80-usb-phy + + reg: + maxItems: 1 + + clocks: + anyOf: + - description: Main PHY Clock + + - items: + - description: Main PHY clock + - description: HSIC 12MHz clock + - description: HSIC 480MHz clock + + clock-names: + oneOf: + - const: phy + + - items: + - const: phy + - const: hsic_12M + - const: hsic_480M + + resets: + anyOf: + - description: Normal USB PHY reset + + - items: + - description: Normal USB PHY reset + - description: HSIC Reset + + reset-names: + oneOf: + - const: phy + + - items: + - const: phy + - const: hsic + + phy_type: + const: hsic + description: + When absent, the PHY type will be assumed to be normal USB. + + phy-supply: + description: + Regulator that powers VBUS + +required: + - "#phy-cells" + - compatible + - reg + - clocks + - clock-names + - resets + - reset-names + +additionalProperties: false + +if: + properties: + phy_type: + const: hsic + + required: + - phy_type + +then: + properties: + clocks: + maxItems: 3 + + clock-names: + maxItems: 3 + + resets: + maxItems: 2 + + reset-names: + maxItems: 2 + +examples: + - | + #include + #include + + usbphy1: phy@a00800 { + compatible = "allwinner,sun9i-a80-usb-phy"; + reg = <0x00a00800 0x4>; + clocks = <&usb_clocks CLK_USB0_PHY>; + clock-names = "phy"; + resets = <&usb_clocks RST_USB0_PHY>; + reset-names = "phy"; + phy-supply = <®_usb1_vbus>; + #phy-cells = <0>; + }; + + - | + #include + #include + + usbphy3: phy@a02800 { + compatible = "allwinner,sun9i-a80-usb-phy"; + reg = <0x00a02800 0x4>; + clocks = <&usb_clocks CLK_USB2_PHY>, + <&usb_clocks CLK_USB_HSIC>, + <&usb_clocks CLK_USB2_HSIC>; + clock-names = "phy", + "hsic_12M", + "hsic_480M"; + resets = <&usb_clocks RST_USB2_PHY>, + <&usb_clocks RST_USB2_HSIC>; + reset-names = "phy", + "hsic"; + phy_type = "hsic"; + phy-supply = <®_usb3_vbus>; + #phy-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/phy/sun9i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun9i-usb-phy.txt deleted file mode 100644 index 64f7109aea1f..000000000000 --- a/Documentation/devicetree/bindings/phy/sun9i-usb-phy.txt +++ /dev/null @@ -1,37 +0,0 @@ -Allwinner sun9i USB PHY ------------------------ - -Required properties: -- compatible : should be one of - * allwinner,sun9i-a80-usb-phy -- reg : a list of offset + length pairs -- #phy-cells : from the generic phy bindings, must be 0 -- phy_type : "hsic" for HSIC usage; - other values or absence of this property indicates normal USB -- clocks : phandle + clock specifier for the phy clocks -- clock-names : depending on the "phy_type" property, - * "phy" for normal USB - * "hsic_480M", "hsic_12M" for HSIC -- resets : a list of phandle + reset specifier pairs -- reset-names : depending on the "phy_type" property, - * "phy" for normal USB - * "hsic" for HSIC - -Optional Properties: -- phy-supply : from the generic phy bindings, a phandle to a regulator that - provides power to VBUS. - -It is recommended to list all clocks and resets available. -The driver will only use those matching the phy_type. - -Example: - usbphy1: phy@a01800 { - compatible = "allwinner,sun9i-a80-usb-phy"; - reg = <0x00a01800 0x4>; - clocks = <&usb_phy_clk 2>, <&usb_phy_clk 10>, - <&usb_phy_clk 3>; - clock-names = "hsic_480M", "hsic_12M", "phy"; - resets = <&usb_phy_clk 18>, <&usb_phy_clk 19>; - reset-names = "hsic", "phy"; - #phy-cells = <0>; - }; -- cgit v1.2.3-59-g8ed1b From 704a940d551c9946bce3fdd661e00a6370c40522 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 20 Dec 2019 22:05:26 +0000 Subject: thunderbolt: fix memory leak of object sw In the case where the call tb_switch_exceeds_max_depth is true the error reurn path leaks memory in sw. Fix this by setting the return error code to -EADDRNOTAVAIL and returning via the error exit path err_free_sw_ports to free sw. sw has been kzalloc'd so the free of the NULL sw->ports is fine. Addresses-Coverity: ("Resource leak") Fixes: b04079837b20 ("thunderbolt: Add initial support for USB4") Signed-off-by: Colin Ian King Acked-by: Mika Westerberg Link: https://lore.kernel.org/r/20191220220526.11307-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/switch.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 3454e6154958..ad5479f21174 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -1885,8 +1885,10 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, struct device *parent, sw->config.enabled = 0; /* Make sure we do not exceed maximum topology limit */ - if (tb_switch_exceeds_max_depth(sw, depth)) - return ERR_PTR(-EADDRNOTAVAIL); + if (tb_switch_exceeds_max_depth(sw, depth)) { + ret = -EADDRNOTAVAIL; + goto err_free_sw_ports; + } /* initialize ports */ sw->ports = kcalloc(sw->config.max_port_number + 1, sizeof(*sw->ports), -- cgit v1.2.3-59-g8ed1b From 5b738211fb59e114727381d07c647a77c0010996 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 23 Oct 2019 19:15:43 -0700 Subject: usb: dwc3: gadget: Don't send unintended link state change DCTL.ULSTCHNGREQ is a write-only field. When doing a read-modify-write to DCTL, the driver must make sure that there's no unintended link state change request from whatever is read from DCTL.ULSTCHNGREQ. Set link state change to no-action when the driver writes to DCTL. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/gadget.c | 16 +++++++--------- drivers/usb/dwc3/gadget.h | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 154f3f3e8cff..f8f4af8c48e8 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -57,7 +57,7 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode) return -EINVAL; } - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); return 0; } @@ -1828,7 +1828,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) dwc->pullups_connected = false; } - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); do { reg = dwc3_readl(dwc->regs, DWC3_DSTS); @@ -2761,10 +2761,8 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_INITU1ENA; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); - reg &= ~DWC3_DCTL_INITU2ENA; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); dwc3_disconnect_gadget(dwc); @@ -2816,7 +2814,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_TSTCTRL_MASK; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); dwc->test_mode = false; dwc3_clear_stall_all_ep(dwc); @@ -2920,11 +2918,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A) reg |= DWC3_DCTL_NYET_THRES(dwc->lpm_nyet_threshold); - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); } else { reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_HIRD_THRES_MASK; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); } dep = dwc->eps[0]; @@ -3033,7 +3031,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, reg &= ~u1u2; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); break; default: /* do nothing */ diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 5faf4d1249e0..fbc7d8013f0b 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -127,4 +127,18 @@ static inline void dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep) dep->resource_index = DWC3_DEPCMD_GET_RSC_IDX(res_id); } +/** + * dwc3_gadget_dctl_write_safe - write to DCTL safe from link state change + * @dwc: pointer to our context structure + * @value: value to write to DCTL + * + * Use this function when doing read-modify-write to DCTL. It will not + * send link state change request. + */ +static inline void dwc3_gadget_dctl_write_safe(struct dwc3 *dwc, u32 value) +{ + value &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; + dwc3_writel(dwc->regs, DWC3_DCTL, value); +} + #endif /* __DRIVERS_USB_DWC3_GADGET_H */ -- cgit v1.2.3-59-g8ed1b From 1b6009ea88ec3657792c6d15e33990abf2b907b0 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 23 Oct 2019 19:15:49 -0700 Subject: usb: dwc3: gadget: Set link state to RX_Detect on disconnect When DWC3 receives disconnect event, it needs to set the link state to RX_Detect. DWC_usb3 3.30a programming guide 4.1.7 Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/gadget.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f8f4af8c48e8..caf12fd71e73 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2759,6 +2759,8 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) { int reg; + dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RX_DET); + reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_INITU1ENA; reg &= ~DWC3_DCTL_INITU2ENA; -- cgit v1.2.3-59-g8ed1b From 2e708fa3b8987a6a76853cd4deaee990095a7b20 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 23 Oct 2019 19:15:55 -0700 Subject: usb: dwc3: gadget: Clear DCTL.ULSTCHNGREQ before set Send a no-action link state change request before the actual request so DWC3 can send the same request whenever we call dwc3_gadget_set_link_state(). Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/gadget.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index caf12fd71e73..edc478c20846 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -111,6 +111,9 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; + /* set no action before sending new link state change */ + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + /* set requested state */ reg |= DWC3_DCTL_ULSTCHNGREQ(state); dwc3_writel(dwc->regs, DWC3_DCTL, reg); -- cgit v1.2.3-59-g8ed1b From 6070636c4918c3c06e54edecdb323c8b57116768 Mon Sep 17 00:00:00 2001 From: Minas Harutyunyan Date: Thu, 24 Oct 2019 13:44:15 +0400 Subject: usb: dwc2: Fix Stalling a Non-Isochronous OUT EP Stalling a Non-Isochronous OUT Endpoint flow changed according programming guide. In dwc2_hsotg_ep_sethalt() function for OUT EP should not be set STALL bit. Instead should set SGOUTNAK in DCTL register. Set STALL bit should be set only after GOUTNAKEFF interrupt asserted. Signed-off-by: Minas Harutyunyan Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc2/gadget.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 6be10e496e10..d3335f7907fa 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3784,15 +3784,26 @@ irq_retry: for (idx = 1; idx < hsotg->num_of_eps; idx++) { hs_ep = hsotg->eps_out[idx]; /* Proceed only unmasked ISOC EPs */ - if ((BIT(idx) & ~daintmsk) || !hs_ep->isochronous) + if (BIT(idx) & ~daintmsk) continue; epctrl = dwc2_readl(hsotg, DOEPCTL(idx)); - if (epctrl & DXEPCTL_EPENA) { + //ISOC Ep's only + if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous) { epctrl |= DXEPCTL_SNAK; epctrl |= DXEPCTL_EPDIS; dwc2_writel(hsotg, epctrl, DOEPCTL(idx)); + continue; + } + + //Non-ISOC EP's + if (hs_ep->halted) { + if (!(epctrl & DXEPCTL_EPENA)) + epctrl |= DXEPCTL_EPENA; + epctrl |= DXEPCTL_EPDIS; + epctrl |= DXEPCTL_STALL; + dwc2_writel(hsotg, epctrl, DOEPCTL(idx)); } } @@ -4310,19 +4321,20 @@ static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value, bool now) epctl = dwc2_readl(hs, epreg); if (value) { - epctl |= DXEPCTL_STALL; + if (!(dwc2_readl(hs, GINTSTS) & GINTSTS_GOUTNAKEFF)) + dwc2_set_bit(hs, DCTL, DCTL_SGOUTNAK); + // STALL bit will be set in GOUTNAKEFF interrupt handler } else { epctl &= ~DXEPCTL_STALL; xfertype = epctl & DXEPCTL_EPTYPE_MASK; if (xfertype == DXEPCTL_EPTYPE_BULK || xfertype == DXEPCTL_EPTYPE_INTERRUPT) epctl |= DXEPCTL_SETD0PID; + dwc2_writel(hs, epctl, epreg); } - dwc2_writel(hs, epctl, epreg); } hs_ep->halted = value; - return 0; } -- cgit v1.2.3-59-g8ed1b From 7b8137676457d99181fb2952f0b996b8569e6420 Mon Sep 17 00:00:00 2001 From: Alexandru M Stan Date: Wed, 23 Oct 2019 14:06:31 -0700 Subject: usb: dwc2: Fix NULL qh in dwc2_queue_transaction When a usb device disconnects in a certain way, dwc2_queue_transaction still gets called after dwc2_hcd_cleanup_channels. dwc2_hcd_cleanup_channels does "channel->qh = NULL;" but dwc2_queue_transaction still wants to dereference qh. This adds a check for a null qh. Acked-by: Minas Harutyunyan Signed-off-by: Alexandru M Stan [dianders: rebased to mainline] Signed-off-by: Douglas Anderson Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc2/hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 81afe553aa66..b90f858af960 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -2824,7 +2824,7 @@ static int dwc2_queue_transaction(struct dwc2_hsotg *hsotg, list_move_tail(&chan->split_order_list_entry, &hsotg->split_order); - if (hsotg->params.host_dma) { + if (hsotg->params.host_dma && chan->qh) { if (hsotg->params.dma_desc_enable) { if (!chan->xfer_started || chan->ep_type == USB_ENDPOINT_XFER_ISOC) { -- cgit v1.2.3-59-g8ed1b From b267ddf6a5abecad100e7139617ffb12415f9156 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 31 Dec 2019 18:42:35 +0100 Subject: usb: phy-generic: Delete unused platform data The last user of the phy generic platform data was deleted in commit 1e041b6f313aaa966612a7e415cfc09c90d6b829 ("usb: dwc3: exynos: Remove dead code"). So get rid of the platform data, which rids us of another consumer of the legacy GPIO API at the same time. Make sure we only inlcude which is all we use. Alter the usb_phy_gen_create_phy() function prototype to not pass any platform data as this is just hardcoded to NULL at all locations calling it in the kernel. Move the devm_gpiod_get* calls out of the if (of_node) parenthesis, as these calls are generic and do not depend on device tree, they are used by any hardware description. Cc: Marek Szyprowski Cc: Felipe Balbi Signed-off-by: Linus Walleij Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-am335x.c | 2 +- drivers/usb/phy/phy-generic.c | 39 ++++++++++++------------------------- drivers/usb/phy/phy-generic.h | 3 +-- drivers/usb/phy/phy-keystone.c | 2 +- include/linux/usb/usb_phy_generic.h | 12 ------------ 5 files changed, 15 insertions(+), 43 deletions(-) diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c index f5f0568d8533..8524475d942d 100644 --- a/drivers/usb/phy/phy-am335x.c +++ b/drivers/usb/phy/phy-am335x.c @@ -57,7 +57,7 @@ static int am335x_phy_probe(struct platform_device *pdev) am_phy->dr_mode = of_usb_get_dr_mode_by_phy(pdev->dev.of_node, -1); - ret = usb_phy_gen_create_phy(dev, &am_phy->usb_phy_gen, NULL); + ret = usb_phy_gen_create_phy(dev, &am_phy->usb_phy_gen); if (ret) return ret; diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index a53b89be5324..661a229c105d 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -21,8 +21,7 @@ #include #include #include -#include -#include +#include #include #include "phy-generic.h" @@ -204,8 +203,7 @@ static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) return 0; } -int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop, - struct usb_phy_generic_platform_data *pdata) +int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop) { enum usb_phy_type type = USB_PHY_TYPE_USB2; int err = 0; @@ -221,28 +219,15 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop, needs_vcc = of_property_read_bool(node, "vcc-supply"); needs_clk = of_property_read_bool(node, "clocks"); - nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset", - GPIOD_ASIS); - err = PTR_ERR_OR_ZERO(nop->gpiod_reset); - if (!err) { - nop->gpiod_vbus = devm_gpiod_get_optional(dev, - "vbus-detect", - GPIOD_ASIS); - err = PTR_ERR_OR_ZERO(nop->gpiod_vbus); - } - } else if (pdata) { - type = pdata->type; - clk_rate = pdata->clk_rate; - needs_vcc = pdata->needs_vcc; - if (gpio_is_valid(pdata->gpio_reset)) { - err = devm_gpio_request_one(dev, pdata->gpio_reset, - GPIOF_ACTIVE_LOW, - dev_name(dev)); - if (!err) - nop->gpiod_reset = - gpio_to_desc(pdata->gpio_reset); - } - nop->gpiod_vbus = pdata->gpiod_vbus; + } + nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset", + GPIOD_ASIS); + err = PTR_ERR_OR_ZERO(nop->gpiod_reset); + if (!err) { + nop->gpiod_vbus = devm_gpiod_get_optional(dev, + "vbus-detect", + GPIOD_ASIS); + err = PTR_ERR_OR_ZERO(nop->gpiod_vbus); } if (err == -EPROBE_DEFER) @@ -308,7 +293,7 @@ static int usb_phy_generic_probe(struct platform_device *pdev) if (!nop) return -ENOMEM; - err = usb_phy_gen_create_phy(dev, nop, dev_get_platdata(&pdev->dev)); + err = usb_phy_gen_create_phy(dev, nop); if (err) return err; if (nop->gpiod_vbus) { diff --git a/drivers/usb/phy/phy-generic.h b/drivers/usb/phy/phy-generic.h index 97289627561d..7ee80211a688 100644 --- a/drivers/usb/phy/phy-generic.h +++ b/drivers/usb/phy/phy-generic.h @@ -22,7 +22,6 @@ struct usb_phy_generic { int usb_gen_phy_init(struct usb_phy *phy); void usb_gen_phy_shutdown(struct usb_phy *phy); -int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop, - struct usb_phy_generic_platform_data *pdata); +int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop); #endif diff --git a/drivers/usb/phy/phy-keystone.c b/drivers/usb/phy/phy-keystone.c index 110e6e9ad621..9c226b57153b 100644 --- a/drivers/usb/phy/phy-keystone.c +++ b/drivers/usb/phy/phy-keystone.c @@ -76,7 +76,7 @@ static int keystone_usbphy_probe(struct platform_device *pdev) if (IS_ERR(k_phy->phy_ctrl)) return PTR_ERR(k_phy->phy_ctrl); - ret = usb_phy_gen_create_phy(dev, &k_phy->usb_phy_gen, NULL); + ret = usb_phy_gen_create_phy(dev, &k_phy->usb_phy_gen); if (ret) return ret; diff --git a/include/linux/usb/usb_phy_generic.h b/include/linux/usb/usb_phy_generic.h index 7408cf52c710..cd9e70a552a0 100644 --- a/include/linux/usb/usb_phy_generic.h +++ b/include/linux/usb/usb_phy_generic.h @@ -3,18 +3,6 @@ #define __LINUX_USB_NOP_XCEIV_H #include -#include - -struct usb_phy_generic_platform_data { - enum usb_phy_type type; - unsigned long clk_rate; - - /* if set fails with -EPROBE_DEFER if can't get regulator */ - unsigned int needs_vcc:1; - unsigned int needs_reset:1; /* deprecated */ - int gpio_reset; - struct gpio_desc *gpiod_vbus; -}; #if IS_ENABLED(CONFIG_NOP_USB_XCEIV) /* sometimes transceivers are accessed only through e.g. ULPI */ -- cgit v1.2.3-59-g8ed1b From 644139f8b64d818f6345351455f14471510879a5 Mon Sep 17 00:00:00 2001 From: John Keeping Date: Thu, 19 Dec 2019 11:34:31 +0000 Subject: usb: dwc2: Fix IN FIFO allocation On chips with fewer FIFOs than endpoints (for example RK3288 which has 9 endpoints, but only 6 which are cabable of input), the DPTXFSIZN registers above the FIFO count may return invalid values. With logging added on startup, I see: dwc2 ff580000.usb: dwc2_hsotg_init_fifo: ep=1 sz=256 dwc2 ff580000.usb: dwc2_hsotg_init_fifo: ep=2 sz=128 dwc2 ff580000.usb: dwc2_hsotg_init_fifo: ep=3 sz=128 dwc2 ff580000.usb: dwc2_hsotg_init_fifo: ep=4 sz=64 dwc2 ff580000.usb: dwc2_hsotg_init_fifo: ep=5 sz=64 dwc2 ff580000.usb: dwc2_hsotg_init_fifo: ep=6 sz=32 dwc2 ff580000.usb: dwc2_hsotg_init_fifo: ep=7 sz=0 dwc2 ff580000.usb: dwc2_hsotg_init_fifo: ep=8 sz=0 dwc2 ff580000.usb: dwc2_hsotg_init_fifo: ep=9 sz=0 dwc2 ff580000.usb: dwc2_hsotg_init_fifo: ep=10 sz=0 dwc2 ff580000.usb: dwc2_hsotg_init_fifo: ep=11 sz=0 dwc2 ff580000.usb: dwc2_hsotg_init_fifo: ep=12 sz=0 dwc2 ff580000.usb: dwc2_hsotg_init_fifo: ep=13 sz=0 dwc2 ff580000.usb: dwc2_hsotg_init_fifo: ep=14 sz=0 dwc2 ff580000.usb: dwc2_hsotg_init_fifo: ep=15 sz=0 but: # cat /sys/kernel/debug/ff580000.usb/fifo Non-periodic FIFOs: RXFIFO: Size 275 NPTXFIFO: Size 16, Start 0x00000113 Periodic TXFIFOs: DPTXFIFO 1: Size 256, Start 0x00000123 DPTXFIFO 2: Size 128, Start 0x00000223 DPTXFIFO 3: Size 128, Start 0x000002a3 DPTXFIFO 4: Size 64, Start 0x00000323 DPTXFIFO 5: Size 64, Start 0x00000363 DPTXFIFO 6: Size 32, Start 0x000003a3 DPTXFIFO 7: Size 0, Start 0x000003e3 DPTXFIFO 8: Size 0, Start 0x000003a3 DPTXFIFO 9: Size 256, Start 0x00000123 so it seems that FIFO 9 is mirroring FIFO 1. Fix the allocation by using the FIFO count instead of the endpoint count when selecting a FIFO for an endpoint. Acked-by: Minas Harutyunyan Signed-off-by: John Keeping Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc2/gadget.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index d3335f7907fa..88f7d6d4ff2d 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -4067,11 +4067,12 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep, * a unique tx-fifo even if it is non-periodic. */ if (dir_in && hsotg->dedicated_fifos) { + unsigned fifo_count = dwc2_hsotg_tx_fifo_count(hsotg); u32 fifo_index = 0; u32 fifo_size = UINT_MAX; size = hs_ep->ep.maxpacket * hs_ep->mc; - for (i = 1; i < hsotg->num_of_eps; ++i) { + for (i = 1; i <= fifo_count; ++i) { if (hsotg->fifo_map & (1 << i)) continue; val = dwc2_readl(hsotg, DPTXFSIZN(i)); -- cgit v1.2.3-59-g8ed1b From 7037e101b648f8534119733e0aba215097ecd4d4 Mon Sep 17 00:00:00 2001 From: John Keeping Date: Thu, 19 Dec 2019 11:34:32 +0000 Subject: usb: dwc2: fix debugfs FIFO count The number of FIFOs may be lower than the number of endpoints. Use the correct total when printing FIFO details in debugfs. Acked-by: Minas Harutyunyan Signed-off-by: John Keeping Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc2/debugfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c index b8f2790abf91..3a0dcbfbc827 100644 --- a/drivers/usb/dwc2/debugfs.c +++ b/drivers/usb/dwc2/debugfs.c @@ -183,6 +183,7 @@ DEFINE_SHOW_ATTRIBUTE(state); static int fifo_show(struct seq_file *seq, void *v) { struct dwc2_hsotg *hsotg = seq->private; + int fifo_count = dwc2_hsotg_tx_fifo_count(hsotg); u32 val; int idx; @@ -196,7 +197,7 @@ static int fifo_show(struct seq_file *seq, void *v) seq_puts(seq, "\nPeriodic TXFIFOs:\n"); - for (idx = 1; idx < hsotg->num_of_eps; idx++) { + for (idx = 1; idx <= fifo_count; idx++) { val = dwc2_readl(hsotg, DPTXFSIZN(idx)); seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx, -- cgit v1.2.3-59-g8ed1b From 463f67aec2837f981b0a0ce8617721ff59685c00 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 23 Dec 2019 08:47:35 +0200 Subject: usb: gadget: legacy: set max_speed to super-speed These interfaces do support super-speed so let's not limit maximum speed to high-speed. Cc: Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/legacy/cdc2.c | 2 +- drivers/usb/gadget/legacy/g_ffs.c | 2 +- drivers/usb/gadget/legacy/multi.c | 2 +- drivers/usb/gadget/legacy/ncm.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/legacy/cdc2.c b/drivers/usb/gadget/legacy/cdc2.c index da1c37933ca1..8d7a556ece30 100644 --- a/drivers/usb/gadget/legacy/cdc2.c +++ b/drivers/usb/gadget/legacy/cdc2.c @@ -225,7 +225,7 @@ static struct usb_composite_driver cdc_driver = { .name = "g_cdc", .dev = &device_desc, .strings = dev_strings, - .max_speed = USB_SPEED_HIGH, + .max_speed = USB_SPEED_SUPER, .bind = cdc_bind, .unbind = cdc_unbind, }; diff --git a/drivers/usb/gadget/legacy/g_ffs.c b/drivers/usb/gadget/legacy/g_ffs.c index b640ed3fcf70..ae6d8f7092b8 100644 --- a/drivers/usb/gadget/legacy/g_ffs.c +++ b/drivers/usb/gadget/legacy/g_ffs.c @@ -149,7 +149,7 @@ static struct usb_composite_driver gfs_driver = { .name = DRIVER_NAME, .dev = &gfs_dev_desc, .strings = gfs_dev_strings, - .max_speed = USB_SPEED_HIGH, + .max_speed = USB_SPEED_SUPER, .bind = gfs_bind, .unbind = gfs_unbind, }; diff --git a/drivers/usb/gadget/legacy/multi.c b/drivers/usb/gadget/legacy/multi.c index 50515f9e1022..ec9749845660 100644 --- a/drivers/usb/gadget/legacy/multi.c +++ b/drivers/usb/gadget/legacy/multi.c @@ -482,7 +482,7 @@ static struct usb_composite_driver multi_driver = { .name = "g_multi", .dev = &device_desc, .strings = dev_strings, - .max_speed = USB_SPEED_HIGH, + .max_speed = USB_SPEED_SUPER, .bind = multi_bind, .unbind = multi_unbind, .needs_serial = 1, diff --git a/drivers/usb/gadget/legacy/ncm.c b/drivers/usb/gadget/legacy/ncm.c index 8465f081e921..c61e71ba7045 100644 --- a/drivers/usb/gadget/legacy/ncm.c +++ b/drivers/usb/gadget/legacy/ncm.c @@ -197,7 +197,7 @@ static struct usb_composite_driver ncm_driver = { .name = "g_ncm", .dev = &device_desc, .strings = dev_strings, - .max_speed = USB_SPEED_HIGH, + .max_speed = USB_SPEED_SUPER, .bind = gncm_bind, .unbind = gncm_unbind, }; -- cgit v1.2.3-59-g8ed1b From 1d039a80613dd8b8d95e181f918db5004bd5cc48 Mon Sep 17 00:00:00 2001 From: Dejin Zheng Date: Sat, 4 Jan 2020 21:27:40 +0800 Subject: usb: gadget: udc: core: Warn about failed to find udc If we do not warn here, the user may not know failed to find udc device by a gadget driver with the same name because it silently fails. Let's print a warning in that case so developers find these problems faster. Signed-off-by: Dejin Zheng Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 51fa614b4079..9b11046480fe 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -1414,6 +1414,8 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver) } mutex_unlock(&udc_lock); + if (ret) + pr_warn("udc-core: couldn't find an available UDC or it's busy\n"); return ret; found: ret = udc_bind_to_driver(udc, driver); -- cgit v1.2.3-59-g8ed1b From a02497033e8e04c30501abb78c92d862982b9912 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 8 Jan 2020 18:38:10 -0800 Subject: usb: gadget: configfs: Add max_speed setting Some functions support speeds other than SuperSpeed. Add max_speed attribute to configfs gadget allowing user to specify the maximum speed the composite driver supports. The valid input speed names are super-speed-plus, super-speed, high-speed, full-speed, and low-speed. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/configfs-usb-gadget | 4 +++ drivers/usb/gadget/configfs.c | 43 +++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/Documentation/ABI/testing/configfs-usb-gadget b/Documentation/ABI/testing/configfs-usb-gadget index 95a36589a66b..4594cc2435e8 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget +++ b/Documentation/ABI/testing/configfs-usb-gadget @@ -16,6 +16,10 @@ Description: write UDC's name found in /sys/class/udc/* to bind a gadget, empty string "" to unbind. + max_speed - maximum speed the driver supports. Valid + names are super-speed-plus, super-speed, + high-speed, full-speed, and low-speed. + bDeviceClass - USB device class code bDeviceSubClass - USB device subclass code bDeviceProtocol - USB device protocol code diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index ab9ac48a751a..32b637e3e1fa 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -293,6 +293,47 @@ err: return ret; } +static ssize_t gadget_dev_desc_max_speed_show(struct config_item *item, + char *page) +{ + enum usb_device_speed speed = to_gadget_info(item)->composite.max_speed; + + return sprintf(page, "%s\n", usb_speed_string(speed)); +} + +static ssize_t gadget_dev_desc_max_speed_store(struct config_item *item, + const char *page, size_t len) +{ + struct gadget_info *gi = to_gadget_info(item); + + mutex_lock(&gi->lock); + + /* Prevent changing of max_speed after the driver is binded */ + if (gi->composite.gadget_driver.udc_name) + goto err; + + if (strncmp(page, "super-speed-plus", 16) == 0) + gi->composite.max_speed = USB_SPEED_SUPER_PLUS; + else if (strncmp(page, "super-speed", 11) == 0) + gi->composite.max_speed = USB_SPEED_SUPER; + else if (strncmp(page, "high-speed", 10) == 0) + gi->composite.max_speed = USB_SPEED_HIGH; + else if (strncmp(page, "full-speed", 10) == 0) + gi->composite.max_speed = USB_SPEED_FULL; + else if (strncmp(page, "low-speed", 9) == 0) + gi->composite.max_speed = USB_SPEED_LOW; + else + goto err; + + gi->composite.gadget_driver.max_speed = gi->composite.max_speed; + + mutex_unlock(&gi->lock); + return len; +err: + mutex_unlock(&gi->lock); + return -EINVAL; +} + CONFIGFS_ATTR(gadget_dev_desc_, bDeviceClass); CONFIGFS_ATTR(gadget_dev_desc_, bDeviceSubClass); CONFIGFS_ATTR(gadget_dev_desc_, bDeviceProtocol); @@ -302,6 +343,7 @@ CONFIGFS_ATTR(gadget_dev_desc_, idProduct); CONFIGFS_ATTR(gadget_dev_desc_, bcdDevice); CONFIGFS_ATTR(gadget_dev_desc_, bcdUSB); CONFIGFS_ATTR(gadget_dev_desc_, UDC); +CONFIGFS_ATTR(gadget_dev_desc_, max_speed); static struct configfs_attribute *gadget_root_attrs[] = { &gadget_dev_desc_attr_bDeviceClass, @@ -313,6 +355,7 @@ static struct configfs_attribute *gadget_root_attrs[] = { &gadget_dev_desc_attr_bcdDevice, &gadget_dev_desc_attr_bcdUSB, &gadget_dev_desc_attr_UDC, + &gadget_dev_desc_attr_max_speed, NULL, }; -- cgit v1.2.3-59-g8ed1b From d2450c6937018d40d4111fe830fa48d4ddceb8d0 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 12 Dec 2019 16:35:03 +0800 Subject: usb: gadget: f_fs: set req->num_sgs as 0 for non-sg transfer The UDC core uses req->num_sgs to judge if scatter buffer list is used. Eg: usb_gadget_map_request_by_dev. For f_fs sync io mode, the request is re-used for each request, so if the 1st request->length > PAGE_SIZE, and the 2nd request->length is <= PAGE_SIZE, the f_fs uses the 1st req->num_sgs for the 2nd request, it causes the UDC core get the wrong req->num_sgs value (The 2nd request doesn't use sg). For f_fs async io mode, it is not harm to initialize req->num_sgs as 0 either, in case, the UDC driver doesn't zeroed request structure. Cc: Jun Li Cc: stable Fixes: 772a7a724f69 ("usb: gadget: f_fs: Allow scatter-gather buffers") Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 0bbccac94d6c..6f8b67e61771 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1062,6 +1062,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) req->num_sgs = io_data->sgt.nents; } else { req->buf = data; + req->num_sgs = 0; } req->length = data_len; @@ -1105,6 +1106,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) req->num_sgs = io_data->sgt.nents; } else { req->buf = data; + req->num_sgs = 0; } req->length = data_len; -- cgit v1.2.3-59-g8ed1b From 9c1ed62ae0690dfe5d5e31d8f70e70a95cb48e52 Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Wed, 18 Dec 2019 11:43:49 +0800 Subject: usb: gadget: udc: fix possible sleep-in-atomic-context bugs in gr_probe() The driver may sleep while holding a spinlock. The function call path (from bottom to top) in Linux 4.19 is: drivers/usb/gadget/udc/core.c, 1175: kzalloc(GFP_KERNEL) in usb_add_gadget_udc_release drivers/usb/gadget/udc/core.c, 1272: usb_add_gadget_udc_release in usb_add_gadget_udc drivers/usb/gadget/udc/gr_udc.c, 2186: usb_add_gadget_udc in gr_probe drivers/usb/gadget/udc/gr_udc.c, 2183: spin_lock in gr_probe drivers/usb/gadget/udc/core.c, 1195: mutex_lock in usb_add_gadget_udc_release drivers/usb/gadget/udc/core.c, 1272: usb_add_gadget_udc_release in usb_add_gadget_udc drivers/usb/gadget/udc/gr_udc.c, 2186: usb_add_gadget_udc in gr_probe drivers/usb/gadget/udc/gr_udc.c, 2183: spin_lock in gr_probe drivers/usb/gadget/udc/gr_udc.c, 212: debugfs_create_file in gr_probe drivers/usb/gadget/udc/gr_udc.c, 2197: gr_dfs_create in gr_probe drivers/usb/gadget/udc/gr_udc.c, 2183: spin_lock in gr_probe drivers/usb/gadget/udc/gr_udc.c, 2114: devm_request_threaded_irq in gr_request_irq drivers/usb/gadget/udc/gr_udc.c, 2202: gr_request_irq in gr_probe drivers/usb/gadget/udc/gr_udc.c, 2183: spin_lock in gr_probe kzalloc(GFP_KERNEL), mutex_lock(), debugfs_create_file() and devm_request_threaded_irq() can sleep at runtime. To fix these possible bugs, usb_add_gadget_udc(), gr_dfs_create() and gr_request_irq() are called without handling the spinlock. These bugs are found by a static analysis tool STCheck written by myself. Signed-off-by: Jia-Ju Bai Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/gr_udc.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index 64d80c65bb96..aaf975c809bf 100644 --- a/drivers/usb/gadget/udc/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -2175,8 +2175,6 @@ static int gr_probe(struct platform_device *pdev) return -ENOMEM; } - spin_lock(&dev->lock); - /* Inside lock so that no gadget can use this udc until probe is done */ retval = usb_add_gadget_udc(dev->dev, &dev->gadget); if (retval) { @@ -2185,15 +2183,21 @@ static int gr_probe(struct platform_device *pdev) } dev->added = 1; + spin_lock(&dev->lock); + retval = gr_udc_init(dev); - if (retval) + if (retval) { + spin_unlock(&dev->lock); goto out; - - gr_dfs_create(dev); + } /* Clear all interrupt enables that might be left on since last boot */ gr_disable_interrupts_and_pullup(dev); + spin_unlock(&dev->lock); + + gr_dfs_create(dev); + retval = gr_request_irq(dev, dev->irq); if (retval) { dev_err(dev->dev, "Failed to request irq %d\n", dev->irq); @@ -2222,8 +2226,6 @@ static int gr_probe(struct platform_device *pdev) dev_info(dev->dev, "regs: %p, irq %d\n", dev->regs, dev->irq); out: - spin_unlock(&dev->lock); - if (retval) gr_remove(pdev); -- cgit v1.2.3-59-g8ed1b From 54c4c69f0baa433233a0c33b4ed26bf0659bc5a5 Mon Sep 17 00:00:00 2001 From: Jayshri Pawar Date: Fri, 13 Dec 2019 06:25:42 +0100 Subject: usb: cdns3: Add streams support to cadence USB3 DRD driver This patch includes streams implementation changes. The current changes has been validated on FPGA platform. Enabled streams related interrupts only for streams capable endpoints. Processed PRIME and IOT interrupts related to streams capable endpoints. Based on PRIME interrupt prime_flag is set and transfer is armed otherwise just adding request to the deferred request queue. For streams capable endpoints preparing TD with correct stream ID. TDL calculation: Updated tdl calculation based on controller versions. 1. For controller version DEV_VER_V2 :We have enabled USB_CONF2_EN_TDL_TRB bit in usb_conf2 register in DMULT configuration. This enables TDL calculation based on TRB, hence setting TDL in TRB. 2. For controller Version < DEV_VER_V2 : Writing TDL and STDL in ep_cmd register 3. For controller version > DEV_VER_V2 : Writing TDL in ep_tdl register. Writing ERDY with correct Stream ID to ep_cmd register. Added stream id related information to trace logs. Signed-off-by: Rahul Kumar Signed-off-by: Pawel Laszczak Signed-off-by: Jayshri Pawar Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/gadget.c | 533 +++++++++++++++++++++++++++++++++++++++++---- drivers/usb/cdns3/gadget.h | 26 ++- drivers/usb/cdns3/trace.h | 93 +++++++- 3 files changed, 596 insertions(+), 56 deletions(-) diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index e4820bd4b579..736b0c6e27fe 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -71,6 +71,23 @@ static int __cdns3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, gfp_t gfp_flags); +static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, + struct usb_request *request); + +static int cdns3_ep_run_stream_transfer(struct cdns3_endpoint *priv_ep, + struct usb_request *request); + +/** + * cdns3_clear_register_bit - clear bit in given register. + * @ptr: address of device controller register to be read and changed + * @mask: bits requested to clar + */ +void cdns3_clear_register_bit(void __iomem *ptr, u32 mask) +{ + mask = readl(ptr) & ~mask; + writel(mask, ptr); +} + /** * cdns3_set_register_bit - set bit in given register. * @ptr: address of device controller register to be read and changed @@ -150,6 +167,21 @@ void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep) writel(ep, &priv_dev->regs->ep_sel); } +/** + * cdns3_get_tdl - gets current tdl for selected endpoint. + * @priv_dev: extended gadget object + * + * Before calling this function the appropriate endpoint must + * be selected by means of cdns3_select_ep function. + */ +static int cdns3_get_tdl(struct cdns3_device *priv_dev) +{ + if (priv_dev->dev_ver < DEV_VER_V3) + return EP_CMD_TDL_GET(readl(&priv_dev->regs->ep_cmd)); + else + return readl(&priv_dev->regs->ep_tdl); +} + dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb) { @@ -166,7 +198,22 @@ int cdns3_ring_size(struct cdns3_endpoint *priv_ep) case USB_ENDPOINT_XFER_CONTROL: return TRB_CTRL_RING_SIZE; default: - return TRB_RING_SIZE; + if (priv_ep->use_streams) + return TRB_STREAM_RING_SIZE; + else + return TRB_RING_SIZE; + } +} + +static void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep) +{ + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; + + if (priv_ep->trb_pool) { + dma_free_coherent(priv_dev->sysdev, + cdns3_ring_size(priv_ep), + priv_ep->trb_pool, priv_ep->trb_pool_dma); + priv_ep->trb_pool = NULL; } } @@ -180,8 +227,12 @@ int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep) { struct cdns3_device *priv_dev = priv_ep->cdns3_dev; int ring_size = cdns3_ring_size(priv_ep); + int num_trbs = ring_size / TRB_SIZE; struct cdns3_trb *link_trb; + if (priv_ep->trb_pool && priv_ep->alloc_ring_size < ring_size) + cdns3_free_trb_pool(priv_ep); + if (!priv_ep->trb_pool) { priv_ep->trb_pool = dma_alloc_coherent(priv_dev->sysdev, ring_size, @@ -189,32 +240,30 @@ int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep) GFP_DMA32 | GFP_ATOMIC); if (!priv_ep->trb_pool) return -ENOMEM; - } else { + + priv_ep->alloc_ring_size = ring_size; memset(priv_ep->trb_pool, 0, ring_size); } + priv_ep->num_trbs = num_trbs; + if (!priv_ep->num) return 0; - priv_ep->num_trbs = ring_size / TRB_SIZE; - /* Initialize the last TRB as Link TRB. */ + /* Initialize the last TRB as Link TRB */ link_trb = (priv_ep->trb_pool + (priv_ep->num_trbs - 1)); - link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma); - link_trb->control = TRB_CYCLE | TRB_TYPE(TRB_LINK) | TRB_TOGGLE; - return 0; -} - -static void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep) -{ - struct cdns3_device *priv_dev = priv_ep->cdns3_dev; - - if (priv_ep->trb_pool) { - dma_free_coherent(priv_dev->sysdev, - cdns3_ring_size(priv_ep), - priv_ep->trb_pool, priv_ep->trb_pool_dma); - priv_ep->trb_pool = NULL; + if (priv_ep->use_streams) { + /* + * For stream capable endpoints driver use single correct TRB. + * The last trb has zeroed cycle bit + */ + link_trb->control = 0; + } else { + link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma); + link_trb->control = TRB_CYCLE | TRB_TYPE(TRB_LINK) | TRB_TOGGLE; } + return 0; } /** @@ -253,6 +302,7 @@ void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev) priv_dev->onchip_used_size = 0; priv_dev->out_mem_is_allocated = 0; priv_dev->wait_for_setup = 0; + priv_dev->using_streams = 0; } /** @@ -356,17 +406,43 @@ static int cdns3_start_all_request(struct cdns3_device *priv_dev, { struct usb_request *request; int ret = 0; + u8 pending_empty = list_empty(&priv_ep->pending_req_list); + + /* + * If the last pending transfer is INTERNAL + * OR streams are enabled for this endpoint + * do NOT start new transfer till the last one is pending + */ + if (!pending_empty) { + struct cdns3_request *priv_req; + + request = cdns3_next_request(&priv_ep->pending_req_list); + priv_req = to_cdns3_request(request); + if ((priv_req->flags & REQUEST_INTERNAL) || + (priv_ep->flags & EP_TDLCHK_EN) || + priv_ep->use_streams) { + trace_printk("Blocking external request\n"); + return ret; + } + } while (!list_empty(&priv_ep->deferred_req_list)) { request = cdns3_next_request(&priv_ep->deferred_req_list); - ret = cdns3_ep_run_transfer(priv_ep, request); + if (!priv_ep->use_streams) { + ret = cdns3_ep_run_transfer(priv_ep, request); + } else { + priv_ep->stream_sg_idx = 0; + ret = cdns3_ep_run_stream_transfer(priv_ep, request); + } if (ret) return ret; list_del(&request->list); list_add_tail(&request->list, &priv_ep->pending_req_list); + if (request->stream_id != 0 || (priv_ep->flags & EP_TDLCHK_EN)) + break; } priv_ep->flags &= ~EP_RING_FULL; @@ -379,7 +455,7 @@ static int cdns3_start_all_request(struct cdns3_device *priv_dev, * buffer for unblocking on-chip FIFO buffer. This flag will be cleared * if before first DESCMISS interrupt the DMA will be armed. */ -#define cdns3_wa2_enable_detection(priv_dev, ep_priv, reg) do { \ +#define cdns3_wa2_enable_detection(priv_dev, priv_ep, reg) do { \ if (!priv_ep->dir && priv_ep->type != USB_ENDPOINT_XFER_ISOC) { \ priv_ep->flags |= EP_QUIRK_EXTRA_BUF_DET; \ (reg) |= EP_STS_EN_DESCMISEN; \ @@ -450,10 +526,17 @@ struct usb_request *cdns3_wa2_gadget_giveback(struct cdns3_device *priv_dev, if (!req) return NULL; + /* unmap the gadget request before copying data */ + usb_gadget_unmap_request_by_dev(priv_dev->sysdev, req, + priv_ep->dir); + cdns3_wa2_descmiss_copy_data(priv_ep, req); if (!(priv_ep->flags & EP_QUIRK_END_TRANSFER) && req->length != req->actual) { /* wait for next part of transfer */ + /* re-map the gadget request buffer*/ + usb_gadget_map_request_by_dev(priv_dev->sysdev, req, + usb_endpoint_dir_in(priv_ep->endpoint.desc)); return NULL; } @@ -570,6 +653,13 @@ static void cdns3_wa2_descmissing_packet(struct cdns3_endpoint *priv_ep) { struct cdns3_request *priv_req; struct usb_request *request; + u8 pending_empty = list_empty(&priv_ep->pending_req_list); + + /* check for pending transfer */ + if (!pending_empty) { + trace_cdns3_wa2(priv_ep, "Ignoring Descriptor missing IRQ\n"); + return; + } if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) { priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET; @@ -578,8 +668,10 @@ static void cdns3_wa2_descmissing_packet(struct cdns3_endpoint *priv_ep) trace_cdns3_wa2(priv_ep, "Description Missing detected\n"); - if (priv_ep->wa2_counter >= CDNS3_WA2_NUM_BUFFERS) + if (priv_ep->wa2_counter >= CDNS3_WA2_NUM_BUFFERS) { + trace_cdns3_wa2(priv_ep, "WA2 overflow\n"); cdns3_wa2_remove_old_request(priv_ep); + } request = cdns3_gadget_ep_alloc_request(&priv_ep->endpoint, GFP_ATOMIC); @@ -621,6 +713,78 @@ err: "Failed: No sufficient memory for DESCMIS\n"); } +static void cdns3_wa2_reset_tdl(struct cdns3_device *priv_dev) +{ + u16 tdl = EP_CMD_TDL_GET(readl(&priv_dev->regs->ep_cmd)); + + if (tdl) { + u16 reset_val = EP_CMD_TDL_MAX + 1 - tdl; + + writel(EP_CMD_TDL_SET(reset_val) | EP_CMD_STDL, + &priv_dev->regs->ep_cmd); + } +} + +static void cdns3_wa2_check_outq_status(struct cdns3_device *priv_dev) +{ + u32 ep_sts_reg; + + /* select EP0-out */ + cdns3_select_ep(priv_dev, 0); + + ep_sts_reg = readl(&priv_dev->regs->ep_sts); + + if (EP_STS_OUTQ_VAL(ep_sts_reg)) { + u32 outq_ep_num = EP_STS_OUTQ_NO(ep_sts_reg); + struct cdns3_endpoint *outq_ep = priv_dev->eps[outq_ep_num]; + + if ((outq_ep->flags & EP_ENABLED) && !(outq_ep->use_streams) && + outq_ep->type != USB_ENDPOINT_XFER_ISOC && outq_ep_num) { + u8 pending_empty = list_empty(&outq_ep->pending_req_list); + + if ((outq_ep->flags & EP_QUIRK_EXTRA_BUF_DET) || + (outq_ep->flags & EP_QUIRK_EXTRA_BUF_EN) || + !pending_empty) { + } else { + u32 ep_sts_en_reg; + u32 ep_cmd_reg; + + cdns3_select_ep(priv_dev, outq_ep->num | + outq_ep->dir); + ep_sts_en_reg = readl(&priv_dev->regs->ep_sts_en); + ep_cmd_reg = readl(&priv_dev->regs->ep_cmd); + + outq_ep->flags |= EP_TDLCHK_EN; + cdns3_set_register_bit(&priv_dev->regs->ep_cfg, + EP_CFG_TDL_CHK); + + cdns3_wa2_enable_detection(priv_dev, outq_ep, + ep_sts_en_reg); + writel(ep_sts_en_reg, + &priv_dev->regs->ep_sts_en); + /* reset tdl value to zero */ + cdns3_wa2_reset_tdl(priv_dev); + /* + * Memory barrier - Reset tdl before ringing the + * doorbell. + */ + wmb(); + if (EP_CMD_DRDY & ep_cmd_reg) { + trace_cdns3_wa2(outq_ep, "Enabling WA2 skipping doorbell\n"); + + } else { + trace_cdns3_wa2(outq_ep, "Enabling WA2 ringing doorbell\n"); + /* + * ring doorbell to generate DESCMIS irq + */ + writel(EP_CMD_DRDY, + &priv_dev->regs->ep_cmd); + } + } + } + } +} + /** * cdns3_gadget_giveback - call struct usb_request's ->complete callback * @priv_ep: The endpoint to whom the request belongs to @@ -807,14 +971,120 @@ static void cdns3_wa1_tray_restore_cycle_bit(struct cdns3_device *priv_dev, cdns3_wa1_restore_cycle_bit(priv_ep); } +static int cdns3_ep_run_stream_transfer(struct cdns3_endpoint *priv_ep, + struct usb_request *request) +{ + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; + struct cdns3_request *priv_req; + struct cdns3_trb *trb; + dma_addr_t trb_dma; + int address; + u32 control; + u32 length; + u32 tdl; + unsigned int sg_idx = priv_ep->stream_sg_idx; + + priv_req = to_cdns3_request(request); + address = priv_ep->endpoint.desc->bEndpointAddress; + + priv_ep->flags |= EP_PENDING_REQUEST; + + /* must allocate buffer aligned to 8 */ + if (priv_req->flags & REQUEST_UNALIGNED) + trb_dma = priv_req->aligned_buf->dma; + else + trb_dma = request->dma; + + /* For stream capable endpoints driver use only single TD. */ + trb = priv_ep->trb_pool + priv_ep->enqueue; + priv_req->start_trb = priv_ep->enqueue; + priv_req->end_trb = priv_req->start_trb; + priv_req->trb = trb; + + cdns3_select_ep(priv_ep->cdns3_dev, address); + + control = TRB_TYPE(TRB_NORMAL) | TRB_CYCLE | + TRB_STREAM_ID(priv_req->request.stream_id) | TRB_ISP; + + if (!request->num_sgs) { + trb->buffer = TRB_BUFFER(trb_dma); + length = request->length; + } else { + trb->buffer = TRB_BUFFER(request->sg[sg_idx].dma_address); + length = request->sg[sg_idx].length; + } + + tdl = DIV_ROUND_UP(length, priv_ep->endpoint.maxpacket); + + trb->length = TRB_BURST_LEN(16 /*priv_ep->trb_burst_size*/) | + TRB_LEN(length); + + /* + * For DEV_VER_V2 controller version we have enabled + * USB_CONF2_EN_TDL_TRB in DMULT configuration. + * This enables TDL calculation based on TRB, hence setting TDL in TRB. + */ + if (priv_dev->dev_ver >= DEV_VER_V2) { + if (priv_dev->gadget.speed == USB_SPEED_SUPER) + trb->length |= TRB_TDL_SS_SIZE(tdl); + } + priv_req->flags |= REQUEST_PENDING; + + trb->control = control; + + trace_cdns3_prepare_trb(priv_ep, priv_req->trb); + + /* + * Memory barrier - Cycle Bit must be set before trb->length and + * trb->buffer fields. + */ + wmb(); + + /* always first element */ + writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma), + &priv_dev->regs->ep_traddr); + + if (!(priv_ep->flags & EP_STALLED)) { + trace_cdns3_ring(priv_ep); + /*clearing TRBERR and EP_STS_DESCMIS before seting DRDY*/ + writel(EP_STS_TRBERR | EP_STS_DESCMIS, &priv_dev->regs->ep_sts); + + priv_ep->prime_flag = false; + + /* + * Controller version DEV_VER_V2 tdl calculation + * is based on TRB + */ + + if (priv_dev->dev_ver < DEV_VER_V2) + writel(EP_CMD_TDL_SET(tdl) | EP_CMD_STDL, + &priv_dev->regs->ep_cmd); + else if (priv_dev->dev_ver > DEV_VER_V2) + writel(tdl, &priv_dev->regs->ep_tdl); + + priv_ep->last_stream_id = priv_req->request.stream_id; + writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd); + writel(EP_CMD_ERDY_SID(priv_req->request.stream_id) | + EP_CMD_ERDY, &priv_dev->regs->ep_cmd); + + trace_cdns3_doorbell_epx(priv_ep->name, + readl(&priv_dev->regs->ep_traddr)); + } + + /* WORKAROUND for transition to L0 */ + __cdns3_gadget_wakeup(priv_dev); + + return 0; +} + /** * cdns3_ep_run_transfer - start transfer on no-default endpoint hardware * @priv_ep: endpoint object * * Returns zero on success or negative value on failure */ -int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, - struct usb_request *request) +static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, + struct usb_request *request) { struct cdns3_device *priv_dev = priv_ep->cdns3_dev; struct cdns3_request *priv_req; @@ -826,6 +1096,7 @@ int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, int address; u32 control; int pcs; + u16 total_tdl = 0; if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) num_trb = priv_ep->interval; @@ -910,6 +1181,9 @@ int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, if (likely(priv_dev->dev_ver >= DEV_VER_V2)) td_size = DIV_ROUND_UP(length, priv_ep->endpoint.maxpacket); + else if (priv_ep->flags & EP_TDLCHK_EN) + total_tdl += DIV_ROUND_UP(length, + priv_ep->endpoint.maxpacket); trb->length = TRB_BURST_LEN(priv_ep->trb_burst_size) | TRB_LEN(length); @@ -954,6 +1228,23 @@ int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, if (sg_iter == 1) trb->control |= TRB_IOC | TRB_ISP; + if (priv_dev->dev_ver < DEV_VER_V2 && + (priv_ep->flags & EP_TDLCHK_EN)) { + u16 tdl = total_tdl; + u16 old_tdl = EP_CMD_TDL_GET(readl(&priv_dev->regs->ep_cmd)); + + if (tdl > EP_CMD_TDL_MAX) { + tdl = EP_CMD_TDL_MAX; + priv_ep->pending_tdl = total_tdl - EP_CMD_TDL_MAX; + } + + if (old_tdl < tdl) { + tdl -= old_tdl; + writel(EP_CMD_TDL_SET(tdl) | EP_CMD_STDL, + &priv_dev->regs->ep_cmd); + } + } + /* * Memory barrier - cycle bit must be set before other filds in trb. */ @@ -1153,29 +1444,56 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev, cdns3_move_deq_to_next_trb(priv_req); } - /* Re-select endpoint. It could be changed by other CPU during - * handling usb_gadget_giveback_request. - */ - cdns3_select_ep(priv_dev, priv_ep->endpoint.address); + if (!request->stream_id) { + /* Re-select endpoint. It could be changed by other CPU + * during handling usb_gadget_giveback_request. + */ + cdns3_select_ep(priv_dev, priv_ep->endpoint.address); - if (!cdns3_request_handled(priv_ep, priv_req)) - goto prepare_next_td; + if (!cdns3_request_handled(priv_ep, priv_req)) + goto prepare_next_td; - trb = priv_ep->trb_pool + priv_ep->dequeue; - trace_cdns3_complete_trb(priv_ep, trb); + trb = priv_ep->trb_pool + priv_ep->dequeue; + trace_cdns3_complete_trb(priv_ep, trb); + + if (trb != priv_req->trb) + dev_warn(priv_dev->dev, + "request_trb=0x%p, queue_trb=0x%p\n", + priv_req->trb, trb); + + request->actual = TRB_LEN(le32_to_cpu(trb->length)); + cdns3_move_deq_to_next_trb(priv_req); + cdns3_gadget_giveback(priv_ep, priv_req, 0); + + if (priv_ep->type != USB_ENDPOINT_XFER_ISOC && + TRBS_PER_SEGMENT == 2) + break; + } else { + /* Re-select endpoint. It could be changed by other CPU + * during handling usb_gadget_giveback_request. + */ + cdns3_select_ep(priv_dev, priv_ep->endpoint.address); + + trb = priv_ep->trb_pool; + trace_cdns3_complete_trb(priv_ep, trb); - if (trb != priv_req->trb) - dev_warn(priv_dev->dev, - "request_trb=0x%p, queue_trb=0x%p\n", - priv_req->trb, trb); + if (trb != priv_req->trb) + dev_warn(priv_dev->dev, + "request_trb=0x%p, queue_trb=0x%p\n", + priv_req->trb, trb); - request->actual = TRB_LEN(le32_to_cpu(trb->length)); - cdns3_move_deq_to_next_trb(priv_req); - cdns3_gadget_giveback(priv_ep, priv_req, 0); + request->actual += TRB_LEN(le32_to_cpu(trb->length)); - if (priv_ep->type != USB_ENDPOINT_XFER_ISOC && - TRBS_PER_SEGMENT == 2) + if (!request->num_sgs || + (request->num_sgs == (priv_ep->stream_sg_idx + 1))) { + priv_ep->stream_sg_idx = 0; + cdns3_gadget_giveback(priv_ep, priv_req, 0); + } else { + priv_ep->stream_sg_idx++; + cdns3_ep_run_stream_transfer(priv_ep, request); + } break; + } } priv_ep->flags &= ~EP_PENDING_REQUEST; @@ -1205,6 +1523,21 @@ void cdns3_rearm_transfer(struct cdns3_endpoint *priv_ep, u8 rearm) } } +static void cdns3_reprogram_tdl(struct cdns3_endpoint *priv_ep) +{ + u16 tdl = priv_ep->pending_tdl; + struct cdns3_device *priv_dev = priv_ep->cdns3_dev; + + if (tdl > EP_CMD_TDL_MAX) { + tdl = EP_CMD_TDL_MAX; + priv_ep->pending_tdl -= EP_CMD_TDL_MAX; + } else { + priv_ep->pending_tdl = 0; + } + + writel(EP_CMD_TDL_SET(tdl) | EP_CMD_STDL, &priv_dev->regs->ep_cmd); +} + /** * cdns3_check_ep_interrupt_proceed - Processes interrupt related to endpoint * @priv_ep: endpoint object @@ -1215,6 +1548,9 @@ static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep) { struct cdns3_device *priv_dev = priv_ep->cdns3_dev; u32 ep_sts_reg; + struct usb_request *deferred_request; + struct usb_request *pending_request; + u32 tdl = 0; cdns3_select_ep(priv_dev, priv_ep->endpoint.address); @@ -1223,6 +1559,36 @@ static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep) ep_sts_reg = readl(&priv_dev->regs->ep_sts); writel(ep_sts_reg, &priv_dev->regs->ep_sts); + if ((ep_sts_reg & EP_STS_PRIME) && priv_ep->use_streams) { + bool dbusy = !!(ep_sts_reg & EP_STS_DBUSY); + + tdl = cdns3_get_tdl(priv_dev); + + /* + * Continue the previous transfer: + * There is some racing between ERDY and PRIME. The device send + * ERDY and almost in the same time Host send PRIME. It cause + * that host ignore the ERDY packet and driver has to send it + * again. + */ + if (tdl && (dbusy | !EP_STS_BUFFEMPTY(ep_sts_reg) | + EP_STS_HOSTPP(ep_sts_reg))) { + writel(EP_CMD_ERDY | + EP_CMD_ERDY_SID(priv_ep->last_stream_id), + &priv_dev->regs->ep_cmd); + ep_sts_reg &= ~(EP_STS_MD_EXIT | EP_STS_IOC); + } else { + priv_ep->prime_flag = true; + + pending_request = cdns3_next_request(&priv_ep->pending_req_list); + deferred_request = cdns3_next_request(&priv_ep->deferred_req_list); + + if (deferred_request && !pending_request) { + cdns3_start_all_request(priv_dev, priv_ep); + } + } + } + if (ep_sts_reg & EP_STS_TRBERR) { if (priv_ep->flags & EP_STALL_PENDING && !(ep_sts_reg & EP_STS_DESCMIS && @@ -1259,7 +1625,8 @@ static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep) } } - if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) { + if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP) || + (ep_sts_reg & EP_STS_IOT)) { if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) { if (ep_sts_reg & EP_STS_ISP) priv_ep->flags |= EP_QUIRK_END_TRANSFER; @@ -1267,6 +1634,29 @@ static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep) priv_ep->flags &= ~EP_QUIRK_END_TRANSFER; } + if (!priv_ep->use_streams) { + if ((ep_sts_reg & EP_STS_IOC) || + (ep_sts_reg & EP_STS_ISP)) { + cdns3_transfer_completed(priv_dev, priv_ep); + } else if ((priv_ep->flags & EP_TDLCHK_EN) & + priv_ep->pending_tdl) { + /* handle IOT with pending tdl */ + cdns3_reprogram_tdl(priv_ep); + } + } else if (priv_ep->dir == USB_DIR_OUT) { + priv_ep->ep_sts_pending |= ep_sts_reg; + } else if (ep_sts_reg & EP_STS_IOT) { + cdns3_transfer_completed(priv_dev, priv_ep); + } + } + + /* + * MD_EXIT interrupt sets when stream capable endpoint exits + * from MOVE DATA state of Bulk IN/OUT stream protocol state machine + */ + if (priv_ep->dir == USB_DIR_OUT && (ep_sts_reg & EP_STS_MD_EXIT) && + (priv_ep->ep_sts_pending & EP_STS_IOT) && priv_ep->use_streams) { + priv_ep->ep_sts_pending = 0; cdns3_transfer_completed(priv_dev, priv_ep); } @@ -1274,7 +1664,7 @@ static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep) * WA2: this condition should only be meet when * priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET or * priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN. - * In other cases this interrupt will be disabled/ + * In other cases this interrupt will be disabled. */ if (ep_sts_reg & EP_STS_DESCMIS && priv_dev->dev_ver < DEV_VER_V2 && !(priv_ep->flags & EP_STALLED)) @@ -1457,6 +1847,9 @@ static irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data) ret = IRQ_HANDLED; } + if (priv_dev->dev_ver < DEV_VER_V2 && priv_dev->using_streams) + cdns3_wa2_check_outq_status(priv_dev); + irqend: writel(~0, &priv_dev->regs->ep_ien); spin_unlock_irqrestore(&priv_dev->lock, flags); @@ -1511,6 +1904,27 @@ static int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev, return 0; } +void cdns3_stream_ep_reconfig(struct cdns3_device *priv_dev, + struct cdns3_endpoint *priv_ep) +{ + if (!priv_ep->use_streams || priv_dev->gadget.speed < USB_SPEED_SUPER) + return; + + if (priv_dev->dev_ver >= DEV_VER_V3) { + u32 mask = BIT(priv_ep->num + (priv_ep->dir ? 16 : 0)); + + /* + * Stream capable endpoints are handled by using ep_tdl + * register. Other endpoints use TDL from TRB feature. + */ + cdns3_clear_register_bit(&priv_dev->regs->tdl_from_trb, mask); + } + + /* Enable Stream Bit TDL chk and SID chk */ + cdns3_set_register_bit(&priv_dev->regs->ep_cfg, EP_CFG_STREAM_EN | + EP_CFG_TDL_CHK | EP_CFG_SID_CHK); +} + void cdns3_configure_dmult(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep) { @@ -1772,6 +2186,7 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep, { struct cdns3_endpoint *priv_ep; struct cdns3_device *priv_dev; + const struct usb_ss_ep_comp_descriptor *comp_desc; u32 reg = EP_STS_EN_TRBERREN; u32 bEndpointAddress; unsigned long flags; @@ -1781,6 +2196,7 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep, priv_ep = ep_to_cdns3_ep(ep); priv_dev = priv_ep->cdns3_dev; + comp_desc = priv_ep->endpoint.comp_desc; if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) { dev_dbg(priv_dev->dev, "usbss: invalid parameters\n"); @@ -1811,6 +2227,24 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep, goto exit; } + bEndpointAddress = priv_ep->num | priv_ep->dir; + cdns3_select_ep(priv_dev, bEndpointAddress); + + if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) { + /* + * Enable stream support (SS mode) related interrupts + * in EP_STS_EN Register + */ + if (priv_dev->gadget.speed >= USB_SPEED_SUPER) { + reg |= EP_STS_EN_IOTEN | EP_STS_EN_PRIMEEEN | + EP_STS_EN_SIDERREN | EP_STS_EN_MD_EXITEN | + EP_STS_EN_STREAMREN; + priv_ep->use_streams = true; + cdns3_stream_ep_reconfig(priv_dev, priv_ep); + priv_dev->using_streams |= true; + } + } + ret = cdns3_allocate_trb_pool(priv_ep); if (ret) @@ -1957,6 +2391,7 @@ static int cdns3_gadget_ep_disable(struct usb_ep *ep) ep->desc = NULL; priv_ep->flags &= ~EP_ENABLED; + priv_ep->use_streams = false; spin_unlock_irqrestore(&priv_dev->lock, flags); @@ -2005,13 +2440,21 @@ static int __cdns3_gadget_ep_queue(struct usb_ep *ep, list_add_tail(&request->list, &priv_ep->deferred_req_list); /* + * For stream capable endpoint if prime irq flag is set then only start + * request. * If hardware endpoint configuration has not been set yet then * just queue request in deferred list. Transfer will be started in * cdns3_set_hw_configuration. */ - if (priv_dev->hw_configured_flag && !(priv_ep->flags & EP_STALLED) && - !(priv_ep->flags & EP_STALL_PENDING)) - cdns3_start_all_request(priv_dev, priv_ep); + if (!request->stream_id) { + if (priv_dev->hw_configured_flag && + !(priv_ep->flags & EP_STALLED) && + !(priv_ep->flags & EP_STALL_PENDING)) + cdns3_start_all_request(priv_dev, priv_ep); + } else { + if (priv_dev->hw_configured_flag && priv_ep->prime_flag) + cdns3_start_all_request(priv_dev, priv_ep); + } return 0; } diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h index bc4024041ef2..f003a7801872 100644 --- a/drivers/usb/cdns3/gadget.h +++ b/drivers/usb/cdns3/gadget.h @@ -599,6 +599,7 @@ struct cdns3_usb_regs { #define EP_CMD_TDL_MASK GENMASK(15, 9) #define EP_CMD_TDL_SET(p) (((p) << 9) & EP_CMD_TDL_MASK) #define EP_CMD_TDL_GET(p) (((p) & EP_CMD_TDL_MASK) >> 9) +#define EP_CMD_TDL_MAX (EP_CMD_TDL_MASK >> 9) /* ERDY Stream ID value (used in SS mode). */ #define EP_CMD_ERDY_SID_MASK GENMASK(31, 16) @@ -969,10 +970,18 @@ struct cdns3_usb_regs { #define ISO_MAX_INTERVAL 10 +#define MAX_TRB_LENGTH BIT(16) + #if TRBS_PER_SEGMENT < 2 #error "Incorrect TRBS_PER_SEGMENT. Minimal Transfer Ring size is 2." #endif +#define TRBS_PER_STREAM_SEGMENT 2 + +#if TRBS_PER_STREAM_SEGMENT < 2 +#error "Incorrect TRBS_PER_STREAMS_SEGMENT. Minimal Transfer Ring size is 2." +#endif + /* *Only for ISOC endpoints - maximum number of TRBs is calculated as * pow(2, bInterval-1) * number of usb requests. It is limitation made by @@ -1000,6 +1009,7 @@ struct cdns3_trb { #define TRB_SIZE (sizeof(struct cdns3_trb)) #define TRB_RING_SIZE (TRB_SIZE * TRBS_PER_SEGMENT) +#define TRB_STREAM_RING_SIZE (TRB_SIZE * TRBS_PER_STREAM_SEGMENT) #define TRB_ISO_RING_SIZE (TRB_SIZE * TRBS_PER_ISOC_SEGMENT) #define TRB_CTRL_RING_SIZE (TRB_SIZE * 2) @@ -1078,7 +1088,7 @@ struct cdns3_trb { #define CDNS3_ENDPOINTS_MAX_COUNT 32 #define CDNS3_EP_ZLP_BUF_SIZE 1024 -#define CDNS3_EP_BUF_SIZE 2 /* KB */ +#define CDNS3_EP_BUF_SIZE 4 /* KB */ #define CDNS3_EP_ISO_HS_MULT 3 #define CDNS3_EP_ISO_SS_BURST 3 #define CDNS3_MAX_NUM_DESCMISS_BUF 32 @@ -1109,6 +1119,7 @@ struct cdns3_device; * @interval: interval between packets used for ISOC endpoint. * @free_trbs: number of free TRBs in transfer ring * @num_trbs: number of all TRBs in transfer ring + * @alloc_ring_size: size of the allocated TRB ring * @pcs: producer cycle state * @ccs: consumer cycle state * @enqueue: enqueue index in transfer ring @@ -1142,6 +1153,7 @@ struct cdns3_endpoint { #define EP_QUIRK_END_TRANSFER BIT(11) #define EP_QUIRK_EXTRA_BUF_DET BIT(12) #define EP_QUIRK_EXTRA_BUF_EN BIT(13) +#define EP_TDLCHK_EN BIT(15) u32 flags; struct cdns3_request *descmis_req; @@ -1153,6 +1165,7 @@ struct cdns3_endpoint { int free_trbs; int num_trbs; + int alloc_ring_size; u8 pcs; u8 ccs; int enqueue; @@ -1163,6 +1176,14 @@ struct cdns3_endpoint { struct cdns3_trb *wa1_trb; unsigned int wa1_trb_index; unsigned int wa1_cycle_bit:1; + + /* Stream related */ + unsigned int use_streams:1; + unsigned int prime_flag:1; + u32 ep_sts_pending; + u16 last_stream_id; + u16 pending_tdl; + unsigned int stream_sg_idx; }; /** @@ -1290,6 +1311,7 @@ struct cdns3_device { int hw_configured_flag:1; int wake_up_flag:1; unsigned status_completion_no_call:1; + unsigned using_streams:1; int out_mem_is_allocated; struct work_struct pending_status_wq; @@ -1310,8 +1332,6 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev); void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep); void cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable); struct usb_request *cdns3_next_request(struct list_head *list); -int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, - struct usb_request *request); void cdns3_rearm_transfer(struct cdns3_endpoint *priv_ep, u8 rearm); int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep); u8 cdns3_ep_addr_to_index(u8 ep_addr); diff --git a/drivers/usb/cdns3/trace.h b/drivers/usb/cdns3/trace.h index e92348c9b4d7..8d121e207fd8 100644 --- a/drivers/usb/cdns3/trace.h +++ b/drivers/usb/cdns3/trace.h @@ -122,18 +122,24 @@ DECLARE_EVENT_CLASS(cdns3_log_epx_irq, __string(ep_name, priv_ep->name) __field(u32, ep_sts) __field(u32, ep_traddr) + __field(u32, ep_last_sid) + __field(u32, use_streams) __dynamic_array(char, str, CDNS3_MSG_MAX) ), TP_fast_assign( __assign_str(ep_name, priv_ep->name); __entry->ep_sts = readl(&priv_dev->regs->ep_sts); __entry->ep_traddr = readl(&priv_dev->regs->ep_traddr); + __entry->ep_last_sid = priv_ep->last_stream_id; + __entry->use_streams = priv_ep->use_streams; ), - TP_printk("%s, ep_traddr: %08x", + TP_printk("%s, ep_traddr: %08x ep_last_sid: %08x use_streams: %d", cdns3_decode_epx_irq(__get_str(str), __get_str(ep_name), __entry->ep_sts), - __entry->ep_traddr) + __entry->ep_traddr, + __entry->ep_last_sid, + __entry->use_streams) ); DEFINE_EVENT(cdns3_log_epx_irq, cdns3_epx_irq, @@ -210,6 +216,7 @@ DECLARE_EVENT_CLASS(cdns3_log_request, __field(int, end_trb) __field(struct cdns3_trb *, start_trb_addr) __field(int, flags) + __field(unsigned int, stream_id) ), TP_fast_assign( __assign_str(name, req->priv_ep->name); @@ -225,9 +232,10 @@ DECLARE_EVENT_CLASS(cdns3_log_request, __entry->end_trb = req->end_trb; __entry->start_trb_addr = req->trb; __entry->flags = req->flags; + __entry->stream_id = req->request.stream_id; ), TP_printk("%s: req: %p, req buff %p, length: %u/%u %s%s%s, status: %d," - " trb: [start:%d, end:%d: virt addr %pa], flags:%x ", + " trb: [start:%d, end:%d: virt addr %pa], flags:%x SID: %u", __get_str(name), __entry->req, __entry->buf, __entry->actual, __entry->length, __entry->zero ? "Z" : "z", @@ -237,7 +245,8 @@ DECLARE_EVENT_CLASS(cdns3_log_request, __entry->start_trb, __entry->end_trb, __entry->start_trb_addr, - __entry->flags + __entry->flags, + __entry->stream_id ) ); @@ -281,6 +290,39 @@ TRACE_EVENT(cdns3_ep0_queue, __entry->length) ); +DECLARE_EVENT_CLASS(cdns3_stream_split_transfer_len, + TP_PROTO(struct cdns3_request *req), + TP_ARGS(req), + TP_STRUCT__entry( + __string(name, req->priv_ep->name) + __field(struct cdns3_request *, req) + __field(unsigned int, length) + __field(unsigned int, actual) + __field(unsigned int, stream_id) + ), + TP_fast_assign( + __assign_str(name, req->priv_ep->name); + __entry->req = req; + __entry->actual = req->request.length; + __entry->length = req->request.actual; + __entry->stream_id = req->request.stream_id; + ), + TP_printk("%s: req: %p,request length: %u actual length: %u SID: %u", + __get_str(name), __entry->req, __entry->length, + __entry->actual, __entry->stream_id) +); + +DEFINE_EVENT(cdns3_stream_split_transfer_len, cdns3_stream_transfer_split, + TP_PROTO(struct cdns3_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(cdns3_stream_split_transfer_len, + cdns3_stream_transfer_split_next_part, + TP_PROTO(struct cdns3_request *req), + TP_ARGS(req) +); + DECLARE_EVENT_CLASS(cdns3_log_aligned_request, TP_PROTO(struct cdns3_request *priv_req), TP_ARGS(priv_req), @@ -319,6 +361,34 @@ DEFINE_EVENT(cdns3_log_aligned_request, cdns3_prepare_aligned_request, TP_ARGS(req) ); +DECLARE_EVENT_CLASS(cdns3_log_map_request, + TP_PROTO(struct cdns3_request *priv_req), + TP_ARGS(priv_req), + TP_STRUCT__entry( + __string(name, priv_req->priv_ep->name) + __field(struct usb_request *, req) + __field(void *, buf) + __field(dma_addr_t, dma) + ), + TP_fast_assign( + __assign_str(name, priv_req->priv_ep->name); + __entry->req = &priv_req->request; + __entry->buf = priv_req->request.buf; + __entry->dma = priv_req->request.dma; + ), + TP_printk("%s: req: %p, req buf %p, dma %p", + __get_str(name), __entry->req, __entry->buf, &__entry->dma + ) +); +DEFINE_EVENT(cdns3_log_map_request, cdns3_map_request, + TP_PROTO(struct cdns3_request *req), + TP_ARGS(req) +); +DEFINE_EVENT(cdns3_log_map_request, cdns3_mapped_request, + TP_PROTO(struct cdns3_request *req), + TP_ARGS(req) +); + DECLARE_EVENT_CLASS(cdns3_log_trb, TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb), TP_ARGS(priv_ep, trb), @@ -329,6 +399,7 @@ DECLARE_EVENT_CLASS(cdns3_log_trb, __field(u32, length) __field(u32, control) __field(u32, type) + __field(unsigned int, last_stream_id) ), TP_fast_assign( __assign_str(name, priv_ep->name); @@ -337,8 +408,9 @@ DECLARE_EVENT_CLASS(cdns3_log_trb, __entry->length = trb->length; __entry->control = trb->control; __entry->type = usb_endpoint_type(priv_ep->endpoint.desc); + __entry->last_stream_id = priv_ep->last_stream_id; ), - TP_printk("%s: trb %p, dma buf: 0x%08x, size: %ld, burst: %d ctrl: 0x%08x (%s%s%s%s%s%s%s)", + TP_printk("%s: trb %p, dma buf: 0x%08x, size: %ld, burst: %d ctrl: 0x%08x (%s%s%s%s%s%s%s) SID:%lu LAST_SID:%u", __get_str(name), __entry->trb, __entry->buffer, TRB_LEN(__entry->length), (u8)TRB_BURST_LEN_GET(__entry->length), @@ -349,7 +421,9 @@ DECLARE_EVENT_CLASS(cdns3_log_trb, __entry->control & TRB_FIFO_MODE ? "FIFO, " : "", __entry->control & TRB_CHAIN ? "CHAIN, " : "", __entry->control & TRB_IOC ? "IOC, " : "", - TRB_FIELD_TO_TYPE(__entry->control) == TRB_NORMAL ? "Normal" : "LINK" + TRB_FIELD_TO_TYPE(__entry->control) == TRB_NORMAL ? "Normal" : "LINK", + TRB_FIELD_TO_STREAMID(__entry->control), + __entry->last_stream_id ) ); @@ -398,6 +472,7 @@ DECLARE_EVENT_CLASS(cdns3_log_ep, __field(unsigned int, maxpacket) __field(unsigned int, maxpacket_limit) __field(unsigned int, max_streams) + __field(unsigned int, use_streams) __field(unsigned int, maxburst) __field(unsigned int, flags) __field(unsigned int, dir) @@ -409,16 +484,18 @@ DECLARE_EVENT_CLASS(cdns3_log_ep, __entry->maxpacket = priv_ep->endpoint.maxpacket; __entry->maxpacket_limit = priv_ep->endpoint.maxpacket_limit; __entry->max_streams = priv_ep->endpoint.max_streams; + __entry->use_streams = priv_ep->use_streams; __entry->maxburst = priv_ep->endpoint.maxburst; __entry->flags = priv_ep->flags; __entry->dir = priv_ep->dir; __entry->enqueue = priv_ep->enqueue; __entry->dequeue = priv_ep->dequeue; ), - TP_printk("%s: mps: %d/%d. streams: %d, burst: %d, enq idx: %d, " - "deq idx: %d, flags %s%s%s%s%s%s%s%s, dir: %s", + TP_printk("%s: mps: %d/%d. streams: %d, stream enable: %d, burst: %d, " + "enq idx: %d, deq idx: %d, flags %s%s%s%s%s%s%s%s, dir: %s", __get_str(name), __entry->maxpacket, __entry->maxpacket_limit, __entry->max_streams, + __entry->use_streams, __entry->maxburst, __entry->enqueue, __entry->dequeue, __entry->flags & EP_ENABLED ? "EN | " : "", -- cgit v1.2.3-59-g8ed1b From 09ed259fac621634d51cd986aa8d65f035662658 Mon Sep 17 00:00:00 2001 From: Bin Liu Date: Wed, 11 Dec 2019 10:10:03 -0600 Subject: usb: dwc3: turn off VBUS when leaving host mode VBUS should be turned off when leaving the host mode. Set GCTL_PRTCAP to device mode in teardown to de-assert DRVVBUS pin to turn off VBUS power. Fixes: 5f94adfeed97 ("usb: dwc3: core: refactor mode initialization to its own function") Cc: stable@vger.kernel.org Signed-off-by: Bin Liu Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index f561c6c9e8a9..1d85c42b9c67 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1246,6 +1246,9 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc) /* do nothing */ break; } + + /* de-assert DRVVBUS for HOST and OTG mode */ + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE); } static void dwc3_get_properties(struct dwc3 *dwc) -- cgit v1.2.3-59-g8ed1b From 1e056efab9931366d1e1685736dfc978eca3bf06 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 9 Jan 2020 17:35:58 +0800 Subject: usb: cdns3: add NXP imx8qm glue layer There is a Cadence USB3 core for imx8qm and imx8qxp SoCs, the cdns core is the child for this glue layer device. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/Kconfig | 10 ++ drivers/usb/cdns3/Makefile | 1 + drivers/usb/cdns3/cdns3-imx.c | 216 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 227 insertions(+) create mode 100644 drivers/usb/cdns3/cdns3-imx.c diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig index 2a1e89d12ed9..84716d216ae5 100644 --- a/drivers/usb/cdns3/Kconfig +++ b/drivers/usb/cdns3/Kconfig @@ -53,4 +53,14 @@ config USB_CDNS3_TI e.g. J721e. +config USB_CDNS3_IMX + tristate "Cadence USB3 support on NXP i.MX platforms" + depends on ARCH_MXC || COMPILE_TEST + default USB_CDNS3 + help + Say 'Y' or 'M' here if you are building for NXP i.MX + platforms that contain Cadence USB3 controller core. + + For example, imx8qm and imx8qxp. + endif diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile index 948e6b88d1a9..d47e341a6f39 100644 --- a/drivers/usb/cdns3/Makefile +++ b/drivers/usb/cdns3/Makefile @@ -15,3 +15,4 @@ cdns3-$(CONFIG_USB_CDNS3_HOST) += host.o obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci-wrap.o obj-$(CONFIG_USB_CDNS3_TI) += cdns3-ti.o +obj-$(CONFIG_USB_CDNS3_IMX) += cdns3-imx.o diff --git a/drivers/usb/cdns3/cdns3-imx.c b/drivers/usb/cdns3/cdns3-imx.c new file mode 100644 index 000000000000..aba988e71958 --- /dev/null +++ b/drivers/usb/cdns3/cdns3-imx.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * cdns3-imx.c - NXP i.MX specific Glue layer for Cadence USB Controller + * + * Copyright (C) 2019 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USB3_CORE_CTRL1 0x00 +#define USB3_CORE_CTRL2 0x04 +#define USB3_INT_REG 0x08 +#define USB3_CORE_STATUS 0x0c +#define XHCI_DEBUG_LINK_ST 0x10 +#define XHCI_DEBUG_BUS 0x14 +#define USB3_SSPHY_CTRL1 0x40 +#define USB3_SSPHY_CTRL2 0x44 +#define USB3_SSPHY_STATUS 0x4c +#define USB2_PHY_CTRL1 0x50 +#define USB2_PHY_CTRL2 0x54 +#define USB2_PHY_STATUS 0x5c + +/* Register bits definition */ + +/* USB3_CORE_CTRL1 */ +#define SW_RESET_MASK (0x3f << 26) +#define PWR_SW_RESET BIT(31) +#define APB_SW_RESET BIT(30) +#define AXI_SW_RESET BIT(29) +#define RW_SW_RESET BIT(28) +#define PHY_SW_RESET BIT(27) +#define PHYAHB_SW_RESET BIT(26) +#define ALL_SW_RESET (PWR_SW_RESET | APB_SW_RESET | AXI_SW_RESET | \ + RW_SW_RESET | PHY_SW_RESET | PHYAHB_SW_RESET) +#define OC_DISABLE BIT(9) +#define MDCTRL_CLK_SEL BIT(7) +#define MODE_STRAP_MASK (0x7) +#define DEV_MODE (1 << 2) +#define HOST_MODE (1 << 1) +#define OTG_MODE (1 << 0) + +/* USB3_INT_REG */ +#define CLK_125_REQ BIT(29) +#define LPM_CLK_REQ BIT(28) +#define DEVU3_WAEKUP_EN BIT(14) +#define OTG_WAKEUP_EN BIT(12) +#define DEV_INT_EN (3 << 8) /* DEV INT b9:8 */ +#define HOST_INT1_EN (1 << 0) /* HOST INT b7:0 */ + +/* USB3_CORE_STATUS */ +#define MDCTRL_CLK_STATUS BIT(15) +#define DEV_POWER_ON_READY BIT(13) +#define HOST_POWER_ON_READY BIT(12) + +/* USB3_SSPHY_STATUS */ +#define CLK_VALID_MASK (0x3f << 26) +#define CLK_VALID_COMPARE_BITS (0xf << 28) +#define PHY_REFCLK_REQ (1 << 0) + +struct cdns_imx { + struct device *dev; + void __iomem *noncore; + struct clk_bulk_data *clks; + int num_clks; +}; + +static inline u32 cdns_imx_readl(struct cdns_imx *data, u32 offset) +{ + return readl(data->noncore + offset); +} + +static inline void cdns_imx_writel(struct cdns_imx *data, u32 offset, u32 value) +{ + writel(value, data->noncore + offset); +} + +static const struct clk_bulk_data imx_cdns3_core_clks[] = { + { .id = "usb3_lpm_clk" }, + { .id = "usb3_bus_clk" }, + { .id = "usb3_aclk" }, + { .id = "usb3_ipg_clk" }, + { .id = "usb3_core_pclk" }, +}; + +static int cdns_imx_noncore_init(struct cdns_imx *data) +{ + u32 value; + int ret; + struct device *dev = data->dev; + + cdns_imx_writel(data, USB3_SSPHY_STATUS, CLK_VALID_MASK); + udelay(1); + ret = readl_poll_timeout(data->noncore + USB3_SSPHY_STATUS, value, + (value & CLK_VALID_COMPARE_BITS) == CLK_VALID_COMPARE_BITS, + 10, 100000); + if (ret) { + dev_err(dev, "wait clkvld timeout\n"); + return ret; + } + + value = cdns_imx_readl(data, USB3_CORE_CTRL1); + value |= ALL_SW_RESET; + cdns_imx_writel(data, USB3_CORE_CTRL1, value); + udelay(1); + + value = cdns_imx_readl(data, USB3_CORE_CTRL1); + value = (value & ~MODE_STRAP_MASK) | OTG_MODE | OC_DISABLE; + cdns_imx_writel(data, USB3_CORE_CTRL1, value); + + value = cdns_imx_readl(data, USB3_INT_REG); + value |= HOST_INT1_EN | DEV_INT_EN; + cdns_imx_writel(data, USB3_INT_REG, value); + + value = cdns_imx_readl(data, USB3_CORE_CTRL1); + value &= ~ALL_SW_RESET; + cdns_imx_writel(data, USB3_CORE_CTRL1, value); + return ret; +} + +static int cdns_imx_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct cdns_imx *data; + int ret; + + if (!node) + return -ENODEV; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + platform_set_drvdata(pdev, data); + data->dev = dev; + data->noncore = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(data->noncore)) { + dev_err(dev, "can't map IOMEM resource\n"); + return PTR_ERR(data->noncore); + } + + data->num_clks = ARRAY_SIZE(imx_cdns3_core_clks); + data->clks = (struct clk_bulk_data *)imx_cdns3_core_clks; + ret = devm_clk_bulk_get(dev, data->num_clks, data->clks); + if (ret) + return ret; + + ret = clk_bulk_prepare_enable(data->num_clks, data->clks); + if (ret) + return ret; + + ret = cdns_imx_noncore_init(data); + if (ret) + goto err; + + ret = of_platform_populate(node, NULL, NULL, dev); + if (ret) { + dev_err(dev, "failed to create children: %d\n", ret); + goto err; + } + + return ret; + +err: + clk_bulk_disable_unprepare(data->num_clks, data->clks); + return ret; +} + +static int cdns_imx_remove_core(struct device *dev, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + + platform_device_unregister(pdev); + + return 0; +} + +static int cdns_imx_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + device_for_each_child(dev, NULL, cdns_imx_remove_core); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static const struct of_device_id cdns_imx_of_match[] = { + { .compatible = "fsl,imx8qm-usb3", }, + {}, +}; +MODULE_DEVICE_TABLE(of, cdns_imx_of_match); + +static struct platform_driver cdns_imx_driver = { + .probe = cdns_imx_probe, + .remove = cdns_imx_remove, + .driver = { + .name = "cdns3-imx", + .of_match_table = cdns_imx_of_match, + }, +}; +module_platform_driver(cdns_imx_driver); + +MODULE_ALIAS("platform:cdns3-imx"); +MODULE_AUTHOR("Peter Chen "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Cadence USB3 i.MX Glue Layer"); -- cgit v1.2.3-59-g8ed1b From 6b02af3465ee11b63a938b13bddbf7ecd92860f3 Mon Sep 17 00:00:00 2001 From: John Keeping Date: Fri, 10 Jan 2020 11:28:14 +0000 Subject: usb: gadget: f_uac2: fix packet size calculation The packet size for USB audio must always be a multiple of the frame size, otherwise we are transmitting a partial frame which omits some channels (and these end up at the wrong offset in the next packet). Furthermore, it breaks the residue handling such that we end up trying to send a packet exceeding the maximum packet size for the endpoint. Signed-off-by: John Keeping Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/u_audio.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index cf4f2358889b..6d956f190f5a 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -407,7 +407,7 @@ int u_audio_start_playback(struct g_audio *audio_dev) struct usb_ep *ep; struct uac_rtd_params *prm; struct uac_params *params = &audio_dev->params; - unsigned int factor, rate; + unsigned int factor; const struct usb_endpoint_descriptor *ep_desc; int req_len, i; @@ -426,13 +426,15 @@ int u_audio_start_playback(struct g_audio *audio_dev) /* pre-compute some values for iso_complete() */ uac->p_framesize = params->p_ssize * num_channels(params->p_chmask); - rate = params->p_srate * uac->p_framesize; uac->p_interval = factor / (1 << (ep_desc->bInterval - 1)); - uac->p_pktsize = min_t(unsigned int, rate / uac->p_interval, + uac->p_pktsize = min_t(unsigned int, + uac->p_framesize * + (params->p_srate / uac->p_interval), prm->max_psize); if (uac->p_pktsize < prm->max_psize) - uac->p_pktsize_residue = rate % uac->p_interval; + uac->p_pktsize_residue = uac->p_framesize * + (params->p_srate % uac->p_interval); else uac->p_pktsize_residue = 0; -- cgit v1.2.3-59-g8ed1b From c58d8bfc77a2c7f6ff6339b58c9fca7ae6f57e70 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 18 Dec 2019 18:14:44 -0800 Subject: usb: dwc3: gadget: Check END_TRANSFER completion While the END_TRANSFER command is sent but not completed, any request dequeue during this time will cause the driver to issue the END_TRANSFER command. The driver needs to submit the command only once to stop the controller from processing further. The controller may take more time to process the same command multiple times unnecessarily. Let's add a flag DWC3_EP_END_TRANSFER_PENDING to check for this condition. Fixes: 3aec99154db3 ("usb: dwc3: gadget: remove DWC3_EP_END_TRANSFER_PENDING") Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.h | 1 + drivers/usb/dwc3/ep0.c | 4 +++- drivers/usb/dwc3/gadget.c | 6 +++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 1c8b349379af..da0af11fbc1a 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -688,6 +688,7 @@ struct dwc3_ep { #define DWC3_EP_STALL BIT(1) #define DWC3_EP_WEDGE BIT(2) #define DWC3_EP_TRANSFER_STARTED BIT(3) +#define DWC3_EP_END_TRANSFER_PENDING BIT(4) #define DWC3_EP_PENDING_REQUEST BIT(5) /* This last one is specific to EP0 */ diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index fd1b100d2927..6dee4dabc0a4 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -1136,8 +1136,10 @@ void dwc3_ep0_interrupt(struct dwc3 *dwc, case DWC3_DEPEVT_EPCMDCMPLT: cmd = DEPEVT_PARAMETER_CMD(event->parameters); - if (cmd == DWC3_DEPCMD_ENDTRANSFER) + if (cmd == DWC3_DEPCMD_ENDTRANSFER) { + dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING; dep->flags &= ~DWC3_EP_TRANSFER_STARTED; + } break; } } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index edc478c20846..cf163dcc5524 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2628,6 +2628,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, cmd = DEPEVT_PARAMETER_CMD(event->parameters); if (cmd == DWC3_DEPCMD_ENDTRANSFER) { + dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING; dep->flags &= ~DWC3_EP_TRANSFER_STARTED; dwc3_gadget_ep_cleanup_cancelled_requests(dep); } @@ -2686,7 +2687,8 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, u32 cmd; int ret; - if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) + if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) || + (dep->flags & DWC3_EP_END_TRANSFER_PENDING)) return; /* @@ -2731,6 +2733,8 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, if (!interrupt) dep->flags &= ~DWC3_EP_TRANSFER_STARTED; + else + dep->flags |= DWC3_EP_END_TRANSFER_PENDING; if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A) udelay(100); -- cgit v1.2.3-59-g8ed1b From da10bcdd6f70dc9977f2cf18f4783cf78520623a Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 18 Dec 2019 18:14:50 -0800 Subject: usb: dwc3: gadget: Delay starting transfer If the END_TRANSFER command hasn't completed yet, then don't send the START_TRANSFER command. The controller may not be able to start if that's the case. Some controller revisions depend on this. See commit 76a638f8ac0d ("usb: dwc3: gadget: wait for End Transfer to complete"). Let's only send START_TRANSFER command after the END_TRANSFER command had completed. Fixes: 3aec99154db3 ("usb: dwc3: gadget: remove DWC3_EP_END_TRANSFER_PENDING") Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.h | 1 + drivers/usb/dwc3/gadget.c | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index da0af11fbc1a..77c4a9abe365 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -690,6 +690,7 @@ struct dwc3_ep { #define DWC3_EP_TRANSFER_STARTED BIT(3) #define DWC3_EP_END_TRANSFER_PENDING BIT(4) #define DWC3_EP_PENDING_REQUEST BIT(5) +#define DWC3_EP_DELAY_START BIT(6) /* This last one is specific to EP0 */ #define DWC3_EP0_DIR_IN BIT(31) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index cf163dcc5524..696fbeb8d03a 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1450,6 +1450,12 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) list_add_tail(&req->list, &dep->pending_list); req->status = DWC3_REQUEST_STATUS_QUEUED; + /* Start the transfer only after the END_TRANSFER is completed */ + if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) { + dep->flags |= DWC3_EP_DELAY_START; + return 0; + } + /* * NOTICE: Isochronous endpoints should NEVER be prestarted. We must * wait for a XferNotReady event so we will know what's the current @@ -2631,6 +2637,11 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING; dep->flags &= ~DWC3_EP_TRANSFER_STARTED; dwc3_gadget_ep_cleanup_cancelled_requests(dep); + if ((dep->flags & DWC3_EP_DELAY_START) && + !usb_endpoint_xfer_isoc(dep->endpoint.desc)) + __dwc3_gadget_kick_transfer(dep); + + dep->flags &= ~DWC3_EP_DELAY_START; } break; case DWC3_DEPEVT_STREAMEVT: -- cgit v1.2.3-59-g8ed1b From cf2f8b63f7f1f6763e4fcdf89145312224ee736b Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 18 Dec 2019 18:14:56 -0800 Subject: usb: dwc3: gadget: Remove END_TRANSFER delay We had a 100us delay to synchronize the END_TRANSFER command completion before giving back requests to the function drivers. Now, the controller driver can handle cancelled TRBs with the requests' cancelled_list and it can also wait until the END_TRANSFER completion before starting new transfers. Synchronization can simply base on the controller's command completion interrupt. The 100us delay is no longer needed. Remove this arbitrary delay. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/gadget.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 696fbeb8d03a..1b8014ab0b25 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2693,7 +2693,6 @@ static void dwc3_reset_gadget(struct dwc3 *dwc) static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt) { - struct dwc3 *dwc = dep->dwc; struct dwc3_gadget_ep_cmd_params params; u32 cmd; int ret; @@ -2709,16 +2708,13 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, * much trouble synchronizing between us and gadget driver. * * We have discussed this with the IP Provider and it was - * suggested to giveback all requests here, but give HW some - * extra time to synchronize with the interconnect. We're using - * an arbitrary 100us delay for that. + * suggested to giveback all requests here. * * Note also that a similar handling was tested by Synopsys * (thanks a lot Paul) and nothing bad has come out of it. - * In short, what we're doing is: - * - * - Issue EndTransfer WITH CMDIOC bit set - * - Wait 100us + * In short, what we're doing is issuing EndTransfer with + * CMDIOC bit set and delay kicking transfer until the + * EndTransfer command had completed. * * As of IP version 3.10a of the DWC_usb3 IP, the controller * supports a mode to work around the above limitation. The @@ -2727,8 +2723,7 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, * by writing GUCTL2[14]. This polling is already done in the * dwc3_send_gadget_ep_cmd() function so if the mode is * enabled, the EndTransfer command will have completed upon - * returning from this function and we don't need to delay for - * 100us. + * returning from this function. * * This mode is NOT available on the DWC_usb31 IP. */ @@ -2746,9 +2741,6 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, dep->flags &= ~DWC3_EP_TRANSFER_STARTED; else dep->flags |= DWC3_EP_END_TRANSFER_PENDING; - - if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A) - udelay(100); } static void dwc3_clear_stall_all_ep(struct dwc3 *dwc) -- cgit v1.2.3-59-g8ed1b From 5b24c28cfe136597dc3913e1c00b119307a20c7e Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Thu, 9 Jan 2020 13:17:21 +0000 Subject: usb: gadget: f_ncm: Use atomic_t to track in-flight request Currently ncm->notify_req is used to flag when a request is in-flight. ncm->notify_req is set to NULL and when a request completes it is subsequently reset. This is fundamentally buggy in that the unbind logic of the NCM driver will unconditionally free ncm->notify_req leading to a NULL pointer dereference. Fixes: 40d133d7f542 ("usb: gadget: f_ncm: convert to new function interface with backward compatibility") Cc: stable Signed-off-by: Bryan O'Donoghue Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_ncm.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 2d6e76e4cffa..1d900081b1f0 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -53,6 +53,7 @@ struct f_ncm { struct usb_ep *notify; struct usb_request *notify_req; u8 notify_state; + atomic_t notify_count; bool is_open; const struct ndp_parser_opts *parser_opts; @@ -547,7 +548,7 @@ static void ncm_do_notify(struct f_ncm *ncm) int status; /* notification already in flight? */ - if (!req) + if (atomic_read(&ncm->notify_count)) return; event = req->buf; @@ -587,7 +588,8 @@ static void ncm_do_notify(struct f_ncm *ncm) event->bmRequestType = 0xA1; event->wIndex = cpu_to_le16(ncm->ctrl_id); - ncm->notify_req = NULL; + atomic_inc(&ncm->notify_count); + /* * In double buffering if there is a space in FIFO, * completion callback can be called right after the call, @@ -597,7 +599,7 @@ static void ncm_do_notify(struct f_ncm *ncm) status = usb_ep_queue(ncm->notify, req, GFP_ATOMIC); spin_lock(&ncm->lock); if (status < 0) { - ncm->notify_req = req; + atomic_dec(&ncm->notify_count); DBG(cdev, "notify --> %d\n", status); } } @@ -632,17 +634,19 @@ static void ncm_notify_complete(struct usb_ep *ep, struct usb_request *req) case 0: VDBG(cdev, "Notification %02x sent\n", event->bNotificationType); + atomic_dec(&ncm->notify_count); break; case -ECONNRESET: case -ESHUTDOWN: + atomic_set(&ncm->notify_count, 0); ncm->notify_state = NCM_NOTIFY_NONE; break; default: DBG(cdev, "event %02x --> %d\n", event->bNotificationType, req->status); + atomic_dec(&ncm->notify_count); break; } - ncm->notify_req = req; ncm_do_notify(ncm); spin_unlock(&ncm->lock); } @@ -1649,6 +1653,11 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) ncm_string_defs[0].id = 0; usb_free_all_descriptors(f); + if (atomic_read(&ncm->notify_count)) { + usb_ep_dequeue(ncm->notify, ncm->notify_req); + atomic_set(&ncm->notify_count, 0); + } + kfree(ncm->notify_req->buf); usb_ep_free_request(ncm->notify, ncm->notify_req); } -- cgit v1.2.3-59-g8ed1b From d710562e01c48d59be3f60d58b7a85958b39aeda Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Thu, 9 Jan 2020 13:17:22 +0000 Subject: usb: gadget: f_ecm: Use atomic_t to track in-flight request Currently ecm->notify_req is used to flag when a request is in-flight. ecm->notify_req is set to NULL and when a request completes it is subsequently reset. This is fundamentally buggy in that the unbind logic of the ECM driver will unconditionally free ecm->notify_req leading to a NULL pointer dereference. Fixes: da741b8c56d6 ("usb ethernet gadget: split CDC Ethernet function") Cc: stable Signed-off-by: Bryan O'Donoghue Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_ecm.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c index 460d5d7c984f..7f5cf488b2b1 100644 --- a/drivers/usb/gadget/function/f_ecm.c +++ b/drivers/usb/gadget/function/f_ecm.c @@ -52,6 +52,7 @@ struct f_ecm { struct usb_ep *notify; struct usb_request *notify_req; u8 notify_state; + atomic_t notify_count; bool is_open; /* FIXME is_open needs some irq-ish locking @@ -380,7 +381,7 @@ static void ecm_do_notify(struct f_ecm *ecm) int status; /* notification already in flight? */ - if (!req) + if (atomic_read(&ecm->notify_count)) return; event = req->buf; @@ -420,10 +421,10 @@ static void ecm_do_notify(struct f_ecm *ecm) event->bmRequestType = 0xA1; event->wIndex = cpu_to_le16(ecm->ctrl_id); - ecm->notify_req = NULL; + atomic_inc(&ecm->notify_count); status = usb_ep_queue(ecm->notify, req, GFP_ATOMIC); if (status < 0) { - ecm->notify_req = req; + atomic_dec(&ecm->notify_count); DBG(cdev, "notify --> %d\n", status); } } @@ -448,17 +449,19 @@ static void ecm_notify_complete(struct usb_ep *ep, struct usb_request *req) switch (req->status) { case 0: /* no fault */ + atomic_dec(&ecm->notify_count); break; case -ECONNRESET: case -ESHUTDOWN: + atomic_set(&ecm->notify_count, 0); ecm->notify_state = ECM_NOTIFY_NONE; break; default: DBG(cdev, "event %02x --> %d\n", event->bNotificationType, req->status); + atomic_dec(&ecm->notify_count); break; } - ecm->notify_req = req; ecm_do_notify(ecm); } @@ -907,6 +910,11 @@ static void ecm_unbind(struct usb_configuration *c, struct usb_function *f) usb_free_all_descriptors(f); + if (atomic_read(&ecm->notify_count)) { + usb_ep_dequeue(ecm->notify, ecm->notify_req); + atomic_set(&ecm->notify_count, 0); + } + kfree(ecm->notify_req->buf); usb_ep_free_request(ecm->notify, ecm->notify_req); } -- cgit v1.2.3-59-g8ed1b From d8bc3bf8deede6d9c32f97b6a256264609ce2baa Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Wed, 20 Nov 2019 11:15:15 +0100 Subject: usb: dwc2: Drop unlock/lock upon queueing a work item The original dwc_otg driver used a DWC_WORKQ_SCHEDULE() wrapper to queue work items. Because that wrapper acquired the driver's global spinlock, an unlock/lock dance was necessary whenever a work item was queued up while the global spinlock was already held. The dwc2 driver dropped DWC_WORKQ_SCHEDULE() in favor of a direct call to queue_work(), but retained the (now gratuitous) unlock/lock dance in dwc2_handle_conn_id_status_change_intr(). Drop it. Signed-off-by: Lukas Wunner Acked-by: Minas Harutyunyan Acked-by: Felipe Balbi Link: https://lore.kernel.org/r/77c07f00a6a9d94323c4a060a3c72817b0703b97.1574244795.git.lukas@wunner.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc2/core_intr.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 6af6add3d4c0..876ff31261d5 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -288,14 +288,9 @@ static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg) /* * Need to schedule a work, as there are possible DELAY function calls. - * Release lock before scheduling workq as it holds spinlock during - * scheduling. */ - if (hsotg->wq_otg) { - spin_unlock(&hsotg->lock); + if (hsotg->wq_otg) queue_work(hsotg->wq_otg, &hsotg->wf_otg); - spin_lock(&hsotg->lock); - } } /** -- cgit v1.2.3-59-g8ed1b From 9f101a73b085efbd1f6708f51a57f56fdd46a11b Mon Sep 17 00:00:00 2001 From: Saurav Girepunje Date: Wed, 15 Jan 2020 07:25:23 -0600 Subject: usb: musb: core: Update the function description Update the function description of musb_stage0_irq() to remove unused parameter. Signed-off-by: Saurav Girepunje [b-liu@ti.com: revised commit log] Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-2-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 5ebf30bd61bd..48d95850152d 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -909,7 +909,6 @@ static void musb_handle_intr_reset(struct musb *musb) * @param musb instance pointer * @param int_usb register contents * @param devctl - * @param power */ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, -- cgit v1.2.3-59-g8ed1b From 908f6fc3a14050961210f9754855f9e0eccb6d46 Mon Sep 17 00:00:00 2001 From: Mans Rullgard Date: Wed, 15 Jan 2020 07:25:24 -0600 Subject: usb: musb: sunxi: propagate devicetree node to glue pdev In order for devicetree nodes to be correctly associated with attached devices, the controller node needs to be propagated to the glue device. Signed-off-by: Mans Rullgard Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-3-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/sunxi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c index 832a41f9ee7d..a72665fbf111 100644 --- a/drivers/usb/musb/sunxi.c +++ b/drivers/usb/musb/sunxi.c @@ -781,6 +781,8 @@ static int sunxi_musb_probe(struct platform_device *pdev) pinfo.name = "musb-hdrc"; pinfo.id = PLATFORM_DEVID_AUTO; pinfo.parent = &pdev->dev; + pinfo.fwnode = of_fwnode_handle(pdev->dev.of_node); + pinfo.of_node_reused = true; pinfo.res = pdev->resource; pinfo.num_res = pdev->num_resources; pinfo.data = &pdata; -- cgit v1.2.3-59-g8ed1b From 1b569569a955c53b00a04d86cd84e60efe4c14be Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 15 Jan 2020 07:25:25 -0600 Subject: usb: musb: jz4740: Drop dependency on NOP_USB_XCEIV The driver does not depend directly on the NOP transceiver. It can compile and work just fine without it. Signed-off-by: Paul Cercueil Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-4-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 52f8e2b57ad5..4678ebc889b5 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -111,7 +111,6 @@ config USB_MUSB_UX500 config USB_MUSB_JZ4740 tristate "JZ4740" - depends on NOP_USB_XCEIV depends on MIPS || COMPILE_TEST depends on USB_MUSB_GADGET depends on USB=n || USB_OTG_BLACKLIST_HUB -- cgit v1.2.3-59-g8ed1b From 91b6dec32e5c25fbdbb564d1e5af23764ec17ef1 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 15 Jan 2020 07:25:26 -0600 Subject: usb: musb: omap2430: Get rid of musb .set_vbus for omap2430 glue We currently have musb_set_vbus() called from two different paths. Mostly it gets called from the USB PHY via omap_musb_set_mailbox(), but in some cases it can get also called from musb_stage0_irq() rather via .set_vbus: (musb_set_host [musb_hdrc]) (omap2430_musb_set_vbus [omap2430]) (musb_stage0_irq [musb_hdrc]) (musb_interrupt [musb_hdrc]) (omap2430_musb_interrupt [omap2430]) This is racy and will not work with introducing generic helper functions for musb_set_host() and musb_set_peripheral(). We want to get rid of the busy loops in favor of usleep_range(). Let's just get rid of .set_vbus for omap2430 glue layer and let the PHY code handle VBUS with musb_set_vbus(). Note that in the follow-up patch we can completely remove omap2430_musb_set_vbus(), but let's do it in a separate patch as this change may actually turn out to be needed as a fix. Reported-by: Pavel Machek Acked-by: Pavel Machek Signed-off-by: Tony Lindgren Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-5-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/omap2430.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index a3d2fef67746..5c93226e0e20 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -361,8 +361,6 @@ static const struct musb_platform_ops omap2430_ops = { .init = omap2430_musb_init, .exit = omap2430_musb_exit, - .set_vbus = omap2430_musb_set_vbus, - .enable = omap2430_musb_enable, .disable = omap2430_musb_disable, -- cgit v1.2.3-59-g8ed1b From ce3ab6503ebaba976405858616d7f6094e364973 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 15 Jan 2020 07:25:27 -0600 Subject: usb: musb: omap2430: Wait on enable to avoid babble We can get babble interrupt if we attempt to switch to USB host mode too soon after enabling musb. Let's fix the issue by waiting a bit in runtime_resume. Cc: Jacopo Mondi Cc: Marcel Partap Cc: Merlijn Wajer Cc: Michael Scott Cc: NeKit Cc: Pavel Machek Cc: Sebastian Reichel Acked-by: Pavel Machek Signed-off-by: Tony Lindgren Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-6-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/omap2430.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 5c93226e0e20..920862c3fc64 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -550,6 +550,9 @@ static int omap2430_runtime_resume(struct device *dev) musb_writel(musb->mregs, OTG_INTERFSEL, musb->context.otg_interfsel); + /* Wait for musb to get oriented. Otherwise we can get babble */ + usleep_range(200000, 250000); + return 0; } -- cgit v1.2.3-59-g8ed1b From 15f1122f92c02f654037d8fb98164b785207b7af Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 15 Jan 2020 07:25:28 -0600 Subject: usb: musb: omap2430: Handle multiple ID ground interrupts We currently get "unhandled DISCONNECT transition" warnings from musb core on device disconnect as things are wrongly set to OTG_STATE_A_IDLE in host mode when enumerating devices. We can also get "Failed to write reg index" errors after enumerating. This is happening at least with cpcap phy where we get multiple ID ground interrupts. Looks like it's VBUS keeps timing out and needs to be kicked when the phy sends multiple ID ground interrupts during host mode. Cc: Jacopo Mondi Cc: Marcel Partap Cc: Merlijn Wajer Cc: Michael Scott Cc: NeKit Cc: Pavel Machek Cc: Sebastian Reichel Acked-by: Pavel Machek Signed-off-by: Tony Lindgren Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-7-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/omap2430.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 920862c3fc64..e572ee624128 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -151,13 +151,26 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) switch (glue->status) { case MUSB_ID_GROUND: dev_dbg(musb->controller, "ID GND\n"); - - musb->xceiv->otg->state = OTG_STATE_A_IDLE; - musb->xceiv->last_event = USB_EVENT_ID; - if (musb->gadget_driver) { - omap_control_usb_set_mode(glue->control_otghs, - USB_MODE_HOST); + switch (musb->xceiv->otg->state) { + case OTG_STATE_A_WAIT_VRISE: + case OTG_STATE_A_WAIT_BCON: + case OTG_STATE_A_HOST: + case OTG_STATE_A_IDLE: + /* + * On multiple ID ground interrupts just keep enabling + * VBUS. At least cpcap VBUS shuts down otherwise. + */ omap2430_musb_set_vbus(musb, 1); + break; + default: + musb->xceiv->otg->state = OTG_STATE_A_IDLE; + musb->xceiv->last_event = USB_EVENT_ID; + if (musb->gadget_driver) { + omap_control_usb_set_mode(glue->control_otghs, + USB_MODE_HOST); + omap2430_musb_set_vbus(musb, 1); + } + break; } break; -- cgit v1.2.3-59-g8ed1b From 93dc256871297071a24f7f9ce0abfddfff15094d Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 15 Jan 2020 07:25:29 -0600 Subject: usb: musb: Add musb_set_host and peripheral and use them for omap2430 At least some revisions of musb core need to set devctl session bit in peripheral mode to force musb to host mode. And we have places clearing the devctl session bit. Let's add a generic function to do this, and use it for omap2430. This should get us a bit closer to completely removing devctl register tinkering in the SoC glue code. Before making use of this code for the other glue layers, things need to be tested carefully as there may be a approximately a 200 ms delay needed between powering up musb and calling musb_set_host() to avoid. Otherwise the system hangs at least with omap2430 glue layer. Acked-by: Pavel Machek Signed-off-by: Tony Lindgren [b-liu@ti.com: fixed "line over 80 characters" warning] Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-8-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_core.c | 103 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/musb/musb_core.h | 3 ++ drivers/usb/musb/omap2430.c | 71 +++++++++-------------------- 3 files changed, 128 insertions(+), 49 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 48d95850152d..34090707d122 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -73,6 +73,7 @@ #include #include #include +#include #include #include #include @@ -414,6 +415,108 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) return hw_ep->musb->io.write_fifo(hw_ep, len, src); } +static u8 musb_read_devctl(struct musb *musb) +{ + return musb_readb(musb->mregs, MUSB_DEVCTL); +} + +/** + * musb_set_host - set and initialize host mode + * @musb: musb controller driver data + * + * At least some musb revisions need to enable devctl session bit in + * peripheral mode to switch to host mode. Initializes things to host + * mode and sets A_IDLE. SoC glue needs to advance state further + * based on phy provided VBUS state. + * + * Note that the SoC glue code may need to wait for musb to settle + * on enable before calling this to avoid babble. + */ +int musb_set_host(struct musb *musb) +{ + int error = 0; + u8 devctl; + + if (!musb) + return -EINVAL; + + devctl = musb_read_devctl(musb); + if (!(devctl & MUSB_DEVCTL_BDEVICE)) { + dev_info(musb->controller, + "%s: already in host mode: %02x\n", + __func__, devctl); + goto init_data; + } + + devctl |= MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + + error = readx_poll_timeout(musb_read_devctl, musb, devctl, + !(devctl & MUSB_DEVCTL_BDEVICE), 5000, + 1000000); + if (error) { + dev_err(musb->controller, "%s: could not set host: %02x\n", + __func__, devctl); + + return error; + } + +init_data: + musb->is_active = 1; + musb->xceiv->otg->state = OTG_STATE_A_IDLE; + MUSB_HST_MODE(musb); + + return error; +} +EXPORT_SYMBOL_GPL(musb_set_host); + +/** + * musb_set_peripheral - set and initialize peripheral mode + * @musb: musb controller driver data + * + * Clears devctl session bit and initializes things for peripheral + * mode and sets B_IDLE. SoC glue needs to advance state further + * based on phy provided VBUS state. + */ +int musb_set_peripheral(struct musb *musb) +{ + int error = 0; + u8 devctl; + + if (!musb) + return -EINVAL; + + devctl = musb_read_devctl(musb); + if (devctl & MUSB_DEVCTL_BDEVICE) { + dev_info(musb->controller, + "%s: already in peripheral mode: %02x\n", + __func__, devctl); + + goto init_data; + } + + devctl &= ~MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + + error = readx_poll_timeout(musb_read_devctl, musb, devctl, + devctl & MUSB_DEVCTL_BDEVICE, 5000, + 1000000); + if (error) { + dev_err(musb->controller, "%s: could not set periperal: %02x\n", + __func__, devctl); + + return error; + } + +init_data: + musb->is_active = 0; + musb->xceiv->otg->state = OTG_STATE_B_IDLE; + MUSB_DEV_MODE(musb); + + return error; +} +EXPORT_SYMBOL_GPL(musb_set_peripheral); + /*-------------------------------------------------------------------------*/ /* for high speed test mode; see USB 2.0 spec 7.1.20 */ diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 04203b7126d5..8a13a46cd891 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -487,6 +487,9 @@ extern void musb_start(struct musb *musb); extern void musb_write_fifo(struct musb_hw_ep *ep, u16 len, const u8 *src); extern void musb_read_fifo(struct musb_hw_ep *ep, u16 len, u8 *dst); +extern int musb_set_host(struct musb *musb); +extern int musb_set_peripheral(struct musb *musb); + extern void musb_load_testpacket(struct musb *); extern irqreturn_t musb_interrupt(struct musb *); diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index e572ee624128..9c1b72a4b12f 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -38,65 +38,38 @@ struct omap2430_glue { static struct omap2430_glue *_glue; +/* + * HDRC controls CPEN, but beware current surges during device connect. + * They can trigger transient overcurrent conditions that must be ignored. + * + * Note that we're skipping A_WAIT_VFALL -> A_IDLE and jumping right to B_IDLE + * as set by musb_set_peripheral(). + */ static void omap2430_musb_set_vbus(struct musb *musb, int is_on) { - struct usb_otg *otg = musb->xceiv->otg; - u8 devctl; - unsigned long timeout = jiffies + msecs_to_jiffies(1000); - /* HDRC controls CPEN, but beware current surges during device - * connect. They can trigger transient overcurrent conditions - * that must be ignored. - */ - - devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + struct usb_otg *otg = musb->xceiv->otg; + int error; if (is_on) { - if (musb->xceiv->otg->state == OTG_STATE_A_IDLE) { - int loops = 100; - /* start the session */ - devctl |= MUSB_DEVCTL_SESSION; - musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); - /* - * Wait for the musb to set as A device to enable the - * VBUS - */ - while (musb_readb(musb->mregs, MUSB_DEVCTL) & - MUSB_DEVCTL_BDEVICE) { - - mdelay(5); - cpu_relax(); - - if (time_after(jiffies, timeout) - || loops-- <= 0) { - dev_err(musb->controller, - "configured as A device timeout"); - break; - } + switch (musb->xceiv->otg->state) { + case OTG_STATE_A_IDLE: + error = musb_set_host(musb); + if (!error) { + musb->xceiv->otg->state = + OTG_STATE_A_WAIT_VRISE; + otg_set_vbus(otg, 1); } - + break; + default: otg_set_vbus(otg, 1); - } else { - musb->is_active = 1; - musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; - devctl |= MUSB_DEVCTL_SESSION; - MUSB_HST_MODE(musb); + break; } } else { - musb->is_active = 0; - - /* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and - * jumping right to B_IDLE... - */ - - musb->xceiv->otg->state = OTG_STATE_B_IDLE; - devctl &= ~MUSB_DEVCTL_SESSION; - - MUSB_DEV_MODE(musb); + error = musb_set_peripheral(musb); + otg_set_vbus(otg, 0); } - musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); - dev_dbg(musb->controller, "VBUS %s, devctl %02x " - /* otg %3x conf %08x prcm %08x */ "\n", + dev_dbg(musb->controller, "VBUS %s, devctl %02x\n", usb_otg_state_string(musb->xceiv->otg->state), musb_readb(musb->mregs, MUSB_DEVCTL)); } -- cgit v1.2.3-59-g8ed1b From b769ae4f26e5892df70bf4d3005fe0db35c8ba7b Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 15 Jan 2020 07:25:30 -0600 Subject: usb: musb: omap2430: Clean up enable and remove devctl tinkering There should be no need to tinker with devctl in enable in the SoC glue code. We have musb_start() to take care of handling it already. Acked-by: Pavel Machek Signed-off-by: Tony Lindgren Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-9-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/omap2430.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 9c1b72a4b12f..2cc54135bb8b 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -276,33 +276,13 @@ static int omap2430_musb_init(struct musb *musb) static void omap2430_musb_enable(struct musb *musb) { - u8 devctl; - unsigned long timeout = jiffies + msecs_to_jiffies(1000); struct device *dev = musb->controller; struct omap2430_glue *glue = dev_get_drvdata(dev->parent); - struct musb_hdrc_platform_data *pdata = dev_get_platdata(dev); - struct omap_musb_board_data *data = pdata->board_data; - switch (glue->status) { case MUSB_ID_GROUND: omap_control_usb_set_mode(glue->control_otghs, USB_MODE_HOST); - if (data->interface_type != MUSB_INTERFACE_UTMI) - break; - devctl = musb_readb(musb->mregs, MUSB_DEVCTL); - /* start the session */ - devctl |= MUSB_DEVCTL_SESSION; - musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); - while (musb_readb(musb->mregs, MUSB_DEVCTL) & - MUSB_DEVCTL_BDEVICE) { - cpu_relax(); - - if (time_after(jiffies, timeout)) { - dev_err(dev, "configured as A device timeout"); - break; - } - } break; case MUSB_VBUS_VALID: -- cgit v1.2.3-59-g8ed1b From 8b359cbc3cdeb6145ca096334509df2a49ebaa0e Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 15 Jan 2020 07:25:31 -0600 Subject: usb: musb: omap2430: Idle musb on init We want to configure musb state in omap2430_musb_enable() instead of omap2430_musb_init(). Otherwise musb may not idle properly until USB cable has been connected at least once. And we already have omap_musb_set_mailbox() configure mode with omap_control_usb_set_mode() so we can remove those calls from omap2430_musb_enable(). Cc: Jacopo Mondi Cc: Marcel Partap Cc: Merlijn Wajer Cc: Michael Scott Cc: NeKit Cc: Pavel Machek Cc: Sebastian Reichel Acked-by: Pavel Machek Signed-off-by: Tony Lindgren Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-10-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/omap2430.c | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 2cc54135bb8b..bc5810e14ebb 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -212,7 +212,6 @@ static int omap2430_musb_init(struct musb *musb) u32 l; int status = 0; struct device *dev = musb->controller; - struct omap2430_glue *glue = dev_get_drvdata(dev->parent); struct musb_hdrc_platform_data *plat = dev_get_platdata(dev); struct omap_musb_board_data *data = plat->board_data; @@ -268,9 +267,6 @@ static int omap2430_musb_init(struct musb *musb) musb_readl(musb->mregs, OTG_INTERFSEL), musb_readl(musb->mregs, OTG_SIMENABLE)); - if (glue->status != MUSB_UNKNOWN) - omap_musb_set_mailbox(glue); - return 0; } @@ -279,19 +275,9 @@ static void omap2430_musb_enable(struct musb *musb) struct device *dev = musb->controller; struct omap2430_glue *glue = dev_get_drvdata(dev->parent); - switch (glue->status) { - - case MUSB_ID_GROUND: - omap_control_usb_set_mode(glue->control_otghs, USB_MODE_HOST); - break; - - case MUSB_VBUS_VALID: - omap_control_usb_set_mode(glue->control_otghs, USB_MODE_DEVICE); - break; - - default: - break; - } + if (glue->status == MUSB_UNKNOWN) + glue->status = MUSB_VBUS_OFF; + omap_musb_set_mailbox(glue); } static void omap2430_musb_disable(struct musb *musb) -- cgit v1.2.3-59-g8ed1b From 98827105d8c32fffd22d963c493fc3f61388e939 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 15 Jan 2020 07:25:32 -0600 Subject: usb: musb: Get rid of omap2430_musb_set_vbus() Now that we've removed direct calls from interrupt handler to omap2430_musb_set_vbus(), let's make things less confusing and configure VBUS directly in omap_musb_set_mailbox(). We have omap_musb_set_mailbox() called from the PHYs, and that's all we need. Note that we can now also drop the check for MUSB_INTERFACE_UTMI, we've been already calling otg_set_vbus(musb->xceiv->otg, 0) unconditionally via omap2430_musb_set_vbus() and we should only need to call it once. And we want to disable VBUS unconditionally on disconnect even without musb->gadget_driver, so let's drop that check too. Acked-by: Pavel Machek Signed-off-by: Tony Lindgren Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-11-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/omap2430.c | 71 +++++++++++++++------------------------------ 1 file changed, 23 insertions(+), 48 deletions(-) diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index bc5810e14ebb..d62c78b97cad 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -38,42 +38,6 @@ struct omap2430_glue { static struct omap2430_glue *_glue; -/* - * HDRC controls CPEN, but beware current surges during device connect. - * They can trigger transient overcurrent conditions that must be ignored. - * - * Note that we're skipping A_WAIT_VFALL -> A_IDLE and jumping right to B_IDLE - * as set by musb_set_peripheral(). - */ -static void omap2430_musb_set_vbus(struct musb *musb, int is_on) -{ - struct usb_otg *otg = musb->xceiv->otg; - int error; - - if (is_on) { - switch (musb->xceiv->otg->state) { - case OTG_STATE_A_IDLE: - error = musb_set_host(musb); - if (!error) { - musb->xceiv->otg->state = - OTG_STATE_A_WAIT_VRISE; - otg_set_vbus(otg, 1); - } - break; - default: - otg_set_vbus(otg, 1); - break; - } - } else { - error = musb_set_peripheral(musb); - otg_set_vbus(otg, 0); - } - - dev_dbg(musb->controller, "VBUS %s, devctl %02x\n", - usb_otg_state_string(musb->xceiv->otg->state), - musb_readb(musb->mregs, MUSB_DEVCTL)); -} - static inline void omap2430_low_level_exit(struct musb *musb) { u32 l; @@ -113,27 +77,42 @@ static int omap2430_musb_mailbox(enum musb_vbus_id_status status) return 0; } +/* + * HDRC controls CPEN, but beware current surges during device connect. + * They can trigger transient overcurrent conditions that must be ignored. + * + * Note that we're skipping A_WAIT_VFALL -> A_IDLE and jumping right to B_IDLE + * as set by musb_set_peripheral(). + */ static void omap_musb_set_mailbox(struct omap2430_glue *glue) { struct musb *musb = glue_to_musb(glue); - struct musb_hdrc_platform_data *pdata = - dev_get_platdata(musb->controller); - struct omap_musb_board_data *data = pdata->board_data; + int error; pm_runtime_get_sync(musb->controller); + + dev_dbg(musb->controller, "VBUS %s, devctl %02x\n", + usb_otg_state_string(musb->xceiv->otg->state), + musb_readb(musb->mregs, MUSB_DEVCTL)); + switch (glue->status) { case MUSB_ID_GROUND: dev_dbg(musb->controller, "ID GND\n"); switch (musb->xceiv->otg->state) { + case OTG_STATE_A_IDLE: + error = musb_set_host(musb); + if (error) + break; + musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; + /* Fall through */ case OTG_STATE_A_WAIT_VRISE: case OTG_STATE_A_WAIT_BCON: case OTG_STATE_A_HOST: - case OTG_STATE_A_IDLE: /* * On multiple ID ground interrupts just keep enabling * VBUS. At least cpcap VBUS shuts down otherwise. */ - omap2430_musb_set_vbus(musb, 1); + otg_set_vbus(musb->xceiv->otg, 1); break; default: musb->xceiv->otg->state = OTG_STATE_A_IDLE; @@ -141,7 +120,7 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) if (musb->gadget_driver) { omap_control_usb_set_mode(glue->control_otghs, USB_MODE_HOST); - omap2430_musb_set_vbus(musb, 1); + otg_set_vbus(musb->xceiv->otg, 1); } break; } @@ -160,12 +139,8 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) dev_dbg(musb->controller, "VBUS Disconnect\n"); musb->xceiv->last_event = USB_EVENT_NONE; - if (musb->gadget_driver) - omap2430_musb_set_vbus(musb, 0); - - if (data->interface_type == MUSB_INTERFACE_UTMI) - otg_set_vbus(musb->xceiv->otg, 0); - + musb_set_peripheral(musb); + otg_set_vbus(musb->xceiv->otg, 0); omap_control_usb_set_mode(glue->control_otghs, USB_MODE_DISCONNECT); break; -- cgit v1.2.3-59-g8ed1b From 7e2ee1ab023cda87aa5f738076d83671484204d3 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 15 Jan 2020 07:25:33 -0600 Subject: usb: musb: jz4740: Suppress useless field in priv structure The 'dev' field was never read anywhere. Signed-off-by: Paul Cercueil Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-12-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/jz4740.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index e3b8c84ccdb8..03c555679e5e 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -17,7 +17,6 @@ #include "musb_core.h" struct jz4740_glue { - struct device *dev; struct platform_device *musb; struct clk *clk; }; @@ -141,7 +140,6 @@ static int jz4740_probe(struct platform_device *pdev) musb->dev.parent = &pdev->dev; - glue->dev = &pdev->dev; glue->musb = musb; glue->clk = clk; -- cgit v1.2.3-59-g8ed1b From 4b70331b6fa13e4ae6ba634c336a616a7e2bf9d0 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 15 Jan 2020 07:25:34 -0600 Subject: usb: musb: jz4740: Add local dev variable to clean up probe Clean up the probe function by using a local 'struct device *dev' variable, instead of referencing &pdev->dev everytime. Signed-off-by: Paul Cercueil Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-13-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/jz4740.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index 03c555679e5e..9aca12f99c9f 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -109,36 +109,37 @@ static const struct musb_platform_ops jz4740_musb_ops = { static int jz4740_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct musb_hdrc_platform_data *pdata = &jz4740_musb_platform_data; struct platform_device *musb; struct jz4740_glue *glue; struct clk *clk; int ret; - glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); + glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL); if (!glue) return -ENOMEM; musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); if (!musb) { - dev_err(&pdev->dev, "failed to allocate musb device\n"); + dev_err(dev, "failed to allocate musb device"); return -ENOMEM; } - clk = devm_clk_get(&pdev->dev, "udc"); + clk = devm_clk_get(dev, "udc"); if (IS_ERR(clk)) { - dev_err(&pdev->dev, "failed to get clock\n"); + dev_err(dev, "failed to get clock"); ret = PTR_ERR(clk); goto err_platform_device_put; } ret = clk_prepare_enable(clk); if (ret) { - dev_err(&pdev->dev, "failed to enable clock\n"); + dev_err(dev, "failed to enable clock"); goto err_platform_device_put; } - musb->dev.parent = &pdev->dev; + musb->dev.parent = dev; glue->musb = musb; glue->clk = clk; @@ -150,19 +151,19 @@ static int jz4740_probe(struct platform_device *pdev) ret = platform_device_add_resources(musb, pdev->resource, pdev->num_resources); if (ret) { - dev_err(&pdev->dev, "failed to add resources\n"); + dev_err(dev, "failed to add resources"); goto err_clk_disable; } ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); if (ret) { - dev_err(&pdev->dev, "failed to add platform_data\n"); + dev_err(dev, "failed to add platform_data"); goto err_clk_disable; } ret = platform_device_add(musb); if (ret) { - dev_err(&pdev->dev, "failed to register musb device\n"); + dev_err(dev, "failed to register musb device"); goto err_clk_disable; } -- cgit v1.2.3-59-g8ed1b From 31cecb6bb6982e2976d3f95bd0d44855ec52cc29 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 15 Jan 2020 07:25:35 -0600 Subject: usb: musb: jz4740: Constify jz4740_musb_pdata struct By moving around the jz4740_musb_pdata structure, we can have the .platform_ops field initialized, so that we don't have to initialize it manually in the probe function. Therefore, the struct can be const now. Signed-off-by: Paul Cercueil Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-14-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/jz4740.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index 9aca12f99c9f..1400e5763d44 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -66,11 +66,6 @@ static const struct musb_hdrc_config jz4740_musb_config = { .fifo_cfg_size = ARRAY_SIZE(jz4740_musb_fifo_cfg), }; -static struct musb_hdrc_platform_data jz4740_musb_platform_data = { - .mode = MUSB_PERIPHERAL, - .config = &jz4740_musb_config, -}; - static int jz4740_musb_init(struct musb *musb) { struct device *dev = musb->controller->parent; @@ -107,10 +102,16 @@ static const struct musb_platform_ops jz4740_musb_ops = { .init = jz4740_musb_init, }; +static const struct musb_hdrc_platform_data jz4740_musb_pdata = { + .mode = MUSB_PERIPHERAL, + .config = &jz4740_musb_config, + .platform_ops = &jz4740_musb_ops, +}; + static int jz4740_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct musb_hdrc_platform_data *pdata = &jz4740_musb_platform_data; + const struct musb_hdrc_platform_data *pdata = &jz4740_musb_pdata; struct platform_device *musb; struct jz4740_glue *glue; struct clk *clk; @@ -144,8 +145,6 @@ static int jz4740_probe(struct platform_device *pdev) glue->musb = musb; glue->clk = clk; - pdata->platform_ops = &jz4740_musb_ops; - platform_set_drvdata(pdev, glue); ret = platform_device_add_resources(musb, pdev->resource, -- cgit v1.2.3-59-g8ed1b From 90fad5d7621e8edc9f8bb096c387a09cb563284c Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 15 Jan 2020 07:25:36 -0600 Subject: usb: musb: jz4740: Rename platform_device field in priv struct Name the platform_device pointer 'pdev' instead of 'musb'. Since the driver also deal with pointers to 'struct musb', it can be very confusing to have a pointer named after this struct but with a different type. Signed-off-by: Paul Cercueil Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-15-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/jz4740.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index 1400e5763d44..64e0b0f8c45b 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -17,7 +17,7 @@ #include "musb_core.h" struct jz4740_glue { - struct platform_device *musb; + struct platform_device *pdev; struct clk *clk; }; @@ -142,7 +142,7 @@ static int jz4740_probe(struct platform_device *pdev) musb->dev.parent = dev; - glue->musb = musb; + glue->pdev = musb; glue->clk = clk; platform_set_drvdata(pdev, glue); @@ -179,7 +179,7 @@ static int jz4740_remove(struct platform_device *pdev) { struct jz4740_glue *glue = platform_get_drvdata(pdev); - platform_device_unregister(glue->musb); + platform_device_unregister(glue->pdev); clk_disable_unprepare(glue->clk); return 0; -- cgit v1.2.3-59-g8ed1b From 94203e1a1a254743b9c8ac2755be86ac02496966 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 15 Jan 2020 07:25:37 -0600 Subject: usb: musb: jz4740: Comments fix Add a /* sentinel */ comment to the sentinel entry of the devicetree ID table, and fix a multi-line comment not having its opening token on a separate line. Signed-off-by: Paul Cercueil Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-16-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/jz4740.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index 64e0b0f8c45b..b4884575e37a 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -82,7 +82,8 @@ static int jz4740_musb_init(struct musb *musb) return err; } - /* Silicon does not implement ConfigData register. + /* + * Silicon does not implement ConfigData register. * Set dyn_fifo to avoid reading EP config from hardware. */ musb->dyn_fifo = true; @@ -188,7 +189,7 @@ static int jz4740_remove(struct platform_device *pdev) #ifdef CONFIG_OF static const struct of_device_id jz4740_musb_of_match[] = { { .compatible = "ingenic,jz4740-musb" }, - {}, + { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, jz4740_musb_of_match); #endif -- cgit v1.2.3-59-g8ed1b From 3fc32907b8ab688c26d7b213e866412379f73db7 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 15 Jan 2020 07:25:38 -0600 Subject: usb: musb: jz4740: Whitespace and indentation fixes Fix lines with too much or not enough indentation, and lines which were indented with spaces instead of tabs. Signed-off-by: Paul Cercueil Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-17-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/jz4740.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index b4884575e37a..bc0109f4700b 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -23,9 +23,9 @@ struct jz4740_glue { static irqreturn_t jz4740_musb_interrupt(int irq, void *__hci) { - unsigned long flags; - irqreturn_t retval = IRQ_NONE; - struct musb *musb = __hci; + unsigned long flags; + irqreturn_t retval = IRQ_NONE; + struct musb *musb = __hci; spin_lock_irqsave(&musb->lock, flags); @@ -39,7 +39,7 @@ static irqreturn_t jz4740_musb_interrupt(int irq, void *__hci) * never see them set */ musb->int_usb &= MUSB_INTR_SUSPEND | MUSB_INTR_RESUME | - MUSB_INTR_RESET | MUSB_INTR_SOF; + MUSB_INTR_RESET | MUSB_INTR_SOF; if (musb->int_usb || musb->int_tx || musb->int_rx) retval = musb_interrupt(musb); @@ -50,20 +50,20 @@ static irqreturn_t jz4740_musb_interrupt(int irq, void *__hci) } static struct musb_fifo_cfg jz4740_musb_fifo_cfg[] = { -{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, -{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, -{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 64, }, + { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, + { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, + { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 64, }, }; static const struct musb_hdrc_config jz4740_musb_config = { /* Silicon does not implement USB OTG. */ - .multipoint = 0, + .multipoint = 0, /* Max EPs scanned, driver will decide which EP can be used. */ - .num_eps = 4, + .num_eps = 4, /* RAMbits needed to configure EPs from table */ - .ram_bits = 9, - .fifo_cfg = jz4740_musb_fifo_cfg, - .fifo_cfg_size = ARRAY_SIZE(jz4740_musb_fifo_cfg), + .ram_bits = 9, + .fifo_cfg = jz4740_musb_fifo_cfg, + .fifo_cfg_size = ARRAY_SIZE(jz4740_musb_fifo_cfg), }; static int jz4740_musb_init(struct musb *musb) @@ -115,7 +115,7 @@ static int jz4740_probe(struct platform_device *pdev) const struct musb_hdrc_platform_data *pdata = &jz4740_musb_pdata; struct platform_device *musb; struct jz4740_glue *glue; - struct clk *clk; + struct clk *clk; int ret; glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL); @@ -178,7 +178,7 @@ err_platform_device_put: static int jz4740_remove(struct platform_device *pdev) { - struct jz4740_glue *glue = platform_get_drvdata(pdev); + struct jz4740_glue *glue = platform_get_drvdata(pdev); platform_device_unregister(glue->pdev); clk_disable_unprepare(glue->clk); -- cgit v1.2.3-59-g8ed1b From 3709ff5dc35203c5e93424941fa660e770a1e728 Mon Sep 17 00:00:00 2001 From: "Ben Dooks (Codethink)" Date: Wed, 15 Jan 2020 07:25:39 -0600 Subject: USB: musb: fix __iomem in trace functions The trace functions should have __iomem on the addr pointers. Add __iomem to avoid the following warnings from sparse: drivers/usb/musb/musb_core.c:253:55: warning: incorrect type in argument 2 (different address spaces) drivers/usb/musb/musb_core.c:253:55: expected void const *addr drivers/usb/musb/musb_core.c:253:55: got void const [noderef] *addr drivers/usb/musb/musb_core.c:259:56: warning: incorrect type in argument 2 (different address spaces) drivers/usb/musb/musb_core.c:259:56: expected void const *addr drivers/usb/musb/musb_core.c:259:56: got void [noderef] *addr drivers/usb/musb/musb_core.c:267:55: warning: incorrect type in argument 2 (different address spaces) drivers/usb/musb/musb_core.c:267:55: expected void const *addr drivers/usb/musb/musb_core.c:267:55: got void const [noderef] *addr drivers/usb/musb/musb_core.c:273:56: warning: incorrect type in argument 2 (different address spaces) drivers/usb/musb/musb_core.c:273:56: expected void const *addr drivers/usb/musb/musb_core.c:273:56: got void [noderef] *addr drivers/usb/musb/musb_core.c:383:55: warning: incorrect type in argument 2 (different address spaces) drivers/usb/musb/musb_core.c:383:55: expected void const *addr drivers/usb/musb/musb_core.c:383:55: got void const [noderef] *addr drivers/usb/musb/musb_core.c:390:56: warning: incorrect type in argument 2 (different address spaces) drivers/usb/musb/musb_core.c:390:56: expected void const *addr drivers/usb/musb/musb_core.c:390:56: got void [noderef] *addr Signed-off-by: Ben Dooks (Codethink) Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-18-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_trace.h | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/drivers/usb/musb/musb_trace.h b/drivers/usb/musb/musb_trace.h index a97d618fe8ff..b193daf69685 100644 --- a/drivers/usb/musb/musb_trace.h +++ b/drivers/usb/musb/musb_trace.h @@ -38,11 +38,12 @@ TRACE_EVENT(musb_log, ); DECLARE_EVENT_CLASS(musb_regb, - TP_PROTO(void *caller, const void *addr, unsigned int offset, u8 data), + TP_PROTO(void *caller, const void __iomem *addr, + unsigned int offset, u8 data), TP_ARGS(caller, addr, offset, data), TP_STRUCT__entry( __field(void *, caller) - __field(const void *, addr) + __field(const void __iomem *, addr) __field(unsigned int, offset) __field(u8, data) ), @@ -57,21 +58,24 @@ DECLARE_EVENT_CLASS(musb_regb, ); DEFINE_EVENT(musb_regb, musb_readb, - TP_PROTO(void *caller, const void *addr, unsigned int offset, u8 data), + TP_PROTO(void *caller, const void __iomem *addr, + unsigned int offset, u8 data), TP_ARGS(caller, addr, offset, data) ); DEFINE_EVENT(musb_regb, musb_writeb, - TP_PROTO(void *caller, const void *addr, unsigned int offset, u8 data), + TP_PROTO(void *caller, const void __iomem *addr, + unsigned int offset, u8 data), TP_ARGS(caller, addr, offset, data) ); DECLARE_EVENT_CLASS(musb_regw, - TP_PROTO(void *caller, const void *addr, unsigned int offset, u16 data), + TP_PROTO(void *caller, const void __iomem *addr, + unsigned int offset, u16 data), TP_ARGS(caller, addr, offset, data), TP_STRUCT__entry( __field(void *, caller) - __field(const void *, addr) + __field(const void __iomem *, addr) __field(unsigned int, offset) __field(u16, data) ), @@ -86,21 +90,24 @@ DECLARE_EVENT_CLASS(musb_regw, ); DEFINE_EVENT(musb_regw, musb_readw, - TP_PROTO(void *caller, const void *addr, unsigned int offset, u16 data), + TP_PROTO(void *caller, const void __iomem *addr, + unsigned int offset, u16 data), TP_ARGS(caller, addr, offset, data) ); DEFINE_EVENT(musb_regw, musb_writew, - TP_PROTO(void *caller, const void *addr, unsigned int offset, u16 data), + TP_PROTO(void *caller, const void __iomem *addr, + unsigned int offset, u16 data), TP_ARGS(caller, addr, offset, data) ); DECLARE_EVENT_CLASS(musb_regl, - TP_PROTO(void *caller, const void *addr, unsigned int offset, u32 data), + TP_PROTO(void *caller, const void __iomem *addr, + unsigned int offset, u32 data), TP_ARGS(caller, addr, offset, data), TP_STRUCT__entry( __field(void *, caller) - __field(const void *, addr) + __field(const void __iomem *, addr) __field(unsigned int, offset) __field(u32, data) ), @@ -115,12 +122,14 @@ DECLARE_EVENT_CLASS(musb_regl, ); DEFINE_EVENT(musb_regl, musb_readl, - TP_PROTO(void *caller, const void *addr, unsigned int offset, u32 data), + TP_PROTO(void *caller, const void __iomem *addr, + unsigned int offset, u32 data), TP_ARGS(caller, addr, offset, data) ); DEFINE_EVENT(musb_regl, musb_writel, - TP_PROTO(void *caller, const void *addr, unsigned int offset, u32 data), + TP_PROTO(void *caller, const void __iomem *addr, + unsigned int offset, u32 data), TP_ARGS(caller, addr, offset, data) ); -- cgit v1.2.3-59-g8ed1b From b7962fb45f8fe0678b2d2eaa48382ea32fcf9400 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 15 Jan 2020 07:25:40 -0600 Subject: usb: musb/ux500: Use dma_request_chan() instead dma_request_slave_channel() dma_request_slave_channel() is a wrapper on top of dma_request_chan() eating up the error code. Signed-off-by: Peter Ujfalusi Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-19-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/ux500_dma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index d19bb3e89da6..d5cf5e8bb1ca 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -310,9 +310,9 @@ static int ux500_dma_controller_start(struct ux500_dma_controller *controller) dma_channel->max_len = SZ_16M; ux500_channel->dma_chan = - dma_request_slave_channel(dev, chan_names[ch_num]); + dma_request_chan(dev, chan_names[ch_num]); - if (!ux500_channel->dma_chan) + if (IS_ERR(ux500_channel->dma_chan)) ux500_channel->dma_chan = dma_request_channel(mask, data ? -- cgit v1.2.3-59-g8ed1b From 8d817d79349d953c8b05559e4cfd448785550e78 Mon Sep 17 00:00:00 2001 From: Min Guo Date: Wed, 15 Jan 2020 07:25:41 -0600 Subject: dt-bindings: usb: musb: Add support for MediaTek musb controller This adds support for MediaTek musb controller in host, peripheral and otg mode. Signed-off-by: Min Guo Reviewed-by: Rob Herring Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-20-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/mediatek,musb.txt | 57 ++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/mediatek,musb.txt diff --git a/Documentation/devicetree/bindings/usb/mediatek,musb.txt b/Documentation/devicetree/bindings/usb/mediatek,musb.txt new file mode 100644 index 000000000000..2b8a87c90d9e --- /dev/null +++ b/Documentation/devicetree/bindings/usb/mediatek,musb.txt @@ -0,0 +1,57 @@ +MediaTek musb DRD/OTG controller +------------------------------------------- + +Required properties: + - compatible : should be one of: + "mediatek,mt2701-musb" + ... + followed by "mediatek,mtk-musb" + - reg : specifies physical base address and size of + the registers + - interrupts : interrupt used by musb controller + - interrupt-names : must be "mc" + - phys : PHY specifier for the OTG phy + - dr_mode : should be one of "host", "peripheral" or "otg", + refer to usb/generic.txt + - clocks : a list of phandle + clock-specifier pairs, one for + each entry in clock-names + - clock-names : must contain "main", "mcu", "univpll" + for clocks of controller + +Optional properties: + - power-domains : a phandle to USB power domain node to control USB's + MTCMOS + +Required child nodes: + usb connector node as defined in bindings/connector/usb-connector.txt +Optional properties: + - id-gpios : input GPIO for USB ID pin. + - vbus-gpios : input GPIO for USB VBUS pin. + - vbus-supply : reference to the VBUS regulator, needed when supports + dual-role mode + - usb-role-switch : use USB Role Switch to support dual-role switch, see + usb/generic.txt. + +Example: + +usb2: usb@11200000 { + compatible = "mediatek,mt2701-musb", + "mediatek,mtk-musb"; + reg = <0 0x11200000 0 0x1000>; + interrupts = ; + interrupt-names = "mc"; + phys = <&u2port2 PHY_TYPE_USB2>; + dr_mode = "otg"; + clocks = <&pericfg CLK_PERI_USB0>, + <&pericfg CLK_PERI_USB0_MCU>, + <&pericfg CLK_PERI_USB_SLV>; + clock-names = "main","mcu","univpll"; + power-domains = <&scpsys MT2701_POWER_DOMAIN_IFR_MSC>; + usb-role-switch; + connector{ + compatible = "gpio-usb-b-connector", "usb-b-connector"; + type = "micro"; + id-gpios = <&pio 44 GPIO_ACTIVE_HIGH>; + vbus-supply = <&usb_vbus>; + }; +}; -- cgit v1.2.3-59-g8ed1b From fe3bbd6b383fbc62128fd1fe850105080cb4c9da Mon Sep 17 00:00:00 2001 From: Min Guo Date: Wed, 15 Jan 2020 07:25:42 -0600 Subject: usb: musb: Add get/set toggle hooks Add get/set toggle hooks in struct musb_io and struct musb_platform_ops for special platform; remove function musb_save_toggle, use the set/get callback to handle toggle. Signed-off-by: Min Guo Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-21-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_core.c | 42 ++++++++++++++++++++++++++++++++++++++++ drivers/usb/musb/musb_core.h | 5 +++++ drivers/usb/musb/musb_host.c | 46 ++++++++++---------------------------------- drivers/usb/musb/musb_io.h | 4 ++++ 4 files changed, 61 insertions(+), 36 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 34090707d122..f3c95cd12e04 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -275,6 +275,38 @@ static void musb_default_writew(void __iomem *addr, unsigned offset, u16 data) __raw_writew(data, addr + offset); } +static u16 musb_default_get_toggle(struct musb_qh *qh, int is_out) +{ + void __iomem *epio = qh->hw_ep->regs; + u16 csr; + + if (is_out) + csr = musb_readw(epio, MUSB_TXCSR) & MUSB_TXCSR_H_DATATOGGLE; + else + csr = musb_readw(epio, MUSB_RXCSR) & MUSB_RXCSR_H_DATATOGGLE; + + return csr; +} + +static u16 musb_default_set_toggle(struct musb_qh *qh, int is_out, + struct urb *urb) +{ + u16 csr; + u16 toggle; + + toggle = usb_gettoggle(urb->dev, qh->epnum, is_out); + + if (is_out) + csr = toggle ? (MUSB_TXCSR_H_WR_DATATOGGLE + | MUSB_TXCSR_H_DATATOGGLE) + : MUSB_TXCSR_CLRDATATOG; + else + csr = toggle ? (MUSB_RXCSR_H_WR_DATATOGGLE + | MUSB_RXCSR_H_DATATOGGLE) : 0; + + return csr; +} + /* * Load an endpoint's FIFO */ @@ -2381,6 +2413,16 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) else musb->io.write_fifo = musb_default_write_fifo; + if (musb->ops->get_toggle) + musb->io.get_toggle = musb->ops->get_toggle; + else + musb->io.get_toggle = musb_default_get_toggle; + + if (musb->ops->set_toggle) + musb->io.set_toggle = musb->ops->set_toggle; + else + musb->io.set_toggle = musb_default_set_toggle; + if (!musb->xceiv->io_ops) { musb->xceiv->io_dev = musb->controller; musb->xceiv->io_priv = musb->mregs; diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 8a13a46cd891..2e4fcf1a1a5c 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -27,6 +27,7 @@ struct musb; struct musb_hw_ep; struct musb_ep; +struct musb_qh; /* Helper defines for struct musb->hwvers */ #define MUSB_HWVERS_MAJOR(x) ((x >> 10) & 0x1f) @@ -123,6 +124,8 @@ struct musb_io; * @writew: write 16 bits * @read_fifo: reads the fifo * @write_fifo: writes to fifo + * @get_toggle: platform specific get toggle function + * @set_toggle: platform specific set toggle function * @dma_init: platform specific dma init function * @dma_exit: platform specific dma exit function * @init: turns on clocks, sets up platform-specific registers, etc @@ -167,6 +170,8 @@ struct musb_platform_ops { void (*writew)(void __iomem *addr, unsigned offset, u16 data); void (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf); void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf); + u16 (*get_toggle)(struct musb_qh *qh, int is_out); + u16 (*set_toggle)(struct musb_qh *qh, int is_out, struct urb *urb); struct dma_controller * (*dma_init) (struct musb *musb, void __iomem *base); void (*dma_exit)(struct dma_controller *c); diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 5a44b70372d9..886c9b602f8c 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -286,26 +286,6 @@ __acquires(musb->lock) spin_lock(&musb->lock); } -/* For bulk/interrupt endpoints only */ -static inline void musb_save_toggle(struct musb_qh *qh, int is_in, - struct urb *urb) -{ - void __iomem *epio = qh->hw_ep->regs; - u16 csr; - - /* - * FIXME: the current Mentor DMA code seems to have - * problems getting toggle correct. - */ - - if (is_in) - csr = musb_readw(epio, MUSB_RXCSR) & MUSB_RXCSR_H_DATATOGGLE; - else - csr = musb_readw(epio, MUSB_TXCSR) & MUSB_TXCSR_H_DATATOGGLE; - - usb_settoggle(urb->dev, qh->epnum, !is_in, csr ? 1 : 0); -} - /* * Advance this hardware endpoint's queue, completing the specified URB and * advancing to either the next URB queued to that qh, or else invalidating @@ -320,6 +300,7 @@ static void musb_advance_schedule(struct musb *musb, struct urb *urb, struct musb_hw_ep *ep = qh->hw_ep; int ready = qh->is_ready; int status; + u16 toggle; status = (urb->status == -EINPROGRESS) ? 0 : urb->status; @@ -327,7 +308,8 @@ static void musb_advance_schedule(struct musb *musb, struct urb *urb, switch (qh->type) { case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_INT: - musb_save_toggle(qh, is_in, urb); + toggle = musb->io.get_toggle(qh, !is_in); + usb_settoggle(urb->dev, qh->epnum, !is_in, toggle ? 1 : 0); break; case USB_ENDPOINT_XFER_ISOC: if (status == 0 && urb->error_count) @@ -772,13 +754,8 @@ static void musb_ep_program(struct musb *musb, u8 epnum, ); csr |= MUSB_TXCSR_MODE; - if (!hw_ep->tx_double_buffered) { - if (usb_gettoggle(urb->dev, qh->epnum, 1)) - csr |= MUSB_TXCSR_H_WR_DATATOGGLE - | MUSB_TXCSR_H_DATATOGGLE; - else - csr |= MUSB_TXCSR_CLRDATATOG; - } + if (!hw_ep->tx_double_buffered) + csr |= musb->io.set_toggle(qh, is_out, urb); musb_writew(epio, MUSB_TXCSR, csr); /* REVISIT may need to clear FLUSHFIFO ... */ @@ -860,17 +837,12 @@ finish: /* IN/receive */ } else { - u16 csr; + u16 csr = 0; if (hw_ep->rx_reinit) { musb_rx_reinit(musb, qh, epnum); + csr |= musb->io.set_toggle(qh, is_out, urb); - /* init new state: toggle and NYET, maybe DMA later */ - if (usb_gettoggle(urb->dev, qh->epnum, 0)) - csr = MUSB_RXCSR_H_WR_DATATOGGLE - | MUSB_RXCSR_H_DATATOGGLE; - else - csr = 0; if (qh->type == USB_ENDPOINT_XFER_INT) csr |= MUSB_RXCSR_DISNYET; @@ -933,6 +905,7 @@ static void musb_bulk_nak_timeout(struct musb *musb, struct musb_hw_ep *ep, void __iomem *epio = ep->regs; struct musb_qh *cur_qh, *next_qh; u16 rx_csr, tx_csr; + u16 toggle; musb_ep_select(mbase, ep->epnum); if (is_in) { @@ -970,7 +943,8 @@ static void musb_bulk_nak_timeout(struct musb *musb, struct musb_hw_ep *ep, urb->actual_length += dma->actual_len; dma->actual_len = 0L; } - musb_save_toggle(cur_qh, is_in, urb); + toggle = musb->io.get_toggle(cur_qh, !is_in); + usb_settoggle(urb->dev, cur_qh->epnum, !is_in, toggle ? 1 : 0); if (is_in) { /* move cur_qh to end of queue */ diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h index 8058a58092cf..8179334f405b 100644 --- a/drivers/usb/musb/musb_io.h +++ b/drivers/usb/musb/musb_io.h @@ -22,6 +22,8 @@ * @read_fifo: platform specific function to read fifo * @write_fifo: platform specific function to write fifo * @busctl_offset: platform specific function to get busctl offset + * @get_toggle: platform specific function to get toggle + * @set_toggle: platform specific function to set toggle */ struct musb_io { u32 (*ep_offset)(u8 epnum, u16 offset); @@ -30,6 +32,8 @@ struct musb_io { void (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf); void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf); u32 (*busctl_offset)(u8 epnum, u16 offset); + u16 (*get_toggle)(struct musb_qh *qh, int is_out); + u16 (*set_toggle)(struct musb_qh *qh, int is_out, struct urb *urb); }; /* Do not add new entries here, add them the struct musb_io instead */ -- cgit v1.2.3-59-g8ed1b From edce61776c7e212d8b3d61e69afe7672efbacb04 Mon Sep 17 00:00:00 2001 From: Min Guo Date: Wed, 15 Jan 2020 07:25:43 -0600 Subject: usb: musb: Add noirq type of dma create interface Add noirq type of dma create interface for platform which do not have dedicated DMA interrupt line, move musbhsdma macro definition to musb_dma.h Signed-off-by: Min Guo Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-22-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_dma.h | 9 ++++++++ drivers/usb/musb/musbhsdma.c | 54 ++++++++++++++++++++++++++++++-------------- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index 8f60271c0a9d..4b4d8dc5d3f2 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -35,6 +35,12 @@ struct musb_hw_ep; * whether shared with the Inventra core or separate. */ +#define MUSB_HSDMA_BASE 0x200 +#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0) +#define MUSB_HSDMA_CONTROL 0x4 +#define MUSB_HSDMA_ADDRESS 0x8 +#define MUSB_HSDMA_COUNT 0xc + #define DMA_ADDR_INVALID (~(dma_addr_t)0) #ifdef CONFIG_MUSB_PIO_ONLY @@ -191,6 +197,9 @@ extern void (*musb_dma_controller_destroy)(struct dma_controller *); extern struct dma_controller * musbhs_dma_controller_create(struct musb *musb, void __iomem *base); extern void musbhs_dma_controller_destroy(struct dma_controller *c); +extern struct dma_controller * +musbhs_dma_controller_create_noirq(struct musb *musb, void __iomem *base); +extern irqreturn_t dma_controller_irq(int irq, void *private_data); extern struct dma_controller * tusb_dma_controller_create(struct musb *musb, void __iomem *base); diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index 2d3751d885b4..bcc0fbf42ba8 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -10,12 +10,7 @@ #include #include #include "musb_core.h" - -#define MUSB_HSDMA_BASE 0x200 -#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0) -#define MUSB_HSDMA_CONTROL 0x4 -#define MUSB_HSDMA_ADDRESS 0x8 -#define MUSB_HSDMA_COUNT 0xc +#include "musb_dma.h" #define MUSB_HSDMA_CHANNEL_OFFSET(_bchannel, _offset) \ (MUSB_HSDMA_BASE + (_bchannel << 4) + _offset) @@ -268,7 +263,7 @@ static int dma_channel_abort(struct dma_channel *channel) return 0; } -static irqreturn_t dma_controller_irq(int irq, void *private_data) +irqreturn_t dma_controller_irq(int irq, void *private_data) { struct musb_dma_controller *controller = private_data; struct musb *musb = controller->private_data; @@ -383,6 +378,7 @@ done: spin_unlock_irqrestore(&musb->lock, flags); return retval; } +EXPORT_SYMBOL_GPL(dma_controller_irq); void musbhs_dma_controller_destroy(struct dma_controller *c) { @@ -398,18 +394,10 @@ void musbhs_dma_controller_destroy(struct dma_controller *c) } EXPORT_SYMBOL_GPL(musbhs_dma_controller_destroy); -struct dma_controller *musbhs_dma_controller_create(struct musb *musb, - void __iomem *base) +static struct musb_dma_controller * +dma_controller_alloc(struct musb *musb, void __iomem *base) { struct musb_dma_controller *controller; - struct device *dev = musb->controller; - struct platform_device *pdev = to_platform_device(dev); - int irq = platform_get_irq_byname(pdev, "dma"); - - if (irq <= 0) { - dev_err(dev, "No DMA interrupt line!\n"); - return NULL; - } controller = kzalloc(sizeof(*controller), GFP_KERNEL); if (!controller) @@ -423,6 +411,25 @@ struct dma_controller *musbhs_dma_controller_create(struct musb *musb, controller->controller.channel_release = dma_channel_release; controller->controller.channel_program = dma_channel_program; controller->controller.channel_abort = dma_channel_abort; + return controller; +} + +struct dma_controller * +musbhs_dma_controller_create(struct musb *musb, void __iomem *base) +{ + struct musb_dma_controller *controller; + struct device *dev = musb->controller; + struct platform_device *pdev = to_platform_device(dev); + int irq = platform_get_irq_byname(pdev, "dma"); + + if (irq <= 0) { + dev_err(dev, "No DMA interrupt line!\n"); + return NULL; + } + + controller = dma_controller_alloc(musb, base); + if (!controller) + return NULL; if (request_irq(irq, dma_controller_irq, 0, dev_name(musb->controller), controller)) { @@ -437,3 +444,16 @@ struct dma_controller *musbhs_dma_controller_create(struct musb *musb, return &controller->controller; } EXPORT_SYMBOL_GPL(musbhs_dma_controller_create); + +struct dma_controller * +musbhs_dma_controller_create_noirq(struct musb *musb, void __iomem *base) +{ + struct musb_dma_controller *controller; + + controller = dma_controller_alloc(musb, base); + if (!controller) + return NULL; + + return &controller->controller; +} +EXPORT_SYMBOL_GPL(musbhs_dma_controller_create_noirq); -- cgit v1.2.3-59-g8ed1b From 9c93d7fd464e7aad59c2afc261f80e6e0fbe2ca9 Mon Sep 17 00:00:00 2001 From: Min Guo Date: Wed, 15 Jan 2020 07:25:44 -0600 Subject: usb: musb: Add musb_clearb/w() interface Delete the const attribute of addr parameter in readb/w/l hooks, these changes are for implementing clearing W1C registers. Replace musb_readb/w with musb_clearb/w to clear the interrupt status. While at here, change some unsigned type to u32 to fix checkpatch.pl warnings. Signed-off-by: Min Guo [b-liu@ti.com: fix checkpatch.pl warnings.] Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-23-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_core.c | 42 ++++++++++++++++++++++++++++-------------- drivers/usb/musb/musb_core.h | 12 ++++++++---- drivers/usb/musb/musb_io.h | 14 ++++++++------ drivers/usb/musb/musbhsdma.c | 2 +- drivers/usb/musb/sunxi.c | 4 ++-- drivers/usb/musb/tusb6010.c | 2 +- 6 files changed, 48 insertions(+), 28 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index f3c95cd12e04..71691a1f8ae3 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -247,7 +247,7 @@ static u32 musb_default_busctl_offset(u8 epnum, u16 offset) return 0x80 + (0x08 * epnum) + offset; } -static u8 musb_default_readb(const void __iomem *addr, unsigned offset) +static u8 musb_default_readb(void __iomem *addr, u32 offset) { u8 data = __raw_readb(addr + offset); @@ -255,13 +255,13 @@ static u8 musb_default_readb(const void __iomem *addr, unsigned offset) return data; } -static void musb_default_writeb(void __iomem *addr, unsigned offset, u8 data) +static void musb_default_writeb(void __iomem *addr, u32 offset, u8 data) { trace_musb_writeb(__builtin_return_address(0), addr, offset, data); __raw_writeb(data, addr + offset); } -static u16 musb_default_readw(const void __iomem *addr, unsigned offset) +static u16 musb_default_readw(void __iomem *addr, u32 offset) { u16 data = __raw_readw(addr + offset); @@ -269,7 +269,7 @@ static u16 musb_default_readw(const void __iomem *addr, unsigned offset) return data; } -static void musb_default_writew(void __iomem *addr, unsigned offset, u16 data) +static void musb_default_writew(void __iomem *addr, u32 offset, u16 data) { trace_musb_writew(__builtin_return_address(0), addr, offset, data); __raw_writew(data, addr + offset); @@ -397,19 +397,25 @@ static void musb_default_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) /* * Old style IO functions */ -u8 (*musb_readb)(const void __iomem *addr, unsigned offset); +u8 (*musb_readb)(void __iomem *addr, u32 offset); EXPORT_SYMBOL_GPL(musb_readb); -void (*musb_writeb)(void __iomem *addr, unsigned offset, u8 data); +void (*musb_writeb)(void __iomem *addr, u32 offset, u8 data); EXPORT_SYMBOL_GPL(musb_writeb); -u16 (*musb_readw)(const void __iomem *addr, unsigned offset); +u8 (*musb_clearb)(void __iomem *addr, u32 offset); +EXPORT_SYMBOL_GPL(musb_clearb); + +u16 (*musb_readw)(void __iomem *addr, u32 offset); EXPORT_SYMBOL_GPL(musb_readw); -void (*musb_writew)(void __iomem *addr, unsigned offset, u16 data); +void (*musb_writew)(void __iomem *addr, u32 offset, u16 data); EXPORT_SYMBOL_GPL(musb_writew); -u32 musb_readl(const void __iomem *addr, unsigned offset) +u16 (*musb_clearw)(void __iomem *addr, u32 offset); +EXPORT_SYMBOL_GPL(musb_clearw); + +u32 musb_readl(void __iomem *addr, u32 offset) { u32 data = __raw_readl(addr + offset); @@ -418,7 +424,7 @@ u32 musb_readl(const void __iomem *addr, unsigned offset) } EXPORT_SYMBOL_GPL(musb_readl); -void musb_writel(void __iomem *addr, unsigned offset, u32 data) +void musb_writel(void __iomem *addr, u32 offset, u32 data) { trace_musb_writel(__builtin_return_address(0), addr, offset, data); __raw_writel(data, addr + offset); @@ -1149,7 +1155,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, static void musb_disable_interrupts(struct musb *musb) { void __iomem *mbase = musb->mregs; - u16 temp; /* disable interrupts */ musb_writeb(mbase, MUSB_INTRUSBE, 0); @@ -1159,9 +1164,9 @@ static void musb_disable_interrupts(struct musb *musb) musb_writew(mbase, MUSB_INTRRXE, 0); /* flush pending interrupts */ - temp = musb_readb(mbase, MUSB_INTRUSB); - temp = musb_readw(mbase, MUSB_INTRTX); - temp = musb_readw(mbase, MUSB_INTRRX); + musb_clearb(mbase, MUSB_INTRUSB); + musb_clearw(mbase, MUSB_INTRTX); + musb_clearw(mbase, MUSB_INTRRX); } static void musb_enable_interrupts(struct musb *musb) @@ -2388,10 +2393,19 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) musb_readb = musb->ops->readb; if (musb->ops->writeb) musb_writeb = musb->ops->writeb; + if (musb->ops->clearb) + musb_clearb = musb->ops->clearb; + else + musb_clearb = musb_readb; + if (musb->ops->readw) musb_readw = musb->ops->readw; if (musb->ops->writew) musb_writew = musb->ops->writew; + if (musb->ops->clearw) + musb_clearw = musb->ops->clearw; + else + musb_clearw = musb_readw; #ifndef CONFIG_MUSB_PIO_ONLY if (!musb->ops->dma_init || !musb->ops->dma_exit) { diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 2e4fcf1a1a5c..290a2bc46606 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -120,8 +120,10 @@ struct musb_io; * @fifo_offset: returns the fifo offset * @readb: read 8 bits * @writeb: write 8 bits + * @clearb: could be clear-on-readb or W1C * @readw: read 16 bits * @writew: write 16 bits + * @clearw: could be clear-on-readw or W1C * @read_fifo: reads the fifo * @write_fifo: writes to fifo * @get_toggle: platform specific get toggle function @@ -164,10 +166,12 @@ struct musb_platform_ops { u16 fifo_mode; u32 (*fifo_offset)(u8 epnum); u32 (*busctl_offset)(u8 epnum, u16 offset); - u8 (*readb)(const void __iomem *addr, unsigned offset); - void (*writeb)(void __iomem *addr, unsigned offset, u8 data); - u16 (*readw)(const void __iomem *addr, unsigned offset); - void (*writew)(void __iomem *addr, unsigned offset, u16 data); + u8 (*readb)(void __iomem *addr, u32 offset); + void (*writeb)(void __iomem *addr, u32 offset, u8 data); + u8 (*clearb)(void __iomem *addr, u32 offset); + u16 (*readw)(void __iomem *addr, u32 offset); + void (*writew)(void __iomem *addr, u32 offset, u16 data); + u16 (*clearw)(void __iomem *addr, u32 offset); void (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf); void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf); u16 (*get_toggle)(struct musb_qh *qh, int is_out); diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h index 8179334f405b..f17aabd95a50 100644 --- a/drivers/usb/musb/musb_io.h +++ b/drivers/usb/musb/musb_io.h @@ -37,11 +37,13 @@ struct musb_io { }; /* Do not add new entries here, add them the struct musb_io instead */ -extern u8 (*musb_readb)(const void __iomem *addr, unsigned offset); -extern void (*musb_writeb)(void __iomem *addr, unsigned offset, u8 data); -extern u16 (*musb_readw)(const void __iomem *addr, unsigned offset); -extern void (*musb_writew)(void __iomem *addr, unsigned offset, u16 data); -extern u32 musb_readl(const void __iomem *addr, unsigned offset); -extern void musb_writel(void __iomem *addr, unsigned offset, u32 data); +extern u8 (*musb_readb)(void __iomem *addr, u32 offset); +extern void (*musb_writeb)(void __iomem *addr, u32 offset, u8 data); +extern u8 (*musb_clearb)(void __iomem *addr, u32 offset); +extern u16 (*musb_readw)(void __iomem *addr, u32 offset); +extern void (*musb_writew)(void __iomem *addr, u32 offset, u16 data); +extern u16 (*musb_clearw)(void __iomem *addr, u32 offset); +extern u32 musb_readl(void __iomem *addr, u32 offset); +extern void musb_writel(void __iomem *addr, u32 offset, u32 data); #endif diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index bcc0fbf42ba8..0aacfc8be5a1 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -284,7 +284,7 @@ irqreturn_t dma_controller_irq(int irq, void *private_data) spin_lock_irqsave(&musb->lock, flags); - int_hsdma = musb_readb(mbase, MUSB_HSDMA_INTR); + int_hsdma = musb_clearb(mbase, MUSB_HSDMA_INTR); if (!int_hsdma) { musb_dbg(musb, "spurious DMA irq"); diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c index a72665fbf111..f3f76f2ac63f 100644 --- a/drivers/usb/musb/sunxi.c +++ b/drivers/usb/musb/sunxi.c @@ -407,7 +407,7 @@ static u32 sunxi_musb_busctl_offset(u8 epnum, u16 offset) return SUNXI_MUSB_TXFUNCADDR + offset; } -static u8 sunxi_musb_readb(const void __iomem *addr, unsigned offset) +static u8 sunxi_musb_readb(void __iomem *addr, u32 offset) { struct sunxi_glue *glue; @@ -520,7 +520,7 @@ static void sunxi_musb_writeb(void __iomem *addr, unsigned offset, u8 data) (int)(addr - sunxi_musb->mregs)); } -static u16 sunxi_musb_readw(const void __iomem *addr, unsigned offset) +static u16 sunxi_musb_readw(void __iomem *addr, u32 offset) { if (addr == sunxi_musb->mregs) { /* generic control or fifo control reg access */ diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 39453287b5c3..5d449089e3ad 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -142,7 +142,7 @@ static void tusb_ep_select(void __iomem *mbase, u8 epnum) /* * TUSB6010 doesn't allow 8-bit access; 16-bit access is the minimum. */ -static u8 tusb_readb(const void __iomem *addr, unsigned offset) +static u8 tusb_readb(void __iomem *addr, u32 offset) { u16 tmp; u8 val; -- cgit v1.2.3-59-g8ed1b From 0990366bab3c6afb93b276106e1e24d4bc69db7b Mon Sep 17 00:00:00 2001 From: Min Guo Date: Wed, 15 Jan 2020 07:25:45 -0600 Subject: usb: musb: Add support for MediaTek musb controller This adds support for MediaTek musb controller in host, peripheral and otg mode. There are some quirk of MediaTek musb controller, such as: -W1C interrupt status registers -Private data toggle registers -No dedicated DMA interrupt line Signed-off-by: Min Guo Signed-off-by: Yonglong Wu Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-24-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/Kconfig | 9 +- drivers/usb/musb/Makefile | 1 + drivers/usb/musb/mediatek.c | 582 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 591 insertions(+), 1 deletion(-) create mode 100644 drivers/usb/musb/mediatek.c diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 4678ebc889b5..63376d494f0f 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -115,6 +115,13 @@ config USB_MUSB_JZ4740 depends on USB_MUSB_GADGET depends on USB=n || USB_OTG_BLACKLIST_HUB +config USB_MUSB_MEDIATEK + tristate "MediaTek platforms" + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on NOP_USB_XCEIV + depends on GENERIC_PHY + select USB_ROLE_SWITCH + config USB_MUSB_AM335X_CHILD tristate @@ -141,7 +148,7 @@ config USB_UX500_DMA config USB_INVENTRA_DMA bool 'Inventra' - depends on USB_MUSB_OMAP2PLUS + depends on USB_MUSB_OMAP2PLUS || USB_MUSB_MEDIATEK help Enable DMA transfers using Mentor's engine. diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile index 3a88c79e650c..63d82d0fab67 100644 --- a/drivers/usb/musb/Makefile +++ b/drivers/usb/musb/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_USB_MUSB_DA8XX) += da8xx.o obj-$(CONFIG_USB_MUSB_UX500) += ux500.o obj-$(CONFIG_USB_MUSB_JZ4740) += jz4740.o obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o +obj-$(CONFIG_USB_MUSB_MEDIATEK) += mediatek.o obj-$(CONFIG_USB_MUSB_AM335X_CHILD) += musb_am335x.o diff --git a/drivers/usb/musb/mediatek.c b/drivers/usb/musb/mediatek.c new file mode 100644 index 000000000000..6b88c2f5d970 --- /dev/null +++ b/drivers/usb/musb/mediatek.c @@ -0,0 +1,582 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 MediaTek Inc. + * + * Author: + * Min Guo + * Yonglong Wu + */ + +#include +#include +#include +#include +#include +#include +#include +#include "musb_core.h" +#include "musb_dma.h" + +#define USB_L1INTS 0x00a0 +#define USB_L1INTM 0x00a4 +#define MTK_MUSB_TXFUNCADDR 0x0480 + +/* MediaTek controller toggle enable and status reg */ +#define MUSB_RXTOG 0x80 +#define MUSB_RXTOGEN 0x82 +#define MUSB_TXTOG 0x84 +#define MUSB_TXTOGEN 0x86 +#define MTK_TOGGLE_EN GENMASK(15, 0) + +#define TX_INT_STATUS BIT(0) +#define RX_INT_STATUS BIT(1) +#define USBCOM_INT_STATUS BIT(2) +#define DMA_INT_STATUS BIT(3) + +#define DMA_INTR_STATUS_MSK GENMASK(7, 0) +#define DMA_INTR_UNMASK_SET_MSK GENMASK(31, 24) + +struct mtk_glue { + struct device *dev; + struct musb *musb; + struct platform_device *musb_pdev; + struct platform_device *usb_phy; + struct phy *phy; + struct usb_phy *xceiv; + enum phy_mode phy_mode; + struct clk *main; + struct clk *mcu; + struct clk *univpll; + enum usb_role role; + struct usb_role_switch *role_sw; +}; + +static int mtk_musb_clks_get(struct mtk_glue *glue) +{ + struct device *dev = glue->dev; + + glue->main = devm_clk_get(dev, "main"); + if (IS_ERR(glue->main)) { + dev_err(dev, "fail to get main clock\n"); + return PTR_ERR(glue->main); + } + + glue->mcu = devm_clk_get(dev, "mcu"); + if (IS_ERR(glue->mcu)) { + dev_err(dev, "fail to get mcu clock\n"); + return PTR_ERR(glue->mcu); + } + + glue->univpll = devm_clk_get(dev, "univpll"); + if (IS_ERR(glue->univpll)) { + dev_err(dev, "fail to get univpll clock\n"); + return PTR_ERR(glue->univpll); + } + + return 0; +} + +static int mtk_musb_clks_enable(struct mtk_glue *glue) +{ + int ret; + + ret = clk_prepare_enable(glue->main); + if (ret) { + dev_err(glue->dev, "failed to enable main clock\n"); + goto err_main_clk; + } + + ret = clk_prepare_enable(glue->mcu); + if (ret) { + dev_err(glue->dev, "failed to enable mcu clock\n"); + goto err_mcu_clk; + } + + ret = clk_prepare_enable(glue->univpll); + if (ret) { + dev_err(glue->dev, "failed to enable univpll clock\n"); + goto err_univpll_clk; + } + + return 0; + +err_univpll_clk: + clk_disable_unprepare(glue->mcu); +err_mcu_clk: + clk_disable_unprepare(glue->main); +err_main_clk: + return ret; +} + +static void mtk_musb_clks_disable(struct mtk_glue *glue) +{ + clk_disable_unprepare(glue->univpll); + clk_disable_unprepare(glue->mcu); + clk_disable_unprepare(glue->main); +} + +static int musb_usb_role_sx_set(struct device *dev, enum usb_role role) +{ + struct mtk_glue *glue = dev_get_drvdata(dev); + struct musb *musb = glue->musb; + u8 devctl = readb(musb->mregs + MUSB_DEVCTL); + enum usb_role new_role; + + if (role == glue->role) + return 0; + + switch (role) { + case USB_ROLE_HOST: + musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; + glue->phy_mode = PHY_MODE_USB_HOST; + new_role = USB_ROLE_HOST; + if (glue->role == USB_ROLE_NONE) + phy_power_on(glue->phy); + + devctl |= MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + MUSB_HST_MODE(musb); + break; + case USB_ROLE_DEVICE: + musb->xceiv->otg->state = OTG_STATE_B_IDLE; + glue->phy_mode = PHY_MODE_USB_DEVICE; + new_role = USB_ROLE_DEVICE; + devctl &= ~MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + if (glue->role == USB_ROLE_NONE) + phy_power_on(glue->phy); + + MUSB_DEV_MODE(musb); + break; + case USB_ROLE_NONE: + glue->phy_mode = PHY_MODE_USB_OTG; + new_role = USB_ROLE_NONE; + devctl &= ~MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + if (glue->role != USB_ROLE_NONE) + phy_power_off(glue->phy); + + break; + default: + dev_err(glue->dev, "Invalid State\n"); + return -EINVAL; + } + + glue->role = new_role; + phy_set_mode(glue->phy, glue->phy_mode); + + return 0; +} + +static enum usb_role musb_usb_role_sx_get(struct device *dev) +{ + struct mtk_glue *glue = dev_get_drvdata(dev); + + return glue->role; +} + +static int mtk_otg_switch_init(struct mtk_glue *glue) +{ + struct usb_role_switch_desc role_sx_desc = { 0 }; + + role_sx_desc.set = musb_usb_role_sx_set; + role_sx_desc.get = musb_usb_role_sx_get; + role_sx_desc.fwnode = dev_fwnode(glue->dev); + glue->role_sw = usb_role_switch_register(glue->dev, &role_sx_desc); + + return PTR_ERR_OR_ZERO(glue->role_sw); +} + +static void mtk_otg_switch_exit(struct mtk_glue *glue) +{ + return usb_role_switch_unregister(glue->role_sw); +} + +static irqreturn_t generic_interrupt(int irq, void *__hci) +{ + unsigned long flags; + irqreturn_t retval = IRQ_NONE; + struct musb *musb = __hci; + + spin_lock_irqsave(&musb->lock, flags); + musb->int_usb = musb_clearb(musb->mregs, MUSB_INTRUSB); + musb->int_rx = musb_clearw(musb->mregs, MUSB_INTRRX); + musb->int_tx = musb_clearw(musb->mregs, MUSB_INTRTX); + + if (musb->int_usb || musb->int_tx || musb->int_rx) + retval = musb_interrupt(musb); + + spin_unlock_irqrestore(&musb->lock, flags); + + return retval; +} + +static irqreturn_t mtk_musb_interrupt(int irq, void *dev_id) +{ + irqreturn_t retval = IRQ_NONE; + struct musb *musb = (struct musb *)dev_id; + u32 l1_ints; + + l1_ints = musb_readl(musb->mregs, USB_L1INTS) & + musb_readl(musb->mregs, USB_L1INTM); + + if (l1_ints & (TX_INT_STATUS | RX_INT_STATUS | USBCOM_INT_STATUS)) + retval = generic_interrupt(irq, musb); + +#if defined(CONFIG_USB_INVENTRA_DMA) + if (l1_ints & DMA_INT_STATUS) + retval = dma_controller_irq(irq, musb->dma_controller); +#endif + return retval; +} + +static u32 mtk_musb_busctl_offset(u8 epnum, u16 offset) +{ + return MTK_MUSB_TXFUNCADDR + offset + 8 * epnum; +} + +static u8 mtk_musb_clearb(void __iomem *addr, unsigned int offset) +{ + u8 data; + + /* W1C */ + data = musb_readb(addr, offset); + musb_writeb(addr, offset, data); + return data; +} + +static u16 mtk_musb_clearw(void __iomem *addr, unsigned int offset) +{ + u16 data; + + /* W1C */ + data = musb_readw(addr, offset); + musb_writew(addr, offset, data); + return data; +} + +static int mtk_musb_set_mode(struct musb *musb, u8 mode) +{ + struct device *dev = musb->controller; + struct mtk_glue *glue = dev_get_drvdata(dev->parent); + enum phy_mode new_mode; + enum usb_role new_role; + + switch (mode) { + case MUSB_HOST: + new_mode = PHY_MODE_USB_HOST; + new_role = USB_ROLE_HOST; + break; + case MUSB_PERIPHERAL: + new_mode = PHY_MODE_USB_DEVICE; + new_role = USB_ROLE_DEVICE; + break; + case MUSB_OTG: + new_mode = PHY_MODE_USB_OTG; + new_role = USB_ROLE_NONE; + break; + default: + dev_err(glue->dev, "Invalid mode request\n"); + return -EINVAL; + } + + if (glue->phy_mode == new_mode) + return 0; + + if (musb->port_mode != MUSB_OTG) { + dev_err(glue->dev, "Does not support changing modes\n"); + return -EINVAL; + } + + glue->role = new_role; + musb_usb_role_sx_set(dev, glue->role); + return 0; +} + +static int mtk_musb_init(struct musb *musb) +{ + struct device *dev = musb->controller; + struct mtk_glue *glue = dev_get_drvdata(dev->parent); + int ret; + + glue->musb = musb; + musb->phy = glue->phy; + musb->xceiv = glue->xceiv; + musb->is_host = false; + musb->isr = mtk_musb_interrupt; + + /* Set TX/RX toggle enable */ + musb_writew(musb->mregs, MUSB_TXTOGEN, MTK_TOGGLE_EN); + musb_writew(musb->mregs, MUSB_RXTOGEN, MTK_TOGGLE_EN); + + if (musb->port_mode == MUSB_OTG) { + ret = mtk_otg_switch_init(glue); + if (ret) + return ret; + } + + ret = phy_init(glue->phy); + if (ret) + goto err_phy_init; + + ret = phy_power_on(glue->phy); + if (ret) + goto err_phy_power_on; + + phy_set_mode(glue->phy, glue->phy_mode); + +#if defined(CONFIG_USB_INVENTRA_DMA) + musb_writel(musb->mregs, MUSB_HSDMA_INTR, + DMA_INTR_STATUS_MSK | DMA_INTR_UNMASK_SET_MSK); +#endif + musb_writel(musb->mregs, USB_L1INTM, TX_INT_STATUS | RX_INT_STATUS | + USBCOM_INT_STATUS | DMA_INT_STATUS); + return 0; + +err_phy_power_on: + phy_exit(glue->phy); +err_phy_init: + mtk_otg_switch_exit(glue); + return ret; +} + +static u16 mtk_musb_get_toggle(struct musb_qh *qh, int is_out) +{ + struct musb *musb = qh->hw_ep->musb; + u8 epnum = qh->hw_ep->epnum; + u16 toggle; + + toggle = musb_readw(musb->mregs, is_out ? MUSB_TXTOG : MUSB_RXTOG); + return toggle & (1 << epnum); +} + +static u16 mtk_musb_set_toggle(struct musb_qh *qh, int is_out, struct urb *urb) +{ + struct musb *musb = qh->hw_ep->musb; + u8 epnum = qh->hw_ep->epnum; + u16 value, toggle; + + toggle = usb_gettoggle(urb->dev, qh->epnum, is_out); + + if (is_out) { + value = musb_readw(musb->mregs, MUSB_TXTOG); + value |= toggle << epnum; + musb_writew(musb->mregs, MUSB_TXTOG, value); + } else { + value = musb_readw(musb->mregs, MUSB_RXTOG); + value |= toggle << epnum; + musb_writew(musb->mregs, MUSB_RXTOG, value); + } + + return 0; +} + +static int mtk_musb_exit(struct musb *musb) +{ + struct device *dev = musb->controller; + struct mtk_glue *glue = dev_get_drvdata(dev->parent); + + mtk_otg_switch_exit(glue); + phy_power_off(glue->phy); + phy_exit(glue->phy); + mtk_musb_clks_disable(glue); + + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + return 0; +} + +static const struct musb_platform_ops mtk_musb_ops = { + .quirks = MUSB_DMA_INVENTRA, + .init = mtk_musb_init, + .get_toggle = mtk_musb_get_toggle, + .set_toggle = mtk_musb_set_toggle, + .exit = mtk_musb_exit, +#ifdef CONFIG_USB_INVENTRA_DMA + .dma_init = musbhs_dma_controller_create_noirq, + .dma_exit = musbhs_dma_controller_destroy, +#endif + .clearb = mtk_musb_clearb, + .clearw = mtk_musb_clearw, + .busctl_offset = mtk_musb_busctl_offset, + .set_mode = mtk_musb_set_mode, +}; + +#define MTK_MUSB_MAX_EP_NUM 8 +#define MTK_MUSB_RAM_BITS 11 + +static struct musb_fifo_cfg mtk_musb_mode_cfg[] = { + { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, + { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, + { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, + { .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, }, + { .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, }, + { .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, }, + { .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 512, }, + { .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 512, }, + { .hw_ep_num = 5, .style = FIFO_TX, .maxpacket = 512, }, + { .hw_ep_num = 5, .style = FIFO_RX, .maxpacket = 512, }, + { .hw_ep_num = 6, .style = FIFO_TX, .maxpacket = 1024, }, + { .hw_ep_num = 6, .style = FIFO_RX, .maxpacket = 1024, }, + { .hw_ep_num = 7, .style = FIFO_TX, .maxpacket = 512, }, + { .hw_ep_num = 7, .style = FIFO_RX, .maxpacket = 64, }, +}; + +static const struct musb_hdrc_config mtk_musb_hdrc_config = { + .fifo_cfg = mtk_musb_mode_cfg, + .fifo_cfg_size = ARRAY_SIZE(mtk_musb_mode_cfg), + .multipoint = true, + .dyn_fifo = true, + .num_eps = MTK_MUSB_MAX_EP_NUM, + .ram_bits = MTK_MUSB_RAM_BITS, +}; + +static const struct platform_device_info mtk_dev_info = { + .name = "musb-hdrc", + .id = PLATFORM_DEVID_AUTO, + .dma_mask = DMA_BIT_MASK(32), +}; + +static int mtk_musb_probe(struct platform_device *pdev) +{ + struct musb_hdrc_platform_data *pdata; + struct mtk_glue *glue; + struct platform_device_info pinfo; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + int ret = -ENOMEM; + + glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL); + if (!glue) + return -ENOMEM; + + glue->dev = dev; + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + ret = of_platform_populate(np, NULL, NULL, dev); + if (ret) { + dev_err(dev, "failed to create child devices at %p\n", np); + return ret; + } + + ret = mtk_musb_clks_get(glue); + if (ret) + return ret; + + pdata->config = &mtk_musb_hdrc_config; + pdata->platform_ops = &mtk_musb_ops; + pdata->mode = usb_get_dr_mode(dev); + + if (IS_ENABLED(CONFIG_USB_MUSB_HOST)) + pdata->mode = USB_DR_MODE_HOST; + else if (IS_ENABLED(CONFIG_USB_MUSB_GADGET)) + pdata->mode = USB_DR_MODE_PERIPHERAL; + + switch (pdata->mode) { + case USB_DR_MODE_HOST: + glue->phy_mode = PHY_MODE_USB_HOST; + glue->role = USB_ROLE_HOST; + break; + case USB_DR_MODE_PERIPHERAL: + glue->phy_mode = PHY_MODE_USB_DEVICE; + glue->role = USB_ROLE_DEVICE; + break; + case USB_DR_MODE_OTG: + glue->phy_mode = PHY_MODE_USB_OTG; + glue->role = USB_ROLE_NONE; + break; + default: + dev_err(&pdev->dev, "Error 'dr_mode' property\n"); + return -EINVAL; + } + + glue->phy = devm_of_phy_get_by_index(dev, np, 0); + if (IS_ERR(glue->phy)) { + dev_err(dev, "fail to getting phy %ld\n", + PTR_ERR(glue->phy)); + return PTR_ERR(glue->phy); + } + + glue->usb_phy = usb_phy_generic_register(); + if (IS_ERR(glue->usb_phy)) { + dev_err(dev, "fail to registering usb-phy %ld\n", + PTR_ERR(glue->usb_phy)); + return PTR_ERR(glue->usb_phy); + } + + glue->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); + if (IS_ERR(glue->xceiv)) { + dev_err(dev, "fail to getting usb-phy %d\n", ret); + ret = PTR_ERR(glue->xceiv); + goto err_unregister_usb_phy; + } + + platform_set_drvdata(pdev, glue); + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + ret = mtk_musb_clks_enable(glue); + if (ret) + goto err_enable_clk; + + pinfo = mtk_dev_info; + pinfo.parent = dev; + pinfo.res = pdev->resource; + pinfo.num_res = pdev->num_resources; + pinfo.data = pdata; + pinfo.size_data = sizeof(*pdata); + + glue->musb_pdev = platform_device_register_full(&pinfo); + if (IS_ERR(glue->musb_pdev)) { + ret = PTR_ERR(glue->musb_pdev); + dev_err(dev, "failed to register musb device: %d\n", ret); + goto err_device_register; + } + + return 0; + +err_device_register: + mtk_musb_clks_disable(glue); +err_enable_clk: + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); +err_unregister_usb_phy: + usb_phy_generic_unregister(glue->usb_phy); + return ret; +} + +static int mtk_musb_remove(struct platform_device *pdev) +{ + struct mtk_glue *glue = platform_get_drvdata(pdev); + struct platform_device *usb_phy = glue->usb_phy; + + platform_device_unregister(glue->musb_pdev); + usb_phy_generic_unregister(usb_phy); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id mtk_musb_match[] = { + {.compatible = "mediatek,mtk-musb",}, + {}, +}; +MODULE_DEVICE_TABLE(of, mtk_musb_match); +#endif + +static struct platform_driver mtk_musb_driver = { + .probe = mtk_musb_probe, + .remove = mtk_musb_remove, + .driver = { + .name = "musb-mtk", + .of_match_table = of_match_ptr(mtk_musb_match), + }, +}; + +module_platform_driver(mtk_musb_driver); + +MODULE_DESCRIPTION("MediaTek MUSB Glue Layer"); +MODULE_AUTHOR("Min Guo "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 1ea1859f8498275aeee35acde77fcb566cd8b999 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 15 Jan 2020 07:25:46 -0600 Subject: usb: musb: davinci: Convert to use GPIO descriptor The DaVinci MUSB glue contains an optional GPIO line to control VBUS power, convert this to use a GPIO descriptor and augment the EVM board file to provide this descriptor. I can't get this driver to compile properly and it depends on broken but when I didn get it to compile brokenly, it did at least not complain about THIS code being broken so I don't think I broke the driver any more than what it already is. I did away with the ifdefs that do not work with multiplatform anyway so the day someone decides to resurrect the code, the path to get it working should be easier as well since DaVinci is now multiplatform. Cc: Sekhar Nori Cc: Bartosz Golaszewski Cc: Tony Lindgren Signed-off-by: Linus Walleij [b-liu@ti.com: fixed one instance still ref to global variable vbus_state] Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-25-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-davinci/board-dm644x-evm.c | 12 +++++++ drivers/usb/musb/davinci.c | 57 ++++++++++++++++++-------------- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index 9d87d4e440ea..040c949414fa 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -823,6 +823,17 @@ static int davinci_phy_fixup(struct phy_device *phydev) #define HAS_NAND IS_ENABLED(CONFIG_MTD_NAND_DAVINCI) +#define GPIO_nVBUS_DRV 160 + +static struct gpiod_lookup_table dm644evm_usb_gpio_table = { + .dev_id = "musb-davinci", + .table = { + GPIO_LOOKUP("davinci_gpio", GPIO_nVBUS_DRV, NULL, + GPIO_ACTIVE_HIGH), + { } + }, +}; + static __init void davinci_evm_init(void) { int ret; @@ -875,6 +886,7 @@ static __init void davinci_evm_init(void) dm644x_init_asp(); /* irlml6401 switches over 1A, in under 8 msec */ + gpiod_add_lookup_table(&dm644evm_usb_gpio_table); davinci_setup_usb(1000, 8); if (IS_BUILTIN(CONFIG_PHYLIB)) { diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index fb6bbd254ab7..704435526394 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -25,10 +25,6 @@ #include "musb_core.h" -#ifdef CONFIG_MACH_DAVINCI_EVM -#define GPIO_nVBUS_DRV 160 -#endif - #include "davinci.h" #include "cppi_dma.h" @@ -40,6 +36,9 @@ struct davinci_glue { struct device *dev; struct platform_device *musb; struct clk *clk; + bool vbus_state; + struct gpio_desc *vbus; + struct work_struct vbus_work; }; /* REVISIT (PM) we should be able to keep the PHY in low power mode most @@ -135,43 +134,44 @@ static void davinci_musb_disable(struct musb *musb) * when J10 is out, and TI documents it as handling OTG. */ -#ifdef CONFIG_MACH_DAVINCI_EVM - -static int vbus_state = -1; - /* I2C operations are always synchronous, and require a task context. * With unloaded systems, using the shared workqueue seems to suffice * to satisfy the 100msec A_WAIT_VRISE timeout... */ -static void evm_deferred_drvvbus(struct work_struct *ignored) +static void evm_deferred_drvvbus(struct work_struct *work) { - gpio_set_value_cansleep(GPIO_nVBUS_DRV, vbus_state); - vbus_state = !vbus_state; -} + struct davinci_glue *glue = container_of(work, struct davinci_glue, + vbus_work); -#endif /* EVM */ + gpiod_set_value_cansleep(glue->vbus, glue->vbus_state); + glue->vbus_state = !glue->vbus_state; +} -static void davinci_musb_source_power(struct musb *musb, int is_on, int immediate) +static void davinci_musb_source_power(struct musb *musb, int is_on, + int immediate) { -#ifdef CONFIG_MACH_DAVINCI_EVM + struct davinci_glue *glue = dev_get_drvdata(musb->controller->parent); + + /* This GPIO handling is entirely optional */ + if (!glue->vbus) + return; + if (is_on) is_on = 1; - if (vbus_state == is_on) + if (glue->vbus_state == is_on) return; - vbus_state = !is_on; /* 0/1 vs "-1 == unknown/init" */ + /* 0/1 vs "-1 == unknown/init" */ + glue->vbus_state = !is_on; if (machine_is_davinci_evm()) { - static DECLARE_WORK(evm_vbus_work, evm_deferred_drvvbus); - if (immediate) - gpio_set_value_cansleep(GPIO_nVBUS_DRV, vbus_state); + gpiod_set_value_cansleep(glue->vbus, glue->vbus_state); else - schedule_work(&evm_vbus_work); + schedule_work(&glue->vbus_work); } if (immediate) - vbus_state = is_on; -#endif + glue->vbus_state = is_on; } static void davinci_musb_set_vbus(struct musb *musb, int is_on) @@ -524,6 +524,15 @@ static int davinci_probe(struct platform_device *pdev) pdata->platform_ops = &davinci_ops; + glue->vbus = devm_gpiod_get_optional(&pdev->dev, NULL, GPIOD_OUT_LOW); + if (IS_ERR(glue->vbus)) { + ret = PTR_ERR(glue->vbus); + goto err0; + } else { + glue->vbus_state = -1; + INIT_WORK(&glue->vbus_work, evm_deferred_drvvbus); + } + usb_phy_generic_register(); platform_set_drvdata(pdev, glue); -- cgit v1.2.3-59-g8ed1b From 4baa550ecc86693106493bd92382e0edb8caf64d Mon Sep 17 00:00:00 2001 From: Bin Liu Date: Wed, 15 Jan 2020 07:25:47 -0600 Subject: usb: musb: remove dummy driver musb_am335x.c Since commit 0782e8572ce4 ("ARM: dts: Probe am335x musb with ti-sysc"), the dummy driver musb_am335x.c is no longer needed, let's drop it. Acked-by: Tony Lindgren Signed-off-by: Bin Liu Link: https://lore.kernel.org/r/20200115132547.364-26-b-liu@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/Kconfig | 4 ---- drivers/usb/musb/Makefile | 3 --- drivers/usb/musb/musb_am335x.c | 44 ------------------------------------------ 3 files changed, 51 deletions(-) delete mode 100644 drivers/usb/musb/musb_am335x.c diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 63376d494f0f..eb2ded1026ee 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -101,7 +101,6 @@ config USB_MUSB_AM35X config USB_MUSB_DSPS tristate "TI DSPS platforms" - select USB_MUSB_AM335X_CHILD depends on ARCH_OMAP2PLUS || COMPILE_TEST depends on OF_IRQ @@ -122,9 +121,6 @@ config USB_MUSB_MEDIATEK depends on GENERIC_PHY select USB_ROLE_SWITCH -config USB_MUSB_AM335X_CHILD - tristate - comment "MUSB DMA mode" config MUSB_PIO_ONLY diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile index 63d82d0fab67..932247360a9f 100644 --- a/drivers/usb/musb/Makefile +++ b/drivers/usb/musb/Makefile @@ -26,9 +26,6 @@ obj-$(CONFIG_USB_MUSB_JZ4740) += jz4740.o obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o obj-$(CONFIG_USB_MUSB_MEDIATEK) += mediatek.o - -obj-$(CONFIG_USB_MUSB_AM335X_CHILD) += musb_am335x.o - # the kconfig must guarantee that only one of the # possible I/O schemes will be enabled at a time ... # PIO only, or DMA (several potential schemes). diff --git a/drivers/usb/musb/musb_am335x.c b/drivers/usb/musb/musb_am335x.c deleted file mode 100644 index 5f04f8e3a640..000000000000 --- a/drivers/usb/musb/musb_am335x.c +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include - -static int am335x_child_probe(struct platform_device *pdev) -{ - int ret; - - pm_runtime_enable(&pdev->dev); - - ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); - if (ret) - goto err; - - return 0; -err: - pm_runtime_disable(&pdev->dev); - return ret; -} - -static const struct of_device_id am335x_child_of_match[] = { - { .compatible = "ti,am33xx-usb" }, - { }, -}; -MODULE_DEVICE_TABLE(of, am335x_child_of_match); - -static struct platform_driver am335x_child_driver = { - .probe = am335x_child_probe, - .driver = { - .name = "am335x-usb-childs", - .of_match_table = am335x_child_of_match, - }, -}; - -static int __init am335x_child_init(void) -{ - return platform_driver_register(&am335x_child_driver); -} -module_init(am335x_child_init); - -MODULE_DESCRIPTION("AM33xx child devices"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 0d5677ecb014dfa26dca0c96a4c44fd9012e17ba Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 10 Jan 2020 01:25:33 +0000 Subject: phy: ti: j721e-wiz: Fix return value check in wiz_probe() In case of error, the function devm_ioremap() returns NULL pointer not ERR_PTR(). The IS_ERR() test in the return value check should be replaced with NULL test. Fixes: 091876cc355d ("phy: ti: j721e-wiz: Add support for WIZ module present in TI J721E SoC") Signed-off-by: Wei Yongjun Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/ti/phy-j721e-wiz.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/ti/phy-j721e-wiz.c b/drivers/phy/ti/phy-j721e-wiz.c index b5f6019e5c7d..7b51045df783 100644 --- a/drivers/phy/ti/phy-j721e-wiz.c +++ b/drivers/phy/ti/phy-j721e-wiz.c @@ -794,7 +794,7 @@ static int wiz_probe(struct platform_device *pdev) } base = devm_ioremap(dev, res.start, resource_size(&res)); - if (IS_ERR(base)) + if (!base) goto err_addr_to_resource; regmap = devm_regmap_init_mmio(dev, base, &wiz_regmap_config); -- cgit v1.2.3-59-g8ed1b From 8a79db5e83a5d52c74e6f3c40d6f312cf899213e Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Wed, 8 Jan 2020 10:30:07 +0200 Subject: dt-bindings: phy: Add PHY_TYPE_DP definition Add definition for DisplayPort phy type. Signed-off-by: Jyri Sarha Reviewed-by: Roger Quadros Reviewed-by: Kishon Vijay Abraham I Signed-off-by: Kishon Vijay Abraham I --- include/dt-bindings/phy/phy.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dt-bindings/phy/phy.h b/include/dt-bindings/phy/phy.h index b6a1eaf1b339..1f3f866fae7b 100644 --- a/include/dt-bindings/phy/phy.h +++ b/include/dt-bindings/phy/phy.h @@ -16,5 +16,6 @@ #define PHY_TYPE_USB2 3 #define PHY_TYPE_USB3 4 #define PHY_TYPE_UFS 5 +#define PHY_TYPE_DP 6 #endif /* _DT_BINDINGS_PHY */ -- cgit v1.2.3-59-g8ed1b From a00e7182308f41cac1aa071912ff7a16797dade9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 14 Jan 2020 12:01:45 +0100 Subject: USB: serial: opticon: add chars_in_buffer() implementation Add a chars_in_buffer() implementation so that the tty layer will wait for outgoing buffered data to be drained when needed (e.g. on final close()). Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/opticon.c | 45 +++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index cb7aac9cd9e7..05ea21ed967c 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -41,6 +41,7 @@ struct opticon_private { bool rts; bool cts; int outstanding_urbs; + int outstanding_bytes; }; @@ -169,6 +170,7 @@ static void opticon_write_control_callback(struct urb *urb) spin_lock_irqsave(&priv->lock, flags); --priv->outstanding_urbs; + priv->outstanding_bytes -= urb->transfer_buffer_length; spin_unlock_irqrestore(&priv->lock, flags); usb_serial_port_softint(port); @@ -182,8 +184,8 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port, struct urb *urb; unsigned char *buffer; unsigned long flags; - int status; struct usb_ctrlrequest *dr; + int ret = -ENOMEM; spin_lock_irqsave(&priv->lock, flags); if (priv->outstanding_urbs > URB_UPPER_LIMIT) { @@ -192,19 +194,16 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port, return 0; } priv->outstanding_urbs++; + priv->outstanding_bytes += count; spin_unlock_irqrestore(&priv->lock, flags); buffer = kmalloc(count, GFP_ATOMIC); - if (!buffer) { - count = -ENOMEM; + if (!buffer) goto error_no_buffer; - } urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!urb) { - count = -ENOMEM; + if (!urb) goto error_no_urb; - } memcpy(buffer, buf, count); @@ -213,10 +212,8 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port, /* The connected devices do not have a bulk write endpoint, * to transmit data to de barcode device the control endpoint is used */ dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC); - if (!dr) { - count = -ENOMEM; + if (!dr) goto error_no_dr; - } dr->bRequestType = USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT; dr->bRequest = 0x01; @@ -230,12 +227,9 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port, opticon_write_control_callback, port); /* send it down the pipe */ - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status) { - dev_err(&port->dev, - "%s - usb_submit_urb(write endpoint) failed status = %d\n", - __func__, status); - count = status; + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) { + dev_err(&port->dev, "failed to submit write urb: %d\n", ret); goto error; } @@ -253,8 +247,10 @@ error_no_urb: error_no_buffer: spin_lock_irqsave(&priv->lock, flags); --priv->outstanding_urbs; + priv->outstanding_bytes -= count; spin_unlock_irqrestore(&priv->lock, flags); - return count; + + return ret; } static int opticon_write_room(struct tty_struct *tty) @@ -279,6 +275,20 @@ static int opticon_write_room(struct tty_struct *tty) return 2048; } +static int opticon_chars_in_buffer(struct tty_struct *tty) +{ + struct usb_serial_port *port = tty->driver_data; + struct opticon_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + int count; + + spin_lock_irqsave(&priv->lock, flags); + count = priv->outstanding_bytes; + spin_unlock_irqrestore(&priv->lock, flags); + + return count; +} + static int opticon_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; @@ -383,6 +393,7 @@ static struct usb_serial_driver opticon_device = { .open = opticon_open, .write = opticon_write, .write_room = opticon_write_room, + .chars_in_buffer = opticon_chars_in_buffer, .throttle = usb_serial_generic_throttle, .unthrottle = usb_serial_generic_unthrottle, .get_serial = get_serial_info, -- cgit v1.2.3-59-g8ed1b From e6421583953fd92eba1785f90b228d70345125d6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 14 Jan 2020 12:01:46 +0100 Subject: USB: serial: opticon: stop all I/O on close() Make sure to stop any submitted write URBs on close(). Note that the tty layer will wait up to 30 seconds for the buffers to drain before close() is called. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/opticon.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index 05ea21ed967c..9fd9caab397b 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -42,6 +42,8 @@ struct opticon_private { bool cts; int outstanding_urbs; int outstanding_bytes; + + struct usb_anchor anchor; }; @@ -150,6 +152,15 @@ static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port) return res; } +static void opticon_close(struct usb_serial_port *port) +{ + struct opticon_private *priv = usb_get_serial_port_data(port); + + usb_kill_anchored_urbs(&priv->anchor); + + usb_serial_generic_close(port); +} + static void opticon_write_control_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; @@ -226,10 +237,13 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port, (unsigned char *)dr, buffer, count, opticon_write_control_callback, port); + usb_anchor_urb(urb, &priv->anchor); + /* send it down the pipe */ ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret) { dev_err(&port->dev, "failed to submit write urb: %d\n", ret); + usb_unanchor_urb(urb); goto error; } @@ -364,6 +378,7 @@ static int opticon_port_probe(struct usb_serial_port *port) return -ENOMEM; spin_lock_init(&priv->lock); + init_usb_anchor(&priv->anchor); usb_set_serial_port_data(port, priv); @@ -391,6 +406,7 @@ static struct usb_serial_driver opticon_device = { .port_probe = opticon_port_probe, .port_remove = opticon_port_remove, .open = opticon_open, + .close = opticon_close, .write = opticon_write, .write_room = opticon_write_room, .chars_in_buffer = opticon_chars_in_buffer, -- cgit v1.2.3-59-g8ed1b From 50c3c5e1c1b000d6a321ffdc0003bc6b7ac0b0e5 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 16 Jan 2020 16:03:27 -0600 Subject: USB: serial: garmin_gps: Use flexible-array member Old code in the kernel uses 1-byte and 0-byte arrays to indicate the presence of a "variable length array": struct something { int length; u8 data[1]; }; struct something *instance; instance = kmalloc(sizeof(*instance) + size, GFP_KERNEL); instance->length = size; memcpy(instance->data, source, size); There is also 0-byte arrays. Both cases pose confusion for things like sizeof(), CONFIG_FORTIFY_SOURCE, etc.[1] Instead, the preferred mechanism to declare variable-length types such as the one above is a flexible array member[2] which need to be the last member of a structure and empty-sized: struct something { int stuff; u8 data[]; }; Also, by making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being unadvertenly introduced[3] to the codebase from now on. [1] https://github.com/KSPP/linux/issues/21 [2] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: Johan Hovold --- drivers/usb/serial/garmin_gps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 633550ec3025..ffd984142171 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -104,7 +104,7 @@ struct garmin_packet { int seq; /* the real size of the data array, always > 0 */ int size; - __u8 data[1]; + __u8 data[]; }; /* structure used to keep the current state of the driver */ -- cgit v1.2.3-59-g8ed1b From fdd64df7b9d1e20dbe28c9c205682b66ad821e6c Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 17 Jan 2020 10:47:13 -0500 Subject: USB: usbfs: Always unlink URBs in reverse order When the kernel unlinks a bunch of URBs for a single endpoint, it should always unlink them in reverse order. This eliminates any possibility that some URB x will be unlinked before it can execute but the following URB x+1 will execute before it can be unlinked. Such an event would be bad, for obvious reasons. Chris Dickens pointed out that usbfs doesn't behave this way when it is unbound from an interface. All pending URBs are cancelled, but in the order of submission. This patch changes the behavior to make the unlinks occur in reverse order. It similarly changes the behavior when usbfs cancels the continuation URBs for a BULK endpoint. Suggested-by: Chris Dickens Signed-off-by: Alan Stern Link: https://lore.kernel.org/r/Pine.LNX.4.44L0.2001171045380.1571-100000@iolanthe.rowland.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 12bb5722b420..6833c918abce 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -574,7 +574,7 @@ __acquires(ps->lock) /* Now carefully unlink all the marked pending URBs */ rescan: - list_for_each_entry(as, &ps->async_pending, asynclist) { + list_for_each_entry_reverse(as, &ps->async_pending, asynclist) { if (as->bulk_status == AS_UNLINK) { as->bulk_status = 0; /* Only once */ urb = as->urb; @@ -636,7 +636,7 @@ static void destroy_async(struct usb_dev_state *ps, struct list_head *list) spin_lock_irqsave(&ps->lock, flags); while (!list_empty(list)) { - as = list_entry(list->next, struct async, asynclist); + as = list_last_entry(list, struct async, asynclist); list_del_init(&as->asynclist); urb = as->urb; usb_get_urb(urb); -- cgit v1.2.3-59-g8ed1b From 42bbdd99221bac206dde2b5e87098177fcd2a48e Mon Sep 17 00:00:00 2001 From: Hongbo Yao Date: Sat, 18 Jan 2020 02:53:10 +0530 Subject: phy: ti: j721e-wiz: Fix build error without CONFIG_OF_ADDRESS If CONFIG_OF_ADDRESS is not set and COMPILE_TEST=y, the following error is seen while building phy-j721e-wiz.c drivers/phy/ti/phy-j721e-wiz.o: In function `wiz_remove': phy-j721e-wiz.c:(.text+0x1a): undefined reference to `of_platform_device_destroy' Fix the config dependency for PHY_J721E_WIZ here. Reported-by: Hulk Robot Fixes: 091876cc355d ("phy: ti: j721e-wiz: Add support for WIZ module present in TI J721E SoC") Signed-off-by: Hongbo Yao Signed-off-by: Kishon Vijay Abraham I Link: https://lore.kernel.org/r/20200117212310.2864-1-kishon@ti.com Signed-off-by: Greg Kroah-Hartman --- drivers/phy/ti/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/phy/ti/Kconfig b/drivers/phy/ti/Kconfig index 3a1d3887c99c..6dbe9d0b9ff3 100644 --- a/drivers/phy/ti/Kconfig +++ b/drivers/phy/ti/Kconfig @@ -36,6 +36,7 @@ config PHY_AM654_SERDES config PHY_J721E_WIZ tristate "TI J721E WIZ (SERDES Wrapper) support" depends on OF && ARCH_K3 || COMPILE_TEST + depends on HAS_IOMEM && OF_ADDRESS depends on COMMON_CLK select GENERIC_PHY select MULTIPLEXER -- cgit v1.2.3-59-g8ed1b From 1e31d3caa262cffa728e007ad209fb8e72b276a8 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 17 Jan 2020 09:31:24 +0000 Subject: usb: musb: fix spelling mistake: "periperal" -> "peripheral" There is a spelling mistake in a dev_err error message. Fix it. Signed-off-by: Colin Ian King Acked-by: Bin Liu Link: https://lore.kernel.org/r/20200117093124.97965-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 71691a1f8ae3..f616fb489542 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -540,7 +540,7 @@ int musb_set_peripheral(struct musb *musb) devctl & MUSB_DEVCTL_BDEVICE, 5000, 1000000); if (error) { - dev_err(musb->controller, "%s: could not set periperal: %02x\n", + dev_err(musb->controller, "%s: could not set peripheral: %02x\n", __func__, devctl); return error; -- cgit v1.2.3-59-g8ed1b From 27bf5be8fbe0b60b1f1aa13083198dac64fc4249 Mon Sep 17 00:00:00 2001 From: Jun Li Date: Wed, 22 Jan 2020 01:46:59 +0000 Subject: usb: chipidea: handle single role for usb role class If usb port is configed to be single role, but usb role class is trying to set unavailable role, don't try to do role change. Signed-off-by: Jun Li Signed-off-by: Peter Chen Link: https://lore.kernel.org/r/20200122014639.22667-2-peter.chen@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci.h | 10 ++++++++++ drivers/usb/chipidea/core.c | 4 +++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 6911aef500e9..d49d5e1235d0 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -302,6 +302,16 @@ static inline enum usb_role ci_role_to_usb_role(struct ci_hdrc *ci) return USB_ROLE_NONE; } +static inline enum ci_role usb_role_to_ci_role(enum usb_role role) +{ + if (role == USB_ROLE_HOST) + return CI_ROLE_HOST; + else if (role == USB_ROLE_DEVICE) + return CI_ROLE_GADGET; + else + return CI_ROLE_END; +} + /** * hw_read_id_reg: reads from a identification register * @ci: the controller diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index dce5db41501c..52139c2a9924 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -618,9 +618,11 @@ static int ci_usb_role_switch_set(struct device *dev, enum usb_role role) struct ci_hdrc *ci = dev_get_drvdata(dev); struct ci_hdrc_cable *cable = NULL; enum usb_role current_role = ci_role_to_usb_role(ci); + enum ci_role ci_role = usb_role_to_ci_role(role); unsigned long flags; - if (current_role == role) + if ((ci_role != CI_ROLE_END && !ci->roles[ci_role]) || + (current_role == role)) return 0; pm_runtime_get_sync(ci->dev); -- cgit v1.2.3-59-g8ed1b From 9d69cd82fe02619ec68e5d770283576712188874 Mon Sep 17 00:00:00 2001 From: Jun Li Date: Wed, 22 Jan 2020 01:47:02 +0000 Subject: usb: chipidea: add inline for ci_hdrc_host_driver_init if host is not defined Otherwise, there is a build warning if this header file is included by non host source file, eg, otg.c. Signed-off-by: Jun Li Signed-off-by: Peter Chen Link: https://lore.kernel.org/r/20200122014639.22667-3-peter.chen@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/host.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/chipidea/host.h b/drivers/usb/chipidea/host.h index 70112cf0f195..2625aa01a911 100644 --- a/drivers/usb/chipidea/host.h +++ b/drivers/usb/chipidea/host.h @@ -20,7 +20,7 @@ static inline void ci_hdrc_host_destroy(struct ci_hdrc *ci) } -static void ci_hdrc_host_driver_init(void) +static inline void ci_hdrc_host_driver_init(void) { } -- cgit v1.2.3-59-g8ed1b From e1f236efd9c579a29d7df75aa052127d0d975267 Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Mon, 20 Jan 2020 14:19:10 +0000 Subject: usb: host: xhci-tegra: set MODULE_FIRMWARE for tegra186 Set the MODULE_FIRMWARE for tegra186, it's registered for 124/210 and ensures the firmware is available at the appropriate time such as in the initrd, else if the firmware is unavailable the driver fails with the following errors: tegra-xusb 3530000.usb: Direct firmware load for nvidia/tegra186/xusb.bin failed with error -2 tegra-xusb 3530000.usb: failed to request firmware: -2 tegra-xusb 3530000.usb: failed to load firmware: -2 tegra-xusb: probe of 3530000.usb failed with error -2 Fixes: 5f9be5f3f899 ("usb: host: xhci-tegra: Add Tegra186 XUSB support") Signed-off-by: Peter Robinson Acked-by: Thierry Reding Cc: stable Link: https://lore.kernel.org/r/20200120141910.116097-1-pbrobinson@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 0b58ef3a7f7f..8163aefc6c6b 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -1744,6 +1744,7 @@ MODULE_FIRMWARE("nvidia/tegra210/xusb.bin"); static const char * const tegra186_supply_names[] = { }; +MODULE_FIRMWARE("nvidia/tegra186/xusb.bin"); static const struct tegra_xusb_phy_type tegra186_phy_types[] = { { .name = "usb3", .num = 3, }, -- cgit v1.2.3-59-g8ed1b From 3ba76256fc4e2a0d7fb26cc95459041ea0e88972 Mon Sep 17 00:00:00 2001 From: Jun Li Date: Mon, 20 Jan 2020 06:43:19 +0000 Subject: usb: typec: tcpci: mask event interrupts when remove driver This is to prevent any possible events generated while unregister tpcm port. Fixes: 74e656d6b055 ("staging: typec: Type-C Port Controller Interface driver (tcpci)") Signed-off-by: Li Jun Reviewed-by: Heikki Krogerus Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/1579502333-4145-1-git-send-email-jun.li@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpci.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index 8b4ff9fff340..753645bb2527 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -591,6 +591,12 @@ static int tcpci_probe(struct i2c_client *client, static int tcpci_remove(struct i2c_client *client) { struct tcpci_chip *chip = i2c_get_clientdata(client); + int err; + + /* Disable chip interrupts before unregistering port */ + err = tcpci_write16(chip->tcpci, TCPC_ALERT_MASK, 0); + if (err < 0) + return err; tcpci_unregister_port(chip->tcpci); -- cgit v1.2.3-59-g8ed1b From f5ae8869095552e3396ee3e404f9586cc6a828f0 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Fri, 17 Jan 2020 12:30:33 +0300 Subject: usb: dwc3: pci: add ID for the Intel Comet Lake -V variant There is one more Comet Lake PCH variant, CML-V, that has its own PCI ID. Signed-off-by: Heikki Krogerus Cc: stable Link: https://lore.kernel.org/r/20200117093033.48616-1-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-pci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 294276f7deb9..7051611229c9 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -34,6 +34,7 @@ #define PCI_DEVICE_ID_INTEL_GLK 0x31aa #define PCI_DEVICE_ID_INTEL_CNPLP 0x9dee #define PCI_DEVICE_ID_INTEL_CNPH 0xa36e +#define PCI_DEVICE_ID_INTEL_CNPV 0xa3b0 #define PCI_DEVICE_ID_INTEL_ICLLP 0x34ee #define PCI_DEVICE_ID_INTEL_EHLLP 0x4b7e #define PCI_DEVICE_ID_INTEL_TGPLP 0xa0ee @@ -342,6 +343,9 @@ static const struct pci_device_id dwc3_pci_id_table[] = { { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CNPH), (kernel_ulong_t) &dwc3_pci_intel_properties, }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CNPV), + (kernel_ulong_t) &dwc3_pci_intel_properties, }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICLLP), (kernel_ulong_t) &dwc3_pci_intel_properties, }, -- cgit v1.2.3-59-g8ed1b From 0e64350bf4668d0fbbfec66fd8e637b971b4e976 Mon Sep 17 00:00:00 2001 From: Thomas Hebb Date: Mon, 20 Jan 2020 06:09:05 -0800 Subject: usb: typec: wcove: fix "op-sink-microwatt" default that was in mW commit 4c912bff46cc ("usb: typec: wcove: Provide fwnode for the port") didn't convert this value from mW to uW when migrating to a new specification format like it should have. Fixes: 4c912bff46cc ("usb: typec: wcove: Provide fwnode for the port") Cc: stable@vger.kernel.org Signed-off-by: Thomas Hebb Reviewed-by: Heikki Krogerus Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/d8be32512efd31995ad7d65b27df9d443131b07c.1579529334.git.tommyhebb@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/wcove.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/typec/tcpm/wcove.c b/drivers/usb/typec/tcpm/wcove.c index edc271da14f4..9b745f432c91 100644 --- a/drivers/usb/typec/tcpm/wcove.c +++ b/drivers/usb/typec/tcpm/wcove.c @@ -597,7 +597,7 @@ static const struct property_entry wcove_props[] = { PROPERTY_ENTRY_STRING("try-power-role", "sink"), PROPERTY_ENTRY_U32_ARRAY("source-pdos", src_pdo), PROPERTY_ENTRY_U32_ARRAY("sink-pdos", snk_pdo), - PROPERTY_ENTRY_U32("op-sink-microwatt", 15000), + PROPERTY_ENTRY_U32("op-sink-microwatt", 15000000), { } }; -- cgit v1.2.3-59-g8ed1b From eb7a3bb8c955b3694e0e0998413ce1563c02f90c Mon Sep 17 00:00:00 2001 From: Thomas Hebb Date: Mon, 20 Jan 2020 06:09:06 -0800 Subject: usb: typec: fusb302: fix "op-sink-microwatt" default that was in mW commit 8f6244055bd3 ("usb: typec: fusb302: Always provide fwnode for the port") didn't convert this value from mW to uW when migrating to a new specification format like it should have. Fixes: 8f6244055bd3 ("usb: typec: fusb302: Always provide fwnode for the port") Cc: stable@vger.kernel.org Signed-off-by: Thomas Hebb Reviewed-by: Heikki Krogerus Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/0da564559af75ec829c6c7e3aa4024f857c91bee.1579529334.git.tommyhebb@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/fusb302.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index ed8655c6af8c..b498960ff72b 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c @@ -1666,7 +1666,7 @@ static const struct property_entry port_props[] = { PROPERTY_ENTRY_STRING("try-power-role", "sink"), PROPERTY_ENTRY_U32_ARRAY("source-pdos", src_pdo), PROPERTY_ENTRY_U32_ARRAY("sink-pdos", snk_pdo), - PROPERTY_ENTRY_U32("op-sink-microwatt", 2500), + PROPERTY_ENTRY_U32("op-sink-microwatt", 2500000), { } }; -- cgit v1.2.3-59-g8ed1b From 2988a8ae7476fe9535ab620320790d1714bdad1d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 22 Jan 2020 11:15:26 +0100 Subject: USB: serial: ir-usb: add missing endpoint sanity check Add missing endpoint sanity check to avoid dereferencing a NULL-pointer on open() in case a device lacks a bulk-out endpoint. Note that prior to commit f4a4cbb2047e ("USB: ir-usb: reimplement using generic framework") the oops would instead happen on open() if the device lacked a bulk-in endpoint and on write() if it lacked a bulk-out endpoint. Fixes: f4a4cbb2047e ("USB: ir-usb: reimplement using generic framework") Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/ir-usb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 302eb9530859..c3b06fc5a7f0 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -195,6 +195,9 @@ static int ir_startup(struct usb_serial *serial) struct usb_irda_cs_descriptor *irda_desc; int rates; + if (serial->num_bulk_in < 1 || serial->num_bulk_out < 1) + return -ENODEV; + irda_desc = irda_usb_find_class_desc(serial, 0); if (!irda_desc) { dev_err(&serial->dev->dev, -- cgit v1.2.3-59-g8ed1b From 17a0184ca17e288decdca8b2841531e34d49285f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 22 Jan 2020 11:15:27 +0100 Subject: USB: serial: ir-usb: fix link-speed handling Commit e0d795e4f36c ("usb: irda: cleanup on ir-usb module") added a USB IrDA header with common defines, but mistakingly switched to using the class-descriptor baud-rate bitmask values for the outbound header. This broke link-speed handling for rates above 9600 baud, but a device would also be able to operate at the default 9600 baud until a link-speed request was issued (e.g. using the TCGETS ioctl). Fixes: e0d795e4f36c ("usb: irda: cleanup on ir-usb module") Cc: stable # 2.6.27 Cc: Felipe Balbi Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/ir-usb.c | 20 ++++++++++---------- include/linux/usb/irda.h | 13 ++++++++++++- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index c3b06fc5a7f0..26eab1307165 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -335,34 +335,34 @@ static void ir_set_termios(struct tty_struct *tty, switch (baud) { case 2400: - ir_baud = USB_IRDA_BR_2400; + ir_baud = USB_IRDA_LS_2400; break; case 9600: - ir_baud = USB_IRDA_BR_9600; + ir_baud = USB_IRDA_LS_9600; break; case 19200: - ir_baud = USB_IRDA_BR_19200; + ir_baud = USB_IRDA_LS_19200; break; case 38400: - ir_baud = USB_IRDA_BR_38400; + ir_baud = USB_IRDA_LS_38400; break; case 57600: - ir_baud = USB_IRDA_BR_57600; + ir_baud = USB_IRDA_LS_57600; break; case 115200: - ir_baud = USB_IRDA_BR_115200; + ir_baud = USB_IRDA_LS_115200; break; case 576000: - ir_baud = USB_IRDA_BR_576000; + ir_baud = USB_IRDA_LS_576000; break; case 1152000: - ir_baud = USB_IRDA_BR_1152000; + ir_baud = USB_IRDA_LS_1152000; break; case 4000000: - ir_baud = USB_IRDA_BR_4000000; + ir_baud = USB_IRDA_LS_4000000; break; default: - ir_baud = USB_IRDA_BR_9600; + ir_baud = USB_IRDA_LS_9600; baud = 9600; } diff --git a/include/linux/usb/irda.h b/include/linux/usb/irda.h index 396d2b043e64..556a801efce3 100644 --- a/include/linux/usb/irda.h +++ b/include/linux/usb/irda.h @@ -119,11 +119,22 @@ struct usb_irda_cs_descriptor { * 6 - 115200 bps * 7 - 576000 bps * 8 - 1.152 Mbps - * 9 - 5 mbps + * 9 - 4 Mbps * 10..15 - Reserved */ #define USB_IRDA_STATUS_LINK_SPEED 0x0f +#define USB_IRDA_LS_NO_CHANGE 0 +#define USB_IRDA_LS_2400 1 +#define USB_IRDA_LS_9600 2 +#define USB_IRDA_LS_19200 3 +#define USB_IRDA_LS_38400 4 +#define USB_IRDA_LS_57600 5 +#define USB_IRDA_LS_115200 6 +#define USB_IRDA_LS_576000 7 +#define USB_IRDA_LS_1152000 8 +#define USB_IRDA_LS_4000000 9 + /* The following is a 4-bit value used only for * outbound header: * -- cgit v1.2.3-59-g8ed1b From 38c0d5bdf4973f9f5a888166e9d3e9ed0d32057a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 22 Jan 2020 11:15:28 +0100 Subject: USB: serial: ir-usb: fix IrLAP framing Commit f4a4cbb2047e ("USB: ir-usb: reimplement using generic framework") switched to using the generic write implementation which may combine multiple write requests into larger transfers. This can break the IrLAP protocol where end-of-frame is determined using the USB short packet mechanism, for example, if multiple frames are sent in rapid succession. Fixes: f4a4cbb2047e ("USB: ir-usb: reimplement using generic framework") Cc: stable # 2.6.35 Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/ir-usb.c | 113 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 91 insertions(+), 22 deletions(-) diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 26eab1307165..627bea7e6cfb 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -45,9 +45,10 @@ static int buffer_size; static int xbof = -1; static int ir_startup (struct usb_serial *serial); -static int ir_open(struct tty_struct *tty, struct usb_serial_port *port); -static int ir_prepare_write_buffer(struct usb_serial_port *port, - void *dest, size_t size); +static int ir_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count); +static int ir_write_room(struct tty_struct *tty); +static void ir_write_bulk_callback(struct urb *urb); static void ir_process_read_urb(struct urb *urb); static void ir_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios); @@ -77,8 +78,9 @@ static struct usb_serial_driver ir_device = { .num_ports = 1, .set_termios = ir_set_termios, .attach = ir_startup, - .open = ir_open, - .prepare_write_buffer = ir_prepare_write_buffer, + .write = ir_write, + .write_room = ir_write_room, + .write_bulk_callback = ir_write_bulk_callback, .process_read_urb = ir_process_read_urb, }; @@ -254,35 +256,102 @@ static int ir_startup(struct usb_serial *serial) return 0; } -static int ir_open(struct tty_struct *tty, struct usb_serial_port *port) +static int ir_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { - int i; + struct urb *urb = NULL; + unsigned long flags; + int ret; - for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) - port->write_urbs[i]->transfer_flags = URB_ZERO_PACKET; + if (port->bulk_out_size == 0) + return -EINVAL; - /* Start reading from the device */ - return usb_serial_generic_open(tty, port); -} + if (count == 0) + return 0; -static int ir_prepare_write_buffer(struct usb_serial_port *port, - void *dest, size_t size) -{ - unsigned char *buf = dest; - int count; + count = min(count, port->bulk_out_size - 1); + + spin_lock_irqsave(&port->lock, flags); + if (__test_and_clear_bit(0, &port->write_urbs_free)) { + urb = port->write_urbs[0]; + port->tx_bytes += count; + } + spin_unlock_irqrestore(&port->lock, flags); + + if (!urb) + return 0; /* * The first byte of the packet we send to the device contains an - * inbound header which indicates an additional number of BOFs and + * outbound header which indicates an additional number of BOFs and * a baud rate change. * * See section 5.4.2.2 of the USB IrDA spec. */ - *buf = ir_xbof | ir_baud; + *(u8 *)urb->transfer_buffer = ir_xbof | ir_baud; + + memcpy(urb->transfer_buffer + 1, buf, count); + + urb->transfer_buffer_length = count + 1; + urb->transfer_flags = URB_ZERO_PACKET; + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) { + dev_err(&port->dev, "failed to submit write urb: %d\n", ret); + + spin_lock_irqsave(&port->lock, flags); + __set_bit(0, &port->write_urbs_free); + port->tx_bytes -= count; + spin_unlock_irqrestore(&port->lock, flags); + + return ret; + } + + return count; +} + +static void ir_write_bulk_callback(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + int status = urb->status; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + __set_bit(0, &port->write_urbs_free); + port->tx_bytes -= urb->transfer_buffer_length - 1; + spin_unlock_irqrestore(&port->lock, flags); + + switch (status) { + case 0: + break; + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + dev_dbg(&port->dev, "write urb stopped: %d\n", status); + return; + case -EPIPE: + dev_err(&port->dev, "write urb stopped: %d\n", status); + return; + default: + dev_err(&port->dev, "nonzero write-urb status: %d\n", status); + break; + } + + usb_serial_port_softint(port); +} + +static int ir_write_room(struct tty_struct *tty) +{ + struct usb_serial_port *port = tty->driver_data; + int count = 0; + + if (port->bulk_out_size == 0) + return 0; + + if (test_bit(0, &port->write_urbs_free)) + count = port->bulk_out_size - 1; - count = kfifo_out_locked(&port->write_fifo, buf + 1, size - 1, - &port->lock); - return count + 1; + return count; } static void ir_process_read_urb(struct urb *urb) -- cgit v1.2.3-59-g8ed1b From e7542bc382f8ca2eae25adaa444044513e474925 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 22 Jan 2020 11:15:29 +0100 Subject: USB: serial: ir-usb: make set_termios synchronous Use a synchronous usb_bulk_msg() when switching link speed in set_termios(). This way we do not need to keep track of outstanding URBs in order to be able to stop them at close. Note that there's no need to set URB_ZERO_PACKET as the one-byte transfer will always be short. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/ir-usb.c | 50 ++++++++++----------------------------------- 1 file changed, 11 insertions(+), 39 deletions(-) diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 627bea7e6cfb..3cd70392e2a2 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -376,23 +376,15 @@ static void ir_process_read_urb(struct urb *urb) tty_flip_buffer_push(&port->port); } -static void ir_set_termios_callback(struct urb *urb) -{ - kfree(urb->transfer_buffer); - - if (urb->status) - dev_dbg(&urb->dev->dev, "%s - non-zero urb status: %d\n", - __func__, urb->status); -} - static void ir_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { - struct urb *urb; + struct usb_device *udev = port->serial->dev; unsigned char *transfer_buffer; - int result; + int actual_length; speed_t baud; int ir_baud; + int ret; baud = tty_get_baud_rate(tty); @@ -447,42 +439,22 @@ static void ir_set_termios(struct tty_struct *tty, /* * send the baud change out on an "empty" data packet */ - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) - return; - transfer_buffer = kmalloc(1, GFP_KERNEL); if (!transfer_buffer) - goto err_buf; + return; *transfer_buffer = ir_xbof | ir_baud; - usb_fill_bulk_urb( - urb, - port->serial->dev, - usb_sndbulkpipe(port->serial->dev, - port->bulk_out_endpointAddress), - transfer_buffer, - 1, - ir_set_termios_callback, - port); - - urb->transfer_flags = URB_ZERO_PACKET; - - result = usb_submit_urb(urb, GFP_KERNEL); - if (result) { - dev_err(&port->dev, "%s - failed to submit urb: %d\n", - __func__, result); - goto err_subm; + ret = usb_bulk_msg(udev, + usb_sndbulkpipe(udev, port->bulk_out_endpointAddress), + transfer_buffer, 1, &actual_length, 5000); + if (ret || actual_length != 1) { + if (actual_length != 1) + ret = -EIO; + dev_err(&port->dev, "failed to change line speed: %d\n", ret); } - usb_free_urb(urb); - - return; -err_subm: kfree(transfer_buffer); -err_buf: - usb_free_urb(urb); } static int __init ir_init(void) -- cgit v1.2.3-59-g8ed1b From a1c91c1036397f2f7074aa4f5df8a2412e94ab97 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 22 Jan 2020 11:15:30 +0100 Subject: USB: serial: ir-usb: simplify endpoint check Simplify the endpoint sanity check by letting core verify that the required endpoints are present. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/ir-usb.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 3cd70392e2a2..79d0586e2b33 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -76,6 +76,8 @@ static struct usb_serial_driver ir_device = { .description = "IR Dongle", .id_table = ir_id_table, .num_ports = 1, + .num_bulk_in = 1, + .num_bulk_out = 1, .set_termios = ir_set_termios, .attach = ir_startup, .write = ir_write, @@ -197,9 +199,6 @@ static int ir_startup(struct usb_serial *serial) struct usb_irda_cs_descriptor *irda_desc; int rates; - if (serial->num_bulk_in < 1 || serial->num_bulk_out < 1) - return -ENODEV; - irda_desc = irda_usb_find_class_desc(serial, 0); if (!irda_desc) { dev_err(&serial->dev->dev, -- cgit v1.2.3-59-g8ed1b From 19c64e7354e50d19e7b5ddf94bfb5fa24d69594c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 22 Jan 2020 23:39:55 +0000 Subject: USB: serial: cyberjack: fix spelling mistake "To" -> "Too" There is a spelling mistake in a dev_dbg message. Fix it. Signed-off-by: Colin Ian King Signed-off-by: Johan Hovold --- drivers/usb/serial/cyberjack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index ebd76ab07b72..821970609695 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -276,7 +276,7 @@ static void cyberjack_read_int_callback(struct urb *urb) old_rdtodo = priv->rdtodo; if (old_rdtodo > SHRT_MAX - size) { - dev_dbg(dev, "To many bulk_in urbs to do.\n"); + dev_dbg(dev, "Too many bulk_in urbs to do.\n"); spin_unlock_irqrestore(&priv->lock, flags); goto resubmit; } -- cgit v1.2.3-59-g8ed1b From fdabc466f335bc3cbda8eca2270a8af783cae7eb Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 23 Jan 2020 16:50:13 +0100 Subject: usb: phy: phy-gpio-vbus-usb: Convert to GPIO descriptors Instead of using the legacy GPIO API and keeping track on polarity inversion semantics in the driver, switch to use GPIO descriptors for this driver and change all consumers in the process. This makes it possible to retire platform data completely: the only remaining platform data member was "wakeup" which was intended to make the vbus interrupt wakeup capable, but was not set by any users and thus remained unused. VBUS was not waking any devices up. Leave a comment about it so later developers using the platform can consider setting it to always enabled so plugging in USB wakes up the platform. Cc: Daniel Mack Cc: Haojian Zhuang Acked-by: Robert Jarzmik Acked-by: Felipe Balbi Acked-by: Sylwester Nawrocki Acked-by: Philipp Zabel Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20200123155013.93249-1-linus.walleij@linaro.org Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-pxa/colibri-pxa320.c | 16 ++++--- arch/arm/mach-pxa/eseries.c | 40 ++++++++++------ arch/arm/mach-pxa/gumstix.c | 18 ++++--- arch/arm/mach-pxa/hx4700.c | 22 +++++---- arch/arm/mach-pxa/magician.c | 22 ++++++--- arch/arm/mach-pxa/mioa701.c | 15 +++--- arch/arm/mach-pxa/palm27x.c | 34 ++++++------- arch/arm/mach-pxa/palmt5.c | 1 - arch/arm/mach-pxa/palmtc.c | 18 +++---- arch/arm/mach-pxa/palmte2.c | 18 +++---- arch/arm/mach-pxa/palmtx.c | 1 - arch/arm/mach-pxa/palmz72.c | 1 - arch/arm/mach-pxa/tosa.c | 18 +++---- arch/arm/mach-pxa/vpac270.c | 15 +++--- arch/arm/mach-s3c64xx/mach-smartq.c | 13 ++--- drivers/usb/phy/phy-gpio-vbus-usb.c | 96 +++++++++++++++++-------------------- include/linux/usb/gpio_vbus.h | 33 ------------- 17 files changed, 188 insertions(+), 193 deletions(-) delete mode 100644 include/linux/usb/gpio_vbus.h diff --git a/arch/arm/mach-pxa/colibri-pxa320.c b/arch/arm/mach-pxa/colibri-pxa320.c index eba917d69c0a..35dd3adb7712 100644 --- a/arch/arm/mach-pxa/colibri-pxa320.c +++ b/arch/arm/mach-pxa/colibri-pxa320.c @@ -11,9 +11,9 @@ #include #include #include +#include #include #include -#include #include #include @@ -144,17 +144,18 @@ static inline void __init colibri_pxa320_init_eth(void) {} #endif /* CONFIG_AX88796 */ #if defined(CONFIG_USB_PXA27X)||defined(CONFIG_USB_PXA27X_MODULE) -static struct gpio_vbus_mach_info colibri_pxa320_gpio_vbus_info = { - .gpio_vbus = mfp_to_gpio(MFP_PIN_GPIO96), - .gpio_pullup = -1, +static struct gpiod_lookup_table gpio_vbus_gpiod_table = { + .dev_id = "gpio-vbus", + .table = { + GPIO_LOOKUP("gpio-pxa", MFP_PIN_GPIO96, + "vbus", GPIO_ACTIVE_HIGH), + { }, + }, }; static struct platform_device colibri_pxa320_gpio_vbus = { .name = "gpio-vbus", .id = -1, - .dev = { - .platform_data = &colibri_pxa320_gpio_vbus_info, - }, }; static void colibri_pxa320_udc_command(int cmd) @@ -173,6 +174,7 @@ static struct pxa2xx_udc_mach_info colibri_pxa320_udc_info __initdata = { static void __init colibri_pxa320_init_udc(void) { pxa_set_udc_info(&colibri_pxa320_udc_info); + gpiod_add_lookup_table(&gpio_vbus_gpiod_table); platform_device_register(&colibri_pxa320_gpio_vbus); } #else diff --git a/arch/arm/mach-pxa/eseries.c b/arch/arm/mach-pxa/eseries.c index 91f7c3e40065..f37c44b6139d 100644 --- a/arch/arm/mach-pxa/eseries.c +++ b/arch/arm/mach-pxa/eseries.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -22,7 +23,6 @@ #include #include #include -#include #include #include