diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-06-02 10:47:03 +0900 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-06-02 10:47:03 +0900 |
commit | e152813ff5fea74094ae296367f477411df1ba24 (patch) | |
tree | 6e8c38a3b83f0f07f75df50793332aa47a81c709 /drivers | |
parent | 74db22cb3a16dcd782a31236eb139f5865804ae6 (diff) | |
parent | e18b7975c885bc3a938b9a76daf32957ea0235fa (diff) | |
download | linux-e152813ff5fea74094ae296367f477411df1ba24.tar.gz linux-e152813ff5fea74094ae296367f477411df1ba24.tar.bz2 linux-e152813ff5fea74094ae296367f477411df1ba24.zip |
Merge tag 'usb-for-v4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
Felipe writes:
usb: patches for v4.2 merge window
- dwc2 adds hibernation support
- preparation for sunxi glue to musb driver
- new ULPI bus
- new ULPI PHY driver for TUSB1210
- musb patches to support multiple DMA engines on same binary
- support for R-Car E2 on renesas_usbhs
Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers')
71 files changed, 3607 insertions, 1277 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index a53bd5b52df9..fceac96c2a31 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -309,4 +309,11 @@ config PHY_QCOM_UFS help Support for UFS PHY on QCOM chipsets. +config PHY_TUSB1210 + tristate "TI TUSB1210 ULPI PHY module" + depends on USB_ULPI_BUS + select GENERIC_PHY + help + Support for TI TUSB1210 USB ULPI PHY. + endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index f12625178780..0a2041803135 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -40,3 +40,4 @@ obj-$(CONFIG_PHY_STIH41X_USB) += phy-stih41x-usb.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o +obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c index a2b08f3ccb03..e17c539e4f6f 100644 --- a/drivers/phy/phy-sun4i-usb.c +++ b/drivers/phy/phy-sun4i-usb.c @@ -30,6 +30,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/phy/phy.h> +#include <linux/phy/phy-sun4i-usb.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> #include <linux/reset.h> @@ -58,6 +59,7 @@ #define PHY_OTG_FUNC_EN 0x28 #define PHY_VBUS_DET_EN 0x29 #define PHY_DISCON_TH_SEL 0x2a +#define PHY_SQUELCH_DETECT 0x3c #define MAX_PHYS 3 @@ -204,6 +206,13 @@ static int sun4i_usb_phy_power_off(struct phy *_phy) return 0; } +void sun4i_usb_phy_set_squelch_detect(struct phy *_phy, bool enabled) +{ + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); + + sun4i_usb_phy_write(phy, PHY_SQUELCH_DETECT, enabled ? 0 : 2, 2); +} + static struct phy_ops sun4i_usb_phy_ops = { .init = sun4i_usb_phy_init, .exit = sun4i_usb_phy_exit, diff --git a/drivers/phy/phy-tusb1210.c b/drivers/phy/phy-tusb1210.c new file mode 100644 index 000000000000..07efdd318bdc --- /dev/null +++ b/drivers/phy/phy-tusb1210.c @@ -0,0 +1,153 @@ +/** + * tusb1210.c - TUSB1210 USB ULPI PHY driver + * + * Copyright (C) 2015 Intel Corporation + * + * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/ulpi/driver.h> +#include <linux/gpio/consumer.h> + +#include "ulpi_phy.h" + +#define TUSB1210_VENDOR_SPECIFIC2 0x80 +#define TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT 0 +#define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT 4 +#define TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT 6 + +struct tusb1210 { + struct ulpi *ulpi; + struct phy *phy; + struct gpio_desc *gpio_reset; + struct gpio_desc *gpio_cs; + u8 vendor_specific2; +}; + +static int tusb1210_power_on(struct phy *phy) +{ + struct tusb1210 *tusb = phy_get_drvdata(phy); + + gpiod_set_value_cansleep(tusb->gpio_reset, 1); + gpiod_set_value_cansleep(tusb->gpio_cs, 1); + + /* Restore the optional eye diagram optimization value */ + if (tusb->vendor_specific2) + ulpi_write(tusb->ulpi, TUSB1210_VENDOR_SPECIFIC2, + tusb->vendor_specific2); + + return 0; +} + +static int tusb1210_power_off(struct phy *phy) +{ + struct tusb1210 *tusb = phy_get_drvdata(phy); + + gpiod_set_value_cansleep(tusb->gpio_reset, 0); + gpiod_set_value_cansleep(tusb->gpio_cs, 0); + + return 0; +} + +static struct phy_ops phy_ops = { + .power_on = tusb1210_power_on, + .power_off = tusb1210_power_off, + .owner = THIS_MODULE, +}; + +static int tusb1210_probe(struct ulpi *ulpi) +{ + struct gpio_desc *gpio; + struct tusb1210 *tusb; + u8 val, reg; + int ret; + + tusb = devm_kzalloc(&ulpi->dev, sizeof(*tusb), GFP_KERNEL); + if (!tusb) + return -ENOMEM; + + gpio = devm_gpiod_get(&ulpi->dev, "reset"); + if (!IS_ERR(gpio)) { + ret = gpiod_direction_output(gpio, 0); + if (ret) + return ret; + gpiod_set_value_cansleep(gpio, 1); + tusb->gpio_reset = gpio; + } + + gpio = devm_gpiod_get(&ulpi->dev, "cs"); + if (!IS_ERR(gpio)) { + ret = gpiod_direction_output(gpio, 0); + if (ret) + return ret; + gpiod_set_value_cansleep(gpio, 1); + tusb->gpio_cs = gpio; + } + + /* + * VENDOR_SPECIFIC2 register in TUSB1210 can be used for configuring eye + * diagram optimization and DP/DM swap. + */ + + /* High speed output drive strength configuration */ + device_property_read_u8(&ulpi->dev, "ihstx", &val); + reg = val << TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT; + + /* High speed output impedance configuration */ + device_property_read_u8(&ulpi->dev, "zhsdrv", &val); + reg |= val << TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT; + + /* DP/DM swap control */ + device_property_read_u8(&ulpi->dev, "datapolarity", &val); + reg |= val << TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT; + + if (reg) { + ulpi_write(ulpi, TUSB1210_VENDOR_SPECIFIC2, reg); + tusb->vendor_specific2 = reg; + } + + tusb->phy = ulpi_phy_create(ulpi, &phy_ops); + if (IS_ERR(tusb->phy)) + return PTR_ERR(tusb->phy); + + tusb->ulpi = ulpi; + + phy_set_drvdata(tusb->phy, tusb); + ulpi_set_drvdata(ulpi, tusb); + return 0; +} + +static void tusb1210_remove(struct ulpi *ulpi) +{ + struct tusb1210 *tusb = ulpi_get_drvdata(ulpi); + + ulpi_phy_destroy(ulpi, tusb->phy); +} + +#define TI_VENDOR_ID 0x0451 + +static const struct ulpi_device_id tusb1210_ulpi_id[] = { + { TI_VENDOR_ID, 0x1507, }, + { }, +}; +MODULE_DEVICE_TABLE(ulpi, tusb1210_ulpi_id); + +static struct ulpi_driver tusb1210_driver = { + .id_table = tusb1210_ulpi_id, + .probe = tusb1210_probe, + .remove = tusb1210_remove, + .driver = { + .name = "tusb1210", + .owner = THIS_MODULE, + }, +}; + +module_ulpi_driver(tusb1210_driver); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TUSB1210 ULPI PHY driver"); diff --git a/drivers/phy/ulpi_phy.h b/drivers/phy/ulpi_phy.h new file mode 100644 index 000000000000..ac49fb6285ee --- /dev/null +++ b/drivers/phy/ulpi_phy.h @@ -0,0 +1,31 @@ +#include <linux/phy/phy.h> + +/** + * Helper that registers PHY for a ULPI device and adds a lookup for binding it + * and it's controller, which is always the parent. + */ +static inline struct phy +*ulpi_phy_create(struct ulpi *ulpi, struct phy_ops *ops) +{ + struct phy *phy; + int ret; + + phy = phy_create(&ulpi->dev, NULL, ops); + if (IS_ERR(phy)) + return phy; + + ret = phy_create_lookup(phy, "usb2-phy", dev_name(ulpi->dev.parent)); + if (ret) { + phy_destroy(phy); + return ERR_PTR(ret); + } + + return phy; +} + +/* Remove a PHY that was created with ulpi_phy_create() and it's lookup. */ +static inline void ulpi_phy_destroy(struct ulpi *ulpi, struct phy *phy) +{ + phy_remove_lookup(phy, "usb2-phy", dev_name(ulpi->dev.parent)); + phy_destroy(phy); +} diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 02a522cb7753..022b8910e443 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -638,10 +638,15 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) INIT_WORK(&bci->work, twl4030_bci_usb_work); - bci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (!IS_ERR_OR_NULL(bci->transceiver)) { - bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; - usb_register_notifier(bci->transceiver, &bci->usb_nb); + bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; + if (bci->dev->of_node) { + struct device_node *phynode; + + phynode = of_find_compatible_node(bci->dev->of_node->parent, + NULL, "ti,twl4030-usb"); + if (phynode) + bci->transceiver = devm_usb_get_phy_by_node( + bci->dev, phynode, &bci->usb_nb); } /* Enable interrupts now. */ @@ -671,10 +676,6 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) return 0; fail_unmask_interrupts: - if (!IS_ERR_OR_NULL(bci->transceiver)) { - usb_unregister_notifier(bci->transceiver, &bci->usb_nb); - usb_put_phy(bci->transceiver); - } free_irq(bci->irq_bci, bci); fail_bci_irq: free_irq(bci->irq_chg, bci); @@ -703,10 +704,6 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, TWL4030_INTERRUPTS_BCIIMR2A); - if (!IS_ERR_OR_NULL(bci->transceiver)) { - usb_unregister_notifier(bci->transceiver, &bci->usb_nb); - usb_put_phy(bci->transceiver); - } free_irq(bci->irq_bci, bci); free_irq(bci->irq_chg, bci); power_supply_unregister(bci->usb); diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile index ca2f8bd0e431..6bbb3ec17018 100644 --- a/drivers/usb/common/Makefile +++ b/drivers/usb/common/Makefile @@ -7,3 +7,4 @@ usb-common-y += common.o usb-common-$(CONFIG_USB_LED_TRIG) += led.o obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o +obj-$(CONFIG_USB_ULPI_BUS) += ulpi.o diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c new file mode 100644 index 000000000000..0e6f968e93fe --- /dev/null +++ b/drivers/usb/common/ulpi.c @@ -0,0 +1,255 @@ +/** + * ulpi.c - USB ULPI PHY bus + * + * Copyright (C) 2015 Intel Corporation + * + * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/ulpi/interface.h> +#include <linux/ulpi/driver.h> +#include <linux/ulpi/regs.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/acpi.h> + +/* -------------------------------------------------------------------------- */ + +int ulpi_read(struct ulpi *ulpi, u8 addr) +{ + return ulpi->ops->read(ulpi->ops, addr); +} +EXPORT_SYMBOL_GPL(ulpi_read); + +int ulpi_write(struct ulpi *ulpi, u8 addr, u8 val) +{ + return ulpi->ops->write(ulpi->ops, addr, val); +} +EXPORT_SYMBOL_GPL(ulpi_write); + +/* -------------------------------------------------------------------------- */ + +static int ulpi_match(struct device *dev, struct device_driver *driver) +{ + struct ulpi_driver *drv = to_ulpi_driver(driver); + struct ulpi *ulpi = to_ulpi_dev(dev); + const struct ulpi_device_id *id; + + for (id = drv->id_table; id->vendor; id++) + if (id->vendor == ulpi->id.vendor && + id->product == ulpi->id.product) + return 1; + + return 0; +} + +static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct ulpi *ulpi = to_ulpi_dev(dev); + + if (add_uevent_var(env, "MODALIAS=ulpi:v%04xp%04x", + ulpi->id.vendor, ulpi->id.product)) + return -ENOMEM; + return 0; +} + +static int ulpi_probe(struct device *dev) +{ + struct ulpi_driver *drv = to_ulpi_driver(dev->driver); + + return drv->probe(to_ulpi_dev(dev)); +} + +static int ulpi_remove(struct device *dev) +{ + struct ulpi_driver *drv = to_ulpi_driver(dev->driver); + + if (drv->remove) + drv->remove(to_ulpi_dev(dev)); + + return 0; +} + +static struct bus_type ulpi_bus = { + .name = "ulpi", + .match = ulpi_match, + .uevent = ulpi_uevent, + .probe = ulpi_probe, + .remove = ulpi_remove, +}; + +/* -------------------------------------------------------------------------- */ + +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ulpi *ulpi = to_ulpi_dev(dev); + + return sprintf(buf, "ulpi:v%04xp%04x\n", + ulpi->id.vendor, ulpi->id.product); +} +static DEVICE_ATTR_RO(modalias); + +static struct attribute *ulpi_dev_attrs[] = { + &dev_attr_modalias.attr, + NULL +}; + +static struct attribute_group ulpi_dev_attr_group = { + .attrs = ulpi_dev_attrs, +}; + +static const struct attribute_group *ulpi_dev_attr_groups[] = { + &ulpi_dev_attr_group, + NULL +}; + +static void ulpi_dev_release(struct device *dev) +{ + kfree(to_ulpi_dev(dev)); +} + +static struct device_type ulpi_dev_type = { + .name = "ulpi_device", + .groups = ulpi_dev_attr_groups, + .release = ulpi_dev_release, +}; + +/* -------------------------------------------------------------------------- */ + +/** + * ulpi_register_driver - register a driver with the ULPI bus + * @drv: driver being registered + * + * Registers a driver with the ULPI bus. + */ +int ulpi_register_driver(struct ulpi_driver *drv) +{ + if (!drv->probe) + return -EINVAL; + + drv->driver.bus = &ulpi_bus; + + return driver_register(&drv->driver); +} +EXPORT_SYMBOL_GPL(ulpi_register_driver); + +/** + * ulpi_unregister_driver - unregister a driver with the ULPI bus + * @drv: driver to unregister + * + * Unregisters a driver with the ULPI bus. + */ +void ulpi_unregister_driver(struct ulpi_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(ulpi_unregister_driver); + +/* -------------------------------------------------------------------------- */ + +static int ulpi_register(struct device *dev, struct ulpi *ulpi) +{ + int ret; + + /* Test the interface */ + ret = ulpi_write(ulpi, ULPI_SCRATCH, 0xaa); + if (ret < 0) + return ret; + + ret = ulpi_read(ulpi, ULPI_SCRATCH); + if (ret < 0) + return ret; + + if (ret != 0xaa) + return -ENODEV; + + ulpi->id.vendor = ulpi_read(ulpi, ULPI_VENDOR_ID_LOW); + ulpi->id.vendor |= ulpi_read(ulpi, ULPI_VENDOR_ID_HIGH) << 8; + + ulpi->id.product = ulpi_read(ulpi, ULPI_PRODUCT_ID_LOW); + ulpi->id.product |= ulpi_read(ulpi, ULPI_PRODUCT_ID_HIGH) << 8; + + ulpi->dev.parent = dev; + ulpi->dev.bus = &ulpi_bus; + ulpi->dev.type = &ulpi_dev_type; + dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev)); + + ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev)); + + request_module("ulpi:v%04xp%04x", ulpi->id.vendor, ulpi->id.product); + + ret = device_register(&ulpi->dev); + if (ret) + return ret; + + dev_dbg(&ulpi->dev, "registered ULPI PHY: vendor %04x, product %04x\n", + ulpi->id.vendor, ulpi->id.product); + + return 0; +} + +/** + * ulpi_register_interface - instantiate new ULPI device + * @dev: USB controller's device interface + * @ops: ULPI register access + * + * Allocates and registers a ULPI device and an interface for it. Called from + * the USB controller that provides the ULPI interface. + */ +struct ulpi *ulpi_register_interface(struct device *dev, struct ulpi_ops *ops) +{ + struct ulpi *ulpi; + int ret; + + ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL); + if (!ulpi) + return ERR_PTR(-ENOMEM); + + ulpi->ops = ops; + ops->dev = dev; + + ret = ulpi_register(dev, ulpi); + if (ret) { + kfree(ulpi); + return ERR_PTR(ret); + } + + return ulpi; +} +EXPORT_SYMBOL_GPL(ulpi_register_interface); + +/** + * ulpi_unregister_interface - unregister ULPI interface + * @intrf: struct ulpi_interface + * + * Unregisters a ULPI device and it's interface that was created with + * ulpi_create_interface(). + */ +void ulpi_unregister_interface(struct ulpi *ulpi) +{ + device_unregister(&ulpi->dev); +} +EXPORT_SYMBOL_GPL(ulpi_unregister_interface); + +/* -------------------------------------------------------------------------- */ + +static int __init ulpi_init(void) +{ + return bus_register(&ulpi_bus); +} +module_init(ulpi_init); + +static void __exit ulpi_exit(void) +{ + bus_unregister(&ulpi_bus); +} +module_exit(ulpi_exit); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("USB ULPI PHY bus"); diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index cc0ced08bae2..a99c89e78126 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -84,3 +84,23 @@ config USB_OTG_FSM Implements OTG Finite State Machine as specified in On-The-Go and Embedded Host Supplement to the USB Revision 2.0 Specification. +config USB_ULPI_BUS + tristate "USB ULPI PHY interface support" + depends on USB_SUPPORT + help + UTMI+ Low Pin Interface (ULPI) is specification for a commonly used + USB 2.0 PHY interface. The ULPI specification defines a standard set + of registers that can be used to detect the vendor and product which + allows ULPI to be handled as a bus. This module is the driver for that + bus. + + The ULPI interfaces (the buses) are registered by the drivers for USB + controllers which support ULPI register access and have ULPI PHY + attached to them. The ULPI PHY drivers themselves are normal PHY + drivers. + + ULPI PHYs provide often functions such as ADP sensing/probing (OTG + protocol) and USB charger detection. + + To compile this driver as a module, choose M here: the module will + be called ulpi. diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig index 1bcb36ae6505..fd95ba6ec317 100644 --- a/drivers/usb/dwc2/Kconfig +++ b/drivers/usb/dwc2/Kconfig @@ -50,18 +50,10 @@ config USB_DWC2_DUAL_ROLE option requires USB_GADGET to be enabled. endchoice -config USB_DWC2_PLATFORM - tristate "DWC2 Platform" - default USB_DWC2_HOST || USB_DWC2_PERIPHERAL - help - The Designware USB2.0 platform interface module for - controllers directly connected to the CPU. - config USB_DWC2_PCI tristate "DWC2 PCI" depends on PCI default n - select USB_DWC2_PLATFORM select NOP_USB_XCEIV help The Designware USB2.0 PCI interface module for controllers diff --git a/drivers/usb/dwc2/Makefile b/drivers/usb/dwc2/Makefile index f07b425eaff3..50fdaace1e73 100644 --- a/drivers/usb/dwc2/Makefile +++ b/drivers/usb/dwc2/Makefile @@ -2,7 +2,7 @@ ccflags-$(CONFIG_USB_DWC2_DEBUG) += -DDEBUG ccflags-$(CONFIG_USB_DWC2_VERBOSE) += -DVERBOSE_DEBUG obj-$(CONFIG_USB_DWC2) += dwc2.o -dwc2-y := core.o core_intr.o +dwc2-y := core.o core_intr.o platform.o ifneq ($(filter y,$(CONFIG_USB_DWC2_HOST) $(CONFIG_USB_DWC2_DUAL_ROLE)),) dwc2-y += hcd.o hcd_intr.o @@ -13,6 +13,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC2_PERIPHERAL) $(CONFIG_USB_DWC2_DUAL_ROLE)),) dwc2-y += gadget.o endif +ifneq ($(CONFIG_DEBUG_FS),) + dwc2-y += debugfs.o +endif + # NOTE: The previous s3c-hsotg peripheral mode only driver has been moved to # this location and renamed gadget.c. When building for dynamically linked # modules, dwc2.ko will get built for host mode, peripheral mode, and dual-role @@ -21,6 +25,3 @@ endif obj-$(CONFIG_USB_DWC2_PCI) += dwc2_pci.o dwc2_pci-y := pci.o - -obj-$(CONFIG_USB_DWC2_PLATFORM) += dwc2_platform.o -dwc2_platform-y := platform.o diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index d5197d492e21..e5b546f1152e 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -56,6 +56,389 @@ #include "core.h" #include "hcd.h" +#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) +/** + * dwc2_backup_host_registers() - Backup controller host registers. + * When suspending usb bus, registers needs to be backuped + * if controller power is disabled once suspended. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_hregs_backup *hr; + int i; + + dev_dbg(hsotg->dev, "%s\n", __func__); + + /* Backup Host regs */ + hr = hsotg->hr_backup; + if (!hr) { + hr = devm_kzalloc(hsotg->dev, sizeof(*hr), GFP_KERNEL); + if (!hr) { + dev_err(hsotg->dev, "%s: can't allocate host regs\n", + __func__); + return -ENOMEM; + } + + hsotg->hr_backup = hr; + } + hr->hcfg = readl(hsotg->regs + HCFG); + hr->haintmsk = readl(hsotg->regs + HAINTMSK); + for (i = 0; i < hsotg->core_params->host_channels; ++i) + hr->hcintmsk[i] = readl(hsotg->regs + HCINTMSK(i)); + + hr->hprt0 = readl(hsotg->regs + HPRT0); + hr->hfir = readl(hsotg->regs + HFIR); + + return 0; +} + +/** + * dwc2_restore_host_registers() - Restore controller host registers. + * When resuming usb bus, device registers needs to be restored + * if controller power were disabled. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_hregs_backup *hr; + int i; + + dev_dbg(hsotg->dev, "%s\n", __func__); + + /* Restore host regs */ + hr = hsotg->hr_backup; + if (!hr) { + dev_err(hsotg->dev, "%s: no host registers to restore\n", + __func__); + return -EINVAL; + } + + writel(hr->hcfg, hsotg->regs + HCFG); + writel(hr->haintmsk, hsotg->regs + HAINTMSK); + + for (i = 0; i < hsotg->core_params->host_channels; ++i) + writel(hr->hcintmsk[i], hsotg->regs + HCINTMSK(i)); + + writel(hr->hprt0, hsotg->regs + HPRT0); + writel(hr->hfir, hsotg->regs + HFIR); + + return 0; +} +#else +static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg) +{ return 0; } + +static inline int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg) +{ return 0; } +#endif + +#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \ + IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) +/** + * dwc2_backup_device_registers() - Backup controller device registers. + * When suspending usb bus, registers needs to be backuped + * if controller power is disabled once suspended. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_dregs_backup *dr; + int i; + + dev_dbg(hsotg->dev, "%s\n", __func__); + + /* Backup dev regs */ + dr = hsotg->dr_backup; + if (!dr) { + dr = devm_kzalloc(hsotg->dev, sizeof(*dr), GFP_KERNEL); + if (!dr) { + dev_err(hsotg->dev, "%s: can't allocate device regs\n", + __func__); + return -ENOMEM; + } + + hsotg->dr_backup = dr; + } + + dr->dcfg = readl(hsotg->regs + DCFG); + dr->dctl = readl(hsotg->regs + DCTL); + dr->daintmsk = readl(hsotg->regs + DAINTMSK); + dr->diepmsk = readl(hsotg->regs + DIEPMSK); + dr->doepmsk = readl(hsotg->regs + DOEPMSK); + + for (i = 0; i < hsotg->num_of_eps; i++) { + /* Backup IN EPs */ + dr->diepctl[i] = readl(hsotg->regs + DIEPCTL(i)); + + /* Ensure DATA PID is correctly configured */ + if (dr->diepctl[i] & DXEPCTL_DPID) + dr->diepctl[i] |= DXEPCTL_SETD1PID; + else + dr->diepctl[i] |= DXEPCTL_SETD0PID; + + dr->dieptsiz[i] = readl(hsotg->regs + DIEPTSIZ(i)); + dr->diepdma[i] = readl(hsotg->regs + DIEPDMA(i)); + + /* Backup OUT EPs */ + dr->doepctl[i] = readl(hsotg->regs + DOEPCTL(i)); + + /* Ensure DATA PID is correctly configured */ + if (dr->doepctl[i] & DXEPCTL_DPID) + dr->doepctl[i] |= DXEPCTL_SETD1PID; + else + dr->doepctl[i] |= DXEPCTL_SETD0PID; + + dr->doeptsiz[i] = readl(hsotg->regs + DOEPTSIZ(i)); + dr->doepdma[i] = readl(hsotg->regs + DOEPDMA(i)); + } + + return 0; +} + +/** + * dwc2_restore_device_registers() - Restore controller device registers. + * When resuming usb bus, device registers needs to be restored + * if controller power were disabled. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_dregs_backup *dr; + u32 dctl; + int i; + + dev_dbg(hsotg->dev, "%s\n", __func__); + + /* Restore dev regs */ + dr = hsotg->dr_backup; + if (!dr) { + dev_err(hsotg->dev, "%s: no device registers to restore\n", + __func__); + return -EINVAL; + } + + writel(dr->dcfg, hsotg->regs + DCFG); + writel(dr->dctl, hsotg->regs + DCTL); + writel(dr->daintmsk, hsotg->regs + DAINTMSK); + writel(dr->diepmsk, hsotg->regs + DIEPMSK); + writel(dr->doepmsk, hsotg->regs + DOEPMSK); + + for (i = 0; i < hsotg->num_of_eps; i++) { + /* Restore IN EPs */ + writel(dr->diepctl[i], hsotg->regs + DIEPCTL(i)); + writel(dr->dieptsiz[i], hsotg->regs + DIEPTSIZ(i)); + writel(dr->diepdma[i], hsotg->regs + DIEPDMA(i)); + + /* Restore OUT EPs */ + writel(dr->doepctl[i], hsotg->regs + DOEPCTL(i)); + writel(dr->doeptsiz[i], hsotg->regs + DOEPTSIZ(i)); + writel(dr->doepdma[i], hsotg->regs + DOEPDMA(i)); + } + + /* Set the Power-On Programming done bit */ + dctl = readl(hsotg->regs + DCTL); + dctl |= DCTL_PWRONPRGDONE; + writel(dctl, hsotg->regs + DCTL); + + return 0; +} +#else +static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) +{ return 0; } + +static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg) +{ return 0; } +#endif + +/** + * dwc2_backup_global_registers() - Backup global controller registers. + * When suspending usb bus, registers needs to be backuped + * if controller power is disabled once suspended. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_gregs_backup *gr; + int i; + + /* Backup global regs */ + gr = hsotg->gr_backup; + if (!gr) { + gr = devm_kzalloc(hsotg->dev, sizeof(*gr), GFP_KERNEL); + if (!gr) { + dev_err(hsotg->dev, "%s: can't allocate global regs\n", + __func__); + return -ENOMEM; + } + + hsotg->gr_backup = gr; + } + + gr->gotgctl = readl(hsotg->regs + GOTGCTL); + gr->gintmsk = readl(hsotg->regs + GINTMSK); + gr->gahbcfg = readl(hsotg->regs + GAHBCFG); + gr->gusbcfg = readl(hsotg->regs + GUSBCFG); + gr->grxfsiz = readl(hsotg->regs + GRXFSIZ); + gr->gnptxfsiz = readl(hsotg->regs + GNPTXFSIZ); + gr->hptxfsiz = readl(hsotg->regs + HPTXFSIZ); + gr->gdfifocfg = readl(hsotg->regs + GDFIFOCFG); + for (i = 0; i < MAX_EPS_CHANNELS; i++) + gr->dtxfsiz[i] = readl(hsotg->regs + DPTXFSIZN(i)); + + return 0; +} + +/** + * dwc2_restore_global_registers() - Restore controller global registers. + * When resuming usb bus, device registers needs to be restored + * if controller power were disabled. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_gregs_backup *gr; + int i; + + dev_dbg(hsotg->dev, "%s\n", __func__); + + /* Restore global regs */ + gr = hsotg->gr_backup; + if (!gr) { + dev_err(hsotg->dev, "%s: no global registers to restore\n", + __func__); + return -EINVAL; + } + + writel(0xffffffff, hsotg->regs + GINTSTS); + writel(gr->gotgctl, hsotg->regs + GOTGCTL); + writel(gr->gintmsk, hsotg->regs + GINTMSK); + writel(gr->gusbcfg, hsotg->regs + GUSBCFG); + writel(gr->gahbcfg, hsotg->regs + GAHBCFG); + writel(gr->grxfsiz, hsotg->regs + GRXFSIZ); + writel(gr->gnptxfsiz, hsotg->regs + GNPTXFSIZ); + writel(gr->hptxfsiz, hsotg->regs + HPTXFSIZ); + writel(gr->gdfifocfg, hsotg->regs + GDFIFOCFG); + for (i = 0; i < MAX_EPS_CHANNELS; i++) + writel(gr->dtxfsiz[i], hsotg->regs + DPTXFSIZN(i)); + + return 0; +} + +/** + * dwc2_exit_hibernation() - Exit controller from Partial Power Down. + * + * @hsotg: Programming view of the DWC_otg controller + * @restore: Controller registers need to be restored + */ +int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore) +{ + u32 pcgcctl; + int ret = 0; + + if (!hsotg->core_params->hibernation) + return -ENOTSUPP; + + pcgcctl = readl(hsotg->regs + PCGCTL); + pcgcctl &= ~PCGCTL_STOPPCLK; + writel(pcgcctl, hsotg->regs + PCGCTL); + + pcgcctl = readl(hsotg->regs + PCGCTL); + pcgcctl &= ~PCGCTL_PWRCLMP; + writel(pcgcctl, hsotg->regs + PCGCTL); + + pcgcctl = readl(hsotg->regs + PCGCTL); + pcgcctl &= ~PCGCTL_RSTPDWNMODULE; + writel(pcgcctl, hsotg->regs + PCGCTL); + + udelay(100); + if (restore) { + ret = dwc2_restore_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore registers\n", + __func__); + return ret; + } + if (dwc2_is_host_mode(hsotg)) { + ret = dwc2_restore_host_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore host registers\n", + __func__); + return ret; + } + } else { + ret = dwc2_restore_device_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore device registers\n", + __func__); + return ret; + } + } + } + + return ret; +} + +/** + * dwc2_enter_hibernation() - Put controller in Partial Power Down. + * + * @hsotg: Programming view of the DWC_otg controller + */ +int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg) +{ + u32 pcgcctl; + int ret = 0; + + if (!hsotg->core_params->hibernation) + return -ENOTSUPP; + + /* Backup all registers */ + ret = dwc2_backup_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup global registers\n", + __func__); + return ret; + } + + if (dwc2_is_host_mode(hsotg)) { + ret = dwc2_backup_host_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup host registers\n", + __func__); + return ret; + } + } else { + ret = dwc2_backup_device_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup device registers\n", + __func__); + return ret; + } + } + + /* Put the controller in low power state */ + pcgcctl = readl(hsotg->regs + PCGCTL); + + pcgcctl |= PCGCTL_PWRCLMP; + writel(pcgcctl, hsotg->regs + PCGCTL); + ndelay(20); + + pcgcctl |= PCGCTL_RSTPDWNMODULE; + writel(pcgcctl, hsotg->regs + PCGCTL); + ndelay(20); + + pcgcctl |= PCGCTL_STOPPCLK; + writel(pcgcctl, hsotg->regs + PCGCTL); + + return ret; +} + /** * dwc2_enable_common_interrupts() - Initializes the commmon interrupts, * used in both device and host modes @@ -77,8 +460,10 @@ static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg) if (hsotg->core_params->dma_enable <= 0) intmsk |= GINTSTS_RXFLVL; + if (hsotg->core_params->external_id_pin_ctl <= 0) + intmsk |= GINTSTS_CONIDSTSCHNG; - intmsk |= GINTSTS_CONIDSTSCHNG | GINTSTS_WKUPINT | GINTSTS_USBSUSP | + intmsk |= GINTSTS_WKUPINT | GINTSTS_USBSUSP | GINTSTS_SESSREQINT; writel(intmsk, hsotg->regs + GINTMSK); @@ -2602,6 +2987,40 @@ static void dwc2_set_param_uframe_sched(struct dwc2_hsotg *hsotg, int val) hsotg->core_params->uframe_sched = val; } +static void dwc2_set_param_external_id_pin_ctl(struct dwc2_hsotg *hsotg, + int val) +{ + if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) { + if (val >= 0) { + dev_err(hsotg->dev, + "'%d' invalid for parameter external_id_pin_ctl\n", + val); + dev_err(hsotg->dev, "external_id_pin_ctl must be 0 or 1\n"); + } + val = 0; + dev_dbg(hsotg->dev, "Setting external_id_pin_ctl to %d\n", val); + } + + hsotg->core_params->external_id_pin_ctl = val; +} + +static void dwc2_set_param_hibernation(struct dwc2_hsotg *hsotg, + int val) +{ + if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) { + if (val >= 0) { + dev_err(hsotg->dev, + "'%d' invalid for parameter hibernation\n", + val); + dev_err(hsotg->dev, "hibernation must be 0 or 1\n"); + } + val = 0; + dev_dbg(hsotg->dev, "Setting hibernation to %d\n", val); + } + + hsotg->core_params->hibernation = val; +} + /* * This function is called during module intialization to pass module parameters * for the DWC_otg core. @@ -2646,6 +3065,8 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg, dwc2_set_param_ahbcfg(hsotg, params->ahbcfg); dwc2_set_param_otg_ver(hsotg, params->otg_ver); dwc2_set_param_uframe_sched(hsotg, params->uframe_sched); + dwc2_set_param_external_id_pin_ctl(hsotg, params->external_id_pin_ctl); + dwc2_set_param_hibernation(hsotg, params->hibernation); } /** @@ -2814,6 +3235,22 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) return 0; } +/* + * Sets all parameters to the given value. + * + * Assumes that the dwc2_core_params struct contains only integers. + */ +void dwc2_set_all_params(struct dwc2_core_params *params, int value) +{ + int *p = (int *)params; + size_t size = sizeof(*params) / sizeof(*p); + int i; + + for (i = 0; i < size; i++) + p[i] = value; +} + + u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg) { return hsotg->core_params->otg_ver == 1 ? 0x0200 : 0x0103; diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 836c012c7707..53b8de03f102 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -331,6 +331,17 @@ enum dwc2_ep0_state { * by the driver and are ignored in this * configuration value. * @uframe_sched: True to enable the microframe scheduler + * @external_id_pin_ctl: Specifies whether ID pin is handled externally. + * Disable CONIDSTSCHNG controller interrupt in such + * case. + * 0 - No (default) + * 1 - Yes + * @hibernation: Specifies whether the controller support hibernation. + * If hibernation is enabled, the controller will enter + * hibernation in both peripheral and host mode when + * needed. + * 0 - No (default) + * 1 - Yes * * The following parameters may be specified when starting the module. These * parameters define how the DWC_otg controller should be configured. A @@ -368,6 +379,8 @@ struct dwc2_core_params { int reload_ctl; int ahbcfg; int uframe_sched; + int external_id_pin_ctl; + int hibernation; }; /** @@ -452,6 +465,82 @@ struct dwc2_hw_params { #define DWC2_CTRL_BUFF_SIZE 8 /** + * struct dwc2_gregs_backup - Holds global registers state before entering partial + * power down + * @gotgctl: Backup of GOTGCTL register + * @gintmsk: Backup of GINTMSK register + * @gahbcfg: Backup of GAHBCFG register + * @gusbcfg: Backup of GUSBCFG register + * @grxfsiz: Backup of GRXFSIZ register + * @gnptxfsiz: Backup of GNPTXFSIZ register + * @gi2cctl: Backup of GI2CCTL register + * @hptxfsiz: Backup of HPTXFSIZ register + * @gdfifocfg: Backup of GDFIFOCFG register + * @dtxfsiz: Backup of DTXFSIZ registers for each endpoint + * @gpwrdn: Backup of GPWRDN register + */ +struct dwc2_gregs_backup { + u32 gotgctl; + u32 gintmsk; + u32 gahbcfg; + u32 gusbcfg; + u32 grxfsiz; + u32 gnptxfsiz; + u32 gi2cctl; + u32 hptxfsiz; + u32 pcgcctl; + u32 gdfifocfg; + u32 dtxfsiz[MAX_EPS_CHANNELS]; + u32 gpwrdn; +}; + +/** + * struct dwc2_dregs_backup - Holds device registers state before entering partial + * power down + * @dcfg: Backup of DCFG register + * @dctl: Backup of DCTL register + * @daintmsk: Backup of DAINTMSK register + * @diepmsk: Backup of DIEPMSK register + * @doepmsk: Backup of DOEPMSK register + * @diepctl: Backup of DIEPCTL register + * @dieptsiz: Backup of DIEPTSIZ register + * @diepdma: Backup of DIEPDMA register + * @doepctl: Backup of DOEPCTL register + * @doeptsiz: Backup of DOEPTSIZ register + * @doepdma: Backup of DOEPDMA register + */ +struct dwc2_dregs_backup { + u32 dcfg; + u32 dctl; + u32 daintmsk; + u32 diepmsk; + u32 doepmsk; + u32 diepctl[MAX_EPS_CHANNELS]; + u32 dieptsiz[MAX_EPS_CHANNELS]; + u32 diepdma[MAX_EPS_CHANNELS]; + u32 doepctl[MAX_EPS_CHANNELS]; + u32 doeptsiz[MAX_EPS_CHANNELS]; + u32 doepdma[MAX_EPS_CHANNELS]; +}; + +/** + * struct dwc2_hregs_backup - Holds host registers state before entering partial + * power down + * @hcfg: Backup of HCFG register + * @haintmsk: Backup of HAINTMSK register + * @hcintmsk: Backup of HCINTMSK register + * @hptr0: Backup of HPTR0 register + * @hfir: Backup of HFIR register + */ +struct dwc2_hregs_backup { + u32 hcfg; + u32 haintmsk; + u32 hcintmsk[MAX_EPS_CHANNELS]; + u32 hprt0; + u32 hfir; +}; + +/** * struct dwc2_hsotg - Holds the state of the driver, including the non-periodic * and periodic schedules * @@ -481,6 +570,9 @@ struct dwc2_hw_params { * interrupt * @wkp_timer: Timer object for handling Wakeup Detected interrupt * @lx_state: Lx state of connected device + * @gregs_backup: Backup of global registers during suspend + * @dregs_backup: Backup of device registers during suspend + * @hregs_backup: Backup of host registers during suspend * * These are for host mode: * @@ -613,11 +705,12 @@ struct dwc2_hsotg { struct work_struct wf_otg; struct timer_list wkp_timer; enum dwc2_lx_state lx_state; + struct dwc2_gregs_backup *gr_backup; + struct dwc2_dregs_backup *dr_backup; + struct dwc2_hregs_backup *hr_backup; struct dentry *debug_root; - struct dentry *debug_file; - struct dentry *debug_testmode; - struct dentry *debug_fifo; + struct debugfs_regset32 *regset; /* DWC OTG HW Release versions */ #define DWC2_CORE_REV_2_71a 0x4f54271a @@ -751,6 +844,8 @@ enum dwc2_halt_status { * and the DWC_otg controller */ extern void dwc2_core_host_init(struct dwc2_hsotg *hsotg); +extern int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg); +extern int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore); /* * Host core Functions. @@ -983,6 +1078,15 @@ extern void dwc2_set_param_ahbcfg(struct dwc2_hsotg *hsotg, int val); extern void dwc2_set_param_otg_ver(struct dwc2_hsotg *hsotg, int val); +extern void dwc2_set_parameters(struct dwc2_hsotg *hsotg, + const struct dwc2_core_params *params); + +extern void dwc2_set_all_params(struct dwc2_core_params *params, int value); + +extern int dwc2_get_hwparams(struct dwc2_hsotg *hsotg); + + + /* * Dump core registers and SPRAM */ @@ -1005,6 +1109,8 @@ extern void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2, bool reset); extern void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg); extern void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2); +extern int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode); +#define dwc2_is_device_connected(hsotg) (hsotg->connected) #else static inline int s3c_hsotg_remove(struct dwc2_hsotg *dwc2) { return 0; } @@ -1018,6 +1124,10 @@ static inline void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2, bool reset) {} static inline void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg) {} static inline void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2) {} +static inline int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, + int testmode) +{ return 0; } +#define dwc2_is_device_connected(hsotg) (0) #endif #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) @@ -1025,14 +1135,12 @@ extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg); extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg); extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg); #else -static inline void dwc2_set_all_params(struct dwc2_core_params *params, int value) {} static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg) { return 0; } static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg) {} static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {} static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {} -static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, - const struct dwc2_core_params *params) +static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq) { return 0; } #endif diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 6cf047878dba..927be1e8b3dc 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -334,6 +334,7 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) */ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) { + int ret; dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n"); dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state); @@ -345,6 +346,11 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) /* Clear Remote Wakeup Signaling */ dctl &= ~DCTL_RMTWKUPSIG; writel(dctl, hsotg->regs + DCTL); + ret = dwc2_exit_hibernation(hsotg, true); + if (ret && (ret != -ENOTSUPP)) + dev_err(hsotg->dev, "exit hibernation failed\n"); + + call_gadget(hsotg, resume); } /* Change to L0 state */ hsotg->lx_state = DWC2_L0; @@ -397,6 +403,7 @@ static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg) static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) { u32 dsts; + int ret; dev_dbg(hsotg->dev, "USB SUSPEND\n"); @@ -411,10 +418,43 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n", !!(dsts & DSTS_SUSPSTS), hsotg->hw_params.power_optimized); + if ((dsts & DSTS_SUSPSTS) && hsotg->hw_params.power_optimized) { + /* Ignore suspend request before enumeration */ + if (!dwc2_is_device_connected(hsotg)) { + dev_dbg(hsotg->dev, + "ignore suspend request before enumeration\n"); + goto clear_int; + } + + ret = dwc2_enter_hibernation(hsotg); + if (ret) { + if (ret != -ENOTSUPP) + dev_err(hsotg->dev, + "enter hibernation failed\n"); + goto skip_power_saving; + } + + udelay(100); + + /* Ask phy to be suspended */ + if (!IS_ERR_OR_NULL(hsotg->uphy)) + usb_phy_set_suspend(hsotg->uphy, true); +skip_power_saving: + /* + * Change to L2 (suspend) state before releasing + * spinlock + */ + hsotg->lx_state = DWC2_L2; + + /* Call gadget suspend callback */ + call_gadget(hsotg, suspend); + } } else { if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) { dev_dbg(hsotg->dev, "a_peripheral->a_host\n"); + /* Change to L2 (suspend) state */ + hsotg->lx_state = DWC2_L2; /* Clear the a_peripheral flag, back to a_host */ spin_unlock(&hsotg->lock); dwc2_hcd_start(hsotg); @@ -423,9 +463,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) } } - /* Change to L2 (suspend) state */ - hsotg->lx_state = DWC2_L2; - +clear_int: /* Clear interrupt */ writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS); } @@ -522,4 +560,3 @@ out: spin_unlock(&hsotg->lock); return retval; } -EXPORT_SYMBOL_GPL(dwc2_handle_common_intr); diff --git a/drivers/usb/dwc2/debug.h b/drivers/usb/dwc2/debug.h new file mode 100644 index 000000000000..12dbd1daec87 --- /dev/null +++ b/drivers/usb/dwc2/debug.h @@ -0,0 +1,27 @@ +/** + * debug.h - Designware USB2 DRD controller debug header + * + * Copyright (C) 2015 Intel Corporation + * Mian Yousaf Kaukab <yousaf.kaukab@intel.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "core.h" + +#ifdef CONFIG_DEBUG_FS +extern int dwc2_debugfs_init(struct dwc2_hsotg *); +extern void dwc2_debugfs_exit(struct dwc2_hsotg *); +#else +static inline int dwc2_debugfs_init(struct dwc2_hsotg *hsotg) +{ return 0; } +static inline void dwc2_debugfs_exit(struct dwc2_hsotg *hsotg) +{ } +#endif diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c new file mode 100644 index 000000000000..ef2ee3d9a25d --- /dev/null +++ b/drivers/usb/dwc2/debugfs.c @@ -0,0 +1,771 @@ +/** + * debugfs.c - Designware USB2 DRD controller debugfs + * + * Copyright (C) 2015 Intel Corporation + * Mian Yousaf Kaukab <yousaf.kaukab@intel.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/spinlock.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/uaccess.h> + +#include "core.h" +#include "debug.h" + +#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \ + IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) +/** + * testmode_write - debugfs: change usb test mode + * @seq: The seq file to write to. + * @v: Unused parameter. + * + * This debugfs entry modify the current usb test mode. + */ +static ssize_t testmode_write(struct file *file, const char __user *ubuf, size_t + count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct dwc2_hsotg *hsotg = s->private; + unsigned long flags; + u32 testmode = 0; + char buf[32]; + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + if (!strncmp(buf, "test_j", 6)) + testmode = TEST_J; + else if (!strncmp(buf, "test_k", 6)) + testmode = TEST_K; + else if (!strncmp(buf, "test_se0_nak", 12)) + testmode = TEST_SE0_NAK; + else if (!strncmp(buf, "test_packet", 11)) + testmode = TEST_PACKET; + else if (!strncmp(buf, "test_force_enable", 17)) + testmode = TEST_FORCE_EN; + else + testmode = 0; + + spin_lock_irqsave(&hsotg->lock, flags); + s3c_hsotg_set_test_mode(hsotg, testmode); + spin_unlock_irqrestore(&hsotg->lock, flags); + return count; +} + +/** + * testmode_show - debugfs: show usb test mode state + * @seq: The seq file to write to. + * @v: Unused parameter. + * + * This debugfs entry shows which usb test mode is currently enabled. + */ +static int testmode_show(struct seq_file *s, void *unused) +{ + struct dwc2_hsotg *hsotg = s->private; + unsigned long flags; + int dctl; + + spin_lock_irqsave(&hsotg->lock, flags); + dctl = readl(hsotg->regs + DCTL); + dctl &= DCTL_TSTCTL_MASK; + dctl >>= DCTL_TSTCTL_SHIFT; + spin_unlock_irqrestore(&hsotg->lock, flags); + + switch (dctl) { + case 0: + seq_puts(s, "no test\n"); + break; + case TEST_J: + seq_puts(s, "test_j\n"); + break; + case TEST_K: + seq_puts(s, "test_k\n"); + break; + case TEST_SE0_NAK: + seq_puts(s, "test_se0_nak\n"); + break; + case TEST_PACKET: + seq_puts(s, "test_packet\n"); + break; + case TEST_FORCE_EN: + seq_puts(s, "test_force_enable\n"); + break; + default: + seq_printf(s, "UNKNOWN %d\n", dctl); + } + + return 0; +} + +static int testmode_open(struct inode *inode, struct file *file) +{ + return single_open(file, testmode_show, inode->i_private); +} + +static const struct file_operations testmode_fops = { + .owner = THIS_MODULE, + .open = testmode_open, + .write = testmode_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/** + * state_show - debugfs: show overall driver and device state. + * @seq: The seq file to write to. + * @v: Unused parameter. + * + * This debugfs entry shows the overall state of the hardware and + * some general information about each of the endpoints available + * to the system. + */ +static int state_show(struct seq_file *seq, void *v) +{ + struct dwc2_hsotg *hsotg = seq->private; + void __iomem *regs = hsotg->regs; + int idx; + + seq_printf(seq, "DCFG=0x%08x, DCTL=0x%08x, DSTS=0x%08x\n", + readl(regs + DCFG), + readl(regs + DCTL), + readl(regs + DSTS)); + + seq_printf(seq, "DIEPMSK=0x%08x, DOEPMASK=0x%08x\n", + readl(regs + DIEPMSK), readl(regs + DOEPMSK)); + + seq_printf(seq, "GINTMSK=0x%08x, GINTSTS=0x%08x\n", + readl(regs + GINTMSK), + readl(regs + GINTSTS)); + + seq_printf(seq, "DAINTMSK=0x%08x, DAINT=0x%08x\n", + readl(regs + DAINTMSK), + readl(regs + DAINT)); + + seq_printf(seq, "GNPTXSTS=0x%08x, GRXSTSR=%08x\n", + readl(regs + GNPTXSTS), + readl(regs + GRXSTSR)); + + seq_puts(seq, "\nEndpoint status:\n"); + + for (idx = 0; idx < hsotg->num_of_eps; idx++) { + u32 in, out; + + in = readl(regs + DIEPCTL(idx)); + out = readl(regs + DOEPCTL(idx)); + + seq_printf(seq, "ep%d: DIEPCTL=0x%08x, DOEPCTL=0x%08x", + idx, in, out); + + in = readl(regs + DIEPTSIZ(idx)); + out = readl(regs + DOEPTSIZ(idx)); + + seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x", + in, out); + + seq_puts(seq, "\n"); + } + + return 0; +} + +static int state_open(struct inode *inode, struct file *file) +{ + return single_open(file, state_show, inode->i_private); +} + +static const struct file_operations state_fops = { + .owner = THIS_MODULE, + .open = state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/** + * fifo_show - debugfs: show the fifo information + * @seq: The seq_file to write data to. + * @v: Unused parameter. + * + * Show the FIFO information for the overall fifo and all the + * periodic transmission FIFOs. + */ +static int fifo_show(struct seq_file *seq, void *v) +{ + struct dwc2_hsotg *hsotg = seq->private; + void __iomem *regs = hsotg->regs; + u32 val; + int idx; + + seq_puts(seq, "Non-periodic FIFOs:\n"); + seq_printf(seq, "RXFIFO: Size %d\n", readl(regs + GRXFSIZ)); + + val = readl(regs + GNPTXFSIZ); + seq_printf(seq, "NPTXFIFO: Size %d, Start 0x%08x\n", + val >> FIFOSIZE_DEPTH_SHIFT, + val & FIFOSIZE_DEPTH_MASK); + + seq_puts(seq, "\nPeriodic TXFIFOs:\n"); + + for (idx = 1; idx < hsotg->num_of_eps; idx++) { + val = readl(regs + DPTXFSIZN(idx)); + + seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx, + val >> FIFOSIZE_DEPTH_SHIFT, + val & FIFOSIZE_STARTADDR_MASK); + } + + return 0; +} + +static int fifo_open(struct inode *inode, struct file *file) +{ + return single_open(file, fifo_show, inode->i_private); +} + +static const struct file_operations fifo_fops = { + .owner = THIS_MODULE, + .open = fifo_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const char *decode_direction(int is_in) +{ + return is_in ? "in" : "out"; +} + +/** + * ep_show - debugfs: show the state of an endpoint. + * @seq: The seq_file to write data to. + * @v: Unused parameter. + * + * This debugfs entry shows the state of the given endpoint (one is + * registered for each available). + */ +static int ep_show(struct seq_file *seq, void *v) +{ + struct s3c_hsotg_ep *ep = seq->private; + struct dwc2_hsotg *hsotg = ep->parent; + struct s3c_hsotg_req *req; + void __iomem *regs = hsotg->regs; + int index = ep->index; + int show_limit = 15; + unsigned long flags; + + seq_printf(seq, "Endpoint index %d, named %s, dir %s:\n", + ep->index, ep->ep.name, decode_direction(ep->dir_in)); + + /* first show the register state */ + + seq_printf(seq, "\tDIEPCTL=0x%08x, DOEPCTL=0x%08x\n", + readl(regs + DIEPCTL(index)), + readl(regs + DOEPCTL(index))); + + seq_printf(seq, "\tDIEPDMA=0x%08x, DOEPDMA=0x%08x\n", + readl(regs + DIEPDMA(index)), + readl(regs + DOEPDMA(index))); + + seq_printf(seq, "\tDIEPINT=0x%08x, DOEPINT=0x%08x\n", + readl(regs + DIEPINT(index)), + readl(regs + DOEPINT(index))); + + seq_printf(seq, "\tDIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x\n", + readl(regs + DIEPTSIZ(index)), + readl(regs + DOEPTSIZ(index))); + + seq_puts(seq, "\n"); + seq_printf(seq, "mps %d\n", ep->ep.maxpacket); + seq_printf(seq, "total_data=%ld\n", ep->total_data); + + seq_printf(seq, "request list (%p,%p):\n", + ep->queue.next, ep->queue.prev); + + spin_lock_irqsave(&hsotg->lock, flags); + + list_for_each_entry(req, &ep->queue, queue) { + if (--show_limit < 0) { + seq_puts(seq, "not showing more requests...\n"); + break; + } + + seq_printf(seq, "%c req %p: %d bytes @%p, ", + req == ep->req ? '*' : ' ', + req, req->req.length, req->req.buf); + seq_printf(seq, "%d done, res %d\n", + req->req.actual, req->req.status); + } + + spin_unlock_irqrestore(&hsotg->lock, flags); + + return 0; +} + +static int ep_open(struct inode *inode, struct file *file) +{ + return single_open(file, ep_show, inode->i_private); +} + +static const struct file_operations ep_fops = { + .owner = THIS_MODULE, + .open = ep_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/** + * s3c_hsotg_create_debug - create debugfs directory and files + * @hsotg: The driver state + * + * Create the debugfs files to allow the user to get information + * about the state of the system. The directory name is created + * with the same name as the device itself, in case we end up + * with multiple blocks in future systems. + */ +static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) +{ + struct dentry *root; + struct dentry *file; + unsigned epidx; + + root = hsotg->debug_root; + + /* create general state file */ + + file = debugfs_create_file("state", S_IRUGO, root, hsotg, &state_fops); + if (IS_ERR(file)) + dev_err(hsotg->dev, "%s: failed to create state\n", __func__); + + file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root, hsotg, + &testmode_fops); + if (IS_ERR(file)) + dev_err(hsotg->dev, "%s: failed to create testmode\n", + __func__); + + file = debugfs_create_file("fifo", S_IRUGO, root, hsotg, &fifo_fops); + if (IS_ERR(file)) + dev_err(hsotg->dev, "%s: failed to create fifo\n", __func__); + + /* Create one file for each out endpoint */ + for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) { + struct s3c_hsotg_ep *ep; + + ep = hsotg->eps_out[epidx]; + if (ep) { + file = debugfs_create_file(ep->name, S_IRUGO, + root, ep, &ep_fops); + if (IS_ERR(file)) + dev_err(hsotg->dev, "failed to create %s debug file\n", + ep->name); + } + } + /* Create one file for each in endpoint. EP0 is handled with out eps */ + for (epidx = 1; epidx < hsotg->num_of_eps; epidx++) { + struct s3c_hsotg_ep *ep; + + ep = hsotg->eps_in[epidx]; + if (ep) { + file = debugfs_create_file(ep->name, S_IRUGO, + root, ep, &ep_fops); + if (IS_ERR(file)) + dev_err(hsotg->dev, "failed to create %s debug file\n", + ep->name); + } + } +} +#else +static inline void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) {} +#endif + +/* s3c_hsotg_delete_debug is removed as cleanup in done in dwc2_debugfs_exit */ + +#define dump_register(nm) \ +{ \ + .name = #nm, \ + .offset = nm, \ +} + +static const struct debugfs_reg32 dwc2_regs[] = { + /* + * Accessing registers like this can trigger mode mismatch interrupt. + * However, according to dwc2 databook, the register access, in this + * case, is completed on the processor bus but is ignored by the core + * and does not affect its operation. + */ + dump_register(GOTGCTL), + dump_register(GOTGINT), + dump_register(GAHBCFG), + dump_register(GUSBCFG), + dump_register(GRSTCTL), + dump_register(GINTSTS), + dump_register(GINTMSK), + dump_register(GRXSTSR), + dump_register(GRXSTSP), + dump_register(GRXFSIZ), + dump_register(GNPTXFSIZ), + dump_register(GNPTXSTS), + dump_register(GI2CCTL), + dump_register(GPVNDCTL), + dump_register(GGPIO), + dump_register(GUID), + dump_register(GSNPSID), + dump_register(GHWCFG1), + dump_register(GHWCFG2), + dump_register(GHWCFG3), + dump_register(GHWCFG4), + dump_register(GLPMCFG), + dump_register(GPWRDN), + dump_register(GDFIFOCFG), + dump_register(ADPCTL), + dump_register(HPTXFSIZ), + dump_register(DPTXFSIZN(1)), + dump_register(DPTXFSIZN(2)), + dump_register(DPTXFSIZN(3)), + dump_register(DPTXFSIZN(4)), + dump_register(DPTXFSIZN(5)), + dump_register(DPTXFSIZN(6)), + dump_register(DPTXFSIZN(7)), + dump_register(DPTXFSIZN(8)), + dump_register(DPTXFSIZN(9)), + dump_register(DPTXFSIZN(10)), + dump_register(DPTXFSIZN(11)), + dump_register(DPTXFSIZN(12)), + dump_register(DPTXFSIZN(13)), + dump_register(DPTXFSIZN(14)), + dump_register(DPTXFSIZN(15)), + dump_register(DCFG), + dump_register(DCTL), + dump_register(DSTS), + dump_register(DIEPMSK), + dump_register(DOEPMSK), + dump_register(DAINT), + dump_register(DAINTMSK), + dump_register(DTKNQR1), + dump_register(DTKNQR2), + dump_register(DTKNQR3), + dump_register(DTKNQR4), + dump_register(DVBUSDIS), + dump_register(DVBUSPULSE), + dump_register(DIEPCTL(0)), + dump_register(DIEPCTL(1)), + dump_register(DIEPCTL(2)), + dump_register(DIEPCTL(3)), + dump_register(DIEPCTL(4)), + dump_register(DIEPCTL(5)), + dump_register(DIEPCTL(6)), + dump_register(DIEPCTL(7)), + dump_register(DIEPCTL(8)), + dump_register(DIEPCTL(9)), + dump_register(DIEPCTL(10)), + dump_register(DIEPCTL(11)), + dump_register(DIEPCTL(12)), + dump_register(DIEPCTL(13)), + dump_register(DIEPCTL(14)), + dump_register(DIEPCTL(15)), + dump_register(DOEPCTL(0)), + dump_register(DOEPCTL(1)), + dump_register(DOEPCTL(2)), + dump_register(DOEPCTL(3)), + dump_register(DOEPCTL(4)), + dump_register(DOEPCTL(5)), + dump_register(DOEPCTL(6)), + dump_register(DOEPCTL(7)), + dump_register(DOEPCTL(8)), + dump_register(DOEPCTL(9)), + dump_register(DOEPCTL(10)), + dump_register(DOEPCTL(11)), + dump_register(DOEPCTL(12)), + dump_register(DOEPCTL(13)), + dump_register(DOEPCTL(14)), + dump_register(DOEPCTL(15)), + dump_register(DIEPINT(0)), + dump_register(DIEPINT(1)), + dump_register(DIEPINT(2)), + dump_register(DIEPINT(3)), + dump_register(DIEPINT(4)), + dump_register(DIEPINT(5)), + dump_register(DIEPINT(6)), + dump_register(DIEPINT(7)), + dump_register(DIEPINT(8)), + dump_register(DIEPINT(9)), + dump_register(DIEPINT(10)), + dump_register(DIEPINT(11)), + dump_register(DIEPINT(12)), + dump_register(DIEPINT(13)), + dump_register(DIEPINT(14)), + dump_register(DIEPINT(15)), + dump_register(DOEPINT(0)), + dump_register(DOEPINT(1)), + dump_register(DOEPINT(2)), + dump_register(DOEPINT(3)), + dump_register(DOEPINT(4)), + dump_register(DOEPINT(5)), + dump_register(DOEPINT(6)), + dump_register(DOEPINT(7)), + dump_register(DOEPINT(8)), + dump_register(DOEPINT(9)), + dump_register(DOEPINT(10)), + dump_register(DOEPINT(11)), + dump_register(DOEPINT(12)), + dump_register(DOEPINT(13)), + dump_register(DOEPINT(14)), + dump_register(DOEPINT(15)), + dump_register(DIEPTSIZ(0)), + dump_register(DIEPTSIZ(1)), + dump_register(DIEPTSIZ(2)), + dump_register(DIEPTSIZ(3)), + dump_register(DIEPTSIZ(4)), + dump_register(DIEPTSIZ(5)), + dump_register(DIEPTSIZ(6)), + dump_register(DIEPTSIZ(7)), + dump_register(DIEPTSIZ(8)), + dump_register(DIEPTSIZ(9)), + dump_register(DIEPTSIZ(10)), + dump_register(DIEPTSIZ(11)), + dump_register(DIEPTSIZ(12)), + dump_register(DIEPTSIZ(13)), + dump_register(DIEPTSIZ(14)), + dump_register(DIEPTSIZ(15)), + dump_register(DOEPTSIZ(0)), + dump_register(DOEPTSIZ(1)), + dump_register(DOEPTSIZ(2)), + dump_register(DOEPTSIZ(3)), + dump_register(DOEPTSIZ(4)), + dump_register(DOEPTSIZ(5)), + dump_register(DOEPTSIZ(6)), + dump_register(DOEPTSIZ(7)), + dump_register(DOEPTSIZ(8)), + dump_register(DOEPTSIZ(9)), + dump_register(DOEPTSIZ(10)), + dump_register(DOEPTSIZ(11)), + dump_register(DOEPTSIZ(12)), + dump_register(DOEPTSIZ(13)), + dump_register(DOEPTSIZ(14)), + dump_register(DOEPTSIZ(15)), + dump_register(DIEPDMA(0)), + dump_register(DIEPDMA(1)), + dump_register(DIEPDMA(2)), + dump_register(DIEPDMA(3)), + dump_register(DIEPDMA(4)), + dump_register(DIEPDMA(5)), + dump_register(DIEPDMA(6)), + dump_register(DIEPDMA(7)), + dump_register(DIEPDMA(8)), + dump_register(DIEPDMA(9)), + dump_register(DIEPDMA(10)), + dump_register(DIEPDMA(11)), + dump_register(DIEPDMA(12)), + dump_register(DIEPDMA(13)), + dump_register(DIEPDMA(14)), + dump_register(DIEPDMA(15)), + dump_register(DOEPDMA(0)), + dump_register(DOEPDMA(1)), + dump_register(DOEPDMA(2)), + dump_register(DOEPDMA(3)), + dump_register(DOEPDMA(4)), + dump_register(DOEPDMA(5)), + dump_register(DOEPDMA(6)), + dump_register(DOEPDMA(7)), + dump_register(DOEPDMA(8)), + dump_register(DOEPDMA(9)), + dump_register(DOEPDMA(10)), + dump_register(DOEPDMA(11)), + dump_register(DOEPDMA(12)), + dump_register(DOEPDMA(13)), + dump_register(DOEPDMA(14)), + dump_register(DOEPDMA(15)), + dump_register(DTXFSTS(0)), + dump_register(DTXFSTS(1)), + dump_register(DTXFSTS(2)), + dump_register(DTXFSTS(3)), + dump_register(DTXFSTS(4)), + dump_register(DTXFSTS(5)), + dump_register(DTXFSTS(6)), + dump_register(DTXFSTS(7)), + dump_register(DTXFSTS(8)), + dump_register(DTXFSTS(9)), + dump_register(DTXFSTS(10)), + dump_register(DTXFSTS(11)), + dump_register(DTXFSTS(12)), + dump_register(DTXFSTS(13)), + dump_register(DTXFSTS(14)), + dump_register(DTXFSTS(15)), + dump_register(PCGCTL), + dump_register(HCFG), + dump_register(HFIR), + dump_register(HFNUM), + dump_register(HPTXSTS), + dump_register(HAINT), + dump_register(HAINTMSK), + dump_register(HFLBADDR), + dump_register(HPRT0), + dump_register(HCCHAR(0)), + dump_register(HCCHAR(1)), + dump_register(HCCHAR(2)), + dump_register(HCCHAR(3)), + dump_register(HCCHAR(4)), + dump_register(HCCHAR(5)), + dump_register(HCCHAR(6)), + dump_register(HCCHAR(7)), + dump_register(HCCHAR(8)), + dump_register(HCCHAR(9)), + dump_register(HCCHAR(10)), + dump_register(HCCHAR(11)), + dump_register(HCCHAR(12)), + dump_register(HCCHAR(13)), + dump_register(HCCHAR(14)), + dump_register(HCCHAR(15)), + dump_register(HCSPLT(0)), + dump_register(HCSPLT(1)), + dump_register(HCSPLT(2)), + dump_register(HCSPLT(3)), + dump_register(HCSPLT(4)), + dump_register(HCSPLT(5)), + dump_register(HCSPLT(6)), + dump_register(HCSPLT(7)), + dump_register(HCSPLT(8)), + dump_register(HCSPLT(9)), + dump_register(HCSPLT(10)), + dump_register(HCSPLT(11)), + dump_register(HCSPLT(12)), + dump_register(HCSPLT(13)), + dump_register(HCSPLT(14)), + dump_register(HCSPLT(15)), + dump_register(HCINT(0)), + dump_register(HCINT(1)), + dump_register(HCINT(2)), + dump_register(HCINT(3)), + dump_register(HCINT(4)), + dump_register(HCINT(5)), + dump_register(HCINT(6)), + dump_register(HCINT(7)), + dump_register(HCINT(8)), + dump_register(HCINT(9)), + dump_register(HCINT(10)), + dump_register(HCINT(11)), + dump_register(HCINT(12)), + dump_register(HCINT(13)), + dump_register(HCINT(14)), + dump_register(HCINT(15)), + dump_register(HCINTMSK(0)), + dump_register(HCINTMSK(1)), + dump_register(HCINTMSK(2)), + dump_register(HCINTMSK(3)), + dump_register(HCINTMSK(4)), + dump_register(HCINTMSK(5)), + dump_register(HCINTMSK(6)), + dump_register(HCINTMSK(7)), + dump_register(HCINTMSK(8)), + dump_register(HCINTMSK(9)), + dump_register(HCINTMSK(10)), + dump_register(HCINTMSK(11)), + dump_register(HCINTMSK(12)), + dump_register(HCINTMSK(13)), + dump_register(HCINTMSK(14)), + dump_register(HCINTMSK(15)), + dump_register(HCTSIZ(0)), + dump_register(HCTSIZ(1)), + dump_register(HCTSIZ(2)), + dump_register(HCTSIZ(3)), + dump_register(HCTSIZ(4)), + dump_register(HCTSIZ(5)), + dump_register(HCTSIZ(6)), + dump_register(HCTSIZ(7)), + dump_register(HCTSIZ(8)), + dump_register(HCTSIZ(9)), + dump_register(HCTSIZ(10)), + dump_register(HCTSIZ(11)), + dump_register(HCTSIZ(12)), + dump_register(HCTSIZ(13)), + dump_register(HCTSIZ(14)), + dump_register(HCTSIZ(15)), + dump_register(HCDMA(0)), + dump_register(HCDMA(1)), + dump_register(HCDMA(2)), + dump_register(HCDMA(3)), + dump_register(HCDMA(4)), + dump_register(HCDMA(5)), + dump_register(HCDMA(6)), + dump_register(HCDMA(7)), + dump_register(HCDMA(8)), + dump_register(HCDMA(9)), + dump_register(HCDMA(10)), + dump_register(HCDMA(11)), + dump_register(HCDMA(12)), + dump_register(HCDMA(13)), + dump_register(HCDMA(14)), + dump_register(HCDMA(15)), + dump_register(HCDMAB(0)), + dump_register(HCDMAB(1)), + dump_register(HCDMAB(2)), + dump_register(HCDMAB(3)), + dump_register(HCDMAB(4)), + dump_register(HCDMAB(5)), + dump_register(HCDMAB(6)), + dump_register(HCDMAB(7)), + dump_register(HCDMAB(8)), + dump_register(HCDMAB(9)), + dump_register(HCDMAB(10)), + dump_register(HCDMAB(11)), + dump_register(HCDMAB(12)), + dump_register(HCDMAB(13)), + dump_register(HCDMAB(14)), + dump_register(HCDMAB(15)), +}; + +int dwc2_debugfs_init(struct dwc2_hsotg *hsotg) +{ + int ret; + struct dentry *file; + + hsotg->debug_root = debugfs_create_dir(dev_name(hsotg->dev), NULL); + if (!hsotg->debug_root) { + ret = -ENOMEM; + goto err0; + } + + /* Add gadget debugfs nodes */ + s3c_hsotg_create_debug(hsotg); + + hsotg->regset = devm_kzalloc(hsotg->dev, sizeof(*hsotg->regset), + GFP_KERNEL); + if (!hsotg->regset) { + ret = -ENOMEM; + goto err1; + } + + hsotg->regset->regs = dwc2_regs; + hsotg->regset->nregs = ARRAY_SIZE(dwc2_regs); + hsotg->regset->base = hsotg->regs; + + file = debugfs_create_regset32("regdump", S_IRUGO, hsotg->debug_root, + hsotg->regset); + if (!file) { + ret = -ENOMEM; + goto err1; + } + + return 0; +err1: + debugfs_remove_recursive(hsotg->debug_root); +err0: + return ret; +} + +void dwc2_debugfs_exit(struct dwc2_hsotg *hsotg) +{ + debugfs_remove_recursive(hsotg->debug_root); + hsotg->debug_root = NULL; +} diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 6a30887082cd..4d47b7c09238 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -20,7 +20,6 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> -#include <linux/debugfs.h> #include <linux/mutex.h> #include <linux/seq_file.h> #include <linux/delay.h> @@ -35,7 +34,6 @@ #include <linux/usb/gadget.h> #include <linux/usb/phy.h> #include <linux/platform_data/s3c-hsotg.h> -#include <linux/uaccess.h> #include "core.h" #include "hw.h" @@ -792,6 +790,13 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, ep->name, req, req->length, req->buf, req->no_interrupt, req->zero, req->short_not_ok); + /* Prevent new request submission when controller is suspended */ + if (hs->lx_state == DWC2_L2) { + dev_dbg(hs->dev, "%s: don't submit request while suspended\n", + __func__); + return -EAGAIN; + } + /* initialise status of the request */ INIT_LIST_HEAD(&hs_req->queue); req->actual = 0; @@ -894,7 +899,7 @@ static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg, * @testmode: requested usb test mode * Enable usb Test Mode requested by the Host. */ -static int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode) +int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode) { int dctl = readl(hsotg->regs + DCTL); @@ -2185,7 +2190,6 @@ void s3c_hsotg_disconnect(struct dwc2_hsotg *hsotg) call_gadget(hsotg, disconnect); } -EXPORT_SYMBOL_GPL(s3c_hsotg_disconnect); /** * s3c_hsotg_irq_fifoempty - TX FIFO empty interrupt handler @@ -2310,8 +2314,9 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, writel(GINTSTS_ERLYSUSP | GINTSTS_SESSREQINT | GINTSTS_GOUTNAKEFF | GINTSTS_GINNAKEFF | GINTSTS_CONIDSTSCHNG | GINTSTS_USBRST | - GINTSTS_ENUMDONE | GINTSTS_OTGINT | - GINTSTS_USBSUSP | GINTSTS_WKUPINT, + GINTSTS_RESETDET | GINTSTS_ENUMDONE | + GINTSTS_OTGINT | GINTSTS_USBSUSP | + GINTSTS_WKUPINT, hsotg->regs + GINTMSK); if (using_dma(hsotg)) @@ -2477,7 +2482,19 @@ irq_retry: } } - if (gintsts & GINTSTS_USBRST) { + if (gintsts & GINTSTS_RESETDET) { + dev_dbg(hsotg->dev, "%s: USBRstDet\n", __func__); + + writel(GINTSTS_RESETDET, hsotg->regs + GINTSTS); + + /* This event must be used only if controller is suspended */ + if (hsotg->lx_state == DWC2_L2) { + dwc2_exit_hibernation(hsotg, true); + hsotg->lx_state = DWC2_L0; + } + } + + if (gintsts & (GINTSTS_USBRST | GINTSTS_RESETDET)) { u32 usb_status = readl(hsotg->regs + GOTGCTL); @@ -2497,6 +2514,7 @@ irq_retry: kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET); + hsotg->lx_state = DWC2_L0; s3c_hsotg_core_init_disconnected(hsotg, true); } } @@ -2745,7 +2763,7 @@ error: * s3c_hsotg_ep_disable - disable given endpoint * @ep: The endpoint to disable. */ -static int s3c_hsotg_ep_disable_force(struct usb_ep *ep, bool force) +static int s3c_hsotg_ep_disable(struct usb_ep *ep) { struct s3c_hsotg_ep *hs_ep = our_ep(ep); struct dwc2_hsotg *hsotg = hs_ep->parent; @@ -2788,10 +2806,6 @@ static int s3c_hsotg_ep_disable_force(struct usb_ep *ep, bool force) return 0; } -static int s3c_hsotg_ep_disable(struct usb_ep *ep) -{ - return s3c_hsotg_ep_disable_force(ep, false); -} /** * on_list - check request is on the given endpoint * @ep: The endpoint to check. @@ -3187,6 +3201,14 @@ static int s3c_hsotg_vbus_session(struct usb_gadget *gadget, int is_active) spin_lock_irqsave(&hsotg->lock, flags); if (is_active) { + /* + * If controller is hibernated, it must exit from hibernation + * before being initialized + */ + if (hsotg->lx_state == DWC2_L2) { + dwc2_exit_hibernation(hsotg, false); + hsotg->lx_state = DWC2_L0; + } /* Kill any ep0 requests as controller will be reinitialized */ kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET); s3c_hsotg_core_init_disconnected(hsotg, false); @@ -3391,404 +3413,6 @@ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg) #endif } -/** - * testmode_write - debugfs: change usb test mode - * @seq: The seq file to write to. - * @v: Unused parameter. - * - * This debugfs entry modify the current usb test mode. - */ -static ssize_t testmode_write(struct file *file, const char __user *ubuf, size_t - count, loff_t *ppos) -{ - struct seq_file *s = file->private_data; - struct dwc2_hsotg *hsotg = s->private; - unsigned long flags; - u32 testmode = 0; - char buf[32]; - - if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) - return -EFAULT; - - if (!strncmp(buf, "test_j", 6)) - testmode = TEST_J; - else if (!strncmp(buf, "test_k", 6)) - testmode = TEST_K; - else if (!strncmp(buf, "test_se0_nak", 12)) - testmode = TEST_SE0_NAK; - else if (!strncmp(buf, "test_packet", 11)) - testmode = TEST_PACKET; - else if (!strncmp(buf, "test_force_enable", 17)) - testmode = TEST_FORCE_EN; - else - testmode = 0; - - spin_lock_irqsave(&hsotg->lock, flags); - s3c_hsotg_set_test_mode(hsotg, testmode); - spin_unlock_irqrestore(&hsotg->lock, flags); - return count; -} - -/** - * testmode_show - debugfs: show usb test mode state - * @seq: The seq file to write to. - * @v: Unused parameter. - * - * This debugfs entry shows which usb test mode is currently enabled. - */ -static int testmode_show(struct seq_file *s, void *unused) -{ - struct dwc2_hsotg *hsotg = s->private; - unsigned long flags; - int dctl; - - spin_lock_irqsave(&hsotg->lock, flags); - dctl = readl(hsotg->regs + DCTL); - dctl &= DCTL_TSTCTL_MASK; - dctl >>= DCTL_TSTCTL_SHIFT; - spin_unlock_irqrestore(&hsotg->lock, flags); - - switch (dctl) { - case 0: - seq_puts(s, "no test\n"); - break; - case TEST_J: - seq_puts(s, "test_j\n"); - break; - case TEST_K: - seq_puts(s, "test_k\n"); - break; - case TEST_SE0_NAK: - seq_puts(s, "test_se0_nak\n"); - break; - case TEST_PACKET: - seq_puts(s, "test_packet\n"); - break; - case TEST_FORCE_EN: - seq_puts(s, "test_force_enable\n"); - break; - default: - seq_printf(s, "UNKNOWN %d\n", dctl); - } - - return 0; -} - -static int testmode_open(struct inode *inode, struct file *file) -{ - return single_open(file, testmode_show, inode->i_private); -} - -static const struct file_operations testmode_fops = { - .owner = THIS_MODULE, - .open = testmode_open, - .write = testmode_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/** - * state_show - debugfs: show overall driver and device state. - * @seq: The seq file to write to. - * @v: Unused parameter. - * - * This debugfs entry shows the overall state of the hardware and - * some general information about each of the endpoints available - * to the system. - */ -static int state_show(struct seq_file *seq, void *v) -{ - struct dwc2_hsotg *hsotg = seq->private; - void __iomem *regs = hsotg->regs; - int idx; - - seq_printf(seq, "DCFG=0x%08x, DCTL=0x%08x, DSTS=0x%08x\n", - readl(regs + DCFG), - readl(regs + DCTL), - readl(regs + DSTS)); - - seq_printf(seq, "DIEPMSK=0x%08x, DOEPMASK=0x%08x\n", - readl(regs + DIEPMSK), readl(regs + DOEPMSK)); - - seq_printf(seq, "GINTMSK=0x%08x, GINTSTS=0x%08x\n", - readl(regs + GINTMSK), - readl(regs + GINTSTS)); - - seq_printf(seq, "DAINTMSK=0x%08x, DAINT=0x%08x\n", - readl(regs + DAINTMSK), - readl(regs + DAINT)); - - seq_printf(seq, "GNPTXSTS=0x%08x, GRXSTSR=%08x\n", - readl(regs + GNPTXSTS), - readl(regs + GRXSTSR)); - - seq_puts(seq, "\nEndpoint status:\n"); - - for (idx = 0; idx < hsotg->num_of_eps; idx++) { - u32 in, out; - - in = readl(regs + DIEPCTL(idx)); - out = readl(regs + DOEPCTL(idx)); - - seq_printf(seq, "ep%d: DIEPCTL=0x%08x, DOEPCTL=0x%08x", - idx, in, out); - - in = readl(regs + DIEPTSIZ(idx)); - out = readl(regs + DOEPTSIZ(idx)); - - seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x", - in, out); - - seq_puts(seq, "\n"); - } - - return 0; -} - -static int state_open(struct inode *inode, struct file *file) -{ - return single_open(file, state_show, inode->i_private); -} - -static const struct file_operations state_fops = { - .owner = THIS_MODULE, - .open = state_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/** - * fifo_show - debugfs: show the fifo information - * @seq: The seq_file to write data to. - * @v: Unused parameter. - * - * Show the FIFO information for the overall fifo and all the - * periodic transmission FIFOs. - */ -static int fifo_show(struct seq_file *seq, void *v) -{ - struct dwc2_hsotg *hsotg = seq->private; - void __iomem *regs = hsotg->regs; - u32 val; - int idx; - - seq_puts(seq, "Non-periodic FIFOs:\n"); - seq_printf(seq, "RXFIFO: Size %d\n", readl(regs + GRXFSIZ)); - - val = readl(regs + GNPTXFSIZ); - seq_printf(seq, "NPTXFIFO: Size %d, Start 0x%08x\n", - val >> FIFOSIZE_DEPTH_SHIFT, - val & FIFOSIZE_DEPTH_MASK); - - seq_puts(seq, "\nPeriodic TXFIFOs:\n"); - - for (idx = 1; idx < hsotg->num_of_eps; idx++) { - val = readl(regs + DPTXFSIZN(idx)); - - seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx, - val >> FIFOSIZE_DEPTH_SHIFT, - val & FIFOSIZE_STARTADDR_MASK); - } - - return 0; -} - -static int fifo_open(struct inode *inode, struct file *file) -{ - return single_open(file, fifo_show, inode->i_private); -} - -static const struct file_operations fifo_fops = { - .owner = THIS_MODULE, - .open = fifo_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - - -static const char *decode_direction(int is_in) -{ - return is_in ? "in" : "out"; -} - -/** - * ep_show - debugfs: show the state of an endpoint. - * @seq: The seq_file to write data to. - * @v: Unused parameter. - * - * This debugfs entry shows the state of the given endpoint (one is - * registered for each available). - */ -static int ep_show(struct seq_file *seq, void *v) -{ - struct s3c_hsotg_ep *ep = seq->private; - struct dwc2_hsotg *hsotg = ep->parent; - struct s3c_hsotg_req *req; - void __iomem *regs = hsotg->regs; - int index = ep->index; - int show_limit = 15; - unsigned long flags; - - seq_printf(seq, "Endpoint index %d, named %s, dir %s:\n", - ep->index, ep->ep.name, decode_direction(ep->dir_in)); - - /* first show the register state */ - - seq_printf(seq, "\tDIEPCTL=0x%08x, DOEPCTL=0x%08x\n", - readl(regs + DIEPCTL(index)), - readl(regs + DOEPCTL(index))); - - seq_printf(seq, "\tDIEPDMA=0x%08x, DOEPDMA=0x%08x\n", - readl(regs + DIEPDMA(index)), - readl(regs + DOEPDMA(index))); - - seq_printf(seq, "\tDIEPINT=0x%08x, DOEPINT=0x%08x\n", - readl(regs + DIEPINT(index)), - readl(regs + DOEPINT(index))); - - seq_printf(seq, "\tDIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x\n", - readl(regs + DIEPTSIZ(index)), - readl(regs + DOEPTSIZ(index))); - - seq_puts(seq, "\n"); - seq_printf(seq, "mps %d\n", ep->ep.maxpacket); - seq_printf(seq, "total_data=%ld\n", ep->total_data); - - seq_printf(seq, "request list (%p,%p):\n", - ep->queue.next, ep->queue.prev); - - spin_lock_irqsave(&hsotg->lock, flags); - - list_for_each_entry(req, &ep->queue, queue) { - if (--show_limit < 0) { - seq_puts(seq, "not showing more requests...\n"); - break; - } - - seq_printf(seq, "%c req %p: %d bytes @%p, ", - req == ep->req ? '*' : ' ', - req, req->req.length, req->req.buf); - seq_printf(seq, "%d done, res %d\n", - req->req.actual, req->req.status); - } - - spin_unlock_irqrestore(&hsotg->lock, flags); - - return 0; -} - -static int ep_open(struct inode *inode, struct file *file) -{ - return single_open(file, ep_show, inode->i_private); -} - -static const struct file_operations ep_fops = { - .owner = THIS_MODULE, - .open = ep_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/** - * s3c_hsotg_create_debug - create debugfs directory and files - * @hsotg: The driver state - * - * Create the debugfs files to allow the user to get information - * about the state of the system. The directory name is created - * with the same name as the device itself, in case we end up - * with multiple blocks in future systems. - */ -static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) -{ - struct dentry *root; - unsigned epidx; - - root = debugfs_create_dir(dev_name(hsotg->dev), NULL); - hsotg->debug_root = root; - if (IS_ERR(root)) { - dev_err(hsotg->dev, "cannot create debug root\n"); - return; - } - - /* create general state file */ - - hsotg->debug_file = debugfs_create_file("state", S_IRUGO, root, - hsotg, &state_fops); - - if (IS_ERR(hsotg->debug_file)) - dev_err(hsotg->dev, "%s: failed to create state\n", __func__); - - hsotg->debug_testmode = debugfs_create_file("testmode", - S_IRUGO | S_IWUSR, root, - hsotg, &testmode_fops); - - if (IS_ERR(hsotg->debug_testmode)) - dev_err(hsotg->dev, "%s: failed to create testmode\n", - __func__); - - hsotg->debug_fifo = debugfs_create_file("fifo", S_IRUGO, root, - hsotg, &fifo_fops); - - if (IS_ERR(hsotg->debug_fifo)) - dev_err(hsotg->dev, "%s: failed to create fifo\n", __func__); - - /* Create one file for each out endpoint */ - for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) { - struct s3c_hsotg_ep *ep; - - ep = hsotg->eps_out[epidx]; - if (ep) { - ep->debugfs = debugfs_create_file(ep->name, S_IRUGO, - root, ep, &ep_fops); - - if (IS_ERR(ep->debugfs)) - dev_err(hsotg->dev, "failed to create %s debug file\n", - ep->name); - } - } - /* Create one file for each in endpoint. EP0 is handled with out eps */ - for (epidx = 1; epidx < hsotg->num_of_eps; epidx++) { - struct s3c_hsotg_ep *ep; - - ep = hsotg->eps_in[epidx]; - if (ep) { - ep->debugfs = debugfs_create_file(ep->name, S_IRUGO, - root, ep, &ep_fops); - - if (IS_ERR(ep->debugfs)) - dev_err(hsotg->dev, "failed to create %s debug file\n", - ep->name); - } - } -} - -/** - * s3c_hsotg_delete_debug - cleanup debugfs entries - * @hsotg: The driver state - * - * Cleanup (remove) the debugfs files for use on module exit. - */ -static void s3c_hsotg_delete_debug(struct dwc2_hsotg *hsotg) -{ - unsigned epidx; - - for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) { - if (hsotg->eps_in[epidx]) - debugfs_remove(hsotg->eps_in[epidx]->debugfs); - if (hsotg->eps_out[epidx]) - debugfs_remove(hsotg->eps_out[epidx]->debugfs); - } - - debugfs_remove(hsotg->debug_file); - debugfs_remove(hsotg->debug_testmode); - debugfs_remove(hsotg->debug_fifo); - debugfs_remove(hsotg->debug_root); -} - #ifdef CONFIG_OF static void s3c_hsotg_of_probe(struct dwc2_hsotg *hsotg) { @@ -3896,6 +3520,8 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) hsotg->gadget.max_speed = USB_SPEED_HIGH; hsotg->gadget.ops = &s3c_hsotg_gadget_ops; hsotg->gadget.name = dev_name(dev); + if (hsotg->dr_mode == USB_DR_MODE_OTG) + hsotg->gadget.is_otg = 1; /* reset the system */ @@ -4028,8 +3654,6 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) if (ret) goto err_supplies; - s3c_hsotg_create_debug(hsotg); - s3c_hsotg_dump(hsotg); return 0; @@ -4041,7 +3665,6 @@ err_clk: return ret; } -EXPORT_SYMBOL_GPL(dwc2_gadget_init); /** * s3c_hsotg_remove - remove function for hsotg driver @@ -4050,18 +3673,19 @@ EXPORT_SYMBOL_GPL(dwc2_gadget_init); int s3c_hsotg_remove(struct dwc2_hsotg *hsotg) { usb_del_gadget_udc(&hsotg->gadget); - s3c_hsotg_delete_debug(hsotg); clk_disable_unprepare(hsotg->clk); return 0; } -EXPORT_SYMBOL_GPL(s3c_hsotg_remove); int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg) { unsigned long flags; int ret = 0; + if (hsotg->lx_state != DWC2_L0) + return ret; + mutex_lock(&hsotg->init_mutex); if (hsotg->driver) { @@ -4095,13 +3719,15 @@ int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg) return ret; } -EXPORT_SYMBOL_GPL(s3c_hsotg_suspend); int s3c_hsotg_resume(struct dwc2_hsotg *hsotg) { unsigned long flags; int ret = 0; + if (hsotg->lx_state == DWC2_L2) + return ret; + mutex_lock(&hsotg->init_mutex); if (hsotg->driver) { @@ -4124,4 +3750,3 @@ int s3c_hsotg_resume(struct dwc2_hsotg *hsotg) return ret; } -EXPORT_SYMBOL_GPL(s3c_hsotg_resume); diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index fbbbac2150a5..b10377c65064 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -357,12 +357,12 @@ void dwc2_hcd_stop(struct dwc2_hsotg *hsotg) writel(0, hsotg->regs + HPRT0); } +/* Caller must hold driver lock */ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg, struct dwc2_hcd_urb *urb, void **ep_handle, gfp_t mem_flags) { struct dwc2_qtd *qtd; - unsigned long flags; u32 intr_mask; int retval; int dev_speed; @@ -413,11 +413,9 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg, */ return 0; - spin_lock_irqsave(&hsotg->lock, flags); tr_type = dwc2_hcd_select_transactions(hsotg); if (tr_type != DWC2_TRANSACTION_NONE) dwc2_hcd_queue_transactions(hsotg, tr_type); - spin_unlock_irqrestore(&hsotg->lock, flags); } return 0; @@ -721,9 +719,7 @@ static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, /* 3072 = 3 max-size Isoc packets */ buf_size = 3072; - qh->dw_align_buf = dma_alloc_coherent(hsotg->dev, buf_size, - &qh->dw_align_buf_dma, - GFP_ATOMIC); + qh->dw_align_buf = kmalloc(buf_size, GFP_ATOMIC | GFP_DMA); if (!qh->dw_align_buf) return -ENOMEM; qh->dw_align_buf_size = buf_size; @@ -748,6 +744,15 @@ static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, } } + qh->dw_align_buf_dma = dma_map_single(hsotg->dev, + qh->dw_align_buf, qh->dw_align_buf_size, + chan->ep_is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (dma_mapping_error(hsotg->dev, qh->dw_align_buf_dma)) { + dev_err(hsotg->dev, "can't map align_buf\n"); + chan->align_buf = 0; + return -EINVAL; + } + chan->align_buf = qh->dw_align_buf_dma; return 0; } @@ -1774,6 +1779,15 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, /* Not supported */ break; + case USB_PORT_FEAT_TEST: + hprt0 = dwc2_read_hprt0(hsotg); + dev_dbg(hsotg->dev, + "SetPortFeature - USB_PORT_FEAT_TEST\n"); + hprt0 &= ~HPRT0_TSTCTL_MASK; + hprt0 |= (windex >> 8) << HPRT0_TSTCTL_SHIFT; + writel(hprt0, hsotg->regs + HPRT0); + break; + default: retval = -EINVAL; dev_err(hsotg->dev, @@ -2313,6 +2327,22 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd) usleep_range(1000, 3000); } +static int _dwc2_hcd_suspend(struct usb_hcd *hcd) +{ + struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); + + hsotg->lx_state = DWC2_L2; + return 0; +} + +static int _dwc2_hcd_resume(struct usb_hcd *hcd) +{ + struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); + + hsotg->lx_state = DWC2_L0; + return 0; +} + /* Returns the current frame number */ static int _dwc2_hcd_get_frame_number(struct usb_hcd *hcd) { @@ -2468,7 +2498,7 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, "%s: unaligned transfer with no transfer_buffer", __func__); retval = -EINVAL; - goto fail1; + goto fail0; } } @@ -2496,7 +2526,6 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, spin_lock_irqsave(&hsotg->lock, flags); retval = usb_hcd_link_urb_to_ep(hcd, urb); - spin_unlock_irqrestore(&hsotg->lock, flags); if (retval) goto fail1; @@ -2505,22 +2534,22 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, goto fail2; if (alloc_bandwidth) { - spin_lock_irqsave(&hsotg->lock, flags); dwc2_allocate_bus_bandwidth(hcd, dwc2_hcd_get_ep_bandwidth(hsotg, ep), urb); - spin_unlock_irqrestore(&hsotg->lock, flags); } + spin_unlock_irqrestore(&hsotg->lock, flags); + return 0; fail2: - spin_lock_irqsave(&hsotg->lock, flags); dwc2_urb->priv = NULL; usb_hcd_unlink_urb_from_ep(hcd, urb); - spin_unlock_irqrestore(&hsotg->lock, flags); fail1: + spin_unlock_irqrestore(&hsotg->lock, flags); urb->hcpriv = NULL; +fail0: kfree(dwc2_urb); return retval; @@ -2683,6 +2712,9 @@ static struct hc_driver dwc2_hc_driver = { .hub_status_data = _dwc2_hcd_hub_status_data, .hub_control = _dwc2_hcd_hub_control, .clear_tt_buffer_complete = _dwc2_hcd_clear_tt_buffer_complete, + + .bus_suspend = _dwc2_hcd_suspend, + .bus_resume = _dwc2_hcd_resume, }; /* @@ -2748,8 +2780,6 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg) destroy_workqueue(hsotg->wq_otg); } - kfree(hsotg->core_params); - hsotg->core_params = NULL; del_timer(&hsotg->wkp_timer); } @@ -2762,29 +2792,12 @@ static void dwc2_hcd_release(struct dwc2_hsotg *hsotg) } /* - * Sets all parameters to the given value. - * - * Assumes that the dwc2_core_params struct contains only integers. - */ -void dwc2_set_all_params(struct dwc2_core_params *params, int value) -{ - int *p = (int *)params; - size_t size = sizeof(*params) / sizeof(*p); - int i; - - for (i = 0; i < size; i++) - p[i] = value; -} -EXPORT_SYMBOL_GPL(dwc2_set_all_params); - -/* * Initializes the HCD. This function allocates memory for and initializes the * static parts of the usb_hcd and dwc2_hsotg structures. It also registers the * USB bus with the core and calls the hc_driver->start() function. It returns * a negative error on failure. */ -int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, - const struct dwc2_core_params *params) +int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq) { struct usb_hcd *hcd; struct dwc2_host_chan *channel; @@ -2797,12 +2810,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, dev_dbg(hsotg->dev, "DWC OTG HCD INIT\n"); - /* Detect config values from hardware */ - retval = dwc2_get_hwparams(hsotg); - - if (retval) - return retval; - retval = -ENOMEM; hcfg = readl(hsotg->regs + HCFG); @@ -2821,15 +2828,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, hsotg->last_frame_num = HFNUM_MAX_FRNUM; #endif - hsotg->core_params = kzalloc(sizeof(*hsotg->core_params), GFP_KERNEL); - if (!hsotg->core_params) - goto error1; - - dwc2_set_all_params(hsotg->core_params, -1); - - /* Validate parameter values */ - dwc2_set_parameters(hsotg, params); - /* Check if the bus driver or platform code has setup a dma_mask */ if (hsotg->core_params->dma_enable > 0 && hsotg->dev->dma_mask == NULL) { @@ -2947,6 +2945,9 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, /* Don't support SG list at this point */ hcd->self.sg_tablesize = 0; + if (!IS_ERR_OR_NULL(hsotg->uphy)) + otg_set_host(hsotg->uphy->otg, &hcd->self); + /* * Finish generic HCD initialization and start the HCD. This function * allocates the DMA buffer pool, registers the USB bus, requests the @@ -2979,7 +2980,6 @@ error1: dev_err(hsotg->dev, "%s() FAILED, returning %d\n", __func__, retval); return retval; } -EXPORT_SYMBOL_GPL(dwc2_hcd_init); /* * Removes the HCD. @@ -3000,6 +3000,9 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) return; } + if (!IS_ERR_OR_NULL(hsotg->uphy)) + otg_set_host(hsotg->uphy->otg, NULL); + usb_remove_hcd(hcd); hsotg->priv = NULL; dwc2_hcd_release(hsotg); @@ -3010,4 +3013,3 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) kfree(hsotg->frame_num_array); #endif } -EXPORT_SYMBOL_GPL(dwc2_hcd_remove); diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index e69a843d8928..7b5841c40033 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -451,13 +451,8 @@ static inline u8 dwc2_hcd_is_pipe_out(struct dwc2_hcd_pipe_info *pipe) return !dwc2_hcd_is_pipe_in(pipe); } -extern int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, - const struct dwc2_core_params *params); +extern int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq); extern void dwc2_hcd_remove(struct dwc2_hsotg *hsotg); -extern void dwc2_set_parameters(struct dwc2_hsotg *hsotg, - const struct dwc2_core_params *params); -extern void dwc2_set_all_params(struct dwc2_core_params *params, int value); -extern int dwc2_get_hwparams(struct dwc2_hsotg *hsotg); /* Transaction Execution Functions */ extern enum dwc2_transaction_type dwc2_hcd_select_transactions( diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index 551ba878b003..4cc95df4262d 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -350,6 +350,9 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg) dev_vdbg(hsotg->dev, "--Port Interrupt HPRT0=0x%08x Port Connect Detected--\n", hprt0); + if (hsotg->lx_state != DWC2_L0) + usb_hcd_resume_root_hub(hsotg->priv); + hsotg->flags.b.port_connect_status_change = 1; hsotg->flags.b.port_connect_status = 1; hprt0_modify |= HPRT0_CONNDET; @@ -463,10 +466,15 @@ static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg, } /* Non DWORD-aligned buffer case handling */ - if (chan->align_buf && xfer_length && chan->ep_is_in) { + if (chan->align_buf && xfer_length) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf, - xfer_length); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, + chan->ep_is_in ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (chan->ep_is_in) + memcpy(urb->buf + urb->actual_length, + chan->qh->dw_align_buf, xfer_length); } dev_vdbg(hsotg->dev, "urb->actual_length=%d xfer_length=%d\n", @@ -552,13 +560,18 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state( chan, chnum, qtd, halt_status, NULL); /* Non DWORD-aligned buffer case handling */ - if (chan->align_buf && frame_desc->actual_length && - chan->ep_is_in) { + if (chan->align_buf && frame_desc->actual_length) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - memcpy(urb->buf + frame_desc->offset + - qtd->isoc_split_offset, chan->qh->dw_align_buf, - frame_desc->actual_length); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, + chan->ep_is_in ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (chan->ep_is_in) + memcpy(urb->buf + frame_desc->offset + + qtd->isoc_split_offset, + chan->qh->dw_align_buf, + frame_desc->actual_length); } break; case DWC2_HC_XFER_FRAME_OVERRUN: @@ -581,13 +594,18 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state( chan, chnum, qtd, halt_status, NULL); /* Non DWORD-aligned buffer case handling */ - if (chan->align_buf && frame_desc->actual_length && - chan->ep_is_in) { + if (chan->align_buf && frame_desc->actual_length) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - memcpy(urb->buf + frame_desc->offset + - qtd->isoc_split_offset, chan->qh->dw_align_buf, - frame_desc->actual_length); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, + chan->ep_is_in ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (chan->ep_is_in) + memcpy(urb->buf + frame_desc->offset + + qtd->isoc_split_offset, + chan->qh->dw_align_buf, + frame_desc->actual_length); } /* Skip whole frame */ @@ -923,6 +941,8 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg, if (chan->align_buf) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, DMA_FROM_DEVICE); memcpy(qtd->urb->buf + frame_desc->offset + qtd->isoc_split_offset, chan->qh->dw_align_buf, len); } @@ -1152,8 +1172,14 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg, /* Non DWORD-aligned buffer case handling */ if (chan->align_buf && xfer_length && chan->ep_is_in) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf, - xfer_length); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, + chan->ep_is_in ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (chan->ep_is_in) + memcpy(urb->buf + urb->actual_length, + chan->qh->dw_align_buf, + xfer_length); } urb->actual_length += xfer_length; @@ -1182,6 +1208,16 @@ static void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, int chnum, struct dwc2_qtd *qtd) { + if (!qtd) { + dev_dbg(hsotg->dev, "%s: qtd is NULL\n", __func__); + return; + } + + if (!qtd->urb) { + dev_dbg(hsotg->dev, "%s: qtd->urb is NULL\n", __func__); + return; + } + if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "--Host Channel %d Interrupt: NAK Received--\n", chnum); diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index bb97838bc6c0..9b5c36256627 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -229,11 +229,13 @@ static struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg, */ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) { - if (hsotg->core_params->dma_desc_enable > 0) + if (hsotg->core_params->dma_desc_enable > 0) { dwc2_hcd_qh_free_ddma(hsotg, qh); - else if (qh->dw_align_buf) - dma_free_coherent(hsotg->dev, qh->dw_align_buf_size, - qh->dw_align_buf, qh->dw_align_buf_dma); + } else { + /* kfree(NULL) is safe */ + kfree(qh->dw_align_buf); + qh->dw_align_buf_dma = (dma_addr_t)0; + } kfree(qh); } @@ -761,6 +763,7 @@ void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb) /** * dwc2_hcd_qtd_add() - Adds a QTD to the QTD-list of a QH + * Caller must hold driver lock. * * @hsotg: The DWC HCD structure * @qtd: The QTD to add @@ -777,7 +780,6 @@ int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd, struct dwc2_qh **qh, gfp_t mem_flags) { struct dwc2_hcd_urb *urb = qtd->urb; - unsigned long flags; int allocated = 0; int retval; @@ -792,15 +794,12 @@ int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd, allocated = 1; } - spin_lock_irqsave(&hsotg->lock, flags); - retval = dwc2_hcd_qh_add(hsotg, *qh); if (retval) goto fail; qtd->qh = *qh; list_add_tail(&qtd->qtd_list_entry, &(*qh)->qtd_list); - spin_unlock_irqrestore(&hsotg->lock, flags); return 0; @@ -817,10 +816,7 @@ fail: qtd_list_entry) dwc2_hcd_qtd_unlink_and_free(hsotg, qtd2, qh_tmp); - spin_unlock_irqrestore(&hsotg->lock, flags); dwc2_hcd_qh_free(hsotg, qh_tmp); - } else { - spin_unlock_irqrestore(&hsotg->lock, flags); } return retval; diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 185663e0b5f4..90935304185a 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -47,6 +47,7 @@ #include "core.h" #include "hcd.h" +#include "debug.h" static const char dwc2_driver_name[] = "dwc2"; @@ -76,6 +77,8 @@ static const struct dwc2_core_params params_bcm2835 = { .reload_ctl = 0, .ahbcfg = 0x10, .uframe_sched = 0, + .external_id_pin_ctl = -1, + .hibernation = -1, }; static const struct dwc2_core_params params_rk3066 = { @@ -104,6 +107,8 @@ static const struct dwc2_core_params params_rk3066 = { .reload_ctl = -1, .ahbcfg = 0x7, /* INCR16 */ .uframe_sched = -1, + .external_id_pin_ctl = -1, + .hibernation = -1, }; /** @@ -121,6 +126,7 @@ static int dwc2_driver_remove(struct platform_device *dev) { struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); + dwc2_debugfs_exit(hsotg); if (hsotg->hcd_enabled) dwc2_hcd_remove(hsotg); if (hsotg->gadget_enabled) @@ -237,6 +243,21 @@ static int dwc2_driver_probe(struct platform_device *dev) spin_lock_init(&hsotg->lock); mutex_init(&hsotg->init_mutex); + /* Detect config values from hardware */ + retval = dwc2_get_hwparams(hsotg); + if (retval) + return retval; + + hsotg->core_params = devm_kzalloc(&dev->dev, + sizeof(*hsotg->core_params), GFP_KERNEL); + if (!hsotg->core_params) + return -ENOMEM; + + dwc2_set_all_params(hsotg->core_params, -1); + + /* Validate parameter values */ + dwc2_set_parameters(hsotg, params); + if (hsotg->dr_mode != USB_DR_MODE_HOST) { retval = dwc2_gadget_init(hsotg, irq); if (retval) @@ -245,7 +266,7 @@ static int dwc2_driver_probe(struct platform_device *dev) } if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) { - retval = dwc2_hcd_init(hsotg, irq, params); + retval = dwc2_hcd_init(hsotg, irq); if (retval) { if (hsotg->gadget_enabled) s3c_hsotg_remove(hsotg); @@ -256,6 +277,8 @@ static int dwc2_driver_probe(struct platform_device *dev) platform_set_drvdata(dev, hsotg); + dwc2_debugfs_init(hsotg); + return retval; } diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 827c4f80379f..473bfaa96c30 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -11,6 +11,13 @@ config USB_DWC3 if USB_DWC3 +config USB_DWC3_ULPI + bool "Register ULPI PHY Interface" + depends on USB_ULPI_BUS=y ||Â USB_ULPI_BUS=USB_DWC3 + help + Select this if you have ULPI type PHY attached to your DWC3 + controller. + choice bool "DWC3 Mode Selection" default USB_DWC3_DUAL_ROLE if (USB && USB_GADGET) diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 46172f47f02d..c7076e37c4ed 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -15,6 +15,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),) dwc3-y += gadget.o ep0.o endif +ifneq ($(CONFIG_USB_DWC3_ULPI),) + dwc3-y += ulpi.o +endif + ifneq ($(CONFIG_DEBUG_FS),) dwc3-y += debugfs.o endif diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 2bbab3d86fff..5c110d8e293b 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -117,6 +117,33 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) } /** + * dwc3_soft_reset - Issue soft reset + * @dwc: Pointer to our controller context structure + */ +static int dwc3_soft_reset(struct dwc3 *dwc) +{ + unsigned long timeout; + u32 reg; + + timeout = jiffies + msecs_to_jiffies(500); + dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST); + do { + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (!(reg & DWC3_DCTL_CSFTRST)) + break; + + if (time_after(jiffies, timeout)) { + dev_err(dwc->dev, "Reset Timed Out\n"); + return -ETIMEDOUT; + } + + cpu_relax(); + } while (true); + + return 0; +} + +/** * dwc3_free_one_event_buffer - Frees one event buffer * @dwc: Pointer to our controller context structure * @evt: Pointer to event buffer to be freed @@ -367,10 +394,15 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc) /** * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core * @dwc: Pointer to our controller context structure + * + * Returns 0 on success. The USB PHY interfaces are configured but not + * initialized. The PHY interfaces and the PHYs get initialized together with + * the core in dwc3_core_init. */ -static void dwc3_phy_setup(struct dwc3 *dwc) +static int dwc3_phy_setup(struct dwc3 *dwc) { u32 reg; + int ret; reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); @@ -409,10 +441,41 @@ static void dwc3_phy_setup(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); - mdelay(100); - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + /* Select the HS PHY interface */ + switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) { + case DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI: + if (!strncmp(dwc->hsphy_interface, "utmi", 4)) { + reg &= ~DWC3_GUSB2PHYCFG_ULPI_UTMI; + break; + } else if (!strncmp(dwc->hsphy_interface, "ulpi", 4)) { + reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + } else { + dev_warn(dwc->dev, "HSPHY Interface not defined\n"); + + /* Relying on default value. */ + if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI)) + break; + } + /* FALLTHROUGH */ + case DWC3_GHWPARAMS3_HSPHY_IFC_ULPI: + /* Making sure the interface and PHY are operational */ + ret = dwc3_soft_reset(dwc); + if (ret) + return ret; + + udelay(1); + + ret = dwc3_ulpi_init(dwc); + if (ret) + return ret; + /* FALLTHROUGH */ + default: + break; + } + /* * Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to * '0' during coreConsultant configuration. So default value will @@ -427,7 +490,7 @@ static void dwc3_phy_setup(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); - mdelay(100); + return 0; } /** @@ -438,7 +501,6 @@ static void dwc3_phy_setup(struct dwc3 *dwc) */ static int dwc3_core_init(struct dwc3 *dwc) { - unsigned long timeout; u32 hwparams4 = dwc->hwparams.hwparams4; u32 reg; int ret; @@ -466,21 +528,9 @@ static int dwc3_core_init(struct dwc3 *dwc) } /* issue device SoftReset too */ - timeout = jiffies + msecs_to_jiffies(500); - dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST); - do { - reg = dwc3_readl(dwc->regs, DWC3_DCTL); - if (!(reg & DWC3_DCTL_CSFTRST)) - break; - - if (time_after(jiffies, timeout)) { - dev_err(dwc->dev, "Reset Timed Out\n"); - ret = -ETIMEDOUT; - goto err0; - } - - cpu_relax(); - } while (true); + ret = dwc3_soft_reset(dwc); + if (ret) + goto err0; ret = dwc3_core_soft_reset(dwc); if (ret) @@ -555,8 +605,6 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GCTL, reg); - dwc3_phy_setup(dwc); - ret = dwc3_alloc_scratch_buffers(dwc); if (ret) goto err1; @@ -836,6 +884,8 @@ static int dwc3_probe(struct platform_device *pdev) "snps,tx_de_emphasis_quirk"); of_property_read_u8(node, "snps,tx_de_emphasis", &tx_de_emphasis); + of_property_read_string(node, "snps,hsphy_interface", + &dwc->hsphy_interface); } else if (pdata) { dwc->maximum_speed = pdata->maximum_speed; dwc->has_lpm_erratum = pdata->has_lpm_erratum; @@ -863,6 +913,8 @@ static int dwc3_probe(struct platform_device *pdev) dwc->tx_de_emphasis_quirk = pdata->tx_de_emphasis_quirk; if (pdata->tx_de_emphasis) tx_de_emphasis = pdata->tx_de_emphasis; + + dwc->hsphy_interface = pdata->hsphy_interface; } /* default to superspeed if no maximum_speed passed */ @@ -875,12 +927,18 @@ static int dwc3_probe(struct platform_device *pdev) dwc->hird_threshold = hird_threshold | (dwc->is_utmi_l1_suspend << 4); + platform_set_drvdata(pdev, dwc); + dwc3_cache_hwparams(dwc); + + ret = dwc3_phy_setup(dwc); + if (ret) + goto err0; + ret = dwc3_core_get_phy(dwc); if (ret) goto err0; spin_lock_init(&dwc->lock); - platform_set_drvdata(pdev, dwc); if (!dev->dma_mask) { dev->dma_mask = dev->parent->dma_mask; @@ -892,8 +950,6 @@ static int dwc3_probe(struct platform_device *pdev) pm_runtime_get_sync(dev); pm_runtime_forbid(dev); - dwc3_cache_hwparams(dwc); - ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE); if (ret) { dev_err(dwc->dev, "failed to allocate event buffers\n"); @@ -964,6 +1020,7 @@ err2: err1: dwc3_free_event_buffers(dwc); + dwc3_ulpi_exit(dwc); err0: /* @@ -999,6 +1056,7 @@ static int dwc3_remove(struct platform_device *pdev) phy_power_off(dwc->usb3_generic_phy); dwc3_core_exit(dwc); + dwc3_ulpi_exit(dwc); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index fdab715a0631..d70da2041d19 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -30,6 +30,7 @@ #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/usb/otg.h> +#include <linux/ulpi/interface.h> #include <linux/phy/phy.h> @@ -173,6 +174,15 @@ /* Global USB2 PHY Configuration Register */ #define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31) #define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6) +#define DWC3_GUSB2PHYCFG_ULPI_UTMI (1 << 4) + +/* Global USB2 PHY Vendor Control Register */ +#define DWC3_GUSB2PHYACC_NEWREGREQ (1 << 25) +#define DWC3_GUSB2PHYACC_BUSY (1 << 23) +#define DWC3_GUSB2PHYACC_WRITE (1 << 22) +#define DWC3_GUSB2PHYACC_ADDR(n) (n << 16) +#define DWC3_GUSB2PHYACC_EXTEND_ADDR(n) (n << 8) +#define DWC3_GUSB2PHYACC_DATA(n) (n & 0xff) /* Global USB3 PIPE Control Register */ #define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31) @@ -652,6 +662,7 @@ struct dwc3_scratchpad_array { * @usb3_phy: pointer to USB3 PHY * @usb2_generic_phy: pointer to USB2 PHY * @usb3_generic_phy: pointer to USB3 PHY + * @ulpi: pointer to ulpi interface * @dcfg: saved contents of DCFG register * @gctl: saved contents of GCTL register * @isoch_delay: wValue from Set Isochronous Delay request; @@ -673,6 +684,7 @@ struct dwc3_scratchpad_array { * @test_mode_nr: test feature selector * @lpm_nyet_threshold: LPM NYET response threshold * @hird_threshold: HIRD threshold + * @hsphy_interface: "utmi" or "ulpi" * @delayed_status: true when gadget driver asks for delayed status * @ep0_bounced: true when we used bounce buffer * @ep0_expect_in: true when we expect a DATA IN transfer @@ -739,6 +751,8 @@ struct dwc3 { struct phy *usb2_generic_phy; struct phy *usb3_generic_phy; + struct ulpi *ulpi; + void __iomem *regs; size_t regs_size; @@ -800,6 +814,8 @@ struct dwc3 { u8 lpm_nyet_threshold; u8 hird_threshold; + const char *hsphy_interface; + unsigned delayed_status:1; unsigned ep0_bounced:1; unsigned ep0_expect_in:1; @@ -1035,4 +1051,14 @@ static inline int dwc3_gadget_resume(struct dwc3 *dwc) } #endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */ +#if IS_ENABLED(CONFIG_USB_DWC3_ULPI) +int dwc3_ulpi_init(struct dwc3 *dwc); +void dwc3_ulpi_exit(struct dwc3 *dwc); +#else +static inline int dwc3_ulpi_init(struct dwc3 *dwc) +{ return 0; } +static inline void dwc3_ulpi_exit(struct dwc3 *dwc) +{ } +#endif + #endif /* __DRIVERS_USB_DWC3_CORE_H */ diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index b773fb53d6a7..27e4fc896e9d 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -21,6 +21,8 @@ #include <linux/slab.h> #include <linux/pci.h> #include <linux/platform_device.h> +#include <linux/gpio/consumer.h> +#include <linux/acpi.h> #include "platform_data.h" @@ -31,6 +33,15 @@ #define PCI_DEVICE_ID_INTEL_SPTLP 0x9d30 #define PCI_DEVICE_ID_INTEL_SPTH 0xa130 +static const struct acpi_gpio_params reset_gpios = { 0, 0, false }; +static const struct acpi_gpio_params cs_gpios = { 1, 0, false }; + +static const struct acpi_gpio_mapping acpi_dwc3_byt_gpios[] = { + { "reset-gpios", &reset_gpios, 1 }, + { "cs-gpios", &cs_gpios, 1 }, + { }, +}; + static int dwc3_pci_quirks(struct pci_dev *pdev) { if (pdev->vendor == PCI_VENDOR_ID_AMD && @@ -65,6 +76,30 @@ static int dwc3_pci_quirks(struct pci_dev *pdev) sizeof(pdata)); } + if (pdev->vendor == PCI_VENDOR_ID_INTEL && + pdev->device == PCI_DEVICE_ID_INTEL_BYT) { + struct gpio_desc *gpio; + + acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev), + acpi_dwc3_byt_gpios); + + /* These GPIOs will turn on the USB2 PHY */ + gpio = gpiod_get(&pdev->dev, "cs"); + if (!IS_ERR(gpio)) { + gpiod_direction_output(gpio, 0); + gpiod_set_value_cansleep(gpio, 1); + gpiod_put(gpio); + } + + gpio = gpiod_get(&pdev->dev, "reset"); + if (!IS_ERR(gpio)) { + gpiod_direction_output(gpio, 0); + gpiod_set_value_cansleep(gpio, 1); + gpiod_put(gpio); + usleep_range(10000, 11000); + } + } + return 0; } @@ -128,6 +163,7 @@ err: static void dwc3_pci_remove(struct pci_dev *pci) { + acpi_dev_remove_driver_gpios(ACPI_COMPANION(&pci->dev)); platform_device_unregister(pci_get_drvdata(pci)); } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 8946c32047e9..333a7c0078fc 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -291,6 +291,8 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) dwc3_trace(trace_dwc3_gadget, "Command Complete --> %d", DWC3_DGCMD_STATUS(reg)); + if (DWC3_DGCMD_STATUS(reg)) + return -EINVAL; return 0; } @@ -328,6 +330,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, dwc3_trace(trace_dwc3_gadget, "Command Complete --> %d", DWC3_DEPCMD_STATUS(reg)); + if (DWC3_DEPCMD_STATUS(reg)) + return -EINVAL; return 0; } @@ -1902,12 +1906,16 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, { unsigned status = 0; int clean_busy; + u32 is_xfer_complete; + + is_xfer_complete = (event->endpoint_event == DWC3_DEPEVT_XFERCOMPLETE); if (event->status & DEPEVT_STATUS_BUSERR) status = -ECONNRESET; clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status); - if (clean_busy) + if (clean_busy && (is_xfer_complete || + usb_endpoint_xfer_isoc(dep->endpoint.desc))) dep->flags &= ~DWC3_EP_BUSY; /* diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h index a2bd464be828..d3614ecbb9ca 100644 --- a/drivers/usb/dwc3/platform_data.h +++ b/drivers/usb/dwc3/platform_data.h @@ -45,4 +45,6 @@ struct dwc3_platform_data { unsigned tx_de_emphasis_quirk:1; unsigned tx_de_emphasis:2; + + const char *hsphy_interface; }; diff --git a/drivers/usb/dwc3/ulpi.c b/drivers/usb/dwc3/ulpi.c new file mode 100644 index 000000000000..ec004c6d76f2 --- /dev/null +++ b/drivers/usb/dwc3/ulpi.c @@ -0,0 +1,91 @@ +/** + * ulpi.c - DesignWare USB3 Controller's ULPI PHY interface + * + * Copyright (C) 2015 Intel Corporation + * + * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/ulpi/regs.h> + +#include "core.h" +#include "io.h" + +#define DWC3_ULPI_ADDR(a) \ + ((a >= ULPI_EXT_VENDOR_SPECIFIC) ? \ + DWC3_GUSB2PHYACC_ADDR(ULPI_ACCESS_EXTENDED) | \ + DWC3_GUSB2PHYACC_EXTEND_ADDR(a) : DWC3_GUSB2PHYACC_ADDR(a)) + +static int dwc3_ulpi_busyloop(struct dwc3 *dwc) +{ + unsigned count = 1000; + u32 reg; + + while (count--) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0)); + if (!(reg & DWC3_GUSB2PHYACC_BUSY)) + return 0; + cpu_relax(); + } + + return -ETIMEDOUT; +} + +static int dwc3_ulpi_read(struct ulpi_ops *ops, u8 addr) +{ + struct dwc3 *dwc = dev_get_drvdata(ops->dev); + u32 reg; + int ret; + + reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr); + dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg); + + ret = dwc3_ulpi_busyloop(dwc); + if (ret) + return ret; + + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0)); + + return DWC3_GUSB2PHYACC_DATA(reg); +} + +static int dwc3_ulpi_write(struct ulpi_ops *ops, u8 addr, u8 val) +{ + struct dwc3 *dwc = dev_get_drvdata(ops->dev); + u32 reg; + + reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr); + reg |= DWC3_GUSB2PHYACC_WRITE | val; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg); + + return dwc3_ulpi_busyloop(dwc); +} + +static struct ulpi_ops dwc3_ulpi_ops = { + .read = dwc3_ulpi_read, + .write = dwc3_ulpi_write, +}; + +int dwc3_ulpi_init(struct dwc3 *dwc) +{ + /* Register the interface */ + dwc->ulpi = ulpi_register_interface(dwc->dev, &dwc3_ulpi_ops); + if (IS_ERR(dwc->ulpi)) { + dev_err(dwc->dev, "failed to register ULPI interface"); + return PTR_ERR(dwc->ulpi); + } + + return 0; +} + +void dwc3_ulpi_exit(struct dwc3 *dwc) +{ + if (dwc->ulpi) { + ulpi_unregister_interface(dwc->ulpi); + dwc->ulpi = NULL; + } +} diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 0567cca1465e..919cdfdda78b 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -258,15 +258,25 @@ struct usb_ep *usb_ep_autoconfig_ss( /* First, apply chip-specific "best usage" knowledge. * This might make a good usb_gadget_ops hook ... */ - if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) { - /* ep-e, ep-f are PIO with only 64 byte fifos */ - ep = find_ep (gadget, "ep-e"); - if (ep && ep_matches(gadget, ep, desc, ep_comp)) - goto found_ep; - ep = find_ep (gadget, "ep-f"); + if (gadget_is_net2280(gadget)) { + char name[8]; + + if (type == USB_ENDPOINT_XFER_INT) { + /* ep-e, ep-f are PIO with only 64 byte fifos */ + ep = find_ep(gadget, "ep-e"); + if (ep && ep_matches(gadget, ep, desc, ep_comp)) + goto found_ep; + ep = find_ep(gadget, "ep-f"); + if (ep && ep_matches(gadget, ep, desc, ep_comp)) + goto found_ep; + } + + /* USB3380: use same address for usb and hardware endpoints */ + snprintf(name, sizeof(name), "ep%d%s", usb_endpoint_num(desc), + usb_endpoint_dir_in(desc) ? "in" : "out"); + ep = find_ep(gadget, name); if (ep && ep_matches(gadget, ep, desc, ep_comp)) goto found_ep; - } else if (gadget_is_goku (gadget)) { if (USB_ENDPOINT_XFER_INT == type) { /* single buffering is enough */ diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 6bdb57069044..e455eeee6444 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -3433,6 +3433,7 @@ done: static void ffs_closed(struct ffs_data *ffs) { struct ffs_dev *ffs_obj; + struct f_fs_opts *opts; ENTER(); ffs_dev_lock(); @@ -3446,8 +3447,13 @@ static void ffs_closed(struct ffs_data *ffs) if (ffs_obj->ffs_closed_callback) ffs_obj->ffs_closed_callback(ffs); - if (!ffs_obj->opts || ffs_obj->opts->no_configfs - || !ffs_obj->opts->func_inst.group.cg_item.ci_parent) + if (ffs_obj->opts) + opts = ffs_obj->opts; + else + goto done; + + if (opts->no_configfs || !opts->func_inst.group.cg_item.ci_parent + || !atomic_read(&opts->func_inst.group.cg_item.ci_kref.refcount)) goto done; unregister_gadget_item(ffs_obj->opts-> diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 829edf878dac..32985dade838 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -76,7 +76,7 @@ struct f_rndis { u8 ethaddr[ETH_ALEN]; u32 vendorID; const char *manufacturer; - int config; + struct rndis_params *params; struct usb_ep *notify; struct usb_request *notify_req; @@ -453,7 +453,7 @@ static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req) /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ // spin_lock(&dev->lock); - status = rndis_msg_parser(rndis->config, (u8 *) req->buf); + status = rndis_msg_parser(rndis->params, (u8 *) req->buf); if (status < 0) pr_err("RNDIS command error %d, %d/%d\n", status, req->actual, req->length); @@ -499,12 +499,12 @@ rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) u32 n; /* return the result */ - buf = rndis_get_next_response(rndis->config, &n); + buf = rndis_get_next_response(rndis->params, &n); if (buf) { memcpy(req->buf, buf, n); req->complete = rndis_response_complete; req->context = rndis; - rndis_free_response(rndis->config, buf); + rndis_free_response(rndis->params, buf); value = n; } /* else stalls ... spec says to avoid that */ @@ -597,7 +597,7 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (IS_ERR(net)) return PTR_ERR(net); - rndis_set_param_dev(rndis->config, net, + rndis_set_param_dev(rndis->params, net, &rndis->port.cdc_filter); } else goto fail; @@ -617,7 +617,7 @@ static void rndis_disable(struct usb_function *f) DBG(cdev, "rndis deactivated\n"); - rndis_uninit(rndis->config); + rndis_uninit(rndis->params); gether_disconnect(&rndis->port); usb_ep_disable(rndis->notify); @@ -640,9 +640,9 @@ static void rndis_open(struct gether *geth) DBG(cdev, "%s\n", __func__); - rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, + rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, bitrate(cdev->gadget) / 100); - rndis_signal_connect(rndis->config); + rndis_signal_connect(rndis->params); } static void rndis_close(struct gether *geth) @@ -651,8 +651,8 @@ static void rndis_close(struct gether *geth) DBG(geth->func.config->cdev, "%s\n", __func__); - rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0); - rndis_signal_disconnect(rndis->config); + rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0); + rndis_signal_disconnect(rndis->params); } /*-------------------------------------------------------------------------*/ @@ -796,11 +796,11 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis->port.open = rndis_open; rndis->port.close = rndis_close; - rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0); - rndis_set_host_mac(rndis->config, rndis->ethaddr); + rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0); + rndis_set_host_mac(rndis->params, rndis->ethaddr); if (rndis->manufacturer && rndis->vendorID && - rndis_set_param_vendor(rndis->config, rndis->vendorID, + rndis_set_param_vendor(rndis->params, rndis->vendorID, rndis->manufacturer)) { status = -EINVAL; goto fail_free_descs; @@ -944,7 +944,7 @@ static void rndis_free(struct usb_function *f) struct f_rndis_opts *opts; rndis = func_to_rndis(f); - rndis_deregister(rndis->config); + rndis_deregister(rndis->params); opts = container_of(f->fi, struct f_rndis_opts, func_inst); kfree(rndis); mutex_lock(&opts->lock); @@ -968,7 +968,7 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi) { struct f_rndis *rndis; struct f_rndis_opts *opts; - int status; + struct rndis_params *params; /* allocate and initialize one new instance */ rndis = kzalloc(sizeof(*rndis), GFP_KERNEL); @@ -1002,36 +1002,16 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi) rndis->port.func.disable = rndis_disable; rndis->port.func.free_func = rndis_free; - status = rndis_register(rndis_response_available, rndis); - if (status < 0) { + params = rndis_register(rndis_response_available, rndis); + if (IS_ERR(params)) { kfree(rndis); - return ERR_PTR(status); + return ERR_CAST(params); } - rndis->config = status; + rndis->params = params; return &rndis->port.func; } -DECLARE_USB_FUNCTION(rndis, rndis_alloc_inst, rndis_alloc); - -static int __init rndis_mod_init(void) -{ - int ret; - - ret = rndis_init(); - if (ret) - return ret; - - return usb_function_register(&rndisusb_func); -} -module_init(rndis_mod_init); - -static void __exit rndis_mod_exit(void) -{ - usb_function_unregister(&rndisusb_func); - rndis_exit(); -} -module_exit(rndis_mod_exit); - +DECLARE_USB_FUNCTION_INIT(rndis, rndis_alloc_inst, rndis_alloc); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Brownell"); diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index 95d2324f6977..70d3917cc003 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -25,6 +25,7 @@ #include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/errno.h> +#include <linux/idr.h> #include <linux/list.h> #include <linux/proc_fs.h> #include <linux/slab.h> @@ -57,17 +58,26 @@ MODULE_PARM_DESC (rndis_debug, "enable debugging"); #define rndis_debug 0 #endif -#define RNDIS_MAX_CONFIGS 1 +#ifdef CONFIG_USB_GADGET_DEBUG_FILES +#define NAME_TEMPLATE "driver/rndis-%03d" -static rndis_params rndis_per_dev_params[RNDIS_MAX_CONFIGS]; +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ + +static DEFINE_IDA(rndis_ida); /* Driver Version */ static const __le32 rndis_driver_version = cpu_to_le32(1); /* Function Prototypes */ -static rndis_resp_t *rndis_add_response(int configNr, u32 length); +static rndis_resp_t *rndis_add_response(struct rndis_params *params, + u32 length); + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + +static const struct file_operations rndis_proc_fops; +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ /* supported OIDs */ static const u32 oid_supported_list[] = @@ -160,7 +170,7 @@ static const u32 oid_supported_list[] = /* NDIS Functions */ -static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, +static int gen_ndis_query_resp(struct rndis_params *params, u32 OID, u8 *buf, unsigned buf_len, rndis_resp_t *r) { int retval = -ENOTSUPP; @@ -192,7 +202,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, outbuf = (__le32 *)&resp[1]; resp->InformationBufferOffset = cpu_to_le32(16); - net = rndis_per_dev_params[configNr].dev; + net = params->dev; stats = dev_get_stats(net, &temp); switch (OID) { @@ -225,7 +235,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_GEN_MEDIA_SUPPORTED: pr_debug("%s: RNDIS_OID_GEN_MEDIA_SUPPORTED\n", __func__); - *outbuf = cpu_to_le32(rndis_per_dev_params[configNr].medium); + *outbuf = cpu_to_le32(params->medium); retval = 0; break; @@ -233,16 +243,15 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, case RNDIS_OID_GEN_MEDIA_IN_USE: pr_debug("%s: RNDIS_OID_GEN_MEDIA_IN_USE\n", __func__); /* one medium, one transport... (maybe you do it better) */ - *outbuf = cpu_to_le32(rndis_per_dev_params[configNr].medium); + *outbuf = cpu_to_le32(params->medium); retval = 0; break; /* mandatory */ case RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE: pr_debug("%s: RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE\n", __func__); - if (rndis_per_dev_params[configNr].dev) { - *outbuf = cpu_to_le32( - rndis_per_dev_params[configNr].dev->mtu); + if (params->dev) { + *outbuf = cpu_to_le32(params->dev->mtu); retval = 0; } break; @@ -251,21 +260,18 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, case RNDIS_OID_GEN_LINK_SPEED: if (rndis_debug > 1) pr_debug("%s: RNDIS_OID_GEN_LINK_SPEED\n", __func__); - if (rndis_per_dev_params[configNr].media_state - == RNDIS_MEDIA_STATE_DISCONNECTED) + if (params->media_state == RNDIS_MEDIA_STATE_DISCONNECTED) *outbuf = cpu_to_le32(0); else - *outbuf = cpu_to_le32( - rndis_per_dev_params[configNr].speed); + *outbuf = cpu_to_le32(params->speed); retval = 0; break; /* mandatory */ case RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE: pr_debug("%s: RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE\n", __func__); - if (rndis_per_dev_params[configNr].dev) { - *outbuf = cpu_to_le32( - rndis_per_dev_params[configNr].dev->mtu); + if (params->dev) { + *outbuf = cpu_to_le32(params->dev->mtu); retval = 0; } break; @@ -273,9 +279,8 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE: pr_debug("%s: RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE\n", __func__); - if (rndis_per_dev_params[configNr].dev) { - *outbuf = cpu_to_le32( - rndis_per_dev_params[configNr].dev->mtu); + if (params->dev) { + *outbuf = cpu_to_le32(params->dev->mtu); retval = 0; } break; @@ -283,20 +288,16 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_GEN_VENDOR_ID: pr_debug("%s: RNDIS_OID_GEN_VENDOR_ID\n", __func__); - *outbuf = cpu_to_le32( - rndis_per_dev_params[configNr].vendorID); + *outbuf = cpu_to_le32(params->vendorID); retval = 0; break; /* mandatory */ case RNDIS_OID_GEN_VENDOR_DESCRIPTION: pr_debug("%s: RNDIS_OID_GEN_VENDOR_DESCRIPTION\n", __func__); - if (rndis_per_dev_params[configNr].vendorDescr) { - length = strlen(rndis_per_dev_params[configNr]. - vendorDescr); - memcpy(outbuf, - rndis_per_dev_params[configNr].vendorDescr, - length); + if (params->vendorDescr) { + length = strlen(params->vendorDescr); + memcpy(outbuf, params->vendorDescr, length); } else { outbuf[0] = 0; } @@ -313,7 +314,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_GEN_CURRENT_PACKET_FILTER: pr_debug("%s: RNDIS_OID_GEN_CURRENT_PACKET_FILTER\n", __func__); - *outbuf = cpu_to_le32(*rndis_per_dev_params[configNr].filter); + *outbuf = cpu_to_le32(*params->filter); retval = 0; break; @@ -328,8 +329,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, case RNDIS_OID_GEN_MEDIA_CONNECT_STATUS: if (rndis_debug > 1) pr_debug("%s: RNDIS_OID_GEN_MEDIA_CONNECT_STATUS\n", __func__); - *outbuf = cpu_to_le32(rndis_per_dev_params[configNr] - .media_state); + *outbuf = cpu_to_le32(params->media_state); retval = 0; break; @@ -409,11 +409,9 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_802_3_PERMANENT_ADDRESS: pr_debug("%s: RNDIS_OID_802_3_PERMANENT_ADDRESS\n", __func__); - if (rndis_per_dev_params[configNr].dev) { + if (params->dev) { length = ETH_ALEN; - memcpy(outbuf, - rndis_per_dev_params[configNr].host_mac, - length); + memcpy(outbuf, params->host_mac, length); retval = 0; } break; @@ -421,11 +419,9 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_802_3_CURRENT_ADDRESS: pr_debug("%s: RNDIS_OID_802_3_CURRENT_ADDRESS\n", __func__); - if (rndis_per_dev_params[configNr].dev) { + if (params->dev) { length = ETH_ALEN; - memcpy(outbuf, - rndis_per_dev_params [configNr].host_mac, - length); + memcpy(outbuf, params->host_mac, length); retval = 0; } break; @@ -490,12 +486,11 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, return retval; } -static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len, - rndis_resp_t *r) +static int gen_ndis_set_resp(struct rndis_params *params, u32 OID, + u8 *buf, u32 buf_len, rndis_resp_t *r) { rndis_set_cmplt_type *resp; int i, retval = -ENOTSUPP; - struct rndis_params *params; if (!r) return -ENOMEM; @@ -514,7 +509,6 @@ static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len, } } - params = &rndis_per_dev_params[configNr]; switch (OID) { case RNDIS_OID_GEN_CURRENT_PACKET_FILTER: @@ -563,16 +557,16 @@ static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len, * Response Functions */ -static int rndis_init_response(int configNr, rndis_init_msg_type *buf) +static int rndis_init_response(struct rndis_params *params, + rndis_init_msg_type *buf) { rndis_init_cmplt_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; if (!params->dev) return -ENOTSUPP; - r = rndis_add_response(configNr, sizeof(rndis_init_cmplt_type)); + r = rndis_add_response(params, sizeof(rndis_init_cmplt_type)); if (!r) return -ENOMEM; resp = (rndis_init_cmplt_type *)r->buf; @@ -599,11 +593,11 @@ static int rndis_init_response(int configNr, rndis_init_msg_type *buf) return 0; } -static int rndis_query_response(int configNr, rndis_query_msg_type *buf) +static int rndis_query_response(struct rndis_params *params, + rndis_query_msg_type *buf) { rndis_query_cmplt_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; /* pr_debug("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID)); */ if (!params->dev) @@ -615,7 +609,7 @@ static int rndis_query_response(int configNr, rndis_query_msg_type *buf) * rndis_query_cmplt_type followed by data. * oid_supported_list is the largest data reply */ - r = rndis_add_response(configNr, + r = rndis_add_response(params, sizeof(oid_supported_list) + sizeof(rndis_query_cmplt_type)); if (!r) return -ENOMEM; @@ -624,7 +618,7 @@ static int rndis_query_response(int configNr, rndis_query_msg_type *buf) resp->MessageType = cpu_to_le32(RNDIS_MSG_QUERY_C); resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - if (gen_ndis_query_resp(configNr, le32_to_cpu(buf->OID), + if (gen_ndis_query_resp(params, le32_to_cpu(buf->OID), le32_to_cpu(buf->InformationBufferOffset) + 8 + (u8 *)buf, le32_to_cpu(buf->InformationBufferLength), @@ -641,14 +635,14 @@ static int rndis_query_response(int configNr, rndis_query_msg_type *buf) return 0; } -static int rndis_set_response(int configNr, rndis_set_msg_type *buf) +static int rndis_set_response(struct rndis_params *params, + rndis_set_msg_type *buf) { u32 BufLength, BufOffset; rndis_set_cmplt_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; - r = rndis_add_response(configNr, sizeof(rndis_set_cmplt_type)); + r = rndis_add_response(params, sizeof(rndis_set_cmplt_type)); if (!r) return -ENOMEM; resp = (rndis_set_cmplt_type *)r->buf; @@ -671,7 +665,7 @@ static int rndis_set_response(int configNr, rndis_set_msg_type *buf) resp->MessageType = cpu_to_le32(RNDIS_MSG_SET_C); resp->MessageLength = cpu_to_le32(16); resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - if (gen_ndis_set_resp(configNr, le32_to_cpu(buf->OID), + if (gen_ndis_set_resp(params, le32_to_cpu(buf->OID), ((u8 *)buf) + 8 + BufOffset, BufLength, r)) resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED); else @@ -681,13 +675,13 @@ static int rndis_set_response(int configNr, rndis_set_msg_type *buf) return 0; } -static int rndis_reset_response(int configNr, rndis_reset_msg_type *buf) +static int rndis_reset_response(struct rndis_params *params, + rndis_reset_msg_type *buf) { rndis_reset_cmplt_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; - r = rndis_add_response(configNr, sizeof(rndis_reset_cmplt_type)); + r = rndis_add_response(params, sizeof(rndis_reset_cmplt_type)); if (!r) return -ENOMEM; resp = (rndis_reset_cmplt_type *)r->buf; @@ -702,16 +696,15 @@ static int rndis_reset_response(int configNr, rndis_reset_msg_type *buf) return 0; } -static int rndis_keepalive_response(int configNr, +static int rndis_keepalive_response(struct rndis_params *params, rndis_keepalive_msg_type *buf) { rndis_keepalive_cmplt_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; /* host "should" check only in RNDIS_DATA_INITIALIZED state */ - r = rndis_add_response(configNr, sizeof(rndis_keepalive_cmplt_type)); + r = rndis_add_response(params, sizeof(rndis_keepalive_cmplt_type)); if (!r) return -ENOMEM; resp = (rndis_keepalive_cmplt_type *)r->buf; @@ -729,17 +722,15 @@ static int rndis_keepalive_response(int configNr, /* * Device to Host Comunication */ -static int rndis_indicate_status_msg(int configNr, u32 status) +static int rndis_indicate_status_msg(struct rndis_params *params, u32 status) { rndis_indicate_status_msg_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; if (params->state == RNDIS_UNINITIALIZED) return -ENOTSUPP; - r = rndis_add_response(configNr, - sizeof(rndis_indicate_status_msg_type)); + r = rndis_add_response(params, sizeof(rndis_indicate_status_msg_type)); if (!r) return -ENOMEM; resp = (rndis_indicate_status_msg_type *)r->buf; @@ -754,53 +745,48 @@ static int rndis_indicate_status_msg(int configNr, u32 status) return 0; } -int rndis_signal_connect(int configNr) +int rndis_signal_connect(struct rndis_params *params) { - rndis_per_dev_params[configNr].media_state - = RNDIS_MEDIA_STATE_CONNECTED; - return rndis_indicate_status_msg(configNr, - RNDIS_STATUS_MEDIA_CONNECT); + params->media_state = RNDIS_MEDIA_STATE_CONNECTED; + return rndis_indicate_status_msg(params, RNDIS_STATUS_MEDIA_CONNECT); } EXPORT_SYMBOL_GPL(rndis_signal_connect); -int rndis_signal_disconnect(int configNr) +int rndis_signal_disconnect(struct rndis_params *params) { - rndis_per_dev_params[configNr].media_state - = RNDIS_MEDIA_STATE_DISCONNECTED; - return rndis_indicate_status_msg(configNr, - RNDIS_STATUS_MEDIA_DISCONNECT); + params->media_state = RNDIS_MEDIA_STATE_DISCONNECTED; + return rndis_indicate_status_msg(params, RNDIS_STATUS_MEDIA_DISCONNECT); } EXPORT_SYMBOL_GPL(rndis_signal_disconnect); -void rndis_uninit(int configNr) +void rndis_uninit(struct rndis_params *params) { u8 *buf; u32 length; - if (configNr >= RNDIS_MAX_CONFIGS) + if (!params) return; - rndis_per_dev_params[configNr].state = RNDIS_UNINITIALIZED; + params->state = RNDIS_UNINITIALIZED; /* drain the response queue */ - while ((buf = rndis_get_next_response(configNr, &length))) - rndis_free_response(configNr, buf); + while ((buf = rndis_get_next_response(params, &length))) + rndis_free_response(params, buf); } EXPORT_SYMBOL_GPL(rndis_uninit); -void rndis_set_host_mac(int configNr, const u8 *addr) +void rndis_set_host_mac(struct rndis_params *params, const u8 *addr) { - rndis_per_dev_params[configNr].host_mac = addr; + params->host_mac = addr; } EXPORT_SYMBOL_GPL(rndis_set_host_mac); /* * Message Parser */ -int rndis_msg_parser(u8 configNr, u8 *buf) +int rndis_msg_parser(struct rndis_params *params, u8 *buf) { u32 MsgType, MsgLength; __le32 *tmp; - struct rndis_params *params; if (!buf) return -ENOMEM; @@ -809,9 +795,8 @@ int rndis_msg_parser(u8 configNr, u8 *buf) MsgType = get_unaligned_le32(tmp++); MsgLength = get_unaligned_le32(tmp++); - if (configNr >= RNDIS_MAX_CONFIGS) + if (!params) return -ENOTSUPP; - params = &rndis_per_dev_params[configNr]; /* NOTE: RNDIS is *EXTREMELY* chatty ... Windows constantly polls for * rx/tx statistics and link status, in addition to KEEPALIVE traffic @@ -824,8 +809,7 @@ int rndis_msg_parser(u8 configNr, u8 *buf) pr_debug("%s: RNDIS_MSG_INIT\n", __func__); params->state = RNDIS_INITIALIZED; - return rndis_init_response(configNr, - (rndis_init_msg_type *)buf); + return rndis_init_response(params, (rndis_init_msg_type *)buf); case RNDIS_MSG_HALT: pr_debug("%s: RNDIS_MSG_HALT\n", @@ -838,17 +822,16 @@ int rndis_msg_parser(u8 configNr, u8 *buf) return 0; case RNDIS_MSG_QUERY: - return rndis_query_response(configNr, + return rndis_query_response(params, (rndis_query_msg_type *)buf); case RNDIS_MSG_SET: - return rndis_set_response(configNr, - (rndis_set_msg_type *)buf); + return rndis_set_response(params, (rndis_set_msg_type *)buf); case RNDIS_MSG_RESET: pr_debug("%s: RNDIS_MSG_RESET\n", __func__); - return rndis_reset_response(configNr, + return rndis_reset_response(params, (rndis_reset_msg_type *)buf); case RNDIS_MSG_KEEPALIVE: @@ -856,7 +839,7 @@ int rndis_msg_parser(u8 configNr, u8 *buf) if (rndis_debug > 1) pr_debug("%s: RNDIS_MSG_KEEPALIVE\n", __func__); - return rndis_keepalive_response(configNr, + return rndis_keepalive_response(params, (rndis_keepalive_msg_type *) buf); @@ -876,71 +859,131 @@ int rndis_msg_parser(u8 configNr, u8 *buf) } EXPORT_SYMBOL_GPL(rndis_msg_parser); -int rndis_register(void (*resp_avail)(void *v), void *v) +static inline int rndis_get_nr(void) { - u8 i; + return ida_simple_get(&rndis_ida, 0, 0, GFP_KERNEL); +} + +static inline void rndis_put_nr(int nr) +{ + ida_simple_remove(&rndis_ida, nr); +} + +struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v) +{ + struct rndis_params *params; + int i; if (!resp_avail) - return -EINVAL; + return ERR_PTR(-EINVAL); + + i = rndis_get_nr(); + if (i < 0) { + pr_debug("failed\n"); + + return ERR_PTR(-ENODEV); + } + + params = kzalloc(sizeof(*params), GFP_KERNEL); + if (!params) { + rndis_put_nr(i); - for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { - if (!rndis_per_dev_params[i].used) { - rndis_per_dev_params[i].used = 1; - rndis_per_dev_params[i].resp_avail = resp_avail; - rndis_per_dev_params[i].v = v; - pr_debug("%s: configNr = %d\n", __func__, i); - return i; + return ERR_PTR(-ENOMEM); + } + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + { + struct proc_dir_entry *proc_entry; + char name[20]; + + sprintf(name, NAME_TEMPLATE, i); + proc_entry = proc_create_data(name, 0660, NULL, + &rndis_proc_fops, params); + if (!proc_entry) { + kfree(params); + rndis_put_nr(i); + + return ERR_PTR(-EIO); } } - pr_debug("failed\n"); +#endif + + params->confignr = i; + params->used = 1; + params->state = RNDIS_UNINITIALIZED; + params->media_state = RNDIS_MEDIA_STATE_DISCONNECTED; + params->resp_avail = resp_avail; + params->v = v; + INIT_LIST_HEAD(&(params->resp_queue)); + pr_debug("%s: configNr = %d\n", __func__, i); - return -ENODEV; + return params; } EXPORT_SYMBOL_GPL(rndis_register); -void rndis_deregister(int configNr) +void rndis_deregister(struct rndis_params *params) { + int i; + pr_debug("%s:\n", __func__); - if (configNr >= RNDIS_MAX_CONFIGS) return; - rndis_per_dev_params[configNr].used = 0; + if (!params) + return; + + i = params->confignr; + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + { + char name[20]; + + sprintf(name, NAME_TEMPLATE, i); + remove_proc_entry(name, NULL); + } +#endif + + kfree(params); + rndis_put_nr(i); } EXPORT_SYMBOL_GPL(rndis_deregister); - -int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter) +int rndis_set_param_dev(struct rndis_params *params, struct net_device *dev, + u16 *cdc_filter) { pr_debug("%s:\n", __func__); if (!dev) return -EINVAL; - if (configNr >= RNDIS_MAX_CONFIGS) return -1; + if (!params) + return -1; - rndis_per_dev_params[configNr].dev = dev; - rndis_per_dev_params[configNr].filter = cdc_filter; + params->dev = dev; + params->filter = cdc_filter; return 0; } EXPORT_SYMBOL_GPL(rndis_set_param_dev); -int rndis_set_param_vendor(u8 configNr, u32 vendorID, const char *vendorDescr) +int rndis_set_param_vendor(struct rndis_params *params, u32 vendorID, + const char *vendorDescr) { pr_debug("%s:\n", __func__); if (!vendorDescr) return -1; - if (configNr >= RNDIS_MAX_CONFIGS) return -1; + if (!params) + return -1; - rndis_per_dev_params[configNr].vendorID = vendorID; - rndis_per_dev_params[configNr].vendorDescr = vendorDescr; + params->vendorID = vendorID; + params->vendorDescr = vendorDescr; return 0; } EXPORT_SYMBOL_GPL(rndis_set_param_vendor); -int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed) +int rndis_set_param_medium(struct rndis_params *params, u32 medium, u32 speed) { pr_debug("%s: %u %u\n", __func__, medium, speed); - if (configNr >= RNDIS_MAX_CONFIGS) return -1; + if (!params) + return -1; - rndis_per_dev_params[configNr].medium = medium; - rndis_per_dev_params[configNr].speed = speed; + params->medium = medium; + params->speed = speed; return 0; } @@ -961,13 +1004,12 @@ void rndis_add_hdr(struct sk_buff *skb) } EXPORT_SYMBOL_GPL(rndis_add_hdr); -void rndis_free_response(int configNr, u8 *buf) +void rndis_free_response(struct rndis_params *params, u8 *buf) { rndis_resp_t *r; struct list_head *act, *tmp; - list_for_each_safe(act, tmp, - &(rndis_per_dev_params[configNr].resp_queue)) + list_for_each_safe(act, tmp, &(params->resp_queue)) { r = list_entry(act, rndis_resp_t, list); if (r && r->buf == buf) { @@ -978,15 +1020,14 @@ void rndis_free_response(int configNr, u8 *buf) } EXPORT_SYMBOL_GPL(rndis_free_response); -u8 *rndis_get_next_response(int configNr, u32 *length) +u8 *rndis_get_next_response(struct rndis_params *params, u32 *length) { rndis_resp_t *r; struct list_head *act, *tmp; if (!length) return NULL; - list_for_each_safe(act, tmp, - &(rndis_per_dev_params[configNr].resp_queue)) + list_for_each_safe(act, tmp, &(params->resp_queue)) { r = list_entry(act, rndis_resp_t, list); if (!r->send) { @@ -1000,7 +1041,7 @@ u8 *rndis_get_next_response(int configNr, u32 *length) } EXPORT_SYMBOL_GPL(rndis_get_next_response); -static rndis_resp_t *rndis_add_response(int configNr, u32 length) +static rndis_resp_t *rndis_add_response(struct rndis_params *params, u32 length) { rndis_resp_t *r; @@ -1012,8 +1053,7 @@ static rndis_resp_t *rndis_add_response(int configNr, u32 length) r->length = length; r->send = 0; - list_add_tail(&r->list, - &(rndis_per_dev_params[configNr].resp_queue)); + list_add_tail(&r->list, &(params->resp_queue)); return r; } @@ -1103,11 +1143,11 @@ static ssize_t rndis_proc_write(struct file *file, const char __user *buffer, break; case 'C': case 'c': - rndis_signal_connect(p->confignr); + rndis_signal_connect(p); break; case 'D': case 'd': - rndis_signal_disconnect(p->confignr); + rndis_signal_disconnect(p); break; default: if (fl_speed) p->speed = speed; @@ -1137,54 +1177,4 @@ static const struct file_operations rndis_proc_fops = { #define NAME_TEMPLATE "driver/rndis-%03d" -static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; - #endif /* CONFIG_USB_GADGET_DEBUG_FILES */ - - -int rndis_init(void) -{ - u8 i; - - for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { -#ifdef CONFIG_USB_GADGET_DEBUG_FILES - char name [20]; - - sprintf(name, NAME_TEMPLATE, i); - rndis_connect_state[i] = proc_create_data(name, 0660, NULL, - &rndis_proc_fops, - (void *)(rndis_per_dev_params + i)); - if (!rndis_connect_state[i]) { - pr_debug("%s: remove entries", __func__); - while (i) { - sprintf(name, NAME_TEMPLATE, --i); - remove_proc_entry(name, NULL); - } - pr_debug("\n"); - return -EIO; - } -#endif - rndis_per_dev_params[i].confignr = i; - rndis_per_dev_params[i].used = 0; - rndis_per_dev_params[i].state = RNDIS_UNINITIALIZED; - rndis_per_dev_params[i].media_state - = RNDIS_MEDIA_STATE_DISCONNECTED; - INIT_LIST_HEAD(&(rndis_per_dev_params[i].resp_queue)); - } - - return 0; -} - -void rndis_exit(void) -{ -#ifdef CONFIG_USB_GADGET_DEBUG_FILES - u8 i; - char name[20]; - - for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { - sprintf(name, NAME_TEMPLATE, i); - remove_proc_entry(name, NULL); - } -#endif -} - diff --git a/drivers/usb/gadget/function/rndis.h b/drivers/usb/gadget/function/rndis.h index 0f4abb4c3775..ef92eb66d8ad 100644 --- a/drivers/usb/gadget/function/rndis.h +++ b/drivers/usb/gadget/function/rndis.h @@ -177,7 +177,7 @@ typedef struct rndis_resp_t typedef struct rndis_params { - u8 confignr; + int confignr; u8 used; u16 saved_filter; enum rndis_state state; @@ -197,24 +197,25 @@ typedef struct rndis_params } rndis_params; /* RNDIS Message parser and other useless functions */ -int rndis_msg_parser (u8 configNr, u8 *buf); -int rndis_register(void (*resp_avail)(void *v), void *v); -void rndis_deregister (int configNr); -int rndis_set_param_dev (u8 configNr, struct net_device *dev, +int rndis_msg_parser(struct rndis_params *params, u8 *buf); +struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v); +void rndis_deregister(struct rndis_params *params); +int rndis_set_param_dev(struct rndis_params *params, struct net_device *dev, u16 *cdc_filter); -int rndis_set_param_vendor (u8 configNr, u32 vendorID, +int rndis_set_param_vendor(struct rndis_params *params, u32 vendorID, const char *vendorDescr); -int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed); -void rndis_add_hdr (struct sk_buff *skb); +int rndis_set_param_medium(struct rndis_params *params, u32 medium, + u32 speed); +void rndis_add_hdr(struct sk_buff *skb); int rndis_rm_hdr(struct gether *port, struct sk_buff *skb, struct sk_buff_head *list); -u8 *rndis_get_next_response (int configNr, u32 *length); -void rndis_free_response (int configNr, u8 *buf); - -void rndis_uninit (int configNr); -int rndis_signal_connect (int configNr); -int rndis_signal_disconnect (int configNr); -int rndis_state (int configNr); -extern void rndis_set_host_mac (int configNr, const u8 *addr); +u8 *rndis_get_next_response(struct rndis_params *params, u32 *length); +void rndis_free_response(struct rndis_params *params, u8 *buf); + +void rndis_uninit(struct rndis_params *params); +int rndis_signal_connect(struct rndis_params *params); +int rndis_signal_disconnect(struct rndis_params *params); +int rndis_state(struct rndis_params *params); +extern void rndis_set_host_mac(struct rndis_params *params, const u8 *addr); #endif /* _LINUX_RNDIS_H */ diff --git a/drivers/usb/gadget/function/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h index e902aa42a297..4eafd5050545 100644 --- a/drivers/usb/gadget/function/u_rndis.h +++ b/drivers/usb/gadget/function/u_rndis.h @@ -39,8 +39,6 @@ struct f_rndis_opts { int refcnt; }; -int rndis_init(void); -void rndis_exit(void); void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net); #endif /* U_RNDIS_H */ diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index ebe409b9e419..7d3bb6272e06 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -56,7 +56,6 @@ struct uvc_event #include <linux/usb/composite.h> #include <linux/usb/gadget.h> #include <linux/videodev2.h> -#include <linux/version.h> #include <media/v4l2-fh.h> #include <media/v4l2-device.h> diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 351d48550c33..4095cce05e6a 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -704,8 +704,8 @@ static int queue_dma(struct usba_udc *udc, struct usba_ep *ep, unsigned long flags; int ret; - DBG(DBG_DMA, "%s: req l/%u d/%08x %c%c%c\n", - ep->ep.name, req->req.length, req->req.dma, + DBG(DBG_DMA, "%s: req l/%u d/%pad %c%c%c\n", + ep->ep.name, req->req.length, &req->req.dma, req->req.zero ? 'Z' : 'z', req->req.short_not_ok ? 'S' : 's', req->req.no_interrupt ? 'I' : 'i'); @@ -2203,7 +2203,7 @@ static int usba_udc_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int usba_udc_suspend(struct device *dev) { struct usba_udc *udc = dev_get_drvdata(dev); diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 9871b90195ad..2bee912ca65b 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -123,6 +123,11 @@ static char *type_string(u8 bmAttributes) #define valid_bit cpu_to_le32(BIT(VALID_BIT)) #define dma_done_ie cpu_to_le32(BIT(DMA_DONE_INTERRUPT_ENABLE)) +static void ep_clear_seqnum(struct net2280_ep *ep); +static void stop_activity(struct net2280 *dev, + struct usb_gadget_driver *driver); +static void ep0_start(struct net2280 *dev); + /*-------------------------------------------------------------------------*/ static inline void enable_pciirqenb(struct net2280_ep *ep) { @@ -142,7 +147,9 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) { struct net2280 *dev; struct net2280_ep *ep; - u32 max, tmp; + u32 max; + u32 tmp = 0; + u32 type; unsigned long flags; static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 }; int ret = 0; @@ -198,15 +205,29 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) /* set type, direction, address; reset fifo counters */ writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat); - tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); - if (tmp == USB_ENDPOINT_XFER_INT) { + + if ((dev->quirks & PLX_SUPERSPEED) && dev->enhanced_mode) { + tmp = readl(&ep->cfg->ep_cfg); + /* If USB ep number doesn't match hardware ep number */ + if ((tmp & 0xf) != usb_endpoint_num(desc)) { + ret = -EINVAL; + spin_unlock_irqrestore(&dev->lock, flags); + goto print_err; + } + if (ep->is_in) + tmp &= ~USB3380_EP_CFG_MASK_IN; + else + tmp &= ~USB3380_EP_CFG_MASK_OUT; + } + type = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); + if (type == USB_ENDPOINT_XFER_INT) { /* erratum 0105 workaround prevents hs NYET */ if (dev->chiprev == 0100 && dev->gadget.speed == USB_SPEED_HIGH && !(desc->bEndpointAddress & USB_DIR_IN)) writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp); - } else if (tmp == USB_ENDPOINT_XFER_BULK) { + } else if (type == USB_ENDPOINT_XFER_BULK) { /* catch some particularly blatant driver bugs */ if ((dev->gadget.speed == USB_SPEED_SUPER && max != 1024) || (dev->gadget.speed == USB_SPEED_HIGH && max != 512) || @@ -216,10 +237,10 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) goto print_err; } } - ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC); + ep->is_iso = (type == USB_ENDPOINT_XFER_ISOC); /* Enable this endpoint */ if (dev->quirks & PLX_LEGACY) { - tmp <<= ENDPOINT_TYPE; + tmp |= type << ENDPOINT_TYPE; tmp |= desc->bEndpointAddress; /* default full fifo lines */ tmp |= (4 << ENDPOINT_BYTE_COUNT); @@ -228,17 +249,17 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) } else { /* In Legacy mode, only OUT endpoints are used */ if (dev->enhanced_mode && ep->is_in) { - tmp <<= IN_ENDPOINT_TYPE; + tmp |= type << IN_ENDPOINT_TYPE; tmp |= BIT(IN_ENDPOINT_ENABLE); - /* Not applicable to Legacy */ - tmp |= BIT(ENDPOINT_DIRECTION); } else { - tmp <<= OUT_ENDPOINT_TYPE; + tmp |= type << OUT_ENDPOINT_TYPE; tmp |= BIT(OUT_ENDPOINT_ENABLE); tmp |= (ep->is_in << ENDPOINT_DIRECTION); } - tmp |= usb_endpoint_num(desc); + tmp |= (4 << ENDPOINT_BYTE_COUNT); + if (!dev->enhanced_mode) + tmp |= usb_endpoint_num(desc); tmp |= (ep->ep.maxburst << MAX_BURST_SIZE); } @@ -256,6 +277,8 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) BIT(CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp); } + if (dev->quirks & PLX_SUPERSPEED) + ep_clear_seqnum(ep); writel(tmp, &ep->cfg->ep_cfg); /* enable irqs */ @@ -441,6 +464,13 @@ static void ep_reset_338x(struct net2280_regs __iomem *regs, BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | BIT(DATA_IN_TOKEN_INTERRUPT), &ep->regs->ep_stat); + + tmp = readl(&ep->cfg->ep_cfg); + if (ep->is_in) + tmp &= ~USB3380_EP_CFG_MASK_IN; + else + tmp &= ~USB3380_EP_CFG_MASK_OUT; + writel(tmp, &ep->cfg->ep_cfg); } static void nuke(struct net2280_ep *); @@ -1468,11 +1498,14 @@ static int net2280_pullup(struct usb_gadget *_gadget, int is_on) spin_lock_irqsave(&dev->lock, flags); tmp = readl(&dev->usb->usbctl); dev->softconnect = (is_on != 0); - if (is_on) - tmp |= BIT(USB_DETECT_ENABLE); - else - tmp &= ~BIT(USB_DETECT_ENABLE); - writel(tmp, &dev->usb->usbctl); + if (is_on) { + ep0_start(dev); + writel(tmp | BIT(USB_DETECT_ENABLE), &dev->usb->usbctl); + } else { + writel(tmp & ~BIT(USB_DETECT_ENABLE), &dev->usb->usbctl); + stop_activity(dev, dev->driver); + } + spin_unlock_irqrestore(&dev->lock, flags); return 0; @@ -1860,8 +1893,8 @@ static void defect7374_enable_data_eps_zero(struct net2280 *dev) tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_DIRECTION) | (2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) | ((dev->enhanced_mode) ? - BIT(OUT_ENDPOINT_ENABLE) : BIT(ENDPOINT_ENABLE)) | - BIT(IN_ENDPOINT_ENABLE)); + BIT(OUT_ENDPOINT_ENABLE) | BIT(IN_ENDPOINT_ENABLE) : + BIT(ENDPOINT_ENABLE))); for (i = 1; i < 5; i++) writel(tmp, &dev->ep[i].cfg->ep_cfg); @@ -1975,9 +2008,15 @@ static void usb_reset_338x(struct net2280 *dev) /* clear old dma and irq state */ for (tmp = 0; tmp < 4; tmp++) { struct net2280_ep *ep = &dev->ep[tmp + 1]; + struct net2280_dma_regs __iomem *dma; - if (ep->dma) + if (ep->dma) { abort_dma(ep); + } else { + dma = &dev->dma[tmp]; + writel(BIT(DMA_ABORT), &dma->dmastat); + writel(0, &dma->dmactl); + } } writel(~0, &dev->regs->irqstat0), writel(~0, &dev->regs->irqstat1); @@ -2065,6 +2104,12 @@ static void usb_reinit_338x(struct net2280 *dev) if (dev->enhanced_mode) { ep->cfg = &dev->epregs[ne[i]]; + /* + * Set USB endpoint number, hardware allows same number + * in both directions. + */ + if (i > 0 && i < 5) + writel(ne[i], &ep->cfg->ep_cfg); ep->regs = (struct net2280_ep_regs __iomem *) (((void __iomem *)&dev->epregs[ne[i]]) + ep_reg_addr[i]); @@ -2874,6 +2919,26 @@ next_endpoints3: return; } +static void usb338x_handle_ep_intr(struct net2280 *dev, u32 stat0) +{ + u32 index; + u32 bit; + + for (index = 0; index < ARRAY_SIZE(ep_bit); index++) { + bit = BIT(ep_bit[index]); + + if (!stat0) + break; + + if (!(stat0 & bit)) + continue; + + stat0 &= ~bit; + + handle_ep_small(&dev->ep[index]); + } +} + static void handle_stat0_irqs(struct net2280 *dev, u32 stat) { struct net2280_ep *ep; @@ -3098,20 +3163,31 @@ do_stall: #undef w_length next_endpoints: - /* endpoint data irq ? */ - scratch = stat & 0x7f; - stat &= ~0x7f; - for (num = 0; scratch; num++) { - u32 t; - - /* do this endpoint's FIFO and queue need tending? */ - t = BIT(num); - if ((scratch & t) == 0) - continue; - scratch ^= t; + if ((dev->quirks & PLX_SUPERSPEED) && dev->enhanced_mode) { + u32 mask = (BIT(ENDPOINT_0_INTERRUPT) | + USB3380_IRQSTAT0_EP_INTR_MASK_IN | + USB3380_IRQSTAT0_EP_INTR_MASK_OUT); + + if (stat & mask) { + usb338x_handle_ep_intr(dev, stat & mask); + stat &= ~mask; + } + } else { + /* endpoint data irq ? */ + scratch = stat & 0x7f; + stat &= ~0x7f; + for (num = 0; scratch; num++) { + u32 t; + + /* do this endpoint's FIFO and queue need tending? */ + t = BIT(num); + if ((scratch & t) == 0) + continue; + scratch ^= t; - ep = &dev->ep[num]; - handle_ep_small(ep); + ep = &dev->ep[num]; + handle_ep_small(ep); + } } if (stat) diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c index b808951491cc..297957512662 100644 --- a/drivers/usb/gadget/udc/s3c2410_udc.c +++ b/drivers/usb/gadget/udc/s3c2410_udc.c @@ -92,40 +92,38 @@ static struct s3c2410_udc_mach_info *udc_info; static uint32_t s3c2410_ticks = 0; -static int dprintk(int level, const char *fmt, ...) +__printf(2, 3) +static void dprintk(int level, const char *fmt, ...) { - static char printk_buf[1024]; static long prevticks; static int invocation; + struct va_format vaf; va_list args; - int len; if (level > USB_S3C2410_DEBUG_LEVEL) - return 0; + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; if (s3c2410_ticks != prevticks) { prevticks = s3c2410_ticks; invocation = 0; } - len = scnprintf(printk_buf, - sizeof(printk_buf), "%1lu.%02d USB: ", - prevticks, invocation++); + pr_debug("%1lu.%02d USB: %pV", prevticks, invocation++, &vaf); - va_start(args, fmt); - len = vscnprintf(printk_buf+len, - sizeof(printk_buf)-len, fmt, args); va_end(args); - - pr_debug("%s", printk_buf); - return len; } #else -static int dprintk(int level, const char *fmt, ...) +__printf(2, 3) +static void dprintk(int level, const char *fmt, ...) { - return 0; } #endif + static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p) { u32 addr_reg, pwr_reg, ep_int_reg, usb_int_reg; diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 220fd4d3b41c..c41fe588d14d 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -438,11 +438,15 @@ static void am35x_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) } static const struct musb_platform_ops am35x_ops = { - .quirks = MUSB_INDEXED_EP, + .quirks = MUSB_DMA_INVENTRA | MUSB_INDEXED_EP, .init = am35x_musb_init, .exit = am35x_musb_exit, .read_fifo = am35x_read_fifo, +#ifdef CONFIG_USB_INVENTRA_DMA + .dma_init = musbhs_dma_controller_create, + .dma_exit = musbhs_dma_controller_destroy, +#endif .enable = am35x_musb_enable, .disable = am35x_musb_disable, @@ -565,7 +569,7 @@ static int am35x_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int am35x_suspend(struct device *dev) { struct am35x_glue *glue = dev_get_drvdata(dev); diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 6123b748d262..310238c6b5cd 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -465,6 +465,7 @@ static int bfin_musb_exit(struct musb *musb) } static const struct musb_platform_ops bfin_ops = { + .quirks = MUSB_DMA_INVENTRA, .init = bfin_musb_init, .exit = bfin_musb_exit, @@ -477,6 +478,10 @@ static const struct musb_platform_ops bfin_ops = { .fifo_mode = 2, .read_fifo = bfin_read_fifo, .write_fifo = bfin_write_fifo, +#ifdef CONFIG_USB_INVENTRA_DMA + .dma_init = musbhs_dma_controller_create, + .dma_exit = musbhs_dma_controller_destroy, +#endif .enable = bfin_musb_enable, .disable = bfin_musb_disable, diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c index 904fb85d85a6..cc134109b056 100644 --- a/drivers/usb/musb/cppi_dma.c +++ b/drivers/usb/musb/cppi_dma.c @@ -1297,7 +1297,8 @@ irqreturn_t cppi_interrupt(int irq, void *dev_id) EXPORT_SYMBOL_GPL(cppi_interrupt); /* Instantiate a software object representing a DMA controller. */ -struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *mregs) +struct dma_controller * +cppi_dma_controller_create(struct musb *musb, void __iomem *mregs) { struct cppi *controller; struct device *dev = musb->controller; @@ -1334,7 +1335,7 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *mr if (irq > 0) { if (request_irq(irq, cppi_interrupt, 0, "cppi-dma", musb)) { dev_err(dev, "request_irq %d failed!\n", irq); - dma_controller_destroy(&controller->controller); + musb_dma_controller_destroy(&controller->controller); return NULL; } controller->irq = irq; @@ -1343,11 +1344,12 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *mr cppi_controller_start(controller); return &controller->controller; } +EXPORT_SYMBOL_GPL(cppi_dma_controller_create); /* * Destroy a previously-instantiated DMA controller. */ -void dma_controller_destroy(struct dma_controller *c) +void cppi_dma_controller_destroy(struct dma_controller *c) { struct cppi *cppi; @@ -1363,6 +1365,7 @@ void dma_controller_destroy(struct dma_controller *c) kfree(cppi); } +EXPORT_SYMBOL_GPL(cppi_dma_controller_destroy); /* * Context: controller irqlocked, endpoint selected diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 9a9c82a4d35d..b03d3b867fca 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -458,11 +458,15 @@ static int da8xx_musb_exit(struct musb *musb) } static const struct musb_platform_ops da8xx_ops = { - .quirks = MUSB_INDEXED_EP, + .quirks = MUSB_DMA_CPPI | MUSB_INDEXED_EP, .init = da8xx_musb_init, .exit = da8xx_musb_exit, .fifo_mode = 2, +#ifdef CONFIG_USB_TI_CPPI_DMA + .dma_init = cppi_dma_controller_create, + .dma_exit = cppi_dma_controller_destroy, +#endif .enable = da8xx_musb_enable, .disable = da8xx_musb_disable, diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 3c1d9b211b51..cee61a51645e 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -284,7 +284,7 @@ static irqreturn_t davinci_musb_interrupt(int irq, void *__hci) * mask, state, "vector", and EOI registers. */ cppi = container_of(musb->dma_controller, struct cppi, controller); - if (is_cppi_enabled() && musb->dma_controller && !cppi->irq) + if (is_cppi_enabled(musb) && musb->dma_controller && !cppi->irq) retval = cppi_interrupt(irq, __hci); /* ack and handle non-CPPI interrupts */ @@ -491,9 +491,14 @@ static int davinci_musb_exit(struct musb *musb) } static const struct musb_platform_ops davinci_ops = { + .quirks = MUSB_DMA_CPPI, .init = davinci_musb_init, .exit = davinci_musb_exit, +#ifdef CONFIG_USB_TI_CPPI_DMA + .dma_init = cppi_dma_controller_create, + .dma_exit = cppi_dma_controller_destroy, +#endif .enable = davinci_musb_enable, .disable = davinci_musb_disable, diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index bb7b26325a74..5e5a8fa005f8 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -105,8 +105,12 @@ static int jz4740_musb_exit(struct musb *musb) return 0; } +/* + * DMA has not been confirmed to work with CONFIG_USB_INVENTRA_DMA, + * so let's not set up the dma function pointers yet. + */ static const struct musb_platform_ops jz4740_musb_ops = { - .quirks = MUSB_INDEXED_EP, + .quirks = MUSB_DMA_INVENTRA | MUSB_INDEXED_EP, .fifo_mode = 2, .init = jz4740_musb_init, .exit = jz4740_musb_exit, diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 3789b08ef67b..d878aee404bf 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -251,6 +251,11 @@ static u32 musb_indexed_ep_offset(u8 epnum, u16 offset) return 0x10 + offset; } +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) { return __raw_readb(addr + offset); @@ -309,7 +314,7 @@ static void musb_default_write_fifo(struct musb_hw_ep *hw_ep, u16 len, index += len & ~0x03; } if (len & 0x02) { - musb_writew(fifo, 0, *(u16 *)&src[index]); + __raw_writew(*(u16 *)&src[index], fifo); index += 2; } } else { @@ -319,7 +324,7 @@ static void musb_default_write_fifo(struct musb_hw_ep *hw_ep, u16 len, } } if (len & 0x01) - musb_writeb(fifo, 0, src[index]); + __raw_writeb(src[index], fifo); } else { /* byte aligned */ iowrite8_rep(fifo, src, len); @@ -351,7 +356,7 @@ static void musb_default_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) index = len & ~0x03; } if (len & 0x02) { - *(u16 *)&dst[index] = musb_readw(fifo, 0); + *(u16 *)&dst[index] = __raw_readw(fifo); index += 2; } } else { @@ -361,7 +366,7 @@ static void musb_default_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) } } if (len & 0x01) - dst[index] = musb_readb(fifo, 0); + dst[index] = __raw_readb(fifo); } else { /* byte aligned */ ioread8_rep(fifo, dst, len); @@ -389,6 +394,15 @@ EXPORT_SYMBOL_GPL(musb_readl); void (*musb_writel)(void __iomem *addr, unsigned offset, u32 data); EXPORT_SYMBOL_GPL(musb_writel); +#ifndef CONFIG_MUSB_PIO_ONLY +struct dma_controller * +(*musb_dma_controller_create)(struct musb *musb, void __iomem *base); +EXPORT_SYMBOL(musb_dma_controller_create); + +void (*musb_dma_controller_destroy)(struct dma_controller *c); +EXPORT_SYMBOL(musb_dma_controller_destroy); +#endif + /* * New style IO functions */ @@ -1535,7 +1549,6 @@ static int musb_core_init(u16 musb_type, struct musb *musb) #endif hw_ep->regs = musb->io.ep_offset(i, 0) + mbase; - hw_ep->target_regs = musb_read_target_reg_base(i, mbase); hw_ep->rx_reinit = 1; hw_ep->tx_reinit = 1; @@ -1658,15 +1671,13 @@ void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit) /* called with controller lock already held */ if (!epnum) { -#ifndef CONFIG_USB_TUSB_OMAP_DMA - if (!is_cppi_enabled()) { + if (!is_cppi_enabled(musb)) { /* endpoint 0 */ if (is_host_active(musb)) musb_h_ep0_irq(musb); else musb_g_ep0_irq(musb); } -#endif } else { /* endpoints 1..15 */ if (transmit) { @@ -2021,13 +2032,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) if (musb->ops->quirks) musb->io.quirks = musb->ops->quirks; - /* At least tusb6010 has it's own offsets.. */ - if (musb->ops->ep_offset) - musb->io.ep_offset = musb->ops->ep_offset; - if (musb->ops->ep_select) - musb->io.ep_select = musb->ops->ep_select; - - /* ..and some devices use indexed offset or flat offset */ + /* Set default ep access to indexed offset or flat offset ops */ if (musb->io.quirks & MUSB_INDEXED_EP) { musb->io.ep_offset = musb_indexed_ep_offset; musb->io.ep_select = musb_indexed_ep_select; @@ -2035,6 +2040,11 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) musb->io.ep_offset = musb_flat_ep_offset; musb->io.ep_select = musb_flat_ep_select; } + /* And override them with platform specific ops if specified. */ + if (musb->ops->ep_offset) + musb->io.ep_offset = musb->ops->ep_offset; + if (musb->ops->ep_select) + musb->io.ep_select = musb->ops->ep_select; if (musb->ops->fifo_mode) fifo_mode = musb->ops->fifo_mode; @@ -2046,6 +2056,11 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) else musb->io.fifo_offset = musb_default_fifo_offset; + if (musb->ops->busctl_offset) + musb->io.busctl_offset = musb->ops->busctl_offset; + else + musb->io.busctl_offset = musb_default_busctl_offset; + if (musb->ops->readb) musb_readb = musb->ops->readb; if (musb->ops->writeb) @@ -2059,6 +2074,15 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) if (musb->ops->writel) musb_writel = musb->ops->writel; +#ifndef CONFIG_MUSB_PIO_ONLY + if (!musb->ops->dma_init || !musb->ops->dma_exit) { + dev_err(dev, "DMA controller not set\n"); + goto fail2; + } + musb_dma_controller_create = musb->ops->dma_init; + musb_dma_controller_destroy = musb->ops->dma_exit; +#endif + if (musb->ops->read_fifo) musb->io.read_fifo = musb->ops->read_fifo; else @@ -2078,7 +2102,8 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) pm_runtime_get_sync(musb->controller); if (use_dma && dev->dma_mask) { - musb->dma_controller = dma_controller_create(musb, musb->mregs); + musb->dma_controller = + musb_dma_controller_create(musb, musb->mregs); if (IS_ERR(musb->dma_controller)) { status = PTR_ERR(musb->dma_controller); goto fail2_5; @@ -2189,7 +2214,7 @@ fail3: cancel_delayed_work_sync(&musb->finish_resume_work); cancel_delayed_work_sync(&musb->deassert_reset_work); if (musb->dma_controller) - dma_controller_destroy(musb->dma_controller); + musb_dma_controller_destroy(musb->dma_controller); fail2_5: pm_runtime_put_sync(musb->controller); @@ -2248,7 +2273,7 @@ static int musb_remove(struct platform_device *pdev) musb_shutdown(pdev); if (musb->dma_controller) - dma_controller_destroy(musb->dma_controller); + musb_dma_controller_destroy(musb->dma_controller); cancel_work_sync(&musb->irq_work); cancel_delayed_work_sync(&musb->finish_resume_work); @@ -2316,18 +2341,18 @@ static void musb_save_context(struct musb *musb) musb_readb(epio, MUSB_RXINTERVAL); musb->context.index_regs[i].txfunaddr = - musb_read_txfunaddr(musb_base, i); + musb_read_txfunaddr(musb, i); musb->context.index_regs[i].txhubaddr = - musb_read_txhubaddr(musb_base, i); + musb_read_txhubaddr(musb, i); musb->context.index_regs[i].txhubport = - musb_read_txhubport(musb_base, i); + musb_read_txhubport(musb, i); musb->context.index_regs[i].rxfunaddr = - musb_read_rxfunaddr(musb_base, i); + musb_read_rxfunaddr(musb, i); musb->context.index_regs[i].rxhubaddr = - musb_read_rxhubaddr(musb_base, i); + musb_read_rxhubaddr(musb, i); musb->context.index_regs[i].rxhubport = - musb_read_rxhubport(musb_base, i); + musb_read_rxhubport(musb, i); } } @@ -2335,7 +2360,6 @@ static void musb_restore_context(struct musb *musb) { int i; void __iomem *musb_base = musb->mregs; - void __iomem *ep_target_regs; void __iomem *epio; u8 power; @@ -2396,21 +2420,18 @@ static void musb_restore_context(struct musb *musb) musb_writeb(epio, MUSB_RXINTERVAL, musb->context.index_regs[i].rxinterval); - musb_write_txfunaddr(musb_base, i, + musb_write_txfunaddr(musb, i, musb->context.index_regs[i].txfunaddr); - musb_write_txhubaddr(musb_base, i, + musb_write_txhubaddr(musb, i, musb->context.index_regs[i].txhubaddr); - musb_write_txhubport(musb_base, i, + musb_write_txhubport(musb, i, musb->context.index_regs[i].txhubport); - ep_target_regs = - musb_read_target_reg_base(i, musb_base); - - musb_write_rxfunaddr(ep_target_regs, + musb_write_rxfunaddr(musb, i, musb->context.index_regs[i].rxfunaddr); - musb_write_rxhubaddr(ep_target_regs, + musb_write_rxhubaddr(musb, i, musb->context.index_regs[i].rxhubaddr); - musb_write_rxhubport(ep_target_regs, + musb_write_rxhubport(musb, i, musb->context.index_regs[i].rxhubport); } musb_writeb(musb_base, MUSB_INDEX, musb->context.index); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 3877249a8b2d..4b886d0f6bdf 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -67,7 +67,6 @@ struct musb_ep; #include "musb_dma.h" #include "musb_io.h" -#include "musb_regs.h" #include "musb_gadget.h" #include <linux/usb/hcd.h> @@ -157,6 +156,8 @@ struct musb_io; * @writel: write 32 bits * @read_fifo: reads the fifo * @write_fifo: writes to fifo + * @dma_init: platform specific dma init function + * @dma_exit: platform specific dma exit function * @init: turns on clocks, sets up platform-specific registers, etc * @exit: undoes @init * @set_mode: forcefully changes operating mode @@ -165,6 +166,8 @@ struct musb_io; * @vbus_status: returns vbus status if possible * @set_vbus: forces vbus status * @adjust_channel_params: pre check for standard dma channel_program func + * @pre_root_reset_end: called before the root usb port reset flag gets cleared + * @post_root_reset_end: called after the root usb port reset flag gets cleared */ struct musb_platform_ops { @@ -187,6 +190,7 @@ struct musb_platform_ops { void (*ep_select)(void __iomem *mbase, u8 epnum); 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); @@ -195,6 +199,9 @@ struct musb_platform_ops { void (*writel)(void __iomem *addr, unsigned offset, u32 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); + struct dma_controller * + (*dma_init) (struct musb *musb, void __iomem *base); + void (*dma_exit)(struct dma_controller *c); int (*set_mode)(struct musb *musb, u8 mode); void (*try_idle)(struct musb *musb, unsigned long timeout); int (*recover)(struct musb *musb); @@ -205,6 +212,8 @@ struct musb_platform_ops { int (*adjust_channel_params)(struct dma_channel *channel, u16 packet_sz, u8 *mode, dma_addr_t *dma_addr, u32 *len); + void (*pre_root_reset_end)(struct musb *musb); + void (*post_root_reset_end)(struct musb *musb); }; /* @@ -241,8 +250,6 @@ struct musb_hw_ep { void __iomem *fifo_sync_va; #endif - void __iomem *target_regs; - /* currently scheduled peripheral endpoint */ struct musb_qh *in_qh; struct musb_qh *out_qh; @@ -437,6 +444,9 @@ struct musb { #endif }; +/* This must be included after struct musb is defined */ +#include "musb_regs.h" + static inline struct musb *gadget_to_musb(struct usb_gadget *g) { return container_of(g, struct musb, g); @@ -590,4 +600,16 @@ static inline int musb_platform_exit(struct musb *musb) return musb->ops->exit(musb); } +static inline void musb_platform_pre_root_reset_end(struct musb *musb) +{ + if (musb->ops->pre_root_reset_end) + musb->ops->pre_root_reset_end(musb); +} + +static inline void musb_platform_post_root_reset_end(struct musb *musb) +{ + if (musb->ops->post_root_reset_end) + musb->ops->post_root_reset_end(musb); +} + #endif /* __MUSB_CORE_H__ */ diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index 8bd8c5e26921..4d1b44c232ee 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -678,7 +678,7 @@ err: return ret; } -void dma_controller_destroy(struct dma_controller *c) +void cppi41_dma_controller_destroy(struct dma_controller *c) { struct cppi41_dma_controller *controller = container_of(c, struct cppi41_dma_controller, controller); @@ -687,9 +687,10 @@ void dma_controller_destroy(struct dma_controller *c) cppi41_dma_controller_stop(controller); kfree(controller); } +EXPORT_SYMBOL_GPL(cppi41_dma_controller_destroy); -struct dma_controller *dma_controller_create(struct musb *musb, - void __iomem *base) +struct dma_controller * +cppi41_dma_controller_create(struct musb *musb, void __iomem *base) { struct cppi41_dma_controller *controller; int ret = 0; @@ -726,3 +727,4 @@ kzalloc_fail: return ERR_PTR(ret); return NULL; } +EXPORT_SYMBOL_GPL(cppi41_dma_controller_create); diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c index 78a283e9ce40..9b22d946c089 100644 --- a/drivers/usb/musb/musb_debugfs.c +++ b/drivers/usb/musb/musb_debugfs.c @@ -191,9 +191,16 @@ static ssize_t musb_test_mode_write(struct file *file, { struct seq_file *s = file->private_data; struct musb *musb = s->private; - u8 test = 0; + u8 test; char buf[18]; + test = musb_readb(musb->mregs, MUSB_TESTMODE); + if (test) { + dev_err(musb->controller, "Error: test mode is already set. " + "Please do USB Bus Reset to start a new test.\n"); + return count; + } + memset(buf, 0x00, sizeof(buf)); if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) @@ -238,6 +245,90 @@ static const struct file_operations musb_test_mode_fops = { .release = single_release, }; +static int musb_softconnect_show(struct seq_file *s, void *unused) +{ + struct musb *musb = s->private; + u8 reg; + int connect; + + switch (musb->xceiv->otg->state) { + case OTG_STATE_A_HOST: + case OTG_STATE_A_WAIT_BCON: + reg = musb_readb(musb->mregs, MUSB_DEVCTL); + connect = reg & MUSB_DEVCTL_SESSION ? 1 : 0; + break; + default: + connect = -1; + } + + seq_printf(s, "%d\n", connect); + + return 0; +} + +static int musb_softconnect_open(struct inode *inode, struct file *file) +{ + return single_open(file, musb_softconnect_show, inode->i_private); +} + +static ssize_t musb_softconnect_write(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct musb *musb = s->private; + char buf[2]; + u8 reg; + + memset(buf, 0x00, sizeof(buf)); + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + if (!strncmp(buf, "0", 1)) { + switch (musb->xceiv->otg->state) { + case OTG_STATE_A_HOST: + musb_root_disconnect(musb); + reg = musb_readb(musb->mregs, MUSB_DEVCTL); + reg &= ~MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, reg); + break; + default: + break; + } + } else if (!strncmp(buf, "1", 1)) { + switch (musb->xceiv->otg->state) { + case OTG_STATE_A_WAIT_BCON: + /* + * musb_save_context() called in musb_runtime_suspend() + * might cache devctl with SESSION bit cleared during + * soft-disconnect, so specifically set SESSION bit + * here to preserve it for musb_runtime_resume(). + */ + musb->context.devctl |= MUSB_DEVCTL_SESSION; + reg = musb_readb(musb->mregs, MUSB_DEVCTL); + reg |= MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, reg); + break; + default: + break; + } + } + + return count; +} + +/* + * In host mode, connect/disconnect the bus without physically + * remove the devices. + */ +static const struct file_operations musb_softconnect_fops = { + .open = musb_softconnect_open, + .write = musb_softconnect_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + int musb_init_debugfs(struct musb *musb) { struct dentry *root; @@ -264,6 +355,13 @@ int musb_init_debugfs(struct musb *musb) goto err1; } + file = debugfs_create_file("softconnect", S_IRUGO | S_IWUSR, + root, musb, &musb_softconnect_fops); + if (!file) { + ret = -ENOMEM; + goto err1; + } + musb->debugfs_root = root; return 0; diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index 1d44faa86252..46357e183b4c 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -68,16 +68,41 @@ struct musb_hw_ep; #define is_dma_capable() (1) #endif -#if defined(CONFIG_USB_TI_CPPI_DMA) || defined(CONFIG_USB_TI_CPPI41_DMA) -#define is_cppi_enabled() 1 +#ifdef CONFIG_USB_UX500_DMA +#define musb_dma_ux500(musb) (musb->io.quirks & MUSB_DMA_UX500) +#else +#define musb_dma_ux500(musb) 0 +#endif + +#ifdef CONFIG_USB_TI_CPPI41_DMA +#define musb_dma_cppi41(musb) (musb->io.quirks & MUSB_DMA_CPPI41) +#else +#define musb_dma_cppi41(musb) 0 +#endif + +#ifdef CONFIG_USB_TI_CPPI_DMA +#define musb_dma_cppi(musb) (musb->io.quirks & MUSB_DMA_CPPI) #else -#define is_cppi_enabled() 0 +#define musb_dma_cppi(musb) 0 #endif #ifdef CONFIG_USB_TUSB_OMAP_DMA -#define tusb_dma_omap() 1 +#define tusb_dma_omap(musb) (musb->io.quirks & MUSB_DMA_TUSB_OMAP) +#else +#define tusb_dma_omap(musb) 0 +#endif + +#ifdef CONFIG_USB_INVENTRA_DMA +#define musb_dma_inventra(musb) (musb->io.quirks & MUSB_DMA_INVENTRA) #else -#define tusb_dma_omap() 0 +#define musb_dma_inventra(musb) 0 +#endif + +#if defined(CONFIG_USB_TI_CPPI_DMA) || defined(CONFIG_USB_TI_CPPI41_DMA) +#define is_cppi_enabled(musb) \ + (musb_dma_cppi(musb) || musb_dma_cppi41(musb)) +#else +#define is_cppi_enabled(musb) 0 #endif /* Anomaly 05000456 - USB Receive Interrupt Is Not Generated in DMA Mode 1 @@ -177,19 +202,41 @@ struct dma_controller { extern void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit); #ifdef CONFIG_MUSB_PIO_ONLY -static inline struct dma_controller *dma_controller_create(struct musb *m, - void __iomem *io) +static inline struct dma_controller * +musb_dma_controller_create(struct musb *m, void __iomem *io) { return NULL; } -static inline void dma_controller_destroy(struct dma_controller *d) { } +static inline void musb_dma_controller_destroy(struct dma_controller *d) { } #else -extern struct dma_controller *dma_controller_create(struct musb *, void __iomem *); +extern struct dma_controller * +(*musb_dma_controller_create)(struct musb *, void __iomem *); -extern void dma_controller_destroy(struct dma_controller *); +extern void (*musb_dma_controller_destroy)(struct dma_controller *); #endif +/* Platform specific DMA functions */ +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 * +tusb_dma_controller_create(struct musb *musb, void __iomem *base); +extern void tusb_dma_controller_destroy(struct dma_controller *c); + +extern struct dma_controller * +cppi_dma_controller_create(struct musb *musb, void __iomem *base); +extern void cppi_dma_controller_destroy(struct dma_controller *c); + +extern struct dma_controller * +cppi41_dma_controller_create(struct musb *musb, void __iomem *base); +extern void cppi41_dma_controller_destroy(struct dma_controller *c); + +extern struct dma_controller * +ux500_dma_controller_create(struct musb *musb, void __iomem *base); +extern void ux500_dma_controller_destroy(struct dma_controller *c); + #endif /* __MUSB_DMA_H__ */ diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 65d931a28a14..1334a3de31b8 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -634,10 +634,14 @@ static void dsps_read_fifo32(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) } static struct musb_platform_ops dsps_ops = { - .quirks = MUSB_INDEXED_EP, + .quirks = MUSB_DMA_CPPI41 | MUSB_INDEXED_EP, .init = dsps_musb_init, .exit = dsps_musb_exit, +#ifdef CONFIG_USB_TI_CPPI41_DMA + .dma_init = cppi41_dma_controller_create, + .dma_exit = cppi41_dma_controller_destroy, +#endif .enable = dsps_musb_enable, .disable = dsps_musb_disable, diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 4c481cd66c77..625d482f1a97 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -366,7 +366,7 @@ static void txstate(struct musb *musb, struct musb_request *req) } #endif - if (is_cppi_enabled()) { + if (is_cppi_enabled(musb)) { /* program endpoint CSR first, then setup DMA */ csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY); csr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_DMAMODE | @@ -402,7 +402,7 @@ static void txstate(struct musb *musb, struct musb_request *req) musb_writew(epio, MUSB_TXCSR, csr); /* invariant: prequest->buf is non-null */ } - } else if (tusb_dma_omap()) + } else if (tusb_dma_omap(musb)) use_dma = use_dma && c->channel_program( musb_ep->dma, musb_ep->packet_sz, request->zero, @@ -489,6 +489,7 @@ void musb_g_tx(struct musb *musb, u8 epnum) if (request) { u8 is_dma = 0; + bool short_packet = false; if (dma && (csr & MUSB_TXCSR_DMAENAB)) { is_dma = 1; @@ -507,15 +508,18 @@ void musb_g_tx(struct musb *musb, u8 epnum) * First, maybe a terminating short packet. Some DMA * engines might handle this by themselves. */ - if ((request->zero && request->length + if ((request->zero && request->length) && (request->length % musb_ep->packet_sz == 0) && (request->actual == request->length)) -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) - || (is_dma && (!dma->desired_mode || + short_packet = true; + + if ((musb_dma_inventra(musb) || musb_dma_ux500(musb)) && + (is_dma && (!dma->desired_mode || (request->actual & - (musb_ep->packet_sz - 1)))) -#endif - ) { + (musb_ep->packet_sz - 1))))) + short_packet = true; + + if (short_packet) { /* * On DMA completion, FIFO may not be * available yet... @@ -595,7 +599,7 @@ static void rxstate(struct musb *musb, struct musb_request *req) return; } - if (is_cppi_enabled() && is_buffer_mapped(req)) { + if (is_cppi_enabled(musb) && is_buffer_mapped(req)) { struct dma_controller *c = musb->dma_controller; struct dma_channel *channel = musb_ep->dma; @@ -772,7 +776,7 @@ static void rxstate(struct musb *musb, struct musb_request *req) fifo_count = min_t(unsigned, len, fifo_count); #ifdef CONFIG_USB_TUSB_OMAP_DMA - if (tusb_dma_omap() && is_buffer_mapped(req)) { + if (tusb_dma_omap(musb) && is_buffer_mapped(req)) { struct dma_controller *c = musb->dma_controller; struct dma_channel *channel = musb_ep->dma; u32 dma_addr = request->dma + request->actual; diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index e1fb5d885c18..26c65e66cc0f 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -181,7 +181,7 @@ static inline void musb_h_tx_dma_start(struct musb_hw_ep *ep) /* NOTE: no locks here; caller should lock and select EP */ txcsr = musb_readw(ep->regs, MUSB_TXCSR); txcsr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_H_WZC_BITS; - if (is_cppi_enabled()) + if (is_cppi_enabled(ep->musb)) txcsr |= MUSB_TXCSR_DMAMODE; musb_writew(ep->regs, MUSB_TXCSR, txcsr); } @@ -294,7 +294,7 @@ start: if (!hw_ep->tx_channel) musb_h_tx_start(hw_ep); - else if (is_cppi_enabled() || tusb_dma_omap()) + else if (is_cppi_enabled(musb) || tusb_dma_omap(musb)) musb_h_tx_dma_start(hw_ep); } } @@ -555,8 +555,9 @@ musb_host_packet_rx(struct musb *musb, struct urb *urb, u8 epnum, u8 iso_err) * the busy/not-empty tests are basically paranoia. */ static void -musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep) +musb_rx_reinit(struct musb *musb, struct musb_qh *qh, u8 epnum) { + struct musb_hw_ep *ep = musb->endpoints + epnum; u16 csr; /* NOTE: we know the "rx" fifo reinit never triggers for ep0. @@ -594,10 +595,9 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep) /* target addr and (for multipoint) hub addr/port */ if (musb->is_multipoint) { - musb_write_rxfunaddr(ep->target_regs, qh->addr_reg); - musb_write_rxhubaddr(ep->target_regs, qh->h_addr_reg); - musb_write_rxhubport(ep->target_regs, qh->h_port_reg); - + musb_write_rxfunaddr(musb, epnum, qh->addr_reg); + musb_write_rxhubaddr(musb, epnum, qh->h_addr_reg); + musb_write_rxhubport(musb, epnum, qh->h_port_reg); } else musb_writeb(musb->mregs, MUSB_FADDR, qh->addr_reg); @@ -617,23 +617,22 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep) ep->rx_reinit = 0; } -static bool musb_tx_dma_program(struct dma_controller *dma, +static int musb_tx_dma_set_mode_mentor(struct dma_controller *dma, struct musb_hw_ep *hw_ep, struct musb_qh *qh, - struct urb *urb, u32 offset, u32 length) + struct urb *urb, u32 offset, + u32 *length, u8 *mode) { struct dma_channel *channel = hw_ep->tx_channel; void __iomem *epio = hw_ep->regs; u16 pkt_size = qh->maxpacket; u16 csr; - u8 mode; -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) - if (length > channel->max_len) - length = channel->max_len; + if (*length > channel->max_len) + *length = channel->max_len; csr = musb_readw(epio, MUSB_TXCSR); - if (length > pkt_size) { - mode = 1; + if (*length > pkt_size) { + *mode = 1; csr |= MUSB_TXCSR_DMAMODE | MUSB_TXCSR_DMAENAB; /* autoset shouldn't be set in high bandwidth */ /* @@ -649,15 +648,28 @@ static bool musb_tx_dma_program(struct dma_controller *dma, can_bulk_split(hw_ep->musb, qh->type))) csr |= MUSB_TXCSR_AUTOSET; } else { - mode = 0; + *mode = 0; csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAMODE); csr |= MUSB_TXCSR_DMAENAB; /* against programmer's guide */ } channel->desired_mode = mode; musb_writew(epio, MUSB_TXCSR, csr); -#else - if (!is_cppi_enabled() && !tusb_dma_omap()) - return false; + + return 0; +} + +static int musb_tx_dma_set_mode_cppi_tusb(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + u32 offset, + u32 *length, + u8 *mode) +{ + struct dma_channel *channel = hw_ep->tx_channel; + + if (!is_cppi_enabled(hw_ep->musb) && !tusb_dma_omap(hw_ep->musb)) + return -ENODEV; channel->actual_len = 0; @@ -665,8 +677,28 @@ static bool musb_tx_dma_program(struct dma_controller *dma, * TX uses "RNDIS" mode automatically but needs help * to identify the zero-length-final-packet case. */ - mode = (urb->transfer_flags & URB_ZERO_PACKET) ? 1 : 0; -#endif + *mode = (urb->transfer_flags & URB_ZERO_PACKET) ? 1 : 0; + + return 0; +} + +static bool musb_tx_dma_program(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, struct musb_qh *qh, + struct urb *urb, u32 offset, u32 length) +{ + struct dma_channel *channel = hw_ep->tx_channel; + u16 pkt_size = qh->maxpacket; + u8 mode; + int res; + + if (musb_dma_inventra(hw_ep->musb) || musb_dma_ux500(hw_ep->musb)) + res = musb_tx_dma_set_mode_mentor(dma, hw_ep, qh, urb, + offset, &length, &mode); + else + res = musb_tx_dma_set_mode_cppi_tusb(dma, hw_ep, qh, urb, + offset, &length, &mode); + if (res) + return false; qh->segsize = length; @@ -678,6 +710,9 @@ static bool musb_tx_dma_program(struct dma_controller *dma, if (!dma->channel_program(channel, pkt_size, mode, urb->transfer_dma + offset, length)) { + void __iomem *epio = hw_ep->regs; + u16 csr; + dma->channel_release(channel); hw_ep->tx_channel = NULL; @@ -801,9 +836,9 @@ static void musb_ep_program(struct musb *musb, u8 epnum, /* target addr and (for multipoint) hub addr/port */ if (musb->is_multipoint) { - musb_write_txfunaddr(mbase, epnum, qh->addr_reg); - musb_write_txhubaddr(mbase, epnum, qh->h_addr_reg); - musb_write_txhubport(mbase, epnum, qh->h_port_reg); + musb_write_txfunaddr(musb, epnum, qh->addr_reg); + musb_write_txhubaddr(musb, epnum, qh->h_addr_reg); + musb_write_txhubport(musb, epnum, qh->h_port_reg); /* FIXME if !epnum, do the same for RX ... */ } else musb_writeb(mbase, MUSB_FADDR, qh->addr_reg); @@ -875,7 +910,7 @@ finish: u16 csr; if (hw_ep->rx_reinit) { - musb_rx_reinit(musb, qh, hw_ep); + musb_rx_reinit(musb, qh, epnum); /* init new state: toggle and NYET, maybe DMA later */ if (usb_gettoggle(urb->dev, qh->epnum, 0)) @@ -901,7 +936,7 @@ finish: /* kick things off */ - if ((is_cppi_enabled() || tusb_dma_omap()) && dma_channel) { + if ((is_cppi_enabled(musb) || tusb_dma_omap(musb)) && dma_channel) { /* Candidate for DMA */ dma_channel->actual_len = 0L; qh->segsize = len; @@ -1441,7 +1476,7 @@ done: } else if ((usb_pipeisoc(pipe) || transfer_pending) && dma) { if (musb_tx_dma_program(musb->dma_controller, hw_ep, qh, urb, offset, length)) { - if (is_cppi_enabled() || tusb_dma_omap()) + if (is_cppi_enabled(musb) || tusb_dma_omap(musb)) musb_h_tx_dma_start(hw_ep); return; } @@ -1498,9 +1533,47 @@ done: MUSB_TXCSR_H_WZC_BITS | MUSB_TXCSR_TXPKTRDY); } +#ifdef CONFIG_USB_TI_CPPI41_DMA +/* Seems to set up ISO for cppi41 and not advance len. See commit c57c41d */ +static int musb_rx_dma_iso_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len) +{ + struct dma_channel *channel = hw_ep->tx_channel; + void __iomem *epio = hw_ep->regs; + dma_addr_t *buf; + u32 length, res; + u16 val; -#ifdef CONFIG_USB_INVENTRA_DMA + buf = (void *)urb->iso_frame_desc[qh->iso_idx].offset + + (u32)urb->transfer_dma; + + length = urb->iso_frame_desc[qh->iso_idx].length; + + val = musb_readw(epio, MUSB_RXCSR); + val |= MUSB_RXCSR_DMAENAB; + musb_writew(hw_ep->regs, MUSB_RXCSR, val); + + res = dma->channel_program(channel, qh->maxpacket, 0, + (u32)buf, length); + + return res; +} +#else +static inline int musb_rx_dma_iso_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len) +{ + return false; +} +#endif +#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \ + defined(CONFIG_USB_TI_CPPI41_DMA) /* Host side RX (IN) using Mentor DMA works as follows: submit_urb -> - if queue was empty, ProgramEndpoint @@ -1535,7 +1608,194 @@ done: * thus be a great candidate for using mode 1 ... for all but the * last packet of one URB's transfer. */ +static int musb_rx_dma_inventra_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len) +{ + struct dma_channel *channel = hw_ep->rx_channel; + void __iomem *epio = hw_ep->regs; + u16 val; + int pipe; + bool done; + + pipe = urb->pipe; + + if (usb_pipeisoc(pipe)) { + struct usb_iso_packet_descriptor *d; + + d = urb->iso_frame_desc + qh->iso_idx; + d->actual_length = len; + + /* even if there was an error, we did the dma + * for iso_frame_desc->length + */ + if (d->status != -EILSEQ && d->status != -EOVERFLOW) + d->status = 0; + + if (++qh->iso_idx >= urb->number_of_packets) { + done = true; + } else { + /* REVISIT: Why ignore return value here? */ + if (musb_dma_cppi41(hw_ep->musb)) + done = musb_rx_dma_iso_cppi41(dma, hw_ep, qh, + urb, len); + done = false; + } + + } else { + /* done if urb buffer is full or short packet is recd */ + done = (urb->actual_length + len >= + urb->transfer_buffer_length + || channel->actual_len < qh->maxpacket + || channel->rx_packet_done); + } + + /* send IN token for next packet, without AUTOREQ */ + if (!done) { + val = musb_readw(epio, MUSB_RXCSR); + val |= MUSB_RXCSR_H_REQPKT; + musb_writew(epio, MUSB_RXCSR, MUSB_RXCSR_H_WZC_BITS | val); + } + + return done; +} + +/* Disadvantage of using mode 1: + * It's basically usable only for mass storage class; essentially all + * other protocols also terminate transfers on short packets. + * + * Details: + * An extra IN token is sent at the end of the transfer (due to AUTOREQ) + * If you try to use mode 1 for (transfer_buffer_length - 512), and try + * to use the extra IN token to grab the last packet using mode 0, then + * the problem is that you cannot be sure when the device will send the + * last packet and RxPktRdy set. Sometimes the packet is recd too soon + * such that it gets lost when RxCSR is re-set at the end of the mode 1 + * transfer, while sometimes it is recd just a little late so that if you + * try to configure for mode 0 soon after the mode 1 transfer is + * completed, you will find rxcount 0. Okay, so you might think why not + * wait for an interrupt when the pkt is recd. Well, you won't get any! + */ +static int musb_rx_dma_in_inventra_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len, + u8 iso_err) +{ + struct musb *musb = hw_ep->musb; + void __iomem *epio = hw_ep->regs; + struct dma_channel *channel = hw_ep->rx_channel; + u16 rx_count, val; + int length, pipe, done; + dma_addr_t buf; + + rx_count = musb_readw(epio, MUSB_RXCOUNT); + pipe = urb->pipe; + + if (usb_pipeisoc(pipe)) { + int d_status = 0; + struct usb_iso_packet_descriptor *d; + + d = urb->iso_frame_desc + qh->iso_idx; + + if (iso_err) { + d_status = -EILSEQ; + urb->error_count++; + } + if (rx_count > d->length) { + if (d_status == 0) { + d_status = -EOVERFLOW; + urb->error_count++; + } + dev_dbg(musb->controller, "** OVERFLOW %d into %d\n", + rx_count, d->length); + + length = d->length; + } else + length = rx_count; + d->status = d_status; + buf = urb->transfer_dma + d->offset; + } else { + length = rx_count; + buf = urb->transfer_dma + urb->actual_length; + } + channel->desired_mode = 0; +#ifdef USE_MODE1 + /* because of the issue below, mode 1 will + * only rarely behave with correct semantics. + */ + if ((urb->transfer_flags & URB_SHORT_NOT_OK) + && (urb->transfer_buffer_length - urb->actual_length) + > qh->maxpacket) + channel->desired_mode = 1; + if (rx_count < hw_ep->max_packet_sz_rx) { + length = rx_count; + channel->desired_mode = 0; + } else { + length = urb->transfer_buffer_length; + } +#endif + + /* See comments above on disadvantages of using mode 1 */ + val = musb_readw(epio, MUSB_RXCSR); + val &= ~MUSB_RXCSR_H_REQPKT; + + if (channel->desired_mode == 0) + val &= ~MUSB_RXCSR_H_AUTOREQ; + else + val |= MUSB_RXCSR_H_AUTOREQ; + val |= MUSB_RXCSR_DMAENAB; + + /* autoclear shouldn't be set in high bandwidth */ + if (qh->hb_mult == 1) + val |= MUSB_RXCSR_AUTOCLEAR; + + musb_writew(epio, MUSB_RXCSR, MUSB_RXCSR_H_WZC_BITS | val); + + /* REVISIT if when actual_length != 0, + * transfer_buffer_length needs to be + * adjusted first... + */ + done = dma->channel_program(channel, qh->maxpacket, + channel->desired_mode, + buf, length); + + if (!done) { + dma->channel_release(channel); + hw_ep->rx_channel = NULL; + channel = NULL; + val = musb_readw(epio, MUSB_RXCSR); + val &= ~(MUSB_RXCSR_DMAENAB + | MUSB_RXCSR_H_AUTOREQ + | MUSB_RXCSR_AUTOCLEAR); + musb_writew(epio, MUSB_RXCSR, val); + } + + return done; +} +#else +static inline int musb_rx_dma_inventra_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len) +{ + return false; +} + +static inline int musb_rx_dma_in_inventra_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len, + u8 iso_err) +{ + return false; +} #endif /* @@ -1546,6 +1806,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) { struct urb *urb; struct musb_hw_ep *hw_ep = musb->endpoints + epnum; + struct dma_controller *c = musb->dma_controller; void __iomem *epio = hw_ep->regs; struct musb_qh *qh = hw_ep->in_qh; size_t xfer_len; @@ -1661,9 +1922,8 @@ void musb_host_rx(struct musb *musb, u8 epnum) */ /* FIXME this is _way_ too much in-line logic for Mentor DMA */ - -#if !defined(CONFIG_USB_INVENTRA_DMA) && !defined(CONFIG_USB_UX500_DMA) - if (rx_csr & MUSB_RXCSR_H_REQPKT) { + if (!musb_dma_inventra(musb) && !musb_dma_ux500(musb) && + (rx_csr & MUSB_RXCSR_H_REQPKT)) { /* REVISIT this happened for a while on some short reads... * the cleanup still needs investigation... looks bad... * and also duplicates dma cleanup code above ... plus, @@ -1684,7 +1944,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) musb_writew(epio, MUSB_RXCSR, MUSB_RXCSR_H_WZC_BITS | rx_csr); } -#endif + if (dma && (rx_csr & MUSB_RXCSR_DMAENAB)) { xfer_len = dma->actual_len; @@ -1694,67 +1954,18 @@ void musb_host_rx(struct musb *musb, u8 epnum) | MUSB_RXCSR_RXPKTRDY); musb_writew(hw_ep->regs, MUSB_RXCSR, val); -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \ - defined(CONFIG_USB_TI_CPPI41_DMA) - if (usb_pipeisoc(pipe)) { - struct usb_iso_packet_descriptor *d; - - d = urb->iso_frame_desc + qh->iso_idx; - d->actual_length = xfer_len; - - /* even if there was an error, we did the dma - * for iso_frame_desc->length - */ - if (d->status != -EILSEQ && d->status != -EOVERFLOW) - d->status = 0; - - if (++qh->iso_idx >= urb->number_of_packets) { - done = true; - } else { -#if defined(CONFIG_USB_TI_CPPI41_DMA) - struct dma_controller *c; - dma_addr_t *buf; - u32 length, ret; - - c = musb->dma_controller; - buf = (void *) - urb->iso_frame_desc[qh->iso_idx].offset - + (u32)urb->transfer_dma; - - length = - urb->iso_frame_desc[qh->iso_idx].length; - - val |= MUSB_RXCSR_DMAENAB; - musb_writew(hw_ep->regs, MUSB_RXCSR, val); - - ret = c->channel_program(dma, qh->maxpacket, - 0, (u32) buf, length); -#endif - done = false; - } - - } else { - /* done if urb buffer is full or short packet is recd */ - done = (urb->actual_length + xfer_len >= - urb->transfer_buffer_length - || dma->actual_len < qh->maxpacket - || dma->rx_packet_done); - } - - /* send IN token for next packet, without AUTOREQ */ - if (!done) { - val |= MUSB_RXCSR_H_REQPKT; - musb_writew(epio, MUSB_RXCSR, - MUSB_RXCSR_H_WZC_BITS | val); + if (musb_dma_inventra(musb) || musb_dma_ux500(musb) || + musb_dma_cppi41(musb)) { + done = musb_rx_dma_inventra_cppi41(c, hw_ep, qh, urb, xfer_len); + dev_dbg(hw_ep->musb->controller, + "ep %d dma %s, rxcsr %04x, rxcount %d\n", + epnum, done ? "off" : "reset", + musb_readw(epio, MUSB_RXCSR), + musb_readw(epio, MUSB_RXCOUNT)); + } else { + done = true; } - dev_dbg(musb->controller, "ep %d dma %s, rxcsr %04x, rxcount %d\n", epnum, - done ? "off" : "reset", - musb_readw(epio, MUSB_RXCSR), - musb_readw(epio, MUSB_RXCOUNT)); -#else - done = true; -#endif } else if (urb->status == -EINPROGRESS) { /* if no errors, be sure a packet is ready for unloading */ if (unlikely(!(rx_csr & MUSB_RXCSR_RXPKTRDY))) { @@ -1772,126 +1983,24 @@ void musb_host_rx(struct musb *musb, u8 epnum) } /* we are expecting IN packets */ -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \ - defined(CONFIG_USB_TI_CPPI41_DMA) - if (dma) { - struct dma_controller *c; - u16 rx_count; - int ret, length; - dma_addr_t buf; - - rx_count = musb_readw(epio, MUSB_RXCOUNT); - - dev_dbg(musb->controller, "RX%d count %d, buffer 0x%llx len %d/%d\n", - epnum, rx_count, - (unsigned long long) urb->transfer_dma - + urb->actual_length, - qh->offset, - urb->transfer_buffer_length); - - c = musb->dma_controller; - - if (usb_pipeisoc(pipe)) { - int d_status = 0; - struct usb_iso_packet_descriptor *d; - - d = urb->iso_frame_desc + qh->iso_idx; - - if (iso_err) { - d_status = -EILSEQ; - urb->error_count++; - } - if (rx_count > d->length) { - if (d_status == 0) { - d_status = -EOVERFLOW; - urb->error_count++; - } - dev_dbg(musb->controller, "** OVERFLOW %d into %d\n",\ - rx_count, d->length); - - length = d->length; - } else - length = rx_count; - d->status = d_status; - buf = urb->transfer_dma + d->offset; - } else { - length = rx_count; - buf = urb->transfer_dma + - urb->actual_length; - } - - dma->desired_mode = 0; -#ifdef USE_MODE1 - /* because of the issue below, mode 1 will - * only rarely behave with correct semantics. - */ - if ((urb->transfer_flags & - URB_SHORT_NOT_OK) - && (urb->transfer_buffer_length - - urb->actual_length) - > qh->maxpacket) - dma->desired_mode = 1; - if (rx_count < hw_ep->max_packet_sz_rx) { - length = rx_count; - dma->desired_mode = 0; - } else { - length = urb->transfer_buffer_length; - } -#endif - -/* Disadvantage of using mode 1: - * It's basically usable only for mass storage class; essentially all - * other protocols also terminate transfers on short packets. - * - * Details: - * An extra IN token is sent at the end of the transfer (due to AUTOREQ) - * If you try to use mode 1 for (transfer_buffer_length - 512), and try - * to use the extra IN token to grab the last packet using mode 0, then - * the problem is that you cannot be sure when the device will send the - * last packet and RxPktRdy set. Sometimes the packet is recd too soon - * such that it gets lost when RxCSR is re-set at the end of the mode 1 - * transfer, while sometimes it is recd just a little late so that if you - * try to configure for mode 0 soon after the mode 1 transfer is - * completed, you will find rxcount 0. Okay, so you might think why not - * wait for an interrupt when the pkt is recd. Well, you won't get any! - */ - - val = musb_readw(epio, MUSB_RXCSR); - val &= ~MUSB_RXCSR_H_REQPKT; - - if (dma->desired_mode == 0) - val &= ~MUSB_RXCSR_H_AUTOREQ; + if ((musb_dma_inventra(musb) || musb_dma_ux500(musb) || + musb_dma_cppi41(musb)) && dma) { + dev_dbg(hw_ep->musb->controller, + "RX%d count %d, buffer 0x%llx len %d/%d\n", + epnum, musb_readw(epio, MUSB_RXCOUNT), + (unsigned long long) urb->transfer_dma + + urb->actual_length, + qh->offset, + urb->transfer_buffer_length); + + done = musb_rx_dma_in_inventra_cppi41(c, hw_ep, qh, + urb, xfer_len, + iso_err); + if (done) + goto finish; else - val |= MUSB_RXCSR_H_AUTOREQ; - val |= MUSB_RXCSR_DMAENAB; - - /* autoclear shouldn't be set in high bandwidth */ - if (qh->hb_mult == 1) - val |= MUSB_RXCSR_AUTOCLEAR; - - musb_writew(epio, MUSB_RXCSR, - MUSB_RXCSR_H_WZC_BITS | val); - - /* REVISIT if when actual_length != 0, - * transfer_buffer_length needs to be - * adjusted first... - */ - ret = c->channel_program( - dma, qh->maxpacket, - dma->desired_mode, buf, length); - - if (!ret) { - c->channel_release(dma); - hw_ep->rx_channel = NULL; - dma = NULL; - val = musb_readw(epio, MUSB_RXCSR); - val &= ~(MUSB_RXCSR_DMAENAB - | MUSB_RXCSR_H_AUTOREQ - | MUSB_RXCSR_AUTOCLEAR); - musb_writew(epio, MUSB_RXCSR, val); - } + dev_err(musb->controller, "error: rx_dma failed\n"); } -#endif /* Mentor DMA */ if (!dma) { unsigned int received_len; diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h index 8a57a6f4b3a6..17a80ae20674 100644 --- a/drivers/usb/musb/musb_io.h +++ b/drivers/usb/musb/musb_io.h @@ -47,6 +47,7 @@ * @fifo_offset: platform specific function to get fifo offset * @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 */ struct musb_io { u32 quirks; @@ -55,6 +56,7 @@ struct musb_io { u32 (*fifo_offset)(u8 epnum); 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); }; /* Do not add new entries here, add them the struct musb_io instead */ diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h index 11f0be07491e..cff5bcf0d00f 100644 --- a/drivers/usb/musb/musb_regs.h +++ b/drivers/usb/musb/musb_regs.h @@ -300,9 +300,6 @@ #define MUSB_RXHUBADDR 0x06 #define MUSB_RXHUBPORT 0x07 -#define MUSB_BUSCTL_OFFSET(_epnum, _offset) \ - (0x80 + (8*(_epnum)) + (_offset)) - static inline void musb_write_txfifosz(void __iomem *mbase, u8 c_size) { musb_writeb(mbase, MUSB_TXFIFOSZ, c_size); @@ -364,78 +361,84 @@ static inline u16 musb_read_hwvers(void __iomem *mbase) return musb_readw(mbase, MUSB_HWVERS); } -static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase) -{ - return (MUSB_BUSCTL_OFFSET(i, 0) + mbase); -} - -static inline void musb_write_rxfunaddr(void __iomem *ep_target_regs, +static inline void musb_write_rxfunaddr(struct musb *musb, u8 epnum, u8 qh_addr_reg) { - musb_writeb(ep_target_regs, MUSB_RXFUNCADDR, qh_addr_reg); + musb_writeb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_RXFUNCADDR), + qh_addr_reg); } -static inline void musb_write_rxhubaddr(void __iomem *ep_target_regs, +static inline void musb_write_rxhubaddr(struct musb *musb, u8 epnum, u8 qh_h_addr_reg) { - musb_writeb(ep_target_regs, MUSB_RXHUBADDR, qh_h_addr_reg); + musb_writeb(musb->mregs, musb->io.busctl_offset(epnum, MUSB_RXHUBADDR), + qh_h_addr_reg); } -static inline void musb_write_rxhubport(void __iomem *ep_target_regs, +static inline void musb_write_rxhubport(struct musb *musb, u8 epnum, u8 qh_h_port_reg) { - musb_writeb(ep_target_regs, MUSB_RXHUBPORT, qh_h_port_reg); + musb_writeb(musb->mregs, musb->io.busctl_offset(epnum, MUSB_RXHUBPORT), + qh_h_port_reg); } -static inline void musb_write_txfunaddr(void __iomem *mbase, u8 epnum, +static inline void musb_write_txfunaddr(struct musb *musb, u8 epnum, u8 qh_addr_reg) { - musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXFUNCADDR), - qh_addr_reg); + musb_writeb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_TXFUNCADDR), + qh_addr_reg); } -static inline void musb_write_txhubaddr(void __iomem *mbase, u8 epnum, +static inline void musb_write_txhubaddr(struct musb *musb, u8 epnum, u8 qh_addr_reg) { - musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBADDR), + musb_writeb(musb->mregs, musb->io.busctl_offset(epnum, MUSB_TXHUBADDR), qh_addr_reg); } -static inline void musb_write_txhubport(void __iomem *mbase, u8 epnum, +static inline void musb_write_txhubport(struct musb *musb, u8 epnum, u8 qh_h_port_reg) { - musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBPORT), + musb_writeb(musb->mregs, musb->io.busctl_offset(epnum, MUSB_TXHUBPORT), qh_h_port_reg); } -static inline u8 musb_read_rxfunaddr(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_rxfunaddr(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXFUNCADDR)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_RXFUNCADDR)); } -static inline u8 musb_read_rxhubaddr(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_rxhubaddr(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXHUBADDR)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_RXHUBADDR)); } -static inline u8 musb_read_rxhubport(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_rxhubport(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXHUBPORT)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_RXHUBPORT)); } -static inline u8 musb_read_txfunaddr(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_txfunaddr(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXFUNCADDR)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_TXFUNCADDR)); } -static inline u8 musb_read_txhubaddr(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_txhubaddr(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBADDR)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_TXHUBADDR)); } -static inline u8 musb_read_txhubport(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_txhubport(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBPORT)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_TXHUBPORT)); } #else /* CONFIG_BLACKFIN */ @@ -556,22 +559,17 @@ static inline u16 musb_read_hwvers(void __iomem *mbase) return MUSB_HWVERS_1900; } -static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase) -{ - return NULL; -} - -static inline void musb_write_rxfunaddr(void __iomem *ep_target_regs, +static inline void musb_write_rxfunaddr(void __iomem *mbase, u8 epnum, u8 qh_addr_req) { } -static inline void musb_write_rxhubaddr(void __iomem *ep_target_regs, +static inline void musb_write_rxhubaddr(void __iomem *mbase, u8 epnum, u8 qh_h_addr_reg) { } -static inline void musb_write_rxhubport(void __iomem *ep_target_regs, +static inline void musb_write_rxhubport(void __iomem *mbase, u8 epnum, u8 qh_h_port_reg) { } diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index 86c4b533e90b..30842bc195f5 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -195,8 +195,10 @@ void musb_port_reset(struct musb *musb, bool do_reset) msecs_to_jiffies(50)); } else { dev_dbg(musb->controller, "root port reset stopped\n"); + musb_platform_pre_root_reset_end(musb); musb_writeb(mbase, MUSB_POWER, power & ~MUSB_POWER_RESET); + musb_platform_post_root_reset_end(musb); power = musb_readb(mbase, MUSB_POWER); if (power & MUSB_POWER_HSMODE) { diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index ab7ec09a8afe..7539c3188ffc 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -357,7 +357,7 @@ done: return retval; } -void dma_controller_destroy(struct dma_controller *c) +void musbhs_dma_controller_destroy(struct dma_controller *c) { struct musb_dma_controller *controller = container_of(c, struct musb_dma_controller, controller); @@ -369,8 +369,10 @@ void dma_controller_destroy(struct dma_controller *c) kfree(controller); } +EXPORT_SYMBOL_GPL(musbhs_dma_controller_destroy); -struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *base) +struct dma_controller *musbhs_dma_controller_create(struct musb *musb, + void __iomem *base) { struct musb_dma_controller *controller; struct device *dev = musb->controller; @@ -398,7 +400,7 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *ba if (request_irq(irq, dma_controller_irq, 0, dev_name(musb->controller), &controller->controller)) { dev_err(dev, "request_irq %d failed!\n", irq); - dma_controller_destroy(&controller->controller); + musb_dma_controller_destroy(&controller->controller); return NULL; } @@ -407,3 +409,4 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *ba return &controller->controller; } +EXPORT_SYMBOL_GPL(musbhs_dma_controller_create); diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index cc752d8c7773..70f2b8a2e6cf 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -493,6 +493,11 @@ static int omap2430_musb_exit(struct musb *musb) } static const struct musb_platform_ops omap2430_ops = { + .quirks = MUSB_DMA_INVENTRA, +#ifdef CONFIG_USB_INVENTRA_DMA + .dma_init = musbhs_dma_controller_create, + .dma_exit = musbhs_dma_controller_destroy, +#endif .init = omap2430_musb_init, .exit = omap2430_musb_exit, diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 3a5ffd575438..df7c9f46be54 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -890,7 +890,7 @@ static irqreturn_t tusb_musb_interrupt(int irq, void *__hci) dev_dbg(musb->controller, "DMA IRQ %08x\n", dma_src); real_dma_src = ~real_dma_src & dma_src; - if (tusb_dma_omap() && real_dma_src) { + if (tusb_dma_omap(musb) && real_dma_src) { int tx_source = (real_dma_src & 0xffff); int i; @@ -1181,7 +1181,7 @@ static int tusb_musb_exit(struct musb *musb) } static const struct musb_platform_ops tusb_ops = { - .quirks = MUSB_IN_TUSB, + .quirks = MUSB_DMA_TUSB_OMAP | MUSB_IN_TUSB, .init = tusb_musb_init, .exit = tusb_musb_exit, @@ -1192,6 +1192,10 @@ static const struct musb_platform_ops tusb_ops = { .writeb = tusb_writeb, .read_fifo = tusb_read_fifo, .write_fifo = tusb_write_fifo, +#ifdef CONFIG_USB_TUSB_OMAP_DMA + .dma_init = tusb_dma_controller_create, + .dma_exit = tusb_dma_controller_destroy, +#endif .enable = tusb_musb_enable, .disable = tusb_musb_disable, diff --git a/drivers/usb/musb/tusb6010.h b/drivers/usb/musb/tusb6010.h index aec86c86ce32..72cdad23ced9 100644 --- a/drivers/usb/musb/tusb6010.h +++ b/drivers/usb/musb/tusb6010.h @@ -12,12 +12,6 @@ #ifndef __TUSB6010_H__ #define __TUSB6010_H__ -#ifdef CONFIG_USB_TUSB_OMAP_DMA -#define tusb_dma_omap() 1 -#else -#define tusb_dma_omap() 0 -#endif - /* VLYNQ control register. 32-bit at offset 0x000 */ #define TUSB_VLYNQ_CTRL 0x004 diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c index 3ce152c0408e..4c82077da475 100644 --- a/drivers/usb/musb/tusb6010_omap.c +++ b/drivers/usb/musb/tusb6010_omap.c @@ -625,7 +625,7 @@ static void tusb_omap_dma_release(struct dma_channel *channel) channel = NULL; } -void dma_controller_destroy(struct dma_controller *c) +void tusb_dma_controller_destroy(struct dma_controller *c) { struct tusb_omap_dma *tusb_dma; int i; @@ -644,8 +644,10 @@ void dma_controller_destroy(struct dma_controller *c) kfree(tusb_dma); } +EXPORT_SYMBOL_GPL(tusb_dma_controller_destroy); -struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *base) +struct dma_controller * +tusb_dma_controller_create(struct musb *musb, void __iomem *base) { void __iomem *tbase = musb->ctrl_base; struct tusb_omap_dma *tusb_dma; @@ -701,7 +703,8 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *ba return &tusb_dma->controller; cleanup: - dma_controller_destroy(&tusb_dma->controller); + musb_dma_controller_destroy(&tusb_dma->controller); out: return NULL; } +EXPORT_SYMBOL_GPL(tusb_dma_controller_create); diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index abf72728825f..39168fe9b406 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -188,7 +188,11 @@ static int ux500_musb_exit(struct musb *musb) } static const struct musb_platform_ops ux500_ops = { - .quirks = MUSB_INDEXED_EP, + .quirks = MUSB_DMA_UX500 | MUSB_INDEXED_EP, +#ifdef CONFIG_USB_UX500_DMA + .dma_init = ux500_dma_controller_create, + .dma_exit = ux500_dma_controller_destroy, +#endif .init = ux500_musb_init, .exit = ux500_musb_exit, .fifo_mode = 5, @@ -338,7 +342,7 @@ static int ux500_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int ux500_suspend(struct device *dev) { struct ux500_glue *glue = dev_get_drvdata(dev); diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index e93845c26bdb..d0b6a1cd7f62 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -359,7 +359,7 @@ static int ux500_dma_controller_start(struct ux500_dma_controller *controller) return 0; } -void dma_controller_destroy(struct dma_controller *c) +void ux500_dma_controller_destroy(struct dma_controller *c) { struct ux500_dma_controller *controller = container_of(c, struct ux500_dma_controller, controller); @@ -367,9 +367,10 @@ void dma_controller_destroy(struct dma_controller *c) ux500_dma_controller_stop(controller); kfree(controller); } +EXPORT_SYMBOL_GPL(ux500_dma_controller_destroy); -struct dma_controller *dma_controller_create(struct musb *musb, - void __iomem *base) +struct dma_controller * +ux500_dma_controller_create(struct musb *musb, void __iomem *base) { struct ux500_dma_controller *controller; struct platform_device *pdev = to_platform_device(musb->controller); @@ -407,3 +408,4 @@ plat_get_fail: kzalloc_fail: return NULL; } +EXPORT_SYMBOL_GPL(ux500_dma_controller_create); diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 3cd3bee54ca6..869c0cfcad98 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -91,7 +91,7 @@ config TWL6030_USB config USB_GPIO_VBUS tristate "GPIO based peripheral-only VBUS sensing 'transceiver'" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST select USB_PHY help Provides simple GPIO VBUS sensing for controllers with an @@ -141,6 +141,7 @@ config USB_MSM_OTG tristate "Qualcomm on-chip USB OTG controller support" depends on (USB || USB_GADGET) && (ARCH_QCOM || COMPILE_TEST) depends on RESET_CONTROLLER + depends on EXTCON select USB_PHY help Enable this to support the USB OTG transceiver on Qualcomm chips. It diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index c9156beeadef..00c49bb1bd29 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -240,8 +240,14 @@ static void ulpi_init(struct msm_otg *motg) static int msm_phy_notify_disconnect(struct usb_phy *phy, enum usb_device_speed speed) { + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); int val; + if (motg->manual_pullup) { + val = ULPI_MISC_A_VBUSVLDEXT | ULPI_MISC_A_VBUSVLDEXTSEL; + usb_phy_io_write(phy, val, ULPI_CLR(ULPI_MISC_A)); + } + /* * Put the transceiver in non-driving mode. Otherwise host * may not detect soft-disconnection. @@ -422,6 +428,24 @@ static int msm_phy_init(struct usb_phy *phy) ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_FALL); } + if (motg->manual_pullup) { + val = ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT; + ulpi_write(phy, val, ULPI_SET(ULPI_MISC_A)); + + val = readl(USB_GENCONFIG_2); + val |= GENCONFIG_2_SESS_VLD_CTRL_EN; + writel(val, USB_GENCONFIG_2); + + val = readl(USB_USBCMD); + val |= USBCMD_SESS_VLD_CTRL; + writel(val, USB_USBCMD); + + val = ulpi_read(phy, ULPI_FUNC_CTRL); + val &= ~ULPI_FUNC_CTRL_OPMODE_MASK; + val |= ULPI_FUNC_CTRL_OPMODE_NORMAL; + ulpi_write(phy, val, ULPI_FUNC_CTRL); + } + if (motg->phy_number) writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2); @@ -1436,9 +1460,42 @@ static const struct of_device_id msm_otg_dt_match[] = { }; MODULE_DEVICE_TABLE(of, msm_otg_dt_match); +static int msm_otg_vbus_notifier(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct msm_usb_cable *vbus = container_of(nb, struct msm_usb_cable, nb); + struct msm_otg *motg = container_of(vbus, struct msm_otg, vbus); + + if (event) + set_bit(B_SESS_VLD, &motg->inputs); + else + clear_bit(B_SESS_VLD, &motg->inputs); + + schedule_work(&motg->sm_work); + + return NOTIFY_DONE; +} + +static int msm_otg_id_notifier(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct msm_usb_cable *id = container_of(nb, struct msm_usb_cable, nb); + struct msm_otg *motg = container_of(id, struct msm_otg, id); + + if (event) + clear_bit(ID, &motg->inputs); + else + set_bit(ID, &motg->inputs); + + schedule_work(&motg->sm_work); + + return NOTIFY_DONE; +} + static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) { struct msm_otg_platform_data *pdata; + struct extcon_dev *ext_id, *ext_vbus; const struct of_device_id *id; struct device_node *node = pdev->dev.of_node; struct property *prop; @@ -1487,6 +1544,54 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) motg->vdd_levels[VDD_LEVEL_MAX] = tmp[VDD_LEVEL_MAX]; } + motg->manual_pullup = of_property_read_bool(node, "qcom,manual-pullup"); + + ext_id = ERR_PTR(-ENODEV); + ext_vbus = ERR_PTR(-ENODEV); + if (of_property_read_bool(node, "extcon")) { + + /* Each one of them is not mandatory */ + ext_vbus = extcon_get_edev_by_phandle(&pdev->dev, 0); + if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV) + return PTR_ERR(ext_vbus); + + ext_id = extcon_get_edev_by_phandle(&pdev->dev, 1); + if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV) + return PTR_ERR(ext_id); + } + + if (!IS_ERR(ext_vbus)) { + motg->vbus.nb.notifier_call = msm_otg_vbus_notifier; + ret = extcon_register_interest(&motg->vbus.conn, ext_vbus->name, + "USB", &motg->vbus.nb); + if (ret < 0) { + dev_err(&pdev->dev, "register VBUS notifier failed\n"); + return ret; + } + + ret = extcon_get_cable_state(ext_vbus, "USB"); + if (ret) + set_bit(B_SESS_VLD, &motg->inputs); + else + clear_bit(B_SESS_VLD, &motg->inputs); + } + + if (!IS_ERR(ext_id)) { + motg->id.nb.notifier_call = msm_otg_id_notifier; + ret = extcon_register_interest(&motg->id.conn, ext_id->name, + "USB-HOST", &motg->id.nb); + if (ret < 0) { + dev_err(&pdev->dev, "register ID notifier failed\n"); + return ret; + } + + ret = extcon_get_cable_state(ext_id, "USB-HOST"); + if (ret) + clear_bit(ID, &motg->inputs); + else + set_bit(ID, &motg->inputs); + } + prop = of_find_property(node, "qcom,phy-init-sequence", &len); if (!prop || !len) return 0; @@ -1700,6 +1805,11 @@ static int msm_otg_remove(struct platform_device *pdev) if (phy->otg->host || phy->otg->gadget) return -EBUSY; + if (motg->id.conn.edev) + extcon_unregister_interest(&motg->id.conn); + if (motg->vbus.conn.edev) + extcon_unregister_interest(&motg->vbus.conn); + msm_otg_debugfs_cleanup(); cancel_delayed_work_sync(&motg->chg_work); cancel_work_sync(&motg->sm_work); diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index d1cd6b50f520..98f75d2842b7 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -22,6 +22,11 @@ static LIST_HEAD(phy_list); static LIST_HEAD(phy_bind_list); static DEFINE_SPINLOCK(phy_lock); +struct phy_devm { + struct usb_phy *phy; + struct notifier_block *nb; +}; + static struct usb_phy *__usb_find_phy(struct list_head *list, enum usb_phy_type type) { @@ -79,6 +84,15 @@ static void devm_usb_phy_release(struct device *dev, void *res) usb_put_phy(phy); } +static void devm_usb_phy_release2(struct device *dev, void *_res) +{ + struct phy_devm *res = _res; + + if (res->nb) + usb_unregister_notifier(res->phy, res->nb); + usb_put_phy(res->phy); +} + static int devm_usb_phy_match(struct device *dev, void *res, void *match_data) { struct usb_phy **phy = res; @@ -153,40 +167,30 @@ err0: EXPORT_SYMBOL_GPL(usb_get_phy); /** - * devm_usb_get_phy_by_phandle - find the USB PHY by phandle + * devm_usb_get_phy_by_node - find the USB PHY by device_node * @dev - device that requests this phy - * @phandle - name of the property holding the phy phandle value - * @index - the index of the phy + * @node - the device_node for the phy device. + * @nb - a notifier_block to register with the phy. * - * Returns the phy driver associated with the given phandle value, + * Returns the phy driver associated with the given device_node, * after getting a refcount to it, -ENODEV if there is no such phy or - * -EPROBE_DEFER if there is a phandle to the phy, but the device is - * not yet loaded. While at that, it also associates the device with + * -EPROBE_DEFER if the device is not yet loaded. While at that, it + * also associates the device with * the phy using devres. On driver detach, release function is invoked * on the devres data, then, devres data is freed. * - * For use by USB host and peripheral drivers. + * For use by peripheral drivers for devices related to a phy, + * such as a charger. */ -struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, - const char *phandle, u8 index) +struct usb_phy *devm_usb_get_phy_by_node(struct device *dev, + struct device_node *node, + struct notifier_block *nb) { - struct usb_phy *phy = ERR_PTR(-ENOMEM), **ptr; + struct usb_phy *phy = ERR_PTR(-ENOMEM); + struct phy_devm *ptr; unsigned long flags; - struct device_node *node; - if (!dev->of_node) { - dev_dbg(dev, "device does not have a device node entry\n"); - return ERR_PTR(-EINVAL); - } - - node = of_parse_phandle(dev->of_node, phandle, index); - if (!node) { - dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle, - dev->of_node->full_name); - return ERR_PTR(-ENODEV); - } - - ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL); + ptr = devres_alloc(devm_usb_phy_release2, sizeof(*ptr), GFP_KERNEL); if (!ptr) { dev_dbg(dev, "failed to allocate memory for devres\n"); goto err0; @@ -205,8 +209,10 @@ struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, devres_free(ptr); goto err1; } - - *ptr = phy; + if (nb) + usb_register_notifier(phy, nb); + ptr->phy = phy; + ptr->nb = nb; devres_add(dev, ptr); get_device(phy->dev); @@ -215,10 +221,47 @@ err1: spin_unlock_irqrestore(&phy_lock, flags); err0: - of_node_put(node); return phy; } +EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_node); + +/** + * devm_usb_get_phy_by_phandle - find the USB PHY by phandle + * @dev - device that requests this phy + * @phandle - name of the property holding the phy phandle value + * @index - the index of the phy + * + * Returns the phy driver associated with the given phandle value, + * after getting a refcount to it, -ENODEV if there is no such phy or + * -EPROBE_DEFER if there is a phandle to the phy, but the device is + * not yet loaded. While at that, it also associates the device with + * the phy using devres. On driver detach, release function is invoked + * on the devres data, then, devres data is freed. + * + * For use by USB host and peripheral drivers. + */ +struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, + const char *phandle, u8 index) +{ + struct device_node *node; + struct usb_phy *phy; + + if (!dev->of_node) { + dev_dbg(dev, "device does not have a device node entry\n"); + return ERR_PTR(-EINVAL); + } + + node = of_parse_phandle(dev->of_node, phandle, index); + if (!node) { + dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle, + dev->of_node->full_name); + return ERR_PTR(-ENODEV); + } + phy = devm_usb_get_phy_by_node(dev, node, NULL); + of_node_put(node); + return phy; +} EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_phandle); /** diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 0f7e850fd4aa..e8bf40808b39 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -466,11 +466,15 @@ static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev) static const struct of_device_id usbhs_of_match[] = { { .compatible = "renesas,usbhs-r8a7790", - .data = (void *)USBHS_TYPE_R8A7790, + .data = (void *)USBHS_TYPE_RCAR_GEN2, }, { .compatible = "renesas,usbhs-r8a7791", - .data = (void *)USBHS_TYPE_R8A7791, + .data = (void *)USBHS_TYPE_RCAR_GEN2, + }, + { + .compatible = "renesas,usbhs-r8a7794", + .data = (void *)USBHS_TYPE_RCAR_GEN2, }, { }, }; @@ -497,14 +501,8 @@ static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev) if (gpio > 0) dparam->enable_gpio = gpio; - switch (dparam->type) { - case USBHS_TYPE_R8A7790: - case USBHS_TYPE_R8A7791: + if (dparam->type == USBHS_TYPE_RCAR_GEN2) dparam->has_usb_dmac = 1; - break; - default: - break; - } return info; } @@ -559,8 +557,7 @@ static int usbhs_probe(struct platform_device *pdev) sizeof(struct renesas_usbhs_driver_param)); switch (priv->dparam.type) { - case USBHS_TYPE_R8A7790: - case USBHS_TYPE_R8A7791: + case USBHS_TYPE_RCAR_GEN2: priv->pfunc = usbhs_rcar2_ops; if (!priv->dparam.pipe_type) { priv->dparam.pipe_type = usbhsc_new_pipe_type; diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index fa126a2b0c33..c0f5c652d272 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -1239,15 +1239,21 @@ static void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo, { char name[16]; - snprintf(name, sizeof(name), "tx%d", channel); - fifo->tx_chan = dma_request_slave_channel_reason(dev, name); - if (IS_ERR(fifo->tx_chan)) - fifo->tx_chan = NULL; - - snprintf(name, sizeof(name), "rx%d", channel); - fifo->rx_chan = dma_request_slave_channel_reason(dev, name); - if (IS_ERR(fifo->rx_chan)) - fifo->rx_chan = NULL; + /* + * To avoid complex handing for DnFIFOs, the driver uses each + * DnFIFO as TX or RX direction (not bi-direction). + * So, the driver uses odd channels for TX, even channels for RX. + */ + snprintf(name, sizeof(name), "ch%d", channel); + if (channel & 1) { + fifo->tx_chan = dma_request_slave_channel_reason(dev, name); + if (IS_ERR(fifo->tx_chan)) + fifo->tx_chan = NULL; + } else { + fifo->rx_chan = dma_request_slave_channel_reason(dev, name); + if (IS_ERR(fifo->rx_chan)) + fifo->rx_chan = NULL; + } } static void usbhsf_dma_init(struct usbhs_priv *priv, struct usbhs_fifo *fifo, diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index 04d3f8abad9e..c7d9b86d51bf 100644 --- a/drivers/usb/renesas_usbhs/fifo.h +++ b/drivers/usb/renesas_usbhs/fifo.h @@ -44,10 +44,11 @@ struct usbhs_fifo_info { struct usbhs_fifo dfifo[USBHS_MAX_NUM_DFIFO]; }; #define usbhsf_get_dnfifo(p, n) (&((p)->fifo_info.dfifo[n])) -#define usbhs_for_each_dfifo(priv, dfifo, i) \ - for ((i) = 0, dfifo = usbhsf_get_dnfifo(priv, (i)); \ - ((i) < USBHS_MAX_NUM_DFIFO); \ - (i)++, dfifo = usbhsf_get_dnfifo(priv, (i))) +#define usbhs_for_each_dfifo(priv, dfifo, i) \ + for ((i) = 0; \ + ((i) < USBHS_MAX_NUM_DFIFO) && \ + ((dfifo) = usbhsf_get_dnfifo(priv, (i))); \ + (i)++) struct usbhs_pkt_handle; struct usbhs_pkt { diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index 9a705b15b3a1..d4be5d594896 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -218,10 +218,14 @@ static int usbhs_status_get_each_irq(struct usbhs_priv *priv, /******************** spin lock ********************/ usbhs_lock(priv, flags); state->intsts0 = usbhs_read(priv, INTSTS0); - state->intsts1 = usbhs_read(priv, INTSTS1); - intenb0 = usbhs_read(priv, INTENB0); - intenb1 = usbhs_read(priv, INTENB1); + + if (usbhs_mod_is_host(priv)) { + state->intsts1 = usbhs_read(priv, INTSTS1); + intenb1 = usbhs_read(priv, INTENB1); + } else { + state->intsts1 = intenb1 = 0; + } /* mask */ if (mod) { @@ -275,7 +279,8 @@ static irqreturn_t usbhs_interrupt(int irq, void *data) * - Function :: VALID bit should 0 */ usbhs_write(priv, INTSTS0, ~irq_state.intsts0 & INTSTS0_MAGIC); - usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC); + if (usbhs_mod_is_host(priv)) + usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC); usbhs_write(priv, BRDYSTS, ~irq_state.brdysts); usbhs_write(priv, NRDYSTS, ~irq_state.nrdysts); @@ -303,19 +308,20 @@ static irqreturn_t usbhs_interrupt(int irq, void *data) if (irq_state.intsts0 & BRDY) usbhs_mod_call(priv, irq_ready, priv, &irq_state); - /* INTSTS1 */ - if (irq_state.intsts1 & ATTCH) - usbhs_mod_call(priv, irq_attch, priv, &irq_state); - - if (irq_state.intsts1 & DTCH) - usbhs_mod_call(priv, irq_dtch, priv, &irq_state); + if (usbhs_mod_is_host(priv)) { + /* INTSTS1 */ + if (irq_state.intsts1 & ATTCH) + usbhs_mod_call(priv, irq_attch, priv, &irq_state); - if (irq_state.intsts1 & SIGN) - usbhs_mod_call(priv, irq_sign, priv, &irq_state); + if (irq_state.intsts1 & DTCH) + usbhs_mod_call(priv, irq_dtch, priv, &irq_state); - if (irq_state.intsts1 & SACK) - usbhs_mod_call(priv, irq_sack, priv, &irq_state); + if (irq_state.intsts1 & SIGN) + usbhs_mod_call(priv, irq_sign, priv, &irq_state); + if (irq_state.intsts1 & SACK) + usbhs_mod_call(priv, irq_sack, priv, &irq_state); + } return IRQ_HANDLED; } @@ -334,7 +340,8 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) * - update INTSTS0 */ usbhs_write(priv, INTENB0, 0); - usbhs_write(priv, INTENB1, 0); + if (usbhs_mod_is_host(priv)) + usbhs_write(priv, INTENB1, 0); usbhs_write(priv, BEMPENB, 0); usbhs_write(priv, BRDYENB, 0); @@ -368,25 +375,27 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) intenb0 |= BRDYE; } - /* - * INTSTS1 - */ - if (mod->irq_attch) - intenb1 |= ATTCHE; + if (usbhs_mod_is_host(priv)) { + /* + * INTSTS1 + */ + if (mod->irq_attch) + intenb1 |= ATTCHE; - if (mod->irq_dtch) - intenb1 |= DTCHE; + if (mod->irq_dtch) + intenb1 |= DTCHE; - if (mod->irq_sign) - intenb1 |= SIGNE; + if (mod->irq_sign) + intenb1 |= SIGNE; - if (mod->irq_sack) - intenb1 |= SACKE; + if (mod->irq_sack) + intenb1 |= SACKE; + } } if (intenb0) usbhs_write(priv, INTENB0, intenb0); - if (intenb1) + if (usbhs_mod_is_host(priv) && intenb1) usbhs_write(priv, INTENB1, intenb1); } |