Merge tag 'usb-5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb

Pull USB updates from Greg KH:
 "Here is the big set of USB patches for 5.4-rc1.

  Two major chunks of code are moving out of the tree and into the
  staging directory, uwb and wusb (wireless USB support), because there
  are no devices that actually use this protocol anymore, and what we
  have today probably doesn't work at all given that the maintainers
  left many many years ago. So move it to staging where it will be
  removed in a few releases if no one screams.

  Other than that, lots of little things. The usual gadget and xhci and
  usb serial driver updates, along with a bunch of sysfs file cleanups
  due to the driver core changes to support that. Nothing really major,
  just constant forward progress.

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'usb-5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (159 commits)
  USB: usbcore: Fix slab-out-of-bounds bug during device reset
  usb: cdns3: Remove redundant dev_err call in cdns3_probe()
  USB: rio500: Fix lockdep violation
  USB: rio500: simplify locking
  usb: mtu3: register a USB Role Switch for dual role mode
  usb: common: add USB GPIO based connection detection driver
  usb: common: create Kconfig file
  usb: roles: get usb-role-switch from parent
  usb: roles: Add fwnode_usb_role_switch_get() function
  device connection: Add fwnode_connection_find_match()
  usb: roles: Introduce stubs for the exiting functions in role.h
  dt-bindings: usb: mtu3: add properties about USB Role Switch
  dt-bindings: usb: add binding for USB GPIO based connection detection driver
  dt-bindings: connector: add optional properties for Type-B
  dt-binding: usb: add usb-role-switch property
  usbip: Implement SG support to vhci-hcd and stub driver
  usb: roles: intel: Enable static DRD mode for role switch
  xhci-ext-caps.c: Add property to disable Intel SW switch
  usb: dwc3: remove generic PHY calibrate() calls
  usb: core: phy: add support for PHY calibration
  ...
This commit is contained in:
Linus Torvalds
2019-09-18 10:33:46 -07:00
310 changed files with 10292 additions and 2445 deletions

View File

@@ -36,8 +36,7 @@ menuconfig USB_SUPPORT
if USB_SUPPORT
config USB_COMMON
tristate
source "drivers/usb/common/Kconfig"
config USB_ARCH_HAS_HCD
def_bool y
@@ -98,8 +97,6 @@ source "drivers/usb/core/Kconfig"
source "drivers/usb/mon/Kconfig"
source "drivers/usb/wusbcore/Kconfig"
source "drivers/usb/host/Kconfig"
source "drivers/usb/renesas_usbhs/Kconfig"
@@ -114,6 +111,8 @@ source "drivers/usb/usbip/Kconfig"
endif
source "drivers/usb/cdns3/Kconfig"
source "drivers/usb/mtu3/Kconfig"
source "drivers/usb/musb/Kconfig"
@@ -175,36 +174,4 @@ source "drivers/usb/typec/Kconfig"
source "drivers/usb/roles/Kconfig"
config USB_LED_TRIG
bool "USB LED Triggers"
depends on LEDS_CLASS && LEDS_TRIGGERS
select USB_COMMON
help
This option adds LED triggers for USB host and/or gadget activity.
Say Y here if you are working on a system with led-class supported
LEDs and you want to use them as activity indicators for USB host or
gadget.
config USB_ULPI_BUS
tristate "USB ULPI PHY interface support"
select USB_COMMON
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.
endif # USB_SUPPORT

View File

@@ -13,6 +13,8 @@ obj-$(CONFIG_USB_DWC3) += dwc3/
obj-$(CONFIG_USB_DWC2) += dwc2/
obj-$(CONFIG_USB_ISP1760) += isp1760/
obj-$(CONFIG_USB_CDNS3) += cdns3/
obj-$(CONFIG_USB_MON) += mon/
obj-$(CONFIG_USB_MTU3) += mtu3/
@@ -35,8 +37,6 @@ obj-$(CONFIG_USB_MAX3421_HCD) += host/
obj-$(CONFIG_USB_C67X00_HCD) += c67x00/
obj-$(CONFIG_USB_WUSB) += wusbcore/
obj-$(CONFIG_USB_ACM) += class/
obj-$(CONFIG_USB_PRINTER) += class/
obj-$(CONFIG_USB_WDM) += class/

View File

@@ -539,6 +539,37 @@ CXACRU_SET_##_action( adsl_config);
CXACRU_ALL_FILES(INIT);
static struct attribute *cxacru_attrs[] = {
&dev_attr_adsl_config.attr,
&dev_attr_adsl_state.attr,
&dev_attr_adsl_controller_version.attr,
&dev_attr_adsl_headend_environment.attr,
&dev_attr_adsl_headend.attr,
&dev_attr_modulation.attr,
&dev_attr_line_startable.attr,
&dev_attr_downstream_hec_errors.attr,
&dev_attr_upstream_hec_errors.attr,
&dev_attr_downstream_fec_errors.attr,
&dev_attr_upstream_fec_errors.attr,
&dev_attr_downstream_crc_errors.attr,
&dev_attr_upstream_crc_errors.attr,
&dev_attr_startup_attempts.attr,
&dev_attr_downstream_bits_per_frame.attr,
&dev_attr_upstream_bits_per_frame.attr,
&dev_attr_transmitter_power.attr,
&dev_attr_downstream_attenuation.attr,
&dev_attr_upstream_attenuation.attr,
&dev_attr_downstream_snr_margin.attr,
&dev_attr_upstream_snr_margin.attr,
&dev_attr_mac_address.attr,
&dev_attr_line_status.attr,
&dev_attr_link_status.attr,
&dev_attr_upstream_rate.attr,
&dev_attr_downstream_rate.attr,
NULL,
};
ATTRIBUTE_GROUPS(cxacru);
/* the following three functions are stolen from drivers/usb/core/message.c */
static void cxacru_blocking_completion(struct urb *urb)
{
@@ -736,17 +767,6 @@ static int cxacru_card_status(struct cxacru_data *instance)
return 0;
}
static void cxacru_remove_device_files(struct usbatm_data *usbatm_instance,
struct atm_dev *atm_dev)
{
struct usb_interface *intf = usbatm_instance->usb_intf;
#define CXACRU_DEVICE_REMOVE_FILE(_name) \
device_remove_file(&intf->dev, &dev_attr_##_name);
CXACRU_ALL_FILES(REMOVE);
#undef CXACRU_DEVICE_REMOVE_FILE
}
static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
struct atm_dev *atm_dev)
{
@@ -765,13 +785,6 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
return ret;
}
#define CXACRU_DEVICE_CREATE_FILE(_name) \
ret = device_create_file(&intf->dev, &dev_attr_##_name); \
if (unlikely(ret)) \
goto fail_sysfs;
CXACRU_ALL_FILES(CREATE);
#undef CXACRU_DEVICE_CREATE_FILE
/* start ADSL */
mutex_lock(&instance->adsl_state_serialize);
ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0);
@@ -804,11 +817,6 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
if (start_polling)
cxacru_poll_status(&instance->poll_work.work);
return 0;
fail_sysfs:
usb_err(usbatm_instance, "cxacru_atm_start: device_create_file failed (%d)\n", ret);
cxacru_remove_device_files(usbatm_instance, atm_dev);
return ret;
}
static void cxacru_poll_status(struct work_struct *work)
@@ -1332,7 +1340,6 @@ static struct usbatm_driver cxacru_driver = {
.heavy_init = cxacru_heavy_init,
.unbind = cxacru_unbind,
.atm_start = cxacru_atm_start,
.atm_stop = cxacru_remove_device_files,
.bulk_in = CXACRU_EP_DATA,
.bulk_out = CXACRU_EP_DATA,
.rx_padding = 3,
@@ -1364,7 +1371,8 @@ static struct usb_driver cxacru_usb_driver = {
.name = cxacru_driver_name,
.probe = cxacru_usb_probe,
.disconnect = usbatm_usb_disconnect,
.id_table = cxacru_usb_ids
.id_table = cxacru_usb_ids,
.dev_groups = cxacru_groups,
};
module_usb_driver(cxacru_usb_driver);

View File

@@ -2458,7 +2458,7 @@ static int claim_interface(struct usb_device *usb_dev,
return ret;
}
static struct attribute *attrs[] = {
static struct attribute *uea_attrs[] = {
&dev_attr_stat_status.attr,
&dev_attr_stat_mflags.attr,
&dev_attr_stat_human_status.attr,
@@ -2479,9 +2479,7 @@ static struct attribute *attrs[] = {
&dev_attr_stat_firmid.attr,
NULL,
};
static const struct attribute_group attr_grp = {
.attrs = attrs,
};
ATTRIBUTE_GROUPS(uea);
static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
const struct usb_device_id *id)
@@ -2550,18 +2548,12 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
}
}
ret = sysfs_create_group(&intf->dev.kobj, &attr_grp);
ret = uea_boot(sc);
if (ret < 0)
goto error;
ret = uea_boot(sc);
if (ret < 0)
goto error_rm_grp;
return 0;
error_rm_grp:
sysfs_remove_group(&intf->dev.kobj, &attr_grp);
error:
kfree(sc);
return ret;
@@ -2571,7 +2563,6 @@ static void uea_unbind(struct usbatm_data *usbatm, struct usb_interface *intf)
{
struct uea_softc *sc = usbatm->driver_data;
sysfs_remove_group(&intf->dev.kobj, &attr_grp);
uea_stop(sc);
kfree(sc);
}
@@ -2721,6 +2712,7 @@ static struct usb_driver uea_driver = {
.id_table = uea_ids,
.probe = uea_probe,
.disconnect = uea_disconnect,
.dev_groups = uea_groups,
};
MODULE_DEVICE_TABLE(usb, uea_ids);

46
drivers/usb/cdns3/Kconfig Normal file
View File

@@ -0,0 +1,46 @@
config USB_CDNS3
tristate "Cadence USB3 Dual-Role Controller"
depends on USB_SUPPORT && (USB || USB_GADGET) && HAS_DMA
select USB_XHCI_PLATFORM if USB_XHCI_HCD
select USB_ROLE_SWITCH
help
Say Y here if your system has a Cadence USB3 dual-role controller.
It supports: dual-role switch, Host-only, and Peripheral-only.
If you choose to build this driver is a dynamically linked
as module, the module will be called cdns3.ko.
if USB_CDNS3
config USB_CDNS3_GADGET
bool "Cadence USB3 device controller"
depends on USB_GADGET=y || USB_GADGET=USB_CDNS3
help
Say Y here to enable device controller functionality of the
Cadence USBSS-DEV driver.
This controller supports FF, HS and SS mode. It doesn't support
LS and SSP mode.
config USB_CDNS3_HOST
bool "Cadence USB3 host controller"
depends on USB=y || USB=USB_CDNS3
help
Say Y here to enable host controller functionality of the
Cadence driver.
Host controller is compliant with XHCI so it will use
standard XHCI driver.
config USB_CDNS3_PCI_WRAP
tristate "Cadence USB3 support on PCIe-based platforms"
depends on USB_PCI && ACPI
default USB_CDNS3
help
If you're using the USBSS Core IP with a PCIe, please say
'Y' or 'M' here.
If you choose to build this driver as module it will
be dynamically linked and module will be called cdns3-pci.ko
endif

View File

@@ -0,0 +1,16 @@
# SPDX-License-Identifier: GPL-2.0
# define_trace.h needs to know how to find our header
CFLAGS_trace.o := -I$(src)
cdns3-y := core.o drd.o
obj-$(CONFIG_USB_CDNS3) += cdns3.o
cdns3-$(CONFIG_USB_CDNS3_GADGET) += gadget.o ep0.o
ifneq ($(CONFIG_USB_CDNS3_GADGET),)
cdns3-$(CONFIG_TRACING) += trace.o
endif
cdns3-$(CONFIG_USB_CDNS3_HOST) += host.o
obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci-wrap.o

View File

@@ -0,0 +1,203 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence USBSS PCI Glue driver
*
* Copyright (C) 2018-2019 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
struct cdns3_wrap {
struct platform_device *plat_dev;
struct resource dev_res[6];
int devfn;
};
#define RES_IRQ_HOST_ID 0
#define RES_IRQ_PERIPHERAL_ID 1
#define RES_IRQ_OTG_ID 2
#define RES_HOST_ID 3
#define RES_DEV_ID 4
#define RES_DRD_ID 5
#define PCI_BAR_HOST 0
#define PCI_BAR_DEV 2
#define PCI_BAR_OTG 0
#define PCI_DEV_FN_HOST_DEVICE 0
#define PCI_DEV_FN_OTG 1
#define PCI_DRIVER_NAME "cdns3-pci-usbss"
#define PLAT_DRIVER_NAME "cdns-usb3"
#define CDNS_VENDOR_ID 0x17cd
#define CDNS_DEVICE_ID 0x0100
static struct pci_dev *cdns3_get_second_fun(struct pci_dev *pdev)
{
struct pci_dev *func;
/*
* Gets the second function.
* It's little tricky, but this platform has two function.
* The fist keeps resources for Host/Device while the second
* keeps resources for DRD/OTG.
*/
func = pci_get_device(pdev->vendor, pdev->device, NULL);
if (unlikely(!func))
return NULL;
if (func->devfn == pdev->devfn) {
func = pci_get_device(pdev->vendor, pdev->device, func);
if (unlikely(!func))
return NULL;
}
return func;
}
static int cdns3_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct platform_device_info plat_info;
struct cdns3_wrap *wrap;
struct resource *res;
struct pci_dev *func;
int err;
/*
* for GADGET/HOST PCI (devfn) function number is 0,
* for OTG PCI (devfn) function number is 1
*/
if (!id || (pdev->devfn != PCI_DEV_FN_HOST_DEVICE &&
pdev->devfn != PCI_DEV_FN_OTG))
return -EINVAL;
func = cdns3_get_second_fun(pdev);
if (unlikely(!func))
return -EINVAL;
err = pcim_enable_device(pdev);
if (err) {
dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", err);
return err;
}
pci_set_master(pdev);
if (pci_is_enabled(func)) {
wrap = pci_get_drvdata(func);
} else {
wrap = kzalloc(sizeof(*wrap), GFP_KERNEL);
if (!wrap) {
pci_disable_device(pdev);
return -ENOMEM;
}
}
res = wrap->dev_res;
if (pdev->devfn == PCI_DEV_FN_HOST_DEVICE) {
/* function 0: host(BAR_0) + device(BAR_1).*/
dev_dbg(&pdev->dev, "Initialize Device resources\n");
res[RES_DEV_ID].start = pci_resource_start(pdev, PCI_BAR_DEV);
res[RES_DEV_ID].end = pci_resource_end(pdev, PCI_BAR_DEV);
res[RES_DEV_ID].name = "dev";
res[RES_DEV_ID].flags = IORESOURCE_MEM;
dev_dbg(&pdev->dev, "USBSS-DEV physical base addr: %pa\n",
&res[RES_DEV_ID].start);
res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST);
res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST);
res[RES_HOST_ID].name = "xhci";
res[RES_HOST_ID].flags = IORESOURCE_MEM;
dev_dbg(&pdev->dev, "USBSS-XHCI physical base addr: %pa\n",
&res[RES_HOST_ID].start);
/* Interrupt for XHCI */
wrap->dev_res[RES_IRQ_HOST_ID].start = pdev->irq;
wrap->dev_res[RES_IRQ_HOST_ID].name = "host";
wrap->dev_res[RES_IRQ_HOST_ID].flags = IORESOURCE_IRQ;
/* Interrupt device. It's the same as for HOST. */
wrap->dev_res[RES_IRQ_PERIPHERAL_ID].start = pdev->irq;
wrap->dev_res[RES_IRQ_PERIPHERAL_ID].name = "peripheral";
wrap->dev_res[RES_IRQ_PERIPHERAL_ID].flags = IORESOURCE_IRQ;
} else {
res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG);
res[RES_DRD_ID].end = pci_resource_end(pdev, PCI_BAR_OTG);
res[RES_DRD_ID].name = "otg";
res[RES_DRD_ID].flags = IORESOURCE_MEM;
dev_dbg(&pdev->dev, "USBSS-DRD physical base addr: %pa\n",
&res[RES_DRD_ID].start);
/* Interrupt for OTG/DRD. */
wrap->dev_res[RES_IRQ_OTG_ID].start = pdev->irq;
wrap->dev_res[RES_IRQ_OTG_ID].name = "otg";
wrap->dev_res[RES_IRQ_OTG_ID].flags = IORESOURCE_IRQ;
}
if (pci_is_enabled(func)) {
/* set up platform device info */
memset(&plat_info, 0, sizeof(plat_info));
plat_info.parent = &pdev->dev;
plat_info.fwnode = pdev->dev.fwnode;
plat_info.name = PLAT_DRIVER_NAME;
plat_info.id = pdev->devfn;
wrap->devfn = pdev->devfn;
plat_info.res = wrap->dev_res;
plat_info.num_res = ARRAY_SIZE(wrap->dev_res);
plat_info.dma_mask = pdev->dma_mask;
/* register platform device */
wrap->plat_dev = platform_device_register_full(&plat_info);
if (IS_ERR(wrap->plat_dev)) {
pci_disable_device(pdev);
kfree(wrap);
return PTR_ERR(wrap->plat_dev);
}
}
pci_set_drvdata(pdev, wrap);
return err;
}
static void cdns3_pci_remove(struct pci_dev *pdev)
{
struct cdns3_wrap *wrap;
struct pci_dev *func;
func = cdns3_get_second_fun(pdev);
wrap = (struct cdns3_wrap *)pci_get_drvdata(pdev);
if (wrap->devfn == pdev->devfn)
platform_device_unregister(wrap->plat_dev);
if (!pci_is_enabled(func))
kfree(wrap);
}
static const struct pci_device_id cdns3_pci_ids[] = {
{ PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), },
{ 0, }
};
static struct pci_driver cdns3_pci_driver = {
.name = PCI_DRIVER_NAME,
.id_table = cdns3_pci_ids,
.probe = cdns3_pci_probe,
.remove = cdns3_pci_remove,
};
module_pci_driver(cdns3_pci_driver);
MODULE_DEVICE_TABLE(pci, cdns3_pci_ids);
MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Cadence USBSS PCI wrapperr");

651
drivers/usb/cdns3/core.c Normal file
View File

@@ -0,0 +1,651 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence USBSS DRD Driver.
*
* Copyright (C) 2018-2019 Cadence.
* Copyright (C) 2017-2018 NXP
* Copyright (C) 2019 Texas Instruments
*
* Author: Peter Chen <peter.chen@nxp.com>
* Pawel Laszczak <pawell@cadence.com>
* Roger Quadros <rogerq@ti.com>
*/
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/pm_runtime.h>
#include "gadget.h"
#include "core.h"
#include "host-export.h"
#include "gadget-export.h"
#include "drd.h"
static int cdns3_idle_init(struct cdns3 *cdns);
static inline
struct cdns3_role_driver *cdns3_get_current_role_driver(struct cdns3 *cdns)
{
WARN_ON(!cdns->roles[cdns->role]);
return cdns->roles[cdns->role];
}
static int cdns3_role_start(struct cdns3 *cdns, enum usb_role role)
{
int ret;
if (WARN_ON(role > USB_ROLE_DEVICE))
return 0;
mutex_lock(&cdns->mutex);
cdns->role = role;
mutex_unlock(&cdns->mutex);
if (!cdns->roles[role])
return -ENXIO;
if (cdns->roles[role]->state == CDNS3_ROLE_STATE_ACTIVE)
return 0;
mutex_lock(&cdns->mutex);
ret = cdns->roles[role]->start(cdns);
if (!ret)
cdns->roles[role]->state = CDNS3_ROLE_STATE_ACTIVE;
mutex_unlock(&cdns->mutex);
return ret;
}
static void cdns3_role_stop(struct cdns3 *cdns)
{
enum usb_role role = cdns->role;
if (WARN_ON(role > USB_ROLE_DEVICE))
return;
if (cdns->roles[role]->state == CDNS3_ROLE_STATE_INACTIVE)
return;
mutex_lock(&cdns->mutex);
cdns->roles[role]->stop(cdns);
cdns->roles[role]->state = CDNS3_ROLE_STATE_INACTIVE;
mutex_unlock(&cdns->mutex);
}
static void cdns3_exit_roles(struct cdns3 *cdns)
{
cdns3_role_stop(cdns);
cdns3_drd_exit(cdns);
}
static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns);
/**
* cdns3_core_init_role - initialize role of operation
* @cdns: Pointer to cdns3 structure
*
* Returns 0 on success otherwise negative errno
*/
static int cdns3_core_init_role(struct cdns3 *cdns)
{
struct device *dev = cdns->dev;
enum usb_dr_mode best_dr_mode;
enum usb_dr_mode dr_mode;
int ret = 0;
dr_mode = usb_get_dr_mode(dev);
cdns->role = USB_ROLE_NONE;
/*
* If driver can't read mode by means of usb_get_dr_mode function then
* chooses mode according with Kernel configuration. This setting
* can be restricted later depending on strap pin configuration.
*/
if (dr_mode == USB_DR_MODE_UNKNOWN) {
if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) &&
IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
dr_mode = USB_DR_MODE_OTG;
else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST))
dr_mode = USB_DR_MODE_HOST;
else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
dr_mode = USB_DR_MODE_PERIPHERAL;
}
/*
* At this point cdns->dr_mode contains strap configuration.
* Driver try update this setting considering kernel configuration
*/
best_dr_mode = cdns->dr_mode;
ret = cdns3_idle_init(cdns);
if (ret)
return ret;
if (dr_mode == USB_DR_MODE_OTG) {
best_dr_mode = cdns->dr_mode;
} else if (cdns->dr_mode == USB_DR_MODE_OTG) {
best_dr_mode = dr_mode;
} else if (cdns->dr_mode != dr_mode) {
dev_err(dev, "Incorrect DRD configuration\n");
return -EINVAL;
}
dr_mode = best_dr_mode;
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
ret = cdns3_host_init(cdns);
if (ret) {
dev_err(dev, "Host initialization failed with %d\n",
ret);
goto err;
}
}
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
ret = cdns3_gadget_init(cdns);
if (ret) {
dev_err(dev, "Device initialization failed with %d\n",
ret);
goto err;
}
}
cdns->dr_mode = dr_mode;
ret = cdns3_drd_update_mode(cdns);
if (ret)
goto err;
if (cdns->dr_mode != USB_DR_MODE_OTG) {
ret = cdns3_hw_role_switch(cdns);
if (ret)
goto err;
}
return ret;
err:
cdns3_exit_roles(cdns);
return ret;
}
/**
* cdsn3_hw_role_state_machine - role switch state machine based on hw events.
* @cdns: Pointer to controller structure.
*
* Returns next role to be entered based on hw events.
*/
static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns)
{
enum usb_role role;
int id, vbus;
if (cdns->dr_mode != USB_DR_MODE_OTG)
goto not_otg;
id = cdns3_get_id(cdns);
vbus = cdns3_get_vbus(cdns);
/*
* Role change state machine
* Inputs: ID, VBUS
* Previous state: cdns->role
* Next state: role
*/
role = cdns->role;
switch (role) {
case USB_ROLE_NONE:
/*
* Driver treats USB_ROLE_NONE synonymous to IDLE state from
* controller specification.
*/
if (!id)
role = USB_ROLE_HOST;
else if (vbus)
role = USB_ROLE_DEVICE;
break;
case USB_ROLE_HOST: /* from HOST, we can only change to NONE */
if (id)
role = USB_ROLE_NONE;
break;
case USB_ROLE_DEVICE: /* from GADGET, we can only change to NONE*/
if (!vbus)
role = USB_ROLE_NONE;
break;
}
dev_dbg(cdns->dev, "role %d -> %d\n", cdns->role, role);
return role;
not_otg:
if (cdns3_is_host(cdns))
role = USB_ROLE_HOST;
if (cdns3_is_device(cdns))
role = USB_ROLE_DEVICE;
return role;
}
static int cdns3_idle_role_start(struct cdns3 *cdns)
{
return 0;
}
static void cdns3_idle_role_stop(struct cdns3 *cdns)
{
/* Program Lane swap and bring PHY out of RESET */
phy_reset(cdns->usb3_phy);
}
static int cdns3_idle_init(struct cdns3 *cdns)
{
struct cdns3_role_driver *rdrv;
rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
if (!rdrv)
return -ENOMEM;
rdrv->start = cdns3_idle_role_start;
rdrv->stop = cdns3_idle_role_stop;
rdrv->state = CDNS3_ROLE_STATE_INACTIVE;
rdrv->suspend = NULL;
rdrv->resume = NULL;
rdrv->name = "idle";
cdns->roles[USB_ROLE_NONE] = rdrv;
return 0;
}
/**
* cdns3_hw_role_switch - switch roles based on HW state
* @cdns3: controller
*/
int cdns3_hw_role_switch(struct cdns3 *cdns)
{
enum usb_role real_role, current_role;
int ret = 0;
/* Do nothing if role based on syfs. */
if (cdns->role_override)
return 0;
pm_runtime_get_sync(cdns->dev);
current_role = cdns->role;
real_role = cdsn3_hw_role_state_machine(cdns);
/* Do nothing if nothing changed */
if (current_role == real_role)
goto exit;
cdns3_role_stop(cdns);
dev_dbg(cdns->dev, "Switching role %d -> %d", current_role, real_role);
ret = cdns3_role_start(cdns, real_role);
if (ret) {
/* Back to current role */
dev_err(cdns->dev, "set %d has failed, back to %d\n",
real_role, current_role);
ret = cdns3_role_start(cdns, current_role);
if (ret)
dev_err(cdns->dev, "back to %d failed too\n",
current_role);
}
exit:
pm_runtime_put_sync(cdns->dev);
return ret;
}
/**
* cdsn3_role_get - get current role of controller.
*
* @dev: Pointer to device structure
*
* Returns role
*/
static enum usb_role cdns3_role_get(struct device *dev)
{
struct cdns3 *cdns = dev_get_drvdata(dev);
return cdns->role;
}
/**
* cdns3_role_set - set current role of controller.
*
* @dev: pointer to device object
* @role - the previous role
* Handles below events:
* - Role switch for dual-role devices
* - USB_ROLE_GADGET <--> USB_ROLE_NONE for peripheral-only devices
*/
static int cdns3_role_set(struct device *dev, enum usb_role role)
{
struct cdns3 *cdns = dev_get_drvdata(dev);
int ret = 0;
pm_runtime_get_sync(cdns->dev);
/*
* FIXME: switch role framework should be extended to meet
* requirements. Driver assumes that role can be controlled
* by SW or HW. Temporary workaround is to use USB_ROLE_NONE to
* switch from SW to HW control.
*
* For dr_mode == USB_DR_MODE_OTG:
* if user sets USB_ROLE_HOST or USB_ROLE_DEVICE then driver
* sets role_override flag and forces that role.
* if user sets USB_ROLE_NONE, driver clears role_override and lets
* HW state machine take over.
*
* For dr_mode != USB_DR_MODE_OTG:
* Assumptions:
* 1. Restricted user control between NONE and dr_mode.
* 2. Driver doesn't need to rely on role_override flag.
* 3. Driver needs to ensure that HW state machine is never called
* if dr_mode != USB_DR_MODE_OTG.
*/
if (role == USB_ROLE_NONE)
cdns->role_override = 0;
else
cdns->role_override = 1;
/*
* HW state might have changed so driver need to trigger
* HW state machine if dr_mode == USB_DR_MODE_OTG.
*/
if (!cdns->role_override && cdns->dr_mode == USB_DR_MODE_OTG) {
cdns3_hw_role_switch(cdns);
goto pm_put;
}
if (cdns->role == role)
goto pm_put;
if (cdns->dr_mode == USB_DR_MODE_HOST) {
switch (role) {
case USB_ROLE_NONE:
case USB_ROLE_HOST:
break;
default:
ret = -EPERM;
goto pm_put;
}
}
if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL) {
switch (role) {
case USB_ROLE_NONE:
case USB_ROLE_DEVICE:
break;
default:
ret = -EPERM;
goto pm_put;
}
}
cdns3_role_stop(cdns);
ret = cdns3_role_start(cdns, role);
if (ret) {
dev_err(cdns->dev, "set role %d has failed\n", role);
ret = -EPERM;
}
pm_put:
pm_runtime_put_sync(cdns->dev);
return ret;
}
static const struct usb_role_switch_desc cdns3_switch_desc = {
.set = cdns3_role_set,
.get = cdns3_role_get,
.allow_userspace_control = true,
};
/**
* cdns3_probe - probe for cdns3 core device
* @pdev: Pointer to cdns3 core platform device
*
* Returns 0 on success otherwise negative errno
*/
static int cdns3_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
struct cdns3 *cdns;
void __iomem *regs;
int ret;
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(dev, "error setting dma mask: %d\n", ret);
return -ENODEV;
}
cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
if (!cdns)
return -ENOMEM;
cdns->dev = dev;
platform_set_drvdata(pdev, cdns);
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "host");
if (!res) {
dev_err(dev, "missing host IRQ\n");
return -ENODEV;
}
cdns->xhci_res[0] = *res;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "xhci");
if (!res) {
dev_err(dev, "couldn't get xhci resource\n");
return -ENXIO;
}
cdns->xhci_res[1] = *res;
cdns->dev_irq = platform_get_irq_byname(pdev, "peripheral");
if (cdns->dev_irq == -EPROBE_DEFER)
return cdns->dev_irq;
if (cdns->dev_irq < 0)
dev_err(dev, "couldn't get peripheral irq\n");
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dev");
regs = devm_ioremap_resource(dev, res);
if (IS_ERR(regs))
return PTR_ERR(regs);
cdns->dev_regs = regs;
cdns->otg_irq = platform_get_irq_byname(pdev, "otg");
if (cdns->otg_irq == -EPROBE_DEFER)
return cdns->otg_irq;
if (cdns->otg_irq < 0) {
dev_err(dev, "couldn't get otg irq\n");
return cdns->otg_irq;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otg");
if (!res) {
dev_err(dev, "couldn't get otg resource\n");
return -ENXIO;
}
cdns->otg_res = *res;
mutex_init(&cdns->mutex);
cdns->usb2_phy = devm_phy_optional_get(dev, "cdns3,usb2-phy");
if (IS_ERR(cdns->usb2_phy))
return PTR_ERR(cdns->usb2_phy);
ret = phy_init(cdns->usb2_phy);
if (ret)
return ret;
cdns->usb3_phy = devm_phy_optional_get(dev, "cdns3,usb3-phy");
if (IS_ERR(cdns->usb3_phy))
return PTR_ERR(cdns->usb3_phy);
ret = phy_init(cdns->usb3_phy);
if (ret)
goto err1;
ret = phy_power_on(cdns->usb2_phy);
if (ret)
goto err2;
ret = phy_power_on(cdns->usb3_phy);
if (ret)
goto err3;
cdns->role_sw = usb_role_switch_register(dev, &cdns3_switch_desc);
if (IS_ERR(cdns->role_sw)) {
ret = PTR_ERR(cdns->role_sw);
dev_warn(dev, "Unable to register Role Switch\n");
goto err4;
}
ret = cdns3_drd_init(cdns);
if (ret)
goto err5;
ret = cdns3_core_init_role(cdns);
if (ret)
goto err5;
device_set_wakeup_capable(dev, true);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
/*
* The controller needs less time between bus and controller suspend,
* and we also needs a small delay to avoid frequently entering low
* power mode.
*/
pm_runtime_set_autosuspend_delay(dev, 20);
pm_runtime_mark_last_busy(dev);
pm_runtime_use_autosuspend(dev);
dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
return 0;
err5:
cdns3_drd_exit(cdns);
usb_role_switch_unregister(cdns->role_sw);
err4:
phy_power_off(cdns->usb3_phy);
err3:
phy_power_off(cdns->usb2_phy);
err2:
phy_exit(cdns->usb3_phy);
err1:
phy_exit(cdns->usb2_phy);
return ret;
}
/**
* cdns3_remove - unbind drd driver and clean up
* @pdev: Pointer to Linux platform device
*
* Returns 0 on success otherwise negative errno
*/
static int cdns3_remove(struct platform_device *pdev)
{
struct cdns3 *cdns = platform_get_drvdata(pdev);
pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
cdns3_exit_roles(cdns);
usb_role_switch_unregister(cdns->role_sw);
phy_power_off(cdns->usb2_phy);
phy_power_off(cdns->usb3_phy);
phy_exit(cdns->usb2_phy);
phy_exit(cdns->usb3_phy);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int cdns3_suspend(struct device *dev)
{
struct cdns3 *cdns = dev_get_drvdata(dev);
unsigned long flags;
if (cdns->role == USB_ROLE_HOST)
return 0;
if (pm_runtime_status_suspended(dev))
pm_runtime_resume(dev);
if (cdns->roles[cdns->role]->suspend) {
spin_lock_irqsave(&cdns->gadget_dev->lock, flags);
cdns->roles[cdns->role]->suspend(cdns, false);
spin_unlock_irqrestore(&cdns->gadget_dev->lock, flags);
}
return 0;
}
static int cdns3_resume(struct device *dev)
{
struct cdns3 *cdns = dev_get_drvdata(dev);
unsigned long flags;
if (cdns->role == USB_ROLE_HOST)
return 0;
if (cdns->roles[cdns->role]->resume) {
spin_lock_irqsave(&cdns->gadget_dev->lock, flags);
cdns->roles[cdns->role]->resume(cdns, false);
spin_unlock_irqrestore(&cdns->gadget_dev->lock, flags);
}
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
return 0;
}
#endif
static const struct dev_pm_ops cdns3_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(cdns3_suspend, cdns3_resume)
};
#ifdef CONFIG_OF
static const struct of_device_id of_cdns3_match[] = {
{ .compatible = "cdns,usb3" },
{ },
};
MODULE_DEVICE_TABLE(of, of_cdns3_match);
#endif
static struct platform_driver cdns3_driver = {
.probe = cdns3_probe,
.remove = cdns3_remove,
.driver = {
.name = "cdns-usb3",
.of_match_table = of_match_ptr(of_cdns3_match),
.pm = &cdns3_pm_ops,
},
};
module_platform_driver(cdns3_driver);
MODULE_ALIAS("platform:cdns3");
MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Cadence USB3 DRD Controller Driver");

98
drivers/usb/cdns3/core.h Normal file
View File

@@ -0,0 +1,98 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cadence USBSS DRD Header File.
*
* Copyright (C) 2017-2018 NXP
* Copyright (C) 2018-2019 Cadence.
*
* Authors: Peter Chen <peter.chen@nxp.com>
* Pawel Laszczak <pawell@cadence.com>
*/
#include <linux/usb/otg.h>
#include <linux/usb/role.h>
#ifndef __LINUX_CDNS3_CORE_H
#define __LINUX_CDNS3_CORE_H
struct cdns3;
/**
* struct cdns3_role_driver - host/gadget role driver
* @start: start this role
* @stop: stop this role
* @suspend: suspend callback for this role
* @resume: resume callback for this role
* @irq: irq handler for this role
* @name: role name string (host/gadget)
* @state: current state
*/
struct cdns3_role_driver {
int (*start)(struct cdns3 *cdns);
void (*stop)(struct cdns3 *cdns);
int (*suspend)(struct cdns3 *cdns, bool do_wakeup);
int (*resume)(struct cdns3 *cdns, bool hibernated);
const char *name;
#define CDNS3_ROLE_STATE_INACTIVE 0
#define CDNS3_ROLE_STATE_ACTIVE 1
int state;
};
#define CDNS3_XHCI_RESOURCES_NUM 2
/**
* struct cdns3 - Representation of Cadence USB3 DRD controller.
* @dev: pointer to Cadence device struct
* @xhci_regs: pointer to base of xhci registers
* @xhci_res: the resource for xhci
* @dev_regs: pointer to base of dev registers
* @otg_res: the resource for otg
* @otg_v0_regs: pointer to base of v0 otg registers
* @otg_v1_regs: pointer to base of v1 otg registers
* @otg_regs: pointer to base of otg registers
* @otg_irq: irq number for otg controller
* @dev_irq: irq number for device controller
* @roles: array of supported roles for this controller
* @role: current role
* @host_dev: the child host device pointer for cdns3 core
* @gadget_dev: the child gadget device pointer for cdns3 core
* @usb2_phy: pointer to USB2 PHY
* @usb3_phy: pointer to USB3 PHY
* @mutex: the mutex for concurrent code at driver
* @dr_mode: supported mode of operation it can be only Host, only Device
* or OTG mode that allow to switch between Device and Host mode.
* This field based on firmware setting, kernel configuration
* and hardware configuration.
* @role_sw: pointer to role switch object.
* @role_override: set 1 if role rely on SW.
*/
struct cdns3 {
struct device *dev;
void __iomem *xhci_regs;
struct resource xhci_res[CDNS3_XHCI_RESOURCES_NUM];
struct cdns3_usb_regs __iomem *dev_regs;
struct resource otg_res;
struct cdns3_otg_legacy_regs *otg_v0_regs;
struct cdns3_otg_regs *otg_v1_regs;
struct cdns3_otg_common_regs *otg_regs;
#define CDNS3_CONTROLLER_V0 0
#define CDNS3_CONTROLLER_V1 1
u32 version;
int otg_irq;
int dev_irq;
struct cdns3_role_driver *roles[USB_ROLE_DEVICE + 1];
enum usb_role role;
struct platform_device *host_dev;
struct cdns3_device *gadget_dev;
struct phy *usb2_phy;
struct phy *usb3_phy;
/* mutext used in workqueue*/
struct mutex mutex;
enum usb_dr_mode dr_mode;
struct usb_role_switch *role_sw;
int role_override;
};
int cdns3_hw_role_switch(struct cdns3 *cdns);
#endif /* __LINUX_CDNS3_CORE_H */

161
drivers/usb/cdns3/debug.h Normal file
View File

@@ -0,0 +1,161 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cadence USBSS DRD Driver.
* Debug header file.
*
* Copyright (C) 2018-2019 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
#ifndef __LINUX_CDNS3_DEBUG
#define __LINUX_CDNS3_DEBUG
#include "core.h"
static inline char *cdns3_decode_usb_irq(char *str,
enum usb_device_speed speed,
u32 usb_ists)
{
int ret;
ret = sprintf(str, "IRQ %08x = ", usb_ists);
if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
ret += sprintf(str + ret, "Connection %s\n",
usb_speed_string(speed));
}
if (usb_ists & USB_ISTS_DIS2I || usb_ists & USB_ISTS_DISI)
ret += sprintf(str + ret, "Disconnection ");
if (usb_ists & USB_ISTS_L2ENTI)
ret += sprintf(str + ret, "suspended ");
if (usb_ists & USB_ISTS_L1ENTI)
ret += sprintf(str + ret, "L1 enter ");
if (usb_ists & USB_ISTS_L1EXTI)
ret += sprintf(str + ret, "L1 exit ");
if (usb_ists & USB_ISTS_L2ENTI)
ret += sprintf(str + ret, "L2 enter ");
if (usb_ists & USB_ISTS_L2EXTI)
ret += sprintf(str + ret, "L2 exit ");
if (usb_ists & USB_ISTS_U3EXTI)
ret += sprintf(str + ret, "U3 exit ");
if (usb_ists & USB_ISTS_UWRESI)
ret += sprintf(str + ret, "Warm Reset ");
if (usb_ists & USB_ISTS_UHRESI)
ret += sprintf(str + ret, "Hot Reset ");
if (usb_ists & USB_ISTS_U2RESI)
ret += sprintf(str + ret, "Reset");
return str;
}
static inline char *cdns3_decode_ep_irq(char *str,
u32 ep_sts,
const char *ep_name)
{
int ret;
ret = sprintf(str, "IRQ for %s: %08x ", ep_name, ep_sts);
if (ep_sts & EP_STS_SETUP)
ret += sprintf(str + ret, "SETUP ");
if (ep_sts & EP_STS_IOC)
ret += sprintf(str + ret, "IOC ");
if (ep_sts & EP_STS_ISP)
ret += sprintf(str + ret, "ISP ");
if (ep_sts & EP_STS_DESCMIS)
ret += sprintf(str + ret, "DESCMIS ");
if (ep_sts & EP_STS_STREAMR)
ret += sprintf(str + ret, "STREAMR ");
if (ep_sts & EP_STS_MD_EXIT)
ret += sprintf(str + ret, "MD_EXIT ");
if (ep_sts & EP_STS_TRBERR)
ret += sprintf(str + ret, "TRBERR ");
if (ep_sts & EP_STS_NRDY)
ret += sprintf(str + ret, "NRDY ");
if (ep_sts & EP_STS_PRIME)
ret += sprintf(str + ret, "PRIME ");
if (ep_sts & EP_STS_SIDERR)
ret += sprintf(str + ret, "SIDERRT ");
if (ep_sts & EP_STS_OUTSMM)
ret += sprintf(str + ret, "OUTSMM ");
if (ep_sts & EP_STS_ISOERR)
ret += sprintf(str + ret, "ISOERR ");
if (ep_sts & EP_STS_IOT)
ret += sprintf(str + ret, "IOT ");
return str;
}
static inline char *cdns3_decode_epx_irq(char *str,
char *ep_name,
u32 ep_sts)
{
return cdns3_decode_ep_irq(str, ep_sts, ep_name);
}
static inline char *cdns3_decode_ep0_irq(char *str,
int dir,
u32 ep_sts)
{
return cdns3_decode_ep_irq(str, ep_sts,
dir ? "ep0IN" : "ep0OUT");
}
/**
* Debug a transfer ring.
*
* Prints out all TRBs in the endpoint ring, even those after the Link TRB.
*.
*/
static inline char *cdns3_dbg_ring(struct cdns3_endpoint *priv_ep,
struct cdns3_trb *ring, char *str)
{
dma_addr_t addr = priv_ep->trb_pool_dma;
struct cdns3_trb *trb;
int trb_per_sector;
int ret = 0;
int i;
trb_per_sector = GET_TRBS_PER_SEGMENT(priv_ep->type);
trb = &priv_ep->trb_pool[priv_ep->dequeue];
ret += sprintf(str + ret, "\n\t\tRing contents for %s:", priv_ep->name);
ret += sprintf(str + ret,
"\n\t\tRing deq index: %d, trb: %p (virt), 0x%llx (dma)\n",
priv_ep->dequeue, trb,
(unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
trb = &priv_ep->trb_pool[priv_ep->enqueue];
ret += sprintf(str + ret,
"\t\tRing enq index: %d, trb: %p (virt), 0x%llx (dma)\n",
priv_ep->enqueue, trb,
(unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
ret += sprintf(str + ret,
"\t\tfree trbs: %d, CCS=%d, PCS=%d\n",
priv_ep->free_trbs, priv_ep->ccs, priv_ep->pcs);
if (trb_per_sector > TRBS_PER_SEGMENT)
trb_per_sector = TRBS_PER_SEGMENT;
if (trb_per_sector > TRBS_PER_SEGMENT) {
sprintf(str + ret, "\t\tTo big transfer ring %d\n",
trb_per_sector);
return str;
}
for (i = 0; i < trb_per_sector; ++i) {
trb = &ring[i];
ret += sprintf(str + ret,
"\t\t@%pad %08x %08x %08x\n", &addr,
le32_to_cpu(trb->buffer),
le32_to_cpu(trb->length),
le32_to_cpu(trb->control));
addr += sizeof(*trb);
}
return str;
}
#endif /*__LINUX_CDNS3_DEBUG*/

381
drivers/usb/cdns3/drd.c Normal file
View File

@@ -0,0 +1,381 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence USBSS DRD Driver.
*
* Copyright (C) 2018-2019 Cadence.
* Copyright (C) 2019 Texas Instruments
*
* Author: Pawel Laszczak <pawell@cadence.com>
* Roger Quadros <rogerq@ti.com>
*
*
*/
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <linux/usb/otg.h>
#include "gadget.h"
#include "drd.h"
#include "core.h"
/**
* cdns3_set_mode - change mode of OTG Core
* @cdns: pointer to context structure
* @mode: selected mode from cdns_role
*
* Returns 0 on success otherwise negative errno
*/
int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
{
int ret = 0;
u32 reg;
switch (mode) {
case USB_DR_MODE_PERIPHERAL:
break;
case USB_DR_MODE_HOST:
break;
case USB_DR_MODE_OTG:
dev_dbg(cdns->dev, "Set controller to OTG mode\n");
if (cdns->version == CDNS3_CONTROLLER_V1) {
reg = readl(&cdns->otg_v1_regs->override);
reg |= OVERRIDE_IDPULLUP;
writel(reg, &cdns->otg_v1_regs->override);
} else {
reg = readl(&cdns->otg_v0_regs->ctrl1);
reg |= OVERRIDE_IDPULLUP_V0;
writel(reg, &cdns->otg_v0_regs->ctrl1);
}
/*
* Hardware specification says: "ID_VALUE must be valid within
* 50ms after idpullup is set to '1" so driver must wait
* 50ms before reading this pin.
*/
usleep_range(50000, 60000);
break;
default:
dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode);
return -EINVAL;
}
return ret;
}
int cdns3_get_id(struct cdns3 *cdns)
{
int id;
id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE;
dev_dbg(cdns->dev, "OTG ID: %d", id);
return id;
}
int cdns3_get_vbus(struct cdns3 *cdns)
{
int vbus;
vbus = !!(readl(&cdns->otg_regs->sts) & OTGSTS_VBUS_VALID);
dev_dbg(cdns->dev, "OTG VBUS: %d", vbus);
return vbus;
}
int cdns3_is_host(struct cdns3 *cdns)
{
if (cdns->dr_mode == USB_DR_MODE_HOST)
return 1;
else if (!cdns3_get_id(cdns))
return 1;
return 0;
}
int cdns3_is_device(struct cdns3 *cdns)
{
if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL)
return 1;
else if (cdns->dr_mode == USB_DR_MODE_OTG)
if (cdns3_get_id(cdns))
return 1;
return 0;
}
/**
* cdns3_otg_disable_irq - Disable all OTG interrupts
* @cdns: Pointer to controller context structure
*/
static void cdns3_otg_disable_irq(struct cdns3 *cdns)
{
writel(0, &cdns->otg_regs->ien);
}
/**
* cdns3_otg_enable_irq - enable id and sess_valid interrupts
* @cdns: Pointer to controller context structure
*/
static void cdns3_otg_enable_irq(struct cdns3 *cdns)
{
writel(OTGIEN_ID_CHANGE_INT | OTGIEN_VBUSVALID_RISE_INT |
OTGIEN_VBUSVALID_FALL_INT, &cdns->otg_regs->ien);
}
/**
* cdns3_drd_switch_host - start/stop host
* @cdns: Pointer to controller context structure
* @on: 1 for start, 0 for stop
*
* Returns 0 on success otherwise negative errno
*/
int cdns3_drd_switch_host(struct cdns3 *cdns, int on)
{
int ret, val;
u32 reg = OTGCMD_OTG_DIS;
/* switch OTG core */
if (on) {
writel(OTGCMD_HOST_BUS_REQ | reg, &cdns->otg_regs->cmd);
dev_dbg(cdns->dev, "Waiting till Host mode is turned on\n");
ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
val & OTGSTS_XHCI_READY,
1, 100000);
if (ret) {
dev_err(cdns->dev, "timeout waiting for xhci_ready\n");
return ret;
}
} else {
writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
&cdns->otg_regs->cmd);
/* Waiting till H_IDLE state.*/
readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
!(val & OTGSTATE_HOST_STATE_MASK),
1, 2000000);
}
return 0;
}
/**
* cdns3_drd_switch_gadget - start/stop gadget
* @cdns: Pointer to controller context structure
* @on: 1 for start, 0 for stop
*
* Returns 0 on success otherwise negative errno
*/
int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on)
{
int ret, val;
u32 reg = OTGCMD_OTG_DIS;
/* switch OTG core */
if (on) {
writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd);
dev_dbg(cdns->dev, "Waiting till Device mode is turned on\n");
ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
val & OTGSTS_DEV_READY,
1, 100000);
if (ret) {
dev_err(cdns->dev, "timeout waiting for dev_ready\n");
return ret;
}
} else {
/*
* driver should wait at least 10us after disabling Device
* before turning-off Device (DEV_BUS_DROP)
*/
usleep_range(20, 30);
writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
&cdns->otg_regs->cmd);
/* Waiting till DEV_IDLE state.*/
readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
!(val & OTGSTATE_DEV_STATE_MASK),
1, 2000000);
}
return 0;
}
/**
* cdns3_init_otg_mode - initialize drd controller
* @cdns: Pointer to controller context structure
*
* Returns 0 on success otherwise negative errno
*/
static int cdns3_init_otg_mode(struct cdns3 *cdns)
{
int ret = 0;
cdns3_otg_disable_irq(cdns);
/* clear all interrupts */
writel(~0, &cdns->otg_regs->ivect);
ret = cdns3_set_mode(cdns, USB_DR_MODE_OTG);
if (ret)
return ret;
cdns3_otg_enable_irq(cdns);
return ret;
}
/**
* cdns3_drd_update_mode - initialize mode of operation
* @cdns: Pointer to controller context structure
*
* Returns 0 on success otherwise negative errno
*/
int cdns3_drd_update_mode(struct cdns3 *cdns)
{
int ret = 0;
switch (cdns->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
ret = cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
break;
case USB_DR_MODE_HOST:
ret = cdns3_set_mode(cdns, USB_DR_MODE_HOST);
break;
case USB_DR_MODE_OTG:
ret = cdns3_init_otg_mode(cdns);
break;
default:
dev_err(cdns->dev, "Unsupported mode of operation %d\n",
cdns->dr_mode);
return -EINVAL;
}
return ret;
}
static irqreturn_t cdns3_drd_thread_irq(int irq, void *data)
{
struct cdns3 *cdns = data;
cdns3_hw_role_switch(cdns);
return IRQ_HANDLED;
}
/**
* cdns3_drd_irq - interrupt handler for OTG events
*
* @irq: irq number for cdns3 core device
* @data: structure of cdns3
*
* Returns IRQ_HANDLED or IRQ_NONE
*/
static irqreturn_t cdns3_drd_irq(int irq, void *data)
{
irqreturn_t ret = IRQ_NONE;
struct cdns3 *cdns = data;
u32 reg;
if (cdns->dr_mode != USB_DR_MODE_OTG)
return ret;
reg = readl(&cdns->otg_regs->ivect);
if (!reg)
return ret;
if (reg & OTGIEN_ID_CHANGE_INT) {
dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
cdns3_get_id(cdns));
ret = IRQ_WAKE_THREAD;
}
if (reg & (OTGIEN_VBUSVALID_RISE_INT | OTGIEN_VBUSVALID_FALL_INT)) {
dev_dbg(cdns->dev, "OTG IRQ: new VBUS: %d\n",
cdns3_get_vbus(cdns));
ret = IRQ_WAKE_THREAD;
}
writel(~0, &cdns->otg_regs->ivect);
return ret;
}
int cdns3_drd_init(struct cdns3 *cdns)
{
void __iomem *regs;
int ret = 0;
u32 state;
regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
if (IS_ERR(regs))
return PTR_ERR(regs);
/* Detection of DRD version. Controller has been released
* in two versions. Both are similar, but they have same changes
* in register maps.
* The first register in old version is command register and it's read
* only, so driver should read 0 from it. On the other hand, in v1
* the first register contains device ID number which is not set to 0.
* Driver uses this fact to detect the proper version of
* controller.
*/
cdns->otg_v0_regs = regs;
if (!readl(&cdns->otg_v0_regs->cmd)) {
cdns->version = CDNS3_CONTROLLER_V0;
cdns->otg_v1_regs = NULL;
cdns->otg_regs = regs;
writel(1, &cdns->otg_v0_regs->simulate);
dev_info(cdns->dev, "DRD version v0 (%08x)\n",
readl(&cdns->otg_v0_regs->version));
} else {
cdns->otg_v0_regs = NULL;
cdns->otg_v1_regs = regs;
cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
cdns->version = CDNS3_CONTROLLER_V1;
writel(1, &cdns->otg_v1_regs->simulate);
dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
readl(&cdns->otg_v1_regs->did),
readl(&cdns->otg_v1_regs->rid));
}
state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts));
/* Update dr_mode according to STRAP configuration. */
cdns->dr_mode = USB_DR_MODE_OTG;
if (state == OTGSTS_STRAP_HOST) {
dev_dbg(cdns->dev, "Controller strapped to HOST\n");
cdns->dr_mode = USB_DR_MODE_HOST;
} else if (state == OTGSTS_STRAP_GADGET) {
dev_dbg(cdns->dev, "Controller strapped to PERIPHERAL\n");
cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
}
ret = devm_request_threaded_irq(cdns->dev, cdns->otg_irq,
cdns3_drd_irq,
cdns3_drd_thread_irq,
IRQF_SHARED,
dev_name(cdns->dev), cdns);
if (ret) {
dev_err(cdns->dev, "couldn't get otg_irq\n");
return ret;
}
state = readl(&cdns->otg_regs->sts);
if (OTGSTS_OTG_NRDY(state) != 0) {
dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
return -ENODEV;
}
return ret;
}
int cdns3_drd_exit(struct cdns3 *cdns)
{
cdns3_otg_disable_irq(cdns);
return 0;
}

167
drivers/usb/cdns3/drd.h Normal file
View File

@@ -0,0 +1,167 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cadence USB3 DRD header file.
*
* Copyright (C) 2018-2019 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
#ifndef __LINUX_CDNS3_DRD
#define __LINUX_CDNS3_DRD
#include <linux/usb/otg.h>
#include <linux/phy/phy.h>
#include "core.h"
/* DRD register interface for version v1. */
struct cdns3_otg_regs {
__le32 did;
__le32 rid;
__le32 capabilities;
__le32 reserved1;
__le32 cmd;
__le32 sts;
__le32 state;
__le32 reserved2;
__le32 ien;
__le32 ivect;
__le32 refclk;
__le32 tmr;
__le32 reserved3[4];
__le32 simulate;
__le32 override;
__le32 susp_ctrl;
__le32 reserved4;
__le32 anasts;
__le32 adp_ramp_time;
__le32 ctrl1;
__le32 ctrl2;
};
/* DRD register interface for version v0. */
struct cdns3_otg_legacy_regs {
__le32 cmd;
__le32 sts;
__le32 state;
__le32 refclk;
__le32 ien;
__le32 ivect;
__le32 reserved1[3];
__le32 tmr;
__le32 reserved2[2];
__le32 version;
__le32 capabilities;
__le32 reserved3[2];
__le32 simulate;
__le32 reserved4[5];
__le32 ctrl1;
};
/*
* Common registers interface for both version of DRD.
*/
struct cdns3_otg_common_regs {
__le32 cmd;
__le32 sts;
__le32 state;
__le32 different1;
__le32 ien;
__le32 ivect;
};
/* CDNS_RID - bitmasks */
#define CDNS_RID(p) ((p) & GENMASK(15, 0))
/* CDNS_VID - bitmasks */
#define CDNS_DID(p) ((p) & GENMASK(31, 0))
/* OTGCMD - bitmasks */
/* "Request the bus for Device mode. */
#define OTGCMD_DEV_BUS_REQ BIT(0)
/* Request the bus for Host mode */
#define OTGCMD_HOST_BUS_REQ BIT(1)
/* Enable OTG mode. */
#define OTGCMD_OTG_EN BIT(2)
/* Disable OTG mode */
#define OTGCMD_OTG_DIS BIT(3)
/*"Configure OTG as A-Device. */
#define OTGCMD_A_DEV_EN BIT(4)
/*"Configure OTG as A-Device. */
#define OTGCMD_A_DEV_DIS BIT(5)
/* Drop the bus for Device mod e. */
#define OTGCMD_DEV_BUS_DROP BIT(8)
/* Drop the bus for Host mode*/
#define OTGCMD_HOST_BUS_DROP BIT(9)
/* Power Down USBSS-DEV. */
#define OTGCMD_DEV_POWER_OFF BIT(11)
/* Power Down CDNSXHCI. */
#define OTGCMD_HOST_POWER_OFF BIT(12)
/* OTGIEN - bitmasks */
/* ID change interrupt enable */
#define OTGIEN_ID_CHANGE_INT BIT(0)
/* Vbusvalid fall detected interrupt enable.*/
#define OTGIEN_VBUSVALID_RISE_INT BIT(4)
/* Vbusvalid fall detected interrupt enable */
#define OTGIEN_VBUSVALID_FALL_INT BIT(5)
/* OTGSTS - bitmasks */
/*
* Current value of the ID pin. It is only valid when idpullup in
* OTGCTRL1_TYPE register is set to '1'.
*/
#define OTGSTS_ID_VALUE BIT(0)
/* Current value of the vbus_valid */
#define OTGSTS_VBUS_VALID BIT(1)
/* Current value of the b_sess_vld */
#define OTGSTS_SESSION_VALID BIT(2)
/*Device mode is active*/
#define OTGSTS_DEV_ACTIVE BIT(3)
/* Host mode is active. */
#define OTGSTS_HOST_ACTIVE BIT(4)
/* OTG Controller not ready. */
#define OTGSTS_OTG_NRDY_MASK BIT(11)
#define OTGSTS_OTG_NRDY(p) ((p) & OTGSTS_OTG_NRDY_MASK)
/*
* Value of the strap pins.
* 000 - no default configuration
* 010 - Controller initiall configured as Host
* 100 - Controller initially configured as Device
*/
#define OTGSTS_STRAP(p) (((p) & GENMASK(14, 12)) >> 12)
#define OTGSTS_STRAP_NO_DEFAULT_CFG 0x00
#define OTGSTS_STRAP_HOST_OTG 0x01
#define OTGSTS_STRAP_HOST 0x02
#define OTGSTS_STRAP_GADGET 0x04
/* Host mode is turned on. */
#define OTGSTS_XHCI_READY BIT(26)
/* "Device mode is turned on .*/
#define OTGSTS_DEV_READY BIT(27)
/* OTGSTATE- bitmasks */
#define OTGSTATE_DEV_STATE_MASK GENMASK(2, 0)
#define OTGSTATE_HOST_STATE_MASK GENMASK(5, 3)
#define OTGSTATE_HOST_STATE_IDLE 0x0
#define OTGSTATE_HOST_STATE_VBUS_FALL 0x7
#define OTGSTATE_HOST_STATE(p) (((p) & OTGSTATE_HOST_STATE_MASK) >> 3)
/* OTGREFCLK - bitmasks */
#define OTGREFCLK_STB_CLK_SWITCH_EN BIT(31)
/* OVERRIDE - bitmasks */
#define OVERRIDE_IDPULLUP BIT(0)
/* Only for CDNS3_CONTROLLER_V0 version */
#define OVERRIDE_IDPULLUP_V0 BIT(24)
int cdns3_is_host(struct cdns3 *cdns);
int cdns3_is_device(struct cdns3 *cdns);
int cdns3_get_id(struct cdns3 *cdns);
int cdns3_get_vbus(struct cdns3 *cdns);
int cdns3_drd_init(struct cdns3 *cdns);
int cdns3_drd_exit(struct cdns3 *cdns);
int cdns3_drd_update_mode(struct cdns3 *cdns);
int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode);
#endif /* __LINUX_CDNS3_DRD */

886
drivers/usb/cdns3/ep0.c Normal file
View File

@@ -0,0 +1,886 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence USBSS DRD Driver - gadget side.
*
* Copyright (C) 2018 Cadence Design Systems.
* Copyright (C) 2017-2018 NXP
*
* Authors: Pawel Jez <pjez@cadence.com>,
* Pawel Laszczak <pawell@cadence.com>
* Peter Chen <peter.chen@nxp.com>
*/
#include <linux/usb/composite.h>
#include <linux/iopoll.h>
#include "gadget.h"
#include "trace.h"
static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_CONTROL,
};
/**
* cdns3_ep0_run_transfer - Do transfer on default endpoint hardware
* @priv_dev: extended gadget object
* @dma_addr: physical address where data is/will be stored
* @length: data length
* @erdy: set it to 1 when ERDY packet should be sent -
* exit from flow control state
*/
static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
dma_addr_t dma_addr,
unsigned int length, int erdy, int zlp)
{
struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
priv_ep->trb_pool[0].buffer = TRB_BUFFER(dma_addr);
priv_ep->trb_pool[0].length = TRB_LEN(length);
if (zlp) {
priv_ep->trb_pool[0].control = TRB_CYCLE | TRB_TYPE(TRB_NORMAL);
priv_ep->trb_pool[1].buffer = TRB_BUFFER(dma_addr);
priv_ep->trb_pool[1].length = TRB_LEN(0);
priv_ep->trb_pool[1].control = TRB_CYCLE | TRB_IOC |
TRB_TYPE(TRB_NORMAL);
} else {
priv_ep->trb_pool[0].control = TRB_CYCLE | TRB_IOC |
TRB_TYPE(TRB_NORMAL);
priv_ep->trb_pool[1].control = 0;
}
trace_cdns3_prepare_trb(priv_ep, priv_ep->trb_pool);
cdns3_select_ep(priv_dev, priv_dev->ep0_data_dir);
writel(EP_STS_TRBERR, &regs->ep_sts);
writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma), &regs->ep_traddr);
trace_cdns3_doorbell_ep0(priv_dev->ep0_data_dir ? "ep0in" : "ep0out",
readl(&regs->ep_traddr));
/* TRB should be prepared before starting transfer. */
writel(EP_CMD_DRDY, &regs->ep_cmd);
/* Resume controller before arming transfer. */
__cdns3_gadget_wakeup(priv_dev);
if (erdy)
writel(EP_CMD_ERDY, &priv_dev->regs->ep_cmd);
}
/**
* cdns3_ep0_delegate_req - Returns status of handling setup packet
* Setup is handled by gadget driver
* @priv_dev: extended gadget object
* @ctrl_req: pointer to received setup packet
*
* Returns zero on success or negative value on failure
*/
static int cdns3_ep0_delegate_req(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl_req)
{
int ret;
spin_unlock(&priv_dev->lock);
priv_dev->setup_pending = 1;
ret = priv_dev->gadget_driver->setup(&priv_dev->gadget, ctrl_req);
priv_dev->setup_pending = 0;
spin_lock(&priv_dev->lock);
return ret;
}
static void cdns3_prepare_setup_packet(struct cdns3_device *priv_dev)
{
priv_dev->ep0_data_dir = 0;
priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
sizeof(struct usb_ctrlrequest), 0, 0);
}
static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
u8 send_stall, u8 send_erdy)
{
struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
struct usb_request *request;
request = cdns3_next_request(&priv_ep->pending_req_list);
if (request)
list_del_init(&request->list);
if (send_stall) {
trace_cdns3_halt(priv_ep, send_stall, 0);
/* set_stall on ep0 */
cdns3_select_ep(priv_dev, 0x00);
writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
} else {
cdns3_prepare_setup_packet(priv_dev);
}
priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
writel((send_erdy ? EP_CMD_ERDY : 0) | EP_CMD_REQ_CMPL,
&priv_dev->regs->ep_cmd);
cdns3_allow_enable_l1(priv_dev, 1);
}
/**
* cdns3_req_ep0_set_configuration - Handling of SET_CONFIG standard USB request
* @priv_dev: extended gadget object
* @ctrl_req: pointer to received setup packet
*
* Returns 0 if success, USB_GADGET_DELAYED_STATUS on deferred status stage,
* error code on error
*/
static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl_req)
{
enum usb_device_state device_state = priv_dev->gadget.state;
struct cdns3_endpoint *priv_ep;
u32 config = le16_to_cpu(ctrl_req->wValue);
int result = 0;
int i;
switch (device_state) {
case USB_STATE_ADDRESS:
/* Configure non-control EPs */
for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
priv_ep = priv_dev->eps[i];
if (!priv_ep)
continue;
if (priv_ep->flags & EP_CLAIMED)
cdns3_ep_config(priv_ep);
}
result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
if (result)
return result;
if (config) {
cdns3_set_hw_configuration(priv_dev);
} else {
cdns3_hw_reset_eps_config(priv_dev);
usb_gadget_set_state(&priv_dev->gadget,
USB_STATE_ADDRESS);
}
break;
case USB_STATE_CONFIGURED:
result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
if (!config && !result) {
cdns3_hw_reset_eps_config(priv_dev);
usb_gadget_set_state(&priv_dev->gadget,
USB_STATE_ADDRESS);
}
break;
default:
result = -EINVAL;
}
return result;
}
/**
* cdns3_req_ep0_set_address - Handling of SET_ADDRESS standard USB request
* @priv_dev: extended gadget object
* @ctrl_req: pointer to received setup packet
*
* Returns 0 if success, error code on error
*/
static int cdns3_req_ep0_set_address(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl_req)
{
enum usb_device_state device_state = priv_dev->gadget.state;
u32 reg;
u32 addr;
addr = le16_to_cpu(ctrl_req->wValue);
if (addr > USB_DEVICE_MAX_ADDRESS) {
dev_err(priv_dev->dev,
"Device address (%d) cannot be greater than %d\n",
addr, USB_DEVICE_MAX_ADDRESS);
return -EINVAL;
}
if (device_state == USB_STATE_CONFIGURED) {
dev_err(priv_dev->dev,
"can't set_address from configured state\n");
return -EINVAL;
}
reg = readl(&priv_dev->regs->usb_cmd);
writel(reg | USB_CMD_FADDR(addr) | USB_CMD_SET_ADDR,
&priv_dev->regs->usb_cmd);
usb_gadget_set_state(&priv_dev->gadget,
(addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT));
return 0;
}
/**
* cdns3_req_ep0_get_status - Handling of GET_STATUS standard USB request
* @priv_dev: extended gadget object
* @ctrl_req: pointer to received setup packet
*
* Returns 0 if success, error code on error
*/
static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl)
{
__le16 *response_pkt;
u16 usb_status = 0;
u32 recip;
recip = ctrl->bRequestType & USB_RECIP_MASK;
switch (recip) {
case USB_RECIP_DEVICE:
/* self powered */
if (priv_dev->is_selfpowered)
usb_status = BIT(USB_DEVICE_SELF_POWERED);
if (priv_dev->wake_up_flag)
usb_status |= BIT(USB_DEVICE_REMOTE_WAKEUP);
if (priv_dev->gadget.speed != USB_SPEED_SUPER)
break;
if (priv_dev->u1_allowed)
usb_status |= BIT(USB_DEV_STAT_U1_ENABLED);
if (priv_dev->u2_allowed)
usb_status |= BIT(USB_DEV_STAT_U2_ENABLED);
break;
case USB_RECIP_INTERFACE:
return cdns3_ep0_delegate_req(priv_dev, ctrl);
case USB_RECIP_ENDPOINT:
/* check if endpoint is stalled */
cdns3_select_ep(priv_dev, ctrl->wIndex);
if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts)))
usb_status = BIT(USB_ENDPOINT_HALT);
break;
default:
return -EINVAL;
}
response_pkt = (__le16 *)priv_dev->setup_buf;
*response_pkt = cpu_to_le16(usb_status);
cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
sizeof(*response_pkt), 1, 0);
return 0;
}
static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl,
int set)
{
enum usb_device_state state;
enum usb_device_speed speed;
int ret = 0;
u32 wValue;
u16 tmode;
wValue = le16_to_cpu(ctrl->wValue);
state = priv_dev->gadget.state;
speed = priv_dev->gadget.speed;
switch (wValue) {
case USB_DEVICE_REMOTE_WAKEUP:
priv_dev->wake_up_flag = !!set;
break;
case USB_DEVICE_U1_ENABLE:
if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
return -EINVAL;
priv_dev->u1_allowed = !!set;
break;
case USB_DEVICE_U2_ENABLE:
if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
return -EINVAL;
priv_dev->u2_allowed = !!set;
break;
case USB_DEVICE_LTM_ENABLE:
ret = -EINVAL;
break;
case USB_DEVICE_TEST_MODE:
if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH)
return -EINVAL;
tmode = le16_to_cpu(ctrl->wIndex);
if (!set || (tmode & 0xff) != 0)
return -EINVAL;
switch (tmode >> 8) {
case TEST_J:
case TEST_K:
case TEST_SE0_NAK:
case TEST_PACKET:
cdns3_ep0_complete_setup(priv_dev, 0, 1);
/**
* Little delay to give the controller some time
* for sending status stage.
* This time should be less then 3ms.
*/
usleep_range(1000, 2000);
cdns3_set_register_bit(&priv_dev->regs->usb_cmd,
USB_CMD_STMODE |
USB_STS_TMODE_SEL(tmode - 1));
break;
default:
ret = -EINVAL;
}
break;
default:
ret = -EINVAL;
}
return ret;
}
static int cdns3_ep0_feature_handle_intf(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl,
int set)
{
u32 wValue;
int ret = 0;
wValue = le16_to_cpu(ctrl->wValue);
switch (wValue) {
case USB_INTRF_FUNC_SUSPEND:
break;
default:
ret = -EINVAL;
}
return ret;
}
static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl,
int set)
{
struct cdns3_endpoint *priv_ep;
int ret = 0;
u8 index;
if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT)
return -EINVAL;
if (!(ctrl->wIndex & ~USB_DIR_IN))
return 0;
index = cdns3_ep_addr_to_index(ctrl->wIndex);
priv_ep = priv_dev->eps[index];
cdns3_select_ep(priv_dev, ctrl->wIndex);
if (set)
__cdns3_gadget_ep_set_halt(priv_ep);
else if (!(priv_ep->flags & EP_WEDGE))
ret = __cdns3_gadget_ep_clear_halt(priv_ep);
cdns3_select_ep(priv_dev, 0x00);
return ret;
}
/**
* cdns3_req_ep0_handle_feature -
* Handling of GET/SET_FEATURE standard USB request
*
* @priv_dev: extended gadget object
* @ctrl_req: pointer to received setup packet
* @set: must be set to 1 for SET_FEATURE request
*
* Returns 0 if success, error code on error
*/
static int cdns3_req_ep0_handle_feature(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl,
int set)
{
int ret = 0;
u32 recip;
recip = ctrl->bRequestType & USB_RECIP_MASK;
switch (recip) {
case USB_RECIP_DEVICE:
ret = cdns3_ep0_feature_handle_device(priv_dev, ctrl, set);
break;
case USB_RECIP_INTERFACE:
ret = cdns3_ep0_feature_handle_intf(priv_dev, ctrl, set);
break;
case USB_RECIP_ENDPOINT:
ret = cdns3_ep0_feature_handle_endpoint(priv_dev, ctrl, set);
break;
default:
return -EINVAL;
}
return ret;
}
/**
* cdns3_req_ep0_set_sel - Handling of SET_SEL standard USB request
* @priv_dev: extended gadget object
* @ctrl_req: pointer to received setup packet
*
* Returns 0 if success, error code on error
*/
static int cdns3_req_ep0_set_sel(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl_req)
{
if (priv_dev->gadget.state < USB_STATE_ADDRESS)
return -EINVAL;
if (ctrl_req->wLength != 6) {
dev_err(priv_dev->dev, "Set SEL should be 6 bytes, got %d\n",
ctrl_req->wLength);
return -EINVAL;
}
cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1, 0);
return 0;
}
/**
* cdns3_req_ep0_set_isoch_delay -
* Handling of GET_ISOCH_DELAY standard USB request
* @priv_dev: extended gadget object
* @ctrl_req: pointer to received setup packet
*
* Returns 0 if success, error code on error
*/
static int cdns3_req_ep0_set_isoch_delay(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl_req)
{
if (ctrl_req->wIndex || ctrl_req->wLength)
return -EINVAL;
priv_dev->isoch_delay = ctrl_req->wValue;
return 0;
}
/**
* cdns3_ep0_standard_request - Handling standard USB requests
* @priv_dev: extended gadget object
* @ctrl_req: pointer to received setup packet
*
* Returns 0 if success, error code on error
*/
static int cdns3_ep0_standard_request(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl_req)
{
int ret;
switch (ctrl_req->bRequest) {
case USB_REQ_SET_ADDRESS:
ret = cdns3_req_ep0_set_address(priv_dev, ctrl_req);
break;
case USB_REQ_SET_CONFIGURATION:
ret = cdns3_req_ep0_set_configuration(priv_dev, ctrl_req);
break;
case USB_REQ_GET_STATUS:
ret = cdns3_req_ep0_get_status(priv_dev, ctrl_req);
break;
case USB_REQ_CLEAR_FEATURE:
ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 0);
break;
case USB_REQ_SET_FEATURE:
ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 1);
break;
case USB_REQ_SET_SEL:
ret = cdns3_req_ep0_set_sel(priv_dev, ctrl_req);
break;
case USB_REQ_SET_ISOCH_DELAY:
ret = cdns3_req_ep0_set_isoch_delay(priv_dev, ctrl_req);
break;
default:
ret = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
break;
}
return ret;
}
static void __pending_setup_status_handler(struct cdns3_device *priv_dev)
{
struct usb_request *request = priv_dev->pending_status_request;
if (priv_dev->status_completion_no_call && request &&
request->complete) {
request->complete(&priv_dev->eps[0]->endpoint, request);
priv_dev->status_completion_no_call = 0;
}
}
void cdns3_pending_setup_status_handler(struct work_struct *work)
{
struct cdns3_device *priv_dev = container_of(work, struct cdns3_device,
pending_status_wq);
unsigned long flags;
spin_lock_irqsave(&priv_dev->lock, flags);
__pending_setup_status_handler(priv_dev);
spin_unlock_irqrestore(&priv_dev->lock, flags);
}
/**
* cdns3_ep0_setup_phase - Handling setup USB requests
* @priv_dev: extended gadget object
*/
static void cdns3_ep0_setup_phase(struct cdns3_device *priv_dev)
{
struct usb_ctrlrequest *ctrl = priv_dev->setup_buf;
struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
int result;
priv_dev->ep0_data_dir = ctrl->bRequestType & USB_DIR_IN;
trace_cdns3_ctrl_req(ctrl);
if (!list_empty(&priv_ep->pending_req_list)) {
struct usb_request *request;
request = cdns3_next_request(&priv_ep->pending_req_list);
priv_ep->dir = priv_dev->ep0_data_dir;
cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
-ECONNRESET);
}
if (le16_to_cpu(ctrl->wLength))
priv_dev->ep0_stage = CDNS3_DATA_STAGE;
else
priv_dev->ep0_stage = CDNS3_STATUS_STAGE;
if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
result = cdns3_ep0_standard_request(priv_dev, ctrl);
else
result = cdns3_ep0_delegate_req(priv_dev, ctrl);
if (result == USB_GADGET_DELAYED_STATUS)
return;
if (result < 0)
cdns3_ep0_complete_setup(priv_dev, 1, 1);
else if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE)
cdns3_ep0_complete_setup(priv_dev, 0, 1);
}
static void cdns3_transfer_completed(struct cdns3_device *priv_dev)
{
struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
if (!list_empty(&priv_ep->pending_req_list)) {
struct usb_request *request;
trace_cdns3_complete_trb(priv_ep, priv_ep->trb_pool);
request = cdns3_next_request(&priv_ep->pending_req_list);
request->actual =
TRB_LEN(le32_to_cpu(priv_ep->trb_pool->length));
priv_ep->dir = priv_dev->ep0_data_dir;
cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), 0);
}
cdns3_ep0_complete_setup(priv_dev, 0, 0);
}
/**
* cdns3_check_new_setup - Check if controller receive new SETUP packet.
* @priv_dev: extended gadget object
*
* The SETUP packet can be kept in on-chip memory or in system memory.
*/
static bool cdns3_check_new_setup(struct cdns3_device *priv_dev)
{
u32 ep_sts_reg;
cdns3_select_ep(priv_dev, 0 | USB_DIR_OUT);
ep_sts_reg = readl(&priv_dev->regs->ep_sts);
return !!(ep_sts_reg & (EP_STS_SETUP | EP_STS_STPWAIT));
}
/**
* cdns3_check_ep0_interrupt_proceed - Processes interrupt related to endpoint 0
* @priv_dev: extended gadget object
* @dir: USB_DIR_IN for IN direction, USB_DIR_OUT for OUT direction
*/
void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir)
{
u32 ep_sts_reg;
cdns3_select_ep(priv_dev, dir);
ep_sts_reg = readl(&priv_dev->regs->ep_sts);
writel(ep_sts_reg, &priv_dev->regs->ep_sts);
trace_cdns3_ep0_irq(priv_dev, ep_sts_reg);
__pending_setup_status_handler(priv_dev);
if (ep_sts_reg & EP_STS_SETUP)
priv_dev->wait_for_setup = 1;
if (priv_dev->wait_for_setup && ep_sts_reg & EP_STS_IOC) {
priv_dev->wait_for_setup = 0;
cdns3_allow_enable_l1(priv_dev, 0);
cdns3_ep0_setup_phase(priv_dev);
} else if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
priv_dev->ep0_data_dir = dir;
cdns3_transfer_completed(priv_dev);
}
if (ep_sts_reg & EP_STS_DESCMIS) {
if (dir == 0 && !priv_dev->setup_pending)
cdns3_prepare_setup_packet(priv_dev);
}
}
/**
* cdns3_gadget_ep0_enable
* Function shouldn't be called by gadget driver,
* endpoint 0 is allways active
*/
static int cdns3_gadget_ep0_enable(struct usb_ep *ep,
const struct usb_endpoint_descriptor *desc)
{
return -EINVAL;
}
/**
* cdns3_gadget_ep0_disable
* Function shouldn't be called by gadget driver,
* endpoint 0 is allways active
*/
static int cdns3_gadget_ep0_disable(struct usb_ep *ep)
{
return -EINVAL;
}
/**
* cdns3_gadget_ep0_set_halt
* @ep: pointer to endpoint zero object
* @value: 1 for set stall, 0 for clear stall
*
* Returns 0
*/
static int cdns3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
{
/* TODO */
return 0;
}
/**
* cdns3_gadget_ep0_queue Transfer data on endpoint zero
* @ep: pointer to endpoint zero object
* @request: pointer to request object
* @gfp_flags: gfp flags
*
* Returns 0 on success, error code elsewhere
*/
static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
struct usb_request *request,
gfp_t gfp_flags)
{
struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
unsigned long flags;
int erdy_sent = 0;
int ret = 0;
u8 zlp = 0;
trace_cdns3_ep0_queue(priv_dev, request);
/* cancel the request if controller receive new SETUP packet. */
if (cdns3_check_new_setup(priv_dev))
return -ECONNRESET;
/* send STATUS stage. Should be called only for SET_CONFIGURATION */
if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) {
spin_lock_irqsave(&priv_dev->lock, flags);
cdns3_select_ep(priv_dev, 0x00);
erdy_sent = !priv_dev->hw_configured_flag;
cdns3_set_hw_configuration(priv_dev);
if (!erdy_sent)
cdns3_ep0_complete_setup(priv_dev, 0, 1);
cdns3_allow_enable_l1(priv_dev, 1);
request->actual = 0;
priv_dev->status_completion_no_call = true;
priv_dev->pending_status_request = request;
spin_unlock_irqrestore(&priv_dev->lock, flags);
/*
* Since there is no completion interrupt for status stage,
* it needs to call ->completion in software after
* ep0_queue is back.
*/
queue_work(system_freezable_wq, &priv_dev->pending_status_wq);
return 0;
}
spin_lock_irqsave(&priv_dev->lock, flags);
if (!list_empty(&priv_ep->pending_req_list)) {
dev_err(priv_dev->dev,
"can't handle multiple requests for ep0\n");
spin_unlock_irqrestore(&priv_dev->lock, flags);
return -EBUSY;
}
ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
priv_dev->ep0_data_dir);
if (ret) {
spin_unlock_irqrestore(&priv_dev->lock, flags);
dev_err(priv_dev->dev, "failed to map request\n");
return -EINVAL;
}
request->status = -EINPROGRESS;
list_add_tail(&request->list, &priv_ep->pending_req_list);
if (request->zero && request->length &&
(request->length % ep->maxpacket == 0))
zlp = 1;
cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1, zlp);
spin_unlock_irqrestore(&priv_dev->lock, flags);
return ret;
}
/**
* cdns3_gadget_ep_set_wedge Set wedge on selected endpoint
* @ep: endpoint object
*
* Returns 0
*/
int cdns3_gadget_ep_set_wedge(struct usb_ep *ep)
{
struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
dev_dbg(priv_dev->dev, "Wedge for %s\n", ep->name);
cdns3_gadget_ep_set_halt(ep, 1);
priv_ep->flags |= EP_WEDGE;
return 0;
}
const struct usb_ep_ops cdns3_gadget_ep0_ops = {
.enable = cdns3_gadget_ep0_enable,
.disable = cdns3_gadget_ep0_disable,
.alloc_request = cdns3_gadget_ep_alloc_request,
.free_request = cdns3_gadget_ep_free_request,
.queue = cdns3_gadget_ep0_queue,
.dequeue = cdns3_gadget_ep_dequeue,
.set_halt = cdns3_gadget_ep0_set_halt,
.set_wedge = cdns3_gadget_ep_set_wedge,
};
/**
* cdns3_ep0_config - Configures default endpoint
* @priv_dev: extended gadget object
*
* Functions sets parameters: maximal packet size and enables interrupts
*/
void cdns3_ep0_config(struct cdns3_device *priv_dev)
{
struct cdns3_usb_regs __iomem *regs;
struct cdns3_endpoint *priv_ep;
u32 max_packet_size = 64;
regs = priv_dev->regs;
if (priv_dev->gadget.speed == USB_SPEED_SUPER)
max_packet_size = 512;
priv_ep = priv_dev->eps[0];
if (!list_empty(&priv_ep->pending_req_list)) {
struct usb_request *request;
request = cdns3_next_request(&priv_ep->pending_req_list);
list_del_init(&request->list);
}
priv_dev->u1_allowed = 0;
priv_dev->u2_allowed = 0;
priv_dev->gadget.ep0->maxpacket = max_packet_size;
cdns3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(max_packet_size);
/* init ep out */
cdns3_select_ep(priv_dev, USB_DIR_OUT);
if (priv_dev->dev_ver >= DEV_VER_V3) {
cdns3_set_register_bit(&priv_dev->regs->dtrans,
BIT(0) | BIT(16));
cdns3_set_register_bit(&priv_dev->regs->tdl_from_trb,
BIT(0) | BIT(16));
}
writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
&regs->ep_cfg);
writel(EP_STS_EN_SETUPEN | EP_STS_EN_DESCMISEN | EP_STS_EN_TRBERREN,
&regs->ep_sts_en);
/* init ep in */
cdns3_select_ep(priv_dev, USB_DIR_IN);
writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
&regs->ep_cfg);
writel(EP_STS_EN_SETUPEN | EP_STS_EN_TRBERREN, &regs->ep_sts_en);
cdns3_set_register_bit(&regs->usb_conf, USB_CONF_U1DS | USB_CONF_U2DS);
}
/**
* cdns3_init_ep0 Initializes software endpoint 0 of gadget
* @priv_dev: extended gadget object
* @ep_priv: extended endpoint object
*
* Returns 0 on success else error code.
*/
int cdns3_init_ep0(struct cdns3_device *priv_dev,
struct cdns3_endpoint *priv_ep)
{
sprintf(priv_ep->name, "ep0");
/* fill linux fields */
priv_ep->endpoint.ops = &cdns3_gadget_ep0_ops;
priv_ep->endpoint.maxburst = 1;
usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
CDNS3_EP0_MAX_PACKET_LIMIT);
priv_ep->endpoint.address = 0;
priv_ep->endpoint.caps.type_control = 1;
priv_ep->endpoint.caps.dir_in = 1;
priv_ep->endpoint.caps.dir_out = 1;
priv_ep->endpoint.name = priv_ep->name;
priv_ep->endpoint.desc = &cdns3_gadget_ep0_desc;
priv_dev->gadget.ep0 = &priv_ep->endpoint;
priv_ep->type = USB_ENDPOINT_XFER_CONTROL;
return cdns3_allocate_trb_pool(priv_ep);
}

View File

@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cadence USBSS DRD Driver - Gadget Export APIs.
*
* Copyright (C) 2017 NXP
* Copyright (C) 2017-2018 NXP
*
* Authors: Peter Chen <peter.chen@nxp.com>
*/
#ifndef __LINUX_CDNS3_GADGET_EXPORT
#define __LINUX_CDNS3_GADGET_EXPORT
#ifdef CONFIG_USB_CDNS3_GADGET
int cdns3_gadget_init(struct cdns3 *cdns);
void cdns3_gadget_exit(struct cdns3 *cdns);
#else
static inline int cdns3_gadget_init(struct cdns3 *cdns)
{
return -ENXIO;
}
static inline void cdns3_gadget_exit(struct cdns3 *cdns) { }
#endif
#endif /* __LINUX_CDNS3_GADGET_EXPORT */

2744
drivers/usb/cdns3/gadget.c Normal file

File diff suppressed because it is too large Load Diff

1338
drivers/usb/cdns3/gadget.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cadence USBSS DRD Driver - Host Export APIs
*
* Copyright (C) 2017-2018 NXP
*
* Authors: Peter Chen <peter.chen@nxp.com>
*/
#ifndef __LINUX_CDNS3_HOST_EXPORT
#define __LINUX_CDNS3_HOST_EXPORT
#ifdef CONFIG_USB_CDNS3_HOST
int cdns3_host_init(struct cdns3 *cdns);
void cdns3_host_exit(struct cdns3 *cdns);
#else
static inline int cdns3_host_init(struct cdns3 *cdns)
{
return -ENXIO;
}
static inline void cdns3_host_exit(struct cdns3 *cdns) { }
#endif /* CONFIG_USB_CDNS3_HOST */
#endif /* __LINUX_CDNS3_HOST_EXPORT */

74
drivers/usb/cdns3/host.c Normal file
View File

@@ -0,0 +1,74 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence USBSS DRD Driver - host side
*
* Copyright (C) 2018-2019 Cadence Design Systems.
* Copyright (C) 2017-2018 NXP
*
* Authors: Peter Chen <peter.chen@nxp.com>
* Pawel Laszczak <pawell@cadence.com>
*/
#include <linux/platform_device.h>
#include "core.h"
#include "drd.h"
static int __cdns3_host_init(struct cdns3 *cdns)
{
struct platform_device *xhci;
int ret;
cdns3_drd_switch_host(cdns, 1);
xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
if (!xhci) {
dev_err(cdns->dev, "couldn't allocate xHCI device\n");
return -ENOMEM;
}
xhci->dev.parent = cdns->dev;
cdns->host_dev = xhci;
ret = platform_device_add_resources(xhci, cdns->xhci_res,
CDNS3_XHCI_RESOURCES_NUM);
if (ret) {
dev_err(cdns->dev, "couldn't add resources to xHCI device\n");
goto err1;
}
ret = platform_device_add(xhci);
if (ret) {
dev_err(cdns->dev, "failed to register xHCI device\n");
goto err1;
}
return 0;
err1:
platform_device_put(xhci);
return ret;
}
static void cdns3_host_exit(struct cdns3 *cdns)
{
platform_device_unregister(cdns->host_dev);
cdns->host_dev = NULL;
cdns3_drd_switch_host(cdns, 0);
}
int cdns3_host_init(struct cdns3 *cdns)
{
struct cdns3_role_driver *rdrv;
rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
if (!rdrv)
return -ENOMEM;
rdrv->start = __cdns3_host_init;
rdrv->stop = cdns3_host_exit;
rdrv->state = CDNS3_ROLE_STATE_INACTIVE;
rdrv->name = "host";
cdns->roles[USB_ROLE_HOST] = rdrv;
return 0;
}

11
drivers/usb/cdns3/trace.c Normal file
View File

@@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
/*
* USBSS device controller driver Trace Support
*
* Copyright (C) 2018-2019 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
#define CREATE_TRACE_POINTS
#include "trace.h"

493
drivers/usb/cdns3/trace.h Normal file
View File

@@ -0,0 +1,493 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* USBSS device controller driver.
* Trace support header file.
*
* Copyright (C) 2018-2019 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM cdns3
#if !defined(__LINUX_CDNS3_TRACE) || defined(TRACE_HEADER_MULTI_READ)
#define __LINUX_CDNS3_TRACE
#include <linux/types.h>
#include <linux/tracepoint.h>
#include <asm/byteorder.h>
#include <linux/usb/ch9.h>
#include "core.h"
#include "gadget.h"
#include "debug.h"
#define CDNS3_MSG_MAX 500
TRACE_EVENT(cdns3_halt,
TP_PROTO(struct cdns3_endpoint *ep_priv, u8 halt, u8 flush),
TP_ARGS(ep_priv, halt, flush),
TP_STRUCT__entry(
__string(name, ep_priv->name)
__field(u8, halt)
__field(u8, flush)
),
TP_fast_assign(
__assign_str(name, ep_priv->name);
__entry->halt = halt;
__entry->flush = flush;
),
TP_printk("Halt %s for %s: %s", __entry->flush ? " and flush" : "",
__get_str(name), __entry->halt ? "set" : "cleared")
);
TRACE_EVENT(cdns3_wa1,
TP_PROTO(struct cdns3_endpoint *ep_priv, char *msg),
TP_ARGS(ep_priv, msg),
TP_STRUCT__entry(
__string(ep_name, ep_priv->name)
__string(msg, msg)
),
TP_fast_assign(
__assign_str(ep_name, ep_priv->name);
__assign_str(msg, msg);
),
TP_printk("WA1: %s %s", __get_str(ep_name), __get_str(msg))
);
TRACE_EVENT(cdns3_wa2,
TP_PROTO(struct cdns3_endpoint *ep_priv, char *msg),
TP_ARGS(ep_priv, msg),
TP_STRUCT__entry(
__string(ep_name, ep_priv->name)
__string(msg, msg)
),
TP_fast_assign(
__assign_str(ep_name, ep_priv->name);
__assign_str(msg, msg);
),
TP_printk("WA2: %s %s", __get_str(ep_name), __get_str(msg))
);
DECLARE_EVENT_CLASS(cdns3_log_doorbell,
TP_PROTO(const char *ep_name, u32 ep_trbaddr),
TP_ARGS(ep_name, ep_trbaddr),
TP_STRUCT__entry(
__string(name, ep_name)
__field(u32, ep_trbaddr)
),
TP_fast_assign(
__assign_str(name, ep_name);
__entry->ep_trbaddr = ep_trbaddr;
),
TP_printk("%s, ep_trbaddr %08x", __get_str(name),
__entry->ep_trbaddr)
);
DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_ep0,
TP_PROTO(const char *ep_name, u32 ep_trbaddr),
TP_ARGS(ep_name, ep_trbaddr)
);
DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_epx,
TP_PROTO(const char *ep_name, u32 ep_trbaddr),
TP_ARGS(ep_name, ep_trbaddr)
);
DECLARE_EVENT_CLASS(cdns3_log_usb_irq,
TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
TP_ARGS(priv_dev, usb_ists),
TP_STRUCT__entry(
__field(enum usb_device_speed, speed)
__field(u32, usb_ists)
__dynamic_array(char, str, CDNS3_MSG_MAX)
),
TP_fast_assign(
__entry->speed = cdns3_get_speed(priv_dev);
__entry->usb_ists = usb_ists;
),
TP_printk("%s", cdns3_decode_usb_irq(__get_str(str), __entry->speed,
__entry->usb_ists))
);
DEFINE_EVENT(cdns3_log_usb_irq, cdns3_usb_irq,
TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
TP_ARGS(priv_dev, usb_ists)
);
DECLARE_EVENT_CLASS(cdns3_log_epx_irq,
TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
TP_ARGS(priv_dev, priv_ep),
TP_STRUCT__entry(
__string(ep_name, priv_ep->name)
__field(u32, ep_sts)
__field(u32, ep_traddr)
__dynamic_array(char, str, CDNS3_MSG_MAX)
),
TP_fast_assign(
__assign_str(ep_name, priv_ep->name);
__entry->ep_sts = readl(&priv_dev->regs->ep_sts);
__entry->ep_traddr = readl(&priv_dev->regs->ep_traddr);
),
TP_printk("%s, ep_traddr: %08x",
cdns3_decode_epx_irq(__get_str(str),
__get_str(ep_name),
__entry->ep_sts),
__entry->ep_traddr)
);
DEFINE_EVENT(cdns3_log_epx_irq, cdns3_epx_irq,
TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
TP_ARGS(priv_dev, priv_ep)
);
DECLARE_EVENT_CLASS(cdns3_log_ep0_irq,
TP_PROTO(struct cdns3_device *priv_dev, u32 ep_sts),
TP_ARGS(priv_dev, ep_sts),
TP_STRUCT__entry(
__field(int, ep_dir)
__field(u32, ep_sts)
__dynamic_array(char, str, CDNS3_MSG_MAX)
),
TP_fast_assign(
__entry->ep_dir = priv_dev->ep0_data_dir;
__entry->ep_sts = ep_sts;
),
TP_printk("%s", cdns3_decode_ep0_irq(__get_str(str),
__entry->ep_dir,
__entry->ep_sts))
);
DEFINE_EVENT(cdns3_log_ep0_irq, cdns3_ep0_irq,
TP_PROTO(struct cdns3_device *priv_dev, u32 ep_sts),
TP_ARGS(priv_dev, ep_sts)
);
DECLARE_EVENT_CLASS(cdns3_log_ctrl,
TP_PROTO(struct usb_ctrlrequest *ctrl),
TP_ARGS(ctrl),
TP_STRUCT__entry(
__field(u8, bRequestType)
__field(u8, bRequest)
__field(u16, wValue)
__field(u16, wIndex)
__field(u16, wLength)
__dynamic_array(char, str, CDNS3_MSG_MAX)
),
TP_fast_assign(
__entry->bRequestType = ctrl->bRequestType;
__entry->bRequest = ctrl->bRequest;
__entry->wValue = le16_to_cpu(ctrl->wValue);
__entry->wIndex = le16_to_cpu(ctrl->wIndex);
__entry->wLength = le16_to_cpu(ctrl->wLength);
),
TP_printk("%s", usb_decode_ctrl(__get_str(str), CDNS3_MSG_MAX,
__entry->bRequestType,
__entry->bRequest, __entry->wValue,
__entry->wIndex, __entry->wLength)
)
);
DEFINE_EVENT(cdns3_log_ctrl, cdns3_ctrl_req,
TP_PROTO(struct usb_ctrlrequest *ctrl),
TP_ARGS(ctrl)
);
DECLARE_EVENT_CLASS(cdns3_log_request,
TP_PROTO(struct cdns3_request *req),
TP_ARGS(req),
TP_STRUCT__entry(
__string(name, req->priv_ep->name)
__field(struct cdns3_request *, req)
__field(void *, buf)
__field(unsigned int, actual)
__field(unsigned int, length)
__field(int, status)
__field(int, zero)
__field(int, short_not_ok)
__field(int, no_interrupt)
__field(int, start_trb)
__field(int, end_trb)
__field(struct cdns3_trb *, start_trb_addr)
__field(int, flags)
),
TP_fast_assign(
__assign_str(name, req->priv_ep->name);
__entry->req = req;
__entry->buf = req->request.buf;
__entry->actual = req->request.actual;
__entry->length = req->request.length;
__entry->status = req->request.status;
__entry->zero = req->request.zero;
__entry->short_not_ok = req->request.short_not_ok;
__entry->no_interrupt = req->request.no_interrupt;
__entry->start_trb = req->start_trb;
__entry->end_trb = req->end_trb;
__entry->start_trb_addr = req->trb;
__entry->flags = req->flags;
),
TP_printk("%s: req: %p, req buff %p, length: %u/%u %s%s%s, status: %d,"
" trb: [start:%d, end:%d: virt addr %pa], flags:%x ",
__get_str(name), __entry->req, __entry->buf, __entry->actual,
__entry->length,
__entry->zero ? "Z" : "z",
__entry->short_not_ok ? "S" : "s",
__entry->no_interrupt ? "I" : "i",
__entry->status,
__entry->start_trb,
__entry->end_trb,
__entry->start_trb_addr,
__entry->flags
)
);
DEFINE_EVENT(cdns3_log_request, cdns3_alloc_request,
TP_PROTO(struct cdns3_request *req),
TP_ARGS(req)
);
DEFINE_EVENT(cdns3_log_request, cdns3_free_request,
TP_PROTO(struct cdns3_request *req),
TP_ARGS(req)
);
DEFINE_EVENT(cdns3_log_request, cdns3_ep_queue,
TP_PROTO(struct cdns3_request *req),
TP_ARGS(req)
);
DEFINE_EVENT(cdns3_log_request, cdns3_ep_dequeue,
TP_PROTO(struct cdns3_request *req),
TP_ARGS(req)
);
DEFINE_EVENT(cdns3_log_request, cdns3_gadget_giveback,
TP_PROTO(struct cdns3_request *req),
TP_ARGS(req)
);
TRACE_EVENT(cdns3_ep0_queue,
TP_PROTO(struct cdns3_device *dev_priv, struct usb_request *request),
TP_ARGS(dev_priv, request),
TP_STRUCT__entry(
__field(int, dir)
__field(int, length)
),
TP_fast_assign(
__entry->dir = dev_priv->ep0_data_dir;
__entry->length = request->length;
),
TP_printk("Queue to ep0%s length: %u", __entry->dir ? "in" : "out",
__entry->length)
);
DECLARE_EVENT_CLASS(cdns3_log_aligned_request,
TP_PROTO(struct cdns3_request *priv_req),
TP_ARGS(priv_req),
TP_STRUCT__entry(
__string(name, priv_req->priv_ep->name)
__field(struct usb_request *, req)
__field(void *, buf)
__field(dma_addr_t, dma)
__field(void *, aligned_buf)
__field(dma_addr_t, aligned_dma)
__field(u32, aligned_buf_size)
),
TP_fast_assign(
__assign_str(name, priv_req->priv_ep->name);
__entry->req = &priv_req->request;
__entry->buf = priv_req->request.buf;
__entry->dma = priv_req->request.dma;
__entry->aligned_buf = priv_req->aligned_buf->buf;
__entry->aligned_dma = priv_req->aligned_buf->dma;
__entry->aligned_buf_size = priv_req->aligned_buf->size;
),
TP_printk("%s: req: %p, req buf %p, dma %pad a_buf %p a_dma %pad, size %d",
__get_str(name), __entry->req, __entry->buf, &__entry->dma,
__entry->aligned_buf, &__entry->aligned_dma,
__entry->aligned_buf_size
)
);
DEFINE_EVENT(cdns3_log_aligned_request, cdns3_free_aligned_request,
TP_PROTO(struct cdns3_request *req),
TP_ARGS(req)
);
DEFINE_EVENT(cdns3_log_aligned_request, cdns3_prepare_aligned_request,
TP_PROTO(struct cdns3_request *req),
TP_ARGS(req)
);
DECLARE_EVENT_CLASS(cdns3_log_trb,
TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
TP_ARGS(priv_ep, trb),
TP_STRUCT__entry(
__string(name, priv_ep->name)
__field(struct cdns3_trb *, trb)
__field(u32, buffer)
__field(u32, length)
__field(u32, control)
__field(u32, type)
),
TP_fast_assign(
__assign_str(name, priv_ep->name);
__entry->trb = trb;
__entry->buffer = trb->buffer;
__entry->length = trb->length;
__entry->control = trb->control;
__entry->type = usb_endpoint_type(priv_ep->endpoint.desc);
),
TP_printk("%s: trb %p, dma buf: 0x%08x, size: %ld, burst: %d ctrl: 0x%08x (%s%s%s%s%s%s%s)",
__get_str(name), __entry->trb, __entry->buffer,
TRB_LEN(__entry->length),
(u8)TRB_BURST_LEN_GET(__entry->length),
__entry->control,
__entry->control & TRB_CYCLE ? "C=1, " : "C=0, ",
__entry->control & TRB_TOGGLE ? "T=1, " : "T=0, ",
__entry->control & TRB_ISP ? "ISP, " : "",
__entry->control & TRB_FIFO_MODE ? "FIFO, " : "",
__entry->control & TRB_CHAIN ? "CHAIN, " : "",
__entry->control & TRB_IOC ? "IOC, " : "",
TRB_FIELD_TO_TYPE(__entry->control) == TRB_NORMAL ? "Normal" : "LINK"
)
);
DEFINE_EVENT(cdns3_log_trb, cdns3_prepare_trb,
TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
TP_ARGS(priv_ep, trb)
);
DEFINE_EVENT(cdns3_log_trb, cdns3_complete_trb,
TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
TP_ARGS(priv_ep, trb)
);
DECLARE_EVENT_CLASS(cdns3_log_ring,
TP_PROTO(struct cdns3_endpoint *priv_ep),
TP_ARGS(priv_ep),
TP_STRUCT__entry(
__dynamic_array(u8, ring, TRB_RING_SIZE)
__dynamic_array(u8, priv_ep, sizeof(struct cdns3_endpoint))
__dynamic_array(char, buffer,
(TRBS_PER_SEGMENT * 65) + CDNS3_MSG_MAX)
),
TP_fast_assign(
memcpy(__get_dynamic_array(priv_ep), priv_ep,
sizeof(struct cdns3_endpoint));
memcpy(__get_dynamic_array(ring), priv_ep->trb_pool,
TRB_RING_SIZE);
),
TP_printk("%s",
cdns3_dbg_ring((struct cdns3_endpoint *)__get_str(priv_ep),
(struct cdns3_trb *)__get_str(ring),
__get_str(buffer)))
);
DEFINE_EVENT(cdns3_log_ring, cdns3_ring,
TP_PROTO(struct cdns3_endpoint *priv_ep),
TP_ARGS(priv_ep)
);
DECLARE_EVENT_CLASS(cdns3_log_ep,
TP_PROTO(struct cdns3_endpoint *priv_ep),
TP_ARGS(priv_ep),
TP_STRUCT__entry(
__string(name, priv_ep->name)
__field(unsigned int, maxpacket)
__field(unsigned int, maxpacket_limit)
__field(unsigned int, max_streams)
__field(unsigned int, maxburst)
__field(unsigned int, flags)
__field(unsigned int, dir)
__field(u8, enqueue)
__field(u8, dequeue)
),
TP_fast_assign(
__assign_str(name, priv_ep->name);
__entry->maxpacket = priv_ep->endpoint.maxpacket;
__entry->maxpacket_limit = priv_ep->endpoint.maxpacket_limit;
__entry->max_streams = priv_ep->endpoint.max_streams;
__entry->maxburst = priv_ep->endpoint.maxburst;
__entry->flags = priv_ep->flags;
__entry->dir = priv_ep->dir;
__entry->enqueue = priv_ep->enqueue;
__entry->dequeue = priv_ep->dequeue;
),
TP_printk("%s: mps: %d/%d. streams: %d, burst: %d, enq idx: %d, "
"deq idx: %d, flags %s%s%s%s%s%s%s%s, dir: %s",
__get_str(name), __entry->maxpacket,
__entry->maxpacket_limit, __entry->max_streams,
__entry->maxburst, __entry->enqueue,
__entry->dequeue,
__entry->flags & EP_ENABLED ? "EN | " : "",
__entry->flags & EP_STALLED ? "STALLED | " : "",
__entry->flags & EP_WEDGE ? "WEDGE | " : "",
__entry->flags & EP_TRANSFER_STARTED ? "STARTED | " : "",
__entry->flags & EP_UPDATE_EP_TRBADDR ? "UPD TRB | " : "",
__entry->flags & EP_PENDING_REQUEST ? "REQ PEN | " : "",
__entry->flags & EP_RING_FULL ? "RING FULL |" : "",
__entry->flags & EP_CLAIMED ? "CLAIMED " : "",
__entry->dir ? "IN" : "OUT"
)
);
DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_enable,
TP_PROTO(struct cdns3_endpoint *priv_ep),
TP_ARGS(priv_ep)
);
DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_disable,
TP_PROTO(struct cdns3_endpoint *priv_ep),
TP_ARGS(priv_ep)
);
DECLARE_EVENT_CLASS(cdns3_log_request_handled,
TP_PROTO(struct cdns3_request *priv_req, int current_index,
int handled),
TP_ARGS(priv_req, current_index, handled),
TP_STRUCT__entry(
__field(struct cdns3_request *, priv_req)
__field(unsigned int, dma_position)
__field(unsigned int, handled)
__field(unsigned int, dequeue_idx)
__field(unsigned int, enqueue_idx)
__field(unsigned int, start_trb)
__field(unsigned int, end_trb)
),
TP_fast_assign(
__entry->priv_req = priv_req;
__entry->dma_position = current_index;
__entry->handled = handled;
__entry->dequeue_idx = priv_req->priv_ep->dequeue;
__entry->enqueue_idx = priv_req->priv_ep->enqueue;
__entry->start_trb = priv_req->start_trb;
__entry->end_trb = priv_req->end_trb;
),
TP_printk("Req: %p %s, DMA pos: %d, ep deq: %d, ep enq: %d,"
" start trb: %d, end trb: %d",
__entry->priv_req,
__entry->handled ? "handled" : "not handled",
__entry->dma_position, __entry->dequeue_idx,
__entry->enqueue_idx, __entry->start_trb,
__entry->end_trb
)
);
DEFINE_EVENT(cdns3_log_request_handled, cdns3_request_handled,
TP_PROTO(struct cdns3_request *priv_req, int current_index,
int handled),
TP_ARGS(priv_req, current_index, handled)
);
#endif /* __LINUX_CDNS3_TRACE */
/* this part must be outside header guard */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace
#include <trace/define_trace.h>

View File

@@ -6,6 +6,7 @@ config USB_CHIPIDEA
select EXTCON
select RESET_CONTROLLER
select USB_ULPI_BUS
select USB_ROLE_SWITCH
help
Say Y here if your system has a dual role high speed USB
controller based on ChipIdea silicon IP. It supports:

View File

@@ -16,6 +16,7 @@
#include <linux/usb/gadget.h>
#include <linux/usb/otg-fsm.h>
#include <linux/usb/otg.h>
#include <linux/usb/role.h>
#include <linux/ulpi/interface.h>
/******************************************************************************
@@ -217,6 +218,7 @@ struct ci_hdrc {
ktime_t hr_timeouts[NUM_OTG_FSM_TIMERS];
unsigned enabled_otg_timer_bits;
enum otg_fsm_timer next_otg_timer;
struct usb_role_switch *role_switch;
struct work_struct work;
struct workqueue_struct *wq;
@@ -290,6 +292,16 @@ static inline void ci_role_stop(struct ci_hdrc *ci)
ci->roles[role]->stop(ci);
}
static inline enum usb_role ci_role_to_usb_role(struct ci_hdrc *ci)
{
if (ci->role == CI_ROLE_HOST)
return USB_ROLE_HOST;
else if (ci->role == CI_ROLE_GADGET && ci->vbus_active)
return USB_ROLE_DEVICE;
else
return USB_ROLE_NONE;
}
/**
* hw_read_id_reg: reads from a identification register
* @ci: the controller

View File

@@ -216,13 +216,13 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
ci->rcdev.ops = &ci_hdrc_msm_reset_ops;
ci->rcdev.of_node = pdev->dev.of_node;
ci->rcdev.nr_resets = 2;
ret = reset_controller_register(&ci->rcdev);
ret = devm_reset_controller_register(&pdev->dev, &ci->rcdev);
if (ret)
return ret;
ret = clk_prepare_enable(ci->fs_clk);
if (ret)
goto err_fs;
return ret;
reset_control_assert(reset);
usleep_range(10000, 12000);
@@ -232,7 +232,7 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
ret = clk_prepare_enable(ci->core_clk);
if (ret)
goto err_fs;
return ret;
ret = clk_prepare_enable(ci->iface_clk);
if (ret)
@@ -271,8 +271,6 @@ err_mux:
clk_disable_unprepare(ci->iface_clk);
err_iface:
clk_disable_unprepare(ci->core_clk);
err_fs:
reset_controller_unregister(&ci->rcdev);
return ret;
}
@@ -284,7 +282,6 @@ static int ci_hdrc_msm_remove(struct platform_device *pdev)
ci_hdrc_remove_device(ci->ci);
clk_disable_unprepare(ci->iface_clk);
clk_disable_unprepare(ci->core_clk);
reset_controller_unregister(&ci->rcdev);
return 0;
}

View File

@@ -600,6 +600,71 @@ static int ci_cable_notifier(struct notifier_block *nb, unsigned long event,
return NOTIFY_DONE;
}
static enum usb_role ci_usb_role_switch_get(struct device *dev)
{
struct ci_hdrc *ci = dev_get_drvdata(dev);
enum usb_role role;
unsigned long flags;
spin_lock_irqsave(&ci->lock, flags);
role = ci_role_to_usb_role(ci);
spin_unlock_irqrestore(&ci->lock, flags);
return role;
}
static int ci_usb_role_switch_set(struct device *dev, enum usb_role role)
{
struct ci_hdrc *ci = dev_get_drvdata(dev);
struct ci_hdrc_cable *cable = NULL;
enum usb_role current_role = ci_role_to_usb_role(ci);
unsigned long flags;
if (current_role == role)
return 0;
pm_runtime_get_sync(ci->dev);
/* Stop current role */
spin_lock_irqsave(&ci->lock, flags);
if (current_role == USB_ROLE_DEVICE)
cable = &ci->platdata->vbus_extcon;
else if (current_role == USB_ROLE_HOST)
cable = &ci->platdata->id_extcon;
if (cable) {
cable->changed = true;
cable->connected = false;
ci_irq(ci->irq, ci);
spin_unlock_irqrestore(&ci->lock, flags);
if (ci->wq && role != USB_ROLE_NONE)
flush_workqueue(ci->wq);
spin_lock_irqsave(&ci->lock, flags);
}
cable = NULL;
/* Start target role */
if (role == USB_ROLE_DEVICE)
cable = &ci->platdata->vbus_extcon;
else if (role == USB_ROLE_HOST)
cable = &ci->platdata->id_extcon;
if (cable) {
cable->changed = true;
cable->connected = true;
ci_irq(ci->irq, ci);
}
spin_unlock_irqrestore(&ci->lock, flags);
pm_runtime_put_sync(ci->dev);
return 0;
}
static struct usb_role_switch_desc ci_role_switch = {
.set = ci_usb_role_switch_set,
.get = ci_usb_role_switch_get,
};
static int ci_get_platdata(struct device *dev,
struct ci_hdrc_platform_data *platdata)
{
@@ -726,6 +791,9 @@ static int ci_get_platdata(struct device *dev,
cable->connected = false;
}
if (device_property_read_bool(dev, "usb-role-switch"))
ci_role_switch.fwnode = dev->fwnode;
platdata->pctl = devm_pinctrl_get(dev);
if (!IS_ERR(platdata->pctl)) {
struct pinctrl_state *p;
@@ -903,10 +971,7 @@ static struct attribute *ci_attrs[] = {
&dev_attr_role.attr,
NULL,
};
static const struct attribute_group ci_attr_group = {
.attrs = ci_attrs,
};
ATTRIBUTE_GROUPS(ci);
static int ci_hdrc_probe(struct platform_device *pdev)
{
@@ -1008,7 +1073,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
ci->irq = platform_get_irq(pdev, 0);
if (ci->irq < 0) {
dev_err(dev, "missing IRQ\n");
ret = ci->irq;
goto deinit_phy;
}
@@ -1051,6 +1115,15 @@ static int ci_hdrc_probe(struct platform_device *pdev)
}
}
if (ci_role_switch.fwnode) {
ci->role_switch = usb_role_switch_register(dev,
&ci_role_switch);
if (IS_ERR(ci->role_switch)) {
ret = PTR_ERR(ci->role_switch);
goto deinit_otg;
}
}
if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) {
if (ci->is_otg) {
ci->role = ci_otg_role(ci);
@@ -1106,15 +1179,12 @@ static int ci_hdrc_probe(struct platform_device *pdev)
device_set_wakeup_capable(&pdev->dev, true);
dbg_create_files(ci);
ret = sysfs_create_group(&dev->kobj, &ci_attr_group);
if (ret)
goto remove_debug;
return 0;
remove_debug:
dbg_remove_files(ci);
stop:
if (ci->role_switch)
usb_role_switch_unregister(ci->role_switch);
deinit_otg:
if (ci->is_otg && ci->roles[CI_ROLE_GADGET])
ci_hdrc_otg_destroy(ci);
deinit_gadget:
@@ -1133,6 +1203,9 @@ static int ci_hdrc_remove(struct platform_device *pdev)
{
struct ci_hdrc *ci = platform_get_drvdata(pdev);
if (ci->role_switch)
usb_role_switch_unregister(ci->role_switch);
if (ci->supports_runtime_pm) {
pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
@@ -1140,7 +1213,6 @@ static int ci_hdrc_remove(struct platform_device *pdev)
}
dbg_remove_files(ci);
sysfs_remove_group(&ci->dev->kobj, &ci_attr_group);
ci_role_destroy(ci);
ci_hdrc_enter_lpm(ci, true);
ci_usb_phy_exit(ci);
@@ -1319,6 +1391,7 @@ static struct platform_driver ci_hdrc_driver = {
.driver = {
.name = "ci_hdrc",
.pm = &ci_pm_ops,
.dev_groups = ci_groups,
},
};

View File

@@ -35,7 +35,7 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
* detection overwrite OTGSC register value
*/
cable = &ci->platdata->vbus_extcon;
if (!IS_ERR(cable->edev)) {
if (!IS_ERR(cable->edev) || ci->role_switch) {
if (cable->changed)
val |= OTGSC_BSVIS;
else
@@ -53,7 +53,7 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
}
cable = &ci->platdata->id_extcon;
if (!IS_ERR(cable->edev)) {
if (!IS_ERR(cable->edev) || ci->role_switch) {
if (cable->changed)
val |= OTGSC_IDIS;
else
@@ -83,7 +83,7 @@ void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data)
struct ci_hdrc_cable *cable;
cable = &ci->platdata->vbus_extcon;
if (!IS_ERR(cable->edev)) {
if (!IS_ERR(cable->edev) || ci->role_switch) {
if (data & mask & OTGSC_BSVIS)
cable->changed = false;
@@ -97,7 +97,7 @@ void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data)
}
cable = &ci->platdata->id_extcon;
if (!IS_ERR(cable->edev)) {
if (!IS_ERR(cable->edev) || ci->role_switch) {
if (data & mask & OTGSC_IDIS)
cable->changed = false;

View File

@@ -1762,12 +1762,11 @@ static int ci_udc_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget);
int retval = -ENOMEM;
int retval;
if (driver->disconnect == NULL)
return -EINVAL;
ci->ep0out->ep.desc = &ctrl_endpt_out_desc;
retval = usb_ep_enable(&ci->ep0out->ep);
if (retval)

View File

@@ -1082,6 +1082,12 @@ static ssize_t ieee1284_id_show(struct device *dev, struct device_attribute *att
static DEVICE_ATTR_RO(ieee1284_id);
static struct attribute *usblp_attrs[] = {
&dev_attr_ieee1284_id.attr,
NULL,
};
ATTRIBUTE_GROUPS(usblp);
static int usblp_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
@@ -1156,9 +1162,6 @@ static int usblp_probe(struct usb_interface *intf,
/* Retrieve and store the device ID string. */
usblp_cache_device_id_string(usblp);
retval = device_create_file(&intf->dev, &dev_attr_ieee1284_id);
if (retval)
goto abort_intfdata;
#ifdef DEBUG
usblp_check_status(usblp, 0);
@@ -1189,7 +1192,6 @@ static int usblp_probe(struct usb_interface *intf,
abort_intfdata:
usb_set_intfdata(intf, NULL);
device_remove_file(&intf->dev, &dev_attr_ieee1284_id);
abort:
kfree(usblp->readbuf);
kfree(usblp->statusbuf);
@@ -1360,8 +1362,6 @@ static void usblp_disconnect(struct usb_interface *intf)
BUG();
}
device_remove_file(&intf->dev, &dev_attr_ieee1284_id);
mutex_lock(&usblp_mutex);
mutex_lock(&usblp->mut);
usblp->present = 0;
@@ -1421,6 +1421,7 @@ static struct usb_driver usblp_driver = {
.suspend = usblp_suspend,
.resume = usblp_resume,
.id_table = usblp_ids,
.dev_groups = usblp_groups,
.supports_autosuspend = 1,
};

View File

@@ -1836,17 +1836,14 @@ capability_attribute(device_capabilities);
capability_attribute(usb488_interface_capabilities);
capability_attribute(usb488_device_capabilities);
static struct attribute *capability_attrs[] = {
static struct attribute *usbtmc_attrs[] = {
&dev_attr_interface_capabilities.attr,
&dev_attr_device_capabilities.attr,
&dev_attr_usb488_interface_capabilities.attr,
&dev_attr_usb488_device_capabilities.attr,
NULL,
};
static const struct attribute_group capability_attr_grp = {
.attrs = capability_attrs,
};
ATTRIBUTE_GROUPS(usbtmc);
static int usbtmc_ioctl_indicator_pulse(struct usbtmc_device_data *data)
{
@@ -2386,9 +2383,6 @@ static int usbtmc_probe(struct usb_interface *intf,
retcode = get_capabilities(data);
if (retcode)
dev_err(&intf->dev, "can't read capabilities\n");
else
retcode = sysfs_create_group(&intf->dev.kobj,
&capability_attr_grp);
if (data->iin_ep_present) {
/* allocate int urb */
@@ -2435,7 +2429,6 @@ static int usbtmc_probe(struct usb_interface *intf,
return 0;
error_register:
sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);
usbtmc_free_int(data);
err_put:
kref_put(&data->kref, usbtmc_delete);
@@ -2448,7 +2441,6 @@ static void usbtmc_disconnect(struct usb_interface *intf)
struct list_head *elem;
usb_deregister_dev(intf, &usbtmc_class);
sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);
mutex_lock(&data->io_mutex);
data->zombie = 1;
wake_up_interruptible_all(&data->waitq);
@@ -2554,6 +2546,7 @@ static struct usb_driver usbtmc_driver = {
.resume = usbtmc_resume,
.pre_reset = usbtmc_pre_reset,
.post_reset = usbtmc_post_reset,
.dev_groups = usbtmc_groups,
};
module_usb_driver(usbtmc_driver);

View File

@@ -0,0 +1,51 @@
# SPDX-License-Identifier: GPL-2.0
config USB_COMMON
tristate
config USB_LED_TRIG
bool "USB LED Triggers"
depends on LEDS_CLASS && LEDS_TRIGGERS
select USB_COMMON
help
This option adds LED triggers for USB host and/or gadget activity.
Say Y here if you are working on a system with led-class supported
LEDs and you want to use them as activity indicators for USB host or
gadget.
config USB_ULPI_BUS
tristate "USB ULPI PHY interface support"
select USB_COMMON
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.
config USB_CONN_GPIO
tristate "USB GPIO Based Connection Detection Driver"
depends on GPIOLIB
select USB_ROLE_SWITCH
help
The driver supports USB role switch between host and device via GPIO
based USB cable detection, used typically if an input GPIO is used
to detect USB ID pin, and another input GPIO may be also used to detect
Vbus pin at the same time, it also can be used to enable/disable
device if an input GPIO is only used to detect Vbus pin.
To compile the driver as a module, choose M here: the module will
be called usb-conn-gpio.ko

View File

@@ -5,7 +5,9 @@
obj-$(CONFIG_USB_COMMON) += usb-common.o
usb-common-y += common.o
usb-common-$(CONFIG_TRACING) += debug.o
usb-common-$(CONFIG_USB_LED_TRIG) += led.o
obj-$(CONFIG_USB_CONN_GPIO) += usb-conn-gpio.o
obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o
obj-$(CONFIG_USB_ULPI_BUS) += ulpi.o

268
drivers/usb/common/debug.c Normal file
View File

@@ -0,0 +1,268 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Common USB debugging functions
*
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*/
#include <linux/usb/ch9.h>
static void usb_decode_get_status(__u8 bRequestType, __u16 wIndex,
__u16 wLength, char *str, size_t size)
{
switch (bRequestType & USB_RECIP_MASK) {
case USB_RECIP_DEVICE:
snprintf(str, size, "Get Device Status(Length = %d)", wLength);
break;
case USB_RECIP_INTERFACE:
snprintf(str, size,
"Get Interface Status(Intf = %d, Length = %d)",
wIndex, wLength);
break;
case USB_RECIP_ENDPOINT:
snprintf(str, size, "Get Endpoint Status(ep%d%s)",
wIndex & ~USB_DIR_IN,
wIndex & USB_DIR_IN ? "in" : "out");
break;
}
}
static const char *usb_decode_device_feature(u16 wValue)
{
switch (wValue) {
case USB_DEVICE_SELF_POWERED:
return "Self Powered";
case USB_DEVICE_REMOTE_WAKEUP:
return "Remote Wakeup";
case USB_DEVICE_TEST_MODE:
return "Test Mode";
case USB_DEVICE_U1_ENABLE:
return "U1 Enable";
case USB_DEVICE_U2_ENABLE:
return "U2 Enable";
case USB_DEVICE_LTM_ENABLE:
return "LTM Enable";
default:
return "UNKNOWN";
}
}
static const char *usb_decode_test_mode(u16 wIndex)
{
switch (wIndex) {
case TEST_J:
return ": TEST_J";
case TEST_K:
return ": TEST_K";
case TEST_SE0_NAK:
return ": TEST_SE0_NAK";
case TEST_PACKET:
return ": TEST_PACKET";
case TEST_FORCE_EN:
return ": TEST_FORCE_EN";
default:
return ": UNKNOWN";
}
}
static void usb_decode_set_clear_feature(__u8 bRequestType,
__u8 bRequest, __u16 wValue,
__u16 wIndex, char *str, size_t size)
{
switch (bRequestType & USB_RECIP_MASK) {
case USB_RECIP_DEVICE:
snprintf(str, size, "%s Device Feature(%s%s)",
bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
usb_decode_device_feature(wValue),
wValue == USB_DEVICE_TEST_MODE ?
usb_decode_test_mode(wIndex) : "");
break;
case USB_RECIP_INTERFACE:
snprintf(str, size, "%s Interface Feature(%s)",
bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
wValue == USB_INTRF_FUNC_SUSPEND ?
"Function Suspend" : "UNKNOWN");
break;
case USB_RECIP_ENDPOINT:
snprintf(str, size, "%s Endpoint Feature(%s ep%d%s)",
bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
wValue == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN",
wIndex & ~USB_DIR_IN,
wIndex & USB_DIR_IN ? "in" : "out");
break;
}
}
static void usb_decode_set_address(__u16 wValue, char *str, size_t size)
{
snprintf(str, size, "Set Address(Addr = %02x)", wValue);
}
static void usb_decode_get_set_descriptor(__u8 bRequestType, __u8 bRequest,
__u16 wValue, __u16 wIndex,
__u16 wLength, char *str, size_t size)
{
char *s;
switch (wValue >> 8) {
case USB_DT_DEVICE:
s = "Device";
break;
case USB_DT_CONFIG:
s = "Configuration";
break;
case USB_DT_STRING:
s = "String";
break;
case USB_DT_INTERFACE:
s = "Interface";
break;
case USB_DT_ENDPOINT:
s = "Endpoint";
break;
case USB_DT_DEVICE_QUALIFIER:
s = "Device Qualifier";
break;
case USB_DT_OTHER_SPEED_CONFIG:
s = "Other Speed Config";
break;
case USB_DT_INTERFACE_POWER:
s = "Interface Power";
break;
case USB_DT_OTG:
s = "OTG";
break;
case USB_DT_DEBUG:
s = "Debug";
break;
case USB_DT_INTERFACE_ASSOCIATION:
s = "Interface Association";
break;
case USB_DT_BOS:
s = "BOS";
break;
case USB_DT_DEVICE_CAPABILITY:
s = "Device Capability";
break;
case USB_DT_PIPE_USAGE:
s = "Pipe Usage";
break;
case USB_DT_SS_ENDPOINT_COMP:
s = "SS Endpoint Companion";
break;
case USB_DT_SSP_ISOC_ENDPOINT_COMP:
s = "SSP Isochronous Endpoint Companion";
break;
default:
s = "UNKNOWN";
break;
}
snprintf(str, size, "%s %s Descriptor(Index = %d, Length = %d)",
bRequest == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set",
s, wValue & 0xff, wLength);
}
static void usb_decode_get_configuration(__u16 wLength, char *str, size_t size)
{
snprintf(str, size, "Get Configuration(Length = %d)", wLength);
}
static void usb_decode_set_configuration(__u8 wValue, char *str, size_t size)
{
snprintf(str, size, "Set Configuration(Config = %d)", wValue);
}
static void usb_decode_get_intf(__u16 wIndex, __u16 wLength, char *str,
size_t size)
{
snprintf(str, size, "Get Interface(Intf = %d, Length = %d)",
wIndex, wLength);
}
static void usb_decode_set_intf(__u8 wValue, __u16 wIndex, char *str,
size_t size)
{
snprintf(str, size, "Set Interface(Intf = %d, Alt.Setting = %d)",
wIndex, wValue);
}
static void usb_decode_synch_frame(__u16 wIndex, __u16 wLength,
char *str, size_t size)
{
snprintf(str, size, "Synch Frame(Endpoint = %d, Length = %d)",
wIndex, wLength);
}
static void usb_decode_set_sel(__u16 wLength, char *str, size_t size)
{
snprintf(str, size, "Set SEL(Length = %d)", wLength);
}
static void usb_decode_set_isoch_delay(__u8 wValue, char *str, size_t size)
{
snprintf(str, size, "Set Isochronous Delay(Delay = %d ns)", wValue);
}
/**
* usb_decode_ctrl - returns a string representation of ctrl request
*/
const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType,
__u8 bRequest, __u16 wValue, __u16 wIndex,
__u16 wLength)
{
switch (bRequest) {
case USB_REQ_GET_STATUS:
usb_decode_get_status(bRequestType, wIndex, wLength, str, size);
break;
case USB_REQ_CLEAR_FEATURE:
case USB_REQ_SET_FEATURE:
usb_decode_set_clear_feature(bRequestType, bRequest, wValue,
wIndex, str, size);
break;
case USB_REQ_SET_ADDRESS:
usb_decode_set_address(wValue, str, size);
break;
case USB_REQ_GET_DESCRIPTOR:
case USB_REQ_SET_DESCRIPTOR:
usb_decode_get_set_descriptor(bRequestType, bRequest, wValue,
wIndex, wLength, str, size);
break;
case USB_REQ_GET_CONFIGURATION:
usb_decode_get_configuration(wLength, str, size);
break;
case USB_REQ_SET_CONFIGURATION:
usb_decode_set_configuration(wValue, str, size);
break;
case USB_REQ_GET_INTERFACE:
usb_decode_get_intf(wIndex, wLength, str, size);
break;
case USB_REQ_SET_INTERFACE:
usb_decode_set_intf(wValue, wIndex, str, size);
break;
case USB_REQ_SYNCH_FRAME:
usb_decode_synch_frame(wIndex, wLength, str, size);
break;
case USB_REQ_SET_SEL:
usb_decode_set_sel(wLength, str, size);
break;
case USB_REQ_SET_ISOCH_DELAY:
usb_decode_set_isoch_delay(wValue, str, size);
break;
default:
snprintf(str, size, "%02x %02x %02x %02x %02x %02x %02x %02x",
bRequestType, bRequest,
(u8)(cpu_to_le16(wValue) & 0xff),
(u8)(cpu_to_le16(wValue) >> 8),
(u8)(cpu_to_le16(wIndex) & 0xff),
(u8)(cpu_to_le16(wIndex) >> 8),
(u8)(cpu_to_le16(wLength) & 0xff),
(u8)(cpu_to_le16(wLength) >> 8));
}
return str;
}
EXPORT_SYMBOL_GPL(usb_decode_ctrl);

View File

@@ -0,0 +1,284 @@
// SPDX-License-Identifier: GPL-2.0
/*
* USB GPIO Based Connection Detection Driver
*
* Copyright (C) 2019 MediaTek Inc.
*
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
*
* Some code borrowed from drivers/extcon/extcon-usb-gpio.c
*/
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/usb/role.h>
#define USB_GPIO_DEB_MS 20 /* ms */
#define USB_GPIO_DEB_US ((USB_GPIO_DEB_MS) * 1000) /* us */
#define USB_CONN_IRQF \
(IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT)
struct usb_conn_info {
struct device *dev;
struct usb_role_switch *role_sw;
enum usb_role last_role;
struct regulator *vbus;
struct delayed_work dw_det;
unsigned long debounce_jiffies;
struct gpio_desc *id_gpiod;
struct gpio_desc *vbus_gpiod;
int id_irq;
int vbus_irq;
};
/**
* "DEVICE" = VBUS and "HOST" = !ID, so we have:
* Both "DEVICE" and "HOST" can't be set as active at the same time
* so if "HOST" is active (i.e. ID is 0) we keep "DEVICE" inactive
* even if VBUS is on.
*
* Role | ID | VBUS
* ------------------------------------
* [1] DEVICE | H | H
* [2] NONE | H | L
* [3] HOST | L | H
* [4] HOST | L | L
*
* In case we have only one of these signals:
* - VBUS only - we want to distinguish between [1] and [2], so ID is always 1
* - ID only - we want to distinguish between [1] and [4], so VBUS = ID
*/
static void usb_conn_detect_cable(struct work_struct *work)
{
struct usb_conn_info *info;
enum usb_role role;
int id, vbus, ret;
info = container_of(to_delayed_work(work),
struct usb_conn_info, dw_det);
/* check ID and VBUS */
id = info->id_gpiod ?
gpiod_get_value_cansleep(info->id_gpiod) : 1;
vbus = info->vbus_gpiod ?
gpiod_get_value_cansleep(info->vbus_gpiod) : id;
if (!id)
role = USB_ROLE_HOST;
else if (vbus)
role = USB_ROLE_DEVICE;
else
role = USB_ROLE_NONE;
dev_dbg(info->dev, "role %d/%d, gpios: id %d, vbus %d\n",
info->last_role, role, id, vbus);
if (info->last_role == role) {
dev_warn(info->dev, "repeated role: %d\n", role);
return;
}
if (info->last_role == USB_ROLE_HOST)
regulator_disable(info->vbus);
ret = usb_role_switch_set_role(info->role_sw, role);
if (ret)
dev_err(info->dev, "failed to set role: %d\n", ret);
if (role == USB_ROLE_HOST) {
ret = regulator_enable(info->vbus);
if (ret)
dev_err(info->dev, "enable vbus regulator failed\n");
}
info->last_role = role;
dev_dbg(info->dev, "vbus regulator is %s\n",
regulator_is_enabled(info->vbus) ? "enabled" : "disabled");
}
static void usb_conn_queue_dwork(struct usb_conn_info *info,
unsigned long delay)
{
queue_delayed_work(system_power_efficient_wq, &info->dw_det, delay);
}
static irqreturn_t usb_conn_isr(int irq, void *dev_id)
{
struct usb_conn_info *info = dev_id;
usb_conn_queue_dwork(info, info->debounce_jiffies);
return IRQ_HANDLED;
}
static int usb_conn_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct usb_conn_info *info;
int ret = 0;
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->dev = dev;
info->id_gpiod = devm_gpiod_get_optional(dev, "id", GPIOD_IN);
if (IS_ERR(info->id_gpiod))
return PTR_ERR(info->id_gpiod);
info->vbus_gpiod = devm_gpiod_get_optional(dev, "vbus", GPIOD_IN);
if (IS_ERR(info->vbus_gpiod))
return PTR_ERR(info->vbus_gpiod);
if (!info->id_gpiod && !info->vbus_gpiod) {
dev_err(dev, "failed to get gpios\n");
return -ENODEV;
}
if (info->id_gpiod)
ret = gpiod_set_debounce(info->id_gpiod, USB_GPIO_DEB_US);
if (!ret && info->vbus_gpiod)
ret = gpiod_set_debounce(info->vbus_gpiod, USB_GPIO_DEB_US);
if (ret < 0)
info->debounce_jiffies = msecs_to_jiffies(USB_GPIO_DEB_MS);
INIT_DELAYED_WORK(&info->dw_det, usb_conn_detect_cable);
info->vbus = devm_regulator_get(dev, "vbus");
if (IS_ERR(info->vbus)) {
dev_err(dev, "failed to get vbus\n");
return PTR_ERR(info->vbus);
}
info->role_sw = usb_role_switch_get(dev);
if (IS_ERR(info->role_sw)) {
if (PTR_ERR(info->role_sw) != -EPROBE_DEFER)
dev_err(dev, "failed to get role switch\n");
return PTR_ERR(info->role_sw);
}
if (info->id_gpiod) {
info->id_irq = gpiod_to_irq(info->id_gpiod);
if (info->id_irq < 0) {
dev_err(dev, "failed to get ID IRQ\n");
ret = info->id_irq;
goto put_role_sw;
}
ret = devm_request_threaded_irq(dev, info->id_irq, NULL,
usb_conn_isr, USB_CONN_IRQF,
pdev->name, info);
if (ret < 0) {
dev_err(dev, "failed to request ID IRQ\n");
goto put_role_sw;
}
}
if (info->vbus_gpiod) {
info->vbus_irq = gpiod_to_irq(info->vbus_gpiod);
if (info->vbus_irq < 0) {
dev_err(dev, "failed to get VBUS IRQ\n");
ret = info->vbus_irq;
goto put_role_sw;
}
ret = devm_request_threaded_irq(dev, info->vbus_irq, NULL,
usb_conn_isr, USB_CONN_IRQF,
pdev->name, info);
if (ret < 0) {
dev_err(dev, "failed to request VBUS IRQ\n");
goto put_role_sw;
}
}
platform_set_drvdata(pdev, info);
/* Perform initial detection */
usb_conn_queue_dwork(info, 0);
return 0;
put_role_sw:
usb_role_switch_put(info->role_sw);
return ret;
}
static int usb_conn_remove(struct platform_device *pdev)
{
struct usb_conn_info *info = platform_get_drvdata(pdev);
cancel_delayed_work_sync(&info->dw_det);
if (info->last_role == USB_ROLE_HOST)
regulator_disable(info->vbus);
usb_role_switch_put(info->role_sw);
return 0;
}
static int __maybe_unused usb_conn_suspend(struct device *dev)
{
struct usb_conn_info *info = dev_get_drvdata(dev);
if (info->id_gpiod)
disable_irq(info->id_irq);
if (info->vbus_gpiod)
disable_irq(info->vbus_irq);
pinctrl_pm_select_sleep_state(dev);
return 0;
}
static int __maybe_unused usb_conn_resume(struct device *dev)
{
struct usb_conn_info *info = dev_get_drvdata(dev);
pinctrl_pm_select_default_state(dev);
if (info->id_gpiod)
enable_irq(info->id_irq);
if (info->vbus_gpiod)
enable_irq(info->vbus_irq);
usb_conn_queue_dwork(info, 0);
return 0;
}
static SIMPLE_DEV_PM_OPS(usb_conn_pm_ops,
usb_conn_suspend, usb_conn_resume);
static const struct of_device_id usb_conn_dt_match[] = {
{ .compatible = "gpio-usb-b-connector", },
{ }
};
MODULE_DEVICE_TABLE(of, usb_conn_dt_match);
static struct platform_driver usb_conn_driver = {
.probe = usb_conn_probe,
.remove = usb_conn_remove,
.driver = {
.name = "usb-conn-gpio",
.pm = &usb_conn_pm_ops,
.of_match_table = usb_conn_dt_match,
},
};
module_platform_driver(usb_conn_driver);
MODULE_AUTHOR("Chunfeng Yun <chunfeng.yun@mediatek.com>");
MODULE_DESCRIPTION("USB GPIO based connection detection driver");
MODULE_LICENSE("GPL v2");

View File

@@ -921,7 +921,7 @@ int usb_get_bos_descriptor(struct usb_device *dev)
struct usb_bos_descriptor *bos;
struct usb_dev_cap_header *cap;
struct usb_ssp_cap_descriptor *ssp_cap;
unsigned char *buffer;
unsigned char *buffer, *buffer0;
int length, total_len, num, i, ssac;
__u8 cap_type;
int ret;
@@ -966,10 +966,12 @@ int usb_get_bos_descriptor(struct usb_device *dev)
ret = -ENOMSG;
goto err;
}
buffer0 = buffer;
total_len -= length;
buffer += length;
for (i = 0; i < num; i++) {
buffer += length;
cap = (struct usb_dev_cap_header *)buffer;
if (total_len < sizeof(*cap) || total_len < cap->bLength) {
@@ -983,8 +985,6 @@ int usb_get_bos_descriptor(struct usb_device *dev)
break;
}
total_len -= length;
if (cap->bDescriptorType != USB_DT_DEVICE_CAPABILITY) {
dev_warn(ddev, "descriptor type invalid, skip\n");
continue;
@@ -1019,7 +1019,11 @@ int usb_get_bos_descriptor(struct usb_device *dev)
default:
break;
}
total_len -= length;
buffer += length;
}
dev->bos->desc->wTotalLength = cpu_to_le16(buffer - buffer0);
return 0;

View File

@@ -44,10 +44,19 @@
#include "usb.h"
#ifdef CONFIG_PM
#define MAYBE_CAP_SUSPEND USBDEVFS_CAP_SUSPEND
#else
#define MAYBE_CAP_SUSPEND 0
#endif
#define USB_MAXBUS 64
#define USB_DEVICE_MAX (USB_MAXBUS * 128)
#define USB_SG_SIZE 16384 /* split-size for large txs */
/* Mutual exclusion for ps->list in resume vs. release and remove */
static DEFINE_MUTEX(usbfs_mutex);
struct usb_dev_state {
struct list_head list; /* state list */
struct usb_device *dev;
@@ -57,14 +66,17 @@ struct usb_dev_state {
struct list_head async_completed;
struct list_head memory_list;
wait_queue_head_t wait; /* wake up if a request completed */
wait_queue_head_t wait_for_resume; /* wake up upon runtime resume */
unsigned int discsignr;
struct pid *disc_pid;
const struct cred *cred;
sigval_t disccontext;
unsigned long ifclaimed;
u32 disabled_bulk_eps;
bool privileges_dropped;
unsigned long interface_allowed_mask;
int not_yet_resumed;
bool suspend_allowed;
bool privileges_dropped;
};
struct usb_memory {
@@ -694,9 +706,7 @@ static void driver_disconnect(struct usb_interface *intf)
destroy_async_on_interface(ps, ifnum);
}
/* The following routines are merely placeholders. There is no way
* to inform a user task about suspend or resumes.
*/
/* We don't care about suspend/resume of claimed interfaces */
static int driver_suspend(struct usb_interface *intf, pm_message_t msg)
{
return 0;
@@ -707,12 +717,32 @@ static int driver_resume(struct usb_interface *intf)
return 0;
}
/* The following routines apply to the entire device, not interfaces */
void usbfs_notify_suspend(struct usb_device *udev)
{
/* We don't need to handle this */
}
void usbfs_notify_resume(struct usb_device *udev)
{
struct usb_dev_state *ps;
/* Protect against simultaneous remove or release */
mutex_lock(&usbfs_mutex);
list_for_each_entry(ps, &udev->filelist, list) {
WRITE_ONCE(ps->not_yet_resumed, 0);
wake_up_all(&ps->wait_for_resume);
}
mutex_unlock(&usbfs_mutex);
}
struct usb_driver usbfs_driver = {
.name = "usbfs",
.probe = driver_probe,
.disconnect = driver_disconnect,
.suspend = driver_suspend,
.resume = driver_resume,
.supports_autosuspend = 1,
};
static int claimintf(struct usb_dev_state *ps, unsigned int ifnum)
@@ -991,9 +1021,12 @@ static int usbdev_open(struct inode *inode, struct file *file)
INIT_LIST_HEAD(&ps->async_completed);
INIT_LIST_HEAD(&ps->memory_list);
init_waitqueue_head(&ps->wait);
init_waitqueue_head(&ps->wait_for_resume);
ps->disc_pid = get_pid(task_pid(current));
ps->cred = get_current_cred();
smp_wmb();
/* Can't race with resume; the device is already active */
list_add_tail(&ps->list, &dev->filelist);
file->private_data = ps;
usb_unlock_device(dev);
@@ -1019,7 +1052,10 @@ static int usbdev_release(struct inode *inode, struct file *file)
usb_lock_device(dev);
usb_hub_release_all_ports(dev, ps);
/* Protect against simultaneous resume */
mutex_lock(&usbfs_mutex);
list_del_init(&ps->list);
mutex_unlock(&usbfs_mutex);
for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed);
ifnum++) {
@@ -1027,7 +1063,8 @@ static int usbdev_release(struct inode *inode, struct file *file)
releaseintf(ps, ifnum);
}
destroy_all_async(ps);
usb_autosuspend_device(dev);
if (!ps->suspend_allowed)
usb_autosuspend_device(dev);
usb_unlock_device(dev);
usb_put_dev(dev);
put_pid(ps->disc_pid);
@@ -1621,7 +1658,8 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
if (as->usbm)
num_sgs = 0;
u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length +
u += sizeof(struct async) + sizeof(struct urb) +
(as->usbm ? 0 : uurb->buffer_length) +
num_sgs * sizeof(struct scatterlist);
ret = usbfs_increase_memory_usage(u);
if (ret)
@@ -2273,7 +2311,8 @@ static int proc_get_capabilities(struct usb_dev_state *ps, void __user *arg)
caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM |
USBDEVFS_CAP_REAP_AFTER_DISCONNECT | USBDEVFS_CAP_MMAP |
USBDEVFS_CAP_DROP_PRIVILEGES | USBDEVFS_CAP_CONNINFO_EX;
USBDEVFS_CAP_DROP_PRIVILEGES |
USBDEVFS_CAP_CONNINFO_EX | MAYBE_CAP_SUSPEND;
if (!ps->dev->bus->no_stop_on_short)
caps |= USBDEVFS_CAP_BULK_CONTINUATION;
if (ps->dev->bus->sg_tablesize)
@@ -2376,6 +2415,47 @@ static int proc_drop_privileges(struct usb_dev_state *ps, void __user *arg)
return 0;
}
static int proc_forbid_suspend(struct usb_dev_state *ps)
{
int ret = 0;
if (ps->suspend_allowed) {
ret = usb_autoresume_device(ps->dev);
if (ret == 0)
ps->suspend_allowed = false;
else if (ret != -ENODEV)
ret = -EIO;
}
return ret;
}
static int proc_allow_suspend(struct usb_dev_state *ps)
{
if (!connected(ps))
return -ENODEV;
WRITE_ONCE(ps->not_yet_resumed, 1);
if (!ps->suspend_allowed) {
usb_autosuspend_device(ps->dev);
ps->suspend_allowed = true;
}
return 0;
}
static int proc_wait_for_resume(struct usb_dev_state *ps)
{
int ret;
usb_unlock_device(ps->dev);
ret = wait_event_interruptible(ps->wait_for_resume,
READ_ONCE(ps->not_yet_resumed) == 0);
usb_lock_device(ps->dev);
if (ret != 0)
return -EINTR;
return proc_forbid_suspend(ps);
}
/*
* NOTE: All requests here that have interface numbers as parameters
* are assuming that somehow the configuration has been prevented from
@@ -2570,6 +2650,15 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
case USBDEVFS_GET_SPEED:
ret = ps->dev->speed;
break;
case USBDEVFS_FORBID_SUSPEND:
ret = proc_forbid_suspend(ps);
break;
case USBDEVFS_ALLOW_SUSPEND:
ret = proc_allow_suspend(ps);
break;
case USBDEVFS_WAIT_FOR_RESUME:
ret = proc_wait_for_resume(ps);
break;
}
/* Handle variable-length commands */
@@ -2643,15 +2732,20 @@ static void usbdev_remove(struct usb_device *udev)
{
struct usb_dev_state *ps;
/* Protect against simultaneous resume */
mutex_lock(&usbfs_mutex);
while (!list_empty(&udev->filelist)) {
ps = list_entry(udev->filelist.next, struct usb_dev_state, list);
destroy_all_async(ps);
wake_up_all(&ps->wait);
WRITE_ONCE(ps->not_yet_resumed, 0);
wake_up_all(&ps->wait_for_resume);
list_del_init(&ps->list);
if (ps->discsignr)
kill_pid_usb_asyncio(ps->discsignr, EPIPE, ps->disccontext,
ps->disc_pid, ps->cred);
}
mutex_unlock(&usbfs_mutex);
}
static int usbdev_notify(struct notifier_block *self,

View File

@@ -892,6 +892,7 @@ int usb_register_device_driver(struct usb_device_driver *new_udriver,
new_udriver->drvwrap.driver.probe = usb_probe_device;
new_udriver->drvwrap.driver.remove = usb_unbind_device;
new_udriver->drvwrap.driver.owner = owner;
new_udriver->drvwrap.driver.dev_groups = new_udriver->dev_groups;
retval = driver_register(&new_udriver->drvwrap.driver);
@@ -954,6 +955,7 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
new_driver->drvwrap.driver.remove = usb_unbind_interface;
new_driver->drvwrap.driver.owner = owner;
new_driver->drvwrap.driver.mod_name = mod_name;
new_driver->drvwrap.driver.dev_groups = new_driver->dev_groups;
spin_lock_init(&new_driver->dynids.lock);
INIT_LIST_HEAD(&new_driver->dynids.list);

View File

@@ -257,6 +257,8 @@ static int generic_suspend(struct usb_device *udev, pm_message_t msg)
else
rc = usb_port_suspend(udev, msg);
if (rc == 0)
usbfs_notify_suspend(udev);
return rc;
}
@@ -273,6 +275,9 @@ static int generic_resume(struct usb_device *udev, pm_message_t msg)
rc = hcd_bus_resume(udev, msg);
else
rc = usb_port_resume(udev, msg);
if (rc == 0)
usbfs_notify_resume(udev);
return rc;
}

View File

@@ -393,8 +393,7 @@ static inline void powermac_set_asic(struct pci_dev *pci_dev, int enable)
static int check_root_hub_suspended(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
struct usb_hcd *hcd = dev_get_drvdata(dev);
if (HCD_RH_RUNNING(hcd)) {
dev_warn(dev, "Root hub is not suspended\n");

View File

@@ -1249,9 +1249,6 @@ EXPORT_SYMBOL_GPL(usb_hcd_unlink_urb_from_ep);
* To support host controllers with limited dma capabilities we provide dma
* bounce buffers. This feature can be enabled by initializing
* hcd->localmem_pool using usb_hcd_setup_local_mem().
* For this to work properly the host controller code must first use the
* function dma_declare_coherent_memory() to point out which memory area
* that should be used for dma allocations.
*
* The initialized hcd->localmem_pool then tells the usb code to allocate all
* data for dma using the genalloc API.
@@ -2191,6 +2188,9 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
hcd->state = HC_STATE_RESUMING;
status = hcd->driver->bus_resume(hcd);
clear_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags);
if (status == 0)
status = usb_phy_roothub_calibrate(hcd->phy_roothub);
if (status == 0) {
struct usb_device *udev;
int port1;
@@ -2454,7 +2454,6 @@ struct usb_hcd *__usb_create_hcd(const struct hc_driver *driver,
hcd->self.controller = dev;
hcd->self.sysdev = sysdev;
hcd->self.bus_name = bus_name;
hcd->self.uses_dma = (sysdev->dma_mask != NULL);
timer_setup(&hcd->rh_timer, rh_timer_func, 0);
#ifdef CONFIG_PM
@@ -2764,6 +2763,10 @@ int usb_add_hcd(struct usb_hcd *hcd,
}
hcd->rh_pollable = 1;
retval = usb_phy_roothub_calibrate(hcd->phy_roothub);
if (retval)
goto err_hcd_driver_setup;
/* NOTE: root hub and controller capabilities may not be the same */
if (device_can_wakeup(hcd->self.controller)
&& device_can_wakeup(&hcd->self.root_hub->dev))

View File

@@ -151,6 +151,27 @@ err_out:
}
EXPORT_SYMBOL_GPL(usb_phy_roothub_set_mode);
int usb_phy_roothub_calibrate(struct usb_phy_roothub *phy_roothub)
{
struct usb_phy_roothub *roothub_entry;
struct list_head *head;
int err;
if (!phy_roothub)
return 0;
head = &phy_roothub->list;
list_for_each_entry(roothub_entry, head, list) {
err = phy_calibrate(roothub_entry->phy);
if (err)
return err;
}
return 0;
}
EXPORT_SYMBOL_GPL(usb_phy_roothub_calibrate);
int usb_phy_roothub_power_on(struct usb_phy_roothub *phy_roothub)
{
struct usb_phy_roothub *roothub_entry;

View File

@@ -18,6 +18,7 @@ int usb_phy_roothub_exit(struct usb_phy_roothub *phy_roothub);
int usb_phy_roothub_set_mode(struct usb_phy_roothub *phy_roothub,
enum phy_mode mode);
int usb_phy_roothub_calibrate(struct usb_phy_roothub *phy_roothub);
int usb_phy_roothub_power_on(struct usb_phy_roothub *phy_roothub);
void usb_phy_roothub_power_off(struct usb_phy_roothub *phy_roothub);

View File

@@ -285,6 +285,14 @@ static int usb_port_runtime_suspend(struct device *dev)
}
#endif
static void usb_port_shutdown(struct device *dev)
{
struct usb_port *port_dev = to_usb_port(dev);
if (port_dev->child)
usb_disable_usb2_hardware_lpm(port_dev->child);
}
static const struct dev_pm_ops usb_port_pm_ops = {
#ifdef CONFIG_PM
.runtime_suspend = usb_port_runtime_suspend,
@@ -301,6 +309,7 @@ struct device_type usb_port_device_type = {
static struct device_driver usb_port_driver = {
.name = "usb",
.owner = THIS_MODULE,
.shutdown = usb_port_shutdown,
};
static int link_peers(struct usb_port *left, struct usb_port *right)

View File

@@ -933,228 +933,6 @@ void usb_free_coherent(struct usb_device *dev, size_t size, void *addr,
}
EXPORT_SYMBOL_GPL(usb_free_coherent);
/**
* usb_buffer_map - create DMA mapping(s) for an urb
* @urb: urb whose transfer_buffer/setup_packet will be mapped
*
* URB_NO_TRANSFER_DMA_MAP is added to urb->transfer_flags if the operation
* succeeds. If the device is connected to this system through a non-DMA
* controller, this operation always succeeds.
*
* This call would normally be used for an urb which is reused, perhaps
* as the target of a large periodic transfer, with usb_buffer_dmasync()
* calls to synchronize memory and dma state.
*
* Reverse the effect of this call with usb_buffer_unmap().
*
* Return: Either %NULL (indicating no buffer could be mapped), or @urb.
*
*/
#if 0
struct urb *usb_buffer_map(struct urb *urb)
{
struct usb_bus *bus;
struct device *controller;
if (!urb
|| !urb->dev
|| !(bus = urb->dev->bus)
|| !(controller = bus->sysdev))
return NULL;
if (controller->dma_mask) {
urb->transfer_dma = dma_map_single(controller,
urb->transfer_buffer, urb->transfer_buffer_length,
usb_pipein(urb->pipe)
? DMA_FROM_DEVICE : DMA_TO_DEVICE);
/* FIXME generic api broken like pci, can't report errors */
/* if (urb->transfer_dma == DMA_ADDR_INVALID) return 0; */
} else
urb->transfer_dma = ~0;
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
return urb;
}
EXPORT_SYMBOL_GPL(usb_buffer_map);
#endif /* 0 */
/* XXX DISABLED, no users currently. If you wish to re-enable this
* XXX please determine whether the sync is to transfer ownership of
* XXX the buffer from device to cpu or vice verse, and thusly use the
* XXX appropriate _for_{cpu,device}() method. -DaveM
*/
#if 0
/**
* usb_buffer_dmasync - synchronize DMA and CPU view of buffer(s)
* @urb: urb whose transfer_buffer/setup_packet will be synchronized
*/
void usb_buffer_dmasync(struct urb *urb)
{
struct usb_bus *bus;
struct device *controller;
if (!urb
|| !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
|| !urb->dev
|| !(bus = urb->dev->bus)
|| !(controller = bus->sysdev))
return;
if (controller->dma_mask) {
dma_sync_single_for_cpu(controller,
urb->transfer_dma, urb->transfer_buffer_length,
usb_pipein(urb->pipe)
? DMA_FROM_DEVICE : DMA_TO_DEVICE);
if (usb_pipecontrol(urb->pipe))
dma_sync_single_for_cpu(controller,
urb->setup_dma,
sizeof(struct usb_ctrlrequest),
DMA_TO_DEVICE);
}
}
EXPORT_SYMBOL_GPL(usb_buffer_dmasync);
#endif
/**
* usb_buffer_unmap - free DMA mapping(s) for an urb
* @urb: urb whose transfer_buffer will be unmapped
*
* Reverses the effect of usb_buffer_map().
*/
#if 0
void usb_buffer_unmap(struct urb *urb)
{
struct usb_bus *bus;
struct device *controller;
if (!urb
|| !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
|| !urb->dev
|| !(bus = urb->dev->bus)
|| !(controller = bus->sysdev))
return;
if (controller->dma_mask) {
dma_unmap_single(controller,
urb->transfer_dma, urb->transfer_buffer_length,
usb_pipein(urb->pipe)
? DMA_FROM_DEVICE : DMA_TO_DEVICE);
}
urb->transfer_flags &= ~URB_NO_TRANSFER_DMA_MAP;
}
EXPORT_SYMBOL_GPL(usb_buffer_unmap);
#endif /* 0 */
#if 0
/**
* usb_buffer_map_sg - create scatterlist DMA mapping(s) for an endpoint
* @dev: device to which the scatterlist will be mapped
* @is_in: mapping transfer direction
* @sg: the scatterlist to map
* @nents: the number of entries in the scatterlist
*
* Return: Either < 0 (indicating no buffers could be mapped), or the
* number of DMA mapping array entries in the scatterlist.
*
* Note:
* The caller is responsible for placing the resulting DMA addresses from
* the scatterlist into URB transfer buffer pointers, and for setting the
* URB_NO_TRANSFER_DMA_MAP transfer flag in each of those URBs.
*
* Top I/O rates come from queuing URBs, instead of waiting for each one
* to complete before starting the next I/O. This is particularly easy
* to do with scatterlists. Just allocate and submit one URB for each DMA
* mapping entry returned, stopping on the first error or when all succeed.
* Better yet, use the usb_sg_*() calls, which do that (and more) for you.
*
* This call would normally be used when translating scatterlist requests,
* rather than usb_buffer_map(), since on some hardware (with IOMMUs) it
* may be able to coalesce mappings for improved I/O efficiency.
*
* Reverse the effect of this call with usb_buffer_unmap_sg().
*/
int usb_buffer_map_sg(const struct usb_device *dev, int is_in,
struct scatterlist *sg, int nents)
{
struct usb_bus *bus;
struct device *controller;
if (!dev
|| !(bus = dev->bus)
|| !(controller = bus->sysdev)
|| !controller->dma_mask)
return -EINVAL;
/* FIXME generic api broken like pci, can't report errors */
return dma_map_sg(controller, sg, nents,
is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE) ? : -ENOMEM;
}
EXPORT_SYMBOL_GPL(usb_buffer_map_sg);
#endif
/* XXX DISABLED, no users currently. If you wish to re-enable this
* XXX please determine whether the sync is to transfer ownership of
* XXX the buffer from device to cpu or vice verse, and thusly use the
* XXX appropriate _for_{cpu,device}() method. -DaveM
*/
#if 0
/**
* usb_buffer_dmasync_sg - synchronize DMA and CPU view of scatterlist buffer(s)
* @dev: device to which the scatterlist will be mapped
* @is_in: mapping transfer direction
* @sg: the scatterlist to synchronize
* @n_hw_ents: the positive return value from usb_buffer_map_sg
*
* Use this when you are re-using a scatterlist's data buffers for
* another USB request.
*/
void usb_buffer_dmasync_sg(const struct usb_device *dev, int is_in,
struct scatterlist *sg, int n_hw_ents)
{
struct usb_bus *bus;
struct device *controller;
if (!dev
|| !(bus = dev->bus)
|| !(controller = bus->sysdev)
|| !controller->dma_mask)
return;
dma_sync_sg_for_cpu(controller, sg, n_hw_ents,
is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
}
EXPORT_SYMBOL_GPL(usb_buffer_dmasync_sg);
#endif
#if 0
/**
* usb_buffer_unmap_sg - free DMA mapping(s) for a scatterlist
* @dev: device to which the scatterlist will be mapped
* @is_in: mapping transfer direction
* @sg: the scatterlist to unmap
* @n_hw_ents: the positive return value from usb_buffer_map_sg
*
* Reverses the effect of usb_buffer_map_sg().
*/
void usb_buffer_unmap_sg(const struct usb_device *dev, int is_in,
struct scatterlist *sg, int n_hw_ents)
{
struct usb_bus *bus;
struct device *controller;
if (!dev
|| !(bus = dev->bus)
|| !(controller = bus->sysdev)
|| !controller->dma_mask)
return;
dma_unmap_sg(controller, sg, n_hw_ents,
is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
}
EXPORT_SYMBOL_GPL(usb_buffer_unmap_sg);
#endif
/*
* Notifications of device and interface registration
*/

View File

@@ -95,6 +95,9 @@ extern int usb_runtime_idle(struct device *dev);
extern int usb_enable_usb2_hardware_lpm(struct usb_device *udev);
extern int usb_disable_usb2_hardware_lpm(struct usb_device *udev);
extern void usbfs_notify_suspend(struct usb_device *udev);
extern void usbfs_notify_resume(struct usb_device *udev);
#else
static inline int usb_port_suspend(struct usb_device *udev, pm_message_t msg)

View File

@@ -3224,14 +3224,15 @@ static void kill_all_requests(struct dwc2_hsotg *hsotg,
struct dwc2_hsotg_ep *ep,
int result)
{
struct dwc2_hsotg_req *req, *treq;
unsigned int size;
ep->req = NULL;
list_for_each_entry_safe(req, treq, &ep->queue, queue)
dwc2_hsotg_complete_request(hsotg, ep, req,
result);
while (!list_empty(&ep->queue)) {
struct dwc2_hsotg_req *req = get_ep_head(ep);
dwc2_hsotg_complete_request(hsotg, ep, req, result);
}
if (!hsotg->dedicated_fifos)
return;

View File

@@ -5062,13 +5062,13 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
dwc2_hc_driver.reset_device = dwc2_reset_device;
}
if (hsotg->params.host_dma)
dwc2_hc_driver.flags |= HCD_DMA;
hcd = usb_create_hcd(&dwc2_hc_driver, hsotg->dev, dev_name(hsotg->dev));
if (!hcd)
goto error1;
if (!hsotg->params.host_dma)
hcd->self.uses_dma = 0;
hcd->has_tt = 1;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

View File

@@ -404,10 +404,7 @@ static void dwc2_get_device_properties(struct dwc2_hsotg *hsotg)
device_property_read_u32(hsotg->dev, "g-np-tx-fifo-size",
&p->g_np_tx_fifo_size);
num = device_property_read_u32_array(hsotg->dev,
"g-tx-fifo-size",
NULL, 0);
num = device_property_count_u32(hsotg->dev, "g-tx-fifo-size");
if (num > 0) {
num = min(num, 15);
memset(p->g_tx_fifo_size, 0,

View File

@@ -407,10 +407,8 @@ static int dwc2_driver_probe(struct platform_device *dev)
spin_lock_init(&hsotg->lock);
hsotg->irq = platform_get_irq(dev, 0);
if (hsotg->irq < 0) {
dev_err(&dev->dev, "missing IRQ resource\n");
if (hsotg->irq < 0)
return hsotg->irq;
}
dev_dbg(hsotg->dev, "registering common handler for irq%d\n",
hsotg->irq);

View File

@@ -168,7 +168,6 @@ static void __dwc3_set_mode(struct work_struct *work)
otg_set_vbus(dwc->usb2_phy->otg, true);
phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);
phy_calibrate(dwc->usb2_generic_phy);
}
break;
case DWC3_GCTL_PRTCAP_DEVICE:
@@ -252,12 +251,25 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
reg |= DWC3_DCTL_CSFTRST;
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
/*
* For DWC_usb31 controller 1.90a and later, the DCTL.CSFRST bit
* is cleared only after all the clocks are synchronized. This can
* take a little more than 50ms. Set the polling rate at 20ms
* for 10 times instead.
*/
if (dwc3_is_usb31(dwc) && dwc->revision >= DWC3_USB31_REVISION_190A)
retries = 10;
do {
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (!(reg & DWC3_DCTL_CSFTRST))
goto done;
udelay(1);
if (dwc3_is_usb31(dwc) &&
dwc->revision >= DWC3_USB31_REVISION_190A)
msleep(20);
else
udelay(1);
} while (--retries);
phy_exit(dwc->usb3_generic_phy);
@@ -267,11 +279,11 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
done:
/*
* For DWC_usb31 controller, once DWC3_DCTL_CSFTRST bit is cleared,
* we must wait at least 50ms before accessing the PHY domain
* (synchronization delay). DWC_usb31 programming guide section 1.3.2.
* For DWC_usb31 controller 1.80a and prior, once DCTL.CSFRST bit
* is cleared, we must wait at least 50ms before accessing the PHY
* domain (synchronization delay).
*/
if (dwc3_is_usb31(dwc))
if (dwc3_is_usb31(dwc) && dwc->revision <= DWC3_USB31_REVISION_180A)
msleep(50);
return 0;
@@ -686,8 +698,7 @@ static void dwc3_core_exit(struct dwc3 *dwc)
usb_phy_set_suspend(dwc->usb3_phy, 1);
phy_power_off(dwc->usb2_generic_phy);
phy_power_off(dwc->usb3_generic_phy);
clk_bulk_disable(dwc->num_clks, dwc->clks);
clk_bulk_unprepare(dwc->num_clks, dwc->clks);
clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
reset_control_assert(dwc->reset);
}
@@ -813,8 +824,7 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
* result = 1, means INCRx burst mode supported.
* result > 1, means undefined length burst mode supported.
*/
ntype = device_property_read_u32_array(dev,
"snps,incr-burst-type-adjustment", NULL, 0);
ntype = device_property_count_u32(dev, "snps,incr-burst-type-adjustment");
if (ntype <= 0)
return;
@@ -1166,7 +1176,6 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
dev_err(dev, "failed to initialize host\n");
return ret;
}
phy_calibrate(dwc->usb2_generic_phy);
break;
case USB_DR_MODE_OTG:
INIT_WORK(&dwc->drd_work, __dwc3_set_mode);
@@ -1310,8 +1319,7 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
dwc->tx_de_emphasis = tx_de_emphasis;
dwc->hird_threshold = hird_threshold
| (dwc->is_utmi_l1_suspend << 4);
dwc->hird_threshold = hird_threshold;
dwc->rx_thr_num_pkt_prd = rx_thr_num_pkt_prd;
dwc->rx_max_burst_prd = rx_max_burst_prd;
@@ -1436,7 +1444,7 @@ static int dwc3_probe(struct platform_device *pdev)
if (dev->of_node) {
dwc->num_clks = ARRAY_SIZE(dwc3_core_clks);
ret = clk_bulk_get(dev, dwc->num_clks, dwc->clks);
ret = devm_clk_bulk_get(dev, dwc->num_clks, dwc->clks);
if (ret == -EPROBE_DEFER)
return ret;
/*
@@ -1449,16 +1457,12 @@ static int dwc3_probe(struct platform_device *pdev)
ret = reset_control_deassert(dwc->reset);
if (ret)
goto put_clks;
return ret;
ret = clk_bulk_prepare(dwc->num_clks, dwc->clks);
ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks);
if (ret)
goto assert_reset;
ret = clk_bulk_enable(dwc->num_clks, dwc->clks);
if (ret)
goto unprepare_clks;
if (!dwc3_core_is_valid(dwc)) {
dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
ret = -ENODEV;
@@ -1531,13 +1535,9 @@ err1:
pm_runtime_disable(&pdev->dev);
disable_clks:
clk_bulk_disable(dwc->num_clks, dwc->clks);
unprepare_clks:
clk_bulk_unprepare(dwc->num_clks, dwc->clks);
clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
assert_reset:
reset_control_assert(dwc->reset);
put_clks:
clk_bulk_put(dwc->num_clks, dwc->clks);
return ret;
}
@@ -1560,7 +1560,6 @@ static int dwc3_remove(struct platform_device *pdev)
dwc3_free_event_buffers(dwc);
dwc3_free_scratch_buffers(dwc);
clk_bulk_put(dwc->num_clks, dwc->clks);
return 0;
}
@@ -1574,14 +1573,10 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc)
if (ret)
return ret;
ret = clk_bulk_prepare(dwc->num_clks, dwc->clks);
ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks);
if (ret)
goto assert_reset;
ret = clk_bulk_enable(dwc->num_clks, dwc->clks);
if (ret)
goto unprepare_clks;
ret = dwc3_core_init(dwc);
if (ret)
goto disable_clks;
@@ -1589,9 +1584,7 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc)
return 0;
disable_clks:
clk_bulk_disable(dwc->num_clks, dwc->clks);
unprepare_clks:
clk_bulk_unprepare(dwc->num_clks, dwc->clks);
clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
assert_reset:
reset_control_assert(dwc->reset);

View File

@@ -1137,6 +1137,8 @@ struct dwc3 {
#define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31)
#define DWC3_USB31_REVISION_160A (0x3136302a | DWC3_REVISION_IS_DWC31)
#define DWC3_USB31_REVISION_170A (0x3137302a | DWC3_REVISION_IS_DWC31)
#define DWC3_USB31_REVISION_180A (0x3138302a | DWC3_REVISION_IS_DWC31)
#define DWC3_USB31_REVISION_190A (0x3139302a | DWC3_REVISION_IS_DWC31)
u32 version_type;

View File

@@ -246,258 +246,6 @@ static inline const char *dwc3_gadget_event_string(char *str, size_t size,
return str;
}
static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str,
size_t size)
{
switch (t & USB_RECIP_MASK) {
case USB_RECIP_DEVICE:
snprintf(str, size, "Get Device Status(Length = %d)", l);
break;
case USB_RECIP_INTERFACE:
snprintf(str, size, "Get Interface Status(Intf = %d, Length = %d)",
i, l);
break;
case USB_RECIP_ENDPOINT:
snprintf(str, size, "Get Endpoint Status(ep%d%s)",
i & ~USB_DIR_IN,
i & USB_DIR_IN ? "in" : "out");
break;
}
}
static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v,
__u16 i, char *str, size_t size)
{
switch (t & USB_RECIP_MASK) {
case USB_RECIP_DEVICE:
snprintf(str, size, "%s Device Feature(%s%s)",
b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
({char *s;
switch (v) {
case USB_DEVICE_SELF_POWERED:
s = "Self Powered";
break;
case USB_DEVICE_REMOTE_WAKEUP:
s = "Remote Wakeup";
break;
case USB_DEVICE_TEST_MODE:
s = "Test Mode";
break;
case USB_DEVICE_U1_ENABLE:
s = "U1 Enable";
break;
case USB_DEVICE_U2_ENABLE:
s = "U2 Enable";
break;
case USB_DEVICE_LTM_ENABLE:
s = "LTM Enable";
break;
default:
s = "UNKNOWN";
} s; }),
v == USB_DEVICE_TEST_MODE ?
({ char *s;
switch (i) {
case TEST_J:
s = ": TEST_J";
break;
case TEST_K:
s = ": TEST_K";
break;
case TEST_SE0_NAK:
s = ": TEST_SE0_NAK";
break;
case TEST_PACKET:
s = ": TEST_PACKET";
break;
case TEST_FORCE_EN:
s = ": TEST_FORCE_EN";
break;
default:
s = ": UNKNOWN";
} s; }) : "");
break;
case USB_RECIP_INTERFACE:
snprintf(str, size, "%s Interface Feature(%s)",
b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
v == USB_INTRF_FUNC_SUSPEND ?
"Function Suspend" : "UNKNOWN");
break;
case USB_RECIP_ENDPOINT:
snprintf(str, size, "%s Endpoint Feature(%s ep%d%s)",
b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
v == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN",
i & ~USB_DIR_IN,
i & USB_DIR_IN ? "in" : "out");
break;
}
}
static inline void dwc3_decode_set_address(__u16 v, char *str, size_t size)
{
snprintf(str, size, "Set Address(Addr = %02x)", v);
}
static inline void dwc3_decode_get_set_descriptor(__u8 t, __u8 b, __u16 v,
__u16 i, __u16 l, char *str, size_t size)
{
snprintf(str, size, "%s %s Descriptor(Index = %d, Length = %d)",
b == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set",
({ char *s;
switch (v >> 8) {
case USB_DT_DEVICE:
s = "Device";
break;
case USB_DT_CONFIG:
s = "Configuration";
break;
case USB_DT_STRING:
s = "String";
break;
case USB_DT_INTERFACE:
s = "Interface";
break;
case USB_DT_ENDPOINT:
s = "Endpoint";
break;
case USB_DT_DEVICE_QUALIFIER:
s = "Device Qualifier";
break;
case USB_DT_OTHER_SPEED_CONFIG:
s = "Other Speed Config";
break;
case USB_DT_INTERFACE_POWER:
s = "Interface Power";
break;
case USB_DT_OTG:
s = "OTG";
break;
case USB_DT_DEBUG:
s = "Debug";
break;
case USB_DT_INTERFACE_ASSOCIATION:
s = "Interface Association";
break;
case USB_DT_BOS:
s = "BOS";
break;
case USB_DT_DEVICE_CAPABILITY:
s = "Device Capability";
break;
case USB_DT_PIPE_USAGE:
s = "Pipe Usage";
break;
case USB_DT_SS_ENDPOINT_COMP:
s = "SS Endpoint Companion";
break;
case USB_DT_SSP_ISOC_ENDPOINT_COMP:
s = "SSP Isochronous Endpoint Companion";
break;
default:
s = "UNKNOWN";
break;
} s; }), v & 0xff, l);
}
static inline void dwc3_decode_get_configuration(__u16 l, char *str,
size_t size)
{
snprintf(str, size, "Get Configuration(Length = %d)", l);
}
static inline void dwc3_decode_set_configuration(__u8 v, char *str, size_t size)
{
snprintf(str, size, "Set Configuration(Config = %d)", v);
}
static inline void dwc3_decode_get_intf(__u16 i, __u16 l, char *str,
size_t size)
{
snprintf(str, size, "Get Interface(Intf = %d, Length = %d)", i, l);
}
static inline void dwc3_decode_set_intf(__u8 v, __u16 i, char *str, size_t size)
{
snprintf(str, size, "Set Interface(Intf = %d, Alt.Setting = %d)", i, v);
}
static inline void dwc3_decode_synch_frame(__u16 i, __u16 l, char *str,
size_t size)
{
snprintf(str, size, "Synch Frame(Endpoint = %d, Length = %d)", i, l);
}
static inline void dwc3_decode_set_sel(__u16 l, char *str, size_t size)
{
snprintf(str, size, "Set SEL(Length = %d)", l);
}
static inline void dwc3_decode_set_isoch_delay(__u8 v, char *str, size_t size)
{
snprintf(str, size, "Set Isochronous Delay(Delay = %d ns)", v);
}
/**
* dwc3_decode_ctrl - returns a string represetion of ctrl request
*/
static inline const char *dwc3_decode_ctrl(char *str, size_t size,
__u8 bRequestType, __u8 bRequest, __u16 wValue, __u16 wIndex,
__u16 wLength)
{
switch (bRequest) {
case USB_REQ_GET_STATUS:
dwc3_decode_get_status(bRequestType, wIndex, wLength, str,
size);
break;
case USB_REQ_CLEAR_FEATURE:
case USB_REQ_SET_FEATURE:
dwc3_decode_set_clear_feature(bRequestType, bRequest, wValue,
wIndex, str, size);
break;
case USB_REQ_SET_ADDRESS:
dwc3_decode_set_address(wValue, str, size);
break;
case USB_REQ_GET_DESCRIPTOR:
case USB_REQ_SET_DESCRIPTOR:
dwc3_decode_get_set_descriptor(bRequestType, bRequest, wValue,
wIndex, wLength, str, size);
break;
case USB_REQ_GET_CONFIGURATION:
dwc3_decode_get_configuration(wLength, str, size);
break;
case USB_REQ_SET_CONFIGURATION:
dwc3_decode_set_configuration(wValue, str, size);
break;
case USB_REQ_GET_INTERFACE:
dwc3_decode_get_intf(wIndex, wLength, str, size);
break;
case USB_REQ_SET_INTERFACE:
dwc3_decode_set_intf(wValue, wIndex, str, size);
break;
case USB_REQ_SYNCH_FRAME:
dwc3_decode_synch_frame(wIndex, wLength, str, size);
break;
case USB_REQ_SET_SEL:
dwc3_decode_set_sel(wLength, str, size);
break;
case USB_REQ_SET_ISOCH_DELAY:
dwc3_decode_set_isoch_delay(wValue, str, size);
break;
default:
snprintf(str, size, "%02x %02x %02x %02x %02x %02x %02x %02x",
bRequestType, bRequest,
cpu_to_le16(wValue) & 0xff,
cpu_to_le16(wValue) >> 8,
cpu_to_le16(wIndex) & 0xff,
cpu_to_le16(wIndex) >> 8,
cpu_to_le16(wLength) & 0xff,
cpu_to_le16(wLength) >> 8);
}
return str;
}
/**
* dwc3_ep_event_string - returns event name
* @event: then event code

View File

@@ -81,7 +81,6 @@ static int kdwc3_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *node = pdev->dev.of_node;
struct dwc3_keystone *kdwc;
struct resource *res;
int error, irq;
kdwc = devm_kzalloc(dev, sizeof(*kdwc), GFP_KERNEL);
@@ -92,8 +91,7 @@ static int kdwc3_probe(struct platform_device *pdev)
kdwc->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
kdwc->usbss = devm_ioremap_resource(dev, res);
kdwc->usbss = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(kdwc->usbss))
return PTR_ERR(kdwc->usbss);
@@ -112,7 +110,6 @@ static int kdwc3_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "missing irq\n");
error = irq;
goto err_irq;
}

View File

@@ -386,7 +386,6 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
void __iomem *base;
struct resource *res;
enum phy_mode otg_id;
int ret, i, irq;
@@ -394,8 +393,7 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, res);
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -564,7 +562,13 @@ static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev)
static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev)
{
struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
int i;
int i, ret;
if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) {
ret = regulator_disable(priv->vbus);
if (ret)
return ret;
}
for (i = 0 ; i < PHY_COUNT ; ++i) {
phy_power_off(priv->phys[i]);
@@ -599,6 +603,12 @@ static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
return ret;
}
if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) {
ret = regulator_enable(priv->vbus);
if (ret)
return ret;
}
return 0;
}

View File

@@ -14,7 +14,6 @@
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/platform_data/dwc3-omap.h>
#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
#include <linux/ioport.h>
@@ -106,6 +105,12 @@
#define USBOTGSS_UTMI_OTG_CTRL_SESSVALID BIT(2)
#define USBOTGSS_UTMI_OTG_CTRL_VBUSVALID BIT(1)
enum dwc3_omap_utmi_mode {
DWC3_OMAP_UTMI_MODE_UNKNOWN = 0,
DWC3_OMAP_UTMI_MODE_HW,
DWC3_OMAP_UTMI_MODE_SW,
};
struct dwc3_omap {
struct device *dev;
@@ -446,7 +451,6 @@ static int dwc3_omap_probe(struct platform_device *pdev)
struct device_node *node = pdev->dev.of_node;
struct dwc3_omap *omap;
struct resource *res;
struct device *dev = &pdev->dev;
struct regulator *vbus_reg = NULL;
@@ -469,13 +473,10 @@ static int dwc3_omap_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, omap);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(dev, "missing IRQ resource: %d\n", irq);
if (irq < 0)
return irq;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, res);
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);

View File

@@ -255,24 +255,26 @@ static int st_dwc3_probe(struct platform_device *pdev)
if (!child) {
dev_err(&pdev->dev, "failed to find dwc3 core node\n");
ret = -ENODEV;
goto undo_softreset;
goto err_node_put;
}
/* Allocate and initialize the core */
ret = of_platform_populate(node, NULL, NULL, dev);
if (ret) {
dev_err(dev, "failed to add dwc3 core\n");
goto undo_softreset;
goto err_node_put;
}
child_pdev = of_find_device_by_node(child);
if (!child_pdev) {
dev_err(dev, "failed to find dwc3 core device\n");
ret = -ENODEV;
goto undo_softreset;
goto err_node_put;
}
dwc3_data->dr_mode = usb_get_dr_mode(&child_pdev->dev);
of_node_put(child);
of_dev_put(child_pdev);
/*
* Configure the USB port as device or host according to the static
@@ -292,6 +294,8 @@ static int st_dwc3_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dwc3_data);
return 0;
err_node_put:
of_node_put(child);
undo_softreset:
reset_control_assert(dwc3_data->rstc_rst);
undo_powerdown:

View File

@@ -2078,6 +2078,26 @@ static void dwc3_gadget_config_params(struct usb_gadget *g,
{
struct dwc3 *dwc = gadget_to_dwc(g);
params->besl_baseline = USB_DEFAULT_BESL_UNSPECIFIED;
params->besl_deep = USB_DEFAULT_BESL_UNSPECIFIED;
/* Recommended BESL */
if (!dwc->dis_enblslpm_quirk) {
/*
* If the recommended BESL baseline is 0 or if the BESL deep is
* less than 2, Microsoft's Windows 10 host usb stack will issue
* a usb reset immediately after it receives the extended BOS
* descriptor and the enumeration will fail. To maintain
* compatibility with the Windows' usb stack, let's set the
* recommended BESL baseline to 1 and clamp the BESL deep to be
* within 2 to 15.
*/
params->besl_baseline = 1;
if (dwc->is_utmi_l1_suspend)
params->besl_deep =
clamp_t(u8, dwc->hird_threshold, 2, 15);
}
/* U1 Device exit Latency */
if (dwc->dis_u1_entry_quirk)
params->bU1devExitLat = 0;
@@ -2868,7 +2888,8 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold);
reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold |
(dwc->is_utmi_l1_suspend << 4));
/*
* When dwc3 revisions >= 2.40a, LPM Erratum is enabled and
@@ -3318,7 +3339,6 @@ int dwc3_gadget_init(struct dwc3 *dwc)
dwc->gadget.speed = USB_SPEED_UNKNOWN;
dwc->gadget.sg_supported = true;
dwc->gadget.name = "dwc3-gadget";
dwc->gadget.is_otg = dwc->dr_mode == USB_DR_MODE_OTG;
dwc->gadget.lpm_capable = true;
/*

View File

@@ -85,7 +85,7 @@ int dwc3_host_init(struct dwc3 *dwc)
DWC3_XHCI_RESOURCES_NUM);
if (ret) {
dev_err(dwc->dev, "couldn't add resources to xHCI device\n");
goto err1;
goto err;
}
memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props));
@@ -112,37 +112,23 @@ int dwc3_host_init(struct dwc3 *dwc)
ret = platform_device_add_properties(xhci, props);
if (ret) {
dev_err(dwc->dev, "failed to add properties to xHCI\n");
goto err1;
goto err;
}
}
phy_create_lookup(dwc->usb2_generic_phy, "usb2-phy",
dev_name(dwc->dev));
phy_create_lookup(dwc->usb3_generic_phy, "usb3-phy",
dev_name(dwc->dev));
ret = platform_device_add(xhci);
if (ret) {
dev_err(dwc->dev, "failed to register xHCI device\n");
goto err2;
goto err;
}
return 0;
err2:
phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy",
dev_name(dwc->dev));
phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
dev_name(dwc->dev));
err1:
err:
platform_device_put(xhci);
return ret;
}
void dwc3_host_exit(struct dwc3 *dwc)
{
phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy",
dev_name(dwc->dev));
phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
dev_name(dwc->dev));
platform_device_unregister(dwc->xhci);
}

View File

@@ -86,7 +86,7 @@ DECLARE_EVENT_CLASS(dwc3_log_ctrl,
__entry->wIndex = le16_to_cpu(ctrl->wIndex);
__entry->wLength = le16_to_cpu(ctrl->wLength);
),
TP_printk("%s", dwc3_decode_ctrl(__get_str(str), DWC3_MSG_MAX,
TP_printk("%s", usb_decode_ctrl(__get_str(str), DWC3_MSG_MAX,
__entry->bRequestType,
__entry->bRequest, __entry->wValue,
__entry->wIndex, __entry->wLength)

View File

@@ -612,6 +612,7 @@ static int bos_desc(struct usb_composite_dev *cdev)
struct usb_ext_cap_descriptor *usb_ext;
struct usb_dcd_config_params dcd_config_params;
struct usb_bos_descriptor *bos = cdev->req->buf;
unsigned int besl = 0;
bos->bLength = USB_DT_BOS_SIZE;
bos->bDescriptorType = USB_DT_BOS;
@@ -619,6 +620,29 @@ static int bos_desc(struct usb_composite_dev *cdev)
bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE);
bos->bNumDeviceCaps = 0;
/* Get Controller configuration */
if (cdev->gadget->ops->get_config_params) {
cdev->gadget->ops->get_config_params(cdev->gadget,
&dcd_config_params);
} else {
dcd_config_params.besl_baseline =
USB_DEFAULT_BESL_UNSPECIFIED;
dcd_config_params.besl_deep =
USB_DEFAULT_BESL_UNSPECIFIED;
dcd_config_params.bU1devExitLat =
USB_DEFAULT_U1_DEV_EXIT_LAT;
dcd_config_params.bU2DevExitLat =
cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
}
if (dcd_config_params.besl_baseline != USB_DEFAULT_BESL_UNSPECIFIED)
besl = USB_BESL_BASELINE_VALID |
USB_SET_BESL_BASELINE(dcd_config_params.besl_baseline);
if (dcd_config_params.besl_deep != USB_DEFAULT_BESL_UNSPECIFIED)
besl |= USB_BESL_DEEP_VALID |
USB_SET_BESL_DEEP(dcd_config_params.besl_deep);
/*
* A SuperSpeed device shall include the USB2.0 extension descriptor
* and shall support LPM when operating in USB2.0 HS mode.
@@ -629,7 +653,8 @@ static int bos_desc(struct usb_composite_dev *cdev)
usb_ext->bLength = USB_DT_USB_EXT_CAP_SIZE;
usb_ext->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT;
usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT | USB_BESL_SUPPORT);
usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT |
USB_BESL_SUPPORT | besl);
/*
* The Superspeed USB Capability descriptor shall be implemented by all
@@ -650,17 +675,6 @@ static int bos_desc(struct usb_composite_dev *cdev)
USB_HIGH_SPEED_OPERATION |
USB_5GBPS_OPERATION);
ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
/* Get Controller configuration */
if (cdev->gadget->ops->get_config_params) {
cdev->gadget->ops->get_config_params(cdev->gadget,
&dcd_config_params);
} else {
dcd_config_params.bU1devExitLat =
USB_DEFAULT_U1_DEV_EXIT_LAT;
dcd_config_params.bU2DevExitLat =
cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
}
ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
}

View File

@@ -65,14 +65,16 @@ void ast_vhub_done(struct ast_vhub_ep *ep, struct ast_vhub_req *req,
void ast_vhub_nuke(struct ast_vhub_ep *ep, int status)
{
struct ast_vhub_req *req;
EPDBG(ep, "Nuking\n");
int count = 0;
/* Beware, lock will be dropped & req-acquired by done() */
while (!list_empty(&ep->queue)) {
req = list_first_entry(&ep->queue, struct ast_vhub_req, queue);
ast_vhub_done(ep, req, status);
count++;
}
if (count)
EPDBG(ep, "Nuked %d request(s)\n", count);
}
struct usb_request *ast_vhub_alloc_request(struct usb_ep *u_ep,
@@ -348,7 +350,6 @@ static int ast_vhub_probe(struct platform_device *pdev)
/* Find interrupt and install handler */
vhub->irq = platform_get_irq(pdev, 0);
if (vhub->irq < 0) {
dev_err(&pdev->dev, "Failed to get interrupt\n");
rc = vhub->irq;
goto err;
}

View File

@@ -50,11 +50,14 @@ void ast_vhub_dev_irq(struct ast_vhub_dev *d)
static void ast_vhub_dev_enable(struct ast_vhub_dev *d)
{
u32 reg, hmsk;
u32 reg, hmsk, i;
if (d->enabled)
return;
/* Cleanup EP0 state */
ast_vhub_reset_ep0(d);
/* Enable device and its EP0 interrupts */
reg = VHUB_DEV_EN_ENABLE_PORT |
VHUB_DEV_EN_EP0_IN_ACK_IRQEN |
@@ -73,6 +76,19 @@ static void ast_vhub_dev_enable(struct ast_vhub_dev *d)
/* Set EP0 DMA buffer address */
writel(d->ep0.buf_dma, d->regs + AST_VHUB_DEV_EP0_DATA);
/* Clear stall on all EPs */
for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) {
struct ast_vhub_ep *ep = d->epns[i];
if (ep && (ep->epn.stalled || ep->epn.wedged)) {
ep->epn.stalled = false;
ep->epn.wedged = false;
ast_vhub_update_epn_stall(ep);
}
}
/* Additional cleanups */
d->wakeup_en = false;
d->enabled = true;
}
@@ -93,7 +109,6 @@ static void ast_vhub_dev_disable(struct ast_vhub_dev *d)
writel(0, d->regs + AST_VHUB_DEV_EN_CTRL);
d->gadget.speed = USB_SPEED_UNKNOWN;
d->enabled = false;
d->suspended = false;
}
static int ast_vhub_dev_feature(struct ast_vhub_dev *d,
@@ -201,14 +216,19 @@ int ast_vhub_std_dev_request(struct ast_vhub_ep *ep,
u16 wValue, wIndex;
/* No driver, we shouldn't be enabled ... */
if (!d->driver || !d->enabled || d->suspended) {
if (!d->driver || !d->enabled) {
EPDBG(ep,
"Device is wrong state driver=%p enabled=%d"
" suspended=%d\n",
d->driver, d->enabled, d->suspended);
"Device is wrong state driver=%p enabled=%d\n",
d->driver, d->enabled);
return std_req_stall;
}
/*
* Note: we used to reject/stall requests while suspended,
* we don't do that anymore as we seem to have cases of
* mass storage getting very upset.
*/
/* First packet, grab speed */
if (d->gadget.speed == USB_SPEED_UNKNOWN) {
d->gadget.speed = ep->vhub->speed;
@@ -449,8 +469,7 @@ static const struct usb_gadget_ops ast_vhub_udc_ops = {
void ast_vhub_dev_suspend(struct ast_vhub_dev *d)
{
d->suspended = true;
if (d->driver) {
if (d->driver && d->driver->suspend) {
spin_unlock(&d->vhub->lock);
d->driver->suspend(&d->gadget);
spin_lock(&d->vhub->lock);
@@ -459,8 +478,7 @@ void ast_vhub_dev_suspend(struct ast_vhub_dev *d)
void ast_vhub_dev_resume(struct ast_vhub_dev *d)
{
d->suspended = false;
if (d->driver) {
if (d->driver && d->driver->resume) {
spin_unlock(&d->vhub->lock);
d->driver->resume(&d->gadget);
spin_lock(&d->vhub->lock);
@@ -469,46 +487,28 @@ void ast_vhub_dev_resume(struct ast_vhub_dev *d)
void ast_vhub_dev_reset(struct ast_vhub_dev *d)
{
/*
* If speed is not set, we enable the port. If it is,
* send reset to the gadget and reset "speed".
*
* Speed is an indication that we have got the first
* setup packet to the device.
*/
if (d->gadget.speed == USB_SPEED_UNKNOWN && !d->enabled) {
DDBG(d, "Reset at unknown speed of disabled device, enabling...\n");
ast_vhub_dev_enable(d);
d->suspended = false;
/* No driver, just disable the device and return */
if (!d->driver) {
ast_vhub_dev_disable(d);
return;
}
if (d->gadget.speed != USB_SPEED_UNKNOWN && d->driver) {
unsigned int i;
DDBG(d, "Reset at known speed of bound device, resetting...\n");
/* If the port isn't enabled, just enable it */
if (!d->enabled) {
DDBG(d, "Reset of disabled device, enabling...\n");
ast_vhub_dev_enable(d);
} else {
DDBG(d, "Reset of enabled device, resetting...\n");
spin_unlock(&d->vhub->lock);
d->driver->reset(&d->gadget);
usb_gadget_udc_reset(&d->gadget, d->driver);
spin_lock(&d->vhub->lock);
/*
* Disable/re-enable HW, this will clear the address
* Disable and maybe re-enable HW, this will clear the address
* and speed setting.
*/
ast_vhub_dev_disable(d);
ast_vhub_dev_enable(d);
/* Clear stall on all EPs */
for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) {
struct ast_vhub_ep *ep = d->epns[i];
if (ep && ep->epn.stalled) {
ep->epn.stalled = false;
ast_vhub_update_epn_stall(ep);
}
}
/* Additional cleanups */
d->wakeup_en = false;
d->suspended = false;
}
}

View File

@@ -105,18 +105,20 @@ void ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep)
(crq.bRequestType & USB_DIR_IN) ? "in" : "out",
ep->ep0.state);
/* Check our state, cancel pending requests if needed */
if (ep->ep0.state != ep0_state_token) {
/*
* Check our state, cancel pending requests if needed
*
* Note: Under some circumstances, we can get a new setup
* packet while waiting for the stall ack, just accept it.
*
* In any case, a SETUP packet in wrong state should have
* reset the HW state machine, so let's just log, nuke
* requests, move on.
*/
if (ep->ep0.state != ep0_state_token &&
ep->ep0.state != ep0_state_stall) {
EPDBG(ep, "wrong state\n");
ast_vhub_nuke(ep, -EIO);
/*
* Accept the packet regardless, this seems to happen
* when stalling a SETUP packet that has an OUT data
* phase.
*/
ast_vhub_nuke(ep, 0);
goto stall;
}
/* Calculate next state for EP0 */
@@ -165,7 +167,7 @@ void ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep)
stall:
EPDBG(ep, "stalling\n");
writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat);
ep->ep0.state = ep0_state_status;
ep->ep0.state = ep0_state_stall;
ep->ep0.dir_in = false;
return;
@@ -299,8 +301,8 @@ void ast_vhub_ep0_handle_ack(struct ast_vhub_ep *ep, bool in_ack)
if ((ep->ep0.dir_in && (stat & VHUB_EP0_TX_BUFF_RDY)) ||
(!ep->ep0.dir_in && (stat & VHUB_EP0_RX_BUFF_RDY)) ||
(ep->ep0.dir_in != in_ack)) {
/* In that case, ignore interrupt */
dev_warn(dev, "irq state mismatch");
stall = true;
break;
}
/*
@@ -335,12 +337,22 @@ void ast_vhub_ep0_handle_ack(struct ast_vhub_ep *ep, bool in_ack)
dev_warn(dev, "status direction mismatch\n");
stall = true;
}
break;
case ep0_state_stall:
/*
* There shouldn't be any request left, but nuke just in case
* otherwise the stale request will block subsequent ones
*/
ast_vhub_nuke(ep, -EIO);
break;
}
/* Reset to token state */
ep->ep0.state = ep0_state_token;
if (stall)
/* Reset to token state or stall */
if (stall) {
writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat);
ep->ep0.state = ep0_state_stall;
} else
ep->ep0.state = ep0_state_token;
}
static int ast_vhub_ep0_queue(struct usb_ep* u_ep, struct usb_request *u_req,
@@ -367,7 +379,7 @@ static int ast_vhub_ep0_queue(struct usb_ep* u_ep, struct usb_request *u_req,
return -EINVAL;
/* Disabled device */
if (ep->dev && (!ep->dev->enabled || ep->dev->suspended))
if (ep->dev && !ep->dev->enabled)
return -ESHUTDOWN;
/* Data, no buffer and not internal ? */
@@ -390,8 +402,12 @@ static int ast_vhub_ep0_queue(struct usb_ep* u_ep, struct usb_request *u_req,
spin_lock_irqsave(&vhub->lock, flags);
/* EP0 can only support a single request at a time */
if (!list_empty(&ep->queue) || ep->ep0.state == ep0_state_token) {
if (!list_empty(&ep->queue) ||
ep->ep0.state == ep0_state_token ||
ep->ep0.state == ep0_state_stall) {
dev_warn(dev, "EP0: Request in wrong state\n");
EPVDBG(ep, "EP0: list_empty=%d state=%d\n",
list_empty(&ep->queue), ep->ep0.state);
spin_unlock_irqrestore(&vhub->lock, flags);
return -EBUSY;
}
@@ -459,6 +475,15 @@ static const struct usb_ep_ops ast_vhub_ep0_ops = {
.free_request = ast_vhub_free_request,
};
void ast_vhub_reset_ep0(struct ast_vhub_dev *dev)
{
struct ast_vhub_ep *ep = &dev->ep0;
ast_vhub_nuke(ep, -EIO);
ep->ep0.state = ep0_state_token;
}
void ast_vhub_init_ep0(struct ast_vhub *vhub, struct ast_vhub_ep *ep,
struct ast_vhub_dev *dev)
{

View File

@@ -352,7 +352,7 @@ static int ast_vhub_epn_queue(struct usb_ep* u_ep, struct usb_request *u_req,
/* Endpoint enabled ? */
if (!ep->epn.enabled || !u_ep->desc || !ep->dev || !ep->d_idx ||
!ep->dev->enabled || ep->dev->suspended) {
!ep->dev->enabled) {
EPDBG(ep, "Enqueuing request on wrong or disabled EP\n");
return -ESHUTDOWN;
}

View File

@@ -449,8 +449,15 @@ static void ast_vhub_change_port_stat(struct ast_vhub *vhub,
USB_PORT_STAT_C_OVERCURRENT |
USB_PORT_STAT_C_RESET |
USB_PORT_STAT_C_L1;
p->change |= chg;
/*
* We only set USB_PORT_STAT_C_ENABLE if we are disabling
* the port as per USB spec, otherwise MacOS gets upset
*/
if (p->status & USB_PORT_STAT_ENABLE)
chg &= ~USB_PORT_STAT_C_ENABLE;
p->change = chg;
ast_vhub_update_hub_ep1(vhub, port);
}
}
@@ -723,6 +730,12 @@ enum std_req_rc ast_vhub_class_hub_request(struct ast_vhub_ep *ep,
case ClearPortFeature:
EPDBG(ep, "ClearPortFeature(%d,%d)\n", wIndex & 0xf, wValue);
return ast_vhub_clr_port_feature(ep, wIndex & 0xf, wValue);
case ClearTTBuffer:
case ResetTT:
case StopTT:
return std_req_complete;
case GetTTState:
return ast_vhub_simple_reply(ep, 0, 0, 0, 0);
default:
EPDBG(ep, "Unknown class request\n");
}

View File

@@ -257,6 +257,7 @@ enum ep0_state {
ep0_state_token,
ep0_state_data,
ep0_state_status,
ep0_state_stall,
};
/*
@@ -353,7 +354,6 @@ struct ast_vhub_dev {
struct usb_gadget_driver *driver;
bool registered : 1;
bool wakeup_en : 1;
bool suspended : 1;
bool enabled : 1;
/* Endpoint structures */
@@ -507,6 +507,7 @@ void ast_vhub_init_hw(struct ast_vhub *vhub);
/* ep0.c */
void ast_vhub_ep0_handle_ack(struct ast_vhub_ep *ep, bool in_ack);
void ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep);
void ast_vhub_reset_ep0(struct ast_vhub_dev *dev);
void ast_vhub_init_ep0(struct ast_vhub *vhub, struct ast_vhub_ep *ep,
struct ast_vhub_dev *dev);
int ast_vhub_reply(struct ast_vhub_ep *ep, char *ptr, int len);

View File

@@ -327,6 +327,7 @@ static int usba_config_fifo_table(struct usba_udc *udc)
switch (fifo_mode) {
default:
fifo_mode = 0;
/* fall through */
case 0:
udc->fifo_cfg = NULL;
n = 0;

View File

@@ -2328,10 +2328,8 @@ static int bcm63xx_udc_probe(struct platform_device *pdev)
/* IRQ resource #0: control interrupt (VBUS, speed, etc.) */
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(dev, "missing IRQ resource #0\n");
if (irq < 0)
goto out_uninit;
}
if (devm_request_irq(dev, irq, &bcm63xx_udc_ctrl_isr, 0,
dev_name(dev), udc) < 0)
goto report_request_failure;
@@ -2339,10 +2337,8 @@ static int bcm63xx_udc_probe(struct platform_device *pdev)
/* IRQ resources #1-6: data interrupts for IUDMA channels 0-5 */
for (i = 0; i < BCM63XX_NUM_IUDMA; i++) {
irq = platform_get_irq(pdev, i + 1);
if (irq < 0) {
dev_err(dev, "missing IRQ resource #%d\n", i + 1);
if (irq < 0)
goto out_uninit;
}
if (devm_request_irq(dev, irq, &bcm63xx_udc_data_isr, 0,
dev_name(dev), &udc->iudma[i]) < 0)
goto report_request_failure;

View File

@@ -515,10 +515,8 @@ static int bdc_probe(struct platform_device *pdev)
return -ENOMEM;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(dev, "platform_get_irq failed:%d\n", irq);
if (irq < 0)
return irq;
}
spin_lock_init(&bdc->lock);
platform_set_drvdata(pdev, bdc);
bdc->irq = irq;

View File

@@ -1143,7 +1143,7 @@ static int check_pending_gadget_drivers(struct usb_udc *udc)
dev_name(&udc->dev)) == 0) {
ret = udc_bind_to_driver(udc, driver);
if (ret != -EPROBE_DEFER)
list_del(&driver->pending);
list_del_init(&driver->pending);
break;
}

View File

@@ -2134,19 +2134,15 @@ static int gr_probe(struct platform_device *pdev)
return PTR_ERR(regs);
dev->irq = platform_get_irq(pdev, 0);
if (dev->irq <= 0) {
dev_err(dev->dev, "No irq found\n");
if (dev->irq <= 0)
return -ENODEV;
}
/* Some core configurations has separate irqs for IN and OUT events */
dev->irqi = platform_get_irq(pdev, 1);
if (dev->irqi > 0) {
dev->irqo = platform_get_irq(pdev, 2);
if (dev->irqo <= 0) {
dev_err(dev->dev, "Found irqi but not irqo\n");
if (dev->irqo <= 0)
return -ENODEV;
}
} else {
dev->irqi = 0;
}

View File

@@ -741,7 +741,6 @@ static inline void udc_protocol_cmd_data_w(struct lpc32xx_udc *udc, u32 cmd,
* response data */
static u32 udc_protocol_cmd_r(struct lpc32xx_udc *udc, u32 cmd)
{
u32 tmp;
int to = 1000;
/* Write a command and read data from the protocol engine */
@@ -751,7 +750,6 @@ static u32 udc_protocol_cmd_r(struct lpc32xx_udc *udc, u32 cmd)
/* Write command code */
udc_protocol_cmd_w(udc, cmd);
tmp = readl(USBD_DEVINTST(udc->udp_baseaddr));
while ((!(readl(USBD_DEVINTST(udc->udp_baseaddr)) & USBD_CDFULL))
&& (to > 0))
to--;
@@ -1991,7 +1989,7 @@ void udc_handle_eps(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
/* DMA end of transfer completion */
static void udc_handle_dma_ep(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
{
u32 status, epstatus;
u32 status;
struct lpc32xx_request *req;
struct lpc32xx_usbd_dd_gad *dd;
@@ -2085,7 +2083,7 @@ static void udc_handle_dma_ep(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
if (udc_clearep_getsts(udc, ep->hwep_num) & EP_SEL_F) {
udc_clearep_getsts(udc, ep->hwep_num);
uda_enable_hwepint(udc, ep->hwep_num);
epstatus = udc_clearep_getsts(udc, ep->hwep_num);
udc_clearep_getsts(udc, ep->hwep_num);
/* Let the EP interrupt handle the ZLP */
return;
@@ -2197,7 +2195,7 @@ static void udc_handle_ep0_setup(struct lpc32xx_udc *udc)
struct lpc32xx_ep *ep, *ep0 = &udc->ep[0];
struct usb_ctrlrequest ctrlpkt;
int i, bytes;
u16 wIndex, wValue, wLength, reqtype, req, tmp;
u16 wIndex, wValue, reqtype, req, tmp;
/* Nuke previous transfers */
nuke(ep0, -EPROTO);
@@ -2213,7 +2211,6 @@ static void udc_handle_ep0_setup(struct lpc32xx_udc *udc)
/* Native endianness */
wIndex = le16_to_cpu(ctrlpkt.wIndex);
wValue = le16_to_cpu(ctrlpkt.wValue);
wLength = le16_to_cpu(ctrlpkt.wLength);
reqtype = le16_to_cpu(ctrlpkt.bRequestType);
/* Set direction of EP0 */
@@ -3060,11 +3057,8 @@ static int lpc32xx_udc_probe(struct platform_device *pdev)
/* Get IRQs */
for (i = 0; i < 4; i++) {
udc->udp_irq[i] = platform_get_irq(pdev, i);
if (udc->udp_irq[i] < 0) {
dev_err(udc->dev,
"irq resource %d not available!\n", i);
if (udc->udp_irq[i] < 0)
return udc->udp_irq[i];
}
}
udc->udp_baseaddr = devm_ioremap_resource(dev, res);

View File

@@ -2244,30 +2244,40 @@ static void usb_reinit_338x(struct net2280 *dev)
}
/* Hardware Defect and Workaround */
val = readl(&dev->ll_lfps_regs->ll_lfps_5);
val = readl(&dev->llregs->ll_lfps_5);
val &= ~(0xf << TIMER_LFPS_6US);
val |= 0x5 << TIMER_LFPS_6US;
writel(val, &dev->ll_lfps_regs->ll_lfps_5);
writel(val, &dev->llregs->ll_lfps_5);
val = readl(&dev->ll_lfps_regs->ll_lfps_6);
val = readl(&dev->llregs->ll_lfps_6);
val &= ~(0xffff << TIMER_LFPS_80US);
val |= 0x0100 << TIMER_LFPS_80US;
writel(val, &dev->ll_lfps_regs->ll_lfps_6);
writel(val, &dev->llregs->ll_lfps_6);
/*
* AA_AB Errata. Issue 4. Workaround for SuperSpeed USB
* Hot Reset Exit Handshake may Fail in Specific Case using
* Default Register Settings. Workaround for Enumeration test.
*/
val = readl(&dev->ll_tsn_regs->ll_tsn_counters_2);
val = readl(&dev->llregs->ll_tsn_counters_2);
val &= ~(0x1f << HOT_TX_NORESET_TS2);
val |= 0x10 << HOT_TX_NORESET_TS2;
writel(val, &dev->ll_tsn_regs->ll_tsn_counters_2);
writel(val, &dev->llregs->ll_tsn_counters_2);
val = readl(&dev->ll_tsn_regs->ll_tsn_counters_3);
val = readl(&dev->llregs->ll_tsn_counters_3);
val &= ~(0x1f << HOT_RX_RESET_TS2);
val |= 0x3 << HOT_RX_RESET_TS2;
writel(val, &dev->ll_tsn_regs->ll_tsn_counters_3);
writel(val, &dev->llregs->ll_tsn_counters_3);
/*
* AB errata. Errata 11. Workaround for Default Duration of LFPS
* Handshake Signaling for Device-Initiated U1 Exit is too short.
* Without this, various enumeration failures observed with
* modern superspeed hosts.
*/
val = readl(&dev->llregs->ll_lfps_timers_2);
writel((val & 0xffff0000) | LFPS_TIMERS_2_WORKAROUND_VALUE,
&dev->llregs->ll_lfps_timers_2);
/*
* Set Recovery Idle to Recover bit:
@@ -2276,10 +2286,10 @@ static void usb_reinit_338x(struct net2280 *dev)
* - It is safe to set for all connection speeds; all chip revisions.
* - R-M-W to leave other bits undisturbed.
* - Reference PLX TT-7372
*/
val = readl(&dev->ll_chicken_reg->ll_tsn_chicken_bit);
*/
val = readl(&dev->llregs->ll_tsn_chicken_bit);
val |= BIT(RECOVERY_IDLE_TO_RECOVER_FMW);
writel(val, &dev->ll_chicken_reg->ll_tsn_chicken_bit);
writel(val, &dev->llregs->ll_tsn_chicken_bit);
INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
@@ -3669,12 +3679,6 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
(base + 0x00b4);
dev->llregs = (struct usb338x_ll_regs __iomem *)
(base + 0x0700);
dev->ll_lfps_regs = (struct usb338x_ll_lfps_regs __iomem *)
(base + 0x0748);
dev->ll_tsn_regs = (struct usb338x_ll_tsn_regs __iomem *)
(base + 0x077c);
dev->ll_chicken_reg = (struct usb338x_ll_chi_regs __iomem *)
(base + 0x079c);
dev->plregs = (struct usb338x_pl_regs __iomem *)
(base + 0x0800);
usbstat = readl(&dev->usb->usbstat);

View File

@@ -178,9 +178,6 @@ struct net2280 {
struct net2280_dep_regs __iomem *dep;
struct net2280_ep_regs __iomem *epregs;
struct usb338x_ll_regs __iomem *llregs;
struct usb338x_ll_lfps_regs __iomem *ll_lfps_regs;
struct usb338x_ll_tsn_regs __iomem *ll_tsn_regs;
struct usb338x_ll_chi_regs __iomem *ll_chicken_reg;
struct usb338x_pl_regs __iomem *plregs;
struct dma_pool *requests;

View File

@@ -3046,8 +3046,7 @@ static void pch_udc_remove(struct pci_dev *pdev)
#ifdef CONFIG_PM_SLEEP
static int pch_udc_suspend(struct device *d)
{
struct pci_dev *pdev = to_pci_dev(d);
struct pch_udc_dev *dev = pci_get_drvdata(pdev);
struct pch_udc_dev *dev = dev_get_drvdata(d);
pch_udc_disable_interrupts(dev, UDC_DEVINT_MSK);
pch_udc_disable_ep_interrupts(dev, UDC_EPINT_MSK_DISABLE_ALL);

View File

@@ -2744,10 +2744,8 @@ static int renesas_usb3_probe(struct platform_device *pdev)
priv = of_device_get_match_data(&pdev->dev);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq);
if (irq < 0)
return irq;
}
usb3 = devm_kzalloc(&pdev->dev, sizeof(*usb3), GFP_KERNEL);
if (!usb3)

View File

@@ -1311,10 +1311,8 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
s3c_hsudc_setup_ep(hsudc);
ret = platform_get_irq(pdev, 0);
if (ret < 0) {
dev_err(dev, "unable to obtain IRQ number\n");
if (ret < 0)
goto err_res;
}
hsudc->irq = ret;
ret = devm_request_irq(&pdev->dev, hsudc->irq, s3c_hsudc_irq, 0,

View File

@@ -312,6 +312,7 @@ static int s3c2410_udc_write_fifo(struct s3c2410_ep *ep,
switch (idx) {
default:
idx = 0;
/* fall through */
case 0:
fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
break;
@@ -416,6 +417,7 @@ static int s3c2410_udc_read_fifo(struct s3c2410_ep *ep,
switch (idx) {
default:
idx = 0;
/* fall through */
case 0:
fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
break;

View File

@@ -2074,10 +2074,8 @@ static int xudc_probe(struct platform_device *pdev)
return PTR_ERR(udc->addr);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "unable to get irq\n");
if (irq < 0)
return irq;
}
ret = devm_request_irq(&pdev->dev, irq, xudc_irq, 0,
dev_name(&pdev->dev), udc);
if (ret < 0) {

View File

@@ -287,12 +287,6 @@ config USB_EHCI_MV
Dova, Armada 370 and Armada XP. See "Support for Marvell EBU
on-chip EHCI USB controller" for those.
config USB_W90X900_EHCI
tristate "W90X900(W90P910) EHCI support"
depends on ARCH_W90X900
---help---
Enables support for the W90X900 USB controller
config USB_CNS3XXX_EHCI
bool "Cavium CNS3XXX EHCI Module (DEPRECATED)"
depends on ARCH_CNS3XXX
@@ -718,32 +712,6 @@ config USB_RENESAS_USBHS_HCD
To compile this driver as a module, choose M here: the
module will be called renesas-usbhs.
config USB_WHCI_HCD
tristate "Wireless USB Host Controller Interface (WHCI) driver"
depends on USB_PCI && USB && UWB
select USB_WUSB
select UWB_WHCI
help
A driver for PCI-based Wireless USB Host Controllers that are
compliant with the WHCI specification.
To compile this driver a module, choose M here: the module
will be called "whci-hcd".
config USB_HWA_HCD
tristate "Host Wire Adapter (HWA) driver"
depends on USB && UWB
select USB_WUSB
select UWB_HWA
help
This driver enables you to connect Wireless USB devices to
your system using a Host Wire Adaptor USB dongle. This is an
UWB Radio Controller and WUSB Host Controller connected to
your machine via USB (specified in WUSB1.0).
To compile this driver a module, choose M here: the module
will be called "hwa-hc".
config USB_IMX21_HCD
tristate "i.MX21 HCD support"
depends on ARM && ARCH_MXC

View File

@@ -35,8 +35,6 @@ ifneq ($(CONFIG_DEBUG_FS),)
xhci-hcd-y += xhci-debugfs.o
endif
obj-$(CONFIG_USB_WHCI_HCD) += whci/
obj-$(CONFIG_USB_PCI) += pci-quirks.o
obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
@@ -51,7 +49,6 @@ obj-$(CONFIG_USB_EHCI_HCD_STI) += ehci-st.o
obj-$(CONFIG_USB_EHCI_EXYNOS) += ehci-exynos.o
obj-$(CONFIG_USB_EHCI_HCD_AT91) += ehci-atmel.o
obj-$(CONFIG_USB_EHCI_TEGRA) += ehci-tegra.o
obj-$(CONFIG_USB_W90X900_EHCI) += ehci-w90x900.o
obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o
obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o
@@ -82,7 +79,6 @@ obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o
obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o
obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o
obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o
obj-$(CONFIG_USB_FSL_USB2) += fsl-mph-dr-of.o
obj-$(CONFIG_USB_EHCI_FSL) += fsl-mph-dr-of.o

View File

@@ -100,9 +100,6 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
dev_err(&pdev->dev,
"Found HC with no IRQ. Check %s setup!\n",
dev_name(&pdev->dev));
retval = -ENODEV;
goto fail_create_hcd;
}

View File

@@ -41,6 +41,7 @@ struct exynos_ehci_hcd {
struct clk *clk;
struct device_node *of_node;
struct phy *phy[PHY_NUMBER];
bool legacy_phy;
};
#define to_exynos_ehci(hcd) (struct exynos_ehci_hcd *)(hcd_to_ehci(hcd)->priv)
@@ -50,10 +51,22 @@ static int exynos_ehci_get_phy(struct device *dev,
{
struct device_node *child;
struct phy *phy;
int phy_number;
int phy_number, num_phys;
int ret;
/* Get PHYs for the controller */
num_phys = of_count_phandle_with_args(dev->of_node, "phys",
"#phy-cells");
for (phy_number = 0; phy_number < num_phys; phy_number++) {
phy = devm_of_phy_get_by_index(dev, dev->of_node, phy_number);
if (IS_ERR(phy))
return PTR_ERR(phy);
exynos_ehci->phy[phy_number] = phy;
}
if (num_phys > 0)
return 0;
/* Get PHYs using legacy bindings */
for_each_available_child_of_node(dev->of_node, child) {
ret = of_property_read_u32(child, "reg", &phy_number);
if (ret) {
@@ -84,6 +97,7 @@ static int exynos_ehci_get_phy(struct device *dev,
}
}
exynos_ehci->legacy_phy = true;
return 0;
}
@@ -205,11 +219,12 @@ static int exynos_ehci_probe(struct platform_device *pdev)
ehci->caps = hcd->regs;
/*
* Workaround: reset of_node pointer to avoid conflict between Exynos
* EHCI port subnodes and generic USB device bindings
* Workaround: reset of_node pointer to avoid conflict between legacy
* Exynos EHCI port subnodes and generic USB device bindings
*/
exynos_ehci->of_node = pdev->dev.of_node;
pdev->dev.of_node = NULL;
if (exynos_ehci->legacy_phy)
pdev->dev.of_node = NULL;
/* DMA burst Enable */
writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));

View File

@@ -30,7 +30,7 @@ static const struct hc_driver ehci_grlib_hc_driver = {
* generic hardware linkage
*/
.irq = ehci_irq,
.flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
.flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH,
/*
* basic lifecycle operations

View File

@@ -1193,7 +1193,7 @@ static const struct hc_driver ehci_hc_driver = {
* generic hardware linkage
*/
.irq = ehci_irq,
.flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
.flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH,
/*
* basic lifecycle operations

View File

@@ -115,10 +115,8 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(dev, "EHCI irq failed: %d\n", irq);
if (irq < 0)
return irq;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(dev, res);

View File

@@ -223,9 +223,6 @@ static int ehci_orion_drv_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
dev_err(&pdev->dev,
"Found HC with no IRQ. Check %s setup!\n",
dev_name(&pdev->dev));
err = -ENODEV;
goto err;
}

View File

@@ -145,10 +145,8 @@ static int ehci_platform_probe(struct platform_device *dev)
}
irq = platform_get_irq(dev, 0);
if (irq < 0) {
dev_err(&dev->dev, "no irq provided");
if (irq < 0)
return irq;
}
hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,
dev_name(&dev->dev));

View File

@@ -250,7 +250,7 @@ static const struct hc_driver ehci_msp_hc_driver = {
* generic hardware linkage
*/
.irq = ehci_irq,
.flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
.flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH,
/*
* basic lifecycle operations

View File

@@ -31,7 +31,7 @@ static const struct hc_driver ehci_ppc_of_hc_driver = {
* generic hardware linkage
*/
.irq = ehci_irq,
.flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
.flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH,
/*
* basic lifecycle operations

View File

@@ -59,7 +59,7 @@ static const struct hc_driver ps3_ehci_hc_driver = {
.product_desc = "PS3 EHCI Host Controller",
.hcd_priv_size = sizeof(struct ehci_hcd),
.irq = ehci_irq,
.flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
.flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH,
.reset = ps3_ehci_hc_reset,
.start = ehci_run,
.stop = ehci_stop,

View File

@@ -33,7 +33,7 @@ static const struct hc_driver ehci_sh_hc_driver = {
* generic hardware linkage
*/
.irq = ehci_irq,
.flags = HCD_USB2 | HCD_MEMORY | HCD_BH,
.flags = HCD_USB2 | HCD_DMA | HCD_MEMORY | HCD_BH,
/*
* basic lifecycle operations
@@ -85,9 +85,6 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
dev_err(&pdev->dev,
"Found HC with no IRQ. Check %s setup!\n",
dev_name(&pdev->dev));
ret = -ENODEV;
goto fail_create_hcd;
}

View File

@@ -158,10 +158,8 @@ static int st_ehci_platform_probe(struct platform_device *dev)
return -ENODEV;
irq = platform_get_irq(dev, 0);
if (irq < 0) {
dev_err(&dev->dev, "no irq provided");
if (irq < 0)
return irq;
}
res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (!res_mem) {
dev_err(&dev->dev, "no memory resource provided");

View File

@@ -1,130 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* linux/driver/usb/host/ehci-w90x900.c
*
* Copyright (c) 2008 Nuvoton technology corporation.
*
* Wan ZongShun <mcuos.com@gmail.com>
*/
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include "ehci.h"
/* enable phy0 and phy1 for w90p910 */
#define ENPHY (0x01<<8)
#define PHY0_CTR (0xA4)
#define PHY1_CTR (0xA8)
#define DRIVER_DESC "EHCI w90x900 driver"
static const char hcd_name[] = "ehci-w90x900 ";
static struct hc_driver __read_mostly ehci_w90x900_hc_driver;
static int ehci_w90x900_probe(struct platform_device *pdev)
{
struct usb_hcd *hcd;
struct ehci_hcd *ehci;
struct resource *res;
int retval = 0, irq;
unsigned long val;
hcd = usb_create_hcd(&ehci_w90x900_hc_driver,
&pdev->dev, "w90x900 EHCI");
if (!hcd) {
retval = -ENOMEM;
goto err1;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
hcd->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(hcd->regs)) {
retval = PTR_ERR(hcd->regs);
goto err2;
}
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
ehci = hcd_to_ehci(hcd);
ehci->caps = hcd->regs;
ehci->regs = hcd->regs +
HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
/* enable PHY 0,1,the regs only apply to w90p910
* 0xA4,0xA8 were offsets of PHY0 and PHY1 controller of
* w90p910 IC relative to ehci->regs.
*/
val = __raw_readl(ehci->regs+PHY0_CTR);
val |= ENPHY;
__raw_writel(val, ehci->regs+PHY0_CTR);
val = __raw_readl(ehci->regs+PHY1_CTR);
val |= ENPHY;
__raw_writel(val, ehci->regs+PHY1_CTR);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
retval = irq;
goto err2;
}
retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (retval != 0)
goto err2;
device_wakeup_enable(hcd->self.controller);
return retval;
err2:
usb_put_hcd(hcd);
err1:
return retval;
}
static int ehci_w90x900_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_remove_hcd(hcd);
usb_put_hcd(hcd);
return 0;
}
static struct platform_driver ehci_hcd_w90x900_driver = {
.probe = ehci_w90x900_probe,
.remove = ehci_w90x900_remove,
.driver = {
.name = "w90x900-ehci",
},
};
static int __init ehci_w90X900_init(void)
{
if (usb_disabled())
return -ENODEV;
pr_info("%s: " DRIVER_DESC "\n", hcd_name);
ehci_init_driver(&ehci_w90x900_hc_driver, NULL);
return platform_driver_register(&ehci_hcd_w90x900_driver);
}
module_init(ehci_w90X900_init);
static void __exit ehci_w90X900_cleanup(void)
{
platform_driver_unregister(&ehci_hcd_w90x900_driver);
}
module_exit(ehci_w90X900_cleanup);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
MODULE_ALIAS("platform:w90p910-ehci");
MODULE_LICENSE("GPL v2");

View File

@@ -66,7 +66,7 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = {
* generic hardware linkage
*/
.irq = ehci_irq,
.flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
.flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH,
/*
* basic lifecycle operations

View File

@@ -538,7 +538,7 @@ static const struct hc_driver fhci_driver = {
/* generic hardware linkage */
.irq = fhci_irq,
.flags = HCD_USB11 | HCD_MEMORY,
.flags = HCD_DMA | HCD_USB11 | HCD_MEMORY,
/* basic lifecycle operation */
.start = fhci_start,

View File

@@ -5508,7 +5508,7 @@ static const struct hc_driver fotg210_fotg210_hc_driver = {
* generic hardware linkage
*/
.irq = fotg210_irq,
.flags = HCD_MEMORY | HCD_USB2,
.flags = HCD_MEMORY | HCD_DMA | HCD_USB2,
/*
* basic lifecycle operations

View File

@@ -1,875 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Host Wire Adapter:
* Driver glue, HWA-specific functions, bridges to WAHC and WUSBHC
*
* Copyright (C) 2005-2006 Intel Corporation
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*
* The HWA driver is a simple layer that forwards requests to the WAHC
* (Wire Adater Host Controller) or WUSBHC (Wireless USB Host
* Controller) layers.
*
* Host Wire Adapter is the 'WUSB 1.0 standard' name for Wireless-USB
* Host Controller that is connected to your system via USB (a USB
* dongle that implements a USB host...). There is also a Device Wired
* Adaptor, DWA (Wireless USB hub) that uses the same mechanism for
* transferring data (it is after all a USB host connected via
* Wireless USB), we have a common layer called Wire Adapter Host
* Controller that does all the hard work. The WUSBHC (Wireless USB
* Host Controller) is the part common to WUSB Host Controllers, the
* HWA and the PCI-based one, that is implemented following the WHCI
* spec. All these layers are implemented in ../wusbcore.
*
* The main functions are hwahc_op_urb_{en,de}queue(), that pass the
* job of converting a URB to a Wire Adapter
*
* Entry points:
*
* hwahc_driver_*() Driver initialization, registration and
* teardown.
*
* hwahc_probe() New device came up, create an instance for
* it [from device enumeration].
*
* hwahc_disconnect() Remove device instance [from device
* enumeration].
*
* [__]hwahc_op_*() Host-Wire-Adaptor specific functions for
* starting/stopping/etc (some might be made also
* DWA).
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/workqueue.h>
#include <linux/wait.h>
#include <linux/completion.h>
#include "../wusbcore/wa-hc.h"
#include "../wusbcore/wusbhc.h"
struct hwahc {
struct wusbhc wusbhc; /* has to be 1st */
struct wahc wa;
};
/*
* FIXME should be wusbhc
*
* NOTE: we need to cache the Cluster ID because later...there is no
* way to get it :)
*/
static int __hwahc_set_cluster_id(struct hwahc *hwahc, u8 cluster_id)
{
int result;
struct wusbhc *wusbhc = &hwahc->wusbhc;
struct wahc *wa = &hwahc->wa;
struct device *dev = &wa->usb_iface->dev;
result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
WUSB_REQ_SET_CLUSTER_ID,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
cluster_id,
wa->usb_iface->cur_altsetting->desc.bInterfaceNumber,
NULL, 0, USB_CTRL_SET_TIMEOUT);
if (result < 0)
dev_err(dev, "Cannot set WUSB Cluster ID to 0x%02x: %d\n",
cluster_id, result);
else
wusbhc->cluster_id = cluster_id;
dev_info(dev, "Wireless USB Cluster ID set to 0x%02x\n", cluster_id);
return result;
}
static int __hwahc_op_set_num_dnts(struct wusbhc *wusbhc, u8 interval, u8 slots)
{
struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
struct wahc *wa = &hwahc->wa;
return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
WUSB_REQ_SET_NUM_DNTS,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
interval << 8 | slots,
wa->usb_iface->cur_altsetting->desc.bInterfaceNumber,
NULL, 0, USB_CTRL_SET_TIMEOUT);
}
/*
* Reset a WUSB host controller and wait for it to complete doing it.
*
* @usb_hcd: Pointer to WUSB Host Controller instance.
*
*/
static int hwahc_op_reset(struct usb_hcd *usb_hcd)
{
int result;
struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
struct device *dev = &hwahc->wa.usb_iface->dev;
mutex_lock(&wusbhc->mutex);
wa_nep_disarm(&hwahc->wa);
result = __wa_set_feature(&hwahc->wa, WA_RESET);
if (result < 0) {
dev_err(dev, "error commanding HC to reset: %d\n", result);
goto error_unlock;
}
result = __wa_wait_status(&hwahc->wa, WA_STATUS_RESETTING, 0);
if (result < 0) {
dev_err(dev, "error waiting for HC to reset: %d\n", result);
goto error_unlock;
}
error_unlock:
mutex_unlock(&wusbhc->mutex);
return result;
}
/*
* FIXME: break this function up
*/
static int hwahc_op_start(struct usb_hcd *usb_hcd)
{
u8 addr;
int result;
struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
result = -ENOSPC;
mutex_lock(&wusbhc->mutex);
addr = wusb_cluster_id_get();
if (addr == 0)
goto error_cluster_id_get;
result = __hwahc_set_cluster_id(hwahc, addr);
if (result < 0)
goto error_set_cluster_id;
usb_hcd->uses_new_polling = 1;
set_bit(HCD_FLAG_POLL_RH, &usb_hcd->flags);
usb_hcd->state = HC_STATE_RUNNING;
/*
* prevent USB core from suspending the root hub since
* bus_suspend and bus_resume are not yet supported.
*/
pm_runtime_get_noresume(&usb_hcd->self.root_hub->dev);
result = 0;
out:
mutex_unlock(&wusbhc->mutex);
return result;
error_set_cluster_id:
wusb_cluster_id_put(addr);
error_cluster_id_get:
goto out;
}
/*
* No need to abort pipes, as when this is called, all the children
* has been disconnected and that has done it [through
* usb_disable_interface() -> usb_disable_endpoint() ->
* hwahc_op_ep_disable() - >rpipe_ep_disable()].
*/
static void hwahc_op_stop(struct usb_hcd *usb_hcd)
{
struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
mutex_lock(&wusbhc->mutex);
wusb_cluster_id_put(wusbhc->cluster_id);
mutex_unlock(&wusbhc->mutex);
}
static int hwahc_op_get_frame_number(struct usb_hcd *usb_hcd)
{
struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
struct wahc *wa = &hwahc->wa;
/*
* We cannot query the HWA for the WUSB time since that requires sending
* a synchronous URB and this function can be called in_interrupt.
* Instead, query the USB frame number for our parent and use that.
*/
return usb_get_current_frame_number(wa->usb_dev);
}
static int hwahc_op_urb_enqueue(struct usb_hcd *usb_hcd, struct urb *urb,
gfp_t gfp)
{
struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
return wa_urb_enqueue(&hwahc->wa, urb->ep, urb, gfp);
}
static int hwahc_op_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb,
int status)
{
struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
return wa_urb_dequeue(&hwahc->wa, urb, status);
}
/*
* Release resources allocated for an endpoint
*
* If there is an associated rpipe to this endpoint, go ahead and put it.
*/
static void hwahc_op_endpoint_disable(struct usb_hcd *usb_hcd,
struct usb_host_endpoint *ep)
{
struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
rpipe_ep_disable(&hwahc->wa, ep);
}
static int __hwahc_op_wusbhc_start(struct wusbhc *wusbhc)
{
int result;
struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
struct device *dev = &hwahc->wa.usb_iface->dev;
result = __wa_set_feature(&hwahc->wa, WA_ENABLE);
if (result < 0) {
dev_err(dev, "error commanding HC to start: %d\n", result);
goto error_stop;
}
result = __wa_wait_status(&hwahc->wa, WA_ENABLE, WA_ENABLE);
if (result < 0) {
dev_err(dev, "error waiting for HC to start: %d\n", result);
goto error_stop;
}
result = wa_nep_arm(&hwahc->wa, GFP_KERNEL);
if (result < 0) {
dev_err(dev, "cannot listen to notifications: %d\n", result);
goto error_stop;
}
/*
* If WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS is set,
* disable transfer notifications.
*/
if (hwahc->wa.quirks &
WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS) {
struct usb_host_interface *cur_altsetting =
hwahc->wa.usb_iface->cur_altsetting;
result = usb_control_msg(hwahc->wa.usb_dev,
usb_sndctrlpipe(hwahc->wa.usb_dev, 0),
WA_REQ_ALEREON_DISABLE_XFER_NOTIFICATIONS,
USB_DIR_OUT | USB_TYPE_VENDOR |
USB_RECIP_INTERFACE,
WA_REQ_ALEREON_FEATURE_SET,
cur_altsetting->desc.bInterfaceNumber,
NULL, 0,
USB_CTRL_SET_TIMEOUT);
/*
* If we successfully sent the control message, start DTI here
* because no transfer notifications will be received which is
* where DTI is normally started.
*/
if (result == 0)
result = wa_dti_start(&hwahc->wa);
else
result = 0; /* OK. Continue normally. */
if (result < 0) {
dev_err(dev, "cannot start DTI: %d\n", result);
goto error_dti_start;
}
}
return result;
error_dti_start:
wa_nep_disarm(&hwahc->wa);
error_stop:
__wa_clear_feature(&hwahc->wa, WA_ENABLE);
return result;
}
static void __hwahc_op_wusbhc_stop(struct wusbhc *wusbhc, int delay)
{
struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
struct wahc *wa = &hwahc->wa;
u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber;
int ret;
ret = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
WUSB_REQ_CHAN_STOP,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
delay * 1000,
iface_no,
NULL, 0, USB_CTRL_SET_TIMEOUT);
if (ret == 0)
msleep(delay);
wa_nep_disarm(&hwahc->wa);
__wa_stop(&hwahc->wa);
}
/*
* Set the UWB MAS allocation for the WUSB cluster
*
* @stream_index: stream to use (-1 for cancelling the allocation)
* @mas: mas bitmap to use
*/
static int __hwahc_op_bwa_set(struct wusbhc *wusbhc, s8 stream_index,
const struct uwb_mas_bm *mas)
{
int result;
struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
struct wahc *wa = &hwahc->wa;
struct device *dev = &wa->usb_iface->dev;
u8 mas_le[UWB_NUM_MAS/8];
/* Set the stream index */
result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
WUSB_REQ_SET_STREAM_IDX,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
stream_index,
wa->usb_iface->cur_altsetting->desc.bInterfaceNumber,
NULL, 0, USB_CTRL_SET_TIMEOUT);
if (result < 0) {
dev_err(dev, "Cannot set WUSB stream index: %d\n", result);
goto out;
}
uwb_mas_bm_copy_le(mas_le, mas);
/* Set the MAS allocation */
result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
WUSB_REQ_SET_WUSB_MAS,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
0, wa->usb_iface->cur_altsetting->desc.bInterfaceNumber,
mas_le, 32, USB_CTRL_SET_TIMEOUT);
if (result < 0)
dev_err(dev, "Cannot set WUSB MAS allocation: %d\n", result);
out:
return result;
}
/*
* Add an IE to the host's MMC
*
* @interval: See WUSB1.0[8.5.3.1]
* @repeat_cnt: See WUSB1.0[8.5.3.1]
* @handle: See WUSB1.0[8.5.3.1]
* @wuie: Pointer to the header of the WUSB IE data to add.
* MUST BE allocated in a kmalloc buffer (no stack or
* vmalloc).
*
* NOTE: the format of the WUSB IEs for MMCs are different to the
* normal MBOA MAC IEs (IE Id + Length in MBOA MAC vs. Length +
* Id in WUSB IEs). Standards...you gotta love'em.
*/
static int __hwahc_op_mmcie_add(struct wusbhc *wusbhc, u8 interval,
u8 repeat_cnt, u8 handle,
struct wuie_hdr *wuie)
{
struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
struct wahc *wa = &hwahc->wa;
u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber;
return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
WUSB_REQ_ADD_MMC_IE,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
interval << 8 | repeat_cnt,
handle << 8 | iface_no,
wuie, wuie->bLength, USB_CTRL_SET_TIMEOUT);
}
/*
* Remove an IE to the host's MMC
*
* @handle: See WUSB1.0[8.5.3.1]
*/
static int __hwahc_op_mmcie_rm(struct wusbhc *wusbhc, u8 handle)
{
struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
struct wahc *wa = &hwahc->wa;
u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber;
return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
WUSB_REQ_REMOVE_MMC_IE,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
0, handle << 8 | iface_no,
NULL, 0, USB_CTRL_SET_TIMEOUT);
}
/*
* Update device information for a given fake port
*
* @port_idx: Fake port to which device is connected (wusbhc index, not
* USB port number).
*/
static int __hwahc_op_dev_info_set(struct wusbhc *wusbhc,
struct wusb_dev *wusb_dev)
{
struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
struct wahc *wa = &hwahc->wa;
u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber;
struct hwa_dev_info *dev_info;
int ret;
/* fill out the Device Info buffer and send it */
dev_info = kzalloc(sizeof(struct hwa_dev_info), GFP_KERNEL);
if (!dev_info)
return -ENOMEM;
uwb_mas_bm_copy_le(dev_info->bmDeviceAvailability,
&wusb_dev->availability);
dev_info->bDeviceAddress = wusb_dev->addr;
/*
* If the descriptors haven't been read yet, use a default PHY
* rate of 53.3 Mbit/s only. The correct value will be used
* when this will be called again as part of the
* authentication process (which occurs after the descriptors
* have been read).
*/
if (wusb_dev->wusb_cap_descr)
dev_info->wPHYRates = wusb_dev->wusb_cap_descr->wPHYRates;
else
dev_info->wPHYRates = cpu_to_le16(USB_WIRELESS_PHY_53);
ret = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
WUSB_REQ_SET_DEV_INFO,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
0, wusb_dev->port_idx << 8 | iface_no,
dev_info, sizeof(struct hwa_dev_info),
USB_CTRL_SET_TIMEOUT);
kfree(dev_info);
return ret;
}
/*
* Set host's idea of which encryption (and key) method to use when
* talking to ad evice on a given port.
*
* If key is NULL, it means disable encryption for that "virtual port"
* (used when we disconnect).
*/
static int __hwahc_dev_set_key(struct wusbhc *wusbhc, u8 port_idx, u32 tkid,
const void *key, size_t key_size,
u8 key_idx)
{
int result = -ENOMEM;
struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
struct wahc *wa = &hwahc->wa;
u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber;
struct usb_key_descriptor *keyd;
size_t keyd_len;
keyd_len = sizeof(*keyd) + key_size;
keyd = kzalloc(keyd_len, GFP_KERNEL);
if (keyd == NULL)
return -ENOMEM;
keyd->bLength = keyd_len;
keyd->bDescriptorType = USB_DT_KEY;
keyd->tTKID[0] = (tkid >> 0) & 0xff;
keyd->tTKID[1] = (tkid >> 8) & 0xff;
keyd->tTKID[2] = (tkid >> 16) & 0xff;
memcpy(keyd->bKeyData, key, key_size);
result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
USB_REQ_SET_DESCRIPTOR,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
USB_DT_KEY << 8 | key_idx,
port_idx << 8 | iface_no,
keyd, keyd_len, USB_CTRL_SET_TIMEOUT);
kzfree(keyd); /* clear keys etc. */
return result;
}
/*
* Set host's idea of which encryption (and key) method to use when
* talking to ad evice on a given port.
*
* If key is NULL, it means disable encryption for that "virtual port"
* (used when we disconnect).
*/
static int __hwahc_op_set_ptk(struct wusbhc *wusbhc, u8 port_idx, u32 tkid,
const void *key, size_t key_size)
{
int result = -ENOMEM;
struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
struct wahc *wa = &hwahc->wa;
u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber;
u8 encryption_value;
/* Tell the host which key to use to talk to the device */
if (key) {
u8 key_idx = wusb_key_index(0, WUSB_KEY_INDEX_TYPE_PTK,
WUSB_KEY_INDEX_ORIGINATOR_HOST);
result = __hwahc_dev_set_key(wusbhc, port_idx, tkid,
key, key_size, key_idx);
if (result < 0)
goto error_set_key;
encryption_value = wusbhc->ccm1_etd->bEncryptionValue;
} else {
/* FIXME: this should come from wusbhc->etd[UNSECURE].value */
encryption_value = 0;
}
/* Set the encryption type for communicating with the device */
result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
USB_REQ_SET_ENCRYPTION,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
encryption_value, port_idx << 8 | iface_no,
NULL, 0, USB_CTRL_SET_TIMEOUT);
if (result < 0)
dev_err(wusbhc->dev, "Can't set host's WUSB encryption for "
"port index %u to %s (value %d): %d\n", port_idx,
wusb_et_name(wusbhc->ccm1_etd->bEncryptionType),
wusbhc->ccm1_etd->bEncryptionValue, result);
error_set_key:
return result;
}
/*
* Set host's GTK key
*/
static int __hwahc_op_set_gtk(struct wusbhc *wusbhc, u32 tkid,
const void *key, size_t key_size)
{
u8 key_idx = wusb_key_index(0, WUSB_KEY_INDEX_TYPE_GTK,
WUSB_KEY_INDEX_ORIGINATOR_HOST);
return __hwahc_dev_set_key(wusbhc, 0, tkid, key, key_size, key_idx);
}
/*
* Get the Wire Adapter class-specific descriptor
*
* NOTE: this descriptor comes with the big bundled configuration
* descriptor that includes the interfaces' and endpoints', so
* we just look for it in the cached copy kept by the USB stack.
*
* NOTE2: We convert LE fields to CPU order.
*/
static int wa_fill_descr(struct wahc *wa)
{
int result;
struct device *dev = &wa->usb_iface->dev;
char *itr;
struct usb_device *usb_dev = wa->usb_dev;
struct usb_descriptor_header *hdr;
struct usb_wa_descriptor *wa_descr;
size_t itr_size, actconfig_idx;
actconfig_idx = (usb_dev->actconfig - usb_dev->config) /
sizeof(usb_dev->config[0]);
itr = usb_dev->rawdescriptors[actconfig_idx];
itr_size = le16_to_cpu(usb_dev->actconfig->desc.wTotalLength);
while (itr_size >= sizeof(*hdr)) {
hdr = (struct usb_descriptor_header *) itr;
dev_dbg(dev, "Extra device descriptor: "
"type %02x/%u bytes @ %zu (%zu left)\n",
hdr->bDescriptorType, hdr->bLength,
(itr - usb_dev->rawdescriptors[actconfig_idx]),
itr_size);
if (hdr->bDescriptorType == USB_DT_WIRE_ADAPTER)
goto found;
itr += hdr->bLength;
itr_size -= hdr->bLength;
}
dev_err(dev, "cannot find Wire Adapter Class descriptor\n");
return -ENODEV;
found:
result = -EINVAL;
if (hdr->bLength > itr_size) { /* is it available? */
dev_err(dev, "incomplete Wire Adapter Class descriptor "
"(%zu bytes left, %u needed)\n",
itr_size, hdr->bLength);
goto error;
}
if (hdr->bLength < sizeof(*wa->wa_descr)) {
dev_err(dev, "short Wire Adapter Class descriptor\n");
goto error;
}
wa->wa_descr = wa_descr = (struct usb_wa_descriptor *) hdr;
if (le16_to_cpu(wa_descr->bcdWAVersion) > 0x0100)
dev_warn(dev, "Wire Adapter v%d.%d newer than groked v1.0\n",
(le16_to_cpu(wa_descr->bcdWAVersion) & 0xff00) >> 8,
le16_to_cpu(wa_descr->bcdWAVersion) & 0x00ff);
result = 0;
error:
return result;
}
static const struct hc_driver hwahc_hc_driver = {
.description = "hwa-hcd",
.product_desc = "Wireless USB HWA host controller",
.hcd_priv_size = sizeof(struct hwahc) - sizeof(struct usb_hcd),
.irq = NULL, /* FIXME */
.flags = HCD_USB25,
.reset = hwahc_op_reset,
.start = hwahc_op_start,
.stop = hwahc_op_stop,
.get_frame_number = hwahc_op_get_frame_number,
.urb_enqueue = hwahc_op_urb_enqueue,
.urb_dequeue = hwahc_op_urb_dequeue,
.endpoint_disable = hwahc_op_endpoint_disable,
.hub_status_data = wusbhc_rh_status_data,
.hub_control = wusbhc_rh_control,
.start_port_reset = wusbhc_rh_start_port_reset,
};
static int hwahc_security_create(struct hwahc *hwahc)
{
int result;
struct wusbhc *wusbhc = &hwahc->wusbhc;
struct usb_device *usb_dev = hwahc->wa.usb_dev;
struct device *dev = &usb_dev->dev;
struct usb_security_descriptor *secd;
struct usb_encryption_descriptor *etd;
void *itr, *top;
size_t itr_size, needed, bytes;
u8 index;
char buf[64];
/* Find the host's security descriptors in the config descr bundle */
index = (usb_dev->actconfig - usb_dev->config) /
sizeof(usb_dev->config[0]);
itr = usb_dev->rawdescriptors[index];
itr_size = le16_to_cpu(usb_dev->actconfig->desc.wTotalLength);
top = itr + itr_size;
result = __usb_get_extra_descriptor(usb_dev->rawdescriptors[index],
le16_to_cpu(usb_dev->actconfig->desc.wTotalLength),
USB_DT_SECURITY, (void **) &secd, sizeof(*secd));
if (result == -1) {
dev_warn(dev, "BUG? WUSB host has no security descriptors\n");
return 0;
}
needed = sizeof(*secd);
if (top - (void *)secd < needed) {
dev_err(dev, "BUG? Not enough data to process security "
"descriptor header (%zu bytes left vs %zu needed)\n",
top - (void *) secd, needed);
return 0;
}
needed = le16_to_cpu(secd->wTotalLength);
if (top - (void *)secd < needed) {
dev_err(dev, "BUG? Not enough data to process security "
"descriptors (%zu bytes left vs %zu needed)\n",
top - (void *) secd, needed);
return 0;
}
/* Walk over the sec descriptors and store CCM1's on wusbhc */
itr = (void *) secd + sizeof(*secd);
top = (void *) secd + le16_to_cpu(secd->wTotalLength);
index = 0;
bytes = 0;
while (itr < top) {
etd = itr;
if (top - itr < sizeof(*etd)) {
dev_err(dev, "BUG: bad host security descriptor; "
"not enough data (%zu vs %zu left)\n",
top - itr, sizeof(*etd));
break;
}
if (etd->bLength < sizeof(*etd)) {
dev_err(dev, "BUG: bad host encryption descriptor; "
"descriptor is too short "
"(%zu vs %zu needed)\n",
(size_t)etd->bLength, sizeof(*etd));
break;
}
itr += etd->bLength;
bytes += snprintf(buf + bytes, sizeof(buf) - bytes,
"%s (0x%02x) ",
wusb_et_name(etd->bEncryptionType),
etd->bEncryptionValue);
wusbhc->ccm1_etd = etd;
}
dev_info(dev, "supported encryption types: %s\n", buf);
if (wusbhc->ccm1_etd == NULL) {
dev_err(dev, "E: host doesn't support CCM-1 crypto\n");
return 0;
}
/* Pretty print what we support */
return 0;
}
static void hwahc_security_release(struct hwahc *hwahc)
{
/* nothing to do here so far... */
}
static int hwahc_create(struct hwahc *hwahc, struct usb_interface *iface,
kernel_ulong_t quirks)
{
int result;
struct device *dev = &iface->dev;
struct wusbhc *wusbhc = &hwahc->wusbhc;
struct wahc *wa = &hwahc->wa;
struct usb_device *usb_dev = interface_to_usbdev(iface);
wa->usb_dev = usb_get_dev(usb_dev); /* bind the USB device */
wa->usb_iface = usb_get_intf(iface);
wusbhc->dev = dev;
/* defer getting the uwb_rc handle until it is needed since it
* may not have been registered by the hwa_rc driver yet. */
wusbhc->uwb_rc = NULL;
result = wa_fill_descr(wa); /* Get the device descriptor */
if (result < 0)
goto error_fill_descriptor;
if (wa->wa_descr->bNumPorts > USB_MAXCHILDREN) {
dev_err(dev, "FIXME: USB_MAXCHILDREN too low for WUSB "
"adapter (%u ports)\n", wa->wa_descr->bNumPorts);
wusbhc->ports_max = USB_MAXCHILDREN;
} else {
wusbhc->ports_max = wa->wa_descr->bNumPorts;
}
wusbhc->mmcies_max = wa->wa_descr->bNumMMCIEs;
wusbhc->start = __hwahc_op_wusbhc_start;
wusbhc->stop = __hwahc_op_wusbhc_stop;
wusbhc->mmcie_add = __hwahc_op_mmcie_add;
wusbhc->mmcie_rm = __hwahc_op_mmcie_rm;
wusbhc->dev_info_set = __hwahc_op_dev_info_set;
wusbhc->bwa_set = __hwahc_op_bwa_set;
wusbhc->set_num_dnts = __hwahc_op_set_num_dnts;
wusbhc->set_ptk = __hwahc_op_set_ptk;
wusbhc->set_gtk = __hwahc_op_set_gtk;
result = hwahc_security_create(hwahc);
if (result < 0) {
dev_err(dev, "Can't initialize security: %d\n", result);
goto error_security_create;
}
wa->wusb = wusbhc; /* FIXME: ugly, need to fix */
result = wusbhc_create(&hwahc->wusbhc);
if (result < 0) {
dev_err(dev, "Can't create WUSB HC structures: %d\n", result);
goto error_wusbhc_create;
}
result = wa_create(&hwahc->wa, iface, quirks);
if (result < 0)
goto error_wa_create;
return 0;
error_wa_create:
wusbhc_destroy(&hwahc->wusbhc);
error_wusbhc_create:
/* WA Descr fill allocs no resources */
error_security_create:
error_fill_descriptor:
usb_put_intf(iface);
usb_put_dev(usb_dev);
return result;
}
static void hwahc_destroy(struct hwahc *hwahc)
{
struct wusbhc *wusbhc = &hwahc->wusbhc;
mutex_lock(&wusbhc->mutex);
__wa_destroy(&hwahc->wa);
wusbhc_destroy(&hwahc->wusbhc);
hwahc_security_release(hwahc);
hwahc->wusbhc.dev = NULL;
uwb_rc_put(wusbhc->uwb_rc);
usb_put_intf(hwahc->wa.usb_iface);
usb_put_dev(hwahc->wa.usb_dev);
mutex_unlock(&wusbhc->mutex);
}
static void hwahc_init(struct hwahc *hwahc)
{
wa_init(&hwahc->wa);
}
static int hwahc_probe(struct usb_interface *usb_iface,
const struct usb_device_id *id)
{
int result;
struct usb_hcd *usb_hcd;
struct wusbhc *wusbhc;
struct hwahc *hwahc;
struct device *dev = &usb_iface->dev;
result = -ENOMEM;
usb_hcd = usb_create_hcd(&hwahc_hc_driver, &usb_iface->dev, "wusb-hwa");
if (usb_hcd == NULL) {
dev_err(dev, "unable to allocate instance\n");
goto error_alloc;
}
usb_hcd->wireless = 1;
usb_hcd->self.sg_tablesize = ~0;
wusbhc = usb_hcd_to_wusbhc(usb_hcd);
hwahc = container_of(wusbhc, struct hwahc, wusbhc);
hwahc_init(hwahc);
result = hwahc_create(hwahc, usb_iface, id->driver_info);
if (result < 0) {
dev_err(dev, "Cannot initialize internals: %d\n", result);
goto error_hwahc_create;
}
result = usb_add_hcd(usb_hcd, 0, 0);
if (result < 0) {
dev_err(dev, "Cannot add HCD: %d\n", result);
goto error_add_hcd;
}
device_wakeup_enable(usb_hcd->self.controller);
result = wusbhc_b_create(&hwahc->wusbhc);
if (result < 0) {
dev_err(dev, "Cannot setup phase B of WUSBHC: %d\n", result);
goto error_wusbhc_b_create;
}
return 0;
error_wusbhc_b_create:
usb_remove_hcd(usb_hcd);
error_add_hcd:
hwahc_destroy(hwahc);
error_hwahc_create:
usb_put_hcd(usb_hcd);
error_alloc:
return result;
}
static void hwahc_disconnect(struct usb_interface *usb_iface)
{
struct usb_hcd *usb_hcd;
struct wusbhc *wusbhc;
struct hwahc *hwahc;
usb_hcd = usb_get_intfdata(usb_iface);
wusbhc = usb_hcd_to_wusbhc(usb_hcd);
hwahc = container_of(wusbhc, struct hwahc, wusbhc);
wusbhc_b_destroy(&hwahc->wusbhc);
usb_remove_hcd(usb_hcd);
hwahc_destroy(hwahc);
usb_put_hcd(usb_hcd);
}
static const struct usb_device_id hwahc_id_table[] = {
/* Alereon 5310 */
{ USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5310, 0xe0, 0x02, 0x01),
.driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC |
WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS },
/* Alereon 5611 */
{ USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5611, 0xe0, 0x02, 0x01),
.driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC |
WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS },
/* FIXME: use class labels for this */
{ USB_INTERFACE_INFO(0xe0, 0x02, 0x01), },
{},
};
MODULE_DEVICE_TABLE(usb, hwahc_id_table);
static struct usb_driver hwahc_driver = {
.name = "hwa-hc",
.probe = hwahc_probe,
.disconnect = hwahc_disconnect,
.id_table = hwahc_id_table,
};
module_usb_driver(hwahc_driver);
MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
MODULE_DESCRIPTION("Host Wired Adapter USB Host Control Driver");
MODULE_LICENSE("GPL");

View File

@@ -1771,7 +1771,7 @@ static const struct hc_driver imx21_hc_driver = {
.product_desc = "IMX21 USB Host Controller",
.hcd_priv_size = sizeof(struct imx21),
.flags = HCD_USB11,
.flags = HCD_DMA | HCD_USB11,
.irq = imx21_irq,
.reset = imx21_hc_reset,
@@ -1836,10 +1836,8 @@ static int imx21_probe(struct platform_device *pdev)
if (!res)
return -ENODEV;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq);
if (irq < 0)
return irq;
}
hcd = usb_create_hcd(&imx21_hc_driver,
&pdev->dev, dev_name(&pdev->dev));

View File

@@ -1581,12 +1581,6 @@ static int isp116x_probe(struct platform_device *pdev)
irq = ires->start;
irqflags = ires->flags & IRQF_TRIGGER_MASK;
if (pdev->dev.dma_mask) {
DBG("DMA not supported\n");
ret = -EINVAL;
goto err1;
}
if (!request_mem_region(addr->start, 2, hcd_name)) {
ret = -EBUSY;
goto err1;

View File

@@ -2645,11 +2645,6 @@ static int isp1362_probe(struct platform_device *pdev)
if (pdev->num_resources < 3)
return -ENODEV;
if (pdev->dev.dma_mask) {
DBG(1, "won't do DMA");
return -ENODEV;
}
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!irq_res)
return -ENODEV;

View File

@@ -1800,21 +1800,6 @@ max3421_bus_resume(struct usb_hcd *hcd)
return -1;
}
/*
* The SPI driver already takes care of DMA-mapping/unmapping, so no
* reason to do it twice.
*/
static int
max3421_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
{
return 0;
}
static void
max3421_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
{
}
static const struct hc_driver max3421_hcd_desc = {
.description = "max3421",
.product_desc = DRIVER_DESC,
@@ -1826,8 +1811,6 @@ static const struct hc_driver max3421_hcd_desc = {
.get_frame_number = max3421_get_frame_number,
.urb_enqueue = max3421_urb_enqueue,
.urb_dequeue = max3421_urb_dequeue,
.map_urb_for_dma = max3421_map_urb_for_dma,
.unmap_urb_for_dma = max3421_unmap_urb_for_dma,
.endpoint_disable = max3421_endpoint_disable,
.hub_status_data = max3421_hub_status_data,
.hub_control = max3421_hub_control,

Some files were not shown because too many files have changed in this diff Show More