diff options
Diffstat (limited to 'drivers/usb')
119 files changed, 17824 insertions, 6186 deletions
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 99eff1c8d2..d66a75635d 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config USB bool @@ -9,6 +10,8 @@ if USB_HOST source "drivers/usb/imx/Kconfig" +source "drivers/usb/dwc2/Kconfig" + source "drivers/usb/dwc3/Kconfig" source "drivers/usb/host/Kconfig" @@ -21,6 +24,8 @@ source "drivers/usb/misc/Kconfig" endif +source "drivers/usb/typec/Kconfig" + source "drivers/usb/gadget/Kconfig" source "drivers/usb/musb/Kconfig" diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 64d4bddad4..0cac50c0f3 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -1,10 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_USB) += core/ obj-$(CONFIG_USB_IMX_CHIPIDEA) += imx/ +obj-$(CONFIG_USB_DWC2) += dwc2/ obj-$(CONFIG_USB_DWC3) += dwc3/ obj-$(CONFIG_USB_MUSB) += musb/ obj-$(CONFIG_USB_STORAGE) += storage/ obj-y += host/ obj-y += otg/ obj-y += gadget/ +obj-y += typec/ obj-$(CONFIG_USB) += misc/ diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index 58f6c5e027..60e03caad7 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_USB_HOST) += usb.o hub.o obj-$(CONFIG_USB) += common.o obj-$(CONFIG_OFDEVICE) += of.o diff --git a/drivers/usb/core/common.c b/drivers/usb/core/common.c index bcbe3a155d..61ccc13024 100644 --- a/drivers/usb/core/common.c +++ b/drivers/usb/core/common.c @@ -1,5 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> -#include <usb/ch9.h> +#include <linux/usb/ch9.h> static const char *const speed_names[] = { [USB_SPEED_UNKNOWN] = "UNKNOWN", diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 01653d8c20..650af0e884 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1,26 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * hub.c - USB hub support * * Copyright (c) 2011 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <common.h> #include <init.h> #include <malloc.h> #include <errno.h> -#include <usb/phy.h> -#include <usb/usb.h> -#include <usb/usb_defs.h> +#include <linux/usb/phy.h> +#include <linux/usb/usb.h> +#include <linux/usb/usb_defs.h> #include "usb.h" @@ -669,7 +660,7 @@ static int usb_hub_configure_ports(struct usb_device *dev) return usb_device_list_scan(); } -static int usb_hub_detect(struct device_d *dev) +static int usb_hub_detect(struct device *dev) { struct usb_device *usbdev = container_of(dev, struct usb_device, dev); int i; diff --git a/drivers/usb/core/of.c b/drivers/usb/core/of.c index b534ede18a..25203c6064 100644 --- a/drivers/usb/core/of.c +++ b/drivers/usb/core/of.c @@ -1,19 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * usb devicetree helper functions - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <common.h> -#include <usb/usb.h> -#include <usb/phy.h> +#include <linux/usb/usb.h> +#include <linux/usb/phy.h> #include <of.h> static const char *usb_dr_modes[] = { diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 30c251f405..1f6f1d7c41 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * * Most of this source has been derived from the Linux USB @@ -14,18 +15,6 @@ * * Adapted for barebox: * (C) Copyright 2001 Denis Peter, MPL AG Switzerland - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * */ /* @@ -49,8 +38,8 @@ #include <init.h> #include <dma.h> -#include <usb/usb.h> -#include <usb/ch9.h> +#include <linux/usb/usb.h> +#include <linux/usb/ch9.h> #include "usb.h" @@ -75,28 +64,44 @@ static int host_busnum = 1; static inline int usb_host_acquire(struct usb_host *host) { - if (host->sem) + if (slice_acquired(&host->slice)) return -EAGAIN; - host->sem++; + + slice_acquire(&host->slice); + return 0; } static inline void usb_host_release(struct usb_host *host) { - if (host->sem > 0) - host->sem--; + slice_release(&host->slice); +} + +static int usb_hw_detect(struct device *dev) +{ + struct usb_host *host; + + list_for_each_entry(host, &host_list, list) { + if (dev == host->hw_dev) + return usb_host_detect(host); + } + + return -ENODEV; } int usb_register_host(struct usb_host *host) { list_add_tail(&host->list, &host_list); host->busnum = host_busnum++; - host->sem = 0; + slice_init(&host->slice, dev_name(host->hw_dev)); + if (!host->hw_dev->detect) + host->hw_dev->detect = usb_hw_detect; return 0; } void usb_unregister_host(struct usb_host *host) { + slice_exit(&host->slice); list_del(&host->list); } @@ -330,6 +335,8 @@ static int usb_setup_descriptor(struct usb_device *dev, bool do_read) dev->descriptor->bMaxPacketSize0 = 64; dev->maxpacketsize = PACKET_SIZE_64; } + dev->epmaxpacketin[0] = dev->descriptor->bMaxPacketSize0; + dev->epmaxpacketout[0] = dev->descriptor->bMaxPacketSize0; if (do_read && dev->speed == USB_SPEED_FULL) { int err; @@ -353,10 +360,10 @@ static int usb_setup_descriptor(struct usb_device *dev, bool do_read) err = get_descriptor_len(dev, 64, 8); if (err) return err; - } - dev->epmaxpacketin[0] = dev->descriptor->bMaxPacketSize0; - dev->epmaxpacketout[0] = dev->descriptor->bMaxPacketSize0; + dev->epmaxpacketin[0] = dev->descriptor->bMaxPacketSize0; + dev->epmaxpacketout[0] = dev->descriptor->bMaxPacketSize0; + } switch (dev->descriptor->bMaxPacketSize0) { case 8: @@ -470,8 +477,8 @@ int usb_new_device(struct usb_device *dev) /* we set the default configuration here */ err = usb_set_configuration(dev, dev->config.desc.bConfigurationValue); if (err) { - dev_err(&dev->dev, "Setting default configuration failed with: %s\n" \ - "len %d, status %lX\n", strerror(-err), + dev_err(&dev->dev, "Setting default configuration failed with: %pe\n" \ + "len %d, status %lX\n", ERR_PTR(err), dev->act_len, dev->status); goto err_out; } @@ -495,7 +502,7 @@ int usb_new_device(struct usb_device *dev) err = register_device(&dev->dev); if (err) { - dev_err(&dev->dev, "Failed to register device: %s\n", strerror(-err)); + dev_err(&dev->dev, "Failed to register device: %pe\n", ERR_PTR(err)); goto err_out; } @@ -527,6 +534,7 @@ void usb_free_device(struct usb_device *usbdev) { dma_free(usbdev->descriptor); dma_free(usbdev->setup_packet); + free_device_res(&usbdev->dev); free(usbdev); } @@ -568,6 +576,8 @@ int usb_host_detect(struct usb_host *host) { int ret; + of_usb_host_probe_hubs(host); + if (!host->root_dev) { if (host->init) { ret = host->init(host); @@ -591,7 +601,7 @@ int usb_host_detect(struct usb_host *host) return 0; } -void usb_rescan(void) +int usb_rescan(void) { struct usb_host *host; int ret; @@ -605,6 +615,22 @@ void usb_rescan(void) } pr_info("%d USB Device(s) found\n", dev_count); + + if (IS_ENABLED(CONFIG_USB_OTGDEV)) { + unsigned int skipped_otg = 0; + struct device *dev; + + bus_for_each_device(&otg_bus_type, dev) { + if (otg_device_get_mode(dev) == USB_DR_MODE_OTG) + skipped_otg++; + } + + if (skipped_otg) + pr_notice("%u unconfigured OTG controller(s) were not scanned\n", + skipped_otg); + } + + return dev_count; } /*------------------------------------------------------------------- @@ -952,15 +978,15 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid, */ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) { - unsigned char mybuf[USB_BUFSIZ]; unsigned char *tbuf; - int err; + int err = 0; unsigned int u, idx; if (size <= 0 || !buf || !index) return -1; + + tbuf = dma_alloc(USB_BUFSIZ); buf[0] = 0; - tbuf = &mybuf[0]; /* get langid for strings if it's not yet known */ if (!dev->have_langid) { @@ -968,10 +994,12 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) if (err < 0) { dev_dbg(&dev->dev, "error getting string descriptor 0 " \ "(error=%lx)\n", dev->status); - return -1; + err = -1; + goto fail; } else if (tbuf[0] < 4) { pr_debug("string descriptor 0 too short\n"); - return -1; + err = -1; + goto fail; } else { dev->have_langid = -1; dev->string_langid = tbuf[2] | (tbuf[3] << 8); @@ -984,7 +1012,7 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) err = usb_string_sub(dev, dev->string_langid, index, tbuf); if (err < 0) - return err; + goto fail; size--; /* leave room for trailing NULL char in output buffer */ for (idx = 0, u = 2; u < err; u += 2) { @@ -997,6 +1025,8 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) } buf[idx] = 0; err = idx; +fail: + dma_free(tbuf); return err; } @@ -1092,7 +1122,7 @@ static const struct usb_device_id *usb_match_id(struct usb_device *usbdev, } EXPORT_SYMBOL(usb_match_id); -static int usb_match(struct device_d *dev, struct driver_d *drv) +static int usb_match(struct device *dev, struct driver *drv) { struct usb_device *usbdev = container_of(dev, struct usb_device, dev); struct usb_driver *usbdrv = container_of(dev->driver, struct usb_driver, driver); @@ -1109,7 +1139,7 @@ static int usb_match(struct device_d *dev, struct driver_d *drv) return 1; } -static int usb_probe(struct device_d *dev) +static int usb_probe(struct device *dev) { struct usb_device *usbdev = container_of(dev, struct usb_device, dev); struct usb_driver *usbdrv = container_of(dev->driver, struct usb_driver, driver); @@ -1120,7 +1150,7 @@ static int usb_probe(struct device_d *dev) return usbdrv->probe(usbdev, id); } -static void usb_remove(struct device_d *dev) +static void usb_remove(struct device *dev) { struct usb_device *usbdev = container_of(dev, struct usb_device, dev); struct usb_driver *usbdrv = container_of(dev->driver, struct usb_driver, driver); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index a5bb255121..0d4f80c21d 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ #ifndef __CORE_USB_H #define __CORE_USB_H diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig new file mode 100644 index 0000000000..08bb11cc6a --- /dev/null +++ b/drivers/usb/dwc2/Kconfig @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0-only +config USB_DWC2 + bool + depends on USB && HAS_DMA + select USB_OTGDEV + select OFDEVICE + help + DesignWare Core USB2 OTG driver. + +config USB_DWC2_HOST + bool "DWC2 Host mode support" + depends on USB_HOST + select USB_DWC2 + help + Select this when you want to use DWC2 in host mode. + +config USB_DWC2_GADGET + bool "DWC2 Gadget mode support" + depends on USB_GADGET + select USB_DWC2 + help + Select this when you want to use DWC2 in gadget mode. diff --git a/drivers/usb/dwc2/Makefile b/drivers/usb/dwc2/Makefile new file mode 100644 index 0000000000..1ca89e9b66 --- /dev/null +++ b/drivers/usb/dwc2/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_USB_DWC2_HOST) += dwc2.o core.o host.o rhub.o +obj-$(CONFIG_USB_DWC2_GADGET) += dwc2.o core.o gadget.o diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c new file mode 100644 index 0000000000..b198ba6bf8 --- /dev/null +++ b/drivers/usb/dwc2/core.c @@ -0,0 +1,882 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include "dwc2.h" + +/* Returns the controller's GHWCFG2.OTG_MODE. */ +static unsigned int dwc2_op_mode(struct dwc2 *dwc2) +{ + u32 ghwcfg2 = dwc2_readl(dwc2, GHWCFG2); + + return (ghwcfg2 & GHWCFG2_OP_MODE_MASK) >> + GHWCFG2_OP_MODE_SHIFT; +} + +/* Returns true if the controller is host-only. */ +static bool dwc2_hw_is_host(struct dwc2 *dwc2) +{ + unsigned int op_mode = dwc2_op_mode(dwc2); + + return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_HOST) || + (op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST); +} + +/* Returns true if the controller is device-only. */ +static bool dwc2_hw_is_device(struct dwc2 *dwc2) +{ + unsigned int op_mode = dwc2_op_mode(dwc2); + + return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) || + (op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE); +} + +static void dwc2_set_param_otg_cap(struct dwc2 *dwc2) +{ + u8 val; + + switch (dwc2->hw_params.op_mode) { + case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE: + val = DWC2_CAP_PARAM_HNP_SRP_CAPABLE; + break; + case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE: + case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE: + case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST: + val = DWC2_CAP_PARAM_SRP_ONLY_CAPABLE; + break; + default: + val = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; + break; + } + + dwc2->params.otg_cap = val; +} + +static void dwc2_set_param_phy_type(struct dwc2 *dwc2) +{ + u8 val; + + switch (dwc2->hw_params.hs_phy_type) { + case GHWCFG2_HS_PHY_TYPE_UTMI: + case GHWCFG2_HS_PHY_TYPE_UTMI_ULPI: + val = DWC2_PHY_TYPE_PARAM_UTMI; + break; + case GHWCFG2_HS_PHY_TYPE_ULPI: + val = DWC2_PHY_TYPE_PARAM_ULPI; + break; + default: + dwc2_warn(dwc2, "Unhandled HS PHY type\n"); + fallthrough; + case GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED: + val = DWC2_PHY_TYPE_PARAM_FS; + break; + } + + dwc2->params.phy_type = val; +} + +static void dwc2_set_param_speed(struct dwc2 *dwc2) +{ + if (dwc2->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) + dwc2->params.speed = DWC2_SPEED_PARAM_FULL; + else + dwc2->params.speed = DWC2_SPEED_PARAM_HIGH; +} + +static void dwc2_set_param_phy_utmi_width(struct dwc2 *dwc2) +{ + int val; + + val = (dwc2->hw_params.utmi_phy_data_width == + GHWCFG4_UTMI_PHY_DATA_WIDTH_8) ? 8 : 16; + + dwc2->params.phy_utmi_width = val; +} + +/** + * dwc2_hsotg_tx_fifo_count - return count of TX FIFOs in device mode + * + * @hsotg: Programming view of the DWC_otg controller + */ +int dwc2_tx_fifo_count(struct dwc2 *dwc2) +{ + if (dwc2->hw_params.en_multiple_tx_fifo) + /* In dedicated FIFO mode we need count of IN EPs */ + return dwc2->hw_params.num_dev_in_eps; + else + /* In shared FIFO mode we need count of Periodic IN EPs */ + return dwc2->hw_params.num_dev_perio_in_ep; +} + +static void dwc2_set_param_fifo_sizes(struct dwc2 *dwc2) +{ + struct dwc2_hw_params *hw = &dwc2->hw_params; + struct dwc2_core_params *p = &dwc2->params; + u32 total_fifo_size = dwc2->hw_params.total_fifo_size; + u32 max_np_tx_fifo_size = hw->dev_nperio_tx_fifo_size; + u32 max_rx_fifo_size = hw->rx_fifo_size; + u32 depth; + int count, i; + + count = dwc2_tx_fifo_count(dwc2); + + depth = total_fifo_size / 4; + p->g_np_tx_fifo_size = min(max_np_tx_fifo_size, depth); + total_fifo_size -= p->g_np_tx_fifo_size; + + depth = 8 * count + 256; + depth = max(total_fifo_size / count, depth); + p->g_rx_fifo_size = min(max_rx_fifo_size, depth); + total_fifo_size -= p->g_rx_fifo_size; + + for (i = 1; i <= count; i++) + p->g_tx_fifo_size[i] = total_fifo_size / count; +} + +/** + * dwc2_set_default_params() - Set all core parameters to their + * auto-detected default values. + * + * @dwc2: Programming view of the DWC2 controller + * + */ +void dwc2_set_default_params(struct dwc2 *dwc2) +{ + struct dwc2_hw_params *hw = &dwc2->hw_params; + struct dwc2_core_params *p = &dwc2->params; + bool dma_capable = !(hw->arch == GHWCFG2_SLAVE_ONLY_ARCH); + + dwc2_set_param_otg_cap(dwc2); + dwc2_set_param_phy_type(dwc2); + dwc2_set_param_speed(dwc2); + dwc2_set_param_phy_utmi_width(dwc2); + p->phy_ulpi_ddr = false; + p->phy_ulpi_ext_vbus = false; + + p->enable_dynamic_fifo = hw->enable_dynamic_fifo; + p->en_multiple_tx_fifo = hw->en_multiple_tx_fifo; + p->i2c_enable = hw->i2c_enable; + p->acg_enable = hw->acg_enable; + p->ulpi_fs_ls = false; + p->ts_dline = false; + p->reload_ctl = (hw->snpsid >= DWC2_CORE_REV_2_92a); + p->uframe_sched = true; + p->external_id_pin_ctl = false; + p->lpm = true; + p->lpm_clock_gating = true; + p->besl = true; + p->hird_threshold_en = true; + p->hird_threshold = 4; + p->ipg_isoc_en = false; + p->max_packet_count = hw->max_packet_count; + p->max_transfer_size = hw->max_transfer_size; + p->ahbcfg = GAHBCFG_HBSTLEN_INCR << GAHBCFG_HBSTLEN_SHIFT; + + p->dma = dma_capable; + p->dma_desc = false; + + if (dwc2->dr_mode == USB_DR_MODE_HOST || + dwc2->dr_mode == USB_DR_MODE_OTG) { + p->host_support_fs_ls_low_power = false; + p->host_ls_low_power_phy_clk = false; + p->host_channels = hw->host_channels; + p->host_rx_fifo_size = hw->rx_fifo_size; + p->host_nperio_tx_fifo_size = hw->host_nperio_tx_fifo_size; + p->host_perio_tx_fifo_size = hw->host_perio_tx_fifo_size; + } + + if ((dwc2->dr_mode == USB_DR_MODE_PERIPHERAL) || + (dwc2->dr_mode == USB_DR_MODE_OTG)) { + dwc2_set_param_fifo_sizes(dwc2); + } +} + +void dwc2_get_device_properties(struct dwc2 *dwc2) +{ + struct dwc2_core_params *p = &dwc2->params; + struct device_node *np = dwc2->dev->of_node; + int num; + + if ((dwc2->dr_mode == USB_DR_MODE_PERIPHERAL) || + (dwc2->dr_mode == USB_DR_MODE_OTG)) { + of_property_read_u32(np, "g-rx-fifo-size", + &p->g_rx_fifo_size); + + of_property_read_u32(np, "g-np-tx-fifo-size", + &p->g_np_tx_fifo_size); + + num = of_property_count_elems_of_size(np, "g-tx-fifo-size", sizeof(u32)); + if (num > 0) { + num = min(num, 15); + memset(p->g_tx_fifo_size, 0, + sizeof(p->g_tx_fifo_size)); + of_property_read_u32_array(np, + "g-tx-fifo-size", + &p->g_tx_fifo_size[1], + num); + } + } +} + +int dwc2_check_core_version(struct dwc2 *dwc2) +{ + struct dwc2_hw_params *hw = &dwc2->hw_params; + + /* + * Attempt to ensure this device is really a DWC2 Controller. + * Read and verify the GSNPSID register contents. The value should be + * 0x4f54xxxx, 0x5531xxxx or 0x5532xxxx + */ + hw->snpsid = dwc2_readl(dwc2, GSNPSID); + if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID && + (hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID && + (hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) { + dwc2_err(dwc2, "Bad value for GSNPSID: 0x%08x\n", + hw->snpsid); + return -ENODEV; + } + + dwc2_dbg(dwc2, "Core Release: %1x.%1x%1x%1x (snpsid=%x)\n", + hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf, + hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid); + + return 0; +} + +static void dwc2_get_dev_hwparams(struct dwc2 *dwc2) +{ + struct dwc2_hw_params *hw = &dwc2->hw_params; + u32 size; + int count, i; + + size = FIFOSIZE_DEPTH_GET(dwc2_readl(dwc2, GNPTXFSIZ)); + hw->dev_nperio_tx_fifo_size = size; + + count = dwc2_tx_fifo_count(dwc2); + + for (i = 1; i <= count; i++) { + size = FIFOSIZE_DEPTH_GET(dwc2_readl(dwc2, DPTXFSIZN(i))); + hw->g_tx_fifo_size[i] = size; + } +} + +/** + * During device initialization, read various hardware configuration + * registers and interpret the contents. + * + * @dwc2: Programming view of the DWC2 controller + * + */ +void dwc2_get_hwparams(struct dwc2 *dwc2) +{ + struct dwc2_hw_params *hw = &dwc2->hw_params; + unsigned int width; + u32 hwcfg1, hwcfg2, hwcfg3, hwcfg4; + u32 grxfsiz; + + hwcfg1 = dwc2_readl(dwc2, GHWCFG1); + hwcfg2 = dwc2_readl(dwc2, GHWCFG2); + hwcfg3 = dwc2_readl(dwc2, GHWCFG3); + hwcfg4 = dwc2_readl(dwc2, GHWCFG4); + grxfsiz = dwc2_readl(dwc2, GRXFSIZ); + + /* hwcfg1 */ + hw->dev_ep_dirs = hwcfg1; + + /* hwcfg2 */ + hw->op_mode = (hwcfg2 & GHWCFG2_OP_MODE_MASK) >> + GHWCFG2_OP_MODE_SHIFT; + hw->arch = (hwcfg2 & GHWCFG2_ARCHITECTURE_MASK) >> + GHWCFG2_ARCHITECTURE_SHIFT; + hw->enable_dynamic_fifo = !!(hwcfg2 & GHWCFG2_DYNAMIC_FIFO); + hw->host_channels = 1 + ((hwcfg2 & GHWCFG2_NUM_HOST_CHAN_MASK) >> + GHWCFG2_NUM_HOST_CHAN_SHIFT); + hw->hs_phy_type = (hwcfg2 & GHWCFG2_HS_PHY_TYPE_MASK) >> + GHWCFG2_HS_PHY_TYPE_SHIFT; + hw->fs_phy_type = (hwcfg2 & GHWCFG2_FS_PHY_TYPE_MASK) >> + GHWCFG2_FS_PHY_TYPE_SHIFT; + hw->num_dev_ep = (hwcfg2 & GHWCFG2_NUM_DEV_EP_MASK) >> + GHWCFG2_NUM_DEV_EP_SHIFT; + hw->nperio_tx_q_depth = + (hwcfg2 & GHWCFG2_NONPERIO_TX_Q_DEPTH_MASK) >> + GHWCFG2_NONPERIO_TX_Q_DEPTH_SHIFT << 1; + hw->host_perio_tx_q_depth = + (hwcfg2 & GHWCFG2_HOST_PERIO_TX_Q_DEPTH_MASK) >> + GHWCFG2_HOST_PERIO_TX_Q_DEPTH_SHIFT << 1; + hw->dev_token_q_depth = + (hwcfg2 & GHWCFG2_DEV_TOKEN_Q_DEPTH_MASK) >> + GHWCFG2_DEV_TOKEN_Q_DEPTH_SHIFT; + + /* hwcfg3 */ + width = (hwcfg3 & GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK) >> + GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT; + hw->max_transfer_size = (1 << (width + 11)) - 1; + width = (hwcfg3 & GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK) >> + GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT; + hw->max_packet_count = (1 << (width + 4)) - 1; + hw->i2c_enable = !!(hwcfg3 & GHWCFG3_I2C); + hw->total_fifo_size = (hwcfg3 & GHWCFG3_DFIFO_DEPTH_MASK) >> + GHWCFG3_DFIFO_DEPTH_SHIFT; + hw->lpm_mode = !!(hwcfg3 & GHWCFG3_OTG_LPM_EN); + + /* hwcfg4 */ + hw->en_multiple_tx_fifo = !!(hwcfg4 & GHWCFG4_DED_FIFO_EN); + hw->num_dev_perio_in_ep = (hwcfg4 & GHWCFG4_NUM_DEV_PERIO_IN_EP_MASK) >> + GHWCFG4_NUM_DEV_PERIO_IN_EP_SHIFT; + hw->num_dev_in_eps = (hwcfg4 & GHWCFG4_NUM_IN_EPS_MASK) >> + GHWCFG4_NUM_IN_EPS_SHIFT; + hw->dma_desc_enable = !!(hwcfg4 & GHWCFG4_DESC_DMA); + hw->power_optimized = !!(hwcfg4 & GHWCFG4_POWER_OPTIMIZ); + hw->hibernation = !!(hwcfg4 & GHWCFG4_HIBER); + hw->utmi_phy_data_width = (hwcfg4 & GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK) >> + GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT; + hw->acg_enable = !!(hwcfg4 & GHWCFG4_ACG_SUPPORTED); + hw->ipg_isoc_en = !!(hwcfg4 & GHWCFG4_IPG_ISOC_SUPPORTED); + + /* fifo sizes */ + hw->rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >> + GRXFSIZ_DEPTH_SHIFT; + + dwc2_get_dev_hwparams(dwc2); +} + +/* + * Initializes the FSLSPClkSel field of the HCFG register depending on the + * PHY type + */ +void dwc2_init_fs_ls_pclk_sel(struct dwc2 *dwc2) +{ + u32 hcfg, val; + + if ((dwc2->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI && + dwc2->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED && + dwc2->params.ulpi_fs_ls) || + dwc2->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) { + /* Full speed PHY */ + val = HCFG_FSLSPCLKSEL_48_MHZ; + } else { + /* High speed PHY running at full speed or high speed */ + val = HCFG_FSLSPCLKSEL_30_60_MHZ; + } + + dwc2_dbg(dwc2, "Initializing HCFG.FSLSPClkSel to %08x\n", val); + hcfg = dwc2_readl(dwc2, HCFG); + hcfg &= ~HCFG_FSLSPCLKSEL_MASK; + hcfg |= val << HCFG_FSLSPCLKSEL_SHIFT; + dwc2_writel(dwc2, hcfg, HCFG); +} + +void dwc2_flush_all_fifo(struct dwc2 *dwc2) +{ + uint32_t greset; + + /* Wait for AHB master IDLE state */ + if (dwc2_wait_bit_set(dwc2, GRSTCTL, GRSTCTL_AHBIDLE, 10000)) { + dwc2_warn(dwc2, "%s: Timeout waiting for AHB Idle\n", __func__); + return; + } + + greset = GRSTCTL_TXFFLSH | GRSTCTL_RXFFLSH; + /* TXFNUM of 0x10 is to flush all TX FIFO */ + dwc2_writel(dwc2, greset | GRSTCTL_TXFNUM(0x10), GRSTCTL); + + /* Wait for TxFIFO and RxFIFO flush done */ + if (dwc2_wait_bit_clear(dwc2, GRSTCTL, greset, 10000)) + dwc2_warn(dwc2, "Timeout flushing fifos (GRSTCTL=%08x)\n", + dwc2_readl(dwc2, GRSTCTL)); + + /* Wait for 3 PHY Clocks */ + udelay(1); +} + +/** + * dwc2_flush_tx_fifo() - Flushes a Tx FIFO + * + * @hsotg: Programming view of DWC_otg controller + * @idx: The fifo index (0..15) + */ +void dwc2_flush_tx_fifo(struct dwc2 *dwc2, const int idx) +{ + u32 greset; + + if (idx > 15) + return; + + dwc2_dbg(dwc2, "Flush Tx FIFO %d\n", idx); + + /* Wait for AHB master IDLE state */ + if (dwc2_wait_bit_set(dwc2, GRSTCTL, GRSTCTL_AHBIDLE, 10000)) { + dwc2_warn(dwc2, "%s: Timeout waiting for AHB Idle\n", __func__); + return; + } + + greset = GRSTCTL_TXFFLSH; + greset |= GRSTCTL_TXFNUM(idx) & GRSTCTL_TXFNUM_MASK; + dwc2_writel(dwc2, greset, GRSTCTL); + + if (dwc2_wait_bit_clear(dwc2, GRSTCTL, GRSTCTL_TXFFLSH, 10000)) + dwc2_warn(dwc2, "%s: Timeout flushing tx fifo (GRSTCTL=%08x)\n", + __func__, dwc2_readl(dwc2, GRSTCTL)); + + /* Wait for at least 3 PHY Clocks */ + udelay(1); +} + +static int dwc2_fs_phy_init(struct dwc2 *dwc2, bool select_phy) +{ + u32 usbcfg, ggpio, i2cctl; + int retval = 0; + + /* + * core_init() is now called on every switch so only call the + * following for the first time through + */ + if (select_phy) { + dwc2_dbg(dwc2, "FS PHY selected\n"); + + usbcfg = dwc2_readl(dwc2, GUSBCFG); + if (!(usbcfg & GUSBCFG_PHYSEL)) { + usbcfg |= GUSBCFG_PHYSEL; + dwc2_writel(dwc2, usbcfg, GUSBCFG); + + /* Reset after a PHY select */ + retval = dwc2_core_reset(dwc2); + + if (retval) { + dwc2_err(dwc2, + "%s: Reset failed, aborting", __func__); + return retval; + } + } + + if (dwc2->params.activate_stm_fs_transceiver) { + ggpio = dwc2_readl(dwc2, GGPIO); + if (!(ggpio & GGPIO_STM32_OTG_GCCFG_PWRDWN)) { + dwc2_dbg(dwc2, "Activating transceiver\n"); + /* + * STM32F4x9 uses the GGPIO register as general + * core configuration register. + */ + ggpio |= GGPIO_STM32_OTG_GCCFG_PWRDWN; + dwc2_writel(dwc2, ggpio, GGPIO); + } + } + } + + if (dwc2->params.i2c_enable) { + dwc2_dbg(dwc2, "FS PHY enabling I2C\n"); + + /* Program GUSBCFG.OtgUtmiFsSel to I2C */ + usbcfg = dwc2_readl(dwc2, GUSBCFG); + usbcfg |= GUSBCFG_OTG_UTMI_FS_SEL; + dwc2_writel(dwc2, usbcfg, GUSBCFG); + + /* Program GI2CCTL.I2CEn */ + i2cctl = dwc2_readl(dwc2, GI2CCTL); + i2cctl &= ~GI2CCTL_I2CDEVADDR_MASK; + i2cctl |= 1 << GI2CCTL_I2CDEVADDR_SHIFT; + i2cctl &= ~GI2CCTL_I2CEN; + dwc2_writel(dwc2, i2cctl, GI2CCTL); + i2cctl |= GI2CCTL_I2CEN; + dwc2_writel(dwc2, i2cctl, GI2CCTL); + } + + return retval; +} + +static int dwc2_hs_phy_init(struct dwc2 *dwc2, bool select_phy) +{ + u32 usbcfg, usbcfg_old; + int retval = 0; + + if (!select_phy) + return 0; + + usbcfg = dwc2_readl(dwc2, GUSBCFG); + usbcfg_old = usbcfg; + + /* + * HS PHY parameters. These parameters are preserved during soft reset + * so only program the first time. Do a soft reset immediately after + * setting phyif. + */ + switch (dwc2->params.phy_type) { + case DWC2_PHY_TYPE_PARAM_ULPI: + /* ULPI interface */ + dwc2_dbg(dwc2, "HS ULPI PHY selected\n"); + usbcfg |= GUSBCFG_ULPI_UTMI_SEL; + usbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL | GUSBCFG_PHYSEL); + if (dwc2->params.phy_ulpi_ddr) + usbcfg |= GUSBCFG_DDRSEL; + + /* Set external VBUS indicator as needed. */ + if (dwc2->params.phy_ulpi_ext_vbus_ind) { + dwc2_dbg(dwc2, "Use external VBUS indicator\n"); + usbcfg |= GUSBCFG_ULPI_EXT_VBUS_IND; + usbcfg &= ~GUSBCFG_INDICATORCOMPLEMENT; + usbcfg &= ~GUSBCFG_INDICATORPASSTHROUGH; + + if (dwc2->params.phy_ulpi_ext_vbus_ind_complement) + usbcfg |= GUSBCFG_INDICATORCOMPLEMENT; + if (dwc2->params.phy_ulpi_ext_vbus_ind_passthrough) + usbcfg |= GUSBCFG_INDICATORPASSTHROUGH; + } + break; + case DWC2_PHY_TYPE_PARAM_UTMI: + /* UTMI+ interface */ + dwc2_dbg(dwc2, "HS UTMI+ PHY selected\n"); + usbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16); + if (dwc2->params.phy_utmi_width == 16) + usbcfg |= GUSBCFG_PHYIF16; + break; + default: + dwc2_err(dwc2, "FS PHY selected at HS!\n"); + break; + } + + if (usbcfg != usbcfg_old) { + dwc2_writel(dwc2, usbcfg, GUSBCFG); + + /* Reset after setting the PHY parameters */ + retval = dwc2_core_reset(dwc2); + if (retval) { + dwc2_err(dwc2, + "%s: Reset failed, aborting", __func__); + return retval; + } + } + + return retval; +} + +int dwc2_phy_init(struct dwc2 *dwc2, bool select_phy) +{ + u32 usbcfg; + int retval = 0; + + if ((dwc2->params.speed == DWC2_SPEED_PARAM_FULL || + dwc2->params.speed == DWC2_SPEED_PARAM_LOW) && + dwc2->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) { + /* If FS/LS mode with FS/LS PHY */ + retval = dwc2_fs_phy_init(dwc2, select_phy); + if (retval) + return retval; + } else { + /* High speed PHY */ + retval = dwc2_hs_phy_init(dwc2, select_phy); + if (retval) + return retval; + } + + usbcfg = dwc2_readl(dwc2, GUSBCFG); + usbcfg &= ~GUSBCFG_ULPI_FS_LS; + usbcfg &= ~GUSBCFG_ULPI_CLK_SUSP_M; + if (dwc2->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI && + dwc2->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED && + dwc2->params.ulpi_fs_ls) { + dwc2_dbg(dwc2, "Setting ULPI FSLS\n"); + usbcfg |= GUSBCFG_ULPI_FS_LS; + usbcfg |= GUSBCFG_ULPI_CLK_SUSP_M; + } + dwc2_writel(dwc2, usbcfg, GUSBCFG); + + return retval; +} + +int dwc2_gahbcfg_init(struct dwc2 *dwc2) +{ + u32 ahbcfg = dwc2_readl(dwc2, GAHBCFG); + + switch (dwc2->hw_params.arch) { + case GHWCFG2_EXT_DMA_ARCH: + dwc2_err(dwc2, "External DMA Mode not supported\n"); + return -EINVAL; + + case GHWCFG2_INT_DMA_ARCH: + dwc2_dbg(dwc2, "Internal DMA Mode\n"); + if (dwc2->params.ahbcfg != -1) { + ahbcfg &= GAHBCFG_CTRL_MASK; + ahbcfg |= dwc2->params.ahbcfg & + ~GAHBCFG_CTRL_MASK; + } + break; + + case GHWCFG2_SLAVE_ONLY_ARCH: + default: + dwc2_dbg(dwc2, "Slave Only Mode\n"); + break; + } + + if (dwc2->params.dma) + ahbcfg |= GAHBCFG_DMA_EN; + else + dwc2->params.dma_desc = false; + + dwc2_writel(dwc2, ahbcfg, GAHBCFG); + + return 0; +} + +void dwc2_gusbcfg_init(struct dwc2 *dwc2) +{ + u32 usbcfg; + + usbcfg = dwc2_readl(dwc2, GUSBCFG); + usbcfg &= ~(GUSBCFG_HNPCAP | GUSBCFG_SRPCAP); + + switch (dwc2->hw_params.op_mode) { + case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE: + if (dwc2->params.otg_cap == + DWC2_CAP_PARAM_HNP_SRP_CAPABLE) + usbcfg |= GUSBCFG_HNPCAP; + + case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE: + case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE: + case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST: + if (dwc2->params.otg_cap != + DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE) + usbcfg |= GUSBCFG_SRPCAP; + break; + + case GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE: + case GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE: + case GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST: + default: + break; + } + + dwc2_writel(dwc2, usbcfg, GUSBCFG); +} + +/* + * Check the dr_mode against the module configuration and hardware + * capabilities. + * + * The hardware, module, and dr_mode, can each be set to host, device, + * or otg. Check that all these values are compatible and adjust the + * value of dr_mode if possible. + * + * actual + * HW MOD dr_mode dr_mode + * ------------------------------ + * HST HST any : HST + * HST DEV any : --- + * HST OTG any : HST + * + * DEV HST any : --- + * DEV DEV any : DEV + * DEV OTG any : DEV + * + * OTG HST any : HST + * OTG DEV any : DEV + * OTG OTG any : dr_mode + */ +int dwc2_get_dr_mode(struct dwc2 *dwc2) +{ + enum usb_dr_mode mode; + + mode = of_usb_get_dr_mode(dwc2->dev->of_node, NULL); + dwc2->dr_mode = mode; + + if (dwc2_hw_is_device(dwc2)) { + dwc2_dbg(dwc2, "Controller is device only\n"); + if (!IS_ENABLED(CONFIG_USB_DWC2_GADGET)) { + dwc2_err(dwc2, "gadget mode support not compiled in!\n"); + return -ENOTSUPP; + } + mode = USB_DR_MODE_PERIPHERAL; + } else if (dwc2_hw_is_host(dwc2)) { + dwc2_dbg(dwc2, "Controller is host only\n"); + if (!IS_ENABLED(CONFIG_USB_DWC2_HOST)) { + dwc2_err(dwc2, "host mode support not compiled in!\n"); + return -ENOTSUPP; + } + mode = USB_DR_MODE_HOST; + } else { + dwc2_dbg(dwc2, "Controller is otg\n"); + if (IS_ENABLED(CONFIG_USB_DWC2_HOST) && + IS_ENABLED(CONFIG_USB_DWC2_GADGET)) + mode = dwc2->dr_mode; + else if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) + mode = USB_DR_MODE_HOST; + else if (IS_ENABLED(CONFIG_USB_DWC2_GADGET)) + mode = USB_DR_MODE_PERIPHERAL; + } + + if (mode != dwc2->dr_mode) { + dwc2_warn(dwc2, + "Selected dr_mode not supported by controller/driver. Enforcing '%s' mode.\n", + mode == USB_DR_MODE_HOST ? "host" : "peripheral"); + + dwc2->dr_mode = mode; + } + + return 0; +} + +/** + * dwc2_wait_for_mode() - Waits for the controller mode. + * @dwc2: Programming view of the DWC_otg controller. + * @host_mode: If true, waits for host mode, otherwise device mode. + */ +void dwc2_wait_for_mode(struct dwc2 *dwc2, bool host_mode) +{ + unsigned int timeout = 110 * MSECOND; + int ret; + + dev_vdbg(dwc2->dev, "Waiting for %s mode\n", + host_mode ? "host" : "device"); + + ret = wait_on_timeout(timeout, dwc2_is_host_mode(dwc2) == host_mode); + if (ret) + dev_err(dwc2->dev, "%s: Couldn't set %s mode\n", + __func__, host_mode ? "host" : "device"); + + dev_vdbg(dwc2->dev, "%s mode set\n", + host_mode ? "Host" : "Device"); +} + +/** + * dwc2_iddig_filter_enabled() - Returns true if the IDDIG debounce + * filter is enabled. + * + * @hsotg: Programming view of DWC_otg controller + */ +bool dwc2_iddig_filter_enabled(struct dwc2 *dwc2) +{ + u32 gsnpsid; + u32 ghwcfg4; + + /* Check if core configuration includes the IDDIG filter. */ + ghwcfg4 = dwc2_readl(dwc2, GHWCFG4); + if (!(ghwcfg4 & GHWCFG4_IDDIG_FILT_EN)) + return false; + + /* + * Check if the IDDIG debounce filter is bypassed. Available + * in core version >= 3.10a. + */ + gsnpsid = dwc2_readl(dwc2, GSNPSID); + if (gsnpsid >= DWC2_CORE_REV_3_10a) { + u32 gotgctl = dwc2_readl(dwc2, GOTGCTL); + + if (gotgctl & GOTGCTL_DBNCE_FLTR_BYPASS) + return false; + } + + return true; +} + +/* + * Do core a soft reset of the core. Be careful with this because it + * resets all the internal state machines of the core. + */ +int dwc2_core_reset(struct dwc2 *dwc2) +{ + bool wait_for_host_mode = false; + uint32_t greset; + int ret; + + dwc2_dbg(dwc2, "%s(%p)\n", __func__, dwc2); + + /* Wait for AHB master IDLE state. */ + ret = dwc2_wait_bit_set(dwc2, GRSTCTL, GRSTCTL_AHBIDLE, 10000); + if (ret) { + dwc2_warn(dwc2, "%s: Timeout! Waiting for AHB master IDLE state\n", + __func__); + return ret; + } + + /* + * If the current mode is host, either due to the force mode + * bit being set (which persists after core reset) or the + * connector id pin, a core soft reset will temporarily reset + * the mode to device. A delay from the IDDIG debounce filter + * will occur before going back to host mode. + * + * Determine whether we will go back into host mode after a + * reset and account for this delay after the reset. + */ + if (dwc2_iddig_filter_enabled(dwc2)) { + u32 gotgctl = dwc2_readl(dwc2, GOTGCTL); + u32 gusbcfg = dwc2_readl(dwc2, GUSBCFG); + + if (!(gotgctl & GOTGCTL_CONID_B) || + (gusbcfg & GUSBCFG_FORCEHOSTMODE)) { + wait_for_host_mode = true; + } + } + + /* Core Soft Reset */ + greset = dwc2_readl(dwc2, GRSTCTL); + greset |= GRSTCTL_CSFTRST; + dwc2_writel(dwc2, greset, GRSTCTL); + + ret = dwc2_wait_bit_clear(dwc2, GRSTCTL, GRSTCTL_CSFTRST, 10000); + if (ret) { + dwc2_warn(dwc2, "%s: Timeout! Waiting for Core Soft Reset\n", + __func__); + return ret; + } + + if (wait_for_host_mode) + dwc2_wait_for_mode(dwc2, wait_for_host_mode); + + return 0; +} + +/* + * This function initializes the DWC2 controller registers and + * prepares the core for device mode or host mode operation. + * + * @param regs Programming view of the DWC2 controller + */ +void dwc2_core_init(struct dwc2 *dwc2) +{ + uint32_t otgctl = 0; + uint32_t usbcfg = 0; + int retval; + + dwc2_dbg(dwc2, "%s(%p)\n", __func__, dwc2); + + /* Common Initialization */ + usbcfg = dwc2_readl(dwc2, GUSBCFG); + + /* Set ULPI External VBUS bit if needed */ + usbcfg &= ~GUSBCFG_ULPI_EXT_VBUS_DRV; + if (dwc2->params.phy_ulpi_ext_vbus) + usbcfg |= GUSBCFG_ULPI_EXT_VBUS_DRV; + + /* Set external TS Dline pulsing bit if needed */ + usbcfg &= ~GUSBCFG_TERMSELDLPULSE; + if (dwc2->params.ts_dline) + usbcfg |= GUSBCFG_TERMSELDLPULSE; + + dwc2_writel(dwc2, usbcfg, GUSBCFG); + + /* Reset the Controller */ + dwc2_core_reset(dwc2); + + /* + * This programming sequence needs to happen in FS mode before + * any other programming occurs + */ + retval = dwc2_phy_init(dwc2, true); + if (retval) + return; + + /* Program the GAHBCFG Register */ + retval = dwc2_gahbcfg_init(dwc2); + if (retval) + return; + + /* Program the GUSBCFG register */ + dwc2_gusbcfg_init(dwc2); + + /* Program the GOTGCTL register */ + otgctl = dwc2_readl(dwc2, GOTGCTL); + otgctl &= ~GOTGCTL_OTGVER; + dwc2_writel(dwc2, otgctl, GOTGCTL); + + if (dwc2_is_host_mode(dwc2)) + dwc2_dbg(dwc2, "Host Mode\n"); + else + dwc2_dbg(dwc2, "Device Mode\n"); +} diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h new file mode 100644 index 0000000000..a9526a8c5d --- /dev/null +++ b/drivers/usb/dwc2/core.h @@ -0,0 +1,565 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Maximum number of Endpoints/HostChannels */ +#define DWC2_MAX_EPS_CHANNELS 16 + +/** + * struct dwc2_core_params - Parameters for configuring the core + * + * @otg_cap: Specifies the OTG capabilities. + * 0 - HNP and SRP capable + * 1 - SRP Only capable + * 2 - No HNP/SRP capable (always available) + * Defaults to best available option (0, 1, then 2) + * @host_dma: Specifies whether to use slave or DMA mode for accessing + * the data FIFOs. The driver will automatically detect the + * value for this parameter if none is specified. + * 0 - Slave (always available) + * 1 - DMA (default, if available) + * @dma_desc_enable: When DMA mode is enabled, specifies whether to use + * address DMA mode or descriptor DMA mode for accessing + * the data FIFOs. The driver will automatically detect the + * value for this if none is specified. + * 0 - Address DMA + * 1 - Descriptor DMA (default, if available) + * @dma_desc_fs_enable: When DMA mode is enabled, specifies whether to use + * address DMA mode or descriptor DMA mode for accessing + * the data FIFOs in Full Speed mode only. The driver + * will automatically detect the value for this if none is + * specified. + * 0 - Address DMA + * 1 - Descriptor DMA in FS (default, if available) + * @speed: Specifies the maximum speed of operation in host and + * device mode. The actual speed depends on the speed of + * the attached device and the value of phy_type. + * 0 - High Speed + * (default when phy_type is UTMI+ or ULPI) + * 1 - Full Speed + * (default when phy_type is Full Speed) + * @enable_dynamic_fifo: 0 - Use coreConsultant-specified FIFO size parameters + * 1 - Allow dynamic FIFO sizing (default, if available) + * @en_multiple_tx_fifo: Specifies whether dedicated per-endpoint transmit FIFOs + * are enabled for non-periodic IN endpoints in device + * mode. + * @host_rx_fifo_size: Number of 4-byte words in the Rx FIFO in host mode when + * dynamic FIFO sizing is enabled + * 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + * @host_nperio_tx_fifo_size: Number of 4-byte words in the non-periodic Tx FIFO + * in host mode when dynamic FIFO sizing is enabled + * 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + * @host_perio_tx_fifo_size: Number of 4-byte words in the periodic Tx FIFO in + * host mode when dynamic FIFO sizing is enabled + * 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + * @max_transfer_size: The maximum transfer size supported, in bytes + * 2047 to 65,535 + * Actual maximum value is autodetected and also + * the default. + * @max_packet_count: The maximum number of packets in a transfer + * 15 to 511 + * Actual maximum value is autodetected and also + * the default. + * @host_channels: The number of host channel registers to use + * 1 to 16 + * Actual maximum value is autodetected and also + * the default. + * @phy_type: Specifies the type of PHY interface to use. By default, + * the driver will automatically detect the phy_type. + * 0 - Full Speed Phy + * 1 - UTMI+ Phy + * 2 - ULPI Phy + * Defaults to best available option (2, 1, then 0) + * @phy_utmi_width: Specifies the UTMI+ Data Width (in bits). This parameter + * is applicable for a phy_type of UTMI+ or ULPI. (For a + * ULPI phy_type, this parameter indicates the data width + * between the MAC and the ULPI Wrapper.) Also, this + * parameter is applicable only if the OTG_HSPHY_WIDTH cC + * parameter was set to "8 and 16 bits", meaning that the + * core has been configured to work at either data path + * width. + * 8 or 16 (default 16 if available) + * @phy_ulpi_ddr: Specifies whether the ULPI operates at double or single + * data rate. This parameter is only applicable if phy_type + * is ULPI. + * 0 - single data rate ULPI interface with 8 bit wide + * data bus (default) + * 1 - double data rate ULPI interface with 4 bit wide + * data bus + * @phy_ulpi_ext_vbus: For a ULPI phy, specifies whether to use the internal or + * external supply to drive the VBus + * 0 - Internal supply (default) + * 1 - External supply + * @i2c_enable: Specifies whether to use the I2Cinterface for a full + * speed PHY. This parameter is only applicable if phy_type + * is FS. + * 0 - No (default) + * 1 - Yes + * @ipg_isoc_en: Indicates the IPG supports is enabled or disabled. + * 0 - Disable (default) + * 1 - Enable + * @acg_enable: For enabling Active Clock Gating in the controller + * 0 - No + * 1 - Yes + * @ulpi_fs_ls: Make ULPI phy operate in FS/LS mode only + * 0 - No (default) + * 1 - Yes + * @host_support_fs_ls_low_power: Specifies whether low power mode is supported + * when attached to a Full Speed or Low Speed device in + * host mode. + * 0 - Don't support low power mode (default) + * 1 - Support low power mode + * @host_ls_low_power_phy_clk: Specifies the PHY clock rate in low power mode + * when connected to a Low Speed device in host + * mode. This parameter is applicable only if + * host_support_fs_ls_low_power is enabled. + * 0 - 48 MHz + * (default when phy_type is UTMI+ or ULPI) + * 1 - 6 MHz + * (default when phy_type is Full Speed) + * @oc_disable: Flag to disable overcurrent condition. + * 0 - Allow overcurrent condition to get detected + * 1 - Disable overcurrent condtion to get detected + * @ts_dline: Enable Term Select Dline pulsing + * 0 - No (default) + * 1 - Yes + * @reload_ctl: Allow dynamic reloading of HFIR register during runtime + * 0 - No (default for core < 2.92a) + * 1 - Yes (default for core >= 2.92a) + * @ahbcfg: This field allows the default value of the GAHBCFG + * register to be overridden + * -1 - GAHBCFG value will be set to 0x06 + * (INCR, default) + * all others - GAHBCFG value will be overridden with + * this value + * Not all bits can be controlled like this, the + * bits defined by GAHBCFG_CTRL_MASK are controlled + * by the driver and are ignored in this + * configuration value. + * @uframe_sched: True to enable the microframe scheduler + * @external_id_pin_ctl: Specifies whether ID pin is handled externally. + * Disable CONIDSTSCHNG controller interrupt in such + * case. + * 0 - No (default) + * 1 - Yes + * @power_down: Specifies whether the controller support power_down. + * If power_down is enabled, the controller will enter + * power_down in both peripheral and host mode when + * needed. + * 0 - No (default) + * 1 - Partial power down + * 2 - Hibernation + * @lpm: Enable LPM support. + * 0 - No + * 1 - Yes + * @lpm_clock_gating: Enable core PHY clock gating. + * 0 - No + * 1 - Yes + * @besl: Enable LPM Errata support. + * 0 - No + * 1 - Yes + * @hird_threshold_en: HIRD or HIRD Threshold enable. + * 0 - No + * 1 - Yes + * @hird_threshold: Value of BESL or HIRD Threshold. + * @activate_stm_fs_transceiver: Activate internal transceiver using GGPIO + * register. + * 0 - Deactivate the transceiver (default) + * 1 - Activate the transceiver + * @g_dma: Enables gadget dma usage (default: autodetect). + * @g_dma_desc: Enables gadget descriptor DMA (default: autodetect). + * @g_rx_fifo_size: The periodic rx fifo size for the device, in + * DWORDS from 16-32768 (default: 2048 if + * possible, otherwise autodetect). + * @g_np_tx_fifo_size: The non-periodic tx fifo size for the device in + * DWORDS from 16-32768 (default: 1024 if + * possible, otherwise autodetect). + * @g_tx_fifo_size: An array of TX fifo sizes in dedicated fifo + * mode. Each value corresponds to one EP + * starting from EP1 (max 15 values). Sizes are + * in DWORDS with possible values from from + * 16-32768 (default: 256, 256, 256, 256, 768, + * 768, 768, 768, 0, 0, 0, 0, 0, 0, 0). + * @change_speed_quirk: Change speed configuration to DWC2_SPEED_PARAM_FULL + * while full&low speed device connect. And change speed + * back to DWC2_SPEED_PARAM_HIGH while device is gone. + * 0 - No (default) + * 1 - Yes + * + * The following parameters may be specified when starting the module. These + * parameters define how the DWC_otg controller should be configured. A + * value of -1 (or any other out of range value) for any parameter means + * to read the value from hardware (if possible) or use the builtin + * default described above. + */ +struct dwc2_core_params { + u8 otg_cap; +#define DWC2_CAP_PARAM_HNP_SRP_CAPABLE 0 +#define DWC2_CAP_PARAM_SRP_ONLY_CAPABLE 1 +#define DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE 2 + + u8 phy_type; +#define DWC2_PHY_TYPE_PARAM_FS 0 +#define DWC2_PHY_TYPE_PARAM_UTMI 1 +#define DWC2_PHY_TYPE_PARAM_ULPI 2 + + u8 speed; +#define DWC2_SPEED_PARAM_HIGH 0 +#define DWC2_SPEED_PARAM_FULL 1 +#define DWC2_SPEED_PARAM_LOW 2 + + u8 phy_utmi_width; + bool phy_ulpi_ddr; + bool phy_ulpi_ext_vbus; + bool phy_ulpi_ext_vbus_ind; + bool phy_ulpi_ext_vbus_ind_complement; + bool phy_ulpi_ext_vbus_ind_passthrough; + + bool enable_dynamic_fifo; + bool en_multiple_tx_fifo; + bool i2c_enable; + bool acg_enable; + bool ulpi_fs_ls; + bool ts_dline; + bool reload_ctl; + bool uframe_sched; + bool external_id_pin_ctl; + + int power_down; +#define DWC2_POWER_DOWN_PARAM_NONE 0 +#define DWC2_POWER_DOWN_PARAM_PARTIAL 1 +#define DWC2_POWER_DOWN_PARAM_HIBERNATION 2 + + bool lpm; + bool lpm_clock_gating; + bool besl; + bool hird_threshold_en; + u8 hird_threshold; + bool activate_stm_fs_transceiver; + bool ipg_isoc_en; + u16 max_packet_count; + u32 max_transfer_size; + u32 ahbcfg; + + bool dma; + bool dma_desc; + + /* Host parameters */ + bool host_support_fs_ls_low_power; + bool host_ls_low_power_phy_clk; + + u8 host_channels; + u16 host_rx_fifo_size; + u16 host_nperio_tx_fifo_size; + u16 host_perio_tx_fifo_size; + + /* Gadget parameters */ + u32 g_rx_fifo_size; + u32 g_np_tx_fifo_size; + u32 g_tx_fifo_size[DWC2_MAX_EPS_CHANNELS]; + + bool change_speed_quirk; +}; + +/** + * struct dwc2_hw_params - Autodetected parameters. + * + * These parameters are the various parameters read from hardware + * registers during initialization. They typically contain the best + * supported or maximum value that can be configured in the + * corresponding dwc2_core_params value. + * + * The values that are not in dwc2_core_params are documented below. + * + * @op_mode: Mode of Operation + * 0 - HNP- and SRP-Capable OTG (Host & Device) + * 1 - SRP-Capable OTG (Host & Device) + * 2 - Non-HNP and Non-SRP Capable OTG (Host & Device) + * 3 - SRP-Capable Device + * 4 - Non-OTG Device + * 5 - SRP-Capable Host + * 6 - Non-OTG Host + * @arch: Architecture + * 0 - Slave only + * 1 - External DMA + * 2 - Internal DMA + * @ipg_isoc_en: This feature indicates that the controller supports + * the worst-case scenario of Rx followed by Rx + * Interpacket Gap (IPG) (32 bitTimes) as per the utmi + * specification for any token following ISOC OUT token. + * 0 - Don't support + * 1 - Support + * @power_optimized: Are power optimizations enabled? + * @num_dev_ep: Number of device endpoints available + * @num_dev_in_eps: Number of device IN endpoints available + * @num_dev_perio_in_ep: Number of device periodic IN endpoints + * available + * @dev_token_q_depth: Device Mode IN Token Sequence Learning Queue + * Depth + * 0 to 30 + * @host_perio_tx_q_depth: + * Host Mode Periodic Request Queue Depth + * 2, 4 or 8 + * @nperio_tx_q_depth: + * Non-Periodic Request Queue Depth + * 2, 4 or 8 + * @hs_phy_type: High-speed PHY interface type + * 0 - High-speed interface not supported + * 1 - UTMI+ + * 2 - ULPI + * 3 - UTMI+ and ULPI + * @fs_phy_type: Full-speed PHY interface type + * 0 - Full speed interface not supported + * 1 - Dedicated full speed interface + * 2 - FS pins shared with UTMI+ pins + * 3 - FS pins shared with ULPI pins + * @total_fifo_size: Total internal RAM for FIFOs (bytes) + * @hibernation: Is hibernation enabled? + * @utmi_phy_data_width: UTMI+ PHY data width + * 0 - 8 bits + * 1 - 16 bits + * 2 - 8 or 16 bits + * @snpsid: Value from SNPSID register + * @dev_ep_dirs: Direction of device endpoints (GHWCFG1) + * @g_tx_fifo_size: Power-on values of TxFIFO sizes + * @dma_desc_enable: When DMA mode is enabled, specifies whether to use + * address DMA mode or descriptor DMA mode for accessing + * the data FIFOs. The driver will automatically detect the + * value for this if none is specified. + * 0 - Address DMA + * 1 - Descriptor DMA (default, if available) + * @enable_dynamic_fifo: 0 - Use coreConsultant-specified FIFO size parameters + * 1 - Allow dynamic FIFO sizing (default, if available) + * @en_multiple_tx_fifo: Specifies whether dedicated per-endpoint transmit FIFOs + * are enabled for non-periodic IN endpoints in device + * mode. + * @host_nperio_tx_fifo_size: Number of 4-byte words in the non-periodic Tx FIFO + * in host mode when dynamic FIFO sizing is enabled + * 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + * @host_perio_tx_fifo_size: Number of 4-byte words in the periodic Tx FIFO in + * host mode when dynamic FIFO sizing is enabled + * 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + * @max_transfer_size: The maximum transfer size supported, in bytes + * 2047 to 65,535 + * Actual maximum value is autodetected and also + * the default. + * @max_packet_count: The maximum number of packets in a transfer + * 15 to 511 + * Actual maximum value is autodetected and also + * the default. + * @host_channels: The number of host channel registers to use + * 1 to 16 + * Actual maximum value is autodetected and also + * the default. + * @dev_nperio_tx_fifo_size: Number of 4-byte words in the non-periodic Tx FIFO + * in device mode when dynamic FIFO sizing is enabled + * 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + * @i2c_enable: Specifies whether to use the I2Cinterface for a full + * speed PHY. This parameter is only applicable if phy_type + * is FS. + * 0 - No (default) + * 1 - Yes + * @acg_enable: For enabling Active Clock Gating in the controller + * 0 - Disable + * 1 - Enable + * @lpm_mode: For enabling Link Power Management in the controller + * 0 - Disable + * 1 - Enable + * @rx_fifo_size: Number of 4-byte words in the Rx FIFO when dynamic + * FIFO sizing is enabled 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + */ +struct dwc2_hw_params { + unsigned op_mode:3; + unsigned arch:2; + unsigned dma_desc_enable:1; + unsigned enable_dynamic_fifo:1; + unsigned en_multiple_tx_fifo:1; + unsigned rx_fifo_size:16; + unsigned host_nperio_tx_fifo_size:16; + unsigned dev_nperio_tx_fifo_size:16; + unsigned host_perio_tx_fifo_size:16; + unsigned nperio_tx_q_depth:3; + unsigned host_perio_tx_q_depth:3; + unsigned dev_token_q_depth:5; + unsigned max_transfer_size:26; + unsigned max_packet_count:11; + unsigned host_channels:5; + unsigned hs_phy_type:2; + unsigned fs_phy_type:2; + unsigned i2c_enable:1; + unsigned acg_enable:1; + unsigned num_dev_ep:4; + unsigned num_dev_in_eps:4; + unsigned num_dev_perio_in_ep:4; + unsigned total_fifo_size:16; + unsigned power_optimized:1; + unsigned hibernation:1; + unsigned utmi_phy_data_width:2; + unsigned lpm_mode:1; + unsigned ipg_isoc_en:1; + u32 snpsid; + u32 dev_ep_dirs; + u32 g_tx_fifo_size[DWC2_MAX_EPS_CHANNELS]; +}; + +#define MAX_DEVICE 16 +#define MAX_ENDPOINT DWC2_MAX_EPS_CHANNELS + +struct dwc2_ep { + struct usb_ep ep; + struct dwc2 *dwc2; + struct list_head queue; + struct dwc2_request *req; + char name[8]; + + unsigned int size_loaded; + unsigned int last_load; + unsigned short fifo_size; + unsigned short fifo_index; + + u8 dir_in; + u8 epnum; + u8 mc; + u16 interval; + + unsigned int halted:1; + unsigned int periodic:1; + unsigned int isochronous:1; + unsigned int send_zlp:1; + unsigned int target_frame; +#define TARGET_FRAME_INITIAL 0xFFFFFFFF + bool frame_overrun; +}; + +struct dwc2_request { + struct usb_request req; + struct list_head queue; +}; + +/* Gadget ep0 states */ +enum dwc2_ep0_state { + DWC2_EP0_SETUP, + DWC2_EP0_DATA_IN, + DWC2_EP0_DATA_OUT, + DWC2_EP0_STATUS_IN, + DWC2_EP0_STATUS_OUT, +}; + +/* Size of control and EP0 buffers */ +#define DWC2_CTRL_BUFF_SIZE 8 + +struct dwc2 { + struct device *dev; + void __iomem *regs; + enum usb_dr_mode dr_mode; + struct dwc2_hw_params hw_params; + struct dwc2_core_params params; + + struct phy *phy; /* optional */ + struct clk *clk; + +#ifdef CONFIG_USB_DWC2_HOST + struct usb_host host; + u8 in_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; + u8 out_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; + int root_hub_devnum; +#endif + +#ifdef CONFIG_USB_DWC2_GADGET + struct usb_gadget gadget; + struct dwc2_ep *eps_in[MAX_ENDPOINT]; + struct dwc2_ep *eps_out[MAX_ENDPOINT]; + struct usb_request *ctrl_req; + void *ep0_buff; + void *ctrl_buff; + enum dwc2_ep0_state ep0_state; + struct usb_gadget_driver *driver; + + int num_eps; + u16 frame_number; + u32 fifo_map; + unsigned int dedicated_fifos:1; + unsigned int enabled:1; + unsigned int connected:1; + unsigned int is_selfpowered:1; +#endif +}; + +#define host_to_dwc2(ptr) container_of(ptr, struct dwc2, host) +#define gadget_to_dwc2(ptr) container_of(ptr, struct dwc2, gadget) + +#define dwc2_err(d, arg...) dev_err((d)->dev, ## arg) +#define dwc2_warn(d, arg...) dev_warn((d)->dev, ## arg) +#define dwc2_info(d, arg...) dev_info((d)->dev, ## arg) +#define dwc2_dbg(d, arg...) dev_dbg((d)->dev, ## arg) +#define dwc2_vdbg(d, arg...) dev_vdbg((d)->dev, ## arg) + +static inline u32 dwc2_readl(struct dwc2 *dwc2, u32 offset) +{ + u32 val; + + val = readl(dwc2->regs + offset); + + return val; +} + +static inline void dwc2_writel(struct dwc2 *dwc2, u32 value, u32 offset) +{ + writel(value, dwc2->regs + offset); +} + +/** + * dwc2_wait_bit_set - Waits for bit to be set. + * @dwc2: Programming view of DWC2 controller. + * @offset: Register's offset where bit/bits must be set. + * @mask: Mask of the bit/bits which must be set. + * @timeout: Timeout to wait. + * + * Return: 0 if bit/bits are set or -ETIMEDOUT in case of timeout. + */ +static inline int dwc2_wait_bit_set(struct dwc2 *dwc2, u32 offset, u32 mask, + u32 timeout) +{ + return wait_on_timeout(timeout * USECOND, + (dwc2_readl(dwc2, offset) & mask) == mask); +} + +/** + * dwc2_wait_bit_clear - Waits for bit to be clear. + * @dwc2: Programming view of DWC2 controller. + * @offset: Register's offset where bit/bits must be set. + * @mask: Mask of the bit/bits which must be set. + * @timeout: Timeout to wait. + * + * Return: 0 if bit/bits are set or -ETIMEDOUT in case of timeout. + */ +static inline int dwc2_wait_bit_clear(struct dwc2 *dwc2, u32 offset, u32 mask, + u32 timeout) +{ + return wait_on_timeout(timeout * USECOND, + !(dwc2_readl(dwc2, offset) & mask)); +} + +/* + * Returns the mode of operation, host or device + */ +static inline int dwc2_is_host_mode(struct dwc2 *dwc2) +{ + return (dwc2_readl(dwc2, GINTSTS) & GINTSTS_CURMODE_HOST) != 0; +} + +static inline int dwc2_is_device_mode(struct dwc2 *dwc2) +{ + return (dwc2_readl(dwc2, GINTSTS) & GINTSTS_CURMODE_HOST) == 0; +} diff --git a/drivers/usb/dwc2/dwc2.c b/drivers/usb/dwc2/dwc2.c new file mode 100644 index 0000000000..a35fd0e717 --- /dev/null +++ b/drivers/usb/dwc2/dwc2.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org> + * Copyright (C) 2014 Marek Vasut <marex@denx.de> + * + * Copied from u-Boot + */ +#include <common.h> +#include <of.h> +#include <dma.h> +#include <init.h> +#include <errno.h> +#include <driver.h> +#include <linux/clk.h> +#include <linux/reset.h> +#include <of_device.h> + +#include "dwc2.h" + +static void dwc2_set_stm32mp15_fsotg_params(struct dwc2 *dwc2) +{ + struct dwc2_core_params *p = &dwc2->params; + + p->otg_cap &= ~(DWC2_CAP_PARAM_HNP_SRP_CAPABLE + | DWC2_CAP_PARAM_SRP_ONLY_CAPABLE); + p->otg_cap |= DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; + p->speed = DWC2_SPEED_PARAM_FULL; + p->host_rx_fifo_size = 128; + p->host_nperio_tx_fifo_size = 96; + p->host_perio_tx_fifo_size = 96; + p->max_packet_count = 256; + p->phy_type = DWC2_PHY_TYPE_PARAM_FS; + p->i2c_enable = false; + p->activate_stm_fs_transceiver = true; + p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT; + p->power_down = DWC2_POWER_DOWN_PARAM_NONE; + p->host_support_fs_ls_low_power = true; + p->host_ls_low_power_phy_clk = true; +} + +static void dwc2_set_stm32mp15_hsotg_params(struct dwc2 *dwc2) +{ + struct dwc2_core_params *p = &dwc2->params; + + p->otg_cap &= ~(DWC2_CAP_PARAM_HNP_SRP_CAPABLE + | DWC2_CAP_PARAM_SRP_ONLY_CAPABLE); + p->otg_cap |= DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; + p->host_rx_fifo_size = 440; + p->host_nperio_tx_fifo_size = 256; + p->host_perio_tx_fifo_size = 256; + p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT; + p->power_down = DWC2_POWER_DOWN_PARAM_NONE; + p->lpm = false; + p->lpm_clock_gating = false; + p->besl = false; + p->hird_threshold_en = false; +} + +static int dwc2_set_mode(void *ctx, enum usb_dr_mode mode) +{ + struct dwc2 *dwc2 = ctx; + int ret = -ENODEV; + int oldmode = dwc2->dr_mode; + + dwc2->dr_mode = mode; + + if (mode == USB_DR_MODE_HOST || mode == USB_DR_MODE_OTG) { + if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) + ret = dwc2_register_host(dwc2); + else + dwc2_err(dwc2, "Host support not available\n"); + } + if (mode == USB_DR_MODE_PERIPHERAL || mode == USB_DR_MODE_OTG) { + if (IS_ENABLED(CONFIG_USB_DWC2_GADGET)) + ret = dwc2_gadget_init(dwc2); + else + dwc2_err(dwc2, "Peripheral support not available\n"); + } + + if (ret) + dwc2->dr_mode = oldmode; + + return ret; +} + +typedef void (*set_params_cb)(struct dwc2 *dwc2); + +static int dwc2_probe(struct device *dev) +{ + struct resource *iores; + struct dwc2 *dwc2; + set_params_cb set_params; + int ret; + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + dwc2 = xzalloc(sizeof(*dwc2)); + dwc2->regs = IOMEM(iores->start); + dwc2->dev = dev; + + dwc2->clk = clk_get(dev, "otg"); + if (IS_ERR(dwc2->clk)) { + ret = PTR_ERR(dwc2->clk); + dev_err(dev, "Failed to get USB clock %d\n", ret); + goto release_region; + } + + ret = clk_enable(dwc2->clk); + if (ret) + goto clk_put; + + ret = device_reset_us(dev, 2); + if (ret) + goto clk_disable; + + dwc2->phy = phy_optional_get(dev, "usb2-phy"); + if (IS_ERR(dwc2->phy)) { + ret = PTR_ERR(dwc2->phy); + goto clk_disable; + } + + ret = phy_power_on(dwc2->phy); + if (ret) + goto clk_disable; + + ret = phy_init(dwc2->phy); + if (ret) + goto phy_power_off; + + ret = dwc2_check_core_version(dwc2); + if (ret) + goto error; + + /* + * Reset before dwc2_get_hwparams() then it could get power-on real + * reset value form registers. + */ + ret = dwc2_core_reset(dwc2); + if (ret) + goto error; + + /* Detect config values from hardware */ + dwc2_get_hwparams(dwc2); + + ret = dwc2_get_dr_mode(dwc2); + if (ret) + goto error; + + dwc2_set_default_params(dwc2); + dwc2_get_device_properties(dwc2); + + set_params = of_device_get_match_data(dev); + if (set_params) + set_params(dwc2); + + dma_set_mask(dev, DMA_BIT_MASK(32)); + dev->priv = dwc2; + + if (dwc2->dr_mode == USB_DR_MODE_OTG) + ret = usb_register_otg_device(dwc2->dev, dwc2_set_mode, dwc2); + else + ret = dwc2_set_mode(dwc2, dwc2->dr_mode); + + if (ret) + goto error; + + return 0; +error: + phy_exit(dwc2->phy); +phy_power_off: + phy_power_off(dwc2->phy); +clk_disable: + clk_disable(dwc2->clk); +clk_put: + clk_put(dwc2->clk); +release_region: + release_region(iores); + + free(dwc2); + + return ret; +} + +static void dwc2_remove(struct device *dev) +{ + struct dwc2 *dwc2 = dev->priv; + + dwc2_host_uninit(dwc2); + dwc2_gadget_uninit(dwc2); + + phy_exit(dwc2->phy); + phy_power_off(dwc2->phy); +} + +static const struct of_device_id dwc2_platform_dt_ids[] = { + { .compatible = "brcm,bcm2835-usb", }, + { .compatible = "brcm,bcm2708-usb", }, + { .compatible = "snps,dwc2", }, + { .compatible = "st,stm32mp15-fsotg", + .data = dwc2_set_stm32mp15_fsotg_params }, + { .compatible = "st,stm32mp15-hsotg", + .data = dwc2_set_stm32mp15_hsotg_params }, + { } +}; +MODULE_DEVICE_TABLE(of, dwc2_platform_dt_ids); + +static struct driver dwc2_driver = { + .name = "dwc2", + .probe = dwc2_probe, + .remove = dwc2_remove, + .of_compatible = DRV_OF_COMPAT(dwc2_platform_dt_ids), +}; +device_platform_driver(dwc2_driver); diff --git a/drivers/usb/dwc2/dwc2.h b/drivers/usb/dwc2/dwc2.h new file mode 100644 index 0000000000..2e740a890e --- /dev/null +++ b/drivers/usb/dwc2/dwc2.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include <linux/usb/usb.h> +#include <linux/usb/usb_defs.h> +#include <linux/usb/gadget.h> +#include <linux/phy/phy.h> + +#include "regs.h" +#include "core.h" + +/* Core functions */ +void dwc2_set_default_params(struct dwc2 *dwc2); +void dwc2_get_device_properties(struct dwc2 *dwc2); +int dwc2_check_core_version(struct dwc2 *dwc2); +void dwc2_get_hwparams(struct dwc2 *dwc2); + +void dwc2_init_fs_ls_pclk_sel(struct dwc2 *dwc2); +void dwc2_flush_all_fifo(struct dwc2 *dwc2); +void dwc2_flush_tx_fifo(struct dwc2 *dwc2, const int idx); +int dwc2_tx_fifo_count(struct dwc2 *dwc2); + +int dwc2_phy_init(struct dwc2 *dwc2, bool select_phy); +int dwc2_gahbcfg_init(struct dwc2 *dwc2); +void dwc2_gusbcfg_init(struct dwc2 *dwc2); +int dwc2_get_dr_mode(struct dwc2 *dwc2); + +int dwc2_core_reset(struct dwc2 *dwc2); +void dwc2_core_init(struct dwc2 *dwc2); + +void dwc2_wait_for_mode(struct dwc2 *dwc2, bool host_mode); +bool dwc2_iddig_filter_enabled(struct dwc2 *dwc2); + +/* Host functions */ +#ifdef CONFIG_USB_DWC2_HOST +int dwc2_submit_roothub(struct dwc2 *dwc2, struct usb_device *dev, + unsigned long pipe, void *buf, int len, + struct devrequest *setup); +int dwc2_register_host(struct dwc2 *dwc2); +void dwc2_host_uninit(struct dwc2 *dwc2); +#else +static inline int dwc2_register_host(struct dwc2 *dwc2) { return -ENODEV; } +static inline void dwc2_host_uninit(struct dwc2 *dwc2) {}; +#endif + +/* Gadget functions */ +#ifdef CONFIG_USB_DWC2_GADGET +int dwc2_gadget_init(struct dwc2 *dwc2); +void dwc2_gadget_uninit(struct dwc2 *dwc2); +#else +static inline int dwc2_gadget_init(struct dwc2 *dwc2) { return -ENODEV; } +static inline void dwc2_gadget_uninit(struct dwc2 *dwc2) {}; +#endif diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c new file mode 100644 index 0000000000..3c06c438b6 --- /dev/null +++ b/drivers/usb/dwc2/gadget.c @@ -0,0 +1,2818 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include <dma.h> +#include <linux/usb/gadget.h> +#include <linux/spinlock.h> +#include "dwc2.h" + +#define to_dwc2 gadget_to_dwc2 +#define dwc2_set_bit(d, r, b) dwc2_writel(d, (b) | dwc2_readl(d, r), r) +#define dwc2_clear_bit(d, r, b) dwc2_writel(d, ~(b) & dwc2_readl(d, r), r) + +#define local_irq_save(flags)(void)(flags) +#define local_irq_restore(flags) (void)(flags) + +static void kill_all_requests(struct dwc2 *, struct dwc2_ep *, int); + +static inline struct dwc2_ep *index_to_ep(struct dwc2 *dwc2, + unsigned int ep, int in) +{ + if (ep >= DWC2_MAX_EPS_CHANNELS) + return NULL; + if (in) + return dwc2->eps_in[ep]; + else + return dwc2->eps_out[ep]; +} + +static inline int using_dma(struct dwc2 *dwc2) +{ + /* Only buffer dma is supported */ + return 1; +} + +static void dwc2_dcfg_set_addr(struct dwc2 *dwc2, int addr) +{ + u32 dcfg = dwc2_readl(dwc2, DCFG); + + dcfg &= ~DCFG_DEVADDR_MASK; + dcfg |= (addr << DCFG_DEVADDR_SHIFT) & DCFG_DEVADDR_MASK; + dwc2_writel(dwc2, dcfg, DCFG); +} + +/** + * dwc2_hsotg_ctrl_epint - enable/disable an endpoint irq + * @hsotg: The device state + * @ep: The endpoint index + * @dir_in: True if direction is in. + * @en: The enable value, true to enable + * + * Set or clear the mask for an individual endpoint's interrupt + * request. + */ +static void dwc2_hsotg_ctrl_epint(struct dwc2 *dwc2, + unsigned int ep, unsigned int dir_in, + unsigned int en) +{ + unsigned long flags; + u32 bit = 1 << ep; + u32 daint; + + if (!dir_in) + bit <<= 16; + + local_irq_save(flags); + daint = dwc2_readl(dwc2, DAINTMSK); + if (en) + daint |= bit; + else + daint &= ~bit; + dwc2_writel(dwc2, daint, DAINTMSK); + local_irq_restore(flags); + +} + +/** + * get_ep_head - return the first request on the endpoint + * @hs_ep: The controller endpoint to get + * + * Get the first request on the endpoint. + */ +static struct dwc2_request *get_ep_head(struct dwc2_ep *hs_ep) +{ + return list_first_entry_or_null(&hs_ep->queue, struct dwc2_request, + queue); +} + +/** + * get_ep_limit - get the maximum data legnth for this endpoint + * @hs_ep: The endpoint + * + * Return the maximum data that can be queued in one go on a given endpoint + * so that transfers that are too long can be split. + */ +static unsigned int get_ep_limit(struct dwc2_ep *hs_ep) +{ + int index = hs_ep->epnum; + unsigned int maxsize; + unsigned int maxpkt; + + if (index != 0) { + maxsize = DXEPTSIZ_XFERSIZE_LIMIT + 1; + maxpkt = DXEPTSIZ_PKTCNT_LIMIT + 1; + } else { + maxsize = 64 + 64; + if (hs_ep->dir_in) + maxpkt = DIEPTSIZ0_PKTCNT_LIMIT + 1; + else + maxpkt = 2; + } + + /* we made the constant loading easier above by using +1 */ + maxpkt--; + maxsize--; + + /* + * constrain by packet count if maxpkts*pktsize is greater + * than the length register size. + */ + + if ((maxpkt * hs_ep->ep.maxpacket) < maxsize) + maxsize = maxpkt * hs_ep->ep.maxpacket; + + return maxsize; +} + +static u32 dwc2_read_frameno(struct dwc2 *dwc2) +{ + u32 dsts; + + dsts = dwc2_readl(dwc2, DSTS); + dsts &= DSTS_SOFFN_MASK; + dsts >>= DSTS_SOFFN_SHIFT; + + return dsts; +} + +/** + * dwc2_gadget_incr_frame_num - Increments the targeted frame number. + * @hs_ep: The endpoint + * + * This function will also check if the frame number overruns DSTS_SOFFN_LIMIT. + * If an overrun occurs it will wrap the value and set the frame_overrun flag. + */ +static inline void dwc2_gadget_incr_frame_num(struct dwc2_ep *hs_ep) +{ + hs_ep->target_frame += hs_ep->interval; + if (hs_ep->target_frame > DSTS_SOFFN_LIMIT) { + hs_ep->frame_overrun = true; + hs_ep->target_frame &= DSTS_SOFFN_LIMIT; + } else { + hs_ep->frame_overrun = false; + } +} + +/** + * dwc2_gadget_target_frame_elapsed - Checks target frame + * @hs_ep: The driver endpoint to check + * + * Returns 1 if targeted frame elapsed. If returned 1 then we need to drop + * corresponding transfer. + */ +static bool dwc2_gadget_target_frame_elapsed(struct dwc2_ep *hs_ep) +{ + struct dwc2 *dwc2 = hs_ep->dwc2; + u32 target_frame = hs_ep->target_frame; + u32 current_frame = dwc2->frame_number; + bool frame_overrun = hs_ep->frame_overrun; + + if (!frame_overrun && current_frame >= target_frame) + return true; + + if (frame_overrun && current_frame >= target_frame && + ((current_frame - target_frame) < DSTS_SOFFN_LIMIT / 2)) + return true; + + return false; +} + + +/** + * dwc2_gadget_start_req - start a USB request from an endpoint's queue + * @dwc2: The controller state. + * @hs_ep: The endpoint to process a request for + * @hs_req: The request to start. + * @continuing: True if we are doing more for the current request. + * + * Start the given request running by setting the endpoint registers + * appropriately, and writing any data to the FIFOs. + */ +static void dwc2_gadget_start_req(struct dwc2 *dwc2, + struct dwc2_ep *hs_ep, + struct dwc2_request *hs_req, + bool continuing) +{ + struct usb_request *ureq = &hs_req->req; + int index = hs_ep->epnum; + int dir_in = hs_ep->dir_in; + u32 epctrl_reg; + u32 epsize_reg; + u32 epsize; + u32 ctrl; + unsigned int length; + unsigned int packets; + unsigned int maxreq; + unsigned int dma_reg; + + if (index != 0) { + if (hs_ep->req && !continuing) { + dwc2_err(dwc2, "%s: active request\n", __func__); + WARN_ON(1); + return; + } else if (hs_ep->req != hs_req && continuing) { + dwc2_err(dwc2, + "%s: continue different req\n", __func__); + WARN_ON(1); + return; + } + } + + dma_reg = dir_in ? DIEPDMA(index) : DOEPDMA(index); + epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); + epsize_reg = dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index); + + dwc2_dbg(dwc2, "%s: DxEPCTL=0x%08x, ep %d, dir %s\n", + __func__, dwc2_readl(dwc2, epctrl_reg), index, + hs_ep->dir_in ? "in" : "out"); + + /* If endpoint is stalled, we will restart request later */ + ctrl = dwc2_readl(dwc2, epctrl_reg); + + if (index && ctrl & DXEPCTL_STALL) { + dwc2_warn(dwc2, "%s: ep%d is stalled\n", __func__, index); + return; + } + + length = ureq->length - ureq->actual; + dwc2_dbg(dwc2, "ureq->length:%d ureq->actual:%d\n", + ureq->length, ureq->actual); + + maxreq = get_ep_limit(hs_ep); + + if (length > maxreq) { + int round = maxreq % hs_ep->ep.maxpacket; + + dwc2_dbg(dwc2, "%s: length %d, max-req %d, r %d\n", + __func__, length, maxreq, round); + + /* round down to multiple of packets */ + if (round) + maxreq -= round; + + length = maxreq; + } + + if (length) + packets = DIV_ROUND_UP(length, hs_ep->ep.maxpacket); + else + packets = 1; /* send one packet if length is zero. */ + + if (dir_in && index != 0) + if (hs_ep->isochronous) + epsize = DXEPTSIZ_MC(packets); + else + epsize = DXEPTSIZ_MC(1); + else + epsize = 0; + + /* + * zero length packet should be programmed on its own and should not + * be counted in DIEPTSIZ.PktCnt with other packets. + */ + if (dir_in && ureq->zero && !continuing) { + /* Test if zlp is actually required. */ + if ((ureq->length >= hs_ep->ep.maxpacket) && + !(ureq->length % hs_ep->ep.maxpacket)) + hs_ep->send_zlp = 1; + } + + epsize |= DXEPTSIZ_PKTCNT(packets); + epsize |= DXEPTSIZ_XFERSIZE(length); + + dwc2_dbg(dwc2, "%s: %d@%d/%d, 0x%08x => 0x%08x\n", + __func__, packets, length, ureq->length, epsize, epsize_reg); + + /* store the request as the current one we're doing */ + hs_ep->req = hs_req; + + /* write size / packets */ + dwc2_writel(dwc2, epsize, epsize_reg); + + if (using_dma(dwc2) && !continuing && (length != 0)) { + /* + * write DMA address to control register, buffer + * already synced by dwc2_ep_queue(). + */ + + dwc2_writel(dwc2, ureq->dma, dma_reg); + + dwc2_dbg(dwc2, "%s: 0x%pad => 0x%08x\n", + __func__, &ureq->dma, dma_reg); + } + + if (hs_ep->isochronous && hs_ep->interval == 1) { + hs_ep->target_frame = dwc2_read_frameno(dwc2); + dwc2_gadget_incr_frame_num(hs_ep); + + if (hs_ep->target_frame & 0x1) + ctrl |= DXEPCTL_SETODDFR; + else + ctrl |= DXEPCTL_SETEVENFR; + } + + ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */ + + dwc2_dbg(dwc2, "%s: ep0 state:%d\n", __func__, dwc2->ep0_state); + + /* For Setup request do not clear NAK */ + if (!(index == 0 && dwc2->ep0_state == DWC2_EP0_SETUP)) + ctrl |= DXEPCTL_CNAK; /* clear NAK set by core */ + + dwc2_dbg(dwc2, "%s: DxEPCTL=0x%08x\n", __func__, ctrl); + dwc2_writel(dwc2, ctrl, epctrl_reg); + + /* + * set these, it seems that DMA support increments past the end + * of the packet buffer so we need to calculate the length from + * this information. + */ + hs_ep->size_loaded = length; + hs_ep->last_load = ureq->actual; + + /* + * Note, trying to clear the NAK here causes problems with transmit + * on the S3C6400 ending up with the TXFIFO becoming full. + */ + + /* check ep is enabled */ + if (!(dwc2_readl(dwc2, epctrl_reg) & DXEPCTL_EPENA)) + dwc2_dbg(dwc2, + "ep%d: failed to become enabled (DXEPCTL=0x%08x)?\n", + index, dwc2_readl(dwc2, epctrl_reg)); + + dwc2_dbg(dwc2, "%s: DXEPCTL=0x%08x\n", + __func__, dwc2_readl(dwc2, epctrl_reg)); + + /* enable ep interrupts */ + dwc2_hsotg_ctrl_epint(dwc2, hs_ep->epnum, hs_ep->dir_in, 1); +} + +/* conversion functions */ +static inline struct dwc2_request *our_req(struct usb_request *req) +{ + return container_of(req, struct dwc2_request, req); +} + +static inline struct dwc2_ep *our_ep(struct usb_ep *ep) +{ + return container_of(ep, struct dwc2_ep, ep); +} + + +/** + * dwc2_ep0_mps - turn max packet size into register setting + * @mps: The maximum packet size in bytes. + */ +static u32 dwc2_ep0_mps(unsigned int mps) +{ + switch (mps) { + case 64: + return D0EPCTL_MPS_64; + case 32: + return D0EPCTL_MPS_32; + case 16: + return D0EPCTL_MPS_16; + case 8: + return D0EPCTL_MPS_8; + } + + /* bad max packet size, warn and return invalid result */ + WARN_ON(1); + return (u32)-1; +} + +/** + * dwc2_set_ep_maxpacket - set endpoint's max-packet field + * @dwc2: The driver state. + * @ep: The index number of the endpoint + * @mps: The maximum packet size in bytes + * @mc: The multicount value + * @dir_in: True if direction is in. + * + * Configure the maximum packet size for the given endpoint, updating + * the hardware control registers to reflect this. + */ +static void dwc2_set_ep_maxpacket(struct dwc2 *dwc2, + unsigned int ep, unsigned int mps, + unsigned int mc, unsigned int dir_in) +{ + struct dwc2_ep *hs_ep; + u32 reg; + + hs_ep = index_to_ep(dwc2, ep, dir_in); + if (!hs_ep) + return; + + if (ep == 0) { + u32 mps_bytes = mps; + + /* EP0 is a special case */ + mps = dwc2_ep0_mps(mps_bytes); + if (mps > 3) + goto bad_mps; + hs_ep->ep.maxpacket = mps_bytes; + hs_ep->mc = 1; + } else { + if (mps > 1024) + goto bad_mps; + hs_ep->mc = mc; + if (mc > 3) + goto bad_mps; + hs_ep->ep.maxpacket = mps; + } + + if (dir_in) { + reg = dwc2_readl(dwc2, DIEPCTL(ep)); + reg &= ~DXEPCTL_MPS_MASK; + reg |= mps; + dwc2_writel(dwc2, reg, DIEPCTL(ep)); + } else { + reg = dwc2_readl(dwc2, DOEPCTL(ep)); + reg &= ~DXEPCTL_MPS_MASK; + reg |= mps; + dwc2_writel(dwc2, reg, DOEPCTL(ep)); + } + + return; + +bad_mps: + dwc2_err(dwc2, "ep%d: bad mps of %d\n", ep, mps); +} + +static int dwc2_ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + unsigned long flags; + unsigned int index = hs_ep->epnum; + u32 epctrl_reg; + u32 epctrl; + u32 mps; + u32 mc; + u32 mask; + unsigned int dir_in; + unsigned int i, val, size; + int ret = 0; + unsigned char ep_type; + + dwc2_dbg(dwc2, + "%s: ep %s: a 0x%02x, attr 0x%02x, mps 0x%04x, intr %d\n", + __func__, ep->name, desc->bEndpointAddress, desc->bmAttributes, + desc->wMaxPacketSize, desc->bInterval); + + /* not to be called for EP0 */ + if (index == 0) { + dwc2_err(dwc2, "%s: called for EP 0\n", __func__); + return -EINVAL; + } + + dir_in = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? 1 : 0; + if (dir_in != hs_ep->dir_in) { + dwc2_err(dwc2, "%s: direction mismatch!\n", __func__); + return -EINVAL; + } + + ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + mps = usb_endpoint_maxp(desc) & USB_ENDPOINT_MAXP_MASK; + mc = usb_endpoint_maxp_mult(desc); + + /* note, we handle this here instead of dwc2_set_ep_maxpacket */ + epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); + epctrl = dwc2_readl(dwc2, epctrl_reg); + + dwc2_dbg(dwc2, "%s: read DxEPCTL=0x%08x from 0x%08x\n", + __func__, epctrl, epctrl_reg); + + spin_lock_irqsave(&dwc2->lock, flags); + + epctrl &= ~(DXEPCTL_EPTYPE_MASK | DXEPCTL_MPS_MASK); + epctrl |= DXEPCTL_MPS(mps); + + /* + * mark the endpoint as active, otherwise the core may ignore + * transactions entirely for this endpoint + */ + epctrl |= DXEPCTL_USBACTEP; + + /* update the endpoint state */ + dwc2_set_ep_maxpacket(dwc2, index, mps, mc, dir_in); + + /* default, set to non-periodic */ + hs_ep->isochronous = 0; + hs_ep->periodic = 0; + hs_ep->halted = 0; + hs_ep->interval = desc->bInterval; + + switch (ep_type) { + case USB_ENDPOINT_XFER_ISOC: + epctrl |= DXEPCTL_EPTYPE_ISO; + epctrl |= DXEPCTL_SETEVENFR; + hs_ep->isochronous = 1; + hs_ep->interval = 1 << (desc->bInterval - 1); + hs_ep->target_frame = TARGET_FRAME_INITIAL; + if (dir_in) { + hs_ep->periodic = 1; + mask = dwc2_readl(dwc2, DIEPMSK); + mask |= DIEPMSK_NAKMSK; + dwc2_writel(dwc2, mask, DIEPMSK); + } else { + mask = dwc2_readl(dwc2, DOEPMSK); + mask |= DOEPMSK_OUTTKNEPDISMSK; + dwc2_writel(dwc2, mask, DOEPMSK); + } + break; + + case USB_ENDPOINT_XFER_BULK: + epctrl |= DXEPCTL_EPTYPE_BULK; + break; + + case USB_ENDPOINT_XFER_INT: + if (dir_in) + hs_ep->periodic = 1; + + if (dwc2->gadget.speed == USB_SPEED_HIGH) + hs_ep->interval = 1 << (desc->bInterval - 1); + + epctrl |= DXEPCTL_EPTYPE_INTERRUPT; + break; + + case USB_ENDPOINT_XFER_CONTROL: + epctrl |= DXEPCTL_EPTYPE_CONTROL; + break; + } + + /* + * if the hardware has dedicated fifos, we must give each IN EP + * a unique tx-fifo even if it is non-periodic. + */ + if (dir_in && dwc2->dedicated_fifos) { + unsigned int fifo_count = dwc2_tx_fifo_count(dwc2); + u32 fifo_index = 0; + u32 fifo_size = UINT_MAX; + + size = hs_ep->ep.maxpacket * hs_ep->mc; + for (i = 1; i <= fifo_count; i++) { + if (dwc2->fifo_map & (1 << i)) + continue; + + val = dwc2_readl(dwc2, DPTXFSIZN(i)); + val = (val >> FIFOSIZE_DEPTH_SHIFT) * 4; + if (val < size) + continue; + /* Search for smallest acceptable fifo */ + if (val < fifo_size) { + fifo_size = val; + fifo_index = i; + } + } + if (!fifo_index) { + dwc2_err(dwc2, + "%s: No suitable fifo found\n", __func__); + ret = -ENOMEM; + goto error; + } + epctrl &= ~(DXEPCTL_TXFNUM_LIMIT << DXEPCTL_TXFNUM_SHIFT); + epctrl |= DXEPCTL_TXFNUM(fifo_index); + dwc2->fifo_map |= 1 << fifo_index; + hs_ep->fifo_index = fifo_index; + hs_ep->fifo_size = fifo_size; + } + + /* for non control endpoints, set PID to D0 */ + if (index && !hs_ep->isochronous) + epctrl |= DXEPCTL_SETD0PID; + + dwc2_dbg(dwc2, "%s: write DxEPCTL=0x%08x\n", + __func__, epctrl); + + dwc2_writel(dwc2, epctrl, epctrl_reg); + dwc2_dbg(dwc2, "%s: read DxEPCTL=0x%08x\n", + __func__, dwc2_readl(dwc2, epctrl_reg)); + + /* enable the endpoint interrupt */ + dwc2_hsotg_ctrl_epint(dwc2, index, dir_in, 1); + +error: + spin_unlock_irqrestore(&dwc2->lock, flags); + + return ret; +} + +static void dwc2_ep_stop_xfr(struct dwc2 *dwc2, struct dwc2_ep *hs_ep) +{ + int in = hs_ep->dir_in; + int epnum = hs_ep->epnum; + u32 epctl_reg = in ? DIEPCTL(epnum) : DOEPCTL(epnum); + u32 epint_reg = in ? DIEPINT(epnum) : DOEPINT(epnum); + + dwc2_dbg(dwc2, "%s: stopping transfer on %s\n", __func__, + hs_ep->name); + + if (in) { + if (dwc2->dedicated_fifos || hs_ep->periodic) { + dwc2_set_bit(dwc2, epctl_reg, DXEPCTL_SNAK); + /* Wait for Nak effect */ + if (dwc2_wait_bit_set(dwc2, epint_reg, + DXEPINT_INEPNAKEFF, 100)) + dwc2_warn(dwc2, + "%s: timeout DIEPINT.NAKEFF\n", + __func__); + } else { + dwc2_set_bit(dwc2, DCTL, DCTL_SGNPINNAK); + /* Wait for Nak effect */ + if (dwc2_wait_bit_set(dwc2, GINTSTS, + GINTSTS_GINNAKEFF, 100)) + dwc2_warn(dwc2, + "%s: timeout GINTSTS.GINNAKEFF\n", + __func__); + } + } else { + if (!(dwc2_readl(dwc2, GINTSTS) & GINTSTS_GOUTNAKEFF)) + dwc2_set_bit(dwc2, DCTL, DCTL_SGOUTNAK); + + /* Wait for global nak to take effect */ + if (dwc2_wait_bit_set(dwc2, GINTSTS, + GINTSTS_GOUTNAKEFF, 100)) + dwc2_warn(dwc2, "%s: timeout GINTSTS.GOUTNAKEFF\n", + __func__); + } + + /* Disable ep */ + dwc2_set_bit(dwc2, epctl_reg, DXEPCTL_EPDIS | DXEPCTL_SNAK); + + /* Wait for ep to be disabled */ + if (dwc2_wait_bit_set(dwc2, epint_reg, DXEPINT_EPDISBLD, 100)) + dwc2_warn(dwc2, "%s: timeout DOEPCTL.EPDisable\n", __func__); + + /* Clear EPDISBLD interrupt */ + dwc2_set_bit(dwc2, epint_reg, DXEPINT_EPDISBLD); + + if (in) { + unsigned short fifo_index; + + if (dwc2->dedicated_fifos || hs_ep->periodic) + fifo_index = hs_ep->fifo_index; + else + fifo_index = 0; + + dwc2_flush_tx_fifo(dwc2, fifo_index); + + /* Clear Global In NP NAK in Shared FIFO for non periodic ep */ + if (!dwc2->dedicated_fifos && !hs_ep->periodic) + dwc2_set_bit(dwc2, DCTL, DCTL_CGNPINNAK); + + } else { + /* Remove global NAKs */ + dwc2_set_bit(dwc2, DCTL, DCTL_CGOUTNAK); + } +} + +static int dwc2_ep_disable(struct usb_ep *ep) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + int dir_in = hs_ep->dir_in; + int index = hs_ep->epnum; + u32 epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); + u32 ctrl; + + dwc2_dbg(dwc2, "%s(ep %p)\n", __func__, ep); + + if (index == 0) { + dwc2_err(dwc2, "%s: called for ep0\n", __func__); + return -EINVAL; + } + + ctrl = dwc2_readl(dwc2, epctrl_reg); + if (ctrl & DXEPCTL_EPENA) + dwc2_ep_stop_xfr(dwc2, hs_ep); + + ctrl &= ~DXEPCTL_EPENA; + ctrl &= ~DXEPCTL_USBACTEP; + ctrl |= DXEPCTL_SNAK; + + dwc2_dbg(dwc2, "%s: DxEPCTL=0x%08x\n", __func__, ctrl); + dwc2_writel(dwc2, ctrl, epctrl_reg); + + /* disable endpoint interrupts */ + dwc2_hsotg_ctrl_epint(dwc2, index, hs_ep->dir_in, 0); + + /* terminate all requests with shutdown */ + kill_all_requests(dwc2, hs_ep, -ESHUTDOWN); + + dwc2->fifo_map &= ~(1 << hs_ep->fifo_index); + hs_ep->fifo_index = 0; + hs_ep->fifo_size = 0; + + return 0; +} + +static struct usb_request *dwc2_ep_alloc_req(struct usb_ep *ep) +{ + struct dwc2_request *req; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void dwc2_ep_free_req(struct usb_ep *ep, struct usb_request *req) +{ + struct dwc2_request *hs_req = our_req(req); + + kfree(hs_req); +} + +/** + * dwc2_start_next_request - Starts next request from ep queue + * @hs_ep: Endpoint structure + * + * If queue is empty and EP is ISOC-OUT - unmasks OUTTKNEPDIS which is masked + * in its handler. Hence we need to unmask it here to be able to do + * resynchronization. + */ +static void dwc2_start_next_request(struct dwc2_ep *hs_ep) +{ + u32 mask; + struct dwc2 *dwc2 = hs_ep->dwc2; + int dir_in = hs_ep->dir_in; + struct dwc2_request *hs_req; + u32 epmsk_reg = dir_in ? DIEPMSK : DOEPMSK; + + dwc2_dbg(dwc2, "%s: next req\n", __func__); + + if (!list_empty(&hs_ep->queue)) { + hs_req = get_ep_head(hs_ep); + dwc2_gadget_start_req(dwc2, hs_ep, hs_req, false); + return; + } + if (!hs_ep->isochronous) + return; + + if (dir_in) { + dwc2_dbg(dwc2, "%s: No more ISOC-IN requests\n", __func__); + } else { + dwc2_dbg(dwc2, "%s: No more ISOC-OUT requests\n", __func__); + mask = dwc2_readl(dwc2, epmsk_reg); + mask |= DOEPMSK_OUTTKNEPDISMSK; + dwc2_writel(dwc2, mask, epmsk_reg); + } +} + +static int dwc2_ep_queue(struct usb_ep *ep, struct usb_request *req) +{ + + struct dwc2_request *hs_req = our_req(req); + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + bool first; + + dwc2_dbg(dwc2, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n", + ep->name, req, req->length, req->buf, req->no_interrupt, + req->zero, req->short_not_ok); + + /* initialise status of the request */ + INIT_LIST_HEAD(&hs_req->queue); + req->actual = 0; + req->status = -EINPROGRESS; + + /* Don't queue ISOC request if length greater than mps*mc */ + if (hs_ep->isochronous && + req->length > (hs_ep->mc * hs_ep->ep.maxpacket)) { + dwc2_err(dwc2, "req length > maxpacket*mc\n"); + return -EINVAL; + } + + if ((long)req->buf & 3) + dwc2_err(dwc2, "dma buffer not aligned\n"); + + req->dma = dma_map_single(dwc2->dev, req->buf, req->length, + hs_ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + if (dma_mapping_error(dwc2->dev, req->dma)) { + dwc2_err(dwc2, "failed to map buffer\n"); + return -EFAULT; + } + + first = list_empty(&hs_ep->queue); + list_add_tail(&hs_req->queue, &hs_ep->queue); + + /* Change EP direction if status phase request is after data out */ + if (hs_ep->epnum == 0 && !req->length && !hs_ep->dir_in && + dwc2->ep0_state == DWC2_EP0_DATA_OUT) + hs_ep->dir_in = 1; + + if (first) { + if (!hs_ep->isochronous) { + dwc2_gadget_start_req(dwc2, hs_ep, hs_req, false); + return 0; + } + + /* Update current frame number value. */ + dwc2->frame_number = dwc2_read_frameno(dwc2); + while (dwc2_gadget_target_frame_elapsed(hs_ep)) { + dwc2_gadget_incr_frame_num(hs_ep); + /* Update current frame number value once more as it + * changes here. + */ + dwc2->frame_number = dwc2_read_frameno(dwc2); + } + + if (hs_ep->target_frame != TARGET_FRAME_INITIAL) + dwc2_gadget_start_req(dwc2, hs_ep, hs_req, false); + } + return 0; +} + +static int dwc2_ep_dequeue(struct usb_ep *ep, struct usb_request *req) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + dwc2_warn(dwc2, "%s\n", __func__); + return -EOPNOTSUPP; +} + +static int dwc2_ep_set_halt(struct usb_ep *ep, int value) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + dwc2_warn(dwc2, "%s\n", __func__); + return -EOPNOTSUPP; +} + +static int dwc2_ep_set_wedge(struct usb_ep *ep) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + dwc2_warn(dwc2, "%s\n", __func__); + return -EOPNOTSUPP; +} + +static int dwc2_ep_fifo_status(struct usb_ep *ep) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + dwc2_warn(dwc2, "%s\n", __func__); + return -EOPNOTSUPP; +} + +static void dwc2_ep_fifo_flush(struct usb_ep *ep) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + dwc2_warn(dwc2, "%s\n", __func__); +} + +static const struct usb_ep_ops dwc2_ep_ops = { + .enable = dwc2_ep_enable, + .disable = dwc2_ep_disable, + .alloc_request = dwc2_ep_alloc_req, + .free_request = dwc2_ep_free_req, + .queue = dwc2_ep_queue, + .dequeue = dwc2_ep_dequeue, + .set_halt = dwc2_ep_set_halt, + .set_wedge = dwc2_ep_set_wedge, + .fifo_status = dwc2_ep_fifo_status, + .fifo_flush = dwc2_ep_fifo_flush, +}; + +static void dwc2_program_zlp(struct dwc2 *dwc2, struct dwc2_ep *hs_ep) +{ + u32 ctrl; + u8 index = hs_ep->epnum; + u32 epctl_reg = hs_ep->dir_in ? DIEPCTL(index) : DOEPCTL(index); + u32 epsiz_reg = hs_ep->dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index); + + if (hs_ep->dir_in) + dwc2_dbg(dwc2, "Sending zero-length packet on ep%d\n", index); + else + dwc2_dbg(dwc2, "Receiving zero-length packet on ep%d\n", index); + + dwc2_writel(dwc2, DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) | + DXEPTSIZ_XFERSIZE(0), + epsiz_reg); + + ctrl = dwc2_readl(dwc2, epctl_reg); + ctrl |= DXEPCTL_CNAK; /* clear NAK set by core */ + ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */ + ctrl |= DXEPCTL_USBACTEP; + dwc2_writel(dwc2, ctrl, epctl_reg); +} + +/** + * windex_to_ep - convert control wIndex value to endpoint + * @dwc2: The driver state. + * @windex: The control request wIndex field (in host order). + * + * Convert the given wIndex into a pointer to an driver endpoint + * structure, or return NULL if it is not a valid endpoint. + */ +static struct dwc2_ep *windex_to_ep(struct dwc2 *dwc2, u16 windex) +{ + struct dwc2_ep *ep; + int dir = (windex & USB_DIR_IN) ? 1 : 0; + int idx = windex & 0x7F; + + if (windex >= 0x100) + return NULL; + + if (idx >= dwc2->num_eps) + return NULL; + + ep = index_to_ep(dwc2, idx, dir); + + if (idx && ep->dir_in != dir) + return NULL; + + return ep; +} + +/** + * dwc2_hsotg_send_reply - send reply to control request + * @dwc2: The device state + * @buff: Buffer for request + * @length: Length of reply. + * + * Create a request and queue it on the given endpoint. This is useful as + * an internal method of sending replies to certain control requests, etc. + */ +static int dwc2_hsotg_send_reply(struct dwc2 *dwc2, void *buff, int length) +{ + struct dwc2_ep *ep0 = dwc2->eps_out[0]; + struct usb_request *req; + int ret; + + if (length == 0 && (dwc2->ep0_state == DWC2_EP0_STATUS_IN || + dwc2->ep0_state == DWC2_EP0_STATUS_OUT)) { + dwc2_program_zlp(dwc2, ep0); + return 0; + } + + dwc2_dbg(dwc2, "%s: buff %p, len %d\n", __func__, buff, length); + + req = dwc2_ep_alloc_req(&ep0->ep); + if (!req) { + dwc2_warn(dwc2, "%s: cannot alloc req\n", __func__); + return -ENOMEM; + } + + req->buf = dwc2->ep0_buff; + req->length = length; + /* + * zero flag is for sending zlp in DATA IN stage. It has no impact on + * STATUS stage. + */ + req->zero = 0; + req->complete = dwc2_ep_free_req; + + if (length) + memcpy(req->buf, buff, length); + + ret = dwc2_ep_queue(&ep0->ep, req); + if (ret) { + dwc2_warn(dwc2, "%s: cannot queue req\n", __func__); + return ret; + } + + return 0; +} + +/** + * dwc2_process_req_status - process request GET_STATUS + * @dwc2: The device state + * @ctrl: USB control request + */ +static int dwc2_process_req_status(struct dwc2 *dwc2, + struct usb_ctrlrequest *ctrl) +{ + struct dwc2_ep *ep0 = dwc2->eps_out[0]; + struct dwc2_ep *ep; + __le16 reply; + u16 status; + int ret; + + dwc2_dbg(dwc2, "%s: USB_REQ_GET_STATUS\n", __func__); + + if (!ep0->dir_in) { + dwc2_warn(dwc2, "%s: direction out?\n", __func__); + return -EINVAL; + } + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + status = dwc2->is_selfpowered << USB_DEVICE_SELF_POWERED; + status |= 0 << USB_DEVICE_REMOTE_WAKEUP; + + reply = cpu_to_le16(status); + break; + + case USB_RECIP_INTERFACE: + /* currently, the data result should be zero */ + reply = cpu_to_le16(0); + break; + + case USB_RECIP_ENDPOINT: + ep = windex_to_ep(dwc2, le16_to_cpu(ctrl->wIndex)); + if (!ep) + return -ENOENT; + + reply = cpu_to_le16(ep->halted ? 1 : 0); + break; + + default: + return 0; + } + + if (le16_to_cpu(ctrl->wLength) != 2) + return -EINVAL; + + ret = dwc2_hsotg_send_reply(dwc2, &reply, 2); + if (ret) { + dwc2_err(dwc2, "%s: failed to send reply\n", __func__); + return ret; + } + + return 1; +} + +/** + * dwc2_process_req_feature - process request {SET,CLEAR}_FEATURE + * @dwc2: The device state + * @ctrl: USB control request + */ +static int dwc2_process_req_feature(struct dwc2 *dwc2, + struct usb_ctrlrequest *ctrl) +{ + struct dwc2_request *hs_req; + bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE); + struct dwc2_ep *ep; + int ret; + bool halted; + u32 recip; + u16 wValue; + u16 wIndex; + + dwc2_dbg(dwc2, "%s: %s_FEATURE\n", __func__, set ? "SET" : "CLEAR"); + + wValue = le16_to_cpu(ctrl->wValue); + wIndex = le16_to_cpu(ctrl->wIndex); + recip = ctrl->bRequestType & USB_RECIP_MASK; + + switch (recip) { + case USB_RECIP_DEVICE: + switch (wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + case USB_DEVICE_TEST_MODE: + /* Not supported */ + default: + return -ENOENT; + } + break; + + case USB_RECIP_ENDPOINT: + ep = windex_to_ep(dwc2, wIndex); + if (!ep) + return -ENOENT; + + switch (wValue) { + case USB_ENDPOINT_HALT: + halted = ep->halted; + + dwc2_ep_set_halt(&ep->ep, set); + + ret = dwc2_hsotg_send_reply(dwc2, NULL, 0); + if (ret) { + dwc2_err(dwc2, + "%s: failed to send reply\n", __func__); + return ret; + } + + /* + * we have to complete all requests for ep if it was + * halted, and the halt was cleared by CLEAR_FEATURE + */ + + if (!set && halted) { + /* + * If we have request in progress, + * then complete it + */ + if (ep->req) { + hs_req = ep->req; + ep->req = NULL; + list_del_init(&hs_req->queue); + if (hs_req->req.complete) { + spin_unlock(&dwc2->lock); + hs_req->req.complete( + &ep->ep, &hs_req->req); + spin_lock(&dwc2->lock); + } + } + + /* If we have pending request, then start it */ + if (!ep->req) + dwc2_start_next_request(ep); + } + + break; + + default: + return -ENOENT; + } + break; + default: + return -ENOENT; + } + return 1; +} + +static int dwc2_gadget_get_frame(struct usb_gadget *gadget) +{ + return dwc2_read_frameno(to_dwc2(gadget)); +} + +static void dwc2_core_disconnect(struct dwc2 *dwc2) +{ + u32 dctl; + + dctl = dwc2_readl(dwc2, DCTL); + /* set the soft-disconnect bit */ + dctl |= DCTL_SFTDISCON; + dwc2_writel(dwc2, dctl, DCTL); +} + +static void dwc2_core_connect(struct dwc2 *dwc2) +{ + u32 dctl; + + dctl = dwc2_readl(dwc2, DCTL); + /* clear the soft-disconnect bit */ + dctl &= ~DCTL_SFTDISCON; + dwc2_writel(dwc2, dctl, DCTL); +} + +static void dwc2_enqueue_setup(struct dwc2 *dwc2); + +/** + * dwc2_stall_ep0 - stall ep0 + * @dwc2: The device state + * + * Set stall for ep0 as response for setup request. + */ +static void dwc2_stall_ep0(struct dwc2 *dwc2) +{ + struct dwc2_ep *ep0 = dwc2->eps_out[0]; + u32 reg; + u32 ctrl; + + dwc2_dbg(dwc2, "ep0 stall (dir=%d)\n", ep0->dir_in); + reg = (ep0->dir_in) ? DIEPCTL0 : DOEPCTL0; + + /* + * DxEPCTL_Stall will be cleared by EP once it has + * taken effect, so no need to clear later. + */ + + ctrl = dwc2_readl(dwc2, reg); + ctrl |= DXEPCTL_STALL; + ctrl |= DXEPCTL_CNAK; + dwc2_writel(dwc2, ctrl, reg); + + dwc2_dbg(dwc2, + "written DXEPCTL=0x%08x to %08x (DXEPCTL=0x%08x)\n", + ctrl, reg, dwc2_readl(dwc2, reg)); + + /* + * complete won't be called, so we enqueue + * setup request here + */ + dwc2_enqueue_setup(dwc2); +} + +/** + * dwc2_process_control - process a control request + * @dwc2: The device state + * @ctrl: The control request received + * + * The controller has received the SETUP phase of a control request, and + * needs to work out what to do next (and whether to pass it on to the + * gadget driver). + */ +static void dwc2_process_control(struct dwc2 *dwc2, + struct usb_ctrlrequest *ctrl) +{ + struct dwc2_ep *ep0 = dwc2->eps_out[0]; + int handled = false; + int ret = 0; + + dwc2_dbg(dwc2, + "ctrl Type=%02x, Req=%02x, V=%04x, I=%04x, L=%04x\n", + ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, + ctrl->wIndex, ctrl->wLength); + + if (ctrl->wLength == 0) { + ep0->dir_in = 1; + dwc2->ep0_state = DWC2_EP0_STATUS_IN; + } else if (ctrl->bRequestType & USB_DIR_IN) { + ep0->dir_in = 1; + dwc2->ep0_state = DWC2_EP0_DATA_IN; + } else { + ep0->dir_in = 0; + dwc2->ep0_state = DWC2_EP0_DATA_OUT; + } + + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + switch (ctrl->bRequest) { + case USB_REQ_SET_ADDRESS: + dwc2->connected = 1; + dwc2_dcfg_set_addr(dwc2, le16_to_cpu(ctrl->wValue)); + ret = dwc2_hsotg_send_reply(dwc2, NULL, 0); + handled = true; + dwc2_info(dwc2, "new address %d\n", ctrl->wValue); + break; + + case USB_REQ_GET_STATUS: + ret = dwc2_process_req_status(dwc2, ctrl); + handled = true; + break; + + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + ret = dwc2_process_req_feature(dwc2, ctrl); + handled = true; + break; + } + } + + /* as a fallback, try delivering it to the driver to deal with */ + if (!handled && dwc2->driver) { + spin_unlock(&dwc2->lock); + ret = dwc2->driver->setup(&dwc2->gadget, ctrl); + spin_lock(&dwc2->lock); + if (ret < 0) + dwc2_dbg(dwc2, "driver->setup() ret %d\n", ret); + } + + /* + * the request is either unhandlable, or is not formatted correctly + * so respond with a STALL for the status stage to indicate failure. + */ + + if (ret < 0) { + dwc2_dbg(dwc2, "unhandled ctrl request "); + dwc2_stall_ep0(dwc2); + } +} + +/** + * dwc2_complete_setup - completion of a setup transfer + * @ep: The endpoint the request was on. + * @req: The request completed. + * + * Called on completion of any requests the driver itself submitted for + * EP0 setup packets + */ +static void dwc2_complete_setup(struct usb_ep *ep, struct usb_request *req) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + + if (req->status < 0) { + dwc2_dbg(dwc2, "%s: failed %d\n", __func__, req->status); + return; + } + + spin_lock(&dwc2->lock); + if (req->actual == 0) + dwc2_enqueue_setup(dwc2); + else + dwc2_process_control(dwc2, req->buf); + spin_unlock(&dwc2->lock); +} + +static void dwc2_enqueue_setup(struct dwc2 *dwc2) +{ + struct usb_request *req = dwc2->ctrl_req; + struct dwc2_request *hs_req = our_req(req); + int ret; + + dwc2_dbg(dwc2, "%s: queueing setup request\n", __func__); + + req->zero = 0; + req->length = 8; + req->buf = dwc2->ctrl_buff; + req->complete = dwc2_complete_setup; + + if (!list_empty(&hs_req->queue)) { + dwc2_dbg(dwc2, "%s already queued???\n", __func__); + return; + } + + dwc2->eps_out[0]->dir_in = 0; + dwc2->eps_out[0]->send_zlp = 0; + dwc2->ep0_state = DWC2_EP0_SETUP; + + ret = dwc2_ep_queue(&dwc2->eps_out[0]->ep, req); + if (ret < 0) { + dwc2_err(dwc2, "%s: failed queue (%d)\n", __func__, ret); + /* + * Don't think there's much we can do other than watch the + * driver fail. + */ + } +} + +/** + * dwc2_complete_request - complete a request given to us + * @dwc2: The device state. + * @hs_ep: The endpoint the request was on. + * @hs_req: The request to complete. + * @result: The result code (0 => Ok, otherwise errno) + * + * The given request has finished, so call the necessary completion + * if it has one and then look to see if we can start a new request + * on the endpoint. + * + * Note, expects the ep to already be locked as appropriate. + */ +static void dwc2_complete_request(struct dwc2 *dwc2, struct dwc2_ep *hs_ep, + struct dwc2_request *hs_req, int status) +{ + if (!hs_req) { + dwc2_dbg(dwc2, "%s: nothing to complete?\n", __func__); + return; + } + + dwc2_dbg(dwc2, "complete: ep %p %s, req %p, %d => %p\n", + hs_ep, hs_ep->ep.name, hs_req, status, hs_req->req.complete); + + /* + * only replace the status if we've not already set an error + * from a previous transaction + */ + + if (hs_req->req.status == -EINPROGRESS) + hs_req->req.status = status; + + dma_unmap_single(dwc2->dev, hs_req->req.dma, hs_req->req.length, + hs_ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + hs_ep->req = NULL; + list_del_init(&hs_req->queue); + + /* + * call the complete request with the locks off, just in case the + * request tries to queue more work for this endpoint. + */ + + if (hs_req->req.complete) { + spin_unlock(&dwc2->lock); + hs_req->req.complete(&hs_ep->ep, &hs_req->req); + spin_lock(&dwc2->lock); + } + + /* + * Look to see if there is anything else to do. Note, the completion + * of the previous request may have caused a new request to be started + * so be careful when doing this. + */ + dwc2_dbg(dwc2, "%s: req %p, status %d\n", __func__, hs_ep->req, status); + if (!hs_ep->req && status >= 0) + dwc2_start_next_request(hs_ep); +} + +/** + * dwc2_handle_outdone - handle receiving OutDone/SetupDone from RXFIFO + * @dwc2: The device instance + * @epnum: The endpoint received from + * + * The RXFIFO has delivered an OutDone event, which means that the data + * transfer for an OUT endpoint has been completed, either by a short + * packet or by the finish of a transfer. + */ +static void dwc2_handle_outdone(struct dwc2 *dwc2, int epnum) +{ + u32 epsize = dwc2_readl(dwc2, DOEPTSIZ(epnum)); + struct dwc2_ep *hs_ep = dwc2->eps_out[epnum]; + struct dwc2_request *hs_req = hs_ep->req; + struct usb_request *req = &hs_req->req; + unsigned int size_left = DXEPTSIZ_XFERSIZE_GET(epsize); + int result = 0; + + if (!hs_req) { + dwc2_dbg(dwc2, "%s: no request active\n", __func__); + return; + } + + if (epnum == 0 && dwc2->ep0_state == DWC2_EP0_STATUS_OUT) { + dwc2_dbg(dwc2, "zlp packet received\n"); + dwc2_complete_request(dwc2, hs_ep, hs_req, 0); + dwc2_enqueue_setup(dwc2); + return; + } + + if (using_dma(dwc2)) { + unsigned int size_done; + + /* + * Calculate the size of the transfer by checking how much + * is left in the endpoint size register and then working it + * out from the amount we loaded for the transfer. + * + * We need to do this as DMA pointers are always 32bit aligned + * so may overshoot/undershoot the transfer. + */ + size_done = hs_ep->size_loaded - size_left; + size_done += hs_ep->last_load; + + req->actual = size_done; + } + + if (req->actual < req->length && size_left == 0) { + dwc2_gadget_start_req(dwc2, hs_ep, hs_req, true); + return; + } + + if (req->actual < req->length && req->short_not_ok) { + dwc2_dbg(dwc2, "%s: got %d/%d (short not ok) => error\n", + __func__, req->actual, req->length); + + /* + * todo - what should we return here? there's no one else + * even bothering to check the status. + */ + } + + if (epnum == 0 && dwc2->ep0_state == DWC2_EP0_DATA_OUT) { + /* Move to STATUS IN */ + dwc2->eps_out[0]->dir_in = 1; + dwc2->ep0_state = DWC2_EP0_STATUS_IN; + dwc2_program_zlp(dwc2, dwc2->eps_out[0]); + } + + /* + * Slave mode OUT transfers do not go through XferComplete so + * adjust the ISOC parity here. + */ +#if 0 + if (!using_dma(dwc2)) { + if (hs_ep->isochronous && hs_ep->interval == 1) + dwc2_hsotg_change_ep_iso_parity(dwc2, DOEPCTL(epnum)); + else if (hs_ep->isochronous && hs_ep->interval > 1) + dwc2_gadget_incr_frame_num(hs_ep); + } + + /* Set actual frame number for completed transfers */ + if (hs_ep->isochronous) + req->frame_number = dwc2->frame_number; +#endif + + dwc2_complete_request(dwc2, hs_ep, hs_req, result); +} + +/** + * kill_all_requests - remove all requests from the endpoint's queue + * @dwc2: The device state. + * @ep: The endpoint the requests may be on. + * @result: The result code to use. + * + * Go through the requests on the given endpoint and mark them + * completed with the given result code. + */ +static void kill_all_requests(struct dwc2 *dwc2, struct dwc2_ep *ep, int result) +{ + struct dwc2_request *req, *treq; + unsigned int size; + + ep->req = NULL; + + list_for_each_entry_safe(req, treq, &ep->queue, queue) + dwc2_complete_request(dwc2, ep, req, result); + + if (!dwc2->dedicated_fifos) + return; + size = (dwc2_readl(dwc2, DTXFSTS(ep->fifo_index)) & 0xffff) * 4; + if (size < ep->fifo_size) + dwc2_flush_tx_fifo(dwc2, ep->fifo_index); +} + +/** + * dwc2_gadget_disconnect - disconnect service + * @dwc2: The device state. + * + * The device has been disconnected. Remove all current + * transactions and signal the gadget driver that this + * has happened. + */ +static void dwc2_gadget_disconnect(struct dwc2 *dwc2) +{ + + unsigned int ep; + + if (!dwc2->connected) + return; + + dwc2->connected = 0; + + /* all endpoints should be shutdown */ + for (ep = 0; ep < dwc2->num_eps; ep++) { + if (dwc2->eps_in[ep]) + kill_all_requests(dwc2, dwc2->eps_in[ep], -ESHUTDOWN); + if (dwc2->eps_out[ep]) + kill_all_requests(dwc2, dwc2->eps_out[ep], -ESHUTDOWN); + } + + spin_unlock(&dwc2->lock); + dwc2->driver->disconnect(&dwc2->gadget); + spin_lock(&dwc2->lock); + + usb_gadget_set_state(&dwc2->gadget, USB_STATE_NOTATTACHED); +} + +static void dwc2_gadget_setup_fifo(struct dwc2 *dwc2) +{ + unsigned int ep; + unsigned int addr; + u32 np_tx_fifo_size = dwc2->params.g_np_tx_fifo_size; + u32 rx_fifo_size = dwc2->params.g_rx_fifo_size; + u32 fifo_size = dwc2->hw_params.total_fifo_size; + u32 *txfsz = dwc2->params.g_tx_fifo_size; + u32 size, val; + + /* Reset fifo map if not correctly cleared during previous session */ + WARN_ON(dwc2->fifo_map); + dwc2->fifo_map = 0; + + /* set RX/NPTX FIFO sizes */ + dwc2_writel(dwc2, rx_fifo_size, GRXFSIZ); + size = rx_fifo_size << FIFOSIZE_STARTADDR_SHIFT; + size |= np_tx_fifo_size << FIFOSIZE_DEPTH_SHIFT; + dwc2_writel(dwc2, size, GNPTXFSIZ); + + /* + * arange all the rest of the TX FIFOs, as some versions of this + * block have overlapping default addresses. This also ensures + * that if the settings have been changed, then they are set to + * known values. + */ + + /* start at the end of the GNPTXFSIZ, rounded up */ + addr = rx_fifo_size + np_tx_fifo_size; + + /* + * Configure fifos sizes from provided configuration and assign + * them to endpoints dynamically according to maxpacket size value of + * given endpoint. + */ + for (ep = 1; ep < DWC2_MAX_EPS_CHANNELS; ep++) { + if (!txfsz[ep]) + continue; + val = addr; + val |= txfsz[ep] << FIFOSIZE_DEPTH_SHIFT; + WARN_ONCE(addr + txfsz[ep] > fifo_size, + "insufficient fifo memory"); + addr += txfsz[ep]; + + dwc2_writel(dwc2, val, DPTXFSIZN(ep)); + val = dwc2_readl(dwc2, DPTXFSIZN(ep)); + } + + dwc2_writel(dwc2, dwc2->hw_params.total_fifo_size | + addr << GDFIFOCFG_EPINFOBASE_SHIFT, + GDFIFOCFG); + + dwc2_flush_all_fifo(dwc2); +} + +static void dwc2_gadget_interrupt_init(struct dwc2 *dwc2) +{ + /* unmask subset of endpoint interrupts */ + dwc2_writel(dwc2, DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK | + DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK, + DIEPMSK); + + dwc2_writel(dwc2, DOEPMSK_SETUPMSK | DOEPMSK_AHBERRMSK | + DOEPMSK_EPDISBLDMSK | DOEPMSK_XFERCOMPLMSK, + DOEPMSK); + + dwc2_writel(dwc2, 0, DAINTMSK); +} + +/** + * dwc2_complete_in - complete IN transfer + * @dwc2: The device state. + * @hs_ep: The endpoint that has just completed. + * + * An IN transfer has been completed, update the transfer's state and then + * call the relevant completion routines. + */ +static void dwc2_complete_in(struct dwc2 *dwc2, struct dwc2_ep *hs_ep) +{ + struct dwc2_request *hs_req = hs_ep->req; + u32 epsize = dwc2_readl(dwc2, DIEPTSIZ(hs_ep->epnum)); + int size_left, size_done; + + if (!hs_req) { + dwc2_dbg(dwc2, "XferCompl but no req\n"); + return; + } + + /* Finish ZLP handling for IN EP0 transactions */ + if (hs_ep->epnum == 0 && dwc2->ep0_state == DWC2_EP0_STATUS_IN) { + dwc2_dbg(dwc2, "zlp packet sent\n"); + /* + * While send zlp for DWC2_EP0_STATUS_IN EP direction was + * changed to IN. Change back to complete OUT transfer request + */ + hs_ep->dir_in = 0; + dwc2_complete_request(dwc2, hs_ep, hs_req, 0); + dwc2_enqueue_setup(dwc2); + return; + } + + /* + * Calculate the size of the transfer by checking how much is left + * in the endpoint size register and then working it out from + * the amount we loaded for the transfer. + * + * We do this even for DMA, as the transfer may have incremented + * past the end of the buffer (DMA transfers are always 32bit + * aligned). + */ + size_left = DXEPTSIZ_XFERSIZE_GET(epsize); + + size_done = hs_ep->size_loaded - size_left; + size_done += hs_ep->last_load; + + if (hs_req->req.actual != size_done) + dwc2_dbg(dwc2, "%s: adjusting size done %d => %d\n", + __func__, hs_req->req.actual, size_done); + + hs_req->req.actual = size_done; + dwc2_dbg(dwc2, "req->length:%d req->actual:%d req->zero:%d\n", + hs_req->req.length, hs_req->req.actual, hs_req->req.zero); + + if (!size_left && hs_req->req.actual < hs_req->req.length) { + dwc2_dbg(dwc2, "%s trying more for req...\n", __func__); + dwc2_gadget_start_req(dwc2, hs_ep, hs_req, true); + return; + } + + /* Zlp for all endpoints, for ep0 only in DATA IN stage */ + if (hs_ep->send_zlp) { + dwc2_program_zlp(dwc2, hs_ep); + hs_ep->send_zlp = 0; + /* transfer will be completed on next complete interrupt */ + return; + } + + if (hs_ep->epnum == 0 && dwc2->ep0_state == DWC2_EP0_DATA_IN) { + /* Move to STATUS OUT */ + dwc2->eps_out[0]->dir_in = 0; + dwc2->ep0_state = DWC2_EP0_STATUS_OUT; + dwc2_program_zlp(dwc2, dwc2->eps_out[0]); + return; + } + + dwc2_complete_request(dwc2, hs_ep, hs_req, 0); +} + +/** + * dwc2_core_gadget_init - issue softreset to the core + * @dwc2: The device state + * @usb_reset: Usb resetting flag + * + * Issue a soft reset to the core, and await the core finishing it. + */ +static void dwc2_core_gadget_init(struct dwc2 *dwc2, bool usb_reset) +{ + u32 intmsk; + u32 dctl; + u32 usbcfg; + u32 dcfg; + int ep; + + /* Kill any ep0 requests as controller will be reinitialized */ + kill_all_requests(dwc2, dwc2->eps_out[0], -ECONNRESET); + + if (!usb_reset) { + if (dwc2_core_reset(dwc2)) + return; + } else { + /* all endpoints should be shutdown */ + for (ep = 1; ep < dwc2->num_eps; ep++) { + if (dwc2->eps_in[ep]) + dwc2_ep_disable(&dwc2->eps_in[ep]->ep); + if (dwc2->eps_out[ep]) + dwc2_ep_disable(&dwc2->eps_out[ep]->ep); + } + } + + /* + * we must now enable ep0 ready for host detection and then + * set configuration. + */ + + /* keep other bits untouched (so e.g. forced modes are not lost) */ + usbcfg = dwc2_readl(dwc2, GUSBCFG); + usbcfg &= ~GUSBCFG_TOUTCAL_MASK; + usbcfg |= GUSBCFG_TOUTCAL(7); + dwc2_writel(dwc2, usbcfg, GUSBCFG); + + dwc2_phy_init(dwc2, true); + + dwc2_gadget_setup_fifo(dwc2); + + if (!usb_reset) + dwc2_core_disconnect(dwc2); + + dcfg = DCFG_EPMISCNT(1); + + if (dwc2->params.speed == DWC2_SPEED_PARAM_LOW) { + dcfg |= DCFG_DEVSPD_LS; + } else if (dwc2->params.speed == DWC2_SPEED_PARAM_FULL && + dwc2->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) { + dcfg |= DCFG_DEVSPD_FS48; + } else if (dwc2->params.speed == DWC2_SPEED_PARAM_FULL && + dwc2->params.phy_type != DWC2_PHY_TYPE_PARAM_FS) { + dcfg |= DCFG_DEVSPD_FS; + } + + if (dwc2->params.ipg_isoc_en) + dcfg |= DCFG_IPG_ISOC_SUPPORDED; + + dwc2_writel(dwc2, dcfg, DCFG); + + /* Clear any pending OTG interrupts */ + dwc2_writel(dwc2, 0xffffffff, GOTGINT); + + /* Clear any pending interrupts */ + dwc2_writel(dwc2, 0xffffffff, GINTSTS); + intmsk = GINTSTS_ERLYSUSP | + GINTSTS_GOUTNAKEFF | GINTSTS_GINNAKEFF | + GINTSTS_USBRST | GINTSTS_RESETDET | + GINTSTS_ENUMDONE | + GINTSTS_USBSUSP | GINTSTS_WKUPINT | + GINTSTS_LPMTRANRCVD; + + intmsk |= GINTSTS_INCOMPL_SOIN | GINTSTS_INCOMPL_SOOUT; + + /* enable in and out endpoint interrupts */ + intmsk |= GINTSTS_OEPINT | GINTSTS_IEPINT; + + /* + * Enable the RXFIFO when in slave mode, as this is how we collect + * the data. In DMA mode, we get events from the FIFO but also + * things we cannot process, so do not use it. + */ + if (!using_dma(dwc2)) + intmsk |= GINTSTS_RXFLVL; + + if (!dwc2->params.external_id_pin_ctl) + intmsk |= GINTSTS_CONIDSTSCHNG; + + dwc2_writel(dwc2, intmsk, GINTMSK); + + dwc2_gahbcfg_init(dwc2); + + /* + * If INTknTXFEmpMsk is enabled, it's important to disable ep interrupts + * when we have no data to transfer. Otherwise we get being flooded by + * interrupts. + */ + intmsk = DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK | + DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK; + + if (dwc2->dedicated_fifos && !using_dma(dwc2)) + intmsk |= DIEPMSK_TXFIFOEMPTY | DIEPMSK_INTKNTXFEMPMSK; + + dwc2_writel(dwc2, intmsk, DIEPMSK); + + /* + * don't need XferCompl, we get that from RXFIFO in slave mode. In + * DMA mode we may need this and StsPhseRcvd. + */ + intmsk = DOEPMSK_EPDISBLDMSK | DOEPMSK_AHBERRMSK | DOEPMSK_SETUPMSK; + if (using_dma(dwc2)) + intmsk |= DIEPMSK_XFERCOMPLMSK | DOEPMSK_STSPHSERCVDMSK; + dwc2_writel(dwc2, intmsk, DOEPMSK); + + dwc2_writel(dwc2, 0, DAINTMSK); + + dwc2_dbg(dwc2, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n", + dwc2_readl(dwc2, DIEPCTL0), + dwc2_readl(dwc2, DOEPCTL0)); + + /* Enable interrupts for EP0 in and out */ + dwc2_hsotg_ctrl_epint(dwc2, 0, 0, 1); + dwc2_hsotg_ctrl_epint(dwc2, 0, 1, 1); + dwc2_dbg(dwc2, "DAINTMSK=0x%08x\n", dwc2_readl(dwc2, DAINTMSK)); + + if (!usb_reset) { + dctl = dwc2_readl(dwc2, DCTL); + dwc2_writel(dwc2, dctl | DCTL_PWRONPRGDONE, DCTL); + + udelay(10); /* see openiboot */ + + dctl = dwc2_readl(dwc2, DCTL); + dwc2_writel(dwc2, dctl & ~DCTL_PWRONPRGDONE, DCTL); + } + + dwc2_dbg(dwc2, "DCTL=0x%08x\n", dwc2_readl(dwc2, DCTL)); + + /* + * DxEPCTL_USBActEp says RO in manual, but seems to be set by + * writing to the EPCTL register.. + */ + dwc2_writel(dwc2, dwc2_ep0_mps(dwc2->eps_out[0]->ep.maxpacket) | + DXEPCTL_CNAK | DXEPCTL_EPENA | + DXEPCTL_USBACTEP, DOEPCTL0); + + /* enable, but don't activate EP0in */ + dwc2_writel(dwc2, dwc2_ep0_mps(dwc2->eps_out[0]->ep.maxpacket) | + DXEPCTL_USBACTEP, DIEPCTL0); + + /* clear global NAKs */ + dctl = dwc2_readl(dwc2, DCTL); + dctl |= DCTL_CGOUTNAK | DCTL_CGNPINNAK; + + if (!usb_reset) + dctl |= DCTL_SFTDISCON; + dwc2_writel(dwc2, dctl, DCTL); + + /* must be at-least 3ms to allow bus to see disconnect */ + mdelay(3); +} + +/** + * dwc2_set_selfpowered - set if device is self/bus powered + * @gadget: The usb gadget state + * @is_selfpowered: Whether the device is self-powered + * + * Set if the device is self or bus powered. + */ +static int dwc2_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered) +{ + struct dwc2 *dwc2 = to_dwc2(gadget); + + dwc2->is_selfpowered = !!is_selfpowered; + + return 0; +} + +/** + * dwc2_gadget_pullup - connect/disconnect the USB PHY + * @gadget: The usb gadget state + * @is_on: Current state of the USB PHY + * + * Connect/Disconnect the USB PHY pullup + */ +static int dwc2_gadget_pullup(struct usb_gadget *gadget, int is_on) +{ + struct dwc2 *dwc2 = to_dwc2(gadget); + unsigned long flags = 0; + + dwc2_dbg(dwc2, "%s: is_on: %d\n", __func__, is_on); + + dwc2->enabled = is_on; + /* Don't modify pullup state while in host mode */ + if (dwc2_is_host_mode(dwc2)) { + WARN_ON(dwc2_is_host_mode(dwc2)); + return -EINVAL; + } + + spin_lock_irqsave(&dwc2->lock, flags); + if (is_on) { + dwc2_core_gadget_init(dwc2, false); + /* Enable ACG feature in device mode,if supported */ + dwc2_core_connect(dwc2); + } else { + dwc2_core_disconnect(dwc2); + dwc2_gadget_disconnect(dwc2); + } + + dwc2->gadget.speed = USB_SPEED_UNKNOWN; + spin_unlock_irqrestore(&dwc2->lock, flags); + + return 0; +} + +static int dwc2_gadget_vbus_session(struct usb_gadget *gadget, int is_active) +{ + struct dwc2 *dwc2 = to_dwc2(gadget); + unsigned long flags; + + dwc2_dbg(dwc2, "%s: is_active: %d\n", __func__, is_active); + spin_lock_irqsave(&dwc2->lock, flags); + + if (is_active) { + dwc2_core_gadget_init(dwc2, false); + if (dwc2->enabled) + dwc2_core_connect(dwc2); + } else { + dwc2_core_disconnect(dwc2); + dwc2_gadget_disconnect(dwc2); + } + + spin_unlock_irqrestore(&dwc2->lock, flags); + return 0; +} + + +/** + * dwc2_handle_ep_disabled - handle DXEPINT_EPDISBLD + * @hs_ep: The endpoint on which interrupt is asserted. + * + * This interrupt indicates that the endpoint has been disabled per the + * application's request. + * + * For IN endpoints flushes txfifo, in case of BULK clears DCTL_CGNPINNAK, + * in case of ISOC completes current request. + * + * For ISOC-OUT endpoints completes expired requests. If there is remaining + * request starts it. + */ +static void dwc2_handle_ep_disabled(struct dwc2_ep *hs_ep) +{ + struct dwc2 *dwc2 = hs_ep->dwc2; + struct dwc2_request *hs_req; + unsigned char idx = hs_ep->epnum; + int dir_in = hs_ep->dir_in; + u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx); + int dctl = dwc2_readl(dwc2, DCTL); + + dwc2_dbg(dwc2, "%s: EPDisbld\n", __func__); + + if (dir_in) { + int epctl = dwc2_readl(dwc2, epctl_reg); + + dwc2_flush_tx_fifo(dwc2, hs_ep->fifo_index); + + if (hs_ep->isochronous) { + dwc2_complete_in(dwc2, hs_ep); + return; + } + + if ((epctl & DXEPCTL_STALL) && (epctl & DXEPCTL_EPTYPE_BULK)) { + int dctl = dwc2_readl(dwc2, DCTL); + + dctl |= DCTL_CGNPINNAK; + dwc2_writel(dwc2, dctl, DCTL); + } + return; + } + + if (dctl & DCTL_GOUTNAKSTS) { + dctl |= DCTL_CGOUTNAK; + dwc2_writel(dwc2, dctl, DCTL); + } + + if (!hs_ep->isochronous) + return; + + if (list_empty(&hs_ep->queue)) { + dwc2_dbg(dwc2, "%s: complete_ep 0x%p, ep->queue empty!\n", + __func__, hs_ep); + return; + } + + do { + hs_req = get_ep_head(hs_ep); + if (hs_req) + dwc2_complete_request(dwc2, hs_ep, hs_req, -ENODATA); + dwc2_gadget_incr_frame_num(hs_ep); + /* Update current frame number value. */ + dwc2->frame_number = dwc2_read_frameno(dwc2); + } while (dwc2_gadget_target_frame_elapsed(hs_ep)); + + dwc2_start_next_request(hs_ep); +} + +/** + * dwc2_handle_out_token_ep_disabled - handle DXEPINT_OUTTKNEPDIS + * @ep: The endpoint on which interrupt is asserted. + * + * This is starting point for ISOC-OUT transfer, synchronization done with + * first out token received from host while corresponding EP is disabled. + * + * Device does not know initial frame in which out token will come. For this + * HW generates OUTTKNEPDIS - out token is received while EP is disabled. Upon + * getting this interrupt SW starts calculation for next transfer frame. + */ +static void dwc2_handle_out_token_ep_disabled(struct dwc2_ep *ep) +{ + struct dwc2 *dwc2 = ep->dwc2; + int dir_in = ep->dir_in; + u32 doepmsk; + u32 ctrl; + + dwc2_dbg(dwc2, "%s\n", __func__); + + if (dir_in || !ep->isochronous) + return; + + if (ep->interval > 1 && ep->target_frame == TARGET_FRAME_INITIAL) { + ep->target_frame = dwc2->frame_number; + + dwc2_gadget_incr_frame_num(ep); + + ctrl = dwc2_readl(dwc2, DOEPCTL(ep->epnum)); + + if (ep->target_frame & 0x1) + ctrl |= DXEPCTL_SETODDFR; + else + ctrl |= DXEPCTL_SETEVENFR; + + dwc2_writel(dwc2, ctrl, DOEPCTL(ep->epnum)); + } + + dwc2_start_next_request(ep); + + doepmsk = dwc2_readl(dwc2, DOEPMSK); + doepmsk &= ~DOEPMSK_OUTTKNEPDISMSK; + dwc2_writel(dwc2, doepmsk, DOEPMSK); +} + +/** + * dwc2_handle_nak - handle NAK interrupt + * @hs_ep: The endpoint on which interrupt is asserted. + * + * This is starting point for ISOC-IN transfer, synchronization done with + * first IN token received from host while corresponding EP is disabled. + * + * Device does not know when first one token will arrive from host. On first + * token arrival HW generates 2 interrupts: 'in token received while FIFO empty' + * and 'NAK'. NAK interrupt for ISOC-IN means that token has arrived and ZLP was + * sent in response to that as there was no data in FIFO. SW is basing on this + * interrupt to obtain frame in which token has come and then based on the + * interval calculates next frame for transfer. + */ +static void dwc2_handle_nak(struct dwc2_ep *hs_ep) +{ + struct dwc2 *dwc2 = hs_ep->dwc2; + int dir_in = hs_ep->dir_in; + u32 ctrl; + + if (!dir_in || !hs_ep->isochronous) + return; + + if (hs_ep->target_frame == TARGET_FRAME_INITIAL) { + hs_ep->target_frame = dwc2->frame_number; + if (hs_ep->interval > 1) { + ctrl = dwc2_readl(dwc2, DIEPCTL(hs_ep->epnum)); + + if (hs_ep->target_frame & 0x1) + ctrl |= DXEPCTL_SETODDFR; + else + ctrl |= DXEPCTL_SETEVENFR; + + dwc2_writel(dwc2, ctrl, DIEPCTL(hs_ep->epnum)); + } + + dwc2_complete_request(dwc2, hs_ep, get_ep_head(hs_ep), 0); + } + + dwc2_gadget_incr_frame_num(hs_ep); +} + +/** + * dwc2_handle_epint - handle an in/out endpoint interrupt + * @dwc2: The driver state + * @idx: The index for the endpoint (0..15) + * @dir_in: Set if this is an IN endpoint + * + * Process and clear any interrupt pending for an individual endpoint + */ +static void dwc2_handle_epint(struct dwc2 *dwc2, unsigned int idx, + int dir_in) +{ + struct dwc2_ep *hs_ep = index_to_ep(dwc2, idx, dir_in); + u32 epmsk_reg = dir_in ? DIEPMSK : DOEPMSK; + u32 epint_reg = dir_in ? DIEPINT(idx) : DOEPINT(idx); + u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx); + u32 epsiz_reg = dir_in ? DIEPTSIZ(idx) : DOEPTSIZ(idx); + u32 ints; + u32 mask; + u32 ctrl; + + mask = dwc2_readl(dwc2, epmsk_reg); + if ((dwc2_readl(dwc2, DIEPEMPMSK) >> idx) & 1) + mask |= DIEPMSK_TXFIFOEMPTY; + mask |= DXEPINT_SETUP_RCVD; + + ints = dwc2_readl(dwc2, epint_reg); + + if (ints & DXEPINT_AHBERR) + dwc2_err(dwc2, "%s: AHBErr\n", __func__); + + ints &= mask; + + ctrl = dwc2_readl(dwc2, epctl_reg); + + /* Clear endpoint interrupts */ + dwc2_writel(dwc2, ints, epint_reg); + + if (!hs_ep) { + dwc2_err(dwc2, "%s:Interrupt for unconfigured ep%d(%s)\n", + __func__, idx, dir_in ? "in" : "out"); + return; + } + + dwc2_dbg(dwc2, "%s: ep%d(%s) DxEPINT=0x%08x\n", + __func__, idx, dir_in ? "in" : "out", ints); + + /* Don't process XferCompl interrupt if it is a setup packet */ + if (idx == 0 && (ints & (DXEPINT_SETUP | DXEPINT_SETUP_RCVD))) + ints &= ~DXEPINT_XFERCOMPL; + + if (ints & DXEPINT_XFERCOMPL) { + dwc2_dbg(dwc2, + "%s: XferCompl: DxEPCTL=0x%08x, DXEPTSIZ=%08x\n", + __func__, dwc2_readl(dwc2, epctl_reg), + dwc2_readl(dwc2, epsiz_reg)); + if (dir_in) { + /* + * We get OutDone from the FIFO, so we only + * need to look at completing IN requests here + * if operating slave mode + */ + if (hs_ep->isochronous && hs_ep->interval > 1) + dwc2_gadget_incr_frame_num(hs_ep); + + dwc2_complete_in(dwc2, hs_ep); + if (ints & DXEPINT_NAKINTRPT) + ints &= ~DXEPINT_NAKINTRPT; + + if (idx == 0 && !hs_ep->req) + dwc2_enqueue_setup(dwc2); + } else if (using_dma(dwc2)) { + /* + * We're using DMA, we need to fire an OutDone here + * as we ignore the RXFIFO. + */ + if (hs_ep->isochronous && hs_ep->interval > 1) + dwc2_gadget_incr_frame_num(hs_ep); + + dwc2_handle_outdone(dwc2, idx); + } + } + + if (ints & DXEPINT_EPDISBLD) + dwc2_handle_ep_disabled(hs_ep); + + if (ints & DXEPINT_OUTTKNEPDIS) + dwc2_handle_out_token_ep_disabled(hs_ep); + + if (ints & DXEPINT_NAKINTRPT) + dwc2_handle_nak(hs_ep); + + if (ints & DXEPINT_SETUP) { /* Setup or Timeout */ + dwc2_dbg(dwc2, "%s: Setup/Timeout\n", __func__); + + if (using_dma(dwc2) && idx == 0) { + /* + * this is the notification we've received a + * setup packet. In non-DMA mode we'd get this + * from the RXFIFO, instead we need to process + * the setup here. + */ + + if (!dir_in && dwc2->ep0_state == DWC2_EP0_SETUP) + dwc2_handle_outdone(dwc2, 0); + } + } + + if (ints & DXEPINT_STSPHSERCVD) + dwc2_dbg(dwc2, "%s: StsPhseRcvd\n", __func__); + + if (ints & DXEPINT_BACK2BACKSETUP) + dwc2_dbg(dwc2, "%s: B2BSetup/INEPNakEff\n", __func__); + + if (ints & DXEPINT_BNAINTR) { + dwc2_dbg(dwc2, "%s: BNA interrupt\n", __func__); +#if 0 + if (hs_ep->isochronous) + dwc2_handle_isoc_bna(hs_ep); +#endif + } + + if (dir_in && !hs_ep->isochronous) { + /* not sure if this is important, but we'll clear it anyway */ + if (ints & DXEPINT_INTKNTXFEMP) { + dwc2_dbg(dwc2, "%s: ep%d: INTknTXFEmpMsk\n", + __func__, idx); + } + + /* this probably means something bad is happening */ + if (ints & DXEPINT_INTKNEPMIS) { + dwc2_warn(dwc2, "%s: ep%d: INTknEP\n", + __func__, idx); + } + + /* FIFO has space or is empty (see GAHBCFG) */ + if (dwc2->dedicated_fifos && ints & DXEPINT_TXFEMP) { + dwc2_dbg(dwc2, "%s: ep%d: TxFIFOEmpty\n", + __func__, idx); + } + } +} + +/** + * dwc2_handle_enumdone - Handle EnumDone interrupt (enumeration done) + * @dwc2: The device state. + * + * Handle updating the device settings after the enumeration phase has + * been completed. + */ +static void dwc2_handle_enumdone(struct dwc2 *dwc2) +{ + u32 dsts = dwc2_readl(dwc2, DSTS); + int ep0_mps = 0, ep_mps = 8; + int i; + + /* + * This should signal the finish of the enumeration phase + * of the USB handshaking, so we should now know what rate + * we connected at. + */ + dwc2_dbg(dwc2, "EnumDone (DSTS=0x%08x)\n", dsts); + + /* + * note, since we're limited by the size of transfer on EP0, and + * it seems IN transfers must be a even number of packets we do + * not advertise a 64byte MPS on EP0. + */ + + /* catch both EnumSpd_FS and EnumSpd_FS48 */ + switch ((dsts & DSTS_ENUMSPD_MASK) >> DSTS_ENUMSPD_SHIFT) { + case DSTS_ENUMSPD_FS: + case DSTS_ENUMSPD_FS48: + dwc2->gadget.speed = USB_SPEED_FULL; + ep0_mps = 64; + ep_mps = 1023; + break; + case DSTS_ENUMSPD_HS: + dwc2->gadget.speed = USB_SPEED_HIGH; + ep0_mps = 64; + ep_mps = 1024; + break; + case DSTS_ENUMSPD_LS: + dwc2->gadget.speed = USB_SPEED_LOW; + ep0_mps = 8; + ep_mps = 8; + /* + * note, we don't actually support LS in this driver at the + * moment, and the documentation seems to imply that it isn't + * supported by the PHYs on some of the devices. + */ + break; + } + dwc2_dbg(dwc2, "new %s device\n", usb_speed_string(dwc2->gadget.speed)); + + /* + * we should now know the maximum packet size for an + * endpoint, so set the endpoints to a default value. + */ + if (ep0_mps) { + /* Initialize ep0 for both in and out directions */ + dwc2_set_ep_maxpacket(dwc2, 0, ep0_mps, 0, 1); + dwc2_set_ep_maxpacket(dwc2, 0, ep0_mps, 0, 0); + for (i = 1; i < dwc2->num_eps; i++) { + if (dwc2->eps_in[i]) + dwc2_set_ep_maxpacket(dwc2, i, ep_mps, 0, 1); + if (dwc2->eps_out[i]) + dwc2_set_ep_maxpacket(dwc2, i, ep_mps, 0, 0); + } + } + + /* ensure after enumeration our EP0 is active */ + dwc2_enqueue_setup(dwc2); + + dwc2_dbg(dwc2, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n", + dwc2_readl(dwc2, DIEPCTL0), + dwc2_readl(dwc2, DOEPCTL0)); +} + +/* IRQ flags which will trigger a retry around the IRQ loop */ +#define IRQ_RETRY_MASK (GINTSTS_NPTXFEMP | \ + GINTSTS_PTXFEMP | \ + GINTSTS_RXFLVL) + +static void dwc2_gadget_udc_poll(struct usb_gadget *gadget) +{ + struct dwc2 *dwc2 = to_dwc2(gadget); + int retry_count = 4; + u32 gintsts; + u32 gintmsk; + + if (!dwc2_is_device_mode(dwc2)) + return; + + spin_lock(&dwc2->lock); + +irq_retry: + gintsts = readl(dwc2->regs + GINTSTS); + gintmsk = readl(dwc2->regs + GINTMSK); + gintsts &= gintmsk; + + if (!gintsts) + return; + + dwc2_dbg(dwc2, "%s: %08x (%08x) retry %d\n", + __func__, gintsts, gintmsk, 4 - retry_count); + + if (gintsts & GINTSTS_RESETDET) { + dwc2_dbg(dwc2, "%s: USBRstDet\n", __func__); + dwc2_writel(dwc2, GINTSTS_RESETDET, GINTSTS); + } + + if (gintsts & (GINTSTS_USBRST | GINTSTS_RESETDET)) { + u32 usb_status = dwc2_readl(dwc2, GOTGCTL); + u32 connected = dwc2->connected; + + dwc2_dbg(dwc2, "%s: USBRst\n", __func__); + dwc2_dbg(dwc2, "GNPTXSTS=%08x\n", dwc2_readl(dwc2, GNPTXSTS)); + + dwc2_writel(dwc2, GINTSTS_USBRST, GINTSTS); + + /* Report disconnection if it is not already done. */ + dwc2_gadget_disconnect(dwc2); + + /* Reset device address to zero */ + dwc2_dcfg_set_addr(dwc2, 0); + + if (usb_status & GOTGCTL_BSESVLD && connected) + dwc2_core_gadget_init(dwc2, true); + } + + if (gintsts & GINTSTS_ENUMDONE) { + dwc2_writel(dwc2, GINTSTS_ENUMDONE, GINTSTS); + + dwc2_handle_enumdone(dwc2); + } + + if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) { + u32 daint = dwc2_readl(dwc2, DAINT); + u32 daintmsk = dwc2_readl(dwc2, DAINTMSK); + u32 daint_out, daint_in; + int ep; + + daint &= daintmsk; + daint_out = daint >> DAINT_OUTEP_SHIFT; + daint_in = daint & ~(daint_out << DAINT_OUTEP_SHIFT); + + dwc2_dbg(dwc2, "%s: daint=%08x\n", __func__, daint); + + for (ep = 0; ep < dwc2->num_eps && daint_out; + ep++, daint_out >>= 1) { + if (daint_out & 1) + dwc2_handle_epint(dwc2, ep, 0); + } + + for (ep = 0; ep < dwc2->num_eps && daint_in; + ep++, daint_in >>= 1) { + if (daint_in & 1) + dwc2_handle_epint(dwc2, ep, 1); + } + } + + /* check both FIFOs */ + if (gintsts & GINTSTS_NPTXFEMP) { + dwc2_dbg(dwc2, "NPTxFEmp\n"); + + /* + * Disable the interrupt to stop it happening again + * unless one of these endpoint routines decides that + * it needs re-enabling + */ +#if 0 + dwc2_hsotg_disable_gsint(dwc2, GINTSTS_NPTXFEMP); + dwc2_hsotg_irq_fifoempty(dwc2, false); +#endif + } + + if (gintsts & GINTSTS_PTXFEMP) { + dwc2_dbg(dwc2, "PTxFEmp\n"); + + /* See note in GINTSTS_NPTxFEmp */ +#if 0 + dwc2_hsotg_disable_gsint(dwc2, GINTSTS_PTXFEMP); + dwc2_hsotg_irq_fifoempty(dwc2, true); +#endif + } + + if (gintsts & GINTSTS_RXFLVL) { + /* + * note, since GINTSTS_RxFLvl doubles as FIFO-not-empty, + * we need to retry dwc2_hsotg_handle_rx if this is still + * set. + */ + dwc2_err(dwc2, "RXFLVL\n"); +#if 0 + dwc2_handle_rx(dwc2); +#endif + } + + if (gintsts & GINTSTS_ERLYSUSP) { + dwc2_dbg(dwc2, "GINTSTS_ErlySusp\n"); + dwc2_writel(dwc2, GINTSTS_ERLYSUSP, GINTSTS); + } + if (gintsts & GINTSTS_USBSUSP) { + dwc2_dbg(dwc2, "USBSusp\n"); + dwc2_writel(dwc2, GINTSTS_USBSUSP, GINTSTS); + } + + /* + * these next two seem to crop-up occasionally causing the core + * to shutdown the USB transfer, so try clearing them and logging + * the occurrence. + */ + + if (gintsts & GINTSTS_GOUTNAKEFF) { + u8 idx; + u32 epctrl; + u32 gintmsk; + u32 daintmsk; + struct dwc2_ep *hs_ep; + + daintmsk = dwc2_readl(dwc2, DAINTMSK); + daintmsk >>= DAINT_OUTEP_SHIFT; + /* Mask this interrupt */ + gintmsk = dwc2_readl(dwc2, GINTMSK); + gintmsk &= ~GINTSTS_GOUTNAKEFF; + dwc2_writel(dwc2, gintmsk, GINTMSK); + + dwc2_dbg(dwc2, "GOUTNakEff triggered\n"); + for (idx = 1; idx < dwc2->num_eps; idx++) { + hs_ep = dwc2->eps_out[idx]; + /* Proceed only unmasked ISOC EPs */ + if ((BIT(idx) & ~daintmsk) || !hs_ep->isochronous) + continue; + + epctrl = dwc2_readl(dwc2, DOEPCTL(idx)); + + if (epctrl & DXEPCTL_EPENA) { + epctrl |= DXEPCTL_SNAK; + epctrl |= DXEPCTL_EPDIS; + dwc2_writel(dwc2, epctrl, DOEPCTL(idx)); + } + } + + /* This interrupt bit is cleared in DXEPINT_EPDISBLD handler */ + } + + if (gintsts & GINTSTS_GINNAKEFF) { + dwc2_info(dwc2, "GINNakEff triggered\n"); + dwc2_set_bit(dwc2, DCTL, DCTL_CGNPINNAK); + } +#if 0 + if (gintsts & GINTSTS_INCOMPL_SOIN) + dwc2_handle_incomplete_isoc_in(dwc2); + + if (gintsts & GINTSTS_INCOMPL_SOOUT) + dwc2_handle_incomplete_isoc_out(dwc2); +#endif + + /* + * if we've had fifo events, we should try and go around the + * loop again to see if there's any point in returning yet. + */ + if (gintsts & IRQ_RETRY_MASK && --retry_count > 0) + goto irq_retry; + + spin_unlock(&dwc2->lock); +} + +/** + * dwc2_dwc2_udc_start - prepare the udc for work + * @gadget: The usb gadget state + * @driver: The usb gadget driver + * + * Perform initialization to prepare udc device and driver + * to work. + */ +static int dwc2_gadget_udc_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct dwc2 *dwc2 = to_dwc2(gadget); + + if (!driver) { + dwc2_err(dwc2, "%s: no driver\n", __func__); + return -EINVAL; + } + + if (driver->max_speed < USB_SPEED_FULL) { + dwc2_err(dwc2, "%s: bad speed\n", __func__); + return -EINVAL; + } + + dwc2->driver = driver; + dwc2->gadget.speed = USB_SPEED_UNKNOWN; + + dwc2_core_gadget_init(dwc2, false); + dwc2_gadget_interrupt_init(dwc2); + + dwc2_info(dwc2, "bound driver %s\n", driver->driver.name); + + return 0; +} + +static int dwc2_gadget_udc_stop(struct usb_gadget *gadget) +{ + struct dwc2 *dwc2 = to_dwc2(gadget); + unsigned long flags = 0; + + dwc2_dbg(dwc2, "%s\n", __func__); + + /* all endpoints should be shutdown */ + spin_lock_irqsave(&dwc2->lock, flags); + + dwc2->driver = NULL; + dwc2->gadget.speed = USB_SPEED_UNKNOWN; + dwc2->enabled = 0; + + spin_unlock_irqrestore(&dwc2->lock, flags); + + return 0; +} + +static const struct usb_gadget_ops dwc2_gadget_ops = { + .get_frame = dwc2_gadget_get_frame, + .set_selfpowered = dwc2_set_selfpowered, + .vbus_session = dwc2_gadget_vbus_session, + .vbus_draw = NULL, + .pullup = dwc2_gadget_pullup, + .udc_start = dwc2_gadget_udc_start, + .udc_stop = dwc2_gadget_udc_stop, + .udc_poll = dwc2_gadget_udc_poll, +}; + +/** + * dwc2_gadget_ep_init - initialise a single endpoint + * @dwc2: The device state. + * @epnum: The endpoint number + * @dir_in: True if direction is in. + * + * Initialise the given endpoint (as part of the probe and device state + * creation) to give to the gadget driver. Setup the endpoint name, any + * direction information and other state that may be required. + */ +static void dwc2_ep_init(struct dwc2 *dwc2, int epnum, bool dir_in) +{ + struct dwc2_ep *ep; + char *dir; + + if (dir_in) + ep = dwc2->eps_in[epnum]; + else + ep = dwc2->eps_out[epnum]; + + if (epnum == 0) + dir = ""; + else if (dir_in) + dir = "in"; + else + dir = "out"; + + ep->dir_in = dir_in; + ep->epnum = epnum; + + snprintf(ep->name, sizeof(ep->name), "ep%d%s", epnum, dir); + + INIT_LIST_HEAD(&ep->queue); + INIT_LIST_HEAD(&ep->ep.ep_list); + + if (epnum) + list_add_tail(&ep->ep.ep_list, &dwc2->gadget.ep_list); + + ep->dwc2 = dwc2; + ep->ep.name = ep->name; + + if (dwc2->params.speed == DWC2_SPEED_PARAM_LOW) + usb_ep_set_maxpacket_limit(&ep->ep, 8); + else if (epnum == 0) + usb_ep_set_maxpacket_limit(&ep->ep, D0EPCTL_MPS_LIMIT); + else + usb_ep_set_maxpacket_limit(&ep->ep, 1024); + + ep->ep.ops = &dwc2_ep_ops; +} + +static int dwc2_eps_alloc(struct dwc2 *dwc2) +{ + struct dwc2_ep *ep; + u32 cfg; + u32 ep_type; + u32 i; + + /* Number of Device Endpoints */ + dwc2->num_eps = 1 + dwc2->hw_params.num_dev_ep; + + ep = kzalloc(sizeof(*ep), GFP_KERNEL); + if (!ep) + return -ENOMEM; + + /* Same endpoint is used in both directions for ep0 */ + dwc2->eps_out[0] = dwc2->eps_in[0] = ep; + + cfg = dwc2->hw_params.dev_ep_dirs; + for (i = 1, cfg >>= 2; i < dwc2->num_eps; i++, cfg >>= 2) { + ep_type = cfg & 3; + /* Direction in or both */ + if (!(ep_type & 2)) { + ep = kzalloc(sizeof(*ep), GFP_KERNEL); + if (!ep) + return -ENOMEM; + dwc2->eps_in[i] = ep; + } + /* Direction out or both */ + if (!(ep_type & 1)) { + ep = kzalloc(sizeof(*ep), GFP_KERNEL); + if (!ep) + return -ENOMEM; + dwc2->eps_out[i] = ep; + } + } + + dwc2->dedicated_fifos = dwc2->hw_params.en_multiple_tx_fifo; + + dwc2_info(dwc2, "EPs: %d, %s fifos, 0x%x entries in SPRAM\n", + dwc2->num_eps, + dwc2->dedicated_fifos ? "dedicated" : "shared", + dwc2->hw_params.total_fifo_size); + return 0; +} + +/** + * dwc2_force_mode() - Force the mode of the controller. + * + * Forcing the mode is needed for two cases: + * + * 1) If the dr_mode is set to either HOST or PERIPHERAL we force the + * controller to stay in a particular mode regardless of ID pin + * changes. We do this once during probe. + * + * 2) During probe we want to read reset values of the hw + * configuration registers that are only available in either host or + * device mode. We may need to force the mode if the current mode does + * not allow us to access the register in the mode that we want. + * + * In either case it only makes sense to force the mode if the + * controller hardware is OTG capable. + * + * Checks are done in this function to determine whether doing a force + * would be valid or not. + * + * If a force is done, it requires a IDDIG debounce filter delay if + * the filter is configured and enabled. We poll the current mode of + * the controller to account for this delay. + * + * @dwc2: Programming view of DWC_otg controller + * @host: Host mode flag + */ +static void dwc2_force_mode(struct dwc2 *dwc2, bool host) +{ + u32 gusbcfg; + u32 set; + u32 clear; + + dev_dbg(dwc2->dev, "Forcing mode to %s\n", host ? "host" : "device"); + + /* + * If dr_mode is either peripheral or host only, there is no + * need to ever force the mode to the opposite mode. + */ + if (WARN_ON(host && dwc2->dr_mode == USB_DR_MODE_PERIPHERAL)) + return; + + if (WARN_ON(!host && dwc2->dr_mode == USB_DR_MODE_HOST)) + return; + + gusbcfg = dwc2_readl(dwc2, GUSBCFG); + + set = host ? GUSBCFG_FORCEHOSTMODE : GUSBCFG_FORCEDEVMODE; + clear = host ? GUSBCFG_FORCEDEVMODE : GUSBCFG_FORCEHOSTMODE; + + gusbcfg &= ~clear; + gusbcfg |= set; + dwc2_writel(dwc2, gusbcfg, GUSBCFG); + + dwc2_wait_for_mode(dwc2, host); + + return; +} + +/** + * dwc2_clear_force_mode() - Clears the force mode bits. + * + * After clearing the bits, wait up to 100 ms to account for any + * potential IDDIG filter delay. We can't know if we expect this delay + * or not because the value of the connector ID status is affected by + * the force mode. We only need to call this once during probe if + * dr_mode == OTG. + * + * @dwc2: Programming view of DWC_otg controller + */ +static void dwc2_clear_force_mode(struct dwc2 *dwc2) +{ + u32 gusbcfg; + + dev_dbg(dwc2->dev, "Clearing force mode bits\n"); + + gusbcfg = dwc2_readl(dwc2, GUSBCFG); + gusbcfg &= ~GUSBCFG_FORCEHOSTMODE; + gusbcfg &= ~GUSBCFG_FORCEDEVMODE; + dwc2_writel(dwc2, gusbcfg, GUSBCFG); + + if (dwc2_iddig_filter_enabled(dwc2)) + mdelay(100); +} + +int dwc2_gadget_init(struct dwc2 *dwc2) +{ + u32 dctl; + int epnum; + int ret; + + if (!dwc2->params.dma) { + dwc2_err(dwc2, "no DMA support, required by gadget driver"); + return -ENOTSUPP; + } + + dwc2_core_init(dwc2); + + dwc2->gadget.speed = USB_SPEED_UNKNOWN; + if (dwc2->params.speed == DWC2_SPEED_PARAM_HIGH) + dwc2->gadget.max_speed = USB_SPEED_HIGH; + else + dwc2->gadget.max_speed = USB_SPEED_FULL; + + dwc2->gadget.ops = &dwc2_gadget_ops; + dwc2->gadget.name = "DWC2 gadget"; + + dwc2->gadget.is_otg = (dwc2->dr_mode == USB_DR_MODE_OTG) ? 1 : 0; + + if (dwc2->gadget.is_otg) + dwc2_clear_force_mode(dwc2); + else + dwc2_force_mode(dwc2, false); + + ret = dwc2_eps_alloc(dwc2); + if (ret) { + dwc2_err(dwc2, "Endpoints allocation failed: %d\n", ret); + return ret; + } + + dwc2->ctrl_buff = dma_alloc(DWC2_CTRL_BUFF_SIZE); + if (!dwc2->ctrl_buff) + return -ENOMEM; + + dwc2->ep0_buff = dma_alloc(DWC2_CTRL_BUFF_SIZE); + if (!dwc2->ep0_buff) + return -ENOMEM; + + if (dwc2->num_eps == 0) { + dwc2_err(dwc2, "wrong number of EPs (zero)\n"); + return -EINVAL; + } + + /* setup endpoint information */ + INIT_LIST_HEAD(&dwc2->gadget.ep_list); + dwc2->gadget.ep0 = &dwc2->eps_out[0]->ep; + + /* allocate EP0 request */ + dwc2->ctrl_req = dwc2_ep_alloc_req(&dwc2->eps_out[0]->ep); + if (!dwc2->ctrl_req) { + dwc2_err(dwc2, "failed to allocate ctrl req\n"); + return -ENOMEM; + } + + /* initialise the endpoints now the core has been initialised */ + for (epnum = 0; epnum < dwc2->num_eps; epnum++) { + if (dwc2->eps_in[epnum]) + dwc2_ep_init(dwc2, epnum, 1); + if (dwc2->eps_out[epnum]) + dwc2_ep_init(dwc2, epnum, 0); + } + + /* Be in disconnected state until gadget is registered */ + dctl = dwc2_readl(dwc2, DCTL); + dwc2_writel(dwc2, dctl | DCTL_SFTDISCON, DCTL); + + ret = usb_add_gadget_udc(dwc2->dev, &dwc2->gadget); + if (ret) { + dwc2_ep_free_req(&dwc2->eps_out[0]->ep, dwc2->ctrl_req); + return ret; + } + + return 0; +} + +void dwc2_gadget_uninit(struct dwc2 *dwc2) +{ + dwc2_core_disconnect(dwc2); + dwc2_gadget_disconnect(dwc2); +} diff --git a/drivers/usb/dwc2/host.c b/drivers/usb/dwc2/host.c new file mode 100644 index 0000000000..344f058be2 --- /dev/null +++ b/drivers/usb/dwc2/host.c @@ -0,0 +1,796 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include <dma.h> +#include "dwc2.h" + +#define to_dwc2 host_to_dwc2 + +/* Use only HC channel 0. */ +#define DWC2_HC_CHANNEL 0 + +static int dwc2_do_split(struct dwc2 *dwc2, struct usb_device *dev) +{ + uint32_t hprt0 = dwc2_readl(dwc2, HPRT0); + uint32_t prtspd = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; + + return prtspd == HPRT0_SPD_HIGH_SPEED && dev->speed != USB_SPEED_HIGH; +} + +static void dwc2_host_hub_info(struct dwc2 *dwc2, struct usb_device *dev, + uint8_t *hub_addr, uint8_t *hub_port) +{ + *hub_addr = dev->devnum; + *hub_port = dev->portnr; + + for (; dev->parent; dev = dev->parent) { + if (dev->parent->descriptor->bDeviceClass == USB_CLASS_HUB) { + *hub_addr = dev->parent->devnum; + *hub_port = dev->parent->portnr; + break; + } + } +} + +static void dwc2_hc_init_split(struct dwc2 *dwc2, struct usb_device *dev, + uint8_t hc) +{ + uint8_t hub_addr, hub_port; + uint32_t hcsplt = 0; + + dwc2_host_hub_info(dwc2, dev, &hub_addr, &hub_port); + + hcsplt = HCSPLT_SPLTENA; + hcsplt |= hub_addr << HCSPLT_HUBADDR_SHIFT; + hcsplt |= hub_port << HCSPLT_PRTADDR_SHIFT; + + /* Program the HCSPLIT register for SPLITs */ + dwc2_writel(dwc2, hcsplt, HCSPLT(hc)); +} + +static void dwc2_hc_enable_ints(struct dwc2 *dwc2, uint8_t hc) +{ + uint32_t intmsk; + uint32_t hcintmsk = HCINTMSK_CHHLTD; + + dwc2_writel(dwc2, hcintmsk, HCINTMSK(hc)); + + /* Enable the top level host channel interrupt */ + intmsk = dwc2_readl(dwc2, HAINTMSK); + intmsk |= 1 << hc; + dwc2_writel(dwc2, intmsk, HAINTMSK); + + /* Make sure host channel interrupts are enabled */ + intmsk = dwc2_readl(dwc2, GINTMSK); + intmsk |= GINTSTS_HCHINT; + dwc2_writel(dwc2, intmsk, GINTMSK); +} + +/** + * Prepares a host channel for transferring packets to/from a specific + * endpoint. The HCCHARn register is set up with the characteristics specified + * in _hc. Host channel interrupts that may need to be serviced while this + * transfer is in progress are enabled. + * + * @param regs Programming view of DWC2 controller + * @param hc Information needed to initialize the host channel + */ +static void dwc2_hc_init(struct dwc2 *dwc2, struct usb_device *dev, u8 hc, + unsigned long pipe, int is_in) +{ + int addr = usb_pipedevice(pipe); + int endp = usb_pipeendpoint(pipe); + int type = usb_pipetype(pipe); + int mps = usb_maxpacket(dev, pipe); + uint32_t hcchar = (addr << HCCHAR_DEVADDR_SHIFT) | + (endp << HCCHAR_EPNUM_SHIFT) | + (is_in ? HCCHAR_EPDIR : 0) | + (mps << HCCHAR_MPS_SHIFT); + + switch (type) { + case PIPE_ISOCHRONOUS: + hcchar |= DXEPCTL_EPTYPE_ISO; + break; + case PIPE_INTERRUPT: + hcchar |= DXEPCTL_EPTYPE_INTERRUPT; + break; + case PIPE_CONTROL: + hcchar |= DXEPCTL_EPTYPE_CONTROL; + break; + case PIPE_BULK: + hcchar |= DXEPCTL_EPTYPE_BULK; + break; + } + + if (dev->speed == USB_SPEED_LOW) + hcchar |= HCCHAR_LSPDDEV; + + /* Clear old interrupt conditions for this dwc2 channel */ + dwc2_writel(dwc2, ~HCINTMSK_RESERVED14_31, HCINT(hc)); + + /* Enable channel interrupts required for this transfer */ + dwc2_hc_enable_ints(dwc2, hc); + + /* + * Program the HCCHARn register with the endpoint characteristics + * for the current transfer. + */ + dwc2_writel(dwc2, hcchar, HCCHAR(hc)); + + /* Program the HCSPLIT register, default to no SPLIT */ + dwc2_writel(dwc2, 0, HCSPLT(hc)); +} + +static void dwc2_endpoint_reset(struct dwc2 *dwc2, int in, int devnum, int ep) +{ + if (in) + dwc2->in_data_toggle[devnum][ep] = TSIZ_SC_MC_PID_DATA0; + else + dwc2->out_data_toggle[devnum][ep] = TSIZ_SC_MC_PID_DATA0; +} + +static int wait_for_chhltd(struct dwc2 *dwc2, u8 hc, uint32_t *sub, u8 *tgl) +{ + int ret; + uint32_t hcint, hctsiz, hcchar; + + ret = dwc2_wait_bit_set(dwc2, HCINT(hc), HCINTMSK_CHHLTD, 10000); + if (ret) { + hcchar = dwc2_readl(dwc2, HCCHAR(hc)); + dwc2_writel(dwc2, hcchar | HCCHAR_CHDIS, HCCHAR(hc)); + dwc2_wait_bit_set(dwc2, HCINT(hc), HCINTMSK_CHHLTD, 10000); + return ret; + } + + hcint = dwc2_readl(dwc2, HCINT(hc)); + + if (hcint & HCINTMSK_AHBERR) + dwc2_err(dwc2, "%s: AHB error during internal DMA access\n", + __func__); + + if (hcint & HCINTMSK_XFERCOMPL) { + hctsiz = dwc2_readl(dwc2, HCTSIZ(hc)); + *sub = (hctsiz & TSIZ_XFERSIZE_MASK) >> TSIZ_XFERSIZE_SHIFT; + *tgl = (hctsiz & TSIZ_SC_MC_PID_MASK) >> TSIZ_SC_MC_PID_SHIFT; + + dwc2_dbg(dwc2, "%s: HCINT=%08x sub=%u toggle=%d\n", __func__, + hcint, *sub, *tgl); + return 0; + } + + if (hcint & (HCINTMSK_NAK | HCINTMSK_FRMOVRUN)) + return -EAGAIN; + + dwc2_dbg(dwc2, "%s: Unknown channel status: (HCINT=%08x)\n", __func__, + hcint); + return -EINVAL; +} + +static int transfer_chunk(struct dwc2 *dwc2, u8 hc, + u8 *pid, int in, void *buffer, int num_packets, + int xfer_len, int *actual_len, int odd_frame) +{ + uint32_t hctsiz, hcchar, sub; + dma_addr_t dma_addr = 0; + int ret = 0; + + if (xfer_len) + dma_addr = dma_map_single(dwc2->dev, buffer, xfer_len, + in ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + + if (dma_mapping_error(dwc2->dev, dma_addr)) { + dwc2_err(dwc2, "Failed to map buffer@0x%p for dma\n", buffer); + return -EFAULT; + } + + dwc2_dbg(dwc2, "chunk: pid=%d xfer_len=%u pkts=%u dma_addr=%pad\n", + *pid, xfer_len, num_packets, &dma_addr); + + dwc2_writel(dwc2, dma_addr, HCDMA(hc)); + + hctsiz = (xfer_len << TSIZ_XFERSIZE_SHIFT) + | (num_packets << TSIZ_PKTCNT_SHIFT) + | (*pid << TSIZ_SC_MC_PID_SHIFT); + + dwc2_writel(dwc2, hctsiz, HCTSIZ(hc)); + + /* Clear old interrupt conditions for this dwc2 channel. */ + dwc2_writel(dwc2, 0x3fff, HCINT(hc)); + + /* Set dwc2 channel enable after all other setup is complete. */ + hcchar = dwc2_readl(dwc2, HCCHAR(hc)); + hcchar &= ~(HCCHAR_MULTICNT_MASK | HCCHAR_CHDIS); + hcchar |= (1 << HCCHAR_MULTICNT_SHIFT) | HCCHAR_CHENA; + if (odd_frame) + hcchar |= HCCHAR_ODDFRM; + else + hcchar &= ~HCCHAR_ODDFRM; + dwc2_writel(dwc2, hcchar, HCCHAR(hc)); + + ret = wait_for_chhltd(dwc2, hc, &sub, pid); + if (ret < 0) + goto exit; + + if (in) + xfer_len -= sub; + *actual_len = xfer_len; + +exit: + if (xfer_len) + dma_unmap_single(dwc2->dev, dma_addr, xfer_len, + in ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + + return ret; +} + +static int dwc2_submit_packet(struct dwc2 *dwc2, struct usb_device *dev, u8 hc, + unsigned long pipe, u8 *pid, int in, void *buf, + int len) +{ + int mps = usb_maxpacket(dev, pipe); + int do_split = dwc2_do_split(dwc2, dev); + int complete_split = 0; + int done = 0; + int ret = 0; + uint32_t xfer_len; + uint32_t num_packets; + int stop_transfer = 0; + uint32_t max_xfer_len; + int ssplit_frame_num = 0; + + max_xfer_len = dwc2->params.max_packet_count * mps; + + if (max_xfer_len > dwc2->params.max_transfer_size) + max_xfer_len = dwc2->params.max_transfer_size; + + /* Make sure that max_xfer_len is a multiple of max packet size. */ + num_packets = max_xfer_len / mps; + max_xfer_len = num_packets * mps; + + /* Initialize channel */ + dwc2_hc_init(dwc2, dev, hc, pipe, in); + + /* Check if the target is a FS/LS device behind a HS hub */ + if (do_split) { + dwc2_hc_init_split(dwc2, dev, hc); + num_packets = 1; + max_xfer_len = mps; + } + do { + int actual_len = 0; + uint32_t hcint, hcsplt; + int odd_frame = 0; + + xfer_len = len - done; + + if (xfer_len > max_xfer_len) + xfer_len = max_xfer_len; + else if (xfer_len > mps) + num_packets = (xfer_len + mps - 1) / mps; + else + num_packets = 1; + + if (complete_split || do_split) { + hcsplt = dwc2_readl(dwc2, HCSPLT(hc)); + if (complete_split) + hcsplt |= HCSPLT_COMPSPLT; + else if (do_split) + hcsplt &= ~HCSPLT_COMPSPLT; + dwc2_writel(dwc2, hcsplt, HCSPLT(hc)); + } + + if (usb_pipeint(pipe)) { + int uframe_num = dwc2_readl(dwc2, HFNUM); + + if (!(uframe_num & 0x1)) + odd_frame = 1; + } + + ret = transfer_chunk(dwc2, hc, pid, + in, (char *)buf + done, num_packets, + xfer_len, &actual_len, odd_frame); + + hcint = dwc2_readl(dwc2, HCINT(hc)); + if (complete_split) { + stop_transfer = 0; + if (hcint & HCINTMSK_NYET) { + int frame_num = HFNUM_MAX_FRNUM & + dwc2_readl(dwc2, HFNUM); + + ret = 0; + if (((frame_num - ssplit_frame_num) & + HFNUM_MAX_FRNUM) > 4) + ret = -EAGAIN; + } else { + complete_split = 0; + } + } else if (do_split) { + if (hcint & HCINTMSK_ACK) { + ssplit_frame_num = HFNUM_MAX_FRNUM & + dwc2_readl(dwc2, HFNUM); + ret = 0; + complete_split = 1; + } + } + + if (ret) + break; + + if (actual_len < xfer_len) + stop_transfer = 1; + + done += actual_len; + + /* Transactions are done when when either all data is transferred or + * there is a short transfer. In case of a SPLIT make sure the CSPLIT + * is executed. + */ + } while (((done < len) && !stop_transfer) || complete_split); + + dwc2_writel(dwc2, 0, HCINTMSK(hc)); + dwc2_writel(dwc2, 0xFFFFFFFF, HCINT(hc)); + + dev->status = 0; + dev->act_len = done; + + return ret; +} + +static int dwc2_submit_control_msg(struct usb_device *udev, + unsigned long pipe, void *buffer, int len, + struct devrequest *setup, int timeout) +{ + struct usb_host *host = udev->host; + struct dwc2 *dwc2 = to_dwc2(host); + int devnum = usb_pipedevice(pipe); + int ret, act_len; + u8 pid; + u8 hc = DWC2_HC_CHANNEL; + /* For CONTROL endpoint pid should start with DATA1 */ + int status_direction; + + if (devnum == dwc2->root_hub_devnum) { + udev->speed = USB_SPEED_HIGH; + ret = dwc2_submit_roothub(dwc2, udev, pipe, buffer, len, setup); + return ret; + } + + /* SETUP stage */ + pid = TSIZ_SC_MC_PID_SETUP; + do { + ret = dwc2_submit_packet(dwc2, udev, hc, pipe, &pid, + 0, setup, 8); + } while (ret == -EAGAIN); + if (ret) + return ret; + + /* DATA stage */ + act_len = 0; + if (buffer) { + pid = TSIZ_SC_MC_PID_DATA1; + do { + ret = dwc2_submit_packet(dwc2, udev, hc, pipe, &pid, + usb_pipein(pipe), buffer, len); + act_len += udev->act_len; + buffer += udev->act_len; + len -= udev->act_len; + } while (ret == -EAGAIN); + if (ret) + return ret; + status_direction = usb_pipeout(pipe); + } else { + /* No-data CONTROL always ends with an IN transaction */ + status_direction = 1; + } + + /* STATUS stage */ + pid = TSIZ_SC_MC_PID_DATA1; + do { + ret = dwc2_submit_packet(dwc2, udev, hc, pipe, &pid, + status_direction, NULL, 0); + } while (ret == -EAGAIN); + if (ret) + return ret; + + if (setup->requesttype == USB_RECIP_ENDPOINT + && setup->request == USB_REQ_CLEAR_FEATURE) { + /* From USB 2.0, section 9.4.5: + * ClearFeature(ENDPOINT_HALT) request always results + * in the data toggle being reinitialized to DATA0. + */ + int ep = le16_to_cpu(setup->index) & 0xf; + dwc2_endpoint_reset(dwc2, usb_pipein(pipe), devnum, ep); + } + + udev->act_len = act_len; + udev->status = 0; + + return 0; +} + +static int dwc2_submit_bulk_msg(struct usb_device *udev, unsigned long pipe, + void *buffer, int len, int timeout) +{ + struct usb_host *host = udev->host; + struct dwc2 *dwc2 = to_dwc2(host); + int devnum = usb_pipedevice(pipe); + int ep = usb_pipeendpoint(pipe); + int in = usb_pipein(pipe); + u8 *pid; + u8 hc = DWC2_HC_CHANNEL; + uint64_t start; + int ret; + + if ((devnum >= MAX_DEVICE) || (devnum == dwc2->root_hub_devnum)) { + udev->status = 0; + return -EINVAL; + } + + if (in) + pid = &dwc2->in_data_toggle[devnum][ep]; + else + pid = &dwc2->out_data_toggle[devnum][ep]; + + start = get_time_ns(); + do { + ret = dwc2_submit_packet(dwc2, udev, hc, pipe, pid, in, + buffer, len); + } while (ret == -EAGAIN && !is_timeout(start, timeout * MSECOND)); + if (ret == -EAGAIN) { + dwc2_err(dwc2, "Timeout on bulk endpoint\n"); + ret = -ETIMEDOUT; + } + + dwc2_dbg(dwc2, "%s: return %d\n", __func__, ret); + + return ret; +} + +static int dwc2_submit_int_msg(struct usb_device *udev, unsigned long pipe, + void *buffer, int len, int interval) +{ + struct usb_host *host = udev->host; + struct dwc2 *dwc2 = to_dwc2(host); + int devnum = usb_pipedevice(pipe); + int ep = usb_pipeendpoint(pipe); + int in = usb_pipein(pipe); + u8 *pid; + u8 hc = DWC2_HC_CHANNEL; + uint64_t start; + int ret; + + if ((devnum >= MAX_DEVICE) || (devnum == dwc2->root_hub_devnum)) { + udev->status = 0; + return -EINVAL; + } + + if (usb_pipein(pipe)) + pid = &dwc2->in_data_toggle[devnum][ep]; + else + pid = &dwc2->out_data_toggle[devnum][ep]; + + start = get_time_ns(); + + while (1) { + ret = dwc2_submit_packet(dwc2, udev, hc, pipe, pid, in, + buffer, len); + if (ret != -EAGAIN) + return ret; + if (is_timeout(start, USB_CNTL_TIMEOUT * MSECOND)) { + dwc2_err(dwc2, "Timeout on interrupt endpoint\n"); + return -ETIMEDOUT; + } + } +} + +/** + * dwc2_calculate_dynamic_fifo() - Calculates the default fifo size + * For system that have a total fifo depth that is smaller than the default + * RX + TX fifo size. + * + * @dwc2: Programming view of DWC_otg controller + */ +static void dwc2_calculate_dynamic_fifo(struct dwc2 *dwc2) +{ + struct dwc2_core_params *params = &dwc2->params; + struct dwc2_hw_params *hw = &dwc2->hw_params; + u32 rxfsiz, nptxfsiz, ptxfsiz, total_fifo_size; + + total_fifo_size = hw->total_fifo_size; + rxfsiz = params->host_rx_fifo_size; + nptxfsiz = params->host_nperio_tx_fifo_size; + ptxfsiz = params->host_perio_tx_fifo_size; + + /* + * Will use Method 2 defined in the DWC2 spec: minimum FIFO depth + * allocation with support for high bandwidth endpoints. Synopsys + * defines MPS(Max Packet size) for a periodic EP=1024, and for + * non-periodic as 512. + */ + if (total_fifo_size < (rxfsiz + nptxfsiz + ptxfsiz)) { + /* + * For Buffer DMA mode/Scatter Gather DMA mode + * 2 * ((Largest Packet size / 4) + 1 + 1) + n + * with n = number of host channel. + * 2 * ((1024/4) + 2) = 516 + */ + rxfsiz = 516 + hw->host_channels; + + /* + * min non-periodic tx fifo depth + * 2 * (largest non-periodic USB packet used / 4) + * 2 * (512/4) = 256 + */ + nptxfsiz = 256; + + /* + * min periodic tx fifo depth + * (largest packet size*MC)/4 + * (1024 * 3)/4 = 768 + */ + ptxfsiz = 768; + } + + params->host_rx_fifo_size = rxfsiz; + params->host_nperio_tx_fifo_size = nptxfsiz; + params->host_perio_tx_fifo_size = ptxfsiz; + + /* + * If the summation of RX, NPTX and PTX fifo sizes is still + * bigger than the total_fifo_size, then we have a problem. + * + * We won't be able to allocate as many endpoints. Right now, + * we're just printing an error message, but ideally this FIFO + * allocation algorithm would be improved in the future. + * + * FIXME improve this FIFO allocation algorithm. + */ + if (unlikely(total_fifo_size < (rxfsiz + nptxfsiz + ptxfsiz))) + dwc2_err(dwc2, "invalid fifo sizes\n"); +} + +static void dwc2_config_fifos(struct dwc2 *dwc2) +{ + struct dwc2_core_params *params = &dwc2->params; + u32 nptxfsiz, hptxfsiz, dfifocfg, grxfsiz; + + if (!params->enable_dynamic_fifo) + return; + + dwc2_calculate_dynamic_fifo(dwc2); + + /* Rx FIFO */ + grxfsiz = dwc2_readl(dwc2, GRXFSIZ); + dwc2_dbg(dwc2, "initial grxfsiz=%08x\n", grxfsiz); + grxfsiz &= ~GRXFSIZ_DEPTH_MASK; + grxfsiz |= params->host_rx_fifo_size << + GRXFSIZ_DEPTH_SHIFT & GRXFSIZ_DEPTH_MASK; + dwc2_writel(dwc2, grxfsiz, GRXFSIZ); + dwc2_dbg(dwc2, "new grxfsiz=%08x\n", dwc2_readl(dwc2, GRXFSIZ)); + + /* Non-periodic Tx FIFO */ + dwc2_dbg(dwc2, "initial gnptxfsiz=%08x\n", dwc2_readl(dwc2, GNPTXFSIZ)); + nptxfsiz = params->host_nperio_tx_fifo_size << + FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK; + nptxfsiz |= params->host_rx_fifo_size << + FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK; + dwc2_writel(dwc2, nptxfsiz, GNPTXFSIZ); + dwc2_dbg(dwc2, "new gnptxfsiz=%08x\n", dwc2_readl(dwc2, GNPTXFSIZ)); + + /* Periodic Tx FIFO */ + dwc2_dbg(dwc2, "initial hptxfsiz=%08x\n", dwc2_readl(dwc2, HPTXFSIZ)); + hptxfsiz = params->host_perio_tx_fifo_size << + FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK; + hptxfsiz |= (params->host_rx_fifo_size + + params->host_nperio_tx_fifo_size) << + FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK; + dwc2_writel(dwc2, hptxfsiz, HPTXFSIZ); + dwc2_dbg(dwc2, "new hptxfsiz=%08x\n", dwc2_readl(dwc2, HPTXFSIZ)); + + if (dwc2->params.en_multiple_tx_fifo && + dwc2->hw_params.snpsid >= DWC2_CORE_REV_2_91a) { + /* + * This feature was implemented in 2.91a version + * Global DFIFOCFG calculation for Host mode - + * include RxFIFO, NPTXFIFO and HPTXFIFO + */ + dfifocfg = dwc2_readl(dwc2, GDFIFOCFG); + dfifocfg &= ~GDFIFOCFG_EPINFOBASE_MASK; + dfifocfg |= (params->host_rx_fifo_size + + params->host_nperio_tx_fifo_size + + params->host_perio_tx_fifo_size) << + GDFIFOCFG_EPINFOBASE_SHIFT & + GDFIFOCFG_EPINFOBASE_MASK; + dwc2_writel(dwc2, dfifocfg, GDFIFOCFG); + dwc2_dbg(dwc2, "new dfifocfg=%08x\n", dfifocfg); + } +} + +/* + * This function initializes the DWC2 controller registers for + * host mode. + * + * This function flushes the Tx and Rx FIFOs and it flushes any entries in the + * request queues. Host channels are reset to ensure that they are ready for + * performing transfers. + * + * @param dev USB Device (NULL if driver model is not being used) + * @param regs Programming view of DWC2 controller + * + */ +static void dwc2_core_host_init(struct device *dev, + struct dwc2 *dwc2) +{ + uint32_t hcchar, hcfg, hprt0, hotgctl, usbcfg; + int i, ret, num_channels; + + dwc2_dbg(dwc2, "%s(%p)\n", __func__, dwc2); + + /* Set HS/FS Timeout Calibration to 7 (max available value). + * The number of PHY clocks that the application programs in + * this field is added to the high/full speed interpacket timeout + * duration in the core to account for any additional delays + * introduced by the PHY. This can be required, because the delay + * introduced by the PHY in generating the linestate condition + * can vary from one PHY to another. + */ + usbcfg = dwc2_readl(dwc2, GUSBCFG); + usbcfg |= GUSBCFG_TOUTCAL(7); + dwc2_writel(dwc2, usbcfg, GUSBCFG); + + /* Restart the Phy Clock */ + dwc2_writel(dwc2, 0, PCGCTL); + + /* Initialize Host Configuration Register */ + dwc2_init_fs_ls_pclk_sel(dwc2); + if (dwc2->params.speed == DWC2_SPEED_PARAM_FULL || + dwc2->params.speed == DWC2_SPEED_PARAM_LOW) { + hcfg = dwc2_readl(dwc2, HCFG); + hcfg |= HCFG_FSLSSUPP; + dwc2_writel(dwc2, hcfg, HCFG); + } + + if (dwc2->params.dma_desc) { + u32 op_mode = dwc2->hw_params.op_mode; + + if (dwc2->hw_params.snpsid < DWC2_CORE_REV_2_90a || + !dwc2->hw_params.dma_desc_enable || + op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE || + op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE || + op_mode == GHWCFG2_OP_MODE_UNDEFINED) { + dwc2_err(dwc2, "Descriptor DMA not suppported\n"); + dwc2_err(dwc2, "falling back to buffer DMA mode.\n"); + dwc2->params.dma_desc = false; + } else { + hcfg = dwc2_readl(dwc2, HCFG); + hcfg |= HCFG_DESCDMA; + dwc2_writel(dwc2, hcfg, HCFG); + } + } + + dwc2_config_fifos(dwc2); + + /* Clear Host Set HNP Enable in the OTG Control Register */ + hotgctl = dwc2_readl(dwc2, GOTGCTL); + hotgctl &= ~GOTGCTL_HSTSETHNPEN; + dwc2_writel(dwc2, hotgctl, GOTGCTL); + + /* Make sure the FIFOs are flushed. */ + dwc2_flush_all_fifo(dwc2); + + /* Flush out any leftover queued requests. */ + num_channels = dwc2->params.host_channels; + for (i = 0; i < num_channels; i++) { + hcchar = dwc2_readl(dwc2, HCCHAR(i)); + if (!(hcchar & HCCHAR_CHENA)) + continue; + hcchar |= HCCHAR_CHDIS; + hcchar &= ~(HCCHAR_CHENA | HCCHAR_EPDIR); + dwc2_writel(dwc2, hcchar, HCCHAR(i)); + } + + /* Halt all channels to put them into a known state. */ + for (i = 0; i < num_channels; i++) { + hcchar = dwc2_readl(dwc2, HCCHAR(i)); + if (!(hcchar & HCCHAR_CHENA)) + continue; + hcchar |= HCCHAR_CHENA | HCCHAR_CHDIS; + hcchar &= ~HCCHAR_EPDIR; + + dwc2_writel(dwc2, hcchar, HCCHAR(i)); + ret = dwc2_wait_bit_clear(dwc2, HCCHAR(i), HCCHAR_CHENA, 10000); + if (ret) + dwc2_warn(dwc2, "%s: Timeout! Reseting channel %d\n", + __func__, i); + } + + /* Turn on the vbus power */ + if (dwc2_is_host_mode(dwc2)) { + hprt0 = dwc2_readl(dwc2, HPRT0); + hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET); + hprt0 &= ~(HPRT0_ENACHG | HPRT0_OVRCURRCHG); + if (!(hprt0 & HPRT0_PWR)) { + hprt0 |= HPRT0_PWR; + dwc2_writel(dwc2, hprt0, HPRT0); + } + } + + /* Disable all interrupts */ + dwc2_writel(dwc2, 0, GINTMSK); + dwc2_writel(dwc2, 0, HAINTMSK); +} + +static int dwc2_host_init(struct usb_host *host) +{ + struct dwc2 *dwc2 = to_dwc2(host); + struct device *dev = dwc2->dev; + uint32_t hprt0, gusbcfg; + int i, j; + + /* Force Host mode in case the dwc2 controller is otg, + * otherwise the mode selection is dictated by the id + * pin, thus will require a otg A cable to be plugged-in. + */ + gusbcfg = dwc2_readl(dwc2, GUSBCFG) | GUSBCFG_FORCEHOSTMODE; + dwc2_writel(dwc2, gusbcfg, GUSBCFG); + mdelay(25); + + dwc2_core_init(dwc2); + dwc2_core_host_init(dev, dwc2); + + hprt0 = dwc2_readl(dwc2, HPRT0); + hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET); + /* clear HPRT0_ENACHG and HPRT0_OVRCURRCHG by writing 1 */ + hprt0 |= HPRT0_ENACHG | HPRT0_OVRCURRCHG; + hprt0 |= HPRT0_RST; + dwc2_writel(dwc2, hprt0, HPRT0); + + mdelay(50); + + hprt0 = dwc2_readl(dwc2, HPRT0); + hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET | HPRT0_RST); + dwc2_writel(dwc2, hprt0, HPRT0); + + for (i = 0; i < MAX_DEVICE; i++) { + for (j = 0; j < MAX_ENDPOINT; j++) { + dwc2->in_data_toggle[i][j] = TSIZ_SC_MC_PID_DATA0; + dwc2->out_data_toggle[i][j] = TSIZ_SC_MC_PID_DATA0; + } + } + + /* + * Add a 1 second delay here. This gives the host controller + * a bit time before the comminucation with the USB devices + * is started (the bus is scanned) and fixes the USB detection + * problems with some problematic USB keys. + */ + if (dwc2_is_host_mode(dwc2)) + mdelay(1000); + + return 0; +} + +int dwc2_register_host(struct dwc2 *dwc2) +{ + struct usb_host *host; + + host = &dwc2->host; + host->hw_dev = dwc2->dev; + host->init = dwc2_host_init; + host->submit_bulk_msg = dwc2_submit_bulk_msg; + host->submit_control_msg = dwc2_submit_control_msg; + host->submit_int_msg = dwc2_submit_int_msg; + + return usb_register_host(host); +} + +void dwc2_host_uninit(struct dwc2 *dwc2) +{ + uint32_t hprt0; + + hprt0 = dwc2_readl(dwc2, HPRT0); + + /* Put everything in reset. */ + hprt0 &= ~(HPRT0_ENA | HPRT0_ENACHG | HPRT0_CONNDET | HPRT0_OVRCURRCHG); + hprt0 |= HPRT0_RST; + + dwc2_writel(dwc2, hprt0, HPRT0); +} diff --git a/drivers/usb/dwc2/regs.h b/drivers/usb/dwc2/regs.h new file mode 100644 index 0000000000..4c74f95eb4 --- /dev/null +++ b/drivers/usb/dwc2/regs.h @@ -0,0 +1,841 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2014 Marek Vasut <marex@denx.de> + */ + +#ifndef __DWC2_H__ +#define __DWC2_H__ + +#define HSOTG_REG(x) (x) + +#define GOTGCTL HSOTG_REG(0x000) +#define GOTGCTL_CHIRPEN BIT(27) +#define GOTGCTL_MULT_VALID_BC_MASK (0x1f << 22) +#define GOTGCTL_MULT_VALID_BC_SHIFT 22 +#define GOTGCTL_CURMODE BIT(21) /* was missing wtf ? */ +#define GOTGCTL_OTGVER BIT(20) +#define GOTGCTL_BSESVLD BIT(19) +#define GOTGCTL_ASESVLD BIT(18) +#define GOTGCTL_DBNC_SHORT BIT(17) +#define GOTGCTL_CONID_B BIT(16) +#define GOTGCTL_DBNCE_FLTR_BYPASS BIT(15) +#define GOTGCTL_EMBHOSTEN BIT(12) +#define GOTGCTL_DEVHNPEN BIT(11) +#define GOTGCTL_HSTSETHNPEN BIT(10) +#define GOTGCTL_HNPREQ BIT(9) +#define GOTGCTL_HSTNEGSCS BIT(8) +#define GOTGCTL_SESREQ BIT(1) +#define GOTGCTL_SESREQSCS BIT(0) + +#define GOTGINT HSOTG_REG(0x004) +#define GOTGINT_DBNCE_DONE BIT(19) +#define GOTGINT_A_DEV_TOUT_CHG BIT(18) +#define GOTGINT_HST_NEG_DET BIT(17) +#define GOTGINT_HST_NEG_SUC_STS_CHNG BIT(9) +#define GOTGINT_SES_REQ_SUC_STS_CHNG BIT(8) +#define GOTGINT_SES_END_DET BIT(2) + +#define GAHBCFG HSOTG_REG(0x008) +#define GAHBCFG_AHB_SINGLE BIT(23) +#define GAHBCFG_NOTI_ALL_DMA_WRIT BIT(22) +#define GAHBCFG_REM_MEM_SUPP BIT(21) +#define GAHBCFG_P_TXF_EMP_LVL BIT(8) +#define GAHBCFG_NP_TXF_EMP_LVL BIT(7) +#define GAHBCFG_DMA_EN BIT(5) +#define GAHBCFG_HBSTLEN_MASK (0xf << 1) +#define GAHBCFG_HBSTLEN_SHIFT 1 +#define GAHBCFG_HBSTLEN_SINGLE 0 +#define GAHBCFG_HBSTLEN_INCR 1 +#define GAHBCFG_HBSTLEN_INCR4 3 +#define GAHBCFG_HBSTLEN_INCR8 5 +#define GAHBCFG_HBSTLEN_INCR16 7 +#define GAHBCFG_GLBL_INTR_EN BIT(0) +#define GAHBCFG_CTRL_MASK (GAHBCFG_P_TXF_EMP_LVL | \ + GAHBCFG_NP_TXF_EMP_LVL | \ + GAHBCFG_DMA_EN | \ + GAHBCFG_GLBL_INTR_EN) + +#define GUSBCFG HSOTG_REG(0x00C) +#define GUSBCFG_FORCEDEVMODE BIT(30) +#define GUSBCFG_FORCEHOSTMODE BIT(29) +#define GUSBCFG_TXENDDELAY BIT(28) +#define GUSBCFG_ICTRAFFICPULLREMOVE BIT(27) +#define GUSBCFG_ICUSBCAP BIT(26) +#define GUSBCFG_ULPI_INT_PROT_DIS BIT(25) +#define GUSBCFG_INDICATORPASSTHROUGH BIT(24) +#define GUSBCFG_INDICATORCOMPLEMENT BIT(23) +#define GUSBCFG_TERMSELDLPULSE BIT(22) +#define GUSBCFG_ULPI_EXT_VBUS_IND BIT(21) +#define GUSBCFG_ULPI_EXT_VBUS_DRV BIT(20) +#define GUSBCFG_ULPI_CLK_SUSP_M BIT(19) +#define GUSBCFG_ULPI_AUTO_RES BIT(18) +#define GUSBCFG_ULPI_FS_LS BIT(17) +#define GUSBCFG_OTG_UTMI_FS_SEL BIT(16) +#define GUSBCFG_PHY_LP_CLK_SEL BIT(15) +#define GUSBCFG_USBTRDTIM_MASK (0xf << 10) +#define GUSBCFG_USBTRDTIM_SHIFT 10 +#define GUSBCFG_HNPCAP BIT(9) +#define GUSBCFG_SRPCAP BIT(8) +#define GUSBCFG_DDRSEL BIT(7) +#define GUSBCFG_PHYSEL BIT(6) +#define GUSBCFG_FSINTF BIT(5) +#define GUSBCFG_ULPI_UTMI_SEL BIT(4) +#define GUSBCFG_PHYIF16 BIT(3) +#define GUSBCFG_PHYIF8 (0 << 3) +#define GUSBCFG_TOUTCAL_MASK (0x7 << 0) +#define GUSBCFG_TOUTCAL_SHIFT 0 +#define GUSBCFG_TOUTCAL_LIMIT 0x7 +#define GUSBCFG_TOUTCAL(_x) ((_x) << 0) + +#define GRSTCTL HSOTG_REG(0x010) +#define GRSTCTL_AHBIDLE BIT(31) +#define GRSTCTL_DMAREQ BIT(30) +#define GRSTCTL_TXFNUM_MASK (0x1f << 6) +#define GRSTCTL_TXFNUM_SHIFT 6 +#define GRSTCTL_TXFNUM_LIMIT 0x1f +#define GRSTCTL_TXFNUM(_x) ((_x) << 6) +#define GRSTCTL_TXFFLSH BIT(5) +#define GRSTCTL_RXFFLSH BIT(4) +#define GRSTCTL_IN_TKNQ_FLSH BIT(3) +#define GRSTCTL_FRMCNTRRST BIT(2) +#define GRSTCTL_HSFTRST BIT(1) +#define GRSTCTL_CSFTRST BIT(0) + +#define GINTSTS HSOTG_REG(0x014) +#define GINTMSK HSOTG_REG(0x018) +#define GINTSTS_WKUPINT BIT(31) +#define GINTSTS_SESSREQINT BIT(30) +#define GINTSTS_DISCONNINT BIT(29) +#define GINTSTS_CONIDSTSCHNG BIT(28) +#define GINTSTS_LPMTRANRCVD BIT(27) +#define GINTSTS_PTXFEMP BIT(26) +#define GINTSTS_HCHINT BIT(25) +#define GINTSTS_PRTINT BIT(24) +#define GINTSTS_RESETDET BIT(23) +#define GINTSTS_FET_SUSP BIT(22) +#define GINTSTS_INCOMPL_IP BIT(21) +#define GINTSTS_INCOMPL_SOOUT BIT(21) +#define GINTSTS_INCOMPL_SOIN BIT(20) +#define GINTSTS_OEPINT BIT(19) +#define GINTSTS_IEPINT BIT(18) +#define GINTSTS_EPMIS BIT(17) +#define GINTSTS_RESTOREDONE BIT(16) +#define GINTSTS_EOPF BIT(15) +#define GINTSTS_ISOUTDROP BIT(14) +#define GINTSTS_ENUMDONE BIT(13) +#define GINTSTS_USBRST BIT(12) +#define GINTSTS_USBSUSP BIT(11) +#define GINTSTS_ERLYSUSP BIT(10) +#define GINTSTS_I2CINT BIT(9) +#define GINTSTS_ULPI_CK_INT BIT(8) +#define GINTSTS_GOUTNAKEFF BIT(7) +#define GINTSTS_GINNAKEFF BIT(6) +#define GINTSTS_NPTXFEMP BIT(5) +#define GINTSTS_RXFLVL BIT(4) +#define GINTSTS_SOF BIT(3) +#define GINTSTS_OTGINT BIT(2) +#define GINTSTS_MODEMIS BIT(1) +#define GINTSTS_CURMODE_HOST BIT(0) + +#define GRXSTSR HSOTG_REG(0x01C) +#define GRXSTSP HSOTG_REG(0x020) +#define GRXSTS_FN_MASK (0x7f << 25) +#define GRXSTS_FN_SHIFT 25 +#define GRXSTS_PKTSTS_MASK (0xf << 17) +#define GRXSTS_PKTSTS_SHIFT 17 +#define GRXSTS_PKTSTS_GLOBALOUTNAK 1 +#define GRXSTS_PKTSTS_OUTRX 2 +#define GRXSTS_PKTSTS_HCHIN 2 +#define GRXSTS_PKTSTS_OUTDONE 3 +#define GRXSTS_PKTSTS_HCHIN_XFER_COMP 3 +#define GRXSTS_PKTSTS_SETUPDONE 4 +#define GRXSTS_PKTSTS_DATATOGGLEERR 5 +#define GRXSTS_PKTSTS_SETUPRX 6 +#define GRXSTS_PKTSTS_HCHHALTED 7 +#define GRXSTS_HCHNUM_MASK (0xf << 0) +#define GRXSTS_HCHNUM_SHIFT 0 +#define GRXSTS_DPID_MASK (0x3 << 15) +#define GRXSTS_DPID_SHIFT 15 +#define GRXSTS_BYTECNT_MASK (0x7ff << 4) +#define GRXSTS_BYTECNT_SHIFT 4 +#define GRXSTS_EPNUM_MASK (0xf << 0) +#define GRXSTS_EPNUM_SHIFT 0 + +#define GRXFSIZ HSOTG_REG(0x024) +#define GRXFSIZ_DEPTH_MASK (0xffff << 0) +#define GRXFSIZ_DEPTH_SHIFT 0 + +#define GNPTXFSIZ HSOTG_REG(0x028) +/* Use FIFOSIZE_* constants to access this register */ + +#define GNPTXSTS HSOTG_REG(0x02C) +#define GNPTXSTS_NP_TXQ_TOP_MASK (0x7f << 24) +#define GNPTXSTS_NP_TXQ_TOP_SHIFT 24 +#define GNPTXSTS_NP_TXQ_SPC_AVAIL_MASK (0xff << 16) +#define GNPTXSTS_NP_TXQ_SPC_AVAIL_SHIFT 16 +#define GNPTXSTS_NP_TXQ_SPC_AVAIL_GET(_v) (((_v) >> 16) & 0xff) +#define GNPTXSTS_NP_TXF_SPC_AVAIL_MASK (0xffff << 0) +#define GNPTXSTS_NP_TXF_SPC_AVAIL_SHIFT 0 +#define GNPTXSTS_NP_TXF_SPC_AVAIL_GET(_v) (((_v) >> 0) & 0xffff) + +#define GI2CCTL HSOTG_REG(0x0030) +#define GI2CCTL_BSYDNE BIT(31) +#define GI2CCTL_RW BIT(30) +#define GI2CCTL_I2CDATSE0 BIT(28) +#define GI2CCTL_I2CDEVADDR_MASK (0x3 << 26) +#define GI2CCTL_I2CDEVADDR_SHIFT 26 +#define GI2CCTL_I2CSUSPCTL BIT(25) +#define GI2CCTL_ACK BIT(24) +#define GI2CCTL_I2CEN BIT(23) +#define GI2CCTL_ADDR_MASK (0x7f << 16) +#define GI2CCTL_ADDR_SHIFT 16 +#define GI2CCTL_REGADDR_MASK (0xff << 8) +#define GI2CCTL_REGADDR_SHIFT 8 +#define GI2CCTL_RWDATA_MASK (0xff << 0) +#define GI2CCTL_RWDATA_SHIFT 0 + +#define GPVNDCTL HSOTG_REG(0x0034) +#define GPVNDCTL_REGWR BIT(22) +#define GPVNDCTL_NEWREGREQ BIT(25) +#define GPVNDCTL_VSTSDONE BIT(27) +#define GPVNDCTL_REGADDR_SHIFT 16 +#define GPVNDCTL_REGADDR_MASK (0x3f << 16) +#define GPVNDCTL_REGDATA_SHIFT 0 +#define GPVNDCTL_REGDATA_MASK 0xff + +#define GGPIO HSOTG_REG(0x0038) +#define GGPIO_STM32_OTG_GCCFG_PWRDWN BIT(16) + +#define GUID HSOTG_REG(0x003c) +#define GSNPSID HSOTG_REG(0x0040) +#define GHWCFG1 HSOTG_REG(0x0044) +#define GSNPSID_ID_MASK GENMASK(31, 16) + +#define GHWCFG2 HSOTG_REG(0x0048) +#define GHWCFG2_OTG_ENABLE_IC_USB BIT(31) +#define GHWCFG2_DEV_TOKEN_Q_DEPTH_MASK (0x1f << 26) +#define GHWCFG2_DEV_TOKEN_Q_DEPTH_SHIFT 26 +#define GHWCFG2_HOST_PERIO_TX_Q_DEPTH_MASK (0x3 << 24) +#define GHWCFG2_HOST_PERIO_TX_Q_DEPTH_SHIFT 24 +#define GHWCFG2_NONPERIO_TX_Q_DEPTH_MASK (0x3 << 22) +#define GHWCFG2_NONPERIO_TX_Q_DEPTH_SHIFT 22 +#define GHWCFG2_MULTI_PROC_INT BIT(20) +#define GHWCFG2_DYNAMIC_FIFO BIT(19) +#define GHWCFG2_PERIO_EP_SUPPORTED BIT(18) +#define GHWCFG2_NUM_HOST_CHAN_MASK (0xf << 14) +#define GHWCFG2_NUM_HOST_CHAN_SHIFT 14 +#define GHWCFG2_NUM_DEV_EP_MASK (0xf << 10) +#define GHWCFG2_NUM_DEV_EP_SHIFT 10 +#define GHWCFG2_FS_PHY_TYPE_MASK (0x3 << 8) +#define GHWCFG2_FS_PHY_TYPE_SHIFT 8 +#define GHWCFG2_FS_PHY_TYPE_NOT_SUPPORTED 0 +#define GHWCFG2_FS_PHY_TYPE_DEDICATED 1 +#define GHWCFG2_FS_PHY_TYPE_SHARED_UTMI 2 +#define GHWCFG2_FS_PHY_TYPE_SHARED_ULPI 3 +#define GHWCFG2_HS_PHY_TYPE_MASK (0x3 << 6) +#define GHWCFG2_HS_PHY_TYPE_SHIFT 6 +#define GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED 0 +#define GHWCFG2_HS_PHY_TYPE_UTMI 1 +#define GHWCFG2_HS_PHY_TYPE_ULPI 2 +#define GHWCFG2_HS_PHY_TYPE_UTMI_ULPI 3 +#define GHWCFG2_POINT2POINT BIT(5) +#define GHWCFG2_ARCHITECTURE_MASK (0x3 << 3) +#define GHWCFG2_ARCHITECTURE_SHIFT 3 +#define GHWCFG2_SLAVE_ONLY_ARCH 0 +#define GHWCFG2_EXT_DMA_ARCH 1 +#define GHWCFG2_INT_DMA_ARCH 2 +#define GHWCFG2_OP_MODE_MASK (0x7 << 0) +#define GHWCFG2_OP_MODE_SHIFT 0 +#define GHWCFG2_OP_MODE_HNP_SRP_CAPABLE 0 +#define GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE 1 +#define GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE 2 +#define GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE 3 +#define GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE 4 +#define GHWCFG2_OP_MODE_SRP_CAPABLE_HOST 5 +#define GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST 6 +#define GHWCFG2_OP_MODE_UNDEFINED 7 + +#define GHWCFG3 HSOTG_REG(0x004c) +#define GHWCFG3_DFIFO_DEPTH_MASK (0xffff << 16) +#define GHWCFG3_DFIFO_DEPTH_SHIFT 16 +#define GHWCFG3_OTG_LPM_EN BIT(15) +#define GHWCFG3_BC_SUPPORT BIT(14) +#define GHWCFG3_OTG_ENABLE_HSIC BIT(13) +#define GHWCFG3_ADP_SUPP BIT(12) +#define GHWCFG3_SYNCH_RESET_TYPE BIT(11) +#define GHWCFG3_OPTIONAL_FEATURES BIT(10) +#define GHWCFG3_VENDOR_CTRL_IF BIT(9) +#define GHWCFG3_I2C BIT(8) +#define GHWCFG3_OTG_FUNC BIT(7) +#define GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK (0x7 << 4) +#define GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT 4 +#define GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK (0xf << 0) +#define GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT 0 + +#define GHWCFG4 HSOTG_REG(0x0050) +#define GHWCFG4_DESC_DMA_DYN BIT(31) +#define GHWCFG4_DESC_DMA BIT(30) +#define GHWCFG4_NUM_IN_EPS_MASK (0xf << 26) +#define GHWCFG4_NUM_IN_EPS_SHIFT 26 +#define GHWCFG4_DED_FIFO_EN BIT(25) +#define GHWCFG4_DED_FIFO_SHIFT 25 +#define GHWCFG4_SESSION_END_FILT_EN BIT(24) +#define GHWCFG4_B_VALID_FILT_EN BIT(23) +#define GHWCFG4_A_VALID_FILT_EN BIT(22) +#define GHWCFG4_VBUS_VALID_FILT_EN BIT(21) +#define GHWCFG4_IDDIG_FILT_EN BIT(20) +#define GHWCFG4_NUM_DEV_MODE_CTRL_EP_MASK (0xf << 16) +#define GHWCFG4_NUM_DEV_MODE_CTRL_EP_SHIFT 16 +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK (0x3 << 14) +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT 14 +#define GHWCFG4_ACG_SUPPORTED BIT(12) +#define GHWCFG4_IPG_ISOC_SUPPORTED BIT(11) +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8 0 +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_16 1 +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16 2 +#define GHWCFG4_XHIBER BIT(7) +#define GHWCFG4_HIBER BIT(6) +#define GHWCFG4_MIN_AHB_FREQ BIT(5) +#define GHWCFG4_POWER_OPTIMIZ BIT(4) +#define GHWCFG4_NUM_DEV_PERIO_IN_EP_MASK (0xf << 0) +#define GHWCFG4_NUM_DEV_PERIO_IN_EP_SHIFT 0 + +#define GLPMCFG HSOTG_REG(0x0054) +#define GLPMCFG_INVSELHSIC BIT(31) +#define GLPMCFG_HSICCON BIT(30) +#define GLPMCFG_RSTRSLPSTS BIT(29) +#define GLPMCFG_ENBESL BIT(28) +#define GLPMCFG_LPM_RETRYCNT_STS_MASK (0x7 << 25) +#define GLPMCFG_LPM_RETRYCNT_STS_SHIFT 25 +#define GLPMCFG_SNDLPM BIT(24) +#define GLPMCFG_RETRY_CNT_MASK (0x7 << 21) +#define GLPMCFG_RETRY_CNT_SHIFT 21 +#define GLPMCFG_LPM_CHNL_INDX_MASK (0xf << 17) +#define GLPMCFG_LPM_CHNL_INDX_SHIFT 17 +#define GLPMCFG_L1RESUMEOK BIT(16) +#define GLPMCFG_SLPSTS BIT(15) +#define GLPMCFG_COREL1RES_MASK (0x3 << 13) +#define GLPMCFG_COREL1RES_SHIFT 13 +#define GLPMCFG_HIRD_THRES_MASK (0x1f << 8) +#define GLPMCFG_HIRD_THRES_SHIFT 8 +#define GLPMCFG_HIRD_THRES_EN (0x10 << 8) +#define GLPMCFG_ENBLSLPM BIT(7) +#define GLPMCFG_BREMOTEWAKE BIT(6) +#define GLPMCFG_HIRD_MASK (0xf << 2) +#define GLPMCFG_HIRD_SHIFT 2 +#define GLPMCFG_APPL1RES BIT(1) +#define GLPMCFG_LPMCAP BIT(0) + +#define GPWRDN HSOTG_REG(0x0058) +#define GPWRDN_MULT_VAL_ID_BC_MASK (0x1f << 24) +#define GPWRDN_MULT_VAL_ID_BC_SHIFT 24 +#define GPWRDN_ADP_INT BIT(23) +#define GPWRDN_BSESSVLD BIT(22) +#define GPWRDN_IDSTS BIT(21) +#define GPWRDN_LINESTATE_MASK (0x3 << 19) +#define GPWRDN_LINESTATE_SHIFT 19 +#define GPWRDN_STS_CHGINT_MSK BIT(18) +#define GPWRDN_STS_CHGINT BIT(17) +#define GPWRDN_SRP_DET_MSK BIT(16) +#define GPWRDN_SRP_DET BIT(15) +#define GPWRDN_CONNECT_DET_MSK BIT(14) +#define GPWRDN_CONNECT_DET BIT(13) +#define GPWRDN_DISCONN_DET_MSK BIT(12) +#define GPWRDN_DISCONN_DET BIT(11) +#define GPWRDN_RST_DET_MSK BIT(10) +#define GPWRDN_RST_DET BIT(9) +#define GPWRDN_LNSTSCHG_MSK BIT(8) +#define GPWRDN_LNSTSCHG BIT(7) +#define GPWRDN_DIS_VBUS BIT(6) +#define GPWRDN_PWRDNSWTCH BIT(5) +#define GPWRDN_PWRDNRSTN BIT(4) +#define GPWRDN_PWRDNCLMP BIT(3) +#define GPWRDN_RESTORE BIT(2) +#define GPWRDN_PMUACTV BIT(1) +#define GPWRDN_PMUINTSEL BIT(0) + +#define GDFIFOCFG HSOTG_REG(0x005c) +#define GDFIFOCFG_EPINFOBASE_MASK (0xffff << 16) +#define GDFIFOCFG_EPINFOBASE_SHIFT 16 +#define GDFIFOCFG_GDFIFOCFG_MASK (0xffff << 0) +#define GDFIFOCFG_GDFIFOCFG_SHIFT 0 + +#define ADPCTL HSOTG_REG(0x0060) +#define ADPCTL_AR_MASK (0x3 << 27) +#define ADPCTL_AR_SHIFT 27 +#define ADPCTL_ADP_TMOUT_INT_MSK BIT(26) +#define ADPCTL_ADP_SNS_INT_MSK BIT(25) +#define ADPCTL_ADP_PRB_INT_MSK BIT(24) +#define ADPCTL_ADP_TMOUT_INT BIT(23) +#define ADPCTL_ADP_SNS_INT BIT(22) +#define ADPCTL_ADP_PRB_INT BIT(21) +#define ADPCTL_ADPENA BIT(20) +#define ADPCTL_ADPRES BIT(19) +#define ADPCTL_ENASNS BIT(18) +#define ADPCTL_ENAPRB BIT(17) +#define ADPCTL_RTIM_MASK (0x7ff << 6) +#define ADPCTL_RTIM_SHIFT 6 +#define ADPCTL_PRB_PER_MASK (0x3 << 4) +#define ADPCTL_PRB_PER_SHIFT 4 +#define ADPCTL_PRB_DELTA_MASK (0x3 << 2) +#define ADPCTL_PRB_DELTA_SHIFT 2 +#define ADPCTL_PRB_DSCHRG_MASK (0x3 << 0) +#define ADPCTL_PRB_DSCHRG_SHIFT 0 + +#define HPTXFSIZ HSOTG_REG(0x100) +/* Use FIFOSIZE_* constants to access this register */ + +#define DPTXFSIZN(_a) HSOTG_REG(0x104 + (((_a) - 1) * 4)) +/* Use FIFOSIZE_* constants to access this register */ + +/* These apply to the GNPTXFSIZ, HPTXFSIZ and DPTXFSIZN registers */ +#define FIFOSIZE_DEPTH_MASK (0xffff << 16) +#define FIFOSIZE_DEPTH_SHIFT 16 +#define FIFOSIZE_STARTADDR_MASK (0xffff << 0) +#define FIFOSIZE_STARTADDR_SHIFT 0 +#define FIFOSIZE_DEPTH_GET(_x) (((_x) >> 16) & 0xffff) + +/* Device mode registers */ + +#define DCFG HSOTG_REG(0x800) +#define DCFG_DESCDMA_EN BIT(23) +#define DCFG_EPMISCNT_MASK (0x1f << 18) +#define DCFG_EPMISCNT_SHIFT 18 +#define DCFG_EPMISCNT_LIMIT 0x1f +#define DCFG_EPMISCNT(_x) ((_x) << 18) +#define DCFG_IPG_ISOC_SUPPORDED BIT(17) +#define DCFG_PERFRINT_MASK (0x3 << 11) +#define DCFG_PERFRINT_SHIFT 11 +#define DCFG_PERFRINT_LIMIT 0x3 +#define DCFG_PERFRINT(_x) ((_x) << 11) +#define DCFG_DEVADDR_MASK (0x7f << 4) +#define DCFG_DEVADDR_SHIFT 4 +#define DCFG_DEVADDR_LIMIT 0x7f +#define DCFG_DEVADDR(_x) ((_x) << 4) +#define DCFG_NZ_STS_OUT_HSHK BIT(2) +#define DCFG_DEVSPD_MASK (0x3 << 0) +#define DCFG_DEVSPD_SHIFT 0 +#define DCFG_DEVSPD_HS 0 +#define DCFG_DEVSPD_FS 1 +#define DCFG_DEVSPD_LS 2 +#define DCFG_DEVSPD_FS48 3 + +#define DCTL HSOTG_REG(0x804) +#define DCTL_PWRONPRGDONE BIT(11) +#define DCTL_CGOUTNAK BIT(10) +#define DCTL_SGOUTNAK BIT(9) +#define DCTL_CGNPINNAK BIT(8) +#define DCTL_SGNPINNAK BIT(7) +#define DCTL_TSTCTL_MASK (0x7 << 4) +#define DCTL_TSTCTL_SHIFT 4 +#define DCTL_GOUTNAKSTS BIT(3) +#define DCTL_GNPINNAKSTS BIT(2) +#define DCTL_SFTDISCON BIT(1) +#define DCTL_RMTWKUPSIG BIT(0) + +#define DSTS HSOTG_REG(0x808) +#define DSTS_SOFFN_MASK (0x3fff << 8) +#define DSTS_SOFFN_SHIFT 8 +#define DSTS_SOFFN_LIMIT 0x3fff +#define DSTS_SOFFN(_x) ((_x) << 8) +#define DSTS_ERRATICERR BIT(3) +#define DSTS_ENUMSPD_MASK (0x3 << 1) +#define DSTS_ENUMSPD_SHIFT 1 +#define DSTS_ENUMSPD_HS 0 +#define DSTS_ENUMSPD_FS 1 +#define DSTS_ENUMSPD_LS 2 +#define DSTS_ENUMSPD_FS48 3 +#define DSTS_SUSPSTS BIT(0) + +#define DIEPMSK HSOTG_REG(0x810) +#define DIEPMSK_NAKMSK BIT(13) +#define DIEPMSK_BNAININTRMSK BIT(9) +#define DIEPMSK_TXFIFOUNDRNMSK BIT(8) +#define DIEPMSK_TXFIFOEMPTY BIT(7) +#define DIEPMSK_INEPNAKEFFMSK BIT(6) +#define DIEPMSK_INTKNEPMISMSK BIT(5) +#define DIEPMSK_INTKNTXFEMPMSK BIT(4) +#define DIEPMSK_TIMEOUTMSK BIT(3) +#define DIEPMSK_AHBERRMSK BIT(2) +#define DIEPMSK_EPDISBLDMSK BIT(1) +#define DIEPMSK_XFERCOMPLMSK BIT(0) + +#define DOEPMSK HSOTG_REG(0x814) +#define DOEPMSK_BNAMSK BIT(9) +#define DOEPMSK_BACK2BACKSETUP BIT(6) +#define DOEPMSK_STSPHSERCVDMSK BIT(5) +#define DOEPMSK_OUTTKNEPDISMSK BIT(4) +#define DOEPMSK_SETUPMSK BIT(3) +#define DOEPMSK_AHBERRMSK BIT(2) +#define DOEPMSK_EPDISBLDMSK BIT(1) +#define DOEPMSK_XFERCOMPLMSK BIT(0) + +#define DAINT HSOTG_REG(0x818) +#define DAINTMSK HSOTG_REG(0x81C) +#define DAINT_OUTEP_SHIFT 16 +#define DAINT_OUTEP(_x) (1 << ((_x) + 16)) +#define DAINT_INEP(_x) (1 << (_x)) + +#define DTKNQR1 HSOTG_REG(0x820) +#define DTKNQR2 HSOTG_REG(0x824) +#define DTKNQR3 HSOTG_REG(0x830) +#define DTKNQR4 HSOTG_REG(0x834) +#define DIEPEMPMSK HSOTG_REG(0x834) + +#define DVBUSDIS HSOTG_REG(0x828) +#define DVBUSPULSE HSOTG_REG(0x82C) + +#define DIEPCTL0 HSOTG_REG(0x900) +#define DIEPCTL(_a) HSOTG_REG(0x900 + ((_a) * 0x20)) + +#define DOEPCTL0 HSOTG_REG(0xB00) +#define DOEPCTL(_a) HSOTG_REG(0xB00 + ((_a) * 0x20)) + +/* EP0 specialness: + * bits[29..28] - reserved (no SetD0PID, SetD1PID) + * bits[25..22] - should always be zero, this isn't a periodic endpoint + * bits[10..0] - MPS setting different for EP0 + */ +#define D0EPCTL_MPS_MASK (0x3 << 0) +#define D0EPCTL_MPS_SHIFT 0 +#define D0EPCTL_MPS_64 0 +#define D0EPCTL_MPS_32 1 +#define D0EPCTL_MPS_16 2 +#define D0EPCTL_MPS_8 3 +#define D0EPCTL_MPS_LIMIT 64 + +#define DXEPCTL_EPENA BIT(31) +#define DXEPCTL_EPDIS BIT(30) +#define DXEPCTL_SETD1PID BIT(29) +#define DXEPCTL_SETODDFR BIT(29) +#define DXEPCTL_SETD0PID BIT(28) +#define DXEPCTL_SETEVENFR BIT(28) +#define DXEPCTL_SNAK BIT(27) +#define DXEPCTL_CNAK BIT(26) +#define DXEPCTL_TXFNUM_MASK (0xf << 22) +#define DXEPCTL_TXFNUM_SHIFT 22 +#define DXEPCTL_TXFNUM_LIMIT 0xf +#define DXEPCTL_TXFNUM(_x) ((_x) << 22) +#define DXEPCTL_STALL BIT(21) +#define DXEPCTL_SNP BIT(20) +#define DXEPCTL_EPTYPE_MASK (0x3 << 18) +#define DXEPCTL_EPTYPE_CONTROL (0x0 << 18) +#define DXEPCTL_EPTYPE_ISO (0x1 << 18) +#define DXEPCTL_EPTYPE_BULK (0x2 << 18) +#define DXEPCTL_EPTYPE_INTERRUPT (0x3 << 18) + +#define DXEPCTL_NAKSTS BIT(17) +#define DXEPCTL_DPID BIT(16) +#define DXEPCTL_EOFRNUM BIT(16) +#define DXEPCTL_USBACTEP BIT(15) +#define DXEPCTL_NEXTEP_MASK (0xf << 11) +#define DXEPCTL_NEXTEP_SHIFT 11 +#define DXEPCTL_NEXTEP_LIMIT 0xf +#define DXEPCTL_NEXTEP(_x) ((_x) << 11) +#define DXEPCTL_MPS_MASK (0x7ff << 0) +#define DXEPCTL_MPS_SHIFT 0 +#define DXEPCTL_MPS_LIMIT 0x7ff +#define DXEPCTL_MPS(_x) ((_x) << 0) + +#define DIEPINT(_a) HSOTG_REG(0x908 + ((_a) * 0x20)) +#define DOEPINT(_a) HSOTG_REG(0xB08 + ((_a) * 0x20)) +#define DXEPINT_SETUP_RCVD BIT(15) +#define DXEPINT_NYETINTRPT BIT(14) +#define DXEPINT_NAKINTRPT BIT(13) +#define DXEPINT_BBLEERRINTRPT BIT(12) +#define DXEPINT_PKTDRPSTS BIT(11) +#define DXEPINT_BNAINTR BIT(9) +#define DXEPINT_TXFIFOUNDRN BIT(8) +#define DXEPINT_OUTPKTERR BIT(8) +#define DXEPINT_TXFEMP BIT(7) +#define DXEPINT_INEPNAKEFF BIT(6) +#define DXEPINT_BACK2BACKSETUP BIT(6) +#define DXEPINT_INTKNEPMIS BIT(5) +#define DXEPINT_STSPHSERCVD BIT(5) +#define DXEPINT_INTKNTXFEMP BIT(4) +#define DXEPINT_OUTTKNEPDIS BIT(4) +#define DXEPINT_TIMEOUT BIT(3) +#define DXEPINT_SETUP BIT(3) +#define DXEPINT_AHBERR BIT(2) +#define DXEPINT_EPDISBLD BIT(1) +#define DXEPINT_XFERCOMPL BIT(0) + +#define DIEPTSIZ0 HSOTG_REG(0x910) +#define DIEPTSIZ0_PKTCNT_MASK (0x3 << 19) +#define DIEPTSIZ0_PKTCNT_SHIFT 19 +#define DIEPTSIZ0_PKTCNT_LIMIT 0x3 +#define DIEPTSIZ0_PKTCNT(_x) ((_x) << 19) +#define DIEPTSIZ0_XFERSIZE_MASK (0x7f << 0) +#define DIEPTSIZ0_XFERSIZE_SHIFT 0 +#define DIEPTSIZ0_XFERSIZE_LIMIT 0x7f +#define DIEPTSIZ0_XFERSIZE(_x) ((_x) << 0) + +#define DOEPTSIZ0 HSOTG_REG(0xB10) +#define DOEPTSIZ0_SUPCNT_MASK (0x3 << 29) +#define DOEPTSIZ0_SUPCNT_SHIFT 29 +#define DOEPTSIZ0_SUPCNT_LIMIT 0x3 +#define DOEPTSIZ0_SUPCNT(_x) ((_x) << 29) +#define DOEPTSIZ0_PKTCNT BIT(19) +#define DOEPTSIZ0_XFERSIZE_MASK (0x7f << 0) +#define DOEPTSIZ0_XFERSIZE_SHIFT 0 + +#define DIEPTSIZ(_a) HSOTG_REG(0x910 + ((_a) * 0x20)) +#define DOEPTSIZ(_a) HSOTG_REG(0xB10 + ((_a) * 0x20)) +#define DXEPTSIZ_MC_MASK (0x3 << 29) +#define DXEPTSIZ_MC_SHIFT 29 +#define DXEPTSIZ_MC_LIMIT 0x3 +#define DXEPTSIZ_MC(_x) ((_x) << 29) +#define DXEPTSIZ_PKTCNT_MASK (0x3ff << 19) +#define DXEPTSIZ_PKTCNT_SHIFT 19 +#define DXEPTSIZ_PKTCNT_LIMIT 0x3ff +#define DXEPTSIZ_PKTCNT_GET(_v) (((_v) >> 19) & 0x3ff) +#define DXEPTSIZ_PKTCNT(_x) ((_x) << 19) +#define DXEPTSIZ_XFERSIZE_MASK (0x7ffff << 0) +#define DXEPTSIZ_XFERSIZE_SHIFT 0 +#define DXEPTSIZ_XFERSIZE_LIMIT 0x7ffff +#define DXEPTSIZ_XFERSIZE_GET(_v) (((_v) >> 0) & 0x7ffff) +#define DXEPTSIZ_XFERSIZE(_x) ((_x) << 0) + +#define DIEPDMA(_a) HSOTG_REG(0x914 + ((_a) * 0x20)) +#define DOEPDMA(_a) HSOTG_REG(0xB14 + ((_a) * 0x20)) + +#define DTXFSTS(_a) HSOTG_REG(0x918 + ((_a) * 0x20)) + +#define PCGCTL HSOTG_REG(0x0e00) +#define PCGCTL_IF_DEV_MODE BIT(31) +#define PCGCTL_P2HD_PRT_SPD_MASK (0x3 << 29) +#define PCGCTL_P2HD_PRT_SPD_SHIFT 29 +#define PCGCTL_P2HD_DEV_ENUM_SPD_MASK (0x3 << 27) +#define PCGCTL_P2HD_DEV_ENUM_SPD_SHIFT 27 +#define PCGCTL_MAC_DEV_ADDR_MASK (0x7f << 20) +#define PCGCTL_MAC_DEV_ADDR_SHIFT 20 +#define PCGCTL_MAX_TERMSEL BIT(19) +#define PCGCTL_MAX_XCVRSELECT_MASK (0x3 << 17) +#define PCGCTL_MAX_XCVRSELECT_SHIFT 17 +#define PCGCTL_PORT_POWER BIT(16) +#define PCGCTL_PRT_CLK_SEL_MASK (0x3 << 14) +#define PCGCTL_PRT_CLK_SEL_SHIFT 14 +#define PCGCTL_ESS_REG_RESTORED BIT(13) +#define PCGCTL_EXTND_HIBER_SWITCH BIT(12) +#define PCGCTL_EXTND_HIBER_PWRCLMP BIT(11) +#define PCGCTL_ENBL_EXTND_HIBER BIT(10) +#define PCGCTL_RESTOREMODE BIT(9) +#define PCGCTL_RESETAFTSUSP BIT(8) +#define PCGCTL_DEEP_SLEEP BIT(7) +#define PCGCTL_PHY_IN_SLEEP BIT(6) +#define PCGCTL_ENBL_SLEEP_GATING BIT(5) +#define PCGCTL_RSTPDWNMODULE BIT(3) +#define PCGCTL_PWRCLMP BIT(2) +#define PCGCTL_GATEHCLK BIT(1) +#define PCGCTL_STOPPCLK BIT(0) + +#define PCGCCTL1 HSOTG_REG(0xe04) +#define PCGCCTL1_TIMER (0x3 << 1) +#define PCGCCTL1_GATEEN BIT(0) + +#define EPFIFO(_a) HSOTG_REG(0x1000 + ((_a) * 0x1000)) + +/* Host Mode Registers */ + +#define HCFG HSOTG_REG(0x0400) +#define HCFG_MODECHTIMEN BIT(31) +#define HCFG_PERSCHEDENA BIT(26) +#define HCFG_FRLISTEN_MASK (0x3 << 24) +#define HCFG_FRLISTEN_SHIFT 24 +#define HCFG_FRLISTEN_8 (0 << 24) +#define FRLISTEN_8_SIZE 8 +#define HCFG_FRLISTEN_16 BIT(24) +#define FRLISTEN_16_SIZE 16 +#define HCFG_FRLISTEN_32 (2 << 24) +#define FRLISTEN_32_SIZE 32 +#define HCFG_FRLISTEN_64 (3 << 24) +#define FRLISTEN_64_SIZE 64 +#define HCFG_DESCDMA BIT(23) +#define HCFG_RESVALID_MASK (0xff << 8) +#define HCFG_RESVALID_SHIFT 8 +#define HCFG_ENA32KHZ BIT(7) +#define HCFG_FSLSSUPP BIT(2) +#define HCFG_FSLSPCLKSEL_MASK (0x3 << 0) +#define HCFG_FSLSPCLKSEL_SHIFT 0 +#define HCFG_FSLSPCLKSEL_30_60_MHZ 0 +#define HCFG_FSLSPCLKSEL_48_MHZ 1 +#define HCFG_FSLSPCLKSEL_6_MHZ 2 + +#define HFIR HSOTG_REG(0x0404) +#define HFIR_FRINT_MASK (0xffff << 0) +#define HFIR_FRINT_SHIFT 0 +#define HFIR_RLDCTRL BIT(16) + +#define HFNUM HSOTG_REG(0x0408) +#define HFNUM_FRREM_MASK (0xffff << 16) +#define HFNUM_FRREM_SHIFT 16 +#define HFNUM_FRNUM_MASK (0xffff << 0) +#define HFNUM_FRNUM_SHIFT 0 +#define HFNUM_MAX_FRNUM 0x3fff + +#define HPTXSTS HSOTG_REG(0x0410) +#define TXSTS_QTOP_ODD BIT(31) +#define TXSTS_QTOP_CHNEP_MASK (0xf << 27) +#define TXSTS_QTOP_CHNEP_SHIFT 27 +#define TXSTS_QTOP_TOKEN_MASK (0x3 << 25) +#define TXSTS_QTOP_TOKEN_SHIFT 25 +#define TXSTS_QTOP_TERMINATE BIT(24) +#define TXSTS_QSPCAVAIL_MASK (0xff << 16) +#define TXSTS_QSPCAVAIL_SHIFT 16 +#define TXSTS_FSPCAVAIL_MASK (0xffff << 0) +#define TXSTS_FSPCAVAIL_SHIFT 0 + +#define HAINT HSOTG_REG(0x0414) +#define HAINTMSK HSOTG_REG(0x0418) +#define HFLBADDR HSOTG_REG(0x041c) + +#define HPRT0 HSOTG_REG(0x0440) +#define HPRT0_SPD_MASK (0x3 << 17) +#define HPRT0_SPD_SHIFT 17 +#define HPRT0_SPD_HIGH_SPEED 0 +#define HPRT0_SPD_FULL_SPEED 1 +#define HPRT0_SPD_LOW_SPEED 2 +#define HPRT0_TSTCTL_MASK (0xf << 13) +#define HPRT0_TSTCTL_SHIFT 13 +#define HPRT0_PWR BIT(12) +#define HPRT0_LNSTS_MASK (0x3 << 10) +#define HPRT0_LNSTS_SHIFT 10 +#define HPRT0_RST BIT(8) +#define HPRT0_SUSP BIT(7) +#define HPRT0_RES BIT(6) +#define HPRT0_OVRCURRCHG BIT(5) +#define HPRT0_OVRCURRACT BIT(4) +#define HPRT0_ENACHG BIT(3) +#define HPRT0_ENA BIT(2) +#define HPRT0_CONNDET BIT(1) +#define HPRT0_CONNSTS BIT(0) + +#define HCCHAR(_ch) HSOTG_REG(0x0500 + 0x20 * (_ch)) +#define HCCHAR_CHENA BIT(31) +#define HCCHAR_CHDIS BIT(30) +#define HCCHAR_ODDFRM BIT(29) +#define HCCHAR_DEVADDR_MASK (0x7f << 22) +#define HCCHAR_DEVADDR_SHIFT 22 +#define HCCHAR_MULTICNT_MASK (0x3 << 20) +#define HCCHAR_MULTICNT_SHIFT 20 +#define HCCHAR_EPTYPE_MASK (0x3 << 18) +#define HCCHAR_EPTYPE_SHIFT 18 +#define HCCHAR_LSPDDEV BIT(17) +#define HCCHAR_EPDIR BIT(15) +#define HCCHAR_EPNUM_MASK (0xf << 11) +#define HCCHAR_EPNUM_SHIFT 11 +#define HCCHAR_MPS_MASK (0x7ff << 0) +#define HCCHAR_MPS_SHIFT 0 + +#define HCSPLT(_ch) HSOTG_REG(0x0504 + 0x20 * (_ch)) +#define HCSPLT_SPLTENA BIT(31) +#define HCSPLT_COMPSPLT BIT(16) +#define HCSPLT_XACTPOS_MASK (0x3 << 14) +#define HCSPLT_XACTPOS_SHIFT 14 +#define HCSPLT_XACTPOS_MID 0 +#define HCSPLT_XACTPOS_END 1 +#define HCSPLT_XACTPOS_BEGIN 2 +#define HCSPLT_XACTPOS_ALL 3 +#define HCSPLT_HUBADDR_MASK (0x7f << 7) +#define HCSPLT_HUBADDR_SHIFT 7 +#define HCSPLT_PRTADDR_MASK (0x7f << 0) +#define HCSPLT_PRTADDR_SHIFT 0 + +#define HCINT(_ch) HSOTG_REG(0x0508 + 0x20 * (_ch)) +#define HCINTMSK(_ch) HSOTG_REG(0x050c + 0x20 * (_ch)) +#define HCINTMSK_RESERVED14_31 (0x3ffff << 14) +#define HCINTMSK_FRM_LIST_ROLL BIT(13) +#define HCINTMSK_XCS_XACT BIT(12) +#define HCINTMSK_BNA BIT(11) +#define HCINTMSK_DATATGLERR BIT(10) +#define HCINTMSK_FRMOVRUN BIT(9) +#define HCINTMSK_BBLERR BIT(8) +#define HCINTMSK_XACTERR BIT(7) +#define HCINTMSK_NYET BIT(6) +#define HCINTMSK_ACK BIT(5) +#define HCINTMSK_NAK BIT(4) +#define HCINTMSK_STALL BIT(3) +#define HCINTMSK_AHBERR BIT(2) +#define HCINTMSK_CHHLTD BIT(1) +#define HCINTMSK_XFERCOMPL BIT(0) + +#define HCTSIZ(_ch) HSOTG_REG(0x0510 + 0x20 * (_ch)) +#define TSIZ_DOPNG BIT(31) +#define TSIZ_SC_MC_PID_MASK (0x3 << 29) +#define TSIZ_SC_MC_PID_SHIFT 29 +#define TSIZ_SC_MC_PID_DATA0 0 +#define TSIZ_SC_MC_PID_DATA2 1 +#define TSIZ_SC_MC_PID_DATA1 2 +#define TSIZ_SC_MC_PID_MDATA 3 +#define TSIZ_SC_MC_PID_SETUP 3 +#define TSIZ_PKTCNT_MASK (0x3ff << 19) +#define TSIZ_PKTCNT_SHIFT 19 +#define TSIZ_NTD_MASK (0xff << 8) +#define TSIZ_NTD_SHIFT 8 +#define TSIZ_SCHINFO_MASK (0xff << 0) +#define TSIZ_SCHINFO_SHIFT 0 +#define TSIZ_XFERSIZE_MASK (0x7ffff << 0) +#define TSIZ_XFERSIZE_SHIFT 0 + +#define HCDMA(_ch) HSOTG_REG(0x0514 + 0x20 * (_ch)) + +#define HCDMAB(_ch) HSOTG_REG(0x051c + 0x20 * (_ch)) + +#define HCFIFO(_ch) HSOTG_REG(0x1000 + 0x1000 * (_ch)) + +/** + * struct dwc2_dma_desc - DMA descriptor structure, + * used for both host and gadget modes + * + * @status: DMA descriptor status quadlet + * @buf: DMA descriptor data buffer pointer + * + * DMA Descriptor structure contains two quadlets: + * Status quadlet and Data buffer pointer. + */ +struct dwc2_dma_desc { + u32 status; + u32 buf; +} __packed; + +/* Host Mode DMA descriptor status quadlet */ + +#define HOST_DMA_A BIT(31) +#define HOST_DMA_STS_MASK (0x3 << 28) +#define HOST_DMA_STS_SHIFT 28 +#define HOST_DMA_STS_PKTERR BIT(28) +#define HOST_DMA_EOL BIT(26) +#define HOST_DMA_IOC BIT(25) +#define HOST_DMA_SUP BIT(24) +#define HOST_DMA_ALT_QTD BIT(23) +#define HOST_DMA_QTD_OFFSET_MASK (0x3f << 17) +#define HOST_DMA_QTD_OFFSET_SHIFT 17 +#define HOST_DMA_ISOC_NBYTES_MASK (0xfff << 0) +#define HOST_DMA_ISOC_NBYTES_SHIFT 0 +#define HOST_DMA_NBYTES_MASK (0x1ffff << 0) +#define HOST_DMA_NBYTES_SHIFT 0 +#define HOST_DMA_NBYTES_LIMIT 131071 + +#define MAX_DMA_DESC_NUM_GENERIC 64 +#define MAX_DMA_DESC_NUM_HS_ISOC 256 + + /* DWC OTG HW Release versions */ +#define DWC2_CORE_REV_2_71a 0x4f54271a +#define DWC2_CORE_REV_2_72a 0x4f54272a +#define DWC2_CORE_REV_2_80a 0x4f54280a +#define DWC2_CORE_REV_2_90a 0x4f54290a +#define DWC2_CORE_REV_2_91a 0x4f54291a +#define DWC2_CORE_REV_2_92a 0x4f54292a +#define DWC2_CORE_REV_2_94a 0x4f54294a +#define DWC2_CORE_REV_3_00a 0x4f54300a +#define DWC2_CORE_REV_3_10a 0x4f54310a +#define DWC2_CORE_REV_4_00a 0x4f54400a +#define DWC2_FS_IOT_REV_1_00a 0x5531100a +#define DWC2_HS_IOT_REV_1_00a 0x5532100a + + /* DWC OTG HW Core ID */ +#define DWC2_OTG_ID 0x4f540000 +#define DWC2_FS_IOT_ID 0x55310000 +#define DWC2_HS_IOT_ID 0x55320000 + +#endif /* __DWC2_H__ */ diff --git a/drivers/usb/dwc2/rhub.c b/drivers/usb/dwc2/rhub.c new file mode 100644 index 0000000000..de4d2837ac --- /dev/null +++ b/drivers/usb/dwc2/rhub.c @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include "dwc2.h" + +static struct descriptor { + struct usb_hub_descriptor hub; + struct usb_device_descriptor device; + struct usb_config_descriptor config; + struct usb_interface_descriptor interface; + struct usb_endpoint_descriptor endpoint; +} __packed descriptor = { + .hub = { + .bLength = USB_DT_HUB_NONVAR_SIZE + + ((USB_MAXCHILDREN + 1 + 7) / 8), + .bDescriptorType = USB_DT_HUB, + .bNbrPorts = 1, + .wHubCharacteristics = 0, + .bPwrOn2PwrGood = 0, + .bHubContrCurrent = 0, + .u.hs.DeviceRemovable = {0xff}, + .u.hs.PortPwrCtrlMask = {} + }, + .device = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = cpu_to_le16(2), /* v2.0 */ + .bDeviceClass = USB_CLASS_HUB, + .bDeviceSubClass = 0, + .bDeviceProtocol = USB_HUB_PR_HS_NO_TT, + .bMaxPacketSize0 = 64, + .idVendor = 0x0000, + .idProduct = 0x0000, + .bcdDevice = 0x0000, + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 0, + .bNumConfigurations = 1 + }, + .config = { + .bLength = USB_DT_CONFIG_SIZE, + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = cpu_to_le16( + USB_DT_CONFIG_SIZE + + USB_DT_INTERFACE_SIZE + + USB_DT_ENDPOINT_SIZE), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 0 + }, + .interface = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HUB, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = 0 + }, + .endpoint = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 1 | USB_DIR_IN, /* 0x81 */ + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16( + (USB_MAXCHILDREN + 1 + 7) / 8), + .bInterval = 255 + }, +}; + +static int dwc2_get_port_status(struct dwc2 *dwc2, struct usb_device *dev, + void *buf, int len) +{ + struct usb_port_status *portsts; + uint32_t hprt0; + uint32_t status = 0; + uint32_t change = 0; + int speed; + + if (!buf || len < sizeof(*portsts)) + return -1; + + hprt0 = dwc2_readl(dwc2, HPRT0); + + if (hprt0 & HPRT0_CONNSTS) + status |= USB_PORT_STAT_CONNECTION; + if (hprt0 & HPRT0_ENA) + status |= USB_PORT_STAT_ENABLE; + if (hprt0 & HPRT0_SUSP) + status |= USB_PORT_STAT_SUSPEND; + if (hprt0 & HPRT0_OVRCURRACT) + status |= USB_PORT_STAT_OVERCURRENT; + if (hprt0 & HPRT0_RST) + status |= USB_PORT_STAT_RESET; + if (hprt0 & HPRT0_PWR) + status |= USB_PORT_STAT_POWER; + + speed = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; + if (speed == HPRT0_SPD_HIGH_SPEED) + status |= USB_PORT_STAT_HIGH_SPEED; + else if (speed == HPRT0_SPD_LOW_SPEED) + status |= USB_PORT_STAT_LOW_SPEED; + + if (hprt0 & HPRT0_ENACHG) + change |= USB_PORT_STAT_C_ENABLE; + if (hprt0 & HPRT0_CONNDET) + change |= USB_PORT_STAT_C_CONNECTION; + if (hprt0 & HPRT0_OVRCURRCHG) + change |= USB_PORT_STAT_C_OVERCURRENT; + + portsts = buf; + portsts->wPortStatus = cpu_to_le16(status); + portsts->wPortChange = cpu_to_le16(change); + + dev->act_len = sizeof(*portsts); + dev->status = 0; + + return 0; +} + +static int dwc2_get_hub_status(struct dwc2 *dwc2, struct usb_device *dev, + void *buf, int len) +{ + if (!buf || len < 4) + return -1; + + *(uint32_t *)buf = 0; + dev->act_len = 4; + dev->status = 0; + + return 0; +} + +static int dwc2_get_hub_descriptor(struct dwc2 *dwc2, struct usb_device *dev, + void *buf, int len) +{ + if (!buf) + return -1; + + dev->act_len = min_t(int, len, descriptor.hub.bLength); + dev->status = 0; + memcpy(buf, &descriptor.hub, dev->act_len); + + return 0; +} + +static void strle16(__le16 *dest, char *src, size_t n) +{ + unsigned int i; + + for (i = 0; i < n && *src != '\0'; i++, src++) + dest[i] = cpu_to_le16(*src); +} + +static int dwc2_get_string_descriptor(struct dwc2 *dwc2, struct usb_device *dev, + void *buf, int len, int index) +{ + char *src, *str = buf; + __le16 *le16 = (__le16 *)(str + 2); + int size; + + if (!buf || len < 2) + return -1; + + switch (index) { + case 0: /* Language */ + src = "\x09\x04"; + size = strlen(src) + 2; + len = min_t(int, len, size); + + str[0] = size; + str[1] = 0x03; + memcpy(str + 2, src, len - 2); + break; + case 1: /* Vendor */ + src = "u-boot"; + size = 2 * strlen(src) + 2; + len = min_t(int, len, size); + + str[0] = size; + str[1] = 0x03; + strle16(le16, src, (len - 2) / 2); + break; + case 2: /* Product */ + src = "DWC2 root hub"; + size = 2 * strlen(src) + 2; + len = min_t(int, len, size); + + str[0] = size; + str[1] = 0x03; + strle16(le16, src, (len - 2) / 2); + break; + default: + dwc2_err(dwc2, "roothub: unknown string descriptor: 0x%x\n", + index); + return -1; + } + + dev->act_len = len; + dev->status = 0; + + return 0; +} + +static int dwc2_get_descriptor(struct dwc2 *dwc2, struct usb_device *dev, + void *buf, int len, int value) +{ + int index = value >> 8; + + if (!buf || len < 0) + return -1; + + switch (index) { + case USB_DT_DEVICE: + len = min(len, (int)descriptor.device.bLength); + memcpy(buf, &descriptor.device, len); + break; + case USB_DT_CONFIG: + len = min(len, (int)descriptor.config.wTotalLength); + memcpy(buf, &descriptor.config, len); + break; + case USB_DT_STRING: + value &= 0xff; + return dwc2_get_string_descriptor(dwc2, dev, buf, len, value); + default: + dwc2_err(dwc2, "roothub: unknown descriptor: 0x%x\n", index); + return -1; + } + + dev->act_len = len; + dev->status = 0; + + return 0; +} + +static int dwc2_set_port_feature(struct dwc2 *dwc2, struct usb_device *dev, + int feature) +{ + uint32_t hprt0; + + hprt0 = dwc2_readl(dwc2, HPRT0); + hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET | HPRT0_ENACHG | HPRT0_OVRCURRCHG); + + switch (feature) { + case USB_PORT_FEAT_SUSPEND: + break; + case USB_PORT_FEAT_RESET: + hprt0 |= HPRT0_RST; + dwc2_writel(dwc2, hprt0, HPRT0); + + mdelay(60); + + hprt0 = dwc2_readl(dwc2, HPRT0); + hprt0 &= ~HPRT0_RST; + dwc2_writel(dwc2, hprt0, HPRT0); + break; + case USB_PORT_FEAT_POWER: + break; + case USB_PORT_FEAT_ENABLE: + /* Set by the core after a reset */ + break; + default: + dwc2_dbg(dwc2, "roothub: unsupported set port feature 0x%x\n", + feature); + return -1; + } + + dev->act_len = 0; + dev->status = 0; + + return 0; +} + +static int dwc2_clear_port_feature(struct dwc2 *dwc2, struct usb_device *dev, + int feature) +{ + uint32_t hprt0; + + hprt0 = dwc2_readl(dwc2, HPRT0); + hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET | HPRT0_ENACHG | HPRT0_OVRCURRCHG); + + switch (feature) { + case USB_PORT_FEAT_ENABLE: + hprt0 |= HPRT0_ENA; + break; + case USB_PORT_FEAT_SUSPEND: + break; + case USB_PORT_FEAT_POWER: + break; + case USB_PORT_FEAT_C_CONNECTION: + hprt0 |= HPRT0_CONNDET; + break; + case USB_PORT_FEAT_C_ENABLE: + hprt0 |= HPRT0_ENACHG; + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + hprt0 |= HPRT0_OVRCURRCHG; + break; + default: + dwc2_dbg(dwc2, "roothub: unsupported clear port feature 0x%x\n", + feature); + return -1; + } + + dwc2_writel(dwc2, hprt0, HPRT0); + + dev->act_len = 0; + dev->status = 0; + + return 0; +} + +static int dwc2_set_address(struct dwc2 *dwc2, struct usb_device *dev, int addr) +{ + dwc2_dbg(dwc2, "roothub: set address to %d\n", addr); + dwc2->root_hub_devnum = addr; + + dev->act_len = 0; + dev->status = 0; + + return 0; +} + +int dwc2_submit_roothub(struct dwc2 *dwc2, struct usb_device *dev, + unsigned long pipe, void *buf, int len, + struct devrequest *setup) +{ + unsigned char reqtype = setup->requesttype; + unsigned char request = setup->request; + unsigned short value = le16_to_cpu(setup->value); + unsigned short size = le16_to_cpu(setup->length); + int minlen = min_t(int, len, size); + + if (usb_pipeint(pipe)) { + dwc2_err(dwc2, "roothub: submit IRQ NOT implemented\n"); + return 0; + } + + dev->act_len = 0; + dev->status = USB_ST_STALLED; + +#define REQ(l, u) ((l) | ((u) << 8)) + + switch (REQ(request, reqtype)) { + case REQ(USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB): + return dwc2_get_hub_descriptor(dwc2, dev, buf, minlen); + + case REQ(USB_REQ_GET_DESCRIPTOR, USB_DIR_IN): + return dwc2_get_descriptor(dwc2, dev, buf, minlen, value); + + case REQ(USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB): + return dwc2_get_hub_status(dwc2, dev, buf, len); + + case REQ(USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT): + return dwc2_get_port_status(dwc2, dev, buf, len); + + case REQ(USB_REQ_SET_FEATURE, USB_DIR_OUT | USB_RT_PORT): + return dwc2_set_port_feature(dwc2, dev, value); + + case REQ(USB_REQ_CLEAR_FEATURE, USB_DIR_OUT | USB_RT_PORT): + return dwc2_clear_port_feature(dwc2, dev, value); + + case REQ(USB_REQ_SET_ADDRESS, USB_DIR_OUT): + return dwc2_set_address(dwc2, dev, value); + + case REQ(USB_REQ_SET_CONFIGURATION, USB_DIR_OUT): + dev->act_len = 0; + dev->status = 0; + return 0; + + case REQ(USB_REQ_GET_CONFIGURATION, USB_DIR_IN): + *(char *)buf = 1; + dev->act_len = 1; + dev->status = 0; + return 0; + } + + dwc2_err(dwc2, "roothub: unsupported request 0x%x requesttype 0x%x\n", + request, reqtype); + + return 0; +} diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index aa04897d79..721c0dec63 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config USB_DWC3 tristate "DesignWare USB3 DRD Core Support" depends on USB && HAS_DMA diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index d0c812c883..6672913333 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_USB_DWC3) += dwc3.o diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 04b700d12d..8e6dc59a5d 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * core.c - DesignWare USB3 DRD Controller Core file * - * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com * * Authors: Felipe Balbi <balbi@ti.com>, * Sebastian Andrzej Siewior <bigeasy@linutronix.de> @@ -14,11 +14,13 @@ #include <dma.h> #include <driver.h> #include <init.h> +#include <linux/reset.h> -#include "gadget.h" #include "core.h" +#include "gadget.h" #include "io.h" +#include "debug.h" #define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */ @@ -29,7 +31,7 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc) { enum usb_dr_mode mode; - struct device_d *dev = dwc->dev; + struct device *dev = dwc->dev; unsigned int hw_mode; if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) @@ -66,8 +68,10 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc) * mode. If the controller supports DRD but the dr_mode is not * specified or set to OTG, then set the mode to peripheral. */ - if (mode == USB_DR_MODE_OTG && - dwc->revision >= DWC3_REVISION_330A) + if (mode == USB_DR_MODE_OTG && !dwc->edev && + (!IS_ENABLED(CONFIG_USB_ROLE_SWITCH) || + !of_property_read_bool(dwc->dev->of_node, "usb-role-switch")) && + !DWC3_VER_IS_PRIOR(DWC3, 330A)) mode = USB_DR_MODE_PERIPHERAL; } @@ -94,25 +98,28 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode) dwc->current_dr_role = mode; } +u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type) +{ + struct dwc3 *dwc = dep->dwc; + u32 reg; + + dwc3_writel(dwc->regs, DWC3_GDBGFIFOSPACE, + DWC3_GDBGFIFOSPACE_NUM(dep->number) | + DWC3_GDBGFIFOSPACE_TYPE(type)); + + reg = dwc3_readl(dwc->regs, DWC3_GDBGFIFOSPACE); + + return DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(reg); +} + /** * dwc3_core_soft_reset - Issues core soft reset and PHY reset * @dwc: pointer to our context structure */ -static int dwc3_core_soft_reset(struct dwc3 *dwc) +int dwc3_core_soft_reset(struct dwc3 *dwc) { u32 reg; int retries = 1000; - int ret; - - ret = phy_init(dwc->usb2_generic_phy); - if (ret < 0) - return ret; - - ret = phy_init(dwc->usb3_generic_phy); - if (ret < 0) { - phy_exit(dwc->usb2_generic_phy); - return ret; - } /* * We're resetting only the device side because, if we're in host mode, @@ -124,33 +131,94 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg |= DWC3_DCTL_CSFTRST; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + reg &= ~DWC3_DCTL_RUN_STOP; + dwc3_gadget_dctl_write_safe(dwc, 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_VER_IS_WITHIN(DWC31, 190A, ANY) || DWC3_IP_IS(DWC32)) + retries = 10; do { reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (!(reg & DWC3_DCTL_CSFTRST)) goto done; - udelay(1); + if (DWC3_VER_IS_WITHIN(DWC31, 190A, ANY) || DWC3_IP_IS(DWC32)) + mdelay(20); + else + udelay(1); } while (--retries); - phy_exit(dwc->usb3_generic_phy); - phy_exit(dwc->usb2_generic_phy); - + dev_warn(dwc->dev, "DWC3 controller soft reset failed.\n"); return -ETIMEDOUT; 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_VER_IS_WITHIN(DWC31, ANY, 180A)) mdelay(50); return 0; } +/* + * dwc3_frame_length_adjustment - Adjusts frame length if required + * @dwc3: Pointer to our controller context structure + */ +static void dwc3_frame_length_adjustment(struct dwc3 *dwc) +{ + u32 reg; + u32 dft; + + if (DWC3_VER_IS_PRIOR(DWC3, 250A)) + return; + + if (dwc->fladj == 0) + return; + + reg = dwc3_readl(dwc->regs, DWC3_GFLADJ); + dft = reg & DWC3_GFLADJ_30MHZ_MASK; + if (dft != dwc->fladj) { + reg &= ~DWC3_GFLADJ_30MHZ_MASK; + reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj; + dwc3_writel(dwc->regs, DWC3_GFLADJ, reg); + } +} + +/** + * dwc3_ref_clk_period - Reference clock period configuration + * Default reference clock period depends on hardware + * configuration. For systems with reference clock that differs + * from the default, this will set clock period in DWC3_GUCTL + * register. + * @dwc: Pointer to our controller context structure + */ +static void dwc3_ref_clk_period(struct dwc3 *dwc) +{ + u32 reg; + u32 dft; + + if (DWC3_VER_IS_PRIOR(DWC3, 250A)) + return; + + if (dwc->fladj == 0) + return; + + reg = dwc3_readl(dwc->regs, DWC3_GFLADJ); + dft = reg & DWC3_GFLADJ_30MHZ_MASK; + reg &= ~DWC3_GFLADJ_30MHZ_MASK; + reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj; + dwc3_writel(dwc->regs, DWC3_GFLADJ, reg); +} + /** * dwc3_free_one_event_buffer - Frees one event buffer * @dwc: Pointer to our controller context structure @@ -159,7 +227,7 @@ done: static void dwc3_free_one_event_buffer(struct dwc3 *dwc, struct dwc3_event_buffer *evt) { - dma_free_coherent(evt->buf, 0, sizeof(dma_addr_t)); + dma_free_coherent(evt->buf, evt->dma, evt->length); } /** @@ -171,16 +239,20 @@ static void dwc3_free_one_event_buffer(struct dwc3 *dwc, * otherwise ERR_PTR(errno). */ static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc, - unsigned length) + unsigned int length) { struct dwc3_event_buffer *evt; - evt = xzalloc(sizeof(*evt)); + evt = kzalloc(sizeof(*evt), GFP_KERNEL); if (!evt) return ERR_PTR(-ENOMEM); evt->dwc = dwc; evt->length = length; + evt->cache = kzalloc(length, GFP_KERNEL); + if (!evt->cache) + return ERR_PTR(-ENOMEM); + evt->buf = dma_alloc_coherent(length, &evt->dma); if (!evt->buf) return ERR_PTR(-ENOMEM); @@ -209,7 +281,7 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc) * Returns 0 on success otherwise negative errno. In the error case, dwc * may contain some buffers allocated but not all which were requested. */ -static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) +static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned int length) { struct dwc3_event_buffer *evt; @@ -229,7 +301,7 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) * * Returns 0 on success otherwise negative errno. */ -static int dwc3_event_buffers_setup(struct dwc3 *dwc) +int dwc3_event_buffers_setup(struct dwc3 *dwc) { struct dwc3_event_buffer *evt; @@ -246,8 +318,7 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc) return 0; } - -static void dwc3_event_buffers_cleanup(struct dwc3 *dwc) +void dwc3_event_buffers_cleanup(struct dwc3 *dwc) { struct dwc3_event_buffer *evt; @@ -290,11 +361,15 @@ static int dwc3_setup_scratch_buffers(struct dwc3 *dwc) if (!dwc->nr_scratch) return 0; - scratch_addr = dma_map_single(dwc->dev, dwc->scratchbuf, - dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE, - DMA_BIDIRECTIONAL); - if (dma_mapping_error(dwc->dev, scratch_addr)) { - dev_err(dwc->dev, "failed to map scratch buffer\n"); + /* should never fall here */ + if (!WARN_ON(dwc->scratchbuf)) + return 0; + + scratch_addr = dma_map_single(dwc->sysdev, dwc->scratchbuf, + dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(dwc->sysdev, scratch_addr)) { + dev_err(dwc->sysdev, "failed to map scratch buffer\n"); ret = -EFAULT; goto err0; } @@ -318,39 +393,28 @@ static int dwc3_setup_scratch_buffers(struct dwc3 *dwc) return 0; err1: - dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch * - DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL); + dma_unmap_single(dwc->sysdev, dwc->scratch_addr, dwc->nr_scratch * + DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL); err0: return ret; } -static const struct clk_bulk_data dwc3_core_clks[] = { - { .id = "ref" }, - { .id = "bus_early" }, - { .id = "suspend" }, -}; - -/* - * dwc3_frame_length_adjustment - Adjusts frame length if required - * @dwc3: Pointer to our controller context structure - */ -static void dwc3_frame_length_adjustment(struct dwc3 *dwc) +static void dwc3_free_scratch_buffers(struct dwc3 *dwc) { - u32 reg; - u32 dft; + if (!dwc->has_hibernation) + return; - if (dwc->revision < DWC3_REVISION_250A) + if (!dwc->nr_scratch) return; - if (dwc->fladj == 0) + /* should never fall here */ + if (!WARN_ON(dwc->scratchbuf)) return; - reg = dwc3_readl(dwc->regs, DWC3_GFLADJ); - dft = reg & DWC3_GFLADJ_30MHZ_MASK; - reg &= ~DWC3_GFLADJ_30MHZ_MASK; - reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj; - dwc3_writel(dwc->regs, DWC3_GFLADJ, reg); + dma_unmap_single(dwc->sysdev, dwc->scratch_addr, dwc->nr_scratch * + DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL); + kfree(dwc->scratchbuf); } static void dwc3_core_num_eps(struct dwc3 *dwc) @@ -373,6 +437,9 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc) parms->hwparams6 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6); parms->hwparams7 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS7); parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8); + + if (DWC3_IP_IS(DWC32)) + parms->hwparams9 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS9); } static int dwc3_core_ulpi_init(struct dwc3 *dwc) @@ -401,8 +468,11 @@ static int dwc3_core_ulpi_init(struct dwc3 *dwc) */ static int dwc3_phy_setup(struct dwc3 *dwc) { + unsigned int hw_mode; u32 reg; + hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); /* @@ -417,9 +487,17 @@ static int dwc3_phy_setup(struct dwc3 *dwc) * will be '0' when the core is reset. Application needs to set it * to '1' after the core initialization is completed. */ - if (dwc->revision > DWC3_REVISION_194A) + if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A)) reg |= DWC3_GUSB3PIPECTL_SUSPHY; + /* + * For DRD controllers, GUSB3PIPECTL.SUSPENDENABLE must be cleared after + * power-on reset, and it can be set after core initialization, which is + * after device soft-reset during initialization. + */ + if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD) + reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; + if (dwc->u2ss_inp3_quirk) reg |= DWC3_GUSB3PIPECTL_U2SSINP3OK; @@ -470,9 +548,8 @@ static int dwc3_phy_setup(struct dwc3 *dwc) if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI)) break; } - /* FALLTHROUGH */ + fallthrough; case DWC3_GHWPARAMS3_HSPHY_IFC_ULPI: - /* FALLTHROUGH */ default: break; } @@ -500,9 +577,17 @@ static int dwc3_phy_setup(struct dwc3 *dwc) * be '0' when the core is reset. Application needs to set it to * '1' after the core initialization is completed. */ - if (dwc->revision > DWC3_REVISION_194A) + if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A)) reg |= DWC3_GUSB2PHYCFG_SUSPHY; + /* + * For DRD controllers, GUSB2PHYCFG.SUSPHY must be cleared after + * power-on reset, and it can be set after core initialization, which is + * after device soft-reset during initialization. + */ + if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD) + reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; + if (dwc->dis_u2_susphy_quirk) reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; @@ -511,7 +596,7 @@ static int dwc3_phy_setup(struct dwc3 *dwc) else reg |= DWC3_GUSB2PHYCFG_ENBLSLPM; - if (dwc->dis_u2_freeclk_exists_quirk) + if (dwc->dis_u2_freeclk_exists_quirk || dwc->gfladj_refclk_lpm_sel) reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS; dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); @@ -523,14 +608,17 @@ static void dwc3_core_exit(struct dwc3 *dwc) { dwc3_event_buffers_cleanup(dwc); - phy_exit(dwc->usb2_generic_phy); - phy_exit(dwc->usb3_generic_phy); - + usb_phy_set_suspend(dwc->usb2_phy, 1); + 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); - dwc3_free_event_buffers(dwc); + usb_phy_shutdown(dwc->usb2_phy); + usb_phy_shutdown(dwc->usb3_phy); + phy_exit(dwc->usb2_generic_phy); + phy_exit(dwc->usb3_generic_phy); + + reset_control_assert(dwc->reset); } static bool dwc3_core_is_valid(struct dwc3 *dwc) @@ -538,15 +626,13 @@ static bool dwc3_core_is_valid(struct dwc3 *dwc) u32 reg; reg = dwc3_readl(dwc->regs, DWC3_GSNPSID); + dwc->ip = DWC3_GSNPS_ID(reg); /* This should read as U3 followed by revision number */ - if ((reg & DWC3_GSNPSID_MASK) == 0x55330000) { - /* Detected DWC_usb3 IP */ + if (DWC3_IP_IS(DWC3)) { dwc->revision = reg; - } else if ((reg & DWC3_GSNPSID_MASK) == 0x33310000) { - /* Detected DWC_usb31 IP */ + } else if (DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) { dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER); - dwc->revision |= DWC3_REVISION_IS_DWC31; dwc->version_type = dwc3_readl(dwc->regs, DWC3_VER_TYPE); } else { return false; @@ -579,8 +665,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc) */ if ((dwc->dr_mode == USB_DR_MODE_HOST || dwc->dr_mode == USB_DR_MODE_OTG) && - (dwc->revision >= DWC3_REVISION_210A && - dwc->revision <= DWC3_REVISION_250A)) + DWC3_VER_IS_WITHIN(DWC3, 210A, 250A)) reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC; else reg &= ~DWC3_GCTL_DSBLCLKGTNG; @@ -606,8 +691,8 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc) dwc->is_fpga = true; } - WARN(dwc->disable_scramble_quirk && !dwc->is_fpga, - "disable_scramble cannot be used on non-FPGA builds\n"); + WARN_ONCE(dwc->disable_scramble_quirk && !dwc->is_fpga, + "disable_scramble cannot be used on non-FPGA builds\n"); if (dwc->disable_scramble_quirk && dwc->is_fpga) reg |= DWC3_GCTL_DISSCRAMBLE; @@ -623,25 +708,26 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc) * and falls back to high-speed mode which causes * the device to enter a Connect/Disconnect loop */ - if (dwc->revision < DWC3_REVISION_190A) + if (DWC3_VER_IS_PRIOR(DWC3, 190A)) reg |= DWC3_GCTL_U2RSTECN; dwc3_writel(dwc->regs, DWC3_GCTL, reg); } static int dwc3_core_get_phy(struct dwc3 *dwc); +static int dwc3_core_ulpi_init(struct dwc3 *dwc); /* set global incr burst type configuration registers */ static void dwc3_set_incr_burst_type(struct dwc3 *dwc) { - struct device_d *dev = dwc->dev; + struct device *dev = dwc->dev; /* incrx_mode : for INCR burst type. */ bool incrx_mode; /* incrx_size : for size of INCRX burst. */ u32 incrx_size; u32 *vals; u32 cfg; - int ntype = 0; + int ntype; int ret; int i; @@ -654,24 +740,19 @@ 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. */ - of_find_property(dev->device_node, "snps,incr-burst-type-adjustment", - &ntype); - - ntype /= sizeof(u32); - + ntype = of_property_count_elems_of_size(dev->of_node, + "snps,incr-burst-type-adjustment", + sizeof(u32)); if (ntype <= 0) return; vals = kcalloc(ntype, sizeof(u32), GFP_KERNEL); - if (!vals) { - dev_err(dev, "Error to get memory\n"); + if (!vals) return; - } /* Get INCR burst type, and parse it */ - ret = of_property_read_u32_array(dev->device_node, - "snps,incr-burst-type-adjustment", - vals, ntype); + ret = of_property_read_u32_array(dev->of_node, + "snps,incr-burst-type-adjustment", vals, ntype); if (ret) { kfree(vals); dev_err(dev, "Error to get property\n"); @@ -738,14 +819,11 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc) */ static int dwc3_core_init(struct dwc3 *dwc) { + unsigned int hw_mode; u32 reg; int ret; - if (!dwc3_core_is_valid(dwc)) { - dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); - ret = -ENODEV; - goto err0; - } + hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); /* * Write Linux Version Code to our GUID register so it's easy to figure @@ -753,21 +831,19 @@ static int dwc3_core_init(struct dwc3 *dwc) */ dwc3_writel(dwc->regs, DWC3_GUID, 0xdeadbeef); - /* Handle USB2.0-only core configuration */ - if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == - DWC3_GHWPARAMS3_SSPHY_IFC_DIS) { - if (dwc->maximum_speed == USB_SPEED_SUPER) - dwc->maximum_speed = USB_SPEED_HIGH; - } - ret = dwc3_phy_setup(dwc); if (ret) goto err0; if (!dwc->ulpi_ready) { ret = dwc3_core_ulpi_init(dwc); - if (ret) + if (ret) { + if (ret == -ETIMEDOUT) { + dwc3_core_soft_reset(dwc); + ret = -EPROBE_DEFER; + } goto err0; + } dwc->ulpi_ready = true; } @@ -778,34 +854,66 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc->phys_ready = true; } + usb_phy_init(dwc->usb2_phy); + usb_phy_init(dwc->usb3_phy); + ret = phy_init(dwc->usb2_generic_phy); + if (ret < 0) + goto err0a; + + ret = phy_init(dwc->usb3_generic_phy); + if (ret < 0) { + phy_exit(dwc->usb2_generic_phy); + goto err0a; + } + ret = dwc3_core_soft_reset(dwc); if (ret) - goto err0a; + goto err1; + + if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD && + !DWC3_VER_IS_WITHIN(DWC3, ANY, 194A)) { + if (!dwc->dis_u3_susphy_quirk) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); + reg |= DWC3_GUSB3PIPECTL_SUSPHY; + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + } + + if (!dwc->dis_u2_susphy_quirk) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg |= DWC3_GUSB2PHYCFG_SUSPHY; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + } + } dwc3_core_setup_global_control(dwc); dwc3_core_num_eps(dwc); ret = dwc3_setup_scratch_buffers(dwc); if (ret) - goto err0a; + goto err1; /* Adjust Frame Length */ dwc3_frame_length_adjustment(dwc); + /* Adjust Reference Clock Period */ + dwc3_ref_clk_period(dwc); + dwc3_set_incr_burst_type(dwc); + usb_phy_set_suspend(dwc->usb2_phy, 0); + usb_phy_set_suspend(dwc->usb3_phy, 0); ret = phy_power_on(dwc->usb2_generic_phy); if (ret < 0) - goto err1; + goto err2; ret = phy_power_on(dwc->usb3_generic_phy); if (ret < 0) - goto err2; + goto err3; ret = dwc3_event_buffers_setup(dwc); if (ret) { dev_err(dwc->dev, "failed to setup event buffers\n"); - goto err3; + goto err4; } /* @@ -813,25 +921,57 @@ static int dwc3_core_init(struct dwc3 *dwc) * the DWC_usb3 controller. It is NOT available in the * DWC_usb31 controller. */ - if (!dwc3_is_usb31(dwc) && dwc->revision >= DWC3_REVISION_310A) { + if (DWC3_VER_IS_WITHIN(DWC3, 310A, ANY)) { reg = dwc3_readl(dwc->regs, DWC3_GUCTL2); reg |= DWC3_GUCTL2_RST_ACTBITLATER; dwc3_writel(dwc->regs, DWC3_GUCTL2, reg); } - if (dwc->revision >= DWC3_REVISION_250A) { + /* + * When configured in HOST mode, after issuing U3/L2 exit controller + * fails to send proper CRC checksum in CRC5 feild. Because of this + * behaviour Transaction Error is generated, resulting in reset and + * re-enumeration of usb device attached. All the termsel, xcvrsel, + * opmode becomes 0 during end of resume. Enabling bit 10 of GUCTL1 + * will correct this problem. This option is to support certain + * legacy ULPI PHYs. + */ + if (dwc->resume_hs_terminations) { + reg = dwc3_readl(dwc->regs, DWC3_GUCTL1); + reg |= DWC3_GUCTL1_RESUME_OPMODE_HS_HOST; + dwc3_writel(dwc->regs, DWC3_GUCTL1, reg); + } + + if (!DWC3_VER_IS_PRIOR(DWC3, 250A)) { reg = dwc3_readl(dwc->regs, DWC3_GUCTL1); /* * Enable hardware control of sending remote wakeup * in HS when the device is in the L1 state. */ - if (dwc->revision >= DWC3_REVISION_290A) + if (!DWC3_VER_IS_PRIOR(DWC3, 290A)) reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW; + /* + * Decouple USB 2.0 L1 & L2 events which will allow for + * gadget driver to only receive U3/L2 suspend & wakeup + * events and prevent the more frequent L1 LPM transitions + * from interrupting the driver. + */ + if (!DWC3_VER_IS_PRIOR(DWC3, 300A)) + reg |= DWC3_GUCTL1_DEV_DECOUPLE_L1L2_EVT; + if (dwc->dis_tx_ipgap_linecheck_quirk) reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS; + if (dwc->parkmode_disable_ss_quirk) + reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS; + + if (DWC3_VER_IS_WITHIN(DWC3, 290A, ANY) && + (dwc->maximum_speed == USB_SPEED_HIGH || + dwc->maximum_speed == USB_SPEED_FULL)) + reg |= DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK; + dwc3_writel(dwc->regs, DWC3_GUCTL1, reg); } @@ -855,7 +995,7 @@ static int dwc3_core_init(struct dwc3 *dwc) * Must config both number of packets and max burst settings to enable * RX and/or TX threshold. */ - if (dwc3_is_usb31(dwc) && dwc->dr_mode == USB_DR_MODE_HOST) { + if (!DWC3_IP_IS(DWC3) && dwc->dr_mode == USB_DR_MODE_HOST) { u8 rx_thr_num = dwc->rx_thr_num_pkt_prd; u8 rx_maxburst = dwc->rx_max_burst_prd; u8 tx_thr_num = dwc->tx_thr_num_pkt_prd; @@ -890,80 +1030,88 @@ static int dwc3_core_init(struct dwc3 *dwc) return 0; -err3: +err4: phy_power_off(dwc->usb3_generic_phy); -err2: + +err3: phy_power_off(dwc->usb2_generic_phy); + +err2: + usb_phy_set_suspend(dwc->usb2_phy, 1); + usb_phy_set_suspend(dwc->usb3_phy, 1); + err1: + usb_phy_shutdown(dwc->usb2_phy); + usb_phy_shutdown(dwc->usb3_phy); phy_exit(dwc->usb2_generic_phy); phy_exit(dwc->usb3_generic_phy); + err0a: + dwc3_ulpi_exit(dwc); + err0: return ret; } static int dwc3_core_get_phy(struct dwc3 *dwc) { - struct device_d *dev = dwc->dev; + struct device *dev = dwc->dev; int ret; dwc->usb2_generic_phy = phy_get(dev, "usb2-phy"); if (IS_ERR(dwc->usb2_generic_phy)) { ret = PTR_ERR(dwc->usb2_generic_phy); - if (ret == -ENOSYS || ret == -ENODEV) { + if (ret == -ENOSYS || ret == -ENODEV) dwc->usb2_generic_phy = NULL; - } else if (ret == -EPROBE_DEFER) { - return ret; - } else { - dev_err(dev, "no usb2 phy configured\n"); - return ret; - } + else + return dev_err_probe(dev, ret, "no usb2 phy configured\n"); } dwc->usb3_generic_phy = phy_get(dev, "usb3-phy"); if (IS_ERR(dwc->usb3_generic_phy)) { ret = PTR_ERR(dwc->usb3_generic_phy); - if (ret == -ENOSYS || ret == -ENODEV) { + if (ret == -ENOSYS || ret == -ENODEV) dwc->usb3_generic_phy = NULL; - } else if (ret == -EPROBE_DEFER) { - return ret; - } else { - dev_err(dev, "no usb3 phy configured\n"); - return ret; - } + else + return dev_err_probe(dev, ret, "no usb3 phy configured\n"); } return 0; } -static int dwc3_set_mode(void *ctx, enum usb_dr_mode mode) +static int dwc3_set_dr_mode(void *ctx, enum usb_dr_mode mode) { struct dwc3 *dwc = ctx; - struct device_d *dev = dwc->dev; + struct device *dev = dwc->dev; int ret; switch (mode) { case USB_DR_MODE_PERIPHERAL: dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE); +// if (dwc->usb2_phy) +// otg_set_vbus(dwc->usb2_phy->otg, false); + phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE); + phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE); + ret = dwc3_gadget_init(dwc); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to initialize gadget\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to initialize gadget\n"); break; case USB_DR_MODE_HOST: dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST); +// if (dwc->usb2_phy) +// 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); + ret = dwc3_host_init(dwc); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to initialize host\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to initialize host\n"); break; default: + dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode); return -EINVAL; } @@ -973,20 +1121,43 @@ static int dwc3_set_mode(void *ctx, enum usb_dr_mode mode) static int dwc3_core_init_mode(struct dwc3 *dwc) { if (dwc->dr_mode == USB_DR_MODE_OTG) - return usb_register_otg_device(dwc->dev, dwc3_set_mode, dwc); + return usb_register_otg_device(dwc->dev, dwc3_set_dr_mode, dwc); else - return dwc3_set_mode(dwc, dwc->dr_mode); + return dwc3_set_dr_mode(dwc, dwc->dr_mode); +} + +static void dwc3_core_exit_mode(struct dwc3 *dwc) +{ + switch (dwc->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + dwc3_gadget_exit(dwc); + break; + case USB_DR_MODE_HOST: + dwc3_host_exit(dwc); + break; + default: + /* do nothing */ + break; + } + + /* de-assert DRVVBUS for HOST and OTG mode */ + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE); } static void dwc3_get_properties(struct dwc3 *dwc) { - struct device_d *dev = dwc->dev; + struct device *dev = dwc->dev; u8 lpm_nyet_threshold; u8 tx_de_emphasis; u8 hird_threshold; + u8 rx_thr_num_pkt_prd = 0; + u8 rx_max_burst_prd = 0; + u8 tx_thr_num_pkt_prd = 0; + u8 tx_max_burst_prd = 0; + u8 tx_fifo_resize_max_num; /* default to highest possible threshold */ - lpm_nyet_threshold = 0xff; + lpm_nyet_threshold = 0xf; /* default to -3.5dB de-emphasis */ tx_de_emphasis = 1; @@ -997,39 +1168,142 @@ static void dwc3_get_properties(struct dwc3 *dwc) */ hird_threshold = 12; - dwc->maximum_speed = of_usb_get_maximum_speed(dev->device_node, NULL); - dwc->dr_mode = of_usb_get_dr_mode(dev->device_node, NULL); - dwc->hsphy_mode = of_usb_get_phy_mode(dev->device_node, NULL); + /* + * default to a TXFIFO size large enough to fit 6 max packets. This + * allows for systems with larger bus latencies to have some headroom + * for endpoints that have a large bMaxBurst value. + */ + tx_fifo_resize_max_num = 6; + + dwc->maximum_speed = of_usb_get_maximum_speed(dev->of_node, NULL); +// dwc->max_ssp_rate = usb_get_maximum_ssp_rate(dev); + dwc->dr_mode = of_usb_get_dr_mode(dev->of_node, NULL); + dwc->hsphy_mode = of_usb_get_phy_mode(dev->of_node, NULL); + + dwc->sysdev_is_parent = of_property_read_bool(dev->of_node, + "linux,sysdev_is_parent"); + if (dwc->sysdev_is_parent) + dwc->sysdev = dwc->dev->parent; + else + dwc->sysdev = dwc->dev; + + dwc->has_lpm_erratum = of_property_read_bool(dev->of_node, + "snps,has-lpm-erratum"); + of_property_read_u8(dev->of_node, "snps,lpm-nyet-threshold", + &lpm_nyet_threshold); + dwc->is_utmi_l1_suspend = of_property_read_bool(dev->of_node, + "snps,is-utmi-l1-suspend"); + of_property_read_u8(dev->of_node, "snps,hird-threshold", + &hird_threshold); + dwc->dis_start_transfer_quirk = of_property_read_bool(dev->of_node, + "snps,dis-start-transfer-quirk"); + dwc->usb3_lpm_capable = of_property_read_bool(dev->of_node, + "snps,usb3_lpm_capable"); + dwc->usb2_lpm_disable = of_property_read_bool(dev->of_node, + "snps,usb2-lpm-disable"); + dwc->usb2_gadget_lpm_disable = of_property_read_bool(dev->of_node, + "snps,usb2-gadget-lpm-disable"); + of_property_read_u8(dev->of_node, "snps,rx-thr-num-pkt-prd", + &rx_thr_num_pkt_prd); + of_property_read_u8(dev->of_node, "snps,rx-max-burst-prd", + &rx_max_burst_prd); + of_property_read_u8(dev->of_node, "snps,tx-thr-num-pkt-prd", + &tx_thr_num_pkt_prd); + of_property_read_u8(dev->of_node, "snps,tx-max-burst-prd", + &tx_max_burst_prd); + dwc->do_fifo_resize = of_property_read_bool(dev->of_node, + "tx-fifo-resize"); + if (dwc->do_fifo_resize) + of_property_read_u8(dev->of_node, "tx-fifo-max-num", + &tx_fifo_resize_max_num); + + dwc->disable_scramble_quirk = of_property_read_bool(dev->of_node, + "snps,disable_scramble_quirk"); + dwc->u2exit_lfps_quirk = of_property_read_bool(dev->of_node, + "snps,u2exit_lfps_quirk"); + dwc->u2ss_inp3_quirk = of_property_read_bool(dev->of_node, + "snps,u2ss_inp3_quirk"); + dwc->req_p1p2p3_quirk = of_property_read_bool(dev->of_node, + "snps,req_p1p2p3_quirk"); + dwc->del_p1p2p3_quirk = of_property_read_bool(dev->of_node, + "snps,del_p1p2p3_quirk"); + dwc->del_phy_power_chg_quirk = of_property_read_bool(dev->of_node, + "snps,del_phy_power_chg_quirk"); + dwc->lfps_filter_quirk = of_property_read_bool(dev->of_node, + "snps,lfps_filter_quirk"); + dwc->rx_detect_poll_quirk = of_property_read_bool(dev->of_node, + "snps,rx_detect_poll_quirk"); + dwc->dis_u3_susphy_quirk = of_property_read_bool(dev->of_node, + "snps,dis_u3_susphy_quirk"); + dwc->dis_u2_susphy_quirk = of_property_read_bool(dev->of_node, + "snps,dis_u2_susphy_quirk"); + dwc->dis_enblslpm_quirk = of_property_read_bool(dev->of_node, + "snps,dis_enblslpm_quirk"); + dwc->dis_u1_entry_quirk = of_property_read_bool(dev->of_node, + "snps,dis-u1-entry-quirk"); + dwc->dis_u2_entry_quirk = of_property_read_bool(dev->of_node, + "snps,dis-u2-entry-quirk"); + dwc->dis_rxdet_inp3_quirk = of_property_read_bool(dev->of_node, + "snps,dis_rxdet_inp3_quirk"); + dwc->dis_u2_freeclk_exists_quirk = of_property_read_bool(dev->of_node, + "snps,dis-u2-freeclk-exists-quirk"); + dwc->dis_del_phy_power_chg_quirk = of_property_read_bool(dev->of_node, + "snps,dis-del-phy-power-chg-quirk"); + dwc->dis_tx_ipgap_linecheck_quirk = of_property_read_bool(dev->of_node, + "snps,dis-tx-ipgap-linecheck-quirk"); + dwc->resume_hs_terminations = of_property_read_bool(dev->of_node, + "snps,resume-hs-terminations"); + dwc->parkmode_disable_ss_quirk = of_property_read_bool(dev->of_node, + "snps,parkmode-disable-ss-quirk"); + dwc->gfladj_refclk_lpm_sel = of_property_read_bool(dev->of_node, + "snps,gfladj-refclk-lpm-sel-quirk"); + + dwc->tx_de_emphasis_quirk = of_property_read_bool(dev->of_node, + "snps,tx_de_emphasis_quirk"); + of_property_read_u8(dev->of_node, "snps,tx_de_emphasis", + &tx_de_emphasis); + of_property_read_string(dev->of_node, "snps,hsphy_interface", + &dwc->hsphy_interface); + of_property_read_u32(dev->of_node, "snps,quirk-frame-length-adjustment", + &dwc->fladj); + of_property_read_u32(dev->of_node, "snps,ref-clock-period-ns", + &dwc->ref_clk_per); + + dwc->dis_metastability_quirk = of_property_read_bool(dev->of_node, + "snps,dis_metastability_quirk"); + + dwc->dis_split_quirk = of_property_read_bool(dev->of_node, + "snps,dis-split-quirk"); dwc->lpm_nyet_threshold = lpm_nyet_threshold; dwc->tx_de_emphasis = tx_de_emphasis; - if (of_get_property(dev->device_node, "snps,dis_rxdet_inp3_quirk", - NULL)) - dwc->dis_rxdet_inp3_quirk = 1; + dwc->hird_threshold = hird_threshold; - of_property_read_u32_array(dev->device_node, - "snps,quirk-frame-length-adjustment", - &dwc->fladj, 1); + dwc->rx_thr_num_pkt_prd = rx_thr_num_pkt_prd; + dwc->rx_max_burst_prd = rx_max_burst_prd; - dwc->hird_threshold = hird_threshold - | (dwc->is_utmi_l1_suspend << 4); + dwc->tx_thr_num_pkt_prd = tx_thr_num_pkt_prd; + dwc->tx_max_burst_prd = tx_max_burst_prd; dwc->imod_interval = 0; + + dwc->tx_fifo_resize_max_num = tx_fifo_resize_max_num; } /* check whether the core supports IMOD */ bool dwc3_has_imod(struct dwc3 *dwc) { - return ((dwc3_is_usb3(dwc) && - dwc->revision >= DWC3_REVISION_300A) || - (dwc3_is_usb31(dwc) && - dwc->revision >= DWC3_USB31_REVISION_120A)); + return DWC3_VER_IS_WITHIN(DWC3, 300A, ANY) || + DWC3_VER_IS_WITHIN(DWC31, 120A, ANY) || + DWC3_IP_IS(DWC32); } static void dwc3_check_params(struct dwc3 *dwc) { - struct device_d *dev = dwc->dev; + struct device *dev = dwc->dev; + unsigned int hwparam_gen = + DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3); /* Check for proper value of imod_interval */ if (dwc->imod_interval && !dwc3_has_imod(dwc)) { @@ -1045,57 +1319,88 @@ static void dwc3_check_params(struct dwc3 *dwc) * affected version. */ if (!dwc->imod_interval && - (dwc->revision == DWC3_REVISION_300A)) + DWC3_VER_IS(DWC3, 300A)) dwc->imod_interval = 1; /* Check the maximum_speed parameter */ switch (dwc->maximum_speed) { - case USB_SPEED_LOW: case USB_SPEED_FULL: case USB_SPEED_HIGH: + break; case USB_SPEED_SUPER: + if (hwparam_gen == DWC3_GHWPARAMS3_SSPHY_IFC_DIS) + dev_warn(dev, "UDC doesn't support Gen 1\n"); + break; case USB_SPEED_SUPER_PLUS: + if ((DWC3_IP_IS(DWC32) && + hwparam_gen == DWC3_GHWPARAMS3_SSPHY_IFC_DIS) || + (!DWC3_IP_IS(DWC32) && + hwparam_gen != DWC3_GHWPARAMS3_SSPHY_IFC_GEN2)) + dev_warn(dev, "UDC doesn't support SSP\n"); break; default: dev_err(dev, "invalid maximum_speed parameter %d\n", dwc->maximum_speed); - /* fall through */ + fallthrough; case USB_SPEED_UNKNOWN: - /* default to superspeed */ - dwc->maximum_speed = USB_SPEED_SUPER; - - /* - * default to superspeed plus if we are capable. - */ - if (dwc3_is_usb31(dwc) && - (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == - DWC3_GHWPARAMS3_SSPHY_IFC_GEN2)) + switch (hwparam_gen) { + case DWC3_GHWPARAMS3_SSPHY_IFC_GEN2: dwc->maximum_speed = USB_SPEED_SUPER_PLUS; - + break; + case DWC3_GHWPARAMS3_SSPHY_IFC_GEN1: + if (DWC3_IP_IS(DWC32)) + dwc->maximum_speed = USB_SPEED_SUPER_PLUS; + else + dwc->maximum_speed = USB_SPEED_SUPER; + break; + case DWC3_GHWPARAMS3_SSPHY_IFC_DIS: + dwc->maximum_speed = USB_SPEED_HIGH; + break; + default: + dwc->maximum_speed = USB_SPEED_SUPER; + break; + } break; } -} - -static void dwc3_coresoft_reset(struct dwc3 *dwc) -{ - u32 reg; - - reg = dwc3_readl(dwc->regs, DWC3_GCTL); - reg |= DWC3_GCTL_CORESOFTRESET; - dwc3_writel(dwc->regs, DWC3_GCTL, reg); /* - * Similar reset sequence in U-Boot has a 100ms delay here. In - * practice reset sequence seem to work as expected even - * without a delay. + * Currently the controller does not have visibility into the HW + * parameter to determine the maximum number of lanes the HW supports. + * If the number of lanes is not specified in the device property, then + * set the default to support dual-lane for DWC_usb32 and single-lane + * for DWC_usb31 for super-speed-plus. */ - - reg = dwc3_readl(dwc->regs, DWC3_GCTL); - reg &= ~DWC3_GCTL_CORESOFTRESET; - dwc3_writel(dwc->regs, DWC3_GCTL, reg); + if (dwc->maximum_speed == USB_SPEED_SUPER_PLUS) { + switch (dwc->max_ssp_rate) { + case USB_SSP_GEN_2x1: + if (hwparam_gen == DWC3_GHWPARAMS3_SSPHY_IFC_GEN1) + dev_warn(dev, "UDC only supports Gen 1\n"); + break; + case USB_SSP_GEN_1x2: + case USB_SSP_GEN_2x2: + if (DWC3_IP_IS(DWC31)) + dev_warn(dev, "UDC only supports single lane\n"); + break; + case USB_SSP_GEN_UNKNOWN: + default: + switch (hwparam_gen) { + case DWC3_GHWPARAMS3_SSPHY_IFC_GEN2: + if (DWC3_IP_IS(DWC32)) + dwc->max_ssp_rate = USB_SSP_GEN_2x2; + else + dwc->max_ssp_rate = USB_SSP_GEN_2x1; + break; + case DWC3_GHWPARAMS3_SSPHY_IFC_GEN1: + if (DWC3_IP_IS(DWC32)) + dwc->max_ssp_rate = USB_SSP_GEN_1x2; + break; + } + break; + } + } } -static int dwc3_probe(struct device_d *dev) +static int dwc3_probe(struct device *dev) { struct dwc3 *dwc; int ret; @@ -1103,29 +1408,39 @@ static int dwc3_probe(struct device_d *dev) dwc = xzalloc(sizeof(*dwc)); dev->priv = dwc; - dwc->clks = xmemdup(dwc3_core_clks, sizeof(dwc3_core_clks)); dwc->dev = dev; dwc->regs = dev_get_mem_region(dwc->dev, 0) + DWC3_GLOBALS_REGS_START; dwc3_get_properties(dwc); - if (dev->device_node) { - dwc->num_clks = ARRAY_SIZE(dwc3_core_clks); + if (dev->of_node) { + ret = clk_bulk_get_all(dev, &dwc->clks); + if (ret < 0) + return ret; - if (of_find_property(dev->device_node, "clocks", NULL)) { - ret = clk_bulk_get(dev, dwc->num_clks, dwc->clks); - if (ret == -EPROBE_DEFER) - return ret; - if (ret) - return ret; - } + dwc->num_clks = ret; } ret = clk_bulk_enable(dwc->num_clks, dwc->clks); if (ret) return ret; - dwc3_coresoft_reset(dwc); + dwc->reset = reset_control_get(dev, NULL); + if (IS_ERR(dwc->reset)) { + dev_err(dev, "Failed to get reset control: %pe\n", dwc->reset); + return PTR_ERR(dwc->reset); + } + + reset_control_assert(dwc->reset); + mdelay(1); + reset_control_deassert(dwc->reset); + + if (!dwc3_core_is_valid(dwc)) { + dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); + return -ENODEV; + } + + dwc3_core_soft_reset(dwc); dwc3_cache_hwparams(dwc); @@ -1159,25 +1474,32 @@ static int dwc3_probe(struct device_d *dev) return 0; } -static void dwc3_remove(struct device_d *dev) +static void dwc3_remove(struct device *dev) { struct dwc3 *dwc = dev->priv; + dwc3_core_exit_mode(dwc); dwc3_core_exit(dwc); clk_bulk_put(dwc->num_clks, dwc->clks); + dwc3_free_event_buffers(dwc); + dwc3_free_scratch_buffers(dwc); } static const struct of_device_id of_dwc3_match[] = { { - .compatible = "snps,dwc3" + .compatible = "snps,dwc3", + }, + { + .compatible = "synopsys,dwc3", }, { - .compatible = "synopsys,dwc3" + .compatible = "rockchip,rk3568-dwc3", }, { }, }; +MODULE_DEVICE_TABLE(of, of_dwc3_match); -static struct driver_d dwc3_driver = { +static struct driver dwc3_driver = { .probe = dwc3_probe, .remove = dwc3_remove, .name = "dwc3", diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index f2f7a311d1..52853a4370 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1,8 +1,8 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ /* * core.h - DesignWare USB3 DRD Core Header * - * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com * * Authors: Felipe Balbi <balbi@ti.com>, * Sebastian Andrzej Siewior <bigeasy@linutronix.de> @@ -12,9 +12,11 @@ #define __DRIVERS_USB_DWC3_CORE_H #include <linux/spinlock.h> -#include <usb/usb.h> -#include <usb/phy.h> -#include <usb/gadget.h> +#include <linux/usb/usb.h> +#include <linux/usb/phy.h> +#include <linux/usb/gadget.h> +#include <linux/mutex.h> +#include <linux/completion.h> #define DWC3_MSG_MAX 500 @@ -40,7 +42,7 @@ #define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE 3 #define DWC3_DEVICE_EVENT_WAKEUP 4 #define DWC3_DEVICE_EVENT_HIBER_REQ 5 -#define DWC3_DEVICE_EVENT_EOPF 6 +#define DWC3_DEVICE_EVENT_SUSPEND 6 #define DWC3_DEVICE_EVENT_SOF 7 #define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9 #define DWC3_DEVICE_EVENT_CMD_CMPL 10 @@ -55,6 +57,7 @@ #define DWC3_GEVNTCOUNT_EHB BIT(31) #define DWC3_GSNPSID_MASK 0xffff0000 #define DWC3_GSNPSREV_MASK 0xffff +#define DWC3_GSNPS_ID(p) (((p) & DWC3_GSNPSID_MASK) >> 16) /* DWC3 registers memory space boundries */ #define DWC3_XHCI_REGS_START 0x0 @@ -123,7 +126,9 @@ #define DWC3_GEVNTCOUNT(n) (0xc40c + ((n) * 0x10)) #define DWC3_GHWPARAMS8 0xc600 +#define DWC3_GUCTL3 0xc60c #define DWC3_GFLADJ 0xc630 +#define DWC3_GHWPARAMS9 0xc6e0 /* Device Registers */ #define DWC3_DCFG 0xc700 @@ -133,6 +138,7 @@ #define DWC3_DGCMDPAR 0xc710 #define DWC3_DGCMD 0xc714 #define DWC3_DALEPENA 0xc720 +#define DWC3_DCFG1 0xc740 /* DWC_usb32 only */ #define DWC3_DEP_BASE(n) (0xc800 + ((n) * 0x10)) #define DWC3_DEPCMDPAR2 0x00 @@ -210,6 +216,7 @@ /* Global Configuration Register */ #define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19) +#define DWC3_GCTL_PWRDNSCALE_MASK GENMASK(31, 19) #define DWC3_GCTL_U2RSTECN BIT(16) #define DWC3_GCTL_RAMCLKSEL(x) (((x) & DWC3_GCTL_CLK_MASK) << 6) #define DWC3_GCTL_CLK_BUS (0) @@ -236,8 +243,12 @@ #define DWC3_GUCTL_HSTINAUTORETRY BIT(14) /* Global User Control 1 Register */ +#define DWC3_GUCTL1_DEV_DECOUPLE_L1L2_EVT BIT(31) #define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28) -#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24) +#define DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK BIT(26) +#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24) +#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17) +#define DWC3_GUCTL1_RESUME_OPMODE_HS_HOST BIT(10) /* Global Status Register */ #define DWC3_GSTS_OTG_IP BIT(10) @@ -268,6 +279,7 @@ /* Global USB2 PHY Vendor Control Register */ #define DWC3_GUSB2PHYACC_NEWREGREQ BIT(25) +#define DWC3_GUSB2PHYACC_DONE BIT(24) #define DWC3_GUSB2PHYACC_BUSY BIT(23) #define DWC3_GUSB2PHYACC_WRITE BIT(22) #define DWC3_GUSB2PHYACC_ADDR(n) (n << 16) @@ -292,10 +304,14 @@ /* Global TX Fifo Size Register */ #define DWC31_GTXFIFOSIZ_TXFRAMNUM BIT(15) /* DWC_usb31 only */ -#define DWC31_GTXFIFOSIZ_TXFDEF(n) ((n) & 0x7fff) /* DWC_usb31 only */ -#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff) +#define DWC31_GTXFIFOSIZ_TXFDEP(n) ((n) & 0x7fff) /* DWC_usb31 only */ +#define DWC3_GTXFIFOSIZ_TXFDEP(n) ((n) & 0xffff) #define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000) +/* Global RX Fifo Size Register */ +#define DWC31_GRXFIFOSIZ_RXFDEP(n) ((n) & 0x7fff) /* DWC_usb31 only */ +#define DWC3_GRXFIFOSIZ_RXFDEP(n) ((n) & 0xffff) + /* Global Event Size Registers */ #define DWC3_GEVNTSIZ_INTMASK BIT(31) #define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff) @@ -346,18 +362,38 @@ #define DWC3_GHWPARAMS6_SRPSUPPORT BIT(10) #define DWC3_GHWPARAMS6_EN_FPGA BIT(7) +/* DWC_usb32 only */ +#define DWC3_GHWPARAMS6_MDWIDTH(n) ((n) & (0x3 << 8)) + /* Global HWPARAMS7 Register */ #define DWC3_GHWPARAMS7_RAM1_DEPTH(n) ((n) & 0xffff) #define DWC3_GHWPARAMS7_RAM2_DEPTH(n) (((n) >> 16) & 0xffff) +/* Global HWPARAMS9 Register */ +#define DWC3_GHWPARAMS9_DEV_TXF_FLUSH_BYPASS BIT(0) +#define DWC3_GHWPARAMS9_DEV_MST BIT(1) + /* Global Frame Length Adjustment Register */ #define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7) #define DWC3_GFLADJ_30MHZ_MASK 0x3f +#define DWC3_GFLADJ_REFCLK_FLADJ_MASK GENMASK(21, 8) +#define DWC3_GFLADJ_REFCLK_LPM_SEL BIT(23) +#define DWC3_GFLADJ_240MHZDECR GENMASK(30, 24) +#define DWC3_GFLADJ_240MHZDECR_PLS1 BIT(31) + +/* Global User Control Register*/ +#define DWC3_GUCTL_REFCLKPER_MASK 0xffc00000 +#define DWC3_GUCTL_REFCLKPER_SEL 22 /* Global User Control Register 2 */ #define DWC3_GUCTL2_RST_ACTBITLATER BIT(14) +/* Global User Control Register 3 */ +#define DWC3_GUCTL3_SPLITDISABLE BIT(14) + /* Device Configuration Register */ +#define DWC3_DCFG_NUMLANES(n) (((n) & 0x3) << 30) /* DWC_usb32 only */ + #define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) #define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) @@ -366,13 +402,12 @@ #define DWC3_DCFG_SUPERSPEED (4 << 0) #define DWC3_DCFG_HIGHSPEED (0 << 0) #define DWC3_DCFG_FULLSPEED BIT(0) -#define DWC3_DCFG_LOWSPEED (2 << 0) -#define DWC3_DCFG_FULLSPEED1 (3 << 0) #define DWC3_DCFG_NUMP_SHIFT 17 #define DWC3_DCFG_NUMP(n) (((n) >> DWC3_DCFG_NUMP_SHIFT) & 0x1f) #define DWC3_DCFG_NUMP_MASK (0x1f << DWC3_DCFG_NUMP_SHIFT) #define DWC3_DCFG_LPM_CAP BIT(22) +#define DWC3_DCFG_IGNSTRMPP BIT(23) /* Device Control Register */ #define DWC3_DCTL_RUN_STOP BIT(31) @@ -394,8 +429,7 @@ #define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6)) /* These apply for core versions 1.94a and later */ -#define DWC3_DCTL_LPM_ERRATA_MASK DWC3_DCTL_LPM_ERRATA(0xf) -#define DWC3_DCTL_LPM_ERRATA(n) ((n) << 20) +#define DWC3_DCTL_NYET_THRES(n) (((n) & 0xf) << 20) #define DWC3_DCTL_KEEP_CONNECT BIT(19) #define DWC3_DCTL_L1_HIBER_EN BIT(18) @@ -425,7 +459,7 @@ #define DWC3_DEVTEN_CMDCMPLTEN BIT(10) #define DWC3_DEVTEN_ERRTICERREN BIT(9) #define DWC3_DEVTEN_SOFEN BIT(7) -#define DWC3_DEVTEN_EOPFEN BIT(6) +#define DWC3_DEVTEN_U3L2L1SUSPEN BIT(6) #define DWC3_DEVTEN_HIBERNATIONREQEVTEN BIT(5) #define DWC3_DEVTEN_WKUPEVTEN BIT(4) #define DWC3_DEVTEN_ULSTCNGEN BIT(3) @@ -433,6 +467,8 @@ #define DWC3_DEVTEN_USBRSTEN BIT(1) #define DWC3_DEVTEN_DISCONNEVTEN BIT(0) +#define DWC3_DSTS_CONNLANES(n) (((n) >> 30) & 0x3) /* DWC_usb32 only */ + /* Device Status Register */ #define DWC3_DSTS_DCNRD BIT(29) @@ -460,8 +496,6 @@ #define DWC3_DSTS_SUPERSPEED (4 << 0) #define DWC3_DSTS_HIGHSPEED (0 << 0) #define DWC3_DSTS_FULLSPEED BIT(0) -#define DWC3_DSTS_LOWSPEED (2 << 0) -#define DWC3_DSTS_FULLSPEED1 (3 << 0) /* Device Generic Command Register */ #define DWC3_DGCMD_SET_LMP 0x01 @@ -475,6 +509,7 @@ #define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09 #define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a #define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c +#define DWC3_DGCMD_SET_ENDPOINT_PRIME 0x0d #define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10 #define DWC3_DGCMD_STATUS(n) (((n) >> 12) & 0x0F) @@ -517,6 +552,9 @@ /* The EP number goes 0..31 so ep0 is always out and ep1 is always in */ #define DWC3_DALEPENA_EP(n) BIT(n) +/* DWC_usb32 DCFG1 config */ +#define DWC3_DCFG1_DIS_MST_ENH BIT(1) + #define DWC3_DEPCMD_TYPE_CONTROL 0 #define DWC3_DEPCMD_TYPE_ISOC 1 #define DWC3_DEPCMD_TYPE_BULK 2 @@ -602,16 +640,26 @@ struct dwc3_trb; /** * struct dwc3_event_buffer - Software event buffer representation * @buf: _THE_ buffer + * @cache: The buffer cache used in the threaded interrupt * @length: size of this buffer * @lpos: event offset + * @count: cache of last read event count register + * @flags: flags related to this event buffer * @dma: dma_addr_t * @dwc: pointer to DWC controller */ struct dwc3_event_buffer { void *buf; - unsigned length; + void *cache; + unsigned int length; unsigned int lpos; + unsigned int count; + unsigned int flags; + +#define DWC3_EVENT_PENDING BIT(0) + dma_addr_t dma; + struct dwc3 *dwc; }; @@ -622,57 +670,66 @@ struct dwc3_event_buffer { #define DWC3_EP_DIRECTION_RX false #define DWC3_TRB_NUM 256 -#define DWC3_TRB_MASK (DWC3_TRB_NUM - 1) /** * struct dwc3_ep - device side endpoint representation * @endpoint: usb endpoint - * @pending_list: list of requests for this endpoint + * @cancelled_list: list of cancelled requests for this endpoint + * @pending_list: list of pending requests for this endpoint * @started_list: list of started requests on this endpoint + * @regs: pointer to first endpoint register * @trb_pool: array of transaction buffers * @trb_pool_dma: dma address of @trb_pool - * @free_slot: next slot which is going to be used - * @busy_slot: first slot which is owned by HW - * @desc: usb_endpoint_descriptor pointer + * @trb_enqueue: enqueue 'pointer' into TRB array + * @trb_dequeue: dequeue 'pointer' into TRB array * @dwc: pointer to DWC controller * @saved_state: ep state saved during hibernation * @flags: endpoint flags (wedged, stalled, ...) - * @current_trb: index of current used trb * @number: endpoint number (1 - 15) * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK * @resource_index: Resource transfer index + * @frame_number: set to the frame number we want this transfer to start (ISOC) * @interval: the interval on which the ISOC transfer is started * @name: a human readable name e.g. ep1out-bulk * @direction: true for TX, false for RX * @stream_capable: true when streams are enabled + * @combo_num: the test combination BIT[15:14] of the frame number to test + * isochronous START TRANSFER command failure workaround + * @start_cmd_status: the status of testing START TRANSFER command with + * combo_num = 'b00 */ struct dwc3_ep { - struct usb_ep endpoint; - struct list_head cancelled_list; - struct list_head pending_list; - struct list_head started_list; - - void __iomem *regs; - - struct dwc3_trb *trb_pool; - dma_addr_t trb_pool_dma; - u32 free_slot; - u32 busy_slot; - const struct usb_ss_ep_comp_descriptor *comp_desc; - struct dwc3 *dwc; - - u32 saved_state; - unsigned flags; + struct usb_ep endpoint; + struct list_head cancelled_list; + struct list_head pending_list; + struct list_head started_list; + + void __iomem *regs; + + struct dwc3_trb *trb_pool; + dma_addr_t trb_pool_dma; + struct dwc3 *dwc; + + u32 saved_state; + unsigned int flags; #define DWC3_EP_ENABLED BIT(0) -#define DWC3_EP_STALL BIT(1) -#define DWC3_EP_WEDGE BIT(2) -#define DWC3_EP_TRANSFER_STARTED BIT(3) -#define DWC3_EP_PENDING_REQUEST BIT(4) +#define DWC3_EP_STALL BIT(1) +#define DWC3_EP_WEDGE BIT(2) +#define DWC3_EP_TRANSFER_STARTED BIT(3) +#define DWC3_EP_END_TRANSFER_PENDING BIT(4) +#define DWC3_EP_PENDING_REQUEST BIT(5) +#define DWC3_EP_DELAY_START BIT(6) +#define DWC3_EP_WAIT_TRANSFER_COMPLETE BIT(7) +#define DWC3_EP_IGNORE_NEXT_NOSTREAM BIT(8) +#define DWC3_EP_FORCE_RESTART_STREAM BIT(9) +#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10) +#define DWC3_EP_PENDING_CLEAR_STALL BIT(11) +#define DWC3_EP_TXFIFO_RESIZED BIT(12) +#define DWC3_EP_DELAY_STOP BIT(13) /* This last one is specific to EP0 */ #define DWC3_EP0_DIR_IN BIT(31) - unsigned current_trb; /* * IMPORTANT: we *know* we have 256 TRBs in our @trb_pool, so we will * use a u8 type here. If anybody decides to increase number of TRBs to @@ -682,23 +739,23 @@ struct dwc3_ep { * By using u8 types we ensure that our % operator when incrementing * enqueue and dequeue get optimized away by the compiler. */ - u8 trb_enqueue; - u8 trb_dequeue; + u8 trb_enqueue; + u8 trb_dequeue; - u8 number; - u8 type; - u8 resource_index; - u32 frame_number; - u32 interval; + u8 number; + u8 type; + u8 resource_index; + u32 frame_number; + u32 interval; - char name[20]; + char name[20]; - unsigned direction:1; - unsigned stream_capable:1; + unsigned direction:1; + unsigned stream_capable:1; /* For isochronous START TRANSFER workaround only */ - u8 combo_num; - int start_cmd_status; + u8 combo_num; + int start_cmd_status; }; enum dwc3_phy { @@ -797,6 +854,7 @@ struct dwc3_trb { * @hwparams6: GHWPARAMS6 * @hwparams7: GHWPARAMS7 * @hwparams8: GHWPARAMS8 + * @hwparams9: GHWPARAMS9 */ struct dwc3_hwparams { u32 hwparams0; @@ -808,13 +866,12 @@ struct dwc3_hwparams { u32 hwparams6; u32 hwparams7; u32 hwparams8; + u32 hwparams9; }; /* HWPARAMS0 */ #define DWC3_MODE(n) ((n) & 0x7) -#define DWC3_MDWIDTH(n) (((n) & 0xff00) >> 8) - /* HWPARAMS1 */ #define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15) @@ -829,31 +886,67 @@ struct dwc3_hwparams { /* HWPARAMS7 */ #define DWC3_RAM1_DEPTH(n) ((n) & 0xffff) -struct dwc3_request { - struct usb_request request; - struct list_head list; - struct dwc3_ep *dep; - u32 start_slot; - - unsigned remaining; +/* HWPARAMS9 */ +#define DWC3_MST_CAPABLE(p) (!!((p)->hwparams9 & \ + DWC3_GHWPARAMS9_DEV_MST)) - unsigned int status; -#define DWC3_REQUEST_STATUS_QUEUED 0 -#define DWC3_REQUEST_STATUS_STARTED 1 -#define DWC3_REQUEST_STATUS_CANCELLED 2 -#define DWC3_REQUEST_STATUS_COMPLETED 3 -#define DWC3_REQUEST_STATUS_UNKNOWN -1 - - u8 epnum; +/** + * struct dwc3_request - representation of a transfer request + * @request: struct usb_request to be transferred + * @list: a list_head used for request queueing + * @dep: struct dwc3_ep owning this request + * @sg: pointer to first incomplete sg + * @start_sg: pointer to the sg which should be queued next + * @num_pending_sgs: counter to pending sgs + * @num_queued_sgs: counter to the number of sgs which already got queued + * @remaining: amount of data remaining + * @status: internal dwc3 request status tracking + * @epnum: endpoint number to which this request refers + * @trb: pointer to struct dwc3_trb + * @trb_dma: DMA address of @trb + * @num_trbs: number of TRBs used by this request + * @needs_extra_trb: true when request needs one extra TRB (either due to ZLP + * or unaligned OUT) + * @direction: IN or OUT direction flag + * @mapped: true when request has been dma-mapped + */ +struct dwc3_request { + struct usb_request request; + struct list_head list; + struct dwc3_ep *dep; + struct scatterlist *sg; + struct scatterlist *start_sg; + + unsigned int num_pending_sgs; + unsigned int num_queued_sgs; + unsigned int remaining; + + unsigned int status; +#define DWC3_REQUEST_STATUS_QUEUED 0 +#define DWC3_REQUEST_STATUS_STARTED 1 +#define DWC3_REQUEST_STATUS_DISCONNECTED 2 +#define DWC3_REQUEST_STATUS_DEQUEUED 3 +#define DWC3_REQUEST_STATUS_STALLED 4 +#define DWC3_REQUEST_STATUS_COMPLETED 5 +#define DWC3_REQUEST_STATUS_UNKNOWN -1 + + u8 epnum; struct dwc3_trb *trb; - dma_addr_t trb_dma; + dma_addr_t trb_dma; - unsigned num_trbs; + unsigned int num_trbs; + + unsigned int needs_extra_trb:1; + unsigned int direction:1; + unsigned int mapped:1; +}; - unsigned needs_extra_trb:1; - unsigned direction:1; - unsigned mapped:1; - unsigned queued:1; +/* + * struct dwc3_scratchpad_array - hibernation scratchpad array + * (format defined by hw) + */ +struct dwc3_scratchpad_array { + __le64 dma_adr[DWC3_MAX_HIBER_SCRATCHBUFS]; }; /** @@ -869,6 +962,7 @@ struct dwc3_request { * @scratch_addr: dma address of scratchbuf * @ep0_in_setup: one control transfer is completed and enter setup phase * @lock: for synchronizing + * @mutex: for mode switching * @dev: pointer to our struct device * @sysdev: pointer to the DMA-capable device * @xhci: pointer to our xHCI child @@ -877,12 +971,14 @@ struct dwc3_request { * @eps: endpoint array * @gadget: device side representation of the peripheral controller * @gadget_driver: pointer to the gadget driver - * @clks: array of clocks - * @num_clks: number of clocks + * @bus_clk: clock for accessing the registers + * @ref_clk: reference clock + * @susp_clk: clock used when the SS phy is in low power (S3) state * @reset: reset control * @regs: base address for our registers * @regs_size: address space size * @fladj: frame length adjustment + * @ref_clk_per: reference clock period configuration * @irq_gadget: peripheral controller's IRQ number * @otg_irq: IRQ number for OTG IRQs * @current_otg_role: current role of operation while using the OTG block @@ -891,7 +987,12 @@ struct dwc3_request { * @nr_scratch: number of scratch buffers * @u1u2: only used on revisions <1.83a for workaround * @maximum_speed: maximum speed requested (mainly for testing purposes) - * @revision: revision register contents + * @max_ssp_rate: SuperSpeed Plus maximum signaling rate and lane count + * @gadget_max_speed: maximum gadget speed requested + * @gadget_ssp_rate: Gadget driver's maximum supported SuperSpeed Plus signaling + * rate and lane count. + * @ip: controller's ID + * @revision: controller's version of an IP * @version_type: VERSIONTYPE register contents, a sub release of a revision * @dr_mode: requested mode of operation * @current_dr_role: current role of operation when in dual-role mode @@ -901,6 +1002,10 @@ struct dwc3_request { * @hsphy_mode: UTMI phy mode, one of following: * - USBPHY_INTERFACE_MODE_UTMI * - USBPHY_INTERFACE_MODE_UTMIW + * @role_sw: usb_role_switch handle + * @role_switch_default_mode: default operation mode of controller while + * usb role is USB_ROLE_NONE. + * @usb_psy: pointer to power supply interface. * @usb2_phy: pointer to USB2 PHY * @usb3_phy: pointer to USB3 PHY * @usb2_generic_phy: pointer to USB2 PHY @@ -918,7 +1023,6 @@ struct dwc3_request { * @link_state: link state * @speed: device speed (super, high, full, low) * @hwparams: copy of hwparams registers - * @root: debugfs root folder pointer * @regset: debugfs pointer to regdump file * @dbg_lsp_select: current debug lsp mux register selection * @test_mode: true when we're entering a USB test mode @@ -929,8 +1033,11 @@ struct dwc3_request { * @rx_max_burst_prd: max periodic ESS receive burst size * @tx_thr_num_pkt_prd: periodic ESS transmit packet count * @tx_max_burst_prd: max periodic ESS transmit burst size + * @tx_fifo_resize_max_num: max number of fifos allocated during txfifo resize + * @clear_stall_protocol: endpoint number that requires a delayed status phase * @hsphy_interface: "utmi" or "ulpi" * @connected: true when we're connected to a host, false otherwise + * @softconnect: true when gadget connect is called, false when disconnect runs * @delayed_status: true when gadget driver asks for delayed status * @ep0_bounced: true when we used bounce buffer * @ep0_expect_in: true when we expect a DATA IN transfer @@ -939,17 +1046,19 @@ struct dwc3_request { * @has_lpm_erratum: true when core was configured with LPM Erratum. Note that * there's now way for software to detect this in runtime. * @is_utmi_l1_suspend: the core asserts output signal - * 0 - utmi_sleep_n - * 1 - utmi_l1_suspend_n + * 0 - utmi_sleep_n + * 1 - utmi_l1_suspend_n * @is_fpga: true when we are using the FPGA board * @pending_events: true when we have pending IRQs to be handled + * @do_fifo_resize: true when txfifo resizing is enabled for dwc3 endpoints * @pullups_connected: true when Run/Stop bit is set * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround * @three_stage_setup: set if we perform a three phase setup * @dis_start_transfer_quirk: set if start_transfer failure SW workaround is * not needed for DWC_usb31 version 1.70a-ea06 and below * @usb3_lpm_capable: set if hadrware supports Link Power Management - * @usb2_lpm_disable: set to disable usb2 lpm + * @usb2_lpm_disable: set to disable usb2 lpm for host + * @usb2_gadget_lpm_disable: set to disable usb2 lpm for gadget * @disable_scramble_quirk: set if we enable the disable scramble quirk * @u2exit_lfps_quirk: set if we enable u2exit lfps quirk * @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk @@ -962,7 +1071,11 @@ struct dwc3_request { * @dis_u2_susphy_quirk: set if we disable usb2 suspend phy * @dis_enblslpm_quirk: set if we clear enblslpm in GUSB2PHYCFG, * disabling the suspend signal to the PHY. + * @dis_u1_entry_quirk: set if link entering into U1 state needs to be disabled. + * @dis_u2_entry_quirk: set if link entering into U2 state needs to be disabled. * @dis_rxdet_inp3_quirk: set if we disable Rx.Detect in P3 + * @async_callbacks: if set, indicate that async callbacks will be used. + * * @dis_u2_freeclk_exists_quirk : set if we clear u2_freeclk_exists * in GUSB2PHYCFG, specify that USB2 PHY doesn't * provide a free-running PHY clock. @@ -970,53 +1083,89 @@ struct dwc3_request { * change quirk. * @dis_tx_ipgap_linecheck_quirk: set if we disable u2mac linestate * check during HS transmit. + * @resume-hs-terminations: Set if we enable quirk for fixing improper crc + * generation after resume from suspend. + * @parkmode_disable_ss_quirk: set if we need to disable all SuperSpeed + * instances in park mode. * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk * @tx_de_emphasis: Tx de-emphasis value - * 0 - -6dB de-emphasis - * 1 - -3.5dB de-emphasis - * 2 - No de-emphasis - * 3 - Reserved + * 0 - -6dB de-emphasis + * 1 - -3.5dB de-emphasis + * 2 - No de-emphasis + * 3 - Reserved * @dis_metastability_quirk: set to disable metastability quirk. + * @dis_split_quirk: set to disable split boundary. * @imod_interval: set the interrupt moderation interval in 250ns - * increments or 0 to disable. + * increments or 0 to disable. + * @max_cfg_eps: current max number of IN eps used across all USB configs. + * @last_fifo_depth: last fifo depth used to determine next fifo ram start + * address. + * @num_ep_resized: carries the current number endpoints which have had its tx + * fifo resized. + * @debug_root: root debugfs directory for this device to put its files in. */ struct dwc3 { + struct work_struct drd_work; struct dwc3_trb *ep0_trb; - void *bounce; - void *scratchbuf; - u8 *setup_buf; - dma_addr_t ep0_trb_addr; - dma_addr_t bounce_addr; - dma_addr_t scratch_addr; - struct dwc3_request ep0_usb_req; + void *bounce; + void *scratchbuf; + u8 *setup_buf; + dma_addr_t ep0_trb_addr; + dma_addr_t bounce_addr; + dma_addr_t scratch_addr; + struct dwc3_request ep0_usb_req; + struct completion ep0_in_setup; + + /* device lock */ + spinlock_t lock; - struct device_d *dev; + /* mode switching lock */ + struct mutex mutex; - struct device_d *xhci; + struct device *dev; + struct device *sysdev; - struct dwc3_event_buffer *ev_buf; - struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM]; + struct device *xhci; + struct resource xhci_resources[DWC3_XHCI_RESOURCES_NUM]; - struct usb_gadget gadget; - struct usb_gadget_driver *gadget_driver; + struct dwc3_event_buffer *ev_buf; + struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM]; + + struct usb_gadget *gadget; + struct usb_gadget_driver *gadget_driver; struct clk_bulk_data *clks; int num_clks; + struct reset_control *reset; + + struct usb_phy *usb2_phy; + struct usb_phy *usb3_phy; + struct phy *usb2_generic_phy; struct phy *usb3_generic_phy; bool phys_ready; + + struct ulpi *ulpi; bool ulpi_ready; void __iomem *regs; + size_t regs_size; enum usb_dr_mode dr_mode; u32 current_dr_role; u32 desired_dr_role; + struct extcon_dev *edev; + struct notifier_block edev_nb; enum usb_phy_interface hsphy_mode; + struct usb_role_switch *role_sw; + enum usb_dr_mode role_switch_default_mode; + + struct power_supply *usb_psy; u32 fladj; + u32 ref_clk_per; u32 irq_gadget; u32 otg_irq; u32 current_otg_role; @@ -1025,16 +1174,19 @@ struct dwc3 { u32 nr_scratch; u32 u1u2; u32 maximum_speed; + u32 gadget_max_speed; + enum usb_ssp_rate max_ssp_rate; + enum usb_ssp_rate gadget_ssp_rate; + + u32 ip; + +#define DWC3_IP 0x5533 +#define DWC31_IP 0x3331 +#define DWC32_IP 0x3332 - /* - * All 3.1 IP version constants are greater than the 3.0 IP - * version constants. This works for most version checks in - * dwc3. However, in the future, this may not apply as - * features may be developed on newer versions of the 3.0 IP - * that are not in the 3.1 IP. - */ u32 revision; +#define DWC3_REVISION_ANY 0x0 #define DWC3_REVISION_173A 0x5533173a #define DWC3_REVISION_175A 0x5533175a #define DWC3_REVISION_180A 0x5533180a @@ -1059,18 +1211,20 @@ struct dwc3 { #define DWC3_REVISION_310A 0x5533310a #define DWC3_REVISION_330A 0x5533330a -/* - * NOTICE: we're using bit 31 as a "is usb 3.1" flag. This is really - * just so dwc31 revisions are always larger than dwc3. - */ -#define DWC3_REVISION_IS_DWC31 0x80000000 -#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_DWC31) -#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 DWC31_REVISION_ANY 0x0 +#define DWC31_REVISION_110A 0x3131302a +#define DWC31_REVISION_120A 0x3132302a +#define DWC31_REVISION_160A 0x3136302a +#define DWC31_REVISION_170A 0x3137302a +#define DWC31_REVISION_180A 0x3138302a +#define DWC31_REVISION_190A 0x3139302a + +#define DWC32_REVISION_ANY 0x0 +#define DWC32_REVISION_100A 0x3130302a u32 version_type; +#define DWC31_VERSIONTYPE_ANY 0x0 #define DWC31_VERSIONTYPE_EA01 0x65613031 #define DWC31_VERSIONTYPE_EA02 0x65613032 #define DWC31_VERSIONTYPE_EA03 0x65613033 @@ -1082,7 +1236,6 @@ struct dwc3 { enum dwc3_ep0_state ep0state; enum dwc3_link_state link_state; - u16 isoch_delay; u16 u2sel; u16 u2pel; u8 u1sel; @@ -1093,6 +1246,7 @@ struct dwc3 { u8 num_eps; struct dwc3_hwparams hwparams; + struct debugfs_regset32 *regset; u32 dbg_lsp_select; @@ -1104,10 +1258,13 @@ struct dwc3 { u8 rx_max_burst_prd; u8 tx_thr_num_pkt_prd; u8 tx_max_burst_prd; + u8 tx_fifo_resize_max_num; + u8 clear_stall_protocol; const char *hsphy_interface; unsigned connected:1; + unsigned softconnect:1; unsigned delayed_status:1; unsigned ep0_bounced:1; unsigned ep0_expect_in:1; @@ -1115,18 +1272,16 @@ struct dwc3 { unsigned sysdev_is_parent:1; unsigned has_lpm_erratum:1; unsigned is_utmi_l1_suspend:1; - unsigned is_selfpowered:1; unsigned is_fpga:1; unsigned pending_events:1; - unsigned needs_fifo_resize:1; + unsigned do_fifo_resize:1; unsigned pullups_connected:1; - unsigned resize_fifos:1; unsigned setup_packet_pending:1; - unsigned start_config_issued:1; unsigned three_stage_setup:1; unsigned dis_start_transfer_quirk:1; unsigned usb3_lpm_capable:1; unsigned usb2_lpm_disable:1; + unsigned usb2_gadget_lpm_disable:1; unsigned disable_scramble_quirk:1; unsigned u2exit_lfps_quirk:1; @@ -1139,17 +1294,30 @@ struct dwc3 { unsigned dis_u3_susphy_quirk:1; unsigned dis_u2_susphy_quirk:1; unsigned dis_enblslpm_quirk:1; + unsigned dis_u1_entry_quirk:1; + unsigned dis_u2_entry_quirk:1; unsigned dis_rxdet_inp3_quirk:1; unsigned dis_u2_freeclk_exists_quirk:1; unsigned dis_del_phy_power_chg_quirk:1; unsigned dis_tx_ipgap_linecheck_quirk:1; + unsigned resume_hs_terminations:1; + unsigned parkmode_disable_ss_quirk:1; + unsigned gfladj_refclk_lpm_sel:1; unsigned tx_de_emphasis_quirk:1; unsigned tx_de_emphasis:2; unsigned dis_metastability_quirk:1; + unsigned dis_split_quirk:1; + unsigned async_callbacks:1; + u16 imod_interval; + + int max_cfg_eps; + int last_fifo_depth; + int num_ep_resized; + struct dentry *debug_root; }; #define INCRX_BURST_MODE 0 @@ -1173,31 +1341,7 @@ struct dwc3_event_type { #define DWC3_DEPEVT_EPCMDCMPLT 0x07 /** - * dwc3_ep_event_string - returns event name - * @event: then event code - */ -static inline const char *dwc3_ep_event_string(u8 event) -{ - switch (event) { - case DWC3_DEPEVT_XFERCOMPLETE: - return "Transfer Complete"; - case DWC3_DEPEVT_XFERINPROGRESS: - return "Transfer In-Progress"; - case DWC3_DEPEVT_XFERNOTREADY: - return "Transfer Not Ready"; - case DWC3_DEPEVT_RXTXFIFOEVT: - return "FIFO"; - case DWC3_DEPEVT_STREAMEVT: - return "Stream"; - case DWC3_DEPEVT_EPCMDCMPLT: - return "Endpoint Command Complete"; - } - - return "UNKNOWN"; -} - -/** - * struct dwc3_event_depvt - Device Endpoint Events + * struct dwc3_event_depevt - Device Endpoint Events * @one_bit: indicates this is an endpoint event (not used) * @endpoint_number: number of the endpoint * @endpoint_event: The event we have: @@ -1236,6 +1380,10 @@ struct dwc3_event_depevt { #define DEPEVT_STREAMEVT_FOUND 1 #define DEPEVT_STREAMEVT_NOTFOUND 2 +/* Stream event parameter */ +#define DEPEVT_STREAM_PRIME 0xfffe +#define DEPEVT_STREAM_NOSTREAM 0x0 + /* Control-only Status */ #define DEPEVT_STATUS_CONTROL_DATA 1 #define DEPEVT_STATUS_CONTROL_STATUS 2 @@ -1262,7 +1410,7 @@ struct dwc3_event_depevt { * 3 - ULStChng * 4 - WkUpEvt * 5 - Reserved - * 6 - EOPF + * 6 - Suspend (EOPF on revisions 2.10a and prior) * 7 - SOF * 8 - Reserved * 9 - ErrticErr @@ -1334,28 +1482,63 @@ struct dwc3_gadget_ep_cmd_params { #define DWC3_HAS_OTG BIT(3) /* prototypes */ -int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc); void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode); +void dwc3_set_mode(struct dwc3 *dwc, u32 mode); +u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type); -/* check whether we are on the DWC_usb3 core */ -static inline bool dwc3_is_usb3(struct dwc3 *dwc) -{ - return !(dwc->revision & DWC3_REVISION_IS_DWC31); -} +#define DWC3_IP_IS(_ip) \ + (dwc->ip == _ip##_IP) + +#define DWC3_VER_IS(_ip, _ver) \ + (DWC3_IP_IS(_ip) && dwc->revision == _ip##_REVISION_##_ver) + +#define DWC3_VER_IS_PRIOR(_ip, _ver) \ + (DWC3_IP_IS(_ip) && dwc->revision < _ip##_REVISION_##_ver) -/* check whether we are on the DWC_usb31 core */ -static inline bool dwc3_is_usb31(struct dwc3 *dwc) +#define DWC3_VER_IS_WITHIN(_ip, _from, _to) \ + (DWC3_IP_IS(_ip) && \ + (int)dwc->revision >= _ip##_REVISION_##_from && \ + (!(_ip##_REVISION_##_to) || \ + dwc->revision <= _ip##_REVISION_##_to)) + +#define DWC3_VER_TYPE_IS_WITHIN(_ip, _ver, _from, _to) \ + (DWC3_VER_IS(_ip, _ver) && \ + dwc->version_type >= _ip##_VERSIONTYPE_##_from && \ + (!(_ip##_VERSIONTYPE_##_to) || \ + dwc->version_type <= _ip##_VERSIONTYPE_##_to)) + +/** + * dwc3_mdwidth - get MDWIDTH value in bits + * @dwc: pointer to our context structure + * + * Return MDWIDTH configuration value in bits. + */ +static inline u32 dwc3_mdwidth(struct dwc3 *dwc) { - return !!(dwc->revision & DWC3_REVISION_IS_DWC31); + u32 mdwidth; + + mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0); + if (DWC3_IP_IS(DWC32)) + mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6); + + return mdwidth; } bool dwc3_has_imod(struct dwc3 *dwc); +int dwc3_event_buffers_setup(struct dwc3 *dwc); +void dwc3_event_buffers_cleanup(struct dwc3 *dwc); + +int dwc3_core_soft_reset(struct dwc3 *dwc); + #if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) int dwc3_host_init(struct dwc3 *dwc); +void dwc3_host_exit(struct dwc3 *dwc); #else static inline int dwc3_host_init(struct dwc3 *dwc) { return 0; } +static inline void dwc3_host_exit(struct dwc3 *dwc) +{ } #endif #if IS_ENABLED(CONFIG_USB_DWC3_GADGET) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) @@ -1364,9 +1547,12 @@ void dwc3_gadget_exit(struct dwc3 *dwc); int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode); int dwc3_gadget_get_link_state(struct dwc3 *dwc); int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state); -int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, +int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd, struct dwc3_gadget_ep_cmd_params *params); -int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param); +int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd, + u32 param); +void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc); +void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep, int status); #else static inline int dwc3_gadget_init(struct dwc3 *dwc) { return 0; } @@ -1380,9 +1566,14 @@ static inline int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) { return 0; } +static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd, + struct dwc3_gadget_ep_cmd_params *params) +{ return 0; } static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param) { return 0; } +static inline void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc) +{ } #endif #if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index 4f75ab3505..8bb2c9e3b9 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -1,8 +1,8 @@ -// SPDX-License-Identifier: GPL-2.0 -/** +/* SPDX-License-Identifier: GPL-2.0 */ +/* * debug.h - DesignWare USB3 DRD Controller Debug Header * - * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com * * Authors: Felipe Balbi <balbi@ti.com>, * Sebastian Andrzej Siewior <bigeasy@linutronix.de> @@ -68,6 +68,8 @@ dwc3_gadget_generic_cmd_string(u8 cmd) return "All FIFO Flush"; case DWC3_DGCMD_SET_ENDPOINT_NRDY: return "Set Endpoint NRDY"; + case DWC3_DGCMD_SET_ENDPOINT_PRIME: + return "Set Endpoint Prime"; case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK: return "Run SoC Bus Loopback Test"; default: @@ -112,7 +114,7 @@ dwc3_gadget_link_string(enum dwc3_link_state link_state) case DWC3_LINK_STATE_RESUME: return "Resume"; default: - return "UNKNOWN link state\n"; + return "UNKNOWN link state"; } } @@ -141,7 +143,7 @@ dwc3_gadget_hs_link_string(enum dwc3_link_state link_state) case DWC3_LINK_STATE_RESUME: return "Resume"; default: - return "UNKNOWN link state\n"; + return "UNKNOWN link state"; } } @@ -193,294 +195,54 @@ static inline const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) * dwc3_gadget_event_string - returns event name * @event: the event code */ -static inline const char * -dwc3_gadget_event_string(char *str, const struct dwc3_event_devt *event) +static inline const char *dwc3_gadget_event_string(char *str, size_t size, + const struct dwc3_event_devt *event) { enum dwc3_link_state state = event->event_info & DWC3_LINK_STATE_MASK; switch (event->type) { case DWC3_DEVICE_EVENT_DISCONNECT: - sprintf(str, "Disconnect: [%s]", + snprintf(str, size, "Disconnect: [%s]", dwc3_gadget_link_string(state)); break; case DWC3_DEVICE_EVENT_RESET: - sprintf(str, "Reset [%s]", dwc3_gadget_link_string(state)); + snprintf(str, size, "Reset [%s]", + dwc3_gadget_link_string(state)); break; case DWC3_DEVICE_EVENT_CONNECT_DONE: - sprintf(str, "Connection Done [%s]", + snprintf(str, size, "Connection Done [%s]", dwc3_gadget_link_string(state)); break; case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: - sprintf(str, "Link Change [%s]", + snprintf(str, size, "Link Change [%s]", dwc3_gadget_link_string(state)); break; case DWC3_DEVICE_EVENT_WAKEUP: - sprintf(str, "WakeUp [%s]", dwc3_gadget_link_string(state)); + snprintf(str, size, "WakeUp [%s]", + dwc3_gadget_link_string(state)); break; - case DWC3_DEVICE_EVENT_EOPF: - sprintf(str, "End-Of-Frame [%s]", + case DWC3_DEVICE_EVENT_SUSPEND: + snprintf(str, size, "Suspend [%s]", dwc3_gadget_link_string(state)); break; case DWC3_DEVICE_EVENT_SOF: - sprintf(str, "Start-Of-Frame [%s]", + snprintf(str, size, "Start-Of-Frame [%s]", dwc3_gadget_link_string(state)); break; case DWC3_DEVICE_EVENT_ERRATIC_ERROR: - sprintf(str, "Erratic Error [%s]", + snprintf(str, size, "Erratic Error [%s]", dwc3_gadget_link_string(state)); break; case DWC3_DEVICE_EVENT_CMD_CMPL: - sprintf(str, "Command Complete [%s]", + snprintf(str, size, "Command Complete [%s]", dwc3_gadget_link_string(state)); break; case DWC3_DEVICE_EVENT_OVERFLOW: - sprintf(str, "Overflow [%s]", dwc3_gadget_link_string(state)); - break; - default: - sprintf(str, "UNKNOWN"); - } - - return str; -} - -static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str) -{ - switch (t & USB_RECIP_MASK) { - case USB_RECIP_INTERFACE: - sprintf(str, "Get Interface Status(Intf = %d, Length = %d)", - i, l); - break; - case USB_RECIP_ENDPOINT: - sprintf(str, "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) -{ - switch (t & USB_RECIP_MASK) { - case USB_RECIP_DEVICE: - sprintf(str, "%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: - sprintf(str, "%s Interface Feature(%s)", - b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set", - v == USB_INTRF_FUNC_SUSPEND ? - "Function Suspend" : "UNKNOWN"); - break; - case USB_RECIP_ENDPOINT: - sprintf(str, "%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) -{ - sprintf(str, "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) -{ - sprintf(str, "%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) -{ - sprintf(str, "Get Configuration(Length = %d)", l); -} - -static inline void dwc3_decode_set_configuration(__u8 v, char *str) -{ - sprintf(str, "Set Configuration(Config = %d)", v); -} - -static inline void dwc3_decode_get_intf(__u16 i, __u16 l, char *str) -{ - sprintf(str, "Get Interface(Intf = %d, Length = %d)", i, l); -} - -static inline void dwc3_decode_set_intf(__u8 v, __u16 i, char *str) -{ - sprintf(str, "Set Interface(Intf = %d, Alt.Setting = %d)", i, v); -} - -static inline void dwc3_decode_synch_frame(__u16 i, __u16 l, char *str) -{ - sprintf(str, "Synch Frame(Endpoint = %d, Length = %d)", i, l); -} - -static inline void dwc3_decode_set_sel(__u16 l, char *str) -{ - sprintf(str, "Set SEL(Length = %d)", l); -} - -static inline void dwc3_decode_set_isoch_delay(__u8 v, char *str) -{ - sprintf(str, "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, __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); - break; - case USB_REQ_CLEAR_FEATURE: - case USB_REQ_SET_FEATURE: - dwc3_decode_set_clear_feature(bRequestType, bRequest, wValue, - wIndex, str); - break; - case USB_REQ_SET_ADDRESS: - dwc3_decode_set_address(wValue, str); - break; - case USB_REQ_GET_DESCRIPTOR: - case USB_REQ_SET_DESCRIPTOR: - dwc3_decode_get_set_descriptor(bRequestType, bRequest, wValue, - wIndex, wLength, str); - break; - case USB_REQ_GET_CONFIGURATION: - dwc3_decode_get_configuration(wLength, str); - break; - case USB_REQ_SET_CONFIGURATION: - dwc3_decode_set_configuration(wValue, str); - break; - case USB_REQ_GET_INTERFACE: - dwc3_decode_get_intf(wIndex, wLength, str); - break; - case USB_REQ_SET_INTERFACE: - dwc3_decode_set_intf(wValue, wIndex, str); - break; - case USB_REQ_SYNCH_FRAME: - dwc3_decode_synch_frame(wIndex, wLength, str); - break; - case USB_REQ_SET_SEL: - dwc3_decode_set_sel(wLength, str); - break; - case USB_REQ_SET_ISOCH_DELAY: - dwc3_decode_set_isoch_delay(wValue, str); + snprintf(str, size, "Overflow [%s]", + dwc3_gadget_link_string(state)); break; default: - sprintf(str, "%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); + snprintf(str, size, "UNKNOWN"); } return str; @@ -490,48 +252,41 @@ static inline const char *dwc3_decode_ctrl(char *str, __u8 bRequestType, * dwc3_ep_event_string - returns event name * @event: then event code */ -static inline const char * -dwc3_ep_event_string(char *str, const struct dwc3_event_depevt *event, - u32 ep0state) +static inline const char *dwc3_ep_event_string(char *str, size_t size, + const struct dwc3_event_depevt *event, u32 ep0state) { u8 epnum = event->endpoint_number; size_t len; int status; - int ret; - ret = sprintf(str, "ep%d%s: ", epnum >> 1, + len = scnprintf(str, size, "ep%d%s: ", epnum >> 1, (epnum & 1) ? "in" : "out"); - if (ret < 0) - return "UNKNOWN"; status = event->status; switch (event->endpoint_event) { case DWC3_DEPEVT_XFERCOMPLETE: - len = strlen(str); - sprintf(str + len, "Transfer Complete (%c%c%c)", + len += scnprintf(str + len, size - len, + "Transfer Complete (%c%c%c)", status & DEPEVT_STATUS_SHORT ? 'S' : 's', status & DEPEVT_STATUS_IOC ? 'I' : 'i', status & DEPEVT_STATUS_LST ? 'L' : 'l'); - len = strlen(str); - if (epnum <= 1) - sprintf(str + len, " [%s]", dwc3_ep0_state_string(ep0state)); + scnprintf(str + len, size - len, " [%s]", + dwc3_ep0_state_string(ep0state)); break; case DWC3_DEPEVT_XFERINPROGRESS: - len = strlen(str); - - sprintf(str + len, "Transfer In Progress [%d] (%c%c%c)", + scnprintf(str + len, size - len, + "Transfer In Progress [%08x] (%c%c%c)", event->parameters, status & DEPEVT_STATUS_SHORT ? 'S' : 's', status & DEPEVT_STATUS_IOC ? 'I' : 'i', status & DEPEVT_STATUS_LST ? 'M' : 'm'); break; case DWC3_DEPEVT_XFERNOTREADY: - len = strlen(str); - - sprintf(str + len, "Transfer Not Ready [%d]%s", + len += scnprintf(str + len, size - len, + "Transfer Not Ready [%08x]%s", event->parameters, status & DEPEVT_STATUS_TRANSFER_ACTIVE ? " (Active)" : " (Not Active)"); @@ -542,36 +297,38 @@ dwc3_ep_event_string(char *str, const struct dwc3_event_depevt *event, switch (phase) { case DEPEVT_STATUS_CONTROL_DATA: - strcat(str, " [Data Phase]"); + scnprintf(str + len, size - len, + " [Data Phase]"); break; case DEPEVT_STATUS_CONTROL_STATUS: - strcat(str, " [Status Phase]"); + scnprintf(str + len, size - len, + " [Status Phase]"); } } break; case DWC3_DEPEVT_RXTXFIFOEVT: - strcat(str, "FIFO"); + scnprintf(str + len, size - len, "FIFO"); break; case DWC3_DEPEVT_STREAMEVT: status = event->status; switch (status) { case DEPEVT_STREAMEVT_FOUND: - sprintf(str + ret, " Stream %d Found", + scnprintf(str + len, size - len, " Stream %d Found", event->parameters); break; case DEPEVT_STREAMEVT_NOTFOUND: default: - strcat(str, " Stream Not Found"); + scnprintf(str + len, size - len, " Stream Not Found"); break; } break; case DWC3_DEPEVT_EPCMDCMPLT: - strcat(str, "Endpoint Command Complete"); + scnprintf(str + len, size - len, "Endpoint Command Complete"); break; default: - sprintf(str, "UNKNOWN"); + scnprintf(str + len, size - len, "UNKNOWN"); } return str; @@ -596,8 +353,8 @@ static inline const char *dwc3_gadget_event_type_string(u8 event) return "Wake-Up"; case DWC3_DEVICE_EVENT_HIBER_REQ: return "Hibernation"; - case DWC3_DEVICE_EVENT_EOPF: - return "End of Periodic Frame"; + case DWC3_DEVICE_EVENT_SUSPEND: + return "Suspend"; case DWC3_DEVICE_EVENT_SOF: return "Start of Frame"; case DWC3_DEVICE_EVENT_ERRATIC_ERROR: @@ -611,14 +368,17 @@ static inline const char *dwc3_gadget_event_type_string(u8 event) } } -static inline const char *dwc3_decode_event(char *str, u32 event, u32 ep0state) +static inline const char *dwc3_decode_event(char *str, size_t size, u32 event, + u32 ep0state) { - const union dwc3_event evt = (union dwc3_event) event; + union dwc3_event evt; + + memcpy(&evt, &event, sizeof(event)); if (evt.type.is_devspec) - return dwc3_gadget_event_string(str, &evt.devt); + return dwc3_gadget_event_string(str, size, &evt.devt); else - return dwc3_ep_event_string(str, &evt.depevt, ep0state); + return dwc3_ep_event_string(str, size, &evt.depevt, ep0state); } static inline const char *dwc3_ep_cmd_status_string(int status) @@ -653,9 +413,15 @@ static inline const char *dwc3_gadget_generic_cmd_status_string(int status) #ifdef CONFIG_DEBUG_FS -extern void dwc3_debugfs_init(struct dwc3 *); -extern void dwc3_debugfs_exit(struct dwc3 *); +extern void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep); +extern void dwc3_debugfs_remove_endpoint_dir(struct dwc3_ep *dep); +extern void dwc3_debugfs_init(struct dwc3 *d); +extern void dwc3_debugfs_exit(struct dwc3 *d); #else +static inline void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep) +{ } +static inline void dwc3_debugfs_remove_endpoint_dir(struct dwc3_ep *dep) +{ } static inline void dwc3_debugfs_init(struct dwc3 *d) { } static inline void dwc3_debugfs_exit(struct dwc3 *d) diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index e58d9f95fe..1e62224015 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /** * dwc3-of-simple.c - OF glue layer for simple integrations * @@ -20,100 +20,58 @@ #include <of.h> struct dwc3_of_simple { - struct device_d *dev; - struct clk **clks; + struct device *dev; + struct clk_bulk_data *clks; int num_clocks; }; -static int dwc3_of_simple_clk_init(struct dwc3_of_simple *simple, int count) -{ - struct device_d *dev = simple->dev; - struct device_node *np = dev->device_node; - int i; - - simple->num_clocks = count; - - if (!count) - return 0; - - simple->clks = xzalloc(sizeof(struct clk *)); - if (!simple->clks) - return -ENOMEM; - - for (i = 0; i < simple->num_clocks; i++) { - struct clk *clk; - - clk = of_clk_get(np, i); - if (IS_ERR(clk)) { - while (--i >= 0) { - clk_disable(simple->clks[i]); - clk_put(simple->clks[i]); - } - return PTR_ERR(clk); - } - - simple->clks[i] = clk; - } - - return 0; -} - -static int dwc3_of_simple_probe(struct device_d *dev) +static int dwc3_of_simple_probe(struct device *dev) { struct dwc3_of_simple *simple; - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; int ret; - int i; simple = xzalloc(sizeof(*simple)); if (!simple) return -ENOMEM; - dev->priv = simple; + dev->priv = simple; simple->dev = dev; - ret = dwc3_of_simple_clk_init(simple, of_count_phandle_with_args(np, - "clocks", "#clock-cells")); + ret = clk_bulk_get_all(simple->dev, &simple->clks); + if (ret < 0) + return ret; + + simple->num_clocks = ret; + ret = clk_bulk_enable(simple->num_clocks, simple->clks); if (ret) return ret; ret = of_platform_populate(np, NULL, dev); if (ret) { - for (i = 0; i < simple->num_clocks; i++) { - clk_disable(simple->clks[i]); - clk_put(simple->clks[i]); - } + clk_bulk_disable(simple->num_clocks, simple->clks); return ret; } - return 0; + return 0; } -static void dwc3_of_simple_remove(struct device_d *dev) +static void dwc3_of_simple_remove(struct device *dev) { struct dwc3_of_simple *simple = dev->priv; - int i; - for (i = 0; i < simple->num_clocks; i++) { - clk_disable(simple->clks[i]); - clk_put(simple->clks[i]); - } - simple->num_clocks = 0; + clk_bulk_disable(simple->num_clocks, simple->clks); } static const struct of_device_id of_dwc3_simple_match[] = { {.compatible = "rockchip,rk3399-dwc3"}, {.compatible = "xlnx,zynqmp-dwc3"}, - {.compatible = "fsl,ls1046a-dwc3"}, - {.compatible = "cavium,octeon-7130-usb-uctl"}, - {.compatible = "sprd,sc9860-dwc3"}, - {.compatible = "amlogic,meson-axg-dwc3"}, - {.compatible = "amlogic,meson-gxl-dwc3"}, - {.compatible = "allwinner,sun50i-h6-dwc3"}, + {.compatible = "fsl,imx8mp-dwc3"}, {/* Sentinel */}}; +MODULE_DEVICE_TABLE(of, of_dwc3_simple_match); -static struct driver_d dwc3_of_simple_driver = { +static struct driver dwc3_of_simple_driver = { .probe = dwc3_of_simple_probe, .remove = dwc3_of_simple_remove, .name = "dwc3-of-simple", diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index b757a57886..6285566b4b 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -1,26 +1,26 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling * - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com * * Authors: Felipe Balbi <balbi@ti.com>, * Sebastian Andrzej Siewior <bigeasy@linutronix.de> - * - * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/ep0.c) and ported - * to uboot. - * - * commit c00552ebaf : Merge 3.18-rc7 into usb-next */ #include <common.h> #include <dma.h> #include <linux/kernel.h> #include <linux/list.h> +#include <linux/completion.h> -#include <usb/gadget.h> +#include <linux/usb/gadget.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h> #include "core.h" +#include "debug.h" #include "gadget.h" #include "io.h" @@ -29,11 +29,10 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, struct dwc3_ep *dep, struct dwc3_request *req); static void dwc3_ep0_prepare_one_trb(struct dwc3_ep *dep, - dma_addr_t buf_dma, u32 len, u32 type, - bool chain) + dma_addr_t buf_dma, u32 len, u32 type, bool chain) { - struct dwc3_trb *trb; - struct dwc3 *dwc; + struct dwc3_trb *trb; + struct dwc3 *dwc; dwc = dep->dwc; trb = &dwc->ep0_trb[dep->trb_enqueue]; @@ -47,67 +46,47 @@ static void dwc3_ep0_prepare_one_trb(struct dwc3_ep *dep, trb->ctrl = type; trb->ctrl |= (DWC3_TRB_CTRL_HWO - | DWC3_TRB_CTRL_ISP_IMI); + | DWC3_TRB_CTRL_ISP_IMI); if (chain) trb->ctrl |= DWC3_TRB_CTRL_CHN; else trb->ctrl |= (DWC3_TRB_CTRL_IOC - | DWC3_TRB_CTRL_LST); + | DWC3_TRB_CTRL_LST); } static int dwc3_ep0_start_trans(struct dwc3_ep *dep) { struct dwc3_gadget_ep_cmd_params params; - struct dwc3 *dwc; - int ret; - - dwc = dep->dwc; + struct dwc3 *dwc; + int ret; - if (dep->flags & DWC3_EP_TRANSFER_STARTED) { - dev_err(dwc->dev, "%s: transfer already started\n", dep->name); + if (dep->flags & DWC3_EP_TRANSFER_STARTED) return 0; - } + + dwc = dep->dwc; memset(¶ms, 0, sizeof(params)); params.param0 = upper_32_bits(dwc->ep0_trb_addr); params.param1 = lower_32_bits(dwc->ep0_trb_addr); ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_STARTTRANSFER, ¶ms); - if (ret < 0) { - dev_err(dwc->dev, "%s: STARTTRANSFER failed\n", dep->name); + if (ret < 0) return ret; - } dwc->ep0_next_event = DWC3_EP0_COMPLETE; return 0; } -static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) -{ - switch (state) { - case EP0_UNCONNECTED: - return "Unconnected"; - case EP0_SETUP_PHASE: - return "Setup Phase"; - case EP0_DATA_PHASE: - return "Data Phase"; - case EP0_STATUS_PHASE: - return "Status Phase"; - default: - return "UNKNOWN"; - } -} - static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, - struct dwc3_request *req) + struct dwc3_request *req) { - struct dwc3 *dwc = dep->dwc; + struct dwc3 *dwc = dep->dwc; - req->request.actual = 0; - req->request.status = -EINPROGRESS; - req->epnum = dep->number; + req->request.actual = 0; + req->request.status = -EINPROGRESS; + req->epnum = dep->number; list_add_tail(&req->list, &dep->pending_list); @@ -121,7 +100,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, * IRQ we were waiting for is long gone. */ if (dep->flags & DWC3_EP_PENDING_REQUEST) { - unsigned direction; + unsigned int direction; direction = !!(dep->flags & DWC3_EP0_DIR_IN); @@ -143,16 +122,14 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, * handle it here. */ if (dwc->delayed_status) { - unsigned direction; + unsigned int direction; direction = !dwc->ep0_expect_in; dwc->delayed_status = false; - usb_gadget_set_state(&dwc->gadget, USB_STATE_CONFIGURED); + usb_gadget_set_state(dwc->gadget, USB_STATE_CONFIGURED); if (dwc->ep0state == EP0_STATUS_PHASE) __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]); - else - dev_dbg(dwc->dev, "too early for delayed status\n"); return 0; } @@ -190,7 +167,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, * XferNotReady(STATUS). */ if (dwc->three_stage_setup) { - unsigned direction; + unsigned int direction; direction = dwc->ep0_expect_in; dwc->ep0state = EP0_DATA_PHASE; @@ -205,16 +182,18 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request) { - struct dwc3_request *req = to_dwc3_request(request); - struct dwc3_ep *dep = to_dwc3_ep(ep); - struct dwc3 *dwc = dep->dwc; - unsigned long flags; - int ret; + struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + unsigned long flags; + + int ret; spin_lock_irqsave(&dwc->lock, flags); - if (!dep->endpoint.desc) { - dev_err(dwc->dev, "trying to queue request %p to disabled %s\n", - request, dep->name); + if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) { + dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n", + dep->name); ret = -ESHUTDOWN; goto out; } @@ -225,10 +204,6 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request) goto out; } - dev_dbg(dwc->dev, "queueing request %p to %s length %d state '%s'\n", - request, dep->name, request->length, - dwc3_ep0_state_string(dwc->ep0state)); - ret = __dwc3_gadget_ep0_queue(dep, req); out: @@ -237,9 +212,9 @@ out: return ret; } -static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) +void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) { - struct dwc3_ep *dep; + struct dwc3_ep *dep; /* reinitialize physical ep1 */ dep = dwc->eps[1]; @@ -252,20 +227,22 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) dwc->delayed_status = false; if (!list_empty(&dep->pending_list)) { - struct dwc3_request *req; + struct dwc3_request *req; req = next_request(&dep->pending_list); dwc3_gadget_giveback(dep, req, -ECONNRESET); } + dwc->eps[0]->trb_enqueue = 0; + dwc->eps[1]->trb_enqueue = 0; dwc->ep0state = EP0_SETUP_PHASE; dwc3_ep0_out_start(dwc); } int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value) { - struct dwc3_ep *dep = to_dwc3_ep(ep); - struct dwc3 *dwc = dep->dwc; + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; dwc3_ep0_stall_and_restart(dwc); @@ -274,8 +251,8 @@ int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value) int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value) { - unsigned long flags; - int ret; + unsigned long flags; + int ret; spin_lock_irqsave(&dwc->lock, flags); ret = __dwc3_gadget_ep0_set_halt(ep, value); @@ -286,27 +263,49 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value) void dwc3_ep0_out_start(struct dwc3 *dwc) { - struct dwc3_ep *dep; - int ret; + struct dwc3_ep *dep; + int ret; + int i; + + complete(&dwc->ep0_in_setup); dep = dwc->eps[0]; dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 8, - DWC3_TRBCTL_CONTROL_SETUP, false); + DWC3_TRBCTL_CONTROL_SETUP, false); ret = dwc3_ep0_start_trans(dep); WARN_ON(ret < 0); + for (i = 2; i < DWC3_ENDPOINTS_NUM; i++) { + struct dwc3_ep *dwc3_ep; + + dwc3_ep = dwc->eps[i]; + if (!dwc3_ep) + continue; + + if (!(dwc3_ep->flags & DWC3_EP_DELAY_STOP)) + continue; + + dwc3_ep->flags &= ~DWC3_EP_DELAY_STOP; + if (dwc->connected) + dwc3_stop_active_transfer(dwc3_ep, true, true); + else + dwc3_remove_requests(dwc, dwc3_ep, -ESHUTDOWN); + } } static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le) { - struct dwc3_ep *dep; - u32 windex = le16_to_cpu(wIndex_le); - u32 epnum; + struct dwc3_ep *dep; + u32 windex = le16_to_cpu(wIndex_le); + u32 epnum; epnum = (windex & USB_ENDPOINT_NUMBER_MASK) << 1; if ((windex & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) epnum |= 1; dep = dwc->eps[epnum]; + if (dep == NULL) + return NULL; + if (dep->flags & DWC3_EP_ENABLED) return dep; @@ -320,14 +319,14 @@ static void dwc3_ep0_status_cmpl(struct usb_ep *ep, struct usb_request *req) * ch 9.4.5 */ static int dwc3_ep0_handle_status(struct dwc3 *dwc, - struct usb_ctrlrequest *ctrl) + struct usb_ctrlrequest *ctrl) { - struct dwc3_ep *dep; - u32 recip; - u32 value; - u32 reg; - u16 usb_status = 0; - __le16 *response_pkt; + struct dwc3_ep *dep; + u32 recip; + u32 value; + u32 reg; + u16 usb_status = 0; + __le16 *response_pkt; /* We don't support PTM_STATUS */ value = le16_to_cpu(ctrl->wValue); @@ -340,7 +339,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, /* * LTM will be set once we know how to set this in HW. */ - usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED; + usb_status |= dwc->gadget->is_selfpowered; if ((dwc->speed == DWC3_DSTS_SUPERSPEED) || (dwc->speed == DWC3_DSTS_SUPERSPEED_PLUS)) { @@ -385,7 +384,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, } static int dwc3_ep0_handle_u1(struct dwc3 *dwc, enum usb_device_state state, - int set) + int set) { u32 reg; @@ -394,6 +393,8 @@ static int dwc3_ep0_handle_u1(struct dwc3 *dwc, enum usb_device_state state, if ((dwc->speed != DWC3_DSTS_SUPERSPEED) && (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS)) return -EINVAL; + if (set && dwc->dis_u1_entry_quirk) + return -EINVAL; reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (set) @@ -406,15 +407,18 @@ static int dwc3_ep0_handle_u1(struct dwc3 *dwc, enum usb_device_state state, } static int dwc3_ep0_handle_u2(struct dwc3 *dwc, enum usb_device_state state, - int set) + int set) { u32 reg; + if (state != USB_STATE_CONFIGURED) return -EINVAL; if ((dwc->speed != DWC3_DSTS_SUPERSPEED) && (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS)) return -EINVAL; + if (set && dwc->dis_u2_entry_quirk) + return -EINVAL; reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (set) @@ -427,7 +431,7 @@ static int dwc3_ep0_handle_u2(struct dwc3 *dwc, enum usb_device_state state, } static int dwc3_ep0_handle_test(struct dwc3 *dwc, enum usb_device_state state, - u32 wIndex, int set) + u32 wIndex, int set) { if ((wIndex & 0xff) != 0) return -EINVAL; @@ -435,11 +439,11 @@ static int dwc3_ep0_handle_test(struct dwc3 *dwc, enum usb_device_state state, return -EINVAL; switch (wIndex >> 8) { - case TEST_J: - case TEST_K: - case TEST_SE0_NAK: - case TEST_PACKET: - case TEST_FORCE_EN: + case USB_TEST_J: + case USB_TEST_K: + case USB_TEST_SE0_NAK: + case USB_TEST_PACKET: + case USB_TEST_FORCE_ENABLE: dwc->test_mode_nr = wIndex >> 8; dwc->test_mode = true; break; @@ -451,22 +455,22 @@ static int dwc3_ep0_handle_test(struct dwc3 *dwc, enum usb_device_state state, } static int dwc3_ep0_handle_device(struct dwc3 *dwc, - struct usb_ctrlrequest *ctrl, int set) + struct usb_ctrlrequest *ctrl, int set) { - enum usb_device_state state; - u32 wValue; - u32 wIndex; - int ret = 0; + enum usb_device_state state; + u32 wValue; + u32 wIndex; + int ret = 0; wValue = le16_to_cpu(ctrl->wValue); wIndex = le16_to_cpu(ctrl->wIndex); - state = dwc->gadget.state; + state = dwc->gadget->state; switch (wValue) { case USB_DEVICE_REMOTE_WAKEUP: break; /* - * 9.4.1 says only only for SS, in AddressState only for + * 9.4.1 says only for SS, in AddressState only for * default control pipe */ case USB_DEVICE_U1_ENABLE: @@ -489,10 +493,10 @@ static int dwc3_ep0_handle_device(struct dwc3 *dwc, } static int dwc3_ep0_handle_intf(struct dwc3 *dwc, - struct usb_ctrlrequest *ctrl, int set) + struct usb_ctrlrequest *ctrl, int set) { - u32 wValue; - int ret = 0; + u32 wValue; + int ret = 0; wValue = le16_to_cpu(ctrl->wValue); @@ -514,11 +518,11 @@ static int dwc3_ep0_handle_intf(struct dwc3 *dwc, } static int dwc3_ep0_handle_endpoint(struct dwc3 *dwc, - struct usb_ctrlrequest *ctrl, int set) + struct usb_ctrlrequest *ctrl, int set) { - struct dwc3_ep *dep; - u32 wValue; - int ret; + struct dwc3_ep *dep; + u32 wValue; + int ret; wValue = le16_to_cpu(ctrl->wValue); @@ -534,6 +538,11 @@ static int dwc3_ep0_handle_endpoint(struct dwc3 *dwc, ret = __dwc3_gadget_ep_set_halt(dep, set, true); if (ret) return -EINVAL; + + /* ClearFeature(Halt) may need delayed status */ + if (!set && (dep->flags & DWC3_EP_END_TRANSFER_PENDING)) + return USB_GADGET_DELAYED_STATUS; + break; default: return -EINVAL; @@ -542,12 +551,11 @@ static int dwc3_ep0_handle_endpoint(struct dwc3 *dwc, return 0; } - static int dwc3_ep0_handle_feature(struct dwc3 *dwc, - struct usb_ctrlrequest *ctrl, int set) + struct usb_ctrlrequest *ctrl, int set) { - u32 recip; - int ret; + u32 recip; + int ret; recip = ctrl->bRequestType & USB_RECIP_MASK; @@ -570,7 +578,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc, static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { - enum usb_device_state state = dwc->gadget.state; + enum usb_device_state state = dwc->gadget->state; u32 addr; u32 reg; @@ -581,7 +589,7 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) } if (state == USB_STATE_CONFIGURED) { - dev_err(dwc->dev, "trying to set address when configured\n"); + dev_err(dwc->dev, "can't SetAddress() from Configured State\n"); return -EINVAL; } @@ -591,27 +599,28 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) dwc3_writel(dwc->regs, DWC3_DCFG, reg); if (addr) - usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS); + usb_gadget_set_state(dwc->gadget, USB_STATE_ADDRESS); else - usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT); + usb_gadget_set_state(dwc->gadget, USB_STATE_DEFAULT); return 0; } static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { - int ret; + int ret = -EINVAL; - spin_unlock(&dwc->lock); - ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl); - spin_lock(&dwc->lock); + if (dwc->async_callbacks) { + spin_unlock(&dwc->lock); + ret = dwc->gadget_driver->setup(dwc->gadget, ctrl); + spin_lock(&dwc->lock); + } return ret; } -#define USB_GADGET_DELAYED_STATUS 0x7fff /* Impossibly large value */ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { - enum usb_device_state state = dwc->gadget.state; + enum usb_device_state state = dwc->gadget->state; u32 cfg; int ret; u32 reg; @@ -623,6 +632,8 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) return -EINVAL; case USB_STATE_ADDRESS: + dwc3_gadget_clear_tx_fifos(dwc); + ret = dwc3_ep0_delegate_req(dwc, ctrl); /* if the cfg matches and the cfg is non zero */ if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) { @@ -634,7 +645,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) * to change the state on the next usb_ep_queue() */ if (ret == 0) - usb_gadget_set_state(&dwc->gadget, + usb_gadget_set_state(dwc->gadget, USB_STATE_CONFIGURED); /* @@ -642,7 +653,10 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) * nothing is pending from application. */ reg = dwc3_readl(dwc->regs, DWC3_DCTL); - reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA); + if (!dwc->dis_u1_entry_quirk) + reg |= DWC3_DCTL_ACCEPTU1ENA; + if (!dwc->dis_u2_entry_quirk) + reg |= DWC3_DCTL_ACCEPTU2ENA; dwc3_writel(dwc->regs, DWC3_DCTL, reg); } break; @@ -650,7 +664,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) case USB_STATE_CONFIGURED: ret = dwc3_ep0_delegate_req(dwc, ctrl); if (!cfg && !ret) - usb_gadget_set_state(&dwc->gadget, + usb_gadget_set_state(dwc->gadget, USB_STATE_ADDRESS); break; default: @@ -661,11 +675,11 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req) { - struct dwc3_ep *dep = to_dwc3_ep(ep); - struct dwc3 *dwc = dep->dwc; + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; - u32 param = 0; - u32 reg; + u32 param = 0; + u32 reg; struct timing { u8 u1sel; @@ -705,9 +719,9 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req) static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { - struct dwc3_ep *dep; - enum usb_device_state state = dwc->gadget.state; - u16 wLength; + struct dwc3_ep *dep; + enum usb_device_state state = dwc->gadget->state; + u16 wLength; if (state == USB_STATE_DEFAULT) return -EINVAL; @@ -737,12 +751,11 @@ static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req); } -static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, - struct usb_ctrlrequest *ctrl) +static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { - u16 wLength; - u16 wValue; - u16 wIndex; + u16 wLength; + u16 wValue; + u16 wIndex; wValue = le16_to_cpu(ctrl->wValue); wLength = le16_to_cpu(ctrl->wLength); @@ -751,11 +764,7 @@ static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, if (wIndex || wLength) return -EINVAL; - /* - * REVISIT It's unclear from Databook what to do with this - * value. For now, just cache it. - */ - dwc->isoch_delay = wValue; + dwc->gadget->isoch_delay = wValue; return 0; } @@ -766,35 +775,27 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) switch (ctrl->bRequest) { case USB_REQ_GET_STATUS: - dev_dbg(dwc->dev, "USB_REQ_GET_STATUS\n"); ret = dwc3_ep0_handle_status(dwc, ctrl); break; case USB_REQ_CLEAR_FEATURE: - dev_dbg(dwc->dev, "USB_REQ_CLEAR_FEATURE\n"); ret = dwc3_ep0_handle_feature(dwc, ctrl, 0); break; case USB_REQ_SET_FEATURE: - dev_dbg(dwc->dev, "USB_REQ_SET_FEATURE\n"); ret = dwc3_ep0_handle_feature(dwc, ctrl, 1); break; case USB_REQ_SET_ADDRESS: - dev_dbg(dwc->dev, "USB_REQ_SET_ADDRESS\n"); ret = dwc3_ep0_set_address(dwc, ctrl); break; case USB_REQ_SET_CONFIGURATION: - dev_dbg(dwc->dev, "USB_REQ_SET_CONFIGURATION\n"); ret = dwc3_ep0_set_config(dwc, ctrl); break; case USB_REQ_SET_SEL: - dev_dbg(dwc->dev, "USB_REQ_SET_SEL\n"); ret = dwc3_ep0_set_sel(dwc, ctrl); break; case USB_REQ_SET_ISOCH_DELAY: - dev_dbg(dwc->dev, "USB_REQ_SET_ISOCH_DELAY\n"); ret = dwc3_ep0_set_isoch_delay(dwc, ctrl); break; default: - dev_dbg(dwc->dev, "Forwarding to gadget driver\n"); ret = dwc3_ep0_delegate_req(dwc, ctrl); break; } @@ -803,13 +804,13 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) } static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, - const struct dwc3_event_depevt *event) + const struct dwc3_event_depevt *event) { struct usb_ctrlrequest *ctrl = (void *) dwc->ep0_trb; int ret = -EINVAL; u32 len; - if (!dwc->gadget_driver) + if (!dwc->gadget_driver || !dwc->softconnect || !dwc->connected) goto out; len = le16_to_cpu(ctrl->wLength); @@ -837,16 +838,16 @@ out: } static void dwc3_ep0_complete_data(struct dwc3 *dwc, - const struct dwc3_event_depevt *event) + const struct dwc3_event_depevt *event) { - struct dwc3_request *r = NULL; - struct usb_request *ur; - struct dwc3_trb *trb; - struct dwc3_ep *ep0; - u32 transferred = 0; - u32 status; - u32 length; - u8 epnum; + struct dwc3_request *r; + struct usb_request *ur; + struct dwc3_trb *trb; + struct dwc3_ep *ep0; + u32 transferred = 0; + u32 status; + u32 length; + u8 epnum; epnum = event->endpoint_number; ep0 = dwc->eps[0]; @@ -860,9 +861,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, status = DWC3_TRB_SIZE_TRBSTS(trb->size); if (status == DWC3_TRBSTS_SETUP_PENDING) { - dev_dbg(dwc->dev, "Setup Pending received\n"); dwc->setup_packet_pending = true; - if (r) dwc3_gadget_giveback(ep0, r, -ECONNRESET); @@ -876,7 +875,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, ur->actual += transferred; if ((IS_ALIGNED(ur->length, ep0->endpoint.maxpacket) && - ur->length && ur->zero) || dwc->ep0_bounced) { + ur->length && ur->zero) || dwc->ep0_bounced) { trb++; trb->ctrl &= ~DWC3_TRB_CTRL_HWO; @@ -895,12 +894,12 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, } static void dwc3_ep0_complete_status(struct dwc3 *dwc, - const struct dwc3_event_depevt *event) + const struct dwc3_event_depevt *event) { - struct dwc3_request *r; - struct dwc3_ep *dep; - struct dwc3_trb *trb; - u32 status; + struct dwc3_request *r; + struct dwc3_ep *dep; + struct dwc3_trb *trb; + u32 status; dep = dwc->eps[0]; trb = dwc->ep0_trb; @@ -916,27 +915,25 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc, ret = dwc3_gadget_set_test_mode(dwc, dwc->test_mode_nr); if (ret < 0) { - dev_dbg(dwc->dev, "Invalid Test #%d\n", - dwc->test_mode_nr); + dev_err(dwc->dev, "invalid test #%d\n", + dwc->test_mode_nr); dwc3_ep0_stall_and_restart(dwc); return; } } status = DWC3_TRB_SIZE_TRBSTS(trb->size); - if (status == DWC3_TRBSTS_SETUP_PENDING) { - dev_dbg(dwc->dev, "Setup Pending received\n"); + if (status == DWC3_TRBSTS_SETUP_PENDING) dwc->setup_packet_pending = true; - } dwc->ep0state = EP0_SETUP_PHASE; dwc3_ep0_out_start(dwc); } static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, - const struct dwc3_event_depevt *event) + const struct dwc3_event_depevt *event) { - struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; + struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; dep->flags &= ~DWC3_EP_TRANSFER_STARTED; dep->resource_index = 0; @@ -944,17 +941,14 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, switch (dwc->ep0state) { case EP0_SETUP_PHASE: - dev_dbg(dwc->dev, "Setup Phase\n"); dwc3_ep0_inspect_setup(dwc, event); break; case EP0_DATA_PHASE: - dev_dbg(dwc->dev, "Data Phase\n"); dwc3_ep0_complete_data(dwc, event); break; case EP0_STATUS_PHASE: - dev_dbg(dwc->dev, "Status Phase\n"); dwc3_ep0_complete_status(dwc, event); break; default: @@ -963,31 +957,29 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, } static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, - struct dwc3_ep *dep, - struct dwc3_request *req) + struct dwc3_ep *dep, struct dwc3_request *req) { - dma_addr_t dma_addr; - int ret; + unsigned int trb_length = 0; + int ret; req->direction = !!dep->number; if (req->request.length == 0) { - dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 0, - DWC3_TRBCTL_CONTROL_DATA, false); + if (!req->direction) + trb_length = dep->endpoint.maxpacket; + + dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr, trb_length, + DWC3_TRBCTL_CONTROL_DATA, false); ret = dwc3_ep0_start_trans(dep); - } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) && - (dep->number == 0)) { - u32 maxpacket; - u32 rem; - - dma_addr = dma_map_single(dwc->dev, req->request.buf, - req->request.length, - dep->number ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (dma_mapping_error(dwc->dev, dma_addr)) - return; + } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) + && (dep->number == 0)) { + u32 maxpacket; + u32 rem; - req->request.dma = dma_addr; + ret = usb_gadget_map_request_by_dev(dwc->sysdev, + &req->request, dep->number); + if (ret) + return; maxpacket = dep->endpoint.maxpacket; rem = req->request.length % maxpacket; @@ -1009,14 +1001,11 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, ret = dwc3_ep0_start_trans(dep); } else if (IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) && req->request.length && req->request.zero) { - dma_addr = dma_map_single(dwc->dev, req->request.buf, - req->request.length, - dep->number ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (dma_mapping_error(dwc->dev, dma_addr)) - return; - req->request.dma = dma_addr; + ret = usb_gadget_map_request_by_dev(dwc->sysdev, + &req->request, dep->number); + if (ret) + return; /* prepare normal TRB */ dwc3_ep0_prepare_one_trb(dep, req->request.dma, @@ -1026,21 +1015,20 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, req->trb = &dwc->ep0_trb[dep->trb_enqueue - 1]; + if (!req->direction) + trb_length = dep->endpoint.maxpacket; + /* Now prepare one extra TRB to align transfer size */ dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr, - 0, DWC3_TRBCTL_CONTROL_DATA, + trb_length, DWC3_TRBCTL_CONTROL_DATA, false); ret = dwc3_ep0_start_trans(dep); } else { - dma_addr = dma_map_single(dwc->dev, req->request.buf, - req->request.length, - dep->number ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (dma_mapping_error(dwc->dev, dma_addr)) + ret = usb_gadget_map_request_by_dev(dwc->sysdev, + &req->request, dep->number); + if (ret) return; - req->request.dma = dma_addr; - dwc3_ep0_prepare_one_trb(dep, req->request.dma, req->request.length, DWC3_TRBCTL_CONTROL_DATA, false); @@ -1055,8 +1043,8 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, static int dwc3_ep0_start_control_status(struct dwc3_ep *dep) { - struct dwc3 *dwc = dep->dwc; - u32 type; + struct dwc3 *dwc = dep->dwc; + u32 type; type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3 : DWC3_TRBCTL_CONTROL_STATUS2; @@ -1071,20 +1059,38 @@ static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep) } static void dwc3_ep0_do_control_status(struct dwc3 *dwc, - const struct dwc3_event_depevt *event) + const struct dwc3_event_depevt *event) { - struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; + struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; __dwc3_ep0_do_control_status(dwc, dep); } -static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep) +void dwc3_ep0_send_delayed_status(struct dwc3 *dwc) +{ + unsigned int direction = !dwc->ep0_expect_in; + + dwc->delayed_status = false; + dwc->clear_stall_protocol = 0; + + if (dwc->ep0state != EP0_STATUS_PHASE) + return; + + __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]); +} + +void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep) { struct dwc3_gadget_ep_cmd_params params; - u32 cmd; - int ret; + u32 cmd; + int ret; - if (!dep->resource_index) + /* + * For status/DATA OUT stage, TRB will be queued on ep0 out + * endpoint for which resource index is zero. Hence allow + * queuing ENDXFER command for ep0 out endpoint. + */ + if (!dep->resource_index && dep->number) return; cmd = DWC3_DEPCMD_ENDTRANSFER; @@ -1092,17 +1098,17 @@ static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep) cmd |= DWC3_DEPCMD_PARAM(dep->resource_index); memset(¶ms, 0, sizeof(params)); ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); - WARN_ON(ret); + WARN_ON_ONCE(ret); dep->resource_index = 0; } static void dwc3_ep0_xfernotready(struct dwc3 *dwc, - const struct dwc3_event_depevt *event) + const struct dwc3_event_depevt *event) { switch (event->status) { case DEPEVT_STATUS_CONTROL_DATA: - dev_dbg(dwc->dev, "Control Data\n"); - + if (!dwc->softconnect || !dwc->connected) + return; /* * We already have a DATA transfer in the controller's cache, * if we receive a XferNotReady(DATA) we will ignore it, unless @@ -1113,9 +1119,9 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, * control endpoint. */ if (dwc->ep0_expect_in != event->endpoint_number) { - struct dwc3_ep *dep = dwc->eps[dwc->ep0_expect_in]; + struct dwc3_ep *dep = dwc->eps[dwc->ep0_expect_in]; - dev_dbg(dwc->dev, "Wrong direction for Data phase\n"); + dev_err(dwc->dev, "unexpected direction for Data Phase\n"); dwc3_ep0_end_control_data(dwc, dep); dwc3_ep0_stall_and_restart(dwc); return; @@ -1127,15 +1133,17 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS) return; - dev_dbg(dwc->dev, "Control Status\n"); + if (dwc->setup_packet_pending) { + dwc3_ep0_stall_and_restart(dwc); + return; + } dwc->ep0state = EP0_STATUS_PHASE; if (dwc->delayed_status) { struct dwc3_ep *dep = dwc->eps[0]; - WARN_ON(event->endpoint_number != 1); - dev_dbg(dwc->dev, "Delayed Status\n"); + WARN_ON_ONCE(event->endpoint_number != 1); /* * We should handle the delay STATUS phase here if the * request for handling delay STATUS has been queued @@ -1143,7 +1151,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, */ if (!list_empty(&dep->pending_list)) { dwc->delayed_status = false; - usb_gadget_set_state(&dwc->gadget, + usb_gadget_set_state(dwc->gadget, USB_STATE_CONFIGURED); dwc3_ep0_do_control_status(dwc, event); } @@ -1156,14 +1164,10 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, } void dwc3_ep0_interrupt(struct dwc3 *dwc, - const struct dwc3_event_depevt *event) + const struct dwc3_event_depevt *event) { - u8 epnum = event->endpoint_number; - - dev_dbg(dwc->dev, "%s while ep%d%s in state '%s'\n", - dwc3_ep_event_string(event->endpoint_event), - epnum >> 1, (epnum & 1) ? "in" : "out", - dwc3_ep0_state_string(dwc->ep0state)); + struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; + u8 cmd; switch (event->endpoint_event) { case DWC3_DEPEVT_XFERCOMPLETE: @@ -1177,7 +1181,14 @@ void dwc3_ep0_interrupt(struct dwc3 *dwc, case DWC3_DEPEVT_XFERINPROGRESS: case DWC3_DEPEVT_RXTXFIFOEVT: case DWC3_DEPEVT_STREAMEVT: + break; case DWC3_DEPEVT_EPCMDCMPLT: + cmd = DEPEVT_PARAMETER_CMD(event->parameters); + + if (cmd == DWC3_DEPCMD_ENDTRANSFER) { + dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING; + dep->flags &= ~DWC3_EP_TRANSFER_STARTED; + } break; } } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f416acc999..48be74f7e9 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1,40 +1,37 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * gadget.c - DesignWare USB3 DRD Controller Gadget Framework Link * - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com * * Authors: Felipe Balbi <balbi@ti.com>, * Sebastian Andrzej Siewior <bigeasy@linutronix.de> - * - * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/gadget.c) and ported - * to uboot. - * - * commit 8e74475b0e : usb: dwc3: gadget: use udc-core's reset notifier */ #include <common.h> #include <dma.h> #include <io.h> #include <linux/list.h> -#include <usb/gadget.h> -#include <usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/ch9.h> +#include "debug.h" #include "core.h" #include "gadget.h" +#include "io.h" #define DWC3_ALIGN_FRAME(d, n) (((d)->frame_number + ((d)->interval * (n))) \ & ~((d)->interval - 1)) - /** - * dwc3_gadget_set_test_mode - Enables USB2 Test Modes + * dwc3_gadget_set_test_mode - enables usb2 test modes * @dwc: pointer to our context structure * @mode: the mode to set (J, K SE0 NAK, Force Enable) * - * Caller should take care of locking. This function will - * return 0 on success or -EINVAL if wrong Test Selector - * is passed + * Caller should take care of locking. This function will return 0 on + * success or -EINVAL if wrong Test Selector is passed. */ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode) { @@ -44,24 +41,24 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode) reg &= ~DWC3_DCTL_TSTCTRL_MASK; switch (mode) { - case TEST_J: - case TEST_K: - case TEST_SE0_NAK: - case TEST_PACKET: - case TEST_FORCE_EN: + case USB_TEST_J: + case USB_TEST_K: + case USB_TEST_SE0_NAK: + case USB_TEST_PACKET: + case USB_TEST_FORCE_ENABLE: reg |= mode << 1; break; default: return -EINVAL; } - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); return 0; } /** - * dwc3_gadget_get_link_state - Gets current state of USB Link + * dwc3_gadget_get_link_state - gets current state of usb link * @dwc: pointer to our context structure * * Caller should take care of locking. This function will @@ -69,7 +66,7 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode) */ int dwc3_gadget_get_link_state(struct dwc3 *dwc) { - u32 reg; + u32 reg; reg = dwc3_readl(dwc->regs, DWC3_DSTS); @@ -77,7 +74,7 @@ int dwc3_gadget_get_link_state(struct dwc3 *dwc) } /** - * dwc3_gadget_set_link_state - Sets USB Link to a particular State + * dwc3_gadget_set_link_state - sets usb link to a particular state * @dwc: pointer to our context structure * @state: the state to put link into * @@ -86,14 +83,14 @@ int dwc3_gadget_get_link_state(struct dwc3 *dwc) */ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) { - int retries = 10000; - u32 reg; + int retries = 10000; + u32 reg; /* * Wait until device controller is ready. Only applies to 1.94a and * later RTL. */ - if (dwc->revision >= DWC3_REVISION_194A) { + if (!DWC3_VER_IS_PRIOR(DWC3, 194A)) { while (--retries) { reg = dwc3_readl(dwc->regs, DWC3_DSTS); if (reg & DWC3_DSTS_DCNRD) @@ -109,6 +106,9 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; + /* set no action before sending new link state change */ + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + /* set requested state */ reg |= DWC3_DCTL_ULSTCHNGREQ(state); dwc3_writel(dwc->regs, DWC3_DCTL, reg); @@ -117,7 +117,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) * The following code is racy when called from dwc3_gadget_wakeup, * and is not needed, at least on newer versions */ - if (dwc->revision >= DWC3_REVISION_194A) + if (!DWC3_VER_IS_PRIOR(DWC3, 194A)) return 0; /* wait for a change in DSTS */ @@ -131,8 +131,6 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) udelay(5); } - dev_dbg(dwc->dev, "link state change request timed out\n"); - return -ETIMEDOUT; } @@ -170,10 +168,9 @@ static void dwc3_ep_inc_deq(struct dwc3_ep *dep) } static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep, - struct dwc3_request *req, - int status) + struct dwc3_request *req, int status) { - struct dwc3 *dwc = dep->dwc; + struct dwc3 *dwc = dep->dwc; list_del(&req->list); req->remaining = 0; @@ -182,13 +179,10 @@ static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep, if (req->request.status == -EINPROGRESS) req->request.status = status; - if (req->request.length == 0) - return; - if (req->trb) dma_unmap_single(dwc->dev, req->request.dma, - req->request.length, - req->direction ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + req->request.length, + req->direction ? DMA_TO_DEVICE : DMA_FROM_DEVICE); req->trb = NULL; } @@ -204,18 +198,13 @@ static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep, * layers that it has completed. */ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, - int status) + int status) { - struct dwc3 *dwc = dep->dwc; - dwc3_gadget_del_and_unmap_request(dep, req, status); - dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n", - req, dep->name, req->request.actual, - req->request.length, status); req->status = DWC3_REQUEST_STATUS_COMPLETED; spin_unlock(&dwc->lock); - req->request.complete(&dep->endpoint, &req->request); + usb_gadget_giveback_request(&dep->endpoint, &req->request); spin_lock(&dwc->lock); } @@ -228,12 +217,13 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, * Caller should take care of locking. Issue @cmd with a given @param to @dwc * and wait for its completion. */ -int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) +int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd, + u32 param) { - u32 timeout = 500; - int status = 0; - int ret = 0; - u32 reg; + u32 timeout = 500; + int status = 0; + int ret = 0; + u32 reg; dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param); dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT); @@ -241,16 +231,11 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) do { reg = dwc3_readl(dwc->regs, DWC3_DGCMD); if (!(reg & DWC3_DGCMD_CMDACT)) { - dev_dbg(dwc->dev, "%s: Command Complete --> %d\n", - __func__, - DWC3_DGCMD_STATUS(reg)); status = DWC3_DGCMD_STATUS(reg); if (status) ret = -EINVAL; break; } - - udelay(1); } while (--timeout); if (!timeout) { @@ -262,6 +247,7 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) } static int __dwc3_gadget_wakeup(struct dwc3 *dwc); + /** * dwc3_send_gadget_ep_cmd - issue an endpoint command * @dep: the endpoint to which the command is going to be issued @@ -271,17 +257,17 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc); * Caller should handle locking. This function will issue @cmd with given * @params to @dep and wait for its completion. */ -int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, - struct dwc3_gadget_ep_cmd_params *params) +int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd, + struct dwc3_gadget_ep_cmd_params *params) { const struct usb_endpoint_descriptor *desc = dep->endpoint.desc; - struct dwc3 *dwc = dep->dwc; - u32 timeout = 1000; - u32 saved_config = 0; - u32 reg; + struct dwc3 *dwc = dep->dwc; + u32 timeout = 5000; + u32 saved_config = 0; + u32 reg; - int cmd_status = 0; - int ret = -EINVAL; + int cmd_status = 0; + int ret = -EINVAL; /* * When operating in USB 2.0 speeds (HS/FS), if GUSB2PHYCFG.ENBLSLPM or @@ -293,7 +279,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, * * DWC_usb3 3.30a and DWC_usb31 1.90a programming guide section 3.2.2 */ - if (dwc->gadget.speed <= USB_SPEED_HIGH) { + if (dwc->gadget->speed <= USB_SPEED_HIGH || + DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER) { reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) { saved_config |= DWC3_GUSB2PHYCFG_SUSPHY; @@ -310,21 +297,40 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, } if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) { - int needs_wakeup; + int link_state; - needs_wakeup = (dwc->link_state == DWC3_LINK_STATE_U1 || - dwc->link_state == DWC3_LINK_STATE_U2 || - dwc->link_state == DWC3_LINK_STATE_U3); + /* + * Initiate remote wakeup if the link state is in U3 when + * operating in SS/SSP or L1/L2 when operating in HS/FS. If the + * link state is in U1/U2, no remote wakeup is needed. The Start + * Transfer command will initiate the link recovery. + */ + link_state = dwc3_gadget_get_link_state(dwc); + switch (link_state) { + case DWC3_LINK_STATE_U2: + if (dwc->gadget->speed >= USB_SPEED_SUPER) + break; - if (unlikely(needs_wakeup)) { + fallthrough; + case DWC3_LINK_STATE_U3: ret = __dwc3_gadget_wakeup(dwc); - dev_warn(dwc->dev, "wakeup failed --> %d\n", ret); + dev_WARN_ONCE(dwc->dev, ret, "wakeup failed --> %d\n", + ret); + break; } } - dwc3_writel(dep->regs, DWC3_DEPCMDPAR0, params->param0); - dwc3_writel(dep->regs, DWC3_DEPCMDPAR1, params->param1); - dwc3_writel(dep->regs, DWC3_DEPCMDPAR2, params->param2); + /* + * For some commands such as Update Transfer command, DEPCMDPARn + * registers are reserved. Since the driver often sends Update Transfer + * command, don't write to DEPCMDPARn to avoid register write delays and + * improve performance. + */ + if (DWC3_DEPCMD_CMD(cmd) != DWC3_DEPCMD_UPDATETRANSFER) { + dwc3_writel(dep->regs, DWC3_DEPCMDPAR0, params->param0); + dwc3_writel(dep->regs, DWC3_DEPCMDPAR1, params->param1); + dwc3_writel(dep->regs, DWC3_DEPCMDPAR2, params->param2); + } /* * Synopsys Databook 2.60a states in section 6.3.2.5.6 of that if we're @@ -348,6 +354,14 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, cmd |= DWC3_DEPCMD_CMDACT; dwc3_writel(dep->regs, DWC3_DEPCMD, cmd); + + if (!(cmd & DWC3_DEPCMD_CMDACT) || + (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER && + !(cmd & DWC3_DEPCMD_CMDIOC))) { + ret = 0; + goto skip_status; + } + do { reg = dwc3_readl(dep->regs, DWC3_DEPCMD); if (!(reg & DWC3_DEPCMD_CMDACT)) { @@ -358,6 +372,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, ret = 0; break; case DEPEVT_TRANSFER_NO_RESOURCE: + dev_warn(dwc->dev, "No resource for %s\n", + dep->name); ret = -EINVAL; break; case DEPEVT_TRANSFER_BUS_EXPIRY: @@ -387,9 +403,14 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, cmd_status = -ETIMEDOUT; } - if (ret == 0 && DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) { - dep->flags |= DWC3_EP_TRANSFER_STARTED; - dwc3_gadget_ep_get_transfer_index(dep); +skip_status: + + if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) { + if (ret == 0) + dep->flags |= DWC3_EP_TRANSFER_STARTED; + + if (ret != -ETIMEDOUT) + dwc3_gadget_ep_get_transfer_index(dep); } if (saved_config) { @@ -399,7 +420,6 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, } return ret; - } static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep) @@ -416,8 +436,9 @@ static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep) * IN transfers due to a mishandled error condition. Synopsys * STAR 9000614252. */ - if (dep->direction && (dwc->revision >= DWC3_REVISION_260A) && - (dwc->gadget.speed >= USB_SPEED_SUPER)) + if (dep->direction && + !DWC3_VER_IS_PRIOR(DWC3, 260A) && + (dwc->gadget->speed >= USB_SPEED_SUPER)) cmd |= DWC3_DEPCMD_CLEARPENDIN; memset(¶ms, 0, sizeof(params)); @@ -426,9 +447,9 @@ static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep) } static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep, - struct dwc3_trb *trb) + struct dwc3_trb *trb) { - u32 offset = (char *) trb - (char *) dep->trb_pool; + u32 offset = (char *) trb - (char *) dep->trb_pool; return dep->trb_pool_dma + offset; } @@ -438,12 +459,11 @@ static int dwc3_alloc_trb_pool(struct dwc3_ep *dep) if (dep->trb_pool) return 0; - dep->trb_pool = dma_alloc_coherent(sizeof(struct dwc3_trb) * - DWC3_TRB_NUM, - &dep->trb_pool_dma); + dep->trb_pool = dma_alloc_coherent(sizeof(struct dwc3_trb) * DWC3_TRB_NUM, + &dep->trb_pool_dma); if (!dep->trb_pool) { dev_err(dep->dwc->dev, "failed to allocate trb pool for %s\n", - dep->name); + dep->name); return -ENOMEM; } @@ -452,7 +472,8 @@ static int dwc3_alloc_trb_pool(struct dwc3_ep *dep) static void dwc3_free_trb_pool(struct dwc3_ep *dep) { - dma_free_coherent(dep->trb_pool, 0, sizeof(dma_addr_t)); + dma_free_coherent(dep->trb_pool, dep->trb_pool_dma, + sizeof(struct dwc3_trb) * DWC3_TRB_NUM); dep->trb_pool = NULL; dep->trb_pool_dma = 0; @@ -467,7 +488,7 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3_ep *dep) params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1); return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETTRANSFRESOURCE, - ¶ms); + ¶ms); } /** @@ -506,10 +527,10 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3_ep *dep) static int dwc3_gadget_start_config(struct dwc3_ep *dep) { struct dwc3_gadget_ep_cmd_params params; - struct dwc3 *dwc; - u32 cmd; - int i; - int ret; + struct dwc3 *dwc; + u32 cmd; + int i; + int ret; if (dep->number) return 0; @@ -552,9 +573,10 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action) | DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc)); /* Burst size is only needed in SuperSpeed mode */ - if (dwc->gadget.speed == USB_SPEED_SUPER) { - u32 burst = dep->endpoint.maxburst - 1; - params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst); + if (dwc->gadget->speed >= USB_SPEED_SUPER) { + u32 burst = dep->endpoint.maxburst; + + params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst - 1); } params.param0 |= action; @@ -569,6 +591,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action) if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) { params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE + | DWC3_DEPCFG_XFER_COMPLETE_EN | DWC3_DEPCFG_STREAM_EVENT_EN; dep->stream_capable = true; } @@ -592,31 +615,239 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action) params.param0 |= DWC3_DEPCFG_FIFO_NUMBER(dep->number >> 1); if (desc->bInterval) { - params.param1 |= DWC3_DEPCFG_BINTERVAL_M1(desc->bInterval - 1); - dep->interval = 1 << (desc->bInterval - 1); + u8 bInterval_m1; + + /* + * Valid range for DEPCFG.bInterval_m1 is from 0 to 13. + * + * NOTE: The programming guide incorrectly stated bInterval_m1 + * must be set to 0 when operating in fullspeed. Internally the + * controller does not have this limitation. See DWC_usb3x + * programming guide section 3.2.2.1. + */ + bInterval_m1 = min_t(u8, desc->bInterval - 1, 13); + + if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT && + dwc->gadget->speed == USB_SPEED_FULL) + dep->interval = desc->bInterval; + else + dep->interval = 1 << (desc->bInterval - 1); + + params.param1 |= DWC3_DEPCFG_BINTERVAL_M1(bInterval_m1); } return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, ¶ms); } /** - * __dwc3_gadget_ep_enable - Initializes a HW endpoint + * dwc3_gadget_calc_tx_fifo_size - calculates the txfifo size value + * @dwc: pointer to the DWC3 context + * @mult: multiplier to be used when calculating the fifo_size + * + * Calculates the size value based on the equation below: + * + * DWC3 revision 280A and prior: + * fifo_size = mult * (max_packet / mdwidth) + 1; + * + * DWC3 revision 290A and onwards: + * fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1 + * + * The max packet size is set to 1024, as the txfifo requirements mainly apply + * to super speed USB use cases. However, it is safe to overestimate the fifo + * allocations for other scenarios, i.e. high speed USB. + */ +static int dwc3_gadget_calc_tx_fifo_size(struct dwc3 *dwc, int mult) +{ + int max_packet = 1024; + int fifo_size; + int mdwidth; + + mdwidth = dwc3_mdwidth(dwc); + + /* MDWIDTH is represented in bits, we need it in bytes */ + mdwidth >>= 3; + + if (DWC3_VER_IS_PRIOR(DWC3, 290A)) + fifo_size = mult * (max_packet / mdwidth) + 1; + else + fifo_size = mult * ((max_packet + mdwidth) / mdwidth) + 1; + return fifo_size; +} + +/** + * dwc3_gadget_clear_tx_fifos - Clears txfifo allocation + * @dwc: pointer to the DWC3 context + * + * Iterates through all the endpoint registers and clears the previous txfifo + * allocations. + */ +void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc) +{ + struct dwc3_ep *dep; + int fifo_depth; + int size; + int num; + + if (!dwc->do_fifo_resize) + return; + + /* Read ep0IN related TXFIFO size */ + dep = dwc->eps[1]; + size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0)); + if (DWC3_IP_IS(DWC3)) + fifo_depth = DWC3_GTXFIFOSIZ_TXFDEP(size); + else + fifo_depth = DWC31_GTXFIFOSIZ_TXFDEP(size); + + dwc->last_fifo_depth = fifo_depth; + /* Clear existing TXFIFO for all IN eps except ep0 */ + for (num = 3; num < min_t(int, dwc->num_eps, DWC3_ENDPOINTS_NUM); + num += 2) { + dep = dwc->eps[num]; + /* Don't change TXFRAMNUM on usb31 version */ + size = DWC3_IP_IS(DWC3) ? 0 : + dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1)) & + DWC31_GTXFIFOSIZ_TXFRAMNUM; + + dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1), size); + dep->flags &= ~DWC3_EP_TXFIFO_RESIZED; + } + dwc->num_ep_resized = 0; +} + +/* + * dwc3_gadget_resize_tx_fifos - reallocate fifo spaces for current use-case + * @dwc: pointer to our context structure + * + * This function will a best effort FIFO allocation in order + * to improve FIFO usage and throughput, while still allowing + * us to enable as many endpoints as possible. + * + * Keep in mind that this operation will be highly dependent + * on the configured size for RAM1 - which contains TxFifo -, + * the amount of endpoints enabled on coreConsultant tool, and + * the width of the Master Bus. + * + * In general, FIFO depths are represented with the following equation: + * + * fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1 + * + * In conjunction with dwc3_gadget_check_config(), this resizing logic will + * ensure that all endpoints will have enough internal memory for one max + * packet per endpoint. + */ +static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep) +{ + struct dwc3 *dwc = dep->dwc; + int fifo_0_start; + int ram1_depth; + int fifo_size; + int min_depth; + int num_in_ep; + int remaining; + int num_fifos = 1; + int fifo; + int tmp; + + if (!dwc->do_fifo_resize) + return 0; + + /* resize IN endpoints except ep0 */ + if (!usb_endpoint_dir_in(dep->endpoint.desc) || dep->number <= 1) + return 0; + + /* bail if already resized */ + if (dep->flags & DWC3_EP_TXFIFO_RESIZED) + return 0; + + ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7); + + if ((dep->endpoint.maxburst > 1 && + usb_endpoint_xfer_bulk(dep->endpoint.desc)) || + usb_endpoint_xfer_isoc(dep->endpoint.desc)) + num_fifos = 3; + + if (dep->endpoint.maxburst > 6 && + (usb_endpoint_xfer_bulk(dep->endpoint.desc) || + usb_endpoint_xfer_isoc(dep->endpoint.desc)) && DWC3_IP_IS(DWC31)) + num_fifos = dwc->tx_fifo_resize_max_num; + + /* FIFO size for a single buffer */ + fifo = dwc3_gadget_calc_tx_fifo_size(dwc, 1); + + /* Calculate the number of remaining EPs w/o any FIFO */ + num_in_ep = dwc->max_cfg_eps; + num_in_ep -= dwc->num_ep_resized; + + /* Reserve at least one FIFO for the number of IN EPs */ + min_depth = num_in_ep * (fifo + 1); + remaining = ram1_depth - min_depth - dwc->last_fifo_depth; + remaining = max_t(int, 0, remaining); + /* + * We've already reserved 1 FIFO per EP, so check what we can fit in + * addition to it. If there is not enough remaining space, allocate + * all the remaining space to the EP. + */ + fifo_size = (num_fifos - 1) * fifo; + if (remaining < fifo_size) + fifo_size = remaining; + + fifo_size += fifo; + /* Last increment according to the TX FIFO size equation */ + fifo_size++; + + /* Check if TXFIFOs start at non-zero addr */ + tmp = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0)); + fifo_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(tmp); + + fifo_size |= (fifo_0_start + (dwc->last_fifo_depth << 16)); + if (DWC3_IP_IS(DWC3)) + dwc->last_fifo_depth += DWC3_GTXFIFOSIZ_TXFDEP(fifo_size); + else + dwc->last_fifo_depth += DWC31_GTXFIFOSIZ_TXFDEP(fifo_size); + + /* Check fifo size allocation doesn't exceed available RAM size. */ + if (dwc->last_fifo_depth >= ram1_depth) { + dev_err(dwc->dev, "Fifosize(%d) > RAM size(%d) %s depth:%d\n", + dwc->last_fifo_depth, ram1_depth, + dep->endpoint.name, fifo_size); + if (DWC3_IP_IS(DWC3)) + fifo_size = DWC3_GTXFIFOSIZ_TXFDEP(fifo_size); + else + fifo_size = DWC31_GTXFIFOSIZ_TXFDEP(fifo_size); + + dwc->last_fifo_depth -= fifo_size; + return -ENOMEM; + } + + dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1), fifo_size); + dep->flags |= DWC3_EP_TXFIFO_RESIZED; + dwc->num_ep_resized++; + + return 0; +} + +/** + * __dwc3_gadget_ep_enable - initializes a hw endpoint * @dep: endpoint to be initialized - * @desc: USB Endpoint Descriptor + * @action: one of INIT, MODIFY or RESTORE * - * Caller should take care of locking + * Caller should take care of locking. Execute all necessary commands to + * initialize a HW endpoint so it can be used by a gadget driver. */ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) { const struct usb_endpoint_descriptor *desc = dep->endpoint.desc; - struct dwc3 *dwc = dep->dwc; + struct dwc3 *dwc = dep->dwc; - u32 reg; - int ret; - - dev_dbg(dwc->dev, "Enabling %s\n", dep->name); + u32 reg; + int ret; if (!(dep->flags & DWC3_EP_ENABLED)) { + ret = dwc3_gadget_resize_tx_fifos(dep); + if (ret) + return ret; + ret = dwc3_gadget_start_config(dep); if (ret) return ret; @@ -627,8 +858,8 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) return ret; if (!(dep->flags & DWC3_EP_ENABLED)) { - struct dwc3_trb *trb_st_hw; - struct dwc3_trb *trb_link; + struct dwc3_trb *trb_st_hw; + struct dwc3_trb *trb_link; dep->type = usb_endpoint_type(desc); dep->flags |= DWC3_EP_ENABLED; @@ -637,25 +868,22 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) reg |= DWC3_DALEPENA_EP(dep->number); dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); + dep->trb_dequeue = 0; + dep->trb_enqueue = 0; + if (usb_endpoint_xfer_control(desc)) - return 0; + goto out; /* Initialize the TRB ring */ - dep->trb_dequeue = 0; - dep->trb_enqueue = 0; - memset(dep->trb_pool, 0, + memset_io(dep->trb_pool, 0, sizeof(struct dwc3_trb) * DWC3_TRB_NUM); /* Link TRB. The HWO bit is never reset */ trb_st_hw = &dep->trb_pool[0]; trb_link = &dep->trb_pool[DWC3_TRB_NUM - 1]; - memset(trb_link, 0, sizeof(*trb_link)); - - trb_link->bpl = lower_32_bits(dwc3_trb_dma_offset(dep, - trb_st_hw)); - trb_link->bph = upper_32_bits(dwc3_trb_dma_offset(dep, - trb_st_hw)); + trb_link->bpl = lower_32_bits(dwc3_trb_dma_offset(dep, trb_st_hw)); + trb_link->bph = upper_32_bits(dwc3_trb_dma_offset(dep, trb_st_hw)); trb_link->ctrl |= DWC3_TRBCTL_LINK_TRB; trb_link->ctrl |= DWC3_TRB_CTRL_HWO; } @@ -664,10 +892,10 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) * Issue StartTransfer here with no-op TRB so we can always rely on No * Response Update Transfer command. */ - if ((usb_endpoint_xfer_bulk(desc) && !dep->stream_capable) || + if (usb_endpoint_xfer_bulk(desc) || usb_endpoint_xfer_int(desc)) { struct dwc3_gadget_ep_cmd_params params; - struct dwc3_trb *trb; + struct dwc3_trb *trb; dma_addr_t trb_dma; u32 cmd; @@ -683,47 +911,88 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); if (ret < 0) return ret; + + if (dep->stream_capable) { + /* + * For streams, at start, there maybe a race where the + * host primes the endpoint before the function driver + * queues a request to initiate a stream. In that case, + * the controller will not see the prime to generate the + * ERDY and start stream. To workaround this, issue a + * no-op TRB as normal, but end it immediately. As a + * result, when the function driver queues the request, + * the next START_TRANSFER command will cause the + * controller to generate an ERDY to initiate the + * stream. + */ + dwc3_stop_active_transfer(dep, true, true); + + /* + * All stream eps will reinitiate stream on NoStream + * rejection until we can determine that the host can + * prime after the first transfer. + * + * However, if the controller is capable of + * TXF_FLUSH_BYPASS, then IN direction endpoints will + * automatically restart the stream without the driver + * initiation. + */ + if (!dep->direction || + !(dwc->hwparams.hwparams9 & + DWC3_GHWPARAMS9_DEV_TXF_FLUSH_BYPASS)) + dep->flags |= DWC3_EP_FORCE_RESTART_STREAM; + } } +out: return 0; } -static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, - bool interrupt); -static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep) +void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep, int status) { - struct dwc3_request *req; + struct dwc3_request *req; dwc3_stop_active_transfer(dep, true, false); + /* If endxfer is delayed, avoid unmapping requests */ + if (dep->flags & DWC3_EP_DELAY_STOP) + return; + /* - giveback all requests to gadget driver */ while (!list_empty(&dep->started_list)) { req = next_request(&dep->started_list); - dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + dwc3_gadget_giveback(dep, req, status); } while (!list_empty(&dep->pending_list)) { req = next_request(&dep->pending_list); - dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + dwc3_gadget_giveback(dep, req, status); + } + + while (!list_empty(&dep->cancelled_list)) { + req = next_request(&dep->cancelled_list); + + dwc3_gadget_giveback(dep, req, status); } } /** - * __dwc3_gadget_ep_disable - Disables a HW endpoint + * __dwc3_gadget_ep_disable - disables a hw endpoint * @dep: the endpoint to disable * - * This function also removes requests which are currently processed ny the - * hardware and those which are not yet scheduled. + * This function undoes what __dwc3_gadget_ep_enable did and also removes + * requests which are currently being processed by the hardware and those which + * are not yet scheduled. + * * Caller should take care of locking. */ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) { - struct dwc3 *dwc = dep->dwc; - u32 reg; - - dwc3_remove_requests(dwc, dep); + struct dwc3 *dwc = dep->dwc; + u32 reg; + u32 mask; /* make sure HW endpoint isn't stalled */ if (dep->flags & DWC3_EP_STALL) @@ -733,9 +1002,19 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) reg &= ~DWC3_DALEPENA_EP(dep->number); dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); + dwc3_remove_requests(dwc, dep, -ESHUTDOWN); + dep->stream_capable = false; dep->type = 0; - dep->flags = 0; + mask = DWC3_EP_TXFIFO_RESIZED; + /* + * dwc3_remove_requests() can exit early if DWC3 EP delayed stop is + * set. Do not clear DEP flags, so that the end transfer command will + * be reattempted during the next SETUP stage. + */ + if (dep->flags & DWC3_EP_DELAY_STOP) + mask |= (DWC3_EP_DELAY_STOP | DWC3_EP_TRANSFER_STARTED); + dep->flags &= mask; /* Clear out the ep descriptors for non-ep0 */ if (dep->number > 1) { @@ -749,7 +1028,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) /* -------------------------------------------------------------------------- */ static int dwc3_gadget_ep0_enable(struct usb_ep *ep, - const struct usb_endpoint_descriptor *desc) + const struct usb_endpoint_descriptor *desc) { return -EINVAL; } @@ -762,12 +1041,12 @@ static int dwc3_gadget_ep0_disable(struct usb_ep *ep) /* -------------------------------------------------------------------------- */ static int dwc3_gadget_ep_enable(struct usb_ep *ep, - const struct usb_endpoint_descriptor *desc) + const struct usb_endpoint_descriptor *desc) { - struct dwc3_ep *dep; - struct dwc3 *dwc; - unsigned long flags; - int ret; + struct dwc3_ep *dep; + struct dwc3 *dwc; + unsigned long flags; + int ret; if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) { pr_debug("dwc3: invalid parameters\n"); @@ -782,11 +1061,10 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep, dep = to_dwc3_ep(ep); dwc = dep->dwc; - if (dep->flags & DWC3_EP_ENABLED) { - WARN(true, "%s is already enabled\n", - dep->name); + if (dev_WARN_ONCE(dwc->dev, dep->flags & DWC3_EP_ENABLED, + "%s is already enabled\n", + dep->name)) return 0; - } spin_lock_irqsave(&dwc->lock, flags); ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT); @@ -797,10 +1075,10 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep, static int dwc3_gadget_ep_disable(struct usb_ep *ep) { - struct dwc3_ep *dep; - struct dwc3 *dwc; - unsigned long flags; - int ret; + struct dwc3_ep *dep; + struct dwc3 *dwc; + unsigned long flags; + int ret; if (!ep) { pr_debug("dwc3: invalid parameters\n"); @@ -810,11 +1088,10 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep) dep = to_dwc3_ep(ep); dwc = dep->dwc; - if (!(dep->flags & DWC3_EP_ENABLED)) { - WARN(true, "%s is already disabled\n", - dep->name); + if (dev_WARN_ONCE(dwc->dev, !(dep->flags & DWC3_EP_ENABLED), + "%s is already disabled\n", + dep->name)) return 0; - } spin_lock_irqsave(&dwc->lock, flags); ret = __dwc3_gadget_ep_disable(dep); @@ -825,10 +1102,10 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep) static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep) { - struct dwc3_request *req; - struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3_request *req; + struct dwc3_ep *dep = to_dwc3_ep(ep); - req = xzalloc(sizeof(*req)); + req = kzalloc(sizeof(*req), GFP_KERNEL); if (!req) return NULL; @@ -841,9 +1118,9 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep) } static void dwc3_gadget_ep_free_request(struct usb_ep *ep, - struct usb_request *request) + struct usb_request *request) { - struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_request *req = to_dwc3_request(request); kfree(req); } @@ -869,19 +1146,19 @@ static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index) static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) { - struct dwc3_trb *tmp; - u8 trbs_left; + u8 trbs_left; /* - * If enqueue & dequeue are equal than it is either full or empty. - * - * One way to know for sure is if the TRB right before us has HWO bit - * set or not. If it has, then we're definitely full and can't fit any - * more transfers in our ring. + * If the enqueue & dequeue are equal then the TRB ring is either full + * or empty. It's considered full when there are DWC3_TRB_NUM-1 of TRBs + * pending to be processed by the driver. */ if (dep->trb_enqueue == dep->trb_dequeue) { - tmp = dwc3_ep_prev_trb(dep, dep->trb_enqueue); - if (tmp->ctrl & DWC3_TRB_CTRL_HWO) + /* + * If there is any request remained in the started_list at + * this point, that means there is no TRB available. + */ + if (!list_empty(&dep->started_list)) return 0; return DWC3_TRB_NUM - 1; @@ -896,17 +1173,47 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) return trbs_left; } -static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, - dma_addr_t dma, unsigned length, - unsigned chain, unsigned node, - unsigned stream_id, unsigned short_not_ok, - unsigned no_interrupt) +/** + * dwc3_prepare_one_trb - setup one TRB from one request + * @dep: endpoint for which this request is prepared + * @req: dwc3_request pointer + * @trb_length: buffer size of the TRB + * @chain: should this TRB be chained to the next? + * @node: only for isochronous endpoints. First TRB needs different type. + * @use_bounce_buffer: set to use bounce buffer + * @must_interrupt: set to interrupt on TRB completion + */ +static void dwc3_prepare_one_trb(struct dwc3_ep *dep, + struct dwc3_request *req, unsigned int trb_length, + unsigned int chain, unsigned int node, bool use_bounce_buffer, + bool must_interrupt) { - struct dwc3 *dwc = dep->dwc; - struct usb_gadget *gadget = &dwc->gadget; - enum usb_device_speed speed = gadget->speed; + struct dwc3_trb *trb; + dma_addr_t dma; + unsigned int stream_id = req->request.stream_id; + unsigned int short_not_ok = req->request.short_not_ok; + unsigned int no_interrupt = req->request.no_interrupt; + unsigned int is_last = req->request.is_last; + struct dwc3 *dwc = dep->dwc; + struct usb_gadget *gadget = dwc->gadget; + enum usb_device_speed speed = gadget->speed; + + if (use_bounce_buffer) + dma = dep->dwc->bounce_addr; + else + dma = req->request.dma; + + trb = &dep->trb_pool[dep->trb_enqueue]; - trb->size = DWC3_TRB_SIZE_LENGTH(length); + if (!req->trb) { + dwc3_gadget_move_started_request(req); + req->trb = trb; + req->trb_dma = dwc3_trb_dma_offset(dep, trb); + } + + req->num_trbs++; + + trb->size = DWC3_TRB_SIZE_LENGTH(trb_length); trb->bpl = lower_32_bits(dma); trb->bph = upper_32_bits(dma); @@ -946,10 +1253,10 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, unsigned int mult = 2; unsigned int maxp = usb_endpoint_maxp(ep->desc); - if (length <= (2 * maxp)) + if (req->request.length <= (2 * maxp)) mult--; - if (length <= maxp) + if (req->request.length <= maxp) mult--; trb->size |= DWC3_TRB_SIZE_PCM1(mult); @@ -958,8 +1265,8 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS; } - /* always enable Interrupt on Missed ISOC */ - trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI; + if (!no_interrupt && !chain) + trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI; break; case USB_ENDPOINT_XFER_BULK: @@ -972,7 +1279,7 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, * checked it already :) */ dev_warn(dwc->dev, "Unknown endpoint type %d\n", - usb_endpoint_type(dep->endpoint.desc)); + usb_endpoint_type(dep->endpoint.desc)); } /* @@ -987,149 +1294,197 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI; } - if ((!no_interrupt && !chain) || - (dwc3_calc_trbs_left(dep) == 1)) + /* All TRBs setup for MST must set CSP=1 when LST=0 */ + if (dep->stream_capable && DWC3_MST_CAPABLE(&dwc->hwparams)) + trb->ctrl |= DWC3_TRB_CTRL_CSP; + + if ((!no_interrupt && !chain) || must_interrupt) trb->ctrl |= DWC3_TRB_CTRL_IOC; if (chain) trb->ctrl |= DWC3_TRB_CTRL_CHN; + else if (dep->stream_capable && is_last && + !DWC3_MST_CAPABLE(&dwc->hwparams)) + trb->ctrl |= DWC3_TRB_CTRL_LST; if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable) trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(stream_id); + /* + * As per data book 4.2.3.2TRB Control Bit Rules section + * + * The controller autonomously checks the HWO field of a TRB to determine if the + * entire TRB is valid. Therefore, software must ensure that the rest of the TRB + * is valid before setting the HWO field to '1'. In most systems, this means that + * software must update the fourth DWORD of a TRB last. + * + * However there is a possibility of CPU re-ordering here which can cause + * controller to observe the HWO bit set prematurely. + * Add a write memory barrier to prevent CPU re-ordering. + */ + /* wmb() FIXME */ trb->ctrl |= DWC3_TRB_CTRL_HWO; dwc3_ep_inc_enq(dep); } -/** - * dwc3_prepare_one_trb - setup one TRB from one request - * @dep: endpoint for which this request is prepared - * @req: dwc3_request pointer - * @chain: should this TRB be chained to the next? - * @node: only for isochronous endpoints. First TRB needs different type. - */ -static void dwc3_prepare_one_trb(struct dwc3_ep *dep, - struct dwc3_request *req, - unsigned chain, unsigned node) +static bool dwc3_needs_extra_trb(struct dwc3_ep *dep, struct dwc3_request *req) { - struct dwc3_trb *trb; - unsigned int length; - dma_addr_t dma; - unsigned stream_id = req->request.stream_id; - unsigned short_not_ok = req->request.short_not_ok; - unsigned no_interrupt = req->request.no_interrupt; - - length = req->request.length; - dma = req->request.dma; - - trb = &dep->trb_pool[dep->trb_enqueue]; - - if (!req->trb) { - dwc3_gadget_move_started_request(req); - req->trb = trb; - req->trb_dma = dwc3_trb_dma_offset(dep, trb); - } + unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc); + unsigned int rem = req->request.length % maxp; - req->num_trbs++; + if ((req->request.length && req->request.zero && !rem && + !usb_endpoint_xfer_isoc(dep->endpoint.desc)) || + (!req->direction && rem)) + return true; - __dwc3_prepare_one_trb(dep, trb, dma, length, chain, node, - stream_id, short_not_ok, no_interrupt); + return false; } -static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, - struct dwc3_request *req) +/** + * dwc3_prepare_last_sg - prepare TRBs for the last SG entry + * @dep: The endpoint that the request belongs to + * @req: The request to prepare + * @entry_length: The last SG entry size + * @node: Indicates whether this is not the first entry (for isoc only) + * + * Return the number of TRBs prepared. + */ +static int dwc3_prepare_last_sg(struct dwc3_ep *dep, + struct dwc3_request *req, unsigned int entry_length, + unsigned int node) { - unsigned int length = req->request.length; unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc); - unsigned int rem = length % maxp; + unsigned int rem = req->request.length % maxp; + unsigned int num_trbs = 1; - if ((!length || rem) && usb_endpoint_dir_out(dep->endpoint.desc)) { - struct dwc3 *dwc = dep->dwc; - struct dwc3_trb *trb; + if (dwc3_needs_extra_trb(dep, req)) + num_trbs++; - req->needs_extra_trb = true; + if (dwc3_calc_trbs_left(dep) < num_trbs) + return 0; - /* prepare normal TRB */ - dwc3_prepare_one_trb(dep, req, true, 0); + req->needs_extra_trb = num_trbs > 1; - /* Now prepare one extra TRB to align transfer size */ - trb = &dep->trb_pool[dep->trb_enqueue]; - req->num_trbs++; - __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem, - false, 1, req->request.stream_id, - req->request.short_not_ok, - req->request.no_interrupt); - } else if (req->request.zero && req->request.length && - (IS_ALIGNED(req->request.length, maxp))) { - struct dwc3 *dwc = dep->dwc; - struct dwc3_trb *trb; + /* Prepare a normal TRB */ + if (req->direction || req->request.length) + dwc3_prepare_one_trb(dep, req, entry_length, + req->needs_extra_trb, node, false, false); - req->needs_extra_trb = true; + /* Prepare extra TRBs for ZLP and MPS OUT transfer alignment */ + if ((!req->direction && !req->request.length) || req->needs_extra_trb) + dwc3_prepare_one_trb(dep, req, + req->direction ? 0 : maxp - rem, + false, 1, true, false); - /* prepare normal TRB */ - dwc3_prepare_one_trb(dep, req, true, 0); + return num_trbs; +} - /* Now prepare one extra TRB to handle ZLP */ - trb = &dep->trb_pool[dep->trb_enqueue]; - req->num_trbs++; - __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0, - false, 1, req->request.stream_id, - req->request.short_not_ok, - req->request.no_interrupt); - } else { - dwc3_prepare_one_trb(dep, req, false, 0); - } +static int dwc3_prepare_trbs_linear(struct dwc3_ep *dep, + struct dwc3_request *req) +{ + return dwc3_prepare_last_sg(dep, req, req->request.length, 0); } /* * dwc3_prepare_trbs - setup TRBs from requests * @dep: endpoint for which requests are being prepared - * @starting: true if the endpoint is idle and no requests are queued. * * The function goes through the requests list and sets up TRBs for the * transfers. The function returns once there are no more TRBs available or * it runs out of requests. + * + * Returns the number of TRBs prepared or negative errno. */ -static void dwc3_prepare_trbs(struct dwc3_ep *dep) +static int dwc3_prepare_trbs(struct dwc3_ep *dep) { - struct dwc3_request *req, *n; - struct dwc3 *dwc = dep->dwc; - dma_addr_t dma_addr; + struct dwc3_request *req, *n; + int ret = 0; BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM); + /* + * We can get in a situation where there's a request in the started list + * but there weren't enough TRBs to fully kick it in the first time + * around, so it has been waiting for more TRBs to be freed up. + * + * In that case, we should check if we have a request with pending_sgs + * in the started list and prepare TRBs for that request first, + * otherwise we will prepare TRBs completely out of order and that will + * break things. + */ + list_for_each_entry(req, &dep->started_list, list) { + if (!dwc3_calc_trbs_left(dep)) + return ret; + + /* + * Don't prepare beyond a transfer. In DWC_usb32, its transfer + * burst capability may try to read and use TRBs beyond the + * active transfer instead of stopping. + */ + if (dep->stream_capable && req->request.is_last && + !DWC3_MST_CAPABLE(&dep->dwc->hwparams)) + return ret; + } + list_for_each_entry_safe(req, n, &dep->pending_list, list) { - dma_addr = dma_map_single(dwc->dev, req->request.buf, - req->request.length, - dep->number ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (dma_mapping_error(dwc->dev, dma_addr)) - return; + struct dwc3 *dwc = dep->dwc; - req->request.dma = dma_addr; + ret = usb_gadget_map_request_by_dev(dwc->sysdev, &req->request, + dep->direction); + if (ret) + return ret; - dwc3_prepare_one_trb_linear(dep, req); + req->sg = req->request.sg; + req->start_sg = req->sg; + req->num_queued_sgs = 0; + req->num_pending_sgs = req->request.num_mapped_sgs; - if (!dwc3_calc_trbs_left(dep)) - return; + ret = dwc3_prepare_trbs_linear(dep, req); + + if (!ret || !dwc3_calc_trbs_left(dep)) + return ret; + + /* + * Don't prepare beyond a transfer. In DWC_usb32, its transfer + * burst capability may try to read and use TRBs beyond the + * active transfer instead of stopping. + */ + if (dep->stream_capable && req->request.is_last && + !DWC3_MST_CAPABLE(&dwc->hwparams)) + return ret; } + + return ret; } +static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep); + static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep) { struct dwc3_gadget_ep_cmd_params params; - struct dwc3_request *req; - int starting; - int ret; - u32 cmd; + struct dwc3_request *req; + int starting; + int ret; + u32 cmd; - if (!dwc3_calc_trbs_left(dep)) - return 0; + /* + * Note that it's normal to have no new TRBs prepared (i.e. ret == 0). + * This happens when we need to stop and restart a transfer such as in + * the case of reinitiating a stream or retrying an isoc transfer. + */ + ret = dwc3_prepare_trbs(dep); + if (ret < 0) + return ret; starting = !(dep->flags & DWC3_EP_TRANSFER_STARTED); - dwc3_prepare_trbs(dep); + /* + * If there's no new TRB prepared and we don't need to restart a + * transfer, there's no need to update the transfer. + */ + if (!ret && !starting) + return ret; req = next_request(&dep->started_list); if (!req) { @@ -1156,29 +1511,84 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep) ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); if (ret < 0) { - /* - * FIXME we need to iterate over the list of requests - * here and stop, unmap, free and del each of the linked - * requests instead of what we do now. - */ - if (req->trb) - memset(req->trb, 0, sizeof(struct dwc3_trb)); - dwc3_gadget_del_and_unmap_request(dep, req, ret); + struct dwc3_request *tmp; + + if (ret == -EAGAIN) + return ret; + + dwc3_stop_active_transfer(dep, true, true); + + list_for_each_entry_safe(req, tmp, &dep->started_list, list) + dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_DEQUEUED); + + /* If ep isn't started, then there's no end transfer pending */ + if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING)) + dwc3_gadget_ep_cleanup_cancelled_requests(dep); + return ret; } + if (dep->stream_capable && req->request.is_last && + !DWC3_MST_CAPABLE(&dep->dwc->hwparams)) + dep->flags |= DWC3_EP_WAIT_TRANSFER_COMPLETE; + return 0; } static int __dwc3_gadget_get_frame(struct dwc3 *dwc) { - u32 reg; + u32 reg; reg = dwc3_readl(dwc->regs, DWC3_DSTS); return DWC3_DSTS_SOFFN(reg); } /** + * __dwc3_stop_active_transfer - stop the current active transfer + * @dep: isoc endpoint + * @force: set forcerm bit in the command + * @interrupt: command complete interrupt after End Transfer command + * + * When setting force, the ForceRM bit will be set. In that case + * the controller won't update the TRB progress on command + * completion. It also won't clear the HWO bit in the TRB. + * The command will also not complete immediately in that case. + */ +static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt) +{ + struct dwc3_gadget_ep_cmd_params params; + u32 cmd; + int ret; + + cmd = DWC3_DEPCMD_ENDTRANSFER; + cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0; + cmd |= interrupt ? DWC3_DEPCMD_CMDIOC : 0; + cmd |= DWC3_DEPCMD_PARAM(dep->resource_index); + memset(¶ms, 0, sizeof(params)); + ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); + /* + * If the End Transfer command was timed out while the device is + * not in SETUP phase, it's possible that an incoming Setup packet + * may prevent the command's completion. Let's retry when the + * ep0state returns to EP0_SETUP_PHASE. + */ + if (ret == -ETIMEDOUT && dep->dwc->ep0state != EP0_SETUP_PHASE) { + dep->flags |= DWC3_EP_DELAY_STOP; + return 0; + } + WARN_ON_ONCE(ret); + dep->resource_index = 0; + + if (!interrupt) + dep->flags &= ~DWC3_EP_TRANSFER_STARTED; + else if (!ret) + dep->flags |= DWC3_EP_END_TRANSFER_PENDING; + + dep->flags &= ~DWC3_EP_DELAY_STOP; + return ret; +} + +/** * dwc3_gadget_start_isoc_quirk - workaround invalid frame number * @dep: isoc endpoint * @@ -1235,7 +1645,7 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep) * Check if we can start isoc transfer on the next interval or * 4 uframes in the future with BIT[15:14] as dep->combo_num */ - test_frame_number = dep->frame_number & 0x3fff; + test_frame_number = dep->frame_number & DWC3_FRNUMBER_MASK; test_frame_number |= dep->combo_num << 14; test_frame_number += max_t(u32, 4, dep->interval); @@ -1282,7 +1692,7 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep) else if (test0 && test1) dep->combo_num = 0; - dep->frame_number &= 0x3fff; + dep->frame_number &= DWC3_FRNUMBER_MASK; dep->frame_number |= dep->combo_num << 14; dep->frame_number += max_t(u32, 4, dep->interval); @@ -1295,63 +1705,110 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep) static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep) { + const struct usb_endpoint_descriptor *desc = dep->endpoint.desc; struct dwc3 *dwc = dep->dwc; int ret; int i; - if (list_empty(&dep->pending_list)) { + if (list_empty(&dep->pending_list) && + list_empty(&dep->started_list)) { dep->flags |= DWC3_EP_PENDING_REQUEST; return -EAGAIN; } - if (!dwc->dis_start_transfer_quirk && dwc3_is_usb31(dwc) && - (dwc->revision <= DWC3_USB31_REVISION_160A || - (dwc->revision == DWC3_USB31_REVISION_170A && - dwc->version_type >= DWC31_VERSIONTYPE_EA01 && - dwc->version_type <= DWC31_VERSIONTYPE_EA06))) { - - if (dwc->gadget.speed <= USB_SPEED_HIGH && dep->direction) + if (!dwc->dis_start_transfer_quirk && + (DWC3_VER_IS_PRIOR(DWC31, 170A) || + DWC3_VER_TYPE_IS_WITHIN(DWC31, 170A, EA01, EA06))) { + if (dwc->gadget->speed <= USB_SPEED_HIGH && dep->direction) return dwc3_gadget_start_isoc_quirk(dep); } + if (desc->bInterval <= 14 && + dwc->gadget->speed >= USB_SPEED_HIGH) { + u32 frame = __dwc3_gadget_get_frame(dwc); + bool rollover = frame < + (dep->frame_number & DWC3_FRNUMBER_MASK); + + /* + * frame_number is set from XferNotReady and may be already + * out of date. DSTS only provides the lower 14 bit of the + * current frame number. So add the upper two bits of + * frame_number and handle a possible rollover. + * This will provide the correct frame_number unless more than + * rollover has happened since XferNotReady. + */ + + dep->frame_number = (dep->frame_number & ~DWC3_FRNUMBER_MASK) | + frame; + if (rollover) + dep->frame_number += BIT(14); + } + for (i = 0; i < DWC3_ISOC_MAX_RETRIES; i++) { - dep->frame_number = DWC3_ALIGN_FRAME(dep, i + 1); + int future_interval = i + 1; + + /* Give the controller at least 500us to schedule transfers */ + if (desc->bInterval < 3) + future_interval += 3 - desc->bInterval; + + dep->frame_number = DWC3_ALIGN_FRAME(dep, future_interval); ret = __dwc3_gadget_kick_transfer(dep); if (ret != -EAGAIN) break; } + /* + * After a number of unsuccessful start attempts due to bus-expiry + * status, issue END_TRANSFER command and retry on the next XferNotReady + * event. + */ + if (ret == -EAGAIN) + ret = __dwc3_stop_active_transfer(dep, false, true); + return ret; } static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) { - struct dwc3 *dwc = dep->dwc; + struct dwc3 *dwc = dep->dwc; - if (!dep->endpoint.desc) { - dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n", + if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) { + dev_dbg(dwc->dev, "%s: can't queue to disabled endpoint\n", dep->name); return -ESHUTDOWN; } - if (req->dep != dep) { - WARN(true, "request %p belongs to '%s'\n", - &req->request, req->dep->name); + if (WARN(req->dep != dep, "request %pK belongs to '%s'\n", + &req->request, req->dep->name)) return -EINVAL; - } - if (req->status < DWC3_REQUEST_STATUS_COMPLETED) { - WARN(true, "request %p already in flight\n", &req->request); + if (WARN(req->status < DWC3_REQUEST_STATUS_COMPLETED, + "%s: request %pK already in flight\n", + dep->name, &req->request)) return -EINVAL; - } - req->request.actual = 0; - req->request.status = -EINPROGRESS; + req->request.actual = 0; + req->request.status = -EINPROGRESS; list_add_tail(&req->list, &dep->pending_list); req->status = DWC3_REQUEST_STATUS_QUEUED; + if (dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE) + return 0; + + /* + * Start the transfer only after the END_TRANSFER is completed + * and endpoint STALL is cleared. + */ + if ((dep->flags & DWC3_EP_END_TRANSFER_PENDING) || + (dep->flags & DWC3_EP_WEDGE) || + (dep->flags & DWC3_EP_DELAY_STOP) || + (dep->flags & DWC3_EP_STALL)) { + dep->flags |= DWC3_EP_DELAY_START; + return 0; + } + /* * NOTICE: Isochronous endpoints should NEVER be prestarted. We must * wait for a XferNotReady event so we will know what's the current @@ -1361,28 +1818,27 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) * errors which will force us issue EndTransfer command. */ if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { - if (!(dep->flags & DWC3_EP_PENDING_REQUEST) && - !(dep->flags & DWC3_EP_TRANSFER_STARTED)) - return 0; - - if ((dep->flags & DWC3_EP_PENDING_REQUEST)) { - if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) { + if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) { + if ((dep->flags & DWC3_EP_PENDING_REQUEST)) return __dwc3_gadget_start_isoc(dep); - } + + return 0; } } - return __dwc3_gadget_kick_transfer(dep); + __dwc3_gadget_kick_transfer(dep); + + return 0; } static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request) { - struct dwc3_request *req = to_dwc3_request(request); - struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_ep *dep = to_dwc3_ep(ep); - unsigned long flags; + unsigned long flags; - int ret; + int ret; spin_lock_irqsave(&dwc->lock, flags); ret = __dwc3_gadget_ep_queue(dep, req); @@ -1391,11 +1847,14 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request) return ret; } -static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, - struct dwc3_request *req) +static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *req) { int i; + /* If req->trb is not set, then the request has not started */ + if (!req->trb) + return; + /* * If request was already started, this means we had to * stop the transfer. With that we also need to ignore @@ -1409,7 +1868,7 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, for (i = 0; i < req->num_trbs; i++) { struct dwc3_trb *trb; - trb = req->trb + i; + trb = &dep->trb_pool[dep->trb_dequeue]; trb->ctrl &= ~DWC3_TRB_CTRL_HWO; dwc3_ep_inc_deq(dep); } @@ -1419,61 +1878,87 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep) { - struct dwc3_request *req; - struct dwc3_request *tmp; + struct dwc3_request *req; + struct dwc3 *dwc = dep->dwc; - list_for_each_entry_safe(req, tmp, &dep->cancelled_list, list) { + while (!list_empty(&dep->cancelled_list)) { + req = next_request(&dep->cancelled_list); dwc3_gadget_ep_skip_trbs(dep, req); - dwc3_gadget_giveback(dep, req, -ECONNRESET); + switch (req->status) { + case DWC3_REQUEST_STATUS_DISCONNECTED: + dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + break; + case DWC3_REQUEST_STATUS_DEQUEUED: + dwc3_gadget_giveback(dep, req, -ECONNRESET); + break; + case DWC3_REQUEST_STATUS_STALLED: + dwc3_gadget_giveback(dep, req, -EPIPE); + break; + default: + dev_err(dwc->dev, "request cancelled with wrong reason:%d\n", req->status); + dwc3_gadget_giveback(dep, req, -ECONNRESET); + break; + } + /* + * The endpoint is disabled, let the dwc3_remove_requests() + * handle the cleanup. + */ + if (!dep->endpoint.desc) + break; } } static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, - struct usb_request *request) + struct usb_request *request) { - struct dwc3_request *req = to_dwc3_request(request); - struct dwc3_request *r = NULL; - struct dwc3_ep *dep = to_dwc3_ep(ep); - struct dwc3 *dwc = dep->dwc; - unsigned long flags; - int ret = 0; + struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_request *r = NULL; + + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + unsigned long flags; + int ret = 0; spin_lock_irqsave(&dwc->lock, flags); - list_for_each_entry(r, &dep->pending_list, list) { + list_for_each_entry(r, &dep->cancelled_list, list) { if (r == req) - break; + goto out; } - if (r != req) { - list_for_each_entry(r, &dep->started_list, list) { - if (r == req) - break; + list_for_each_entry(r, &dep->pending_list, list) { + if (r == req) { + dwc3_gadget_giveback(dep, req, -ECONNRESET); + goto out; } + } + + list_for_each_entry(r, &dep->started_list, list) { if (r == req) { + struct dwc3_request *t; + /* wait until it is processed */ dwc3_stop_active_transfer(dep, true, true); - if (!r->trb) - goto out0; + /* + * Remove any started request if the transfer is + * cancelled. + */ + list_for_each_entry_safe(r, t, &dep->started_list, list) + dwc3_gadget_move_cancelled_request(r, + DWC3_REQUEST_STATUS_DEQUEUED); - dwc3_gadget_move_cancelled_request(req); - if (dep->flags & DWC3_EP_TRANSFER_STARTED) - goto out0; - else - goto out1; + dep->flags &= ~DWC3_EP_WAIT_TRANSFER_COMPLETE; + + goto out; } - dev_err(dwc->dev, "request %p was not queued to %s\n", - request, ep->name); - ret = -EINVAL; - goto out0; } -out1: - /* giveback the request */ - dwc3_gadget_giveback(dep, req, -ECONNRESET); - -out0: + dev_err(dwc->dev, "request %pK was not queued to %s\n", + request, ep->name); + ret = -EINVAL; +out: spin_unlock_irqrestore(&dwc->lock, flags); return ret; @@ -1481,9 +1966,11 @@ out0: int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) { - struct dwc3_gadget_ep_cmd_params params; - struct dwc3 *dwc = dep->dwc; - int ret; + struct dwc3_gadget_ep_cmd_params params; + struct dwc3 *dwc = dep->dwc; + struct dwc3_request *req; + struct dwc3_request *tmp; + int ret; if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name); @@ -1494,8 +1981,9 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) if (value) { struct dwc3_trb *trb; - unsigned transfer_in_flight; - unsigned started; + + unsigned int transfer_in_flight; + unsigned int started; if (dep->number > 1) trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue); @@ -1511,19 +1999,53 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) } ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETSTALL, - ¶ms); + ¶ms); if (ret) dev_err(dwc->dev, "failed to set STALL on %s\n", dep->name); else dep->flags |= DWC3_EP_STALL; } else { + /* + * Don't issue CLEAR_STALL command to control endpoints. The + * controller automatically clears the STALL when it receives + * the SETUP token. + */ + if (dep->number <= 1) { + dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE); + return 0; + } + + dwc3_stop_active_transfer(dep, true, true); + + list_for_each_entry_safe(req, tmp, &dep->started_list, list) + dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_STALLED); + + if (dep->flags & DWC3_EP_END_TRANSFER_PENDING || + (dep->flags & DWC3_EP_DELAY_STOP)) { + dep->flags |= DWC3_EP_PENDING_CLEAR_STALL; + if (protocol) + dwc->clear_stall_protocol = dep->number; + + return 0; + } + + dwc3_gadget_ep_cleanup_cancelled_requests(dep); + ret = dwc3_send_clear_stall_ep_cmd(dep); - if (ret) + if (ret) { dev_err(dwc->dev, "failed to clear STALL on %s\n", dep->name); - else - dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE); + return ret; + } + + dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE); + + if ((dep->flags & DWC3_EP_DELAY_START) && + !usb_endpoint_xfer_isoc(dep->endpoint.desc)) + __dwc3_gadget_kick_transfer(dep); + + dep->flags &= ~DWC3_EP_DELAY_START; } return ret; @@ -1531,10 +2053,11 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value) { - struct dwc3_ep *dep = to_dwc3_ep(ep); - unsigned long flags; + struct dwc3_ep *dep = to_dwc3_ep(ep); - int ret; + unsigned long flags; + + int ret; spin_lock_irqsave(&dwc->lock, flags); ret = __dwc3_gadget_ep_set_halt(dep, value, false); @@ -1545,9 +2068,9 @@ static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value) static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep) { - struct dwc3_ep *dep = to_dwc3_ep(ep); - unsigned long flags; - int ret; + struct dwc3_ep *dep = to_dwc3_ep(ep); + unsigned long flags; + int ret; spin_lock_irqsave(&dwc->lock, flags); dep->flags |= DWC3_EP_WEDGE; @@ -1564,13 +2087,13 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep) /* -------------------------------------------------------------------------- */ static struct usb_endpoint_descriptor dwc3_gadget_ep0_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, }; static const struct usb_ep_ops dwc3_gadget_ep0_ops = { - .enable = dwc3_gadget_ep0_enable, + .enable = dwc3_gadget_ep0_enable, .disable = dwc3_gadget_ep0_disable, .alloc_request = dwc3_gadget_ep_alloc_request, .free_request = dwc3_gadget_ep_free_request, @@ -1581,7 +2104,7 @@ static const struct usb_ep_ops dwc3_gadget_ep0_ops = { }; static const struct usb_ep_ops dwc3_gadget_ep_ops = { - .enable = dwc3_gadget_ep_enable, + .enable = dwc3_gadget_ep_enable, .disable = dwc3_gadget_ep_disable, .alloc_request = dwc3_gadget_ep_alloc_request, .free_request = dwc3_gadget_ep_free_request, @@ -1595,20 +2118,19 @@ static const struct usb_ep_ops dwc3_gadget_ep_ops = { static int dwc3_gadget_get_frame(struct usb_gadget *g) { - struct dwc3 *dwc = gadget_to_dwc(g); + struct dwc3 *dwc = gadget_to_dwc(g); return __dwc3_gadget_get_frame(dwc); } static int __dwc3_gadget_wakeup(struct dwc3 *dwc) { - int retries; + int retries; - int ret; - u32 reg; + int ret; + u32 reg; - u8 link_state; - u8 speed; + u8 link_state; /* * According to the Databook Remote wakeup request should @@ -1618,16 +2140,15 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc) */ reg = dwc3_readl(dwc->regs, DWC3_DSTS); - speed = reg & DWC3_DSTS_CONNECTSPD; - if ((speed == DWC3_DSTS_SUPERSPEED) || - (speed == DWC3_DSTS_SUPERSPEED_PLUS)) - return 0; - link_state = DWC3_DSTS_USBLNKST(reg); switch (link_state) { + case DWC3_LINK_STATE_RESET: case DWC3_LINK_STATE_RX_DET: /* in HS, means Early Suspend */ case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */ + case DWC3_LINK_STATE_U2: /* in HS, means Sleep (L1) */ + case DWC3_LINK_STATE_U1: + case DWC3_LINK_STATE_RESUME: break; default: return -EINVAL; @@ -1640,7 +2161,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc) } /* Recent versions do this automatically */ - if (dwc->revision < DWC3_REVISION_194A) { + if (DWC3_VER_IS_PRIOR(DWC3, 194A)) { /* write zeroes to Link Change Request */ reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; @@ -1668,9 +2189,9 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc) static int dwc3_gadget_wakeup(struct usb_gadget *g) { - struct dwc3 *dwc = gadget_to_dwc(g); - unsigned long flags; - int ret; + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + int ret; spin_lock_irqsave(&dwc->lock, flags); ret = __dwc3_gadget_wakeup(dwc); @@ -1680,37 +2201,145 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g) } static int dwc3_gadget_set_selfpowered(struct usb_gadget *g, - int is_selfpowered) + int is_selfpowered) { - struct dwc3 *dwc = gadget_to_dwc(g); - unsigned long flags; + unsigned long flags; spin_lock_irqsave(&dwc->lock, flags); - dwc->is_selfpowered = !!is_selfpowered; + g->is_selfpowered = !!is_selfpowered; spin_unlock_irqrestore(&dwc->lock, flags); return 0; } +static void dwc3_stop_active_transfers(struct dwc3 *dwc) +{ + u32 epnum; + + for (epnum = 2; epnum < dwc->num_eps; epnum++) { + struct dwc3_ep *dep; + + dep = dwc->eps[epnum]; + if (!dep) + continue; + + dwc3_remove_requests(dwc, dep, -ESHUTDOWN); + } +} + +static void __dwc3_gadget_set_ssp_rate(struct dwc3 *dwc) +{ + enum usb_ssp_rate ssp_rate = dwc->gadget_ssp_rate; + u32 reg; + + if (ssp_rate == USB_SSP_GEN_UNKNOWN) + ssp_rate = dwc->max_ssp_rate; + + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~DWC3_DCFG_SPEED_MASK; + reg &= ~DWC3_DCFG_NUMLANES(~0); + + if (ssp_rate == USB_SSP_GEN_1x2) + reg |= DWC3_DCFG_SUPERSPEED; + else if (dwc->max_ssp_rate != USB_SSP_GEN_1x2) + reg |= DWC3_DCFG_SUPERSPEED_PLUS; + + if (ssp_rate != USB_SSP_GEN_2x1 && + dwc->max_ssp_rate != USB_SSP_GEN_2x1) + reg |= DWC3_DCFG_NUMLANES(1); + + dwc3_writel(dwc->regs, DWC3_DCFG, reg); +} + +static void __dwc3_gadget_set_speed(struct dwc3 *dwc) +{ + enum usb_device_speed speed; + u32 reg; + + speed = dwc->gadget_max_speed; + if (speed == USB_SPEED_UNKNOWN || speed > dwc->maximum_speed) + speed = dwc->maximum_speed; + + if (speed == USB_SPEED_SUPER_PLUS && + DWC3_IP_IS(DWC32)) { + __dwc3_gadget_set_ssp_rate(dwc); + return; + } + + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~(DWC3_DCFG_SPEED_MASK); + + /* + * WORKAROUND: DWC3 revision < 2.20a have an issue + * which would cause metastability state on Run/Stop + * bit if we try to force the IP to USB2-only mode. + * + * Because of that, we cannot configure the IP to any + * speed other than the SuperSpeed + * + * Refers to: + * + * STAR#9000525659: Clock Domain Crossing on DCTL in + * USB 2.0 Mode + */ + if (DWC3_VER_IS_PRIOR(DWC3, 220A) && + !dwc->dis_metastability_quirk) { + reg |= DWC3_DCFG_SUPERSPEED; + } else { + switch (speed) { + case USB_SPEED_FULL: + reg |= DWC3_DCFG_FULLSPEED; + break; + case USB_SPEED_HIGH: + reg |= DWC3_DCFG_HIGHSPEED; + break; + case USB_SPEED_SUPER: + reg |= DWC3_DCFG_SUPERSPEED; + break; + case USB_SPEED_SUPER_PLUS: + if (DWC3_IP_IS(DWC3)) + reg |= DWC3_DCFG_SUPERSPEED; + else + reg |= DWC3_DCFG_SUPERSPEED_PLUS; + break; + default: + dev_err(dwc->dev, "invalid speed (%d)\n", speed); + + if (DWC3_IP_IS(DWC3)) + reg |= DWC3_DCFG_SUPERSPEED; + else + reg |= DWC3_DCFG_SUPERSPEED_PLUS; + } + } + + if (DWC3_IP_IS(DWC32) && + speed > USB_SPEED_UNKNOWN && + speed < USB_SPEED_SUPER_PLUS) + reg &= ~DWC3_DCFG_NUMLANES(~0); + + dwc3_writel(dwc->regs, DWC3_DCFG, reg); +} + static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) { - u32 reg; - u32 timeout = 500; + u32 reg; + u32 timeout = 2000; reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (is_on) { - if (dwc->revision <= DWC3_REVISION_187A) { + if (DWC3_VER_IS_WITHIN(DWC3, ANY, 187A)) { reg &= ~DWC3_DCTL_TRGTULST_MASK; reg |= DWC3_DCTL_TRGTULST_RX_DET; } - if (dwc->revision >= DWC3_REVISION_194A) + if (!DWC3_VER_IS_PRIOR(DWC3, 194A)) reg &= ~DWC3_DCTL_KEEP_CONNECT; reg |= DWC3_DCTL_RUN_STOP; if (dwc->has_hibernation) reg |= DWC3_DCTL_KEEP_CONNECT; + __dwc3_gadget_set_speed(dwc); dwc->pullups_connected = true; } else { reg &= ~DWC3_DCTL_RUN_STOP; @@ -1721,9 +2350,10 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) dwc->pullups_connected = false; } - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); do { + udelay(1000); reg = dwc3_readl(dwc->regs, DWC3_DSTS); reg &= DWC3_DSTS_DEVCTRLHLT; } while (--timeout && !(!is_on ^ !reg)); @@ -1731,54 +2361,104 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) if (!timeout) return -ETIMEDOUT; - dev_dbg(dwc->dev, "gadget %s data soft-%s\n", - dwc->gadget_driver - ? dwc->gadget_driver->function : "no-function", - is_on ? "connect" : "disconnect"); - return 0; } -static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) +static void dwc3_gadget_disable_irq(struct dwc3 *dwc); +static void __dwc3_gadget_stop(struct dwc3 *dwc); +static int __dwc3_gadget_start(struct dwc3 *dwc); + +static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc) { - struct dwc3 *dwc = gadget_to_dwc(g); unsigned long flags; - int ret; - is_on = !!is_on; + spin_lock_irqsave(&dwc->lock, flags); + dwc->connected = false; /* * Per databook, when we want to stop the gadget, if a control transfer * is still in process, complete it and get the core into setup phase. */ - if (!is_on && dwc->ep0state != EP0_SETUP_PHASE) - dev_warn(dwc->dev, "not in SETUP phase\n"); + if (dwc->ep0state != EP0_SETUP_PHASE) { + /* + * Original Linux code waits for ep0state bein in setup + * phase here using completions. Completions are not properly + * implemented in barebox, hence the code is skipped here. I wasn't + * able to trigger this code in barebox, but if you hit this compare + * it with Linux code and implement it here. + */ + dev_warn(dwc->dev, "%s: unexpected state\n", __func__); + } - spin_lock_irqsave(&dwc->lock, flags); - ret = dwc3_gadget_run_stop(dwc, is_on, false); + /* + * In the Synopsys DesignWare Cores USB3 Databook Rev. 3.30a + * Section 4.1.8 Table 4-7, it states that for a device-initiated + * disconnect, the SW needs to ensure that it sends "a DEPENDXFER + * command for any active transfers" before clearing the RunStop + * bit. + */ + dwc3_stop_active_transfers(dwc); + __dwc3_gadget_stop(dwc); spin_unlock_irqrestore(&dwc->lock, flags); + /* + * Note: if the GEVNTCOUNT indicates events in the event buffer, the + * driver needs to acknowledge them before the controller can halt. + * Simply let the interrupt handler acknowledges and handle the + * remaining event generated by the controller while polling for + * DSTS.DEVCTLHLT. + */ + return dwc3_gadget_run_stop(dwc, false, false); +} + +static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + int ret; + + is_on = !!is_on; + + dwc->softconnect = is_on; + + if (!is_on) { + ret = dwc3_gadget_soft_disconnect(dwc); + } else { + /* + * In the Synopsys DWC_usb31 1.90a programming guide section + * 4.1.9, it specifies that for a reconnect after a + * device-initiated disconnect requires a core soft reset + * (DCTL.CSftRst) before enabling the run/stop bit. + */ + dwc3_core_soft_reset(dwc); + + dwc3_event_buffers_setup(dwc); + __dwc3_gadget_start(dwc); + ret = dwc3_gadget_run_stop(dwc, true, false); + } + return ret; } static void dwc3_gadget_enable_irq(struct dwc3 *dwc) { - u32 reg; + u32 reg; /* Enable all but Start and End of Frame IRQs */ - reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN | - DWC3_DEVTEN_EVNTOVERFLOWEN | - DWC3_DEVTEN_CMDCMPLTEN | - DWC3_DEVTEN_ERRTICERREN | - DWC3_DEVTEN_WKUPEVTEN | - DWC3_DEVTEN_ULSTCNGEN | - DWC3_DEVTEN_CONNECTDONEEN | - DWC3_DEVTEN_USBRSTEN | - DWC3_DEVTEN_DISCONNEVTEN); - - if (dwc->revision < DWC3_REVISION_250A) + reg = (DWC3_DEVTEN_EVNTOVERFLOWEN | + DWC3_DEVTEN_CMDCMPLTEN | + DWC3_DEVTEN_ERRTICERREN | + DWC3_DEVTEN_WKUPEVTEN | + DWC3_DEVTEN_CONNECTDONEEN | + DWC3_DEVTEN_USBRSTEN | + DWC3_DEVTEN_DISCONNEVTEN); + + if (DWC3_VER_IS_PRIOR(DWC3, 250A)) reg |= DWC3_DEVTEN_ULSTCNGEN; + /* On 2.30a and above this bit enables U3/L2-L1 Suspend Events */ + if (!DWC3_VER_IS_PRIOR(DWC3, 230A)) + reg |= DWC3_DEVTEN_U3L2L1SUSPEN; + dwc3_writel(dwc->regs, DWC3_DEVTEN, reg); } @@ -1817,7 +2497,7 @@ static void dwc3_gadget_setup_nump(struct dwc3 *dwc) u32 reg; ram2_depth = DWC3_GHWPARAMS7_RAM2_DEPTH(dwc->hwparams.hwparams7); - mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0); + mdwidth = dwc3_mdwidth(dwc); nump = ((ram2_depth * mdwidth / 8) - 24 - 16) / 1024; nump = min_t(u32, nump, 16); @@ -1831,9 +2511,20 @@ static void dwc3_gadget_setup_nump(struct dwc3 *dwc) static int __dwc3_gadget_start(struct dwc3 *dwc) { - struct dwc3_ep *dep; - int ret = 0; - u32 reg; + struct dwc3_ep *dep; + int ret = 0; + u32 reg; + + /* + * Use IMOD if enabled via dwc->imod_interval. Otherwise, if + * the core supports IMOD, disable it. + */ + if (dwc->imod_interval) { + dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval); + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB); + } else if (dwc3_has_imod(dwc)) { + dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), 0); + } /* * We are telling dwc3 that we want to use DCFG.NUMP as ACK TP's NUMP @@ -1843,19 +2534,38 @@ static int __dwc3_gadget_start(struct dwc3 *dwc) * bursts of data without going through any sort of endpoint throttling. */ reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG); - if (dwc3_is_usb31(dwc)) - reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL; - else + if (DWC3_IP_IS(DWC3)) reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL; + else + reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL; dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg); dwc3_gadget_setup_nump(dwc); + /* + * Currently the controller handles single stream only. So, Ignore + * Packet Pending bit for stream selection and don't search for another + * stream if the host sends Data Packet with PP=0 (for OUT direction) or + * ACK with NumP=0 and PP=0 (for IN direction). This slightly improves + * the stream performance. + */ + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg |= DWC3_DCFG_IGNSTRMPP; + dwc3_writel(dwc->regs, DWC3_DCFG, reg); + + /* Enable MST by default if the device is capable of MST */ + if (DWC3_MST_CAPABLE(&dwc->hwparams)) { + reg = dwc3_readl(dwc->regs, DWC3_DCFG1); + reg &= ~DWC3_DCFG1_DIS_MST_ENH; + dwc3_writel(dwc->regs, DWC3_DCFG1, reg); + } + /* Start with SuperSpeed Default */ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); dep = dwc->eps[0]; + dep->flags = 0; ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); @@ -1863,6 +2573,7 @@ static int __dwc3_gadget_start(struct dwc3 *dwc) } dep = dwc->eps[1]; + dep->flags = 0; ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); @@ -1871,7 +2582,9 @@ static int __dwc3_gadget_start(struct dwc3 *dwc) /* begin to receive SETUP packets */ dwc->ep0state = EP0_SETUP_PHASE; + dwc->ep0_bounced = false; dwc->link_state = DWC3_LINK_STATE_SS_DIS; + dwc->delayed_status = false; dwc3_ep0_out_start(dwc); dwc3_gadget_enable_irq(dwc); @@ -1888,109 +2601,163 @@ err0: static int dwc3_gadget_start(struct usb_gadget *g, struct usb_gadget_driver *driver) { - struct dwc3 *dwc = gadget_to_dwc(g); - unsigned long flags; - int ret = 0; + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; - //dwc3_gadget_wakeup(g); spin_lock_irqsave(&dwc->lock, flags); - if (dwc->gadget_driver) { - dev_err(dwc->dev, "%s is already bound to %s\n", - dwc->gadget.name, - dwc->gadget_driver->function); - ret = -EBUSY; - goto err1; - } + dwc->gadget_driver = driver; + spin_unlock_irqrestore(&dwc->lock, flags); - dwc->gadget_driver = driver; + return 0; +} - __dwc3_gadget_start(dwc); +static void __dwc3_gadget_stop(struct dwc3 *dwc) +{ + dwc3_gadget_disable_irq(dwc); + __dwc3_gadget_ep_disable(dwc->eps[0]); + __dwc3_gadget_ep_disable(dwc->eps[1]); +} +static int dwc3_gadget_stop(struct usb_gadget *g) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + dwc->gadget_driver = NULL; + dwc->max_cfg_eps = 0; spin_unlock_irqrestore(&dwc->lock, flags); return 0; +} -err1: - spin_unlock_irqrestore(&dwc->lock, flags); +static void dwc3_gadget_config_params(struct usb_gadget *g, + struct usb_dcd_config_params *params) +{ + struct dwc3 *dwc = gadget_to_dwc(g); - return ret; + 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; + else + params->bU1devExitLat = DWC3_DEFAULT_U1_DEV_EXIT_LAT; + + /* U2 Device exit Latency */ + if (dwc->dis_u2_entry_quirk) + params->bU2DevExitLat = 0; + else + params->bU2DevExitLat = + cpu_to_le16(DWC3_DEFAULT_U2_DEV_EXIT_LAT); } -static int dwc3_gadget_stop(struct usb_gadget *g, - struct usb_gadget_driver *driver) +static void dwc3_gadget_set_speed(struct usb_gadget *g, + enum usb_device_speed speed) { - struct dwc3 *dwc = gadget_to_dwc(g); - unsigned long flags; + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; spin_lock_irqsave(&dwc->lock, flags); + dwc->gadget_max_speed = speed; + spin_unlock_irqrestore(&dwc->lock, flags); +} - dwc3_gadget_disable_irq(dwc); - __dwc3_gadget_ep_disable(dwc->eps[0]); - __dwc3_gadget_ep_disable(dwc->eps[1]); +static void dwc3_gadget_set_ssp_rate(struct usb_gadget *g, + enum usb_ssp_rate rate) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; - dwc->gadget_driver = NULL; + spin_lock_irqsave(&dwc->lock, flags); + dwc->gadget_max_speed = USB_SPEED_SUPER_PLUS; + dwc->gadget_ssp_rate = rate; spin_unlock_irqrestore(&dwc->lock, flags); +} + +static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + + if (dwc->usb2_phy) + return usb_phy_set_power(dwc->usb2_phy, mA); + + if (!dwc->usb_psy) + return -EOPNOTSUPP; return 0; } -static void dwc3_gadget_set_speed(struct dwc3 *dwc, - enum usb_device_speed speed) +/** + * dwc3_gadget_check_config - ensure dwc3 can support the USB configuration + * @g: pointer to the USB gadget + * + * Used to record the maximum number of endpoints being used in a USB composite + * device. (across all configurations) This is to be used in the calculation + * of the TXFIFO sizes when resizing internal memory for individual endpoints. + * It will help ensured that the resizing logic reserves enough space for at + * least one max packet. + */ +static int dwc3_gadget_check_config(struct usb_gadget *g) { - unsigned long flags; - u32 reg; - - spin_lock_irqsave(&dwc->lock, flags); - reg = dwc3_readl(dwc->regs, DWC3_DCFG); - reg &= ~(DWC3_DCFG_SPEED_MASK); + struct dwc3 *dwc = gadget_to_dwc(g); + struct usb_ep *ep; + int fifo_size = 0; + int ram1_depth; + int ep_num = 0; - /* - * WORKAROUND: DWC3 revision < 2.20a have an issue - * which would cause metastability state on Run/Stop - * bit if we try to force the IP to USB2-only mode. - * - * Because of that, we cannot configure the IP to any - * speed other than the SuperSpeed - * - * Refers to: - * - * STAR#9000525659: Clock Domain Crossing on DCTL in - * USB 2.0 Mode - */ - if (dwc->revision < DWC3_REVISION_220A && - !dwc->dis_metastability_quirk) { - reg |= DWC3_DCFG_SUPERSPEED; - } else { - switch (speed) { - case USB_SPEED_LOW: - reg |= DWC3_DCFG_LOWSPEED; - break; - case USB_SPEED_FULL: - reg |= DWC3_DCFG_FULLSPEED; - break; - case USB_SPEED_HIGH: - reg |= DWC3_DCFG_HIGHSPEED; - break; - case USB_SPEED_SUPER: - reg |= DWC3_DCFG_SUPERSPEED; - break; - case USB_SPEED_SUPER_PLUS: - if (dwc3_is_usb31(dwc)) - reg |= DWC3_DCFG_SUPERSPEED_PLUS; - else - reg |= DWC3_DCFG_SUPERSPEED; - break; - default: - dev_err(dwc->dev, "invalid speed (%d)\n", speed); + if (!dwc->do_fifo_resize) + return 0; - if (dwc->revision & DWC3_REVISION_IS_DWC31) - reg |= DWC3_DCFG_SUPERSPEED_PLUS; - else - reg |= DWC3_DCFG_SUPERSPEED; - } + list_for_each_entry(ep, &g->ep_list, ep_list) { + /* Only interested in the IN endpoints */ + if (ep->claimed && (ep->address & USB_DIR_IN)) + ep_num++; } - dwc3_writel(dwc->regs, DWC3_DCFG, reg); + if (ep_num <= dwc->max_cfg_eps) + return 0; + + /* Update the max number of eps in the composition */ + dwc->max_cfg_eps = ep_num; + + fifo_size = dwc3_gadget_calc_tx_fifo_size(dwc, dwc->max_cfg_eps); + /* Based on the equation, increment by one for every ep */ + fifo_size += dwc->max_cfg_eps; + + /* Check if we can fit a single fifo per endpoint */ + ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7); + if (fifo_size > ram1_depth) + return -ENOMEM; + + return 0; +} + +static void dwc3_gadget_async_callbacks(struct usb_gadget *g, bool enable) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + dwc->async_callbacks = enable; spin_unlock_irqrestore(&dwc->lock, flags); } @@ -1998,12 +2765,18 @@ static void dwc3_gadget_poll(struct usb_gadget *g); static const struct usb_gadget_ops dwc3_gadget_ops = { .get_frame = dwc3_gadget_get_frame, - .wakeup = dwc3_gadget_wakeup, + .wakeup = dwc3_gadget_wakeup, .set_selfpowered = dwc3_gadget_set_selfpowered, - .pullup = dwc3_gadget_pullup, + .pullup = dwc3_gadget_pullup, .udc_start = dwc3_gadget_start, .udc_stop = dwc3_gadget_stop, .udc_poll = dwc3_gadget_poll, + .udc_set_speed = dwc3_gadget_set_speed, + .udc_set_ssp_rate = dwc3_gadget_set_ssp_rate, + .get_config_params = dwc3_gadget_config_params, + .vbus_draw = dwc3_gadget_vbus_draw, + .check_config = dwc3_gadget_check_config, + .udc_async_callbacks = dwc3_gadget_async_callbacks, }; /* -------------------------------------------------------------------------- */ @@ -2016,7 +2789,9 @@ static int dwc3_gadget_init_control_endpoint(struct dwc3_ep *dep) dep->endpoint.maxburst = 1; dep->endpoint.ops = &dwc3_gadget_ep0_ops; if (!dep->direction) - dwc->gadget.ep0 = &dep->endpoint; + dwc->gadget->ep0 = &dep->endpoint; + + dep->endpoint.caps.type_control = true; return 0; } @@ -2024,41 +2799,48 @@ static int dwc3_gadget_init_control_endpoint(struct dwc3_ep *dep) static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep) { struct dwc3 *dwc = dep->dwc; - int mdwidth; - int kbytes; + u32 mdwidth; int size; + int maxpacket; + + mdwidth = dwc3_mdwidth(dwc); - mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0); /* MDWIDTH is represented in bits, we need it in bytes */ mdwidth /= 8; size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1)); - if (dwc3_is_usb31(dwc)) - size = DWC31_GTXFIFOSIZ_TXFDEF(size); + if (DWC3_IP_IS(DWC3)) + size = DWC3_GTXFIFOSIZ_TXFDEP(size); else - size = DWC3_GTXFIFOSIZ_TXFDEF(size); - - /* FIFO Depth is in MDWDITH bytes. Multiply */ - size *= mdwidth; - - kbytes = size / 1024; - if (kbytes == 0) - kbytes = 1; + size = DWC31_GTXFIFOSIZ_TXFDEP(size); /* - * FIFO sizes account an extra MDWIDTH * (kbytes + 1) bytes for - * internal overhead. We don't really know how these are used, - * but documentation say it exists. + * maxpacket size is determined as part of the following, after assuming + * a mult value of one maxpacket: + * DWC3 revision 280A and prior: + * fifo_size = mult * (max_packet / mdwidth) + 1; + * maxpacket = mdwidth * (fifo_size - 1); + * + * DWC3 revision 290A and onwards: + * fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1 + * maxpacket = mdwidth * ((fifo_size - 1) - 1) - mdwidth; */ - size -= mdwidth * (kbytes + 1); - size /= kbytes; + if (DWC3_VER_IS_PRIOR(DWC3, 290A)) + maxpacket = mdwidth * (size - 1); + else + maxpacket = mdwidth * ((size - 1) - 1) - mdwidth; + /* Functionally, space for one max packet is sufficient */ + size = min_t(int, maxpacket, 1024); usb_ep_set_maxpacket_limit(&dep->endpoint, size); - dep->endpoint.max_streams = 15; + dep->endpoint.max_streams = 16; dep->endpoint.ops = &dwc3_gadget_ep_ops; list_add_tail(&dep->endpoint.ep_list, - &dwc->gadget.ep_list); + &dwc->gadget->ep_list); + dep->endpoint.caps.type_iso = true; + dep->endpoint.caps.type_bulk = true; + dep->endpoint.caps.type_int = true; return dwc3_alloc_trb_pool(dep); } @@ -2066,22 +2848,56 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep) static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep) { struct dwc3 *dwc = dep->dwc; + u32 mdwidth; + int size; + + mdwidth = dwc3_mdwidth(dwc); + + /* MDWIDTH is represented in bits, convert to bytes */ + mdwidth /= 8; + + /* All OUT endpoints share a single RxFIFO space */ + size = dwc3_readl(dwc->regs, DWC3_GRXFIFOSIZ(0)); + if (DWC3_IP_IS(DWC3)) + size = DWC3_GRXFIFOSIZ_RXFDEP(size); + else + size = DWC31_GRXFIFOSIZ_RXFDEP(size); + + /* FIFO depth is in MDWDITH bytes */ + size *= mdwidth; + + /* + * To meet performance requirement, a minimum recommended RxFIFO size + * is defined as follow: + * RxFIFO size >= (3 x MaxPacketSize) + + * (3 x 8 bytes setup packets size) + (16 bytes clock crossing margin) + * + * Then calculate the max packet limit as below. + */ + size -= (3 * 8) + 16; + if (size < 0) + size = 0; + else + size /= 3; - usb_ep_set_maxpacket_limit(&dep->endpoint, 1024); - dep->endpoint.max_streams = 15; + usb_ep_set_maxpacket_limit(&dep->endpoint, size); + dep->endpoint.max_streams = 16; dep->endpoint.ops = &dwc3_gadget_ep_ops; list_add_tail(&dep->endpoint.ep_list, - &dwc->gadget.ep_list); + &dwc->gadget->ep_list); + dep->endpoint.caps.type_iso = true; + dep->endpoint.caps.type_bulk = true; + dep->endpoint.caps.type_int = true; return dwc3_alloc_trb_pool(dep); } static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum) { - struct dwc3_ep *dep; - bool direction = epnum & 1; - int ret; - u8 num = epnum >> 1; + struct dwc3_ep *dep; + bool direction = epnum & 1; + int ret; + u8 num = epnum >> 1; dep = kzalloc(sizeof(*dep), GFP_KERNEL); if (!dep) @@ -2096,7 +2912,7 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum) dep->start_cmd_status = 0; snprintf(dep->name, sizeof(dep->name), "ep%u%s", num, - direction ? "in" : "out"); + direction ? "in" : "out"); dep->endpoint.name = dep->name; @@ -2105,8 +2921,6 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum) dep->endpoint.comp_desc = NULL; } - spin_lock_init(&dep->lock); - if (num == 0) ret = dwc3_gadget_init_control_endpoint(dep); else if (direction) @@ -2117,21 +2931,26 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum) if (ret) return ret; + dep->endpoint.caps.dir_in = direction; + dep->endpoint.caps.dir_out = !direction; + INIT_LIST_HEAD(&dep->pending_list); INIT_LIST_HEAD(&dep->started_list); INIT_LIST_HEAD(&dep->cancelled_list); + dwc3_debugfs_create_endpoint_dir(dep); + return 0; } static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total) { - u8 epnum; + u8 epnum; - INIT_LIST_HEAD(&dwc->gadget.ep_list); + INIT_LIST_HEAD(&dwc->gadget->ep_list); for (epnum = 0; epnum < total; epnum++) { - int ret; + int ret; ret = dwc3_gadget_init_endpoint(dwc, epnum); if (ret) @@ -2143,8 +2962,8 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total) static void dwc3_gadget_free_endpoints(struct dwc3 *dwc) { - struct dwc3_ep *dep; - u8 epnum; + struct dwc3_ep *dep; + u8 epnum; for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) { dep = dwc->eps[epnum]; @@ -2164,6 +2983,7 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc) list_del(&dep->endpoint.ep_list); } + dwc3_debugfs_remove_endpoint_dir(dep); kfree(dep); } } @@ -2171,12 +2991,10 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc) /* -------------------------------------------------------------------------- */ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep, - struct dwc3_request *req, - struct dwc3_trb *trb, - const struct dwc3_event_depevt *event, - int status, int chain) + struct dwc3_request *req, struct dwc3_trb *trb, + const struct dwc3_event_depevt *event, int status, int chain) { - unsigned int count; + unsigned int count; dwc3_ep_inc_deq(dep); @@ -2205,15 +3023,16 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep, frame_number = DWC3_TRB_CTRL_GET_SID_SOFN(trb->ctrl); frame_number &= ~(dep->interval - 1); + req->request.frame_number = frame_number; } /* - * If we're dealing with unaligned size OUT transfer, we will be left - * with one TRB pending in the ring. We need to manually clear HWO bit - * from that TRB. + * We use bounce buffer for requests that needs extra TRB or OUT ZLP. If + * this TRB points to the bounce buffer address, it's a MPS alignment + * TRB. Don't add it to req->remaining calculation. */ - - if (req->needs_extra_trb && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) { + if (trb->bpl == lower_32_bits(dep->dwc->bounce_addr) && + trb->bph == upper_32_bits(dep->dwc->bounce_addr)) { trb->ctrl &= ~DWC3_TRB_CTRL_HWO; return 1; } @@ -2227,16 +3046,20 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep, if (event->status & DEPEVT_STATUS_SHORT && !chain) return 1; - if (event->status & DEPEVT_STATUS_IOC) + if ((trb->ctrl & DWC3_TRB_CTRL_ISP_IMI) && + DWC3_TRB_SIZE_TRBSTS(trb->size) == DWC3_TRBSTS_MISSED_ISOC) + return 1; + + if ((trb->ctrl & DWC3_TRB_CTRL_IOC) || + (trb->ctrl & DWC3_TRB_CTRL_LST)) return 1; return 0; } static int dwc3_gadget_ep_reclaim_trb_linear(struct dwc3_ep *dep, - struct dwc3_request *req, - const struct dwc3_event_depevt *event, - int status) + struct dwc3_request *req, const struct dwc3_event_depevt *event, + int status) { struct dwc3_trb *trb = &dep->trb_pool[dep->trb_dequeue]; @@ -2244,15 +3067,24 @@ static int dwc3_gadget_ep_reclaim_trb_linear(struct dwc3_ep *dep, event, status, false); } +static bool dwc3_gadget_ep_request_completed(struct dwc3_request *req) +{ + return req->num_pending_sgs == 0 && req->num_queued_sgs == 0; +} + static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep, - const struct dwc3_event_depevt *event, - struct dwc3_request *req, - int status) + const struct dwc3_event_depevt *event, + struct dwc3_request *req, int status) { + int request_status; int ret; - ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, - status); + ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, status); + + req->request.actual = req->request.length - req->remaining; + + if (!dwc3_gadget_ep_request_completed(req)) + goto out; if (req->needs_extra_trb) { ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, @@ -2260,69 +3092,121 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep, req->needs_extra_trb = false; } - req->request.actual = req->request.length - req->remaining; + /* + * The event status only reflects the status of the TRB with IOC set. + * For the requests that don't set interrupt on completion, the driver + * needs to check and return the status of the completed TRBs associated + * with the request. Use the status of the last TRB of the request. + */ + if (req->request.no_interrupt) { + struct dwc3_trb *trb; - dwc3_gadget_giveback(dep, req, status); + trb = dwc3_ep_prev_trb(dep, dep->trb_dequeue); + switch (DWC3_TRB_SIZE_TRBSTS(trb->size)) { + case DWC3_TRBSTS_MISSED_ISOC: + /* Isoc endpoint only */ + request_status = -EXDEV; + break; + case DWC3_TRB_STS_XFER_IN_PROG: + /* Applicable when End Transfer with ForceRM=0 */ + case DWC3_TRBSTS_SETUP_PENDING: + /* Control endpoint only */ + case DWC3_TRBSTS_OK: + default: + request_status = 0; + break; + } + } else { + request_status = status; + } + + dwc3_gadget_giveback(dep, req, request_status); +out: return ret; } static void dwc3_gadget_ep_cleanup_completed_requests(struct dwc3_ep *dep, - const struct dwc3_event_depevt *event, - int status) + const struct dwc3_event_depevt *event, int status) { - struct dwc3_request *req; - struct dwc3_request *tmp; + struct dwc3_request *req; - list_for_each_entry_safe(req, tmp, &dep->started_list, list) { + while (!list_empty(&dep->started_list)) { int ret; + req = next_request(&dep->started_list); ret = dwc3_gadget_ep_cleanup_completed_request(dep, event, req, status); if (ret) break; + /* + * The endpoint is disabled, let the dwc3_remove_requests() + * handle the cleanup. + */ + if (!dep->endpoint.desc) + break; } } +static bool dwc3_gadget_ep_should_continue(struct dwc3_ep *dep) +{ + struct dwc3_request *req; + struct dwc3 *dwc = dep->dwc; + + if (!dep->endpoint.desc || !dwc->pullups_connected || + !dwc->connected) + return false; + + if (!list_empty(&dep->pending_list)) + return true; + + /* + * We only need to check the first entry of the started list. We can + * assume the completed requests are removed from the started list. + */ + req = next_request(&dep->started_list); + if (!req) + return false; + + return !dwc3_gadget_ep_request_completed(req); +} + static void dwc3_gadget_endpoint_frame_from_event(struct dwc3_ep *dep, - const struct dwc3_event_depevt *event) + const struct dwc3_event_depevt *event) { dep->frame_number = event->parameters; } -static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep, - const struct dwc3_event_depevt *event) +static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep, + const struct dwc3_event_depevt *event, int status) { - struct dwc3 *dwc = dep->dwc; - unsigned status = 0; - bool stop = false; - - dwc3_gadget_endpoint_frame_from_event(dep, event); - - if (event->status & DEPEVT_STATUS_BUSERR) - status = -ECONNRESET; + struct dwc3 *dwc = dep->dwc; + bool no_started_trb = true; - if (event->status & DEPEVT_STATUS_MISSED_ISOC) { - status = -EXDEV; + dwc3_gadget_ep_cleanup_completed_requests(dep, event, status); - if (list_empty(&dep->started_list)) - stop = true; - } + if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) + goto out; - dwc3_gadget_ep_cleanup_completed_requests(dep, event, status); + if (!dep->endpoint.desc) + return no_started_trb; - if (stop) { + if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && + list_empty(&dep->started_list) && + (list_empty(&dep->pending_list) || status == -EXDEV)) dwc3_stop_active_transfer(dep, true, true); - dep->flags = DWC3_EP_ENABLED; - } + else if (dwc3_gadget_ep_should_continue(dep)) + if (__dwc3_gadget_kick_transfer(dep) == 0) + no_started_trb = false; +out: /* * WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround. * See dwc3_gadget_linksts_change_interrupt() for 1st half. */ - if (dwc->revision < DWC3_REVISION_183A) { - u32 reg; - int i; + if (DWC3_VER_IS_PRIOR(DWC3, 183A)) { + u32 reg; + int i; for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) { dep = dwc->eps[i]; @@ -2331,7 +3215,7 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep, continue; if (!list_empty(&dep->started_list)) - return; + return no_started_trb; } reg = dwc3_readl(dwc->regs, DWC3_DCTL); @@ -2340,30 +3224,188 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep, dwc->u1u2 = 0; } + + return no_started_trb; +} + +static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep, + const struct dwc3_event_depevt *event) +{ + int status = 0; + + if (!dep->endpoint.desc) + return; + + if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) + dwc3_gadget_endpoint_frame_from_event(dep, event); + + if (event->status & DEPEVT_STATUS_BUSERR) + status = -ECONNRESET; + + if (event->status & DEPEVT_STATUS_MISSED_ISOC) + status = -EXDEV; + + dwc3_gadget_endpoint_trbs_complete(dep, event, status); +} + +static void dwc3_gadget_endpoint_transfer_complete(struct dwc3_ep *dep, + const struct dwc3_event_depevt *event) +{ + int status = 0; + + dep->flags &= ~DWC3_EP_TRANSFER_STARTED; + + if (event->status & DEPEVT_STATUS_BUSERR) + status = -ECONNRESET; + + if (dwc3_gadget_endpoint_trbs_complete(dep, event, status)) + dep->flags &= ~DWC3_EP_WAIT_TRANSFER_COMPLETE; } static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep, - const struct dwc3_event_depevt *event) + const struct dwc3_event_depevt *event) { dwc3_gadget_endpoint_frame_from_event(dep, event); + + /* + * The XferNotReady event is generated only once before the endpoint + * starts. It will be generated again when END_TRANSFER command is + * issued. For some controller versions, the XferNotReady event may be + * generated while the END_TRANSFER command is still in process. Ignore + * it and wait for the next XferNotReady event after the command is + * completed. + */ + if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) + return; + (void) __dwc3_gadget_start_isoc(dep); } +static void dwc3_gadget_endpoint_command_complete(struct dwc3_ep *dep, + const struct dwc3_event_depevt *event) +{ + u8 cmd = DEPEVT_PARAMETER_CMD(event->parameters); + + if (cmd != DWC3_DEPCMD_ENDTRANSFER) + return; + + /* + * The END_TRANSFER command will cause the controller to generate a + * NoStream Event, and it's not due to the host DP NoStream rejection. + * Ignore the next NoStream event. + */ + if (dep->stream_capable) + dep->flags |= DWC3_EP_IGNORE_NEXT_NOSTREAM; + + dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING; + dep->flags &= ~DWC3_EP_TRANSFER_STARTED; + dwc3_gadget_ep_cleanup_cancelled_requests(dep); + + if (dep->flags & DWC3_EP_PENDING_CLEAR_STALL) { + struct dwc3 *dwc = dep->dwc; + + dep->flags &= ~DWC3_EP_PENDING_CLEAR_STALL; + if (dwc3_send_clear_stall_ep_cmd(dep)) { + struct usb_ep *ep0 = &dwc->eps[0]->endpoint; + + dev_err(dwc->dev, "failed to clear STALL on %s\n", dep->name); + if (dwc->delayed_status) + __dwc3_gadget_ep0_set_halt(ep0, 1); + return; + } + + dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE); + if (dwc->clear_stall_protocol == dep->number) + dwc3_ep0_send_delayed_status(dwc); + } + + if ((dep->flags & DWC3_EP_DELAY_START) && + !usb_endpoint_xfer_isoc(dep->endpoint.desc)) + __dwc3_gadget_kick_transfer(dep); + + dep->flags &= ~DWC3_EP_DELAY_START; +} + +static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep, + const struct dwc3_event_depevt *event) +{ + struct dwc3 *dwc = dep->dwc; + + if (event->status == DEPEVT_STREAMEVT_FOUND) { + dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED; + goto out; + } + + /* Note: NoStream rejection event param value is 0 and not 0xFFFF */ + switch (event->parameters) { + case DEPEVT_STREAM_PRIME: + /* + * If the host can properly transition the endpoint state from + * idle to prime after a NoStream rejection, there's no need to + * force restarting the endpoint to reinitiate the stream. To + * simplify the check, assume the host follows the USB spec if + * it primed the endpoint more than once. + */ + if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM) { + if (dep->flags & DWC3_EP_FIRST_STREAM_PRIMED) + dep->flags &= ~DWC3_EP_FORCE_RESTART_STREAM; + else + dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED; + } + + break; + case DEPEVT_STREAM_NOSTREAM: + if ((dep->flags & DWC3_EP_IGNORE_NEXT_NOSTREAM) || + !(dep->flags & DWC3_EP_FORCE_RESTART_STREAM) || + (!DWC3_MST_CAPABLE(&dwc->hwparams) && + !(dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE))) + break; + + /* + * If the host rejects a stream due to no active stream, by the + * USB and xHCI spec, the endpoint will be put back to idle + * state. When the host is ready (buffer added/updated), it will + * prime the endpoint to inform the usb device controller. This + * triggers the device controller to issue ERDY to restart the + * stream. However, some hosts don't follow this and keep the + * endpoint in the idle state. No prime will come despite host + * streams are updated, and the device controller will not be + * triggered to generate ERDY to move the next stream data. To + * workaround this and maintain compatibility with various + * hosts, force to reinitiate the stream until the host is ready + * instead of waiting for the host to prime the endpoint. + */ + if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) { + unsigned int cmd = DWC3_DGCMD_SET_ENDPOINT_PRIME; + + dwc3_send_gadget_generic_command(dwc, cmd, dep->number); + } else { + dep->flags |= DWC3_EP_DELAY_START; + dwc3_stop_active_transfer(dep, true, true); + return; + } + break; + } + +out: + dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM; +} + static void dwc3_endpoint_interrupt(struct dwc3 *dwc, - const struct dwc3_event_depevt *event) + const struct dwc3_event_depevt *event) { - struct dwc3_ep *dep; - u8 epnum = event->endpoint_number; - u8 cmd; + struct dwc3_ep *dep; + u8 epnum = event->endpoint_number; dep = dwc->eps[epnum]; if (!(dep->flags & DWC3_EP_ENABLED)) { - if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) + if ((epnum > 1) && !(dep->flags & DWC3_EP_TRANSFER_STARTED)) return; /* Handle only EPCMDCMPLT when EP disabled */ - if (event->endpoint_event != DWC3_DEPEVT_EPCMDCMPLT) + if ((event->endpoint_event != DWC3_DEPEVT_EPCMDCMPLT) && + !(epnum <= 1 && event->endpoint_event == DWC3_DEPEVT_XFERCOMPLETE)) return; } @@ -2380,15 +3422,14 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, dwc3_gadget_endpoint_transfer_not_ready(dep, event); break; case DWC3_DEPEVT_EPCMDCMPLT: - cmd = DEPEVT_PARAMETER_CMD(event->parameters); - - if (cmd == DWC3_DEPCMD_ENDTRANSFER) { - dep->flags &= ~DWC3_EP_TRANSFER_STARTED; - dwc3_gadget_ep_cleanup_cancelled_requests(dep); - } + dwc3_gadget_endpoint_command_complete(dep, event); break; - case DWC3_DEPEVT_STREAMEVT: case DWC3_DEPEVT_XFERCOMPLETE: + dwc3_gadget_endpoint_transfer_complete(dep, event); + break; + case DWC3_DEPEVT_STREAMEVT: + dwc3_gadget_endpoint_stream_event(dep, event); + break; case DWC3_DEPEVT_RXTXFIFOEVT: break; } @@ -2396,27 +3437,27 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, static void dwc3_disconnect_gadget(struct dwc3 *dwc) { - if (dwc->gadget_driver && dwc->gadget_driver->disconnect) { + if (dwc->async_callbacks && dwc->gadget_driver->disconnect) { spin_unlock(&dwc->lock); - dwc->gadget_driver->disconnect(&dwc->gadget); + dwc->gadget_driver->disconnect(dwc->gadget); spin_lock(&dwc->lock); } } static void dwc3_suspend_gadget(struct dwc3 *dwc) { - if (dwc->gadget_driver && dwc->gadget_driver->suspend) { + if (dwc->async_callbacks && dwc->gadget_driver->suspend) { spin_unlock(&dwc->lock); - dwc->gadget_driver->suspend(&dwc->gadget); + dwc->gadget_driver->suspend(dwc->gadget); spin_lock(&dwc->lock); } } static void dwc3_resume_gadget(struct dwc3 *dwc) { - if (dwc->gadget_driver && dwc->gadget_driver->resume) { + if (dwc->async_callbacks && dwc->gadget_driver->resume) { spin_unlock(&dwc->lock); - dwc->gadget_driver->resume(&dwc->gadget); + dwc->gadget_driver->resume(dwc->gadget); spin_lock(&dwc->lock); } } @@ -2426,53 +3467,74 @@ static void dwc3_reset_gadget(struct dwc3 *dwc) if (!dwc->gadget_driver) return; - if (dwc->gadget.speed != USB_SPEED_UNKNOWN) { + if (dwc->async_callbacks && dwc->gadget->speed != USB_SPEED_UNKNOWN) { spin_unlock(&dwc->lock); - usb_gadget_udc_reset(&dwc->gadget, dwc->gadget_driver); + usb_gadget_udc_reset(dwc->gadget, dwc->gadget_driver); spin_lock(&dwc->lock); } } -static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, - bool interrupt) +void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, + bool interrupt) { struct dwc3 *dwc = dep->dwc; - struct dwc3_gadget_ep_cmd_params params; - u32 cmd; - int ret; - if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) + /* + * Only issue End Transfer command to the control endpoint of a started + * Data Phase. Typically we should only do so in error cases such as + * invalid/unexpected direction as described in the control transfer + * flow of the programming guide. + */ + if (dep->number <= 1 && dwc->ep0state != EP0_DATA_PHASE) + return; + + if (interrupt && (dep->flags & DWC3_EP_DELAY_STOP)) + return; + + if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) || + (dep->flags & DWC3_EP_END_TRANSFER_PENDING)) return; /* + * If a Setup packet is received but yet to DMA out, the controller will + * not process the End Transfer command of any endpoint. Polling of its + * DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a + * timeout. Delay issuing the End Transfer command until the Setup TRB is + * prepared. + */ + if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status) { + dep->flags |= DWC3_EP_DELAY_STOP; + return; + } + + /* * NOTICE: We are violating what the Databook says about the * EndTransfer command. Ideally we would _always_ wait for the * EndTransfer Command Completion IRQ, but that's causing too * much trouble synchronizing between us and gadget driver. * * We have discussed this with the IP Provider and it was - * suggested to giveback all requests here, but give HW some - * extra time to synchronize with the interconnect. We're using - * an arbitraty 100us delay for that. + * suggested to giveback all requests here. * * Note also that a similar handling was tested by Synopsys * (thanks a lot Paul) and nothing bad has come out of it. - * In short, what we're doing is: + * In short, what we're doing is issuing EndTransfer with + * CMDIOC bit set and delay kicking transfer until the + * EndTransfer command had completed. + * + * As of IP version 3.10a of the DWC_usb3 IP, the controller + * supports a mode to work around the above limitation. The + * software can poll the CMDACT bit in the DEPCMD register + * after issuing a EndTransfer command. This mode is enabled + * by writing GUCTL2[14]. This polling is already done in the + * dwc3_send_gadget_ep_cmd() function so if the mode is + * enabled, the EndTransfer command will have completed upon + * returning from this function. * - * - Issue EndTransfer WITH CMDIOC bit set - * - Wait 100us + * This mode is NOT available on the DWC_usb31 IP. */ - cmd = DWC3_DEPCMD_ENDTRANSFER; - cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0; - cmd |= DWC3_DEPCMD_CMDIOC; - cmd |= DWC3_DEPCMD_PARAM(dep->resource_index); - memset(¶ms, 0, sizeof(params)); - ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); - dep->resource_index = 0; - - if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A) - udelay(100); + __dwc3_stop_active_transfer(dep, force, interrupt); } static void dwc3_clear_stall_all_ep(struct dwc3 *dwc) @@ -2481,6 +3543,7 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc) for (epnum = 1; epnum < DWC3_ENDPOINTS_NUM; epnum++) { struct dwc3_ep *dep; + int ret; dep = dwc->eps[epnum]; if (!dep) @@ -2491,35 +3554,54 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc) dep->flags &= ~DWC3_EP_STALL; - dwc3_send_clear_stall_ep_cmd(dep); + ret = dwc3_send_clear_stall_ep_cmd(dep); + WARN_ON_ONCE(ret); } } static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) { - int reg; + int reg; + + dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RX_DET); reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_INITU1ENA; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); - reg &= ~DWC3_DCTL_INITU2ENA; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); + + dwc->connected = false; dwc3_disconnect_gadget(dwc); - dwc->gadget.speed = USB_SPEED_UNKNOWN; + dwc->gadget->speed = USB_SPEED_UNKNOWN; dwc->setup_packet_pending = false; - usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED); + usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED); - dwc->connected = false; + if (dwc->ep0state != EP0_SETUP_PHASE) { + unsigned int dir; + + dir = !!dwc->ep0_expect_in; + if (dwc->ep0state == EP0_DATA_PHASE) + dwc3_ep0_end_control_data(dwc, dwc->eps[dir]); + else + dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]); + dwc3_ep0_stall_and_restart(dwc); + } } static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) { - u32 reg; + u32 reg; - dwc->connected = true; + /* + * Ideally, dwc3_reset_gadget() would trigger the function + * drivers to stop any active transfers through ep disable. + * However, for functions which defer ep disable, such as mass + * storage, we will need to rely on the call to stop active + * transfers here, and avoid allowing of request queuing. + */ + dwc->connected = false; /* * WORKAROUND: DWC3 revisions <1.88a have an issue which @@ -2547,16 +3629,45 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) * STAR#9000466709: RTL: Device : Disconnect event not * generated if setup packet pending in FIFO */ - if (dwc->revision < DWC3_REVISION_188A) { + if (DWC3_VER_IS_PRIOR(DWC3, 188A)) { if (dwc->setup_packet_pending) dwc3_gadget_disconnect_interrupt(dwc); } dwc3_reset_gadget(dwc); + /* + * From SNPS databook section 8.1.2, the EP0 should be in setup + * phase. So ensure that EP0 is in setup phase by issuing a stall + * and restart if EP0 is not in setup phase. + */ + if (dwc->ep0state != EP0_SETUP_PHASE) { + unsigned int dir; + + dir = !!dwc->ep0_expect_in; + if (dwc->ep0state == EP0_DATA_PHASE) + dwc3_ep0_end_control_data(dwc, dwc->eps[dir]); + else + dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]); + + dwc->eps[0]->trb_enqueue = 0; + dwc->eps[1]->trb_enqueue = 0; + + dwc3_ep0_stall_and_restart(dwc); + } + + /* + * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a + * Section 4.1.2 Table 4-2, it states that during a USB reset, the SW + * needs to ensure that it sends "a DEPENDXFER command for any active + * transfers." + */ + dwc3_stop_active_transfers(dwc); + dwc->connected = true; + reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_TSTCTRL_MASK; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); dwc->test_mode = false; dwc3_clear_stall_all_ep(dwc); @@ -2568,22 +3679,45 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) { - struct dwc3_ep *dep; - int ret; - u32 reg; - u8 speed; + struct dwc3_ep *dep; + int ret; + u32 reg; + u8 lanes = 1; + u8 speed; + + if (!dwc->softconnect) + return; reg = dwc3_readl(dwc->regs, DWC3_DSTS); speed = reg & DWC3_DSTS_CONNECTSPD; dwc->speed = speed; + if (DWC3_IP_IS(DWC32)) + lanes = DWC3_DSTS_CONNLANES(reg) + 1; + + dwc->gadget->ssp_rate = USB_SSP_GEN_UNKNOWN; + + /* + * RAMClkSel is reset to 0 after USB reset, so it must be reprogrammed + * each time on Connect Done. + * + * Currently we always use the reset value. If any platform + * wants to set this to a different value, we need to add a + * setting and update GCTL.RAMCLKSEL here. + */ + switch (speed) { case DWC3_DSTS_SUPERSPEED_PLUS: dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); - dwc->gadget.ep0->maxpacket = 512; - dwc->gadget.speed = USB_SPEED_SUPER_PLUS; + dwc->gadget->ep0->maxpacket = 512; + dwc->gadget->speed = USB_SPEED_SUPER_PLUS; + + if (lanes > 1) + dwc->gadget->ssp_rate = USB_SSP_GEN_2x2; + else + dwc->gadget->ssp_rate = USB_SSP_GEN_2x1; break; - case DWC3_DCFG_SUPERSPEED: + case DWC3_DSTS_SUPERSPEED: /* * WORKAROUND: DWC3 revisions <1.90a have an issue which * would cause a missing USB3 Reset event. @@ -2597,36 +3731,36 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) * STAR#9000483510: RTL: SS : USB3 reset event may * not be generated always when the link enters poll */ - if (dwc->revision < DWC3_REVISION_190A) + if (DWC3_VER_IS_PRIOR(DWC3, 190A)) dwc3_gadget_reset_interrupt(dwc); dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); - dwc->gadget.ep0->maxpacket = 512; - dwc->gadget.speed = USB_SPEED_SUPER; + dwc->gadget->ep0->maxpacket = 512; + dwc->gadget->speed = USB_SPEED_SUPER; + + if (lanes > 1) { + dwc->gadget->speed = USB_SPEED_SUPER_PLUS; + dwc->gadget->ssp_rate = USB_SSP_GEN_1x2; + } break; - case DWC3_DCFG_HIGHSPEED: + case DWC3_DSTS_HIGHSPEED: dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); - dwc->gadget.ep0->maxpacket = 64; - dwc->gadget.speed = USB_SPEED_HIGH; + dwc->gadget->ep0->maxpacket = 64; + dwc->gadget->speed = USB_SPEED_HIGH; break; - case DWC3_DCFG_FULLSPEED: - case DWC3_DCFG_FULLSPEED1: + case DWC3_DSTS_FULLSPEED: dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); - dwc->gadget.ep0->maxpacket = 64; - dwc->gadget.speed = USB_SPEED_FULL; - break; - case DWC3_DCFG_LOWSPEED: - dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8); - dwc->gadget.ep0->maxpacket = 8; - dwc->gadget.speed = USB_SPEED_LOW; + dwc->gadget->ep0->maxpacket = 64; + dwc->gadget->speed = USB_SPEED_FULL; break; } - dwc->eps[1]->endpoint.maxpacket = dwc->gadget.ep0->maxpacket; + dwc->eps[1]->endpoint.maxpacket = dwc->gadget->ep0->maxpacket; /* Enable USB2 LPM Capability */ - if ((dwc->revision > DWC3_REVISION_194A) && + if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A) && + !dwc->usb2_gadget_lpm_disable && (speed != DWC3_DSTS_SUPERSPEED) && (speed != DWC3_DSTS_SUPERSPEED_PLUS)) { reg = dwc3_readl(dwc->regs, DWC3_DCFG); @@ -2636,7 +3770,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 @@ -2644,17 +3779,23 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) * BESL value in the LPM token is less than or equal to LPM * NYET threshold. */ - if (dwc->revision < DWC3_REVISION_240A && dwc->has_lpm_erratum) - WARN(true, "LPM Erratum not available on dwc3 revisisions < 2.40a\n"); + WARN_ONCE(DWC3_VER_IS_PRIOR(DWC3, 240A) && dwc->has_lpm_erratum, + "LPM Erratum not available on dwc3 revisions < 2.40a\n"); - if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A) - reg |= DWC3_DCTL_LPM_ERRATA(dwc->lpm_nyet_threshold); + if (dwc->has_lpm_erratum && !DWC3_VER_IS_PRIOR(DWC3, 240A)) + reg |= DWC3_DCTL_NYET_THRES(dwc->lpm_nyet_threshold); - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); } else { + if (dwc->usb2_gadget_lpm_disable) { + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~DWC3_DCFG_LPM_CAP; + dwc3_writel(dwc->regs, DWC3_DCFG, reg); + } + reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_HIRD_THRES_MASK; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); } dep = dwc->eps[0]; @@ -2687,15 +3828,18 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc) * implemented. */ - if (dwc->gadget_driver && dwc->gadget_driver->resume) - dwc->gadget_driver->resume(&dwc->gadget); + if (dwc->async_callbacks && dwc->gadget_driver->resume) { + spin_unlock(&dwc->lock); + dwc->gadget_driver->resume(dwc->gadget); + spin_lock(&dwc->lock); + } } static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, - unsigned int evtinfo) + unsigned int evtinfo) { - enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK; - unsigned int pwropt; + enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK; + unsigned int pwropt; /* * WORKAROUND: DWC3 < 2.50a have an issue when configured without @@ -2715,11 +3859,10 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, * operational mode */ pwropt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1); - if ((dwc->revision < DWC3_REVISION_250A) && - (pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) { + if (DWC3_VER_IS_PRIOR(DWC3, 250A) && + (pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) { if ((dwc->link_state == DWC3_LINK_STATE_U3) && - (next == DWC3_LINK_STATE_RESUME)) { - dev_dbg(dwc->dev, "ignoring transition U3 -> Resume\n"); + (next == DWC3_LINK_STATE_RESUME)) { return; } } @@ -2742,10 +3885,10 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, * STAR#9000446952: RTL: Device SS : if U1/U2 ->U0 takes >128us * core send LGO_Ux entering U0 */ - if (dwc->revision < DWC3_REVISION_183A) { + if (DWC3_VER_IS_PRIOR(DWC3, 183A)) { if (next == DWC3_LINK_STATE_U0) { - u32 u1u2; - u32 reg; + u32 u1u2; + u32 reg; switch (dwc->link_state) { case DWC3_LINK_STATE_U1: @@ -2761,7 +3904,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, reg &= ~u1u2; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); break; default: /* do nothing */ @@ -2777,7 +3920,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, break; case DWC3_LINK_STATE_U2: case DWC3_LINK_STATE_U3: - //dwc3_suspend_gadget(dwc); + dwc3_suspend_gadget(dwc); break; case DWC3_LINK_STATE_RESUME: dwc3_resume_gadget(dwc); @@ -2802,12 +3945,12 @@ static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc, } static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc, - unsigned int evtinfo) + unsigned int evtinfo) { - unsigned int is_ss = evtinfo & (1UL << 4); + unsigned int is_ss = evtinfo & BIT(4); - /** - * WORKAROUND: DWC3 revison 2.20a with hibernation support + /* + * WORKAROUND: DWC3 revision 2.20a with hibernation support * have a known issue which can cause USB CV TD.9.23 to fail * randomly. * @@ -2826,9 +3969,8 @@ static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc, } static void dwc3_gadget_interrupt(struct dwc3 *dwc, - const struct dwc3_event_devt *event) + const struct dwc3_event_devt *event) { - switch (event->type) { case DWC3_DEVICE_EVENT_DISCONNECT: dwc3_gadget_disconnect_interrupt(dwc); @@ -2843,96 +3985,154 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc, dwc3_gadget_wakeup_interrupt(dwc); break; case DWC3_DEVICE_EVENT_HIBER_REQ: - if (!dwc->has_hibernation) { - WARN(1 ,"unexpected hibernation event\n"); + if (dev_WARN_ONCE(dwc->dev, !dwc->has_hibernation, + "unexpected hibernation event\n")) break; - } + dwc3_gadget_hibernation_interrupt(dwc, event->event_info); break; case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: dwc3_gadget_linksts_change_interrupt(dwc, event->event_info); break; - case DWC3_DEVICE_EVENT_EOPF: - dev_dbg(dwc->dev, "End of Periodic Frame\n"); + case DWC3_DEVICE_EVENT_SUSPEND: /* It changed to be suspend event for version 2.30a and above */ - if (dwc->revision >= DWC3_REVISION_230A) { + if (!DWC3_VER_IS_PRIOR(DWC3, 230A)) { /* * Ignore suspend event until the gadget enters into * USB_STATE_CONFIGURED state. */ - if (dwc->gadget.state >= USB_STATE_CONFIGURED) + if (dwc->gadget->state >= USB_STATE_CONFIGURED) dwc3_gadget_suspend_interrupt(dwc, event->event_info); } break; case DWC3_DEVICE_EVENT_SOF: - dev_dbg(dwc->dev, "Start of Periodic Frame\n"); - break; case DWC3_DEVICE_EVENT_ERRATIC_ERROR: - dev_dbg(dwc->dev, "Erratic Error\n"); - break; case DWC3_DEVICE_EVENT_CMD_CMPL: - dev_dbg(dwc->dev, "Command Complete\n"); - break; case DWC3_DEVICE_EVENT_OVERFLOW: - dev_dbg(dwc->dev, "Overflow\n"); break; default: - dev_dbg(dwc->dev, "UNKNOWN IRQ %d\n", event->type); + dev_warn(dwc->dev, "UNKNOWN IRQ %d\n", event->type); } } static void dwc3_process_event_entry(struct dwc3 *dwc, - const union dwc3_event *event) + const union dwc3_event *event) { if (!event->type.is_devspec) dwc3_endpoint_interrupt(dwc, &event->depevt); else if (event->type.type == DWC3_EVENT_TYPE_DEV) dwc3_gadget_interrupt(dwc, &event->devt); + else + dev_err(dwc->dev, "UNKNOWN IRQ type %d\n", event->raw); } -static void dwc3_gadget_poll(struct usb_gadget * g) +static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt) { - struct dwc3 *dwc = gadget_to_dwc(g); - struct dwc3_event_buffer *evt = dwc->ev_buf; + struct dwc3 *dwc = evt->dwc; + irqreturn_t ret = IRQ_NONE; + int left; + + left = evt->count; + + if (!(evt->flags & DWC3_EVENT_PENDING)) + return IRQ_NONE; + + while (left > 0) { + union dwc3_event event; + + event.raw = *(u32 *) (evt->cache + evt->lpos); + + dwc3_process_event_entry(dwc, &event); + + /* + * FIXME we wrap around correctly to the next entry as + * almost all entries are 4 bytes in size. There is one + * entry which has 12 bytes which is a regular entry + * followed by 8 bytes data. ATM I don't know how + * things are organized if we get next to the a + * boundary so I worry about that once we try to handle + * that. + */ + evt->lpos = (evt->lpos + 4) % evt->length; + left -= 4; + } + + evt->count = 0; + ret = IRQ_HANDLED; + + /* Unmask interrupt */ + dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), + DWC3_GEVNTSIZ_SIZE(evt->length)); + + if (dwc->imod_interval) { + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB); + dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval); + } + + /* Keep the clearing of DWC3_EVENT_PENDING at the end */ + evt->flags &= ~DWC3_EVENT_PENDING; + + return ret; +} + +static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt) +{ + struct dwc3 *dwc = evt->dwc; u32 amount; u32 count; - void *buf; - int pos = 0; + + /* + * With PCIe legacy interrupt, test shows that top-half irq handler can + * be called again after HW interrupt deassertion. Check if bottom-half + * irq event handler completes before caching new event to prevent + * losing events. + */ + if (evt->flags & DWC3_EVENT_PENDING) + return IRQ_HANDLED; count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); count &= DWC3_GEVNTCOUNT_MASK; if (!count) - return; + return IRQ_NONE; - buf = xzalloc(count); + evt->count = count; + evt->flags |= DWC3_EVENT_PENDING; + + /* Mask interrupt */ + dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), + DWC3_GEVNTSIZ_INTMASK | DWC3_GEVNTSIZ_SIZE(evt->length)); amount = min(count, evt->length - evt->lpos); - memcpy(buf, evt->buf + evt->lpos, amount); + memcpy_fromio(evt->cache + evt->lpos, evt->buf + evt->lpos, amount); if (amount < count) - memcpy(buf + amount, evt->buf, count - amount); - - evt->lpos = (evt->lpos + count) % evt->length; + memcpy_fromio(evt->cache, evt->buf, count - amount); dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count); - while (count > 0) { - union dwc3_event event; + dwc3_process_event_buf(evt); - event.raw = *(u32 *)(buf + pos); + return IRQ_HANDLED; +} - dwc3_process_event_entry(dwc, &event); +static void dwc3_gadget_poll(struct usb_gadget *g) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + struct dwc3_event_buffer *evt = dwc->ev_buf; - count -= 4; - pos += 4; - } + dwc3_check_event_buf(evt); +} + +static void dwc_gadget_release(struct device *dev) +{ + struct usb_gadget *gadget = container_of(dev, struct usb_gadget, dev); - free(buf); + kfree(gadget); } /** - * dwc3_gadget_init - Initializes gadget related registers + * dwc3_gadget_init - initializes gadget related registers * @dwc: pointer to our controller context structure * * Returns 0 on success otherwise negative errno. @@ -2940,46 +4140,69 @@ static void dwc3_gadget_poll(struct usb_gadget * g) int dwc3_gadget_init(struct dwc3 *dwc) { int ret; + struct device *dev; dwc->ep0_trb = dma_alloc_coherent(sizeof(*dwc->ep0_trb) * 2, - &dwc->ep0_trb_addr); + &dwc->ep0_trb_addr); if (!dwc->ep0_trb) { dev_err(dwc->dev, "failed to allocate ep0 trb\n"); ret = -ENOMEM; - goto err1; + goto err0; } - dwc->setup_buf = xzalloc(DWC3_EP0_SETUP_SIZE); + dwc->setup_buf = kzalloc(DWC3_EP0_SETUP_SIZE, GFP_KERNEL); if (!dwc->setup_buf) { ret = -ENOMEM; - goto err2; + goto err1; } - dwc->bounce = dma_alloc_coherent(DWC3_BOUNCE_SIZE, - &dwc->bounce_addr); + dwc->bounce = dma_alloc_coherent(DWC3_BOUNCE_SIZE, &dwc->bounce_addr); if (!dwc->bounce) { - dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n"); + ret = -ENOMEM; + goto err2; + } + + init_completion(&dwc->ep0_in_setup); + dwc->gadget = kzalloc(sizeof(struct usb_gadget), GFP_KERNEL); + if (!dwc->gadget) { ret = -ENOMEM; goto err3; } - dwc->gadget.ops = &dwc3_gadget_ops; - dwc->gadget.max_speed = USB_SPEED_SUPER; - dwc->gadget.speed = USB_SPEED_UNKNOWN; - dwc->gadget.name = "dwc3-gadget"; + + usb_initialize_gadget(dwc->dev, dwc->gadget, dwc_gadget_release); + dev = &dwc->gadget->dev; + dev->platform_data = dwc; + dwc->gadget->ops = &dwc3_gadget_ops; + dwc->gadget->speed = USB_SPEED_UNKNOWN; + dwc->gadget->ssp_rate = USB_SSP_GEN_UNKNOWN; + dwc->gadget->sg_supported = true; + dwc->gadget->name = "dwc3-gadget"; + dwc->gadget->lpm_capable = !dwc->usb2_gadget_lpm_disable; /* - * Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize - * on ep out. + * FIXME We might be setting max_speed to <SUPER, however versions + * <2.20a of dwc3 have an issue with metastability (documented + * elsewhere in this driver) which tells us we can't set max speed to + * anything lower than SUPER. + * + * Because gadget.max_speed is only used by composite.c and function + * drivers (i.e. it won't go into dwc3's registers) we are allowing this + * to happen so we avoid sending SuperSpeed Capability descriptor + * together with our BOS descriptor as that could confuse host into + * thinking we can handle super speed. + * + * Note that, in fact, we won't even support GetBOS requests when speed + * is less than super speed because we don't have means, yet, to tell + * composite.c that we are USB 2.0 + LPM ECN. */ - dwc->gadget.quirk_ep_out_aligned_size = true; - - if (dwc->revision < DWC3_REVISION_220A && + if (DWC3_VER_IS_PRIOR(DWC3, 220A) && !dwc->dis_metastability_quirk) dev_info(dwc->dev, "changing max_speed on rev %08x\n", dwc->revision); - dwc->gadget.max_speed = dwc->maximum_speed; + dwc->gadget->max_speed = dwc->maximum_speed; + dwc->gadget->max_ssp_rate = dwc->max_ssp_rate; /* * REVISIT: Here we should clear all pending IRQs to be @@ -2990,26 +4213,47 @@ int dwc3_gadget_init(struct dwc3 *dwc) if (ret) goto err4; - ret = usb_add_gadget_udc((struct device_d *)dwc->dev, &dwc->gadget); + ret = usb_add_gadget(dwc->gadget); if (ret) { - dev_err(dwc->dev, "failed to register udc\n"); - goto err4; + dev_err(dwc->dev, "failed to add gadget\n"); + goto err5; } - dwc3_gadget_set_speed(dwc, dwc->maximum_speed); + if (DWC3_IP_IS(DWC32) && dwc->maximum_speed == USB_SPEED_SUPER_PLUS) + dwc3_gadget_set_ssp_rate(dwc->gadget, dwc->max_ssp_rate); + else + dwc3_gadget_set_speed(dwc->gadget, dwc->maximum_speed); return 0; -err4: +err5: dwc3_gadget_free_endpoints(dwc); +err4: + usb_put_gadget(dwc->gadget); + dwc->gadget = NULL; err3: - dma_free_coherent(dwc->bounce, 0, DWC3_BOUNCE_SIZE); + dma_free_coherent(dwc->bounce, dwc->bounce_addr, DWC3_BOUNCE_SIZE); err2: kfree(dwc->setup_buf); err1: - dma_free_coherent(dwc->ep0_trb, 0, sizeof(*dwc->ep0_trb) * 2); + dma_free_coherent(dwc->ep0_trb, dwc->ep0_trb_addr, sizeof(*dwc->ep0_trb) * 2); +err0: return ret; } + +/* -------------------------------------------------------------------------- */ + +void dwc3_gadget_exit(struct dwc3 *dwc) +{ + if (!dwc->gadget) + return; + + usb_del_gadget_udc(dwc->gadget); + dwc3_gadget_free_endpoints(dwc); + dma_free_coherent(dwc->bounce, dwc->bounce_addr, DWC3_BOUNCE_SIZE); + kfree(dwc->setup_buf); + dma_free_coherent(dwc->ep0_trb, dwc->ep0_trb_addr, sizeof(*dwc->ep0_trb) * 2); +} diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 3ce748c0b4..0afa10b318 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -1,70 +1,75 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/** +/* * gadget.h - DesignWare USB3 DRD Gadget Header * - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com * * Authors: Felipe Balbi <balbi@ti.com>, * Sebastian Andrzej Siewior <bigeasy@linutronix.de> - * - * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/gadget.h) and ported - * to uboot. - * - * commit 7a60855972 : usb: dwc3: gadget: fix set_halt() bug with pending - transfers - * */ #ifndef __DRIVERS_USB_DWC3_GADGET_H #define __DRIVERS_USB_DWC3_GADGET_H -#include <usb/gadget.h> +#include <linux/usb/gadget.h> #include <linux/list.h> #include "io.h" struct dwc3; #define to_dwc3_ep(ep) (container_of(ep, struct dwc3_ep, endpoint)) -#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget)) +#define gadget_to_dwc(g) (g->dev.platform_data) /* DEPCFG parameter 1 */ -#define DWC3_DEPCFG_INT_NUM(n) ((n) << 0) -#define DWC3_DEPCFG_XFER_COMPLETE_EN (1 << 8) -#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN (1 << 9) -#define DWC3_DEPCFG_XFER_NOT_READY_EN (1 << 10) -#define DWC3_DEPCFG_FIFO_ERROR_EN (1 << 11) -#define DWC3_DEPCFG_STREAM_EVENT_EN (1 << 13) -#define DWC3_DEPCFG_BINTERVAL_M1(n) ((n) << 16) -#define DWC3_DEPCFG_STREAM_CAPABLE (1 << 24) -#define DWC3_DEPCFG_EP_NUMBER(n) ((n) << 25) -#define DWC3_DEPCFG_BULK_BASED (1 << 30) -#define DWC3_DEPCFG_FIFO_BASED (1 << 31) +#define DWC3_DEPCFG_INT_NUM(n) (((n) & 0x1f) << 0) +#define DWC3_DEPCFG_XFER_COMPLETE_EN BIT(8) +#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN BIT(9) +#define DWC3_DEPCFG_XFER_NOT_READY_EN BIT(10) +#define DWC3_DEPCFG_FIFO_ERROR_EN BIT(11) +#define DWC3_DEPCFG_STREAM_EVENT_EN BIT(13) +#define DWC3_DEPCFG_BINTERVAL_M1(n) (((n) & 0xff) << 16) +#define DWC3_DEPCFG_STREAM_CAPABLE BIT(24) +#define DWC3_DEPCFG_EP_NUMBER(n) (((n) & 0x1f) << 25) +#define DWC3_DEPCFG_BULK_BASED BIT(30) +#define DWC3_DEPCFG_FIFO_BASED BIT(31) /* DEPCFG parameter 0 */ -#define DWC3_DEPCFG_EP_TYPE(n) ((n) << 1) -#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) ((n) << 3) -#define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17) -#define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22) +#define DWC3_DEPCFG_EP_TYPE(n) (((n) & 0x3) << 1) +#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) (((n) & 0x7ff) << 3) +#define DWC3_DEPCFG_FIFO_NUMBER(n) (((n) & 0x1f) << 17) +#define DWC3_DEPCFG_BURST_SIZE(n) (((n) & 0xf) << 22) #define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26) /* This applies for core versions earlier than 1.94a */ -#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31) +#define DWC3_DEPCFG_IGN_SEQ_NUM BIT(31) /* These apply for core versions 1.94a and later */ -#define DWC3_DEPCFG_ACTION_INIT (0 << 30) -#define DWC3_DEPCFG_ACTION_RESTORE (1 << 30) +#define DWC3_DEPCFG_ACTION_INIT (0 << 30) +#define DWC3_DEPCFG_ACTION_RESTORE BIT(30) #define DWC3_DEPCFG_ACTION_MODIFY (2 << 30) /* DEPXFERCFG parameter 0 */ #define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff) +/* U1 Device exit Latency */ +#define DWC3_DEFAULT_U1_DEV_EXIT_LAT 0x0A /* Less then 10 microsec */ + +/* U2 Device exit Latency */ +#define DWC3_DEFAULT_U2_DEV_EXIT_LAT 0x1FF /* Less then 511 microsec */ + +/* Frame/Microframe Number Mask */ +#define DWC3_FRNUMBER_MASK 0x3fff /* -------------------------------------------------------------------------- */ #define to_dwc3_request(r) (container_of(r, struct dwc3_request, request)) +/** + * next_request - gets the next request on the given list + * @list: the request list to operate on + * + * Caller should take care of locking. This function return %NULL or the first + * request available on @list. + */ static inline struct dwc3_request *next_request(struct list_head *list) { - if (list_empty(list)) - return NULL; - - return list_first_entry(list, struct dwc3_request, list); + return list_first_entry_or_null(list, struct dwc3_request, list); } /** @@ -76,7 +81,7 @@ static inline struct dwc3_request *next_request(struct list_head *list) */ static inline void dwc3_gadget_move_started_request(struct dwc3_request *req) { - struct dwc3_ep *dep = req->dep; + struct dwc3_ep *dep = req->dep; req->status = DWC3_REQUEST_STATUS_STARTED; list_move_tail(&req->list, &dep->started_list); @@ -85,15 +90,17 @@ static inline void dwc3_gadget_move_started_request(struct dwc3_request *req) /** * dwc3_gadget_move_cancelled_request - move @req to the cancelled_list * @req: the request to be moved + * @reason: cancelled reason for the dwc3 request * * Caller should take care of locking. This function will move @req from its * current list to the endpoint's cancelled_list. */ -static inline void dwc3_gadget_move_cancelled_request(struct dwc3_request *req) +static inline void dwc3_gadget_move_cancelled_request(struct dwc3_request *req, + unsigned int reason) { - struct dwc3_ep *dep = req->dep; + struct dwc3_ep *dep = req->dep; - req->status = DWC3_REQUEST_STATUS_CANCELLED; + req->status = reason; list_move_tail(&req->list, &dep->cancelled_list); } @@ -103,11 +110,14 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, void dwc3_ep0_interrupt(struct dwc3 *dwc, const struct dwc3_event_depevt *event); void dwc3_ep0_out_start(struct dwc3 *dwc); +void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep); +void dwc3_ep0_stall_and_restart(struct dwc3 *dwc); int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request); int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol); -void dwc3_gadget_handle_interrupt(struct dwc3 *dwc); +void dwc3_ep0_send_delayed_status(struct dwc3 *dwc); +void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt); /** * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW @@ -118,10 +128,24 @@ void dwc3_gadget_handle_interrupt(struct dwc3 *dwc); */ static inline void dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep) { - u32 res_id; + u32 res_id; res_id = dwc3_readl(dep->regs, DWC3_DEPCMD); dep->resource_index = DWC3_DEPCMD_GET_RSC_IDX(res_id); } +/** + * dwc3_gadget_dctl_write_safe - write to DCTL safe from link state change + * @dwc: pointer to our context structure + * @value: value to write to DCTL + * + * Use this function when doing read-modify-write to DCTL. It will not + * send link state change request. + */ +static inline void dwc3_gadget_dctl_write_safe(struct dwc3 *dwc, u32 value) +{ + value &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; + dwc3_writel(dwc->regs, DWC3_DCTL, value); +} + #endif /* __DRIVERS_USB_DWC3_GADGET_H */ diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index d5daa7f19e..281d016a86 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /** * host.c - DesignWare USB3 DRD Controller Host Glue * @@ -16,7 +16,7 @@ int dwc3_host_init(struct dwc3 *dwc) { struct resource *io; - struct device_d *dev = dwc->dev; + struct device *dev = dwc->dev; io = dev_get_resource(dev, IORESOURCE_MEM, 0); if (IS_ERR(io)) { @@ -24,13 +24,17 @@ int dwc3_host_init(struct dwc3 *dwc) return PTR_ERR(io); } - dwc->xhci = add_generic_device("xHCI", DEVICE_ID_DYNAMIC, NULL, - io->start, resource_size(io), - IORESOURCE_MEM, NULL); + dwc->xhci = add_child_device(dev, "xHCI", DEVICE_ID_DYNAMIC, NULL, + io->start, resource_size(io), + IORESOURCE_MEM, NULL); if (!dwc->xhci) { dev_err(dev, "Failed to register xHCI device\n"); return -ENODEV; } - + return 0; } + +void dwc3_host_exit(struct dwc3 *dwc) +{ +} diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h index f87b173e90..c76878a3a4 100644 --- a/drivers/usb/dwc3/io.h +++ b/drivers/usb/dwc3/io.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /** * io.h - DesignWare USB3 DRD IO Header * diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 977f6c0dba..517255f477 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -1,6 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only menuconfig USB_GADGET select USB select POLLER + select NLS bool "USB gadget support" if USB_GADGET @@ -15,9 +17,6 @@ config USB_GADGET_DRIVER_ARC default y select USB_GADGET_DUALSPEED -config USB_GADGET_DRIVER_ARC_PBL - bool - config USB_GADGET_DRIVER_AT91 bool prompt "at91 gadget driver" @@ -36,16 +35,18 @@ config USB_GADGET_AUTOSTART bool default y prompt "Automatically start usbgadget on boot" + depends on GLOBALVAR + select SYSTEM_PARTITIONS if USB_GADGET_MASS_STORAGE help Enabling this option allows to automatically start a dfu or fastboot gadget during boot. This behaviour is controlled with - the global.usbgadget.{dfu,fastboot}_function variable. + the global.usbgadget.dfu_function, global.system.partitions + and global.fastboot.* variables. comment "USB Gadget drivers" config USB_GADGET_DFU bool - select FILE_LIST prompt "Device Firmware Update Gadget" config USB_GADGET_SERIAL @@ -56,7 +57,17 @@ config USB_GADGET_SERIAL config USB_GADGET_FASTBOOT bool select BANNER - select FILE_LIST select FASTBOOT_BASE prompt "Android Fastboot USB Gadget" + +config USB_GADGET_MASS_STORAGE + bool + select BTHREAD + prompt "USB Mass Storage Gadget" + help + The Mass Storage Gadget acts as a USB Mass Storage disk drive. + As its storage repository it can use a regular file or a block + device. Multiple storages can be specified at once on + instantiation time. + endif diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 27673fcf0e..f45b23f22d 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -1,9 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_USB_GADGET) += composite.o config.o usbstring.o epautoconf.o udc-core.o functions.o config.o multi.o -obj-$(CONFIG_USB_GADGET_SERIAL) += u_serial.o serial.o f_serial.o f_acm.o -obj-$(CONFIG_USB_GADGET_DFU) += dfu.o -obj-$(CONFIG_USB_GADGET_FASTBOOT) += f_fastboot.o -obj-$(CONFIG_USB_GADGET_DRIVER_ARC) += fsl_udc.o -pbl-$(CONFIG_USB_GADGET_DRIVER_ARC_PBL) += fsl_udc_pbl.o -obj-$(CONFIG_USB_GADGET_DRIVER_AT91) += at91_udc.o -obj-$(CONFIG_USB_GADGET_DRIVER_PXA27X) += pxa27x_udc.o +obj-$(CONFIG_USB_GADGET) += composite.o config.o usbstring.o epautoconf.o functions.o config.o + +obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/ diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index b66aa6be97..f55ae5698e 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1,12 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * composite.c - infrastructure for Composite USB Gadgets * * Copyright (C) 2006-2008 David Brownell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ /* #define VERBOSE_DEBUG */ @@ -16,11 +12,31 @@ #include <dma.h> #include <linux/err.h> #include <linux/bitmap.h> -#include <usb/composite.h> +#include <linux/usb/composite.h> +#include <linux/bitfield.h> +#include <linux/uuid.h> #include <asm/unaligned.h> #include <asm/byteorder.h> -static unsigned int usb_gadget_vbus_draw_ma = 2; +#include "u_os_desc.h" + +#define CONFIG_USB_GADGET_VBUS_DRAW 2 /* FIXME */ + +/** + * struct usb_os_string - represents OS String to be reported by a gadget + * @bLength: total length of the entire descritor, always 0x12 + * @bDescriptorType: USB_DT_STRING + * @qwSignature: the OS String proper + * @bMS_VendorCode: code used by the host for subsequent requests + * @bPad: not used, must be zero + */ +struct usb_os_string { + __u8 bLength; + __u8 bDescriptorType; + __u8 qwSignature[OS_STRING_QW_SIGN_LEN]; + __u8 bMS_VendorCode; + __u8 bPad; +} __packed; /* * The code in this file is utility code, used to build a gadget driver @@ -36,40 +52,89 @@ static struct usb_gadget_strings **get_containers_gs( } /** - * next_ep_desc() - advance to the next EP descriptor + * function_descriptors() - get function descriptors for speed + * @f: the function + * @speed: the speed + * + * Returns the descriptors or NULL if not set. + */ +static struct usb_descriptor_header ** +function_descriptors(struct usb_function *f, + enum usb_device_speed speed) +{ + struct usb_descriptor_header **descriptors; + + /* + * NOTE: we try to help gadget drivers which might not be setting + * max_speed appropriately. + */ + + switch (speed) { + case USB_SPEED_SUPER_PLUS: + descriptors = f->ssp_descriptors; + if (descriptors) + break; + fallthrough; + case USB_SPEED_SUPER: + descriptors = f->ss_descriptors; + if (descriptors) + break; + fallthrough; + case USB_SPEED_HIGH: + descriptors = f->hs_descriptors; + if (descriptors) + break; + fallthrough; + default: + descriptors = f->fs_descriptors; + } + + /* + * if we can't find any descriptors at all, then this gadget deserves to + * Oops with a NULL pointer dereference + */ + + return descriptors; +} + +/** + * next_desc() - advance to the next desc_type descriptor * @t: currect pointer within descriptor array + * @desc_type: descriptor type * - * Return: next EP descriptor or NULL + * Return: next desc_type descriptor or NULL * - * Iterate over @t until either EP descriptor found or + * Iterate over @t until either desc_type descriptor found or * NULL (that indicates end of list) encountered */ static struct usb_descriptor_header** -next_ep_desc(struct usb_descriptor_header **t) +next_desc(struct usb_descriptor_header **t, u8 desc_type) { for (; *t; t++) { - if ((*t)->bDescriptorType == USB_DT_ENDPOINT) + if ((*t)->bDescriptorType == desc_type) return t; } return NULL; } /* - * for_each_ep_desc()- iterate over endpoint descriptors in the - * descriptors list - * @start: pointer within descriptor array. - * @ep_desc: endpoint descriptor to use as the loop cursor + * for_each_desc() - iterate over desc_type descriptors in the + * descriptors list + * @start: pointer within descriptor array. + * @iter_desc: desc_type descriptor to use as the loop cursor + * @desc_type: wanted descriptr type */ -#define for_each_ep_desc(start, ep_desc) \ - for (ep_desc = next_ep_desc(start); \ - ep_desc; ep_desc = next_ep_desc(ep_desc+1)) +#define for_each_desc(start, iter_desc, desc_type) \ + for (iter_desc = next_desc(start, desc_type); \ + iter_desc; iter_desc = next_desc(iter_desc + 1, desc_type)) /** - * config_ep_by_speed() - configures the given endpoint + * config_ep_by_speed_and_alt() - configures the given endpoint * according to gadget speed. * @g: pointer to the gadget * @f: usb function * @_ep: the endpoint to configure + * @alt: alternate setting number * * Return: error code, 0 on success * @@ -82,44 +147,80 @@ next_ep_desc(struct usb_descriptor_header **t) * Note: the supplied function should hold all the descriptors * for supported speeds */ -int config_ep_by_speed(struct usb_gadget *g, - struct usb_function *f, - struct usb_ep *_ep) +int config_ep_by_speed_and_alt(struct usb_gadget *g, + struct usb_function *f, + struct usb_ep *_ep, + u8 alt) { - struct usb_composite_dev *cdev; struct usb_endpoint_descriptor *chosen_desc = NULL; + struct usb_interface_descriptor *int_desc = NULL; struct usb_descriptor_header **speed_desc = NULL; struct usb_ss_ep_comp_descriptor *comp_desc = NULL; int want_comp_desc = 0; struct usb_descriptor_header **d_spd; /* cursor for speed desc */ + struct usb_composite_dev *cdev; + bool incomplete_desc = false; if (!g || !f || !_ep) return -EIO; - cdev = get_gadget_data(g); - /* select desired speed */ switch (g->speed) { + case USB_SPEED_SUPER_PLUS: + if (gadget_is_superspeed_plus(g)) { + if (f->ssp_descriptors) { + speed_desc = f->ssp_descriptors; + want_comp_desc = 1; + break; + } + incomplete_desc = true; + } + fallthrough; case USB_SPEED_SUPER: if (gadget_is_superspeed(g)) { - speed_desc = f->ss_descriptors; - want_comp_desc = 1; - break; + if (f->ss_descriptors) { + speed_desc = f->ss_descriptors; + want_comp_desc = 1; + break; + } + incomplete_desc = true; } - /* else: Fall trough */ + fallthrough; case USB_SPEED_HIGH: if (gadget_is_dualspeed(g)) { - speed_desc = f->hs_descriptors; - break; + if (f->hs_descriptors) { + speed_desc = f->hs_descriptors; + break; + } + incomplete_desc = true; } - /* else: fall through */ + fallthrough; default: speed_desc = f->fs_descriptors; } + + cdev = get_gadget_data(g); + if (incomplete_desc) + WARNING(cdev, + "%s doesn't hold the descriptors for current speed\n", + f->name); + + /* find correct alternate setting descriptor */ + for_each_desc(speed_desc, d_spd, USB_DT_INTERFACE) { + int_desc = (struct usb_interface_descriptor *)*d_spd; + + if (int_desc->bAlternateSetting == alt) { + speed_desc = d_spd; + goto intf_found; + } + } + return -EIO; + +intf_found: /* find descriptors */ - for_each_ep_desc(speed_desc, d_spd) { + for_each_desc(speed_desc, d_spd, USB_DT_ENDPOINT) { chosen_desc = (struct usb_endpoint_descriptor *)*d_spd; if (chosen_desc->bEndpointAddress == _ep->address) goto ep_found; @@ -132,7 +233,12 @@ ep_found: _ep->desc = chosen_desc; _ep->comp_desc = NULL; _ep->maxburst = 0; - _ep->mult = 0; + _ep->mult = 1; + + if (g->speed == USB_SPEED_HIGH && (usb_endpoint_xfer_isoc(_ep->desc) || + usb_endpoint_xfer_int(_ep->desc))) + _ep->mult = usb_endpoint_maxp_mult(_ep->desc); + if (!want_comp_desc) return 0; @@ -145,11 +251,12 @@ ep_found: (comp_desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP)) return -EIO; _ep->comp_desc = comp_desc; - if (g->speed == USB_SPEED_SUPER) { + if (g->speed >= USB_SPEED_SUPER) { switch (usb_endpoint_type(_ep->desc)) { case USB_ENDPOINT_XFER_ISOC: /* mult: bits 1:0 of bmAttributes */ - _ep->mult = comp_desc->bmAttributes & 0x3; + _ep->mult = (comp_desc->bmAttributes & 0x3) + 1; + fallthrough; case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_INT: _ep->maxburst = comp_desc->bMaxBurst + 1; @@ -163,6 +270,32 @@ ep_found: } return 0; } +EXPORT_SYMBOL_GPL(config_ep_by_speed_and_alt); + +/** + * config_ep_by_speed() - configures the given endpoint + * according to gadget speed. + * @g: pointer to the gadget + * @f: usb function + * @_ep: the endpoint to configure + * + * Return: error code, 0 on success + * + * This function chooses the right descriptors for a given + * endpoint according to gadget speed and saves it in the + * endpoint desc field. If the endpoint already has a descriptor + * assigned to it - overwrites it with currently corresponding + * descriptor. The endpoint maxpacket field is updated according + * to the chosen descriptor. + * Note: the supplied function should hold all the descriptors + * for supported speeds + */ +int config_ep_by_speed(struct usb_gadget *g, + struct usb_function *f, + struct usb_ep *_ep) +{ + return config_ep_by_speed_and_alt(g, f, _ep, 0); +} EXPORT_SYMBOL_GPL(config_ep_by_speed); /** @@ -194,6 +327,12 @@ int usb_add_function(struct usb_configuration *config, function->config = config; list_add_tail(&function->list, &config->functions); + if (function->bind_deactivated) { + value = usb_function_deactivate(function); + if (value) + goto done; + } + /* REVISIT *require* function->bind? */ if (function->bind) { value = function->bind(config, function); @@ -215,6 +354,8 @@ int usb_add_function(struct usb_configuration *config, config->highspeed = true; if (!config->superspeed && function->ss_descriptors) config->superspeed = true; + if (!config->superspeed_plus && function->ssp_descriptors) + config->superspeed_plus = true; done: if (value) @@ -233,6 +374,9 @@ void usb_remove_function(struct usb_configuration *c, struct usb_function *f) list_del(&f->list); if (f->unbind) f->unbind(c, f); + + if (f->bind_deactivated) + usb_function_activate(f); } EXPORT_SYMBOL_GPL(usb_remove_function); @@ -258,13 +402,20 @@ EXPORT_SYMBOL_GPL(usb_remove_function); int usb_function_deactivate(struct usb_function *function) { struct usb_composite_dev *cdev = function->config->cdev; + unsigned long flags; int status = 0; - if (cdev->deactivations == 0) - status = usb_gadget_disconnect(cdev->gadget); + spin_lock_irqsave(&cdev->lock, flags); + + if (cdev->deactivations == 0) { + spin_unlock_irqrestore(&cdev->lock, flags); + status = usb_gadget_deactivate(cdev->gadget); + spin_lock_irqsave(&cdev->lock, flags); + } if (status == 0) cdev->deactivations++; + spin_unlock_irqrestore(&cdev->lock, flags); return status; } EXPORT_SYMBOL_GPL(usb_function_deactivate); @@ -282,16 +433,23 @@ EXPORT_SYMBOL_GPL(usb_function_deactivate); int usb_function_activate(struct usb_function *function) { struct usb_composite_dev *cdev = function->config->cdev; + unsigned long flags; int status = 0; + spin_lock_irqsave(&cdev->lock, flags); + if (WARN_ON(cdev->deactivations == 0)) status = -EINVAL; else { cdev->deactivations--; - if (cdev->deactivations == 0) - status = usb_gadget_connect(cdev->gadget); + if (cdev->deactivations == 0) { + spin_unlock_irqrestore(&cdev->lock, flags); + status = usb_gadget_activate(cdev->gadget); + spin_lock_irqsave(&cdev->lock, flags); + } } + spin_unlock_irqrestore(&cdev->lock, flags); return status; } EXPORT_SYMBOL_GPL(usb_function_activate); @@ -338,18 +496,20 @@ static u8 encode_bMaxPower(enum usb_device_speed speed, { unsigned val; - if (c->MaxPower) + if (c->MaxPower || (c->bmAttributes & USB_CONFIG_ATT_SELFPOWER)) val = c->MaxPower; else - val = usb_gadget_vbus_draw_ma; + val = CONFIG_USB_GADGET_VBUS_DRAW; if (!val) return 0; - switch (speed) { - case USB_SPEED_SUPER: - return DIV_ROUND_UP(val, 8); - default: - return DIV_ROUND_UP(val, 2); - } + if (speed < USB_SPEED_SUPER) + return min(val, 500U) / 2; + else + /* + * USB 3.x supports up to 900mA, but since 900 isn't divisible + * by 8 the integral division will effectively cap to 896mA. + */ + return min(val, 900U) / 8; } static int config_buf(struct usb_configuration *config, @@ -387,17 +547,7 @@ static int config_buf(struct usb_configuration *config, list_for_each_entry(f, &config->functions, list) { struct usb_descriptor_header **descriptors; - switch (speed) { - case USB_SPEED_SUPER: - descriptors = f->ss_descriptors; - break; - case USB_SPEED_HIGH: - descriptors = f->hs_descriptors; - break; - default: - descriptors = f->fs_descriptors; - } - + descriptors = function_descriptors(f, speed); if (!descriptors) continue; status = usb_descriptor_fillbuf(next, len, @@ -417,10 +567,11 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) { struct usb_gadget *gadget = cdev->gadget; struct usb_configuration *c; + struct list_head *pos; u8 type = w_value >> 8; enum usb_device_speed speed = USB_SPEED_UNKNOWN; - if (gadget->speed == USB_SPEED_SUPER) + if (gadget->speed >= USB_SPEED_SUPER) speed = gadget->speed; else if (gadget_is_dualspeed(gadget)) { int hs = 0; @@ -435,9 +586,26 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) /* This is a lookup by config *INDEX* */ w_value &= 0xff; - list_for_each_entry(c, &cdev->configs, list) { + + pos = &cdev->configs; + c = cdev->os_desc_config; + if (c) + goto check_config; + + while ((pos = pos->next) != &cdev->configs) { + c = list_entry(pos, typeof(*c), list); + + /* skip OS Descriptors config which is handled separately */ + if (c == cdev->os_desc_config) + continue; + +check_config: /* ignore configs that won't work at this speed */ switch (speed) { + case USB_SPEED_SUPER_PLUS: + if (!c->superspeed_plus) + continue; + break; case USB_SPEED_SUPER: if (!c->superspeed) continue; @@ -465,18 +633,24 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type) unsigned count = 0; int hs = 0; int ss = 0; + int ssp = 0; if (gadget_is_dualspeed(gadget)) { if (gadget->speed == USB_SPEED_HIGH) hs = 1; if (gadget->speed == USB_SPEED_SUPER) ss = 1; + if (gadget->speed == USB_SPEED_SUPER_PLUS) + ssp = 1; if (type == USB_DT_DEVICE_QUALIFIER) hs = !hs; } list_for_each_entry(c, &cdev->configs, list) { /* ignore configs that won't work at this speed */ - if (ss) { + if (ssp) { + if (!c->superspeed_plus) + continue; + } else if (ss) { if (!c->superspeed) continue; } else if (hs) { @@ -503,9 +677,9 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type) static int bos_desc(struct usb_composite_dev *cdev) { struct usb_ext_cap_descriptor *usb_ext; - struct usb_ss_cap_descriptor *ss_cap; 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; @@ -513,45 +687,173 @@ 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. */ - usb_ext = cdev->req->buf + le16_to_cpu(bos->wTotalLength); - bos->bNumDeviceCaps++; - le16_add_cpu(&bos->wTotalLength, USB_DT_USB_EXT_CAP_SIZE); - 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); + if (cdev->gadget->lpm_capable) { + usb_ext = cdev->req->buf + le16_to_cpu(bos->wTotalLength); + bos->bNumDeviceCaps++; + le16_add_cpu(&bos->wTotalLength, USB_DT_USB_EXT_CAP_SIZE); + 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 | besl); + } /* * The Superspeed USB Capability descriptor shall be implemented by all * SuperSpeed devices. */ - ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength); - bos->bNumDeviceCaps++; - le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE); - ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE; - ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY; - ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE; - ss_cap->bmAttributes = 0; /* LTM is not supported yet */ - ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION | - USB_FULL_SPEED_OPERATION | - USB_HIGH_SPEED_OPERATION | - USB_5GBPS_OPERATION); - ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION; + if (gadget_is_superspeed(cdev->gadget)) { + struct usb_ss_cap_descriptor *ss_cap; + + ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength); + bos->bNumDeviceCaps++; + le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE); + ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE; + ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY; + ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE; + ss_cap->bmAttributes = 0; /* LTM is not supported yet */ + ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION | + USB_FULL_SPEED_OPERATION | + USB_HIGH_SPEED_OPERATION | + USB_5GBPS_OPERATION); + ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION; + ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat; + ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat; + } - /* Get Controller configuration */ - if (cdev->gadget->ops->get_config_params) - cdev->gadget->ops->get_config_params(&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); + /* The SuperSpeedPlus USB Device Capability descriptor */ + if (gadget_is_superspeed_plus(cdev->gadget)) { + struct usb_ssp_cap_descriptor *ssp_cap; + u8 ssac = 1; + u8 ssic; + int i; + + if (cdev->gadget->max_ssp_rate == USB_SSP_GEN_2x2) + ssac = 3; + + /* + * Paired RX and TX sublink speed attributes share + * the same SSID. + */ + ssic = (ssac + 1) / 2 - 1; + + ssp_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength); + bos->bNumDeviceCaps++; + + le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SSP_CAP_SIZE(ssac)); + ssp_cap->bLength = USB_DT_USB_SSP_CAP_SIZE(ssac); + ssp_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY; + ssp_cap->bDevCapabilityType = USB_SSP_CAP_TYPE; + ssp_cap->bReserved = 0; + ssp_cap->wReserved = 0; + + ssp_cap->bmAttributes = + cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_ATTRIBS, ssac) | + FIELD_PREP(USB_SSP_SUBLINK_SPEED_IDS, ssic)); + + ssp_cap->wFunctionalitySupport = + cpu_to_le16(FIELD_PREP(USB_SSP_MIN_SUBLINK_SPEED_ATTRIBUTE_ID, 0) | + FIELD_PREP(USB_SSP_MIN_RX_LANE_COUNT, 1) | + FIELD_PREP(USB_SSP_MIN_TX_LANE_COUNT, 1)); + + /* + * Use 1 SSID if the gadget supports up to gen2x1 or not + * specified: + * - SSID 0 for symmetric RX/TX sublink speed of 10 Gbps. + * + * Use 1 SSID if the gadget supports up to gen1x2: + * - SSID 0 for symmetric RX/TX sublink speed of 5 Gbps. + * + * Use 2 SSIDs if the gadget supports up to gen2x2: + * - SSID 0 for symmetric RX/TX sublink speed of 5 Gbps. + * - SSID 1 for symmetric RX/TX sublink speed of 10 Gbps. + */ + for (i = 0; i < ssac + 1; i++) { + u8 ssid; + u8 mantissa; + u8 type; + + ssid = i >> 1; + + if (cdev->gadget->max_ssp_rate == USB_SSP_GEN_2x1 || + cdev->gadget->max_ssp_rate == USB_SSP_GEN_UNKNOWN) + mantissa = 10; + else + mantissa = 5 << ssid; + + if (i % 2) + type = USB_SSP_SUBLINK_SPEED_ST_SYM_TX; + else + type = USB_SSP_SUBLINK_SPEED_ST_SYM_RX; + + ssp_cap->bmSublinkSpeedAttr[i] = + cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_SSID, ssid) | + FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSE, + USB_SSP_SUBLINK_SPEED_LSE_GBPS) | + FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST, type) | + FIELD_PREP(USB_SSP_SUBLINK_SPEED_LP, + USB_SSP_SUBLINK_SPEED_LP_SSP) | + FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSM, mantissa)); + } + } + + /* The WebUSB Platform Capability descriptor */ + if (cdev->use_webusb) { + struct usb_plat_dev_cap_descriptor *webusb_cap; + struct usb_webusb_cap_data *webusb_cap_data; + guid_t webusb_uuid = WEBUSB_UUID; + + webusb_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength); + webusb_cap_data = (struct usb_webusb_cap_data *) webusb_cap->CapabilityData; + bos->bNumDeviceCaps++; + le16_add_cpu(&bos->wTotalLength, + USB_DT_USB_PLAT_DEV_CAP_SIZE(USB_WEBUSB_CAP_DATA_SIZE)); + + webusb_cap->bLength = USB_DT_USB_PLAT_DEV_CAP_SIZE(USB_WEBUSB_CAP_DATA_SIZE); + webusb_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY; + webusb_cap->bDevCapabilityType = USB_PLAT_DEV_CAP_TYPE; + webusb_cap->bReserved = 0; + export_guid(webusb_cap->UUID, &webusb_uuid); + + if (cdev->bcd_webusb_version != 0) + webusb_cap_data->bcdVersion = cpu_to_le16(cdev->bcd_webusb_version); + else + webusb_cap_data->bcdVersion = WEBUSB_VERSION_1_00; + + webusb_cap_data->bVendorCode = cdev->b_webusb_vendor_code; + + if (strnlen(cdev->landing_page, sizeof(cdev->landing_page)) > 0) + webusb_cap_data->iLandingPage = WEBUSB_LANDING_PAGE_PRESENT; + else + webusb_cap_data->iLandingPage = WEBUSB_LANDING_PAGE_NOT_PRESENT; } - ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat; - ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat; return le16_to_cpu(bos->wTotalLength); } @@ -579,45 +881,41 @@ static void reset_config(struct usb_composite_dev *cdev) { struct usb_function *f; - if (cdev->in_reset_config) - return; - - cdev->in_reset_config = 1; - DBG(cdev, "reset config\n"); list_for_each_entry(f, &cdev->config->functions, list) { if (f->disable) f->disable(f); + bitmap_zero(f->endpoints, 32); } cdev->config = NULL; cdev->delayed_status = 0; - cdev->in_reset_config = 0; } static int set_config(struct usb_composite_dev *cdev, const struct usb_ctrlrequest *ctrl, unsigned number) { struct usb_gadget *gadget = cdev->gadget; - struct usb_configuration *c = NULL; + struct usb_configuration *c = NULL, *iter; int result = -EINVAL; unsigned power = gadget_is_otg(gadget) ? 8 : 100; int tmp; if (number) { - list_for_each_entry(c, &cdev->configs, list) { - if (c->bConfigurationValue == number) { - /* - * We disable the FDs of the previous - * configuration only if the new configuration - * is a valid one - */ - if (cdev->config) - reset_config(cdev); - result = 0; - break; - } + list_for_each_entry(iter, &cdev->configs, list) { + if (iter->bConfigurationValue != number) + continue; + /* + * We disable the FDs of the previous + * configuration only if the new configuration + * is a valid one + */ + if (cdev->config) + reset_config(cdev); + c = iter; + result = 0; + break; } if (result < 0) goto done; @@ -628,12 +926,13 @@ static int set_config(struct usb_composite_dev *cdev, } INFO(cdev, "%s config #%d: %s\n", - usb_speed_string(gadget->speed), - number, c ? c->label : "unconfigured"); + usb_speed_string(gadget->speed), + number, c ? c->label : "unconfigured"); if (!c) goto done; + usb_gadget_set_state(gadget, USB_STATE_CONFIGURED); cdev->config = c; /* Initialize all interfaces by setting them to altsetting zero. */ @@ -650,16 +949,7 @@ static int set_config(struct usb_composite_dev *cdev, * function's setup callback instead of the current * configuration's setup callback. */ - switch (gadget->speed) { - case USB_SPEED_SUPER: - descriptors = f->ss_descriptors; - break; - case USB_SPEED_HIGH: - descriptors = f->hs_descriptors; - break; - default: - descriptors = f->fs_descriptors; - } + descriptors = function_descriptors(f, gadget->speed); for (; *descriptors; ++descriptors) { struct usb_endpoint_descriptor *ep; @@ -694,8 +984,21 @@ static int set_config(struct usb_composite_dev *cdev, } /* when we return, be sure our power usage is valid */ - power = c->MaxPower ? c->MaxPower : usb_gadget_vbus_draw_ma; + if (c->MaxPower || (c->bmAttributes & USB_CONFIG_ATT_SELFPOWER)) + power = c->MaxPower; + else + power = CONFIG_USB_GADGET_VBUS_DRAW; + + if (gadget->speed < USB_SPEED_SUPER) + power = min(power, 500U); + else + power = min(power, 900U); done: + if (power <= USB_SELF_POWER_VBUS_MAX_DRAW) + usb_gadget_set_selfpowered(gadget); + else + usb_gadget_clear_selfpowered(gadget); + usb_gadget_vbus_draw(gadget, power); if (result >= 0 && cdev->delayed_status) result = USB_GADGET_DELAYED_STATUS; @@ -779,8 +1082,9 @@ int usb_add_config(struct usb_composite_dev *cdev, } else { unsigned i; - DBG(cdev, "cfg %d/%p speeds:%s%s%s\n", + DBG(cdev, "cfg %d/%p speeds:%s%s%s%s\n", config->bConfigurationValue, config, + config->superspeed_plus ? " superplus" : "", config->superspeed ? " super" : "", config->highspeed ? " high" : "", config->fullspeed @@ -799,9 +1103,7 @@ int usb_add_config(struct usb_composite_dev *cdev, } } - /* set_alt(), or next bind(), sets up - * ep->driver_data as needed. - */ + /* set_alt(), or next bind(), sets up ep->claimed as needed */ usb_ep_autoconfig_reset(cdev->gadget); done: @@ -820,12 +1122,8 @@ static void remove_config(struct usb_composite_dev *cdev, f = list_first_entry(&config->functions, struct usb_function, list); - list_del(&f->list); - if (f->unbind) { - DBG(cdev, "unbind function '%s'/%p\n", f->name, f); - f->unbind(config, f); - /* may free memory for "f" */ - } + + usb_remove_function(config, f); } list_del(&config->list); if (config->unbind) { @@ -847,9 +1145,15 @@ static void remove_config(struct usb_composite_dev *cdev, void usb_remove_config(struct usb_composite_dev *cdev, struct usb_configuration *config) { + unsigned long flags; + + spin_lock_irqsave(&cdev->lock, flags); + if (cdev->config == config) reset_config(cdev); + spin_unlock_irqrestore(&cdev->lock, flags); + remove_config(cdev, config); } @@ -857,7 +1161,7 @@ void usb_remove_config(struct usb_composite_dev *cdev, /* We support strings in multiple languages ... string descriptor zero * says which languages are supported. The typical case will be that - * only one language (probably English) is used, with I18N handled on + * only one language (probably English) is used, with i18n handled on * the host side. */ @@ -870,7 +1174,7 @@ static void collect_langs(struct usb_gadget_strings **sp, __le16 *buf) while (*sp) { s = *sp; language = cpu_to_le16(s->language); - for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) { + for (tmp = buf; *tmp && tmp < &buf[USB_MAX_STRING_LEN]; tmp++) { if (*tmp == language) goto repeat; } @@ -910,7 +1214,7 @@ static int get_string(struct usb_composite_dev *cdev, struct usb_function *f; int len; - /* Yes, not only is USB's I18N support probably more than most + /* Yes, not only is USB's i18n support probably more than most * folk will ever care about ... also, it's all supported here. * (Except for UTF8 support for Unicode's "Astral Planes".) */ @@ -945,7 +1249,7 @@ static int get_string(struct usb_composite_dev *cdev, collect_langs(sp, s->wData); } - for (len = 0; len <= 126 && s->wData[len]; len++) + for (len = 0; len <= USB_MAX_STRING_LEN && s->wData[len]; len++) continue; if (!len) return -EINVAL; @@ -954,6 +1258,19 @@ static int get_string(struct usb_composite_dev *cdev, return s->bLength; } + if (cdev->use_os_string && language == 0 && id == OS_STRING_IDX) { + struct usb_os_string *b = buf; + b->bLength = sizeof(*b); + b->bDescriptorType = USB_DT_STRING; + compiletime_assert( + sizeof(b->qwSignature) == sizeof(cdev->qw_sign), + "qwSignature size must be equal to qw_sign"); + memcpy(&b->qwSignature, cdev->qw_sign, sizeof(b->qwSignature)); + b->bMS_VendorCode = cdev->b_vendor_code; + b->bPad = 0; + return sizeof(*b); + } + list_for_each_entry(uc, &cdev->gstrings, list) { struct usb_gadget_strings **sp; @@ -1017,7 +1334,7 @@ int usb_string_id(struct usb_composite_dev *cdev) EXPORT_SYMBOL_GPL(usb_string_id); /** - * usb_string_ids() - allocate unused string IDs in batch + * usb_string_ids_tab() - allocate unused string IDs in batch * @cdev: the device whose string descriptor IDs are being allocated * @str: an array of usb_string objects to assign numbers to * Context: single threaded during gadget setup @@ -1109,11 +1426,11 @@ static struct usb_gadget_string_container *copy_gadget_strings( * This function will create a deep copy of usb_gadget_strings and usb_string * and attach it to the cdev. The actual string (usb_string.s) will not be * copied but only a referenced will be made. The struct usb_gadget_strings - * array may contain multiple languges and should be NULL terminated. + * array may contain multiple languages and should be NULL terminated. * The ->language pointer of each struct usb_gadget_strings has to contain the * same amount of entries. * For instance: sp[0] is en-US, sp[1] is es-ES. It is expected that the first - * usb_string entry of es-ES containts the translation of the first usb_string + * usb_string entry of es-ES contains the translation of the first usb_string * entry of en-US. Therefore both entries become the same id assign. */ struct usb_string *usb_gstrings_attach(struct usb_composite_dev *cdev, @@ -1215,6 +1532,8 @@ static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req) if (cdev->req == req) cdev->setup_pending = false; + else if (cdev->os_desc_req == req) + cdev->os_desc_pending = false; else WARN(1, "unknown request %p\n", req); } @@ -1228,6 +1547,8 @@ static int composite_ep0_queue(struct usb_composite_dev *cdev, if (ret == 0) { if (cdev->req == req) cdev->setup_pending = true; + else if (cdev->os_desc_req == req) + cdev->os_desc_pending = true; else WARN(1, "unknown request %p\n", req); } @@ -1235,6 +1556,156 @@ static int composite_ep0_queue(struct usb_composite_dev *cdev, return ret; } +static int count_ext_compat(struct usb_configuration *c) +{ + int i, res; + + res = 0; + for (i = 0; i < c->next_interface_id; ++i) { + struct usb_function *f; + int j; + + f = c->interface[i]; + for (j = 0; j < f->os_desc_n; ++j) { + struct usb_os_desc *d; + + if (i != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d && d->ext_compat_id) + ++res; + } + } + BUG_ON(res > 255); + return res; +} + +static int fill_ext_compat(struct usb_configuration *c, u8 *buf) +{ + int i, count; + + count = 16; + buf += 16; + for (i = 0; i < c->next_interface_id; ++i) { + struct usb_function *f; + int j; + + f = c->interface[i]; + for (j = 0; j < f->os_desc_n; ++j) { + struct usb_os_desc *d; + + if (i != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d && d->ext_compat_id) { + *buf++ = i; + *buf++ = 0x01; + memcpy(buf, d->ext_compat_id, 16); + buf += 22; + } else { + ++buf; + *buf = 0x01; + buf += 23; + } + count += 24; + if (count + 24 >= USB_COMP_EP0_OS_DESC_BUFSIZ) + return count; + } + } + + return count; +} + +static int count_ext_prop(struct usb_configuration *c, int interface) +{ + struct usb_function *f; + int j; + + f = c->interface[interface]; + for (j = 0; j < f->os_desc_n; ++j) { + struct usb_os_desc *d; + + if (interface != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d && d->ext_compat_id) + return d->ext_prop_count; + } + return 0; +} + +static int len_ext_prop(struct usb_configuration *c, int interface) +{ + struct usb_function *f; + struct usb_os_desc *d; + int j, res; + + res = 10; /* header length */ + f = c->interface[interface]; + for (j = 0; j < f->os_desc_n; ++j) { + if (interface != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d) + return min(res + d->ext_prop_len, 4096); + } + return res; +} + +static int fill_ext_prop(struct usb_configuration *c, int interface, u8 *buf) +{ + struct usb_function *f; + struct usb_os_desc *d; + struct usb_os_desc_ext_prop *ext_prop; + int j, count, n, ret; + + f = c->interface[interface]; + count = 10; /* header length */ + buf += 10; + for (j = 0; j < f->os_desc_n; ++j) { + if (interface != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d) + list_for_each_entry(ext_prop, &d->ext_prop, entry) { + n = ext_prop->data_len + + ext_prop->name_len + 14; + if (count + n >= USB_COMP_EP0_OS_DESC_BUFSIZ) + return count; + usb_ext_prop_put_size(buf, n); + usb_ext_prop_put_type(buf, ext_prop->type); + ret = usb_ext_prop_put_name(buf, ext_prop->name, + ext_prop->name_len); + if (ret < 0) + return ret; + switch (ext_prop->type) { + case USB_EXT_PROP_UNICODE: + case USB_EXT_PROP_UNICODE_ENV: + case USB_EXT_PROP_UNICODE_LINK: + usb_ext_prop_put_unicode(buf, ret, + ext_prop->data, + ext_prop->data_len); + break; + case USB_EXT_PROP_BINARY: + usb_ext_prop_put_binary(buf, ret, + ext_prop->data, + ext_prop->data_len); + break; + case USB_EXT_PROP_LE32: + /* not implemented */ + case USB_EXT_PROP_BE32: + /* not implemented */ + default: + return -EINVAL; + } + buf += n; + count += n; + } + } + + return count; +} + /* * The setup() callback implements all the ep0 functionality that's * not handled lower down, in hardware or the hardware driver(like @@ -1254,8 +1725,21 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); struct usb_function *f = NULL; + struct usb_function *iter; u8 endp; + if (w_length > USB_COMP_EP0_BUFSIZ) { + if (ctrl->bRequestType & USB_DIR_IN) { + /* Cast away the const, we are going to overwrite on purpose. */ + __le16 *temp = (__le16 *)&ctrl->wLength; + + *temp = cpu_to_le16(USB_COMP_EP0_BUFSIZ); + w_length = USB_COMP_EP0_BUFSIZ; + } else { + goto done; + } + } + /* partial re-init of the response message; the function or the * gadget might need to intercept e.g. a control-OUT completion * when we delegate to it. @@ -1266,6 +1750,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) req->length = 0; gadget->ep0->driver_data = cdev; + /* + * Don't let non-standard requests match any of the cases below + * by accident. + */ + if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) + goto unknown; + switch (ctrl->bRequest) { /* we handle all standard USB descriptors */ @@ -1281,11 +1772,16 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) cdev->gadget->ep0->maxpacket; if (gadget_is_superspeed(gadget)) { if (gadget->speed >= USB_SPEED_SUPER) { - cdev->desc.bcdUSB = cpu_to_le16(0x0300); + cdev->desc.bcdUSB = cpu_to_le16(0x0320); cdev->desc.bMaxPacketSize0 = 9; } else { cdev->desc.bcdUSB = cpu_to_le16(0x0210); } + } else { + if (gadget->lpm_capable || cdev->use_webusb) + cdev->desc.bcdUSB = cpu_to_le16(0x0201); + else + cdev->desc.bcdUSB = cpu_to_le16(0x0200); } value = min(w_length, (u16) sizeof cdev->desc); @@ -1303,7 +1799,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) if (!gadget_is_dualspeed(gadget) || gadget->speed >= USB_SPEED_SUPER) break; - /* FALLTHROUGH */ + fallthrough; case USB_DT_CONFIG: value = config_desc(cdev, w_value); if (value >= 0) @@ -1316,11 +1812,32 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) value = min(w_length, (u16) value); break; case USB_DT_BOS: - if (gadget_is_superspeed(gadget)) { + if (gadget_is_superspeed(gadget) || + gadget->lpm_capable || cdev->use_webusb) { value = bos_desc(cdev); value = min(w_length, (u16) value); } break; + case USB_DT_OTG: + if (gadget_is_otg(gadget)) { + struct usb_configuration *config; + int otg_desc_len = 0; + + if (cdev->config) + config = cdev->config; + else + config = list_first_entry( + &cdev->configs, + struct usb_configuration, list); + if (!config) + goto done; + + otg_desc_len += sizeof(struct usb_otg_descriptor); + + value = min_t(int, w_length, otg_desc_len); + memcpy(req->buf, config->descriptors[0], value); + } + break; } break; @@ -1336,7 +1853,9 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) else VDBG(cdev, "HNP inactive\n"); } + spin_lock(&cdev->lock); value = set_config(cdev, ctrl, w_value); + spin_unlock(&cdev->lock); break; case USB_REQ_GET_CONFIGURATION: if (ctrl->bRequestType != USB_DIR_IN) @@ -1348,9 +1867,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) value = min(w_length, (u16) 1); break; - /* function drivers must handle get/set altsetting; if there's - * no get() method, we know only altsetting zero works. - */ + /* function drivers must handle get/set altsetting */ case USB_REQ_SET_INTERFACE: if (ctrl->bRequestType != USB_RECIP_INTERFACE) goto unknown; @@ -1359,8 +1876,16 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) f = cdev->config->interface[intf]; if (!f) break; - if (w_value && !f->set_alt) + + /* + * If there's no get_alt() method, we know only altsetting zero + * works. There is no need to check if set_alt() is not NULL + * as we check this in usb_add_function(). + */ + if (w_value && !f->get_alt) break; + + spin_lock(&cdev->lock); value = f->set_alt(f, w_index, w_value); if (value == USB_GADGET_DELAYED_STATUS) { DBG(cdev, @@ -1370,6 +1895,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) DBG(cdev, "delayed_status count %d\n", cdev->delayed_status); } + spin_unlock(&cdev->lock); break; case USB_REQ_GET_INTERFACE: if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) @@ -1386,15 +1912,24 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) *((u8 *)req->buf) = value; value = min(w_length, (u16) 1); break; - - /* - * USB 3.0 additions: - * Function driver should handle get_status request. If such cb - * wasn't supplied we respond with default value = 0 - * Note: function driver should supply such cb only for the first - * interface of the function - */ case USB_REQ_GET_STATUS: + if (gadget_is_otg(gadget) && gadget->hnp_polling_support && + (w_index == OTG_STS_SELECTOR)) { + if (ctrl->bRequestType != (USB_DIR_IN | + USB_RECIP_DEVICE)) + goto unknown; + *((u8 *)req->buf) = gadget->host_request_flag; + value = 1; + break; + } + + /* + * USB 3.0 additions: + * Function driver should handle get_status request. If such cb + * wasn't supplied we respond with default value = 0 + * Note: function driver should supply such cb only for the + * first interface of the function + */ if (!gadget_is_superspeed(gadget)) goto unknown; if (ctrl->bRequestType != (USB_DIR_IN | USB_RECIP_INTERFACE)) @@ -1443,6 +1978,116 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) break; default: unknown: + /* + * OS descriptors handling + */ + if (cdev->use_os_string && cdev->os_desc_config && + (ctrl->bRequestType & USB_TYPE_VENDOR) && + ctrl->bRequest == cdev->b_vendor_code) { + struct usb_configuration *os_desc_cfg; + u8 *buf; + int interface; + int count = 0; + + req = cdev->os_desc_req; + req->context = cdev; + req->complete = composite_setup_complete; + buf = req->buf; + os_desc_cfg = cdev->os_desc_config; + w_length = min_t(u16, w_length, USB_COMP_EP0_OS_DESC_BUFSIZ); + memset(buf, 0, w_length); + buf[5] = 0x01; + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + if (w_index != 0x4 || (w_value >> 8)) + break; + buf[6] = w_index; + /* Number of ext compat interfaces */ + count = count_ext_compat(os_desc_cfg); + buf[8] = count; + count *= 24; /* 24 B/ext compat desc */ + count += 16; /* header */ + put_unaligned_le32(count, buf); + value = w_length; + if (w_length > 0x10) { + value = fill_ext_compat(os_desc_cfg, buf); + value = min_t(u16, w_length, value); + } + break; + case USB_RECIP_INTERFACE: + if (w_index != 0x5 || (w_value >> 8)) + break; + interface = w_value & 0xFF; + if (interface >= MAX_CONFIG_INTERFACES || + !os_desc_cfg->interface[interface]) + break; + buf[6] = w_index; + count = count_ext_prop(os_desc_cfg, + interface); + put_unaligned_le16(count, buf + 8); + count = len_ext_prop(os_desc_cfg, + interface); + put_unaligned_le32(count, buf); + value = w_length; + if (w_length > 0x0A) { + value = fill_ext_prop(os_desc_cfg, + interface, buf); + if (value >= 0) + value = min_t(u16, w_length, value); + } + break; + } + + goto check_value; + } + + /* + * WebUSB URL descriptor handling, following: + * https://wicg.github.io/webusb/#device-requests + */ + if (cdev->use_webusb && + ctrl->bRequestType == (USB_DIR_IN | USB_TYPE_VENDOR) && + w_index == WEBUSB_GET_URL && + w_value == WEBUSB_LANDING_PAGE_PRESENT && + ctrl->bRequest == cdev->b_webusb_vendor_code) { + unsigned int landing_page_length; + unsigned int landing_page_offset; + struct webusb_url_descriptor *url_descriptor = + (struct webusb_url_descriptor *)cdev->req->buf; + + url_descriptor->bDescriptorType = WEBUSB_URL_DESCRIPTOR_TYPE; + + if (strncasecmp(cdev->landing_page, "https://", 8) == 0) { + landing_page_offset = 8; + url_descriptor->bScheme = WEBUSB_URL_SCHEME_HTTPS; + } else if (strncasecmp(cdev->landing_page, "http://", 7) == 0) { + landing_page_offset = 7; + url_descriptor->bScheme = WEBUSB_URL_SCHEME_HTTP; + } else { + landing_page_offset = 0; + url_descriptor->bScheme = WEBUSB_URL_SCHEME_NONE; + } + + landing_page_length = strnlen(cdev->landing_page, + sizeof(url_descriptor->URL) + - WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset); + + if (ctrl->wLength < WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + + landing_page_length) + landing_page_length = ctrl->wLength + - WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset; + + memcpy(url_descriptor->URL, + cdev->landing_page + landing_page_offset, + landing_page_length - landing_page_offset); + url_descriptor->bLength = landing_page_length + - landing_page_offset + WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH; + + value = url_descriptor->bLength; + + goto check_value; + } + VDBG(cdev, "non-core control req%02x.%02x v%04x i%04x l%d\n", ctrl->bRequestType, ctrl->bRequest, @@ -1451,11 +2096,22 @@ unknown: /* functions always handle their interfaces and endpoints... * punt other recipients (other, WUSB, ...) to the current * configuration code. - * - * REVISIT it could make sense to let the composite device - * take such requests too, if that's ever needed: to work - * in config 0, etc. */ + if (cdev->config) { + list_for_each_entry(f, &cdev->config->functions, list) + if (f->req_match && + f->req_match(f, ctrl, false)) + goto try_fun_setup; + } else { + struct usb_configuration *c; + list_for_each_entry(c, &cdev->configs, list) + list_for_each_entry(f, &c->functions, list) + if (f->req_match && + f->req_match(f, ctrl, true)) + goto try_fun_setup; + } + f = NULL; + switch (ctrl->bRequestType & USB_RECIP_MASK) { case USB_RECIP_INTERFACE: if (!cdev->config || intf >= MAX_CONFIG_INTERFACES) @@ -1464,16 +2120,18 @@ unknown: break; case USB_RECIP_ENDPOINT: + if (!cdev->config) + break; endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f); - list_for_each_entry(f, &cdev->config->functions, list) { - if (test_bit(endp, f->endpoints)) + list_for_each_entry(iter, &cdev->config->functions, list) { + if (test_bit(endp, iter->endpoints)) { + f = iter; break; + } } - if (&f->list == &cdev->config->functions) - f = NULL; break; } - +try_fun_setup: if (f && f->setup) value = f->setup(f, ctrl); else { @@ -1501,9 +2159,11 @@ unknown: goto done; } +check_value: /* respond with data transfer before status phase? */ if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) { req->length = value; + req->context = cdev; req->zero = value < w_length; value = composite_ep0_queue(cdev, req); if (value < 0) { @@ -1522,17 +2182,38 @@ done: return value; } -void composite_disconnect(struct usb_gadget *gadget) +static void __composite_disconnect(struct usb_gadget *gadget) { struct usb_composite_dev *cdev = get_gadget_data(gadget); + unsigned long flags; /* REVISIT: should we have config and device level * disconnect callbacks? */ + spin_lock_irqsave(&cdev->lock, flags); + cdev->suspended = 0; if (cdev->config) reset_config(cdev); if (cdev->driver->disconnect) cdev->driver->disconnect(cdev); + spin_unlock_irqrestore(&cdev->lock, flags); +} + +void composite_disconnect(struct usb_gadget *gadget) +{ + usb_gadget_vbus_draw(gadget, 0); + __composite_disconnect(gadget); +} + +void composite_reset(struct usb_gadget *gadget) +{ + /* + * Section 1.4.13 Standard Downstream Port of the USB battery charging + * specification v1.2 states that a device connected on a SDP shall only + * draw at max 100mA while in a connected, but unconfigured state. + */ + usb_gadget_vbus_draw(gadget, 100); + __composite_disconnect(gadget); } /*-------------------------------------------------------------------------*/ @@ -1540,6 +2221,8 @@ void composite_disconnect(struct usb_gadget *gadget) static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver) { struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_gadget_strings *gstr = cdev->driver->strings[0]; + struct usb_string *dev_str = gstr->strings; /* composite_disconnect() must already have been called * by the underlying peripheral controller driver! @@ -1559,6 +2242,9 @@ static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver) composite_dev_cleanup(cdev); + if (dev_str[USB_GADGET_MANUFACTURER_IDX].s == cdev->def_manufacturer) + dev_str[USB_GADGET_MANUFACTURER_IDX].s = ""; + kfree(cdev->def_manufacturer); kfree(cdev); set_gadget_data(gadget, NULL); @@ -1623,6 +2309,7 @@ int composite_dev_prepare(struct usb_composite_driver *composite, goto fail; cdev->req->complete = composite_setup_complete; + cdev->req->context = cdev; gadget->ep0->driver_data = cdev; cdev->driver = composite; @@ -1632,7 +2319,7 @@ int composite_dev_prepare(struct usb_composite_driver *composite, * more than 100mA from USB must report itself as bus-powered in * the GetStatus(DEVICE) call. */ - if (usb_gadget_vbus_draw_ma <= USB_SELF_POWER_VBUS_MAX_DRAW) + if (CONFIG_USB_GADGET_VBUS_DRAW <= USB_SELF_POWER_VBUS_MAX_DRAW) usb_gadget_set_selfpowered(gadget); /* interface and string IDs start at zero via kzalloc. @@ -1648,21 +2335,73 @@ fail: return ret; } +int composite_os_desc_req_prepare(struct usb_composite_dev *cdev, + struct usb_ep *ep0) +{ + int ret = 0; + + cdev->os_desc_req = usb_ep_alloc_request(ep0); + if (!cdev->os_desc_req) { + ret = -ENOMEM; + goto end; + } + + cdev->os_desc_req->buf = kmalloc(USB_COMP_EP0_OS_DESC_BUFSIZ, + GFP_KERNEL); + if (!cdev->os_desc_req->buf) { + ret = -ENOMEM; + usb_ep_free_request(ep0, cdev->os_desc_req); + goto end; + } + cdev->os_desc_req->context = cdev; + cdev->os_desc_req->complete = composite_setup_complete; +end: + return ret; +} + void composite_dev_cleanup(struct usb_composite_dev *cdev) { struct usb_gadget_string_container *uc, *tmp; + struct usb_ep *ep, *tmp_ep; list_for_each_entry_safe(uc, tmp, &cdev->gstrings, list) { list_del(&uc->list); kfree(uc); } + if (cdev->os_desc_req) { + if (cdev->os_desc_pending) + usb_ep_dequeue(cdev->gadget->ep0, cdev->os_desc_req); + + kfree(cdev->os_desc_req->buf); + cdev->os_desc_req->buf = NULL; + usb_ep_free_request(cdev->gadget->ep0, cdev->os_desc_req); + cdev->os_desc_req = NULL; + } if (cdev->req) { if (cdev->setup_pending) usb_ep_dequeue(cdev->gadget->ep0, cdev->req); + kfree(cdev->req->buf); + cdev->req->buf = NULL; usb_ep_free_request(cdev->gadget->ep0, cdev->req); + cdev->req = NULL; } cdev->next_string_id = 0; + + /* + * Some UDC backends have a dynamic EP allocation scheme. + * + * In that case, the dispose() callback is used to notify the + * backend that the EPs are no longer in use. + * + * Note: The UDC backend can remove the EP from the ep_list as + * a result, so we need to use the _safe list iterator. + */ + list_for_each_entry_safe(ep, tmp_ep, + &cdev->gadget->ep_list, ep_list) { + if (ep->ops->dispose) + ep->ops->dispose(ep); + } } static int composite_bind(struct usb_gadget *gadget, @@ -1676,6 +2415,7 @@ static int composite_bind(struct usb_gadget *gadget, if (!cdev) return status; + spin_lock_init(&cdev->lock); cdev->gadget = gadget; set_gadget_data(gadget, cdev); INIT_LIST_HEAD(&cdev->configs); @@ -1693,6 +2433,12 @@ static int composite_bind(struct usb_gadget *gadget, if (status < 0) goto fail; + if (cdev->use_os_string) { + status = composite_os_desc_req_prepare(cdev, gadget->ep0); + if (status) + goto fail; + } + update_unchanged_dev_desc(&cdev->desc, composite->dev); /* has userspace failed to provide a serial number? */ @@ -1714,6 +2460,7 @@ static const struct usb_gadget_driver composite_driver_template = { .unbind = composite_unbind, .setup = composite_setup, + .reset = composite_reset, .disconnect = composite_disconnect, }; @@ -1749,8 +2496,9 @@ int usb_composite_probe(struct usb_composite_driver *driver) gadget_driver->function = (char *) driver->name; gadget_driver->driver.name = driver->name; gadget_driver->max_speed = driver->max_speed; + gadget_driver->match_existing_only = true; - return usb_gadget_probe_driver(gadget_driver); + return usb_gadget_register_driver(gadget_driver); } EXPORT_SYMBOL_GPL(usb_composite_probe); @@ -1781,8 +2529,10 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev) { int value; struct usb_request *req = cdev->req; + unsigned long flags; DBG(cdev, "%s\n", __func__); + spin_lock_irqsave(&cdev->lock, flags); if (cdev->delayed_status == 0) { WARN(cdev, "%s: Unexpected call\n", __func__); @@ -1790,6 +2540,7 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev) } else if (--cdev->delayed_status == 0) { DBG(cdev, "%s: Completing delayed status\n", __func__); req->length = 0; + req->context = cdev; value = composite_ep0_queue(cdev, req); if (value < 0) { DBG(cdev, "ep_queue --> %d\n", value); @@ -1797,12 +2548,14 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev) composite_setup_complete(cdev->gadget->ep0, req); } } + + spin_unlock_irqrestore(&cdev->lock, flags); } EXPORT_SYMBOL_GPL(usb_composite_setup_continue); static char *composite_default_mfr(struct usb_gadget *gadget) { - return basprintf("barebox %s", gadget->name); + return basprintf("barebox with %s", gadget->name); } void usb_composite_overwrite_options(struct usb_composite_dev *cdev, diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index b463f79faa..27e4dda52f 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -1,19 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * usb/gadget/config.c -- simplify building config descriptors * * Copyright (C) 2003 David Brownell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <common.h> -#include <usb/ch9.h> -#include <usb/gadget.h> -#include <usb/composite.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h> /** * usb_descriptor_fillbuf - fill buffer with descriptors @@ -156,7 +152,8 @@ EXPORT_SYMBOL_GPL(usb_copy_descriptors); int usb_assign_descriptors(struct usb_function *f, struct usb_descriptor_header **fs, struct usb_descriptor_header **hs, - struct usb_descriptor_header **ss) + struct usb_descriptor_header **ss, + struct usb_descriptor_header **ssp) { struct usb_gadget *g = f->config->cdev->gadget; @@ -175,6 +172,11 @@ int usb_assign_descriptors(struct usb_function *f, if (!f->ss_descriptors) goto err; } + if (ssp && gadget_is_superspeed_plus(g)) { + f->ssp_descriptors = usb_copy_descriptors(ssp); + if (!f->ssp_descriptors) + goto err; + } return 0; err: usb_free_all_descriptors(f); diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index ced568921b..ff16abaf12 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -1,12 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * epautoconf.c -- endpoint autoconfiguration for usb gadget drivers * * Copyright (C) 2004 David Brownell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <init.h> @@ -15,10 +11,10 @@ #include <linux/ctype.h> #include <asm/byteorder.h> -#include <usb/ch9.h> -#include <usb/gadget.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> -#include "gadget_chips.h" +#define gadget_is_pxa(g) (!strcmp("pxa25x_udc", (g)->name)) /* * This should work with endpoints from controller drivers sharing the @@ -186,18 +182,6 @@ ep_matches ( return 1; } -static struct usb_ep * -find_ep (struct usb_gadget *gadget, const char *name) -{ - struct usb_ep *ep; - - list_for_each_entry (ep, &gadget->ep_list, ep_list) { - if (0 == strcmp (ep->name, name)) - return ep; - } - return NULL; -} - /** * usb_ep_autoconfig_ss() - choose an endpoint matching the ep * descriptor and ep companion descriptor @@ -253,34 +237,6 @@ struct usb_ep *usb_ep_autoconfig_ss( type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; - /* First, apply chip-specific "best usage" knowledge. - * This might make a good usb_gadget_ops hook ... - */ - if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) { - /* ep-e, ep-f are PIO with only 64 byte fifos */ - ep = find_ep (gadget, "ep-e"); - if (ep && ep_matches(gadget, ep, desc, ep_comp)) - goto found_ep; - ep = find_ep (gadget, "ep-f"); - if (ep && ep_matches(gadget, ep, desc, ep_comp)) - goto found_ep; - - } else if (gadget_is_goku (gadget)) { - if (USB_ENDPOINT_XFER_INT == type) { - /* single buffering is enough */ - ep = find_ep(gadget, "ep3-bulk"); - if (ep && ep_matches(gadget, ep, desc, ep_comp)) - goto found_ep; - } else if (USB_ENDPOINT_XFER_BULK == type - && (USB_DIR_IN & desc->bEndpointAddress)) { - /* DMA may be available */ - ep = find_ep(gadget, "ep2-bulk"); - if (ep && ep_matches(gadget, ep, desc, - ep_comp)) - goto found_ep; - } - } - /* Second, look at endpoints until an unclaimed one looks usable */ list_for_each_entry (ep, &gadget->ep_list, ep_list) { if (ep_matches(gadget, ep, desc, ep_comp)) diff --git a/drivers/usb/gadget/fsl_udc_pbl.c b/drivers/usb/gadget/fsl_udc_pbl.c deleted file mode 100644 index 978adf0667..0000000000 --- a/drivers/usb/gadget/fsl_udc_pbl.c +++ /dev/null @@ -1,210 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include <common.h> -#include <usb/ch9.h> -#include <soc/fsl/fsl_udc.h> -#include <mach/imx8mm-regs.h> - -static void fsl_queue_td(struct usb_dr_device *dr, struct ep_td_struct *dtd, - int ep_is_in) -{ - int ep_index = 0; - int i = ep_index * 2 + ep_is_in; - u32 bitmask; - volatile struct ep_queue_head *dQH = - (void *)(unsigned long)readl(&dr->endpointlistaddr); - unsigned long td_dma = (unsigned long)dtd; - - dQH = &dQH[i]; - - bitmask = ep_is_in ? (1 << (ep_index + 16)) : (1 << (ep_index)); - - dQH->next_dtd_ptr = cpu_to_le32(td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK); - - dQH->size_ioc_int_sts &= cpu_to_le32(~(EP_QUEUE_HEAD_STATUS_ACTIVE - | EP_QUEUE_HEAD_STATUS_HALT)); - - writel(bitmask, &dr->endpointprime); -} - -static struct ep_td_struct dtd_data __attribute__((aligned(64))); -static struct ep_td_struct dtd_status __attribute__((aligned(64))); - -static int fsl_ep_queue(struct usb_dr_device *dr, struct ep_td_struct *dtd, - void *buf, int len) -{ - u32 swap_temp; - - memset(dtd, 0, sizeof(*dtd)); - - /* Clear reserved field */ - swap_temp = cpu_to_le32(dtd->size_ioc_sts); - swap_temp &= ~DTD_RESERVED_FIELDS; - dtd->size_ioc_sts = cpu_to_le32(swap_temp); - - swap_temp = (unsigned long)buf; - dtd->buff_ptr0 = cpu_to_le32(swap_temp); - dtd->buff_ptr1 = cpu_to_le32(swap_temp + 0x1000); - dtd->buff_ptr2 = cpu_to_le32(swap_temp + 0x2000); - dtd->buff_ptr3 = cpu_to_le32(swap_temp + 0x3000); - dtd->buff_ptr4 = cpu_to_le32(swap_temp + 0x4000); - - /* Fill in the transfer size; set active bit */ - swap_temp = ((len << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE) | DTD_IOC; - - writel(cpu_to_le32(swap_temp), &dtd->size_ioc_sts); - - dtd->next_td_ptr = cpu_to_le32(DTD_NEXT_TERMINATE); - - fsl_queue_td(dr, dtd, len ? 0 : 1); - - return 0; -} - -enum state { - state_init = 0, - state_expect_command, - state_transfer_data, - state_complete, -}; - -#define MAX_TRANSFER_SIZE 2048 - -static enum state state; -static uint8_t databuf[MAX_TRANSFER_SIZE] __attribute__((aligned(64))); -static int actual; -static int to_transfer; -static void *image; - -static void tripwire_handler(struct usb_dr_device *dr, u8 ep_num) -{ - uint32_t val; - struct ep_queue_head *qh; - struct ep_queue_head *dQH = (void *)(unsigned long)readl(&dr->endpointlistaddr); - struct usb_ctrlrequest *ctrl; - - qh = &dQH[ep_num * 2]; - - val = readl(&dr->endptsetupstat); - val |= 1 << ep_num; - writel(val, &dr->endptsetupstat); - - do { - val = readl(&dr->usbcmd); - val |= USB_CMD_SUTW; - writel(val, &dr->usbcmd); - - ctrl = (void *)qh->setup_buffer; - if ((ctrl->wValue & 0xff) == 1) - state = state_expect_command; - - } while (!(readl(&dr->usbcmd) & USB_CMD_SUTW)); - - val = readl(&dr->usbcmd); - val &= ~USB_CMD_SUTW; - writel(val, &dr->usbcmd); - - fsl_ep_queue(dr, &dtd_data, databuf, MAX_TRANSFER_SIZE); -} - -static void dtd_complete_irq(struct usb_dr_device *dr) -{ - struct ep_td_struct *dtd = &dtd_data; - u32 bit_pos; - int len; - - /* Clear the bits in the register */ - bit_pos = readl(&dr->endptcomplete); - writel(bit_pos, &dr->endptcomplete); - - if (!(bit_pos & 1)) - return; - - len = MAX_TRANSFER_SIZE - - (le32_to_cpu(dtd->size_ioc_sts) >> DTD_LENGTH_BIT_POS); - - if (state == state_expect_command) { - state = state_transfer_data; - to_transfer = databuf[8] << 24 | - databuf[9] << 16 | - databuf[10] << 8 | - databuf[11]; - } else { - memcpy(image + actual, &databuf[1], len - 1); - actual += len - 1; - to_transfer -= len - 1; - - if (to_transfer == 0) - state = state_complete; - } - - fsl_ep_queue(dr, &dtd_status, NULL, 0); -} - -static int usb_irq(struct usb_dr_device *dr) -{ - uint32_t irq_src = readl(&dr->usbsts); - - irq_src &= ~0x80; - - if (!irq_src) - return -EAGAIN; - - /* Clear notification bits */ - writel(irq_src, &dr->usbsts); - - /* USB Interrupt */ - if (irq_src & USB_STS_INT) { - /* Setup package, we only support ep0 as control ep */ - if (readl(&dr->endptsetupstat) & EP_SETUP_STATUS_EP0) - tripwire_handler(dr, 0); - - /* completion of dtd */ - if (readl(&dr->endptcomplete)) - dtd_complete_irq(dr); - } - - if (state == state_complete) - return 0; - else - return -EAGAIN; -} - -int imx_barebox_load_usb(void __iomem *dr, void *dest) -{ - int ret; - - image = dest; - - while (1) { - ret = usb_irq(dr); - if (!ret) - break; - } - - return 0; -} - -int imx_barebox_start_usb(void __iomem *dr, void *dest) -{ - void __noreturn (*bb)(void); - int ret; - - ret = imx_barebox_load_usb(dr, dest); - if (ret) - return ret; - - printf("Downloading complete, start barebox\n"); - bb = dest; - bb(); -} - -int imx8mm_barebox_load_usb(void *dest) -{ - return imx_barebox_load_usb(IOMEM(MX8MM_USB1_BASE_ADDR), dest); -} - -int imx8mm_barebox_start_usb(void *dest) -{ - return imx_barebox_start_usb(IOMEM(MX8MM_USB1_BASE_ADDR), dest); -} diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile new file mode 100644 index 0000000000..de306b929f --- /dev/null +++ b/drivers/usb/gadget/function/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_USB_GADGET_SERIAL) += u_serial.o f_serial.o f_acm.o +obj-$(CONFIG_USB_GADGET_DFU) += dfu.o +obj-$(CONFIG_USB_GADGET_FASTBOOT) += f_fastboot.o +obj-$(CONFIG_USB_GADGET_MASS_STORAGE) += f_mass_storage.o storage_common.o diff --git a/drivers/usb/gadget/dfu.c b/drivers/usb/gadget/function/dfu.c index 7e23fa3157..4628b36b17 100644 --- a/drivers/usb/gadget/dfu.c +++ b/drivers/usb/gadget/function/dfu.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * (C) 2007 by OpenMoko, Inc. * Author: Harald Welte <laforge@openmoko.org> @@ -5,17 +6,6 @@ * based on existing SAM7DFU code from OpenPCD: * (C) Copyright 2006 by Harald Welte <hwelte@hmw-consulting.de> * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * * TODO: * - make NAND support reasonably self-contained and put in apropriate * ifdefs @@ -32,18 +22,19 @@ * checking? * - make 'dnstate' attached to 'struct usb_device_instance' */ +#define pr_fmt(fmt) "dfu: " fmt #include <dma.h> #include <asm/byteorder.h> -#include <usb/composite.h> +#include <linux/usb/composite.h> #include <linux/types.h> #include <linux/list.h> -#include <usb/gadget.h> +#include <linux/usb/gadget.h> #include <linux/stat.h> #include <libfile.h> #include <linux/err.h> -#include <usb/ch9.h> -#include <usb/dfu.h> +#include <linux/usb/ch9.h> +#include <linux/usb/dfu.h> #include <config.h> #include <common.h> #include <malloc.h> @@ -54,6 +45,7 @@ #include <fs.h> #include <ioctl.h> #include <linux/mtd/mtd-abi.h> +#include <work.h> #define USB_DT_DFU 0x21 @@ -153,6 +145,7 @@ struct f_dfu { u8 dfu_state; u8 dfu_status; struct usb_request *dnreq; + struct work_queue wq; }; static inline struct f_dfu *func_to_dfu(struct usb_function *f) @@ -173,6 +166,192 @@ static struct usb_gadget_strings *dfu_strings[] = { }; static void dn_complete(struct usb_ep *ep, struct usb_request *req); +static void up_complete(struct usb_ep *ep, struct usb_request *req); +static void dfu_cleanup(struct f_dfu *dfu); + +struct dfu_work { + struct work_struct work; + struct f_dfu *dfu; + void (*task)(struct dfu_work *dw); + size_t len; + uint8_t *rbuf; + uint8_t wbuf[CONFIG_USBD_DFU_XFER_SIZE]; +}; + +static void dfu_do_work(struct work_struct *w) +{ + struct dfu_work *dw = container_of(w, struct dfu_work, work); + struct f_dfu *dfu = dw->dfu; + + if (dfu->dfu_state != DFU_STATE_dfuERROR && dfu->dfu_status == DFU_STATUS_OK) + dw->task(dw); + else + pr_debug("skip work\n"); + + free(dw); +} + +static void dfu_work_cancel(struct work_struct *w) +{ + struct dfu_work *dw = container_of(w, struct dfu_work, work); + + free(dw); +} + +static void dfu_do_write(struct dfu_work *dw) +{ + struct f_dfu *dfu = dw->dfu; + ssize_t size, wlen = dw->len; + ssize_t ret; + + pr_debug("do write\n"); + + if (prog_erase && (dfu_written + wlen) > dfu_erased) { + size = roundup(wlen, dfu_mtdinfo.erasesize); + ret = erase(dfufd, size, dfu_erased); + dfu_erased += size; + if (ret && ret != -ENOSYS) { + perror("erase"); + dfu->dfu_state = DFU_STATE_dfuERROR; + dfu->dfu_status = DFU_STATUS_errERASE; + return; + } + } + + dfu_written += wlen; + ret = write(dfufd, dw->wbuf, wlen); + if (ret < wlen) { + perror("write"); + dfu->dfu_state = DFU_STATE_dfuERROR; + dfu->dfu_status = DFU_STATUS_errWRITE; + } +} + +static void dfu_do_read(struct dfu_work *dw) +{ + struct f_dfu *dfu = dw->dfu; + struct usb_composite_dev *cdev = dfu->func.config->cdev; + ssize_t size, rlen = dw->len; + + pr_debug("do read\n"); + + size = read(dfufd, dfu->dnreq->buf, rlen); + dfu->dnreq->length = size; + if (size < 0) { + perror("read"); + dfu->dnreq->length = 0; + dfu->dfu_state = DFU_STATE_dfuERROR; + dfu->dfu_status = DFU_STATUS_errFILE; + } else if (size < rlen) { + /* this is the last chunk, go to IDLE and close file */ + dfu_cleanup(dfu); + } + + dfu->dnreq->complete = up_complete; + usb_ep_queue(cdev->gadget->ep0, dfu->dnreq); +} + +static void dfu_do_open_dnload(struct dfu_work *dw) +{ + struct f_dfu *dfu = dw->dfu; + int ret; + + pr_debug("do open dnload\n"); + + if (dfu_file_entry->flags & FILE_LIST_FLAG_SAFE) { + dfufd = open(DFU_TEMPFILE, O_WRONLY | O_CREAT); + } else { + unsigned flags = O_WRONLY; + + if (dfu_file_entry->flags & FILE_LIST_FLAG_CREATE) + flags |= O_CREAT | O_TRUNC; + + dfufd = open(dfu_file_entry->filename, flags); + } + + if (dfufd < 0) { + perror("open"); + dfu->dfu_state = DFU_STATE_dfuERROR; + dfu->dfu_status = DFU_STATUS_errFILE; + return; + } + + if (!(dfu_file_entry->flags & FILE_LIST_FLAG_SAFE)) { + ret = ioctl(dfufd, MEMGETINFO, &dfu_mtdinfo); + if (!ret) /* file is on a mtd device */ + prog_erase = 1; + } +} + +static void dfu_do_open_upload(struct dfu_work *dw) +{ + struct f_dfu *dfu = dw->dfu; + + pr_debug("do open upload\n"); + + dfufd = open(dfu_file_entry->filename, O_RDONLY); + if (dfufd < 0) { + perror("open"); + dfu->dfu_state = DFU_STATE_dfuERROR; + dfu->dfu_status = DFU_STATUS_errFILE; + } +} + +static void dfu_do_close(struct dfu_work *dw) +{ + struct stat s; + + pr_debug("do close\n"); + + if (dfufd > 0) { + close(dfufd); + dfufd = -EINVAL; + } + + if (!stat(DFU_TEMPFILE, &s)) + unlink(DFU_TEMPFILE); + + dw->dfu->dfu_state = DFU_STATE_dfuIDLE; +} + +static void dfu_do_copy(struct dfu_work *dw) +{ + struct f_dfu *dfu = dw->dfu; + unsigned flags = O_WRONLY; + int ret, fd; + + pr_debug("do copy\n"); + + if (dfu_file_entry->flags & FILE_LIST_FLAG_CREATE) + flags |= O_CREAT | O_TRUNC; + + fd = open(dfu_file_entry->filename, flags); + if (fd < 0) { + perror("open"); + dfu->dfu_state = DFU_STATE_dfuERROR; + dfu->dfu_status = DFU_STATUS_errERASE; + return; + } + + ret = erase(fd, ERASE_SIZE_ALL, 0); + close(fd); + if (ret && ret != -ENOSYS) { + perror("erase"); + dfu->dfu_state = DFU_STATE_dfuERROR; + dfu->dfu_status = DFU_STATUS_errERASE; + return; + } + + ret = copy_file(DFU_TEMPFILE, dfu_file_entry->filename, 0); + if (ret) { + pr_err("copy file failed\n"); + dfu->dfu_state = DFU_STATE_dfuERROR; + dfu->dfu_status = DFU_STATUS_errWRITE; + return; + } + + dfu->dfu_state = DFU_STATE_dfuIDLE; +} static int dfu_bind(struct usb_configuration *c, struct usb_function *f) @@ -181,7 +360,7 @@ dfu_bind(struct usb_configuration *c, struct usb_function *f) struct usb_descriptor_header **header; struct usb_interface_descriptor *desc; struct file_list_entry *fentry; - struct f_dfu *dfu = container_of(f, struct f_dfu, func); + struct f_dfu *dfu = func_to_dfu(f); int i; int status; struct usb_string *us; @@ -209,7 +388,7 @@ dfu_bind(struct usb_configuration *c, struct usb_function *f) dfu->dnreq = usb_ep_alloc_request(c->cdev->gadget->ep0); if (!dfu->dnreq) { - printf("usb_ep_alloc_request failed\n"); + pr_err("usb_ep_alloc_request failed\n"); status = -ENOMEM; goto out; } @@ -223,6 +402,10 @@ dfu_bind(struct usb_configuration *c, struct usb_function *f) goto out; } + dfu->wq.fn = dfu_do_work; + dfu->wq.cancel = dfu_work_cancel; + wq_register(&dfu->wq); + /* allocate instance-specific interface IDs, and patch descriptors */ status = usb_interface_id(c, f); if (status < 0) @@ -244,7 +427,7 @@ dfu_bind(struct usb_configuration *c, struct usb_function *f) header[i] = (struct usb_descriptor_header *) &usb_dfu_func; header[i + 1] = NULL; - status = usb_assign_descriptors(f, header, header, NULL); + status = usb_assign_descriptors(f, header, header, header, header); free(desc); free(header); @@ -254,8 +437,7 @@ dfu_bind(struct usb_configuration *c, struct usb_function *f) i = 0; file_list_for_each_entry(dfu_files, fentry) { - printf("dfu: register alt%d(%s) with device %s\n", - i, fentry->name, fentry->filename); + pr_info("register alt%d(%s) with device %s\n", i, fentry->name, fentry->filename); i++; } @@ -278,6 +460,8 @@ dfu_unbind(struct usb_configuration *c, struct usb_function *f) dfu_file_entry = NULL; dfudetach = 0; + wq_unregister(&dfu->wq); + usb_free_all_descriptors(f); dma_free(dfu->dnreq->buf); @@ -301,6 +485,20 @@ static int dfu_set_alt(struct usb_function *f, unsigned intf, unsigned alt) return -EINVAL; } +static int dfu_get_alt(struct usb_function *f, unsigned intf) +{ + struct file_list_entry *fentry; + int i = 0; + + file_list_for_each_entry(dfu_files, fentry) { + if (fentry == dfu_file_entry) + return i; + i++; + } + + return -EINVAL; +} + static int dfu_status(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { struct f_dfu *dfu = func_to_dfu(f); @@ -320,47 +518,55 @@ static int dfu_status(struct usb_function *f, const struct usb_ctrlrequest *ctrl static void dfu_cleanup(struct f_dfu *dfu) { - struct stat s; + struct dfu_work *dw; + + pr_debug("dfu cleanup\n"); memset(&dfu_mtdinfo, 0, sizeof(dfu_mtdinfo)); dfu_written = 0; dfu_erased = 0; prog_erase = 0; - if (dfufd > 0) { - close(dfufd); - dfufd = -EINVAL; - } + dfu->dfu_state = DFU_STATE_dfuIDLE; + dfu->dfu_status = DFU_STATUS_OK; - if (!stat(DFU_TEMPFILE, &s)) - unlink(DFU_TEMPFILE); + dw = xzalloc(sizeof(*dw)); + dw->dfu = dfu; + dw->task = dfu_do_close; + wq_queue_work(&dfu->wq, &dw->work); } static void dn_complete(struct usb_ep *ep, struct usb_request *req) { struct f_dfu *dfu = req->context; - loff_t size; - int ret; + struct dfu_work *dw; + + dw = xzalloc(sizeof(*dw)); + dw->dfu = dfu; + dw->task = dfu_do_write; + dw->len = min_t(unsigned int, req->length, CONFIG_USBD_DFU_XFER_SIZE); + memcpy(dw->wbuf, req->buf, dw->len); + wq_queue_work(&dfu->wq, &dw->work); +} - if (prog_erase && (dfu_written + req->length) > dfu_erased) { - size = roundup(req->length, dfu_mtdinfo.erasesize); - ret = erase(dfufd, size, dfu_erased); - dfu_erased += size; - if (ret && ret != -ENOSYS) { - perror("erase"); - dfu->dfu_status = DFU_STATUS_errERASE; - dfu_cleanup(dfu); - return; - } - } +static int handle_manifest(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct f_dfu *dfu = func_to_dfu(f); + struct dfu_work *dw; - dfu_written += req->length; - ret = write(dfufd, req->buf, req->length); - if (ret < (int)req->length) { - perror("write"); - dfu->dfu_status = DFU_STATUS_errWRITE; - dfu_cleanup(dfu); + if (dfu_file_entry->flags & FILE_LIST_FLAG_SAFE) { + dw = xzalloc(sizeof(*dw)); + dw->dfu = dfu; + dw->task = dfu_do_copy; + wq_queue_work(&dfu->wq, &dw->work); } + + dw = xzalloc(sizeof(*dw)); + dw->dfu = dfu; + dw->task = dfu_do_close; + wq_queue_work(&dfu->wq, &dw->work); + + return 0; } static int handle_dnload(struct usb_function *f, const struct usb_ctrlrequest *ctrl) @@ -370,12 +576,8 @@ static int handle_dnload(struct usb_function *f, const struct usb_ctrlrequest *c u16 w_length = le16_to_cpu(ctrl->wLength); if (w_length == 0) { - if (dfu_file_entry->flags & FILE_LIST_FLAG_SAFE) { - dfu->dfu_state = DFU_STATE_dfuMANIFEST; - } else { - dfu->dfu_state = DFU_STATE_dfuIDLE; - dfu_cleanup(dfu); - } + handle_manifest(f, ctrl); + dfu->dfu_state = DFU_STATE_dfuMANIFEST; return 0; } @@ -386,53 +588,6 @@ static int handle_dnload(struct usb_function *f, const struct usb_ctrlrequest *c return 0; } -static int handle_manifest(struct usb_function *f, const struct usb_ctrlrequest *ctrl) -{ - struct f_dfu *dfu = func_to_dfu(f); - int ret; - - dfu->dfu_state = DFU_STATE_dfuIDLE; - - if (dfu_file_entry->flags & FILE_LIST_FLAG_SAFE) { - int fd; - unsigned flags = O_WRONLY; - - if (dfu_file_entry->flags & FILE_LIST_FLAG_CREATE) - flags |= O_CREAT | O_TRUNC; - - fd = open(dfu_file_entry->filename, flags); - if (fd < 0) { - perror("open"); - dfu->dfu_status = DFU_STATUS_errERASE; - ret = -EINVAL; - goto out; - } - - ret = erase(fd, ERASE_SIZE_ALL, 0); - close(fd); - if (ret && ret != -ENOSYS) { - dfu->dfu_status = DFU_STATUS_errERASE; - perror("erase"); - goto out; - } - - ret = copy_file(DFU_TEMPFILE, dfu_file_entry->filename, 0); - if (ret) { - printf("copy file failed\n"); - ret = -EINVAL; - goto out; - } - } - - return 0; - -out: - dfu->dfu_status = DFU_STATUS_errWRITE; - dfu->dfu_state = DFU_STATE_dfuERROR; - dfu_cleanup(dfu); - return ret; -} - static void up_complete(struct usb_ep *ep, struct usb_request *req) { } @@ -440,28 +595,22 @@ static void up_complete(struct usb_ep *ep, struct usb_request *req) static int handle_upload(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { struct f_dfu *dfu = func_to_dfu(f); - struct usb_composite_dev *cdev = f->config->cdev; + struct dfu_work *dw; u16 w_length = le16_to_cpu(ctrl->wLength); - int len; - len = read(dfufd, dfu->dnreq->buf, w_length); - - dfu->dnreq->length = len; - if (len < w_length) { - dfu_cleanup(dfu); - dfu->dfu_state = DFU_STATE_dfuIDLE; - } - - dfu->dnreq->complete = up_complete; - usb_ep_queue(cdev->gadget->ep0, dfu->dnreq); + dw = xzalloc(sizeof(*dw)); + dw->dfu = dfu; + dw->task = dfu_do_read; + dw->len = w_length; + dw->rbuf = dfu->dnreq->buf; + wq_queue_work(&dfu->wq, &dw->work); return 0; } static void dfu_abort(struct f_dfu *dfu) { - dfu->dfu_state = DFU_STATE_dfuIDLE; - dfu->dfu_status = DFU_STATUS_OK; + wq_cancel_work(&dfu->wq); dfu_cleanup(dfu); } @@ -474,7 +623,7 @@ static int dfu_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) int value = -EOPNOTSUPP; int w_length = le16_to_cpu(ctrl->wLength); int w_value = le16_to_cpu(ctrl->wValue); - int ret; + struct dfu_work *dw; if (ctrl->bRequestType == USB_DIR_IN && ctrl->bRequest == USB_REQ_GET_DESCRIPTOR && (w_value >> 8) == 0x21) { @@ -500,46 +649,28 @@ static int dfu_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) value = -EINVAL; goto out; } - debug("dfu: starting download to %s\n", dfu_file_entry->filename); - if (dfu_file_entry->flags & FILE_LIST_FLAG_SAFE) { - dfufd = open(DFU_TEMPFILE, O_WRONLY | O_CREAT); - } else { - unsigned flags = O_WRONLY; - - if (dfu_file_entry->flags & FILE_LIST_FLAG_CREATE) - flags |= O_CREAT | O_TRUNC; - - dfufd = open(dfu_file_entry->filename, flags); - } - - if (dfufd < 0) { - dfu->dfu_state = DFU_STATE_dfuERROR; - perror("open"); - goto out; - } - - if (!(dfu_file_entry->flags & FILE_LIST_FLAG_SAFE)) { - ret = ioctl(dfufd, MEMGETINFO, &dfu_mtdinfo); - if (!ret) /* file is on a mtd device */ - prog_erase = 1; - } + pr_debug("starting download to %s\n", dfu_file_entry->filename); + dw = xzalloc(sizeof(*dw)); + dw->dfu = dfu; + dw->task = dfu_do_open_dnload; + wq_queue_work(&dfu->wq, &dw->work); value = handle_dnload(f, ctrl); dfu->dfu_state = DFU_STATE_dfuDNLOAD_IDLE; return 0; case USB_REQ_DFU_UPLOAD: dfu->dfu_state = DFU_STATE_dfuUPLOAD_IDLE; - debug("dfu: starting upload from %s\n", dfu_file_entry->filename); + pr_debug("starting upload from %s\n", dfu_file_entry->filename); if (!(dfu_file_entry->flags & FILE_LIST_FLAG_READBACK)) { dfu->dfu_state = DFU_STATE_dfuERROR; goto out; } - dfufd = open(dfu_file_entry->filename, O_RDONLY); - if (dfufd < 0) { - dfu->dfu_state = DFU_STATE_dfuERROR; - perror("open"); - goto out; - } + + dw = xzalloc(sizeof(*dw)); + dw->dfu = dfu; + dw->task = dfu_do_open_upload; + wq_queue_work(&dfu->wq, &dw->work); + handle_upload(f, ctrl); return 0; case USB_REQ_DFU_ABORT: @@ -606,6 +737,7 @@ static int dfu_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) } break; case DFU_STATE_dfuERROR: + wq_cancel_work(&dfu->wq); switch (ctrl->bRequest) { case USB_REQ_DFU_GETSTATUS: value = dfu_status(f, ctrl); @@ -629,11 +761,8 @@ static int dfu_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) case DFU_STATE_dfuMANIFEST_SYNC: switch (ctrl->bRequest) { case USB_REQ_DFU_GETSTATUS: + dfu->dfu_state = DFU_STATE_dfuMANIFEST; value = dfu_status(f, ctrl); - if (dfu_file_entry->flags & FILE_LIST_FLAG_SAFE) - dfu->dfu_state = DFU_STATE_dfuMANIFEST; - else - dfu->dfu_state = DFU_STATE_dfuIDLE; value = min(value, w_length); break; case USB_REQ_DFU_GETSTATE: @@ -647,10 +776,7 @@ static int dfu_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) } break; case DFU_STATE_dfuMANIFEST: - value = handle_manifest(f, ctrl); - if (dfu->dfu_state != DFU_STATE_dfuIDLE) { - return 0; - } + dfu->dfu_state = DFU_STATE_dfuMANIFEST_SYNC; switch (ctrl->bRequest) { case USB_REQ_DFU_GETSTATUS: value = dfu_status(f, ctrl); @@ -692,178 +818,17 @@ static void dfu_disable(struct usb_function *f) { struct f_dfu *dfu = func_to_dfu(f); - dfu->dfu_state = DFU_STATE_dfuIDLE; - - dfu_cleanup(dfu); + dfu_abort(dfu); } -#define STRING_MANUFACTURER_IDX 0 -#define STRING_PRODUCT_IDX 1 -#define STRING_DESCRIPTION_IDX 2 - -static struct usb_string strings_dev[] = { - [STRING_MANUFACTURER_IDX].s = NULL, - [STRING_PRODUCT_IDX].s = NULL, - [STRING_DESCRIPTION_IDX].s = "USB Device Firmware Upgrade", - { } /* end of list */ -}; - -static struct usb_gadget_strings stringtab_dev = { - .language = 0x0409, /* en-us */ - .strings = strings_dev, -}; - -static struct usb_gadget_strings *dev_strings[] = { - &stringtab_dev, - NULL, -}; - -static void dfu_unbind_config(struct usb_configuration *c) -{ - free(dfu_string_defs); -} - -static struct usb_configuration dfu_config_driver = { - .label = "USB DFU", - .unbind = dfu_unbind_config, - .bConfigurationValue = 1, - .bmAttributes = USB_CONFIG_ATT_SELFPOWER, -}; - -static struct usb_device_descriptor dfu_dev_descriptor = { - .bLength = USB_DT_DEVICE_SIZE, - .bDescriptorType = USB_DT_DEVICE, - .bcdUSB = 0x0100, - .bDeviceClass = 0x00, - .bDeviceSubClass = 0x00, - .bDeviceProtocol = 0x00, -/* .idVendor = dynamic */ -/* .idProduct = dynamic */ - .bcdDevice = 0x0000, - .bNumConfigurations = 0x01, -}; - -static struct usb_function_instance *fi_dfu; -static struct usb_function *f_dfu; - -static int dfu_driver_bind(struct usb_composite_dev *cdev) +int usb_dfu_detached(void) { - struct usb_gadget *gadget = cdev->gadget; - int status; - - if (gadget->vendor_id && gadget->product_id) { - dfu_dev_descriptor.idVendor = cpu_to_le16(gadget->vendor_id); - dfu_dev_descriptor.idProduct = cpu_to_le16(gadget->product_id); - } else { - dfu_dev_descriptor.idVendor = cpu_to_le16(0x1d50); /* Openmoko, Inc */ - dfu_dev_descriptor.idProduct = cpu_to_le16(0x60a2); /* barebox bootloader USB DFU Mode */ - } - - strings_dev[STRING_MANUFACTURER_IDX].s = gadget->manufacturer; - strings_dev[STRING_PRODUCT_IDX].s = gadget->productname; - - status = usb_string_id(cdev); - if (status < 0) - goto fail; - strings_dev[STRING_MANUFACTURER_IDX].id = status; - dfu_dev_descriptor.iManufacturer = status; - - status = usb_string_id(cdev); - if (status < 0) - goto fail; - strings_dev[STRING_PRODUCT_IDX].id = status; - dfu_dev_descriptor.iProduct = status; - - /* config description */ - status = usb_string_id(cdev); - if (status < 0) - goto fail; - strings_dev[STRING_DESCRIPTION_IDX].id = status; - dfu_config_driver.iConfiguration = status; - - status = usb_add_config_only(cdev, &dfu_config_driver); - if (status < 0) - goto fail; - - fi_dfu = usb_get_function_instance("dfu"); - if (IS_ERR(fi_dfu)) { - status = PTR_ERR(fi_dfu); - goto fail; - } - - f_dfu = usb_get_function(fi_dfu); - if (IS_ERR(f_dfu)) { - status = PTR_ERR(f_dfu); - goto fail; - } - - status = usb_add_function(&dfu_config_driver, f_dfu); - if (status) - goto fail; - - return 0; -fail: - return status; -} - -static int dfu_driver_unbind(struct usb_composite_dev *cdev) -{ - usb_put_function(f_dfu); - usb_put_function_instance(fi_dfu); - - return 0; -} - -static struct usb_composite_driver dfu_driver = { - .name = "g_dfu", - .dev = &dfu_dev_descriptor, - .strings = dev_strings, - .max_speed = USB_SPEED_HIGH, - .bind = dfu_driver_bind, - .unbind = dfu_driver_unbind, -}; - -int usb_dfu_register(struct f_dfu_opts *opts) -{ - int ret; - - if (dfu_files) - return -EBUSY; - - dfu_files = opts->files; - - ret = usb_composite_probe(&dfu_driver); - if (ret) - goto out; - - while (1) { - ret = usb_gadget_poll(); - if (ret < 0) - goto out1; - - if (dfudetach) { - ret = 0; - goto out1; - } - - if (ctrlc()) { - ret = -EINTR; - goto out1; - } - } - -out1: - dfudetach = 0; - usb_composite_unregister(&dfu_driver); -out: - dfu_files = NULL; - - return ret; + return dfudetach; } static void dfu_free_func(struct usb_function *f) { - struct f_dfu *dfu = container_of(f, struct f_dfu, func); + struct f_dfu *dfu = func_to_dfu(f); free(dfu); } @@ -879,6 +844,7 @@ static struct usb_function *dfu_alloc_func(struct usb_function_instance *fi) /* descriptors are per-instance copies */ dfu->func.bind = dfu_bind; dfu->func.set_alt = dfu_set_alt; + dfu->func.get_alt = dfu_get_alt; dfu->func.setup = dfu_setup; dfu->func.disable = dfu_disable; dfu->func.unbind = dfu_unbind; diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/function/f_acm.c index 42a2b03ad2..3532fd5892 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * f_acm.c -- USB CDC serial (ACM) function driver * @@ -6,23 +7,18 @@ * Copyright (C) 2008 by Nokia Corporation * Copyright (C) 2009 by Samsung Electronics * Author: Michal Nazarewicz (mina86@mina86.com) - * - * This software is distributed under the terms of the GNU General - * Public License ("GPL") as published by the Free Software Foundation, - * either version 2 of that License or (at your option) any later version. */ /* #define VERBOSE_DEBUG */ #include <common.h> -#include <usb/cdc.h> +#include <linux/usb/cdc.h> #include <linux/err.h> #include <linux/spinlock.h> #include <asm/byteorder.h> -#include <usb/composite.h> +#include <linux/usb/composite.h> #include "u_serial.h" -#include "gadget_chips.h" /* @@ -682,7 +678,7 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) acm_ss_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress; status = usb_assign_descriptors(f, acm_fs_function, acm_hs_function, - acm_ss_function); + acm_ss_function, acm_ss_function); if (status) goto fail; diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/function/f_fastboot.c index f8a9c32530..41450268fc 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/function/f_fastboot.c @@ -21,10 +21,11 @@ #define pr_fmt(fmt) "fastboot: " fmt #include <dma.h> +#include <work.h> #include <unistd.h> #include <progress.h> #include <fastboot.h> -#include <usb/fastboot.h> +#include <linux/usb/fastboot.h> #define FASTBOOT_INTERFACE_CLASS 0xff #define FASTBOOT_INTERFACE_SUB_CLASS 0x42 @@ -38,7 +39,8 @@ struct f_fastboot { /* IN/OUT EP's and corresponding requests */ struct usb_ep *in_ep, *out_ep; - struct usb_request *in_req, *out_req; + struct usb_request *out_req; + struct work_queue wq; }; static inline struct f_fastboot *func_to_fastboot(struct usb_function *f) @@ -107,6 +109,36 @@ static struct usb_descriptor_header *fb_hs_descs[] = { NULL, }; +static struct usb_endpoint_descriptor ss_ep_in = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_endpoint_descriptor ss_ep_out = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor fb_ss_bulk_comp_desc = { + .bLength = sizeof(fb_ss_bulk_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, +}; + +static struct usb_descriptor_header *fb_ss_descs[] = { + (struct usb_descriptor_header *)&interface_desc, + (struct usb_descriptor_header *)&ss_ep_in, + (struct usb_descriptor_header *)&fb_ss_bulk_comp_desc, + (struct usb_descriptor_header *)&ss_ep_out, + (struct usb_descriptor_header *)&fb_ss_bulk_comp_desc, + NULL, +}; + /* * static strings, in UTF-8 */ @@ -132,8 +164,30 @@ static int fastboot_write_usb(struct fastboot *fb, const char *buffer, unsigned int buffer_size); static void fastboot_start_download_usb(struct fastboot *fb); -static void fastboot_complete(struct usb_ep *ep, struct usb_request *req) +struct fastboot_work { + struct work_struct work; + struct f_fastboot *f_fb; + char command[FASTBOOT_MAX_CMD_LEN + 1]; +}; + +static void fastboot_do_work(struct work_struct *w) +{ + struct fastboot_work *fw = container_of(w, struct fastboot_work, work); + struct f_fastboot *f_fb = fw->f_fb; + + fastboot_exec_cmd(&f_fb->fastboot, fw->command); + + memset(f_fb->out_req->buf, 0, EP_BUFFER_SIZE); + usb_ep_queue(f_fb->out_ep, f_fb->out_req); + + free(fw); +} + +static void fastboot_work_cancel(struct work_struct *w) { + struct fastboot_work *fw = container_of(w, struct fastboot_work, work); + + free(fw); } static struct usb_request *fastboot_alloc_request(struct usb_ep *ep) @@ -155,6 +209,17 @@ static struct usb_request *fastboot_alloc_request(struct usb_ep *ep) return req; } +static void fastboot_free_request(struct usb_ep *ep, struct usb_request *req) +{ + free(req->buf); + usb_ep_free_request(ep, req); +} + +static void fastboot_complete(struct usb_ep *ep, struct usb_request *req) +{ + fastboot_free_request(ep, req); +} + static int fastboot_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; @@ -172,80 +237,94 @@ static int fastboot_bind(struct usb_configuration *c, struct usb_function *f) f_fb->fastboot.cmd_exec = opts->common.cmd_exec; f_fb->fastboot.cmd_flash = opts->common.cmd_flash; + f_fb->wq.fn = fastboot_do_work; + f_fb->wq.cancel = fastboot_work_cancel; + + wq_register(&f_fb->wq); + ret = fastboot_generic_init(&f_fb->fastboot, opts->common.export_bbu); if (ret) - return ret; + goto err_wq_unregister; /* DYNAMIC interface numbers assignments */ id = usb_interface_id(c, f); - if (id < 0) - return id; + if (id < 0) { + ret = id; + goto fb_generic_free; + } + interface_desc.bInterfaceNumber = id; id = usb_string_id(c->cdev); - if (id < 0) - return id; + if (id < 0) { + ret = id; + goto fb_generic_free; + } fastboot_string_defs[0].id = id; interface_desc.iInterface = id; us = usb_gstrings_attach(cdev, fastboot_strings, 1); if (IS_ERR(us)) { ret = PTR_ERR(us); - return ret; + goto fb_generic_free; } f_fb->in_ep = usb_ep_autoconfig(gadget, &fs_ep_in); - if (!f_fb->in_ep) - return -ENODEV; + if (!f_fb->in_ep) { + ret = -ENODEV; + goto fb_generic_free; + } f_fb->in_ep->driver_data = c->cdev; f_fb->out_ep = usb_ep_autoconfig(gadget, &fs_ep_out); - if (!f_fb->out_ep) - return -ENODEV; + if (!f_fb->out_ep) { + ret = -ENODEV; + goto fb_generic_free; + } f_fb->out_ep->driver_data = c->cdev; hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress; hs_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress; + ss_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress; + ss_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress; f_fb->out_req = fastboot_alloc_request(f_fb->out_ep); if (!f_fb->out_req) { puts("failed to alloc out req\n"); ret = -EINVAL; - return ret; + goto fb_generic_free; } f_fb->out_req->complete = rx_handler_command; f_fb->out_req->context = f_fb; - f_fb->in_req = fastboot_alloc_request(f_fb->in_ep); - if (!f_fb->in_req) { - puts("failed alloc req in\n"); - ret = -EINVAL; - return ret; - } - f_fb->in_req->complete = fastboot_complete; - - ret = usb_assign_descriptors(f, fb_fs_descs, fb_hs_descs, NULL); + ret = usb_assign_descriptors(f, fb_fs_descs, fb_hs_descs, fb_ss_descs, fb_ss_descs); if (ret) - return ret; + goto err_free_in_req; return 0; + +err_free_in_req: + free(f_fb->out_req->buf); + usb_ep_free_request(f_fb->out_ep, f_fb->out_req); +fb_generic_free: + fastboot_generic_free(&f_fb->fastboot); +err_wq_unregister: + wq_unregister(&f_fb->wq); + + return ret; } static void fastboot_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_fastboot *f_fb = func_to_fastboot(f); - usb_ep_dequeue(f_fb->in_ep, f_fb->in_req); - free(f_fb->in_req->buf); - usb_ep_free_request(f_fb->in_ep, f_fb->in_req); - f_fb->in_req = NULL; - - usb_ep_dequeue(f_fb->out_ep, f_fb->out_req); free(f_fb->out_req->buf); usb_ep_free_request(f_fb->out_ep, f_fb->out_req); f_fb->out_req = NULL; + wq_unregister(&f_fb->wq); + fastboot_generic_free(&f_fb->fastboot); } @@ -313,8 +392,6 @@ static struct usb_function *fastboot_alloc_func(struct usb_function_instance *fi f_fb = xzalloc(sizeof(*f_fb)); - INIT_LIST_HEAD(&f_fb->fastboot.variables); - f_fb->func.name = "fastboot"; f_fb->func.strings = fastboot_strings; f_fb->func.bind = fastboot_bind; @@ -349,28 +426,23 @@ DECLARE_USB_FUNCTION_INIT(fastboot, fastboot_alloc_instance, fastboot_alloc_func static int fastboot_write_usb(struct fastboot *fb, const char *buffer, unsigned int buffer_size) { struct f_fastboot *f_fb = container_of(fb, struct f_fastboot, fastboot); - struct usb_request *in_req = f_fb->in_req; - uint64_t start; + struct usb_request *in_req; int ret; + in_req = fastboot_alloc_request(f_fb->in_ep); + if (!in_req) + return -ENOMEM; + memcpy(in_req->buf, buffer, buffer_size); in_req->length = buffer_size; + in_req->complete = fastboot_complete; ret = usb_ep_queue(f_fb->in_ep, in_req); - if (ret) + if (ret) { + fastboot_free_request(f_fb->in_ep, in_req); pr_err("Error %d on queue\n", ret); - - start = get_time_ns(); - - while (in_req->status == -EINPROGRESS) { - if (is_timeout(start, 2 * SECOND)) - return -ETIMEDOUT; - usb_gadget_poll(); } - if (in_req->status) - pr_err("Failed to send answer: %d\n", in_req->status); - return 0; } @@ -430,16 +502,19 @@ static void fastboot_start_download_usb(struct fastboot *fb) static void rx_handler_command(struct usb_ep *ep, struct usb_request *req) { - char *cmdbuf = req->buf; struct f_fastboot *f_fb = req->context; + struct fastboot_work *w; + int len; if (req->status != 0) return; - *(cmdbuf + req->actual) = 0; - fastboot_exec_cmd(&f_fb->fastboot, cmdbuf); - *cmdbuf = '\0'; - req->actual = 0; - memset(req->buf, 0, EP_BUFFER_SIZE); - usb_ep_queue(ep, req); + w = xzalloc(sizeof(*w)); + w->f_fb = f_fb; + + len = min_t(unsigned int, req->actual, FASTBOOT_MAX_CMD_LEN); + + memcpy(w->command, req->buf, len); + + wq_queue_work(&f_fb->wq, &w->work); } diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c new file mode 100644 index 0000000000..2c934c621a --- /dev/null +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -0,0 +1,2764 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * f_mass_storage.c -- Mass Storage USB Composite Function + * + * Copyright (C) 2003-2008 Alan Stern + * Copyright (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz <m.nazarewicz@samsung.com> + * All rights reserved. + */ + +/* + * The Mass Storage Function acts as a USB Mass Storage device, + * appearing to the host as a disk drive or as a CD-ROM drive. In + * addition to providing an example of a genuinely useful composite + * function for a USB device, it also illustrates a technique of + * double-buffering for increased throughput. + * + * Function supports multiple logical units (LUNs). Backing storage + * for each LUN is provided by a regular file or a block device. + * Access for each LUN can be limited to read-only. Moreover, the + * function can indicate that LUN is removable and/or CD-ROM. (The + * later implies read-only access.) + * + * MSF is configured by specifying a fsg_config structure. It has the + * following fields: + * + * nluns Number of LUNs function have (anywhere from 1 + * to FSG_MAX_LUNS which is 8). + * luns An array of LUN configuration values. This + * should be filled for each LUN that + * function will include (ie. for "nluns" + * LUNs). Each element of the array has + * the following fields: + * ->filename The path to the backing file for the LUN. + * Required if LUN is not marked as + * removable. + * ->ro Flag specifying access to the LUN shall be + * read-only. This is implied if CD-ROM + * emulation is enabled as well as when + * it was impossible to open "filename" + * in R/W mode. + * ->removable Flag specifying that LUN shall be indicated as + * being removable. + * ->cdrom Flag specifying that LUN shall be reported as + * being a CD-ROM. + * + * vendor_name + * product_name + * release Information used as a reply to INQUIRY + * request. To use default set to NULL, + * NULL, 0xffff respectively. The first + * field should be 8 and the second 16 + * characters or less. + * + * can_stall Set to permit function to halt bulk endpoints. + * Disabled on some USB devices known not + * to work correctly. You should set it + * to true. + * + * If "removable" is not set for a LUN then a backing file must be + * specified. If it is set, then NULL filename means the LUN's medium + * is not loaded (an empty string as "filename" in the fsg_config + * structure causes error). The CD-ROM emulation includes a single + * data track and no audio tracks; hence there need be only one + * backing file per LUN. Note also that the CD-ROM block length is + * set to 512 rather than the more common value 2048. + * + * + * MSF includes support for module parameters. If gadget using it + * decides to use it, the following module parameters will be + * available: + * + * file=filename[,filename...] + * Names of the files or block devices used for + * backing storage. + * ro=b[,b...] Default false, boolean for read-only access. + * removable=b[,b...] + * Default true, boolean for removable media. + * cdrom=b[,b...] Default false, boolean for whether to emulate + * a CD-ROM drive. + * luns=N Default N = number of filenames, number of + * LUNs to support. + * stall Default determined according to the type of + * USB device controller (usually true), + * boolean to permit the driver to halt + * bulk endpoints. + * + * The module parameters may be prefixed with some string. You need + * to consult gadget's documentation or source to verify whether it is + * using those module parameters and if it does what are the prefixes + * (look for FSG_MODULE_PARAMETERS() macro usage, what's inside it is + * the prefix). + * + * + * Requirements are modest; only a bulk-in and a bulk-out endpoint are + * needed. The memory requirement amounts to two 16K buffers, size + * configurable by a parameter. Support is included for both + * full-speed and high-speed operation. + * + * Note that the driver is slightly non-portable in that it assumes a + * single memory/DMA buffer will be useable for bulk-in, bulk-out, and + * interrupt-in endpoints. With most device controllers this isn't an + * issue, but there may be some with hardware restrictions that prevent + * a buffer from being used by more than one endpoint. + * + * When a LUN receive an "eject" SCSI request (Start/Stop Unit), + * if the LUN is removable, the backing file is released to simulate + * ejection. + * + * + * This function is heavily based on "File-backed Storage Gadget" by + * Alan Stern which in turn is heavily based on "Gadget Zero" by David + * Brownell. The driver's SCSI command interface was based on the + * "Information technology - Small Computer System Interface - 2" + * document from X3T9.2 Project 375D, Revision 10L, 7-SEP-93, + * available at <http://www.t10.org/ftp/t10/drafts/s2/s2-r10l.pdf>. + * The single exception is opcode 0x23 (READ FORMAT CAPACITIES), which + * was based on the "Universal Serial Bus Mass Storage Class UFI + * Command Specification" document, Revision 1.0, December 14, 1998, + * available at + * <http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf>. + */ + +/* + * Driver Design + * + * The MSF is fairly straightforward. There is a main kernel + * thread that handles most of the work. Interrupt routines field + * callbacks from the controller driver: bulk- and interrupt-request + * completion notifications, endpoint-0 events, and disconnect events. + * Completion events are passed to the main thread by wakeup calls. Many + * ep0 requests are handled at interrupt time, but SetInterface, + * SetConfiguration, and device reset requests are forwarded to the + * thread in the form of "exceptions" using SIGUSR1 signals (since they + * should interrupt any ongoing file I/O operations). + * + * The thread's main routine implements the standard command/data/status + * parts of a SCSI interaction. It and its subroutines are full of tests + * for pending signals/exceptions -- all this polling is necessary since + * the kernel has no setjmp/longjmp equivalents. (Maybe this is an + * indication that the driver really wants to be running in userspace.) + * An important point is that so long as the thread is alive it keeps an + * open reference to the backing file. This will prevent unmounting + * the backing file's underlying filesystem and could cause problems + * during system shutdown, for example. To prevent such problems, the + * thread catches INT, TERM, and KILL signals and converts them into + * an EXIT exception. + * + * In normal operation the main thread is started during the gadget's + * fsg_bind() callback and stopped during fsg_unbind(). But it can + * also exit when it receives a signal, and there's no point leaving + * the gadget running when the thread is dead. At of this moment, MSF + * provides no way to deregister the gadget when thread dies -- maybe + * a callback functions is needed. + * + * To provide maximum throughput, the driver uses a circular pipeline of + * buffer heads (struct fsg_buffhd). In principle the pipeline can be + * arbitrarily long; in practice the benefits don't justify having more + * than 2 stages (i.e., double buffering). But it helps to think of the + * pipeline as being a long one. Each buffer head contains a bulk-in and + * a bulk-out request pointer (since the buffer can be used for both + * output and input -- directions always are given from the host's + * point of view) as well as a pointer to the buffer and various state + * variables. + * + * Use of the pipeline follows a simple protocol. There is a variable + * (fsg->next_buffhd_to_fill) that points to the next buffer head to use. + * At any time that buffer head may still be in use from an earlier + * request, so each buffer head has a state variable indicating whether + * it is EMPTY, FULL, or BUSY. Typical use involves waiting for the + * buffer head to be EMPTY, filling the buffer either by file I/O or by + * USB I/O (during which the buffer head is BUSY), and marking the buffer + * head FULL when the I/O is complete. Then the buffer will be emptied + * (again possibly by USB I/O, during which it is marked BUSY) and + * finally marked EMPTY again (possibly by a completion routine). + * + * A module parameter tells the driver to avoid stalling the bulk + * endpoints wherever the transport specification allows. This is + * necessary for some UDCs like the SuperH, which cannot reliably clear a + * halt on a bulk endpoint. However, under certain circumstances the + * Bulk-only specification requires a stall. In such cases the driver + * will halt the endpoint and set a flag indicating that it should clear + * the halt in software during the next device reset. Hopefully this + * will permit everything to work correctly. Furthermore, although the + * specification allows the bulk-out endpoint to halt when the host sends + * too much data, implementing this would cause an unavoidable race. + * The driver will always use the "no-stall" approach for OUT transfers. + * + * One subtle point concerns sending status-stage responses for ep0 + * requests. Some of these requests, such as device reset, can involve + * interrupting an ongoing file I/O operation, which might take an + * arbitrarily long time. During that delay the host might give up on + * the original ep0 request and issue a new one. When that happens the + * driver should not notify the host about completion of the original + * request, as the host will no longer be waiting for it. So the driver + * assigns to each ep0 request a unique tag, and it keeps track of the + * tag value of the request associated with a long-running exception + * (device-reset, interface-change, or configuration-change). When the + * exception handler is finished, the status-stage response is submitted + * only if the current ep0 request tag is equal to the exception request + * tag. Thus only the most recently received ep0 request will get a + * status-stage response. + * + * Warning: This driver source file is too long. It ought to be split up + * into a header file plus about 3 separate .c files, to handle the details + * of the Gadget, USB Mass Storage, and SCSI protocols. + */ + +/* #define VERBOSE_DEBUG */ +/* #define DUMP_MSGS */ + +#define pr_fmt(fmt) "f_ums: " fmt + +#include <common.h> +#include <unistd.h> +#include <linux/stat.h> +#include <linux/wait.h> +#include <fcntl.h> +#include <file-list.h> +#include <dma.h> +#include <linux/bug.h> +#include <linux/rwsem.h> +#include <linux/pagemap.h> +#include <disks.h> +#include <scsi.h> + +#include <linux/err.h> +#include <linux/usb/mass_storage.h> + +#include <asm/unaligned.h> +#include <linux/bitops.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h> +#include <linux/bitmap.h> +#include <linux/completion.h> +#include <bthread.h> +#include <sched.h> + + +/*------------------------------------------------------------------------*/ + +#define FSG_DRIVER_DESC "ums" +#define UMS_NAME_LEN 16 + +#define FSG_DRIVER_VERSION "2012/06/5" + +static const char fsg_string_interface[] = "Mass Storage"; + +#include "storage_common.h" + +/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */ +struct usb_string fsg_strings[] = { + {FSG_STRING_INTERFACE, fsg_string_interface}, + {} +}; + +static struct usb_gadget_strings fsg_stringtab = { + .language = 0x0409, /* en-us */ + .strings = fsg_strings, +}; + +/*-------------------------------------------------------------------------*/ + +struct bthread *thread_task; + +struct fsg_dev; + +static struct file_list *ums_files; + +/* Data shared by all the FSG instances. */ +struct fsg_common { + struct usb_gadget *gadget; + struct fsg_dev *fsg, *new_fsg; + + struct usb_ep *ep0; /* Copy of gadget->ep0 */ + struct usb_request *ep0req; /* Copy of cdev->req */ + unsigned int ep0_req_tag; + + struct fsg_buffhd *next_buffhd_to_fill; + struct fsg_buffhd *next_buffhd_to_drain; + struct fsg_buffhd buffhds[FSG_NUM_BUFFERS]; + + struct f_ums_opts *opts; + + int cmnd_size; + u8 cmnd[MAX_COMMAND_SIZE]; + + unsigned int nluns; + unsigned int lun; + struct fsg_lun luns[FSG_MAX_LUNS]; + + unsigned int bulk_out_maxpacket; + enum fsg_state state; /* For exception handling */ + unsigned int exception_req_tag; + + enum data_direction data_dir; + u32 data_size; + u32 data_size_from_cmnd; + u32 tag; + u32 residue; + u32 usb_amount_left; + + unsigned int can_stall:1; + unsigned int phase_error:1; + unsigned int short_packet_received:1; + unsigned int bad_lun_okay:1; + unsigned int running:1; + + struct completion thread_wakeup_needed; + + /* Callback functions. */ + const struct fsg_operations *ops; + /* Gadget's private data. */ + void *private_data; + + const char *vendor_name; /* 8 characters or less */ + const char *product_name; /* 16 characters or less */ + u16 release; + + /* Vendor (8 chars), product (16 chars), release (4 + * hexadecimal digits) and NUL byte */ + char inquiry_string[8 + 16 + 4 + 1]; +}; + +static struct f_ums_opts *f_ums_opts_get(struct f_ums_opts *opts) +{ + opts->refcnt++; + return opts; +} + +static void f_ums_opts_put(struct f_ums_opts *opts) +{ + if (--opts->refcnt == 0) { + kfree(opts->common); + kfree(opts); + } +} + +struct fsg_config { + unsigned nluns; + struct fsg_lun_config { + const char *filename; + char ro; + char removable; + char cdrom; + char nofua; + } luns[FSG_MAX_LUNS]; + + /* Callback functions. */ + const struct fsg_operations *ops; + /* Gadget's private data. */ + void *private_data; + + const char *vendor_name; /* 8 characters or less */ + const char *product_name; /* 16 characters or less */ + + char can_stall; +}; + +struct fsg_dev { + struct usb_function function; + struct usb_gadget *gadget; /* Copy of cdev->gadget */ + struct fsg_common *common; + + int refcnt; + + u16 interface_number; + + unsigned int bulk_in_enabled:1; + unsigned int bulk_out_enabled:1; + + unsigned long atomic_bitflags; +#define IGNORE_BULK_OUT 0 + + struct usb_ep *bulk_in; + struct usb_ep *bulk_out; +}; + +static struct fsg_dev *fsg_dev_get(struct fsg_dev *fsg) +{ + fsg->refcnt++; + return fsg; +} + +static void fsg_dev_put(struct fsg_dev *fsg) +{ + if (--fsg->refcnt == 0) + kfree(fsg); +} + +static inline int __fsg_is_set(struct fsg_common *common, + const char *func, unsigned line) +{ + if (common->fsg) + return 1; + ERROR(common, "common->fsg is NULL in %s at %u\n", func, line); + WARN_ON(1); + + return 0; +} + +#define fsg_is_set(common) likely(__fsg_is_set(common, __func__, __LINE__)) + + +static inline struct fsg_dev *fsg_from_func(struct usb_function *f) +{ + return container_of(f, struct fsg_dev, function); +} + +static inline struct f_ums_opts * +fsg_opts_from_func_inst(const struct usb_function_instance *fi) +{ + return container_of(fi, struct f_ums_opts, func_inst); +} + +typedef void (*fsg_routine_t)(struct fsg_dev *); + +static int exception_in_progress(struct fsg_common *common) +{ + return common->state > FSG_STATE_IDLE; +} + +/* Make bulk-out requests be divisible by the maxpacket size */ +static void set_bulk_out_req_length(struct fsg_common *common, + struct fsg_buffhd *bh, unsigned int length) +{ + unsigned int rem; + + bh->bulk_out_intended_length = length; + rem = length % common->bulk_out_maxpacket; + if (rem > 0) + length += common->bulk_out_maxpacket - rem; + bh->outreq->length = length; +} + +/*-------------------------------------------------------------------------*/ + +static struct f_ums_opts ums[14]; // FIXME +static int ums_count; + +static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) +{ + const char *name; + + if (ep == fsg->bulk_in) + name = "bulk-in"; + else if (ep == fsg->bulk_out) + name = "bulk-out"; + else + name = ep->name; + DBG(fsg, "%s set halt\n", name); + return usb_ep_set_halt(ep); +} + +/*-------------------------------------------------------------------------*/ + +/* These routines may be called in process context or in_irq */ + +/* Caller must hold fsg->lock */ +static void wakeup_thread(struct fsg_common *common) +{ + complete(&common->thread_wakeup_needed); +} + +static void report_exception(const char *prefix, enum fsg_state state) +{ + const char *msg = "<unknown>"; + switch (state) { + /* This one isn't used anywhere */ + case FSG_STATE_COMMAND_PHASE: + msg = "Command Phase"; + break; + case FSG_STATE_DATA_PHASE: + msg = "Data Phase"; + break; + case FSG_STATE_STATUS_PHASE: + msg = "Status Phase"; + break; + + case FSG_STATE_IDLE: + msg = "Idle"; + break; + case FSG_STATE_ABORT_BULK_OUT: + msg = "abort bulk out"; + break; + case FSG_STATE_RESET: + msg = "reset"; + break; + case FSG_STATE_INTERFACE_CHANGE: + msg = "interface change"; + break; + case FSG_STATE_CONFIG_CHANGE: + msg = "config change"; + break; + case FSG_STATE_DISCONNECT: + msg = "disconnect"; + break; + case FSG_STATE_EXIT: + msg = "exit"; + break; + case FSG_STATE_TERMINATED: + msg = "terminated"; + break; + } + + pr_debug("%s: %s\n", prefix, msg); +} + +static void raise_exception(struct fsg_common *common, enum fsg_state new_state) +{ + /* Do nothing if a higher-priority exception is already in progress. + * If a lower-or-equal priority exception is in progress, preempt it + * and notify the main thread by sending it a signal. */ + if (common->state <= new_state) { + report_exception("raising", new_state); + common->exception_req_tag = common->ep0_req_tag; + common->state = new_state; + wakeup_thread(common); + } +} + +/*-------------------------------------------------------------------------*/ + +static int ep0_queue(struct fsg_common *common) +{ + int rc; + + rc = usb_ep_queue(common->ep0, common->ep0req); + common->ep0->driver_data = common; + if (rc != 0 && rc != -ESHUTDOWN) { + /* We can't do much more than wait for a reset */ + WARNING(common, "error in submission: %s --> %d\n", + common->ep0->name, rc); + } + return rc; +} + +/*-------------------------------------------------------------------------*/ + +/* Bulk and interrupt endpoint completion handlers. + * These always run in_irq. */ + +static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_common *common = ep->driver_data; + struct fsg_buffhd *bh = req->context; + + if (req->status || req->actual != req->length) + DBG(common, "%s --> %d, %u/%u\n", __func__, + req->status, req->actual, req->length); + if (req->status == -ECONNRESET) /* Request was cancelled */ + usb_ep_fifo_flush(ep); + + /* Hold the lock while we update the request and buffer states */ + bh->inreq_busy = 0; + bh->state = BUF_STATE_EMPTY; + wakeup_thread(common); +} + +static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_common *common = ep->driver_data; + struct fsg_buffhd *bh = req->context; + + dump_msg(common, "bulk-out", req->buf, req->actual); + if (req->status || req->actual != bh->bulk_out_intended_length) + DBG(common, "%s --> %d, %u/%u\n", __func__, + req->status, req->actual, + bh->bulk_out_intended_length); + if (req->status == -ECONNRESET) /* Request was cancelled */ + usb_ep_fifo_flush(ep); + + /* Hold the lock while we update the request and buffer states */ + bh->outreq_busy = 0; + bh->state = BUF_STATE_FULL; + wakeup_thread(common); +} + +/*-------------------------------------------------------------------------*/ + +/* Ep0 class-specific handlers. These always run in_irq. */ + +static int fsg_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct fsg_dev *fsg = fsg_from_func(f); + struct usb_request *req = fsg->common->ep0req; + u16 w_index = get_unaligned_le16(&ctrl->wIndex); + u16 w_value = get_unaligned_le16(&ctrl->wValue); + u16 w_length = get_unaligned_le16(&ctrl->wLength); + + if (!fsg_is_set(fsg->common)) + return -EOPNOTSUPP; + + switch (ctrl->bRequest) { + + case US_BULK_RESET_REQUEST: + if (ctrl->bRequestType != + (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + break; + if (w_index != fsg->interface_number || w_value != 0) + return -EDOM; + + /* Raise an exception to stop the current operation + * and reinitialize our state. */ + DBG(fsg, "bulk reset request\n"); + raise_exception(fsg->common, FSG_STATE_RESET); + return DELAYED_STATUS; + + case US_BULK_GET_MAX_LUN: + if (ctrl->bRequestType != + (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + break; + if (w_index != fsg->interface_number || w_value != 0) + return -EDOM; + VDBG(fsg, "get max LUN\n"); + *(u8 *) req->buf = fsg->common->nluns - 1; + + /* Respond with data/status */ + req->length = min((u16)1, w_length); + return ep0_queue(fsg->common); + } + + VDBG(fsg, + "unknown class-specific control req " + "%02x.%02x v%04x i%04x l%u\n", + ctrl->bRequestType, ctrl->bRequest, + get_unaligned_le16(&ctrl->wValue), w_index, w_length); + return -EOPNOTSUPP; +} + +/*-------------------------------------------------------------------------*/ + +/* All the following routines run in process context */ + +/* Use this for bulk or interrupt transfers, not ep0 */ +static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, + struct usb_request *req, int *pbusy, + enum fsg_buffer_state *state) +{ + int rc; + + if (ep == fsg->bulk_in) + dump_msg(fsg, "bulk-in", req->buf, req->length); + + *pbusy = 1; + *state = BUF_STATE_BUSY; + rc = usb_ep_queue(ep, req); + if (rc != 0) { + *pbusy = 0; + *state = BUF_STATE_EMPTY; + + /* We can't do much more than wait for a reset */ + + /* Note: currently the net2280 driver fails zero-length + * submissions if DMA is enabled. */ + if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP && + req->length == 0)) + WARNING(fsg, "error in submission: %s --> %d\n", + ep->name, rc); + } +} + +#define START_TRANSFER_OR(common, ep_name, req, pbusy, state) \ + if (fsg_is_set(common)) \ + start_transfer((common)->fsg, (common)->fsg->ep_name, \ + req, pbusy, state); \ + else + +#define START_TRANSFER(common, ep_name, req, pbusy, state) \ + START_TRANSFER_OR(common, ep_name, req, pbusy, state) (void)0 + +static int sleep_thread(struct fsg_common *common) +{ + int ret; + + /* Wait until a signal arrives or we are woken up */ + ret = wait_for_completion_interruptible(&common->thread_wakeup_needed); + if (ret) + return ret; + + reinit_completion(&common->thread_wakeup_needed); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int do_read(struct fsg_common *common) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + u32 lba; + struct fsg_buffhd *bh; + int rc; + u32 amount_left; + loff_t file_offset; + unsigned int amount; + unsigned int partial_page; + ssize_t nread; + + /* Get the starting Logical Block Address and check that it's + * not too big */ + if (common->cmnd[0] == SCSI_READ6) + lba = get_unaligned_be24(&common->cmnd[1]); + else { + lba = get_unaligned_be32(&common->cmnd[2]); + + /* We allow DPO (Disable Page Out = don't save data in the + * cache) and FUA (Force Unit Access = don't read from the + * cache), but we don't implement them. */ + if ((common->cmnd[1] & ~0x18) != 0) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + } + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + file_offset = ((loff_t) lba) << 9; + + /* Carry out the file reads */ + amount_left = common->data_size_from_cmnd; + if (unlikely(amount_left == 0)) + return -EIO; /* No default reply */ + + for (;;) { + /* Wait for the next buffer to become available */ + bh = common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(common); + if (rc) + return rc; + } + + /* Figure out how much we need to read: + * Try to read the remaining amount. + * But don't read more than the buffer size. + * And don't try to read past the end of the file. + * Finally, if we're not at a page boundary, don't read past + * the next page. + * If this means reading 0 then we were asked to read past + * the end of file. */ + amount = min(amount_left, FSG_BUFLEN); + partial_page = file_offset & (PAGE_CACHE_SIZE - 1); + if (partial_page > 0) + amount = min(amount, (unsigned int) PAGE_CACHE_SIZE - + partial_page); + + + /* If we were asked to read past the end of file, + * end with an empty buffer. */ + if (amount == 0) { + curlun->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + curlun->info_valid = 1; + bh->inreq->length = 0; + bh->state = BUF_STATE_FULL; + break; + } + + /* Perform the read */ + nread = pread(ums[common->lun].fd, bh->buf, amount, file_offset); + + VLDBG(curlun, "file read %u @ %llu -> %zd\n", amount, + (unsigned long long) file_offset, + nread); + if (nread <= 0) { + const char *err = nread ? strerror(-nread) : "EOF"; + LDBG(curlun, "error in file read: %s\n", err); + nread = 0; + } else if (nread < amount) { + LDBG(curlun, "partial file read: %d/%u\n", + (int) nread, amount); + nread -= (nread & 511); /* Round down to a block */ + } + file_offset += nread; + amount_left -= nread; + common->residue -= nread; + bh->inreq->length = nread; + bh->state = BUF_STATE_FULL; + + /* If an error occurred, report it and its position */ + if (nread < amount) { + curlun->sense_data = SS_UNRECOVERED_READ_ERROR; + curlun->info_valid = 1; + break; + } + + if (amount_left == 0) + break; /* No more left to read */ + + /* Send this buffer and go read some more */ + bh->inreq->zero = 0; + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + return -EIO; + common->next_buffhd_to_fill = bh->next; + } + + return -EIO; /* No default reply */ +} + +/*-------------------------------------------------------------------------*/ + +static int do_write(struct fsg_common *common) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + u32 lba; + struct fsg_buffhd *bh; + int get_some_more; + u32 amount_left_to_req, amount_left_to_write; + loff_t usb_offset, file_offset; + unsigned int amount; + unsigned int partial_page; + ssize_t nwritten; + int rc; + + if (curlun->ro) { + curlun->sense_data = SS_WRITE_PROTECTED; + return -EINVAL; + } + + /* Get the starting Logical Block Address and check that it's + * not too big */ + if (common->cmnd[0] == SCSI_WRITE6) + lba = get_unaligned_be24(&common->cmnd[1]); + else { + lba = get_unaligned_be32(&common->cmnd[2]); + + /* We allow DPO (Disable Page Out = don't save data in the + * cache) and FUA (Force Unit Access = write directly to the + * medium). We don't implement DPO; we implement FUA by + * performing synchronous output. */ + if (common->cmnd[1] & ~0x18) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + } + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + /* Carry out the file writes */ + get_some_more = 1; + file_offset = usb_offset = ((loff_t) lba) << 9; + amount_left_to_req = common->data_size_from_cmnd; + amount_left_to_write = common->data_size_from_cmnd; + + while (amount_left_to_write > 0) { + + /* Queue a request for more data from the host */ + bh = common->next_buffhd_to_fill; + if (bh->state == BUF_STATE_EMPTY && get_some_more) { + + /* Figure out how much we want to get: + * Try to get the remaining amount. + * But don't get more than the buffer size. + * And don't try to go past the end of the file. + * If we're not at a page boundary, + * don't go past the next page. + * If this means getting 0, then we were asked + * to write past the end of file. + * Finally, round down to a block boundary. */ + amount = min(amount_left_to_req, FSG_BUFLEN); + partial_page = usb_offset & (PAGE_CACHE_SIZE - 1); + if (partial_page > 0) + amount = min(amount, + (unsigned int) PAGE_CACHE_SIZE - partial_page); + + if (amount == 0) { + get_some_more = 0; + curlun->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + curlun->info_valid = 1; + continue; + } + amount -= (amount & 511); + if (amount == 0) { + + /* Why were we were asked to transfer a + * partial block? */ + get_some_more = 0; + continue; + } + + /* Get the next buffer */ + usb_offset += amount; + common->usb_amount_left -= amount; + amount_left_to_req -= amount; + if (amount_left_to_req == 0) + get_some_more = 0; + + /* amount is always divisible by 512, hence by + * the bulk-out maxpacket size */ + bh->outreq->length = amount; + bh->bulk_out_intended_length = amount; + bh->outreq->short_not_ok = 1; + START_TRANSFER_OR(common, bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + return -EIO; + common->next_buffhd_to_fill = bh->next; + continue; + } + + /* Write the received data to the backing file */ + bh = common->next_buffhd_to_drain; + if (bh->state == BUF_STATE_EMPTY && !get_some_more) + break; /* We stopped early */ + if (bh->state == BUF_STATE_FULL) { + common->next_buffhd_to_drain = bh->next; + bh->state = BUF_STATE_EMPTY; + + /* Did something go wrong with the transfer? */ + if (bh->outreq->status != 0) { + curlun->sense_data = SS_COMMUNICATION_FAILURE; + curlun->info_valid = 1; + break; + } + + amount = bh->outreq->actual; + + /* Perform the write */ + nwritten = pwrite(ums[common->lun].fd, bh->buf, amount, file_offset); + + VLDBG(curlun, "file write %u @ %llu -> %zd\n", amount, + (unsigned long long) file_offset, + nwritten); + + if (nwritten < 0) { + LDBG(curlun, "error in file write: %pe\n", ERR_PTR(nwritten)); + nwritten = 0; + } else if (nwritten < amount) { + LDBG(curlun, "partial file write: %d/%u\n", + (int) nwritten, amount); + nwritten -= (nwritten & 511); + /* Round down to a block */ + } + file_offset += nwritten; + amount_left_to_write -= nwritten; + common->residue -= nwritten; + + /* If an error occurred, report it and its position */ + if (nwritten < amount) { + pr_warn("nwritten:%zd amount:%u\n", nwritten, + amount); + curlun->sense_data = SS_WRITE_ERROR; + curlun->info_valid = 1; + break; + } + + /* Did the host decide to stop early? */ + if (bh->outreq->actual != bh->outreq->length) { + common->short_packet_received = 1; + break; + } + continue; + } + + /* Wait for something to happen */ + rc = sleep_thread(common); + if (rc) + return rc; + } + + return -EIO; /* No default reply */ +} + +/*-------------------------------------------------------------------------*/ + +static int do_synchronize_cache(struct fsg_common *common) +{ + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int do_verify(struct fsg_common *common) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + u32 lba; + u32 verification_length; + struct fsg_buffhd *bh = common->next_buffhd_to_fill; + loff_t file_offset; + u32 amount_left; + unsigned int amount; + ssize_t nread; + + /* Get the starting Logical Block Address and check that it's + * not too big */ + lba = get_unaligned_be32(&common->cmnd[2]); + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + /* We allow DPO (Disable Page Out = don't save data in the + * cache) but we don't implement it. */ + if (common->cmnd[1] & ~0x10) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + verification_length = get_unaligned_be16(&common->cmnd[7]); + if (unlikely(verification_length == 0)) + return -EIO; /* No default reply */ + + /* Prepare to carry out the file verify */ + amount_left = verification_length << 9; + file_offset = ((loff_t) lba) << 9; + + /* Write out all the dirty buffers before invalidating them */ + + /* Just try to read the requested blocks */ + while (amount_left > 0) { + + /* Figure out how much we need to read: + * Try to read the remaining amount, but not more than + * the buffer size. + * And don't try to read past the end of the file. + * If this means reading 0 then we were asked to read + * past the end of file. */ + amount = min(amount_left, FSG_BUFLEN); + if (amount == 0) { + curlun->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + curlun->info_valid = 1; + break; + } + + /* Perform the read */ + nread = pread(ums[common->lun].fd, bh->buf, amount, file_offset); + + VLDBG(curlun, "file read %u @ %llu -> %zd\n", amount, + (unsigned long long) file_offset, + nread); + if (nread <= 0) { + const char *err = nread ? strerror(-nread) : "EOF"; + LDBG(curlun, "error in file read: %s\n", err); + nread = 0; + } else if (nread < amount) { + LDBG(curlun, "partial file verify: %d/%u\n", + (int) nread, amount); + nread -= (nread & 511); /* Round down to a sector */ + } + if (nread == 0) { + curlun->sense_data = SS_UNRECOVERED_READ_ERROR; + curlun->info_valid = 1; + break; + } + file_offset += nread; + amount_left -= nread; + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + static const char vendor_id[] = "Linux "; + u8 *buf = (u8 *) bh->buf; + + if (!curlun) { /* Unsupported LUNs are okay */ + common->bad_lun_okay = 1; + memset(buf, 0, 36); + buf[0] = 0x7f; /* Unsupported, no device-type */ + buf[4] = 31; /* Additional length */ + return 36; + } + + memset(buf, 0, 8); + buf[0] = TYPE_DISK; + buf[1] = curlun->removable ? 0x80 : 0; + buf[2] = 2; /* ANSI SCSI level 2 */ + buf[3] = 2; /* SCSI-2 INQUIRY data format */ + buf[4] = 31; /* Additional length */ + /* No special options */ + sprintf((char *) (buf + 8), "%-8s%-16s%04x", (char*) vendor_id , + ums[common->lun].name, (u16) 0xffff); + + return 36; +} + + +static int do_request_sense(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + u8 *buf = (u8 *) bh->buf; + u32 sd, sdinfo = 0; + int valid; + + /* + * From the SCSI-2 spec., section 7.9 (Unit attention condition): + * + * If a REQUEST SENSE command is received from an initiator + * with a pending unit attention condition (before the target + * generates the contingent allegiance condition), then the + * target shall either: + * a) report any pending sense data and preserve the unit + * attention condition on the logical unit, or, + * b) report the unit attention condition, may discard any + * pending sense data, and clear the unit attention + * condition on the logical unit for that initiator. + * + * FSG normally uses option a); enable this code to use option b). + */ +#if 0 + if (curlun && curlun->unit_attention_data != SS_NO_SENSE) { + curlun->sense_data = curlun->unit_attention_data; + curlun->unit_attention_data = SS_NO_SENSE; + } +#endif + + if (!curlun) { /* Unsupported LUNs are okay */ + common->bad_lun_okay = 1; + sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; + valid = 0; + } else { + sd = curlun->sense_data; + valid = curlun->info_valid << 7; + curlun->sense_data = SS_NO_SENSE; + curlun->info_valid = 0; + } + + memset(buf, 0, 18); + buf[0] = valid | 0x70; /* Valid, current error */ + buf[2] = SK(sd); + put_unaligned_be32(sdinfo, &buf[3]); /* Sense information */ + buf[7] = 18 - 8; /* Additional sense length */ + buf[12] = ASC(sd); + buf[13] = ASCQ(sd); + return 18; +} + +static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + u32 lba = get_unaligned_be32(&common->cmnd[2]); + int pmi = common->cmnd[8]; + u8 *buf = (u8 *) bh->buf; + + /* Check the PMI and LBA fields */ + if (pmi > 1 || (pmi == 0 && lba != 0)) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + put_unaligned_be32(curlun->num_sectors - 1, &buf[0]); + /* Max logical block */ + put_unaligned_be32(512, &buf[4]); /* Block length */ + return 8; +} + +static int do_read_header(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + int msf = common->cmnd[1] & 0x02; + u32 lba = get_unaligned_be32(&common->cmnd[2]); + u8 *buf = (u8 *) bh->buf; + + if (common->cmnd[1] & ~0x02) { /* Mask away MSF */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + memset(buf, 0, 8); + buf[0] = 0x01; /* 2048 bytes of user data, rest is EC */ + store_cdrom_address(&buf[4], msf, lba); + return 8; +} + + +static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + int msf = common->cmnd[1] & 0x02; + int start_track = common->cmnd[6]; + u8 *buf = (u8 *) bh->buf; + + if ((common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ + start_track > 1) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + memset(buf, 0, 20); + buf[1] = (20-2); /* TOC data length */ + buf[2] = 1; /* First track number */ + buf[3] = 1; /* Last track number */ + buf[5] = 0x16; /* Data track, copying allowed */ + buf[6] = 0x01; /* Only track is number 1 */ + store_cdrom_address(&buf[8], msf, 0); + + buf[13] = 0x16; /* Lead-out track is data */ + buf[14] = 0xAA; /* Lead-out track number */ + store_cdrom_address(&buf[16], msf, curlun->num_sectors); + + return 20; +} + +static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + int mscmnd = common->cmnd[0]; + u8 *buf = (u8 *) bh->buf; + u8 *buf0 = buf; + int pc, page_code; + int changeable_values, all_pages; + int valid_page = 0; + int len, limit; + + if ((common->cmnd[1] & ~0x08) != 0) { /* Mask away DBD */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + pc = common->cmnd[2] >> 6; + page_code = common->cmnd[2] & 0x3f; + if (pc == 3) { + curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED; + return -EINVAL; + } + changeable_values = (pc == 1); + all_pages = (page_code == 0x3f); + + /* Write the mode parameter header. Fixed values are: default + * medium type, no cache control (DPOFUA), and no block descriptors. + * The only variable value is the WriteProtect bit. We will fill in + * the mode data length later. */ + memset(buf, 0, 8); + if (mscmnd == SCSI_MODE_SEN6) { + buf[2] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */ + buf += 4; + limit = 255; + } else { /* SCSI_MODE_SEN10 */ + buf[3] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */ + buf += 8; + limit = 65535; /* Should really be FSG_BUFLEN */ + } + + /* No block descriptors */ + + /* The mode pages, in numerical order. The only page we support + * is the Caching page. */ + if (page_code == 0x08 || all_pages) { + valid_page = 1; + buf[0] = 0x08; /* Page code */ + buf[1] = 10; /* Page length */ + memset(buf+2, 0, 10); /* None of the fields are changeable */ + + if (!changeable_values) { + buf[2] = 0x04; /* Write cache enable, */ + /* Read cache not disabled */ + /* No cache retention priorities */ + put_unaligned_be16(0xffff, &buf[4]); + /* Don't disable prefetch */ + /* Minimum prefetch = 0 */ + put_unaligned_be16(0xffff, &buf[8]); + /* Maximum prefetch */ + put_unaligned_be16(0xffff, &buf[10]); + /* Maximum prefetch ceiling */ + } + buf += 12; + } + + /* Check that a valid page was requested and the mode data length + * isn't too long. */ + len = buf - buf0; + if (!valid_page || len > limit) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + /* Store the mode data length */ + if (mscmnd == SCSI_MODE_SEN6) + buf0[0] = len - 1; + else + put_unaligned_be16(len - 2, buf0); + return len; +} + + +static int do_start_stop(struct fsg_common *common) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + + if (!curlun) { + return -EINVAL; + } else if (!curlun->removable) { + curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; + } + + return 0; +} + +static int do_prevent_allow(struct fsg_common *common) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + int prevent; + + if (!curlun->removable) { + curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; + } + + prevent = common->cmnd[4] & 0x01; + if ((common->cmnd[4] & ~0x01) != 0) { /* Mask away Prevent */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + if (curlun->prevent_medium_removal && !prevent) + fsg_lun_fsync_sub(curlun); + curlun->prevent_medium_removal = prevent; + return 0; +} + + +static int do_read_format_capacities(struct fsg_common *common, + struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + u8 *buf = (u8 *) bh->buf; + + buf[0] = buf[1] = buf[2] = 0; + buf[3] = 8; /* Only the Current/Maximum Capacity Descriptor */ + buf += 4; + + put_unaligned_be32(curlun->num_sectors, &buf[0]); + /* Number of blocks */ + put_unaligned_be32(512, &buf[4]); /* Block length */ + buf[4] = 0x02; /* Current capacity */ + return 12; +} + + +static int do_mode_select(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + + /* We don't support MODE SELECT */ + if (curlun) + curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; +} + + +/*-------------------------------------------------------------------------*/ + +static int halt_bulk_in_endpoint(struct fsg_dev *fsg) +{ + int rc; + + rc = fsg_set_halt(fsg, fsg->bulk_in); + if (rc == -EAGAIN) + VDBG(fsg, "delayed bulk-in endpoint halt\n"); + while (rc != 0) { + if (rc != -EAGAIN) { + WARNING(fsg, "usb_ep_set_halt -> %d\n", rc); + rc = 0; + break; + } + + rc = usb_ep_set_halt(fsg->bulk_in); + } + return rc; +} + +static int wedge_bulk_in_endpoint(struct fsg_dev *fsg) +{ + int rc; + + DBG(fsg, "bulk-in set wedge\n"); + rc = 0; /* usb_ep_set_wedge(fsg->bulk_in); */ + if (rc == -EAGAIN) + VDBG(fsg, "delayed bulk-in endpoint wedge\n"); + while (rc != 0) { + if (rc != -EAGAIN) { + WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc); + rc = 0; + break; + } + } + return rc; +} + +static int pad_with_zeros(struct fsg_dev *fsg) +{ + struct fsg_buffhd *bh = fsg->common->next_buffhd_to_fill; + u32 nkeep = bh->inreq->length; + u32 nsend; + int rc; + + bh->state = BUF_STATE_EMPTY; /* For the first iteration */ + fsg->common->usb_amount_left = nkeep + fsg->common->residue; + while (fsg->common->usb_amount_left > 0) { + + /* Wait for the next buffer to be free */ + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(fsg->common); + if (rc) + return rc; + } + + nsend = min(fsg->common->usb_amount_left, FSG_BUFLEN); + memset(bh->buf + nkeep, 0, nsend - nkeep); + bh->inreq->length = nsend; + bh->inreq->zero = 0; + start_transfer(fsg, fsg->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); + bh = fsg->common->next_buffhd_to_fill = bh->next; + fsg->common->usb_amount_left -= nsend; + nkeep = 0; + } + return 0; +} + +static int throw_away_data(struct fsg_common *common) +{ + struct fsg_buffhd *bh; + u32 amount; + int rc; + + for (bh = common->next_buffhd_to_drain; + bh->state != BUF_STATE_EMPTY || common->usb_amount_left > 0; + bh = common->next_buffhd_to_drain) { + + /* Throw away the data in a filled buffer */ + if (bh->state == BUF_STATE_FULL) { + bh->state = BUF_STATE_EMPTY; + common->next_buffhd_to_drain = bh->next; + + /* A short packet or an error ends everything */ + if (bh->outreq->actual != bh->outreq->length || + bh->outreq->status != 0) { + raise_exception(common, + FSG_STATE_ABORT_BULK_OUT); + return -EPIPE; + } + continue; + } + + /* Try to submit another request if we need one */ + bh = common->next_buffhd_to_fill; + if (bh->state == BUF_STATE_EMPTY + && common->usb_amount_left > 0) { + amount = min(common->usb_amount_left, FSG_BUFLEN); + + /* amount is always divisible by 512, hence by + * the bulk-out maxpacket size */ + bh->outreq->length = amount; + bh->bulk_out_intended_length = amount; + bh->outreq->short_not_ok = 1; + START_TRANSFER_OR(common, bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + return -EIO; + common->next_buffhd_to_fill = bh->next; + common->usb_amount_left -= amount; + continue; + } + + /* Otherwise wait for something to happen */ + rc = sleep_thread(common); + if (rc) + return rc; + } + return 0; +} + + +static int finish_reply(struct fsg_common *common) +{ + struct fsg_buffhd *bh = common->next_buffhd_to_fill; + int rc = 0; + + switch (common->data_dir) { + case DATA_DIR_NONE: + break; /* Nothing to send */ + + /* If we don't know whether the host wants to read or write, + * this must be CB or CBI with an unknown command. We mustn't + * try to send or receive any data. So stall both bulk pipes + * if we can and wait for a reset. */ + case DATA_DIR_UNKNOWN: + if (!common->can_stall) { + /* Nothing */ + } else if (fsg_is_set(common)) { + fsg_set_halt(common->fsg, common->fsg->bulk_out); + rc = halt_bulk_in_endpoint(common->fsg); + } else { + /* Don't know what to do if common->fsg is NULL */ + rc = -EIO; + } + break; + + /* All but the last buffer of data must have already been sent */ + case DATA_DIR_TO_HOST: + if (common->data_size == 0) { + /* Nothing to send */ + + /* If there's no residue, simply send the last buffer */ + } else if (common->residue == 0) { + bh->inreq->zero = 0; + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + return -EIO; + common->next_buffhd_to_fill = bh->next; + + /* For Bulk-only, if we're allowed to stall then send the + * short packet and halt the bulk-in endpoint. If we can't + * stall, pad out the remaining data with 0's. */ + } else if (common->can_stall) { + bh->inreq->zero = 1; + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + rc = -EIO; + common->next_buffhd_to_fill = bh->next; + if (common->fsg) + rc = halt_bulk_in_endpoint(common->fsg); + } else if (fsg_is_set(common)) { + rc = pad_with_zeros(common->fsg); + } else { + /* Don't know what to do if common->fsg is NULL */ + rc = -EIO; + } + break; + + /* We have processed all we want from the data the host has sent. + * There may still be outstanding bulk-out requests. */ + case DATA_DIR_FROM_HOST: + if (common->residue == 0) { + /* Nothing to receive */ + + /* Did the host stop sending unexpectedly early? */ + } else if (common->short_packet_received) { + raise_exception(common, FSG_STATE_ABORT_BULK_OUT); + rc = -EPIPE; + + /* We haven't processed all the incoming data. Even though + * we may be allowed to stall, doing so would cause a race. + * The controller may already have ACK'ed all the remaining + * bulk-out packets, in which case the host wouldn't see a + * STALL. Not realizing the endpoint was halted, it wouldn't + * clear the halt -- leading to problems later on. */ +#if 0 + } else if (common->can_stall) { + if (fsg_is_set(common)) + fsg_set_halt(common->fsg, + common->fsg->bulk_out); + raise_exception(common, FSG_STATE_ABORT_BULK_OUT); + rc = -EPIPE; +#endif + + /* We can't stall. Read in the excess data and throw it + * all away. */ + } else { + rc = throw_away_data(common); + } + break; + } + return rc; +} + + +static int send_status(struct fsg_common *common) +{ + struct fsg_lun *curlun = &common->luns[common->lun]; + struct fsg_buffhd *bh; + struct bulk_cs_wrap *csw; + int rc; + u8 status = US_BULK_STAT_OK; + u32 sd, sdinfo = 0; + + /* Wait for the next buffer to become available */ + bh = common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(common); + if (rc) + return rc; + } + + if (curlun) + sd = curlun->sense_data; + else if (common->bad_lun_okay) + sd = SS_NO_SENSE; + else + sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; + + if (common->phase_error) { + DBG(common, "sending phase-error status\n"); + status = US_BULK_STAT_PHASE; + sd = SS_INVALID_COMMAND; + } else if (sd != SS_NO_SENSE) { + DBG(common, "sending command-failure status\n"); + status = US_BULK_STAT_FAIL; + VDBG(common, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;" + " info x%x\n", + SK(sd), ASC(sd), ASCQ(sd), sdinfo); + } + + /* Store and send the Bulk-only CSW */ + csw = (void *)bh->buf; + + csw->Signature = cpu_to_le32(US_BULK_CS_SIGN); + csw->Tag = common->tag; + csw->Residue = cpu_to_le32(common->residue); + csw->Status = status; + + bh->inreq->length = US_BULK_CS_WRAP_LEN; + bh->inreq->zero = 0; + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + /* Don't know what to do if common->fsg is NULL */ + return -EIO; + + common->next_buffhd_to_fill = bh->next; + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +/* Check whether the command is properly formed and whether its data size + * and direction agree with the values we already have. */ +static int check_command(struct fsg_common *common, int cmnd_size, + enum data_direction data_dir, unsigned int mask, + int needs_medium, const char *name) +{ + int i; + int lun = common->cmnd[1] >> 5; + static const char dirletter[4] = {'u', 'o', 'i', 'n'}; + char hdlen[20]; + struct fsg_lun *curlun; + + hdlen[0] = 0; + if (common->data_dir != DATA_DIR_UNKNOWN) + sprintf(hdlen, ", H%c=%u", dirletter[(int) common->data_dir], + common->data_size); + VDBG(common, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n", + name, cmnd_size, dirletter[(int) data_dir], + common->data_size_from_cmnd, common->cmnd_size, hdlen); + + /* We can't reply at all until we know the correct data direction + * and size. */ + if (common->data_size_from_cmnd == 0) + data_dir = DATA_DIR_NONE; + if (common->data_size < common->data_size_from_cmnd) { + /* Host data size < Device data size is a phase error. + * Carry out the command, but only transfer as much as + * we are allowed. */ + common->data_size_from_cmnd = common->data_size; + common->phase_error = 1; + } + common->residue = common->data_size; + common->usb_amount_left = common->data_size; + + /* Conflicting data directions is a phase error */ + if (common->data_dir != data_dir + && common->data_size_from_cmnd > 0) { + common->phase_error = 1; + return -EINVAL; + } + + /* Verify the length of the command itself */ + if (cmnd_size != common->cmnd_size) { + + /* Special case workaround: There are plenty of buggy SCSI + * implementations. Many have issues with cbw->Length + * field passing a wrong command size. For those cases we + * always try to work around the problem by using the length + * sent by the host side provided it is at least as large + * as the correct command length. + * Examples of such cases would be MS-Windows, which issues + * REQUEST SENSE with cbw->Length == 12 where it should + * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and + * REQUEST SENSE with cbw->Length == 10 where it should + * be 6 as well. + */ + if (cmnd_size <= common->cmnd_size) { + DBG(common, "%s is buggy! Expected length %d " + "but we got %d\n", name, + cmnd_size, common->cmnd_size); + cmnd_size = common->cmnd_size; + } else { + common->phase_error = 1; + return -EINVAL; + } + } + + /* Check that the LUN values are consistent */ + if (common->lun != lun) + DBG(common, "using LUN %d from CBW, not LUN %d from CDB\n", + common->lun, lun); + + /* Check the LUN */ + if (common->lun < common->nluns) { + curlun = &common->luns[common->lun]; + if (common->cmnd[0] != SCSI_REQ_SENSE) { + curlun->sense_data = SS_NO_SENSE; + curlun->info_valid = 0; + } + } else { + curlun = NULL; + common->bad_lun_okay = 0; + + /* INQUIRY and REQUEST SENSE commands are explicitly allowed + * to use unsupported LUNs; all others may not. */ + if (common->cmnd[0] != SCSI_INQUIRY && + common->cmnd[0] != SCSI_REQ_SENSE) { + DBG(common, "unsupported LUN %d\n", common->lun); + return -EINVAL; + } + } +#if 0 + /* If a unit attention condition exists, only INQUIRY and + * REQUEST SENSE commands are allowed; anything else must fail. */ + if (curlun && curlun->unit_attention_data != SS_NO_SENSE && + common->cmnd[0] != SCSI_INQUIRY && + common->cmnd[0] != SCSI_REQ_SENSE) { + curlun->sense_data = curlun->unit_attention_data; + curlun->unit_attention_data = SS_NO_SENSE; + return -EINVAL; + } +#endif + /* Check that only command bytes listed in the mask are non-zero */ + common->cmnd[1] &= 0x1f; /* Mask away the LUN */ + for (i = 1; i < cmnd_size; ++i) { + if (common->cmnd[i] && !(mask & (1 << i))) { + if (curlun) + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + } + + return 0; +} + +static int do_scsi_command(struct fsg_common *common) +{ + struct fsg_buffhd *bh; + int rc; + int reply = -EINVAL; + int i; + static char unknown[16]; + struct fsg_lun *curlun = &common->luns[common->lun]; + + dump_cdb(common); + + /* Wait for the next buffer to become available for data or status */ + bh = common->next_buffhd_to_fill; + common->next_buffhd_to_drain = bh; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(common); + if (rc) + return rc; + } + common->phase_error = 0; + common->short_packet_received = 0; + + down_read(&common->filesem); /* We're using the backing file */ + switch (common->cmnd[0]) { + + case SCSI_INQUIRY: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<4), 0, + "INQUIRY"); + if (reply == 0) + reply = do_inquiry(common, bh); + break; + + case SCSI_MODE_SEL6: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_FROM_HOST, + (1<<1) | (1<<4), 0, + "MODE SELECT(6)"); + if (reply == 0) + reply = do_mode_select(common, bh); + break; + + case SCSI_MODE_SEL10: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_FROM_HOST, + (1<<1) | (3<<7), 0, + "MODE SELECT(10)"); + if (reply == 0) + reply = do_mode_select(common, bh); + break; + + case SCSI_MODE_SEN6: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (1<<4), 0, + "MODE SENSE(6)"); + if (reply == 0) + reply = do_mode_sense(common, bh); + break; + + case SCSI_MODE_SEN10: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (3<<7), 0, + "MODE SENSE(10)"); + if (reply == 0) + reply = do_mode_sense(common, bh); + break; + + case SCSI_MED_REMOVL: + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, + (1<<4), 0, + "PREVENT-ALLOW MEDIUM REMOVAL"); + if (reply == 0) + reply = do_prevent_allow(common); + break; + + case SCSI_READ6: + i = common->cmnd[4]; + common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (7<<1) | (1<<4), 1, + "READ(6)"); + if (reply == 0) + reply = do_read(common); + break; + + case SCSI_READ10: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]) << 9; + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (1<<1) | (0xf<<2) | (3<<7), 1, + "READ(10)"); + if (reply == 0) + reply = do_read(common); + break; + + case SCSI_READ12: + common->data_size_from_cmnd = + get_unaligned_be32(&common->cmnd[6]) << 9; + reply = check_command(common, 12, DATA_DIR_TO_HOST, + (1<<1) | (0xf<<2) | (0xf<<6), 1, + "READ(12)"); + if (reply == 0) + reply = do_read(common); + break; + + case SCSI_RD_CAPAC: + common->data_size_from_cmnd = 8; + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (0xf<<2) | (1<<8), 1, + "READ CAPACITY"); + if (reply == 0) + reply = do_read_capacity(common, bh); + break; + + case SCSI_RD_HEADER: + if (!common->luns[common->lun].cdrom) + goto unknown_cmnd; + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (3<<7) | (0x1f<<1), 1, + "READ HEADER"); + if (reply == 0) + reply = do_read_header(common, bh); + break; + + case SCSI_RD_TOC: + if (!common->luns[common->lun].cdrom) + goto unknown_cmnd; + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (7<<6) | (1<<1), 1, + "READ TOC"); + if (reply == 0) + reply = do_read_toc(common, bh); + break; + + case SCSI_RD_FMT_CAPAC: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (3<<7), 1, + "READ FORMAT CAPACITIES"); + if (reply == 0) + reply = do_read_format_capacities(common, bh); + break; + + case SCSI_REQ_SENSE: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<4), 0, + "REQUEST SENSE"); + if (reply == 0) + reply = do_request_sense(common, bh); + break; + + case SCSI_START_STP: + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, + (1<<1) | (1<<4), 0, + "START-STOP UNIT"); + if (reply == 0) + reply = do_start_stop(common); + break; + + case SCSI_SYNC_CACHE: + common->data_size_from_cmnd = 0; + reply = check_command(common, 10, DATA_DIR_NONE, + (0xf<<2) | (3<<7), 1, + "SYNCHRONIZE CACHE"); + if (reply == 0) + reply = do_synchronize_cache(common); + break; + + case SCSI_TST_U_RDY: + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, + 0, 1, + "TEST UNIT READY"); + break; + + /* Although optional, this command is used by MS-Windows. We + * support a minimal version: BytChk must be 0. */ + case SCSI_VERIFY: + common->data_size_from_cmnd = 0; + reply = check_command(common, 10, DATA_DIR_NONE, + (1<<1) | (0xf<<2) | (3<<7), 1, + "VERIFY"); + if (reply == 0) + reply = do_verify(common); + break; + + case SCSI_WRITE6: + i = common->cmnd[4]; + common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + reply = check_command(common, 6, DATA_DIR_FROM_HOST, + (7<<1) | (1<<4), 1, + "WRITE(6)"); + if (reply == 0) + reply = do_write(common); + break; + + case SCSI_WRITE10: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]) << 9; + reply = check_command(common, 10, DATA_DIR_FROM_HOST, + (1<<1) | (0xf<<2) | (3<<7), 1, + "WRITE(10)"); + if (reply == 0) + reply = do_write(common); + break; + + case SCSI_WRITE12: + common->data_size_from_cmnd = + get_unaligned_be32(&common->cmnd[6]) << 9; + reply = check_command(common, 12, DATA_DIR_FROM_HOST, + (1<<1) | (0xf<<2) | (0xf<<6), 1, + "WRITE(12)"); + if (reply == 0) + reply = do_write(common); + break; + + /* Some mandatory commands that we recognize but don't implement. + * They don't mean much in this setting. It's left as an exercise + * for anyone interested to implement RESERVE and RELEASE in terms + * of Posix locks. */ + case SCSI_FORMAT: + case SCSI_RELEASE: + case SCSI_RESERVE: + case SCSI_SEND_DIAG: + /* Fall through */ + + default: +unknown_cmnd: + common->data_size_from_cmnd = 0; + sprintf(unknown, "Unknown x%02x", common->cmnd[0]); + reply = check_command(common, common->cmnd_size, + DATA_DIR_UNKNOWN, 0xff, 0, unknown); + if (reply == 0) { + curlun->sense_data = SS_INVALID_COMMAND; + reply = -EINVAL; + } + break; + } + up_read(&common->filesem); + + if (reply == -EPIPE) + return -EPIPE; + + /* Set up the single reply buffer for finish_reply() */ + if (reply == -EINVAL) + reply = 0; /* Error reply length */ + if (reply >= 0 && common->data_dir == DATA_DIR_TO_HOST) { + reply = min((u32) reply, common->data_size_from_cmnd); + bh->inreq->length = reply; + bh->state = BUF_STATE_FULL; + common->residue -= reply; + } /* Otherwise it's already set */ + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + struct usb_request *req = bh->outreq; + struct bulk_cb_wrap *cbw = req->buf; + struct fsg_common *common = fsg->common; + + /* Was this a real packet? Should it be ignored? */ + if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags)) + return -EINVAL; + + /* Is the CBW valid? */ + if (req->actual != US_BULK_CB_WRAP_LEN || + cbw->Signature != cpu_to_le32( + US_BULK_CB_SIGN)) { + DBG(fsg, "invalid CBW: len %u sig 0x%x\n", + req->actual, + le32_to_cpu(cbw->Signature)); + + /* The Bulk-only spec says we MUST stall the IN endpoint + * (6.6.1), so it's unavoidable. It also says we must + * retain this state until the next reset, but there's + * no way to tell the controller driver it should ignore + * Clear-Feature(HALT) requests. + * + * We aren't required to halt the OUT endpoint; instead + * we can simply accept and discard any data received + * until the next reset. */ + wedge_bulk_in_endpoint(fsg); + set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); + return -EINVAL; + } + + /* Is the CBW meaningful? */ + if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~US_BULK_FLAG_IN || + cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) { + DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, " + "cmdlen %u\n", + cbw->Lun, cbw->Flags, cbw->Length); + + /* We can do anything we want here, so let's stall the + * bulk pipes if we are allowed to. */ + if (common->can_stall) { + fsg_set_halt(fsg, fsg->bulk_out); + halt_bulk_in_endpoint(fsg); + } + return -EINVAL; + } + + /* Save the command for later */ + common->cmnd_size = cbw->Length; + memcpy(common->cmnd, cbw->CDB, common->cmnd_size); + if (cbw->Flags & US_BULK_FLAG_IN) + common->data_dir = DATA_DIR_TO_HOST; + else + common->data_dir = DATA_DIR_FROM_HOST; + common->data_size = le32_to_cpu(cbw->DataTransferLength); + if (common->data_size == 0) + common->data_dir = DATA_DIR_NONE; + common->lun = cbw->Lun; + common->tag = cbw->Tag; + return 0; +} + + +static int get_next_command(struct fsg_common *common) +{ + struct fsg_buffhd *bh; + int rc = 0; + + /* Wait for the next buffer to become available */ + bh = common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(common); + if (rc) + return rc; + } + + /* Queue a request to read a Bulk-only CBW */ + set_bulk_out_req_length(common, bh, US_BULK_CB_WRAP_LEN); + bh->outreq->short_not_ok = 1; + START_TRANSFER_OR(common, bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state) + /* Don't know what to do if common->fsg is NULL */ + return -EIO; + + /* We will drain the buffer in software, which means we + * can reuse it for the next filling. No need to advance + * next_buffhd_to_fill. */ + + /* Wait for the CBW to arrive */ + while (bh->state != BUF_STATE_FULL) { + rc = sleep_thread(common); + if (rc) + return rc; + } + + rc = fsg_is_set(common) ? received_cbw(common->fsg, bh) : -EIO; + bh->state = BUF_STATE_EMPTY; + + return rc; +} + + +/*-------------------------------------------------------------------------*/ + +static int enable_endpoint(struct fsg_common *common, struct usb_ep *ep) +{ + int rc; + + ep->driver_data = common; + rc = usb_ep_enable(ep); + if (rc) + ERROR(common, "can't enable %s, result %d\n", ep->name, rc); + return rc; +} + +static int alloc_request(struct fsg_common *common, struct usb_ep *ep, + struct usb_request **preq) +{ + *preq = usb_ep_alloc_request(ep); + if (*preq) + return 0; + ERROR(common, "can't allocate request for %s\n", ep->name); + return -ENOMEM; +} + +/* Reset interface setting and re-init endpoint state (toggle etc). */ +static int do_set_interface(struct fsg_common *common, struct fsg_dev *new_fsg) +{ + struct fsg_dev *fsg; + int i, rc = 0; + + if (common->running) + DBG(common, "reset interface\n"); + +reset: + /* Deallocate the requests */ + if (common->fsg) { + fsg = common->fsg; + + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + struct fsg_buffhd *bh = &common->buffhds[i]; + + if (bh->inreq) { + usb_ep_free_request(fsg->bulk_in, bh->inreq); + bh->inreq = NULL; + } + if (bh->outreq) { + usb_ep_free_request(fsg->bulk_out, bh->outreq); + bh->outreq = NULL; + } + } + + /* Disable the endpoints */ + if (fsg->bulk_in_enabled) { + usb_ep_disable(fsg->bulk_in); + fsg->bulk_in_enabled = 0; + } + if (fsg->bulk_out_enabled) { + usb_ep_disable(fsg->bulk_out); + fsg->bulk_out_enabled = 0; + } + + common->fsg = NULL; + } + + common->running = 0; + if (!new_fsg || rc) + return rc; + + common->fsg = new_fsg; + fsg = common->fsg; + + /* Enable the endpoints */ + rc = config_ep_by_speed(common->gadget, &(fsg->function), fsg->bulk_in); + if (rc) + goto reset; + rc = enable_endpoint(common, fsg->bulk_in); + if (rc) + goto reset; + fsg->bulk_in_enabled = 1; + + rc = config_ep_by_speed(common->gadget, &(fsg->function), + fsg->bulk_out); + if (rc) + goto reset; + rc = enable_endpoint(common, fsg->bulk_out); + if (rc) + goto reset; + fsg->bulk_out_enabled = 1; + common->bulk_out_maxpacket = 512; + clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); + + /* Allocate the requests */ + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + struct fsg_buffhd *bh = &common->buffhds[i]; + + rc = alloc_request(common, fsg->bulk_in, &bh->inreq); + if (rc) + goto reset; + rc = alloc_request(common, fsg->bulk_out, &bh->outreq); + if (rc) + goto reset; + bh->inreq->buf = bh->outreq->buf = bh->buf; + bh->inreq->context = bh->outreq->context = bh; + bh->inreq->complete = bulk_in_complete; + bh->outreq->complete = bulk_out_complete; + } + + common->running = 1; + + return rc; +} + + +/****************************** ALT CONFIGS ******************************/ + + +static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct fsg_dev *fsg = fsg_from_func(f); + fsg->common->new_fsg = fsg; + raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); + return 0; +} + +static void fsg_disable(struct usb_function *f) +{ + struct fsg_dev *fsg = fsg_from_func(f); + fsg->common->new_fsg = NULL; + raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); +} + +/*-------------------------------------------------------------------------*/ + +static void handle_exception(struct fsg_common *common) +{ + int i; + struct fsg_buffhd *bh; + enum fsg_state old_state; + struct fsg_lun *curlun; + unsigned int exception_req_tag; + + /* Cancel all the pending transfers */ + if (common->fsg) { + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + bh = &common->buffhds[i]; + if (bh->inreq_busy) + usb_ep_dequeue(common->fsg->bulk_in, bh->inreq); + if (bh->outreq_busy) + usb_ep_dequeue(common->fsg->bulk_out, + bh->outreq); + } + + /* Wait until everything is idle */ + for (;;) { + int num_active = 0; + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + bh = &common->buffhds[i]; + num_active += bh->inreq_busy + bh->outreq_busy; + } + if (num_active == 0) + break; + if (sleep_thread(common)) + return; + } + + /* Clear out the controller's fifos */ + if (common->fsg->bulk_in_enabled) + usb_ep_fifo_flush(common->fsg->bulk_in); + if (common->fsg->bulk_out_enabled) + usb_ep_fifo_flush(common->fsg->bulk_out); + } + + /* Reset the I/O buffer states and pointers, the SCSI + * state, and the exception. Then invoke the handler. */ + + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + bh = &common->buffhds[i]; + bh->state = BUF_STATE_EMPTY; + } + common->next_buffhd_to_fill = &common->buffhds[0]; + common->next_buffhd_to_drain = &common->buffhds[0]; + exception_req_tag = common->exception_req_tag; + old_state = common->state; + + report_exception("handling", old_state); + + if (old_state == FSG_STATE_ABORT_BULK_OUT) + common->state = FSG_STATE_STATUS_PHASE; + else { + for (i = 0; i < common->nluns; ++i) { + curlun = &common->luns[i]; + curlun->sense_data = SS_NO_SENSE; + curlun->info_valid = 0; + } + common->state = FSG_STATE_IDLE; + } + + /* Carry out any extra actions required for the exception */ + switch (old_state) { + case FSG_STATE_ABORT_BULK_OUT: + send_status(common); + + if (common->state == FSG_STATE_STATUS_PHASE) + common->state = FSG_STATE_IDLE; + break; + + case FSG_STATE_RESET: + /* In case we were forced against our will to halt a + * bulk endpoint, clear the halt now. (The SuperH UDC + * requires this.) */ + if (!fsg_is_set(common)) + break; + if (test_and_clear_bit(IGNORE_BULK_OUT, + &common->fsg->atomic_bitflags)) + usb_ep_clear_halt(common->fsg->bulk_in); + + if (common->ep0_req_tag == exception_req_tag) + ep0_queue(common); /* Complete the status stage */ + + break; + + case FSG_STATE_CONFIG_CHANGE: + do_set_interface(common, common->new_fsg); + break; + + case FSG_STATE_EXIT: + case FSG_STATE_TERMINATED: + do_set_interface(common, NULL); /* Free resources */ + common->state = FSG_STATE_TERMINATED; /* Stop the thread */ + break; + + case FSG_STATE_INTERFACE_CHANGE: + case FSG_STATE_DISCONNECT: + case FSG_STATE_COMMAND_PHASE: + case FSG_STATE_DATA_PHASE: + case FSG_STATE_STATUS_PHASE: + case FSG_STATE_IDLE: + break; + } +} + +/*-------------------------------------------------------------------------*/ + +static void fsg_main_thread(void *fsg_) +{ + struct fsg_dev *fsg = fsg_dev_get(fsg_); + struct fsg_common *common = fsg->common; + struct f_ums_opts *opts = f_ums_opts_get(common->opts); + struct fsg_buffhd *bh; + unsigned i; + int ret = 0; + + + /* The main loop */ + while (common->state != FSG_STATE_TERMINATED) { + if (exception_in_progress(common)) { + handle_exception(common); + continue; + } + + if (!common->running) { + ret = sleep_thread(common); + if (ret) + break; + continue; + } + + ret = get_next_command(common); + if (ret) + continue; + + if (!exception_in_progress(common)) + common->state = FSG_STATE_DATA_PHASE; + + if (do_scsi_command(common) || finish_reply(common)) + continue; + + if (!exception_in_progress(common)) + common->state = FSG_STATE_STATUS_PHASE; + + if (send_status(common)) + continue; + + if (!exception_in_progress(common)) + common->state = FSG_STATE_IDLE; + } + + if (ret && ret != -ERESTARTSYS) + pr_warn("%s: error %pe\n", __func__, ERR_PTR(ret)); + + usb_free_all_descriptors(&fsg->function); + + for (i = 0; i < ums_count; i++) + close(ums[i].fd); + + bh = common->buffhds; + i = FSG_NUM_BUFFERS; + + do { + dma_free(bh->buf); + } while (++bh, --i); + + ums_count = 0; + ums_files = NULL; + + f_ums_opts_put(opts); + fsg_dev_put(fsg); +} + +static void fsg_common_release(struct fsg_common *common); + +static struct fsg_common *fsg_common_setup(struct f_ums_opts *opts) +{ + struct fsg_common *common; + + /* Allocate? */ + common = calloc(sizeof(*common), 1); + if (!common) + return NULL; + + common->ops = NULL; + common->private_data = NULL; + common->opts = opts; + + return common; +} + +static int fsg_common_init(struct fsg_common *common, + struct usb_composite_dev *cdev) +{ + struct usb_gadget *gadget = cdev->gadget; + struct file_list_entry *fentry; + struct fsg_buffhd *bh; + int nluns, i, rc; + + ums_count = 0; + + common->gadget = gadget; + common->ep0 = gadget->ep0; + common->ep0req = cdev->req; + + thread_task = bthread_run(fsg_main_thread, common->fsg, "mass-storage-gadget"); + if (IS_ERR(thread_task)) + return PTR_ERR(thread_task); + + file_list_detect_all(ums_files); + + file_list_for_each_entry(ums_files, fentry) { + unsigned flags = O_RDWR; + struct stat st; + int fd; + + if (fentry->flags) { + pr_err("flags not supported\n"); + rc = -ENOSYS; + goto close; + } + + fd = open(fentry->filename, flags); + if (fd < 0) { + pr_err("open('%s') failed: %pe\n", + fentry->filename, ERR_PTR(fd)); + rc = fd; + goto close; + } + + rc = fstat(fd, &st); + if (rc < 0) { + pr_err("stat('%s') failed: %pe\n", + fentry->filename, ERR_PTR(rc)); + goto close; + } + + if (st.st_size % SECTOR_SIZE != 0) { + pr_err("exporting '%s' failed: invalid block size\n", + fentry->filename); + goto close; + } + + ums[ums_count].fd = fd; + ums[ums_count].num_sectors = st.st_size / SECTOR_SIZE; + + strlcpy(ums[ums_count].name, fentry->name, sizeof(ums[ums_count].name)); + + DBG(common, "LUN %d, %s sector_count %#x\n", + ums_count, fentry->name, ums[ums_count].num_sectors); + + ums_count++; + } + + /* Find out how many LUNs there should be */ + nluns = ums_count; + if (nluns < 1 || nluns > FSG_MAX_LUNS) { + pr_warn("invalid number of LUNs: %u\n", nluns); + rc = -EINVAL; + goto close; + } + + /* Maybe allocate device-global string IDs, and patch descriptors */ + if (fsg_strings[FSG_STRING_INTERFACE].id == 0) { + rc = usb_string_id(cdev); + if (unlikely(rc < 0)) + goto error_release; + fsg_strings[FSG_STRING_INTERFACE].id = rc; + fsg_intf_desc.iInterface = rc; + } + + common->nluns = nluns; + + for (i = 0; i < nluns; i++) { + common->luns[i].removable = 1; + + rc = fsg_lun_open(&common->luns[i], ums[i].num_sectors, ""); + if (rc) + goto error_luns; + } + common->lun = 0; + + /* Data buffers cyclic list */ + bh = common->buffhds; + + i = FSG_NUM_BUFFERS; + goto buffhds_first_it; + do { + bh->next = bh + 1; + ++bh; +buffhds_first_it: + bh->inreq_busy = 0; + bh->outreq_busy = 0; + bh->buf = dma_alloc(FSG_BUFLEN); + if (unlikely(!bh->buf)) { + rc = -ENOMEM; + goto error_release; + } + } while (--i); + bh->next = common->buffhds; + + snprintf(common->inquiry_string, sizeof common->inquiry_string, + "%-8s%-16s%04x", + "Linux ", + "File-Store Gadget", + 0xffff); + + /* Some peripheral controllers are known not to be able to + * halt bulk endpoints correctly. If one of them is present, + * disable stalls. + */ + + /* Information */ + DBG(common, FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n"); + DBG(common, "Number of LUNs=%d\n", common->nluns); + + return 0; + +error_luns: + common->nluns = i + 1; +error_release: + common->state = FSG_STATE_TERMINATED; /* The thread is dead */ + fsg_common_release(common); +close: + for (i = 0; i < ums_count; i++) + close(ums[i].fd); + return rc; +} + +static void fsg_common_release(struct fsg_common *common) +{ + /* If the thread isn't already dead, tell it to exit now */ + if (common->state != FSG_STATE_TERMINATED) { + raise_exception(common, FSG_STATE_EXIT); + } + + bthread_cancel(thread_task); +} + + +static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct fsg_dev *fsg = fsg_from_func(f); + + DBG(fsg, "unbind\n"); + + if (fsg->common->fsg == fsg) { + fsg->common->new_fsg = NULL; + raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); + } + + fsg_common_release(fsg->common); +} + +static int fsg_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct fsg_dev *fsg = fsg_from_func(f); + struct usb_gadget *gadget = c->cdev->gadget; + int ret; + struct usb_ep *ep; + unsigned max_burst; + struct fsg_common *common = fsg->common; + + if (!ums_files) { + struct f_ums_opts *opts = container_of(f->fi, struct f_ums_opts, func_inst); + + ums_files = opts->files; + } + + fsg->gadget = gadget; + + DBG(fsg, "bind\n"); + + ret = fsg_common_init(common, c->cdev); + if (ret) + goto remove_ums_files; + + /* New interface */ + ret = usb_interface_id(c, f); + if (ret < 0) + goto fsg_common_release; + fsg_intf_desc.bInterfaceNumber = ret; + fsg->interface_number = ret; + + ret = -EOPNOTSUPP; + + /* Find all the endpoints we will use */ + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); + if (!ep) + goto autoconf_fail; + + ep->driver_data = common; /* claim the endpoint */ + fsg->bulk_in = ep; + + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc); + if (!ep) + goto autoconf_fail; + + ep->driver_data = common; /* claim the endpoint */ + fsg->bulk_out = ep; + + /* Assume endpoint addresses are the same for both speeds */ + fsg_hs_bulk_in_desc.bEndpointAddress = + fsg_fs_bulk_in_desc.bEndpointAddress; + fsg_hs_bulk_out_desc.bEndpointAddress = + fsg_fs_bulk_out_desc.bEndpointAddress; + + /* Calculate bMaxBurst, we know packet size is 1024 */ + max_burst = min_t(unsigned, FSG_BUFLEN / 1024, 15); + + fsg_ss_bulk_in_desc.bEndpointAddress = + fsg_fs_bulk_in_desc.bEndpointAddress; + fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst; + + fsg_ss_bulk_out_desc.bEndpointAddress = + fsg_fs_bulk_out_desc.bEndpointAddress; + fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst; + + /* Copy descriptors */ + return usb_assign_descriptors(f, fsg_fs_function, fsg_hs_function, + fsg_ss_function, fsg_ss_function); + +autoconf_fail: + ERROR(fsg, "unable to autoconfigure all endpoints\n"); +fsg_common_release: + fsg_common_release(common); +remove_ums_files: + ums_files = NULL; + + return ret; +} + + +/****************************** ADD FUNCTION ******************************/ + +static struct usb_gadget_strings *fsg_strings_array[] = { + &fsg_stringtab, + NULL, +}; + +static void fsg_free(struct usb_function *f) +{ + struct fsg_dev *fsg; + + fsg = container_of(f, struct fsg_dev, function); + + fsg_dev_put(fsg); +} + +static struct usb_function *fsg_alloc(struct usb_function_instance *fi) +{ + struct f_ums_opts *opts = fsg_opts_from_func_inst(fi); + struct fsg_common *common = opts->common; + struct fsg_dev *fsg; + + fsg = kzalloc(sizeof(*fsg), GFP_KERNEL); + if (!fsg) + return ERR_PTR(-ENOMEM); + + fsg->function.name = FSG_DRIVER_DESC; + fsg->function.strings = fsg_strings_array; + /* descriptors are per-instance copies */ + fsg->function.bind = fsg_bind; + fsg->function.set_alt = fsg_set_alt; + fsg->function.setup = fsg_setup; + fsg->function.disable = fsg_disable; + fsg->function.unbind = fsg_unbind; + fsg->function.free_func = fsg_free; + + fsg->common = common; + common->fsg = fsg_dev_get(fsg); + + return &fsg->function; +} + +static void fsg_free_instance(struct usb_function_instance *fi) +{ + struct f_ums_opts *opts = fsg_opts_from_func_inst(fi); + + f_ums_opts_put(opts); +} + +static struct usb_function_instance *fsg_alloc_inst(void) +{ + struct f_ums_opts *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + opts->func_inst.free_func_inst = fsg_free_instance; + + opts->common = fsg_common_setup(opts); + if (!opts->common) { + free(opts); + return ERR_PTR(-ENOMEM); + } + + f_ums_opts_get(opts); + + return &opts->func_inst; +} + +DECLARE_USB_FUNCTION_INIT(ums, fsg_alloc_inst, fsg_alloc); diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/function/f_serial.c index 39c44448c4..a768c580ea 100644 --- a/drivers/usb/gadget/f_serial.c +++ b/drivers/usb/gadget/function/f_serial.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * f_serial.c - generic USB serial function driver * * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) * Copyright (C) 2008 by David Brownell * Copyright (C) 2008 by Nokia Corporation - * - * This software is distributed under the terms of the GNU General - * Public License ("GPL") as published by the Free Software Foundation, - * either version 2 of that License or (at your option) any later version. */ #include <common.h> @@ -15,7 +12,6 @@ #include <linux/err.h> #include "u_serial.h" -#include "gadget_chips.h" /* @@ -235,7 +231,7 @@ static int gser_bind(struct usb_configuration *c, struct usb_function *f) gser_ss_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress; status = usb_assign_descriptors(f, gser_fs_function, gser_hs_function, - gser_ss_function); + gser_ss_function, gser_ss_function); if (status) goto fail; DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n", diff --git a/drivers/usb/gadget/function/storage_common.c b/drivers/usb/gadget/function/storage_common.c new file mode 100644 index 0000000000..60e0994235 --- /dev/null +++ b/drivers/usb/gadget/function/storage_common.c @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * storage_common.c -- Common definitions for mass storage functionality + * + * Copyright (C) 2003-2008 Alan Stern + * Copyeight (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz (m.nazarewicz@samsung.com) + * + * Ported to u-boot: + * Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * + * Code refactoring & cleanup: + * Łukasz Majewski <l.majewski@samsung.com> + */ + +#include "storage_common.h" + +/* + * This file requires the following identifiers used in USB strings to + * be defined (each of type pointer to char): + * - fsg_string_manufacturer -- name of the manufacturer + * - fsg_string_product -- name of the product + * - fsg_string_serial -- product's serial + * - fsg_string_config -- name of the configuration + * - fsg_string_interface -- name of the interface + * The first four are only needed when FSG_DESCRIPTORS_DEVICE_STRINGS + * macro is defined prior to including this file. + */ + +/* There is only one interface. */ + +struct usb_interface_descriptor fsg_intf_desc = { + .bLength = sizeof fsg_intf_desc, + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 2, /* Adjusted during fsg_bind() */ + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = USB_SC_SCSI, /* Adjusted during fsg_bind() */ + .bInterfaceProtocol = USB_PR_BULK, /* Adjusted during fsg_bind() */ + .iInterface = FSG_STRING_INTERFACE, +}; + +/* + * Three full-speed endpoint descriptors: bulk-in, bulk-out, and + * interrupt-in. + */ + +struct usb_endpoint_descriptor fsg_fs_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + /* wMaxPacketSize set by autoconfiguration */ +}; + +struct usb_endpoint_descriptor fsg_fs_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + /* wMaxPacketSize set by autoconfiguration */ +}; + +struct usb_descriptor_header *fsg_fs_function[] = { + (struct usb_descriptor_header *) &fsg_intf_desc, + (struct usb_descriptor_header *) &fsg_fs_bulk_in_desc, + (struct usb_descriptor_header *) &fsg_fs_bulk_out_desc, + NULL, +}; + +/* + * USB 2.0 devices need to expose both high speed and full speed + * descriptors, unless they only run at full speed. + * + * That means alternate endpoint descriptors (bigger packets) + * and a "device qualifier" ... plus more construction options + * for the configuration descriptor. + */ +struct usb_endpoint_descriptor fsg_hs_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +struct usb_endpoint_descriptor fsg_hs_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), + .bInterval = 1, /* NAK every 1 uframe */ +}; + +struct usb_descriptor_header *fsg_hs_function[] = { + (struct usb_descriptor_header *) &fsg_intf_desc, + (struct usb_descriptor_header *) &fsg_hs_bulk_in_desc, + (struct usb_descriptor_header *) &fsg_hs_bulk_out_desc, + NULL, +}; + +struct usb_endpoint_descriptor fsg_ss_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = { + .bLength = sizeof(fsg_ss_bulk_in_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /*.bMaxBurst = DYNAMIC, */ +}; + +struct usb_endpoint_descriptor fsg_ss_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = { + .bLength = sizeof(fsg_ss_bulk_in_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /*.bMaxBurst = DYNAMIC, */ +}; + +struct usb_descriptor_header *fsg_ss_function[] = { + (struct usb_descriptor_header *) &fsg_intf_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_in_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_out_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc, + NULL, +}; +EXPORT_SYMBOL_GPL(fsg_ss_function); + +/* Maxpacket and other transfer characteristics vary by speed. */ +struct usb_endpoint_descriptor * +fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, + struct usb_endpoint_descriptor *hs) +{ + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return hs; + return fs; +} + +/*-------------------------------------------------------------------------*/ + +/* + * If the next two routines are called while the gadget is registered, + * the caller must own fsg->filesem for writing. + */ + +int fsg_lun_open(struct fsg_lun *curlun, unsigned int num_sectors, + const char *filename) +{ + int ro; + + /* R/W if we can, R/O if we must */ + ro = curlun->initially_ro; + + curlun->ro = ro; + curlun->file_length = num_sectors << 9; + curlun->num_sectors = num_sectors; + debug("open backing file: %s\n", filename); + + return 0; +} + +void fsg_lun_close(struct fsg_lun *curlun) +{ +} + +/*-------------------------------------------------------------------------*/ + +/* + * Sync the file data, don't bother with the metadata. + * This code was copied from fs/buffer.c:sys_fdatasync(). + */ +int fsg_lun_fsync_sub(struct fsg_lun *curlun) +{ + return 0; +} + +void store_cdrom_address(u8 *dest, int msf, u32 addr) +{ + if (msf) { + /* Convert to Minutes-Seconds-Frames */ + addr >>= 2; /* Convert to 2048-byte frames */ + addr += 2*75; /* Lead-in occupies 2 seconds */ + dest[3] = addr % 75; /* Frames */ + addr /= 75; + dest[2] = addr % 60; /* Seconds */ + addr /= 60; + dest[1] = addr; /* Minutes */ + dest[0] = 0; /* Reserved */ + } else { + /* Absolute sector */ + put_unaligned_be32(addr, dest); + } +} + +/*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/function/storage_common.h b/drivers/usb/gadget/function/storage_common.h new file mode 100644 index 0000000000..29afe77685 --- /dev/null +++ b/drivers/usb/gadget/function/storage_common.h @@ -0,0 +1,251 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef USB_STORAGE_COMMON_H +#define USB_STORAGE_COMMON_H + +#include <driver.h> +#include <linux/usb/storage.h> +#include <asm/unaligned.h> +#include <linux/usb/mass_storage.h> + +#ifndef DEBUG +#undef VERBOSE_DEBUG +#undef DUMP_MSGS +#endif /* !DEBUG */ + +#define VLDBG(lun, fmt, args...) dev_vdbg(&(lun)->dev, fmt, ## args) +#define LDBG(lun, fmt, args...) dev_dbg (&(lun)->dev, fmt, ## args) +#define LERROR(lun, fmt, args...) dev_err (&(lun)->dev, fmt, ## args) +#define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args) +#define LINFO(lun, fmt, args...) dev_info(&(lun)->dev, fmt, ## args) + +/* + * Keep those macros in sync with those in + * include/linux/usb/composite.h or else GCC will complain. If they + * are identical (the same names of arguments, white spaces in the + * same places) GCC will allow redefinition otherwise (even if some + * white space is removed or added) warning will be issued. + * + * Those macros are needed here because File Storage Gadget does not + * include the composite.h header. For composite gadgets those macros + * are redundant since composite.h is included any way. + * + * One could check whether those macros are already defined (which + * would indicate composite.h had been included) or not (which would + * indicate we were in FSG) but this is not done because a warning is + * desired if definitions here differ from the ones in composite.h. + * + * We want the definitions to match and be the same in File Storage + * Gadget as well as Mass Storage Function (and so composite gadgets + * using MSF). If someone changes them in composite.h it will produce + * a warning in this file when building MSF. + */ + +#define DBG(d, fmt, args...) \ + dev_dbg(&(d)->gadget->dev , fmt , ## args) +#define VDBG(d, fmt, args...) \ + dev_vdbg(&(d)->gadget->dev , fmt , ## args) +#define ERROR(d, fmt, args...) \ + dev_err(&(d)->gadget->dev , fmt , ## args) +#define WARNING(d, fmt, args...) \ + dev_warn(&(d)->gadget->dev , fmt , ## args) +#define INFO(d, fmt, args...) \ + dev_info(&(d)->gadget->dev , fmt , ## args) + +#ifdef DUMP_MSGS + +/* dump_msg(fsg, const char * label, const u8 * buf, unsigned length); */ +# define dump_msg(fsg, label, buf, length) do { \ + if (length < 512) { \ + DBG(fsg, "%s, length %u:\n", label, length); \ + print_hex_dump("", DUMP_PREFIX_OFFSET, \ + 16, 1, buf, length, 0); \ + } \ +} while (0) + +# define dump_cdb(fsg) do { } while (0) + +#else + +# define dump_msg(fsg, /* const char * */ label, \ + /* const u8 * */ buf, /* unsigned */ length) do { } while (0) + +# ifdef VERBOSE_DEBUG + +# define dump_cdb(fsg) \ + print_hex_dump("SCSI CDB: ", DUMP_PREFIX_NONE, \ + 16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0) \ + +# else + +# define dump_cdb(fsg) do { } while (0) + +# endif /* VERBOSE_DEBUG */ + +#endif /* DUMP_MSGS */ + +/* + * Thanks to NetChip Technologies for donating this product ID. + * + * DO NOT REUSE THESE IDs with any other driver!! Ever!! + * Instead: allocate your own, using normal USB-IF procedures. + */ + +#define FSG_VENDOR_ID 0x0525 /* NetChip */ +#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */ + +/* Length of a SCSI Command Data Block */ +#define MAX_COMMAND_SIZE 16 + +/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */ +#define SS_NO_SENSE 0 +#define SS_COMMUNICATION_FAILURE 0x040800 +#define SS_INVALID_COMMAND 0x052000 +#define SS_INVALID_FIELD_IN_CDB 0x052400 +#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100 +#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500 +#define SS_MEDIUM_NOT_PRESENT 0x023a00 +#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302 +#define SS_NOT_READY_TO_READY_TRANSITION 0x062800 +#define SS_RESET_OCCURRED 0x062900 +#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900 +#define SS_UNRECOVERED_READ_ERROR 0x031100 +#define SS_WRITE_ERROR 0x030c02 +#define SS_WRITE_PROTECTED 0x072700 + +#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */ +#define ASC(x) ((u8) ((x) >> 8)) +#define ASCQ(x) ((u8) (x)) + +/*-------------------------------------------------------------------------*/ + +struct fsg_lun { + loff_t file_length; + loff_t num_sectors; + + unsigned int initially_ro:1; + unsigned int ro:1; + unsigned int removable:1; + unsigned int cdrom:1; + unsigned int prevent_medium_removal:1; + unsigned int registered:1; + unsigned int info_valid:1; + unsigned int nofua:1; + + u32 sense_data; + u32 sense_data_info; + u32 unit_attention_data; + + struct device dev; +}; + +#define fsg_lun_is_open(curlun) ((curlun)->filp != NULL) + +/* Big enough to hold our biggest descriptor */ +#define EP0_BUFSIZE 256 +#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */ + +/* Number of buffers we will use. 2 is enough for double-buffering */ +#define FSG_NUM_BUFFERS 2 + +/* Default size of buffer length. */ +#define FSG_BUFLEN ((u32)131072) + +/* Maximal number of LUNs supported in mass storage function */ +#define FSG_MAX_LUNS 8 + +enum fsg_buffer_state { + BUF_STATE_EMPTY = 0, + BUF_STATE_FULL, + BUF_STATE_BUSY +}; + +/* + * When FSG_BUFFHD_STATIC_BUFFER is defined when this file is included + * the fsg_buffhd structure's buf field will be an array of FSG_BUFLEN + * characters rather then a pointer to void. + */ + +struct fsg_buffhd { + void *buf; + enum fsg_buffer_state state; + struct fsg_buffhd *next; + + /* + * The NetChip 2280 is faster, and handles some protocol faults + * better, if we don't submit any short bulk-out read requests. + * So we will record the intended request length here. + */ + unsigned int bulk_out_intended_length; + + struct usb_request *inreq; + int inreq_busy; + struct usb_request *outreq; + int outreq_busy; +}; + +enum fsg_state { + /* This one isn't used anywhere */ + FSG_STATE_COMMAND_PHASE = -10, + FSG_STATE_DATA_PHASE, + FSG_STATE_STATUS_PHASE, + + FSG_STATE_IDLE = 0, + FSG_STATE_ABORT_BULK_OUT, + FSG_STATE_RESET, + FSG_STATE_INTERFACE_CHANGE, + FSG_STATE_CONFIG_CHANGE, + FSG_STATE_DISCONNECT, + FSG_STATE_EXIT, + FSG_STATE_TERMINATED +}; + +enum data_direction { + DATA_DIR_UNKNOWN = 0, + DATA_DIR_FROM_HOST, + DATA_DIR_TO_HOST, + DATA_DIR_NONE +}; + +/*-------------------------------------------------------------------------*/ + +static inline u32 get_unaligned_be24(u8 *buf) +{ + return 0xffffff & (u32) get_unaligned_be32(buf - 1); +} + +/*-------------------------------------------------------------------------*/ + +enum { + FSG_STRING_INTERFACE +}; + +/*-------------------------------------------------------------------------*/ + +extern struct usb_interface_descriptor fsg_intf_desc; + +extern struct usb_endpoint_descriptor fsg_fs_bulk_in_desc; +extern struct usb_endpoint_descriptor fsg_fs_bulk_out_desc; +extern struct usb_descriptor_header *fsg_fs_function[]; + +extern struct usb_endpoint_descriptor fsg_hs_bulk_in_desc; +extern struct usb_endpoint_descriptor fsg_hs_bulk_out_desc; +extern struct usb_descriptor_header *fsg_hs_function[]; + +extern struct usb_endpoint_descriptor fsg_ss_bulk_in_desc; +extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc; +extern struct usb_endpoint_descriptor fsg_ss_bulk_out_desc; +extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc; +extern struct usb_descriptor_header *fsg_ss_function[]; + +int fsg_lun_open(struct fsg_lun *curlun, unsigned int num_sectors, + const char *filename); +void fsg_lun_close(struct fsg_lun *curlun); + +struct usb_endpoint_descriptor * +fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, + struct usb_endpoint_descriptor *hs); +int fsg_lun_fsync_sub(struct fsg_lun *curlun); +void store_cdrom_address(u8 *dest, int msf, u32 addr); + +#endif /* USB_STORAGE_COMMON_H */ diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 140346e770..ca4e77c5ff 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * u_serial.c - utilities for USB gadget "serial port"/TTY support * @@ -9,17 +10,13 @@ * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com) * Copyright (C) 2000 Peter Berger (pberger@brimson.com) * Copyright (C) 2000 Al Borchers (alborchers@steinerpoint.com) - * - * This software is distributed under the terms of the GNU General - * Public License ("GPL") as published by the Free Software Foundation, - * either version 2 of that License or (at your option) any later version. */ /* #define VERBOSE_DEBUG */ #include <common.h> #include <complete.h> -#include <usb/cdc.h> +#include <linux/usb/cdc.h> #include <kfifo.h> #include <clock.h> #include <linux/err.h> @@ -158,12 +155,13 @@ static void gs_read_complete(struct usb_ep *ep, struct usb_request *req) { struct gs_port *port = ep->driver_data; + list_add_tail(&req->list, &port->read_pool); + port->read_nb_queued--; + if (req->status == -ESHUTDOWN) return; kfifo_put(port->recv_fifo, req->buf, req->actual); - list_add_tail(&req->list, &port->read_pool); - port->read_nb_queued--; gs_start_rx(port); } diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/function/u_serial.h index 72772dabd5..44fcace030 100644 --- a/drivers/usb/gadget/u_serial.h +++ b/drivers/usb/gadget/function/u_serial.h @@ -1,19 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * u_serial.h - interface to USB gadget "serial port"/TTY utilities * * Copyright (C) 2008 David Brownell * Copyright (C) 2008 by Nokia Corporation - * - * This software is distributed under the terms of the GNU General - * Public License ("GPL") as published by the Free Software Foundation, - * either version 2 of that License or (at your option) any later version. */ #ifndef __U_SERIAL_H #define __U_SERIAL_H -#include <usb/composite.h> -#include <usb/cdc.h> +#include <linux/usb/composite.h> +#include <linux/usb/cdc.h> #define MAX_U_SERIAL_PORTS 4 diff --git a/drivers/usb/gadget/functions.c b/drivers/usb/gadget/functions.c index 3b5d4dfc28..7a57915007 100644 --- a/drivers/usb/gadget/functions.c +++ b/drivers/usb/gadget/functions.c @@ -1,7 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <linux/err.h> -#include <usb/composite.h> +#include <linux/usb/composite.h> static LIST_HEAD(func_list); diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h deleted file mode 100644 index c41336f698..0000000000 --- a/drivers/usb/gadget/gadget_chips.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * USB device controllers have lots of quirks. Use these macros in - * gadget drivers or other code that needs to deal with them, and which - * autoconfigures instead of using early binding to the hardware. - * - * This SHOULD eventually work like the ARM mach_is_*() stuff, driven by - * some config file that gets updated as new hardware is supported. - * (And avoiding all runtime comparisons in typical one-choice configs!) - * - * NOTE: some of these controller drivers may not be available yet. - * Some are available on 2.4 kernels; several are available, but not - * yet pushed in the 2.6 mainline tree. - */ - -#ifndef __GADGET_CHIPS_H -#define __GADGET_CHIPS_H - -#include <usb/gadget.h> - -/* - * NOTICE: the entries below are alphabetical and should be kept - * that way. - * - * Always be sure to add new entries to the correct position or - * accept the bashing later. - * - * If you have forgotten the alphabetical order let VIM/EMACS - * do that for you. - */ -#define gadget_is_at91(g) (!strcmp("at91_udc", (g)->name)) -#define gadget_is_goku(g) (!strcmp("goku_udc", (g)->name)) -#define gadget_is_musbhdrc(g) (!strcmp("musb-hdrc", (g)->name)) -#define gadget_is_net2280(g) (!strcmp("net2280", (g)->name)) -#define gadget_is_pxa(g) (!strcmp("pxa25x_udc", (g)->name)) -#define gadget_is_pxa27x(g) (!strcmp("pxa27x_udc", (g)->name)) - -/** - * gadget_supports_altsettings - return true if altsettings work - * @gadget: the gadget in question - */ -static inline bool gadget_supports_altsettings(struct usb_gadget *gadget) -{ - /* PXA 21x/25x/26x has no altsettings at all */ - if (gadget_is_pxa(gadget)) - return false; - - /* PXA 27x and 3xx have *broken* altsetting support */ - if (gadget_is_pxa27x(gadget)) - return false; - - /* Everything else is *presumably* fine ... */ - return true; -} - -#endif /* __GADGET_CHIPS_H */ diff --git a/drivers/usb/gadget/legacy/Makefile b/drivers/usb/gadget/legacy/Makefile new file mode 100644 index 0000000000..5d5382a673 --- /dev/null +++ b/drivers/usb/gadget/legacy/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 + +ccflags-y := -I$(srctree)/drivers/usb/gadget/ +ccflags-y += -I$(srctree)/drivers/usb/gadget/udc/ +ccflags-y += -I$(srctree)/drivers/usb/gadget/function/ + +obj-$(CONFIG_USB_GADGET_SERIAL) += serial.o +obj-$(CONFIG_USB_GADGET) += multi.o diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/legacy/multi.c index 445cc32420..ddb3d4158c 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/legacy/multi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * multi.c -- Multifunction Composite driver * @@ -5,15 +6,10 @@ * Copyright (C) 2008 Nokia Corporation * Copyright (C) 2009 Samsung Electronics * Author: Michal Nazarewicz (mina86@mina86.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <common.h> -#include <usb/gadget-multi.h> +#include <linux/usb/gadget-multi.h> #include <linux/err.h> #include "u_serial.h" @@ -60,6 +56,8 @@ static struct usb_function_instance *fi_dfu; static struct usb_function *f_dfu; static struct usb_function_instance *fi_fastboot; static struct usb_function *f_fastboot; +static struct usb_function_instance *fi_ums; +static struct usb_function *f_ums; static struct usb_configuration config = { .bConfigurationValue = 1, @@ -139,6 +137,37 @@ static int multi_bind_fastboot(struct usb_composite_dev *cdev) return usb_add_function(&config, f_fastboot); } +static bool fastboot_has_exports(struct f_multi_opts *opts) +{ + return !file_list_empty(opts->fastboot_opts.files) || + opts->fastboot_opts.export_bbu; +} + +static int multi_bind_ums(struct usb_composite_dev *cdev) +{ + int ret; + struct f_ums_opts *opts; + + fi_ums = usb_get_function_instance("ums"); + if (IS_ERR(fi_ums)) { + ret = PTR_ERR(fi_ums); + fi_ums = NULL; + return ret; + } + + opts = container_of(fi_ums, struct f_ums_opts, func_inst); + opts->files = gadget_multi_opts->ums_opts.files; + + f_ums = usb_get_function(fi_ums); + if (IS_ERR(f_ums)) { + ret = PTR_ERR(f_ums); + f_ums = NULL; + return ret; + } + + return usb_add_function(&config, f_ums); +} + static int multi_unbind(struct usb_composite_dev *cdev) { if (gadget_multi_opts->create_acm) { @@ -146,12 +175,17 @@ static int multi_unbind(struct usb_composite_dev *cdev) usb_put_function_instance(fi_acm); } + if (gadget_multi_opts->ums_opts.files) { + usb_put_function(f_ums); + usb_put_function_instance(fi_ums); + } + if (gadget_multi_opts->dfu_opts.files) { usb_put_function(f_dfu); usb_put_function_instance(fi_dfu); } - if (gadget_multi_opts->fastboot_opts.files) { + if (fastboot_has_exports(gadget_multi_opts)) { usb_put_function(f_fastboot); usb_put_function_instance(fi_fastboot); } @@ -179,8 +213,10 @@ static int multi_bind(struct usb_composite_dev *cdev) strings_dev[USB_GADGET_MANUFACTURER_IDX].s = gadget->manufacturer; strings_dev[USB_GADGET_PRODUCT_IDX].s = gadget->productname; + strings_dev[USB_GADGET_SERIAL_IDX].s = gadget->serialnumber; device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; + device_desc.iSerialNumber = strings_dev[USB_GADGET_SERIAL_IDX].id; config.label = strings_dev[STRING_DESCRIPTION_IDX].s; config.iConfiguration = strings_dev[STRING_DESCRIPTION_IDX].id; @@ -189,25 +225,32 @@ static int multi_bind(struct usb_composite_dev *cdev) if (ret) return ret; - if (gadget_multi_opts->fastboot_opts.files) { + if (fastboot_has_exports(gadget_multi_opts)) { printf("%s: creating Fastboot function\n", __func__); ret = multi_bind_fastboot(cdev); if (ret) - goto out; + return ret; } if (gadget_multi_opts->dfu_opts.files) { printf("%s: creating DFU function\n", __func__); ret = multi_bind_dfu(cdev); if (ret) - goto out; + goto unbind_fastboot; + } + + if (gadget_multi_opts->ums_opts.files) { + printf("%s: creating USB Mass Storage function\n", __func__); + ret = multi_bind_ums(cdev); + if (ret) + goto unbind_dfu; } if (gadget_multi_opts->create_acm) { printf("%s: creating ACM function\n", __func__); ret = multi_bind_acm(cdev); if (ret) - goto out; + goto unbind_ums; } usb_ep_autoconfig_reset(cdev->gadget); @@ -215,8 +258,15 @@ static int multi_bind(struct usb_composite_dev *cdev) dev_info(&gadget->dev, DRIVER_DESC "\n"); return 0; -out: - multi_unbind(cdev); +unbind_ums: + if (gadget_multi_opts->ums_opts.files) + usb_put_function_instance(fi_ums); +unbind_dfu: + if (gadget_multi_opts->dfu_opts.files) + usb_put_function_instance(fi_dfu); +unbind_fastboot: + if (fastboot_has_exports(gadget_multi_opts)) + usb_put_function_instance(fi_fastboot); return ret; } @@ -225,7 +275,7 @@ static struct usb_composite_driver multi_driver = { .name = "g_multi", .dev = &device_desc, .strings = dev_strings, - .max_speed = USB_SPEED_HIGH, + .max_speed = USB_SPEED_SUPER, .bind = multi_bind, .unbind = multi_unbind, .needs_serial = 1, @@ -244,10 +294,8 @@ int usb_multi_register(struct f_multi_opts *opts) gadget_multi_opts = opts; ret = usb_composite_probe(&multi_driver); - if (ret) { - usb_composite_unregister(&multi_driver); + if (ret) gadget_multi_opts = NULL; - } return ret; } @@ -264,12 +312,23 @@ void usb_multi_unregister(void) gadget_multi_opts = NULL; } +unsigned usb_multi_count_functions(struct f_multi_opts *opts) +{ + unsigned count = 0; + + count += fastboot_has_exports(opts); + count += !file_list_empty(opts->dfu_opts.files); + count += !file_list_empty(opts->ums_opts.files); + count += opts->create_acm; + + return count; +} + void usb_multi_opts_release(struct f_multi_opts *opts) { - if (opts->fastboot_opts.files) - file_list_free(opts->fastboot_opts.files); - if (opts->dfu_opts.files) - file_list_free(opts->dfu_opts.files); + file_list_free(opts->fastboot_opts.files); + file_list_free(opts->dfu_opts.files); + file_list_free(opts->ums_opts.files); free(opts); } diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/legacy/serial.c index f1d98b7a39..913d174a91 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/legacy/serial.c @@ -1,27 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * serial.c -- USB gadget serial driver * * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) * Copyright (C) 2008 by David Brownell * Copyright (C) 2008 by Nokia Corporation - * - * This software is distributed under the terms of the GNU General - * Public License ("GPL") as published by the Free Software Foundation, - * either version 2 of that License or (at your option) any later version. */ #include <common.h> #include <errno.h> #include <init.h> #include <linux/err.h> -#include <usb/ch9.h> -#include <usb/gadget.h> -#include <usb/composite.h> -#include <usb/usbserial.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h> +#include <linux/usb/usbserial.h> #include <asm/byteorder.h> #include "u_serial.h" -#include "gadget_chips.h" /* Defines */ @@ -251,8 +247,17 @@ static struct usb_composite_driver gserial_driver = { .unbind = gs_unbind, }; +static bool usb_serial_registered; + int usb_serial_register(struct usb_serial_pdata *pdata) { + int ret; + + if (usb_serial_registered) { + pr_err("USB serial gadget already registered\n"); + return -EBUSY; + } + /* We *could* export two configs; that'd be much cleaner... * but neither of these product IDs was defined that way. */ @@ -277,10 +282,18 @@ int usb_serial_register(struct usb_serial_pdata *pdata) device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; } - return usb_composite_probe(&gserial_driver); + ret = usb_composite_probe(&gserial_driver); + if (!ret) + usb_serial_registered = true; + + return ret; } void usb_serial_unregister(void) { + if (!usb_serial_registered) + return; + usb_composite_unregister(&gserial_driver); + usb_serial_registered = false; } diff --git a/drivers/usb/gadget/u_os_desc.h b/drivers/usb/gadget/u_os_desc.h new file mode 100644 index 0000000000..5d7d35c8cc --- /dev/null +++ b/drivers/usb/gadget/u_os_desc.h @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * u_os_desc.h + * + * Utility definitions for "OS Descriptors" support + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com> + */ + +#ifndef __U_OS_DESC_H__ +#define __U_OS_DESC_H__ + +#include <asm/unaligned.h> +#include <linux/nls.h> + +#define USB_EXT_PROP_DW_SIZE 0 +#define USB_EXT_PROP_DW_PROPERTY_DATA_TYPE 4 +#define USB_EXT_PROP_W_PROPERTY_NAME_LENGTH 8 +#define USB_EXT_PROP_B_PROPERTY_NAME 10 +#define USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH 10 +#define USB_EXT_PROP_B_PROPERTY_DATA 14 + +#define USB_EXT_PROP_RESERVED 0 +#define USB_EXT_PROP_UNICODE 1 +#define USB_EXT_PROP_UNICODE_ENV 2 +#define USB_EXT_PROP_BINARY 3 +#define USB_EXT_PROP_LE32 4 +#define USB_EXT_PROP_BE32 5 +#define USB_EXT_PROP_UNICODE_LINK 6 +#define USB_EXT_PROP_UNICODE_MULTI 7 + +static inline u8 *__usb_ext_prop_ptr(u8 *buf, size_t offset) +{ + return buf + offset; +} + +static inline u8 *usb_ext_prop_size_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_SIZE); +} + +static inline u8 *usb_ext_prop_type_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_PROPERTY_DATA_TYPE); +} + +static inline u8 *usb_ext_prop_name_len_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_W_PROPERTY_NAME_LENGTH); +} + +static inline u8 *usb_ext_prop_name_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_NAME); +} + +static inline u8 *usb_ext_prop_data_len_ptr(u8 *buf, size_t off) +{ + return __usb_ext_prop_ptr(buf, + USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + off); +} + +static inline u8 *usb_ext_prop_data_ptr(u8 *buf, size_t off) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_DATA + off); +} + +static inline void usb_ext_prop_put_size(u8 *buf, int dw_size) +{ + put_unaligned_le32(dw_size, usb_ext_prop_size_ptr(buf)); +} + +static inline void usb_ext_prop_put_type(u8 *buf, int type) +{ + put_unaligned_le32(type, usb_ext_prop_type_ptr(buf)); +} + +static inline int usb_ext_prop_put_name(u8 *buf, const char *name, int pnl) +{ + int result; + + put_unaligned_le16(pnl, usb_ext_prop_name_len_ptr(buf)); + result = utf8s_to_utf16s(name, strlen(name), UTF16_LITTLE_ENDIAN, + (wchar_t *) usb_ext_prop_name_ptr(buf), pnl - 2); + if (result < 0) + return result; + + put_unaligned_le16(0, &buf[USB_EXT_PROP_B_PROPERTY_NAME + pnl - 2]); + + return pnl; +} + +static inline void usb_ext_prop_put_binary(u8 *buf, int pnl, const u8 *data, + int data_len) +{ + put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl)); + memcpy(usb_ext_prop_data_ptr(buf, pnl), data, data_len); +} + +static inline int usb_ext_prop_put_unicode(u8 *buf, int pnl, const char *string, + int data_len) +{ + int result; + put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl)); + result = utf8s_to_utf16s(string, data_len >> 1, UTF16_LITTLE_ENDIAN, + (wchar_t *) usb_ext_prop_data_ptr(buf, pnl), + data_len - 2); + if (result < 0) + return result; + + put_unaligned_le16(0, + &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl + data_len - 2]); + + return data_len; +} + +#endif /* __U_OS_DESC_H__ */ diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c deleted file mode 100644 index 126d76e8bb..0000000000 --- a/drivers/usb/gadget/udc-core.c +++ /dev/null @@ -1,417 +0,0 @@ -/** - * udc.c - Core UDC Framework - * - * Copyright (C) 2010 Texas Instruments - * Author: Felipe Balbi <balbi@ti.com> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 of - * the License as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -#define VERBOSE_DEBUG -#include <common.h> -#include <driver.h> -#include <init.h> -#include <poller.h> -#include <usb/ch9.h> -#include <usb/gadget.h> - -/** - * struct usb_udc - describes one usb device controller - * @driver - the gadget driver pointer. For use by the class code - * @dev - the child device to the actual controller - * @gadget - the gadget. For use by the class code - * @list - for use by the udc class driver - * - * This represents the internal data structure which is used by the UDC-class - * to hold information about udc driver and gadget together. - */ -struct usb_udc { - struct usb_gadget_driver *driver; - struct usb_gadget *gadget; - struct device_d dev; - struct list_head list; - struct poller_struct poller; -}; - -static LIST_HEAD(udc_list); - -/* ------------------------------------------------------------------------- */ - -#ifdef CONFIG_KERNEL_HAS_DMA - -int usb_gadget_map_request(struct usb_gadget *gadget, - struct usb_request *req, int is_in) -{ - if (req->length == 0) - return 0; - - if (req->num_sgs) { - int mapped; - - mapped = dma_map_sg(&gadget->dev, req->sg, req->num_sgs, - is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (mapped == 0) { - dev_err(&gadget->dev, "failed to map SGs\n"); - return -EFAULT; - } - - req->num_mapped_sgs = mapped; - } else { - req->dma = dma_map_single(&gadget->dev, req->buf, req->length, - is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - - if (dma_mapping_error(&gadget->dev, req->dma)) { - dev_err(&gadget->dev, "failed to map buffer\n"); - return -EFAULT; - } - } - - return 0; -} -EXPORT_SYMBOL_GPL(usb_gadget_map_request); - -void usb_gadget_unmap_request(struct usb_gadget *gadget, - struct usb_request *req, int is_in) -{ - if (req->length == 0) - return; - - if (req->num_mapped_sgs) { - dma_unmap_sg(&gadget->dev, req->sg, req->num_mapped_sgs, - is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - - req->num_mapped_sgs = 0; - } else { - dma_unmap_single(&gadget->dev, req->dma, req->length, - is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - } -} -EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); - -#endif /* CONFIG_HAS_DMA */ - -/* ------------------------------------------------------------------------- */ - -void usb_gadget_set_state(struct usb_gadget *gadget, - enum usb_device_state state) -{ - gadget->state = state; -} -EXPORT_SYMBOL_GPL(usb_gadget_set_state); - -/** - * usb_gadget_udc_reset - notifies the udc core that bus reset occurs - * @gadget: The gadget which bus reset occurs - * @driver: The gadget driver we want to notify - * - * If the udc driver has bus reset handler, it needs to call this when the bus - * reset occurs, it notifies the gadget driver that the bus reset occurs as - * well as updates gadget state. - */ -void usb_gadget_udc_reset(struct usb_gadget *gadget, - struct usb_gadget_driver *driver) -{ - usb_gadget_set_state(gadget, USB_STATE_DEFAULT); -} -EXPORT_SYMBOL_GPL(usb_gadget_udc_reset); -/* ------------------------------------------------------------------------- */ - -/** - * usb_gadget_udc_start - tells usb device controller to start up - * @gadget: The gadget we want to get started - * @driver: The driver we want to bind to @gadget - * - * This call is issued by the UDC Class driver when it's about - * to register a gadget driver to the device controller, before - * calling gadget driver's bind() method. - * - * It allows the controller to be powered off until strictly - * necessary to have it powered on. - * - * Returns zero on success, else negative errno. - */ -static inline int usb_gadget_udc_start(struct usb_gadget *gadget, - struct usb_gadget_driver *driver) -{ - return gadget->ops->udc_start(gadget, driver); -} - -/** - * usb_gadget_udc_stop - tells usb device controller we don't need it anymore - * @gadget: The device we want to stop activity - * @driver: The driver to unbind from @gadget - * - * This call is issued by the UDC Class driver after calling - * gadget driver's unbind() method. - * - * The details are implementation specific, but it can go as - * far as powering off UDC completely and disable its data - * line pullups. - */ -static inline void usb_gadget_udc_stop(struct usb_gadget *gadget, - struct usb_gadget_driver *driver) -{ - gadget->ops->udc_stop(gadget, driver); -} - -int usb_gadget_poll(void) -{ - struct usb_udc *udc; - - list_for_each_entry(udc, &udc_list, list) { - if (udc->gadget->ops->udc_poll) - udc->gadget->ops->udc_poll(udc->gadget); - } - - return 0; -} - -/** - * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list - * @parent: the parent device to this udc. Usually the controller driver's - * device. - * @gadget: the gadget to be added to the list. - * @release: a gadget release function. - * - * Returns zero on success, negative errno otherwise. - */ -int usb_add_gadget_udc_release(struct device_d *parent, struct usb_gadget *gadget, - void (*release)(struct device_d *dev)) -{ - struct usb_udc *udc; - int ret = -ENOMEM; - - udc = kzalloc(sizeof(*udc), GFP_KERNEL); - if (!udc) - goto err1; - - dev_set_name(&gadget->dev, "usbgadget"); - gadget->dev.id = DEVICE_ID_SINGLE; - gadget->dev.parent = parent; - - ret = register_device(&gadget->dev); - if (ret) - goto err2; - - dev_add_param_uint32(&gadget->dev, "product", NULL, NULL, - &gadget->product_id, "0x%04x", NULL); - dev_add_param_uint32(&gadget->dev, "vendor", NULL, NULL, - &gadget->vendor_id, "0x%04x", NULL); - gadget->manufacturer = xstrdup("barebox"); - dev_add_param_string(&gadget->dev, "manufacturer", NULL, NULL, - &gadget->manufacturer, NULL); - gadget->productname = xstrdup(barebox_get_model()); - dev_add_param_string(&gadget->dev, "productname", NULL, NULL, - &gadget->productname, NULL); - - dev_set_name(&udc->dev, "udc"); - udc->dev.id = DEVICE_ID_DYNAMIC; - - udc->gadget = gadget; - - list_add_tail(&udc->list, &udc_list); - - register_device(&udc->dev); - - usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED); - - return 0; -err2: - kfree(udc); - -err1: - return ret; -} -EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release); - -/** - * usb_add_gadget_udc - adds a new gadget to the udc class driver list - * @parent: the parent device to this udc. Usually the controller - * driver's device. - * @gadget: the gadget to be added to the list - * - * Returns zero on success, negative errno otherwise. - */ -int usb_add_gadget_udc(struct device_d *parent, struct usb_gadget *gadget) -{ - return usb_add_gadget_udc_release(parent, gadget, NULL); -} -EXPORT_SYMBOL_GPL(usb_add_gadget_udc); - -static void usb_gadget_remove_driver(struct usb_udc *udc) -{ - dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n", - udc->gadget->name); - - if (udc->gadget->ops->udc_poll) - poller_unregister(&udc->poller); - - usb_gadget_disconnect(udc->gadget); - udc->driver->disconnect(udc->gadget); - udc->driver->unbind(udc->gadget); - usb_gadget_udc_stop(udc->gadget, NULL); - - udc->driver = NULL; - udc->dev.driver = NULL; - udc->gadget->dev.driver = NULL; -} - -/** - * usb_del_gadget_udc - deletes @udc from udc_list - * @gadget: the gadget to be removed. - * - * This, will call usb_gadget_unregister_driver() if - * the @udc is still busy. - */ -void usb_del_gadget_udc(struct usb_gadget *gadget) -{ - struct usb_udc *udc = NULL; - - list_for_each_entry(udc, &udc_list, list) - if (udc->gadget == gadget) - goto found; - - dev_err(gadget->dev.parent, "gadget not registered.\n"); - - return; - -found: - dev_vdbg(gadget->dev.parent, "unregistering gadget\n"); - - list_del(&udc->list); - - if (udc->driver) - usb_gadget_remove_driver(udc); - - unregister_device(&udc->dev); - unregister_device(&gadget->dev); -} -EXPORT_SYMBOL_GPL(usb_del_gadget_udc); - -/* ------------------------------------------------------------------------- */ - -static void udc_poll_driver(struct poller_struct *poller) -{ - struct usb_udc *udc = container_of(poller, struct usb_udc, poller); - - udc->gadget->ops->udc_poll(udc->gadget); -} - -static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver) -{ - int ret; - - dev_dbg(&udc->dev, "registering UDC driver [%s]\n", - driver->function); - - udc->driver = driver; - udc->dev.driver = &driver->driver; - udc->gadget->dev.driver = &driver->driver; - - if (udc->gadget->ops->udc_poll) { - udc->poller.func = udc_poll_driver; - ret = poller_register(&udc->poller, dev_name(&udc->dev)); - if (ret) - return ret; - } - - ret = driver->bind(udc->gadget, driver); - if (ret) - goto err1; - - ret = usb_gadget_udc_start(udc->gadget, driver); - if (ret) { - driver->unbind(udc->gadget); - goto err1; - } - usb_gadget_connect(udc->gadget); - - return 0; -err1: - if (udc->gadget->ops->udc_poll) - poller_unregister(&udc->poller); - - if (ret != -EISNAM) - dev_err(&udc->dev, "failed to start %s: %d\n", - udc->driver->function, ret); - udc->driver = NULL; - udc->dev.driver = NULL; - udc->gadget->dev.driver = NULL; - return ret; -} - -int udc_attach_driver(const char *name, struct usb_gadget_driver *driver) -{ - struct usb_udc *udc = NULL; - int ret = -ENODEV; - - list_for_each_entry(udc, &udc_list, list) { - ret = strcmp(name, dev_name(&udc->dev)); - if (!ret) - break; - } - if (ret) { - ret = -ENODEV; - goto out; - } - if (udc->driver) { - ret = -EBUSY; - goto out; - } - ret = udc_bind_to_driver(udc, driver); -out: - return ret; -} -EXPORT_SYMBOL_GPL(udc_attach_driver); - -int usb_gadget_probe_driver(struct usb_gadget_driver *driver) -{ - struct usb_udc *udc = NULL; - int ret; - - if (!driver || !driver->bind || !driver->setup) - return -EINVAL; - - list_for_each_entry(udc, &udc_list, list) { - /* For now we take the first one */ - if (!udc->driver) - goto found; - } - - pr_debug("couldn't find an available UDC\n"); - - return -ENODEV; -found: - ret = udc_bind_to_driver(udc, driver); - - return ret; -} -EXPORT_SYMBOL_GPL(usb_gadget_probe_driver); - -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) -{ - struct usb_udc *udc = NULL; - int ret = -ENODEV; - - if (!driver || !driver->unbind) - return -EINVAL; - - list_for_each_entry(udc, &udc_list, list) - if (udc->driver == driver) { - usb_gadget_remove_driver(udc); - ret = 0; - break; - } - - return ret; -} -EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver); diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile new file mode 100644 index 0000000000..6e79e80cfa --- /dev/null +++ b/drivers/usb/gadget/udc/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_USB_GADGET) += core.o + +obj-$(CONFIG_USB_GADGET_DRIVER_ARC) += fsl_udc.o +obj-$(CONFIG_USB_GADGET_DRIVER_AT91) += at91_udc.o +obj-$(CONFIG_USB_GADGET_DRIVER_PXA27X) += pxa27x_udc.o diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index 411464690d..ffd39f489f 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * at91_udc -- driver for at91-series USB peripheral controller * * Copyright (C) 2004 by Thomas Rathbone * Copyright (C) 2005 by HP Labs * Copyright (C) 2005 by David Brownell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #undef VERBOSE_DEBUG @@ -20,8 +16,8 @@ #include <gpio.h> #include <io.h> #include <clock.h> -#include <usb/ch9.h> -#include <usb/gadget.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> #include <of_gpio.h> #include <linux/list.h> @@ -30,11 +26,11 @@ #include <asm/byteorder.h> -#include <mach/hardware.h> -#include <mach/at91sam9261.h> -#include <mach/board.h> -#include <mach/cpu.h> -#include <mach/at91sam9261_matrix.h> +#include <mach/at91/hardware.h> +#include <mach/at91/at91sam9261.h> +#include <mach/at91/board.h> +#include <mach/at91/cpu.h> +#include <mach/at91/at91sam9261_matrix.h> #include "at91_udc.h" @@ -1244,7 +1240,7 @@ static int at91_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *d return 0; } -static int at91_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver) +static int at91_udc_stop(struct usb_gadget *gadget) { struct at91_udc *udc = container_of(gadget, struct at91_udc, gadget); @@ -1252,7 +1248,8 @@ static int at91_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *dr at91_udp_write(udc, AT91_UDP_IDR, ~0); udc->driver = NULL; - DBG(udc, "unbound from %s\n", driver->function); + DBG(udc, "unbound\n"); + return 0; } @@ -1393,7 +1390,7 @@ static void __init at91udc_of_init(struct at91_udc *udc, struct device_node *np) /*-------------------------------------------------------------------------*/ -static int __init at91udc_probe(struct device_d *dev) +static int __init at91udc_probe(struct device *dev) { struct resource *iores; struct at91_udc *udc = &controller; @@ -1411,12 +1408,12 @@ static int __init at91udc_probe(struct device_d *dev) iclk_name = "udc_clk"; fclk_name = "udpck"; } else { - if (!IS_ENABLED(CONFIG_OFDEVICE) || !dev->device_node) { + if (!IS_ENABLED(CONFIG_OFDEVICE) || !dev->of_node) { dev_err(dev, "no DT and no platform_data\n"); return -ENODEV; } - at91udc_of_init(udc, dev->device_node); + at91udc_of_init(udc, dev->of_node); iclk_name = "pclk"; fclk_name = "hclk"; } @@ -1525,8 +1522,9 @@ static const struct of_device_id at91_udc_dt_ids[] = { { .compatible = "atmel,at91sam9263-udc" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, at91_udc_dt_ids); -static struct driver_d at91_udc_driver = { +static struct driver at91_udc_driver = { .name = driver_name, .probe = at91udc_probe, .of_compatible = DRV_OF_COMPAT(at91_udc_dt_ids), diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/udc/at91_udc.h index e592cc54ff..cecaa5b52e 100644 --- a/drivers/usb/gadget/at91_udc.h +++ b/drivers/usb/gadget/udc/at91_udc.h @@ -1,12 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2004 by Thomas Rathbone, HP Labs * Copyright (C) 2005 by Ivan Kokshaysky * Copyright (C) 2006 by SAN People - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef AT91_UDC_H @@ -131,7 +127,7 @@ struct at91_udc { u32 gpio_vbus_val; struct at91_udc_data board; struct clk *iclk, *fclk; - struct device_d *dev; + struct device *dev; void __iomem *udp_baseaddr; int udp_irq; }; diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c new file mode 100644 index 0000000000..e7cfa0d5d8 --- /dev/null +++ b/drivers/usb/gadget/udc/core.c @@ -0,0 +1,1517 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * udc.c - Core UDC Framework + * + * Copyright (C) 2010 Texas Instruments + * Author: Felipe Balbi <balbi@ti.com> + */ + +#define pr_fmt(fmt) "UDC core: " fmt + +#include <common.h> +#include <dma.h> +#include <linux/usb/gadget.h> +#include <linux/usb/ch9.h> + +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/idr.h> +#include <linux/err.h> + +static int gadget_id_numbers; + +static struct bus_type gadget_bus_type; + +/** + * struct usb_udc - describes one usb device controller + * @driver: the gadget driver pointer. For use by the class code + * @dev: the child device to the actual controller + * @gadget: the gadget. For use by the class code + * @list: for use by the udc class driver + * @vbus: for udcs who care about vbus status, this value is real vbus status; + * for udcs who do not care about vbus status, this value is always true + * @started: the UDC's started state. True if the UDC had started. + * + * This represents the internal data structure which is used by the UDC-class + * to hold information about udc driver and gadget together. + */ +struct usb_udc { + struct usb_gadget_driver *driver; + struct usb_gadget *gadget; + struct device dev; + struct list_head list; + bool vbus; + bool started; + struct poller_struct poller; +}; + +static LIST_HEAD(udc_list); + +/* Protects udc_list, udc->driver, driver->is_bound, and related calls */ +static DEFINE_MUTEX(udc_lock); + +/* ------------------------------------------------------------------------- */ + +/** + * usb_ep_set_maxpacket_limit - set maximum packet size limit for endpoint + * @ep:the endpoint being configured + * @maxpacket_limit:value of maximum packet size limit + * + * This function should be used only in UDC drivers to initialize endpoint + * (usually in probe function). + */ +void usb_ep_set_maxpacket_limit(struct usb_ep *ep, + unsigned maxpacket_limit) +{ + ep->maxpacket_limit = maxpacket_limit; + ep->maxpacket = maxpacket_limit; +} +EXPORT_SYMBOL_GPL(usb_ep_set_maxpacket_limit); + +/** + * usb_ep_enable - configure endpoint, making it usable + * @ep:the endpoint being configured. may not be the endpoint named "ep0". + * drivers discover endpoints through the ep_list of a usb_gadget. + * + * When configurations are set, or when interface settings change, the driver + * will enable or disable the relevant endpoints. while it is enabled, an + * endpoint may be used for i/o until the driver receives a disconnect() from + * the host or until the endpoint is disabled. + * + * the ep0 implementation (which calls this routine) must ensure that the + * hardware capabilities of each endpoint match the descriptor provided + * for it. for example, an endpoint named "ep2in-bulk" would be usable + * for interrupt transfers as well as bulk, but it likely couldn't be used + * for iso transfers or for endpoint 14. some endpoints are fully + * configurable, with more generic names like "ep-a". (remember that for + * USB, "in" means "towards the USB host".) + * + * This routine may be called in an atomic (interrupt) context. + * + * returns zero, or a negative error code. + */ +int usb_ep_enable(struct usb_ep *ep) +{ + int ret = 0; + + if (ep->enabled) + goto out; + + /* UDC drivers can't handle endpoints with maxpacket size 0 */ + if (usb_endpoint_maxp(ep->desc) == 0) { + /* + * We should log an error message here, but we can't call + * dev_err() because there's no way to find the gadget + * given only ep. + */ + ret = -EINVAL; + goto out; + } + + ret = ep->ops->enable(ep, ep->desc); + if (ret) + goto out; + + ep->enabled = true; + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_ep_enable); + +/** + * usb_ep_disable - endpoint is no longer usable + * @ep:the endpoint being unconfigured. may not be the endpoint named "ep0". + * + * no other task may be using this endpoint when this is called. + * any pending and uncompleted requests will complete with status + * indicating disconnect (-ESHUTDOWN) before this call returns. + * gadget drivers must call usb_ep_enable() again before queueing + * requests to the endpoint. + * + * This routine may be called in an atomic (interrupt) context. + * + * returns zero, or a negative error code. + */ +int usb_ep_disable(struct usb_ep *ep) +{ + int ret = 0; + + if (!ep->enabled) + goto out; + + ret = ep->ops->disable(ep); + if (ret) + goto out; + + ep->enabled = false; + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_ep_disable); + +/** + * usb_ep_alloc_request - allocate a request object to use with this endpoint + * @ep:the endpoint to be used with with the request + * @gfp_flags:GFP_* flags to use + * + * Request objects must be allocated with this call, since they normally + * need controller-specific setup and may even need endpoint-specific + * resources such as allocation of DMA descriptors. + * Requests may be submitted with usb_ep_queue(), and receive a single + * completion callback. Free requests with usb_ep_free_request(), when + * they are no longer needed. + * + * Returns the request, or null if one could not be allocated. + */ +struct usb_request *usb_ep_alloc_request(struct usb_ep *ep) +{ + struct usb_request *req = NULL; + + req = ep->ops->alloc_request(ep); + + return req; +} +EXPORT_SYMBOL_GPL(usb_ep_alloc_request); + +/** + * usb_ep_free_request - frees a request object + * @ep:the endpoint associated with the request + * @req:the request being freed + * + * Reverses the effect of usb_ep_alloc_request(). + * Caller guarantees the request is not queued, and that it will + * no longer be requeued (or otherwise used). + */ +void usb_ep_free_request(struct usb_ep *ep, + struct usb_request *req) +{ + ep->ops->free_request(ep, req); +} +EXPORT_SYMBOL_GPL(usb_ep_free_request); + +/** + * usb_ep_queue - queues (submits) an I/O request to an endpoint. + * @ep:the endpoint associated with the request + * @req:the request being submitted + * @gfp_flags: GFP_* flags to use in case the lower level driver couldn't + * pre-allocate all necessary memory with the request. + * + * This tells the device controller to perform the specified request through + * that endpoint (reading or writing a buffer). When the request completes, + * including being canceled by usb_ep_dequeue(), the request's completion + * routine is called to return the request to the driver. Any endpoint + * (except control endpoints like ep0) may have more than one transfer + * request queued; they complete in FIFO order. Once a gadget driver + * submits a request, that request may not be examined or modified until it + * is given back to that driver through the completion callback. + * + * Each request is turned into one or more packets. The controller driver + * never merges adjacent requests into the same packet. OUT transfers + * will sometimes use data that's already buffered in the hardware. + * Drivers can rely on the fact that the first byte of the request's buffer + * always corresponds to the first byte of some USB packet, for both + * IN and OUT transfers. + * + * Bulk endpoints can queue any amount of data; the transfer is packetized + * automatically. The last packet will be short if the request doesn't fill it + * out completely. Zero length packets (ZLPs) should be avoided in portable + * protocols since not all usb hardware can successfully handle zero length + * packets. (ZLPs may be explicitly written, and may be implicitly written if + * the request 'zero' flag is set.) Bulk endpoints may also be used + * for interrupt transfers; but the reverse is not true, and some endpoints + * won't support every interrupt transfer. (Such as 768 byte packets.) + * + * Interrupt-only endpoints are less functional than bulk endpoints, for + * example by not supporting queueing or not handling buffers that are + * larger than the endpoint's maxpacket size. They may also treat data + * toggle differently. + * + * Control endpoints ... after getting a setup() callback, the driver queues + * one response (even if it would be zero length). That enables the + * status ack, after transferring data as specified in the response. Setup + * functions may return negative error codes to generate protocol stalls. + * (Note that some USB device controllers disallow protocol stall responses + * in some cases.) When control responses are deferred (the response is + * written after the setup callback returns), then usb_ep_set_halt() may be + * used on ep0 to trigger protocol stalls. Depending on the controller, + * it may not be possible to trigger a status-stage protocol stall when the + * data stage is over, that is, from within the response's completion + * routine. + * + * For periodic endpoints, like interrupt or isochronous ones, the usb host + * arranges to poll once per interval, and the gadget driver usually will + * have queued some data to transfer at that time. + * + * Note that @req's ->complete() callback must never be called from + * within usb_ep_queue() as that can create deadlock situations. + * + * This routine may be called in interrupt context. + * + * Returns zero, or a negative error code. Endpoints that are not enabled + * report errors; errors will also be + * reported when the usb peripheral is disconnected. + * + * If and only if @req is successfully queued (the return value is zero), + * @req->complete() will be called exactly once, when the Gadget core and + * UDC are finished with the request. When the completion function is called, + * control of the request is returned to the device driver which submitted it. + * The completion handler may then immediately free or reuse @req. + */ +int usb_ep_queue(struct usb_ep *ep, + struct usb_request *req) +{ + int ret = 0; + + if (WARN_ON_ONCE(!ep->enabled && ep->address)) { + ret = -ESHUTDOWN; + goto out; + } + + ret = ep->ops->queue(ep, req); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_ep_queue); + +/** + * usb_ep_dequeue - dequeues (cancels, unlinks) an I/O request from an endpoint + * @ep:the endpoint associated with the request + * @req:the request being canceled + * + * If the request is still active on the endpoint, it is dequeued and + * eventually its completion routine is called (with status -ECONNRESET); + * else a negative error code is returned. This routine is asynchronous, + * that is, it may return before the completion routine runs. + * + * Note that some hardware can't clear out write fifos (to unlink the request + * at the head of the queue) except as part of disconnecting from usb. Such + * restrictions prevent drivers from supporting configuration changes, + * even to configuration zero (a "chapter 9" requirement). + * + * This routine may be called in interrupt context. + */ +int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req) +{ + int ret; + + ret = ep->ops->dequeue(ep, req); + + return ret; +} +EXPORT_SYMBOL_GPL(usb_ep_dequeue); + +/** + * usb_ep_set_halt - sets the endpoint halt feature. + * @ep: the non-isochronous endpoint being stalled + * + * Use this to stall an endpoint, perhaps as an error report. + * Except for control endpoints, + * the endpoint stays halted (will not stream any data) until the host + * clears this feature; drivers may need to empty the endpoint's request + * queue first, to make sure no inappropriate transfers happen. + * + * Note that while an endpoint CLEAR_FEATURE will be invisible to the + * gadget driver, a SET_INTERFACE will not be. To reset endpoints for the + * current altsetting, see usb_ep_clear_halt(). When switching altsettings, + * it's simplest to use usb_ep_enable() or usb_ep_disable() for the endpoints. + * + * This routine may be called in interrupt context. + * + * Returns zero, or a negative error code. On success, this call sets + * underlying hardware state that blocks data transfers. + * Attempts to halt IN endpoints will fail (returning -EAGAIN) if any + * transfer requests are still queued, or if the controller hardware + * (usually a FIFO) still holds bytes that the host hasn't collected. + */ +int usb_ep_set_halt(struct usb_ep *ep) +{ + int ret; + + ret = ep->ops->set_halt(ep, 1); + + return ret; +} +EXPORT_SYMBOL_GPL(usb_ep_set_halt); + +/** + * usb_ep_clear_halt - clears endpoint halt, and resets toggle + * @ep:the bulk or interrupt endpoint being reset + * + * Use this when responding to the standard usb "set interface" request, + * for endpoints that aren't reconfigured, after clearing any other state + * in the endpoint's i/o queue. + * + * This routine may be called in interrupt context. + * + * Returns zero, or a negative error code. On success, this call clears + * the underlying hardware state reflecting endpoint halt and data toggle. + * Note that some hardware can't support this request (like pxa2xx_udc), + * and accordingly can't correctly implement interface altsettings. + */ +int usb_ep_clear_halt(struct usb_ep *ep) +{ + int ret; + + ret = ep->ops->set_halt(ep, 0); + + return ret; +} +EXPORT_SYMBOL_GPL(usb_ep_clear_halt); + +/** + * usb_ep_set_wedge - sets the halt feature and ignores clear requests + * @ep: the endpoint being wedged + * + * Use this to stall an endpoint and ignore CLEAR_FEATURE(HALT_ENDPOINT) + * requests. If the gadget driver clears the halt status, it will + * automatically unwedge the endpoint. + * + * This routine may be called in interrupt context. + * + * Returns zero on success, else negative errno. + */ +int usb_ep_set_wedge(struct usb_ep *ep) +{ + int ret; + + if (ep->ops->set_wedge) + ret = ep->ops->set_wedge(ep); + else + ret = ep->ops->set_halt(ep, 1); + + return ret; +} +EXPORT_SYMBOL_GPL(usb_ep_set_wedge); + +/** + * usb_ep_fifo_status - returns number of bytes in fifo, or error + * @ep: the endpoint whose fifo status is being checked. + * + * FIFO endpoints may have "unclaimed data" in them in certain cases, + * such as after aborted transfers. Hosts may not have collected all + * the IN data written by the gadget driver (and reported by a request + * completion). The gadget driver may not have collected all the data + * written OUT to it by the host. Drivers that need precise handling for + * fault reporting or recovery may need to use this call. + * + * This routine may be called in interrupt context. + * + * This returns the number of such bytes in the fifo, or a negative + * errno if the endpoint doesn't use a FIFO or doesn't support such + * precise handling. + */ +int usb_ep_fifo_status(struct usb_ep *ep) +{ + int ret; + + if (ep->ops->fifo_status) + ret = ep->ops->fifo_status(ep); + else + ret = -EOPNOTSUPP; + + return ret; +} +EXPORT_SYMBOL_GPL(usb_ep_fifo_status); + +/** + * usb_ep_fifo_flush - flushes contents of a fifo + * @ep: the endpoint whose fifo is being flushed. + * + * This call may be used to flush the "unclaimed data" that may exist in + * an endpoint fifo after abnormal transaction terminations. The call + * must never be used except when endpoint is not being used for any + * protocol translation. + * + * This routine may be called in interrupt context. + */ +void usb_ep_fifo_flush(struct usb_ep *ep) +{ + if (ep->ops->fifo_flush) + ep->ops->fifo_flush(ep); +} +EXPORT_SYMBOL_GPL(usb_ep_fifo_flush); + +/* ------------------------------------------------------------------------- */ + +/** + * usb_gadget_frame_number - returns the current frame number + * @gadget: controller that reports the frame number + * + * Returns the usb frame number, normally eleven bits from a SOF packet, + * or negative errno if this device doesn't support this capability. + */ +int usb_gadget_frame_number(struct usb_gadget *gadget) +{ + int ret; + + ret = gadget->ops->get_frame(gadget); + + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_frame_number); + +/** + * usb_gadget_wakeup - tries to wake up the host connected to this gadget + * @gadget: controller used to wake up the host + * + * Returns zero on success, else negative error code if the hardware + * doesn't support such attempts, or its support has not been enabled + * by the usb host. Drivers must return device descriptors that report + * their ability to support this, or hosts won't enable it. + * + * This may also try to use SRP to wake the host and start enumeration, + * even if OTG isn't otherwise in use. OTG devices may also start + * remote wakeup even when hosts don't explicitly enable it. + */ +int usb_gadget_wakeup(struct usb_gadget *gadget) +{ + int ret = 0; + + if (!gadget->ops->wakeup) { + ret = -EOPNOTSUPP; + goto out; + } + + ret = gadget->ops->wakeup(gadget); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_wakeup); + +/** + * usb_gadget_set_selfpowered - sets the device selfpowered feature. + * @gadget:the device being declared as self-powered + * + * this affects the device status reported by the hardware driver + * to reflect that it now has a local power supply. + * + * returns zero on success, else negative errno. + */ +int usb_gadget_set_selfpowered(struct usb_gadget *gadget) +{ + int ret = 0; + + if (!gadget->ops->set_selfpowered) { + ret = -EOPNOTSUPP; + goto out; + } + + ret = gadget->ops->set_selfpowered(gadget, 1); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_set_selfpowered); + +/** + * usb_gadget_clear_selfpowered - clear the device selfpowered feature. + * @gadget:the device being declared as bus-powered + * + * this affects the device status reported by the hardware driver. + * some hardware may not support bus-powered operation, in which + * case this feature's value can never change. + * + * returns zero on success, else negative errno. + */ +int usb_gadget_clear_selfpowered(struct usb_gadget *gadget) +{ + int ret = 0; + + if (!gadget->ops->set_selfpowered) { + ret = -EOPNOTSUPP; + goto out; + } + + ret = gadget->ops->set_selfpowered(gadget, 0); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_clear_selfpowered); + +/** + * usb_gadget_vbus_connect - Notify controller that VBUS is powered + * @gadget:The device which now has VBUS power. + * Context: can sleep + * + * This call is used by a driver for an external transceiver (or GPIO) + * that detects a VBUS power session starting. Common responses include + * resuming the controller, activating the D+ (or D-) pullup to let the + * host detect that a USB device is attached, and starting to draw power + * (8mA or possibly more, especially after SET_CONFIGURATION). + * + * Returns zero on success, else negative errno. + */ +int usb_gadget_vbus_connect(struct usb_gadget *gadget) +{ + int ret = 0; + + if (!gadget->ops->vbus_session) { + ret = -EOPNOTSUPP; + goto out; + } + + ret = gadget->ops->vbus_session(gadget, 1); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_vbus_connect); + +/** + * usb_gadget_vbus_draw - constrain controller's VBUS power usage + * @gadget:The device whose VBUS usage is being described + * @mA:How much current to draw, in milliAmperes. This should be twice + * the value listed in the configuration descriptor bMaxPower field. + * + * This call is used by gadget drivers during SET_CONFIGURATION calls, + * reporting how much power the device may consume. For example, this + * could affect how quickly batteries are recharged. + * + * Returns zero on success, else negative errno. + */ +int usb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA) +{ + int ret = 0; + + if (!gadget->ops->vbus_draw) { + ret = -EOPNOTSUPP; + goto out; + } + + ret = gadget->ops->vbus_draw(gadget, mA); + if (!ret) + gadget->mA = mA; + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_vbus_draw); + +/** + * usb_gadget_vbus_disconnect - notify controller about VBUS session end + * @gadget:the device whose VBUS supply is being described + * Context: can sleep + * + * This call is used by a driver for an external transceiver (or GPIO) + * that detects a VBUS power session ending. Common responses include + * reversing everything done in usb_gadget_vbus_connect(). + * + * Returns zero on success, else negative errno. + */ +int usb_gadget_vbus_disconnect(struct usb_gadget *gadget) +{ + int ret = 0; + + if (!gadget->ops->vbus_session) { + ret = -EOPNOTSUPP; + goto out; + } + + ret = gadget->ops->vbus_session(gadget, 0); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_vbus_disconnect); + +/** + * usb_gadget_connect - software-controlled connect to USB host + * @gadget:the peripheral being connected + * + * Enables the D+ (or potentially D-) pullup. The host will start + * enumerating this gadget when the pullup is active and a VBUS session + * is active (the link is powered). + * + * Returns zero on success, else negative errno. + */ +int usb_gadget_connect(struct usb_gadget *gadget) +{ + int ret = 0; + + if (!gadget->ops->pullup) { + ret = -EOPNOTSUPP; + goto out; + } + + if (gadget->deactivated) { + /* + * If gadget is deactivated we only save new state. + * Gadget will be connected automatically after activation. + */ + gadget->connected = true; + goto out; + } + + ret = gadget->ops->pullup(gadget, 1); + if (!ret) + gadget->connected = 1; + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_connect); + +/** + * usb_gadget_disconnect - software-controlled disconnect from USB host + * @gadget:the peripheral being disconnected + * + * Disables the D+ (or potentially D-) pullup, which the host may see + * as a disconnect (when a VBUS session is active). Not all systems + * support software pullup controls. + * + * Following a successful disconnect, invoke the ->disconnect() callback + * for the current gadget driver so that UDC drivers don't need to. + * + * Returns zero on success, else negative errno. + */ +int usb_gadget_disconnect(struct usb_gadget *gadget) +{ + int ret = 0; + + if (!gadget->ops->pullup) { + ret = -EOPNOTSUPP; + goto out; + } + + if (!gadget->connected) + goto out; + + if (gadget->deactivated) { + /* + * If gadget is deactivated we only save new state. + * Gadget will stay disconnected after activation. + */ + gadget->connected = false; + goto out; + } + + ret = gadget->ops->pullup(gadget, 0); + if (!ret) + gadget->connected = 0; + + mutex_lock(&udc_lock); + if (gadget->udc->driver) + gadget->udc->driver->disconnect(gadget); + mutex_unlock(&udc_lock); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_disconnect); + +/** + * usb_gadget_deactivate - deactivate function which is not ready to work + * @gadget: the peripheral being deactivated + * + * This routine may be used during the gadget driver bind() call to prevent + * the peripheral from ever being visible to the USB host, unless later + * usb_gadget_activate() is called. For example, user mode components may + * need to be activated before the system can talk to hosts. + * + * Returns zero on success, else negative errno. + */ +int usb_gadget_deactivate(struct usb_gadget *gadget) +{ + int ret = 0; + + if (gadget->deactivated) + goto out; + + if (gadget->connected) { + ret = usb_gadget_disconnect(gadget); + if (ret) + goto out; + + /* + * If gadget was being connected before deactivation, we want + * to reconnect it in usb_gadget_activate(). + */ + gadget->connected = true; + } + gadget->deactivated = true; + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_deactivate); + +/** + * usb_gadget_activate - activate function which is not ready to work + * @gadget: the peripheral being activated + * + * This routine activates gadget which was previously deactivated with + * usb_gadget_deactivate() call. It calls usb_gadget_connect() if needed. + * + * Returns zero on success, else negative errno. + */ +int usb_gadget_activate(struct usb_gadget *gadget) +{ + int ret = 0; + + if (!gadget->deactivated) + goto out; + + gadget->deactivated = false; + + /* + * If gadget has been connected before deactivation, or became connected + * while it was being deactivated, we call usb_gadget_connect(). + */ + if (gadget->connected) + ret = usb_gadget_connect(gadget); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_activate); + +/* ------------------------------------------------------------------------- */ + +#ifdef CONFIG_HAS_DMA + +int usb_gadget_map_request_by_dev(struct device *dev, + struct usb_request *req, int is_in) +{ + if (req->length == 0) + return 0; + + req->dma = dma_map_single(dev, req->buf, req->length, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + if (dma_mapping_error(dev, req->dma)) { + dev_err(dev, "failed to map buffer\n"); + return -EFAULT; + } + + req->dma_mapped = 1; + + return 0; +} +EXPORT_SYMBOL_GPL(usb_gadget_map_request_by_dev); + +int usb_gadget_map_request(struct usb_gadget *gadget, + struct usb_request *req, int is_in) +{ + return usb_gadget_map_request_by_dev(gadget->dev.parent, req, is_in); +} +EXPORT_SYMBOL_GPL(usb_gadget_map_request); + +void usb_gadget_unmap_request_by_dev(struct device *dev, + struct usb_request *req, int is_in) +{ + if (req->length == 0) + return; + + if (req->dma_mapped) { + dma_unmap_single(dev, req->dma, req->length, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + req->dma_mapped = 0; + } +} +EXPORT_SYMBOL_GPL(usb_gadget_unmap_request_by_dev); + +void usb_gadget_unmap_request(struct usb_gadget *gadget, + struct usb_request *req, int is_in) +{ + usb_gadget_unmap_request_by_dev(gadget->dev.parent, req, is_in); +} +EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); + +#endif /* CONFIG_HAS_DMA */ + +/* ------------------------------------------------------------------------- */ + +/** + * usb_gadget_giveback_request - give the request back to the gadget layer + * @ep: the endpoint to be used with with the request + * @req: the request being given back + * + * This is called by device controller drivers in order to return the + * completed request back to the gadget layer. + */ +void usb_gadget_giveback_request(struct usb_ep *ep, + struct usb_request *req) +{ + req->complete(ep, req); +} +EXPORT_SYMBOL_GPL(usb_gadget_giveback_request); + +/* ------------------------------------------------------------------------- */ + +/** + * gadget_find_ep_by_name - returns ep whose name is the same as sting passed + * in second parameter or NULL if searched endpoint not found + * @g: controller to check for quirk + * @name: name of searched endpoint + */ +struct usb_ep *gadget_find_ep_by_name(struct usb_gadget *g, const char *name) +{ + struct usb_ep *ep; + + gadget_for_each_ep(ep, g) { + if (!strcmp(ep->name, name)) + return ep; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(gadget_find_ep_by_name); + +/* ------------------------------------------------------------------------- */ + +int usb_gadget_ep_match_desc(struct usb_gadget *gadget, + struct usb_ep *ep, struct usb_endpoint_descriptor *desc, + struct usb_ss_ep_comp_descriptor *ep_comp) +{ + u8 type; + u16 max; + int num_req_streams = 0; + + /* endpoint already claimed? */ + if (ep->claimed) + return 0; + + type = usb_endpoint_type(desc); + max = usb_endpoint_maxp(desc); + + if (usb_endpoint_dir_in(desc) && !ep->caps.dir_in) + return 0; + if (usb_endpoint_dir_out(desc) && !ep->caps.dir_out) + return 0; + + if (max > ep->maxpacket_limit) + return 0; + + /* "high bandwidth" works only at high speed */ + if (!gadget_is_dualspeed(gadget) && usb_endpoint_maxp_mult(desc) > 1) + return 0; + + switch (type) { + case USB_ENDPOINT_XFER_CONTROL: + /* only support ep0 for portable CONTROL traffic */ + return 0; + case USB_ENDPOINT_XFER_ISOC: + if (!ep->caps.type_iso) + return 0; + /* ISO: limit 1023 bytes full speed, 1024 high/super speed */ + if (!gadget_is_dualspeed(gadget) && max > 1023) + return 0; + break; + case USB_ENDPOINT_XFER_BULK: + if (!ep->caps.type_bulk) + return 0; + if (ep_comp && gadget_is_superspeed(gadget)) { + /* Get the number of required streams from the + * EP companion descriptor and see if the EP + * matches it + */ + num_req_streams = ep_comp->bmAttributes & 0x1f; + if (num_req_streams > ep->max_streams) + return 0; + } + break; + case USB_ENDPOINT_XFER_INT: + /* Bulk endpoints handle interrupt transfers, + * except the toggle-quirky iso-synch kind + */ + if (!ep->caps.type_int && !ep->caps.type_bulk) + return 0; + /* INT: limit 64 bytes full speed, 1024 high/super speed */ + if (!gadget_is_dualspeed(gadget) && max > 64) + return 0; + break; + } + + return 1; +} +EXPORT_SYMBOL_GPL(usb_gadget_ep_match_desc); + +/** + * usb_gadget_check_config - checks if the UDC can support the binded + * configuration + * @gadget: controller to check the USB configuration + * + * Ensure that a UDC is able to support the requested resources by a + * configuration, and that there are no resource limitations, such as + * internal memory allocated to all requested endpoints. + * + * Returns zero on success, else a negative errno. + */ +int usb_gadget_check_config(struct usb_gadget *gadget) +{ + if (gadget->ops->check_config) + return gadget->ops->check_config(gadget); + return 0; +} +EXPORT_SYMBOL_GPL(usb_gadget_check_config); + +/* ------------------------------------------------------------------------- */ + +void usb_gadget_set_state(struct usb_gadget *gadget, + enum usb_device_state state) +{ + gadget->state = state; +} +EXPORT_SYMBOL_GPL(usb_gadget_set_state); + +/* ------------------------------------------------------------------------- */ + +static void usb_udc_connect_control(struct usb_udc *udc) +{ + if (udc->vbus) + usb_gadget_connect(udc->gadget); + else + usb_gadget_disconnect(udc->gadget); +} + +/** + * usb_udc_vbus_handler - updates the udc core vbus status, and try to + * connect or disconnect gadget + * @gadget: The gadget which vbus change occurs + * @status: The vbus status + * + * The udc driver calls it when it wants to connect or disconnect gadget + * according to vbus status. + */ +void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status) +{ + struct usb_udc *udc = gadget->udc; + + if (udc) { + udc->vbus = status; + usb_udc_connect_control(udc); + } +} +EXPORT_SYMBOL_GPL(usb_udc_vbus_handler); + +/** + * usb_gadget_udc_reset - notifies the udc core that bus reset occurs + * @gadget: The gadget which bus reset occurs + * @driver: The gadget driver we want to notify + * + * If the udc driver has bus reset handler, it needs to call this when the bus + * reset occurs, it notifies the gadget driver that the bus reset occurs as + * well as updates gadget state. + */ +void usb_gadget_udc_reset(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + driver->reset(gadget); + usb_gadget_set_state(gadget, USB_STATE_DEFAULT); +} +EXPORT_SYMBOL_GPL(usb_gadget_udc_reset); + +/** + * usb_gadget_udc_start - tells usb device controller to start up + * @udc: The UDC to be started + * + * This call is issued by the UDC Class driver when it's about + * to register a gadget driver to the device controller, before + * calling gadget driver's bind() method. + * + * It allows the controller to be powered off until strictly + * necessary to have it powered on. + * + * Returns zero on success, else negative errno. + */ +static inline int usb_gadget_udc_start(struct usb_udc *udc) +{ + int ret; + + if (udc->started) { + dev_err(&udc->dev, "UDC had already started\n"); + return -EBUSY; + } + + ret = udc->gadget->ops->udc_start(udc->gadget, udc->driver); + if (!ret) + udc->started = true; + + return ret; +} + +/** + * usb_gadget_udc_stop - tells usb device controller we don't need it anymore + * @udc: The UDC to be stopped + * + * This call is issued by the UDC Class driver after calling + * gadget driver's unbind() method. + * + * The details are implementation specific, but it can go as + * far as powering off UDC completely and disable its data + * line pullups. + */ +static inline void usb_gadget_udc_stop(struct usb_udc *udc) +{ + if (!udc->started) { + dev_err(&udc->dev, "UDC had already stopped\n"); + return; + } + + udc->gadget->ops->udc_stop(udc->gadget); + udc->started = false; +} + +/** + * usb_gadget_udc_set_speed - tells usb device controller speed supported by + * current driver + * @udc: The device we want to set maximum speed + * @speed: The maximum speed to allowed to run + * + * This call is issued by the UDC Class driver before calling + * usb_gadget_udc_start() in order to make sure that we don't try to + * connect on speeds the gadget driver doesn't support. + */ +static inline void usb_gadget_udc_set_speed(struct usb_udc *udc, + enum usb_device_speed speed) +{ + struct usb_gadget *gadget = udc->gadget; + enum usb_device_speed s; + + if (speed == USB_SPEED_UNKNOWN) + s = gadget->max_speed; + else + s = min(speed, gadget->max_speed); + + if (s == USB_SPEED_SUPER_PLUS && gadget->ops->udc_set_ssp_rate) + gadget->ops->udc_set_ssp_rate(gadget, gadget->max_ssp_rate); + else if (gadget->ops->udc_set_speed) + gadget->ops->udc_set_speed(gadget, s); +} + +/** + * usb_gadget_enable_async_callbacks - tell usb device controller to enable asynchronous callbacks + * @udc: The UDC which should enable async callbacks + * + * This routine is used when binding gadget drivers. It undoes the effect + * of usb_gadget_disable_async_callbacks(); the UDC driver should enable IRQs + * (if necessary) and resume issuing callbacks. + * + * This routine will always be called in process context. + */ +static inline void usb_gadget_enable_async_callbacks(struct usb_udc *udc) +{ + struct usb_gadget *gadget = udc->gadget; + + if (gadget->ops->udc_async_callbacks) + gadget->ops->udc_async_callbacks(gadget, true); +} + +/** + * usb_gadget_disable_async_callbacks - tell usb device controller to disable asynchronous callbacks + * @udc: The UDC which should disable async callbacks + * + * This routine is used when unbinding gadget drivers. It prevents a race: + * The UDC driver doesn't know when the gadget driver's ->unbind callback + * runs, so unless it is told to disable asynchronous callbacks, it might + * issue a callback (such as ->disconnect) after the unbind has completed. + * + * After this function runs, the UDC driver must suppress all ->suspend, + * ->resume, ->disconnect, ->reset, and ->setup callbacks to the gadget driver + * until async callbacks are again enabled. A simple-minded but effective + * way to accomplish this is to tell the UDC hardware not to generate any + * more IRQs. + * + * Request completion callbacks must still be issued. However, it's okay + * to defer them until the request is cancelled, since the pull-up will be + * turned off during the time period when async callbacks are disabled. + * + * This routine will always be called in process context. + */ +static inline void usb_gadget_disable_async_callbacks(struct usb_udc *udc) +{ + struct usb_gadget *gadget = udc->gadget; + + if (gadget->ops->udc_async_callbacks) + gadget->ops->udc_async_callbacks(gadget, false); +} + +/** + * usb_initialize_gadget - initialize a gadget and its embedded struct device + * @parent: the parent device to this udc. Usually the controller driver's + * device. + * @gadget: the gadget to be initialized. + * @release: a gadget release function. + */ +void usb_initialize_gadget(struct device *parent, struct usb_gadget *gadget, + void (*release)(struct device *dev)) +{ + gadget->dev.parent = parent; + gadget->dev.bus = &gadget_bus_type; +} +EXPORT_SYMBOL_GPL(usb_initialize_gadget); + +/** + * usb_add_gadget - adds a new gadget to the udc class driver list + * @gadget: the gadget to be added to the list. + * + * Returns zero on success, negative errno otherwise. + * Does not do a final usb_put_gadget() if an error occurs. + */ +int usb_add_gadget(struct usb_gadget *gadget) +{ + struct usb_udc *udc; + int ret = -ENOMEM; + + udc = kzalloc(sizeof(*udc), GFP_KERNEL); + if (!udc) + goto error; + + udc->dev.parent = gadget->dev.parent; + ret = dev_set_name(&udc->dev, "usbgadget"); + if (ret) + goto err_put_udc; + + udc->gadget = gadget; + gadget->udc = udc; + + udc->started = false; + + mutex_lock(&udc_lock); + list_add_tail(&udc->list, &udc_list); + mutex_unlock(&udc_lock); + + ret = register_device(&udc->dev); + if (ret) + goto err_unlist_udc; + + usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED); + udc->vbus = true; + + ret = gadget_id_numbers++; + if (ret < 0) + goto err_del_udc; + gadget->id_number = ret; + dev_set_name(&gadget->dev, "gadget"); + gadget->dev.id = ret; + + ret = register_device(&gadget->dev); + if (ret) + goto err_free_id; + + dev_add_param_uint32(&gadget->dev, "product", NULL, NULL, + &gadget->product_id, "0x%04x", NULL); + dev_add_param_uint32(&gadget->dev, "vendor", NULL, NULL, + &gadget->vendor_id, "0x%04x", NULL); + gadget->manufacturer = xstrdup("barebox"); + dev_add_param_string(&gadget->dev, "manufacturer", NULL, NULL, + &gadget->manufacturer, NULL); + gadget->productname = xstrdup(barebox_get_model()); + dev_add_param_string(&gadget->dev, "productname", NULL, NULL, + &gadget->productname, NULL); + gadget->serialnumber = xstrdup(barebox_get_serial_number() ? : "unset"); + dev_add_param_string(&gadget->dev, "serialnumber", NULL, NULL, + &gadget->serialnumber, NULL); + + return 0; + + err_free_id: + err_del_udc: + unregister_device(&udc->dev); + + err_unlist_udc: + mutex_lock(&udc_lock); + list_del(&udc->list); + mutex_unlock(&udc_lock); + + err_put_udc: + error: + return ret; +} +EXPORT_SYMBOL_GPL(usb_add_gadget); + +int usb_gadget_poll(void) +{ + struct usb_udc *udc; + + list_for_each_entry(udc, &udc_list, list) { + if (udc->gadget->ops->udc_poll) + udc->gadget->ops->udc_poll(udc->gadget); + } + + return 0; +} + +/** + * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list + * @parent: the parent device to this udc. Usually the controller driver's + * device. + * @gadget: the gadget to be added to the list. + * @release: a gadget release function. + * + * Returns zero on success, negative errno otherwise. + * Calls the gadget release function in the latter case. + */ +int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, + void (*release)(struct device *dev)) +{ + int ret; + + usb_initialize_gadget(parent, gadget, release); + ret = usb_add_gadget(gadget); + if (ret) + usb_put_gadget(gadget); + return ret; +} +EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release); + +/** + * usb_get_gadget_udc_name - get the name of the first UDC controller + * This functions returns the name of the first UDC controller in the system. + * Please note that this interface is usefull only for legacy drivers which + * assume that there is only one UDC controller in the system and they need to + * get its name before initialization. There is no guarantee that the UDC + * of the returned name will be still available, when gadget driver registers + * itself. + * + * Returns pointer to string with UDC controller name on success, NULL + * otherwise. Caller should kfree() returned string. + */ +char *usb_get_gadget_udc_name(void) +{ + struct usb_udc *udc; + char *name = NULL; + + /* For now we take the first available UDC */ + mutex_lock(&udc_lock); + list_for_each_entry(udc, &udc_list, list) { + if (!udc->driver) { + name = kstrdup(udc->gadget->name, GFP_KERNEL); + break; + } + } + mutex_unlock(&udc_lock); + return name; +} +EXPORT_SYMBOL_GPL(usb_get_gadget_udc_name); + +/** + * usb_add_gadget_udc - adds a new gadget to the udc class driver list + * @parent: the parent device to this udc. Usually the controller + * driver's device. + * @gadget: the gadget to be added to the list + * + * Returns zero on success, negative errno otherwise. + */ +int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) +{ + return usb_add_gadget_udc_release(parent, gadget, NULL); +} +EXPORT_SYMBOL_GPL(usb_add_gadget_udc); + +/** + * usb_del_gadget - deletes a gadget and unregisters its udc + * @gadget: the gadget to be deleted. + * + * This will unbind @gadget, if it is bound. + * It will not do a final usb_put_gadget(). + */ +void usb_del_gadget(struct usb_gadget *gadget) +{ + struct usb_udc *udc = gadget->udc; + + if (!udc) + return; + + dev_vdbg(gadget->dev.parent, "unregistering gadget\n"); + + mutex_lock(&udc_lock); + list_del(&udc->list); + mutex_unlock(&udc_lock); + + unregister_device(&gadget->dev); + unregister_device(&udc->dev); +} +EXPORT_SYMBOL_GPL(usb_del_gadget); + +/** + * usb_del_gadget_udc - unregisters a gadget + * @gadget: the gadget to be unregistered. + * + * Calls usb_del_gadget() and does a final usb_put_gadget(). + */ +void usb_del_gadget_udc(struct usb_gadget *gadget) +{ + usb_del_gadget(gadget); + usb_put_gadget(gadget); +} +EXPORT_SYMBOL_GPL(usb_del_gadget_udc); + +/* ------------------------------------------------------------------------- */ + +static int gadget_match_driver(struct device *dev, struct driver *drv) +{ + struct usb_gadget *gadget = dev_to_usb_gadget(dev); + struct usb_udc *udc = gadget->udc; + struct usb_gadget_driver *driver = container_of(drv, + struct usb_gadget_driver, driver); + + /* If the driver specifies a udc_name, it must match the UDC's name */ + if (driver->udc_name && + strcmp(driver->udc_name, dev_name(&udc->dev)) != 0) + return -1; + + /* If the driver is already bound to a gadget, it doesn't match */ + if (driver->is_bound) + return -1; + + /* Otherwise any gadget driver matches any UDC */ + return 0; +} + +static void udc_poll_driver(struct poller_struct *poller) +{ + struct usb_udc *udc = container_of(poller, struct usb_udc, poller); + + udc->gadget->ops->udc_poll(udc->gadget); +} + +static int gadget_bind_driver(struct device *dev) +{ + struct usb_gadget *gadget = dev_to_usb_gadget(dev); + struct usb_udc *udc = gadget->udc; + struct usb_gadget_driver *driver = container_of(dev->driver, + struct usb_gadget_driver, driver); + int ret = 0; + + mutex_lock(&udc_lock); + if (driver->is_bound) { + mutex_unlock(&udc_lock); + return -ENXIO; /* Driver binds to only one gadget */ + } + driver->is_bound = true; + udc->driver = driver; + mutex_unlock(&udc_lock); + + dev_dbg(&udc->dev, "binding gadget driver [%s]\n", driver->function); + + usb_gadget_udc_set_speed(udc, driver->max_speed); + + if (udc->gadget->ops->udc_poll) { + udc->poller.func = udc_poll_driver; + ret = poller_register(&udc->poller, dev_name(&udc->dev)); + if (ret) + return ret; + } + + ret = driver->bind(udc->gadget, driver); + if (ret) + goto err_bind; + + ret = usb_gadget_udc_start(udc); + if (ret) + goto err_start; + usb_gadget_enable_async_callbacks(udc); + usb_udc_connect_control(udc); + + return 0; + + err_start: + driver->unbind(udc->gadget); + + err_bind: + if (ret != -EISNAM) + dev_err(&udc->dev, "failed to start %s: %d\n", + driver->function, ret); + + if (udc->gadget->ops->udc_poll) + poller_unregister(&udc->poller); + + mutex_lock(&udc_lock); + udc->driver = NULL; + driver->is_bound = false; + mutex_unlock(&udc_lock); + + return ret; +} + +static void gadget_unbind_driver(struct device *dev) +{ + struct usb_gadget *gadget = dev_to_usb_gadget(dev); + struct usb_udc *udc = gadget->udc; + struct usb_gadget_driver *driver = udc->driver; + + dev_dbg(&udc->dev, "unbinding gadget driver [%s]\n", driver->function); + + if (udc->gadget->ops->udc_poll) + poller_unregister(&udc->poller); + + usb_gadget_disconnect(gadget); + usb_gadget_disable_async_callbacks(udc); + udc->driver->unbind(gadget); + usb_gadget_udc_stop(udc); + + mutex_lock(&udc_lock); + driver->is_bound = false; + udc->driver = NULL; + mutex_unlock(&udc_lock); +} + +/* ------------------------------------------------------------------------- */ + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + int ret; + + if (!driver || !driver->bind || !driver->setup) + return -EINVAL; + + driver->driver.bus = &gadget_bus_type; + ret = register_driver(&driver->driver); + if (ret) { + pr_warn("%s: driver registration failed: %d\n", + driver->function, ret); + return ret; + } + + mutex_lock(&udc_lock); + if (!driver->is_bound) { + if (driver->match_existing_only) { + pr_warn("%s: couldn't find an available UDC or it's busy\n", + driver->function); + ret = -EBUSY; + } else { + pr_info("%s: couldn't find an available UDC\n", + driver->function); + ret = 0; + } + } + mutex_unlock(&udc_lock); + + if (ret) + unregister_driver(&driver->driver); + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_register_driver); + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + if (!driver || !driver->unbind) + return -EINVAL; + + unregister_driver(&driver->driver); + return 0; +} +EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver); + +/* ------------------------------------------------------------------------- */ + +static struct bus_type gadget_bus_type = { + .name = "gadget", + .probe = gadget_bind_driver, + .remove = gadget_unbind_driver, + .match = gadget_match_driver, +}; + +static int usb_udc_init(void) +{ + bus_register(&gadget_bus_type); + + return 0; +} +coredevice_initcall(usb_udc_init); diff --git a/drivers/usb/gadget/fsl_udc.c b/drivers/usb/gadget/udc/fsl_udc.c index cffe9bdab7..41de44b30d 100644 --- a/drivers/usb/gadget/fsl_udc.c +++ b/drivers/usb/gadget/udc/fsl_udc.c @@ -1,12 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> -#include <dma.h> #include <errno.h> #include <dma.h> #include <init.h> #include <clock.h> -#include <usb/ch9.h> -#include <usb/gadget.h> -#include <usb/fsl_usb2.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/fsl_usb2.h> #include <io.h> #include <asm/byteorder.h> #include <linux/err.h> @@ -197,8 +197,8 @@ static void done(struct fsl_ep *ep, struct fsl_req *req, int status) dma_free_coherent(curr_td, 0, sizeof(struct ep_td_struct)); } - dma_sync_single_for_cpu((unsigned long)req->req.buf, req->req.length, - DMA_BIDIRECTIONAL); + dma_sync_single_for_cpu(udc->gadget.dev.parent, (unsigned long)req->req.buf, + req->req.length, DMA_BIDIRECTIONAL); if (status && (status != -ESHUTDOWN)) VDBG("complete %s req %p stat %d len %u/%u", @@ -885,8 +885,8 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req) req->ep = ep; - dma_sync_single_for_device((unsigned long)req->req.buf, req->req.length, - DMA_BIDIRECTIONAL); + dma_sync_single_for_device(udc->gadget.dev.parent, (unsigned long)req->req.buf, + req->req.length, DMA_BIDIRECTIONAL); req->req.status = -EINPROGRESS; req->req.actual = 0; @@ -980,7 +980,8 @@ static int fsl_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) done(ep, req, -ECONNRESET); /* Enable EP */ -out: epctrl = readl(&dr_regs->endptctrl[ep_num]); +out: + epctrl = readl(&dr_regs->endptctrl[ep_num]); if (ep_is_in(ep)) epctrl |= EPCTRL_TX_ENABLE; else @@ -1646,7 +1647,7 @@ static int fsl_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *dr } /* Disconnect from gadget driver */ -static int fsl_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver) +static int fsl_udc_stop(struct usb_gadget *gadget) { struct fsl_udc *udc = to_fsl_udc(gadget); struct fsl_ep *loop_ep; @@ -1670,7 +1671,7 @@ static int fsl_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *dri } static int struct_udc_setup(struct fsl_udc *udc, - struct device_d *dev) + struct device *dev) { struct fsl_usb2_platform_data *pdata = dev->platform_data; size_t size; @@ -1853,7 +1854,7 @@ static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index, return 0; } -struct fsl_udc *ci_udc_register(struct device_d *dev, void __iomem *regs) +struct fsl_udc *ci_udc_register(struct device *dev, void __iomem *regs) { struct fsl_udc *udc_controller; int ret, i; @@ -1935,7 +1936,7 @@ void ci_udc_unregister(struct fsl_udc *udc) free(udc); } -static int fsl_udc_probe(struct device_d *dev) +static int fsl_udc_probe(struct device *dev) { struct fsl_udc *udc; void __iomem *regs = dev_request_mem_region(dev, 0); @@ -1952,14 +1953,14 @@ static int fsl_udc_probe(struct device_d *dev) return 0; } -static void fsl_udc_remove(struct device_d *dev) +static void fsl_udc_remove(struct device *dev) { struct fsl_udc *udc = dev->priv; ci_udc_unregister(udc); } -static struct driver_d fsl_udc_driver = { +static struct driver fsl_udc_driver = { .name = "fsl-udc", .probe = fsl_udc_probe, .remove = fsl_udc_remove, diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index 442c90ca05..20148f4878 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -1,20 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Handles the Intel 27x USB Device Controller (UDC) * * Inspired by original driver by Frank Becker, David Brownell, and others. * Copyright (C) 2008 Robert Jarzmik * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * * Taken from linux-2.6 kernel and adapted to barebox. */ #include <common.h> @@ -24,12 +14,12 @@ #include <gpio.h> #include <init.h> -#include <usb/ch9.h> -#include <usb/gadget.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> #include "pxa27x_udc.h" -#include <mach/udc_pxa2xx.h> -#include <mach/pxa-regs.h> +#include <mach/pxa/udc_pxa2xx.h> +#include <mach/pxa/pxa-regs.h> #define DRIVER_VERSION "2008-04-18" #define DRIVER_DESC "PXA 27x USB Device Controller driver" @@ -341,7 +331,6 @@ static int write_packet(struct pxa_ep *ep, struct pxa27x_request *req, u8 *buf_8; buf = (u32 *)(req->req.buf + req->req.actual); - prefetch(buf); length = min(req->req.length - req->req.actual, max); req->req.actual += length; @@ -880,7 +869,7 @@ static int pxa_udc_vbus_session(struct usb_gadget *_gadget, int is_active) } static int pxa_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver); -static int pxa_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver); +static int pxa_udc_stop(struct usb_gadget *gadget); static void pxa_udc_gadget_poll(struct usb_gadget *gadget); static const struct usb_gadget_ops pxa_udc_ops = { @@ -893,12 +882,12 @@ static const struct usb_gadget_ops pxa_udc_ops = { .udc_poll = pxa_udc_gadget_poll, }; -static void clk_enable(void) +static void usb_clk_enable(void) { CKEN |= CKEN_USB; } -static void clk_disable(void) +static void usb_clk_disable(void) { CKEN &= ~CKEN_USB; } @@ -912,7 +901,7 @@ static void udc_disable(struct pxa_udc *udc) udc_writel(udc, UDCICR1, 0); udc_clear_mask_UDCCR(udc, UDCCR_UDE); - clk_disable(); + usb_clk_disable(); ep0_idle(udc); udc->gadget.speed = USB_SPEED_UNKNOWN; @@ -957,7 +946,7 @@ static void udc_enable(struct pxa_udc *udc) udc_writel(udc, UDCICR1, 0); udc_clear_mask_UDCCR(udc, UDCCR_UDE); - clk_enable(); + usb_clk_enable(); ep0_idle(udc); udc->gadget.speed = USB_SPEED_FULL; @@ -998,36 +987,26 @@ static int pxa_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *dr return 0; } -static void stop_activity(struct pxa_udc *udc, struct usb_gadget_driver *driver) +static void stop_activity(struct pxa_udc *udc) { int i; - /* don't disconnect drivers more than once */ - if (udc->gadget.speed == USB_SPEED_UNKNOWN) - driver = NULL; udc->gadget.speed = USB_SPEED_UNKNOWN; for (i = 0; i < NR_USB_ENDPOINTS; i++) pxa_ep_disable(&udc->udc_usb_ep[i].usb_ep); - - if (driver) - driver->disconnect(&udc->gadget); } -static int pxa_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver) +static int pxa_udc_stop(struct usb_gadget *gadget) { struct pxa_udc *udc = the_controller; if (!udc) return -ENODEV; - if (!driver || driver != udc->driver || !driver->unbind) - return -EINVAL; - stop_activity(udc, driver); + stop_activity(udc); udc_disable(udc); - driver->disconnect(&udc->gadget); - driver->unbind(&udc->gadget); udc->driver = NULL; /* @@ -1359,7 +1338,7 @@ static void irq_udc_reset(struct pxa_udc *udc) if ((udccr & UDCCR_UDA) == 0) { dev_dbg(udc->dev, "USB reset start\n"); - stop_activity(udc, udc->driver); + stop_activity(udc); } udc->gadget.speed = USB_SPEED_FULL; @@ -1380,7 +1359,7 @@ static void pxa_udc_gadget_poll(struct usb_gadget *gadget) if (should_enable_udc(udc)) udc_enable(udc); if (should_disable_udc(udc)) { - stop_activity(udc, udc->driver); + stop_activity(udc); udc_disable(udc); } @@ -1448,7 +1427,7 @@ static struct pxa_udc memory = { } }; -static int __init pxa_udc_probe(struct device_d *dev) +static int __init pxa_udc_probe(struct device *dev) { struct resource *iores; struct pxa_udc *udc = &memory; @@ -1483,7 +1462,7 @@ static int __init pxa_udc_probe(struct device_d *dev) #define pxa27x_clear_otgph() do {} while (0) -static struct driver_d udc_driver = { +static struct driver udc_driver = { .name = "pxa27x-udc", .probe = pxa_udc_probe, }; diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/udc/pxa27x_udc.h index 554626e9e3..80a93cdcf9 100644 --- a/drivers/usb/gadget/pxa27x_udc.h +++ b/drivers/usb/gadget/udc/pxa27x_udc.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * linux/drivers/usb/gadget/pxa27x_udc.h * Intel PXA27x on-chip full speed USB device controller @@ -5,16 +6,6 @@ * Inspired by original driver by Frank Becker, David Brownell, and others. * Copyright (C) 2008 Robert Jarzmik * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * * Taken from linux-2.6 kernel and adapted to barebox. */ @@ -345,7 +336,7 @@ struct pxa_udc { void __iomem *regs; int irq; struct clk *clk; - struct device_d *dev; + struct device *dev; struct usb_gadget gadget; struct usb_gadget_driver *driver; diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c index 201ff9eafd..58eb28ad1a 100644 --- a/drivers/usb/gadget/usbstring.c +++ b/drivers/usb/gadget/usbstring.c @@ -1,17 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2003 David Brownell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. */ #include <common.h> #include <errno.h> -#include <usb/ch9.h> -#include <usb/gadget.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> static inline void put_unaligned_le16(u16 val, u8 *p) { @@ -101,7 +97,7 @@ fail: * characters (which are also widely used in C strings). */ int -usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf) +usb_gadget_get_string (const struct usb_gadget_strings *table, int id, u8 *buf) { struct usb_string *s; int len; diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 51697e109e..d38b4dcac4 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config USB_EHCI bool "EHCI driver" depends on HAS_DMA @@ -21,9 +22,6 @@ config USB_OHCI_AT91 depends on ARCH_AT91 bool "AT91 OHCI driver" -config USB_DWC2_HOST - bool "DWC2 Host driver" - config USB_XHCI bool "xHCI driver" depends on HAS_DMA diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index d417410e90..e55dff4580 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -1,7 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_USB_EHCI) += ehci-hcd.o obj-$(CONFIG_USB_EHCI_OMAP) += ehci-omap.o obj-$(CONFIG_USB_EHCI_ATMEL) += ehci-atmel.o obj-$(CONFIG_USB_OHCI) += ohci-hcd.o obj-$(CONFIG_USB_OHCI_AT91) += ohci-at91.o -obj-$(CONFIG_USB_DWC2_HOST) += dwc2.o obj-$(CONFIG_USB_XHCI) += xhci.o xhci-mem.o xhci-ring.o diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c deleted file mode 100644 index 1df1149686..0000000000 --- a/drivers/usb/host/dwc2.c +++ /dev/null @@ -1,1132 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org> - * Copyright (C) 2014 Marek Vasut <marex@denx.de> - */ - -#include <common.h> -#include <usb/usb.h> -#include <usb/usbroothubdes.h> -#include <malloc.h> -#include <init.h> -#include <io.h> -#include <of.h> -#include <linux/iopoll.h> -#include <dma.h> - -#include "dwc2.h" - -/* Use only HC channel 0. */ -#define DWC2_HC_CHANNEL 0 - -#define DWC2_STATUS_BUF_SIZE 64 -#define DWC2_DATA_BUF_SIZE (16 * 1024) - -#define MAX_DEVICE 16 -#define MAX_ENDPOINT 16 - -struct dwc2_priv { - struct device_d *dev; - struct usb_host host; - uint8_t *dmabuf; - - u8 in_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; - u8 out_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; - struct dwc2_core_regs *regs; - int root_hub_devnum; - bool ext_vbus; - /* - * The hnp/srp capability must be disabled if the platform - * does't support hnp/srp. Otherwise the force mode can't work. - */ - bool hnp_srp_disable; - bool oc_disable; -}; - -/* - * Initializes the FSLSPClkSel field of the HCFG register - * depending on the PHY type. - */ -static void init_fslspclksel(struct dwc2_priv *priv) -{ - struct dwc2_core_regs *regs = priv->regs; - uint32_t phyclk; - - phyclk = DWC2_HCFG_FSLSPCLKSEL_48_MHZ; /* Full speed PHY */ - - clrsetbits_le32(®s->host_regs.hcfg, - DWC2_HCFG_FSLSPCLKSEL_MASK, - phyclk << DWC2_HCFG_FSLSPCLKSEL_OFFSET); -} - -/* - * Flush a Tx FIFO. - * - * @param regs Programming view of DWC_otg controller. - * @param num Tx FIFO to flush. - */ -static void dwc_otg_flush_tx_fifo(struct dwc2_priv *priv, const int num) -{ - struct dwc2_core_regs *regs = priv->regs; - struct device_d *dev = priv->dev; - int ret; - uint32_t val; - - writel(DWC2_GRSTCTL_TXFFLSH | (num << DWC2_GRSTCTL_TXFNUM_OFFSET), - ®s->grstctl); - ret = readl_poll_timeout(®s->grstctl, val, !(val & DWC2_GRSTCTL_TXFFLSH), - 1000000); - if (ret) - dev_err(dev, "%s: Timeout!\n", __func__); - - /* Wait for 3 PHY Clocks */ - udelay(1); -} - -/* - * Flush Rx FIFO. - * - * @param regs Programming view of DWC_otg controller. - */ -static void dwc_otg_flush_rx_fifo(struct dwc2_priv *priv) -{ - struct dwc2_core_regs *regs = priv->regs; - struct device_d *dev = priv->dev; - int ret; - uint32_t val; - - writel(DWC2_GRSTCTL_RXFFLSH, ®s->grstctl); - ret = readl_poll_timeout(®s->grstctl, val, !(val & DWC2_GRSTCTL_RXFFLSH), - 1000000); - if (ret) - dev_err(dev, "%s: Timeout!\n", __func__); - - /* Wait for 3 PHY Clocks */ - udelay(1); -} - -/* - * Do core a soft reset of the core. Be careful with this because it - * resets all the internal state machines of the core. - */ -static void dwc_otg_core_reset(struct dwc2_priv *priv) -{ - struct dwc2_core_regs *regs = priv->regs; - struct device_d *dev = priv->dev; - uint32_t val; - int ret; - - /* Wait for AHB master IDLE state. */ - ret = readl_poll_timeout(®s->grstctl, val, val & DWC2_GRSTCTL_AHBIDLE, - 1000000); - if (ret) - dev_err(dev, "%s: Timeout!\n", __func__); - - /* Core Soft Reset */ - writel(DWC2_GRSTCTL_CSFTRST, ®s->grstctl); - ret = readl_poll_timeout(®s->grstctl, val, !(val & DWC2_GRSTCTL_CSFTRST), - 1000000); - if (ret) - dev_err(dev, "%s: Timeout!\n", __func__); - - /* - * Wait for core to come out of reset. - * NOTE: This long sleep is _very_ important, otherwise the core will - * not stay in host mode after a connector ID change! - */ - mdelay(100); -} - -/* - * This function initializes the DWC_otg controller registers for - * host mode. - * - * This function flushes the Tx and Rx FIFOs and it flushes any entries in the - * request queues. Host channels are reset to ensure that they are ready for - * performing transfers. - * - * @param dev USB Device (NULL if driver model is not being used) - * @param regs Programming view of DWC_otg controller - * - */ -static void dwc_otg_core_host_init(struct dwc2_priv *priv) -{ - struct dwc2_core_regs *regs = priv->regs; - struct device_d *dev = priv->dev; - uint32_t nptxfifosize = 0; - uint32_t ptxfifosize = 0; - uint32_t hprt0 = 0; - uint32_t val; - int i, ret, num_channels; - - /* Restart the Phy Clock */ - writel(0, ®s->pcgcctl); - - /* Initialize Host Configuration Register */ - init_fslspclksel(priv); - - /* Configure data FIFO sizes */ - if (readl(®s->ghwcfg2) & DWC2_HWCFG2_DYNAMIC_FIFO) { - /* Rx FIFO */ - writel(CONFIG_DWC2_HOST_RX_FIFO_SIZE, ®s->grxfsiz); - - /* Non-periodic Tx FIFO */ - nptxfifosize |= CONFIG_DWC2_HOST_NPERIO_TX_FIFO_SIZE << - DWC2_FIFOSIZE_DEPTH_OFFSET; - nptxfifosize |= CONFIG_DWC2_HOST_RX_FIFO_SIZE << - DWC2_FIFOSIZE_STARTADDR_OFFSET; - writel(nptxfifosize, ®s->gnptxfsiz); - - /* Periodic Tx FIFO */ - ptxfifosize |= CONFIG_DWC2_HOST_PERIO_TX_FIFO_SIZE << - DWC2_FIFOSIZE_DEPTH_OFFSET; - ptxfifosize |= (CONFIG_DWC2_HOST_RX_FIFO_SIZE + - CONFIG_DWC2_HOST_NPERIO_TX_FIFO_SIZE) << - DWC2_FIFOSIZE_STARTADDR_OFFSET; - writel(ptxfifosize, ®s->hptxfsiz); - } - - /* Clear Host Set HNP Enable in the OTG Control Register */ - clrbits_le32(®s->gotgctl, DWC2_GOTGCTL_HSTSETHNPEN); - - /* Make sure the FIFOs are flushed. */ - dwc_otg_flush_tx_fifo(priv, 0x10); /* All Tx FIFOs */ - dwc_otg_flush_rx_fifo(priv); - - /* Flush out any leftover queued requests. */ - num_channels = readl(®s->ghwcfg2); - num_channels &= DWC2_HWCFG2_NUM_HOST_CHAN_MASK; - num_channels >>= DWC2_HWCFG2_NUM_HOST_CHAN_OFFSET; - num_channels += 1; - - for (i = 0; i < num_channels; i++) - clrsetbits_le32(®s->hc_regs[i].hcchar, - DWC2_HCCHAR_CHEN | DWC2_HCCHAR_EPDIR, - DWC2_HCCHAR_CHDIS); - - /* Halt all channels to put them into a known state. */ - for (i = 0; i < num_channels; i++) { - clrsetbits_le32(®s->hc_regs[i].hcchar, - DWC2_HCCHAR_EPDIR, - DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS); - ret = readl_poll_timeout(®s->hc_regs[i].hcchar, val, - !(val & DWC2_HCCHAR_CHEN), - 1000000); - if (ret) - dev_err(dev, "%s: Timeout!\n", __func__); - } - - /* Turn on the vbus power. */ - if (readl(®s->gintsts) & DWC2_GINTSTS_CURMODE_HOST) { - hprt0 = readl(®s->hprt0); - hprt0 &= ~(DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET); - hprt0 &= ~(DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG); - if (!(hprt0 & DWC2_HPRT0_PRTPWR)) { - hprt0 |= DWC2_HPRT0_PRTPWR; - writel(hprt0, ®s->hprt0); - } - } -} - -/* - * This function initializes the DWC_otg controller registers and - * prepares the core for device mode or host mode operation. - * - * @param regs Programming view of the DWC_otg controller - */ -static void dwc_otg_core_init(struct dwc2_priv *priv) -{ - struct dwc2_core_regs *regs = priv->regs; - uint32_t ahbcfg = 0; - uint32_t usbcfg = 0; - uint8_t brst_sz = 32; - - /* Common Initialization */ - usbcfg = readl(®s->gusbcfg); - - /* Program the ULPI External VBUS bit if needed */ - if (priv->ext_vbus) { - usbcfg |= DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV; - if (!priv->oc_disable) { - usbcfg |= DWC2_GUSBCFG_ULPI_INT_VBUS_INDICATOR | - DWC2_GUSBCFG_INDICATOR_PASSTHROUGH; - } - } else { - usbcfg &= ~DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV; - } - - /* Set external TS Dline pulsing */ - usbcfg &= ~DWC2_GUSBCFG_TERM_SEL_DL_PULSE; - writel(usbcfg, ®s->gusbcfg); - - /* Reset the Controller */ - dwc_otg_core_reset(priv); - - /* High speed PHY. */ - - /* - * HS PHY parameters. These parameters are preserved during - * soft reset so only program the first time. Do a soft reset - * immediately after setting phyif. - */ - usbcfg &= ~(DWC2_GUSBCFG_ULPI_UTMI_SEL | DWC2_GUSBCFG_PHYIF); - usbcfg |= CONFIG_DWC2_PHY_TYPE << DWC2_GUSBCFG_ULPI_UTMI_SEL_OFFSET; - - if (usbcfg & DWC2_GUSBCFG_ULPI_UTMI_SEL) /* ULPI interface */ - usbcfg &= ~DWC2_GUSBCFG_DDRSEL; - - writel(usbcfg, ®s->gusbcfg); - - /* Reset after setting the PHY parameters */ - dwc_otg_core_reset(priv); - - usbcfg = readl(®s->gusbcfg); - usbcfg &= ~(DWC2_GUSBCFG_ULPI_FSLS | DWC2_GUSBCFG_ULPI_CLK_SUS_M); - - if (priv->hnp_srp_disable) - usbcfg |= DWC2_GUSBCFG_FORCEHOSTMODE; - - writel(usbcfg, ®s->gusbcfg); - - /* Program the GAHBCFG Register. */ - switch (readl(®s->ghwcfg2) & DWC2_HWCFG2_ARCHITECTURE_MASK) { - case DWC2_HWCFG2_ARCHITECTURE_SLAVE_ONLY: - break; - case DWC2_HWCFG2_ARCHITECTURE_EXT_DMA: - while (brst_sz > 1) { - ahbcfg |= ahbcfg + (1 << DWC2_GAHBCFG_HBURSTLEN_OFFSET); - ahbcfg &= DWC2_GAHBCFG_HBURSTLEN_MASK; - brst_sz >>= 1; - } - - ahbcfg |= DWC2_GAHBCFG_DMAENABLE; - break; - - case DWC2_HWCFG2_ARCHITECTURE_INT_DMA: - ahbcfg |= DWC2_GAHBCFG_HBURSTLEN_INCR4; - ahbcfg |= DWC2_GAHBCFG_DMAENABLE; - break; - } - - writel(ahbcfg, ®s->gahbcfg); - - /* Program the capabilities in GUSBCFG Register */ - usbcfg = 0; - - if (!priv->hnp_srp_disable) - usbcfg |= DWC2_GUSBCFG_HNPCAP | DWC2_GUSBCFG_SRPCAP; - - setbits_le32(®s->gusbcfg, usbcfg); -} - -/* - * Prepares a host channel for transferring packets to/from a specific - * endpoint. The HCCHARn register is set up with the characteristics specified - * in _hc. Host channel interrupts that may need to be serviced while this - * transfer is in progress are enabled. - * - * @param regs Programming view of DWC_otg controller - * @param hc Information needed to initialize the host channel - */ -static void dwc_otg_hc_init(struct dwc2_core_regs *regs, uint8_t hc_num, - struct usb_device *dev, uint8_t dev_addr, uint8_t ep_num, - uint8_t ep_is_in, uint8_t ep_type, uint16_t max_packet) -{ - struct dwc2_hc_regs *hc_regs = ®s->hc_regs[hc_num]; - uint32_t hcchar = (dev_addr << DWC2_HCCHAR_DEVADDR_OFFSET) | - (ep_num << DWC2_HCCHAR_EPNUM_OFFSET) | - (ep_is_in << DWC2_HCCHAR_EPDIR_OFFSET) | - (ep_type << DWC2_HCCHAR_EPTYPE_OFFSET) | - (max_packet << DWC2_HCCHAR_MPS_OFFSET); - - if (dev->speed == USB_SPEED_LOW) - hcchar |= DWC2_HCCHAR_LSPDDEV; - - /* - * Program the HCCHARn register with the endpoint characteristics - * for the current transfer. - */ - writel(hcchar, &hc_regs->hcchar); - - /* Program the HCSPLIT register, default to no SPLIT */ - writel(0, &hc_regs->hcsplt); -} - -static void dwc_otg_hc_init_split(struct dwc2_hc_regs *hc_regs, - uint8_t hub_devnum, uint8_t hub_port) -{ - uint32_t hcsplt = 0; - - hcsplt = DWC2_HCSPLT_SPLTENA; - hcsplt |= hub_devnum << DWC2_HCSPLT_HUBADDR_OFFSET; - hcsplt |= hub_port << DWC2_HCSPLT_PRTADDR_OFFSET; - - /* Program the HCSPLIT register for SPLITs */ - writel(hcsplt, &hc_regs->hcsplt); -} - -/* - * DWC2 to USB API interface - */ -/* Direction: In ; Request: Status */ -static int dwc_otg_submit_rh_msg_in_status(struct dwc2_core_regs *regs, - struct usb_device *dev, void *buffer, - int txlen, struct devrequest *cmd) -{ - uint32_t hprt0 = 0; - uint32_t port_status = 0; - uint32_t port_change = 0; - int len = 0; - int stat = 0; - - switch (cmd->requesttype & ~USB_DIR_IN) { - case 0: - *(uint16_t *)buffer = cpu_to_le16(1); - len = 2; - break; - case USB_RECIP_INTERFACE: - case USB_RECIP_ENDPOINT: - *(uint16_t *)buffer = cpu_to_le16(0); - len = 2; - break; - case USB_TYPE_CLASS: - *(uint32_t *)buffer = cpu_to_le32(0); - len = 4; - break; - case USB_RECIP_OTHER | USB_TYPE_CLASS: - hprt0 = readl(®s->hprt0); - if (hprt0 & DWC2_HPRT0_PRTCONNSTS) - port_status |= USB_PORT_STAT_CONNECTION; - if (hprt0 & DWC2_HPRT0_PRTENA) - port_status |= USB_PORT_STAT_ENABLE; - if (hprt0 & DWC2_HPRT0_PRTSUSP) - port_status |= USB_PORT_STAT_SUSPEND; - if (hprt0 & DWC2_HPRT0_PRTOVRCURRACT) - port_status |= USB_PORT_STAT_OVERCURRENT; - if (hprt0 & DWC2_HPRT0_PRTRST) - port_status |= USB_PORT_STAT_RESET; - if (hprt0 & DWC2_HPRT0_PRTPWR) - port_status |= USB_PORT_STAT_POWER; - - if ((hprt0 & DWC2_HPRT0_PRTSPD_MASK) == DWC2_HPRT0_PRTSPD_LOW) - port_status |= USB_PORT_STAT_LOW_SPEED; - else if ((hprt0 & DWC2_HPRT0_PRTSPD_MASK) == - DWC2_HPRT0_PRTSPD_HIGH) - port_status |= USB_PORT_STAT_HIGH_SPEED; - - if (hprt0 & DWC2_HPRT0_PRTENCHNG) - port_change |= USB_PORT_STAT_C_ENABLE; - if (hprt0 & DWC2_HPRT0_PRTCONNDET) - port_change |= USB_PORT_STAT_C_CONNECTION; - if (hprt0 & DWC2_HPRT0_PRTOVRCURRCHNG) - port_change |= USB_PORT_STAT_C_OVERCURRENT; - - *(uint32_t *)buffer = cpu_to_le32(port_status | - (port_change << 16)); - len = 4; - break; - default: - pr_err("%s: unsupported root hub command\n", __func__); - stat = USB_ST_STALLED; - } - - dev->act_len = min(len, txlen); - dev->status = stat; - - return stat; -} - -/* Direction: In ; Request: Descriptor */ -static int dwc_otg_submit_rh_msg_in_descriptor(struct usb_device *dev, - void *buffer, int txlen, - struct devrequest *cmd) -{ - unsigned char data[32]; - uint32_t dsc; - int len = 0; - int stat = 0; - uint16_t wValue = cpu_to_le16(cmd->value); - uint16_t wLength = cpu_to_le16(cmd->length); - - switch (cmd->requesttype & ~USB_DIR_IN) { - case 0: - switch (wValue & 0xff00) { - case 0x0100: /* device descriptor */ - len = min3(txlen, (int)sizeof(root_hub_dev_des), (int)wLength); - memcpy(buffer, root_hub_dev_des, len); - break; - case 0x0200: /* configuration descriptor */ - len = min3(txlen, (int)sizeof(root_hub_config_des), (int)wLength); - memcpy(buffer, root_hub_config_des, len); - break; - case 0x0300: /* string descriptors */ - switch (wValue & 0xff) { - case 0x00: - len = min3(txlen, (int)sizeof(root_hub_str_index0), - (int)wLength); - memcpy(buffer, root_hub_str_index0, len); - break; - case 0x01: - len = min3(txlen, (int)sizeof(root_hub_str_index1), - (int)wLength); - memcpy(buffer, root_hub_str_index1, len); - break; - } - break; - default: - stat = USB_ST_STALLED; - } - break; - - case USB_TYPE_CLASS: - /* Root port config, set 1 port and nothing else. */ - dsc = 0x00000001; - - data[0] = 9; /* min length; */ - data[1] = 0x29; - data[2] = dsc & RH_A_NDP; - data[3] = 0; - if (dsc & RH_A_PSM) - data[3] |= 0x1; - if (dsc & RH_A_NOCP) - data[3] |= 0x10; - else if (dsc & RH_A_OCPM) - data[3] |= 0x8; - - /* corresponds to data[4-7] */ - data[5] = (dsc & RH_A_POTPGT) >> 24; - data[7] = dsc & RH_B_DR; - if (data[2] < 7) { - data[8] = 0xff; - } else { - data[0] += 2; - data[8] = (dsc & RH_B_DR) >> 8; - data[9] = 0xff; - data[10] = data[9]; - } - - len = min3(txlen, (int)data[0], (int)wLength); - memcpy(buffer, data, len); - break; - default: - pr_err("%s: unsupported root hub command\n", __func__); - stat = USB_ST_STALLED; - } - - dev->act_len = min(len, txlen); - dev->status = stat; - - return stat; -} - -/* Direction: In ; Request: Configuration */ -static int dwc_otg_submit_rh_msg_in_configuration(struct usb_device *dev, - void *buffer, int txlen, - struct devrequest *cmd) -{ - int len = 0; - int stat = 0; - - switch (cmd->requesttype & ~USB_DIR_IN) { - case 0: - *(uint8_t *)buffer = 0x01; - len = 1; - break; - default: - pr_err("%s: unsupported root hub command\n", __func__); - stat = USB_ST_STALLED; - } - - dev->act_len = min(len, txlen); - dev->status = stat; - - return stat; -} - -/* Direction: In */ -static int dwc_otg_submit_rh_msg_in(struct dwc2_priv *priv, - struct usb_device *dev, void *buffer, - int txlen, struct devrequest *cmd) -{ - switch (cmd->request) { - case USB_REQ_GET_STATUS: - return dwc_otg_submit_rh_msg_in_status(priv->regs, dev, buffer, - txlen, cmd); - case USB_REQ_GET_DESCRIPTOR: - return dwc_otg_submit_rh_msg_in_descriptor(dev, buffer, - txlen, cmd); - case USB_REQ_GET_CONFIGURATION: - return dwc_otg_submit_rh_msg_in_configuration(dev, buffer, - txlen, cmd); - default: - pr_err("%s: unsupported root hub command\n", __func__); - return USB_ST_STALLED; - } -} - -/* Direction: Out */ -static int dwc_otg_submit_rh_msg_out(struct dwc2_priv *priv, - struct usb_device *dev, - void *buffer, int txlen, - struct devrequest *cmd) -{ - struct dwc2_core_regs *regs = priv->regs; - int len = 0; - int stat = 0; - uint16_t bmrtype_breq = cmd->requesttype | (cmd->request << 8); - uint16_t wValue = cpu_to_le16(cmd->value); - - switch (bmrtype_breq & ~USB_DIR_IN) { - case (USB_REQ_CLEAR_FEATURE << 8) | USB_RECIP_ENDPOINT: - case (USB_REQ_CLEAR_FEATURE << 8) | USB_TYPE_CLASS: - break; - - case (USB_REQ_CLEAR_FEATURE << 8) | USB_RECIP_OTHER | USB_TYPE_CLASS: - switch (wValue) { - case USB_PORT_FEAT_C_CONNECTION: - setbits_le32(®s->hprt0, DWC2_HPRT0_PRTCONNDET); - break; - } - break; - - case (USB_REQ_SET_FEATURE << 8) | USB_RECIP_OTHER | USB_TYPE_CLASS: - switch (wValue) { - case USB_PORT_FEAT_SUSPEND: - break; - - case USB_PORT_FEAT_RESET: - clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | - DWC2_HPRT0_PRTCONNDET | - DWC2_HPRT0_PRTENCHNG | - DWC2_HPRT0_PRTOVRCURRCHNG, - DWC2_HPRT0_PRTRST); - mdelay(50); - clrbits_le32(®s->hprt0, DWC2_HPRT0_PRTRST); - break; - - case USB_PORT_FEAT_POWER: - clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | - DWC2_HPRT0_PRTCONNDET | - DWC2_HPRT0_PRTENCHNG | - DWC2_HPRT0_PRTOVRCURRCHNG, - DWC2_HPRT0_PRTRST); - break; - - case USB_PORT_FEAT_ENABLE: - break; - } - break; - case (USB_REQ_SET_ADDRESS << 8): - priv->root_hub_devnum = wValue; - break; - case (USB_REQ_SET_CONFIGURATION << 8): - break; - default: - pr_err("%s: unsupported root hub command\n", __func__); - stat = USB_ST_STALLED; - } - - len = min(len, txlen); - - dev->act_len = len; - dev->status = stat; - - return stat; -} - -static int dwc_otg_submit_rh_msg(struct dwc2_priv *priv, struct usb_device *dev, - unsigned long pipe, void *buffer, int txlen, - struct devrequest *cmd) -{ - int stat = 0; - - if (usb_pipeint(pipe)) { - pr_err("Root-Hub submit IRQ: NOT implemented\n"); - return 0; - } - - if (cmd->requesttype & USB_DIR_IN) - stat = dwc_otg_submit_rh_msg_in(priv, dev, buffer, txlen, cmd); - else - stat = dwc_otg_submit_rh_msg_out(priv, dev, buffer, txlen, cmd); - - mdelay(1); - - return stat; -} - -static int wait_for_chhltd(struct dwc2_priv *priv, uint32_t *sub, - u8 *toggle, int timeout_ms) -{ - struct dwc2_core_regs *regs = priv->regs; - struct dwc2_hc_regs *hc_regs = ®s->hc_regs[DWC2_HC_CHANNEL]; - struct device_d *dev = priv->dev; - int ret; - uint32_t hcint, hctsiz; - uint32_t val; - int timeout_us = timeout_ms * 1000; - - ret = readl_poll_timeout(&hc_regs->hcint, val, - val & DWC2_HCINT_CHHLTD, timeout_us); - if (ret) { - clrsetbits_le32(&hc_regs->hcchar, 0, DWC2_HCCHAR_CHDIS); - readl_poll_timeout(&hc_regs->hcint, val, - val & DWC2_HCINT_CHHLTD, 10000); - - return ret; - } - - hcint = readl(&hc_regs->hcint); - hctsiz = readl(&hc_regs->hctsiz); - *sub = (hctsiz & DWC2_HCTSIZ_XFERSIZE_MASK) >> - DWC2_HCTSIZ_XFERSIZE_OFFSET; - *toggle = (hctsiz & DWC2_HCTSIZ_PID_MASK) >> DWC2_HCTSIZ_PID_OFFSET; - - dev_dbg(dev, "%s: HCINT=%08x sub=%u toggle=%d\n", __func__, hcint, *sub, - *toggle); - - if (hcint & DWC2_HCINT_XFERCOMP) - return 0; - - if (hcint & (DWC2_HCINT_NAK | DWC2_HCINT_FRMOVRUN)) - return -EAGAIN; - - dev_dbg(dev, "%s: Error (HCINT=%08x)\n", __func__, hcint); - - return -EINVAL; -} - -static int dwc2_eptype[] = { - DWC2_HCCHAR_EPTYPE_ISOC, - DWC2_HCCHAR_EPTYPE_INTR, - DWC2_HCCHAR_EPTYPE_CONTROL, - DWC2_HCCHAR_EPTYPE_BULK, -}; - -static int transfer_chunk(struct dwc2_priv *priv, u8 *pid, int in, void *buffer, - int num_packets, int xfer_len, int *actual_len, - int odd_frame, int timeout_ms) -{ - struct dwc2_core_regs *regs = priv->regs; - struct dwc2_hc_regs *hc_regs = ®s->hc_regs[DWC2_HC_CHANNEL]; - int ret = 0; - uint32_t sub = 0; - enum dma_data_direction dir; - dma_addr_t dma = 0; - - dev_dbg(priv->dev, "%s: chunk: pid %d xfer_len %u pkts %u\n", - __func__, *pid, xfer_len, num_packets); - - writel((xfer_len << DWC2_HCTSIZ_XFERSIZE_OFFSET) | - (num_packets << DWC2_HCTSIZ_PKTCNT_OFFSET) | - (*pid << DWC2_HCTSIZ_PID_OFFSET), - &hc_regs->hctsiz); - - if (xfer_len) { - if (in) { - dir = DMA_FROM_DEVICE; - } else { - memcpy(priv->dmabuf, buffer, xfer_len); - dir = DMA_TO_DEVICE; - } - dma = dma_map_single(priv->dev, priv->dmabuf, xfer_len, dir); - } - - writel(dma, &hc_regs->hcdma); - - /* Clear old interrupt conditions for this host channel. */ - writel(0x3fff, &hc_regs->hcint); - - /* Set host channel enable after all other setup is complete. */ - clrsetbits_le32(&hc_regs->hcchar, DWC2_HCCHAR_MULTICNT_MASK | - DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS | - DWC2_HCCHAR_ODDFRM, - (1 << DWC2_HCCHAR_MULTICNT_OFFSET) | - (odd_frame << DWC2_HCCHAR_ODDFRM_OFFSET) | - DWC2_HCCHAR_CHEN); - - ret = wait_for_chhltd(priv, &sub, pid, timeout_ms); - - if (xfer_len) - dma_unmap_single(priv->dev, dma, xfer_len, dir); - - if (in) { - xfer_len -= sub; - - memcpy(buffer, priv->dmabuf, xfer_len); - } - - if (!ret) - *actual_len = xfer_len; - - return ret; -} - -static int usb_find_usb2_hub_address_port(struct usb_device *udev, - uint8_t *hub_address, uint8_t *hub_port) -{ - /* Find out the nearest parent which is high speed */ - while (udev->parent->parent) { - if (udev->parent->speed != USB_SPEED_HIGH) { - udev = udev->parent; - } else { - *hub_address = udev->parent->devnum; - *hub_port = udev->portnr; - return 0; - } - } - - return -EINVAL; -} - -static int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev, - unsigned long pipe, u8 *pid, int in, void *buffer, int len, - int timeout_ms) -{ - struct dwc2_core_regs *regs = priv->regs; - struct dwc2_hc_regs *hc_regs = ®s->hc_regs[DWC2_HC_CHANNEL]; - struct dwc2_host_regs *host_regs = ®s->host_regs; - int devnum = usb_pipedevice(pipe); - int ep = usb_pipeendpoint(pipe); - int max = usb_maxpacket(dev, pipe); - int eptype = dwc2_eptype[usb_pipetype(pipe)]; - int done = 0; - int ret = 0; - int do_split = 0; - int complete_split = 0; - uint32_t xfer_len; - uint32_t num_packets; - int stop_transfer = 0; - uint32_t max_xfer_len; - int ssplit_frame_num = 0; - - dev_dbg(priv->dev, "%s: msg: pipe %lx pid %d in %d len %d\n", - __func__, pipe, *pid, in, len); - - max_xfer_len = CONFIG_DWC2_MAX_PACKET_COUNT * max; - if (max_xfer_len > CONFIG_DWC2_MAX_TRANSFER_SIZE) - max_xfer_len = CONFIG_DWC2_MAX_TRANSFER_SIZE; - if (max_xfer_len > DWC2_DATA_BUF_SIZE) - max_xfer_len = DWC2_DATA_BUF_SIZE; - - /* Make sure that max_xfer_len is a multiple of max packet size. */ - num_packets = max_xfer_len / max; - max_xfer_len = num_packets * max; - - /* Initialize channel */ - dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, dev, devnum, ep, in, - eptype, max); - - /* Check if the target is a FS/LS device behind a HS hub */ - if (dev->speed != USB_SPEED_HIGH) { - uint8_t hub_addr; - uint8_t hub_port; - uint32_t hprt0 = readl(®s->hprt0); - - if ((hprt0 & DWC2_HPRT0_PRTSPD_MASK) == DWC2_HPRT0_PRTSPD_HIGH) { - ret = usb_find_usb2_hub_address_port(dev, &hub_addr, - &hub_port); - if (ret) - return ret; - dwc_otg_hc_init_split(hc_regs, hub_addr, hub_port); - - do_split = 1; - num_packets = 1; - max_xfer_len = max; - } - } - - do { - int actual_len = 0; - uint32_t hcint; - int odd_frame = 0; - xfer_len = len - done; - - if (xfer_len > max_xfer_len) - xfer_len = max_xfer_len; - else if (xfer_len > max) - num_packets = (xfer_len + max - 1) / max; - else - num_packets = 1; - - if (complete_split) - setbits_le32(&hc_regs->hcsplt, DWC2_HCSPLT_COMPSPLT); - else if (do_split) - clrbits_le32(&hc_regs->hcsplt, DWC2_HCSPLT_COMPSPLT); - - if (eptype == DWC2_HCCHAR_EPTYPE_INTR) { - int uframe_num = readl(&host_regs->hfnum); - if (!(uframe_num & 0x1)) - odd_frame = 1; - } - - ret = transfer_chunk(priv, pid, in, (char *)buffer + done, - num_packets, xfer_len, &actual_len, - odd_frame, timeout_ms); - - hcint = readl(&hc_regs->hcint); - if (complete_split) { - stop_transfer = 0; - if (hcint & DWC2_HCINT_NYET) { - int frame_num = DWC2_HFNUM_MAX_FRNUM & - readl(&host_regs->hfnum); - ret = 0; - if (((frame_num - ssplit_frame_num) & - DWC2_HFNUM_MAX_FRNUM) > 4) - ret = -EAGAIN; - } else - complete_split = 0; - } else if (do_split) { - if (hcint & DWC2_HCINT_ACK) { - ssplit_frame_num = DWC2_HFNUM_MAX_FRNUM & - readl(&host_regs->hfnum); - ret = 0; - complete_split = 1; - } - } - - if (ret) - break; - - if (actual_len < xfer_len) - stop_transfer = 1; - - done += actual_len; - - /* - * Transactions are done when when either all data is transferred or - * there is a short transfer. In case of a SPLIT make sure the CSPLIT - * is executed. - */ - } while (((done < len) && !stop_transfer) || complete_split); - - writel(0, &hc_regs->hcintmsk); - writel(0xFFFFFFFF, &hc_regs->hcint); - - dev->status = 0; - dev->act_len = done; - - return ret; -} - -#define to_dwc2(ptr) container_of(ptr, struct dwc2_priv, host) - -static int dwc2_init_common(struct usb_host *host) -{ - struct dwc2_priv *priv = to_dwc2(host); - struct dwc2_core_regs *regs = priv->regs; - int i, j; - - priv->ext_vbus = 0; - - dwc_otg_core_init(priv); - dwc_otg_core_host_init(priv); - - clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | - DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | - DWC2_HPRT0_PRTOVRCURRCHNG, - DWC2_HPRT0_PRTRST); - mdelay(50); - clrbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET | - DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG | - DWC2_HPRT0_PRTRST); - - for (i = 0; i < MAX_DEVICE; i++) { - for (j = 0; j < MAX_ENDPOINT; j++) { - priv->in_data_toggle[i][j] = DWC2_HC_PID_DATA0; - priv->out_data_toggle[i][j] = DWC2_HC_PID_DATA0; - } - } - - return 0; -} - -static void dwc2_uninit_common(struct dwc2_core_regs *regs) -{ - /* Put everything in reset. */ - clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | - DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | - DWC2_HPRT0_PRTOVRCURRCHNG, - DWC2_HPRT0_PRTRST); -} - -static int dwc2_submit_control_msg(struct usb_device *udev, - unsigned long pipe, void *buffer, int len, - struct devrequest *setup, int timeout_ms) -{ - struct usb_host *host = udev->host; - struct dwc2_priv *priv = to_dwc2(host); - int devnum = usb_pipedevice(pipe); - int ret, act_len; - u8 pid; - /* For CONTROL endpoint pid should start with DATA1 */ - int status_direction; - - if (devnum == priv->root_hub_devnum) { - udev->status = 0; - udev->speed = USB_SPEED_HIGH; - return dwc_otg_submit_rh_msg(priv, udev, pipe, buffer, len, - setup); - } - - /* SETUP stage */ - pid = DWC2_HC_PID_SETUP; - do { - ret = chunk_msg(priv, udev, pipe, &pid, 0, setup, 8, timeout_ms); - } while (ret == -EAGAIN); - - if (ret) - return ret; - - /* DATA stage */ - act_len = 0; - if (buffer) { - pid = DWC2_HC_PID_DATA1; - do { - ret = chunk_msg(priv, udev, pipe, &pid, usb_pipein(pipe), - buffer, len, timeout_ms); - act_len += udev->act_len; - buffer += udev->act_len; - len -= udev->act_len; - } while (ret == -EAGAIN); - if (ret) - return ret; - status_direction = usb_pipeout(pipe); - } else { - /* No-data CONTROL always ends with an IN transaction */ - status_direction = 1; - } - - /* STATUS stage */ - pid = DWC2_HC_PID_DATA1; - do { - ret = chunk_msg(priv, udev, pipe, &pid, status_direction, - NULL, 0, timeout_ms); - } while (ret == -EAGAIN); - - if (ret) - return ret; - - udev->act_len = act_len; - - return 0; -} - -static int dwc2_submit_bulk_msg(struct usb_device *udev, unsigned long pipe, - void *buffer, int len, int timeout_ms) -{ - struct usb_host *host = udev->host; - struct dwc2_priv *priv = to_dwc2(host); - int devnum = usb_pipedevice(pipe); - int ep = usb_pipeendpoint(pipe); - u8* pid; - - if ((devnum >= MAX_DEVICE) || (devnum == priv->root_hub_devnum)) { - udev->status = 0; - return -EINVAL; - } - - if (usb_pipein(pipe)) - pid = &priv->in_data_toggle[devnum][ep]; - else - pid = &priv->out_data_toggle[devnum][ep]; - - return chunk_msg(priv, udev, pipe, pid, usb_pipein(pipe), buffer, len, - timeout_ms); -} - -static int dwc2_submit_int_msg(struct usb_device *udev, unsigned long pipe, - void *buffer, int len, int interval) -{ - uint64_t start; - int ret; - - start = get_time_ns(); - - while (1) { - ret = dwc2_submit_bulk_msg(udev, pipe, buffer, len, 0); - if (ret != -EAGAIN) - return ret; - if (is_timeout(start, USB_CNTL_TIMEOUT * MSECOND)) - return -ETIMEDOUT; - } -} - -static int dwc2_detect(struct device_d *dev) -{ - struct dwc2_priv *priv = dev->priv; - - return usb_host_detect(&priv->host); -} - -static int dwc2_probe(struct device_d *dev) -{ - struct resource *iores; - struct dwc2_priv *priv; - struct usb_host *host; - struct device_node *np = dev->device_node; - int ret; - uint32_t snpsid; - - priv = xzalloc(sizeof(*priv)); - - iores = dev_request_mem_resource(dev, 0); - if (IS_ERR(iores)) - return PTR_ERR(iores); - - priv->regs = IOMEM(iores->start); - priv->dev = dev; - - snpsid = readl(&priv->regs->gsnpsid); - dev_info(dev, "Core Release: %x.%03x\n", - snpsid >> 12 & 0xf, snpsid & 0xfff); - - if ((snpsid & DWC2_SNPSID_DEVID_MASK) != DWC2_SNPSID_DEVID_VER_2xx && - (snpsid & DWC2_SNPSID_DEVID_MASK) != DWC2_SNPSID_DEVID_VER_3xx) { - dev_info(dev, "SNPSID invalid (not DWC2 OTG device): %08x\n", - snpsid); - return -ENODEV; - } - - priv->oc_disable = of_property_read_bool(np, "disable-over-current"); - priv->hnp_srp_disable = of_property_read_bool(np, "hnp-srp-disable"); - priv->dmabuf = dma_alloc(DWC2_DATA_BUF_SIZE); - - host = &priv->host; - - host->init = dwc2_init_common; - host->submit_int_msg = dwc2_submit_int_msg; - host->submit_control_msg = dwc2_submit_control_msg; - host->submit_bulk_msg = dwc2_submit_bulk_msg; - - dev->priv = priv; - dev->detect = dwc2_detect; - - ret = usb_register_host(host); - if (ret) - return ret; - - return 0; -} - -static void dwc2_remove(struct device_d *dev) -{ - struct dwc2_priv *priv = dev->priv; - - dwc2_uninit_common(priv->regs); -} - -static const struct of_device_id dwc2_dt_ids[] = { - { .compatible = "brcm,bcm2835-usb" }, - { .compatible = "brcm,bcm2708-usb" }, - { .compatible = "snps,dwc2" }, - { /* sentinel */ } -}; - -static struct driver_d dwc2_driver = { - .name = "dwc2", - .probe = dwc2_probe, - .remove = dwc2_remove, - .of_compatible = DRV_OF_COMPAT(dwc2_dt_ids), -}; -device_platform_driver(dwc2_driver); diff --git a/drivers/usb/host/dwc2.h b/drivers/usb/host/dwc2.h deleted file mode 100644 index bdf338f1ed..0000000000 --- a/drivers/usb/host/dwc2.h +++ /dev/null @@ -1,778 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2014 Marek Vasut <marex@denx.de> - */ - -#ifndef __DWC2_H__ -#define __DWC2_H__ - -struct dwc2_hc_regs { - u32 hcchar; /* 0x00 */ - u32 hcsplt; - u32 hcint; - u32 hcintmsk; - u32 hctsiz; /* 0x10 */ - u32 hcdma; - u32 reserved; - u32 hcdmab; -}; - -struct dwc2_host_regs { - u32 hcfg; /* 0x00 */ - u32 hfir; - u32 hfnum; - u32 _pad_0x40c; - u32 hptxsts; /* 0x10 */ - u32 haint; - u32 haintmsk; - u32 hflbaddr; -}; - -struct dwc2_core_regs { - u32 gotgctl; /* 0x000 */ - u32 gotgint; - u32 gahbcfg; - u32 gusbcfg; - u32 grstctl; /* 0x010 */ - u32 gintsts; - u32 gintmsk; - u32 grxstsr; - u32 grxstsp; /* 0x020 */ - u32 grxfsiz; - u32 gnptxfsiz; - u32 gnptxsts; - u32 gi2cctl; /* 0x030 */ - u32 gpvndctl; - u32 ggpio; - u32 guid; - u32 gsnpsid; /* 0x040 */ - u32 ghwcfg1; - u32 ghwcfg2; - u32 ghwcfg3; - u32 ghwcfg4; /* 0x050 */ - u32 glpmcfg; - u32 _pad_0x58_0x9c[42]; - u32 hptxfsiz; /* 0x100 */ - u32 dptxfsiz_dieptxf[15]; - u32 _pad_0x140_0x3fc[176]; - struct dwc2_host_regs host_regs; /* 0x400 */ - u32 _pad_0x420_0x43c[8]; - u32 hprt0; /* 0x440 */ - u32 _pad_0x444_0x4fc[47]; - struct dwc2_hc_regs hc_regs[16]; /* 0x500 */ - u32 _pad_0x700_0xe00[448]; - u32 pcgcctl; /* 0xe00 */ -}; - -#define DWC2_GOTGCTL_SESREQSCS (1 << 0) -#define DWC2_GOTGCTL_SESREQSCS_OFFSET 0 -#define DWC2_GOTGCTL_SESREQ (1 << 1) -#define DWC2_GOTGCTL_SESREQ_OFFSET 1 -#define DWC2_GOTGCTL_HSTNEGSCS (1 << 8) -#define DWC2_GOTGCTL_HSTNEGSCS_OFFSET 8 -#define DWC2_GOTGCTL_HNPREQ (1 << 9) -#define DWC2_GOTGCTL_HNPREQ_OFFSET 9 -#define DWC2_GOTGCTL_HSTSETHNPEN (1 << 10) -#define DWC2_GOTGCTL_HSTSETHNPEN_OFFSET 10 -#define DWC2_GOTGCTL_DEVHNPEN (1 << 11) -#define DWC2_GOTGCTL_DEVHNPEN_OFFSET 11 -#define DWC2_GOTGCTL_CONIDSTS (1 << 16) -#define DWC2_GOTGCTL_CONIDSTS_OFFSET 16 -#define DWC2_GOTGCTL_DBNCTIME (1 << 17) -#define DWC2_GOTGCTL_DBNCTIME_OFFSET 17 -#define DWC2_GOTGCTL_ASESVLD (1 << 18) -#define DWC2_GOTGCTL_ASESVLD_OFFSET 18 -#define DWC2_GOTGCTL_BSESVLD (1 << 19) -#define DWC2_GOTGCTL_BSESVLD_OFFSET 19 -#define DWC2_GOTGCTL_OTGVER (1 << 20) -#define DWC2_GOTGCTL_OTGVER_OFFSET 20 -#define DWC2_GOTGINT_SESENDDET (1 << 2) -#define DWC2_GOTGINT_SESENDDET_OFFSET 2 -#define DWC2_GOTGINT_SESREQSUCSTSCHNG (1 << 8) -#define DWC2_GOTGINT_SESREQSUCSTSCHNG_OFFSET 8 -#define DWC2_GOTGINT_HSTNEGSUCSTSCHNG (1 << 9) -#define DWC2_GOTGINT_HSTNEGSUCSTSCHNG_OFFSET 9 -#define DWC2_GOTGINT_RESERVER10_16_MASK (0x7F << 10) -#define DWC2_GOTGINT_RESERVER10_16_OFFSET 10 -#define DWC2_GOTGINT_HSTNEGDET (1 << 17) -#define DWC2_GOTGINT_HSTNEGDET_OFFSET 17 -#define DWC2_GOTGINT_ADEVTOUTCHNG (1 << 18) -#define DWC2_GOTGINT_ADEVTOUTCHNG_OFFSET 18 -#define DWC2_GOTGINT_DEBDONE (1 << 19) -#define DWC2_GOTGINT_DEBDONE_OFFSET 19 -#define DWC2_GAHBCFG_GLBLINTRMSK (1 << 0) -#define DWC2_GAHBCFG_GLBLINTRMSK_OFFSET 0 -#define DWC2_GAHBCFG_HBURSTLEN_SINGLE (0 << 1) -#define DWC2_GAHBCFG_HBURSTLEN_INCR (1 << 1) -#define DWC2_GAHBCFG_HBURSTLEN_INCR4 (3 << 1) -#define DWC2_GAHBCFG_HBURSTLEN_INCR8 (5 << 1) -#define DWC2_GAHBCFG_HBURSTLEN_INCR16 (7 << 1) -#define DWC2_GAHBCFG_HBURSTLEN_MASK (0xF << 1) -#define DWC2_GAHBCFG_HBURSTLEN_OFFSET 1 -#define DWC2_GAHBCFG_DMAENABLE (1 << 5) -#define DWC2_GAHBCFG_DMAENABLE_OFFSET 5 -#define DWC2_GAHBCFG_NPTXFEMPLVL_TXFEMPLVL (1 << 7) -#define DWC2_GAHBCFG_NPTXFEMPLVL_TXFEMPLVL_OFFSET 7 -#define DWC2_GAHBCFG_PTXFEMPLVL (1 << 8) -#define DWC2_GAHBCFG_PTXFEMPLVL_OFFSET 8 -#define DWC2_GUSBCFG_TOUTCAL_MASK (0x7 << 0) -#define DWC2_GUSBCFG_TOUTCAL_OFFSET 0 -#define DWC2_GUSBCFG_PHYIF (1 << 3) -#define DWC2_GUSBCFG_PHYIF_OFFSET 3 -#define DWC2_GUSBCFG_ULPI_UTMI_SEL (1 << 4) -#define DWC2_GUSBCFG_ULPI_UTMI_SEL_OFFSET 4 -#define DWC2_GUSBCFG_FSINTF (1 << 5) -#define DWC2_GUSBCFG_FSINTF_OFFSET 5 -#define DWC2_GUSBCFG_PHYSEL (1 << 6) -#define DWC2_GUSBCFG_PHYSEL_OFFSET 6 -#define DWC2_GUSBCFG_DDRSEL (1 << 7) -#define DWC2_GUSBCFG_DDRSEL_OFFSET 7 -#define DWC2_GUSBCFG_SRPCAP (1 << 8) -#define DWC2_GUSBCFG_SRPCAP_OFFSET 8 -#define DWC2_GUSBCFG_HNPCAP (1 << 9) -#define DWC2_GUSBCFG_HNPCAP_OFFSET 9 -#define DWC2_GUSBCFG_USBTRDTIM_MASK (0xF << 10) -#define DWC2_GUSBCFG_USBTRDTIM_OFFSET 10 -#define DWC2_GUSBCFG_NPTXFRWNDEN (1 << 14) -#define DWC2_GUSBCFG_NPTXFRWNDEN_OFFSET 14 -#define DWC2_GUSBCFG_PHYLPWRCLKSEL (1 << 15) -#define DWC2_GUSBCFG_PHYLPWRCLKSEL_OFFSET 15 -#define DWC2_GUSBCFG_OTGUTMIFSSEL (1 << 16) -#define DWC2_GUSBCFG_OTGUTMIFSSEL_OFFSET 16 -#define DWC2_GUSBCFG_ULPI_FSLS (1 << 17) -#define DWC2_GUSBCFG_ULPI_FSLS_OFFSET 17 -#define DWC2_GUSBCFG_ULPI_AUTO_RES (1 << 18) -#define DWC2_GUSBCFG_ULPI_AUTO_RES_OFFSET 18 -#define DWC2_GUSBCFG_ULPI_CLK_SUS_M (1 << 19) -#define DWC2_GUSBCFG_ULPI_CLK_SUS_M_OFFSET 19 -#define DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV (1 << 20) -#define DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV_OFFSET 20 -#define DWC2_GUSBCFG_ULPI_INT_VBUS_INDICATOR (1 << 21) -#define DWC2_GUSBCFG_ULPI_INT_VBUS_INDICATOR_OFFSET 21 -#define DWC2_GUSBCFG_TERM_SEL_DL_PULSE (1 << 22) -#define DWC2_GUSBCFG_TERM_SEL_DL_PULSE_OFFSET 22 -#define DWC2_GUSBCFG_INDICATOR_PASSTHROUGH (1 << 24) -#define DWC2_GUSBCFG_INDICATOR_PASSTHROUGH_OFFSET 24 -#define DWC2_GUSBCFG_IC_USB_CAP (1 << 26) -#define DWC2_GUSBCFG_IC_USB_CAP_OFFSET 26 -#define DWC2_GUSBCFG_IC_TRAFFIC_PULL_REMOVE (1 << 27) -#define DWC2_GUSBCFG_IC_TRAFFIC_PULL_REMOVE_OFFSET 27 -#define DWC2_GUSBCFG_TX_END_DELAY (1 << 28) -#define DWC2_GUSBCFG_TX_END_DELAY_OFFSET 28 -#define DWC2_GUSBCFG_FORCEHOSTMODE (1 << 29) -#define DWC2_GUSBCFG_FORCEHOSTMODE_OFFSET 29 -#define DWC2_GUSBCFG_FORCEDEVMODE (1 << 30) -#define DWC2_GUSBCFG_FORCEDEVMODE_OFFSET 30 -#define DWC2_GLPMCTL_LPM_CAP_EN (1 << 0) -#define DWC2_GLPMCTL_LPM_CAP_EN_OFFSET 0 -#define DWC2_GLPMCTL_APPL_RESP (1 << 1) -#define DWC2_GLPMCTL_APPL_RESP_OFFSET 1 -#define DWC2_GLPMCTL_HIRD_MASK (0xF << 2) -#define DWC2_GLPMCTL_HIRD_OFFSET 2 -#define DWC2_GLPMCTL_REM_WKUP_EN (1 << 6) -#define DWC2_GLPMCTL_REM_WKUP_EN_OFFSET 6 -#define DWC2_GLPMCTL_EN_UTMI_SLEEP (1 << 7) -#define DWC2_GLPMCTL_EN_UTMI_SLEEP_OFFSET 7 -#define DWC2_GLPMCTL_HIRD_THRES_MASK (0x1F << 8) -#define DWC2_GLPMCTL_HIRD_THRES_OFFSET 8 -#define DWC2_GLPMCTL_LPM_RESP_MASK (0x3 << 13) -#define DWC2_GLPMCTL_LPM_RESP_OFFSET 13 -#define DWC2_GLPMCTL_PRT_SLEEP_STS (1 << 15) -#define DWC2_GLPMCTL_PRT_SLEEP_STS_OFFSET 15 -#define DWC2_GLPMCTL_SLEEP_STATE_RESUMEOK (1 << 16) -#define DWC2_GLPMCTL_SLEEP_STATE_RESUMEOK_OFFSET 16 -#define DWC2_GLPMCTL_LPM_CHAN_INDEX_MASK (0xF << 17) -#define DWC2_GLPMCTL_LPM_CHAN_INDEX_OFFSET 17 -#define DWC2_GLPMCTL_RETRY_COUNT_MASK (0x7 << 21) -#define DWC2_GLPMCTL_RETRY_COUNT_OFFSET 21 -#define DWC2_GLPMCTL_SEND_LPM (1 << 24) -#define DWC2_GLPMCTL_SEND_LPM_OFFSET 24 -#define DWC2_GLPMCTL_RETRY_COUNT_STS_MASK (0x7 << 25) -#define DWC2_GLPMCTL_RETRY_COUNT_STS_OFFSET 25 -#define DWC2_GLPMCTL_HSIC_CONNECT (1 << 30) -#define DWC2_GLPMCTL_HSIC_CONNECT_OFFSET 30 -#define DWC2_GLPMCTL_INV_SEL_HSIC (1 << 31) -#define DWC2_GLPMCTL_INV_SEL_HSIC_OFFSET 31 -#define DWC2_GRSTCTL_CSFTRST (1 << 0) -#define DWC2_GRSTCTL_CSFTRST_OFFSET 0 -#define DWC2_GRSTCTL_HSFTRST (1 << 1) -#define DWC2_GRSTCTL_HSFTRST_OFFSET 1 -#define DWC2_GRSTCTL_HSTFRM (1 << 2) -#define DWC2_GRSTCTL_HSTFRM_OFFSET 2 -#define DWC2_GRSTCTL_INTKNQFLSH (1 << 3) -#define DWC2_GRSTCTL_INTKNQFLSH_OFFSET 3 -#define DWC2_GRSTCTL_RXFFLSH (1 << 4) -#define DWC2_GRSTCTL_RXFFLSH_OFFSET 4 -#define DWC2_GRSTCTL_TXFFLSH (1 << 5) -#define DWC2_GRSTCTL_TXFFLSH_OFFSET 5 -#define DWC2_GRSTCTL_TXFNUM_MASK (0x1F << 6) -#define DWC2_GRSTCTL_TXFNUM_OFFSET 6 -#define DWC2_GRSTCTL_DMAREQ (1 << 30) -#define DWC2_GRSTCTL_DMAREQ_OFFSET 30 -#define DWC2_GRSTCTL_AHBIDLE (1 << 31) -#define DWC2_GRSTCTL_AHBIDLE_OFFSET 31 -#define DWC2_GINTMSK_MODEMISMATCH (1 << 1) -#define DWC2_GINTMSK_MODEMISMATCH_OFFSET 1 -#define DWC2_GINTMSK_OTGINTR (1 << 2) -#define DWC2_GINTMSK_OTGINTR_OFFSET 2 -#define DWC2_GINTMSK_SOFINTR (1 << 3) -#define DWC2_GINTMSK_SOFINTR_OFFSET 3 -#define DWC2_GINTMSK_RXSTSQLVL (1 << 4) -#define DWC2_GINTMSK_RXSTSQLVL_OFFSET 4 -#define DWC2_GINTMSK_NPTXFEMPTY (1 << 5) -#define DWC2_GINTMSK_NPTXFEMPTY_OFFSET 5 -#define DWC2_GINTMSK_GINNAKEFF (1 << 6) -#define DWC2_GINTMSK_GINNAKEFF_OFFSET 6 -#define DWC2_GINTMSK_GOUTNAKEFF (1 << 7) -#define DWC2_GINTMSK_GOUTNAKEFF_OFFSET 7 -#define DWC2_GINTMSK_I2CINTR (1 << 9) -#define DWC2_GINTMSK_I2CINTR_OFFSET 9 -#define DWC2_GINTMSK_ERLYSUSPEND (1 << 10) -#define DWC2_GINTMSK_ERLYSUSPEND_OFFSET 10 -#define DWC2_GINTMSK_USBSUSPEND (1 << 11) -#define DWC2_GINTMSK_USBSUSPEND_OFFSET 11 -#define DWC2_GINTMSK_USBRESET (1 << 12) -#define DWC2_GINTMSK_USBRESET_OFFSET 12 -#define DWC2_GINTMSK_ENUMDONE (1 << 13) -#define DWC2_GINTMSK_ENUMDONE_OFFSET 13 -#define DWC2_GINTMSK_ISOOUTDROP (1 << 14) -#define DWC2_GINTMSK_ISOOUTDROP_OFFSET 14 -#define DWC2_GINTMSK_EOPFRAME (1 << 15) -#define DWC2_GINTMSK_EOPFRAME_OFFSET 15 -#define DWC2_GINTMSK_EPMISMATCH (1 << 17) -#define DWC2_GINTMSK_EPMISMATCH_OFFSET 17 -#define DWC2_GINTMSK_INEPINTR (1 << 18) -#define DWC2_GINTMSK_INEPINTR_OFFSET 18 -#define DWC2_GINTMSK_OUTEPINTR (1 << 19) -#define DWC2_GINTMSK_OUTEPINTR_OFFSET 19 -#define DWC2_GINTMSK_INCOMPLISOIN (1 << 20) -#define DWC2_GINTMSK_INCOMPLISOIN_OFFSET 20 -#define DWC2_GINTMSK_INCOMPLISOOUT (1 << 21) -#define DWC2_GINTMSK_INCOMPLISOOUT_OFFSET 21 -#define DWC2_GINTMSK_PORTINTR (1 << 24) -#define DWC2_GINTMSK_PORTINTR_OFFSET 24 -#define DWC2_GINTMSK_HCINTR (1 << 25) -#define DWC2_GINTMSK_HCINTR_OFFSET 25 -#define DWC2_GINTMSK_PTXFEMPTY (1 << 26) -#define DWC2_GINTMSK_PTXFEMPTY_OFFSET 26 -#define DWC2_GINTMSK_LPMTRANRCVD (1 << 27) -#define DWC2_GINTMSK_LPMTRANRCVD_OFFSET 27 -#define DWC2_GINTMSK_CONIDSTSCHNG (1 << 28) -#define DWC2_GINTMSK_CONIDSTSCHNG_OFFSET 28 -#define DWC2_GINTMSK_DISCONNECT (1 << 29) -#define DWC2_GINTMSK_DISCONNECT_OFFSET 29 -#define DWC2_GINTMSK_SESSREQINTR (1 << 30) -#define DWC2_GINTMSK_SESSREQINTR_OFFSET 30 -#define DWC2_GINTMSK_WKUPINTR (1 << 31) -#define DWC2_GINTMSK_WKUPINTR_OFFSET 31 -#define DWC2_GINTSTS_CURMODE_DEVICE (0 << 0) -#define DWC2_GINTSTS_CURMODE_HOST (1 << 0) -#define DWC2_GINTSTS_CURMODE (1 << 0) -#define DWC2_GINTSTS_CURMODE_OFFSET 0 -#define DWC2_GINTSTS_MODEMISMATCH (1 << 1) -#define DWC2_GINTSTS_MODEMISMATCH_OFFSET 1 -#define DWC2_GINTSTS_OTGINTR (1 << 2) -#define DWC2_GINTSTS_OTGINTR_OFFSET 2 -#define DWC2_GINTSTS_SOFINTR (1 << 3) -#define DWC2_GINTSTS_SOFINTR_OFFSET 3 -#define DWC2_GINTSTS_RXSTSQLVL (1 << 4) -#define DWC2_GINTSTS_RXSTSQLVL_OFFSET 4 -#define DWC2_GINTSTS_NPTXFEMPTY (1 << 5) -#define DWC2_GINTSTS_NPTXFEMPTY_OFFSET 5 -#define DWC2_GINTSTS_GINNAKEFF (1 << 6) -#define DWC2_GINTSTS_GINNAKEFF_OFFSET 6 -#define DWC2_GINTSTS_GOUTNAKEFF (1 << 7) -#define DWC2_GINTSTS_GOUTNAKEFF_OFFSET 7 -#define DWC2_GINTSTS_I2CINTR (1 << 9) -#define DWC2_GINTSTS_I2CINTR_OFFSET 9 -#define DWC2_GINTSTS_ERLYSUSPEND (1 << 10) -#define DWC2_GINTSTS_ERLYSUSPEND_OFFSET 10 -#define DWC2_GINTSTS_USBSUSPEND (1 << 11) -#define DWC2_GINTSTS_USBSUSPEND_OFFSET 11 -#define DWC2_GINTSTS_USBRESET (1 << 12) -#define DWC2_GINTSTS_USBRESET_OFFSET 12 -#define DWC2_GINTSTS_ENUMDONE (1 << 13) -#define DWC2_GINTSTS_ENUMDONE_OFFSET 13 -#define DWC2_GINTSTS_ISOOUTDROP (1 << 14) -#define DWC2_GINTSTS_ISOOUTDROP_OFFSET 14 -#define DWC2_GINTSTS_EOPFRAME (1 << 15) -#define DWC2_GINTSTS_EOPFRAME_OFFSET 15 -#define DWC2_GINTSTS_INTOKENRX (1 << 16) -#define DWC2_GINTSTS_INTOKENRX_OFFSET 16 -#define DWC2_GINTSTS_EPMISMATCH (1 << 17) -#define DWC2_GINTSTS_EPMISMATCH_OFFSET 17 -#define DWC2_GINTSTS_INEPINT (1 << 18) -#define DWC2_GINTSTS_INEPINT_OFFSET 18 -#define DWC2_GINTSTS_OUTEPINTR (1 << 19) -#define DWC2_GINTSTS_OUTEPINTR_OFFSET 19 -#define DWC2_GINTSTS_INCOMPLISOIN (1 << 20) -#define DWC2_GINTSTS_INCOMPLISOIN_OFFSET 20 -#define DWC2_GINTSTS_INCOMPLISOOUT (1 << 21) -#define DWC2_GINTSTS_INCOMPLISOOUT_OFFSET 21 -#define DWC2_GINTSTS_PORTINTR (1 << 24) -#define DWC2_GINTSTS_PORTINTR_OFFSET 24 -#define DWC2_GINTSTS_HCINTR (1 << 25) -#define DWC2_GINTSTS_HCINTR_OFFSET 25 -#define DWC2_GINTSTS_PTXFEMPTY (1 << 26) -#define DWC2_GINTSTS_PTXFEMPTY_OFFSET 26 -#define DWC2_GINTSTS_LPMTRANRCVD (1 << 27) -#define DWC2_GINTSTS_LPMTRANRCVD_OFFSET 27 -#define DWC2_GINTSTS_CONIDSTSCHNG (1 << 28) -#define DWC2_GINTSTS_CONIDSTSCHNG_OFFSET 28 -#define DWC2_GINTSTS_DISCONNECT (1 << 29) -#define DWC2_GINTSTS_DISCONNECT_OFFSET 29 -#define DWC2_GINTSTS_SESSREQINTR (1 << 30) -#define DWC2_GINTSTS_SESSREQINTR_OFFSET 30 -#define DWC2_GINTSTS_WKUPINTR (1 << 31) -#define DWC2_GINTSTS_WKUPINTR_OFFSET 31 -#define DWC2_GRXSTS_EPNUM_MASK (0xF << 0) -#define DWC2_GRXSTS_EPNUM_OFFSET 0 -#define DWC2_GRXSTS_BCNT_MASK (0x7FF << 4) -#define DWC2_GRXSTS_BCNT_OFFSET 4 -#define DWC2_GRXSTS_DPID_MASK (0x3 << 15) -#define DWC2_GRXSTS_DPID_OFFSET 15 -#define DWC2_GRXSTS_PKTSTS_MASK (0xF << 17) -#define DWC2_GRXSTS_PKTSTS_OFFSET 17 -#define DWC2_GRXSTS_FN_MASK (0xF << 21) -#define DWC2_GRXSTS_FN_OFFSET 21 -#define DWC2_FIFOSIZE_STARTADDR_MASK (0xFFFF << 0) -#define DWC2_FIFOSIZE_STARTADDR_OFFSET 0 -#define DWC2_FIFOSIZE_DEPTH_MASK (0xFFFF << 16) -#define DWC2_FIFOSIZE_DEPTH_OFFSET 16 -#define DWC2_GNPTXSTS_NPTXFSPCAVAIL_MASK (0xFFFF << 0) -#define DWC2_GNPTXSTS_NPTXFSPCAVAIL_OFFSET 0 -#define DWC2_GNPTXSTS_NPTXQSPCAVAIL_MASK (0xFF << 16) -#define DWC2_GNPTXSTS_NPTXQSPCAVAIL_OFFSET 16 -#define DWC2_GNPTXSTS_NPTXQTOP_TERMINATE (1 << 24) -#define DWC2_GNPTXSTS_NPTXQTOP_TERMINATE_OFFSET 24 -#define DWC2_GNPTXSTS_NPTXQTOP_TOKEN_MASK (0x3 << 25) -#define DWC2_GNPTXSTS_NPTXQTOP_TOKEN_OFFSET 25 -#define DWC2_GNPTXSTS_NPTXQTOP_CHNEP_MASK (0xF << 27) -#define DWC2_GNPTXSTS_NPTXQTOP_CHNEP_OFFSET 27 -#define DWC2_DTXFSTS_TXFSPCAVAIL_MASK (0xFFFF << 0) -#define DWC2_DTXFSTS_TXFSPCAVAIL_OFFSET 0 -#define DWC2_GI2CCTL_RWDATA_MASK (0xFF << 0) -#define DWC2_GI2CCTL_RWDATA_OFFSET 0 -#define DWC2_GI2CCTL_REGADDR_MASK (0xFF << 8) -#define DWC2_GI2CCTL_REGADDR_OFFSET 8 -#define DWC2_GI2CCTL_ADDR_MASK (0x7F << 16) -#define DWC2_GI2CCTL_ADDR_OFFSET 16 -#define DWC2_GI2CCTL_I2CEN (1 << 23) -#define DWC2_GI2CCTL_I2CEN_OFFSET 23 -#define DWC2_GI2CCTL_ACK (1 << 24) -#define DWC2_GI2CCTL_ACK_OFFSET 24 -#define DWC2_GI2CCTL_I2CSUSPCTL (1 << 25) -#define DWC2_GI2CCTL_I2CSUSPCTL_OFFSET 25 -#define DWC2_GI2CCTL_I2CDEVADDR_MASK (0x3 << 26) -#define DWC2_GI2CCTL_I2CDEVADDR_OFFSET 26 -#define DWC2_GI2CCTL_RW (1 << 30) -#define DWC2_GI2CCTL_RW_OFFSET 30 -#define DWC2_GI2CCTL_BSYDNE (1 << 31) -#define DWC2_GI2CCTL_BSYDNE_OFFSET 31 -#define DWC2_HWCFG1_EP_DIR0_MASK (0x3 << 0) -#define DWC2_HWCFG1_EP_DIR0_OFFSET 0 -#define DWC2_HWCFG1_EP_DIR1_MASK (0x3 << 2) -#define DWC2_HWCFG1_EP_DIR1_OFFSET 2 -#define DWC2_HWCFG1_EP_DIR2_MASK (0x3 << 4) -#define DWC2_HWCFG1_EP_DIR2_OFFSET 4 -#define DWC2_HWCFG1_EP_DIR3_MASK (0x3 << 6) -#define DWC2_HWCFG1_EP_DIR3_OFFSET 6 -#define DWC2_HWCFG1_EP_DIR4_MASK (0x3 << 8) -#define DWC2_HWCFG1_EP_DIR4_OFFSET 8 -#define DWC2_HWCFG1_EP_DIR5_MASK (0x3 << 10) -#define DWC2_HWCFG1_EP_DIR5_OFFSET 10 -#define DWC2_HWCFG1_EP_DIR6_MASK (0x3 << 12) -#define DWC2_HWCFG1_EP_DIR6_OFFSET 12 -#define DWC2_HWCFG1_EP_DIR7_MASK (0x3 << 14) -#define DWC2_HWCFG1_EP_DIR7_OFFSET 14 -#define DWC2_HWCFG1_EP_DIR8_MASK (0x3 << 16) -#define DWC2_HWCFG1_EP_DIR8_OFFSET 16 -#define DWC2_HWCFG1_EP_DIR9_MASK (0x3 << 18) -#define DWC2_HWCFG1_EP_DIR9_OFFSET 18 -#define DWC2_HWCFG1_EP_DIR10_MASK (0x3 << 20) -#define DWC2_HWCFG1_EP_DIR10_OFFSET 20 -#define DWC2_HWCFG1_EP_DIR11_MASK (0x3 << 22) -#define DWC2_HWCFG1_EP_DIR11_OFFSET 22 -#define DWC2_HWCFG1_EP_DIR12_MASK (0x3 << 24) -#define DWC2_HWCFG1_EP_DIR12_OFFSET 24 -#define DWC2_HWCFG1_EP_DIR13_MASK (0x3 << 26) -#define DWC2_HWCFG1_EP_DIR13_OFFSET 26 -#define DWC2_HWCFG1_EP_DIR14_MASK (0x3 << 28) -#define DWC2_HWCFG1_EP_DIR14_OFFSET 28 -#define DWC2_HWCFG1_EP_DIR15_MASK (0x3 << 30) -#define DWC2_HWCFG1_EP_DIR15_OFFSET 30 -#define DWC2_HWCFG2_OP_MODE_MASK (0x7 << 0) -#define DWC2_HWCFG2_OP_MODE_OFFSET 0 -#define DWC2_HWCFG2_ARCHITECTURE_SLAVE_ONLY (0x0 << 3) -#define DWC2_HWCFG2_ARCHITECTURE_EXT_DMA (0x1 << 3) -#define DWC2_HWCFG2_ARCHITECTURE_INT_DMA (0x2 << 3) -#define DWC2_HWCFG2_ARCHITECTURE_MASK (0x3 << 3) -#define DWC2_HWCFG2_ARCHITECTURE_OFFSET 3 -#define DWC2_HWCFG2_POINT2POINT (1 << 5) -#define DWC2_HWCFG2_POINT2POINT_OFFSET 5 -#define DWC2_HWCFG2_HS_PHY_TYPE_MASK (0x3 << 6) -#define DWC2_HWCFG2_HS_PHY_TYPE_OFFSET 6 -#define DWC2_HWCFG2_FS_PHY_TYPE_MASK (0x3 << 8) -#define DWC2_HWCFG2_FS_PHY_TYPE_OFFSET 8 -#define DWC2_HWCFG2_NUM_DEV_EP_MASK (0xF << 10) -#define DWC2_HWCFG2_NUM_DEV_EP_OFFSET 10 -#define DWC2_HWCFG2_NUM_HOST_CHAN_MASK (0xF << 14) -#define DWC2_HWCFG2_NUM_HOST_CHAN_OFFSET 14 -#define DWC2_HWCFG2_PERIO_EP_SUPPORTED (1 << 18) -#define DWC2_HWCFG2_PERIO_EP_SUPPORTED_OFFSET 18 -#define DWC2_HWCFG2_DYNAMIC_FIFO (1 << 19) -#define DWC2_HWCFG2_DYNAMIC_FIFO_OFFSET 19 -#define DWC2_HWCFG2_MULTI_PROC_INT (1 << 20) -#define DWC2_HWCFG2_MULTI_PROC_INT_OFFSET 20 -#define DWC2_HWCFG2_NONPERIO_TX_Q_DEPTH_MASK (0x3 << 22) -#define DWC2_HWCFG2_NONPERIO_TX_Q_DEPTH_OFFSET 22 -#define DWC2_HWCFG2_HOST_PERIO_TX_Q_DEPTH_MASK (0x3 << 24) -#define DWC2_HWCFG2_HOST_PERIO_TX_Q_DEPTH_OFFSET 24 -#define DWC2_HWCFG2_DEV_TOKEN_Q_DEPTH_MASK (0x1F << 26) -#define DWC2_HWCFG2_DEV_TOKEN_Q_DEPTH_OFFSET 26 -#define DWC2_HWCFG3_XFER_SIZE_CNTR_WIDTH_MASK (0xF << 0) -#define DWC2_HWCFG3_XFER_SIZE_CNTR_WIDTH_OFFSET 0 -#define DWC2_HWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK (0x7 << 4) -#define DWC2_HWCFG3_PACKET_SIZE_CNTR_WIDTH_OFFSET 4 -#define DWC2_HWCFG3_OTG_FUNC (1 << 7) -#define DWC2_HWCFG3_OTG_FUNC_OFFSET 7 -#define DWC2_HWCFG3_I2C (1 << 8) -#define DWC2_HWCFG3_I2C_OFFSET 8 -#define DWC2_HWCFG3_VENDOR_CTRL_IF (1 << 9) -#define DWC2_HWCFG3_VENDOR_CTRL_IF_OFFSET 9 -#define DWC2_HWCFG3_OPTIONAL_FEATURES (1 << 10) -#define DWC2_HWCFG3_OPTIONAL_FEATURES_OFFSET 10 -#define DWC2_HWCFG3_SYNCH_RESET_TYPE (1 << 11) -#define DWC2_HWCFG3_SYNCH_RESET_TYPE_OFFSET 11 -#define DWC2_HWCFG3_OTG_ENABLE_IC_USB (1 << 12) -#define DWC2_HWCFG3_OTG_ENABLE_IC_USB_OFFSET 12 -#define DWC2_HWCFG3_OTG_ENABLE_HSIC (1 << 13) -#define DWC2_HWCFG3_OTG_ENABLE_HSIC_OFFSET 13 -#define DWC2_HWCFG3_OTG_LPM_EN (1 << 15) -#define DWC2_HWCFG3_OTG_LPM_EN_OFFSET 15 -#define DWC2_HWCFG3_DFIFO_DEPTH_MASK (0xFFFF << 16) -#define DWC2_HWCFG3_DFIFO_DEPTH_OFFSET 16 -#define DWC2_HWCFG4_NUM_DEV_PERIO_IN_EP_MASK (0xF << 0) -#define DWC2_HWCFG4_NUM_DEV_PERIO_IN_EP_OFFSET 0 -#define DWC2_HWCFG4_POWER_OPTIMIZ (1 << 4) -#define DWC2_HWCFG4_POWER_OPTIMIZ_OFFSET 4 -#define DWC2_HWCFG4_MIN_AHB_FREQ_MASK (0x1FF << 5) -#define DWC2_HWCFG4_MIN_AHB_FREQ_OFFSET 5 -#define DWC2_HWCFG4_UTMI_PHY_DATA_WIDTH_MASK (0x3 << 14) -#define DWC2_HWCFG4_UTMI_PHY_DATA_WIDTH_OFFSET 14 -#define DWC2_HWCFG4_NUM_DEV_MODE_CTRL_EP_MASK (0xF << 16) -#define DWC2_HWCFG4_NUM_DEV_MODE_CTRL_EP_OFFSET 16 -#define DWC2_HWCFG4_IDDIG_FILT_EN (1 << 20) -#define DWC2_HWCFG4_IDDIG_FILT_EN_OFFSET 20 -#define DWC2_HWCFG4_VBUS_VALID_FILT_EN (1 << 21) -#define DWC2_HWCFG4_VBUS_VALID_FILT_EN_OFFSET 21 -#define DWC2_HWCFG4_A_VALID_FILT_EN (1 << 22) -#define DWC2_HWCFG4_A_VALID_FILT_EN_OFFSET 22 -#define DWC2_HWCFG4_B_VALID_FILT_EN (1 << 23) -#define DWC2_HWCFG4_B_VALID_FILT_EN_OFFSET 23 -#define DWC2_HWCFG4_SESSION_END_FILT_EN (1 << 24) -#define DWC2_HWCFG4_SESSION_END_FILT_EN_OFFSET 24 -#define DWC2_HWCFG4_DED_FIFO_EN (1 << 25) -#define DWC2_HWCFG4_DED_FIFO_EN_OFFSET 25 -#define DWC2_HWCFG4_NUM_IN_EPS_MASK (0xF << 26) -#define DWC2_HWCFG4_NUM_IN_EPS_OFFSET 26 -#define DWC2_HWCFG4_DESC_DMA (1 << 30) -#define DWC2_HWCFG4_DESC_DMA_OFFSET 30 -#define DWC2_HWCFG4_DESC_DMA_DYN (1 << 31) -#define DWC2_HWCFG4_DESC_DMA_DYN_OFFSET 31 -#define DWC2_HCFG_FSLSPCLKSEL_30_60_MHZ 0 -#define DWC2_HCFG_FSLSPCLKSEL_48_MHZ 1 -#define DWC2_HCFG_FSLSPCLKSEL_6_MHZ 2 -#define DWC2_HCFG_FSLSPCLKSEL_MASK (0x3 << 0) -#define DWC2_HCFG_FSLSPCLKSEL_OFFSET 0 -#define DWC2_HCFG_FSLSSUPP (1 << 2) -#define DWC2_HCFG_FSLSSUPP_OFFSET 2 -#define DWC2_HCFG_DESCDMA (1 << 23) -#define DWC2_HCFG_DESCDMA_OFFSET 23 -#define DWC2_HCFG_FRLISTEN_MASK (0x3 << 24) -#define DWC2_HCFG_FRLISTEN_OFFSET 24 -#define DWC2_HCFG_PERSCHEDENA (1 << 26) -#define DWC2_HCFG_PERSCHEDENA_OFFSET 26 -#define DWC2_HCFG_PERSCHEDSTAT (1 << 27) -#define DWC2_HCFG_PERSCHEDSTAT_OFFSET 27 -#define DWC2_HFIR_FRINT_MASK (0xFFFF << 0) -#define DWC2_HFIR_FRINT_OFFSET 0 -#define DWC2_HFNUM_FRNUM_MASK (0xFFFF << 0) -#define DWC2_HFNUM_FRNUM_OFFSET 0 -#define DWC2_HFNUM_FRREM_MASK (0xFFFF << 16) -#define DWC2_HFNUM_FRREM_OFFSET 16 -#define DWC2_HFNUM_MAX_FRNUM 0x3FFF -#define DWC2_HPTXSTS_PTXFSPCAVAIL_MASK (0xFFFF << 0) -#define DWC2_HPTXSTS_PTXFSPCAVAIL_OFFSET 0 -#define DWC2_HPTXSTS_PTXQSPCAVAIL_MASK (0xFF << 16) -#define DWC2_HPTXSTS_PTXQSPCAVAIL_OFFSET 16 -#define DWC2_HPTXSTS_PTXQTOP_TERMINATE (1 << 24) -#define DWC2_HPTXSTS_PTXQTOP_TERMINATE_OFFSET 24 -#define DWC2_HPTXSTS_PTXQTOP_TOKEN_MASK (0x3 << 25) -#define DWC2_HPTXSTS_PTXQTOP_TOKEN_OFFSET 25 -#define DWC2_HPTXSTS_PTXQTOP_CHNUM_MASK (0xF << 27) -#define DWC2_HPTXSTS_PTXQTOP_CHNUM_OFFSET 27 -#define DWC2_HPTXSTS_PTXQTOP_ODD (1 << 31) -#define DWC2_HPTXSTS_PTXQTOP_ODD_OFFSET 31 -#define DWC2_HPRT0_PRTCONNSTS (1 << 0) -#define DWC2_HPRT0_PRTCONNSTS_OFFSET 0 -#define DWC2_HPRT0_PRTCONNDET (1 << 1) -#define DWC2_HPRT0_PRTCONNDET_OFFSET 1 -#define DWC2_HPRT0_PRTENA (1 << 2) -#define DWC2_HPRT0_PRTENA_OFFSET 2 -#define DWC2_HPRT0_PRTENCHNG (1 << 3) -#define DWC2_HPRT0_PRTENCHNG_OFFSET 3 -#define DWC2_HPRT0_PRTOVRCURRACT (1 << 4) -#define DWC2_HPRT0_PRTOVRCURRACT_OFFSET 4 -#define DWC2_HPRT0_PRTOVRCURRCHNG (1 << 5) -#define DWC2_HPRT0_PRTOVRCURRCHNG_OFFSET 5 -#define DWC2_HPRT0_PRTRES (1 << 6) -#define DWC2_HPRT0_PRTRES_OFFSET 6 -#define DWC2_HPRT0_PRTSUSP (1 << 7) -#define DWC2_HPRT0_PRTSUSP_OFFSET 7 -#define DWC2_HPRT0_PRTRST (1 << 8) -#define DWC2_HPRT0_PRTRST_OFFSET 8 -#define DWC2_HPRT0_PRTLNSTS_MASK (0x3 << 10) -#define DWC2_HPRT0_PRTLNSTS_OFFSET 10 -#define DWC2_HPRT0_PRTPWR (1 << 12) -#define DWC2_HPRT0_PRTPWR_OFFSET 12 -#define DWC2_HPRT0_PRTTSTCTL_MASK (0xF << 13) -#define DWC2_HPRT0_PRTTSTCTL_OFFSET 13 -#define DWC2_HPRT0_PRTSPD_HIGH (0 << 17) -#define DWC2_HPRT0_PRTSPD_FULL (1 << 17) -#define DWC2_HPRT0_PRTSPD_LOW (2 << 17) -#define DWC2_HPRT0_PRTSPD_MASK (0x3 << 17) -#define DWC2_HPRT0_PRTSPD_OFFSET 17 -#define DWC2_HAINT_CH0 (1 << 0) -#define DWC2_HAINT_CH0_OFFSET 0 -#define DWC2_HAINT_CH1 (1 << 1) -#define DWC2_HAINT_CH1_OFFSET 1 -#define DWC2_HAINT_CH2 (1 << 2) -#define DWC2_HAINT_CH2_OFFSET 2 -#define DWC2_HAINT_CH3 (1 << 3) -#define DWC2_HAINT_CH3_OFFSET 3 -#define DWC2_HAINT_CH4 (1 << 4) -#define DWC2_HAINT_CH4_OFFSET 4 -#define DWC2_HAINT_CH5 (1 << 5) -#define DWC2_HAINT_CH5_OFFSET 5 -#define DWC2_HAINT_CH6 (1 << 6) -#define DWC2_HAINT_CH6_OFFSET 6 -#define DWC2_HAINT_CH7 (1 << 7) -#define DWC2_HAINT_CH7_OFFSET 7 -#define DWC2_HAINT_CH8 (1 << 8) -#define DWC2_HAINT_CH8_OFFSET 8 -#define DWC2_HAINT_CH9 (1 << 9) -#define DWC2_HAINT_CH9_OFFSET 9 -#define DWC2_HAINT_CH10 (1 << 10) -#define DWC2_HAINT_CH10_OFFSET 10 -#define DWC2_HAINT_CH11 (1 << 11) -#define DWC2_HAINT_CH11_OFFSET 11 -#define DWC2_HAINT_CH12 (1 << 12) -#define DWC2_HAINT_CH12_OFFSET 12 -#define DWC2_HAINT_CH13 (1 << 13) -#define DWC2_HAINT_CH13_OFFSET 13 -#define DWC2_HAINT_CH14 (1 << 14) -#define DWC2_HAINT_CH14_OFFSET 14 -#define DWC2_HAINT_CH15 (1 << 15) -#define DWC2_HAINT_CH15_OFFSET 15 -#define DWC2_HAINT_CHINT_MASK 0xffff -#define DWC2_HAINT_CHINT_OFFSET 0 -#define DWC2_HAINTMSK_CH0 (1 << 0) -#define DWC2_HAINTMSK_CH0_OFFSET 0 -#define DWC2_HAINTMSK_CH1 (1 << 1) -#define DWC2_HAINTMSK_CH1_OFFSET 1 -#define DWC2_HAINTMSK_CH2 (1 << 2) -#define DWC2_HAINTMSK_CH2_OFFSET 2 -#define DWC2_HAINTMSK_CH3 (1 << 3) -#define DWC2_HAINTMSK_CH3_OFFSET 3 -#define DWC2_HAINTMSK_CH4 (1 << 4) -#define DWC2_HAINTMSK_CH4_OFFSET 4 -#define DWC2_HAINTMSK_CH5 (1 << 5) -#define DWC2_HAINTMSK_CH5_OFFSET 5 -#define DWC2_HAINTMSK_CH6 (1 << 6) -#define DWC2_HAINTMSK_CH6_OFFSET 6 -#define DWC2_HAINTMSK_CH7 (1 << 7) -#define DWC2_HAINTMSK_CH7_OFFSET 7 -#define DWC2_HAINTMSK_CH8 (1 << 8) -#define DWC2_HAINTMSK_CH8_OFFSET 8 -#define DWC2_HAINTMSK_CH9 (1 << 9) -#define DWC2_HAINTMSK_CH9_OFFSET 9 -#define DWC2_HAINTMSK_CH10 (1 << 10) -#define DWC2_HAINTMSK_CH10_OFFSET 10 -#define DWC2_HAINTMSK_CH11 (1 << 11) -#define DWC2_HAINTMSK_CH11_OFFSET 11 -#define DWC2_HAINTMSK_CH12 (1 << 12) -#define DWC2_HAINTMSK_CH12_OFFSET 12 -#define DWC2_HAINTMSK_CH13 (1 << 13) -#define DWC2_HAINTMSK_CH13_OFFSET 13 -#define DWC2_HAINTMSK_CH14 (1 << 14) -#define DWC2_HAINTMSK_CH14_OFFSET 14 -#define DWC2_HAINTMSK_CH15 (1 << 15) -#define DWC2_HAINTMSK_CH15_OFFSET 15 -#define DWC2_HAINTMSK_CHINT_MASK 0xffff -#define DWC2_HAINTMSK_CHINT_OFFSET 0 -#define DWC2_HCCHAR_MPS_MASK (0x7FF << 0) -#define DWC2_HCCHAR_MPS_OFFSET 0 -#define DWC2_HCCHAR_EPNUM_MASK (0xF << 11) -#define DWC2_HCCHAR_EPNUM_OFFSET 11 -#define DWC2_HCCHAR_EPDIR (1 << 15) -#define DWC2_HCCHAR_EPDIR_OFFSET 15 -#define DWC2_HCCHAR_LSPDDEV (1 << 17) -#define DWC2_HCCHAR_LSPDDEV_OFFSET 17 -#define DWC2_HCCHAR_EPTYPE_CONTROL 0 -#define DWC2_HCCHAR_EPTYPE_ISOC 1 -#define DWC2_HCCHAR_EPTYPE_BULK 2 -#define DWC2_HCCHAR_EPTYPE_INTR 3 -#define DWC2_HCCHAR_EPTYPE_MASK (0x3 << 18) -#define DWC2_HCCHAR_EPTYPE_OFFSET 18 -#define DWC2_HCCHAR_MULTICNT_MASK (0x3 << 20) -#define DWC2_HCCHAR_MULTICNT_OFFSET 20 -#define DWC2_HCCHAR_DEVADDR_MASK (0x7F << 22) -#define DWC2_HCCHAR_DEVADDR_OFFSET 22 -#define DWC2_HCCHAR_ODDFRM (1 << 29) -#define DWC2_HCCHAR_ODDFRM_OFFSET 29 -#define DWC2_HCCHAR_CHDIS (1 << 30) -#define DWC2_HCCHAR_CHDIS_OFFSET 30 -#define DWC2_HCCHAR_CHEN (1 << 31) -#define DWC2_HCCHAR_CHEN_OFFSET 31 -#define DWC2_HCSPLT_PRTADDR_MASK (0x7F << 0) -#define DWC2_HCSPLT_PRTADDR_OFFSET 0 -#define DWC2_HCSPLT_HUBADDR_MASK (0x7F << 7) -#define DWC2_HCSPLT_HUBADDR_OFFSET 7 -#define DWC2_HCSPLT_XACTPOS_MASK (0x3 << 14) -#define DWC2_HCSPLT_XACTPOS_OFFSET 14 -#define DWC2_HCSPLT_COMPSPLT (1 << 16) -#define DWC2_HCSPLT_COMPSPLT_OFFSET 16 -#define DWC2_HCSPLT_SPLTENA (1 << 31) -#define DWC2_HCSPLT_SPLTENA_OFFSET 31 -#define DWC2_HCINT_XFERCOMP (1 << 0) -#define DWC2_HCINT_XFERCOMP_OFFSET 0 -#define DWC2_HCINT_CHHLTD (1 << 1) -#define DWC2_HCINT_CHHLTD_OFFSET 1 -#define DWC2_HCINT_AHBERR (1 << 2) -#define DWC2_HCINT_AHBERR_OFFSET 2 -#define DWC2_HCINT_STALL (1 << 3) -#define DWC2_HCINT_STALL_OFFSET 3 -#define DWC2_HCINT_NAK (1 << 4) -#define DWC2_HCINT_NAK_OFFSET 4 -#define DWC2_HCINT_ACK (1 << 5) -#define DWC2_HCINT_ACK_OFFSET 5 -#define DWC2_HCINT_NYET (1 << 6) -#define DWC2_HCINT_NYET_OFFSET 6 -#define DWC2_HCINT_XACTERR (1 << 7) -#define DWC2_HCINT_XACTERR_OFFSET 7 -#define DWC2_HCINT_BBLERR (1 << 8) -#define DWC2_HCINT_BBLERR_OFFSET 8 -#define DWC2_HCINT_FRMOVRUN (1 << 9) -#define DWC2_HCINT_FRMOVRUN_OFFSET 9 -#define DWC2_HCINT_DATATGLERR (1 << 10) -#define DWC2_HCINT_DATATGLERR_OFFSET 10 -#define DWC2_HCINT_BNA (1 << 11) -#define DWC2_HCINT_BNA_OFFSET 11 -#define DWC2_HCINT_XCS_XACT (1 << 12) -#define DWC2_HCINT_XCS_XACT_OFFSET 12 -#define DWC2_HCINT_FRM_LIST_ROLL (1 << 13) -#define DWC2_HCINT_FRM_LIST_ROLL_OFFSET 13 -#define DWC2_HCINTMSK_XFERCOMPL (1 << 0) -#define DWC2_HCINTMSK_XFERCOMPL_OFFSET 0 -#define DWC2_HCINTMSK_CHHLTD (1 << 1) -#define DWC2_HCINTMSK_CHHLTD_OFFSET 1 -#define DWC2_HCINTMSK_AHBERR (1 << 2) -#define DWC2_HCINTMSK_AHBERR_OFFSET 2 -#define DWC2_HCINTMSK_STALL (1 << 3) -#define DWC2_HCINTMSK_STALL_OFFSET 3 -#define DWC2_HCINTMSK_NAK (1 << 4) -#define DWC2_HCINTMSK_NAK_OFFSET 4 -#define DWC2_HCINTMSK_ACK (1 << 5) -#define DWC2_HCINTMSK_ACK_OFFSET 5 -#define DWC2_HCINTMSK_NYET (1 << 6) -#define DWC2_HCINTMSK_NYET_OFFSET 6 -#define DWC2_HCINTMSK_XACTERR (1 << 7) -#define DWC2_HCINTMSK_XACTERR_OFFSET 7 -#define DWC2_HCINTMSK_BBLERR (1 << 8) -#define DWC2_HCINTMSK_BBLERR_OFFSET 8 -#define DWC2_HCINTMSK_FRMOVRUN (1 << 9) -#define DWC2_HCINTMSK_FRMOVRUN_OFFSET 9 -#define DWC2_HCINTMSK_DATATGLERR (1 << 10) -#define DWC2_HCINTMSK_DATATGLERR_OFFSET 10 -#define DWC2_HCINTMSK_BNA (1 << 11) -#define DWC2_HCINTMSK_BNA_OFFSET 11 -#define DWC2_HCINTMSK_XCS_XACT (1 << 12) -#define DWC2_HCINTMSK_XCS_XACT_OFFSET 12 -#define DWC2_HCINTMSK_FRM_LIST_ROLL (1 << 13) -#define DWC2_HCINTMSK_FRM_LIST_ROLL_OFFSET 13 -#define DWC2_HCTSIZ_XFERSIZE_MASK 0x7ffff -#define DWC2_HCTSIZ_XFERSIZE_OFFSET 0 -#define DWC2_HCTSIZ_SCHINFO_MASK 0xff -#define DWC2_HCTSIZ_SCHINFO_OFFSET 0 -#define DWC2_HCTSIZ_NTD_MASK (0xff << 8) -#define DWC2_HCTSIZ_NTD_OFFSET 8 -#define DWC2_HCTSIZ_PKTCNT_MASK (0x3ff << 19) -#define DWC2_HCTSIZ_PKTCNT_OFFSET 19 -#define DWC2_HCTSIZ_PID_MASK (0x3 << 29) -#define DWC2_HCTSIZ_PID_OFFSET 29 -#define DWC2_HCTSIZ_DOPNG (1 << 31) -#define DWC2_HCTSIZ_DOPNG_OFFSET 31 -#define DWC2_HCDMA_CTD_MASK (0xFF << 3) -#define DWC2_HCDMA_CTD_OFFSET 3 -#define DWC2_HCDMA_DMA_ADDR_MASK (0x1FFFFF << 11) -#define DWC2_HCDMA_DMA_ADDR_OFFSET 11 -#define DWC2_PCGCCTL_STOPPCLK (1 << 0) -#define DWC2_PCGCCTL_STOPPCLK_OFFSET 0 -#define DWC2_PCGCCTL_GATEHCLK (1 << 1) -#define DWC2_PCGCCTL_GATEHCLK_OFFSET 1 -#define DWC2_PCGCCTL_PWRCLMP (1 << 2) -#define DWC2_PCGCCTL_PWRCLMP_OFFSET 2 -#define DWC2_PCGCCTL_RSTPDWNMODULE (1 << 3) -#define DWC2_PCGCCTL_RSTPDWNMODULE_OFFSET 3 -#define DWC2_PCGCCTL_PHYSUSPENDED (1 << 4) -#define DWC2_PCGCCTL_PHYSUSPENDED_OFFSET 4 -#define DWC2_PCGCCTL_ENBL_SLEEP_GATING (1 << 5) -#define DWC2_PCGCCTL_ENBL_SLEEP_GATING_OFFSET 5 -#define DWC2_PCGCCTL_PHY_IN_SLEEP (1 << 6) -#define DWC2_PCGCCTL_PHY_IN_SLEEP_OFFSET 6 -#define DWC2_PCGCCTL_DEEP_SLEEP (1 << 7) -#define DWC2_PCGCCTL_DEEP_SLEEP_OFFSET 7 -#define DWC2_SNPSID_DEVID_VER_2xx (0x4f542 << 12) -#define DWC2_SNPSID_DEVID_VER_3xx (0x4f543 << 12) -#define DWC2_SNPSID_DEVID_MASK (0xfffff << 12) -#define DWC2_SNPSID_DEVID_OFFSET 12 - -/* Host controller specific */ -#define DWC2_HC_PID_DATA0 0 -#define DWC2_HC_PID_DATA2 1 -#define DWC2_HC_PID_DATA1 2 -#define DWC2_HC_PID_MDATA 3 -#define DWC2_HC_PID_SETUP 3 - -/* roothub.a masks */ -#define RH_A_NDP (0xff << 0) /* number of downstream ports */ -#define RH_A_PSM (1 << 8) /* power switching mode */ -#define RH_A_NPS (1 << 9) /* no power switching */ -#define RH_A_DT (1 << 10) /* device type (mbz) */ -#define RH_A_OCPM (1 << 11) /* over current protection mode */ -#define RH_A_NOCP (1 << 12) /* no over current protection */ -#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ - -/* roothub.b masks */ -#define RH_B_DR 0x0000ffff /* device removable flags */ -#define RH_B_PPCM 0xffff0000 /* port power control mask */ - -/* Default driver configuration */ -#define CONFIG_DWC2_MAX_CHANNELS 16 /* Max # of EPs */ -#define CONFIG_DWC2_HOST_RX_FIFO_SIZE (516 + CONFIG_DWC2_MAX_CHANNELS) -#define CONFIG_DWC2_HOST_NPERIO_TX_FIFO_SIZE 0x100 /* nPeriodic TX FIFO */ -#define CONFIG_DWC2_HOST_PERIO_TX_FIFO_SIZE 0x200 /* Periodic TX FIFO */ -#define CONFIG_DWC2_MAX_TRANSFER_SIZE 65535 -#define CONFIG_DWC2_MAX_PACKET_COUNT 511 - -#define DWC2_PHY_TYPE_FS 0 -#define DWC2_PHY_TYPE_UTMI 1 -#define DWC2_PHY_TYPE_ULPI 2 -#define CONFIG_DWC2_PHY_TYPE DWC2_PHY_TYPE_UTMI /* PHY type */ -#define CONFIG_DWC2_UTMI_WIDTH 8 /* UTMI bus width (8/16) */ - -#define CONFIG_DWC2_PHY_ULPI_EXT_VBUS /* ULPI PHY controls VBUS */ -#define CONFIG_DWC2_TX_THR_LENGTH 64 - -#endif /* __DWC2_H__ */ diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index 192c5ef9d1..f176babfa7 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -1,16 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * (C) Copyright 2010 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; version 2 of - * the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <common.h> @@ -18,9 +8,9 @@ #include <linux/err.h> #include <driver.h> #include <init.h> -#include <usb/usb.h> -#include <usb/usb_defs.h> -#include <usb/ehci.h> +#include <linux/usb/usb.h> +#include <linux/usb/usb_defs.h> +#include <linux/usb/ehci.h> #include <errno.h> #include <io.h> @@ -28,7 +18,7 @@ struct atmel_ehci_priv { struct ehci_host *ehci; - struct device_d *dev; + struct device *dev; struct clk *iclk; struct clk *uclk; }; @@ -57,14 +47,7 @@ static void atmel_stop_clock(struct atmel_ehci_priv *atehci) clk_disable(atehci->uclk); } -static int atmel_ehci_detect(struct device_d *dev) -{ - struct atmel_ehci_priv *atehci = dev->priv; - - return ehci_detect(atehci->ehci); -} - -static int atmel_ehci_probe(struct device_d *dev) +static int atmel_ehci_probe(struct device *dev) { int ret; struct resource *iores; @@ -73,12 +56,11 @@ static int atmel_ehci_probe(struct device_d *dev) const char *uclk_name; struct ehci_host *ehci; - uclk_name = (dev->device_node) ? "usb_clk" : "uhpck"; + uclk_name = (dev->of_node) ? "usb_clk" : "uhpck"; atehci = xzalloc(sizeof(*atehci)); atehci->dev = dev; dev->priv = atehci; - dev->detect = atmel_ehci_detect; atehci->iclk = clk_get(dev, "ehci_clk"); if (IS_ERR(atehci->iclk)) { @@ -115,7 +97,7 @@ static int atmel_ehci_probe(struct device_d *dev) return 0; } -static void atmel_ehci_remove(struct device_d *dev) +static void atmel_ehci_remove(struct device *dev) { struct atmel_ehci_priv *atehci = dev->priv; @@ -131,8 +113,9 @@ static const struct of_device_id atmel_ehci_dt_ids[] = { { .compatible = "atmel,at91sam9g45-ehci" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, atmel_ehci_dt_ids); -static struct driver_d atmel_ehci_driver = { +static struct driver atmel_ehci_driver = { .name = "atmel-ehci", .probe = atmel_ehci_probe, .remove = atmel_ehci_remove, diff --git a/drivers/usb/host/ehci-core.h b/drivers/usb/host/ehci-core.h index 584dd541ad..8f43783f7e 100644 --- a/drivers/usb/host/ehci-core.h +++ b/drivers/usb/host/ehci-core.h @@ -1,18 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /*- * Copyright (c) 2007-2008, Juniper Networks, Inc. * Copyright (c) 2008, Excito Elektronik i Skåne AB * All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2 of - * the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #ifndef USB_EHCI_CORE_H diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index ead63b2c9f..7ae3a285a0 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1,26 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-only /*- * Copyright (c) 2007-2008, Juniper Networks, Inc. * Copyright (c) 2008, Excito Elektronik i Skåne AB * Copyright (c) 2008, Michael Trimarchi <trimarchimichael@yahoo.it> * * All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2 of - * the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ /*#define DEBUG */ #include <common.h> #include <dma.h> #include <asm/byteorder.h> -#include <usb/usb.h> +#include <linux/usb/usb.h> #include <io.h> #include <malloc.h> #include <driver.h> @@ -29,15 +19,17 @@ #include <clock.h> #include <errno.h> #include <of.h> -#include <usb/ehci.h> +#include <linux/usb/ehci.h> #include <linux/err.h> #include <linux/sizes.h> +#include <linux/clk.h> +#include <linux/phy/phy.h> #include "ehci.h" struct ehci_host { int rootdev; - struct device_d *dev; + struct device *dev; struct ehci_hccr *hccr; struct ehci_hcor *hcor; struct usb_host host; @@ -240,7 +232,7 @@ static int ehci_td_buffer(struct qTD *td, dma_addr_t addr, size_t sz) } if (idx == buffer_count) { - pr_debug("out of buffer pointers (%u bytes left)\n", sz); + pr_debug("out of buffer pointers (%zu bytes left)\n", sz); return -ENOMEM; } @@ -250,7 +242,7 @@ static int ehci_td_buffer(struct qTD *td, dma_addr_t addr, size_t sz) return 0; } -static int ehci_prepare_qtd(struct device_d *dev, +static int ehci_prepare_qtd(struct device *dev, struct qTD *td, uint32_t token, void *buffer, size_t length, dma_addr_t *buffer_dma, @@ -305,15 +297,17 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, struct usb_host *host = dev->host; struct ehci_host *ehci = to_ehci(host); const bool dir_in = usb_pipein(pipe); - dma_addr_t buffer_dma, req_dma; + dma_addr_t buffer_dma = DMA_ERROR_CODE, req_dma; struct QH *qh = &ehci->qh_list[1]; struct qTD *td; + volatile struct qTD *vtd; uint32_t *tdp; uint32_t endpt, token, usbsts; uint32_t status; uint32_t toggle; bool c; int ret; + uint64_t start, timeout_val; dev_dbg(ehci->dev, "pipe=%lx, buffer=%p, length=%d, req=%p\n", pipe, @@ -442,13 +436,18 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, return ret; } - ret = handshake(&ehci->hcor->or_usbsts, STS_USBINT, STS_USBINT, - timeout_ms * 1000); - if (ret < 0) { - ehci_enable_async_schedule(ehci, false); - ehci_writel(&qh->qt_token, 0); - return -ETIMEDOUT; - } + /* Wait for TDs to be processed. */ + timeout_val = timeout_ms * MSECOND; + start = get_time_ns(); + vtd = td; + do { + token = hc32_to_cpu(vtd->qt_token); + if (is_timeout_non_interruptible(start, timeout_val)) { + ehci_enable_async_schedule(ehci, false); + ehci_writel(&qh->qt_token, 0); + return -ETIMEDOUT; + } + } while (token & QT_TOKEN_STATUS_ACTIVE); if (req) dma_unmap_single(ehci->dev, req_dma, sizeof(*req), @@ -511,7 +510,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, } #if defined(CONFIG_MACH_EFIKA_MX_SMARTBOOK) && defined(CONFIG_USB_ULPI) -#include <usb/ulpi.h> +#include <linux/usb/ulpi.h> /* * Add support for setting CHRGVBUS to workaround a hardware bug on efika mx/sb * boards. @@ -1342,12 +1341,7 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, return result; } -int ehci_detect(struct ehci_host *ehci) -{ - return usb_host_detect(&ehci->host); -} - -struct ehci_host *ehci_register(struct device_d *dev, struct ehci_data *data) +struct ehci_host *ehci_register(struct device *dev, struct ehci_data *data) { struct usb_host *host; struct ehci_host *ehci; @@ -1404,20 +1398,16 @@ void ehci_unregister(struct ehci_host *ehci) free(ehci); } -static int ehci_dev_detect(struct device_d *dev) -{ - struct ehci_host *ehci = dev->priv; - - return ehci_detect(ehci); -} - -static int ehci_probe(struct device_d *dev) +static int ehci_probe(struct device *dev) { struct resource *iores; struct ehci_data data = {}; struct ehci_platform_data *pdata = dev->platform_data; - struct device_node *dn = dev->device_node; + struct device_node *dn = dev->of_node; struct ehci_host *ehci; + struct clk_bulk_data *clks; + int num_clocks, ret; + struct phy *usb2_generic_phy; if (pdata) data.flags = pdata->flags; @@ -1431,6 +1421,27 @@ static int ehci_probe(struct device_d *dev) */ data.flags = EHCI_HAS_TT; + usb2_generic_phy = phy_optional_get(dev, "usb"); + if (IS_ERR(usb2_generic_phy)) + return PTR_ERR(usb2_generic_phy); + + ret = phy_init(usb2_generic_phy); + if (ret) + return ret; + + ret = phy_power_on(usb2_generic_phy); + if (ret) + return ret; + + ret = clk_bulk_get_all(dev, &clks); + if (ret < 0) + return ret; + + num_clocks = ret; + ret = clk_bulk_enable(num_clocks, clks); + if (ret) + return ret; + iores = dev_request_mem_resource(dev, 0); if (IS_ERR(iores)) return PTR_ERR(iores); @@ -1450,12 +1461,11 @@ static int ehci_probe(struct device_d *dev) return PTR_ERR(ehci); dev->priv = ehci; - dev->detect = ehci_dev_detect; return 0; } -static void ehci_remove(struct device_d *dev) +static void ehci_remove(struct device *dev) { struct ehci_host *ehci = dev->priv; @@ -1469,8 +1479,9 @@ static __maybe_unused struct of_device_id ehci_platform_dt_ids[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, ehci_platform_dt_ids); -static struct driver_d ehci_driver = { +static struct driver ehci_driver = { .name = "ehci", .probe = ehci_probe, .remove = ehci_remove, diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index fc5a54f671..e364796a1a 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -1,8 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2010 Michael Grzeschik <mgr@pengutronix.de> - * - * This file is released under the GPLv2 - * */ /* @@ -12,16 +10,16 @@ /*-------------------------------------------------------------------------*/ #include <mfd/twl4030.h> -#include <usb/twl4030.h> -#include <mach/ehci.h> +#include <linux/usb/twl4030.h> +#include <mach/omap/ehci.h> #include <common.h> #include <io.h> #include <clock.h> #include <gpio.h> -#include <mach/omap3-silicon.h> -#include <mach/omap3-clock.h> -#include <mach/cm-regbits-34xx.h> -#include <mach/sys_info.h> +#include <mach/omap/omap3-silicon.h> +#include <mach/omap/omap3-clock.h> +#include <mach/omap/cm-regbits-34xx.h> +#include <mach/omap/sys_info.h> void omap_usb_utmi_init(struct omap_hcd *omap, u8 tll_channel_mask) { diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index e88e37e14c..49196bfc7d 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -1,18 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /*- * Copyright (c) 2007-2008, Juniper Networks, Inc. * Copyright (c) 2008, Michael Trimarchi <trimarchimichael@yahoo.it> * All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2 of - * the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #ifndef USB_EHCI_H @@ -53,7 +43,6 @@ struct ehci_hcor { #define STD_ASS (1 << 15) #define STS_PSS (1 << 14) #define STS_HALT (1 << 12) -#define STS_USBINT BIT(0) uint32_t or_usbintr; uint32_t or_frindex; uint32_t or_ctrldssegment; diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 05ed2876c6..867c0977be 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -1,30 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * (C) Copyright 2010 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; version 2 of - * the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <common.h> #include <linux/clk.h> #include <driver.h> #include <init.h> -#include <usb/usb.h> -#include <usb/usb_defs.h> +#include <linux/usb/usb.h> +#include <linux/usb/usb_defs.h> #include <errno.h> #include <gpio.h> #include <of_gpio.h> #include <io.h> -#include <mach/board.h> +#include <mach/at91/board.h> #include "ohci.h" @@ -33,7 +23,7 @@ struct ohci_at91_priv { - struct device_d *dev; + struct device *dev; struct clk *iclk; struct clk *fclk; struct ohci_regs __iomem *regs; @@ -64,13 +54,13 @@ static void at91_stop_clock(struct ohci_at91_priv *ohci_at91) clk_disable(ohci_at91->iclk); } -static int at91_ohci_probe_dt(struct device_d *dev) +static int at91_ohci_probe_dt(struct device *dev) { u32 ports; int i, ret, gpio; enum of_gpio_flags flags; struct at91_usbh_data *pdata; - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; pdata = xzalloc(sizeof(*pdata)); dev->platform_data = pdata; @@ -117,7 +107,7 @@ static int at91_ohci_probe_dt(struct device_d *dev) return 0; } -static int at91_ohci_probe(struct device_d *dev) +static int at91_ohci_probe(struct device *dev) { int ret; struct resource *io; @@ -126,7 +116,7 @@ static int at91_ohci_probe(struct device_d *dev) dev->priv = ohci_at91; ohci_at91->dev = dev; - if (dev->device_node) { + if (dev->of_node) { ret = at91_ohci_probe_dt(dev); if (ret < 0) return ret; @@ -169,7 +159,7 @@ static int at91_ohci_probe(struct device_d *dev) return 0; } -static void at91_ohci_remove(struct device_d *dev) +static void at91_ohci_remove(struct device *dev) { struct at91_usbh_data *pdata = dev->platform_data; struct ohci_at91_priv *ohci_at91 = dev->priv; @@ -204,8 +194,9 @@ static const struct of_device_id at91_ohci_dt_ids[] = { { .compatible = "atmel,at91rm9200-ohci" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, at91_ohci_dt_ids); -static struct driver_d at91_ohci_driver = { +static struct driver at91_ohci_driver = { .name = "at91_ohci", .probe = at91_ohci_probe, .remove = at91_ohci_remove, diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index a28c48b4c8..ae4c34e818 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * URB OHCI HCD (Host Controller Driver) for USB on the AT91RM9200 and PCI bus. * @@ -16,18 +17,6 @@ * * Modified for the MP2USB by (C) Copyright 2005 Eric Benard * ebenard@eukrea.com - based on s3c24x0's driver - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * */ /* * IMPORTANT NOTES @@ -38,12 +27,11 @@ * to activate workaround for bug #41 or this driver will NOT work! */ #include <common.h> -#include <dma.h> #include <clock.h> #include <dma.h> #include <malloc.h> -#include <usb/usb.h> -#include <usb/usb_defs.h> +#include <linux/usb/usb.h> +#include <linux/usb/usb_defs.h> #include <init.h> #include <errno.h> #include <linux/err.h> @@ -854,7 +842,8 @@ static void td_fill(struct ohci *ohci, unsigned int info, td->hwNextTD = virt_to_phys((void *)m32_swap((unsigned long)td_pt)); - dma_sync_single_for_device((unsigned long)data, len, DMA_BIDIRECTIONAL); + dma_sync_single_for_device(ohci->host.hw_dev, (unsigned long)data, + len, DMA_BIDIRECTIONAL); /* append to queue */ td->ed->hwTailP = td->hwNextTD; @@ -1090,7 +1079,7 @@ static int dl_done_list(struct ohci *ohci) unsigned long ptdphys = virt_to_phys(ptd); struct td *td_list; - dma_sync_single_for_device((unsigned long)ptdphys, + dma_sync_single_for_device(ohci->host.hw_dev, (unsigned long)ptdphys, sizeof(struct td) * NUM_TD, DMA_BIDIRECTIONAL); td_list = dl_reverse_done_list(ohci); @@ -1527,7 +1516,7 @@ static int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *b dev->status = stat; dev->act_len = urb->actual_length; - dma_sync_single_for_cpu((unsigned long)buffer, transfer_len, + dma_sync_single_for_cpu(host->hw_dev, (unsigned long)buffer, transfer_len, DMA_BIDIRECTIONAL); pkt_print(urb, dev, pipe, buffer, transfer_len, @@ -1787,7 +1776,7 @@ static int ohci_init(struct usb_host *host) return 0; } -static int ohci_probe(struct device_d *dev) +static int ohci_probe(struct device *dev) { struct resource *iores; struct usb_host *host; @@ -1822,7 +1811,7 @@ static int ohci_probe(struct device_d *dev) return 0; } -static struct driver_d ohci_driver = { +static struct driver ohci_driver = { .name = "ohci", .probe = ohci_probe, }; diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 9c9b8375ce..7d70bcb719 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * URB OHCI HCD (Host Controller Driver) for USB. * diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index f66dadef12..e962bfde3f 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-or-later /* * USB HOST XHCI Controller stack * @@ -20,8 +20,8 @@ #include <io.h> #include <linux/err.h> #include <linux/sizes.h> -#include <usb/usb.h> -#include <usb/xhci.h> +#include <linux/usb/usb.h> +#include <linux/usb/xhci.h> #include <asm/unaligned.h> #include "xhci.h" @@ -82,11 +82,11 @@ static void xhci_free(struct xhci_ctrl *ctrl, void *ptr) * @param size size of memory to be allocated * @return allocates the memory and returns the aligned pointer */ -static void *xhci_malloc(struct xhci_ctrl *ctrl, unsigned int size) +static void *xhci_malloc(struct xhci_ctrl *ctrl, unsigned int size, dma_addr_t *dma_addr) { void *ptr; - ptr = dma_alloc_coherent(size, DMA_ADDRESS_BROKEN); + ptr = dma_alloc_coherent(size, dma_addr); if (!ptr) return NULL; @@ -145,7 +145,7 @@ static void xhci_scratchpad_free(struct xhci_ctrl *ctrl) ctrl->dcbaa->dev_context_ptrs[0] = 0; - xhci_free(ctrl, (void *)(uintptr_t)ctrl->scratchpad->sp_array[0]); + xhci_free(ctrl, ctrl->scratchpad->scratchpad); xhci_free(ctrl, ctrl->scratchpad->sp_array); free(ctrl->scratchpad); ctrl->scratchpad = NULL; @@ -234,14 +234,13 @@ static void xhci_link_segments(struct xhci_segment *prev, struct xhci_segment *next, bool link_trbs) { u32 val; - u64 val_64 = 0; if (!prev || !next) return; prev->next = next; if (link_trbs) { - val_64 = (uintptr_t)next->trbs; - prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr = val_64; + prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr = + cpu_to_le64(next->dma); /* * Set the last TRB in the segment to @@ -249,8 +248,7 @@ static void xhci_link_segments(struct xhci_segment *prev, */ val = le32_to_cpu(prev->trbs[TRBS_PER_SEGMENT-1].link.control); val &= ~TRB_TYPE_BITMASK; - val |= (TRB_LINK << TRB_TYPE_SHIFT); - + val |= TRB_TYPE(TRB_LINK); prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val); } } @@ -295,7 +293,7 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_ctrl *ctrl) seg = xzalloc(sizeof(*seg)); - seg->trbs = xhci_malloc(ctrl, SEGMENT_SIZE); + seg->trbs = xhci_malloc(ctrl, SEGMENT_SIZE, &seg->dma); return seg; } @@ -365,6 +363,7 @@ static int xhci_scratchpad_alloc(struct xhci_ctrl *ctrl) struct xhci_hccr *hccr = ctrl->hccr; struct xhci_hcor *hcor = ctrl->hcor; struct xhci_scratchpad *scratchpad; + dma_addr_t val_64; int num_sp; uint32_t page_size; void *buf; @@ -379,11 +378,11 @@ static int xhci_scratchpad_alloc(struct xhci_ctrl *ctrl) goto fail_sp; ctrl->scratchpad = scratchpad; - scratchpad->sp_array = xhci_malloc(ctrl, num_sp * sizeof(u64)); + scratchpad->sp_array = xhci_malloc(ctrl, num_sp * sizeof(u64), &val_64); if (!scratchpad->sp_array) goto fail_sp2; - ctrl->dcbaa->dev_context_ptrs[0] = - cpu_to_le64((uintptr_t)scratchpad->sp_array); + + ctrl->dcbaa->dev_context_ptrs[0] = cpu_to_le64(val_64); xhci_flush_cache((uintptr_t)&ctrl->dcbaa->dev_context_ptrs[0], sizeof(ctrl->dcbaa->dev_context_ptrs[0])); @@ -397,17 +396,21 @@ static int xhci_scratchpad_alloc(struct xhci_ctrl *ctrl) BUG_ON(i == 16); page_size = 1 << (i + 12); - buf = xhci_malloc(ctrl, num_sp * page_size); + buf = xhci_malloc(ctrl, num_sp * page_size, &val_64); if (!buf) goto fail_sp3; xhci_flush_cache((uintptr_t)buf, num_sp * page_size); + scratchpad->scratchpad = buf; for (i = 0; i < num_sp; i++) { - uintptr_t ptr = (uintptr_t)buf + i * page_size; - scratchpad->sp_array[i] = cpu_to_le64(ptr); + scratchpad->sp_array[i] = cpu_to_le64(val_64); + val_64 += page_size; } + xhci_flush_cache((uintptr_t)scratchpad->sp_array, + sizeof(u64) * num_sp); + return 0; fail_sp3: @@ -438,11 +441,11 @@ static struct xhci_container_ctx BUG_ON((type != XHCI_CTX_TYPE_DEVICE) && (type != XHCI_CTX_TYPE_INPUT)); ctx->type = type; ctx->size = (MAX_EP_CTX_NUM + 1) * - CTX_SIZE(readl(&ctrl->hccr->cr_hccparams)); + CTX_SIZE(xhci_readl(&ctrl->hccr->cr_hccparams)); if (type == XHCI_CTX_TYPE_INPUT) - ctx->size += CTX_SIZE(readl(&ctrl->hccr->cr_hccparams)); + ctx->size += CTX_SIZE(xhci_readl(&ctrl->hccr->cr_hccparams)); - ctx->bytes = xhci_malloc(ctrl, ctx->size); + ctx->bytes = xhci_malloc(ctrl, ctx->size, &ctx->dma); return ctx; } @@ -464,8 +467,7 @@ int xhci_alloc_virt_device(struct xhci_ctrl *ctrl, unsigned int slot_id) return -EEXIST; } - ctrl->devs[slot_id] = (struct xhci_virt_device *) - malloc(sizeof(struct xhci_virt_device)); + ctrl->devs[slot_id] = malloc(sizeof(struct xhci_virt_device)); if (!ctrl->devs[slot_id]) { dev_err(ctrl->dev, "Failed to allocate virtual device\n"); @@ -494,10 +496,10 @@ int xhci_alloc_virt_device(struct xhci_ctrl *ctrl, unsigned int slot_id) /* Allocate endpoint 0 ring */ virt_dev->eps[0].ring = xhci_ring_alloc(ctrl, 1, true); - byte_64 = (uintptr_t)(virt_dev->out_ctx->bytes); + byte_64 = virt_dev->out_ctx->dma; /* Point to output device context in dcbaa. */ - ctrl->dcbaa->dev_context_ptrs[slot_id] = byte_64; + ctrl->dcbaa->dev_context_ptrs[slot_id] = cpu_to_le64(byte_64); xhci_flush_cache((uintptr_t)&ctrl->dcbaa->dev_context_ptrs[slot_id], sizeof(__le64)); @@ -516,29 +518,27 @@ int xhci_alloc_virt_device(struct xhci_ctrl *ctrl, unsigned int slot_id) int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr, struct xhci_hcor *hcor) { + dma_addr_t dma; uint64_t val_64; uint64_t trb_64; uint32_t val; - unsigned long deq; + uint64_t deq; int i; struct xhci_segment *seg; /* DCBAA initialization */ - ctrl->dcbaa = xhci_malloc(ctrl, sizeof(*ctrl->dcbaa)); - if (!ctrl->dcbaa) { - dev_err(ctrl->dev, "unable to allocate DCBA\n"); - return -ENOMEM; - } + ctrl->dcbaa = xhci_malloc(ctrl, sizeof(struct xhci_device_context_array), + &dma); + ctrl->dcbaa->dma = dma; - val_64 = (uintptr_t)ctrl->dcbaa; /* Set the pointer in DCBAA register */ - xhci_writeq(&hcor->or_dcbaap, val_64); + xhci_writeq(&hcor->or_dcbaap, dma); /* Command ring control pointer register initialization */ ctrl->cmd_ring = xhci_ring_alloc(ctrl, 1, true); /* Set the address in the Command Ring Control register */ - trb_64 = (uintptr_t)ctrl->cmd_ring->first_seg->trbs; + trb_64 = ctrl->cmd_ring->first_seg->dma; val_64 = xhci_readq(&hcor->or_crcr); val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) | (trb_64 & (u64) ~CMD_RING_RSVD_BITS) | @@ -560,8 +560,8 @@ int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr, /* Event ring does not maintain link TRB */ ctrl->event_ring = xhci_ring_alloc(ctrl, ERST_NUM_SEGS, false); - ctrl->erst.entries = - xhci_malloc(ctrl, sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS); + ctrl->erst.entries = xhci_malloc(ctrl, sizeof(struct xhci_erst_entry) * + ERST_NUM_SEGS, &ctrl->erst.erst_dma_addr); ctrl->erst.num_entries = ERST_NUM_SEGS; @@ -570,9 +570,8 @@ int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr, val++) { struct xhci_erst_entry *entry = &ctrl->erst.entries[val]; - trb_64 = 0; - trb_64 = (uintptr_t)seg->trbs; - xhci_writeq(&entry->seg_addr, trb_64); + trb_64 = seg->dma; + entry->seg_addr = cpu_to_le64(trb_64); entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT); entry->rsvd = 0; seg = seg->next; @@ -580,7 +579,8 @@ int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr, xhci_flush_cache((uintptr_t)ctrl->erst.entries, ERST_NUM_SEGS * sizeof(struct xhci_erst_entry)); - deq = (unsigned long)ctrl->event_ring->dequeue; + deq = xhci_trb_virt_to_dma(ctrl->event_ring->deq_seg, + ctrl->event_ring->dequeue); /* Update HC event ring dequeue pointer */ xhci_writeq(&ctrl->ir_set->erst_dequeue, @@ -595,7 +595,7 @@ int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr, /* this is the event ring segment table pointer */ val_64 = xhci_readq(&ctrl->ir_set->erst_base); val_64 &= ERST_PTR_MASK; - val_64 |= ((uintptr_t)(ctrl->erst.entries) & ~ERST_PTR_MASK); + val_64 |= ctrl->erst.erst_dma_addr & ~ERST_PTR_MASK; xhci_writeq(&ctrl->ir_set->erst_base, val_64); @@ -645,7 +645,7 @@ struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_ctrl *ctrl, return (struct xhci_slot_ctx *)ctx->bytes; return (struct xhci_slot_ctx *) - (ctx->bytes + CTX_SIZE(readl(&ctrl->hccr->cr_hccparams))); + (ctx->bytes + CTX_SIZE(xhci_readl(&ctrl->hccr->cr_hccparams))); } /** @@ -667,7 +667,7 @@ struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_ctrl *ctrl, return (struct xhci_ep_ctx *) (ctx->bytes + - (ep_index * CTX_SIZE(readl(&ctrl->hccr->cr_hccparams)))); + (ep_index * CTX_SIZE(xhci_readl(&ctrl->hccr->cr_hccparams)))); } /** @@ -776,7 +776,7 @@ void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl, dev_dbg(&udev->dev, "route string 0x%x\n", route); - slot_ctx->dev_info |= route; + slot_ctx->dev_info |= cpu_to_le32(route); switch (speed) { case USB_SPEED_SUPER: @@ -824,25 +824,22 @@ void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl, /* Step 4 - ring already allocated */ /* Step 5 */ - ep0_ctx->ep_info2 = cpu_to_le32(CTRL_EP << EP_TYPE_SHIFT); + ep0_ctx->ep_info2 = cpu_to_le32(EP_TYPE(CTRL_EP)); dev_dbg(&udev->dev, "SPEED = %d\n", speed); switch (speed) { case USB_SPEED_SUPER: - ep0_ctx->ep_info2 |= cpu_to_le32(((512 & MAX_PACKET_MASK) << - MAX_PACKET_SHIFT)); + ep0_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(512)); dev_dbg(&udev->dev, "Setting Packet size = 512bytes\n"); break; case USB_SPEED_HIGH: /* USB core guesses at a 64-byte max packet first for FS devices */ case USB_SPEED_FULL: - ep0_ctx->ep_info2 |= cpu_to_le32(((64 & MAX_PACKET_MASK) << - MAX_PACKET_SHIFT)); + ep0_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(64)); dev_dbg(&udev->dev, "Setting Packet size = 64bytes\n"); break; case USB_SPEED_LOW: - ep0_ctx->ep_info2 |= cpu_to_le32(((8 & MAX_PACKET_MASK) << - MAX_PACKET_SHIFT)); + ep0_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(8)); dev_dbg(&udev->dev, "Setting Packet size = 8bytes\n"); break; default: @@ -851,11 +848,9 @@ void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl, } /* EP 0 can handle "burst" sizes of 1, so Max Burst Size field is 0 */ - ep0_ctx->ep_info2 |= - cpu_to_le32(((0 & MAX_BURST_MASK) << MAX_BURST_SHIFT) | - ((3 & ERROR_COUNT_MASK) << ERROR_COUNT_SHIFT)); + ep0_ctx->ep_info2 |= cpu_to_le32(MAX_BURST(0) | ERROR_COUNT(3)); - trb_64 = (uintptr_t)virt_dev->eps[0].ring->first_seg->trbs; + trb_64 = virt_dev->eps[0].ring->first_seg->dma; ep0_ctx->deq = cpu_to_le64(trb_64 | virt_dev->eps[0].ring->cycle_state); /* diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 61b1b55a27..691d9c7463 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-or-later /* * USB HOST XHCI Controller stack * @@ -19,12 +19,30 @@ #include <io.h> #include <linux/err.h> #include <linux/sizes.h> -#include <usb/usb.h> -#include <usb/xhci.h> +#include <linux/usb/usb.h> +#include <linux/usb/xhci.h> #include <asm/unaligned.h> #include "xhci.h" +/* + * Returns zero if the TRB isn't in this segment, otherwise it returns the DMA + * address of the TRB. + */ +dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, + union xhci_trb *trb) +{ + unsigned long segment_offset; + + BUG_ON(!seg || !trb || trb < seg->trbs); + + /* offset in TRBs */ + segment_offset = trb - seg->trbs; + BUG_ON(segment_offset >= TRBS_PER_SEGMENT); + + return seg->dma + (segment_offset * sizeof(*trb)); +} + /** * Is this TRB a link TRB or was the last TRB the last TRB in this event ring * segment? I.e. would the updated event TRB pointer step off the end of the @@ -181,12 +199,11 @@ static void inc_deq(struct xhci_ctrl *ctrl, struct xhci_ring *ring) * @param trb_fields pointer to trb field array containing TRB contents * @return pointer to the enqueued trb */ -static struct xhci_generic_trb *queue_trb(struct xhci_ctrl *ctrl, - struct xhci_ring *ring, - bool more_trbs_coming, - unsigned int *trb_fields) +static dma_addr_t queue_trb(struct xhci_ctrl *ctrl, struct xhci_ring *ring, + bool more_trbs_coming, unsigned int *trb_fields) { struct xhci_generic_trb *trb; + dma_addr_t addr; int i; trb = &ring->enqueue->generic; @@ -196,9 +213,11 @@ static struct xhci_generic_trb *queue_trb(struct xhci_ctrl *ctrl, xhci_flush_cache((uintptr_t)trb, sizeof(struct xhci_generic_trb)); + addr = xhci_trb_virt_to_dma(ring->enq_seg, (union xhci_trb *)trb); + inc_enq(ctrl, ring, more_trbs_coming); - return trb; + return addr; } /** @@ -273,16 +292,15 @@ static int prepare_ring(struct xhci_ctrl *ctrl, struct xhci_ring *ep_ring, * @param cmd Command type to enqueue * @return none */ -void xhci_queue_command(struct xhci_ctrl *ctrl, u8 *ptr, u32 slot_id, +void xhci_queue_command(struct xhci_ctrl *ctrl, dma_addr_t addr, u32 slot_id, u32 ep_index, trb_type cmd) { u32 fields[4]; - u64 val_64 = (uintptr_t)ptr; BUG_ON(prepare_ring(ctrl, ctrl->cmd_ring, EP_STATE_RUNNING)); - fields[0] = lower_32_bits(val_64); - fields[1] = upper_32_bits(val_64); + fields[0] = lower_32_bits(addr); + fields[1] = upper_32_bits(addr); fields[2] = 0; fields[3] = TRB_TYPE(cmd) | SLOT_ID_FOR_TRB(slot_id) | ctrl->cmd_ring->cycle_state; @@ -396,12 +414,15 @@ static void giveback_first_trb(struct usb_device *udev, int ep_index, */ void xhci_acknowledge_event(struct xhci_ctrl *ctrl) { + dma_addr_t deq; + /* Advance our dequeue pointer to the next event */ inc_deq(ctrl, ctrl->event_ring); /* Inform the hardware */ - xhci_writeq(&ctrl->ir_set->erst_dequeue, - (uintptr_t)ctrl->event_ring->dequeue | ERST_EHB); + deq = xhci_trb_virt_to_dma(ctrl->event_ring->deq_seg, + ctrl->event_ring->dequeue); + xhci_writeq(&ctrl->ir_set->erst_dequeue, deq | ERST_EHB); } /** @@ -436,7 +457,8 @@ static int event_ready(struct xhci_ctrl *ctrl) * @param expected TRB type expected from Event TRB * @return pointer to event trb */ -union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected) +union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected, + unsigned int timeout_ms) { trb_type type; uint64_t start = get_time_ns(); @@ -448,7 +470,8 @@ union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected) continue; type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->event_cmd.flags)); - if (type == expected) + if (type == expected || + (expected == TRB_NONE && type != TRB_PORT_STATUS)) return event; if (type == TRB_PORT_STATUS) @@ -461,7 +484,7 @@ union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected) le32_to_cpu(event->generic.field[2])) != COMP_SUCCESS); else - dev_err(ctrl->dev, "Unexpected XHCI event TRB, skipping... " + dev_dbg(ctrl->dev, "Unexpected XHCI event TRB, skipping... " "(%08x %08x %08x %08x)\n", le32_to_cpu(event->generic.field[0]), le32_to_cpu(event->generic.field[1]), @@ -469,13 +492,50 @@ union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected) le32_to_cpu(event->generic.field[3])); xhci_acknowledge_event(ctrl); - } while (!is_timeout_non_interruptible(start, 5 * SECOND)); + } while (!is_timeout_non_interruptible(start, timeout_ms * MSECOND)); if (expected == TRB_TRANSFER) return NULL; - dev_err(ctrl->dev, "XHCI timeout on event type %d... cannot recover.\n", expected); - BUG(); + dev_warn(ctrl->dev, "XHCI timeout on event type %d...\n", expected); + + return NULL; +} + +/* + * Send reset endpoint command for given endpoint. This recovers from a + * halted endpoint (e.g. due to a stall error). + */ +static void reset_ep(struct usb_device *udev, int ep_index, unsigned int timeout_ms) +{ + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); + struct xhci_ring *ring = ctrl->devs[udev->slot_id]->eps[ep_index].ring; + union xhci_trb *event; + u64 addr; + u32 field; + + dev_info(&udev->dev, "Resetting EP %d...\n", ep_index); + + xhci_queue_command(ctrl, 0, udev->slot_id, ep_index, TRB_RESET_EP); + event = xhci_wait_for_event(ctrl, TRB_COMPLETION, timeout_ms); + if (!event) + return; + + field = le32_to_cpu(event->trans_event.flags); + BUG_ON(TRB_TO_SLOT_ID(field) != udev->slot_id); + xhci_acknowledge_event(ctrl); + + addr = xhci_trb_virt_to_dma(ring->enq_seg, + (void *)((uintptr_t)ring->enqueue | ring->cycle_state)); + xhci_queue_command(ctrl, addr, udev->slot_id, ep_index, TRB_SET_DEQ); + event = xhci_wait_for_event(ctrl, TRB_COMPLETION, timeout_ms); + if (!event) + return; + + BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) + != udev->slot_id || GET_COMP_CODE(le32_to_cpu( + event->event_cmd.status)) != COMP_SUCCESS); + xhci_acknowledge_event(ctrl); } /* @@ -491,27 +551,49 @@ static void abort_td(struct usb_device *udev, int ep_index) struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); struct xhci_ring *ring = ctrl->devs[udev->slot_id]->eps[ep_index].ring; union xhci_trb *event; + xhci_comp_code comp; + trb_type type; + dma_addr_t addr; u32 field; - xhci_queue_command(ctrl, NULL, udev->slot_id, ep_index, TRB_STOP_RING); + xhci_queue_command(ctrl, 0, udev->slot_id, ep_index, TRB_STOP_RING); - event = xhci_wait_for_event(ctrl, TRB_TRANSFER); - field = le32_to_cpu(event->trans_event.flags); - BUG_ON(TRB_TO_SLOT_ID(field) != udev->slot_id); - BUG_ON(TRB_TO_EP_INDEX(field) != ep_index); - BUG_ON(GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len - != COMP_STOP))); - xhci_acknowledge_event(ctrl); + event = xhci_wait_for_event(ctrl, TRB_NONE, XHCI_TIMEOUT_DEFAULT); + if (!event) + return; - event = xhci_wait_for_event(ctrl, TRB_COMPLETION); - BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) - != udev->slot_id || GET_COMP_CODE(le32_to_cpu( - event->event_cmd.status)) != COMP_SUCCESS); + type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->event_cmd.flags)); + if (type == TRB_TRANSFER) { + field = le32_to_cpu(event->trans_event.flags); + BUG_ON(TRB_TO_SLOT_ID(field) != udev->slot_id); + BUG_ON(TRB_TO_EP_INDEX(field) != ep_index); + BUG_ON(GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len + != COMP_STOP))); + xhci_acknowledge_event(ctrl); + + event = xhci_wait_for_event(ctrl, TRB_COMPLETION, XHCI_TIMEOUT_DEFAULT); + if (!event) + return; + type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->event_cmd.flags)); + + } else { + dev_warn(ctrl->dev, "abort_td: Expected a TRB_TRANSFER TRB first\n"); + } + + comp = GET_COMP_CODE(le32_to_cpu(event->event_cmd.status)); + BUG_ON(type != TRB_COMPLETION || + TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) + != udev->slot_id || (comp != COMP_SUCCESS && comp + != COMP_CTX_STATE)); xhci_acknowledge_event(ctrl); - xhci_queue_command(ctrl, (void *)((uintptr_t)ring->enqueue | - ring->cycle_state), udev->slot_id, ep_index, TRB_SET_DEQ); - event = xhci_wait_for_event(ctrl, TRB_COMPLETION); + addr = xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue); + addr |= ring->cycle_state; + xhci_queue_command(ctrl, addr, udev->slot_id, ep_index, TRB_SET_DEQ); + event = xhci_wait_for_event(ctrl, TRB_COMPLETION, XHCI_TIMEOUT_DEFAULT); + if (!event) + return; + BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) != udev->slot_id || GET_COMP_CODE(le32_to_cpu( event->event_cmd.status)) != COMP_SUCCESS); @@ -557,7 +639,7 @@ static void record_transfer_result(struct usb_device *udev, * @return returns 0 if successful else -1 on failure */ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, - int length, void *buffer) + int length, void *buffer, unsigned int timeout_ms) { int num_trbs = 0; struct xhci_generic_trb *start_trb; @@ -601,7 +683,7 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, memcpy(bounce, buffer, length); } - map = addr = dma_map_single(ctrl->dev, bounce, length, direction); + map = addr = dma_map_single(ctrl->host.hw_dev, bounce, length, direction); dev_dbg(&udev->dev, "pipe=0x%lx, buffer=%p, length=%d\n", pipe, buffer, length); @@ -614,6 +696,14 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, ep_ctx = xhci_get_ep_ctx(ctrl, virt_dev->out_ctx, ep_index); + /* + * If the endpoint was halted due to a prior error, resume it before + * the next transfer. It is the responsibility of the upper layer to + * have dealt with whatever caused the error. + */ + if ((le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) == EP_STATE_HALTED) + reset_ep(udev, ep_index, timeout_ms); + ring = virt_dev->eps[ep_index].ring; /* * How much data is (potentially) left before the 64KB boundary? @@ -646,8 +736,10 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, */ ret = prepare_ring(ctrl, ring, le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK); - if (ret < 0) + if (ret < 0) { + dma_unmap_single(ctrl->host.hw_dev, map, length, direction); return ret; + } /* * Don't give the first TRB to the hardware (by toggling the cycle bit) @@ -667,6 +759,9 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, first_trb = true; + /* flush the buffer before use */ + xhci_flush_cache((uintptr_t)buffer, length); + /* Queue the first TRB, even if it's zero-length */ do { u32 remainder = 0; @@ -703,15 +798,14 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, maxpacketsize, num_trbs - 1); - length_field = ((trb_buff_len & TRB_LEN_MASK) | + length_field = (TRB_LEN(trb_buff_len) | remainder | - ((0 & TRB_INTR_TARGET_MASK) << - TRB_INTR_TARGET_SHIFT)); + TRB_INTR_TARGET(0)); trb_fields[0] = lower_32_bits(addr); trb_fields[1] = upper_32_bits(addr); trb_fields[2] = length_field; - trb_fields[3] = field | (TRB_NORMAL << TRB_TYPE_SHIFT); + trb_fields[3] = field | TRB_TYPE(TRB_NORMAL); queue_trb(ctrl, ring, (num_trbs > 1), trb_fields); @@ -726,12 +820,13 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, giveback_first_trb(udev, ep_index, start_cycle, start_trb); - event = xhci_wait_for_event(ctrl, TRB_TRANSFER); + event = xhci_wait_for_event(ctrl, TRB_TRANSFER, timeout_ms); if (!event) { dev_dbg(&udev->dev, "XHCI bulk transfer timed out, aborting...\n"); abort_td(udev, ep_index); udev->status = USB_ST_NAK_REC; /* closest thing to a timeout */ udev->act_len = 0; + dma_unmap_single(ctrl->host.hw_dev, map, length, direction); return -ETIMEDOUT; } field = le32_to_cpu(event->trans_event.flags); @@ -747,7 +842,7 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, record_transfer_result(udev, event, length); xhci_acknowledge_event(ctrl); - dma_unmap_single(ctrl->dev, map, length, direction); + dma_unmap_single(ctrl->host.hw_dev, map, length, direction); if (usb_pipein(pipe)) memcpy(buffer, bounce, length); @@ -767,7 +862,7 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, */ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, struct devrequest *req, int length, - void *buffer) + void *buffer, unsigned int timeout_ms) { int ret; int start_cycle; @@ -846,7 +941,7 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, /* Queue setup TRB - see section 6.4.1.2.1 */ /* FIXME better way to translate setup_packet into two u32 fields? */ field = 0; - field |= TRB_IDT | (TRB_SETUP << TRB_TYPE_SHIFT); + field |= TRB_IDT | TRB_TYPE(TRB_SETUP); if (start_cycle == 0) field |= 0x1; @@ -854,9 +949,9 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, if (HC_VERSION(xhci_readl(&ctrl->hccr->cr_capbase)) >= 0x100) { if (length > 0) { if (req->requesttype & USB_DIR_IN) - field |= (TRB_DATA_IN << TRB_TX_TYPE_SHIFT); + field |= TRB_TX_TYPE(TRB_DATA_IN); else - field |= (TRB_DATA_OUT << TRB_TX_TYPE_SHIFT); + field |= TRB_TX_TYPE(TRB_DATA_OUT); } } @@ -872,8 +967,7 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, trb_fields[1] = le16_to_cpu(req->index) | le16_to_cpu(req->length) << 16; /* TRB_LEN | (TRB_INTR_TARGET) */ - trb_fields[2] = (8 | ((0 & TRB_INTR_TARGET_MASK) << - TRB_INTR_TARGET_SHIFT)); + trb_fields[2] = (TRB_LEN(8) | TRB_INTR_TARGET(0)); /* Immediate data in pointer */ trb_fields[3] = field; queue_trb(ctrl, ep_ring, true, trb_fields); @@ -883,15 +977,15 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, /* If there's data, queue data TRBs */ /* Only set interrupt on short packet for IN endpoints */ if (usb_pipein(pipe)) - field = TRB_ISP | (TRB_DATA << TRB_TYPE_SHIFT); + field = TRB_ISP | TRB_TYPE(TRB_DATA); else - field = (TRB_DATA << TRB_TYPE_SHIFT); + field = TRB_TYPE(TRB_DATA); - length_field = (length & TRB_LEN_MASK) | xhci_td_remainder(length) | + length_field = TRB_LEN(length) | xhci_td_remainder(length) | ((0 & TRB_INTR_TARGET_MASK) << TRB_INTR_TARGET_SHIFT); dev_dbg(&udev->dev, "length_field = %d, length = %d," "xhci_td_remainder(length) = %d , TRB_INTR_TARGET(0) = %d\n", - length_field, (length & TRB_LEN_MASK), + length_field, TRB_LEN(length), xhci_td_remainder(length), 0); if (req->requesttype & USB_DIR_IN) @@ -902,13 +996,14 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, if (length > 0) { if (req->requesttype & USB_DIR_IN) field |= TRB_DIR_IN; - map = buf_64 = dma_map_single(ctrl->dev, buffer, length, direction); + map = buf_64 = dma_map_single(ctrl->host.hw_dev, buffer, length, direction); trb_fields[0] = lower_32_bits(buf_64); trb_fields[1] = upper_32_bits(buf_64); trb_fields[2] = length_field; trb_fields[3] = field | ep_ring->cycle_state; + xhci_flush_cache((uintptr_t)buffer, length); queue_trb(ctrl, ep_ring, true, trb_fields); } @@ -926,17 +1021,16 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, trb_fields[0] = 0; trb_fields[1] = 0; - trb_fields[2] = ((0 & TRB_INTR_TARGET_MASK) << TRB_INTR_TARGET_SHIFT); + trb_fields[2] = TRB_INTR_TARGET(0); /* Event on completion */ trb_fields[3] = field | TRB_IOC | - (TRB_STATUS << TRB_TYPE_SHIFT) | - ep_ring->cycle_state; + TRB_TYPE(TRB_STATUS) | ep_ring->cycle_state; queue_trb(ctrl, ep_ring, false, trb_fields); giveback_first_trb(udev, ep_index, start_cycle, start_trb); - event = xhci_wait_for_event(ctrl, TRB_TRANSFER); + event = xhci_wait_for_event(ctrl, TRB_TRANSFER, timeout_ms); if (!event) goto abort; field = le32_to_cpu(event->trans_event.flags); @@ -954,12 +1048,17 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, /* Invalidate buffer to make it available to usb-core */ if (length > 0) - dma_unmap_single(ctrl->dev, map, length, direction); + dma_unmap_single(ctrl->host.hw_dev, map, length, direction); + + if (udev->status == USB_ST_STALLED) { + reset_ep(udev, ep_index, timeout_ms); + return -EPIPE; + } if (GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len)) == COMP_SHORT_TX) { /* Short data stage, clear up additional status stage event */ - event = xhci_wait_for_event(ctrl, TRB_TRANSFER); + event = xhci_wait_for_event(ctrl, TRB_TRANSFER, timeout_ms); if (!event) goto abort; BUG_ON(TRB_TO_SLOT_ID(field) != slot_id); @@ -974,5 +1073,6 @@ abort: abort_td(udev, ep_index); udev->status = USB_ST_NAK_REC; udev->act_len = 0; + dma_unmap_single(ctrl->host.hw_dev, map, length, direction); return -ETIMEDOUT; } diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 317000d650..e7b8344181 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-or-later /* * USB HOST XHCI Controller stack * @@ -24,13 +24,13 @@ #include <init.h> #include <io.h> #include <linux/err.h> -#include <usb/usb.h> -#include <usb/xhci.h> +#include <linux/usb/usb.h> +#include <linux/usb/xhci.h> #include <asm/unaligned.h> #include "xhci.h" -static struct descriptor { +static const struct descriptor { struct usb_hub_descriptor hub; struct usb_device_descriptor device; struct usb_config_descriptor config; @@ -151,6 +151,8 @@ static int xhci_start(struct xhci_ctrl *ctrl) u32 temp; int ret; + dev_dbg(ctrl->dev, "Starting the controller\n"); + temp = xhci_readl(&hcor->or_usbcmd); temp |= (CMD_RUN); xhci_writel(&hcor->or_usbcmd, temp); @@ -441,9 +443,12 @@ static int xhci_configure_endpoints(struct usb_device *udev, bool ctx_change) in_ctx = virt_dev->in_ctx; xhci_flush_cache((uintptr_t)in_ctx->bytes, in_ctx->size); - xhci_queue_command(ctrl, in_ctx->bytes, udev->slot_id, 0, + xhci_queue_command(ctrl, in_ctx->dma, udev->slot_id, 0, ctx_change ? TRB_EVAL_CONTEXT : TRB_CONFIG_EP); - event = xhci_wait_for_event(ctrl, TRB_COMPLETION); + event = xhci_wait_for_event(ctrl, TRB_COMPLETION, XHCI_TIMEOUT_DEFAULT); + if (!event) + return -ETIMEDOUT; + BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) != udev->slot_id); @@ -566,8 +571,7 @@ static int xhci_set_configuration(struct usb_device *udev) cpu_to_le32(EP_MAX_ESIT_PAYLOAD_HI(max_esit_payload) | EP_INTERVAL(interval) | EP_MULT(mult)); - ep_ctx[ep_index]->ep_info2 = - cpu_to_le32(ep_type << EP_TYPE_SHIFT); + ep_ctx[ep_index]->ep_info2 = cpu_to_le32(EP_TYPE(ep_type)); ep_ctx[ep_index]->ep_info2 |= cpu_to_le32(MAX_PACKET (get_unaligned(&endpt_desc->wMaxPacketSize))); @@ -579,8 +583,8 @@ static int xhci_set_configuration(struct usb_device *udev) cpu_to_le32(MAX_BURST(max_burst) | ERROR_COUNT(err_count)); - trb_64 = (uintptr_t) - virt_dev->eps[ep_index].ring->enqueue; + trb_64 = xhci_trb_virt_to_dma(virt_dev->eps[ep_index].ring->enq_seg, + virt_dev->eps[ep_index].ring->enqueue); ep_ctx[ep_index]->deq = cpu_to_le64(trb_64 | virt_dev->eps[ep_index].ring->cycle_state); @@ -628,8 +632,12 @@ static int xhci_address_device(struct usb_device *udev, int root_portnr) ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG); ctrl_ctx->drop_flags = 0; - xhci_queue_command(ctrl, (void *)ctrl_ctx, slot_id, 0, TRB_ADDR_DEV); - event = xhci_wait_for_event(ctrl, TRB_COMPLETION); + xhci_queue_command(ctrl, virt_dev->in_ctx->dma, + slot_id, 0, TRB_ADDR_DEV); + event = xhci_wait_for_event(ctrl, TRB_COMPLETION, XHCI_TIMEOUT_DEFAULT); + if (!event) + return -ETIMEDOUT; + BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) != slot_id); switch (GET_COMP_CODE(le32_to_cpu(event->event_cmd.status))) { @@ -703,8 +711,11 @@ static int _xhci_alloc_device(struct usb_device *udev) return 0; } - xhci_queue_command(ctrl, NULL, 0, 0, TRB_ENABLE_SLOT); - event = xhci_wait_for_event(ctrl, TRB_COMPLETION); + xhci_queue_command(ctrl, 0, 0, 0, TRB_ENABLE_SLOT); + event = xhci_wait_for_event(ctrl, TRB_COMPLETION, XHCI_TIMEOUT_DEFAULT); + if (!event) + return -ETIMEDOUT; + BUG_ON(GET_COMP_CODE(le32_to_cpu(event->event_cmd.status)) != COMP_SUCCESS); @@ -764,8 +775,7 @@ int xhci_check_maxpacket(struct usb_device *udev) ctrl->devs[slot_id]->out_ctx, ep_index); in_ctx = ctrl->devs[slot_id]->in_ctx; ep_ctx = xhci_get_ep_ctx(ctrl, in_ctx, ep_index); - ep_ctx->ep_info2 &= cpu_to_le32(~((0xffff & MAX_PACKET_MASK) - << MAX_PACKET_SHIFT)); + ep_ctx->ep_info2 &= cpu_to_le32(~MAX_PACKET(MAX_PACKET_MASK)); ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet_size)); /* @@ -860,7 +870,7 @@ static int xhci_submit_root(struct usb_device *udev, unsigned long pipe, { uint8_t tmpbuf[4]; u16 typeReq; - void *srcptr = NULL; + const void *srcptr = NULL; int len, srclen; uint32_t reg; volatile uint32_t *status_reg; @@ -928,7 +938,7 @@ static int xhci_submit_root(struct usb_device *udev, unsigned long pipe, case USB_DT_HUB: case USB_DT_SS_HUB: dev_dbg(&udev->dev, "USB_DT_HUB config\n"); - srcptr = &descriptor.hub; + srcptr = &ctrl->hub_desc; srclen = 0x8; break; default: @@ -1088,8 +1098,10 @@ unknown: static int _xhci_submit_int_msg(struct usb_device *udev, unsigned long pipe, void *buffer, int length, int interval) { - if (usb_pipetype(pipe) != PIPE_INTERRUPT) + if (usb_pipetype(pipe) != PIPE_INTERRUPT) { + dev_err(&udev->dev, "non-interrupt pipe (type=%lu)", usb_pipetype(pipe)); return -EINVAL; + } /* * xHCI uses normal TRBs for both bulk and interrupt. When the @@ -1097,7 +1109,7 @@ static int _xhci_submit_int_msg(struct usb_device *udev, unsigned long pipe, * (at most) one TD. A TD (comprised of sg list entries) can * take several service intervals to transmit. */ - return xhci_bulk_tx(udev, pipe, length, buffer); + return xhci_bulk_tx(udev, pipe, length, buffer, 0); } /** @@ -1112,10 +1124,12 @@ static int _xhci_submit_int_msg(struct usb_device *udev, unsigned long pipe, static int _xhci_submit_bulk_msg(struct usb_device *udev, unsigned long pipe, void *buffer, int length, int timeout_ms) { - if (usb_pipetype(pipe) != PIPE_BULK) + if (usb_pipetype(pipe) != PIPE_BULK) { + dev_err(&udev->dev, "non-bulk pipe (type=%lu)", usb_pipetype(pipe)); return -EINVAL; + } - return xhci_bulk_tx(udev, pipe, length, buffer); + return xhci_bulk_tx(udev, pipe, length, buffer, timeout_ms); } /** @@ -1131,13 +1145,16 @@ static int _xhci_submit_bulk_msg(struct usb_device *udev, unsigned long pipe, */ static int _xhci_submit_control_msg(struct usb_device *udev, unsigned long pipe, void *buffer, int length, - struct devrequest *setup, int root_portnr) + struct devrequest *setup, int root_portnr, + unsigned int timeout_ms) { struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); int ret = 0; - if (usb_pipetype(pipe) != PIPE_CONTROL) + if (usb_pipetype(pipe) != PIPE_CONTROL) { + dev_err(&udev->dev, "non-control pipe (type=%lu)", usb_pipetype(pipe)); return -EINVAL; + } if (usb_pipedevice(pipe) == ctrl->rootdev) return xhci_submit_root(udev, pipe, buffer, setup); @@ -1155,7 +1172,7 @@ static int _xhci_submit_control_msg(struct usb_device *udev, unsigned long pipe, } } - return xhci_ctrl_tx(udev, pipe, setup, length, buffer); + return xhci_ctrl_tx(udev, pipe, setup, length, buffer, timeout_ms); } static int xhci_lowlevel_init(struct xhci_ctrl *ctrl) @@ -1180,21 +1197,23 @@ static int xhci_lowlevel_init(struct xhci_ctrl *ctrl) /* initializing xhci data structures */ if (xhci_mem_init(ctrl, hccr, hcor) < 0) return -ENOMEM; + ctrl->hub_desc = descriptor.hub; reg = xhci_readl(&hccr->cr_hcsparams1); - descriptor.hub.bNbrPorts = ((reg & HCS_MAX_PORTS_MASK) >> - HCS_MAX_PORTS_SHIFT); + ctrl->hub_desc.bNbrPorts = HCS_MAX_PORTS(reg); + + dev_dbg(ctrl->dev, "Register 0x%x NbrPorts %d\n", reg, ctrl->hub_desc.bNbrPorts); /* Port Indicators */ reg = xhci_readl(&hccr->cr_hccparams); if (HCS_INDICATOR(reg)) - put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics) - | 0x80, &descriptor.hub.wHubCharacteristics); + put_unaligned(get_unaligned(&ctrl->hub_desc.wHubCharacteristics) + | 0x80, &ctrl->hub_desc.wHubCharacteristics); /* Port Power Control */ if (HCC_PPC(reg)) - put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics) - | 0x01, &descriptor.hub.wHubCharacteristics); + put_unaligned(get_unaligned(&ctrl->hub_desc.wHubCharacteristics) + | 0x01, &ctrl->hub_desc.wHubCharacteristics); if (xhci_start(ctrl)) { xhci_reset(ctrl); @@ -1217,6 +1236,8 @@ static int xhci_lowlevel_stop(struct xhci_ctrl *ctrl) xhci_reset(ctrl); + dev_dbg(ctrl->dev, "Disabling event ring interrupts\n"); + temp = xhci_readl(&ctrl->hcor->or_usbsts); xhci_writel(&ctrl->hcor->or_usbsts, temp & ~STS_EINT); temp = xhci_readl(&ctrl->ir_set->irq_pending); @@ -1239,7 +1260,7 @@ static int xhci_submit_control_msg(struct usb_device *udev, } return _xhci_submit_control_msg(udev, pipe, buffer, length, setup, - root_portnr); + root_portnr, timeout_ms); } static int xhci_submit_bulk_msg(struct usb_device *udev, @@ -1343,7 +1364,7 @@ static __maybe_unused int xhci_get_max_xfer_size(size_t *size) int xhci_register(struct xhci_ctrl *ctrl) { struct usb_host *host; - struct device_d *dev = ctrl->dev; + struct device *dev = ctrl->dev; int ret; dev_dbg(dev, "%s: hccr=%p, hcor=%p\n", __func__, ctrl->hccr, ctrl->hcor); @@ -1358,7 +1379,11 @@ int xhci_register(struct xhci_ctrl *ctrl) */ host->no_desc_before_addr = true; - host->hw_dev = dev; + /* + * If xHCI doesn't have its own DT node, it'll be a child of a + * physical USB host controller device that should be used for DMA + */ + host->hw_dev = dev_of_node(dev) ? dev : dev->parent; host->submit_int_msg = xhci_submit_int_msg; host->submit_control_msg = xhci_submit_control_msg; host->submit_bulk_msg = xhci_submit_bulk_msg; @@ -1389,18 +1414,11 @@ int xhci_deregister(struct xhci_ctrl *ctrl) return 0; } -static int xhci_detect(struct device_d *dev) -{ - struct xhci_ctrl *ctrl = dev->priv; - - return usb_host_detect(&ctrl->host); -} - /* * xHCI platform driver */ -static int xhci_probe(struct device_d *dev) +static int xhci_probe(struct device *dev) { struct resource *iores; struct xhci_ctrl *ctrl; @@ -1417,19 +1435,18 @@ static int xhci_probe(struct device_d *dev) HC_LENGTH(xhci_readl(&(ctrl->hccr)->cr_capbase))); dev->priv = ctrl; - dev->detect = xhci_detect; return xhci_register(ctrl); } -static void xhci_remove(struct device_d *dev) +static void xhci_remove(struct device *dev) { struct xhci_ctrl *ctrl = dev->priv; xhci_deregister(ctrl); } -static struct driver_d xhci_driver = { +static struct driver xhci_driver = { .name = "xHCI", .probe = xhci_probe, .remove = xhci_remove, diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 9ffbb103d5..37e8cee843 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * USB HOST XHCI Controller * @@ -16,7 +16,7 @@ #ifndef HOST_XHCI_H_ #define HOST_XHCI_H_ -#include <asm/types.h> +#include <linux/types.h> #include <io.h> #include <io-64-nonatomic-lo-hi.h> #include <linux/list.h> @@ -30,8 +30,8 @@ /* Section 5.3.3 - MaxPorts */ #define MAX_HC_PORTS 255 -/* Up to 16 ms to halt an HC */ -#define XHCI_MAX_HALT_USEC (16*1000) +/* Up to 32 ms to halt an HC */ +#define XHCI_MAX_HALT_USEC (32*1000) #define XHCI_MAX_RESET_USEC (250*1000) @@ -490,6 +490,7 @@ struct xhci_container_ctx { int size; u8 *bytes; + dma_addr_t dma; }; /** @@ -691,6 +692,8 @@ struct xhci_input_control_ctx { struct xhci_device_context_array { /* 64-bit device addresses; we only write 32-bit addresses */ __le64 dev_context_ptrs[MAX_HC_SLOTS]; + /* private xHCD pointers */ + dma_addr_t dma; }; /* TODO: write function to set the 64-bit device DMA address */ /* @@ -903,6 +906,8 @@ union xhci_trb { /* TRB type IDs */ typedef enum { + /* reserved, used as a software sentinel */ + TRB_NONE = 0, /* bulk, interrupt, isoc scatter/gather, and control data stage */ TRB_NORMAL = 1, /* setup stage for control transfers */ @@ -1003,6 +1008,7 @@ struct xhci_segment { union xhci_trb *trbs; /* private to HCD */ struct xhci_segment *next; + dma_addr_t dma; }; struct xhci_ring { @@ -1031,11 +1037,14 @@ struct xhci_erst_entry { struct xhci_erst { struct xhci_erst_entry *entries; unsigned int num_entries; + /* xhci->event_ring keeps track of segment dma addresses */ + dma_addr_t erst_dma_addr; /* Num entries the ERST can contain */ unsigned int erst_size; }; struct xhci_scratchpad { + void *scratchpad; u64 *sp_array; }; @@ -1134,8 +1143,6 @@ void xhci_hcd_stop(int index); /************************************************************* EXTENDED CAPABILITY DEFINITIONS *************************************************************/ -/* Up to 16 ms to halt an HC */ -#define XHCI_MAX_HALT_USEC (16*1000) /* HC not running - set to 1 when run/stop bit is cleared. */ #define XHCI_STS_HALT (1 << 0) @@ -1203,7 +1210,7 @@ void xhci_hcd_stop(int index); struct xhci_ctrl { struct usb_host host; - struct device_d *dev; + struct device *dev; struct xhci_hccr *hccr; /* R/O registers, not need for volatile */ struct xhci_hcor *hcor; struct xhci_doorbell_array *dba; @@ -1218,6 +1225,7 @@ struct xhci_ctrl { struct xhci_erst_entry entry[ERST_NUM_SEGS]; struct xhci_scratchpad *scratchpad; struct xhci_virt_device *devs[MAX_HC_SLOTS]; + struct usb_hub_descriptor hub_desc; void *bounce_buffer; int rootdev; }; @@ -1227,7 +1235,7 @@ static inline struct xhci_ctrl *to_xhci(struct usb_host *host) return container_of(host, struct xhci_ctrl, host); } -unsigned long trb_addr(struct xhci_segment *seg, union xhci_trb *trb); +dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb); struct xhci_input_control_ctx *xhci_get_input_control_ctx(struct xhci_container_ctx *ctx); struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_ctrl *ctrl, @@ -1244,14 +1252,16 @@ void xhci_slot_copy(struct xhci_ctrl *ctrl, struct xhci_container_ctx *out_ctx); void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl, struct usb_device *udev, int hop_portnr); -void xhci_queue_command(struct xhci_ctrl *ctrl, u8 *ptr, +void xhci_queue_command(struct xhci_ctrl *ctrl, dma_addr_t addr, u32 slot_id, u32 ep_index, trb_type cmd); void xhci_acknowledge_event(struct xhci_ctrl *ctrl); -union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected); +#define XHCI_TIMEOUT_DEFAULT 5000 +union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected, + unsigned int timeout_ms); int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, - int length, void *buffer); + int length, void *buffer, unsigned int timeout_ms); int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, - struct devrequest *req, int length, void *buffer); + struct devrequest *req, int length, void *buffer, unsigned int timeout_ms); int xhci_check_maxpacket(struct usb_device *udev); void xhci_flush_cache(uintptr_t addr, u32 type_len); void xhci_inval_cache(uintptr_t addr, u32 type_len); diff --git a/drivers/usb/imx/Kconfig b/drivers/usb/imx/Kconfig index 2b9e63b21c..2b9ae607bc 100644 --- a/drivers/usb/imx/Kconfig +++ b/drivers/usb/imx/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config USB_IMX_CHIPIDEA bool "i.MX USB support (read help)" diff --git a/drivers/usb/imx/Makefile b/drivers/usb/imx/Makefile index ac17e91e99..8a70fceb78 100644 --- a/drivers/usb/imx/Makefile +++ b/drivers/usb/imx/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_USB_IMX_CHIPIDEA) += chipidea-imx.o obj-$(CONFIG_USB_IMX_CHIPIDEA_USBMISC) += imx-usb-misc.o obj-$(CONFIG_USB_IMX_PHY) += imx-usb-phy.o diff --git a/drivers/usb/imx/chipidea-imx.c b/drivers/usb/imx/chipidea-imx.c index 635be02929..c5e6ce61e9 100644 --- a/drivers/usb/imx/chipidea-imx.c +++ b/drivers/usb/imx/chipidea-imx.c @@ -1,15 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. */ #include <common.h> @@ -18,13 +9,13 @@ #include <of.h> #include <errno.h> #include <driver.h> -#include <usb/usb.h> -#include <usb/ehci.h> +#include <linux/usb/usb.h> +#include <linux/usb/ehci.h> #include <regulator.h> -#include <usb/chipidea-imx.h> -#include <usb/phy.h> -#include <usb/ulpi.h> -#include <usb/fsl_usb2.h> +#include <linux/usb/chipidea-imx.h> +#include <linux/usb/phy.h> +#include <linux/usb/ulpi.h> +#include <linux/usb/fsl_usb2.h> #include <linux/err.h> #include <linux/phy/phy.h> #include <linux/clk.h> @@ -36,13 +27,13 @@ struct imx_chipidea_data { }; struct imx_chipidea { - struct device_d *dev; + struct device *dev; void __iomem *base; struct ehci_data data; unsigned long flags; enum usb_dr_mode mode; int portno; - struct device_d *usbmisc; + struct device *usbmisc; enum usb_phy_interface phymode; struct param_d *param_mode; struct regulator *vbus; @@ -113,7 +104,7 @@ static int imx_chipidea_probe_dt(struct imx_chipidea *ci) struct of_phandle_args out_args; if (ci->have_usb_misc) { - if (of_parse_phandle_with_args(ci->dev->device_node, "fsl,usbmisc", + if (of_parse_phandle_with_args(ci->dev->of_node, "fsl,usbmisc", "#index-cells", 0, &out_args)) return -ENODEV; @@ -126,7 +117,7 @@ static int imx_chipidea_probe_dt(struct imx_chipidea *ci) ci->flags = MXC_EHCI_MODE_UTMI_8BIT; - ci->mode = of_usb_get_dr_mode(ci->dev->device_node, NULL); + ci->mode = of_usb_get_dr_mode(ci->dev->of_node, NULL); if (ci->mode == USB_DR_MODE_UNKNOWN) { /* @@ -139,7 +130,7 @@ static int imx_chipidea_probe_dt(struct imx_chipidea *ci) ci->mode = USB_DR_MODE_HOST; } - ci->phymode = of_usb_get_phy_mode(ci->dev->device_node, NULL); + ci->phymode = of_usb_get_phy_mode(ci->dev->of_node, NULL); switch (ci->phymode) { case USBPHY_INTERFACE_MODE_UTMI: ci->flags = MXC_EHCI_MODE_UTMI_8BIT; @@ -160,28 +151,24 @@ static int imx_chipidea_probe_dt(struct imx_chipidea *ci) dev_dbg(ci->dev, "no phy_type setting. Relying on reset default\n"); } - if (of_find_property(ci->dev->device_node, + if (of_find_property(ci->dev->of_node, "disable-over-current", NULL)) ci->flags |= MXC_EHCI_DISABLE_OVERCURRENT; - else if (!of_find_property(ci->dev->device_node, + else if (!of_find_property(ci->dev->of_node, "over-current-active-high", NULL)) ci->flags |= MXC_EHCI_OC_PIN_ACTIVE_LOW; - if (of_usb_get_maximum_speed(ci->dev->device_node, NULL) == + if (of_find_property(ci->dev->of_node, "power-active-high", NULL)) + ci->flags |= MXC_EHCI_PWR_PIN_ACTIVE_HIGH; + + if (of_usb_get_maximum_speed(ci->dev->of_node, NULL) == USB_SPEED_FULL) ci->flags |= MXC_EHCI_PFSC; return 0; } -static int ci_ehci_detect(struct device_d *dev) -{ - struct imx_chipidea *ci = dev->priv; - - return ehci_detect(ci->ehci); -} - static int ci_set_mode(void *ctx, enum usb_dr_mode mode) { struct imx_chipidea *ci = ctx; @@ -202,8 +189,6 @@ static int ci_set_mode(void *ctx, enum usb_dr_mode mode) } ci->ehci = ehci; - - ci->dev->detect = ci_ehci_detect; } else { dev_err(ci->dev, "Host support not available\n"); return -ENODEV; @@ -228,11 +213,12 @@ static int ci_set_mode(void *ctx, enum usb_dr_mode mode) return 0; } -static int imx_chipidea_probe(struct device_d *dev) +static int imx_chipidea_probe(struct device *dev) { struct resource *iores; struct imx_chipidea_data *imx_data; struct imxusb_platformdata *pdata = dev->platform_data; + char const *phynode_name; int ret; void __iomem *base; struct imx_chipidea *ci; @@ -246,7 +232,7 @@ static int imx_chipidea_probe(struct device_d *dev) if (!ret) ci->have_usb_misc = imx_data->have_usb_misc; - if (IS_ENABLED(CONFIG_OFDEVICE) && dev->device_node) { + if (IS_ENABLED(CONFIG_OFDEVICE) && dev->of_node) { ret = imx_chipidea_probe_dt(ci); if (ret) return ret; @@ -262,8 +248,11 @@ static int imx_chipidea_probe(struct device_d *dev) } ci->vbus = regulator_get(dev, "vbus"); - if (IS_ERR(ci->vbus)) + if (IS_ERR(ci->vbus)) { + dev_warn(dev, "Cannot get vbus regulator: %pe (ignoring)\n", + ci->vbus); ci->vbus = NULL; + } /* * Some devices have more than one clock, in this case they are enabled @@ -271,15 +260,23 @@ static int imx_chipidea_probe(struct device_d *dev) * devices which have only one. */ ci->clk = clk_get(dev, NULL); - if (!IS_ERR(ci->clk)) - clk_enable(ci->clk); - if (of_property_read_bool(dev->device_node, "fsl,usbphy")) { - ci->phy = of_phy_get_by_phandle(dev, "fsl,usbphy", 0); + /* Device trees are using both "phys" and "fsl,usbphy". Prefer the + * more modern former one but fall back to the old one. + * + * Code should be removed when all devicetrees are using "phys" */ + if (of_property_read_bool(dev->of_node, "phys")) + phynode_name = "phys"; + else if (of_property_read_bool(dev->of_node, "fsl,usbphy")) + phynode_name = "fsl,usbphy"; + else + phynode_name = NULL; + + if (phynode_name) { + ci->phy = of_phy_get_by_phandle(dev, phynode_name, 0); if (IS_ERR(ci->phy)) { - ret = PTR_ERR(ci->phy); - dev_err(dev, "Cannot get phy: %s\n", strerror(-ret)); - return ret; + dev_err(dev, "Cannot get phy: %pe\n", ci->phy); + return PTR_ERR(ci->phy); } else { ci->usbphy = phy_to_usbphy(ci->phy); if (IS_ERR(ci->usbphy)) @@ -303,6 +300,14 @@ static int imx_chipidea_probe(struct device_d *dev) ci->data.drvdata = ci; ci->data.usbphy = ci->usbphy; + /* + * Enable the clock after we ensured that all resources are available. + * This is crucial since the phy can be missing which and so the + * usb-controller <-> usb-phy communication is only partly initialized. + * This can trigger strange system hangs at least on i.MX8M SoCs. + */ + clk_enable(ci->clk); + if ((ci->flags & MXC_EHCI_PORTSC_MASK) == MXC_EHCI_MODE_HSIC) imx_chipidea_port_init(ci); @@ -325,7 +330,7 @@ static int imx_chipidea_probe(struct device_d *dev) return ret; }; -static void imx_chipidea_remove(struct device_d *dev) +static void imx_chipidea_remove(struct device *dev) { struct imx_chipidea *ci = dev->priv; @@ -358,8 +363,9 @@ static __maybe_unused struct of_device_id imx_chipidea_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, imx_chipidea_dt_ids); -static struct driver_d imx_chipidea_driver = { +static struct driver imx_chipidea_driver = { .name = "imx-usb", .probe = imx_chipidea_probe, .of_compatible = DRV_OF_COMPAT(imx_chipidea_dt_ids), diff --git a/drivers/usb/imx/imx-usb-misc.c b/drivers/usb/imx/imx-usb-misc.c index aa4485ccba..bf9583e626 100644 --- a/drivers/usb/imx/imx-usb-misc.c +++ b/drivers/usb/imx/imx-usb-misc.c @@ -1,16 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> * Copyright (C) 2010 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. */ #include <common.h> @@ -18,9 +9,9 @@ #include <io.h> #include <of.h> #include <errno.h> -#include <usb/chipidea-imx.h> -#include <mach/imx6-regs.h> -#include <mach/iomux-mx6.h> +#include <linux/usb/chipidea-imx.h> +#include <mach/imx/imx6-regs.h> +#include <mach/imx/iomux-mx6.h> #define MX25_OTG_SIC_SHIFT 29 #define MX25_OTG_SIC_MASK (0x3 << MX25_OTG_SIC_SHIFT) @@ -353,7 +344,8 @@ static __maybe_unused struct imx_usb_misc_data mx5_data = { #define MX6_USB_CTRL(n) ((n) * 4) #define MX6_USB_CTRL_OVER_CUR_DIS (1 << 7) -#define MX6_USB_CTRL_OVER_CUR_ACT_HIGH (1 << 8) +#define MX6_USB_CTRL_OVER_CUR_ACT_LOW (1 << 8) +#define MX6_USB_CTRL_PWR_POLARITY (1 << 9) static void mx6_hsic_pullup(unsigned long reg, int on) { @@ -378,8 +370,17 @@ static __maybe_unused int mx6_initialize_usb_hw(void __iomem *base, int port, case 0: case 1: val = readl(base + MX6_USB_CTRL(port)); - if (flags & MXC_EHCI_DISABLE_OVERCURRENT) + if (flags & MXC_EHCI_DISABLE_OVERCURRENT) { val |= MX6_USB_CTRL_OVER_CUR_DIS; + } else { + val &= ~MX6_USB_CTRL_OVER_CUR_DIS; + if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW) + val |= MX6_USB_CTRL_OVER_CUR_ACT_LOW; + else + val &= ~MX6_USB_CTRL_OVER_CUR_ACT_LOW; + } + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + val |= MX6_USB_CTRL_PWR_POLARITY; writel(val, base + MX6_USB_CTRL(port)); break; case 2: /* HSIC port */ @@ -450,10 +451,12 @@ static int usbmisc_imx7d_init(void __iomem *base, int port, } else { reg &= ~MX6_USB_CTRL_OVER_CUR_DIS; if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW) - reg &= ~MX6_USB_CTRL_OVER_CUR_ACT_HIGH; + reg |= MX6_USB_CTRL_OVER_CUR_ACT_LOW; else - reg |= MX6_USB_CTRL_OVER_CUR_ACT_HIGH; + reg &= ~MX6_USB_CTRL_OVER_CUR_ACT_LOW; } + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + reg |= MX6_USB_CTRL_PWR_POLARITY; writel(reg, base); reg = readl(base + MX7D_USBNC_USB_CTRL2); @@ -599,11 +602,15 @@ static __maybe_unused struct of_device_id imx_usbmisc_dt_ids[] = { .data = &mx7_data, }, #endif -#ifdef CONFIG_ARCH_IMX8M +#if defined CONFIG_ARCH_IMX8M || defined CONFIG_ARCH_IMX93 { .compatible = "fsl,imx8mm-usbmisc", .data = &mx7_data, }, + { + .compatible = "fsl,imx8mn-usbmisc", + .data = &mx7_data, + }, #endif #ifdef CONFIG_ARCH_VF610 { @@ -615,8 +622,9 @@ static __maybe_unused struct of_device_id imx_usbmisc_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, imx_usbmisc_dt_ids); -int imx_usbmisc_port_init(struct device_d *dev, int port, unsigned flags) +int imx_usbmisc_port_init(struct device *dev, int port, unsigned flags) { struct imx_usb_misc_priv *usbmisc = dev->priv; @@ -629,7 +637,7 @@ int imx_usbmisc_port_init(struct device_d *dev, int port, unsigned flags) return usbmisc->data->init(usbmisc->base, port, flags); } -int imx_usbmisc_port_post_init(struct device_d *dev, int port, unsigned flags) +int imx_usbmisc_port_post_init(struct device *dev, int port, unsigned flags) { struct imx_usb_misc_priv *usbmisc = dev->priv; @@ -642,7 +650,7 @@ int imx_usbmisc_port_post_init(struct device_d *dev, int port, unsigned flags) return usbmisc->data->post_init(usbmisc->base, port, flags); } -static int imx_usbmisc_probe(struct device_d *dev) +static int imx_usbmisc_probe(struct device *dev) { struct resource *iores; struct imx_usb_misc_data *devtype; @@ -666,17 +674,11 @@ static int imx_usbmisc_probe(struct device_d *dev) return 0; } -static struct driver_d imx_usbmisc_driver = { +static struct driver imx_usbmisc_driver = { .name = "imx-usbmisc", .probe = imx_usbmisc_probe, .id_table = imx_usbmisc_ids, .of_compatible = DRV_OF_COMPAT(imx_usbmisc_dt_ids), }; -static int imx_usbmisc_init(void) -{ - platform_driver_register(&imx_usbmisc_driver); - return 0; -} - -coredevice_initcall(imx_usbmisc_init); +coredevice_platform_driver(imx_usbmisc_driver); diff --git a/drivers/usb/imx/imx-usb-phy.c b/drivers/usb/imx/imx-usb-phy.c index c23a747d0b..70bf292f80 100644 --- a/drivers/usb/imx/imx-usb-phy.c +++ b/drivers/usb/imx/imx-usb-phy.c @@ -1,15 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2013 Sascha Hauer <s.hauer@pengutronix.de> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. */ #include <common.h> @@ -19,7 +10,7 @@ #include <errno.h> #include <driver.h> #include <malloc.h> -#include <usb/phy.h> +#include <linux/usb/phy.h> #include <linux/phy/phy.h> #include <linux/clk.h> #include <linux/err.h> @@ -37,9 +28,12 @@ #define BM_USBPHY_CTRL_ENHOSTDISCONDETECT BIT(1) #define ANADIG_USB1_CHRG_DETECT_SET 0x1b4 -#define ANADIG_USB2_CHRG_DETECT_SET 0x214 #define ANADIG_USB1_CHRG_DETECT_EN_B BIT(20) #define ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B BIT(19) +#define ANADIG_USB1_VBUS_DETECT_STAT 0x1c0 +#define ANADIG_USB1_VBUS_DETECT_STAT_VBUS_VALID BIT(3) +#define ANADIG_USB2_CHRG_DETECT_SET 0x214 +#define ANADIG_USB2_VBUS_DETECT_STAT 0x220 struct imx_usbphy { struct usb_phy usb_phy; @@ -49,6 +43,8 @@ struct imx_usbphy { struct clk *clk; struct phy_provider *provider; int port_id; + + unsigned int vbus_valid; }; static int imx_usbphy_phy_init(struct phy *phy) @@ -112,7 +108,7 @@ static int imx_usbphy_notify_disconnect(struct usb_phy *phy, return 0; } -static struct phy *imx_usbphy_xlate(struct device_d *dev, +static struct phy *imx_usbphy_xlate(struct device *dev, struct of_phandle_args *args) { struct imx_usbphy *imxphy = dev->priv; @@ -132,10 +128,25 @@ static const struct phy_ops imx_phy_ops = { .to_usbphy = imx_usbphy_to_usbphy, }; -static int imx_usbphy_probe(struct device_d *dev) +static int imx_usbphy_get_vbus_state(struct param_d *p, void *priv) +{ + struct imx_usbphy *imxphy = priv; + unsigned int reg, val; + + reg = imxphy->port_id ? + ANADIG_USB1_VBUS_DETECT_STAT : + ANADIG_USB2_VBUS_DETECT_STAT; + val = readl(imxphy->anatop + reg); + + imxphy->vbus_valid = !!(val & ANADIG_USB1_VBUS_DETECT_STAT_VBUS_VALID); + + return 0; +} + +static int imx_usbphy_probe(struct device *dev) { struct resource *iores; - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; int ret; struct imx_usbphy *imxphy; @@ -154,6 +165,15 @@ static int imx_usbphy_probe(struct device_d *dev) ret = PTR_ERR_OR_ZERO(imxphy->anatop); if (ret) goto err_free; + + /* + * This is useful in case of usb-otg = device. In host case + * it isn't that useful since we are the supplier of the vbus + * signal. + */ + dev_add_param_bool(dev, "vbus_valid", param_set_readonly, + imx_usbphy_get_vbus_state, + &imxphy->vbus_valid, imxphy); } iores = dev_request_mem_resource(dev, 0); @@ -165,7 +185,7 @@ static int imx_usbphy_probe(struct device_d *dev) imxphy->clk = clk_get(dev, NULL); if (IS_ERR(imxphy->clk)) { - dev_err(dev, "could not get clk: %s\n", strerrorp(imxphy->clk)); + dev_err(dev, "could not get clk: %pe\n", imxphy->clk); ret = PTR_ERR(imxphy->clk); goto err_clk; } @@ -209,15 +229,12 @@ static __maybe_unused struct of_device_id imx_usbphy_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, imx_usbphy_dt_ids); -static struct driver_d imx_usbphy_driver = { +static struct driver imx_usbphy_driver = { .name = "imx-usb-phy", .probe = imx_usbphy_probe, .of_compatible = DRV_OF_COMPAT(imx_usbphy_dt_ids), }; -static int imx_usbphy_init(void) -{ - return platform_driver_register(&imx_usbphy_driver); -} -fs_initcall(imx_usbphy_init); +fs_platform_driver(imx_usbphy_driver); diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 270606f50b..fde57fd743 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only # # USB Miscellaneous driver configuration # @@ -13,3 +14,13 @@ config USB_HUB_USB251XB Microchip USB251x/xBi USB 2.0 Hub Controller series. Configuration parameters may be set in devicetree or platform data. Say Y or M here if you need to configure such a device via SMBus. + +config USB_ONBOARD_HUB + bool "Onboard USB hub support" + depends on OFDEVICE || COMPILE_TEST + help + Say Y here if you want to support discrete onboard USB hubs that + don't require an additional control bus for initialization, but + need some non-trivial form of initialization, such as enabling a + power regulator. An example for such a hub is the Realtek + RTS5411. diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index fb69c454bd..e00f66a5ed 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -1,6 +1,7 @@ -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0-only # # Makefile for the rest of the USB drivers # (the ones that don't fit into any other categories) # obj-$(CONFIG_USB_HUB_USB251XB) += usb251xb.o +obj-$(CONFIG_USB_ONBOARD_HUB) += onboard_usb_hub.o diff --git a/drivers/usb/misc/onboard_usb_hub.c b/drivers/usb/misc/onboard_usb_hub.c new file mode 100644 index 0000000000..9e94caaa84 --- /dev/null +++ b/drivers/usb/misc/onboard_usb_hub.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for onboard USB hubs + * + * Copyright (c) 2022, Google LLC + */ + +#include <driver.h> +#include <linux/gpio/consumer.h> +#include <init.h> +#include <of.h> +#include <linux/printk.h> +#include <of_device.h> +#include <regulator.h> +#include <linux/usb/usb.h> + +#include "onboard_usb_hub.h" + +void of_usb_host_probe_hubs(struct usb_host *host) +{ + struct device_node *np; + + np = dev_of_node(host->hw_dev); + if (!np) + return; + + of_platform_populate(np, onboard_hub_match, host->hw_dev); +} + +struct onboard_hub { + struct regulator *vdd; + struct device *dev; + const struct onboard_hub_pdata *pdata; + struct gpio_desc *reset_gpio; +}; + +static int onboard_hub_power_on(struct onboard_hub *hub) +{ + int err; + + err = regulator_enable(hub->vdd); + if (err) { + dev_err(hub->dev, "failed to enable regulator: %pe\n", + ERR_PTR(err)); + return err; + } + + udelay(hub->pdata->reset_us); + gpiod_set_value(hub->reset_gpio, 0); + + return 0; +} + +static int onboard_hub_probe(struct device *dev) +{ + struct device_node *peer_node; + struct device *peer_dev; + struct onboard_hub *hub; + + peer_node = of_parse_phandle(dev->of_node, "peer-hub", 0); + if (peer_node) { + peer_dev = of_find_device_by_node(peer_node); + if (peer_dev && peer_dev->priv) + return 0; + } + + hub = xzalloc(sizeof(*hub)); + + hub->pdata = device_get_match_data(dev); + if (!hub->pdata) + return -EINVAL; + + hub->vdd = regulator_get(dev, "vdd"); + if (IS_ERR(hub->vdd)) + return PTR_ERR(hub->vdd); + + hub->reset_gpio = gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(hub->reset_gpio)) + return dev_errp_probe(dev, hub->reset_gpio, + "failed to get reset GPIO\n"); + + hub->dev = dev; + dev->priv = hub; + + return onboard_hub_power_on(hub); +} + +static struct driver onboard_hub_driver = { + .name = "onboard-usb-hub", + .probe = onboard_hub_probe, + .of_compatible = onboard_hub_match, +}; +device_platform_driver(onboard_hub_driver); + +MODULE_AUTHOR("Matthias Kaehlcke <mka@chromium.org>"); +MODULE_DESCRIPTION("Driver for discrete onboard USB hubs"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/misc/onboard_usb_hub.h b/drivers/usb/misc/onboard_usb_hub.h new file mode 100644 index 0000000000..e379ca811a --- /dev/null +++ b/drivers/usb/misc/onboard_usb_hub.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2022, Google LLC + */ + +#ifndef _USB_MISC_ONBOARD_USB_HUB_H +#define _USB_MISC_ONBOARD_USB_HUB_H + +struct onboard_hub_pdata { + unsigned long reset_us; /* reset pulse width in us */ +}; + +static const struct onboard_hub_pdata microchip_usb424_data = { + .reset_us = 1, +}; + +static const struct onboard_hub_pdata realtek_rts5411_data = { + .reset_us = 0, +}; + +static const struct onboard_hub_pdata ti_tusb8041_data = { + .reset_us = 3000, +}; + +static const struct onboard_hub_pdata genesys_gl850g_data = { + .reset_us = 3, +}; + +static const struct onboard_hub_pdata genesys_gl852g_data = { + .reset_us = 50, +}; + +static const struct onboard_hub_pdata vialab_vl817_data = { + .reset_us = 10, +}; + +static const struct of_device_id onboard_hub_match[] = { + { .compatible = "usb424,2514", .data = µchip_usb424_data, }, + { .compatible = "usb424,2517", .data = µchip_usb424_data, }, + { .compatible = "usb451,8140", .data = &ti_tusb8041_data, }, + { .compatible = "usb451,8142", .data = &ti_tusb8041_data, }, + { .compatible = "usb5e3,608", .data = &genesys_gl850g_data, }, + { .compatible = "usb5e3,610", .data = &genesys_gl852g_data, }, + { .compatible = "usb5e3,620", .data = &genesys_gl852g_data, }, + { .compatible = "usbbda,411", .data = &realtek_rts5411_data, }, + { .compatible = "usbbda,5411", .data = &realtek_rts5411_data, }, + { .compatible = "usbbda,414", .data = &realtek_rts5411_data, }, + { .compatible = "usbbda,5414", .data = &realtek_rts5411_data, }, + { .compatible = "usb2109,817", .data = &vialab_vl817_data, }, + { .compatible = "usb2109,2817", .data = &vialab_vl817_data, }, + {} +}; +MODULE_DEVICE_TABLE(of, onboard_hub_match); + +#endif /* _USB_MISC_ONBOARD_USB_HUB_H */ diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c index 10d5aa310b..465d97e779 100644 --- a/drivers/usb/misc/usb251xb.c +++ b/drivers/usb/misc/usb251xb.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Driver for Microchip USB251xB USB 2.0 Hi-Speed Hub Controller * Configuration via SMBus. @@ -116,7 +116,7 @@ #define DRIVER_DESC "Microchip USB 2.0 Hi-Speed Hub Controller" struct usb251xb { - struct device_d *dev; + struct device *dev; struct i2c_client *i2c; u8 skip_config; int gpio_reset; @@ -241,7 +241,7 @@ static void usb251xb_reset(struct usb251xb *hub, int state) static int usb251xb_connect(struct usb251xb *hub) { - struct device_d *dev = hub->dev; + struct device *dev = hub->dev; int err, i; char i2c_wb[USB251XB_I2C_REG_SZ]; @@ -337,8 +337,8 @@ out_err: static int usb251xb_get_ofdata(struct usb251xb *hub, struct usb251xb_data *data) { - struct device_d *dev = hub->dev; - struct device_node *np = dev->device_node; + struct device *dev = hub->dev; + struct device_node *np = dev->of_node; int len, i; u32 port, property_u32 = 0; const u32 *cproperty_u32; @@ -560,7 +560,7 @@ static int usb251xb_get_ofdata(struct usb251xb *hub, */ hub->port_swap = USB251XB_DEF_PORT_SWAP; of_property_for_each_u32(np, "swap-dx-lanes", prop, p, port) { - if ((port >= 0) && (port <= data->port_cnt)) + if (port <= data->port_cnt) hub->port_swap |= BIT(port); } @@ -608,6 +608,7 @@ static const struct of_device_id usb251xb_of_match[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, usb251xb_of_match); #else /* CONFIG_OFDEVICE */ static int usb251xb_get_ofdata(struct usb251xb *hub, struct usb251xb_data *data) @@ -618,8 +619,8 @@ static int usb251xb_get_ofdata(struct usb251xb *hub, static int usb251xb_probe(struct usb251xb *hub) { - struct device_d *dev = hub->dev; - struct device_node *np = dev->device_node; + struct device *dev = hub->dev; + struct device_node *np = dev->of_node; const struct of_device_id *of_id = of_match_device(usb251xb_of_match, dev); int err; @@ -644,7 +645,7 @@ static int usb251xb_probe(struct usb251xb *hub) return 0; } -static int usb251xb_i2c_probe(struct device_d *dev) +static int usb251xb_i2c_probe(struct device *dev) { struct i2c_client *i2c = to_i2c_client(dev); struct usb251xb *hub; @@ -670,7 +671,7 @@ static const struct platform_device_id usb251xb_id[] = { { /* sentinel */ } }; -static struct driver_d usb251xb_i2c_driver = { +static struct driver usb251xb_i2c_driver = { .name = DRIVER_NAME, .probe = usb251xb_i2c_probe, .id_table = usb251xb_id, diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 127d6d1955..f99e514790 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config USB_MUSB bool "MUSB support" diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile index 9f9c210300..f60c97004b 100644 --- a/drivers/usb/musb/Makefile +++ b/drivers/usb/musb/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only # # for USB OTG silicon based on Mentor Graphics INVENTRA designs # diff --git a/drivers/usb/musb/am35x-phy-control.h b/drivers/usb/musb/am35x-phy-control.h index c492d421dc..b4eb585e71 100644 --- a/drivers/usb/musb/am35x-phy-control.h +++ b/drivers/usb/musb/am35x-phy-control.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ #ifndef _AM335x_PHY_CONTROL_H_ #define _AM335x_PHY_CONTROL_H_ @@ -16,6 +17,6 @@ static inline void phy_ctrl_wkup(struct phy_control *phy_ctrl, u32 id, bool on) phy_ctrl->phy_wkup(phy_ctrl, id, on); } -struct phy_control *am335x_get_phy_control(struct device_d *dev); +struct phy_control *am335x_get_phy_control(struct device *dev); #endif diff --git a/drivers/usb/musb/musb_am335x.c b/drivers/usb/musb/musb_am335x.c index 2a9167a245..19d780d15b 100644 --- a/drivers/usb/musb/musb_am335x.c +++ b/drivers/usb/musb/musb_am335x.c @@ -1,12 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <init.h> #include <linux/clk.h> -static int am335x_child_probe(struct device_d *dev) +static int am335x_child_probe(struct device *dev) { int ret; - ret = of_platform_populate(dev->device_node, NULL, dev); + ret = of_platform_populate(dev->of_node, NULL, dev); if (ret) return ret; @@ -20,8 +21,9 @@ static __maybe_unused struct of_device_id am335x_child_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, am335x_child_dt_ids); -static struct driver_d am335x_child_driver = { +static struct driver am335x_child_driver = { .name = "am335x_child_probe", .probe = am335x_child_probe, .of_compatible = DRV_OF_COMPAT(am335x_child_dt_ids), diff --git a/drivers/usb/musb/musb_barebox.c b/drivers/usb/musb/musb_barebox.c index b1f38c35ac..81fdd6338f 100644 --- a/drivers/usb/musb/musb_barebox.c +++ b/drivers/usb/musb/musb_barebox.c @@ -1,8 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <init.h> #include <clock.h> -#include <usb/musb.h> -#include <usb/usb.h> +#include <linux/usb/musb.h> +#include <linux/usb/usb.h> #include <linux/types.h> #include <linux/err.h> #include <linux/barebox-wrapper.h> @@ -121,13 +122,6 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, return submit_urb(dev, urb, 100); } -static int musb_detect(struct device_d *dev) -{ - struct musb *musb = dev->priv; - - return usb_host_detect(&musb->host); -} - int musb_register(struct musb *musb) { struct usb_host *host; @@ -139,8 +133,6 @@ int musb_register(struct musb *musb) host->submit_control_msg = submit_control_msg; host->submit_bulk_msg = submit_bulk_msg; - musb->controller->priv = musb; - musb->controller->detect = musb_detect; usb_register_host(host); return 0; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index b84da5516c..9c6c4e7bb4 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1,35 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * MUSB OTG driver core code * * Copyright 2005 Mentor Graphics Corporation * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ /* @@ -92,8 +67,8 @@ #include <common.h> #include <init.h> #include <clock.h> -#include <usb/musb.h> -#include <usb/usb.h> +#include <linux/usb/musb.h> +#include <linux/usb/usb.h> #include <linux/types.h> #include <linux/err.h> #include <linux/barebox-wrapper.h> @@ -215,8 +190,6 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) if (unlikely(len == 0)) return; - prefetch((u8 *)src); - dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n", 'T', hw_ep->epnum, fifo, len, src); @@ -332,7 +305,6 @@ void musb_load_testpacket(struct musb *musb) static void musb_generic_disable(struct musb *musb) { void __iomem *mbase = musb->mregs; - u16 temp; /* disable interrupts */ musb_writeb(mbase, MUSB_INTRUSBE, 0); @@ -345,9 +317,9 @@ static void musb_generic_disable(struct musb *musb) musb_writeb(mbase, MUSB_DEVCTL, 0); /* flush pending interrupts */ - temp = musb_readb(mbase, MUSB_INTRUSB); - temp = musb_readw(mbase, MUSB_INTRTX); - temp = musb_readw(mbase, MUSB_INTRRX); + (void)musb_readb(mbase, MUSB_INTRUSB); + (void)musb_readw(mbase, MUSB_INTRTX); + (void)musb_readw(mbase, MUSB_INTRRX); } @@ -1136,9 +1108,7 @@ fail2: musb_platform_exit(musb); fail1: - if (status != -EPROBE_DEFER) - dev_err(musb->controller, - "musb_init_controller failed with status %d\n", status); + dev_err_probe(musb->controller, status, "musb_init_controller failed\n"); musb_free(musb); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index d9402fcc4a..d954719161 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -1,35 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * MUSB OTG driver defines * * Copyright 2005 Mentor Graphics Corporation * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ #ifndef __MUSB_CORE_H__ @@ -37,8 +12,8 @@ #include <poller.h> #include <notifier.h> -#include <usb/usb.h> -#include <usb/phy.h> +#include <linux/usb/usb.h> +#include <linux/usb/phy.h> #include <linux/spinlock.h> struct musb; @@ -333,7 +308,7 @@ struct musb { struct dma_controller *dma_controller; - struct device_d *controller; + struct device *controller; void __iomem *ctrl_base; void __iomem *mregs; @@ -349,7 +324,6 @@ struct musb { u16 int_rx; u16 int_tx; - //struct device_d *phydev; struct usb_host host; struct usb_phy *xceiv; diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index 97a03cbcf4..75a0977bd1 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -1,35 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * MUSB OTG driver DMA controller abstraction * * Copyright 2005 Mentor Graphics Corporation * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ #ifndef __MUSB_DMA_H__ diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index d54a663e9d..97b64302ec 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Texas Instruments DSPS platforms "glue layer" * @@ -7,22 +8,6 @@ * * This file is part of the Inventra Controller Driver for Linux. * - * The Inventra Controller Driver for Linux is free software; you - * can redistribute it and/or modify it under the terms of the GNU - * General Public License version 2 as published by the Free Software - * Foundation. - * - * The Inventra Controller Driver for Linux is distributed in - * the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public - * License for more details. - * - * You should have received a copy of the GNU General Public License - * along with The Inventra Controller Driver for Linux ; if not, - * write to the Free Software Foundation, Inc., 59 Temple Place, - * Suite 330, Boston, MA 02111-1307 USA - * * musb_dsps.c will be a common file for all the TI DSPS platforms * such as dm64x, dm36x, dm35x, da8x, am35x and ti81x. * For now only ti81x is using this and in future davinci.c, am35x.c @@ -32,8 +17,8 @@ #include <common.h> #include <init.h> #include <clock.h> -#include <usb/usb.h> -#include <usb/musb.h> +#include <linux/usb/usb.h> +#include <linux/usb/musb.h> #include <malloc.h> #include <linux/err.h> #include <linux/barebox-wrapper.h> @@ -114,7 +99,7 @@ struct dsps_musb_wrapper { * DSPS glue structure. */ struct dsps_glue { - struct device_d *dev; + struct device *dev; void __iomem *base; unsigned long flags; enum musb_mode mode; @@ -123,7 +108,7 @@ struct dsps_glue { const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */ struct poller_async timer; /* otg_workaround timer */ uint64_t last_timer; /* last timer data for each instance */ - struct device_d otg_dev; + struct device otg_dev; uint32_t otgmode; struct musb_hdrc_platform_data pdata; }; @@ -275,11 +260,11 @@ static int get_int_prop(struct device_node *dn, const char *s) return val; } -static int get_musb_port_mode(struct device_d *dev) +static int get_musb_port_mode(struct device *dev) { enum usb_dr_mode mode; - mode = of_usb_get_dr_mode(dev->device_node, NULL); + mode = of_usb_get_dr_mode(dev->of_node, NULL); switch (mode) { case USB_DR_MODE_HOST: return MUSB_PORT_MODE_HOST; @@ -312,15 +297,15 @@ static int dsps_set_mode(void *ctx, enum usb_dr_mode mode) return musb_init_controller(&glue->musb, &glue->pdata); } -static int dsps_probe(struct device_d *dev) +static int dsps_probe(struct device *dev) { struct resource *iores[2]; struct musb_hdrc_platform_data *pdata; struct musb_hdrc_config *config; - struct device_node *dn = dev->device_node; + struct device_node *dn = dev->of_node; const struct dsps_musb_wrapper *wrp; struct device_node *phy_node; - struct device_d *phy_dev; + struct device *phy_dev; struct dsps_glue *glue; int ret; @@ -443,8 +428,9 @@ static __maybe_unused struct of_device_id musb_dsps_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, musb_dsps_dt_ids); -static struct driver_d dsps_usbss_driver = { +static struct driver dsps_usbss_driver = { .name = "musb-dsps", .probe = dsps_probe, .of_compatible = DRV_OF_COMPAT(musb_dsps_dt_ids), diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index fc5cfb13f5..87d6602f74 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * MUSB OTG driver peripheral support * @@ -5,32 +6,6 @@ * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2009 MontaVista Software, Inc. <source@mvista.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ #include <common.h> @@ -653,15 +628,6 @@ void musb_free_request(struct usb_ep *ep, struct usb_request *req) kfree(to_musb_request(req)); } -static LIST_HEAD(buffers); - -struct free_record { - struct list_head list; - struct device *dev; - unsigned bytes; - dma_addr_t dma; -}; - /* * Context: controller locked, IRQs blocked. */ @@ -1025,8 +991,7 @@ static void musb_gadget_poll(struct usb_gadget *gadget) static int musb_gadget_start(struct usb_gadget *g, struct usb_gadget_driver *driver); -static int musb_gadget_stop(struct usb_gadget *g, - struct usb_gadget_driver *driver); +static int musb_gadget_stop(struct usb_gadget *g); static const struct usb_gadget_ops musb_gadget_operations = { .get_frame = musb_gadget_get_frame, @@ -1201,16 +1166,9 @@ err: return retval; } -static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver) +static void stop_activity(struct musb *musb) { - int i; - struct musb_hw_ep *hw_ep; - - /* don't disconnect if it's not connected */ - if (musb->g.speed == USB_SPEED_UNKNOWN) - driver = NULL; - else - musb->g.speed = USB_SPEED_UNKNOWN; + musb->g.speed = USB_SPEED_UNKNOWN; /* deactivate the hardware */ if (musb->softconnect) { @@ -1218,25 +1176,6 @@ static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver) musb_pullup(musb, 0); } musb_stop(musb); - - /* killing any outstanding requests will quiesce the driver; - * then report disconnect - */ - if (driver) { - for (i = 0, hw_ep = musb->endpoints; - i < musb->nr_endpoints; - i++, hw_ep++) { - musb_ep_select(musb->mregs, i); - if (hw_ep->is_shared_fifo /* || !epnum */) { - nuke(&hw_ep->ep_in, -ESHUTDOWN); - } else { - if (hw_ep->max_packet_sz_tx) - nuke(&hw_ep->ep_in, -ESHUTDOWN); - if (hw_ep->max_packet_sz_rx) - nuke(&hw_ep->ep_out, -ESHUTDOWN); - } - } - } } /* @@ -1245,8 +1184,7 @@ static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver) * * @param driver the gadget driver to unregister */ -static int musb_gadget_stop(struct usb_gadget *g, - struct usb_gadget_driver *driver) +static int musb_gadget_stop(struct usb_gadget *g) { struct musb *musb = gadget_to_musb(g); unsigned long flags; @@ -1260,10 +1198,7 @@ static int musb_gadget_stop(struct usb_gadget *g, (void) musb_gadget_vbus_draw(&musb->g, 0); - stop_activity(musb, driver); - - dev_dbg(musb->controller, "unregistering driver %s\n", - driver ? driver->function : "(removed)"); + stop_activity(musb); musb->is_active = 0; musb->gadget_driver = NULL; diff --git a/drivers/usb/musb/musb_gadget.h b/drivers/usb/musb/musb_gadget.h index 456c165cc7..bbd1a35880 100644 --- a/drivers/usb/musb/musb_gadget.h +++ b/drivers/usb/musb/musb_gadget.h @@ -1,42 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * MUSB OTG driver peripheral defines * * Copyright 2005 Mentor Graphics Corporation * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ #ifndef __MUSB_GADGET_H #define __MUSB_GADGET_H #include <linux/list.h> -#include <usb/gadget.h> +#include <linux/usb/gadget.h> #if IS_ENABLED(CONFIG_USB_MUSB_GADGET) extern int musb_g_ep0_irq(struct musb *); diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index c8f55ac32c..eed02a8581 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * MUSB OTG peripheral driver ep0 handling * @@ -5,32 +6,6 @@ * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2008-2009 MontaVista Software, Inc. <source@mvista.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ #include <common.h> diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 68d819af2c..8176accb4f 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * MUSB OTG driver host support * @@ -5,32 +6,6 @@ * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2008-2009 MontaVista Software, Inc. <source@mvista.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ #include <common.h> @@ -1189,8 +1164,8 @@ void musb_host_rx(struct musb *musb, u8 epnum) pipe = urb->pipe; - dev_dbg(musb->controller, "<== hw %d rxcsr %04x, urb actual %d (+dma %zu)\n", - epnum, rx_csr, urb->actual_length, 0); + dev_dbg(musb->controller, "<== hw %d rxcsr %04x, urb actual %d (+dma 0)\n", + epnum, rx_csr, urb->actual_length); /* check for errors, concurrent stall & unlink is not really * handled yet! */ diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h index 0937808de8..5585f2e3b5 100644 --- a/drivers/usb/musb/musb_host.h +++ b/drivers/usb/musb/musb_host.h @@ -1,35 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * MUSB OTG driver host defines * * Copyright 2005 Mentor Graphics Corporation * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ #ifndef _MUSB_HOST_H @@ -37,7 +12,7 @@ //#include <linux/scatterlist.h> #include <linux/list.h> -#include <usb/usb.h> +#include <linux/usb/usb.h> #include <asm/unaligned.h> /* diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h index ec474477a1..658bb146fd 100644 --- a/drivers/usb/musb/musb_io.h +++ b/drivers/usb/musb/musb_io.h @@ -1,35 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * MUSB OTG driver register I/O * * Copyright 2005 Mentor Graphics Corporation * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ #ifndef __MUSB_LINUX_PLATFORM_ARCH_H__ diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h index 2cb749140b..e843e58b93 100644 --- a/drivers/usb/musb/musb_regs.h +++ b/drivers/usb/musb/musb_regs.h @@ -1,35 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * MUSB OTG driver register defines * * Copyright 2005 Mentor Graphics Corporation * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ #ifndef __MUSB_REGS_H__ diff --git a/drivers/usb/musb/phy-am335x-control.c b/drivers/usb/musb/phy-am335x-control.c index 41a3689ed3..313c67ef7e 100644 --- a/drivers/usb/musb/phy-am335x-control.c +++ b/drivers/usb/musb/phy-am335x-control.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <init.h> #include <io.h> @@ -7,7 +8,7 @@ #include "am35x-phy-control.h" struct am335x_control_usb { - struct device_d *dev; + struct device *dev; void __iomem *phy_reg; void __iomem *wkup; spinlock_t lock; @@ -101,13 +102,14 @@ static __maybe_unused struct of_device_id omap_control_usb_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, omap_control_usb_dt_ids); -struct phy_control *am335x_get_phy_control(struct device_d *dev) +struct phy_control *am335x_get_phy_control(struct device *dev) { struct device_node *node; struct am335x_control_usb *ctrl_usb; - node = of_parse_phandle(dev->device_node, "ti,ctrl_mod", 0); + node = of_parse_phandle(dev->of_node, "ti,ctrl_mod", 0); if (!node) return ERR_PTR(-ENOENT); @@ -124,7 +126,7 @@ struct phy_control *am335x_get_phy_control(struct device_d *dev) EXPORT_SYMBOL(am335x_get_phy_control); -static int am335x_control_usb_probe(struct device_d *dev) +static int am335x_control_usb_probe(struct device *dev) { struct resource *iores; /*struct resource *res;*/ @@ -168,7 +170,7 @@ free_ctrl: return 0; }; -static struct driver_d am335x_control_driver = { +static struct driver am335x_control_driver = { .name = "am335x-control-usb", .probe = am335x_control_usb_probe, .of_compatible = DRV_OF_COMPAT(omap_control_usb_dt_ids), diff --git a/drivers/usb/musb/phy-am335x.c b/drivers/usb/musb/phy-am335x.c index f2e870d7ee..f2a12182e0 100644 --- a/drivers/usb/musb/phy-am335x.c +++ b/drivers/usb/musb/phy-am335x.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <init.h> #include <io.h> @@ -21,7 +22,7 @@ static int am335x_init(struct usb_phy *phy) return 0; } -static int am335x_phy_probe(struct device_d *dev) +static int am335x_phy_probe(struct device *dev) { struct am335x_usbphy *am_usbphy; struct resource *iores; @@ -42,7 +43,7 @@ static int am335x_phy_probe(struct device_d *dev) goto err_release; } - am_usbphy->id = of_alias_get_id(dev->device_node, "phy"); + am_usbphy->id = of_alias_get_id(dev->of_node, "phy"); if (am_usbphy->id < 0) { dev_err(dev, "Missing PHY id: %d\n", am_usbphy->id); ret = am_usbphy->id; @@ -71,15 +72,12 @@ static __maybe_unused struct of_device_id am335x_phy_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, am335x_phy_dt_ids); -static struct driver_d am335x_phy_driver = { +static struct driver am335x_phy_driver = { .name = "am335x-phy-driver", .probe = am335x_phy_probe, .of_compatible = DRV_OF_COMPAT(am335x_phy_dt_ids), }; -static int am335x_phy_init(void) -{ - return platform_driver_register(&am335x_phy_driver); -} -fs_initcall(am335x_phy_init); +fs_platform_driver(am335x_phy_driver); diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 2c094452b6..f8e592e89e 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config USB_ULPI bool "ULPI Transceiver support" help diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile index 49c2491e58..d6cac86cff 100644 --- a/drivers/usb/otg/Makefile +++ b/drivers/usb/otg/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_USB_ULPI) += ulpi.o obj-$(CONFIG_USB_TWL4030) += twl4030.o obj-$(CONFIG_USB_OTGDEV) += otgdev.o diff --git a/drivers/usb/otg/otgdev.c b/drivers/usb/otg/otgdev.c index 7017796e8c..5a86263430 100644 --- a/drivers/usb/otg/otgdev.c +++ b/drivers/usb/otg/otgdev.c @@ -1,31 +1,36 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <driver.h> -#include <usb/usb.h> - -static int (*set_mode_callback)(void *ctx, enum usb_dr_mode mode); -static unsigned int otg_mode; +#include <linux/usb/usb.h> + +struct otg_mode { + struct device dev; + unsigned int var_mode; + unsigned int cur_mode; + int (*set_mode_callback)(void *ctx, enum usb_dr_mode mode); + void *ctx; +}; static int otg_set_mode(struct param_d *param, void *ctx) { - static int cur_mode = USB_DR_MODE_OTG; + struct otg_mode *otg = ctx; int ret; - if (otg_mode == USB_DR_MODE_UNKNOWN) + if (otg->var_mode == USB_DR_MODE_UNKNOWN) return -EINVAL; - if (otg_mode == cur_mode) + if (otg->var_mode == otg->cur_mode) return 0; - if (cur_mode != USB_DR_MODE_OTG) + if (otg->cur_mode != USB_DR_MODE_OTG) return -EBUSY; - ret = set_mode_callback(ctx, otg_mode); + ret = otg->set_mode_callback(otg->ctx, otg->var_mode); if (ret) return ret; - cur_mode = otg_mode; + otg->cur_mode = otg->var_mode; return 0; } @@ -37,33 +42,70 @@ static const char *otg_mode_names[] = { [USB_DR_MODE_OTG] = "otg", }; -static struct device_d otg_device = { - .name = "otg", - .id = DEVICE_ID_SINGLE, +static int register_otg_device(struct device *dev, struct otg_mode *otg) +{ + struct param_d *param_mode; + int ret; + + ret = register_device(dev); + if (ret) + return ret; + + param_mode = dev_add_param_enum(dev, "mode", + otg_set_mode, NULL, &otg->var_mode, + otg_mode_names, ARRAY_SIZE(otg_mode_names), otg); + + return PTR_ERR_OR_ZERO(param_mode); +} + +struct bus_type otg_bus_type = { + .name = "usbotg" /* "otg" is already taken for the alias */ }; -int usb_register_otg_device(struct device_d *parent, +int otg_device_get_mode(struct device *dev) +{ + struct otg_mode *otg; + + if (dev->bus != &otg_bus_type) + return -ENODEV; + + otg = dev->priv; + + return otg->cur_mode; +} + +int usb_register_otg_device(struct device *parent, int (*set_mode)(void *ctx, enum usb_dr_mode mode), void *ctx) { + bool first_otg = list_empty(&otg_bus_type.device_list); int ret; - struct param_d *param_mode; + struct otg_mode *otg; - if (otg_device.parent) - return -EBUSY; + otg = xzalloc(sizeof(*otg)); + otg->dev.priv = otg; + otg->dev.parent = parent; + otg->dev.bus = &otg_bus_type; + otg->dev.id = DEVICE_ID_DYNAMIC; + dev_set_name(&otg->dev, "otg"); - otg_device.parent = parent; - set_mode_callback = set_mode; - otg_mode = USB_DR_MODE_OTG; + otg->var_mode = USB_DR_MODE_OTG; + otg->cur_mode = USB_DR_MODE_OTG; + otg->set_mode_callback = set_mode; + otg->ctx = ctx; - ret = register_device(&otg_device); + ret = register_otg_device(&otg->dev, otg); if (ret) return ret; - param_mode = dev_add_param_enum(&otg_device, "mode", - otg_set_mode, NULL, &otg_mode, - otg_mode_names, ARRAY_SIZE(otg_mode_names), ctx); - if (IS_ERR(param_mode)) - return PTR_ERR(param_mode); + /* register otg.mode as an alias of otg0.mode */ + if (first_otg) + dev_add_alias(&otg->dev, "otg"); return 0; } + +static int otg_bus_init(void) +{ + return bus_register(&otg_bus_type); +} +pure_initcall(otg_bus_init); diff --git a/drivers/usb/otg/twl4030.c b/drivers/usb/otg/twl4030.c index 3668870b9e..5cbf734ded 100644 --- a/drivers/usb/otg/twl4030.c +++ b/drivers/usb/otg/twl4030.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2009 Wind River Systems, Inc. * Tom Rix <Tom.Rix@windriver.com> @@ -20,21 +21,10 @@ * Author: Atin Malaviya (atin.malaviya@gmail.com) * * ------------------------------------------------------------------------ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <mfd/twl4030.h> -#include <usb/twl4030.h> +#include <linux/usb/twl4030.h> #include <clock.h> static int twl4030_usb_write(u8 address, u8 data) diff --git a/drivers/usb/otg/ulpi.c b/drivers/usb/otg/ulpi.c index 9bc432fa86..d231b49b08 100644 --- a/drivers/usb/otg/ulpi.c +++ b/drivers/usb/otg/ulpi.c @@ -1,20 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <common.h> #include <io.h> #include <errno.h> -#include <usb/ulpi.h> +#include <linux/usb/ulpi.h> /* ULPIVIEW register bits */ #define ULPIVW_WU (1 << 31) /* Wakeup */ diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index b80c039117..efca9874d5 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config USB_STORAGE tristate "USB Mass Storage support" select DISK diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile index adf08433d5..8c60f3a2e5 100644 --- a/drivers/usb/storage/Makefile +++ b/drivers/usb/storage/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_USB_STORAGE) += usb-storage.o usb-storage-objs := usb.o transport.o diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 5186508ba6..be3b18dc66 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -1,21 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Most of this source has been derived from the Linux and * U-Boot USB Mass Storage driver implementations. * * Adapted for barebox: * Copyright (c) 2011, AMK Drives & Controls Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * */ #include <common.h> @@ -60,7 +49,7 @@ static int usb_stor_Bulk_clear_endpt_stall(struct us_data *us, unsigned int pipe /* Determine what the maximum LUN supported is */ int usb_stor_Bulk_max_lun(struct us_data *us) { - struct device_d *dev = &us->pusb_dev->dev; + struct device *dev = &us->pusb_dev->dev; int len, ret = 0; unsigned char *iobuf = dma_alloc(1); @@ -96,40 +85,45 @@ int usb_stor_Bulk_transport(struct us_blk_dev *usb_blkdev, void *data, u32 datalen) { struct us_data *us = usb_blkdev->us; - struct device_d *dev = &us->pusb_dev->dev; - struct bulk_cb_wrap cbw; - struct bulk_cs_wrap csw; + struct device *dev = &us->pusb_dev->dev; + struct bulk_cb_wrap *cbw; + struct bulk_cs_wrap *csw; int actlen, data_actlen; int result; unsigned int residue; unsigned int pipein = usb_rcvbulkpipe(us->pusb_dev, us->recv_bulk_ep); unsigned int pipeout = usb_sndbulkpipe(us->pusb_dev, us->send_bulk_ep); int dir_in = US_DIRECTION(cmd[0]); + int ret = 0; + + cbw = dma_alloc(sizeof(*cbw)); + csw = dma_alloc(sizeof(*csw)); /* set up the command wrapper */ - cbw.Signature = cpu_to_le32(US_BULK_CB_SIGN); - cbw.DataTransferLength = cpu_to_le32(datalen); - cbw.Flags = (dir_in ? US_BULK_FLAG_IN : US_BULK_FLAG_OUT); - cbw.Tag = ++cbw_tag; - cbw.Lun = usb_blkdev->lun; - cbw.Length = cmdlen; + cbw->Signature = cpu_to_le32(US_BULK_CB_SIGN); + cbw->DataTransferLength = cpu_to_le32(datalen); + cbw->Flags = (dir_in ? US_BULK_FLAG_IN : US_BULK_FLAG_OUT); + cbw->Tag = ++cbw_tag; + cbw->Lun = usb_blkdev->lun; + cbw->Length = cmdlen; /* copy the command payload */ - memset(cbw.CDB, 0, sizeof(cbw.CDB)); - memcpy(cbw.CDB, cmd, cbw.Length); + memset(cbw->CDB, 0, sizeof(cbw->CDB)); + memcpy(cbw->CDB, cmd, cbw->Length); /* send it to out endpoint */ dev_dbg(dev, "Bulk Command S 0x%x T 0x%x L %d F %d Trg %d LUN %d CL %d\n", - le32_to_cpu(cbw.Signature), cbw.Tag, - le32_to_cpu(cbw.DataTransferLength), cbw.Flags, - (cbw.Lun >> 4), (cbw.Lun & 0x0F), - cbw.Length); - result = usb_bulk_msg(us->pusb_dev, pipeout, &cbw, US_BULK_CB_WRAP_LEN, + le32_to_cpu(cbw->Signature), cbw->Tag, + le32_to_cpu(cbw->DataTransferLength), cbw->Flags, + (cbw->Lun >> 4), (cbw->Lun & 0x0F), + cbw->Length); + result = usb_bulk_msg(us->pusb_dev, pipeout, cbw, US_BULK_CB_WRAP_LEN, &actlen, USB_BULK_TO); dev_dbg(dev, "Bulk command transfer result=%d\n", result); if (result < 0) { usb_stor_Bulk_reset(us); - return USB_STOR_TRANSPORT_FAILED; + ret = USB_STOR_TRANSPORT_FAILED; + goto fail; } /* DATA STAGE */ @@ -152,13 +146,14 @@ int usb_stor_Bulk_transport(struct us_blk_dev *usb_blkdev, if (result < 0) { dev_dbg(dev, "Device status: %lx\n", us->pusb_dev->status); usb_stor_Bulk_reset(us); - return USB_STOR_TRANSPORT_FAILED; + ret = USB_STOR_TRANSPORT_FAILED; + goto fail; } } /* STATUS phase + error handling */ dev_dbg(dev, "Attempting to get CSW...\n"); - result = usb_bulk_msg(us->pusb_dev, pipein, &csw, US_BULK_CS_WRAP_LEN, + result = usb_bulk_msg(us->pusb_dev, pipein, csw, US_BULK_CS_WRAP_LEN, &actlen, USB_BULK_TO); /* did the endpoint stall? */ @@ -169,7 +164,7 @@ int usb_stor_Bulk_transport(struct us_blk_dev *usb_blkdev, if (result >= 0) { dev_dbg(dev, "Attempting to get CSW...\n"); result = usb_bulk_msg(us->pusb_dev, pipein, - &csw, US_BULK_CS_WRAP_LEN, + csw, US_BULK_CS_WRAP_LEN, &actlen, USB_BULK_TO); } } @@ -177,35 +172,39 @@ int usb_stor_Bulk_transport(struct us_blk_dev *usb_blkdev, if (result < 0) { dev_dbg(dev, "Device status: %lx\n", us->pusb_dev->status); usb_stor_Bulk_reset(us); - return USB_STOR_TRANSPORT_FAILED; + ret = USB_STOR_TRANSPORT_FAILED; + goto fail; } /* check bulk status */ - residue = le32_to_cpu(csw.Residue); + residue = le32_to_cpu(csw->Residue); dev_dbg(dev, "Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n", - le32_to_cpu(csw.Signature), csw.Tag, residue, csw.Status); - if (csw.Signature != cpu_to_le32(US_BULK_CS_SIGN)) { + le32_to_cpu(csw->Signature), csw->Tag, residue, csw->Status); + if (csw->Signature != cpu_to_le32(US_BULK_CS_SIGN)) { dev_dbg(dev, "Bad CSW signature\n"); usb_stor_Bulk_reset(us); - return USB_STOR_TRANSPORT_FAILED; - } else if (csw.Tag != cbw_tag) { + ret = USB_STOR_TRANSPORT_FAILED; + } else if (csw->Tag != cbw_tag) { dev_dbg(dev, "Mismatching tag\n"); usb_stor_Bulk_reset(us); - return USB_STOR_TRANSPORT_FAILED; - } else if (csw.Status >= US_BULK_STAT_PHASE) { + ret = USB_STOR_TRANSPORT_FAILED; + } else if (csw->Status >= US_BULK_STAT_PHASE) { dev_dbg(dev, "Status >= phase\n"); usb_stor_Bulk_reset(us); - return USB_STOR_TRANSPORT_ERROR; + ret = USB_STOR_TRANSPORT_ERROR; } else if (residue > datalen) { dev_dbg(dev, "residue (%uB) > req data (%uB)\n", residue, datalen); - return USB_STOR_TRANSPORT_FAILED; - } else if (csw.Status == US_BULK_STAT_FAIL) { + ret = USB_STOR_TRANSPORT_FAILED; + } else if (csw->Status == US_BULK_STAT_FAIL) { dev_dbg(dev, "FAILED\n"); - return USB_STOR_TRANSPORT_FAILED; + ret = USB_STOR_TRANSPORT_FAILED; } - return 0; +fail: + dma_free(cbw); + dma_free(csw); + return ret; } @@ -214,7 +213,7 @@ int usb_stor_Bulk_transport(struct us_blk_dev *usb_blkdev, */ int usb_stor_Bulk_reset(struct us_data *us) { - struct device_d *dev = &us->pusb_dev->dev; + struct device *dev = &us->pusb_dev->dev; int result; int result2; unsigned int pipe; diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h index 22d7dea3f5..5e08ae718a 100644 --- a/drivers/usb/storage/transport.h +++ b/drivers/usb/storage/transport.h @@ -1,21 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Most of this source has been derived from the Linux and * U-Boot USB Mass Storage driver implementations. * * Adapted for barebox: * Copyright (c) 2011, AMK Drives & Controls Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * */ #ifndef _TRANSPORT_H_ diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index e0ef4f5ef3..cc241e69be 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -1,30 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Most of this source has been derived from the Linux and * U-Boot USB Mass Storage driver implementations. * * Adapted for barebox: * Copyright (c) 2011, AMK Drives & Controls Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * */ #include <common.h> #include <init.h> #include <malloc.h> +#include <dma.h> #include <errno.h> #include <scsi.h> -#include <usb/usb.h> -#include <usb/usb_defs.h> +#include <linux/usb/usb.h> +#include <linux/usb/usb_defs.h> #include <asm/unaligned.h> @@ -41,10 +31,10 @@ static int usb_stor_request_sense(struct us_blk_dev *usb_blkdev) { struct us_data *us = usb_blkdev->us; - struct device_d *dev = &us->pusb_dev->dev; + struct device *dev = &us->pusb_dev->dev; u8 cmd[6]; const u8 datalen = 18; - u8 *data = xzalloc(datalen); + u8 *data = dma_alloc(datalen); dev_dbg(dev, "SCSI_REQ_SENSE\n"); @@ -55,7 +45,7 @@ static int usb_stor_request_sense(struct us_blk_dev *usb_blkdev) dev_dbg(dev, "Request Sense returned %02X %02X %02X\n", data[2], data[12], data[13]); - free(data); + dma_free(data); return 0; } @@ -84,7 +74,7 @@ static int usb_stor_transport(struct us_blk_dev *usb_blkdev, int retries, int request_sense_delay_ms) { struct us_data *us = usb_blkdev->us; - struct device_d *dev = &us->pusb_dev->dev; + struct device *dev = &us->pusb_dev->dev; int i, ret; for (i = 0; i <= retries; i++) { @@ -111,11 +101,11 @@ static int usb_stor_transport(struct us_blk_dev *usb_blkdev, static int usb_stor_inquiry(struct us_blk_dev *usb_blkdev) { - struct device_d *dev = &usb_blkdev->us->pusb_dev->dev; + struct device *dev = &usb_blkdev->us->pusb_dev->dev; int ret; u8 cmd[6]; const u16 datalen = 36; - u8 *data = xzalloc(datalen); + u8 *data = dma_alloc(datalen); memset(cmd, 0, sizeof(cmd)); cmd[0] = SCSI_INQUIRY; @@ -137,55 +127,129 @@ static int usb_stor_inquiry(struct us_blk_dev *usb_blkdev) // TODO: process and store device info exit: - free(data); + dma_free(data); return ret; } -static int usb_stor_test_unit_ready(struct us_blk_dev *usb_blkdev) +static int usb_stor_test_unit_ready(struct us_blk_dev *usb_blkdev, u64 timeout_ns) { + u64 start; u8 cmd[6]; int ret; memset(cmd, 0, sizeof(cmd)); cmd[0] = SCSI_TST_U_RDY; - ret = usb_stor_transport(usb_blkdev, cmd, sizeof(cmd), NULL, 0, - 10, 100); - if (ret < 0) - return -ENODEV; + start = get_time_ns(); - return 0; + do { + ret = usb_stor_transport(usb_blkdev, cmd, sizeof(cmd), NULL, 0, + 10, 100); + } while (ret < 0 && !is_timeout(start, timeout_ns)); + + return ret ? -ENODEV : 0; } -static int usb_stor_read_capacity(struct us_blk_dev *usb_blkdev, - u32 *last_lba, u32 *block_length) +static int read_capacity_16(struct us_blk_dev *usb_blkdev) { - struct device_d *dev = &usb_blkdev->us->pusb_dev->dev; + struct device *dev = &usb_blkdev->us->pusb_dev->dev; + unsigned char cmd[16]; + const u8 datalen = 32; + u8 *data = dma_alloc(datalen); + int ret; + sector_t lba; + unsigned sector_size; + + memset(cmd, 0, 16); + cmd[0] = SERVICE_ACTION_IN_16; + cmd[1] = SAI_READ_CAPACITY_16; + cmd[13] = datalen; + + ret = usb_stor_transport(usb_blkdev, cmd, sizeof(cmd), data, datalen, + 3, USB_STOR_NO_REQUEST_SENSE); + + if (ret < 0) { + dev_warn(dev, "Read Capacity(16) failed\n"); + goto fail; + } + + /* Note this is logical, not physical sector size */ + sector_size = be32_to_cpup((u32 *)&data[8]); + lba = be64_to_cpup((u64 *)&data[0]); + + dev_dbg(dev, "LBA (16) = 0x%llx w/ sector size = %u\n", + lba, sector_size); + + if ((data[12] & 1) == 1) { + dev_warn(dev, "Protection unsupported\n"); + ret = -ENOTSUPP; + goto fail; + } + + usb_blkdev->blk.blockbits = SECTOR_SHIFT; + usb_blkdev->blk.num_blocks = lba + 1; + + ret = sector_size; +fail: + dma_free(data); + return ret; +} + +static int read_capacity_10(struct us_blk_dev *usb_blkdev) +{ + struct device *dev = &usb_blkdev->us->pusb_dev->dev; + unsigned char cmd[16]; const u32 datalen = 8; - u32 *data = xzalloc(datalen); - u8 cmd[10]; + __be32 *data = dma_alloc(datalen); int ret; + sector_t lba; + unsigned sector_size; memset(cmd, 0, sizeof(cmd)); cmd[0] = SCSI_RD_CAPAC; ret = usb_stor_transport(usb_blkdev, cmd, sizeof(cmd), data, datalen, 3, USB_STOR_NO_REQUEST_SENSE); - if (ret < 0) - goto exit; - dev_dbg(dev, "Read Capacity returns: 0x%x, 0x%x\n", - data[0], data[1]); - *last_lba = be32_to_cpu(data[0]); - *block_length = be32_to_cpu(data[1]); + if (ret < 0) { + dev_warn(dev, "Read Capacity(10) failed\n"); + goto fail; + } -exit: - free(data); + sector_size = be32_to_cpu(data[1]); + lba = be32_to_cpu(data[0]); + + dev_dbg(dev, "LBA (10) = 0x%llx w/ sector size = %u\n", + lba, sector_size); + + if (sector_size != SECTOR_SIZE) + dev_warn(dev, "Support only %d bytes sectors\n", SECTOR_SIZE); + + usb_blkdev->blk.num_blocks = lba + 1; + usb_blkdev->blk.blockbits = SECTOR_SHIFT; + + ret = SECTOR_SIZE; +fail: + dma_free(data); return ret; } +static int usb_stor_io_16(struct us_blk_dev *usb_blkdev, u8 opcode, + sector_t start, u8 *data, u16 blocks) +{ + u8 cmd[16]; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = opcode; + put_unaligned_be64(start, &cmd[2]); + put_unaligned_be32(blocks, &cmd[10]); + + return usb_stor_transport(usb_blkdev, cmd, sizeof(cmd), data, + blocks * SECTOR_SIZE, 10, 0); +} + static int usb_stor_io_10(struct us_blk_dev *usb_blkdev, u8 opcode, - u32 start, u8 *data, u16 blocks) + sector_t start, u8 *data, u16 blocks) { u8 cmd[10]; @@ -206,37 +270,49 @@ static int usb_stor_io_10(struct us_blk_dev *usb_blkdev, u8 opcode, /* Read / write a chunk of sectors on media */ static int usb_stor_blk_io(struct block_device *disk_dev, - int sector_start, int sector_count, void *buffer, + sector_t sector_start, blkcnt_t sector_count, void *buffer, bool read) { struct us_blk_dev *pblk_dev = container_of(disk_dev, struct us_blk_dev, blk); struct us_data *us = pblk_dev->us; - struct device_d *dev = &us->pusb_dev->dev; + struct device *dev = &us->pusb_dev->dev; + int result; /* ensure unit ready */ dev_dbg(dev, "Testing for unit ready\n"); - if (usb_stor_test_unit_ready(pblk_dev)) { + if (usb_stor_test_unit_ready(pblk_dev, 0)) { dev_dbg(dev, "Device NOT ready\n"); return -EIO; } /* read / write the requested data */ - dev_dbg(dev, "%s %u block(s), starting from %d\n", + dev_dbg(dev, "%s %llu block(s), starting from %llu\n", read ? "Read" : "Write", sector_count, sector_start); while (sector_count > 0) { - unsigned n = min(sector_count, US_MAX_IO_BLK); + u16 n = min_t(blkcnt_t, sector_count, US_MAX_IO_BLK); + + if (disk_dev->num_blocks > 0xffffffff) { + result = usb_stor_io_16(pblk_dev, + read ? SCSI_READ16 : SCSI_WRITE16, + sector_start, + buffer, n); + } else { + + result = usb_stor_io_10(pblk_dev, + read ? SCSI_READ10 : SCSI_WRITE10, + sector_start, + buffer, n); + } - if (usb_stor_io_10(pblk_dev, - read ? SCSI_READ10 : SCSI_WRITE10, - sector_start, - buffer, n)) { - dev_dbg(dev, "I/O error at sector %d\n", sector_start); + if (result) { + dev_dbg(dev, "I/O error at sector %llu\n", sector_start); break; } + sector_start += n; sector_count -= n; buffer += n * SECTOR_SIZE; @@ -247,14 +323,14 @@ static int usb_stor_blk_io(struct block_device *disk_dev, /* Write a chunk of sectors to media */ static int __maybe_unused usb_stor_blk_write(struct block_device *blk, - const void *buffer, int block, int num_blocks) + const void *buffer, sector_t block, blkcnt_t num_blocks) { return usb_stor_blk_io(blk, block, num_blocks, (void *)buffer, false); } /* Read a chunk of sectors from media */ -static int usb_stor_blk_read(struct block_device *blk, void *buffer, int block, - int num_blocks) +static int usb_stor_blk_read(struct block_device *blk, void *buffer, sector_t block, + blkcnt_t num_blocks) { return usb_stor_blk_io(blk, block, num_blocks, buffer, true); } @@ -274,8 +350,7 @@ static struct block_device_ops usb_mass_storage_ops = { static int usb_stor_init_blkdev(struct us_blk_dev *pblk_dev) { struct us_data *us = pblk_dev->us; - struct device_d *dev = &us->pusb_dev->dev; - u32 last_lba = 0, block_length = 0; + struct device *dev = &us->pusb_dev->dev; int result; /* get device info */ @@ -290,7 +365,8 @@ static int usb_stor_init_blkdev(struct us_blk_dev *pblk_dev) /* ensure unit ready */ dev_dbg(dev, "Testing for unit ready\n"); - result = usb_stor_test_unit_ready(pblk_dev); + /* retry a bit longer than usual as some HDDs take longer to spin up */ + result = usb_stor_test_unit_ready(pblk_dev, 10ULL * NSEC_PER_SEC); if (result) { dev_dbg(dev, "Device NOT ready\n"); return result; @@ -299,23 +375,19 @@ static int usb_stor_init_blkdev(struct us_blk_dev *pblk_dev) /* read capacity */ dev_dbg(dev, "Reading capacity\n"); - result = usb_stor_read_capacity(pblk_dev, &last_lba, &block_length); - if (result < 0) { - dev_dbg(dev, "Cannot read device capacity\n"); + result = read_capacity_10(pblk_dev); + if (result < 0) return result; - } - if (last_lba > INT_MAX - 1) { - last_lba = INT_MAX - 1; - dev_warn(dev, - "Limiting device size due to 31 bit contraints\n"); + if (pblk_dev->blk.num_blocks > 0xffffffff) { + result = read_capacity_16(pblk_dev); + if (result < 0) { + dev_notice(dev, "Using 0xffffffff as device size\n"); + pblk_dev->blk.num_blocks = 1 + (sector_t) 0xffffffff; + } } - pblk_dev->blk.num_blocks = last_lba + 1; - if (block_length != SECTOR_SIZE) - pr_warn("Support only %d bytes sectors\n", SECTOR_SIZE); - pblk_dev->blk.blockbits = SECTOR_SHIFT; - dev_dbg(dev, "Capacity = 0x%x, blockshift = 0x%x\n", + dev_dbg(dev, "Capacity = 0x%llx, blockshift = 0x%x\n", pblk_dev->blk.num_blocks, pblk_dev->blk.blockbits); return 0; @@ -324,7 +396,7 @@ static int usb_stor_init_blkdev(struct us_blk_dev *pblk_dev) /* Create and register a disk device for the specified LUN */ static int usb_stor_add_blkdev(struct us_data *us, unsigned char lun) { - struct device_d *dev = &us->pusb_dev->dev; + struct device *dev = &us->pusb_dev->dev; struct us_blk_dev *pblk_dev; int result; @@ -345,10 +417,11 @@ static int usb_stor_add_blkdev(struct us_data *us, unsigned char lun) result = cdev_find_free_index("disk"); if (result == -1) pr_err("Cannot find a free number for the disk node\n"); - pr_info("Using index %d for the new disk\n", result); + dev_info(dev, "registering as disk%d\n", result); pblk_dev->blk.cdev.name = basprintf("disk%d", result); pblk_dev->blk.blockbits = SECTOR_SHIFT; + pblk_dev->blk.type = BLK_TYPE_USB; result = blockdevice_register(&pblk_dev->blk); if (result != 0) { @@ -356,11 +429,6 @@ static int usb_stor_add_blkdev(struct us_data *us, unsigned char lun) goto BadDevice; } - /* create partitions on demand */ - result = parse_partition_table(&pblk_dev->blk); - if (result != 0) - dev_warn(dev, "No partition table found\n"); - list_add_tail(&pblk_dev->list, &us->blk_dev_list); dev_dbg(dev, "USB disk device successfully added\n"); @@ -379,7 +447,7 @@ BadDevice: /* Get the transport settings */ static void get_transport(struct us_data *us) { - struct device_d *dev = &us->pusb_dev->dev; + struct device *dev = &us->pusb_dev->dev; switch (us->protocol) { case US_PR_BULK: us->transport_name = "Bulk"; @@ -394,7 +462,7 @@ static void get_transport(struct us_data *us) /* Get the endpoint settings */ static int get_pipes(struct us_data *us, struct usb_interface *intf) { - struct device_d *dev = &us->pusb_dev->dev; + struct device *dev = &us->pusb_dev->dev; unsigned int i; struct usb_endpoint_descriptor *ep; struct usb_endpoint_descriptor *ep_in = NULL; @@ -435,7 +503,7 @@ static int get_pipes(struct us_data *us, struct usb_interface *intf) /* Scan device's LUNs, registering a disk device for each LUN */ static int usb_stor_scan(struct usb_device *usbdev, struct us_data *us) { - struct device_d *dev = &usbdev->dev; + struct device *dev = &usbdev->dev; unsigned char lun; int num_devs = 0; @@ -459,7 +527,7 @@ static int usb_stor_scan(struct usb_device *usbdev, struct us_data *us) static int usb_stor_probe(struct usb_device *usbdev, const struct usb_device_id *id) { - struct device_d *dev = &usbdev->dev; + struct device *dev = &usbdev->dev; struct us_data *us; int result; int ifno; diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index cd4904f03b..ae16d7b60b 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -1,27 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Most of this source has been derived from the Linux and * U-Boot USB Mass Storage driver implementations. * * Adapted for barebox: * Copyright (c) 2011, AMK Drives & Controls Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * */ #ifndef _STORAGE_USB_H_ #define _STORAGE_USB_H_ -#include <usb/usb.h> +#include <linux/usb/usb.h> #include <block.h> #include <disks.h> #include <scsi.h> diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig new file mode 100644 index 0000000000..3b32a4e05a --- /dev/null +++ b/drivers/usb/typec/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-only + + +config TYPEC + prompt "Compile USB Type-C framework support" if COMPILE_TEST + bool + +config TYPEC_TUSB320 + tristate "TI TUSB320 Type-C port controller" + depends on I2C + select REGMAP_I2C + select TYPEC + help + Say Y or here if your system has a TI TUSB320 Type-C port controller. diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile new file mode 100644 index 0000000000..456b94afbf --- /dev/null +++ b/drivers/usb/typec/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_TYPEC) += class.o +obj-$(CONFIG_TYPEC_TUSB320) += tusb320.o diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c new file mode 100644 index 0000000000..7f498550f8 --- /dev/null +++ b/drivers/usb/typec/class.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * USB Type-C Connector Class + * + * Copyright (C) 2017, Intel Corporation + * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> + */ + +#include <module.h> +#include <driver.h> +#include <linux/usb/role.h> +#include <linux/usb/typec.h> +#include <linux/usb/typec_altmode.h> +#include <param.h> + +enum typec_param_accessory { + TYPEC_PARAM_ACCESSORY_NONE, + TYPEC_PARAM_ACCESSORY_AUDIO, + TYPEC_PARAM_ACCESSORY_DEBUG, +}; + +struct typec_port { + struct device dev; + const struct typec_operations *ops; + int pwr_role; /* enum typec_role */ + int usb_role; /* enum usb_role role */ + int accessory; /* enum typec_param_accessory */ +}; + +/** + * typec_set_pwr_role - Report power role change + * @port: The USB Type-C Port where the role was changed + * @role: The new data role + * + * This routine is used by the port drivers to report power role changes. + */ +void typec_set_pwr_role(struct typec_port *port, enum typec_role role) +{ + port->pwr_role = role; +} +EXPORT_SYMBOL_GPL(typec_set_pwr_role); + +static inline enum typec_param_accessory typec_mode_to_accessory(int mode) +{ + switch (mode) { + case TYPEC_MODE_AUDIO: + return TYPEC_PARAM_ACCESSORY_AUDIO; + case TYPEC_MODE_DEBUG: + return TYPEC_PARAM_ACCESSORY_DEBUG; + default: + return TYPEC_PARAM_ACCESSORY_NONE; + } +} + +/** + * typec_set_mode - Set mode of operation for USB Type-C connector + * @port: USB Type-C connector + * @mode: Accessory Mode, USB Operation or Safe State + * + * Configure @port for Accessory Mode @mode. This function will configure the + * muxes needed for @mode. + */ +int typec_set_mode(struct typec_port *port, int mode) +{ + port->accessory = typec_mode_to_accessory(mode); + return 0; +} +EXPORT_SYMBOL_GPL(typec_set_mode); + +/** + * typec_set_role - Set USB role for a Type-C connector + * @port: USB Type-C connector + * @role: USB role to be switched to + * + * Set USB role @role for @sw. This is equivalent to Linux + * usb_role_switch_set_role(); + */ +int typec_set_role(struct typec_port *port, enum usb_role role) +{ + port->usb_role = role; + return 0; +} +EXPORT_SYMBOL_GPL(typec_set_role); + +/** + * typec_get_drvdata - Return private driver data pointer + * @port: USB Type-C port + */ +void *typec_get_drvdata(struct typec_port *port) +{ + return port->dev.priv; +} +EXPORT_SYMBOL_GPL(typec_get_drvdata); + +static int typec_register_port_dev(struct typec_port *port, const char *name, int id) +{ + port->dev.id = id; + dev_set_name(&port->dev, name); + + return register_device(&port->dev); +} + +static const char * const usb_role_names[] = { + [USB_ROLE_NONE] = "none", + [USB_ROLE_HOST] = "host", + [USB_ROLE_DEVICE] = "device", +}; + +static const char * const pwr_role_names[] = { + [TYPEC_SINK] = "sink", + [TYPEC_SOURCE] = "source", +}; + +static const char * const accessory_names[] = { + [TYPEC_PARAM_ACCESSORY_NONE] = "none", + [TYPEC_PARAM_ACCESSORY_AUDIO] = "audio", /* analog */ + [TYPEC_PARAM_ACCESSORY_DEBUG] = "debug", +}; + +static int typec_param_update(struct param_d *p, void *priv) +{ + struct typec_port *port = priv; + + return port->ops->poll(port); +} + +/** + * typec_register_port - Register a USB Type-C Port + * @parent: Parent device + * @cap: Description of the port + * + * Registers a device for USB Type-C Port described in @cap. + * + * Returns handle to the port on success or ERR_PTR on failure. + */ +struct typec_port *typec_register_port(struct device *parent, + const struct typec_capability *cap) +{ + struct typec_port *port; + struct device *dev; + const char *alias; + int ret; + + port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) + return ERR_PTR(-ENOMEM); + + port->ops = cap->ops; + dev = &port->dev; + dev->parent = parent; + dev->of_node = cap->of_node; + dev->priv = cap->driver_data; + + alias = dev->of_node ? of_alias_get(dev->of_node) : NULL; + if (alias) + ret = typec_register_port_dev(port, alias, DEVICE_ID_SINGLE); + if (!alias || ret) + ret = typec_register_port_dev(port, "typec", DEVICE_ID_DYNAMIC); + + if (ret) + return ERR_PTR(ret); + + of_platform_device_dummy_drv(dev); + if (dev->of_node) + dev->of_node->dev = dev; + + dev_add_param_enum(dev, "usb_role", param_set_readonly, typec_param_update, + &port->usb_role, usb_role_names, + ARRAY_SIZE(usb_role_names), port); + dev_add_param_enum(dev, "pwr_role", param_set_readonly, typec_param_update, + &port->pwr_role, pwr_role_names, + ARRAY_SIZE(pwr_role_names), port); + dev_add_param_enum(dev, "accessory", param_set_readonly, typec_param_update, + &port->accessory, accessory_names, + ARRAY_SIZE(accessory_names), port); + + return port; +} +EXPORT_SYMBOL_GPL(typec_register_port); diff --git a/drivers/usb/typec/tusb320.c b/drivers/usb/typec/tusb320.c new file mode 100644 index 0000000000..90a846b0fb --- /dev/null +++ b/drivers/usb/typec/tusb320.c @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Based on the Linux driver: + * drivers/typec/typec-tusb320.c - TUSB320 typec driver + * + * Copyright (C) 2020 National Instruments Corporation + * Author: Michael Auchter <michael.auchter@ni.com> + */ + +#include <linux/bitfield.h> +#include <i2c/i2c.h> +#include <init.h> +#include <linux/kernel.h> +#include <linux/printk.h> +#include <linux/bitops.h> +#include <module.h> +#include <linux/regmap.h> +#include <linux/usb/typec.h> +#include <linux/usb/typec_altmode.h> + +#define TUSB320_REG8 0x8 +#define TUSB320_REG8_CURRENT_MODE_ADVERTISE GENMASK(7, 6) +#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_USB 0x0 +#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_15A 0x1 +#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_30A 0x2 +#define TUSB320_REG8_CURRENT_MODE_DETECT GENMASK(5, 4) +#define TUSB320_REG8_CURRENT_MODE_DETECT_DEF 0x0 +#define TUSB320_REG8_CURRENT_MODE_DETECT_MED 0x1 +#define TUSB320_REG8_CURRENT_MODE_DETECT_ACC 0x2 +#define TUSB320_REG8_CURRENT_MODE_DETECT_HI 0x3 +#define TUSB320_REG8_ACCESSORY_CONNECTED GENMASK(3, 1) +#define TUSB320_REG8_ACCESSORY_CONNECTED_NONE 0x0 +#define TUSB320_REG8_ACCESSORY_CONNECTED_AUDIO 0x4 +#define TUSB320_REG8_ACCESSORY_CONNECTED_ACHRG 0x5 +#define TUSB320_REG8_ACCESSORY_CONNECTED_DBGDFP 0x6 +#define TUSB320_REG8_ACCESSORY_CONNECTED_DBGUFP 0x7 +#define TUSB320_REG8_ACTIVE_CABLE_DETECTION BIT(0) + +#define TUSB320_REG9 0x9 +#define TUSB320_REG9_ATTACHED_STATE GENMASK(7, 6) +#define TUSB320_REG9_CABLE_DIRECTION BIT(5) +#define TUSB320_REG9_INTERRUPT_STATUS BIT(4) + +enum tusb320_attached_state { + TUSB320_ATTACHED_STATE_NONE, + TUSB320_ATTACHED_STATE_DFP, + TUSB320_ATTACHED_STATE_UFP, + TUSB320_ATTACHED_STATE_ACC, +}; + +struct tusb320_priv { + struct device *dev; + struct regmap *regmap; + struct typec_port *port; + struct typec_capability cap; +}; + +static int tusb320_typec_irq_handler(struct tusb320_priv *priv, u8 reg9) +{ + struct typec_port *port = priv->port; + int typec_mode; + enum typec_role pwr_role; + enum usb_role usb_role; + u8 state, accessory; + int ret, reg8; + + ret = regmap_read(priv->regmap, TUSB320_REG8, ®8); + if (ret) + return ret; + + state = FIELD_GET(TUSB320_REG9_ATTACHED_STATE, reg9); + accessory = FIELD_GET(TUSB320_REG8_ACCESSORY_CONNECTED, reg8); + + switch (state) { + case TUSB320_ATTACHED_STATE_DFP: + typec_mode = TYPEC_MODE_USB2; + usb_role = USB_ROLE_HOST; + pwr_role = TYPEC_SOURCE; + break; + case TUSB320_ATTACHED_STATE_UFP: + typec_mode = TYPEC_MODE_USB2; + usb_role = USB_ROLE_DEVICE; + pwr_role = TYPEC_SINK; + break; + case TUSB320_ATTACHED_STATE_ACC: + /* + * Accessory detected. For debug accessories, just make some + * qualified guesses as to the role for lack of a better option. + */ + if (accessory == TUSB320_REG8_ACCESSORY_CONNECTED_AUDIO || + accessory == TUSB320_REG8_ACCESSORY_CONNECTED_ACHRG) { + typec_mode = TYPEC_MODE_AUDIO; + usb_role = USB_ROLE_NONE; + pwr_role = TYPEC_SINK; + break; + } else if (accessory == + TUSB320_REG8_ACCESSORY_CONNECTED_DBGDFP) { + typec_mode = TYPEC_MODE_DEBUG; + pwr_role = TYPEC_SOURCE; + usb_role = USB_ROLE_HOST; + break; + } else if (accessory == + TUSB320_REG8_ACCESSORY_CONNECTED_DBGUFP) { + typec_mode = TYPEC_MODE_DEBUG; + pwr_role = TYPEC_SINK; + usb_role = USB_ROLE_DEVICE; + break; + } + + dev_warn(priv->dev, "unexpected ACCESSORY_CONNECTED state %d\n", + accessory); + + fallthrough; + default: + typec_mode = TYPEC_MODE_USB2; + usb_role = USB_ROLE_NONE; + pwr_role = TYPEC_SINK; + break; + } + + typec_set_pwr_role(port, pwr_role); + typec_set_mode(port, typec_mode); + typec_set_role(port, usb_role); + + return 0; +} + +static int tusb320_state_update_handler(struct tusb320_priv *priv, + bool force_update) +{ + unsigned int reg; + int ret; + + ret = regmap_read(priv->regmap, TUSB320_REG9, ®); + if (ret) + return ret; + + if (!force_update && !(reg & TUSB320_REG9_INTERRUPT_STATUS)) + return 0; + + ret = tusb320_typec_irq_handler(priv, reg); + + regmap_write(priv->regmap, TUSB320_REG9, reg); + + return ret; +} + +static irqreturn_t tusb320_irq_handler(struct typec_port *port) +{ + struct tusb320_priv *priv = typec_get_drvdata(port); + + return tusb320_state_update_handler(priv, false); +} + +static const struct typec_operations tusb320_typec_ops = { + .poll = tusb320_irq_handler, +}; + +static const struct regmap_config tusb320_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int tusb320_typec_probe(struct i2c_client *client, + struct tusb320_priv *priv) +{ + struct device_node *connector; + + connector = of_get_child_by_name(client->dev.of_node, "connector"); + + priv->cap.driver_data = priv; + priv->cap.ops = &tusb320_typec_ops; + priv->cap.of_node = connector; + + priv->port = typec_register_port(&client->dev, &priv->cap); + + return PTR_ERR_OR_ZERO(priv->port); +} + +static int tusb320_probe(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct tusb320_priv *priv; + int ret; + + priv = xzalloc(sizeof(*priv)); + + priv->dev = &client->dev; + i2c_set_clientdata(client, priv); + + priv->regmap = regmap_init_i2c(client, &tusb320_regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + ret = tusb320_typec_probe(client, priv); + if (ret) + return ret; + + /* update initial state */ + tusb320_state_update_handler(priv, true); + + return ret; +} + +static const struct of_device_id tusb320_typec_dt_match[] = { + { .compatible = "ti,tusb320" }, + { .compatible = "ti,tusb320l" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, tusb320_typec_dt_match); + +static struct driver tusb320_typec_driver = { + .name = "typec-tusb320", + .of_match_table = tusb320_typec_dt_match, + .probe = tusb320_probe, +}; +device_i2c_driver(tusb320_typec_driver); + +MODULE_AUTHOR("Michael Auchter <michael.auchter@ni.com>"); +MODULE_DESCRIPTION("TI TUSB320 Type-C driver"); +MODULE_LICENSE("GPL v2"); |