From 5de131d2134910d326fe6d6e882e88e9c6da2c71 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 23 Mar 2020 21:20:23 +0800 Subject: usb: chipidea: udc: fix the kernel doc for udc.h The kernel doc for td_node is outdated, update it. Signed-off-by: Peter Chen --- drivers/usb/chipidea/udc.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/udc.h b/drivers/usb/chipidea/udc.h index ebb11b625bb8..32b56f84f77a 100644 --- a/drivers/usb/chipidea/udc.h +++ b/drivers/usb/chipidea/udc.h @@ -67,10 +67,7 @@ struct td_node { * struct ci_hw_req - usb request representation * @req: request structure for gadget drivers * @queue: link to QH list - * @ptr: transfer descriptor for this request - * @dma: dma address for the transfer descriptor - * @zptr: transfer descriptor for the zero packet - * @zdma: dma address of the zero packet's transfer descriptor + * @tds: link to TD list */ struct ci_hw_req { struct usb_request req; -- cgit v1.2.3 From 80990f3fdc0a9e7cf9c86f13ce6ab89a616c3551 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 26 Mar 2020 15:53:57 +0800 Subject: usb: chipidea: core: refine the description for this driver Some descriptions are outdated, update them. Signed-off-by: Peter Chen --- drivers/usb/chipidea/core.c | 40 +++++++--------------------------------- 1 file changed, 7 insertions(+), 33 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index ae0bdc036464..12971c96c19e 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -3,42 +3,16 @@ * core.c - ChipIdea USB IP core family device controller * * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved. + * Copyright (C) 2020 NXP * * Author: David Lopo - */ - -/* - * Description: ChipIdea USB IP core family device controller - * - * This driver is composed of several blocks: - * - HW: hardware interface - * - DBG: debug facilities (optional) - * - UTIL: utilities - * - ISR: interrupts handling - * - ENDPT: endpoint operations (Gadget API) - * - GADGET: gadget operations (Gadget API) - * - BUS: bus glue code, bus abstraction layer - * - * Compile Options - * - STALL_IN: non-empty bulk-in pipes cannot be halted - * if defined mass storage compliance succeeds but with warnings - * => case 4: Hi > Dn - * => case 5: Hi > Di - * => case 8: Hi <> Do - * if undefined usbtest 13 fails - * - TRACE: enable function tracing (depends on DEBUG) - * - * Main Features - * - Chapter 9 & Mass Storage Compliance with Gadget File Storage - * - Chapter 9 Compliance with Gadget Zero (STALL_IN undefined) - * - Normal & LPM support - * - * USBTEST Report - * - OK: 0-12, 13 (STALL_IN defined) & 14 - * - Not Supported: 15 & 16 (ISO) + * Peter Chen * - * TODO List - * - Suspend & Remote Wakeup + * Main Features: + * - Four transfers are supported, usbtest is passed + * - USB Certification for gadget: CH9 and Mass Storage are passed + * - Low power mode + * - USB wakeup */ #include #include -- cgit v1.2.3 From 86b17c7f14ed1a442246a332c90df0e81003a2d3 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 26 Mar 2020 16:26:22 +0800 Subject: usb: chipidea: core: show the real pointer value for register The pointer value is "ptrval" like below at current code: ci_hdrc ci_hdrc.0: ChipIdea HDRC found, revision: 25, lpm: 0; cap: (ptrval) op: (ptrval) According to Documentation/core-api/printk-formats.rst, we change it from %p to %px for real value. Signed-off-by: Peter Chen --- drivers/usb/chipidea/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 12971c96c19e..ea8ac4a54a8d 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -246,7 +246,7 @@ static int hw_device_init(struct ci_hdrc *ci, void __iomem *base) ci->rev = ci_get_revision(ci); dev_dbg(ci->dev, - "ChipIdea HDRC found, revision: %d, lpm: %d; cap: %p op: %p\n", + "revision: %d, lpm: %d; cap: %px op: %px\n", ci->rev, ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op); /* setup lock mode ? */ -- cgit v1.2.3 From fc228ef6397de633703ac671e6eb020913de6918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Sat, 4 Apr 2020 01:59:58 +0200 Subject: usb: chipidea: usb2: constify zynq_pdata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pdata is copied anyway to allow setting device name. Make the source const. Signed-off-by: Michał Mirosław Signed-off-by: Peter Chen --- drivers/usb/chipidea/ci_hdrc_usb2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/ci_hdrc_usb2.c b/drivers/usb/chipidea/ci_hdrc_usb2.c index c044fba463e4..62cf8a99cf78 100644 --- a/drivers/usb/chipidea/ci_hdrc_usb2.c +++ b/drivers/usb/chipidea/ci_hdrc_usb2.c @@ -28,7 +28,7 @@ static const struct ci_hdrc_platform_data ci_default_pdata = { .flags = CI_HDRC_DISABLE_STREAMING, }; -static struct ci_hdrc_platform_data ci_zynq_pdata = { +static const struct ci_hdrc_platform_data ci_zynq_pdata = { .capoffset = DEF_CAPOFFSET, }; -- cgit v1.2.3 From 8b93527071a81425532c1b22067f6201e51c7b7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Sat, 4 Apr 2020 02:00:05 +0200 Subject: usb: chipidea: usb2: fix formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add spaces before closing braces. Signed-off-by: Michał Mirosław Signed-off-by: Peter Chen --- drivers/usb/chipidea/ci_hdrc_usb2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/ci_hdrc_usb2.c b/drivers/usb/chipidea/ci_hdrc_usb2.c index 62cf8a99cf78..bf300a234e64 100644 --- a/drivers/usb/chipidea/ci_hdrc_usb2.c +++ b/drivers/usb/chipidea/ci_hdrc_usb2.c @@ -33,8 +33,8 @@ static const struct ci_hdrc_platform_data ci_zynq_pdata = { }; static const struct of_device_id ci_hdrc_usb2_of_match[] = { - { .compatible = "chipidea,usb2"}, - { .compatible = "xlnx,zynq-usb-2.20a", .data = &ci_zynq_pdata}, + { .compatible = "chipidea,usb2" }, + { .compatible = "xlnx,zynq-usb-2.20a", .data = &ci_zynq_pdata }, { } }; MODULE_DEVICE_TABLE(of, ci_hdrc_usb2_of_match); -- cgit v1.2.3 From c2de37b31f178c4a4164751e9fef19acd3ceb913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Sat, 4 Apr 2020 02:00:05 +0200 Subject: usb: chipidea: usb2: make clock optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow clock to be missing from DT (assume it's enabled then). Signed-off-by: Michał Mirosław Signed-off-by: Peter Chen --- drivers/usb/chipidea/ci_hdrc_usb2.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/ci_hdrc_usb2.c b/drivers/usb/chipidea/ci_hdrc_usb2.c index bf300a234e64..9086514840ed 100644 --- a/drivers/usb/chipidea/ci_hdrc_usb2.c +++ b/drivers/usb/chipidea/ci_hdrc_usb2.c @@ -64,13 +64,14 @@ static int ci_hdrc_usb2_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - priv->clk = devm_clk_get(dev, NULL); - if (!IS_ERR(priv->clk)) { - ret = clk_prepare_enable(priv->clk); - if (ret) { - dev_err(dev, "failed to enable the clock: %d\n", ret); - return ret; - } + priv->clk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk);; + + ret = clk_prepare_enable(priv->clk); + if (ret) { + dev_err(dev, "failed to enable the clock: %d\n", ret); + return ret; } ci_pdata->name = dev_name(dev); @@ -94,8 +95,7 @@ static int ci_hdrc_usb2_probe(struct platform_device *pdev) return 0; clk_err: - if (!IS_ERR(priv->clk)) - clk_disable_unprepare(priv->clk); + clk_disable_unprepare(priv->clk); return ret; } -- cgit v1.2.3 From 1c16f63d1e6c9405d4e6f018c3cc33389adb1f22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Sat, 4 Apr 2020 02:00:06 +0200 Subject: usb: chipidea: usb2: absorb zevio glue driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ZEVIO glue code is is identical to generic binding now, but doesn't enable runtime PM. Let's squash the driver and get runtime PM for free. Signed-off-by: Michał Mirosław Signed-off-by: Peter Chen --- drivers/usb/chipidea/Makefile | 1 - drivers/usb/chipidea/ci_hdrc_usb2.c | 6 ++++ drivers/usb/chipidea/ci_hdrc_zevio.c | 67 ------------------------------------ 3 files changed, 6 insertions(+), 68 deletions(-) delete mode 100644 drivers/usb/chipidea/ci_hdrc_zevio.c (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile index 12df94f78f72..985663ba6e68 100644 --- a/drivers/usb/chipidea/Makefile +++ b/drivers/usb/chipidea/Makefile @@ -10,7 +10,6 @@ ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_usb2.o obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_msm.o -obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_zevio.o obj-$(CONFIG_USB_CHIPIDEA_PCI) += ci_hdrc_pci.o diff --git a/drivers/usb/chipidea/ci_hdrc_usb2.c b/drivers/usb/chipidea/ci_hdrc_usb2.c index 9086514840ed..93c864759135 100644 --- a/drivers/usb/chipidea/ci_hdrc_usb2.c +++ b/drivers/usb/chipidea/ci_hdrc_usb2.c @@ -32,9 +32,15 @@ static const struct ci_hdrc_platform_data ci_zynq_pdata = { .capoffset = DEF_CAPOFFSET, }; +static const struct ci_hdrc_platform_data ci_zevio_pdata = { + .capoffset = DEF_CAPOFFSET, + .flags = CI_HDRC_REGS_SHARED | CI_HDRC_FORCE_FULLSPEED, +}; + static const struct of_device_id ci_hdrc_usb2_of_match[] = { { .compatible = "chipidea,usb2" }, { .compatible = "xlnx,zynq-usb-2.20a", .data = &ci_zynq_pdata }, + { .compatible = "lsi,zevio-usb", .data = &ci_zevio_pdata }, { } }; MODULE_DEVICE_TABLE(of, ci_hdrc_usb2_of_match); diff --git a/drivers/usb/chipidea/ci_hdrc_zevio.c b/drivers/usb/chipidea/ci_hdrc_zevio.c deleted file mode 100644 index e1634da4a4b1..000000000000 --- a/drivers/usb/chipidea/ci_hdrc_zevio.c +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2013 Daniel Tang - * - * Based off drivers/usb/chipidea/ci_hdrc_msm.c - */ - -#include -#include -#include -#include - -#include "ci.h" - -static struct ci_hdrc_platform_data ci_hdrc_zevio_platdata = { - .name = "ci_hdrc_zevio", - .flags = CI_HDRC_REGS_SHARED | CI_HDRC_FORCE_FULLSPEED, - .capoffset = DEF_CAPOFFSET, -}; - -static int ci_hdrc_zevio_probe(struct platform_device *pdev) -{ - struct platform_device *ci_pdev; - - dev_dbg(&pdev->dev, "ci_hdrc_zevio_probe\n"); - - ci_pdev = ci_hdrc_add_device(&pdev->dev, - pdev->resource, pdev->num_resources, - &ci_hdrc_zevio_platdata); - - if (IS_ERR(ci_pdev)) { - dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n"); - return PTR_ERR(ci_pdev); - } - - platform_set_drvdata(pdev, ci_pdev); - - return 0; -} - -static int ci_hdrc_zevio_remove(struct platform_device *pdev) -{ - struct platform_device *ci_pdev = platform_get_drvdata(pdev); - - ci_hdrc_remove_device(ci_pdev); - - return 0; -} - -static const struct of_device_id ci_hdrc_zevio_dt_ids[] = { - { .compatible = "lsi,zevio-usb", }, - { /* sentinel */ } -}; - -static struct platform_driver ci_hdrc_zevio_driver = { - .probe = ci_hdrc_zevio_probe, - .remove = ci_hdrc_zevio_remove, - .driver = { - .name = "zevio_usb", - .of_match_table = ci_hdrc_zevio_dt_ids, - }, -}; - -MODULE_DEVICE_TABLE(of, ci_hdrc_zevio_dt_ids); -module_platform_driver(ci_hdrc_zevio_driver); - -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 95caa2ae70fdbf626335700d24400f47c12825ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Sat, 4 Apr 2020 02:00:06 +0200 Subject: usb: chipidea: allow disabling glue drivers if EMBEDDED MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow to cut down on driver size for embedded config. Signed-off-by: Michał Mirosław Signed-off-by: Peter Chen --- drivers/usb/chipidea/Kconfig | 37 ++++++++++++++++++++++++++----------- drivers/usb/chipidea/Makefile | 12 +++++------- 2 files changed, 31 insertions(+), 18 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig index d53db520e209..8bafcfc6080d 100644 --- a/drivers/usb/chipidea/Kconfig +++ b/drivers/usb/chipidea/Kconfig @@ -18,17 +18,6 @@ config USB_CHIPIDEA if USB_CHIPIDEA -config USB_CHIPIDEA_OF - tristate - depends on OF - default USB_CHIPIDEA - -config USB_CHIPIDEA_PCI - tristate - depends on USB_PCI - depends on NOP_USB_XCEIV - default USB_CHIPIDEA - config USB_CHIPIDEA_UDC bool "ChipIdea device controller" depends on USB_GADGET @@ -43,4 +32,30 @@ config USB_CHIPIDEA_HOST help Say Y here to enable host controller functionality of the ChipIdea driver. + +config USB_CHIPIDEA_PCI + tristate "Enable PCI glue driver" if EMBEDDED + depends on USB_PCI + depends on NOP_USB_XCEIV + default USB_CHIPIDEA + +config USB_CHIPIDEA_MSM + tristate "Enable MSM hsusb glue driver" if EMBEDDED + default USB_CHIPIDEA + +config USB_CHIPIDEA_IMX + tristate "Enable i.MX USB glue driver" if EMBEDDED + depends on OF + default USB_CHIPIDEA + +config USB_CHIPIDEA_GENERIC + tristate "Enable generic USB2 glue driver" if EMBEDDED + default USB_CHIPIDEA + +config USB_CHIPIDEA_TEGRA + tristate "Enable Tegra UDC glue driver" if EMBEDDED + depends on OF + depends on USB_CHIPIDEA_UDC + default USB_CHIPIDEA + endif diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile index 985663ba6e68..fae779a23866 100644 --- a/drivers/usb/chipidea/Makefile +++ b/drivers/usb/chipidea/Makefile @@ -8,10 +8,8 @@ ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o # Glue/Bridge layers go here -obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_usb2.o -obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_msm.o - -obj-$(CONFIG_USB_CHIPIDEA_PCI) += ci_hdrc_pci.o - -obj-$(CONFIG_USB_CHIPIDEA_OF) += usbmisc_imx.o ci_hdrc_imx.o -obj-$(CONFIG_USB_CHIPIDEA_OF) += ci_hdrc_tegra.o +obj-$(CONFIG_USB_CHIPIDEA_GENERIC) += ci_hdrc_usb2.o +obj-$(CONFIG_USB_CHIPIDEA_MSM) += ci_hdrc_msm.o +obj-$(CONFIG_USB_CHIPIDEA_PCI) += ci_hdrc_pci.o +obj-$(CONFIG_USB_CHIPIDEA_IMX) += ci_hdrc_imx.o usbmisc_imx.o +obj-$(CONFIG_USB_CHIPIDEA_TEGRA) += ci_hdrc_tegra.o -- cgit v1.2.3 From 4ee2fc81a631a3a2a45f3f6939323e8a5fea1e29 Mon Sep 17 00:00:00 2001 From: Jason Yan Date: Tue, 28 Apr 2020 14:33:59 +0800 Subject: usb: chipidea: usb2: remove unneeded semicolon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the following coccicheck warning: drivers/usb/chipidea/ci_hdrc_usb2.c:75:28-29: Unneeded semicolon Fixes: c2de37b31f17 ("usb: chipidea: usb2: make clock optional") Reviewed-by: Michał Mirosław Signed-off-by: Jason Yan Signed-off-by: Peter Chen --- drivers/usb/chipidea/ci_hdrc_usb2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/ci_hdrc_usb2.c b/drivers/usb/chipidea/ci_hdrc_usb2.c index 93c864759135..89e1d82d739b 100644 --- a/drivers/usb/chipidea/ci_hdrc_usb2.c +++ b/drivers/usb/chipidea/ci_hdrc_usb2.c @@ -72,7 +72,7 @@ static int ci_hdrc_usb2_probe(struct platform_device *pdev) priv->clk = devm_clk_get_optional(dev, NULL); if (IS_ERR(priv->clk)) - return PTR_ERR(priv->clk);; + return PTR_ERR(priv->clk); ret = clk_prepare_enable(priv->clk); if (ret) { -- cgit v1.2.3 From d755cdb1b9d7e1b645e176b97eb137194bbe8cf9 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 23 Jan 2020 14:00:26 +0800 Subject: usb: chipidea: introduce CI_HDRC_CONTROLLER_VBUS_EVENT glue layer use Some vendors glue layer need to handle some events for vbus, eg, some i.mx platforms (imx7d, imx8mm, imx8mn, etc) needs vbus event to handle charger detection, its charger detection is finished at glue layer code, but not at USB PHY driver. Signed-off-by: Peter Chen --- drivers/usb/chipidea/udc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 921bcf14dc06..da70fbe7ca4c 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1561,6 +1561,7 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) { struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); unsigned long flags; + int ret = 0; spin_lock_irqsave(&ci->lock, flags); ci->vbus_active = is_active; @@ -1570,10 +1571,14 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) usb_phy_set_charger_state(ci->usb_phy, is_active ? USB_CHARGER_PRESENT : USB_CHARGER_ABSENT); + if (ci->platdata->notify_event) + ret = ci->platdata->notify_event(ci, + CI_HDRC_CONTROLLER_VBUS_EVENT); + if (ci->driver) ci_hdrc_gadget_connect(_gadget, is_active); - return 0; + return ret; } static int ci_udc_wakeup(struct usb_gadget *_gadget) -- cgit v1.2.3 From 746f316b753a83e366bfc5f936cbf0d72d1c2d1d Mon Sep 17 00:00:00 2001 From: Jun Li Date: Thu, 23 Jan 2020 14:35:58 +0800 Subject: usb: chipidea: introduce imx7d USB charger detection imx7d (and imx8mm, imx8mn) uses Samsung PHY and USB generic PHY driver. The USB generic PHY driver is impossible to have a charger detection for every user, so we implement USB charger detection routine at glue layer. After the detection has finished, it will notify USB PHY charger framework, and the uevents will be triggered. Signed-off-by: Jun Li Signed-off-by: Peter Chen --- drivers/usb/chipidea/ci_hdrc_imx.c | 13 +- drivers/usb/chipidea/ci_hdrc_imx.h | 2 + drivers/usb/chipidea/usbmisc_imx.c | 245 +++++++++++++++++++++++++++++++++++++ 3 files changed, 259 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index d8e7eb2f97b9..fb4097f02391 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -271,6 +271,7 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event) struct device *dev = ci->dev->parent; struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); int ret = 0; + struct imx_usbmisc_data *mdata = data->usbmisc_data; switch (event) { case CI_HDRC_IMX_HSIC_ACTIVE_EVENT: @@ -284,11 +285,19 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event) } break; case CI_HDRC_IMX_HSIC_SUSPEND_EVENT: - ret = imx_usbmisc_hsic_set_connect(data->usbmisc_data); + ret = imx_usbmisc_hsic_set_connect(mdata); if (ret) dev_err(dev, "hsic_set_connect failed, err=%d\n", ret); break; + case CI_HDRC_CONTROLLER_VBUS_EVENT: + if (ci->vbus_active) + ret = imx_usbmisc_charger_detection(mdata, true); + else + ret = imx_usbmisc_charger_detection(mdata, false); + if (ci->usb_phy) + schedule_work(&ci->usb_phy->chg_work); + break; default: break; } @@ -415,6 +424,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) } pdata.usb_phy = data->phy; + if (data->usbmisc_data) + data->usbmisc_data->usb_phy = data->phy; if ((of_device_is_compatible(np, "fsl,imx53-usb") || of_device_is_compatible(np, "fsl,imx51-usb")) && pdata.usb_phy && diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h index c2051aeba13f..727d02b6dbd3 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.h +++ b/drivers/usb/chipidea/ci_hdrc_imx.h @@ -24,6 +24,7 @@ struct imx_usbmisc_data { unsigned int hsic:1; /* HSIC controlller */ unsigned int ext_id:1; /* ID from exteranl event */ unsigned int ext_vbus:1; /* Vbus from exteranl event */ + struct usb_phy *usb_phy; }; int imx_usbmisc_init(struct imx_usbmisc_data *data); @@ -31,5 +32,6 @@ int imx_usbmisc_init_post(struct imx_usbmisc_data *data); int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled); int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *data); int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on); +int imx_usbmisc_charger_detection(struct imx_usbmisc_data *data, bool connect); #endif /* __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H */ diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index e81e33c26e6c..8d7e78657e3d 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "ci_hdrc_imx.h" @@ -99,6 +100,32 @@ #define MX7D_USB_VBUS_WAKEUP_SOURCE_AVALID MX7D_USB_VBUS_WAKEUP_SOURCE(1) #define MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID MX7D_USB_VBUS_WAKEUP_SOURCE(2) #define MX7D_USB_VBUS_WAKEUP_SOURCE_SESS_END MX7D_USB_VBUS_WAKEUP_SOURCE(3) +/* The default DM/DP value is pull-down */ +#define MX7D_USBNC_USB_CTRL2_OPMODE(v) (v << 6) +#define MX7D_USBNC_USB_CTRL2_OPMODE_NON_DRIVING MX7D_USBNC_USB_CTRL2_OPMODE(1) +#define MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK (BIT(7) | BIT(6)) +#define MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN BIT(8) +#define MX7D_USBNC_USB_CTRL2_DP_OVERRIDE_VAL BIT(12) +#define MX7D_USBNC_USB_CTRL2_DP_OVERRIDE_EN BIT(13) +#define MX7D_USBNC_USB_CTRL2_DM_OVERRIDE_VAL BIT(14) +#define MX7D_USBNC_USB_CTRL2_DM_OVERRIDE_EN BIT(15) +#define MX7D_USBNC_USB_CTRL2_DP_DM_MASK (BIT(12) | BIT(13) | \ + BIT(14) | BIT(15)) + +#define MX7D_USB_OTG_PHY_CFG1 0x30 +#define MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL BIT(0) +#define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 BIT(1) +#define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 BIT(2) +#define MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB BIT(3) +#define MX7D_USB_OTG_PHY_CFG2_DRVVBUS0 BIT(16) + +#define MX7D_USB_OTG_PHY_CFG2 0x34 + +#define MX7D_USB_OTG_PHY_STATUS 0x3c +#define MX7D_USB_OTG_PHY_STATUS_LINE_STATE0 BIT(0) +#define MX7D_USB_OTG_PHY_STATUS_LINE_STATE1 BIT(1) +#define MX7D_USB_OTG_PHY_STATUS_VBUS_VLD BIT(3) +#define MX7D_USB_OTG_PHY_STATUS_CHRGDET BIT(29) #define MX6_USB_OTG_WAKEUP_BITS (MX6_BM_WAKEUP_ENABLE | MX6_BM_VBUS_WAKEUP | \ MX6_BM_ID_WAKEUP) @@ -114,6 +141,8 @@ struct usbmisc_ops { int (*hsic_set_connect)(struct imx_usbmisc_data *data); /* It's called during suspend/resume */ int (*hsic_set_clk)(struct imx_usbmisc_data *data, bool enabled); + /* usb charger detection */ + int (*charger_detection)(struct imx_usbmisc_data *data); }; struct imx_usbmisc { @@ -621,6 +650,188 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data) return 0; } +static int imx7d_charger_secondary_detection(struct imx_usbmisc_data *data) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + struct usb_phy *usb_phy = data->usb_phy; + int val; + unsigned long flags; + + /* VDM_SRC is connected to D- and IDP_SINK is connected to D+ */ + spin_lock_irqsave(&usbmisc->lock, flags); + val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2); + writel(val | MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 | + MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 | + MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL, + usbmisc->base + MX7D_USB_OTG_PHY_CFG2); + spin_unlock_irqrestore(&usbmisc->lock, flags); + + usleep_range(1000, 2000); + + /* + * Per BC 1.2, check voltage of D+: + * DCP: if greater than VDAT_REF; + * CDP: if less than VDAT_REF. + */ + val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS); + if (val & MX7D_USB_OTG_PHY_STATUS_CHRGDET) { + dev_dbg(data->dev, "It is a dedicate charging port\n"); + usb_phy->chg_type = DCP_TYPE; + } else { + dev_dbg(data->dev, "It is a charging downstream port\n"); + usb_phy->chg_type = CDP_TYPE; + } + + return 0; +} + +static void imx7_disable_charger_detector(struct imx_usbmisc_data *data) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&usbmisc->lock, flags); + val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2); + val &= ~(MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB | + MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 | + MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 | + MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL); + writel(val, usbmisc->base + MX7D_USB_OTG_PHY_CFG2); + + /* Set OPMODE to be 2'b00 and disable its override */ + val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2); + val &= ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK; + writel(val, usbmisc->base + MX7D_USBNC_USB_CTRL2); + + val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2); + writel(val & ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN, + usbmisc->base + MX7D_USBNC_USB_CTRL2); + spin_unlock_irqrestore(&usbmisc->lock, flags); +} + +static int imx7d_charger_data_contact_detect(struct imx_usbmisc_data *data) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + unsigned long flags; + u32 val; + int i, data_pin_contact_count = 0; + + /* Enable Data Contact Detect (DCD) per the USB BC 1.2 */ + spin_lock_irqsave(&usbmisc->lock, flags); + val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2); + writel(val | MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB, + usbmisc->base + MX7D_USB_OTG_PHY_CFG2); + spin_unlock_irqrestore(&usbmisc->lock, flags); + + for (i = 0; i < 100; i = i + 1) { + val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS); + if (!(val & MX7D_USB_OTG_PHY_STATUS_LINE_STATE0)) { + if (data_pin_contact_count++ > 5) + /* Data pin makes contact */ + break; + usleep_range(5000, 10000); + } else { + data_pin_contact_count = 0; + usleep_range(5000, 6000); + } + } + + /* Disable DCD after finished data contact check */ + spin_lock_irqsave(&usbmisc->lock, flags); + val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2); + writel(val & ~MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB, + usbmisc->base + MX7D_USB_OTG_PHY_CFG2); + spin_unlock_irqrestore(&usbmisc->lock, flags); + + if (i == 100) { + dev_err(data->dev, + "VBUS is coming from a dedicated power supply.\n"); + return -ENXIO; + } + + return 0; +} + +static int imx7d_charger_primary_detection(struct imx_usbmisc_data *data) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + struct usb_phy *usb_phy = data->usb_phy; + unsigned long flags; + u32 val; + + /* VDP_SRC is connected to D+ and IDM_SINK is connected to D- */ + spin_lock_irqsave(&usbmisc->lock, flags); + val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2); + val &= ~MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL; + writel(val | MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 | + MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0, + usbmisc->base + MX7D_USB_OTG_PHY_CFG2); + spin_unlock_irqrestore(&usbmisc->lock, flags); + + usleep_range(1000, 2000); + + /* Check if D- is less than VDAT_REF to determine an SDP per BC 1.2 */ + val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS); + if (!(val & MX7D_USB_OTG_PHY_STATUS_CHRGDET)) { + dev_dbg(data->dev, "It is a standard downstream port\n"); + usb_phy->chg_type = SDP_TYPE; + } + + return 0; +} + +/** + * Whole charger detection process: + * 1. OPMODE override to be non-driving + * 2. Data contact check + * 3. Primary detection + * 4. Secondary detection + * 5. Disable charger detection + */ +static int imx7d_charger_detection(struct imx_usbmisc_data *data) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + struct usb_phy *usb_phy = data->usb_phy; + unsigned long flags; + u32 val; + int ret; + + /* Check if vbus is valid */ + val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS); + if (!(val & MX7D_USB_OTG_PHY_STATUS_VBUS_VLD)) { + dev_err(data->dev, "vbus is error\n"); + return -EINVAL; + } + + /* + * Keep OPMODE to be non-driving mode during the whole + * charger detection process. + */ + spin_lock_irqsave(&usbmisc->lock, flags); + val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2); + val &= ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK; + val |= MX7D_USBNC_USB_CTRL2_OPMODE_NON_DRIVING; + writel(val, usbmisc->base + MX7D_USBNC_USB_CTRL2); + + val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2); + writel(val | MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN, + usbmisc->base + MX7D_USBNC_USB_CTRL2); + spin_unlock_irqrestore(&usbmisc->lock, flags); + + ret = imx7d_charger_data_contact_detect(data); + if (ret) + return ret; + + ret = imx7d_charger_primary_detection(data); + if (!ret && usb_phy->chg_type != SDP_TYPE) + ret = imx7d_charger_secondary_detection(data); + + imx7_disable_charger_detector(data); + + return ret; +} + static const struct usbmisc_ops imx25_usbmisc_ops = { .init = usbmisc_imx25_init, .post = usbmisc_imx25_post, @@ -659,6 +870,7 @@ static const struct usbmisc_ops imx6sx_usbmisc_ops = { static const struct usbmisc_ops imx7d_usbmisc_ops = { .init = usbmisc_imx7d_init, .set_wakeup = usbmisc_imx7d_set_wakeup, + .charger_detection = imx7d_charger_detection, }; static inline bool is_imx53_usbmisc(struct imx_usbmisc_data *data) @@ -737,6 +949,39 @@ int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on) return usbmisc->ops->hsic_set_clk(data, on); } EXPORT_SYMBOL_GPL(imx_usbmisc_hsic_set_clk); + +int imx_usbmisc_charger_detection(struct imx_usbmisc_data *data, bool connect) +{ + struct imx_usbmisc *usbmisc; + struct usb_phy *usb_phy; + int ret = 0; + + if (!data) + return -EINVAL; + + usbmisc = dev_get_drvdata(data->dev); + usb_phy = data->usb_phy; + if (!usbmisc->ops->charger_detection) + return -ENOTSUPP; + + if (connect) { + ret = usbmisc->ops->charger_detection(data); + if (ret) { + dev_err(data->dev, + "Error occurs during detection: %d\n", + ret); + usb_phy->chg_state = USB_CHARGER_ABSENT; + } else { + usb_phy->chg_state = USB_CHARGER_PRESENT; + } + } else { + usb_phy->chg_state = USB_CHARGER_ABSENT; + usb_phy->chg_type = UNKNOWN_TYPE; + } + return ret; +} +EXPORT_SYMBOL_GPL(imx_usbmisc_charger_detection); + static const struct of_device_id usbmisc_imx_dt_ids[] = { { .compatible = "fsl,imx25-usbmisc", -- cgit v1.2.3 From 5523f06a19502d08449c1e6ca7e33bfc860f6059 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 10 Feb 2020 18:46:27 +0800 Subject: usb: chipidea: pull down dp for possible charger detection operation The bootloader may use device mode, and keep dp up. We need dp to be pulled down before possbile charger detection operation. Signed-off-by: Peter Chen --- drivers/usb/chipidea/core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index ea8ac4a54a8d..804c0a5a213b 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -1123,8 +1123,11 @@ static int ci_hdrc_probe(struct platform_device *pdev) if (!ci_otg_is_fsm_mode(ci)) { /* only update vbus status for peripheral */ - if (ci->role == CI_ROLE_GADGET) + if (ci->role == CI_ROLE_GADGET) { + /* Pull down DP for possible charger detection */ + hw_write(ci, OP_USBCMD, USBCMD_RS, 0); ci_handle_vbus_change(ci); + } ret = ci_role_start(ci, ci->role); if (ret) { -- cgit v1.2.3 From 380a7843688d1fb61dca1382e715f5f40015f132 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 23 Jan 2020 14:43:47 +0800 Subject: usb: chipidea: usbmisc_imx: using different ops for imx7d and imx7ulp imx7ulp uses different USB PHY with imx7d (MXS PHY vs PICO PHY), so the features are supported by non-core register are a little different. For example, autoresume feature is supported by all controllers for imx7ulp, but for imx7d, it is only supported by non-HSIC controller. Besides, these two platforms use different HSIC controller, imx7ulp needs software operation, but imx7d doesn't. Signed-off-by: Peter Chen --- drivers/usb/chipidea/usbmisc_imx.c | 89 +++++++++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 8d7e78657e3d..f136876cb4a3 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -100,6 +100,7 @@ #define MX7D_USB_VBUS_WAKEUP_SOURCE_AVALID MX7D_USB_VBUS_WAKEUP_SOURCE(1) #define MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID MX7D_USB_VBUS_WAKEUP_SOURCE(2) #define MX7D_USB_VBUS_WAKEUP_SOURCE_SESS_END MX7D_USB_VBUS_WAKEUP_SOURCE(3) +#define MX7D_USBNC_AUTO_RESUME BIT(2) /* The default DM/DP value is pull-down */ #define MX7D_USBNC_USB_CTRL2_OPMODE(v) (v << 6) #define MX7D_USBNC_USB_CTRL2_OPMODE_NON_DRIVING MX7D_USBNC_USB_CTRL2_OPMODE(1) @@ -638,10 +639,17 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data) reg |= MX6_BM_PWR_POLARITY; writel(reg, usbmisc->base); - reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2); - reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK; - writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID, - usbmisc->base + MX7D_USBNC_USB_CTRL2); + /* SoC non-burst setting */ + reg = readl(usbmisc->base); + writel(reg | MX6_BM_NON_BURST_SETTING, usbmisc->base); + + if (!data->hsic) { + reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2); + reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK; + writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID + | MX7D_USBNC_AUTO_RESUME, + usbmisc->base + MX7D_USBNC_USB_CTRL2); + } spin_unlock_irqrestore(&usbmisc->lock, flags); @@ -832,6 +840,70 @@ static int imx7d_charger_detection(struct imx_usbmisc_data *data) return ret; } +static int usbmisc_imx7ulp_init(struct imx_usbmisc_data *data) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + unsigned long flags; + u32 reg; + + if (data->index >= 1) + return -EINVAL; + + spin_lock_irqsave(&usbmisc->lock, flags); + reg = readl(usbmisc->base); + if (data->disable_oc) { + reg |= MX6_BM_OVER_CUR_DIS; + } else { + reg &= ~MX6_BM_OVER_CUR_DIS; + + /* + * If the polarity is not configured keep it as setup by the + * bootloader. + */ + if (data->oc_pol_configured && data->oc_pol_active_low) + reg |= MX6_BM_OVER_CUR_POLARITY; + else if (data->oc_pol_configured) + reg &= ~MX6_BM_OVER_CUR_POLARITY; + } + /* If the polarity is not set keep it as setup by the bootlader */ + if (data->pwr_pol == 1) + reg |= MX6_BM_PWR_POLARITY; + + writel(reg, usbmisc->base); + + /* SoC non-burst setting */ + reg = readl(usbmisc->base); + writel(reg | MX6_BM_NON_BURST_SETTING, usbmisc->base); + + if (data->hsic) { + reg = readl(usbmisc->base); + writel(reg | MX6_BM_UTMI_ON_CLOCK, usbmisc->base); + + reg = readl(usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET); + reg |= MX6_BM_HSIC_EN | MX6_BM_HSIC_CLK_ON; + writel(reg, usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET); + + /* + * For non-HSIC controller, the autoresume is enabled + * at MXS PHY driver (usbphy_ctrl bit18). + */ + reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2); + writel(reg | MX7D_USBNC_AUTO_RESUME, + usbmisc->base + MX7D_USBNC_USB_CTRL2); + } else { + reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2); + reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK; + writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID, + usbmisc->base + MX7D_USBNC_USB_CTRL2); + } + + spin_unlock_irqrestore(&usbmisc->lock, flags); + + usbmisc_imx7d_set_wakeup(data, false); + + return 0; +} + static const struct usbmisc_ops imx25_usbmisc_ops = { .init = usbmisc_imx25_init, .post = usbmisc_imx25_post, @@ -873,6 +945,13 @@ static const struct usbmisc_ops imx7d_usbmisc_ops = { .charger_detection = imx7d_charger_detection, }; +static const struct usbmisc_ops imx7ulp_usbmisc_ops = { + .init = usbmisc_imx7ulp_init, + .set_wakeup = usbmisc_imx7d_set_wakeup, + .hsic_set_connect = usbmisc_imx6_hsic_set_connect, + .hsic_set_clk = usbmisc_imx6_hsic_set_clk, +}; + static inline bool is_imx53_usbmisc(struct imx_usbmisc_data *data) { struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); @@ -1025,7 +1104,7 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = { }, { .compatible = "fsl,imx7ulp-usbmisc", - .data = &imx7d_usbmisc_ops, + .data = &imx7ulp_usbmisc_ops, }, { /* sentinel */ } }; -- cgit v1.2.3 From e48aa1eb443f80fc6953734c805add4fb694b835 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 21 Feb 2020 21:40:57 +0800 Subject: usb: chipidea: udc: add software sg list support The chipidea controller doesn't support short transfer for sg list, so we still keep setting IOC per TD, otherwise, there will be no interrupt for short transfer. Each TD has five entries for data buffer, each data buffer could be non-countinuous 4KB buffer, so it could handle up to 5 sg buffers one time. The benefit of this patch is avoiding OOM for low memory system(eg, 256MB) during large USB transfers, see below for detail. The non-sg handling has not changed. ufb: page allocation failure: order:4, mode:0x40cc0(GFP_KERNEL|__GFP_COMP), nodemask=(null),cpuset=/,mems_allowed=0 CPU: 2 PID: 370 Comm: ufb Not tainted 5.4.3-1.1.0+g54b3750d61fd #1 Hardware name: NXP i.MX8MNano DDR4 EVK board (DT) Call trace: dump_backtrace+0x0/0x140 show_stack+0x14/0x20 dump_stack+0xb4/0xf8 warn_alloc+0xec/0x158 __alloc_pages_slowpath+0x9cc/0x9f8 __alloc_pages_nodemask+0x21c/0x280 alloc_pages_current+0x7c/0xe8 kmalloc_order+0x1c/0x88 __kmalloc+0x25c/0x298 ffs_epfile_io.isra.0+0x20c/0x7d0 ffs_epfile_read_iter+0xa8/0x188 new_sync_read+0xe4/0x170 __vfs_read+0x2c/0x40 vfs_read+0xc8/0x1a0 ksys_read+0x68/0xf0 __arm64_sys_read+0x18/0x20 el0_svc_common.constprop.0+0x68/0x160 el0_svc_handler+0x20/0x80 el0_svc+0x8/0xc Mem-Info: active_anon:2856 inactive_anon:5269 isolated_anon:12 active_file:5238 inactive_file:18803 isolated_file:0 unevictable:0 dirty:22 writeback:416 unstable:0 slab_reclaimable:4073 slab_unreclaimable:3408 mapped:727 shmem:7393 pagetables:37 bounce:0 free:4104 free_pcp:118 free_cma:0 Node 0 active_anon:11436kB inactive_anon:21076kB active_file:20988kB inactive_file:75216kB unevictable:0kB isolated(ano Node 0 DMA32 free:16820kB min:1808kB low:2260kB high:2712kB active_anon:11436kB inactive_anon:21076kB active_file:2098B lowmem_reserve[]: 0 0 0 Node 0 DMA32: 508*4kB (UME) 242*8kB (UME) 730*16kB (UM) 21*32kB (UME) 5*64kB (UME) 2*128kB (M) 0*256kB 0*512kB 0*1024kB Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=32768kB Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=2048kB Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=64kB 31455 total pagecache pages 0 pages in swap cache Swap cache stats: add 0, delete 0, find 0/0 Free swap = 0kB Total swap = 0kB 65536 pages RAM 0 pages HighMem/MovableOnly 10766 pages reserved 0 pages cma reserved 0 pages hwpoisoned Reviewed-by: Jun Li Signed-off-by: Peter Chen --- drivers/usb/chipidea/ci.h | 1 + drivers/usb/chipidea/udc.c | 163 ++++++++++++++++++++++++++++++++++++--------- drivers/usb/chipidea/udc.h | 1 + 3 files changed, 133 insertions(+), 32 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 644ecaef17ee..0697eb980e5f 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -25,6 +25,7 @@ #define TD_PAGE_COUNT 5 #define CI_HDRC_PAGE_SIZE 4096ul /* page size for TD's */ #define ENDPT_MAX 32 +#define CI_MAX_BUF_SIZE (TD_PAGE_COUNT * CI_HDRC_PAGE_SIZE) /****************************************************************************** * REGISTERS diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index da70fbe7ca4c..db0cfde0cc3c 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -338,7 +338,7 @@ static int hw_usb_reset(struct ci_hdrc *ci) *****************************************************************************/ static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq, - unsigned length) + unsigned int length, struct scatterlist *s) { int i; u32 temp; @@ -366,7 +366,13 @@ static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq, node->ptr->token |= cpu_to_le32(mul << __ffs(TD_MULTO)); } - temp = (u32) (hwreq->req.dma + hwreq->req.actual); + if (s) { + temp = (u32) (sg_dma_address(s) + hwreq->req.actual); + node->td_remaining_size = CI_MAX_BUF_SIZE - length; + } else { + temp = (u32) (hwreq->req.dma + hwreq->req.actual); + } + if (length) { node->ptr->page[0] = cpu_to_le32(temp); for (i = 1; i < TD_PAGE_COUNT; i++) { @@ -400,6 +406,122 @@ static inline u8 _usb_addr(struct ci_hw_ep *ep) return ((ep->dir == TX) ? USB_ENDPOINT_DIR_MASK : 0) | ep->num; } +static int prepare_td_for_non_sg(struct ci_hw_ep *hwep, + struct ci_hw_req *hwreq) +{ + unsigned int rest = hwreq->req.length; + int pages = TD_PAGE_COUNT; + int ret = 0; + + if (rest == 0) { + ret = add_td_to_list(hwep, hwreq, 0, NULL); + if (ret < 0) + return ret; + } + + /* + * The first buffer could be not page aligned. + * In that case we have to span into one extra td. + */ + if (hwreq->req.dma % PAGE_SIZE) + pages--; + + while (rest > 0) { + unsigned int count = min(hwreq->req.length - hwreq->req.actual, + (unsigned int)(pages * CI_HDRC_PAGE_SIZE)); + + ret = add_td_to_list(hwep, hwreq, count, NULL); + if (ret < 0) + return ret; + + rest -= count; + } + + if (hwreq->req.zero && hwreq->req.length && hwep->dir == TX + && (hwreq->req.length % hwep->ep.maxpacket == 0)) { + ret = add_td_to_list(hwep, hwreq, 0, NULL); + if (ret < 0) + return ret; + } + + return ret; +} + +static int prepare_td_per_sg(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq, + struct scatterlist *s) +{ + unsigned int rest = sg_dma_len(s); + int ret = 0; + + hwreq->req.actual = 0; + while (rest > 0) { + unsigned int count = min_t(unsigned int, rest, + CI_MAX_BUF_SIZE); + + ret = add_td_to_list(hwep, hwreq, count, s); + if (ret < 0) + return ret; + + rest -= count; + } + + return ret; +} + +static void ci_add_buffer_entry(struct td_node *node, struct scatterlist *s) +{ + int empty_td_slot_index = (CI_MAX_BUF_SIZE - node->td_remaining_size) + / CI_HDRC_PAGE_SIZE; + int i; + + node->ptr->token += + cpu_to_le32(sg_dma_len(s) << __ffs(TD_TOTAL_BYTES)); + + for (i = empty_td_slot_index; i < TD_PAGE_COUNT; i++) { + u32 page = (u32) sg_dma_address(s) + + (i - empty_td_slot_index) * CI_HDRC_PAGE_SIZE; + + page &= ~TD_RESERVED_MASK; + node->ptr->page[i] = cpu_to_le32(page); + } +} + +static int prepare_td_for_sg(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) +{ + struct usb_request *req = &hwreq->req; + struct scatterlist *s = req->sg; + int ret = 0, i = 0; + struct td_node *node = NULL; + + if (!s || req->zero || req->length == 0) { + dev_err(hwep->ci->dev, "not supported operation for sg\n"); + return -EINVAL; + } + + while (i++ < req->num_mapped_sgs) { + if (sg_dma_address(s) % PAGE_SIZE) { + dev_err(hwep->ci->dev, "not page aligned sg buffer\n"); + return -EINVAL; + } + + if (node && (node->td_remaining_size >= sg_dma_len(s))) { + ci_add_buffer_entry(node, s); + node->td_remaining_size -= sg_dma_len(s); + } else { + ret = prepare_td_per_sg(hwep, hwreq, s); + if (ret) + return ret; + + node = list_entry(hwreq->tds.prev, + struct td_node, td); + } + + s = sg_next(s); + } + + return ret; +} + /** * _hardware_enqueue: configures a request at hardware level * @hwep: endpoint @@ -411,8 +533,6 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) { struct ci_hdrc *ci = hwep->ci; int ret = 0; - unsigned rest = hwreq->req.length; - int pages = TD_PAGE_COUNT; struct td_node *firstnode, *lastnode; /* don't queue twice */ @@ -426,35 +546,13 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) if (ret) return ret; - /* - * The first buffer could be not page aligned. - * In that case we have to span into one extra td. - */ - if (hwreq->req.dma % PAGE_SIZE) - pages--; - - if (rest == 0) { - ret = add_td_to_list(hwep, hwreq, 0); - if (ret < 0) - goto done; - } - - while (rest > 0) { - unsigned count = min(hwreq->req.length - hwreq->req.actual, - (unsigned)(pages * CI_HDRC_PAGE_SIZE)); - ret = add_td_to_list(hwep, hwreq, count); - if (ret < 0) - goto done; - - rest -= count; - } + if (hwreq->req.num_mapped_sgs) + ret = prepare_td_for_sg(hwep, hwreq); + else + ret = prepare_td_for_non_sg(hwep, hwreq); - if (hwreq->req.zero && hwreq->req.length && hwep->dir == TX - && (hwreq->req.length % hwep->ep.maxpacket == 0)) { - ret = add_td_to_list(hwep, hwreq, 0); - if (ret < 0) - goto done; - } + if (ret) + return ret; firstnode = list_first_entry(&hwreq->tds, struct td_node, td); @@ -1941,6 +2039,7 @@ static int udc_start(struct ci_hdrc *ci) ci->gadget.max_speed = USB_SPEED_HIGH; ci->gadget.name = ci->platdata->name; ci->gadget.otg_caps = otg_caps; + ci->gadget.sg_supported = 1; if (ci->platdata->flags & CI_HDRC_REQUIRES_ALIGNED_DMA) ci->gadget.quirk_avoids_skb_reserve = 1; diff --git a/drivers/usb/chipidea/udc.h b/drivers/usb/chipidea/udc.h index 32b56f84f77a..5193df1e18c7 100644 --- a/drivers/usb/chipidea/udc.h +++ b/drivers/usb/chipidea/udc.h @@ -61,6 +61,7 @@ struct td_node { struct list_head td; dma_addr_t dma; struct ci_hw_td *ptr; + int td_remaining_size; }; /** -- cgit v1.2.3 From 6dbbbccdba6118b30837c42f3d356ecf0aaf4a1f Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 4 May 2020 23:43:46 +0100 Subject: usb: chipidea: Enable user-space triggered role-switching The flag provided by the USB role-switch logic allow_userspace_control allows user-space to trigger a role-switch. Several other USB controller drivers already enable this feature. Let's switch it on for the chipidea core now also. Cc: Peter Chen Cc: Jun Li Cc: Greg Kroah-Hartman Cc: Liam Girdwood Cc: Mark Brown Cc: linux-usb@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: Stephen Boyd Signed-off-by: Bryan O'Donoghue Signed-off-by: Peter Chen --- drivers/usb/chipidea/core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 804c0a5a213b..9a7c53d09ab4 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -640,6 +640,7 @@ static int ci_usb_role_switch_set(struct usb_role_switch *sw, static struct usb_role_switch_desc ci_role_switch = { .set = ci_usb_role_switch_set, .get = ci_usb_role_switch_get, + .allow_userspace_control = true, }; static int ci_get_platdata(struct device *dev, -- cgit v1.2.3