Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+453
View File
@@ -0,0 +1,453 @@
# SPDX-License-Identifier: GPL-2.0
#
# USB Gadget support on a system involves
# (a) a peripheral controller, and
# (b) the gadget driver using it.
#
# NOTE: Gadget support ** DOES NOT ** depend on host-side CONFIG_USB !!
#
# - Host systems (like PCs) need CONFIG_USB (with "A" jacks).
# - Peripherals (like PDAs) need CONFIG_USB_GADGET (with "B" jacks).
# - Some systems have both kinds of controllers.
#
# With help from a special transceiver and a "Mini-AB" jack, systems with
# both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG).
#
#
# USB Peripheral Controller Support
#
# The order here is alphabetical, except that integrated controllers go
# before discrete ones so they will be the initial/default value:
# - integrated/SOC controllers first
# - licensed IP used in both SOC and discrete versions
# - discrete ones (including all PCI-only controllers)
# - debug/dummy gadget+hcd is last.
#
menu "USB Peripheral Controller"
#
# Integrated controllers
#
config USB_AT91
tristate "Atmel AT91 USB Device Port"
depends on ARCH_AT91
depends on OF
help
Many Atmel AT91 processors (such as the AT91RM2000) have a
full speed USB Device Port with support for five configurable
endpoints (plus endpoint zero).
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "at91_udc" and force all
gadget drivers to also be dynamically linked.
config USB_LPC32XX
tristate "LPC32XX USB Peripheral Controller"
depends on ARCH_LPC32XX || COMPILE_TEST
depends on I2C
select USB_ISP1301
help
This option selects the USB device controller in the LPC32xx SoC.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "lpc32xx_udc" and force all
gadget drivers to also be dynamically linked.
config USB_ATMEL_USBA
tristate "Atmel USBA"
depends on ARCH_AT91
help
USBA is the integrated high-speed USB Device controller on some
AT91SAM9 and AT91CAP9 processors from Atmel.
The fifo_mode parameter is used to select endpoint allocation mode.
fifo_mode = 0 is used to let the driver autoconfigure the endpoints.
In this case, for ep1 2 banks are allocated if it works in isochronous
mode and only 1 bank otherwise. For the rest of the endpoints
only 1 bank is allocated.
fifo_mode = 1 is a generic maximum fifo size (1024 bytes) configuration
allowing the usage of ep1 - ep6
fifo_mode = 2 is a generic performance maximum fifo size (1024 bytes)
configuration allowing the usage of ep1 - ep3
fifo_mode = 3 is a balanced performance configuration allowing the
the usage of ep1 - ep8
config USB_BCM63XX_UDC
tristate "Broadcom BCM63xx Peripheral Controller"
depends on BCM63XX
help
Many Broadcom BCM63xx chipsets (such as the BCM6328) have a
high speed USB Device Port with support for four fixed endpoints
(plus endpoint zero).
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "bcm63xx_udc".
config USB_FSL_USB2
tristate "Freescale Highspeed USB DR Peripheral Controller"
depends on FSL_SOC
help
Some of Freescale PowerPC and i.MX processors have a High Speed
Dual-Role(DR) USB controller, which supports device mode.
The number of programmable endpoints is different through
SOC revisions.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "fsl_usb2_udc" and force
all gadget drivers to also be dynamically linked.
config USB_GR_UDC
tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver"
depends on HAS_DMA
help
Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB
VHDL IP core library.
config USB_OMAP
tristate "OMAP USB Device Controller"
depends on ARCH_OMAP1
help
Many Texas Instruments OMAP processors have flexible full
speed USB device controllers, with support for up to 30
endpoints (plus endpoint zero). This driver supports the
controller in the OMAP 1611, and should work with controllers
in other OMAP processors too, given minor tweaks.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "omap_udc" and force all
gadget drivers to also be dynamically linked.
config USB_PXA25X
tristate "PXA 25x or IXP 4xx"
depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX
depends on HAS_IOMEM
help
Intel's PXA 25x series XScale ARM-5TE processors include
an integrated full speed USB 1.1 device controller. The
controller in the IXP 4xx series is register-compatible.
It has fifteen fixed-function endpoints, as well as endpoint
zero (for control transfers).
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "pxa25x_udc" and force all
gadget drivers to also be dynamically linked.
# if there's only one gadget driver, using only two bulk endpoints,
# don't waste memory for the other endpoints
config USB_PXA25X_SMALL
depends on USB_PXA25X
bool
default n if USB_ETH_RNDIS
default y if USB_ZERO
default y if USB_ETH
default y if USB_G_SERIAL
config USB_R8A66597
tristate "Renesas R8A66597 USB Peripheral Controller"
depends on HAS_DMA
help
R8A66597 is a discrete USB host and peripheral controller chip that
supports both full and high speed USB 2.0 data transfers.
It has nine configurable endpoints, and endpoint zero.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "r8a66597_udc" and force all
gadget drivers to also be dynamically linked.
config USB_RENESAS_USBHS_UDC
tristate 'Renesas USBHS controller'
depends on USB_RENESAS_USBHS
help
Renesas USBHS is a discrete USB host and peripheral controller chip
that supports both full and high speed USB 2.0 data transfers.
It has nine or more configurable endpoints, and endpoint zero.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "renesas_usbhs" and force all
gadget drivers to also be dynamically linked.
config USB_RZV2M_USB3DRD
tristate 'Renesas USB3.1 DRD controller'
depends on ARCH_R9A09G011 || COMPILE_TEST
help
Renesas USB3.1 DRD controller is a USB DRD controller
that supports both host and device switching.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "rzv2m_usb3drd".
config USB_RENESAS_USB3
tristate 'Renesas USB3.0 Peripheral controller'
depends on ARCH_RENESAS || COMPILE_TEST
depends on USB_RZV2M_USB3DRD || !USB_RZV2M_USB3DRD
depends on EXTCON
select USB_ROLE_SWITCH
help
Renesas USB3.0 Peripheral controller is a USB peripheral controller
that supports super, high, and full speed USB 3.0 data transfers.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "renesas_usb3" and force all
gadget drivers to also be dynamically linked.
config USB_RENESAS_USBF
tristate 'Renesas USB Function controller'
depends on ARCH_RENESAS || COMPILE_TEST
help
Renesas USB Function controller is a USB peripheral controller
available on RZ/N1 Renesas SoCs.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "renesas_usbf" and force all
gadget drivers to also be dynamically linked.
config USB_PXA27X
tristate "PXA 27x"
depends on HAS_IOMEM
help
Intel's PXA 27x series XScale ARM v5TE processors include
an integrated full speed USB 1.1 device controller.
It has up to 23 endpoints, as well as endpoint zero (for
control transfers).
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "pxa27x_udc" and force all
gadget drivers to also be dynamically linked.
config USB_SNP_CORE
depends on (USB_AMD5536UDC || USB_SNP_UDC_PLAT)
depends on HAS_DMA
tristate
help
This enables core driver support for Synopsys USB 2.0 Device
controller.
This will be enabled when PCI or Platform driver for this UDC is
selected. Currently, this will be enabled by USB_SNP_UDC_PLAT or
USB_AMD5536UDC options.
This IP is different to the High Speed OTG IP that can be enabled
by selecting USB_DWC2 or USB_DWC3 options.
config USB_SNP_UDC_PLAT
tristate "Synopsys USB 2.0 Device controller"
depends on USB_GADGET && OF && HAS_DMA
depends on EXTCON || EXTCON=n
select USB_SNP_CORE
default ARCH_BCM_IPROC
help
This adds Platform Device support for Synopsys Designware core
AHB subsystem USB2.0 Device Controller (UDC).
This driver works with UDCs integrated into Broadcom's Northstar2
and Cygnus SoCs.
If unsure, say N.
#
# Controllers available in both integrated and discrete versions
#
config USB_M66592
tristate "Renesas M66592 USB Peripheral Controller"
depends on HAS_IOMEM
help
M66592 is a discrete USB peripheral controller chip that
supports both full and high speed USB 2.0 data transfers.
It has seven configurable endpoints, and endpoint zero.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "m66592_udc" and force all
gadget drivers to also be dynamically linked.
source "drivers/usb/gadget/udc/bdc/Kconfig"
#
# Controllers available only in discrete form (and all PCI controllers)
#
config USB_AMD5536UDC
tristate "AMD5536 UDC"
depends on USB_PCI && HAS_DMA
select USB_SNP_CORE
help
The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge.
It is a USB Highspeed DMA capable USB device controller. Beside ep0
it provides 4 IN and 4 OUT endpoints (bulk or interrupt type).
The UDC port supports OTG operation, and may be used as a host port
if it's not being used to implement peripheral or OTG roles.
This UDC is based on Synopsys USB device controller IP and selects
CONFIG_USB_SNP_CORE option to build the core driver.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "amd5536udc" and force all
gadget drivers to also be dynamically linked.
config USB_FSL_QE
tristate "Freescale QE/CPM USB Device Controller"
depends on FSL_SOC && (QUICC_ENGINE || CPM)
depends on !64BIT || BROKEN
help
Some of Freescale PowerPC processors have a Full Speed
QE/CPM2 USB controller, which support device mode with 4
programmable endpoints. This driver supports the
controller in the MPC8360 and MPC8272, and should work with
controllers having QE or CPM2, given minor tweaks.
Set CONFIG_USB_GADGET to "m" to build this driver as a
dynamically linked module called "fsl_qe_udc".
config USB_NET2280
tristate "NetChip NET228x / PLX USB3x8x"
depends on USB_PCI
help
NetChip 2280 / 2282 is a PCI based USB peripheral controller which
supports both full and high speed USB 2.0 data transfers.
It has six configurable endpoints, as well as endpoint zero
(for control transfers) and several endpoints with dedicated
functions.
PLX 2380 is a PCIe version of the PLX 2380.
PLX 3380 / 3382 is a PCIe based USB peripheral controller which
supports full, high speed USB 2.0 and super speed USB 3.0
data transfers.
It has eight configurable endpoints, as well as endpoint zero
(for control transfers) and several endpoints with dedicated
functions.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "net2280" and force all
gadget drivers to also be dynamically linked.
config USB_GOKU
tristate "Toshiba TC86C001 'Goku-S'"
depends on USB_PCI
help
The Toshiba TC86C001 is a PCI device which includes controllers
for full speed USB devices, IDE, I2C, SIO, plus a USB host (OHCI).
The device controller has three configurable (bulk or interrupt)
endpoints, plus endpoint zero (for control transfers).
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "goku_udc" and to force all
gadget drivers to also be dynamically linked.
config USB_EG20T
tristate "Intel QUARK X1000/EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC"
depends on USB_PCI
help
This is a USB device driver for EG20T PCH.
EG20T PCH is the platform controller hub that is used in Intel's
general embedded platform. EG20T PCH has USB device interface.
Using this interface, it is able to access system devices connected
to USB device.
This driver enables USB device function.
USB device is a USB peripheral controller which
supports both full and high speed USB 2.0 data transfers.
This driver supports both control transfer and bulk transfer modes.
This driver dose not support interrupt transfer or isochronous
transfer modes.
This driver also can be used for LAPIS Semiconductor's ML7213 which is
for IVI(In-Vehicle Infotainment) use.
ML7831 is for general purpose use.
ML7213/ML7831 is companion chip for Intel Atom E6xx series.
ML7213/ML7831 is completely compatible for Intel EG20T PCH.
This driver can be used with Intel's Quark X1000 SOC platform
config USB_GADGET_XILINX
tristate "Xilinx USB Driver"
depends on HAS_DMA
depends on OF
help
USB peripheral controller driver for Xilinx USB2 device.
Xilinx USB2 device is a soft IP which supports both full
and high speed USB 2.0 data transfers. It has seven configurable
endpoints(bulk or interrupt or isochronous), as well as
endpoint zero(for control transfers).
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "udc-xilinx" and force all
gadget drivers to also be dynamically linked.
config USB_MAX3420_UDC
tristate "MAX3420 (USB-over-SPI) support"
depends on SPI
help
The Maxim MAX3420 chip supports USB2.0 full-speed peripheral mode.
The MAX3420 is run by SPI interface, and hence the dependency.
To compile this driver as a module, choose M here: the module will
be called max3420_udc
config USB_TEGRA_XUDC
tristate "NVIDIA Tegra Superspeed USB 3.0 Device Controller"
depends on ARCH_TEGRA || COMPILE_TEST
depends on PHY_TEGRA_XUSB
help
Enables NVIDIA Tegra USB 3.0 device mode controller driver.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "tegra_xudc" and force all
gadget drivers to also be dynamically linked.
config USB_ASPEED_UDC
tristate "Aspeed UDC driver support"
depends on ARCH_ASPEED || COMPILE_TEST
depends on USB_LIBCOMPOSITE
help
Enables Aspeed USB2.0 Device Controller driver for AST260x
family SoCs. The controller supports 1 control endpoint and
4 programmable endpoints.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "aspeed_udc" and force all
gadget drivers to also be dynamically linked.
source "drivers/usb/gadget/udc/aspeed-vhub/Kconfig"
source "drivers/usb/gadget/udc/cdns2/Kconfig"
#
# LAST -- dummy/emulated controller
#
config USB_DUMMY_HCD
tristate "Dummy HCD (DEVELOPMENT)"
depends on USB=y || (USB=m && USB_GADGET=m)
help
This host controller driver emulates USB, looping all data transfer
requests back to a USB "gadget driver" in the same host. The host
side is the controller; the gadget side is the device. Gadget drivers
can be high, full, or low speed; and they have access to endpoints
like those from NET2280, PXA2xx, or SA1100 hardware.
This may help in some stages of creating a driver to embed in a
Linux device, since it lets you debug several parts of the gadget
driver without its hardware or drivers being involved.
Since such a gadget side driver needs to interoperate with a host
side Linux-USB device driver, this may help to debug both sides
of a USB protocol stack.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "dummy_hcd" and force all
gadget drivers to also be dynamically linked.
# NOTE: Please keep dummy_hcd LAST so that "real hardware" appears
# first and will be selected by default.
endmenu
@@ -0,0 +1,40 @@
# SPDX-License-Identifier: GPL-2.0
# define_trace.h needs to know how to find our header
CFLAGS_trace.o := -I$(src)
udc-core-y := core.o trace.o
#
# USB peripheral controller drivers
#
obj-$(CONFIG_USB_GADGET) += udc-core.o
obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
obj-$(CONFIG_USB_NET2280) += net2280.o
obj-$(CONFIG_USB_SNP_CORE) += snps_udc_core.o
obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc_pci.o
obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o
obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o
obj-$(CONFIG_USB_GOKU) += goku_udc.o
obj-$(CONFIG_USB_OMAP) += omap_udc.o
obj-$(CONFIG_USB_AT91) += at91_udc.o
obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
obj-$(CONFIG_USB_BCM63XX_UDC) += bcm63xx_udc.o
obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
fsl_usb2_udc-y := fsl_udc_core.o
obj-$(CONFIG_USB_TEGRA_XUDC) += tegra-xudc.o
obj-$(CONFIG_USB_M66592) += m66592-udc.o
obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o
obj-$(CONFIG_USB_RENESAS_USB3) += renesas_usb3.o
obj-$(CONFIG_USB_RZV2M_USB3DRD) += rzv2m_usb3drd.o
obj-$(CONFIG_USB_RENESAS_USBF) += renesas_usbf.o
obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
obj-$(CONFIG_USB_LPC32XX) += lpc32xx_udc.o
obj-$(CONFIG_USB_EG20T) += pch_udc.o
obj-$(CONFIG_USB_GR_UDC) += gr_udc.o
obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o
obj-$(CONFIG_USB_SNP_UDC_PLAT) += snps_udc_plat.o
obj-$(CONFIG_USB_ASPEED_VHUB) += aspeed-vhub/
obj-$(CONFIG_USB_ASPEED_UDC) += aspeed_udc.o
obj-$(CONFIG_USB_BDC_UDC) += bdc/
obj-$(CONFIG_USB_MAX3420_UDC) += max3420_udc.o
obj-$(CONFIG_USB_CDNS2_UDC) += cdns2/
@@ -0,0 +1,661 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* amd5536.h -- header for AMD 5536 UDC high/full speed USB device controller
*
* Copyright (C) 2007 AMD (https://www.amd.com)
* Author: Thomas Dahlmann
*/
#ifndef AMD5536UDC_H
#define AMD5536UDC_H
/* debug control */
/* #define UDC_VERBOSE */
#include <linux/extcon.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
/* various constants */
#define UDC_RDE_TIMER_SECONDS 1
#define UDC_RDE_TIMER_DIV 10
#define UDC_POLLSTALL_TIMER_USECONDS 500
/* Hs AMD5536 chip rev. */
#define UDC_HSA0_REV 1
#define UDC_HSB1_REV 2
/* Broadcom chip rev. */
#define UDC_BCM_REV 10
/*
* SETUP usb commands
* needed, because some SETUP's are handled in hw, but must be passed to
* gadget driver above
* SET_CONFIG
*/
#define UDC_SETCONFIG_DWORD0 0x00000900
#define UDC_SETCONFIG_DWORD0_VALUE_MASK 0xffff0000
#define UDC_SETCONFIG_DWORD0_VALUE_OFS 16
#define UDC_SETCONFIG_DWORD1 0x00000000
/* SET_INTERFACE */
#define UDC_SETINTF_DWORD0 0x00000b00
#define UDC_SETINTF_DWORD0_ALT_MASK 0xffff0000
#define UDC_SETINTF_DWORD0_ALT_OFS 16
#define UDC_SETINTF_DWORD1 0x00000000
#define UDC_SETINTF_DWORD1_INTF_MASK 0x0000ffff
#define UDC_SETINTF_DWORD1_INTF_OFS 0
/* Mass storage reset */
#define UDC_MSCRES_DWORD0 0x0000ff21
#define UDC_MSCRES_DWORD1 0x00000000
/* Global CSR's -------------------------------------------------------------*/
#define UDC_CSR_ADDR 0x500
/* EP NE bits */
/* EP number */
#define UDC_CSR_NE_NUM_MASK 0x0000000f
#define UDC_CSR_NE_NUM_OFS 0
/* EP direction */
#define UDC_CSR_NE_DIR_MASK 0x00000010
#define UDC_CSR_NE_DIR_OFS 4
/* EP type */
#define UDC_CSR_NE_TYPE_MASK 0x00000060
#define UDC_CSR_NE_TYPE_OFS 5
/* EP config number */
#define UDC_CSR_NE_CFG_MASK 0x00000780
#define UDC_CSR_NE_CFG_OFS 7
/* EP interface number */
#define UDC_CSR_NE_INTF_MASK 0x00007800
#define UDC_CSR_NE_INTF_OFS 11
/* EP alt setting */
#define UDC_CSR_NE_ALT_MASK 0x00078000
#define UDC_CSR_NE_ALT_OFS 15
/* max pkt */
#define UDC_CSR_NE_MAX_PKT_MASK 0x3ff80000
#define UDC_CSR_NE_MAX_PKT_OFS 19
/* Device Config Register ---------------------------------------------------*/
#define UDC_DEVCFG_ADDR 0x400
#define UDC_DEVCFG_SOFTRESET 31
#define UDC_DEVCFG_HNPSFEN 30
#define UDC_DEVCFG_DMARST 29
#define UDC_DEVCFG_SET_DESC 18
#define UDC_DEVCFG_CSR_PRG 17
#define UDC_DEVCFG_STATUS 7
#define UDC_DEVCFG_DIR 6
#define UDC_DEVCFG_PI 5
#define UDC_DEVCFG_SS 4
#define UDC_DEVCFG_SP 3
#define UDC_DEVCFG_RWKP 2
#define UDC_DEVCFG_SPD_MASK 0x3
#define UDC_DEVCFG_SPD_OFS 0
#define UDC_DEVCFG_SPD_HS 0x0
#define UDC_DEVCFG_SPD_FS 0x1
#define UDC_DEVCFG_SPD_LS 0x2
/*#define UDC_DEVCFG_SPD_FS 0x3*/
/* Device Control Register --------------------------------------------------*/
#define UDC_DEVCTL_ADDR 0x404
#define UDC_DEVCTL_THLEN_MASK 0xff000000
#define UDC_DEVCTL_THLEN_OFS 24
#define UDC_DEVCTL_BRLEN_MASK 0x00ff0000
#define UDC_DEVCTL_BRLEN_OFS 16
#define UDC_DEVCTL_SRX_FLUSH 14
#define UDC_DEVCTL_CSR_DONE 13
#define UDC_DEVCTL_DEVNAK 12
#define UDC_DEVCTL_SD 10
#define UDC_DEVCTL_MODE 9
#define UDC_DEVCTL_BREN 8
#define UDC_DEVCTL_THE 7
#define UDC_DEVCTL_BF 6
#define UDC_DEVCTL_BE 5
#define UDC_DEVCTL_DU 4
#define UDC_DEVCTL_TDE 3
#define UDC_DEVCTL_RDE 2
#define UDC_DEVCTL_RES 0
/* Device Status Register ---------------------------------------------------*/
#define UDC_DEVSTS_ADDR 0x408
#define UDC_DEVSTS_TS_MASK 0xfffc0000
#define UDC_DEVSTS_TS_OFS 18
#define UDC_DEVSTS_SESSVLD 17
#define UDC_DEVSTS_PHY_ERROR 16
#define UDC_DEVSTS_RXFIFO_EMPTY 15
#define UDC_DEVSTS_ENUM_SPEED_MASK 0x00006000
#define UDC_DEVSTS_ENUM_SPEED_OFS 13
#define UDC_DEVSTS_ENUM_SPEED_FULL 1
#define UDC_DEVSTS_ENUM_SPEED_HIGH 0
#define UDC_DEVSTS_SUSP 12
#define UDC_DEVSTS_ALT_MASK 0x00000f00
#define UDC_DEVSTS_ALT_OFS 8
#define UDC_DEVSTS_INTF_MASK 0x000000f0
#define UDC_DEVSTS_INTF_OFS 4
#define UDC_DEVSTS_CFG_MASK 0x0000000f
#define UDC_DEVSTS_CFG_OFS 0
/* Device Interrupt Register ------------------------------------------------*/
#define UDC_DEVINT_ADDR 0x40c
#define UDC_DEVINT_SVC 7
#define UDC_DEVINT_ENUM 6
#define UDC_DEVINT_SOF 5
#define UDC_DEVINT_US 4
#define UDC_DEVINT_UR 3
#define UDC_DEVINT_ES 2
#define UDC_DEVINT_SI 1
#define UDC_DEVINT_SC 0
/* Device Interrupt Mask Register -------------------------------------------*/
#define UDC_DEVINT_MSK_ADDR 0x410
#define UDC_DEVINT_MSK 0x7f
/* Endpoint Interrupt Register ----------------------------------------------*/
#define UDC_EPINT_ADDR 0x414
#define UDC_EPINT_OUT_MASK 0xffff0000
#define UDC_EPINT_OUT_OFS 16
#define UDC_EPINT_IN_MASK 0x0000ffff
#define UDC_EPINT_IN_OFS 0
#define UDC_EPINT_IN_EP0 0
#define UDC_EPINT_IN_EP1 1
#define UDC_EPINT_IN_EP2 2
#define UDC_EPINT_IN_EP3 3
#define UDC_EPINT_OUT_EP0 16
#define UDC_EPINT_OUT_EP1 17
#define UDC_EPINT_OUT_EP2 18
#define UDC_EPINT_OUT_EP3 19
#define UDC_EPINT_EP0_ENABLE_MSK 0x001e001e
/* Endpoint Interrupt Mask Register -----------------------------------------*/
#define UDC_EPINT_MSK_ADDR 0x418
#define UDC_EPINT_OUT_MSK_MASK 0xffff0000
#define UDC_EPINT_OUT_MSK_OFS 16
#define UDC_EPINT_IN_MSK_MASK 0x0000ffff
#define UDC_EPINT_IN_MSK_OFS 0
#define UDC_EPINT_MSK_DISABLE_ALL 0xffffffff
/* mask non-EP0 endpoints */
#define UDC_EPDATAINT_MSK_DISABLE 0xfffefffe
/* mask all dev interrupts */
#define UDC_DEV_MSK_DISABLE 0x7f
/* Endpoint-specific CSR's --------------------------------------------------*/
#define UDC_EPREGS_ADDR 0x0
#define UDC_EPIN_REGS_ADDR 0x0
#define UDC_EPOUT_REGS_ADDR 0x200
#define UDC_EPCTL_ADDR 0x0
#define UDC_EPCTL_RRDY 9
#define UDC_EPCTL_CNAK 8
#define UDC_EPCTL_SNAK 7
#define UDC_EPCTL_NAK 6
#define UDC_EPCTL_ET_MASK 0x00000030
#define UDC_EPCTL_ET_OFS 4
#define UDC_EPCTL_ET_CONTROL 0
#define UDC_EPCTL_ET_ISO 1
#define UDC_EPCTL_ET_BULK 2
#define UDC_EPCTL_ET_INTERRUPT 3
#define UDC_EPCTL_P 3
#define UDC_EPCTL_SN 2
#define UDC_EPCTL_F 1
#define UDC_EPCTL_S 0
/* Endpoint Status Registers ------------------------------------------------*/
#define UDC_EPSTS_ADDR 0x4
#define UDC_EPSTS_RX_PKT_SIZE_MASK 0x007ff800
#define UDC_EPSTS_RX_PKT_SIZE_OFS 11
#define UDC_EPSTS_TDC 10
#define UDC_EPSTS_HE 9
#define UDC_EPSTS_BNA 7
#define UDC_EPSTS_IN 6
#define UDC_EPSTS_OUT_MASK 0x00000030
#define UDC_EPSTS_OUT_OFS 4
#define UDC_EPSTS_OUT_DATA 1
#define UDC_EPSTS_OUT_DATA_CLEAR 0x10
#define UDC_EPSTS_OUT_SETUP 2
#define UDC_EPSTS_OUT_SETUP_CLEAR 0x20
#define UDC_EPSTS_OUT_CLEAR 0x30
/* Endpoint Buffer Size IN/ Receive Packet Frame Number OUT Registers ------*/
#define UDC_EPIN_BUFF_SIZE_ADDR 0x8
#define UDC_EPOUT_FRAME_NUMBER_ADDR 0x8
#define UDC_EPIN_BUFF_SIZE_MASK 0x0000ffff
#define UDC_EPIN_BUFF_SIZE_OFS 0
/* EP0in txfifo = 128 bytes*/
#define UDC_EPIN0_BUFF_SIZE 32
/* EP0in fullspeed txfifo = 128 bytes*/
#define UDC_FS_EPIN0_BUFF_SIZE 32
/* fifo size mult = fifo size / max packet */
#define UDC_EPIN_BUFF_SIZE_MULT 2
/* EPin data fifo size = 1024 bytes DOUBLE BUFFERING */
#define UDC_EPIN_BUFF_SIZE 256
/* EPin small INT data fifo size = 128 bytes */
#define UDC_EPIN_SMALLINT_BUFF_SIZE 32
/* EPin fullspeed data fifo size = 128 bytes DOUBLE BUFFERING */
#define UDC_FS_EPIN_BUFF_SIZE 32
#define UDC_EPOUT_FRAME_NUMBER_MASK 0x0000ffff
#define UDC_EPOUT_FRAME_NUMBER_OFS 0
/* Endpoint Buffer Size OUT/Max Packet Size Registers -----------------------*/
#define UDC_EPOUT_BUFF_SIZE_ADDR 0x0c
#define UDC_EP_MAX_PKT_SIZE_ADDR 0x0c
#define UDC_EPOUT_BUFF_SIZE_MASK 0xffff0000
#define UDC_EPOUT_BUFF_SIZE_OFS 16
#define UDC_EP_MAX_PKT_SIZE_MASK 0x0000ffff
#define UDC_EP_MAX_PKT_SIZE_OFS 0
/* EP0in max packet size = 64 bytes */
#define UDC_EP0IN_MAX_PKT_SIZE 64
/* EP0out max packet size = 64 bytes */
#define UDC_EP0OUT_MAX_PKT_SIZE 64
/* EP0in fullspeed max packet size = 64 bytes */
#define UDC_FS_EP0IN_MAX_PKT_SIZE 64
/* EP0out fullspeed max packet size = 64 bytes */
#define UDC_FS_EP0OUT_MAX_PKT_SIZE 64
/*
* Endpoint dma descriptors ------------------------------------------------
*
* Setup data, Status dword
*/
#define UDC_DMA_STP_STS_CFG_MASK 0x0fff0000
#define UDC_DMA_STP_STS_CFG_OFS 16
#define UDC_DMA_STP_STS_CFG_ALT_MASK 0x000f0000
#define UDC_DMA_STP_STS_CFG_ALT_OFS 16
#define UDC_DMA_STP_STS_CFG_INTF_MASK 0x00f00000
#define UDC_DMA_STP_STS_CFG_INTF_OFS 20
#define UDC_DMA_STP_STS_CFG_NUM_MASK 0x0f000000
#define UDC_DMA_STP_STS_CFG_NUM_OFS 24
#define UDC_DMA_STP_STS_RX_MASK 0x30000000
#define UDC_DMA_STP_STS_RX_OFS 28
#define UDC_DMA_STP_STS_BS_MASK 0xc0000000
#define UDC_DMA_STP_STS_BS_OFS 30
#define UDC_DMA_STP_STS_BS_HOST_READY 0
#define UDC_DMA_STP_STS_BS_DMA_BUSY 1
#define UDC_DMA_STP_STS_BS_DMA_DONE 2
#define UDC_DMA_STP_STS_BS_HOST_BUSY 3
/* IN data, Status dword */
#define UDC_DMA_IN_STS_TXBYTES_MASK 0x0000ffff
#define UDC_DMA_IN_STS_TXBYTES_OFS 0
#define UDC_DMA_IN_STS_FRAMENUM_MASK 0x07ff0000
#define UDC_DMA_IN_STS_FRAMENUM_OFS 0
#define UDC_DMA_IN_STS_L 27
#define UDC_DMA_IN_STS_TX_MASK 0x30000000
#define UDC_DMA_IN_STS_TX_OFS 28
#define UDC_DMA_IN_STS_BS_MASK 0xc0000000
#define UDC_DMA_IN_STS_BS_OFS 30
#define UDC_DMA_IN_STS_BS_HOST_READY 0
#define UDC_DMA_IN_STS_BS_DMA_BUSY 1
#define UDC_DMA_IN_STS_BS_DMA_DONE 2
#define UDC_DMA_IN_STS_BS_HOST_BUSY 3
/* OUT data, Status dword */
#define UDC_DMA_OUT_STS_RXBYTES_MASK 0x0000ffff
#define UDC_DMA_OUT_STS_RXBYTES_OFS 0
#define UDC_DMA_OUT_STS_FRAMENUM_MASK 0x07ff0000
#define UDC_DMA_OUT_STS_FRAMENUM_OFS 0
#define UDC_DMA_OUT_STS_L 27
#define UDC_DMA_OUT_STS_RX_MASK 0x30000000
#define UDC_DMA_OUT_STS_RX_OFS 28
#define UDC_DMA_OUT_STS_BS_MASK 0xc0000000
#define UDC_DMA_OUT_STS_BS_OFS 30
#define UDC_DMA_OUT_STS_BS_HOST_READY 0
#define UDC_DMA_OUT_STS_BS_DMA_BUSY 1
#define UDC_DMA_OUT_STS_BS_DMA_DONE 2
#define UDC_DMA_OUT_STS_BS_HOST_BUSY 3
/* max ep0in packet */
#define UDC_EP0IN_MAXPACKET 1000
/* max dma packet */
#define UDC_DMA_MAXPACKET 65536
/* un-usable DMA address */
#define DMA_DONT_USE (~(dma_addr_t) 0 )
/* other Endpoint register addresses and values-----------------------------*/
#define UDC_EP_SUBPTR_ADDR 0x10
#define UDC_EP_DESPTR_ADDR 0x14
#define UDC_EP_WRITE_CONFIRM_ADDR 0x1c
/* EP number as layouted in AHB space */
#define UDC_EP_NUM 32
#define UDC_EPIN_NUM 16
#define UDC_EPIN_NUM_USED 5
#define UDC_EPOUT_NUM 16
/* EP number of EP's really used = EP0 + 8 data EP's */
#define UDC_USED_EP_NUM 9
/* UDC CSR regs are aligned but AHB regs not - offset for OUT EP's */
#define UDC_CSR_EP_OUT_IX_OFS 12
#define UDC_EP0OUT_IX 16
#define UDC_EP0IN_IX 0
/* Rx fifo address and size = 1k -------------------------------------------*/
#define UDC_RXFIFO_ADDR 0x800
#define UDC_RXFIFO_SIZE 0x400
/* Tx fifo address and size = 1.5k -----------------------------------------*/
#define UDC_TXFIFO_ADDR 0xc00
#define UDC_TXFIFO_SIZE 0x600
/* default data endpoints --------------------------------------------------*/
#define UDC_EPIN_STATUS_IX 1
#define UDC_EPIN_IX 2
#define UDC_EPOUT_IX 18
/* general constants -------------------------------------------------------*/
#define UDC_DWORD_BYTES 4
#define UDC_BITS_PER_BYTE_SHIFT 3
#define UDC_BYTE_MASK 0xff
#define UDC_BITS_PER_BYTE 8
/*---------------------------------------------------------------------------*/
/* UDC CSR's */
struct udc_csrs {
/* sca - setup command address */
u32 sca;
/* ep ne's */
u32 ne[UDC_USED_EP_NUM];
} __attribute__ ((packed));
/* AHB subsystem CSR registers */
struct udc_regs {
/* device configuration */
u32 cfg;
/* device control */
u32 ctl;
/* device status */
u32 sts;
/* device interrupt */
u32 irqsts;
/* device interrupt mask */
u32 irqmsk;
/* endpoint interrupt */
u32 ep_irqsts;
/* endpoint interrupt mask */
u32 ep_irqmsk;
} __attribute__ ((packed));
/* endpoint specific registers */
struct udc_ep_regs {
/* endpoint control */
u32 ctl;
/* endpoint status */
u32 sts;
/* endpoint buffer size in/ receive packet frame number out */
u32 bufin_framenum;
/* endpoint buffer size out/max packet size */
u32 bufout_maxpkt;
/* endpoint setup buffer pointer */
u32 subptr;
/* endpoint data descriptor pointer */
u32 desptr;
/* reserved */
u32 reserved;
/* write/read confirmation */
u32 confirm;
} __attribute__ ((packed));
/* control data DMA desc */
struct udc_stp_dma {
/* status quadlet */
u32 status;
/* reserved */
u32 _reserved;
/* first setup word */
u32 data12;
/* second setup word */
u32 data34;
} __attribute__ ((aligned (16)));
/* normal data DMA desc */
struct udc_data_dma {
/* status quadlet */
u32 status;
/* reserved */
u32 _reserved;
/* buffer pointer */
u32 bufptr;
/* next descriptor pointer */
u32 next;
} __attribute__ ((aligned (16)));
/* request packet */
struct udc_request {
/* embedded gadget ep */
struct usb_request req;
/* flags */
unsigned dma_going : 1,
dma_done : 1;
/* phys. address */
dma_addr_t td_phys;
/* first dma desc. of chain */
struct udc_data_dma *td_data;
/* last dma desc. of chain */
struct udc_data_dma *td_data_last;
struct list_head queue;
/* chain length */
unsigned chain_len;
};
/* UDC specific endpoint parameters */
struct udc_ep {
struct usb_ep ep;
struct udc_ep_regs __iomem *regs;
u32 __iomem *txfifo;
u32 __iomem *dma;
dma_addr_t td_phys;
dma_addr_t td_stp_dma;
struct udc_stp_dma *td_stp;
struct udc_data_dma *td;
/* temp request */
struct udc_request *req;
unsigned req_used;
unsigned req_completed;
/* dummy DMA desc for BNA dummy */
struct udc_request *bna_dummy_req;
unsigned bna_occurred;
/* NAK state */
unsigned naking;
struct udc *dev;
/* queue for requests */
struct list_head queue;
unsigned halted;
unsigned cancel_transfer;
unsigned num : 5,
fifo_depth : 14,
in : 1;
};
/* device struct */
struct udc {
struct usb_gadget gadget;
spinlock_t lock; /* protects all state */
/* all endpoints */
struct udc_ep ep[UDC_EP_NUM];
struct usb_gadget_driver *driver;
/* operational flags */
unsigned stall_ep0in : 1,
waiting_zlp_ack_ep0in : 1,
set_cfg_not_acked : 1,
data_ep_enabled : 1,
data_ep_queued : 1,
sys_suspended : 1,
connected;
u16 chiprev;
/* registers */
struct pci_dev *pdev;
struct udc_csrs __iomem *csr;
struct udc_regs __iomem *regs;
struct udc_ep_regs __iomem *ep_regs;
u32 __iomem *rxfifo;
u32 __iomem *txfifo;
/* DMA desc pools */
struct dma_pool *data_requests;
struct dma_pool *stp_requests;
/* device data */
unsigned long phys_addr;
void __iomem *virt_addr;
unsigned irq;
/* states */
u16 cur_config;
u16 cur_intf;
u16 cur_alt;
/* for platform device and extcon support */
struct device *dev;
struct phy *udc_phy;
struct extcon_dev *edev;
struct extcon_specific_cable_nb extcon_nb;
struct notifier_block nb;
struct delayed_work drd_work;
u32 conn_type;
};
#define to_amd5536_udc(g) (container_of((g), struct udc, gadget))
/* setup request data */
union udc_setup_data {
u32 data[2];
struct usb_ctrlrequest request;
};
/* Function declarations */
int udc_enable_dev_setup_interrupts(struct udc *dev);
int udc_mask_unused_interrupts(struct udc *dev);
irqreturn_t udc_irq(int irq, void *pdev);
void gadget_release(struct device *pdev);
void empty_req_queue(struct udc_ep *ep);
void udc_basic_init(struct udc *dev);
void free_dma_pools(struct udc *dev);
int init_dma_pools(struct udc *dev);
void udc_remove(struct udc *dev);
int udc_probe(struct udc *dev);
/* DMA usage flag */
static bool use_dma = 1;
/* packet per buffer dma */
static bool use_dma_ppb = 1;
/* with per descr. update */
static bool use_dma_ppb_du;
/* full speed only mode */
static bool use_fullspeed;
/* module parameters */
module_param(use_dma, bool, S_IRUGO);
MODULE_PARM_DESC(use_dma, "true for DMA");
module_param(use_dma_ppb, bool, S_IRUGO);
MODULE_PARM_DESC(use_dma_ppb, "true for DMA in packet per buffer mode");
module_param(use_dma_ppb_du, bool, S_IRUGO);
MODULE_PARM_DESC(use_dma_ppb_du,
"true for DMA in packet per buffer mode with descriptor update");
module_param(use_fullspeed, bool, S_IRUGO);
MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only");
/*
*---------------------------------------------------------------------------
* SET and GET bitfields in u32 values
* via constants for mask/offset:
* <bit_field_stub_name> is the text between
* UDC_ and _MASK|_OFS of appropriate
* constant
*
* set bitfield value in u32 u32Val
*/
#define AMD_ADDBITS(u32Val, bitfield_val, bitfield_stub_name) \
(((u32Val) & (((u32) ~((u32) bitfield_stub_name##_MASK)))) \
| (((bitfield_val) << ((u32) bitfield_stub_name##_OFS)) \
& ((u32) bitfield_stub_name##_MASK)))
/*
* set bitfield value in zero-initialized u32 u32Val
* => bitfield bits in u32Val are all zero
*/
#define AMD_INIT_SETBITS(u32Val, bitfield_val, bitfield_stub_name) \
((u32Val) \
| (((bitfield_val) << ((u32) bitfield_stub_name##_OFS)) \
& ((u32) bitfield_stub_name##_MASK)))
/* get bitfield value from u32 u32Val */
#define AMD_GETBITS(u32Val, bitfield_stub_name) \
((u32Val & ((u32) bitfield_stub_name##_MASK)) \
>> ((u32) bitfield_stub_name##_OFS))
/* SET and GET bits in u32 values ------------------------------------------*/
#define AMD_BIT(bit_stub_name) (1 << bit_stub_name)
#define AMD_UNMASK_BIT(bit_stub_name) (~AMD_BIT(bit_stub_name))
#define AMD_CLEAR_BIT(bit_stub_name) (~AMD_BIT(bit_stub_name))
/* debug macros ------------------------------------------------------------*/
#define DBG(udc , args...) dev_dbg(udc->dev, args)
#ifdef UDC_VERBOSE
#define VDBG DBG
#else
#define VDBG(udc , args...) do {} while (0)
#endif
#endif /* #ifdef AMD5536UDC_H */
@@ -0,0 +1,216 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* amd5536udc_pci.c -- AMD 5536 UDC high/full speed USB device controller
*
* Copyright (C) 2005-2007 AMD (https://www.amd.com)
* Author: Thomas Dahlmann
*/
/*
* The AMD5536 UDC is part of the x86 southbridge AMD Geode CS5536.
* It is a USB Highspeed DMA capable USB device controller. Beside ep0 it
* provides 4 IN and 4 OUT endpoints (bulk or interrupt type).
*
* Make sure that UDC is assigned to port 4 by BIOS settings (port can also
* be used as host port) and UOC bits PAD_EN and APU are set (should be done
* by BIOS init).
*
* UDC DMA requires 32-bit aligned buffers so DMA with gadget ether does not
* work without updating NET_IP_ALIGN. Or PIO mode (module param "use_dma=0")
* can be used with gadget ether.
*
* This file does pci device registration, and the core driver implementation
* is done in amd5536udc.c
*
* The driver is split so as to use the core UDC driver which is based on
* Synopsys device controller IP (different than HS OTG IP) in UDCs
* integrated to SoC platforms.
*
*/
/* Driver strings */
#define UDC_MOD_DESCRIPTION "AMD 5536 UDC - USB Device Controller"
/* system */
#include <linux/device.h>
#include <linux/dmapool.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/prefetch.h>
#include <linux/pci.h>
/* udc specific */
#include "amd5536udc.h"
/* pointer to device object */
static struct udc *udc;
/* description */
static const char name[] = "amd5536udc-pci";
/* Reset all pci context */
static void udc_pci_remove(struct pci_dev *pdev)
{
struct udc *dev;
dev = pci_get_drvdata(pdev);
usb_del_gadget_udc(&udc->gadget);
/* gadget driver must not be registered */
if (WARN_ON(dev->driver))
return;
/* dma pool cleanup */
free_dma_pools(dev);
/* reset controller */
writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
free_irq(pdev->irq, dev);
iounmap(dev->virt_addr);
release_mem_region(pci_resource_start(pdev, 0),
pci_resource_len(pdev, 0));
pci_disable_device(pdev);
udc_remove(dev);
}
/* Called by pci bus driver to init pci context */
static int udc_pci_probe(
struct pci_dev *pdev,
const struct pci_device_id *id
)
{
struct udc *dev;
unsigned long resource;
unsigned long len;
int retval = 0;
/* one udc only */
if (udc) {
dev_dbg(&pdev->dev, "already probed\n");
return -EBUSY;
}
/* init */
dev = kzalloc_obj(struct udc);
if (!dev)
return -ENOMEM;
/* pci setup */
if (pci_enable_device(pdev) < 0) {
retval = -ENODEV;
goto err_pcidev;
}
/* PCI resource allocation */
resource = pci_resource_start(pdev, 0);
len = pci_resource_len(pdev, 0);
if (!request_mem_region(resource, len, name)) {
dev_dbg(&pdev->dev, "pci device used already\n");
retval = -EBUSY;
goto err_memreg;
}
dev->virt_addr = ioremap(resource, len);
if (!dev->virt_addr) {
dev_dbg(&pdev->dev, "start address cannot be mapped\n");
retval = -EFAULT;
goto err_ioremap;
}
if (!pdev->irq) {
dev_err(&pdev->dev, "irq not set\n");
retval = -ENODEV;
goto err_irq;
}
spin_lock_init(&dev->lock);
/* udc csr registers base */
dev->csr = dev->virt_addr + UDC_CSR_ADDR;
/* dev registers base */
dev->regs = dev->virt_addr + UDC_DEVCFG_ADDR;
/* ep registers base */
dev->ep_regs = dev->virt_addr + UDC_EPREGS_ADDR;
/* fifo's base */
dev->rxfifo = (u32 __iomem *)(dev->virt_addr + UDC_RXFIFO_ADDR);
dev->txfifo = (u32 __iomem *)(dev->virt_addr + UDC_TXFIFO_ADDR);
if (request_irq(pdev->irq, udc_irq, IRQF_SHARED, name, dev) != 0) {
dev_dbg(&pdev->dev, "request_irq(%d) fail\n", pdev->irq);
retval = -EBUSY;
goto err_irq;
}
pci_set_drvdata(pdev, dev);
/* chip revision for Hs AMD5536 */
dev->chiprev = pdev->revision;
pci_set_master(pdev);
pci_try_set_mwi(pdev);
dev->phys_addr = resource;
dev->irq = pdev->irq;
dev->pdev = pdev;
dev->dev = &pdev->dev;
/* init dma pools */
if (use_dma) {
retval = init_dma_pools(dev);
if (retval != 0)
goto err_dma;
}
/* general probing */
if (udc_probe(dev)) {
retval = -ENODEV;
goto err_probe;
}
udc = dev;
return 0;
err_probe:
if (use_dma)
free_dma_pools(dev);
err_dma:
free_irq(pdev->irq, dev);
err_irq:
iounmap(dev->virt_addr);
err_ioremap:
release_mem_region(resource, len);
err_memreg:
pci_disable_device(pdev);
err_pcidev:
kfree(dev);
return retval;
}
/* PCI device parameters */
static const struct pci_device_id pci_id[] = {
{
PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x2096),
.class = PCI_CLASS_SERIAL_USB_DEVICE,
.class_mask = 0xffffffff,
},
{},
};
MODULE_DEVICE_TABLE(pci, pci_id);
/* PCI functions */
static struct pci_driver udc_pci_driver = {
.name = name,
.id_table = pci_id,
.probe = udc_pci_probe,
.remove = udc_pci_remove,
};
module_pci_driver(udc_pci_driver);
MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
MODULE_AUTHOR("Thomas Dahlmann");
MODULE_LICENSE("GPL");
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0+
config USB_ASPEED_VHUB
tristate "Aspeed vHub UDC driver"
depends on ARCH_ASPEED || COMPILE_TEST
depends on USB_LIBCOMPOSITE
help
USB peripheral controller for the Aspeed AST2400, AST2500 and
AST2600 family SoCs supporting the "vHub" functionality and USB2.0
@@ -0,0 +1,4 @@
# SPDX-License-Identifier: GPL-2.0+
obj-$(CONFIG_USB_ASPEED_VHUB) += aspeed-vhub.o
aspeed-vhub-y := core.o ep0.o epn.o dev.o hub.o
@@ -0,0 +1,471 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* aspeed-vhub -- Driver for Aspeed SoC "vHub" USB gadget
*
* core.c - Top level support
*
* Copyright 2017 IBM Corporation
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <linux/prefetch.h>
#include <linux/clk.h>
#include <linux/usb/gadget.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/dma-mapping.h>
#include <linux/reset.h>
#include "vhub.h"
void ast_vhub_done(struct ast_vhub_ep *ep, struct ast_vhub_req *req,
int status)
{
bool internal = req->internal;
struct ast_vhub *vhub = ep->vhub;
EPVDBG(ep, "completing request @%p, status %d\n", req, status);
list_del_init(&req->queue);
if ((req->req.status == -EINPROGRESS) || (status == -EOVERFLOW))
req->req.status = status;
if (req->req.dma) {
if (!WARN_ON(!ep->dev))
usb_gadget_unmap_request_by_dev(&vhub->pdev->dev,
&req->req, ep->epn.is_in);
req->req.dma = 0;
}
/*
* If this isn't an internal EP0 request, call the core
* to call the gadget completion.
*/
if (!internal) {
spin_unlock(&ep->vhub->lock);
usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&ep->vhub->lock);
}
}
void ast_vhub_nuke(struct ast_vhub_ep *ep, int status)
{
struct ast_vhub_req *req;
int count = 0;
/* Beware, lock will be dropped & req-acquired by done() */
while (!list_empty(&ep->queue)) {
req = list_first_entry(&ep->queue, struct ast_vhub_req, queue);
ast_vhub_done(ep, req, status);
count++;
}
if (count)
EPDBG(ep, "Nuked %d request(s)\n", count);
}
struct usb_request *ast_vhub_alloc_request(struct usb_ep *u_ep,
gfp_t gfp_flags)
{
struct ast_vhub_req *req;
req = kzalloc_obj(*req, gfp_flags);
if (!req)
return NULL;
return &req->req;
}
void ast_vhub_free_request(struct usb_ep *u_ep, struct usb_request *u_req)
{
struct ast_vhub_req *req = to_ast_req(u_req);
kfree(req);
}
static irqreturn_t ast_vhub_irq(int irq, void *data)
{
struct ast_vhub *vhub = data;
irqreturn_t iret = IRQ_NONE;
u32 i, istat;
/* Stale interrupt while tearing down */
if (!vhub->ep0_bufs)
return IRQ_NONE;
spin_lock(&vhub->lock);
/* Read and ACK interrupts */
istat = readl(vhub->regs + AST_VHUB_ISR);
if (!istat)
goto bail;
writel(istat, vhub->regs + AST_VHUB_ISR);
iret = IRQ_HANDLED;
UDCVDBG(vhub, "irq status=%08x, ep_acks=%08x ep_nacks=%08x\n",
istat,
readl(vhub->regs + AST_VHUB_EP_ACK_ISR),
readl(vhub->regs + AST_VHUB_EP_NACK_ISR));
/* Handle generic EPs first */
if (istat & VHUB_IRQ_EP_POOL_ACK_STALL) {
u32 ep_acks = readl(vhub->regs + AST_VHUB_EP_ACK_ISR);
writel(ep_acks, vhub->regs + AST_VHUB_EP_ACK_ISR);
for (i = 0; ep_acks && i < vhub->max_epns; i++) {
u32 mask = VHUB_EP_IRQ(i);
if (ep_acks & mask) {
ast_vhub_epn_ack_irq(&vhub->epns[i]);
ep_acks &= ~mask;
}
}
}
/* Handle device interrupts */
if (istat & vhub->port_irq_mask) {
for (i = 0; i < vhub->max_ports; i++) {
if (istat & VHUB_DEV_IRQ(i))
ast_vhub_dev_irq(&vhub->ports[i].dev);
}
}
/* Handle top-level vHub EP0 interrupts */
if (istat & (VHUB_IRQ_HUB_EP0_OUT_ACK_STALL |
VHUB_IRQ_HUB_EP0_IN_ACK_STALL |
VHUB_IRQ_HUB_EP0_SETUP)) {
if (istat & VHUB_IRQ_HUB_EP0_IN_ACK_STALL)
ast_vhub_ep0_handle_ack(&vhub->ep0, true);
if (istat & VHUB_IRQ_HUB_EP0_OUT_ACK_STALL)
ast_vhub_ep0_handle_ack(&vhub->ep0, false);
if (istat & VHUB_IRQ_HUB_EP0_SETUP)
ast_vhub_ep0_handle_setup(&vhub->ep0);
}
/* Various top level bus events */
if (istat & (VHUB_IRQ_BUS_RESUME |
VHUB_IRQ_BUS_SUSPEND |
VHUB_IRQ_BUS_RESET)) {
if (istat & VHUB_IRQ_BUS_RESUME)
ast_vhub_hub_resume(vhub);
if (istat & VHUB_IRQ_BUS_SUSPEND)
ast_vhub_hub_suspend(vhub);
if (istat & VHUB_IRQ_BUS_RESET)
ast_vhub_hub_reset(vhub);
}
bail:
spin_unlock(&vhub->lock);
return iret;
}
void ast_vhub_init_hw(struct ast_vhub *vhub)
{
u32 ctrl, port_mask, epn_mask;
UDCDBG(vhub,"(Re)Starting HW ...\n");
/* Enable PHY */
ctrl = VHUB_CTRL_PHY_CLK |
VHUB_CTRL_PHY_RESET_DIS;
/*
* We do *NOT* set the VHUB_CTRL_CLK_STOP_SUSPEND bit
* to stop the logic clock during suspend because
* it causes the registers to become inaccessible and
* we haven't yet figured out a good wayt to bring the
* controller back into life to issue a wakeup.
*/
/*
* Set some ISO & split control bits according to Aspeed
* recommendation
*
* VHUB_CTRL_ISO_RSP_CTRL: When set tells the HW to respond
* with 0 bytes data packet to ISO IN endpoints when no data
* is available.
*
* VHUB_CTRL_SPLIT_IN: This makes a SOF complete a split IN
* transaction.
*/
ctrl |= VHUB_CTRL_ISO_RSP_CTRL | VHUB_CTRL_SPLIT_IN;
writel(ctrl, vhub->regs + AST_VHUB_CTRL);
udelay(1);
/* Set descriptor ring size */
if (AST_VHUB_DESCS_COUNT == 256) {
ctrl |= VHUB_CTRL_LONG_DESC;
writel(ctrl, vhub->regs + AST_VHUB_CTRL);
} else {
BUILD_BUG_ON(AST_VHUB_DESCS_COUNT != 32);
}
/* Reset all devices */
port_mask = GENMASK(vhub->max_ports, 1);
writel(VHUB_SW_RESET_ROOT_HUB |
VHUB_SW_RESET_DMA_CONTROLLER |
VHUB_SW_RESET_EP_POOL |
port_mask, vhub->regs + AST_VHUB_SW_RESET);
udelay(1);
writel(0, vhub->regs + AST_VHUB_SW_RESET);
/* Disable and cleanup EP ACK/NACK interrupts */
epn_mask = GENMASK(vhub->max_epns - 1, 0);
writel(0, vhub->regs + AST_VHUB_EP_ACK_IER);
writel(0, vhub->regs + AST_VHUB_EP_NACK_IER);
writel(epn_mask, vhub->regs + AST_VHUB_EP_ACK_ISR);
writel(epn_mask, vhub->regs + AST_VHUB_EP_NACK_ISR);
/* Default settings for EP0, enable HW hub EP1 */
writel(0, vhub->regs + AST_VHUB_EP0_CTRL);
writel(VHUB_EP1_CTRL_RESET_TOGGLE |
VHUB_EP1_CTRL_ENABLE,
vhub->regs + AST_VHUB_EP1_CTRL);
writel(0, vhub->regs + AST_VHUB_EP1_STS_CHG);
/* Configure EP0 DMA buffer */
writel(vhub->ep0.buf_dma, vhub->regs + AST_VHUB_EP0_DATA);
/* Clear address */
writel(0, vhub->regs + AST_VHUB_CONF);
/* Pullup hub (activate on host) */
if (vhub->force_usb1)
ctrl |= VHUB_CTRL_FULL_SPEED_ONLY;
ctrl |= VHUB_CTRL_UPSTREAM_CONNECT;
writel(ctrl, vhub->regs + AST_VHUB_CTRL);
/* Enable some interrupts */
writel(VHUB_IRQ_HUB_EP0_IN_ACK_STALL |
VHUB_IRQ_HUB_EP0_OUT_ACK_STALL |
VHUB_IRQ_HUB_EP0_SETUP |
VHUB_IRQ_EP_POOL_ACK_STALL |
VHUB_IRQ_BUS_RESUME |
VHUB_IRQ_BUS_SUSPEND |
VHUB_IRQ_BUS_RESET,
vhub->regs + AST_VHUB_IER);
}
static void ast_vhub_remove(struct platform_device *pdev)
{
struct ast_vhub *vhub = platform_get_drvdata(pdev);
unsigned long flags;
int i;
if (!vhub || !vhub->regs)
return;
/* Remove devices */
for (i = 0; i < vhub->max_ports; i++)
ast_vhub_del_dev(&vhub->ports[i].dev);
spin_lock_irqsave(&vhub->lock, flags);
/* Mask & ack all interrupts */
writel(0, vhub->regs + AST_VHUB_IER);
writel(VHUB_IRQ_ACK_ALL, vhub->regs + AST_VHUB_ISR);
/* Pull device, leave PHY enabled */
writel(VHUB_CTRL_PHY_CLK |
VHUB_CTRL_PHY_RESET_DIS,
vhub->regs + AST_VHUB_CTRL);
if (vhub->clk)
clk_disable_unprepare(vhub->clk);
reset_control_assert(vhub->rst);
spin_unlock_irqrestore(&vhub->lock, flags);
if (vhub->ep0_bufs)
dma_free_coherent(&pdev->dev,
AST_VHUB_EP0_MAX_PACKET *
(vhub->max_ports + 1),
vhub->ep0_bufs,
vhub->ep0_bufs_dma);
vhub->ep0_bufs = NULL;
}
static int ast_vhub_probe(struct platform_device *pdev)
{
enum usb_device_speed max_speed;
const u64 *dma_mask_ptr;
struct ast_vhub *vhub;
struct resource *res;
int i, rc = 0;
const struct device_node *np = pdev->dev.of_node;
vhub = devm_kzalloc(&pdev->dev, sizeof(*vhub), GFP_KERNEL);
if (!vhub)
return -ENOMEM;
rc = of_property_read_u32(np, "aspeed,vhub-downstream-ports",
&vhub->max_ports);
if (rc < 0)
vhub->max_ports = AST_VHUB_NUM_PORTS;
vhub->ports = devm_kcalloc(&pdev->dev, vhub->max_ports,
sizeof(*vhub->ports), GFP_KERNEL);
if (!vhub->ports)
return -ENOMEM;
rc = of_property_read_u32(np, "aspeed,vhub-generic-endpoints",
&vhub->max_epns);
if (rc < 0)
vhub->max_epns = AST_VHUB_NUM_GEN_EPs;
vhub->epns = devm_kcalloc(&pdev->dev, vhub->max_epns,
sizeof(*vhub->epns), GFP_KERNEL);
if (!vhub->epns)
return -ENOMEM;
spin_lock_init(&vhub->lock);
vhub->pdev = pdev;
vhub->port_irq_mask = GENMASK(VHUB_IRQ_DEV1_BIT + vhub->max_ports - 1,
VHUB_IRQ_DEV1_BIT);
vhub->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(vhub->regs)) {
dev_err(&pdev->dev, "Failed to map resources\n");
return PTR_ERR(vhub->regs);
}
UDCDBG(vhub, "vHub@%pR mapped @%p\n", res, vhub->regs);
platform_set_drvdata(pdev, vhub);
vhub->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(vhub->clk)) {
rc = PTR_ERR(vhub->clk);
goto err;
}
rc = clk_prepare_enable(vhub->clk);
if (rc) {
dev_err(&pdev->dev, "Error couldn't enable clock (%d)\n", rc);
goto err;
}
vhub->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
if (IS_ERR(vhub->rst)) {
rc = PTR_ERR(vhub->rst);
goto err;
}
rc = reset_control_deassert(vhub->rst);
if (rc)
goto err;
/* Check if we need to limit the HW to USB1 */
max_speed = usb_get_maximum_speed(&pdev->dev);
if (max_speed != USB_SPEED_UNKNOWN && max_speed < USB_SPEED_HIGH)
vhub->force_usb1 = true;
/* Mask & ack all interrupts before installing the handler */
writel(0, vhub->regs + AST_VHUB_IER);
writel(VHUB_IRQ_ACK_ALL, vhub->regs + AST_VHUB_ISR);
/* Find interrupt and install handler */
vhub->irq = platform_get_irq(pdev, 0);
if (vhub->irq < 0) {
rc = vhub->irq;
goto err;
}
rc = devm_request_irq(&pdev->dev, vhub->irq, ast_vhub_irq, 0,
KBUILD_MODNAME, vhub);
if (rc) {
dev_err(&pdev->dev, "Failed to request interrupt\n");
goto err;
}
dma_mask_ptr = (u64 *)of_device_get_match_data(&pdev->dev);
if (dma_mask_ptr) {
rc = dma_coerce_mask_and_coherent(&pdev->dev, *dma_mask_ptr);
if (rc)
goto err;
}
/*
* Allocate DMA buffers for all EP0s in one chunk,
* one per port and one for the vHub itself
*/
vhub->ep0_bufs = dma_alloc_coherent(&pdev->dev,
AST_VHUB_EP0_MAX_PACKET *
(vhub->max_ports + 1),
&vhub->ep0_bufs_dma, GFP_KERNEL);
if (!vhub->ep0_bufs) {
dev_err(&pdev->dev, "Failed to allocate EP0 DMA buffers\n");
rc = -ENOMEM;
goto err;
}
UDCVDBG(vhub, "EP0 DMA buffers @%p (DMA 0x%08x)\n",
vhub->ep0_bufs, (u32)vhub->ep0_bufs_dma);
/* Init vHub EP0 */
ast_vhub_init_ep0(vhub, &vhub->ep0, NULL);
/* Init devices */
for (i = 0; i < vhub->max_ports && rc == 0; i++)
rc = ast_vhub_init_dev(vhub, i);
if (rc)
goto err;
/* Init hub emulation */
rc = ast_vhub_init_hub(vhub);
if (rc)
goto err;
/* Initialize HW */
ast_vhub_init_hw(vhub);
dev_info(&pdev->dev, "Initialized virtual hub in USB%d mode\n",
vhub->force_usb1 ? 1 : 2);
return 0;
err:
ast_vhub_remove(pdev);
return rc;
}
static const u64 dma_mask_32 = DMA_BIT_MASK(32);
static const u64 dma_mask_64 = DMA_BIT_MASK(64);
static const struct of_device_id ast_vhub_dt_ids[] = {
{
.compatible = "aspeed,ast2400-usb-vhub",
.data = &dma_mask_32,
},
{
.compatible = "aspeed,ast2500-usb-vhub",
.data = &dma_mask_32,
},
{
.compatible = "aspeed,ast2600-usb-vhub",
.data = &dma_mask_32,
},
{
.compatible = "aspeed,ast2700-usb-vhub",
.data = &dma_mask_64,
},
{ }
};
MODULE_DEVICE_TABLE(of, ast_vhub_dt_ids);
static struct platform_driver ast_vhub_driver = {
.probe = ast_vhub_probe,
.remove = ast_vhub_remove,
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = ast_vhub_dt_ids,
},
};
module_platform_driver(ast_vhub_driver);
MODULE_DESCRIPTION("Aspeed vHub udc driver");
MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
MODULE_LICENSE("GPL");
@@ -0,0 +1,612 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* aspeed-vhub -- Driver for Aspeed SoC "vHub" USB gadget
*
* dev.c - Individual device/gadget management (ie, a port = a gadget)
*
* Copyright 2017 IBM Corporation
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <linux/prefetch.h>
#include <linux/clk.h>
#include <linux/usb/gadget.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/dma-mapping.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include "vhub.h"
void ast_vhub_dev_irq(struct ast_vhub_dev *d)
{
u32 istat = readl(d->regs + AST_VHUB_DEV_ISR);
writel(istat, d->regs + AST_VHUB_DEV_ISR);
if (istat & VHUV_DEV_IRQ_EP0_IN_ACK_STALL)
ast_vhub_ep0_handle_ack(&d->ep0, true);
if (istat & VHUV_DEV_IRQ_EP0_OUT_ACK_STALL)
ast_vhub_ep0_handle_ack(&d->ep0, false);
if (istat & VHUV_DEV_IRQ_EP0_SETUP)
ast_vhub_ep0_handle_setup(&d->ep0);
}
static void ast_vhub_dev_enable(struct ast_vhub_dev *d)
{
u32 reg, hmsk, i;
if (d->enabled)
return;
/* Cleanup EP0 state */
ast_vhub_reset_ep0(d);
/* Enable device and its EP0 interrupts */
reg = VHUB_DEV_EN_ENABLE_PORT |
VHUB_DEV_EN_EP0_IN_ACK_IRQEN |
VHUB_DEV_EN_EP0_OUT_ACK_IRQEN |
VHUB_DEV_EN_EP0_SETUP_IRQEN;
if (d->gadget.speed == USB_SPEED_HIGH)
reg |= VHUB_DEV_EN_SPEED_SEL_HIGH;
writel(reg, d->regs + AST_VHUB_DEV_EN_CTRL);
/* Enable device interrupt in the hub as well */
hmsk = VHUB_IRQ_DEVICE1 << d->index;
reg = readl(d->vhub->regs + AST_VHUB_IER);
reg |= hmsk;
writel(reg, d->vhub->regs + AST_VHUB_IER);
/* Set EP0 DMA buffer address */
writel(d->ep0.buf_dma, d->regs + AST_VHUB_DEV_EP0_DATA);
/* Clear stall on all EPs */
for (i = 0; i < d->max_epns; i++) {
struct ast_vhub_ep *ep = d->epns[i];
if (ep && (ep->epn.stalled || ep->epn.wedged)) {
ep->epn.stalled = false;
ep->epn.wedged = false;
ast_vhub_update_epn_stall(ep);
}
}
/* Additional cleanups */
d->wakeup_en = false;
d->enabled = true;
}
static void ast_vhub_dev_disable(struct ast_vhub_dev *d)
{
u32 reg, hmsk;
if (!d->enabled)
return;
/* Disable device interrupt in the hub */
hmsk = VHUB_IRQ_DEVICE1 << d->index;
reg = readl(d->vhub->regs + AST_VHUB_IER);
reg &= ~hmsk;
writel(reg, d->vhub->regs + AST_VHUB_IER);
/* Then disable device */
writel(0, d->regs + AST_VHUB_DEV_EN_CTRL);
d->gadget.speed = USB_SPEED_UNKNOWN;
d->enabled = false;
}
static int ast_vhub_dev_feature(struct ast_vhub_dev *d,
u16 wIndex, u16 wValue,
bool is_set)
{
u32 val;
DDBG(d, "%s_FEATURE(dev val=%02x)\n",
is_set ? "SET" : "CLEAR", wValue);
if (wValue == USB_DEVICE_REMOTE_WAKEUP) {
d->wakeup_en = is_set;
return std_req_complete;
}
if (wValue == USB_DEVICE_TEST_MODE) {
val = readl(d->vhub->regs + AST_VHUB_CTRL);
val &= ~GENMASK(10, 8);
val |= VHUB_CTRL_SET_TEST_MODE((wIndex >> 8) & 0x7);
writel(val, d->vhub->regs + AST_VHUB_CTRL);
return std_req_complete;
}
return std_req_driver;
}
static int ast_vhub_ep_feature(struct ast_vhub_dev *d,
u16 wIndex, u16 wValue, bool is_set)
{
struct ast_vhub_ep *ep;
int ep_num;
ep_num = wIndex & USB_ENDPOINT_NUMBER_MASK;
DDBG(d, "%s_FEATURE(ep%d val=%02x)\n",
is_set ? "SET" : "CLEAR", ep_num, wValue);
if (ep_num == 0)
return std_req_complete;
if (ep_num >= d->max_epns || !d->epns[ep_num - 1])
return std_req_stall;
if (wValue != USB_ENDPOINT_HALT)
return std_req_driver;
ep = d->epns[ep_num - 1];
if (WARN_ON(!ep))
return std_req_stall;
if (!ep->epn.enabled || !ep->ep.desc || ep->epn.is_iso ||
ep->epn.is_in != !!(wIndex & USB_DIR_IN))
return std_req_stall;
DDBG(d, "%s stall on EP %d\n",
is_set ? "setting" : "clearing", ep_num);
ep->epn.stalled = is_set;
ast_vhub_update_epn_stall(ep);
return std_req_complete;
}
static int ast_vhub_dev_status(struct ast_vhub_dev *d,
u16 wIndex, u16 wValue)
{
u8 st0;
DDBG(d, "GET_STATUS(dev)\n");
st0 = d->gadget.is_selfpowered << USB_DEVICE_SELF_POWERED;
if (d->wakeup_en)
st0 |= 1 << USB_DEVICE_REMOTE_WAKEUP;
return ast_vhub_simple_reply(&d->ep0, st0, 0);
}
static int ast_vhub_ep_status(struct ast_vhub_dev *d,
u16 wIndex, u16 wValue)
{
int ep_num = wIndex & USB_ENDPOINT_NUMBER_MASK;
struct ast_vhub_ep *ep;
u8 st0 = 0;
DDBG(d, "GET_STATUS(ep%d)\n", ep_num);
if (ep_num >= d->max_epns)
return std_req_stall;
if (ep_num != 0) {
ep = d->epns[ep_num - 1];
if (!ep)
return std_req_stall;
if (!ep->epn.enabled || !ep->ep.desc || ep->epn.is_iso ||
ep->epn.is_in != !!(wIndex & USB_DIR_IN))
return std_req_stall;
if (ep->epn.stalled)
st0 |= 1 << USB_ENDPOINT_HALT;
}
return ast_vhub_simple_reply(&d->ep0, st0, 0);
}
static void ast_vhub_dev_set_address(struct ast_vhub_dev *d, u8 addr)
{
u32 reg;
DDBG(d, "SET_ADDRESS: Got address %x\n", addr);
reg = readl(d->regs + AST_VHUB_DEV_EN_CTRL);
reg &= ~VHUB_DEV_EN_ADDR_MASK;
reg |= VHUB_DEV_EN_SET_ADDR(addr);
writel(reg, d->regs + AST_VHUB_DEV_EN_CTRL);
}
int ast_vhub_std_dev_request(struct ast_vhub_ep *ep,
struct usb_ctrlrequest *crq)
{
struct ast_vhub_dev *d = ep->dev;
u16 wValue, wIndex;
/* No driver, we shouldn't be enabled ... */
if (!d->driver || !d->enabled) {
EPDBG(ep,
"Device is wrong state driver=%p enabled=%d\n",
d->driver, d->enabled);
return std_req_stall;
}
/*
* Note: we used to reject/stall requests while suspended,
* we don't do that anymore as we seem to have cases of
* mass storage getting very upset.
*/
/* First packet, grab speed */
if (d->gadget.speed == USB_SPEED_UNKNOWN) {
d->gadget.speed = ep->vhub->speed;
if (d->gadget.speed > d->driver->max_speed)
d->gadget.speed = d->driver->max_speed;
DDBG(d, "fist packet, captured speed %d\n",
d->gadget.speed);
}
wValue = le16_to_cpu(crq->wValue);
wIndex = le16_to_cpu(crq->wIndex);
switch ((crq->bRequestType << 8) | crq->bRequest) {
/* SET_ADDRESS */
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
ast_vhub_dev_set_address(d, wValue);
return std_req_complete;
/* GET_STATUS */
case DeviceRequest | USB_REQ_GET_STATUS:
return ast_vhub_dev_status(d, wIndex, wValue);
case InterfaceRequest | USB_REQ_GET_STATUS:
return ast_vhub_simple_reply(ep, 0, 0);
case EndpointRequest | USB_REQ_GET_STATUS:
return ast_vhub_ep_status(d, wIndex, wValue);
/* SET/CLEAR_FEATURE */
case DeviceOutRequest | USB_REQ_SET_FEATURE:
return ast_vhub_dev_feature(d, wIndex, wValue, true);
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
return ast_vhub_dev_feature(d, wIndex, wValue, false);
case EndpointOutRequest | USB_REQ_SET_FEATURE:
return ast_vhub_ep_feature(d, wIndex, wValue, true);
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
return ast_vhub_ep_feature(d, wIndex, wValue, false);
}
return std_req_driver;
}
static int ast_vhub_udc_wakeup(struct usb_gadget* gadget)
{
struct ast_vhub_dev *d = to_ast_dev(gadget);
unsigned long flags;
int rc = -EINVAL;
spin_lock_irqsave(&d->vhub->lock, flags);
if (!d->wakeup_en)
goto err;
DDBG(d, "Device initiated wakeup\n");
/* Wakeup the host */
ast_vhub_hub_wake_all(d->vhub);
rc = 0;
err:
spin_unlock_irqrestore(&d->vhub->lock, flags);
return rc;
}
static int ast_vhub_udc_get_frame(struct usb_gadget* gadget)
{
struct ast_vhub_dev *d = to_ast_dev(gadget);
return (readl(d->vhub->regs + AST_VHUB_USBSTS) >> 16) & 0x7ff;
}
static void ast_vhub_dev_nuke(struct ast_vhub_dev *d)
{
unsigned int i;
for (i = 0; i < d->max_epns; i++) {
if (!d->epns[i])
continue;
ast_vhub_nuke(d->epns[i], -ESHUTDOWN);
}
}
static int ast_vhub_udc_pullup(struct usb_gadget* gadget, int on)
{
struct ast_vhub_dev *d = to_ast_dev(gadget);
unsigned long flags;
spin_lock_irqsave(&d->vhub->lock, flags);
DDBG(d, "pullup(%d)\n", on);
/* Mark disconnected in the hub */
ast_vhub_device_connect(d->vhub, d->index, on);
/*
* If enabled, nuke all requests if any (there shouldn't be)
* and disable the port. This will clear the address too.
*/
if (d->enabled) {
ast_vhub_dev_nuke(d);
ast_vhub_dev_disable(d);
}
spin_unlock_irqrestore(&d->vhub->lock, flags);
return 0;
}
static int ast_vhub_udc_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
struct ast_vhub_dev *d = to_ast_dev(gadget);
unsigned long flags;
spin_lock_irqsave(&d->vhub->lock, flags);
DDBG(d, "start\n");
/* We don't do much more until the hub enables us */
d->driver = driver;
d->gadget.is_selfpowered = 1;
spin_unlock_irqrestore(&d->vhub->lock, flags);
return 0;
}
static struct usb_ep *ast_vhub_udc_match_ep(struct usb_gadget *gadget,
struct usb_endpoint_descriptor *desc,
struct usb_ss_ep_comp_descriptor *ss)
{
struct ast_vhub_dev *d = to_ast_dev(gadget);
struct ast_vhub_ep *ep;
struct usb_ep *u_ep;
unsigned int max, addr, i;
DDBG(d, "Match EP type %d\n", usb_endpoint_type(desc));
/*
* First we need to look for an existing unclaimed EP as another
* configuration may have already associated a bunch of EPs with
* this gadget. This duplicates the code in usb_ep_autoconfig_ss()
* unfortunately.
*/
list_for_each_entry(u_ep, &gadget->ep_list, ep_list) {
if (usb_gadget_ep_match_desc(gadget, u_ep, desc, ss)) {
DDBG(d, " -> using existing EP%d\n",
to_ast_ep(u_ep)->d_idx);
return u_ep;
}
}
/*
* We didn't find one, we need to grab one from the pool.
*
* First let's do some sanity checking
*/
switch(usb_endpoint_type(desc)) {
case USB_ENDPOINT_XFER_CONTROL:
/* Only EP0 can be a control endpoint */
return NULL;
case USB_ENDPOINT_XFER_ISOC:
/* ISO: limit 1023 bytes full speed, 1024 high/super speed */
if (gadget_is_dualspeed(gadget))
max = 1024;
else
max = 1023;
break;
case USB_ENDPOINT_XFER_BULK:
if (gadget_is_dualspeed(gadget))
max = 512;
else
max = 64;
break;
case USB_ENDPOINT_XFER_INT:
if (gadget_is_dualspeed(gadget))
max = 1024;
else
max = 64;
break;
}
if (usb_endpoint_maxp(desc) > max)
return NULL;
/*
* Find a free EP address for that device. We can't
* let the generic code assign these as it would
* create overlapping numbers for IN and OUT which
* we don't support, so also create a suitable name
* that will allow the generic code to use our
* assigned address.
*/
for (i = 0; i < d->max_epns; i++)
if (d->epns[i] == NULL)
break;
if (i >= d->max_epns)
return NULL;
addr = i + 1;
/*
* Now grab an EP from the shared pool and associate
* it with our device
*/
ep = ast_vhub_alloc_epn(d, addr);
if (!ep)
return NULL;
DDBG(d, "Allocated epn#%d for port EP%d\n",
ep->epn.g_idx, addr);
return &ep->ep;
}
static int ast_vhub_udc_stop(struct usb_gadget *gadget)
{
struct ast_vhub_dev *d = to_ast_dev(gadget);
unsigned long flags;
spin_lock_irqsave(&d->vhub->lock, flags);
DDBG(d, "stop\n");
d->driver = NULL;
d->gadget.speed = USB_SPEED_UNKNOWN;
ast_vhub_dev_nuke(d);
if (d->enabled)
ast_vhub_dev_disable(d);
spin_unlock_irqrestore(&d->vhub->lock, flags);
return 0;
}
static const struct usb_gadget_ops ast_vhub_udc_ops = {
.get_frame = ast_vhub_udc_get_frame,
.wakeup = ast_vhub_udc_wakeup,
.pullup = ast_vhub_udc_pullup,
.udc_start = ast_vhub_udc_start,
.udc_stop = ast_vhub_udc_stop,
.match_ep = ast_vhub_udc_match_ep,
};
void ast_vhub_dev_suspend(struct ast_vhub_dev *d)
{
if (d->driver && d->driver->suspend) {
spin_unlock(&d->vhub->lock);
d->driver->suspend(&d->gadget);
spin_lock(&d->vhub->lock);
}
}
void ast_vhub_dev_resume(struct ast_vhub_dev *d)
{
if (d->driver && d->driver->resume) {
spin_unlock(&d->vhub->lock);
d->driver->resume(&d->gadget);
spin_lock(&d->vhub->lock);
}
}
void ast_vhub_dev_reset(struct ast_vhub_dev *d)
{
/* No driver, just disable the device and return */
if (!d->driver) {
ast_vhub_dev_disable(d);
return;
}
/* If the port isn't enabled, just enable it */
if (!d->enabled) {
DDBG(d, "Reset of disabled device, enabling...\n");
ast_vhub_dev_enable(d);
} else {
DDBG(d, "Reset of enabled device, resetting...\n");
spin_unlock(&d->vhub->lock);
usb_gadget_udc_reset(&d->gadget, d->driver);
spin_lock(&d->vhub->lock);
/*
* Disable and maybe re-enable HW, this will clear the address
* and speed setting.
*/
ast_vhub_dev_disable(d);
ast_vhub_dev_enable(d);
}
}
void ast_vhub_del_dev(struct ast_vhub_dev *d)
{
unsigned long flags;
spin_lock_irqsave(&d->vhub->lock, flags);
if (!d->registered) {
spin_unlock_irqrestore(&d->vhub->lock, flags);
return;
}
d->registered = false;
spin_unlock_irqrestore(&d->vhub->lock, flags);
usb_del_gadget_udc(&d->gadget);
device_unregister(d->port_dev);
kfree(d->epns);
}
static void ast_vhub_dev_release(struct device *dev)
{
kfree(dev);
}
int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx)
{
struct ast_vhub_dev *d = &vhub->ports[idx].dev;
struct device *parent = &vhub->pdev->dev;
int rc;
d->vhub = vhub;
d->index = idx;
d->name = devm_kasprintf(parent, GFP_KERNEL, "port%d", idx+1);
if (!d->name)
return -ENOMEM;
d->regs = vhub->regs + 0x100 + 0x10 * idx;
ast_vhub_init_ep0(vhub, &d->ep0, d);
/*
* A USB device can have up to 30 endpoints besides control
* endpoint 0.
*/
d->max_epns = min_t(u32, vhub->max_epns, 30);
d->epns = kzalloc_objs(*d->epns, d->max_epns);
if (!d->epns)
return -ENOMEM;
/*
* The UDC core really needs us to have separate and uniquely
* named "parent" devices for each port so we create a sub device
* here for that purpose
*/
d->port_dev = kzalloc_obj(struct device);
if (!d->port_dev) {
rc = -ENOMEM;
goto fail_alloc;
}
device_initialize(d->port_dev);
d->port_dev->release = ast_vhub_dev_release;
d->port_dev->parent = parent;
dev_set_name(d->port_dev, "%s:p%d", dev_name(parent), idx + 1);
rc = device_add(d->port_dev);
if (rc)
goto fail_add;
/* Populate gadget */
INIT_LIST_HEAD(&d->gadget.ep_list);
d->gadget.ops = &ast_vhub_udc_ops;
d->gadget.ep0 = &d->ep0.ep;
d->gadget.name = KBUILD_MODNAME;
if (vhub->force_usb1)
d->gadget.max_speed = USB_SPEED_FULL;
else
d->gadget.max_speed = USB_SPEED_HIGH;
d->gadget.speed = USB_SPEED_UNKNOWN;
d->gadget.dev.of_node = vhub->pdev->dev.of_node;
dev_set_of_node_reused(&d->gadget.dev);
rc = usb_add_gadget_udc(d->port_dev, &d->gadget);
if (rc != 0)
goto fail_udc;
d->registered = true;
return 0;
fail_udc:
device_del(d->port_dev);
fail_add:
put_device(d->port_dev);
fail_alloc:
kfree(d->epns);
return rc;
}
@@ -0,0 +1,521 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* aspeed-vhub -- Driver for Aspeed SoC "vHub" USB gadget
*
* ep0.c - Endpoint 0 handling
*
* Copyright 2017 IBM Corporation
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <linux/prefetch.h>
#include <linux/clk.h>
#include <linux/usb/gadget.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/dma-mapping.h>
#include "vhub.h"
int ast_vhub_reply(struct ast_vhub_ep *ep, char *ptr, int len)
{
struct usb_request *req = &ep->ep0.req.req;
int rc;
if (WARN_ON(ep->d_idx != 0))
return std_req_stall;
if (WARN_ON(!ep->ep0.dir_in))
return std_req_stall;
if (WARN_ON(len > AST_VHUB_EP0_MAX_PACKET))
return std_req_stall;
if (WARN_ON(req->status == -EINPROGRESS))
return std_req_stall;
req->buf = ptr;
req->length = len;
req->complete = NULL;
req->zero = true;
/*
* Call internal queue directly after dropping the lock. This is
* safe to do as the reply is always the last thing done when
* processing a SETUP packet, usually as a tail call
*/
spin_unlock(&ep->vhub->lock);
if (ep->ep.ops->queue(&ep->ep, req, GFP_ATOMIC))
rc = std_req_stall;
else
rc = std_req_data;
spin_lock(&ep->vhub->lock);
return rc;
}
int __ast_vhub_simple_reply(struct ast_vhub_ep *ep, int len, ...)
{
u8 *buffer = ep->buf;
unsigned int i;
va_list args;
va_start(args, len);
/* Copy data directly into EP buffer */
for (i = 0; i < len; i++)
buffer[i] = va_arg(args, int);
va_end(args);
/* req->buf NULL means data is already there */
return ast_vhub_reply(ep, NULL, len);
}
void ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep)
{
struct usb_ctrlrequest crq;
enum std_req_rc std_req_rc;
int rc = -ENODEV;
if (WARN_ON(ep->d_idx != 0))
return;
/*
* Grab the setup packet from the chip and byteswap
* interesting fields
*/
memcpy_fromio(&crq, ep->ep0.setup, sizeof(crq));
EPDBG(ep, "SETUP packet %02x/%02x/%04x/%04x/%04x [%s] st=%d\n",
crq.bRequestType, crq.bRequest,
le16_to_cpu(crq.wValue),
le16_to_cpu(crq.wIndex),
le16_to_cpu(crq.wLength),
(crq.bRequestType & USB_DIR_IN) ? "in" : "out",
ep->ep0.state);
/*
* Check our state, cancel pending requests if needed
*
* Note: Under some circumstances, we can get a new setup
* packet while waiting for the stall ack, just accept it.
*
* In any case, a SETUP packet in wrong state should have
* reset the HW state machine, so let's just log, nuke
* requests, move on.
*/
if (ep->ep0.state != ep0_state_token &&
ep->ep0.state != ep0_state_stall) {
EPDBG(ep, "wrong state\n");
ast_vhub_nuke(ep, -EIO);
}
/* Calculate next state for EP0 */
ep->ep0.state = ep0_state_data;
ep->ep0.dir_in = !!(crq.bRequestType & USB_DIR_IN);
/* If this is the vHub, we handle requests differently */
std_req_rc = std_req_driver;
if (ep->dev == NULL) {
if ((crq.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
std_req_rc = ast_vhub_std_hub_request(ep, &crq);
else if ((crq.bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS)
std_req_rc = ast_vhub_class_hub_request(ep, &crq);
else
std_req_rc = std_req_stall;
} else if ((crq.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
std_req_rc = ast_vhub_std_dev_request(ep, &crq);
/* Act upon result */
switch(std_req_rc) {
case std_req_complete:
goto complete;
case std_req_stall:
goto stall;
case std_req_driver:
break;
case std_req_data:
return;
}
/* Pass request up to the gadget driver */
if (WARN_ON(!ep->dev))
goto stall;
if (ep->dev->driver) {
EPDBG(ep, "forwarding to gadget...\n");
spin_unlock(&ep->vhub->lock);
rc = ep->dev->driver->setup(&ep->dev->gadget, &crq);
spin_lock(&ep->vhub->lock);
EPDBG(ep, "driver returned %d\n", rc);
} else {
EPDBG(ep, "no gadget for request !\n");
}
if (rc >= 0)
return;
stall:
EPDBG(ep, "stalling\n");
writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat);
ep->ep0.state = ep0_state_stall;
ep->ep0.dir_in = false;
return;
complete:
EPVDBG(ep, "sending [in] status with no data\n");
writel(VHUB_EP0_TX_BUFF_RDY, ep->ep0.ctlstat);
ep->ep0.state = ep0_state_status;
ep->ep0.dir_in = false;
}
static void ast_vhub_ep0_do_send(struct ast_vhub_ep *ep,
struct ast_vhub_req *req)
{
unsigned int chunk;
u32 reg;
/* If this is a 0-length request, it's the gadget trying to
* send a status on our behalf. We take it from here.
*/
if (req->req.length == 0)
req->last_desc = 1;
/* Are we done ? Complete request, otherwise wait for next interrupt */
if (req->last_desc >= 0) {
EPVDBG(ep, "complete send %d/%d\n",
req->req.actual, req->req.length);
ep->ep0.state = ep0_state_status;
writel(VHUB_EP0_RX_BUFF_RDY, ep->ep0.ctlstat);
ast_vhub_done(ep, req, 0);
return;
}
/*
* Next chunk cropped to max packet size. Also check if this
* is the last packet
*/
chunk = req->req.length - req->req.actual;
if (chunk > ep->ep.maxpacket)
chunk = ep->ep.maxpacket;
else if ((chunk < ep->ep.maxpacket) || !req->req.zero)
req->last_desc = 1;
EPVDBG(ep, "send chunk=%d last=%d, req->act=%d mp=%d\n",
chunk, req->last_desc, req->req.actual, ep->ep.maxpacket);
/*
* Copy data if any (internal requests already have data
* in the EP buffer)
*/
if (chunk && req->req.buf)
memcpy(ep->buf, req->req.buf + req->req.actual, chunk);
vhub_dma_workaround(ep->buf);
/* Remember chunk size and trigger send */
reg = VHUB_EP0_SET_TX_LEN(chunk);
writel(reg, ep->ep0.ctlstat);
writel(reg | VHUB_EP0_TX_BUFF_RDY, ep->ep0.ctlstat);
req->req.actual += chunk;
}
static void ast_vhub_ep0_rx_prime(struct ast_vhub_ep *ep)
{
EPVDBG(ep, "rx prime\n");
/* Prime endpoint for receiving data */
writel(VHUB_EP0_RX_BUFF_RDY, ep->ep0.ctlstat);
}
static void ast_vhub_ep0_do_receive(struct ast_vhub_ep *ep, struct ast_vhub_req *req,
unsigned int len)
{
unsigned int remain;
int rc = 0;
/* We are receiving... grab request */
remain = req->req.length - req->req.actual;
EPVDBG(ep, "receive got=%d remain=%d\n", len, remain);
/* Are we getting more than asked ? */
if (len > remain) {
EPDBG(ep, "receiving too much (ovf: %d) !\n",
len - remain);
len = remain;
rc = -EOVERFLOW;
}
/* Hardware return wrong data len */
if (len < ep->ep.maxpacket && len != remain) {
EPDBG(ep, "using expected data len instead\n");
len = remain;
}
if (len && req->req.buf)
memcpy(req->req.buf + req->req.actual, ep->buf, len);
req->req.actual += len;
/* Done ? */
if (len < ep->ep.maxpacket || len == remain) {
ep->ep0.state = ep0_state_status;
writel(VHUB_EP0_TX_BUFF_RDY, ep->ep0.ctlstat);
ast_vhub_done(ep, req, rc);
} else
ast_vhub_ep0_rx_prime(ep);
}
void ast_vhub_ep0_handle_ack(struct ast_vhub_ep *ep, bool in_ack)
{
struct ast_vhub_req *req;
struct ast_vhub *vhub = ep->vhub;
struct device *dev = &vhub->pdev->dev;
bool stall = false;
u32 stat;
/* Read EP0 status */
stat = readl(ep->ep0.ctlstat);
/* Grab current request if any */
req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue);
EPVDBG(ep, "ACK status=%08x,state=%d is_in=%d in_ack=%d req=%p\n",
stat, ep->ep0.state, ep->ep0.dir_in, in_ack, req);
switch(ep->ep0.state) {
case ep0_state_token:
/* There should be no request queued in that state... */
if (req) {
dev_warn(dev, "request present while in TOKEN state\n");
ast_vhub_nuke(ep, -EINVAL);
}
dev_warn(dev, "ack while in TOKEN state\n");
stall = true;
break;
case ep0_state_data:
/* Check the state bits corresponding to our direction */
if ((ep->ep0.dir_in && (stat & VHUB_EP0_TX_BUFF_RDY)) ||
(!ep->ep0.dir_in && (stat & VHUB_EP0_RX_BUFF_RDY)) ||
(ep->ep0.dir_in != in_ack)) {
/* In that case, ignore interrupt */
dev_warn(dev, "irq state mismatch");
break;
}
/*
* We are in data phase and there's no request, something is
* wrong, stall
*/
if (!req) {
dev_warn(dev, "data phase, no request\n");
stall = true;
break;
}
/* We have a request, handle data transfers */
if (ep->ep0.dir_in)
ast_vhub_ep0_do_send(ep, req);
else
ast_vhub_ep0_do_receive(ep, req, VHUB_EP0_RX_LEN(stat));
return;
case ep0_state_status:
/* Nuke stale requests */
if (req) {
dev_warn(dev, "request present while in STATUS state\n");
ast_vhub_nuke(ep, -EINVAL);
}
/*
* If the status phase completes with the wrong ack, stall
* the endpoint just in case, to abort whatever the host
* was doing.
*/
if (ep->ep0.dir_in == in_ack) {
dev_warn(dev, "status direction mismatch\n");
stall = true;
}
break;
case ep0_state_stall:
/*
* There shouldn't be any request left, but nuke just in case
* otherwise the stale request will block subsequent ones
*/
ast_vhub_nuke(ep, -EIO);
break;
}
/* Reset to token state or stall */
if (stall) {
writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat);
ep->ep0.state = ep0_state_stall;
} else
ep->ep0.state = ep0_state_token;
}
static int ast_vhub_ep0_queue(struct usb_ep* u_ep, struct usb_request *u_req,
gfp_t gfp_flags)
{
struct ast_vhub_req *req = to_ast_req(u_req);
struct ast_vhub_ep *ep = to_ast_ep(u_ep);
struct ast_vhub *vhub = ep->vhub;
struct device *dev = &vhub->pdev->dev;
unsigned long flags;
/* Paranoid cheks */
if (!u_req || (!u_req->complete && !req->internal)) {
dev_warn(dev, "Bogus EP0 request ! u_req=%p\n", u_req);
if (u_req) {
dev_warn(dev, "complete=%p internal=%d\n",
u_req->complete, req->internal);
}
return -EINVAL;
}
/* Not endpoint 0 ? */
if (WARN_ON(ep->d_idx != 0))
return -EINVAL;
/* Disabled device */
if (ep->dev && !ep->dev->enabled)
return -ESHUTDOWN;
/* Data, no buffer and not internal ? */
if (u_req->length && !u_req->buf && !req->internal) {
dev_warn(dev, "Request with no buffer !\n");
return -EINVAL;
}
EPVDBG(ep, "enqueue req @%p\n", req);
EPVDBG(ep, " l=%d zero=%d noshort=%d is_in=%d\n",
u_req->length, u_req->zero,
u_req->short_not_ok, ep->ep0.dir_in);
/* Initialize request progress fields */
u_req->status = -EINPROGRESS;
u_req->actual = 0;
req->last_desc = -1;
req->active = false;
spin_lock_irqsave(&vhub->lock, flags);
/* EP0 can only support a single request at a time */
if (!list_empty(&ep->queue) ||
ep->ep0.state == ep0_state_token ||
ep->ep0.state == ep0_state_stall) {
dev_warn(dev, "EP0: Request in wrong state\n");
EPVDBG(ep, "EP0: list_empty=%d state=%d\n",
list_empty(&ep->queue), ep->ep0.state);
spin_unlock_irqrestore(&vhub->lock, flags);
return -EBUSY;
}
/* Add request to list and kick processing if empty */
list_add_tail(&req->queue, &ep->queue);
if (ep->ep0.dir_in) {
/* IN request, send data */
ast_vhub_ep0_do_send(ep, req);
} else if (u_req->length == 0) {
/* 0-len request, send completion as rx */
EPVDBG(ep, "0-length rx completion\n");
ep->ep0.state = ep0_state_status;
writel(VHUB_EP0_TX_BUFF_RDY, ep->ep0.ctlstat);
ast_vhub_done(ep, req, 0);
} else {
/* OUT request, start receiver */
ast_vhub_ep0_rx_prime(ep);
}
spin_unlock_irqrestore(&vhub->lock, flags);
return 0;
}
static int ast_vhub_ep0_dequeue(struct usb_ep* u_ep, struct usb_request *u_req)
{
struct ast_vhub_ep *ep = to_ast_ep(u_ep);
struct ast_vhub *vhub = ep->vhub;
struct ast_vhub_req *req;
unsigned long flags;
int rc = -EINVAL;
spin_lock_irqsave(&vhub->lock, flags);
/* Only one request can be in the queue */
req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue);
/* Is it ours ? */
if (req && u_req == &req->req) {
EPVDBG(ep, "dequeue req @%p\n", req);
/*
* We don't have to deal with "active" as all
* DMAs go to the EP buffers, not the request.
*/
ast_vhub_done(ep, req, -ECONNRESET);
/* We do stall the EP to clean things up in HW */
writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat);
ep->ep0.state = ep0_state_status;
ep->ep0.dir_in = false;
rc = 0;
}
spin_unlock_irqrestore(&vhub->lock, flags);
return rc;
}
static const struct usb_ep_ops ast_vhub_ep0_ops = {
.queue = ast_vhub_ep0_queue,
.dequeue = ast_vhub_ep0_dequeue,
.alloc_request = ast_vhub_alloc_request,
.free_request = ast_vhub_free_request,
};
void ast_vhub_reset_ep0(struct ast_vhub_dev *dev)
{
struct ast_vhub_ep *ep = &dev->ep0;
ast_vhub_nuke(ep, -EIO);
ep->ep0.state = ep0_state_token;
}
void ast_vhub_init_ep0(struct ast_vhub *vhub, struct ast_vhub_ep *ep,
struct ast_vhub_dev *dev)
{
memset(ep, 0, sizeof(*ep));
INIT_LIST_HEAD(&ep->ep.ep_list);
INIT_LIST_HEAD(&ep->queue);
ep->ep.ops = &ast_vhub_ep0_ops;
ep->ep.name = "ep0";
ep->ep.caps.type_control = true;
usb_ep_set_maxpacket_limit(&ep->ep, AST_VHUB_EP0_MAX_PACKET);
ep->d_idx = 0;
ep->dev = dev;
ep->vhub = vhub;
ep->ep0.state = ep0_state_token;
INIT_LIST_HEAD(&ep->ep0.req.queue);
ep->ep0.req.internal = true;
/* Small difference between vHub and devices */
if (dev) {
ep->ep0.ctlstat = dev->regs + AST_VHUB_DEV_EP0_CTRL;
ep->ep0.setup = vhub->regs +
AST_VHUB_SETUP0 + 8 * (dev->index + 1);
ep->buf = vhub->ep0_bufs +
AST_VHUB_EP0_MAX_PACKET * (dev->index + 1);
ep->buf_dma = vhub->ep0_bufs_dma +
AST_VHUB_EP0_MAX_PACKET * (dev->index + 1);
} else {
ep->ep0.ctlstat = vhub->regs + AST_VHUB_EP0_CTRL;
ep->ep0.setup = vhub->regs + AST_VHUB_SETUP0;
ep->buf = vhub->ep0_bufs;
ep->buf_dma = vhub->ep0_bufs_dma;
}
}
@@ -0,0 +1,854 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* aspeed-vhub -- Driver for Aspeed SoC "vHub" USB gadget
*
* epn.c - Generic endpoints management
*
* Copyright 2017 IBM Corporation
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <linux/prefetch.h>
#include <linux/clk.h>
#include <linux/usb/gadget.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/dma-mapping.h>
#include "vhub.h"
#define EXTRA_CHECKS
#ifdef EXTRA_CHECKS
#define CHECK(ep, expr, fmt...) \
do { \
if (!(expr)) EPDBG(ep, "CHECK:" fmt); \
} while(0)
#else
#define CHECK(ep, expr, fmt...) do { } while(0)
#endif
static void ast_vhub_epn_kick(struct ast_vhub_ep *ep, struct ast_vhub_req *req)
{
unsigned int act = req->req.actual;
unsigned int len = req->req.length;
unsigned int chunk;
/* There should be no DMA ongoing */
WARN_ON(req->active);
/* Calculate next chunk size */
chunk = len - act;
if (chunk > ep->ep.maxpacket)
chunk = ep->ep.maxpacket;
else if ((chunk < ep->ep.maxpacket) || !req->req.zero)
req->last_desc = 1;
EPVDBG(ep, "kick req %p act=%d/%d chunk=%d last=%d\n",
req, act, len, chunk, req->last_desc);
/* If DMA unavailable, using staging EP buffer */
if (!req->req.dma) {
/* For IN transfers, copy data over first */
if (ep->epn.is_in) {
memcpy(ep->buf, req->req.buf + act, chunk);
vhub_dma_workaround(ep->buf);
}
writel(ep->buf_dma, ep->epn.regs + AST_VHUB_EP_DESC_BASE);
} else {
if (ep->epn.is_in)
vhub_dma_workaround(req->req.buf);
writel(req->req.dma + act, ep->epn.regs + AST_VHUB_EP_DESC_BASE);
}
/* Start DMA */
req->active = true;
writel(VHUB_EP_DMA_SET_TX_SIZE(chunk),
ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
writel(VHUB_EP_DMA_SET_TX_SIZE(chunk) | VHUB_EP_DMA_SINGLE_KICK,
ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
}
static void ast_vhub_epn_handle_ack(struct ast_vhub_ep *ep)
{
struct ast_vhub_req *req;
unsigned int len;
int status = 0;
u32 stat;
/* Read EP status */
stat = readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
/* Grab current request if any */
req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue);
EPVDBG(ep, "ACK status=%08x is_in=%d, req=%p (active=%d)\n",
stat, ep->epn.is_in, req, req ? req->active : 0);
/* In absence of a request, bail out, must have been dequeued */
if (!req)
return;
/*
* Request not active, move on to processing queue, active request
* was probably dequeued
*/
if (!req->active)
goto next_chunk;
/* Check if HW has moved on */
if (VHUB_EP_DMA_RPTR(stat) != 0) {
EPDBG(ep, "DMA read pointer not 0 !\n");
return;
}
/* No current DMA ongoing */
req->active = false;
/* Grab length out of HW */
len = VHUB_EP_DMA_TX_SIZE(stat);
/* If not using DMA, copy data out if needed */
if (!req->req.dma && !ep->epn.is_in && len) {
if (req->req.actual + len > req->req.length) {
req->last_desc = 1;
status = -EOVERFLOW;
goto done;
} else {
memcpy(req->req.buf + req->req.actual, ep->buf, len);
}
}
/* Adjust size */
req->req.actual += len;
/* Check for short packet */
if (len < ep->ep.maxpacket)
req->last_desc = 1;
done:
/* That's it ? complete the request and pick a new one */
if (req->last_desc >= 0) {
ast_vhub_done(ep, req, status);
req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req,
queue);
/*
* Due to lock dropping inside "done" the next request could
* already be active, so check for that and bail if needed.
*/
if (!req || req->active)
return;
}
next_chunk:
ast_vhub_epn_kick(ep, req);
}
static inline unsigned int ast_vhub_count_free_descs(struct ast_vhub_ep *ep)
{
/*
* d_next == d_last means descriptor list empty to HW,
* thus we can only have AST_VHUB_DESCS_COUNT-1 descriptors
* in the list
*/
return (ep->epn.d_last + AST_VHUB_DESCS_COUNT - ep->epn.d_next - 1) &
(AST_VHUB_DESCS_COUNT - 1);
}
static void ast_vhub_epn_kick_desc(struct ast_vhub_ep *ep,
struct ast_vhub_req *req)
{
struct ast_vhub_desc *desc = NULL;
unsigned int act = req->act_count;
unsigned int len = req->req.length;
unsigned int chunk;
/* Mark request active if not already */
req->active = true;
/* If the request was already completely written, do nothing */
if (req->last_desc >= 0)
return;
EPVDBG(ep, "kick act=%d/%d chunk_max=%d free_descs=%d\n",
act, len, ep->epn.chunk_max, ast_vhub_count_free_descs(ep));
/* While we can create descriptors */
while (ast_vhub_count_free_descs(ep) && req->last_desc < 0) {
unsigned int d_num;
/* Grab next free descriptor */
d_num = ep->epn.d_next;
desc = &ep->epn.descs[d_num];
ep->epn.d_next = (d_num + 1) & (AST_VHUB_DESCS_COUNT - 1);
/* Calculate next chunk size */
chunk = len - act;
if (chunk <= ep->epn.chunk_max) {
/*
* Is this the last packet ? Because of having up to 8
* packets in a descriptor we can't just compare "chunk"
* with ep.maxpacket. We have to see if it's a multiple
* of it to know if we have to send a zero packet.
* Sadly that involves a modulo which is a bit expensive
* but probably still better than not doing it.
*/
if (!chunk || !req->req.zero || (chunk % ep->ep.maxpacket) != 0)
req->last_desc = d_num;
} else {
chunk = ep->epn.chunk_max;
}
EPVDBG(ep, " chunk: act=%d/%d chunk=%d last=%d desc=%d free=%d\n",
act, len, chunk, req->last_desc, d_num,
ast_vhub_count_free_descs(ep));
/* Populate descriptor */
desc->w0 = cpu_to_le32(req->req.dma + act);
/* Interrupt if end of request or no more descriptors */
/*
* TODO: Be smarter about it, if we don't have enough
* descriptors request an interrupt before queue empty
* or so in order to be able to populate more before
* the HW runs out. This isn't a problem at the moment
* as we use 256 descriptors and only put at most one
* request in the ring.
*/
desc->w1 = cpu_to_le32(VHUB_DSC1_IN_SET_LEN(chunk));
if (req->last_desc >= 0 || !ast_vhub_count_free_descs(ep))
desc->w1 |= cpu_to_le32(VHUB_DSC1_IN_INTERRUPT);
/* Account packet */
req->act_count = act = act + chunk;
}
if (likely(desc))
vhub_dma_workaround(desc);
/* Tell HW about new descriptors */
writel(VHUB_EP_DMA_SET_CPU_WPTR(ep->epn.d_next),
ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
EPVDBG(ep, "HW kicked, d_next=%d dstat=%08x\n",
ep->epn.d_next, readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS));
}
static void ast_vhub_epn_handle_ack_desc(struct ast_vhub_ep *ep)
{
struct ast_vhub_req *req;
unsigned int len, d_last;
u32 stat, stat1;
/* Read EP status, workaround HW race */
do {
stat = readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
stat1 = readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
} while(stat != stat1);
/* Extract RPTR */
d_last = VHUB_EP_DMA_RPTR(stat);
/* Grab current request if any */
req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue);
EPVDBG(ep, "ACK status=%08x is_in=%d ep->d_last=%d..%d\n",
stat, ep->epn.is_in, ep->epn.d_last, d_last);
/* Check all completed descriptors */
while (ep->epn.d_last != d_last) {
struct ast_vhub_desc *desc;
unsigned int d_num;
bool is_last_desc;
/* Grab next completed descriptor */
d_num = ep->epn.d_last;
desc = &ep->epn.descs[d_num];
ep->epn.d_last = (d_num + 1) & (AST_VHUB_DESCS_COUNT - 1);
/* Grab len out of descriptor */
len = VHUB_DSC1_IN_LEN(le32_to_cpu(desc->w1));
EPVDBG(ep, " desc %d len=%d req=%p (act=%d)\n",
d_num, len, req, req ? req->active : 0);
/* If no active request pending, move on */
if (!req || !req->active)
continue;
/* Adjust size */
req->req.actual += len;
/* Is that the last chunk ? */
is_last_desc = req->last_desc == d_num;
CHECK(ep, is_last_desc == (len < ep->ep.maxpacket ||
(req->req.actual >= req->req.length &&
!req->req.zero)),
"Last packet discrepancy: last_desc=%d len=%d r.act=%d "
"r.len=%d r.zero=%d mp=%d\n",
is_last_desc, len, req->req.actual, req->req.length,
req->req.zero, ep->ep.maxpacket);
if (is_last_desc) {
/*
* Because we can only have one request at a time
* in our descriptor list in this implementation,
* d_last and ep->d_last should now be equal
*/
CHECK(ep, d_last == ep->epn.d_last,
"DMA read ptr mismatch %d vs %d\n",
d_last, ep->epn.d_last);
/* Note: done will drop and re-acquire the lock */
ast_vhub_done(ep, req, 0);
req = list_first_entry_or_null(&ep->queue,
struct ast_vhub_req,
queue);
break;
}
}
/* More work ? */
if (req)
ast_vhub_epn_kick_desc(ep, req);
}
void ast_vhub_epn_ack_irq(struct ast_vhub_ep *ep)
{
if (ep->epn.desc_mode)
ast_vhub_epn_handle_ack_desc(ep);
else
ast_vhub_epn_handle_ack(ep);
}
static int ast_vhub_epn_queue(struct usb_ep* u_ep, struct usb_request *u_req,
gfp_t gfp_flags)
{
struct ast_vhub_req *req = to_ast_req(u_req);
struct ast_vhub_ep *ep = to_ast_ep(u_ep);
struct ast_vhub *vhub = ep->vhub;
unsigned long flags;
bool empty;
int rc;
/* Paranoid checks */
if (!u_req || !u_req->complete || !u_req->buf) {
dev_warn(&vhub->pdev->dev, "Bogus EPn request ! u_req=%p\n", u_req);
if (u_req) {
dev_warn(&vhub->pdev->dev, "complete=%p internal=%d\n",
u_req->complete, req->internal);
}
return -EINVAL;
}
/* Endpoint enabled ? */
if (!ep->epn.enabled || !u_ep->desc || !ep->dev || !ep->d_idx ||
!ep->dev->enabled) {
EPDBG(ep, "Enqueuing request on wrong or disabled EP\n");
return -ESHUTDOWN;
}
/* Map request for DMA if possible. For now, the rule for DMA is
* that:
*
* * For single stage mode (no descriptors):
*
* - The buffer is aligned to a 8 bytes boundary (HW requirement)
* - For a OUT endpoint, the request size is a multiple of the EP
* packet size (otherwise the controller will DMA past the end
* of the buffer if the host is sending a too long packet).
*
* * For descriptor mode (tx only for now), always.
*
* We could relax the latter by making the decision to use the bounce
* buffer based on the size of a given *segment* of the request rather
* than the whole request.
*/
if (ep->epn.desc_mode ||
((((unsigned long)u_req->buf & 7) == 0) &&
(ep->epn.is_in || !(u_req->length & (u_ep->maxpacket - 1))))) {
rc = usb_gadget_map_request_by_dev(&vhub->pdev->dev, u_req,
ep->epn.is_in);
if (rc) {
dev_warn(&vhub->pdev->dev,
"Request mapping failure %d\n", rc);
return rc;
}
} else
u_req->dma = 0;
EPVDBG(ep, "enqueue req @%p\n", req);
EPVDBG(ep, " l=%d dma=0x%x zero=%d noshort=%d noirq=%d is_in=%d\n",
u_req->length, (u32)u_req->dma, u_req->zero,
u_req->short_not_ok, u_req->no_interrupt,
ep->epn.is_in);
/* Initialize request progress fields */
u_req->status = -EINPROGRESS;
u_req->actual = 0;
req->act_count = 0;
req->active = false;
req->last_desc = -1;
spin_lock_irqsave(&vhub->lock, flags);
empty = list_empty(&ep->queue);
/* Add request to list and kick processing if empty */
list_add_tail(&req->queue, &ep->queue);
if (empty) {
if (ep->epn.desc_mode)
ast_vhub_epn_kick_desc(ep, req);
else
ast_vhub_epn_kick(ep, req);
}
spin_unlock_irqrestore(&vhub->lock, flags);
return 0;
}
static void ast_vhub_stop_active_req(struct ast_vhub_ep *ep,
bool restart_ep)
{
u32 state, reg, loops;
/* Stop DMA activity */
if (ep->epn.desc_mode)
writel(VHUB_EP_DMA_CTRL_RESET, ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
else
writel(0, ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
/* Wait for it to complete */
for (loops = 0; loops < 1000; loops++) {
state = readl(ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
state = VHUB_EP_DMA_PROC_STATUS(state);
if (state == EP_DMA_PROC_RX_IDLE ||
state == EP_DMA_PROC_TX_IDLE)
break;
udelay(1);
}
if (loops >= 1000)
dev_warn(&ep->vhub->pdev->dev, "Timeout waiting for DMA\n");
/* If we don't have to restart the endpoint, that's it */
if (!restart_ep)
return;
/* Restart the endpoint */
if (ep->epn.desc_mode) {
/*
* Take out descriptors by resetting the DMA read
* pointer to be equal to the CPU write pointer.
*
* Note: If we ever support creating descriptors for
* requests that aren't the head of the queue, we
* may have to do something more complex here,
* especially if the request being taken out is
* not the current head descriptors.
*/
reg = VHUB_EP_DMA_SET_RPTR(ep->epn.d_next) |
VHUB_EP_DMA_SET_CPU_WPTR(ep->epn.d_next);
writel(reg, ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
/* Then turn it back on */
writel(ep->epn.dma_conf,
ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
} else {
/* Single mode: just turn it back on */
writel(ep->epn.dma_conf,
ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
}
}
static int ast_vhub_epn_dequeue(struct usb_ep* u_ep, struct usb_request *u_req)
{
struct ast_vhub_ep *ep = to_ast_ep(u_ep);
struct ast_vhub *vhub = ep->vhub;
struct ast_vhub_req *req = NULL, *iter;
unsigned long flags;
int rc = -EINVAL;
spin_lock_irqsave(&vhub->lock, flags);
/* Make sure it's actually queued on this endpoint */
list_for_each_entry(iter, &ep->queue, queue) {
if (&iter->req != u_req)
continue;
req = iter;
break;
}
if (req) {
EPVDBG(ep, "dequeue req @%p active=%d\n",
req, req->active);
if (req->active)
ast_vhub_stop_active_req(ep, true);
ast_vhub_done(ep, req, -ECONNRESET);
rc = 0;
}
spin_unlock_irqrestore(&vhub->lock, flags);
return rc;
}
void ast_vhub_update_epn_stall(struct ast_vhub_ep *ep)
{
u32 reg;
if (WARN_ON(ep->d_idx == 0))
return;
reg = readl(ep->epn.regs + AST_VHUB_EP_CONFIG);
if (ep->epn.stalled || ep->epn.wedged)
reg |= VHUB_EP_CFG_STALL_CTRL;
else
reg &= ~VHUB_EP_CFG_STALL_CTRL;
writel(reg, ep->epn.regs + AST_VHUB_EP_CONFIG);
if (!ep->epn.stalled && !ep->epn.wedged)
writel(VHUB_EP_TOGGLE_SET_EPNUM(ep->epn.g_idx),
ep->vhub->regs + AST_VHUB_EP_TOGGLE);
}
static int ast_vhub_set_halt_and_wedge(struct usb_ep* u_ep, bool halt,
bool wedge)
{
struct ast_vhub_ep *ep = to_ast_ep(u_ep);
struct ast_vhub *vhub = ep->vhub;
unsigned long flags;
EPDBG(ep, "Set halt (%d) & wedge (%d)\n", halt, wedge);
if (!u_ep || !u_ep->desc)
return -EINVAL;
if (ep->d_idx == 0)
return 0;
if (ep->epn.is_iso)
return -EOPNOTSUPP;
spin_lock_irqsave(&vhub->lock, flags);
/* Fail with still-busy IN endpoints */
if (halt && ep->epn.is_in && !list_empty(&ep->queue)) {
spin_unlock_irqrestore(&vhub->lock, flags);
return -EAGAIN;
}
ep->epn.stalled = halt;
ep->epn.wedged = wedge;
ast_vhub_update_epn_stall(ep);
spin_unlock_irqrestore(&vhub->lock, flags);
return 0;
}
static int ast_vhub_epn_set_halt(struct usb_ep *u_ep, int value)
{
return ast_vhub_set_halt_and_wedge(u_ep, value != 0, false);
}
static int ast_vhub_epn_set_wedge(struct usb_ep *u_ep)
{
return ast_vhub_set_halt_and_wedge(u_ep, true, true);
}
static int ast_vhub_epn_disable(struct usb_ep* u_ep)
{
struct ast_vhub_ep *ep = to_ast_ep(u_ep);
struct ast_vhub *vhub = ep->vhub;
unsigned long flags;
u32 imask, ep_ier;
EPDBG(ep, "Disabling !\n");
spin_lock_irqsave(&vhub->lock, flags);
ep->epn.enabled = false;
/* Stop active DMA if any */
ast_vhub_stop_active_req(ep, false);
/* Disable endpoint */
writel(0, ep->epn.regs + AST_VHUB_EP_CONFIG);
/* Disable ACK interrupt */
imask = VHUB_EP_IRQ(ep->epn.g_idx);
ep_ier = readl(vhub->regs + AST_VHUB_EP_ACK_IER);
ep_ier &= ~imask;
writel(ep_ier, vhub->regs + AST_VHUB_EP_ACK_IER);
writel(imask, vhub->regs + AST_VHUB_EP_ACK_ISR);
/* Nuke all pending requests */
ast_vhub_nuke(ep, -ESHUTDOWN);
/* No more descriptor associated with request */
ep->ep.desc = NULL;
spin_unlock_irqrestore(&vhub->lock, flags);
return 0;
}
static int ast_vhub_epn_enable(struct usb_ep* u_ep,
const struct usb_endpoint_descriptor *desc)
{
struct ast_vhub_ep *ep = to_ast_ep(u_ep);
struct ast_vhub_dev *dev;
struct ast_vhub *vhub;
u16 maxpacket, type;
unsigned long flags;
u32 ep_conf, ep_ier, imask;
/* Check arguments */
if (!u_ep || !desc)
return -EINVAL;
maxpacket = usb_endpoint_maxp(desc);
if (!ep->d_idx || !ep->dev ||
desc->bDescriptorType != USB_DT_ENDPOINT ||
maxpacket == 0 || maxpacket > ep->ep.maxpacket) {
EPDBG(ep, "Invalid EP enable,d_idx=%d,dev=%p,type=%d,mp=%d/%d\n",
ep->d_idx, ep->dev, desc->bDescriptorType,
maxpacket, ep->ep.maxpacket);
return -EINVAL;
}
if (ep->d_idx != usb_endpoint_num(desc)) {
EPDBG(ep, "EP number mismatch !\n");
return -EINVAL;
}
if (ep->epn.enabled) {
EPDBG(ep, "Already enabled\n");
return -EBUSY;
}
dev = ep->dev;
vhub = ep->vhub;
/* Check device state */
if (!dev->driver) {
EPDBG(ep, "Bogus device state: driver=%p speed=%d\n",
dev->driver, dev->gadget.speed);
return -ESHUTDOWN;
}
/* Grab some info from the descriptor */
ep->epn.is_in = usb_endpoint_dir_in(desc);
ep->ep.maxpacket = maxpacket;
type = usb_endpoint_type(desc);
ep->epn.d_next = ep->epn.d_last = 0;
ep->epn.is_iso = false;
ep->epn.stalled = false;
ep->epn.wedged = false;
EPDBG(ep, "Enabling [%s] %s num %d maxpacket=%d\n",
ep->epn.is_in ? "in" : "out", usb_ep_type_string(type),
usb_endpoint_num(desc), maxpacket);
/* Can we use DMA descriptor mode ? */
ep->epn.desc_mode = ep->epn.descs && ep->epn.is_in;
if (ep->epn.desc_mode)
memset(ep->epn.descs, 0, 8 * AST_VHUB_DESCS_COUNT);
/*
* Large send function can send up to 8 packets from
* one descriptor with a limit of 4095 bytes.
*/
ep->epn.chunk_max = ep->ep.maxpacket;
if (ep->epn.is_in) {
ep->epn.chunk_max <<= 3;
while (ep->epn.chunk_max > 4095)
ep->epn.chunk_max -= ep->ep.maxpacket;
}
switch(type) {
case USB_ENDPOINT_XFER_CONTROL:
EPDBG(ep, "Only one control endpoint\n");
return -EINVAL;
case USB_ENDPOINT_XFER_INT:
ep_conf = VHUB_EP_CFG_SET_TYPE(EP_TYPE_INT);
break;
case USB_ENDPOINT_XFER_BULK:
ep_conf = VHUB_EP_CFG_SET_TYPE(EP_TYPE_BULK);
break;
case USB_ENDPOINT_XFER_ISOC:
ep_conf = VHUB_EP_CFG_SET_TYPE(EP_TYPE_ISO);
ep->epn.is_iso = true;
break;
default:
return -EINVAL;
}
/* Encode the rest of the EP config register */
if (maxpacket < 1024)
ep_conf |= VHUB_EP_CFG_SET_MAX_PKT(maxpacket);
if (!ep->epn.is_in)
ep_conf |= VHUB_EP_CFG_DIR_OUT;
ep_conf |= VHUB_EP_CFG_SET_EP_NUM(usb_endpoint_num(desc));
ep_conf |= VHUB_EP_CFG_ENABLE;
ep_conf |= VHUB_EP_CFG_SET_DEV(dev->index + 1);
EPVDBG(ep, "config=%08x\n", ep_conf);
spin_lock_irqsave(&vhub->lock, flags);
/* Disable HW and reset DMA */
writel(0, ep->epn.regs + AST_VHUB_EP_CONFIG);
writel(VHUB_EP_DMA_CTRL_RESET,
ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
/* Configure and enable */
writel(ep_conf, ep->epn.regs + AST_VHUB_EP_CONFIG);
if (ep->epn.desc_mode) {
/* Clear DMA status, including the DMA read ptr */
writel(0, ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
/* Set descriptor base */
writel(ep->epn.descs_dma,
ep->epn.regs + AST_VHUB_EP_DESC_BASE);
/* Set base DMA config value */
ep->epn.dma_conf = VHUB_EP_DMA_DESC_MODE;
if (ep->epn.is_in)
ep->epn.dma_conf |= VHUB_EP_DMA_IN_LONG_MODE;
/* First reset and disable all operations */
writel(ep->epn.dma_conf | VHUB_EP_DMA_CTRL_RESET,
ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
/* Enable descriptor mode */
writel(ep->epn.dma_conf,
ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
} else {
/* Set base DMA config value */
ep->epn.dma_conf = VHUB_EP_DMA_SINGLE_STAGE;
/* Reset and switch to single stage mode */
writel(ep->epn.dma_conf | VHUB_EP_DMA_CTRL_RESET,
ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
writel(ep->epn.dma_conf,
ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
writel(0, ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
}
/* Cleanup data toggle just in case */
writel(VHUB_EP_TOGGLE_SET_EPNUM(ep->epn.g_idx),
vhub->regs + AST_VHUB_EP_TOGGLE);
/* Cleanup and enable ACK interrupt */
imask = VHUB_EP_IRQ(ep->epn.g_idx);
writel(imask, vhub->regs + AST_VHUB_EP_ACK_ISR);
ep_ier = readl(vhub->regs + AST_VHUB_EP_ACK_IER);
ep_ier |= imask;
writel(ep_ier, vhub->regs + AST_VHUB_EP_ACK_IER);
/* Woot, we are online ! */
ep->epn.enabled = true;
spin_unlock_irqrestore(&vhub->lock, flags);
return 0;
}
static void ast_vhub_epn_dispose(struct usb_ep *u_ep)
{
struct ast_vhub_ep *ep = to_ast_ep(u_ep);
if (WARN_ON(!ep->dev || !ep->d_idx))
return;
EPDBG(ep, "Releasing endpoint\n");
/* Take it out of the EP list */
list_del_init(&ep->ep.ep_list);
/* Mark the address free in the device */
ep->dev->epns[ep->d_idx - 1] = NULL;
/* Free name & DMA buffers */
kfree(ep->ep.name);
ep->ep.name = NULL;
dma_free_coherent(&ep->vhub->pdev->dev,
AST_VHUB_EPn_MAX_PACKET +
8 * AST_VHUB_DESCS_COUNT,
ep->buf, ep->buf_dma);
ep->buf = NULL;
ep->epn.descs = NULL;
/* Mark free */
ep->dev = NULL;
}
static const struct usb_ep_ops ast_vhub_epn_ops = {
.enable = ast_vhub_epn_enable,
.disable = ast_vhub_epn_disable,
.dispose = ast_vhub_epn_dispose,
.queue = ast_vhub_epn_queue,
.dequeue = ast_vhub_epn_dequeue,
.set_halt = ast_vhub_epn_set_halt,
.set_wedge = ast_vhub_epn_set_wedge,
.alloc_request = ast_vhub_alloc_request,
.free_request = ast_vhub_free_request,
};
struct ast_vhub_ep *ast_vhub_alloc_epn(struct ast_vhub_dev *d, u8 addr)
{
struct ast_vhub *vhub = d->vhub;
struct ast_vhub_ep *ep;
unsigned long flags;
int i;
/* Find a free one (no device) */
spin_lock_irqsave(&vhub->lock, flags);
for (i = 0; i < vhub->max_epns; i++)
if (vhub->epns[i].dev == NULL)
break;
if (i >= vhub->max_epns) {
spin_unlock_irqrestore(&vhub->lock, flags);
return NULL;
}
/* Set it up */
ep = &vhub->epns[i];
ep->dev = d;
spin_unlock_irqrestore(&vhub->lock, flags);
DDBG(d, "Allocating gen EP %d for addr %d\n", i, addr);
INIT_LIST_HEAD(&ep->queue);
ep->d_idx = addr;
ep->vhub = vhub;
ep->ep.ops = &ast_vhub_epn_ops;
ep->ep.name = kasprintf(GFP_KERNEL, "ep%d", addr);
d->epns[addr-1] = ep;
ep->epn.g_idx = i;
ep->epn.regs = vhub->regs + 0x200 + (i * 0x10);
ep->buf = dma_alloc_coherent(&vhub->pdev->dev,
AST_VHUB_EPn_MAX_PACKET +
8 * AST_VHUB_DESCS_COUNT,
&ep->buf_dma, GFP_KERNEL);
if (!ep->buf) {
kfree(ep->ep.name);
ep->ep.name = NULL;
return NULL;
}
ep->epn.descs = ep->buf + AST_VHUB_EPn_MAX_PACKET;
ep->epn.descs_dma = ep->buf_dma + AST_VHUB_EPn_MAX_PACKET;
usb_ep_set_maxpacket_limit(&ep->ep, AST_VHUB_EPn_MAX_PACKET);
list_add_tail(&ep->ep.ep_list, &d->gadget.ep_list);
ep->ep.caps.type_iso = true;
ep->ep.caps.type_bulk = true;
ep->ep.caps.type_int = true;
ep->ep.caps.dir_in = true;
ep->ep.caps.dir_out = true;
return ep;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,566 @@
/* SPDX-License-Identifier: GPL-2.0+ */
#ifndef __ASPEED_VHUB_H
#define __ASPEED_VHUB_H
#include <linux/usb.h>
#include <linux/usb/ch11.h>
/*****************************
* *
* VHUB register definitions *
* *
*****************************/
#define AST_VHUB_CTRL 0x00 /* Root Function Control & Status Register */
#define AST_VHUB_CONF 0x04 /* Root Configuration Setting Register */
#define AST_VHUB_IER 0x08 /* Interrupt Ctrl Register */
#define AST_VHUB_ISR 0x0C /* Interrupt Status Register */
#define AST_VHUB_EP_ACK_IER 0x10 /* Programmable Endpoint Pool ACK Interrupt Enable Register */
#define AST_VHUB_EP_NACK_IER 0x14 /* Programmable Endpoint Pool NACK Interrupt Enable Register */
#define AST_VHUB_EP_ACK_ISR 0x18 /* Programmable Endpoint Pool ACK Interrupt Status Register */
#define AST_VHUB_EP_NACK_ISR 0x1C /* Programmable Endpoint Pool NACK Interrupt Status Register */
#define AST_VHUB_SW_RESET 0x20 /* Device Controller Soft Reset Enable Register */
#define AST_VHUB_USBSTS 0x24 /* USB Status Register */
#define AST_VHUB_EP_TOGGLE 0x28 /* Programmable Endpoint Pool Data Toggle Value Set */
#define AST_VHUB_ISO_FAIL_ACC 0x2C /* Isochronous Transaction Fail Accumulator */
#define AST_VHUB_EP0_CTRL 0x30 /* Endpoint 0 Contrl/Status Register */
#define AST_VHUB_EP0_DATA 0x34 /* Base Address of Endpoint 0 In/OUT Data Buffer Register */
#define AST_VHUB_EP1_CTRL 0x38 /* Endpoint 1 Contrl/Status Register */
#define AST_VHUB_EP1_STS_CHG 0x3C /* Endpoint 1 Status Change Bitmap Data */
#define AST_VHUB_SETUP0 0x80 /* Root Device Setup Data Buffer0 */
#define AST_VHUB_SETUP1 0x84 /* Root Device Setup Data Buffer1 */
/* Main control reg */
#define VHUB_CTRL_PHY_CLK (1 << 31)
#define VHUB_CTRL_PHY_LOOP_TEST (1 << 25)
#define VHUB_CTRL_DN_PWN (1 << 24)
#define VHUB_CTRL_DP_PWN (1 << 23)
#define VHUB_CTRL_LONG_DESC (1 << 18)
#define VHUB_CTRL_ISO_RSP_CTRL (1 << 17)
#define VHUB_CTRL_SPLIT_IN (1 << 16)
#define VHUB_CTRL_LOOP_T_RESULT (1 << 15)
#define VHUB_CTRL_LOOP_T_STS (1 << 14)
#define VHUB_CTRL_PHY_BIST_RESULT (1 << 13)
#define VHUB_CTRL_PHY_BIST_CTRL (1 << 12)
#define VHUB_CTRL_PHY_RESET_DIS (1 << 11)
#define VHUB_CTRL_SET_TEST_MODE(x) ((x) << 8)
#define VHUB_CTRL_MANUAL_REMOTE_WAKEUP (1 << 4)
#define VHUB_CTRL_AUTO_REMOTE_WAKEUP (1 << 3)
#define VHUB_CTRL_CLK_STOP_SUSPEND (1 << 2)
#define VHUB_CTRL_FULL_SPEED_ONLY (1 << 1)
#define VHUB_CTRL_UPSTREAM_CONNECT (1 << 0)
/* IER & ISR */
#define VHUB_IRQ_DEV1_BIT 9
#define VHUB_IRQ_USB_CMD_DEADLOCK (1 << 18)
#define VHUB_IRQ_EP_POOL_NAK (1 << 17)
#define VHUB_IRQ_EP_POOL_ACK_STALL (1 << 16)
#define VHUB_IRQ_DEVICE1 (1 << (VHUB_IRQ_DEV1_BIT))
#define VHUB_IRQ_BUS_RESUME (1 << 8)
#define VHUB_IRQ_BUS_SUSPEND (1 << 7)
#define VHUB_IRQ_BUS_RESET (1 << 6)
#define VHUB_IRQ_HUB_EP1_IN_DATA_ACK (1 << 5)
#define VHUB_IRQ_HUB_EP0_IN_DATA_NAK (1 << 4)
#define VHUB_IRQ_HUB_EP0_IN_ACK_STALL (1 << 3)
#define VHUB_IRQ_HUB_EP0_OUT_NAK (1 << 2)
#define VHUB_IRQ_HUB_EP0_OUT_ACK_STALL (1 << 1)
#define VHUB_IRQ_HUB_EP0_SETUP (1 << 0)
#define VHUB_IRQ_ACK_ALL 0x1ff
/* Downstream device IRQ mask. */
#define VHUB_DEV_IRQ(n) (VHUB_IRQ_DEVICE1 << (n))
/* SW reset reg */
#define VHUB_SW_RESET_EP_POOL (1 << 9)
#define VHUB_SW_RESET_DMA_CONTROLLER (1 << 8)
#define VHUB_SW_RESET_DEVICE5 (1 << 5)
#define VHUB_SW_RESET_DEVICE4 (1 << 4)
#define VHUB_SW_RESET_DEVICE3 (1 << 3)
#define VHUB_SW_RESET_DEVICE2 (1 << 2)
#define VHUB_SW_RESET_DEVICE1 (1 << 1)
#define VHUB_SW_RESET_ROOT_HUB (1 << 0)
/* EP ACK/NACK IRQ masks */
#define VHUB_EP_IRQ(n) (1 << (n))
/* USB status reg */
#define VHUB_USBSTS_HISPEED (1 << 27)
/* EP toggle */
#define VHUB_EP_TOGGLE_VALUE (1 << 8)
#define VHUB_EP_TOGGLE_SET_EPNUM(x) ((x) & 0x1f)
/* HUB EP0 control */
#define VHUB_EP0_CTRL_STALL (1 << 0)
#define VHUB_EP0_TX_BUFF_RDY (1 << 1)
#define VHUB_EP0_RX_BUFF_RDY (1 << 2)
#define VHUB_EP0_RX_LEN(x) (((x) >> 16) & 0x7f)
#define VHUB_EP0_SET_TX_LEN(x) (((x) & 0x7f) << 8)
/* HUB EP1 control */
#define VHUB_EP1_CTRL_RESET_TOGGLE (1 << 2)
#define VHUB_EP1_CTRL_STALL (1 << 1)
#define VHUB_EP1_CTRL_ENABLE (1 << 0)
/***********************************
* *
* per-device register definitions *
* *
***********************************/
#define AST_VHUB_DEV_EN_CTRL 0x00
#define AST_VHUB_DEV_ISR 0x04
#define AST_VHUB_DEV_EP0_CTRL 0x08
#define AST_VHUB_DEV_EP0_DATA 0x0c
/* Device enable control */
#define VHUB_DEV_EN_SET_ADDR(x) ((x) << 8)
#define VHUB_DEV_EN_ADDR_MASK ((0xff) << 8)
#define VHUB_DEV_EN_EP0_NAK_IRQEN (1 << 6)
#define VHUB_DEV_EN_EP0_IN_ACK_IRQEN (1 << 5)
#define VHUB_DEV_EN_EP0_OUT_NAK_IRQEN (1 << 4)
#define VHUB_DEV_EN_EP0_OUT_ACK_IRQEN (1 << 3)
#define VHUB_DEV_EN_EP0_SETUP_IRQEN (1 << 2)
#define VHUB_DEV_EN_SPEED_SEL_HIGH (1 << 1)
#define VHUB_DEV_EN_ENABLE_PORT (1 << 0)
/* Interrupt status */
#define VHUV_DEV_IRQ_EP0_IN_DATA_NACK (1 << 4)
#define VHUV_DEV_IRQ_EP0_IN_ACK_STALL (1 << 3)
#define VHUV_DEV_IRQ_EP0_OUT_DATA_NACK (1 << 2)
#define VHUV_DEV_IRQ_EP0_OUT_ACK_STALL (1 << 1)
#define VHUV_DEV_IRQ_EP0_SETUP (1 << 0)
/* Control bits.
*
* Note: The driver relies on the bulk of those bits
* matching corresponding vHub EP0 control bits
*/
#define VHUB_DEV_EP0_CTRL_STALL VHUB_EP0_CTRL_STALL
#define VHUB_DEV_EP0_TX_BUFF_RDY VHUB_EP0_TX_BUFF_RDY
#define VHUB_DEV_EP0_RX_BUFF_RDY VHUB_EP0_RX_BUFF_RDY
#define VHUB_DEV_EP0_RX_LEN(x) VHUB_EP0_RX_LEN(x)
#define VHUB_DEV_EP0_SET_TX_LEN(x) VHUB_EP0_SET_TX_LEN(x)
/*************************************
* *
* per-endpoint register definitions *
* *
*************************************/
#define AST_VHUB_EP_CONFIG 0x00
#define AST_VHUB_EP_DMA_CTLSTAT 0x04
#define AST_VHUB_EP_DESC_BASE 0x08
#define AST_VHUB_EP_DESC_STATUS 0x0C
/* EP config reg */
#define VHUB_EP_CFG_SET_MAX_PKT(x) (((x) & 0x3ff) << 16)
#define VHUB_EP_CFG_AUTO_DATA_DISABLE (1 << 13)
#define VHUB_EP_CFG_STALL_CTRL (1 << 12)
#define VHUB_EP_CFG_SET_EP_NUM(x) (((x) & 0xf) << 8)
#define VHUB_EP_CFG_SET_TYPE(x) ((x) << 5)
#define EP_TYPE_OFF 0
#define EP_TYPE_BULK 1
#define EP_TYPE_INT 2
#define EP_TYPE_ISO 3
#define VHUB_EP_CFG_DIR_OUT (1 << 4)
#define VHUB_EP_CFG_SET_DEV(x) ((x) << 1)
#define VHUB_EP_CFG_ENABLE (1 << 0)
/* EP DMA control */
#define VHUB_EP_DMA_PROC_STATUS(x) (((x) >> 4) & 0xf)
#define EP_DMA_PROC_RX_IDLE 0
#define EP_DMA_PROC_TX_IDLE 8
#define VHUB_EP_DMA_IN_LONG_MODE (1 << 3)
#define VHUB_EP_DMA_OUT_CONTIG_MODE (1 << 3)
#define VHUB_EP_DMA_CTRL_RESET (1 << 2)
#define VHUB_EP_DMA_SINGLE_STAGE (1 << 1)
#define VHUB_EP_DMA_DESC_MODE (1 << 0)
/* EP DMA status */
#define VHUB_EP_DMA_SET_TX_SIZE(x) ((x) << 16)
#define VHUB_EP_DMA_TX_SIZE(x) (((x) >> 16) & 0x7ff)
#define VHUB_EP_DMA_RPTR(x) (((x) >> 8) & 0xff)
#define VHUB_EP_DMA_SET_RPTR(x) (((x) & 0xff) << 8)
#define VHUB_EP_DMA_SET_CPU_WPTR(x) (x)
#define VHUB_EP_DMA_SINGLE_KICK (1 << 0) /* WPTR = 1 for single mode */
/*******************************
* *
* DMA descriptors definitions *
* *
*******************************/
/* Desc W1 IN */
#define VHUB_DSC1_IN_INTERRUPT (1 << 31)
#define VHUB_DSC1_IN_SPID_DATA0 (0 << 14)
#define VHUB_DSC1_IN_SPID_DATA2 (1 << 14)
#define VHUB_DSC1_IN_SPID_DATA1 (2 << 14)
#define VHUB_DSC1_IN_SPID_MDATA (3 << 14)
#define VHUB_DSC1_IN_SET_LEN(x) ((x) & 0xfff)
#define VHUB_DSC1_IN_LEN(x) ((x) & 0xfff)
/****************************************
* *
* Data structures and misc definitions *
* *
****************************************/
/*
* AST_VHUB_NUM_GEN_EPs and AST_VHUB_NUM_PORTS are kept to avoid breaking
* existing AST2400/AST2500 platforms. AST2600 and future vhub revisions
* should define number of downstream ports and endpoints in device tree.
*/
#define AST_VHUB_NUM_GEN_EPs 15 /* Generic non-0 EPs */
#define AST_VHUB_NUM_PORTS 5 /* vHub ports */
#define AST_VHUB_EP0_MAX_PACKET 64 /* EP0's max packet size */
#define AST_VHUB_EPn_MAX_PACKET 1024 /* Generic EPs max packet size */
#define AST_VHUB_DESCS_COUNT 256 /* Use 256 descriptor mode (valid
* values are 256 and 32)
*/
struct ast_vhub;
struct ast_vhub_dev;
/*
* DMA descriptor (generic EPs only, currently only used
* for IN endpoints
*/
struct ast_vhub_desc {
__le32 w0;
__le32 w1;
};
/* A transfer request, either core-originated or internal */
struct ast_vhub_req {
struct usb_request req;
struct list_head queue;
/* Actual count written to descriptors (desc mode only) */
unsigned int act_count;
/*
* Desc number of the final packet or -1. For non-desc
* mode (or ep0), any >= 0 value means "last packet"
*/
int last_desc;
/* Request active (pending DMAs) */
bool active : 1;
/* Internal request (don't call back core) */
bool internal : 1;
};
#define to_ast_req(__ureq) container_of(__ureq, struct ast_vhub_req, req)
/* Current state of an EP0 */
enum ep0_state {
ep0_state_token,
ep0_state_data,
ep0_state_status,
ep0_state_stall,
};
/*
* An endpoint, either generic, ep0, actual gadget EP
* or internal use vhub EP0. vhub EP1 doesn't have an
* associated structure as it's mostly HW managed.
*/
struct ast_vhub_ep {
struct usb_ep ep;
/* Request queue */
struct list_head queue;
/* EP index in the device, 0 means this is an EP0 */
unsigned int d_idx;
/* Dev pointer or NULL for vHub EP0 */
struct ast_vhub_dev *dev;
/* vHub itself */
struct ast_vhub *vhub;
/*
* DMA buffer for EP0, fallback DMA buffer for misaligned
* OUT transfers for generic EPs
*/
void *buf;
dma_addr_t buf_dma;
/* The rest depends on the EP type */
union {
/* EP0 (either device or vhub) */
struct {
/*
* EP0 registers are "similar" for
* vHub and devices but located in
* different places.
*/
void __iomem *ctlstat;
void __iomem *setup;
/* Current state & direction */
enum ep0_state state;
bool dir_in;
/* Internal use request */
struct ast_vhub_req req;
} ep0;
/* Generic endpoint (aka EPn) */
struct {
/* Registers */
void __iomem *regs;
/* Index in global pool (zero-based) */
unsigned int g_idx;
/* DMA Descriptors */
struct ast_vhub_desc *descs;
dma_addr_t descs_dma;
unsigned int d_next;
unsigned int d_last;
unsigned int dma_conf;
/* Max chunk size for IN EPs */
unsigned int chunk_max;
/* State flags */
bool is_in : 1;
bool is_iso : 1;
bool stalled : 1;
bool wedged : 1;
bool enabled : 1;
bool desc_mode : 1;
} epn;
};
};
#define to_ast_ep(__uep) container_of(__uep, struct ast_vhub_ep, ep)
/* A device attached to a vHub port */
struct ast_vhub_dev {
struct ast_vhub *vhub;
void __iomem *regs;
/* Device index (zero-based) and name string */
unsigned int index;
const char *name;
/* sysfs enclosure for the gadget gunk */
struct device *port_dev;
/* Link to gadget core */
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
bool registered : 1;
bool wakeup_en : 1;
bool enabled : 1;
/* Endpoint structures */
struct ast_vhub_ep ep0;
struct ast_vhub_ep **epns;
u32 max_epns;
};
#define to_ast_dev(__g) container_of(__g, struct ast_vhub_dev, gadget)
/* Per vhub port stateinfo structure */
struct ast_vhub_port {
/* Port status & status change registers */
u16 status;
u16 change;
/* Associated device slot */
struct ast_vhub_dev dev;
};
struct ast_vhub_full_cdesc {
struct usb_config_descriptor cfg;
struct usb_interface_descriptor intf;
struct usb_endpoint_descriptor ep;
} __packed;
/* Global vhub structure */
struct ast_vhub {
struct platform_device *pdev;
void __iomem *regs;
int irq;
spinlock_t lock;
struct work_struct wake_work;
struct clk *clk;
struct reset_control *rst;
/* EP0 DMA buffers allocated in one chunk */
void *ep0_bufs;
dma_addr_t ep0_bufs_dma;
/* EP0 of the vhub itself */
struct ast_vhub_ep ep0;
/* State of vhub ep1 */
bool ep1_stalled : 1;
/* Per-port info */
struct ast_vhub_port *ports;
u32 max_ports;
u32 port_irq_mask;
/* Generic EP data structures */
struct ast_vhub_ep *epns;
u32 max_epns;
/* Upstream bus is suspended ? */
bool suspended : 1;
/* Hub itself can signal remote wakeup */
bool wakeup_en : 1;
/* Force full speed only */
bool force_usb1 : 1;
/* Upstream bus speed captured at bus reset */
unsigned int speed;
/* Standard USB Descriptors of the vhub. */
struct usb_device_descriptor vhub_dev_desc;
struct ast_vhub_full_cdesc vhub_conf_desc;
struct usb_hub_descriptor vhub_hub_desc;
struct list_head vhub_str_desc;
struct usb_qualifier_descriptor vhub_qual_desc;
};
/* Standard request handlers result codes */
enum std_req_rc {
std_req_stall = -1, /* Stall requested */
std_req_complete = 0, /* Request completed with no data */
std_req_data = 1, /* Request completed with data */
std_req_driver = 2, /* Pass to driver pls */
};
#ifdef CONFIG_USB_GADGET_VERBOSE
#define UDCVDBG(u, fmt...) dev_dbg(&(u)->pdev->dev, fmt)
#define EPVDBG(ep, fmt, ...) do { \
dev_dbg(&(ep)->vhub->pdev->dev, \
"%s:EP%d " fmt, \
(ep)->dev ? (ep)->dev->name : "hub", \
(ep)->d_idx, ##__VA_ARGS__); \
} while(0)
#define DVDBG(d, fmt, ...) do { \
dev_dbg(&(d)->vhub->pdev->dev, \
"%s " fmt, (d)->name, \
##__VA_ARGS__); \
} while(0)
#else
#define UDCVDBG(u, fmt...) do { } while(0)
#define EPVDBG(ep, fmt, ...) do { } while(0)
#define DVDBG(d, fmt, ...) do { } while(0)
#endif
#ifdef CONFIG_USB_GADGET_DEBUG
#define UDCDBG(u, fmt...) dev_dbg(&(u)->pdev->dev, fmt)
#define EPDBG(ep, fmt, ...) do { \
dev_dbg(&(ep)->vhub->pdev->dev, \
"%s:EP%d " fmt, \
(ep)->dev ? (ep)->dev->name : "hub", \
(ep)->d_idx, ##__VA_ARGS__); \
} while(0)
#define DDBG(d, fmt, ...) do { \
dev_dbg(&(d)->vhub->pdev->dev, \
"%s " fmt, (d)->name, \
##__VA_ARGS__); \
} while(0)
#else
#define UDCDBG(u, fmt...) do { } while(0)
#define EPDBG(ep, fmt, ...) do { } while(0)
#define DDBG(d, fmt, ...) do { } while(0)
#endif
static inline void vhub_dma_workaround(void *addr)
{
/*
* This works around a confirmed HW issue with the Aspeed chip.
*
* The core uses a different bus to memory than the AHB going to
* the USB device controller. Due to the latter having a higher
* priority than the core for arbitration on that bus, it's
* possible for an MMIO to the device, followed by a DMA by the
* device from memory to all be performed and services before
* a previous store to memory gets completed.
*
* This the following scenario can happen:
*
* - Driver writes to a DMA descriptor (Mbus)
* - Driver writes to the MMIO register to start the DMA (AHB)
* - The gadget sees the second write and sends a read of the
* descriptor to the memory controller (Mbus)
* - The gadget hits memory before the descriptor write
* causing it to read an obsolete value.
*
* Thankfully the problem is limited to the USB gadget device, other
* masters in the SoC all have a lower priority than the core, thus
* ensuring that the store by the core arrives first.
*
* The workaround consists of using a dummy read of the memory before
* doing the MMIO writes. This will ensure that the previous writes
* have been "pushed out".
*/
mb();
(void)__raw_readl((void __iomem *)addr);
}
/* core.c */
void ast_vhub_done(struct ast_vhub_ep *ep, struct ast_vhub_req *req,
int status);
void ast_vhub_nuke(struct ast_vhub_ep *ep, int status);
struct usb_request *ast_vhub_alloc_request(struct usb_ep *u_ep,
gfp_t gfp_flags);
void ast_vhub_free_request(struct usb_ep *u_ep, struct usb_request *u_req);
void ast_vhub_init_hw(struct ast_vhub *vhub);
/* ep0.c */
void ast_vhub_ep0_handle_ack(struct ast_vhub_ep *ep, bool in_ack);
void ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep);
void ast_vhub_reset_ep0(struct ast_vhub_dev *dev);
void ast_vhub_init_ep0(struct ast_vhub *vhub, struct ast_vhub_ep *ep,
struct ast_vhub_dev *dev);
int ast_vhub_reply(struct ast_vhub_ep *ep, char *ptr, int len);
int __ast_vhub_simple_reply(struct ast_vhub_ep *ep, int len, ...);
#define ast_vhub_simple_reply(udc, ...) \
__ast_vhub_simple_reply((udc), \
sizeof((u8[]) { __VA_ARGS__ })/sizeof(u8), \
__VA_ARGS__)
/* hub.c */
int ast_vhub_init_hub(struct ast_vhub *vhub);
enum std_req_rc ast_vhub_std_hub_request(struct ast_vhub_ep *ep,
struct usb_ctrlrequest *crq);
enum std_req_rc ast_vhub_class_hub_request(struct ast_vhub_ep *ep,
struct usb_ctrlrequest *crq);
void ast_vhub_device_connect(struct ast_vhub *vhub, unsigned int port,
bool on);
void ast_vhub_hub_suspend(struct ast_vhub *vhub);
void ast_vhub_hub_resume(struct ast_vhub *vhub);
void ast_vhub_hub_reset(struct ast_vhub *vhub);
void ast_vhub_hub_wake_all(struct ast_vhub *vhub);
/* dev.c */
int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx);
void ast_vhub_del_dev(struct ast_vhub_dev *d);
void ast_vhub_dev_irq(struct ast_vhub_dev *d);
int ast_vhub_std_dev_request(struct ast_vhub_ep *ep,
struct usb_ctrlrequest *crq);
/* epn.c */
void ast_vhub_epn_ack_irq(struct ast_vhub_ep *ep);
void ast_vhub_update_epn_stall(struct ast_vhub_ep *ep);
struct ast_vhub_ep *ast_vhub_alloc_epn(struct ast_vhub_dev *d, u8 addr);
void ast_vhub_dev_suspend(struct ast_vhub_dev *d);
void ast_vhub_dev_resume(struct ast_vhub_dev *d);
void ast_vhub_dev_reset(struct ast_vhub_dev *d);
#endif /* __ASPEED_VHUB_H */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,177 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2004 by Thomas Rathbone, HP Labs
* Copyright (C) 2005 by Ivan Kokshaysky
* Copyright (C) 2006 by SAN People
*/
#ifndef AT91_UDC_H
#define AT91_UDC_H
/*
* USB Device Port (UDP) registers.
* Based on AT91RM9200 datasheet revision E.
*/
#define AT91_UDP_FRM_NUM 0x00 /* Frame Number Register */
#define AT91_UDP_NUM (0x7ff << 0) /* Frame Number */
#define AT91_UDP_FRM_ERR (1 << 16) /* Frame Error */
#define AT91_UDP_FRM_OK (1 << 17) /* Frame OK */
#define AT91_UDP_GLB_STAT 0x04 /* Global State Register */
#define AT91_UDP_FADDEN (1 << 0) /* Function Address Enable */
#define AT91_UDP_CONFG (1 << 1) /* Configured */
#define AT91_UDP_ESR (1 << 2) /* Enable Send Resume */
#define AT91_UDP_RSMINPR (1 << 3) /* Resume has been sent */
#define AT91_UDP_RMWUPE (1 << 4) /* Remote Wake Up Enable */
#define AT91_UDP_FADDR 0x08 /* Function Address Register */
#define AT91_UDP_FADD (0x7f << 0) /* Function Address Value */
#define AT91_UDP_FEN (1 << 8) /* Function Enable */
#define AT91_UDP_IER 0x10 /* Interrupt Enable Register */
#define AT91_UDP_IDR 0x14 /* Interrupt Disable Register */
#define AT91_UDP_IMR 0x18 /* Interrupt Mask Register */
#define AT91_UDP_ISR 0x1c /* Interrupt Status Register */
#define AT91_UDP_EP(n) (1 << (n)) /* Endpoint Interrupt Status */
#define AT91_UDP_RXSUSP (1 << 8) /* USB Suspend Interrupt Status */
#define AT91_UDP_RXRSM (1 << 9) /* USB Resume Interrupt Status */
#define AT91_UDP_EXTRSM (1 << 10) /* External Resume Interrupt Status [AT91RM9200 only] */
#define AT91_UDP_SOFINT (1 << 11) /* Start of Frame Interrupt Status */
#define AT91_UDP_ENDBUSRES (1 << 12) /* End of Bus Reset Interrupt Status */
#define AT91_UDP_WAKEUP (1 << 13) /* USB Wakeup Interrupt Status [AT91RM9200 only] */
#define AT91_UDP_ICR 0x20 /* Interrupt Clear Register */
#define AT91_UDP_RST_EP 0x28 /* Reset Endpoint Register */
#define AT91_UDP_CSR(n) (0x30+((n)*4)) /* Endpoint Control/Status Registers 0-7 */
#define AT91_UDP_TXCOMP (1 << 0) /* Generates IN packet with data previously written in DPR */
#define AT91_UDP_RX_DATA_BK0 (1 << 1) /* Receive Data Bank 0 */
#define AT91_UDP_RXSETUP (1 << 2) /* Send STALL to the host */
#define AT91_UDP_STALLSENT (1 << 3) /* Stall Sent / Isochronous error (Isochronous endpoints) */
#define AT91_UDP_TXPKTRDY (1 << 4) /* Transmit Packet Ready */
#define AT91_UDP_FORCESTALL (1 << 5) /* Force Stall */
#define AT91_UDP_RX_DATA_BK1 (1 << 6) /* Receive Data Bank 1 */
#define AT91_UDP_DIR (1 << 7) /* Transfer Direction */
#define AT91_UDP_EPTYPE (7 << 8) /* Endpoint Type */
#define AT91_UDP_EPTYPE_CTRL (0 << 8)
#define AT91_UDP_EPTYPE_ISO_OUT (1 << 8)
#define AT91_UDP_EPTYPE_BULK_OUT (2 << 8)
#define AT91_UDP_EPTYPE_INT_OUT (3 << 8)
#define AT91_UDP_EPTYPE_ISO_IN (5 << 8)
#define AT91_UDP_EPTYPE_BULK_IN (6 << 8)
#define AT91_UDP_EPTYPE_INT_IN (7 << 8)
#define AT91_UDP_DTGLE (1 << 11) /* Data Toggle */
#define AT91_UDP_EPEDS (1 << 15) /* Endpoint Enable/Disable */
#define AT91_UDP_RXBYTECNT (0x7ff << 16) /* Number of bytes in FIFO */
#define AT91_UDP_FDR(n) (0x50+((n)*4)) /* Endpoint FIFO Data Registers 0-7 */
#define AT91_UDP_TXVC 0x74 /* Transceiver Control Register */
#define AT91_UDP_TXVC_TXVDIS (1 << 8) /* Transceiver Disable */
#define AT91_UDP_TXVC_PUON (1 << 9) /* PullUp On [AT91SAM9260 only] */
/*-------------------------------------------------------------------------*/
/*
* controller driver data structures
*/
#define NUM_ENDPOINTS 6
/*
* hardware won't disable bus reset, or resume while the controller
* is suspended ... watching suspend helps keep the logic symmetric.
*/
#define MINIMUS_INTERRUPTUS \
(AT91_UDP_ENDBUSRES | AT91_UDP_RXRSM | AT91_UDP_RXSUSP)
struct at91_ep {
struct usb_ep ep;
struct list_head queue;
struct at91_udc *udc;
void __iomem *creg;
unsigned maxpacket:16;
u8 int_mask;
unsigned is_pingpong:1;
unsigned stopped:1;
unsigned is_in:1;
unsigned is_iso:1;
unsigned fifo_bank:1;
};
struct at91_udc_caps {
int (*init)(struct at91_udc *udc);
void (*pullup)(struct at91_udc *udc, int is_on);
};
struct at91_udc_data {
struct gpio_desc *vbus_pin; /* high == host powering us */
u8 vbus_polled; /* Use polling, not interrupt */
struct gpio_desc *pullup_pin; /* active == D+ pulled up */
};
/*
* driver is non-SMP, and just blocks IRQs whenever it needs
* access protection for chip registers or driver state
*/
struct at91_udc {
struct usb_gadget gadget;
struct at91_ep ep[NUM_ENDPOINTS];
struct usb_gadget_driver *driver;
const struct at91_udc_caps *caps;
unsigned vbus:1;
unsigned enabled:1;
unsigned clocked:1;
unsigned suspended:1;
unsigned req_pending:1;
unsigned wait_for_addr_ack:1;
unsigned wait_for_config_ack:1;
unsigned active_suspend:1;
u8 addr;
struct at91_udc_data board;
struct clk *iclk, *fclk;
struct platform_device *pdev;
struct proc_dir_entry *pde;
void __iomem *udp_baseaddr;
int udp_irq;
spinlock_t lock;
struct timer_list vbus_timer;
struct work_struct vbus_timer_work;
struct regmap *matrix;
};
static inline struct at91_udc *to_udc(struct usb_gadget *g)
{
return container_of(g, struct at91_udc, gadget);
}
struct at91_request {
struct usb_request req;
struct list_head queue;
};
/*-------------------------------------------------------------------------*/
#ifdef VERBOSE_DEBUG
# define VDBG DBG
#else
# define VDBG(stuff...) do{}while(0)
#endif
#ifdef PACKET_TRACE
# define PACKET VDBG
#else
# define PACKET(stuff...) do{}while(0)
#endif
#define ERR(stuff...) pr_err("udc: " stuff)
#define WARNING(stuff...) pr_warn("udc: " stuff)
#define INFO(stuff...) pr_info("udc: " stuff)
#define DBG(stuff...) pr_debug("udc: " stuff)
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,381 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for the Atmel USBA high speed USB device controller
*
* Copyright (C) 2005-2007 Atmel Corporation
*/
#ifndef __LINUX_USB_GADGET_USBA_UDC_H__
#define __LINUX_USB_GADGET_USBA_UDC_H__
#include <linux/gpio/consumer.h>
/* USB register offsets */
#define USBA_CTRL 0x0000
#define USBA_FNUM 0x0004
#define USBA_INT_ENB 0x0010
#define USBA_INT_STA 0x0014
#define USBA_INT_CLR 0x0018
#define USBA_EPT_RST 0x001c
#define USBA_TST 0x00e0
/* USB endpoint register offsets */
#define USBA_EPT_CFG 0x0000
#define USBA_EPT_CTL_ENB 0x0004
#define USBA_EPT_CTL_DIS 0x0008
#define USBA_EPT_CTL 0x000c
#define USBA_EPT_SET_STA 0x0014
#define USBA_EPT_CLR_STA 0x0018
#define USBA_EPT_STA 0x001c
/* USB DMA register offsets */
#define USBA_DMA_NXT_DSC 0x0000
#define USBA_DMA_ADDRESS 0x0004
#define USBA_DMA_CONTROL 0x0008
#define USBA_DMA_STATUS 0x000c
/* Bitfields in CTRL */
#define USBA_DEV_ADDR_OFFSET 0
#define USBA_DEV_ADDR_SIZE 7
#define USBA_FADDR_EN (1 << 7)
#define USBA_EN_USBA (1 << 8)
#define USBA_DETACH (1 << 9)
#define USBA_REMOTE_WAKE_UP (1 << 10)
#define USBA_PULLD_DIS (1 << 11)
#define USBA_ENABLE_MASK (USBA_EN_USBA | USBA_PULLD_DIS)
#define USBA_DISABLE_MASK USBA_DETACH
/* Bitfields in FNUM */
#define USBA_MICRO_FRAME_NUM_OFFSET 0
#define USBA_MICRO_FRAME_NUM_SIZE 3
#define USBA_FRAME_NUMBER_OFFSET 3
#define USBA_FRAME_NUMBER_SIZE 11
#define USBA_FRAME_NUM_ERROR (1 << 31)
/* Bitfields in INT_ENB/INT_STA/INT_CLR */
#define USBA_HIGH_SPEED (1 << 0)
#define USBA_DET_SUSPEND (1 << 1)
#define USBA_MICRO_SOF (1 << 2)
#define USBA_SOF (1 << 3)
#define USBA_END_OF_RESET (1 << 4)
#define USBA_WAKE_UP (1 << 5)
#define USBA_END_OF_RESUME (1 << 6)
#define USBA_UPSTREAM_RESUME (1 << 7)
#define USBA_EPT_INT_OFFSET 8
#define USBA_EPT_INT_SIZE 16
#define USBA_DMA_INT_OFFSET 24
#define USBA_DMA_INT_SIZE 8
/* Bitfields in EPT_RST */
#define USBA_RST_OFFSET 0
#define USBA_RST_SIZE 16
/* Bitfields in USBA_TST */
#define USBA_SPEED_CFG_OFFSET 0
#define USBA_SPEED_CFG_SIZE 2
#define USBA_TST_J_MODE (1 << 2)
#define USBA_TST_K_MODE (1 << 3)
#define USBA_TST_PKT_MODE (1 << 4)
#define USBA_OPMODE2 (1 << 5)
/* Bitfields in EPT_CFG */
#define USBA_EPT_SIZE_OFFSET 0
#define USBA_EPT_SIZE_SIZE 3
#define USBA_EPT_DIR_IN (1 << 3)
#define USBA_EPT_TYPE_OFFSET 4
#define USBA_EPT_TYPE_SIZE 2
#define USBA_BK_NUMBER_OFFSET 6
#define USBA_BK_NUMBER_SIZE 2
#define USBA_NB_TRANS_OFFSET 8
#define USBA_NB_TRANS_SIZE 2
#define USBA_EPT_MAPPED (1 << 31)
/* Bitfields in EPT_CTL/EPT_CTL_ENB/EPT_CTL_DIS */
#define USBA_EPT_ENABLE (1 << 0)
#define USBA_AUTO_VALID (1 << 1)
#define USBA_INTDIS_DMA (1 << 3)
#define USBA_NYET_DIS (1 << 4)
#define USBA_DATAX_RX (1 << 6)
#define USBA_MDATA_RX (1 << 7)
/* Bits 8-15 and 31 enable interrupts for respective bits in EPT_STA */
#define USBA_BUSY_BANK_IE (1 << 18)
/* Bitfields in EPT_SET_STA/EPT_CLR_STA/EPT_STA */
#define USBA_FORCE_STALL (1 << 5)
#define USBA_TOGGLE_CLR (1 << 6)
#define USBA_TOGGLE_SEQ_OFFSET 6
#define USBA_TOGGLE_SEQ_SIZE 2
#define USBA_ERR_OVFLW (1 << 8)
#define USBA_RX_BK_RDY (1 << 9)
#define USBA_KILL_BANK (1 << 9)
#define USBA_TX_COMPLETE (1 << 10)
#define USBA_TX_PK_RDY (1 << 11)
#define USBA_ISO_ERR_TRANS (1 << 11)
#define USBA_RX_SETUP (1 << 12)
#define USBA_ISO_ERR_FLOW (1 << 12)
#define USBA_STALL_SENT (1 << 13)
#define USBA_ISO_ERR_CRC (1 << 13)
#define USBA_ISO_ERR_NBTRANS (1 << 13)
#define USBA_NAK_IN (1 << 14)
#define USBA_ISO_ERR_FLUSH (1 << 14)
#define USBA_NAK_OUT (1 << 15)
#define USBA_CURRENT_BANK_OFFSET 16
#define USBA_CURRENT_BANK_SIZE 2
#define USBA_BUSY_BANKS_OFFSET 18
#define USBA_BUSY_BANKS_SIZE 2
#define USBA_BYTE_COUNT_OFFSET 20
#define USBA_BYTE_COUNT_SIZE 11
#define USBA_SHORT_PACKET (1 << 31)
/* Bitfields in DMA_CONTROL */
#define USBA_DMA_CH_EN (1 << 0)
#define USBA_DMA_LINK (1 << 1)
#define USBA_DMA_END_TR_EN (1 << 2)
#define USBA_DMA_END_BUF_EN (1 << 3)
#define USBA_DMA_END_TR_IE (1 << 4)
#define USBA_DMA_END_BUF_IE (1 << 5)
#define USBA_DMA_DESC_LOAD_IE (1 << 6)
#define USBA_DMA_BURST_LOCK (1 << 7)
#define USBA_DMA_BUF_LEN_OFFSET 16
#define USBA_DMA_BUF_LEN_SIZE 16
/* Bitfields in DMA_STATUS */
#define USBA_DMA_CH_ACTIVE (1 << 1)
#define USBA_DMA_END_TR_ST (1 << 4)
#define USBA_DMA_END_BUF_ST (1 << 5)
#define USBA_DMA_DESC_LOAD_ST (1 << 6)
/* Constants for SPEED_CFG */
#define USBA_SPEED_CFG_NORMAL 0
#define USBA_SPEED_CFG_FORCE_HIGH 2
#define USBA_SPEED_CFG_FORCE_FULL 3
/* Constants for EPT_SIZE */
#define USBA_EPT_SIZE_8 0
#define USBA_EPT_SIZE_16 1
#define USBA_EPT_SIZE_32 2
#define USBA_EPT_SIZE_64 3
#define USBA_EPT_SIZE_128 4
#define USBA_EPT_SIZE_256 5
#define USBA_EPT_SIZE_512 6
#define USBA_EPT_SIZE_1024 7
/* Constants for EPT_TYPE */
#define USBA_EPT_TYPE_CONTROL 0
#define USBA_EPT_TYPE_ISO 1
#define USBA_EPT_TYPE_BULK 2
#define USBA_EPT_TYPE_INT 3
/* Constants for BK_NUMBER */
#define USBA_BK_NUMBER_ZERO 0
#define USBA_BK_NUMBER_ONE 1
#define USBA_BK_NUMBER_DOUBLE 2
#define USBA_BK_NUMBER_TRIPLE 3
/* Bit manipulation macros */
#define USBA_BF(name, value) \
(((value) & ((1 << USBA_##name##_SIZE) - 1)) \
<< USBA_##name##_OFFSET)
#define USBA_BFEXT(name, value) \
(((value) >> USBA_##name##_OFFSET) \
& ((1 << USBA_##name##_SIZE) - 1))
#define USBA_BFINS(name, value, old) \
(((old) & ~(((1 << USBA_##name##_SIZE) - 1) \
<< USBA_##name##_OFFSET)) \
| USBA_BF(name, value))
/* Register access macros */
#define usba_readl(udc, reg) \
readl_relaxed((udc)->regs + USBA_##reg)
#define usba_writel(udc, reg, value) \
writel_relaxed((value), (udc)->regs + USBA_##reg)
#define usba_ep_readl(ep, reg) \
readl_relaxed((ep)->ep_regs + USBA_EPT_##reg)
#define usba_ep_writel(ep, reg, value) \
writel_relaxed((value), (ep)->ep_regs + USBA_EPT_##reg)
#define usba_dma_readl(ep, reg) \
readl_relaxed((ep)->dma_regs + USBA_DMA_##reg)
#define usba_dma_writel(ep, reg, value) \
writel_relaxed((value), (ep)->dma_regs + USBA_DMA_##reg)
/* Calculate base address for a given endpoint or DMA controller */
#define USBA_EPT_BASE(x) (0x100 + (x) * 0x20)
#define USBA_DMA_BASE(x) (0x300 + (x) * 0x10)
#define USBA_FIFO_BASE(x) ((x) << 16)
/* Synth parameters */
#define USBA_NR_DMAS 7
#define EP0_FIFO_SIZE 64
#define EP0_EPT_SIZE USBA_EPT_SIZE_64
#define EP0_NR_BANKS 1
#define FIFO_IOMEM_ID 0
#define CTRL_IOMEM_ID 1
#define DBG_ERR 0x0001 /* report all error returns */
#define DBG_HW 0x0002 /* debug hardware initialization */
#define DBG_GADGET 0x0004 /* calls to/from gadget driver */
#define DBG_INT 0x0008 /* interrupts */
#define DBG_BUS 0x0010 /* report changes in bus state */
#define DBG_QUEUE 0x0020 /* debug request queue processing */
#define DBG_FIFO 0x0040 /* debug FIFO contents */
#define DBG_DMA 0x0080 /* debug DMA handling */
#define DBG_REQ 0x0100 /* print out queued request length */
#define DBG_ALL 0xffff
#define DBG_NONE 0x0000
#define DEBUG_LEVEL (DBG_ERR)
#define DBG(level, fmt, ...) \
do { \
if ((level) & DEBUG_LEVEL) \
pr_debug("udc: " fmt, ## __VA_ARGS__); \
} while (0)
enum usba_ctrl_state {
WAIT_FOR_SETUP,
DATA_STAGE_IN,
DATA_STAGE_OUT,
STATUS_STAGE_IN,
STATUS_STAGE_OUT,
STATUS_STAGE_ADDR,
STATUS_STAGE_TEST,
};
/*
EP_STATE_IDLE,
EP_STATE_SETUP,
EP_STATE_IN_DATA,
EP_STATE_OUT_DATA,
EP_STATE_SET_ADDR_STATUS,
EP_STATE_RX_STATUS,
EP_STATE_TX_STATUS,
EP_STATE_HALT,
*/
struct usba_dma_desc {
dma_addr_t next;
dma_addr_t addr;
u32 ctrl;
};
struct usba_fifo_cfg {
u8 hw_ep_num;
u16 fifo_size;
u8 nr_banks;
};
struct usba_ep {
int state;
void __iomem *ep_regs;
void __iomem *dma_regs;
void __iomem *fifo;
char name[8];
struct usb_ep ep;
struct usba_udc *udc;
struct list_head queue;
u16 fifo_size;
u8 nr_banks;
u8 index;
unsigned int can_dma:1;
unsigned int can_isoc:1;
unsigned int is_isoc:1;
unsigned int is_in:1;
unsigned long ept_cfg;
#ifdef CONFIG_USB_GADGET_DEBUG_FS
u32 last_dma_status;
struct dentry *debugfs_dir;
#endif
};
struct usba_ep_config {
u8 nr_banks;
unsigned int can_dma:1;
unsigned int can_isoc:1;
};
struct usba_request {
struct usb_request req;
struct list_head queue;
u32 ctrl;
unsigned int submitted:1;
unsigned int last_transaction:1;
unsigned int using_dma:1;
unsigned int mapped:1;
};
struct usba_udc_errata {
void (*toggle_bias)(struct usba_udc *udc, int is_on);
void (*pulse_bias)(struct usba_udc *udc);
};
struct usba_udc_config {
const struct usba_udc_errata *errata;
const struct usba_ep_config *config;
const int num_ep;
const bool ep_prealloc;
};
struct usba_udc {
/* Protect hw registers from concurrent modifications */
spinlock_t lock;
/* Mutex to prevent concurrent start or stop */
struct mutex vbus_mutex;
void __iomem *regs;
void __iomem *fifo;
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
struct platform_device *pdev;
const struct usba_udc_errata *errata;
int irq;
struct gpio_desc *vbus_pin;
int num_ep;
struct usba_fifo_cfg *fifo_cfg;
struct clk *pclk;
struct clk *hclk;
struct usba_ep *usba_ep;
bool bias_pulse_needed;
bool clocked;
bool suspended;
bool ep_prealloc;
u16 devstatus;
u16 test_mode;
int vbus_prev;
u32 int_enb_cache;
#ifdef CONFIG_USB_GADGET_DEBUG_FS
struct dentry *debugfs_root;
#endif
struct regmap *pmc;
};
static inline struct usba_ep *to_usba_ep(struct usb_ep *ep)
{
return container_of(ep, struct usba_ep, ep);
}
static inline struct usba_request *to_usba_req(struct usb_request *req)
{
return container_of(req, struct usba_request, req);
}
static inline struct usba_udc *to_usba_udc(struct usb_gadget *gadget)
{
return container_of(gadget, struct usba_udc, gadget);
}
#define ep_is_control(ep) ((ep)->index == 0)
#define ep_is_idle(ep) ((ep)->state == EP_STATE_IDLE)
#endif /* __LINUX_USB_GADGET_USBA_UDC_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,13 @@
# SPDX-License-Identifier: GPL-2.0
config USB_BDC_UDC
tristate "Broadcom USB3.0 device controller IP driver(BDC)"
depends on USB_GADGET && HAS_DMA
default ARCH_BRCMSTB
help
BDC is Broadcom's USB3.0 device controller IP. If your SOC has a BDC IP
then select this driver.
Say "y" here to link the driver statically, or "m" to build a dynamically
linked module called "bdc".
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_USB_BDC_UDC) += bdc.o
bdc-y := bdc_core.o bdc_cmd.o bdc_ep.o bdc_udc.o
ifneq ($(CONFIG_USB_GADGET_VERBOSE),)
bdc-y += bdc_dbg.o
endif
@@ -0,0 +1,489 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* bdc.h - header for the BRCM BDC USB3.0 device controller
*
* Copyright (C) 2014 Broadcom Corporation
*
* Author: Ashwini Pahuja
*/
#ifndef __LINUX_BDC_H__
#define __LINUX_BDC_H__
#include <linux/kernel.h>
#include <linux/usb.h>
#include <linux/device.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/dma-mapping.h>
#include <linux/mm.h>
#include <linux/debugfs.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/unaligned.h>
#define BRCM_BDC_NAME "bdc"
#define BRCM_BDC_DESC "Broadcom USB Device Controller driver"
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
/* BDC command operation timeout in usec*/
#define BDC_CMD_TIMEOUT 1000
/* BDC controller operation timeout in usec*/
#define BDC_COP_TIMEOUT 500
/*
* Maximum size of ep0 response buffer for ch9 requests,
* the set_sel request uses 6 so far, the max.
*/
#define EP0_RESPONSE_BUFF 6
/* Start with SS as default */
#define EP0_MAX_PKT_SIZE 512
/* 64 entries in a SRR */
#define NUM_SR_ENTRIES 64
/* Num of bds per table */
#define NUM_BDS_PER_TABLE 64
/* Num of tables in bd list for control,bulk and Int ep */
#define NUM_TABLES 2
/* Num of tables in bd list for Isoch ep */
#define NUM_TABLES_ISOCH 6
/* U1 Timeout default: 248usec */
#define U1_TIMEOUT 0xf8
/* Interrupt coalescence in usec */
#define INT_CLS 500
/* Register offsets */
/* Configuration and Capability registers */
#define BDC_BDCCFG0 0x00
#define BDC_BDCCFG1 0x04
#define BDC_BDCCAP0 0x08
#define BDC_BDCCAP1 0x0c
#define BDC_CMDPAR0 0x10
#define BDC_CMDPAR1 0x14
#define BDC_CMDPAR2 0x18
#define BDC_CMDSC 0x1c
#define BDC_USPC 0x20
#define BDC_USPPMS 0x28
#define BDC_USPPM2 0x2c
#define BDC_SPBBAL 0x38
#define BDC_SPBBAH 0x3c
#define BDC_BDCSC 0x40
#define BDC_XSFNTF 0x4c
#define BDC_DVCSA 0x50
#define BDC_DVCSB 0x54
#define BDC_EPSTS0 0x60
#define BDC_EPSTS1 0x64
#define BDC_EPSTS2 0x68
#define BDC_EPSTS3 0x6c
#define BDC_EPSTS4 0x70
#define BDC_EPSTS5 0x74
#define BDC_EPSTS6 0x78
#define BDC_EPSTS7 0x7c
#define BDC_SRRBAL(n) (0x200 + ((n) * 0x10))
#define BDC_SRRBAH(n) (0x204 + ((n) * 0x10))
#define BDC_SRRINT(n) (0x208 + ((n) * 0x10))
#define BDC_INTCTLS(n) (0x20c + ((n) * 0x10))
/* Extended capability regs */
#define BDC_FSCNOC 0xcd4
#define BDC_FSCNIC 0xce4
#define NUM_NCS(p) ((p) >> 28)
/* Register bit fields and Masks */
/* BDC Configuration 0 */
#define BDC_PGS(p) (((p) & (0x7 << 8)) >> 8)
#define BDC_SPB(p) ((p) & 0x7)
/* BDC Capability1 */
#define BDC_P64 BIT(0)
/* BDC Command register */
#define BDC_CMD_FH 0xe
#define BDC_CMD_DNC 0x6
#define BDC_CMD_EPO 0x4
#define BDC_CMD_BLA 0x3
#define BDC_CMD_EPC 0x2
#define BDC_CMD_DVC 0x1
#define BDC_CMD_CWS BIT(5)
#define BDC_CMD_CST(p) (((p) & (0xf << 6))>>6)
#define BDC_CMD_EPN(p) (((p) & 0x1f) << 10)
#define BDC_SUB_CMD_ADD (0x1 << 17)
#define BDC_SUB_CMD_FWK (0x4 << 17)
/* Reset sequence number */
#define BDC_CMD_EPO_RST_SN (0x1 << 16)
#define BDC_CMD_EP0_XSD (0x1 << 16)
#define BDC_SUB_CMD_ADD_EP (0x1 << 17)
#define BDC_SUB_CMD_DRP_EP (0x2 << 17)
#define BDC_SUB_CMD_EP_STP (0x2 << 17)
#define BDC_SUB_CMD_EP_STL (0x4 << 17)
#define BDC_SUB_CMD_EP_RST (0x1 << 17)
#define BDC_CMD_SRD BIT(27)
/* CMD completion status */
#define BDC_CMDS_SUCC 0x1
#define BDC_CMDS_PARA 0x3
#define BDC_CMDS_STAT 0x4
#define BDC_CMDS_FAIL 0x5
#define BDC_CMDS_INTL 0x6
#define BDC_CMDS_BUSY 0xf
/* CMDSC Param 2 shifts */
#define EPT_SHIFT 22
#define MP_SHIFT 10
#define MB_SHIFT 6
#define EPM_SHIFT 4
/* BDC USPSC */
#define BDC_VBC BIT(31)
#define BDC_PRC BIT(30)
#define BDC_PCE BIT(29)
#define BDC_CFC BIT(28)
#define BDC_PCC BIT(27)
#define BDC_PSC BIT(26)
#define BDC_VBS BIT(25)
#define BDC_PRS BIT(24)
#define BDC_PCS BIT(23)
#define BDC_PSP(p) (((p) & (0x7 << 20))>>20)
#define BDC_SCN BIT(8)
#define BDC_SDC BIT(7)
#define BDC_SWS BIT(4)
#define BDC_USPSC_RW (BDC_SCN|BDC_SDC|BDC_SWS|0xf)
#define BDC_PSP(p) (((p) & (0x7 << 20))>>20)
#define BDC_SPEED_FS 0x1
#define BDC_SPEED_LS 0x2
#define BDC_SPEED_HS 0x3
#define BDC_SPEED_SS 0x4
#define BDC_PST(p) ((p) & 0xf)
#define BDC_PST_MASK 0xf
/* USPPMS */
#define BDC_U2E BIT(31)
#define BDC_U1E BIT(30)
#define BDC_U2A BIT(29)
#define BDC_PORT_W1S BIT(17)
#define BDC_U1T(p) ((p) & 0xff)
#define BDC_U2T(p) (((p) & 0xff) << 8)
#define BDC_U1T_MASK 0xff
/* USBPM2 */
/* Hardware LPM Enable */
#define BDC_HLE BIT(16)
/* BDC Status and Control */
#define BDC_COP_RST (1 << 29)
#define BDC_COP_RUN (2 << 29)
#define BDC_COP_STP (4 << 29)
#define BDC_COP_MASK (BDC_COP_RST|BDC_COP_RUN|BDC_COP_STP)
#define BDC_COS BIT(28)
#define BDC_CSTS(p) (((p) & (0x7 << 20)) >> 20)
#define BDC_MASK_MCW BIT(7)
#define BDC_GIE BIT(1)
#define BDC_GIP BIT(0)
#define BDC_HLT 1
#define BDC_NOR 2
#define BDC_OIP 7
/* Buffer descriptor and Status report bit fields and masks */
#define BD_TYPE_BITMASK (0xf)
#define BD_CHAIN 0xf
#define BD_TFS_SHIFT 4
#define BD_SOT BIT(26)
#define BD_EOT BIT(27)
#define BD_ISP BIT(29)
#define BD_IOC BIT(30)
#define BD_SBF BIT(31)
#define BD_INTR_TARGET(p) (((p) & 0x1f) << 27)
#define BDC_SRR_RWS BIT(4)
#define BDC_SRR_RST BIT(3)
#define BDC_SRR_ISR BIT(2)
#define BDC_SRR_IE BIT(1)
#define BDC_SRR_IP BIT(0)
#define BDC_SRR_EPI(p) (((p) & (0xff << 24)) >> 24)
#define BDC_SRR_DPI(p) (((p) & (0xff << 16)) >> 16)
#define BDC_SRR_DPI_MASK 0x00ff0000
#define MARK_CHAIN_BD (BD_CHAIN|BD_EOT|BD_SOT)
/* Control transfer BD specific fields */
#define BD_DIR_IN BIT(25)
#define BDC_PTC_MASK 0xf0000000
/* status report defines */
#define SR_XSF 0
#define SR_USPC 4
#define SR_BD_LEN(p) ((p) & 0xffffff)
#define XSF_SUCC 0x1
#define XSF_SHORT 0x3
#define XSF_BABB 0x4
#define XSF_SETUP_RECV 0x6
#define XSF_DATA_START 0x7
#define XSF_STATUS_START 0x8
#define XSF_STS(p) (((p) >> 28) & 0xf)
/* Transfer BD fields */
#define BD_LEN(p) ((p) & 0x1ffff)
#define BD_LTF BIT(25)
#define BD_TYPE_DS 0x1
#define BD_TYPE_SS 0x2
#define BDC_EP_ENABLED BIT(0)
#define BDC_EP_STALL BIT(1)
#define BDC_EP_STOP BIT(2)
/* One BD can transfer max 65536 bytes */
#define BD_MAX_BUFF_SIZE (1 << 16)
/* Maximum bytes in one XFR, Refer to BDC spec */
#define MAX_XFR_LEN 16777215
/* defines for Force Header command */
#define DEV_NOTF_TYPE 6
#define FWK_SUBTYPE 1
#define TRA_PACKET 4
#define to_bdc_ep(e) container_of(e, struct bdc_ep, usb_ep)
#define to_bdc_req(r) container_of(r, struct bdc_req, usb_req)
#define gadget_to_bdc(g) container_of(g, struct bdc, gadget)
/* FUNCTION WAKE DEV NOTIFICATION interval, USB3 spec table 8.13 */
#define BDC_TNOTIFY 2500 /*in ms*/
/* Devstatus bitfields */
#define REMOTE_WAKEUP_ISSUED BIT(16)
#define DEVICE_SUSPENDED BIT(17)
#define FUNC_WAKE_ISSUED BIT(18)
#define REMOTE_WAKE_ENABLE (1 << USB_DEVICE_REMOTE_WAKEUP)
/* On disconnect, preserve these bits and clear rest */
#define DEVSTATUS_CLEAR (1 << USB_DEVICE_SELF_POWERED)
/* Hardware and software Data structures */
/* Endpoint bd: buffer descriptor */
struct bdc_bd {
__le32 offset[4];
};
/* Status report in Status report ring(srr) */
struct bdc_sr {
__le32 offset[4];
};
/* bd_table: contiguous bd's in a table */
struct bd_table {
struct bdc_bd *start_bd;
/* dma address of start bd of table*/
dma_addr_t dma;
};
/*
* Each endpoint has a bdl(buffer descriptor list), bdl consists of 1 or more bd
* table's chained to each other through a chain bd, every table has equal
* number of bds. the software uses bdi(bd index) to refer to particular bd in
* the list.
*/
struct bd_list {
/* Array of bd table pointers*/
struct bd_table **bd_table_array;
/* How many tables chained to each other */
int num_tabs;
/* Max_bdi = num_tabs * num_bds_table - 1 */
int max_bdi;
/* current enq bdi from sw point of view */
int eqp_bdi;
/* current deq bdi from sw point of view */
int hwd_bdi;
/* numbers of bds per table */
int num_bds_table;
};
struct bdc_req;
/* Representation of a transfer, one transfer can have multiple bd's */
struct bd_transfer {
struct bdc_req *req;
/* start bd index */
int start_bdi;
/* this will be the next hw dqp when this transfer completes */
int next_hwd_bdi;
/* number of bds in this transfer */
int num_bds;
};
/*
* Representation of a gadget request, every gadget request is contained
* by 1 bd_transfer.
*/
struct bdc_req {
struct usb_request usb_req;
struct list_head queue;
struct bdc_ep *ep;
/* only one Transfer per request */
struct bd_transfer bd_xfr;
int epnum;
};
/* scratchpad buffer needed by bdc hardware */
struct bdc_scratchpad {
dma_addr_t sp_dma;
void *buff;
u32 size;
};
/* endpoint representation */
struct bdc_ep {
struct usb_ep usb_ep;
struct list_head queue;
struct bdc *bdc;
u8 ep_type;
u8 dir;
u8 ep_num;
const struct usb_ss_ep_comp_descriptor *comp_desc;
const struct usb_endpoint_descriptor *desc;
unsigned int flags;
char name[20];
/* endpoint bd list*/
struct bd_list bd_list;
/*
* HW generates extra event for multi bd tranfers, this flag helps in
* ignoring the extra event
*/
bool ignore_next_sr;
};
/* bdc cmmand parameter structure */
struct bdc_cmd_params {
u32 param2;
u32 param1;
u32 param0;
};
/* status report ring(srr), currently one srr is supported for entire system */
struct srr {
struct bdc_sr *sr_bds;
u16 eqp_index;
u16 dqp_index;
dma_addr_t dma_addr;
};
/* EP0 states */
enum bdc_ep0_state {
WAIT_FOR_SETUP = 0,
WAIT_FOR_DATA_START,
WAIT_FOR_DATA_XMIT,
WAIT_FOR_STATUS_START,
WAIT_FOR_STATUS_XMIT,
STATUS_PENDING
};
/* Link states */
enum bdc_link_state {
BDC_LINK_STATE_U0 = 0x00,
BDC_LINK_STATE_U3 = 0x03,
BDC_LINK_STATE_RX_DET = 0x05,
BDC_LINK_STATE_RESUME = 0x0f
};
/* representation of bdc */
struct bdc {
struct usb_gadget gadget;
struct usb_gadget_driver *gadget_driver;
struct device *dev;
/* device lock */
spinlock_t lock;
/* generic phy */
struct phy **phys;
int num_phys;
/* num of endpoints for a particular instantiation of IP */
unsigned int num_eps;
/*
* Array of ep's, it uses the same index covention as bdc hw i.e.
* 1 for ep0, 2 for 1out,3 for 1in ....
*/
struct bdc_ep **bdc_ep_array;
void __iomem *regs;
struct bdc_scratchpad scratchpad;
u32 sp_buff_size;
/* current driver supports 1 status ring */
struct srr srr;
/* Last received setup packet */
struct usb_ctrlrequest setup_pkt;
struct bdc_req ep0_req;
struct bdc_req status_req;
enum bdc_ep0_state ep0_state;
bool delayed_status;
bool zlp_needed;
bool reinit;
bool pullup;
/* Bits 0-15 are standard and 16-31 for proprietary information */
u32 devstatus;
int irq;
void *mem;
u32 dev_addr;
/* DMA pools */
struct dma_pool *bd_table_pool;
u8 test_mode;
/* array of callbacks for various status report handlers */
void (*sr_handler[2])(struct bdc *, struct bdc_sr *);
/* ep0 callback handlers */
void (*sr_xsf_ep0[3])(struct bdc *, struct bdc_sr *);
/* ep0 response buffer for ch9 requests like GET_STATUS and SET_SEL */
unsigned char ep0_response_buff[EP0_RESPONSE_BUFF];
/*
* Timer to check if host resumed transfer after bdc sent Func wake
* notification packet after a remote wakeup. if not, then resend the
* Func Wake packet every 2.5 secs. Refer to USB3 spec section 8.5.6.4
*/
struct delayed_work func_wake_notify;
struct clk *clk;
};
static inline u32 bdc_readl(void __iomem *base, u32 offset)
{
return readl(base + offset);
}
static inline void bdc_writel(void __iomem *base, u32 offset, u32 value)
{
writel(value, base + offset);
}
/* Buffer descriptor list operations */
void bdc_notify_xfr(struct bdc *bdc, u32 epnum);
void bdc_softconn(struct bdc *bdc);
void bdc_softdisconn(struct bdc *bdc);
int bdc_run(struct bdc *bdc);
int bdc_stop(struct bdc *bdc);
int bdc_reset(struct bdc *bdc);
int bdc_udc_init(struct bdc *bdc);
void bdc_udc_exit(struct bdc *bdc);
int bdc_reinit(struct bdc *bdc);
/* Status report handlers */
/* Upstream port status change sr */
void bdc_sr_uspc(struct bdc *bdc, struct bdc_sr *sreport);
/* transfer sr */
void bdc_sr_xsf(struct bdc *bdc, struct bdc_sr *sreport);
/* EP0 XSF handlers */
void bdc_xsf_ep0_setup_recv(struct bdc *bdc, struct bdc_sr *sreport);
void bdc_xsf_ep0_data_start(struct bdc *bdc, struct bdc_sr *sreport);
void bdc_xsf_ep0_status_start(struct bdc *bdc, struct bdc_sr *sreport);
#endif /* __LINUX_BDC_H__ */
@@ -0,0 +1,367 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* bdc_cmd.c - BRCM BDC USB3.0 device controller
*
* Copyright (C) 2014 Broadcom Corporation
*
* Author: Ashwini Pahuja
*/
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include "bdc.h"
#include "bdc_cmd.h"
#include "bdc_dbg.h"
/* Issues a cmd to cmd processor and waits for cmd completion */
static int bdc_issue_cmd(struct bdc *bdc, u32 cmd_sc, u32 param0,
u32 param1, u32 param2)
{
u32 timeout = BDC_CMD_TIMEOUT;
u32 cmd_status;
u32 temp;
bdc_writel(bdc->regs, BDC_CMDPAR0, param0);
bdc_writel(bdc->regs, BDC_CMDPAR1, param1);
bdc_writel(bdc->regs, BDC_CMDPAR2, param2);
/* Issue the cmd */
/* Make sure the cmd params are written before asking HW to exec cmd */
wmb();
bdc_writel(bdc->regs, BDC_CMDSC, cmd_sc | BDC_CMD_CWS | BDC_CMD_SRD);
do {
temp = bdc_readl(bdc->regs, BDC_CMDSC);
dev_dbg_ratelimited(bdc->dev, "cmdsc=%x", temp);
cmd_status = BDC_CMD_CST(temp);
if (cmd_status != BDC_CMDS_BUSY) {
dev_dbg(bdc->dev,
"command completed cmd_sts:%x\n", cmd_status);
return cmd_status;
}
udelay(1);
} while (timeout--);
dev_err(bdc->dev,
"command operation timedout cmd_status=%d\n", cmd_status);
return cmd_status;
}
/* Submits cmd and analyze the return value of bdc_issue_cmd */
static int bdc_submit_cmd(struct bdc *bdc, u32 cmd_sc,
u32 param0, u32 param1, u32 param2)
{
u32 temp, cmd_status;
int ret;
temp = bdc_readl(bdc->regs, BDC_CMDSC);
dev_dbg(bdc->dev,
"%s:CMDSC:%08x cmdsc:%08x param0=%08x param1=%08x param2=%08x\n",
__func__, temp, cmd_sc, param0, param1, param2);
cmd_status = BDC_CMD_CST(temp);
if (cmd_status == BDC_CMDS_BUSY) {
dev_err(bdc->dev, "command processor busy: %x\n", cmd_status);
return -EBUSY;
}
ret = bdc_issue_cmd(bdc, cmd_sc, param0, param1, param2);
switch (ret) {
case BDC_CMDS_SUCC:
dev_dbg(bdc->dev, "command completed successfully\n");
ret = 0;
break;
case BDC_CMDS_PARA:
dev_err(bdc->dev, "command parameter error\n");
ret = -EINVAL;
break;
case BDC_CMDS_STAT:
dev_err(bdc->dev, "Invalid device/ep state\n");
ret = -EINVAL;
break;
case BDC_CMDS_FAIL:
dev_err(bdc->dev, "Command failed?\n");
ret = -EAGAIN;
break;
case BDC_CMDS_INTL:
dev_err(bdc->dev, "BDC Internal error\n");
ret = -ECONNRESET;
break;
case BDC_CMDS_BUSY:
dev_err(bdc->dev,
"command timedout waited for %dusec\n",
BDC_CMD_TIMEOUT);
ret = -ECONNRESET;
break;
default:
dev_dbg(bdc->dev, "Unknown command completion code:%x\n", ret);
}
return ret;
}
/* Deconfigure the endpoint from HW */
int bdc_dconfig_ep(struct bdc *bdc, struct bdc_ep *ep)
{
u32 cmd_sc;
cmd_sc = BDC_SUB_CMD_DRP_EP|BDC_CMD_EPN(ep->ep_num)|BDC_CMD_EPC;
dev_dbg(bdc->dev, "%s ep->ep_num =%d cmd_sc=%x\n", __func__,
ep->ep_num, cmd_sc);
return bdc_submit_cmd(bdc, cmd_sc, 0, 0, 0);
}
/* Reinitalize the bdlist after config ep command */
static void ep_bd_list_reinit(struct bdc_ep *ep)
{
struct bdc *bdc = ep->bdc;
struct bdc_bd *bd;
ep->bd_list.eqp_bdi = 0;
ep->bd_list.hwd_bdi = 0;
bd = ep->bd_list.bd_table_array[0]->start_bd;
dev_dbg(bdc->dev, "%s ep:%p bd:%p\n", __func__, ep, bd);
memset(bd, 0, sizeof(struct bdc_bd));
bd->offset[3] |= cpu_to_le32(BD_SBF);
}
/* Configure an endpoint */
int bdc_config_ep(struct bdc *bdc, struct bdc_ep *ep)
{
const struct usb_ss_ep_comp_descriptor *comp_desc;
const struct usb_endpoint_descriptor *desc;
u32 param0, param1, param2, cmd_sc;
u32 mps, mbs, mul, si;
int ret;
desc = ep->desc;
comp_desc = ep->comp_desc;
cmd_sc = mul = mbs = param2 = 0;
param0 = lower_32_bits(ep->bd_list.bd_table_array[0]->dma);
param1 = upper_32_bits(ep->bd_list.bd_table_array[0]->dma);
cpu_to_le32s(&param0);
cpu_to_le32s(&param1);
dev_dbg(bdc->dev, "%s: param0=%08x param1=%08x",
__func__, param0, param1);
si = desc->bInterval;
si = clamp_val(si, 1, 16) - 1;
mps = usb_endpoint_maxp(desc);
param2 |= mps << MP_SHIFT;
param2 |= usb_endpoint_type(desc) << EPT_SHIFT;
switch (bdc->gadget.speed) {
case USB_SPEED_SUPER:
if (usb_endpoint_xfer_int(desc) ||
usb_endpoint_xfer_isoc(desc)) {
param2 |= si;
if (usb_endpoint_xfer_isoc(desc) && comp_desc)
mul = comp_desc->bmAttributes;
}
param2 |= mul << EPM_SHIFT;
if (comp_desc)
mbs = comp_desc->bMaxBurst;
param2 |= mbs << MB_SHIFT;
break;
case USB_SPEED_HIGH:
if (usb_endpoint_xfer_isoc(desc) ||
usb_endpoint_xfer_int(desc)) {
param2 |= si;
mbs = usb_endpoint_maxp_mult(desc);
param2 |= mbs << MB_SHIFT;
}
break;
case USB_SPEED_FULL:
case USB_SPEED_LOW:
/* the hardware accepts SI in 125usec range */
if (usb_endpoint_xfer_isoc(desc))
si += 3;
/*
* FS Int endpoints can have si of 1-255ms but the controller
* accepts 2^bInterval*125usec, so convert ms to nearest power
* of 2
*/
if (usb_endpoint_xfer_int(desc))
si = fls(desc->bInterval * 8) - 1;
param2 |= si;
break;
default:
dev_err(bdc->dev, "UNKNOWN speed ERR\n");
return -EINVAL;
}
cmd_sc |= BDC_CMD_EPC|BDC_CMD_EPN(ep->ep_num)|BDC_SUB_CMD_ADD_EP;
dev_dbg(bdc->dev, "cmd_sc=%x param2=%08x\n", cmd_sc, param2);
ret = bdc_submit_cmd(bdc, cmd_sc, param0, param1, param2);
if (ret) {
dev_err(bdc->dev, "command failed :%x\n", ret);
return ret;
}
ep_bd_list_reinit(ep);
return ret;
}
/*
* Change the HW deq pointer, if this command is successful, HW will start
* fetching the next bd from address dma_addr.
*/
int bdc_ep_bla(struct bdc *bdc, struct bdc_ep *ep, dma_addr_t dma_addr)
{
u32 param0, param1;
u32 cmd_sc = 0;
dev_dbg(bdc->dev, "%s: add=%08llx\n", __func__,
(unsigned long long)(dma_addr));
param0 = lower_32_bits(dma_addr);
param1 = upper_32_bits(dma_addr);
cpu_to_le32s(&param0);
cpu_to_le32s(&param1);
cmd_sc |= BDC_CMD_EPN(ep->ep_num)|BDC_CMD_BLA;
dev_dbg(bdc->dev, "cmd_sc=%x\n", cmd_sc);
return bdc_submit_cmd(bdc, cmd_sc, param0, param1, 0);
}
/* Set the address sent bu Host in SET_ADD request */
int bdc_address_device(struct bdc *bdc, u32 add)
{
u32 cmd_sc = 0;
u32 param2;
dev_dbg(bdc->dev, "%s: add=%d\n", __func__, add);
cmd_sc |= BDC_SUB_CMD_ADD|BDC_CMD_DVC;
param2 = add & 0x7f;
return bdc_submit_cmd(bdc, cmd_sc, 0, 0, param2);
}
/* Send a Function Wake notification packet using FH command */
int bdc_function_wake_fh(struct bdc *bdc, u8 intf)
{
u32 param0, param1;
u32 cmd_sc = 0;
param0 = param1 = 0;
dev_dbg(bdc->dev, "%s intf=%d\n", __func__, intf);
cmd_sc |= BDC_CMD_FH;
param0 |= TRA_PACKET;
param0 |= (bdc->dev_addr << 25);
param1 |= DEV_NOTF_TYPE;
param1 |= (FWK_SUBTYPE<<4);
dev_dbg(bdc->dev, "param0=%08x param1=%08x\n", param0, param1);
return bdc_submit_cmd(bdc, cmd_sc, param0, param1, 0);
}
/* Send a Function Wake notification packet using DNC command */
int bdc_function_wake(struct bdc *bdc, u8 intf)
{
u32 cmd_sc = 0;
u32 param2 = 0;
dev_dbg(bdc->dev, "%s intf=%d", __func__, intf);
param2 |= intf;
cmd_sc |= BDC_SUB_CMD_FWK|BDC_CMD_DNC;
return bdc_submit_cmd(bdc, cmd_sc, 0, 0, param2);
}
/* Stall the endpoint */
int bdc_ep_set_stall(struct bdc *bdc, int epnum)
{
u32 cmd_sc = 0;
dev_dbg(bdc->dev, "%s epnum=%d\n", __func__, epnum);
/* issue a stall endpoint command */
cmd_sc |= BDC_SUB_CMD_EP_STL | BDC_CMD_EPN(epnum) | BDC_CMD_EPO;
return bdc_submit_cmd(bdc, cmd_sc, 0, 0, 0);
}
/* resets the endpoint, called when host sends CLEAR_FEATURE(HALT) */
int bdc_ep_clear_stall(struct bdc *bdc, int epnum)
{
struct bdc_ep *ep;
u32 cmd_sc = 0;
int ret;
dev_dbg(bdc->dev, "%s: epnum=%d\n", __func__, epnum);
ep = bdc->bdc_ep_array[epnum];
/*
* If we are not in stalled then stall Endpoint and issue clear stall,
* his will reset the seq number for non EP0.
*/
if (epnum != 1) {
/* if the endpoint it not stalled */
if (!(ep->flags & BDC_EP_STALL)) {
ret = bdc_ep_set_stall(bdc, epnum);
if (ret)
return ret;
}
}
/* Preserve the seq number for ep0 only */
if (epnum != 1)
cmd_sc |= BDC_CMD_EPO_RST_SN;
/* issue a reset endpoint command */
cmd_sc |= BDC_SUB_CMD_EP_RST | BDC_CMD_EPN(epnum) | BDC_CMD_EPO;
ret = bdc_submit_cmd(bdc, cmd_sc, 0, 0, 0);
if (ret) {
dev_err(bdc->dev, "command failed:%x\n", ret);
return ret;
}
bdc_notify_xfr(bdc, epnum);
return ret;
}
/* Stop the endpoint, called when software wants to dequeue some request */
int bdc_stop_ep(struct bdc *bdc, int epnum)
{
struct bdc_ep *ep;
u32 cmd_sc = 0;
int ret;
ep = bdc->bdc_ep_array[epnum];
dev_dbg(bdc->dev, "%s: ep:%s ep->flags:%08x\n", __func__,
ep->name, ep->flags);
/* Endpoint has to be in running state to execute stop ep command */
if (!(ep->flags & BDC_EP_ENABLED)) {
dev_err(bdc->dev, "stop endpoint called for disabled ep\n");
return -EINVAL;
}
if ((ep->flags & BDC_EP_STALL) || (ep->flags & BDC_EP_STOP))
return 0;
/* issue a stop endpoint command */
cmd_sc |= BDC_CMD_EP0_XSD | BDC_SUB_CMD_EP_STP
| BDC_CMD_EPN(epnum) | BDC_CMD_EPO;
ret = bdc_submit_cmd(bdc, cmd_sc, 0, 0, 0);
if (ret) {
dev_err(bdc->dev,
"stop endpoint command didn't complete:%d ep:%s\n",
ret, ep->name);
return ret;
}
ep->flags |= BDC_EP_STOP;
bdc_dump_epsts(bdc);
return ret;
}
@@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* bdc_cmd.h - header for the BDC debug functions
*
* Copyright (C) 2014 Broadcom Corporation
*
* Author: Ashwini Pahuja
*/
#ifndef __LINUX_BDC_CMD_H__
#define __LINUX_BDC_CMD_H__
/* Command operations */
int bdc_address_device(struct bdc *bdc, u32 add);
int bdc_config_ep(struct bdc *bdc, struct bdc_ep *ep);
int bdc_dconfig_ep(struct bdc *bdc, struct bdc_ep *ep);
int bdc_stop_ep(struct bdc *bdc, int epnum);
int bdc_ep_set_stall(struct bdc *bdc, int epnum);
int bdc_ep_clear_stall(struct bdc *bdc, int epnum);
int bdc_ep_bla(struct bdc *bdc, struct bdc_ep *ep, dma_addr_t dma_addr);
int bdc_function_wake(struct bdc *bdc, u8 intf);
int bdc_function_wake_fh(struct bdc *bdc, u8 intf);
#endif /* __LINUX_BDC_CMD_H__ */
@@ -0,0 +1,676 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* bdc_core.c - BRCM BDC USB3.0 device controller core operations
*
* Copyright (C) 2014 Broadcom Corporation
*
* Author: Ashwini Pahuja
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/iopoll.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/list.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/moduleparam.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/clk.h>
#include "bdc.h"
#include "bdc_dbg.h"
/* Poll till controller status is not OIP */
static int poll_oip(struct bdc *bdc, u32 usec)
{
u32 status;
int ret;
ret = readl_poll_timeout_atomic(bdc->regs + BDC_BDCSC, status,
(BDC_CSTS(status) != BDC_OIP), 10, usec);
if (ret)
dev_err(bdc->dev, "operation timedout BDCSC: 0x%08x\n", status);
else
dev_dbg(bdc->dev, "%s complete status=%d", __func__, BDC_CSTS(status));
return ret;
}
/* Stop the BDC controller */
int bdc_stop(struct bdc *bdc)
{
int ret;
u32 temp;
dev_dbg(bdc->dev, "%s ()\n\n", __func__);
temp = bdc_readl(bdc->regs, BDC_BDCSC);
/* Check if BDC is already halted */
if (BDC_CSTS(temp) == BDC_HLT) {
dev_vdbg(bdc->dev, "BDC already halted\n");
return 0;
}
temp &= ~BDC_COP_MASK;
temp |= BDC_COS|BDC_COP_STP;
bdc_writel(bdc->regs, BDC_BDCSC, temp);
ret = poll_oip(bdc, BDC_COP_TIMEOUT);
if (ret)
dev_err(bdc->dev, "bdc stop operation failed");
return ret;
}
/* Issue a reset to BDC controller */
int bdc_reset(struct bdc *bdc)
{
u32 temp;
int ret;
dev_dbg(bdc->dev, "%s ()\n", __func__);
/* First halt the controller */
ret = bdc_stop(bdc);
if (ret)
return ret;
temp = bdc_readl(bdc->regs, BDC_BDCSC);
temp &= ~BDC_COP_MASK;
temp |= BDC_COS|BDC_COP_RST;
bdc_writel(bdc->regs, BDC_BDCSC, temp);
ret = poll_oip(bdc, BDC_COP_TIMEOUT);
if (ret)
dev_err(bdc->dev, "bdc reset operation failed");
return ret;
}
/* Run the BDC controller */
int bdc_run(struct bdc *bdc)
{
u32 temp;
int ret;
dev_dbg(bdc->dev, "%s ()\n", __func__);
temp = bdc_readl(bdc->regs, BDC_BDCSC);
/* if BDC is already in running state then do not do anything */
if (BDC_CSTS(temp) == BDC_NOR) {
dev_warn(bdc->dev, "bdc is already in running state\n");
return 0;
}
temp &= ~BDC_COP_MASK;
temp |= BDC_COP_RUN;
temp |= BDC_COS;
bdc_writel(bdc->regs, BDC_BDCSC, temp);
ret = poll_oip(bdc, BDC_COP_TIMEOUT);
if (ret) {
dev_err(bdc->dev, "bdc run operation failed:%d", ret);
return ret;
}
temp = bdc_readl(bdc->regs, BDC_BDCSC);
if (BDC_CSTS(temp) != BDC_NOR) {
dev_err(bdc->dev, "bdc not in normal mode after RUN op :%d\n",
BDC_CSTS(temp));
return -ESHUTDOWN;
}
return 0;
}
/*
* Present the termination to the host, typically called from upstream port
* event with Vbus present =1
*/
void bdc_softconn(struct bdc *bdc)
{
u32 uspc;
uspc = bdc_readl(bdc->regs, BDC_USPC);
uspc &= ~BDC_PST_MASK;
uspc |= BDC_LINK_STATE_RX_DET;
uspc |= BDC_SWS;
dev_dbg(bdc->dev, "%s () uspc=%08x\n", __func__, uspc);
bdc_writel(bdc->regs, BDC_USPC, uspc);
}
/* Remove the termination */
void bdc_softdisconn(struct bdc *bdc)
{
u32 uspc;
uspc = bdc_readl(bdc->regs, BDC_USPC);
uspc |= BDC_SDC;
uspc &= ~BDC_SCN;
dev_dbg(bdc->dev, "%s () uspc=%x\n", __func__, uspc);
bdc_writel(bdc->regs, BDC_USPC, uspc);
}
/* Set up the scratchpad buffer array and scratchpad buffers, if needed. */
static int scratchpad_setup(struct bdc *bdc)
{
int sp_buff_size;
u32 low32;
u32 upp32;
sp_buff_size = BDC_SPB(bdc_readl(bdc->regs, BDC_BDCCFG0));
dev_dbg(bdc->dev, "%s() sp_buff_size=%d\n", __func__, sp_buff_size);
if (!sp_buff_size) {
dev_dbg(bdc->dev, "Scratchpad buffer not needed\n");
return 0;
}
/* Refer to BDC spec, Table 4 for description of SPB */
sp_buff_size = 1 << (sp_buff_size + 5);
dev_dbg(bdc->dev, "Allocating %d bytes for scratchpad\n", sp_buff_size);
bdc->scratchpad.buff = dma_alloc_coherent(bdc->dev, sp_buff_size,
&bdc->scratchpad.sp_dma,
GFP_KERNEL);
if (!bdc->scratchpad.buff)
goto fail;
bdc->sp_buff_size = sp_buff_size;
bdc->scratchpad.size = sp_buff_size;
low32 = lower_32_bits(bdc->scratchpad.sp_dma);
upp32 = upper_32_bits(bdc->scratchpad.sp_dma);
cpu_to_le32s(&low32);
cpu_to_le32s(&upp32);
bdc_writel(bdc->regs, BDC_SPBBAL, low32);
bdc_writel(bdc->regs, BDC_SPBBAH, upp32);
return 0;
fail:
bdc->scratchpad.buff = NULL;
return -ENOMEM;
}
/* Allocate the status report ring */
static int setup_srr(struct bdc *bdc, int interrupter)
{
dev_dbg(bdc->dev, "%s() NUM_SR_ENTRIES:%d\n", __func__, NUM_SR_ENTRIES);
/* Reset the SRR */
bdc_writel(bdc->regs, BDC_SRRINT(0), BDC_SRR_RWS | BDC_SRR_RST);
bdc->srr.dqp_index = 0;
/* allocate the status report descriptors */
bdc->srr.sr_bds = dma_alloc_coherent(bdc->dev,
NUM_SR_ENTRIES * sizeof(struct bdc_bd),
&bdc->srr.dma_addr, GFP_KERNEL);
if (!bdc->srr.sr_bds)
return -ENOMEM;
return 0;
}
/* Initialize the HW regs and internal data structures */
static void bdc_mem_init(struct bdc *bdc, bool reinit)
{
u8 size = 0;
u32 usb2_pm;
u32 low32;
u32 upp32;
u32 temp;
dev_dbg(bdc->dev, "%s ()\n", __func__);
bdc->ep0_state = WAIT_FOR_SETUP;
bdc->dev_addr = 0;
bdc->srr.eqp_index = 0;
bdc->srr.dqp_index = 0;
bdc->zlp_needed = false;
bdc->delayed_status = false;
bdc_writel(bdc->regs, BDC_SPBBAL, bdc->scratchpad.sp_dma);
/* Init the SRR */
temp = BDC_SRR_RWS | BDC_SRR_RST;
/* Reset the SRR */
bdc_writel(bdc->regs, BDC_SRRINT(0), temp);
dev_dbg(bdc->dev, "bdc->srr.sr_bds =%p\n", bdc->srr.sr_bds);
temp = lower_32_bits(bdc->srr.dma_addr);
size = fls(NUM_SR_ENTRIES) - 2;
temp |= size;
dev_dbg(bdc->dev, "SRRBAL[0]=%08x NUM_SR_ENTRIES:%d size:%d\n",
temp, NUM_SR_ENTRIES, size);
low32 = lower_32_bits(temp);
upp32 = upper_32_bits(bdc->srr.dma_addr);
cpu_to_le32s(&low32);
cpu_to_le32s(&upp32);
/* Write the dma addresses into regs*/
bdc_writel(bdc->regs, BDC_SRRBAL(0), low32);
bdc_writel(bdc->regs, BDC_SRRBAH(0), upp32);
temp = bdc_readl(bdc->regs, BDC_SRRINT(0));
temp |= BDC_SRR_IE;
temp &= ~(BDC_SRR_RST | BDC_SRR_RWS);
bdc_writel(bdc->regs, BDC_SRRINT(0), temp);
/* Set the Interrupt Coalescence ~500 usec */
temp = bdc_readl(bdc->regs, BDC_INTCTLS(0));
temp &= ~0xffff;
temp |= INT_CLS;
bdc_writel(bdc->regs, BDC_INTCTLS(0), temp);
usb2_pm = bdc_readl(bdc->regs, BDC_USPPM2);
dev_dbg(bdc->dev, "usb2_pm=%08x", usb2_pm);
/* Enable hardware LPM Enable */
usb2_pm |= BDC_HLE;
bdc_writel(bdc->regs, BDC_USPPM2, usb2_pm);
/* readback for debug */
usb2_pm = bdc_readl(bdc->regs, BDC_USPPM2);
dev_dbg(bdc->dev, "usb2_pm=%08x\n", usb2_pm);
/* Disable any unwanted SR's on SRR */
temp = bdc_readl(bdc->regs, BDC_BDCSC);
/* We don't want Microframe counter wrap SR */
temp |= BDC_MASK_MCW;
bdc_writel(bdc->regs, BDC_BDCSC, temp);
/*
* In some error cases, driver has to reset the entire BDC controller
* in that case reinit is passed as 1
*/
if (reinit) {
int i;
/* Enable interrupts */
temp = bdc_readl(bdc->regs, BDC_BDCSC);
temp |= BDC_GIE;
bdc_writel(bdc->regs, BDC_BDCSC, temp);
/* Init scratchpad to 0 */
memset(bdc->scratchpad.buff, 0, bdc->sp_buff_size);
/* Initialize SRR to 0 */
memset(bdc->srr.sr_bds, 0,
NUM_SR_ENTRIES * sizeof(struct bdc_bd));
/*
* clear ep flags to avoid post disconnect stops/deconfigs but
* not during S2 exit
*/
if (!bdc->gadget.speed)
for (i = 1; i < bdc->num_eps; ++i)
bdc->bdc_ep_array[i]->flags = 0;
} else {
/* One time initiaization only */
/* Enable status report function pointers */
bdc->sr_handler[0] = bdc_sr_xsf;
bdc->sr_handler[1] = bdc_sr_uspc;
/* EP0 status report function pointers */
bdc->sr_xsf_ep0[0] = bdc_xsf_ep0_setup_recv;
bdc->sr_xsf_ep0[1] = bdc_xsf_ep0_data_start;
bdc->sr_xsf_ep0[2] = bdc_xsf_ep0_status_start;
}
}
/* Free the dynamic memory */
static void bdc_mem_free(struct bdc *bdc)
{
dev_dbg(bdc->dev, "%s\n", __func__);
/* Free SRR */
if (bdc->srr.sr_bds)
dma_free_coherent(bdc->dev,
NUM_SR_ENTRIES * sizeof(struct bdc_bd),
bdc->srr.sr_bds, bdc->srr.dma_addr);
/* Free scratchpad */
if (bdc->scratchpad.buff)
dma_free_coherent(bdc->dev, bdc->sp_buff_size,
bdc->scratchpad.buff, bdc->scratchpad.sp_dma);
/* Destroy the dma pools */
dma_pool_destroy(bdc->bd_table_pool);
/* Free the bdc_ep array */
kfree(bdc->bdc_ep_array);
bdc->srr.sr_bds = NULL;
bdc->scratchpad.buff = NULL;
bdc->bd_table_pool = NULL;
bdc->bdc_ep_array = NULL;
}
/*
* bdc reinit gives a controller reset and reinitialize the registers,
* called from disconnect/bus reset scenario's, to ensure proper HW cleanup
*/
int bdc_reinit(struct bdc *bdc)
{
int ret;
dev_dbg(bdc->dev, "%s\n", __func__);
ret = bdc_stop(bdc);
if (ret)
goto out;
ret = bdc_reset(bdc);
if (ret)
goto out;
/* the reinit flag is 1 */
bdc_mem_init(bdc, true);
ret = bdc_run(bdc);
out:
bdc->reinit = false;
return ret;
}
/* Allocate all the dyanmic memory */
static int bdc_mem_alloc(struct bdc *bdc)
{
u32 page_size;
unsigned int num_ieps, num_oeps;
dev_dbg(bdc->dev,
"%s() NUM_BDS_PER_TABLE:%d\n", __func__,
NUM_BDS_PER_TABLE);
page_size = BDC_PGS(bdc_readl(bdc->regs, BDC_BDCCFG0));
/* page size is 2^pgs KB */
page_size = 1 << page_size;
/* KB */
page_size <<= 10;
dev_dbg(bdc->dev, "page_size=%d\n", page_size);
/* Create a pool of bd tables */
bdc->bd_table_pool =
dma_pool_create("BDC BD tables", bdc->dev, NUM_BDS_PER_TABLE * 16,
16, page_size);
if (!bdc->bd_table_pool)
goto fail;
if (scratchpad_setup(bdc))
goto fail;
/* read from regs */
num_ieps = NUM_NCS(bdc_readl(bdc->regs, BDC_FSCNIC));
num_oeps = NUM_NCS(bdc_readl(bdc->regs, BDC_FSCNOC));
/* +2: 1 for ep0 and the other is rsvd i.e. bdc_ep[0] is rsvd */
bdc->num_eps = num_ieps + num_oeps + 2;
dev_dbg(bdc->dev,
"ieps:%d eops:%d num_eps:%d\n",
num_ieps, num_oeps, bdc->num_eps);
/* allocate array of ep pointers */
bdc->bdc_ep_array = kzalloc_objs(struct bdc_ep *, bdc->num_eps);
if (!bdc->bdc_ep_array)
goto fail;
dev_dbg(bdc->dev, "Allocating sr report0\n");
if (setup_srr(bdc, 0))
goto fail;
return 0;
fail:
dev_warn(bdc->dev, "Couldn't initialize memory\n");
bdc_mem_free(bdc);
return -ENOMEM;
}
/* opposite to bdc_hw_init */
static void bdc_hw_exit(struct bdc *bdc)
{
dev_dbg(bdc->dev, "%s ()\n", __func__);
bdc_mem_free(bdc);
}
/* Initialize the bdc HW and memory */
static int bdc_hw_init(struct bdc *bdc)
{
int ret;
dev_dbg(bdc->dev, "%s ()\n", __func__);
ret = bdc_reset(bdc);
if (ret) {
dev_err(bdc->dev, "err resetting bdc abort bdc init%d\n", ret);
return ret;
}
ret = bdc_mem_alloc(bdc);
if (ret) {
dev_err(bdc->dev, "Mem alloc failed, aborting\n");
return -ENOMEM;
}
bdc_mem_init(bdc, 0);
bdc_dbg_regs(bdc);
dev_dbg(bdc->dev, "HW Init done\n");
return 0;
}
static int bdc_phy_init(struct bdc *bdc)
{
int phy_num;
int ret;
for (phy_num = 0; phy_num < bdc->num_phys; phy_num++) {
ret = phy_init(bdc->phys[phy_num]);
if (ret)
goto err_exit_phy;
ret = phy_power_on(bdc->phys[phy_num]);
if (ret) {
phy_exit(bdc->phys[phy_num]);
goto err_exit_phy;
}
}
return 0;
err_exit_phy:
while (--phy_num >= 0) {
phy_power_off(bdc->phys[phy_num]);
phy_exit(bdc->phys[phy_num]);
}
return ret;
}
static void bdc_phy_exit(struct bdc *bdc)
{
int phy_num;
for (phy_num = 0; phy_num < bdc->num_phys; phy_num++) {
phy_power_off(bdc->phys[phy_num]);
phy_exit(bdc->phys[phy_num]);
}
}
static int bdc_probe(struct platform_device *pdev)
{
struct bdc *bdc;
int ret;
int irq;
u32 temp;
struct device *dev = &pdev->dev;
int phy_num;
dev_dbg(dev, "%s()\n", __func__);
bdc = devm_kzalloc(dev, sizeof(*bdc), GFP_KERNEL);
if (!bdc)
return -ENOMEM;
bdc->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(bdc->regs))
return PTR_ERR(bdc->regs);
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
spin_lock_init(&bdc->lock);
platform_set_drvdata(pdev, bdc);
bdc->irq = irq;
bdc->dev = dev;
dev_dbg(dev, "bdc->regs: %p irq=%d\n", bdc->regs, bdc->irq);
bdc->num_phys = of_count_phandle_with_args(dev->of_node,
"phys", "#phy-cells");
if (bdc->num_phys > 0) {
bdc->phys = devm_kcalloc(dev, bdc->num_phys,
sizeof(struct phy *), GFP_KERNEL);
if (!bdc->phys)
return -ENOMEM;
} else {
bdc->num_phys = 0;
}
dev_info(dev, "Using %d phy(s)\n", bdc->num_phys);
for (phy_num = 0; phy_num < bdc->num_phys; phy_num++) {
bdc->phys[phy_num] = devm_of_phy_get_by_index(
dev, dev->of_node, phy_num);
if (IS_ERR(bdc->phys[phy_num])) {
ret = PTR_ERR(bdc->phys[phy_num]);
dev_err(bdc->dev,
"BDC phy specified but not found:%d\n", ret);
return ret;
}
}
bdc->clk = devm_clk_get_optional(dev, "sw_usbd");
if (IS_ERR(bdc->clk))
return PTR_ERR(bdc->clk);
ret = clk_prepare_enable(bdc->clk);
if (ret) {
dev_err(dev, "could not enable clock\n");
return ret;
}
ret = bdc_phy_init(bdc);
if (ret) {
dev_err(bdc->dev, "BDC phy init failure:%d\n", ret);
goto disable_clk;
}
temp = bdc_readl(bdc->regs, BDC_BDCCAP1);
if ((temp & BDC_P64) &&
!dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) {
dev_dbg(dev, "Using 64-bit address\n");
} else {
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(dev,
"No suitable DMA config available, abort\n");
ret = -ENOTSUPP;
goto phycleanup;
}
dev_dbg(dev, "Using 32-bit address\n");
}
ret = bdc_hw_init(bdc);
if (ret) {
dev_err(dev, "BDC init failure:%d\n", ret);
goto phycleanup;
}
ret = bdc_udc_init(bdc);
if (ret) {
dev_err(dev, "BDC Gadget init failure:%d\n", ret);
goto cleanup;
}
return 0;
cleanup:
bdc_hw_exit(bdc);
phycleanup:
bdc_phy_exit(bdc);
disable_clk:
clk_disable_unprepare(bdc->clk);
return ret;
}
static void bdc_remove(struct platform_device *pdev)
{
struct bdc *bdc;
unsigned long flags;
u32 temp;
bdc = platform_get_drvdata(pdev);
dev_dbg(bdc->dev, "%s ()\n", __func__);
/*
* Disable the device interrupt source before freeing the IRQ:
* clear BDC_GIE so the controller stops asserting interrupts,
* then free_irq drains any in-flight handler.
*/
spin_lock_irqsave(&bdc->lock, flags);
temp = bdc_readl(bdc->regs, BDC_BDCSC);
temp &= ~BDC_GIE;
bdc_writel(bdc->regs, BDC_BDCSC, temp);
spin_unlock_irqrestore(&bdc->lock, flags);
free_irq(bdc->irq, bdc);
/*
* Drain func_wake_notify after free_irq: the IRQ handler arms this
* delayed_work via bdc_sr_uspc -> handle_link_state_change ->
* schedule_delayed_work (self-rearmed in bdc_func_wake_timer), so
* the IRQ must be released first to prevent re-arm after cancel.
*/
cancel_delayed_work_sync(&bdc->func_wake_notify);
bdc_udc_exit(bdc);
bdc_hw_exit(bdc);
bdc_phy_exit(bdc);
clk_disable_unprepare(bdc->clk);
}
#ifdef CONFIG_PM_SLEEP
static int bdc_suspend(struct device *dev)
{
struct bdc *bdc = dev_get_drvdata(dev);
int ret;
/* Halt the controller */
ret = bdc_stop(bdc);
if (!ret)
clk_disable_unprepare(bdc->clk);
return ret;
}
static int bdc_resume(struct device *dev)
{
struct bdc *bdc = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(bdc->clk);
if (ret) {
dev_err(bdc->dev, "err enabling the clock\n");
return ret;
}
ret = bdc_reinit(bdc);
if (ret) {
dev_err(bdc->dev, "err in bdc reinit\n");
clk_disable_unprepare(bdc->clk);
return ret;
}
return 0;
}
#endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(bdc_pm_ops, bdc_suspend,
bdc_resume);
static const struct of_device_id bdc_of_match[] = {
{ .compatible = "brcm,bdc-udc-v2" },
{ .compatible = "brcm,bdc" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, bdc_of_match);
static struct platform_driver bdc_driver = {
.driver = {
.name = BRCM_BDC_NAME,
.pm = &bdc_pm_ops,
.of_match_table = bdc_of_match,
},
.probe = bdc_probe,
.remove = bdc_remove,
};
module_platform_driver(bdc_driver);
MODULE_AUTHOR("Ashwini Pahuja <ashwini.linux@gmail.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION(BRCM_BDC_DESC);
@@ -0,0 +1,118 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* bdc_dbg.c - BRCM BDC USB3.0 device controller debug functions
*
* Copyright (C) 2014 Broadcom Corporation
*
* Author: Ashwini Pahuja
*/
#include "bdc.h"
#include "bdc_dbg.h"
void bdc_dbg_regs(struct bdc *bdc)
{
u32 temp;
dev_vdbg(bdc->dev, "bdc->regs:%p\n", bdc->regs);
temp = bdc_readl(bdc->regs, BDC_BDCCFG0);
dev_vdbg(bdc->dev, "bdccfg0:0x%08x\n", temp);
temp = bdc_readl(bdc->regs, BDC_BDCCFG1);
dev_vdbg(bdc->dev, "bdccfg1:0x%08x\n", temp);
temp = bdc_readl(bdc->regs, BDC_BDCCAP0);
dev_vdbg(bdc->dev, "bdccap0:0x%08x\n", temp);
temp = bdc_readl(bdc->regs, BDC_BDCCAP1);
dev_vdbg(bdc->dev, "bdccap1:0x%08x\n", temp);
temp = bdc_readl(bdc->regs, BDC_USPC);
dev_vdbg(bdc->dev, "uspc:0x%08x\n", temp);
temp = bdc_readl(bdc->regs, BDC_DVCSA);
dev_vdbg(bdc->dev, "dvcsa:0x%08x\n", temp);
temp = bdc_readl(bdc->regs, BDC_DVCSB);
dev_vdbg(bdc->dev, "dvcsb:0x%x08\n", temp);
}
void bdc_dump_epsts(struct bdc *bdc)
{
u32 temp;
temp = bdc_readl(bdc->regs, BDC_EPSTS0);
dev_vdbg(bdc->dev, "BDC_EPSTS0:0x%08x\n", temp);
temp = bdc_readl(bdc->regs, BDC_EPSTS1);
dev_vdbg(bdc->dev, "BDC_EPSTS1:0x%x\n", temp);
temp = bdc_readl(bdc->regs, BDC_EPSTS2);
dev_vdbg(bdc->dev, "BDC_EPSTS2:0x%08x\n", temp);
temp = bdc_readl(bdc->regs, BDC_EPSTS3);
dev_vdbg(bdc->dev, "BDC_EPSTS3:0x%08x\n", temp);
temp = bdc_readl(bdc->regs, BDC_EPSTS4);
dev_vdbg(bdc->dev, "BDC_EPSTS4:0x%08x\n", temp);
temp = bdc_readl(bdc->regs, BDC_EPSTS5);
dev_vdbg(bdc->dev, "BDC_EPSTS5:0x%08x\n", temp);
temp = bdc_readl(bdc->regs, BDC_EPSTS6);
dev_vdbg(bdc->dev, "BDC_EPSTS6:0x%08x\n", temp);
temp = bdc_readl(bdc->regs, BDC_EPSTS7);
dev_vdbg(bdc->dev, "BDC_EPSTS7:0x%08x\n", temp);
}
void bdc_dbg_srr(struct bdc *bdc, u32 srr_num)
{
struct bdc_sr *sr;
dma_addr_t addr;
int i;
sr = bdc->srr.sr_bds;
addr = bdc->srr.dma_addr;
dev_vdbg(bdc->dev, "%s sr:%p dqp_index:%d\n", __func__,
sr, bdc->srr.dqp_index);
for (i = 0; i < NUM_SR_ENTRIES; i++) {
sr = &bdc->srr.sr_bds[i];
dev_vdbg(bdc->dev, "%llx %08x %08x %08x %08x\n",
(unsigned long long)addr,
le32_to_cpu(sr->offset[0]),
le32_to_cpu(sr->offset[1]),
le32_to_cpu(sr->offset[2]),
le32_to_cpu(sr->offset[3]));
addr += sizeof(*sr);
}
}
void bdc_dbg_bd_list(struct bdc *bdc, struct bdc_ep *ep)
{
struct bd_list *bd_list = &ep->bd_list;
struct bd_table *bd_table;
struct bdc_bd *bd;
int tbi, bdi, gbdi;
dma_addr_t dma;
gbdi = 0;
dev_vdbg(bdc->dev,
"Dump bd list for %s epnum:%d\n",
ep->name, ep->ep_num);
dev_vdbg(bdc->dev,
"tabs:%d max_bdi:%d eqp_bdi:%d hwd_bdi:%d num_bds_table:%d\n",
bd_list->num_tabs, bd_list->max_bdi, bd_list->eqp_bdi,
bd_list->hwd_bdi, bd_list->num_bds_table);
for (tbi = 0; tbi < bd_list->num_tabs; tbi++) {
bd_table = bd_list->bd_table_array[tbi];
for (bdi = 0; bdi < bd_list->num_bds_table; bdi++) {
bd = bd_table->start_bd + bdi;
dma = bd_table->dma + (sizeof(struct bdc_bd) * bdi);
dev_vdbg(bdc->dev,
"tbi:%2d bdi:%2d gbdi:%2d virt:%p phys:%llx %08x %08x %08x %08x\n",
tbi, bdi, gbdi++, bd, (unsigned long long)dma,
le32_to_cpu(bd->offset[0]),
le32_to_cpu(bd->offset[1]),
le32_to_cpu(bd->offset[2]),
le32_to_cpu(bd->offset[3]));
}
dev_vdbg(bdc->dev, "\n\n");
}
}
@@ -0,0 +1,32 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* bdc_dbg.h - header for the BDC debug functions
*
* Copyright (C) 2014 Broadcom Corporation
*
* Author: Ashwini Pahuja
*/
#ifndef __LINUX_BDC_DBG_H__
#define __LINUX_BDC_DBG_H__
#include "bdc.h"
#ifdef CONFIG_USB_GADGET_VERBOSE
void bdc_dbg_bd_list(struct bdc *bdc, struct bdc_ep *ep);
void bdc_dbg_srr(struct bdc *bdc, u32 srr_num);
void bdc_dbg_regs(struct bdc *bdc);
void bdc_dump_epsts(struct bdc *bdc);
#else
static inline void bdc_dbg_regs(struct bdc *bdc)
{ }
static inline void bdc_dbg_srr(struct bdc *bdc, u32 srr_num)
{ }
static inline void bdc_dbg_bd_list(struct bdc *bdc, struct bdc_ep *ep)
{ }
static inline void bdc_dump_epsts(struct bdc *bdc)
{ }
#endif /* CONFIG_USB_GADGET_VERBOSE */
#endif /* __LINUX_BDC_DBG_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* bdc_ep.h - header for the BDC debug functions
*
* Copyright (C) 2014 Broadcom Corporation
*
* Author: Ashwini Pahuja
*/
#ifndef __LINUX_BDC_EP_H__
#define __LINUX_BDC_EP_H__
int bdc_init_ep(struct bdc *bdc);
int bdc_ep_disable(struct bdc_ep *ep);
int bdc_ep_enable(struct bdc_ep *ep);
void bdc_free_ep(struct bdc *bdc);
#endif /* __LINUX_BDC_EP_H__ */
@@ -0,0 +1,591 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* bdc_udc.c - BRCM BDC USB3.0 device controller gagdet ops
*
* Copyright (C) 2014 Broadcom Corporation
*
* Author: Ashwini Pahuja
*
* Based on drivers under drivers/usb/gadget/udc/
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
#include <linux/pm.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/unaligned.h>
#include <linux/platform_device.h>
#include "bdc.h"
#include "bdc_ep.h"
#include "bdc_cmd.h"
#include "bdc_dbg.h"
static const struct usb_gadget_ops bdc_gadget_ops;
static const char * const conn_speed_str[] = {
"Not connected",
"Full Speed",
"Low Speed",
"High Speed",
"Super Speed",
};
/* EP0 initial descripror */
static struct usb_endpoint_descriptor bdc_gadget_ep0_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_CONTROL,
.bEndpointAddress = 0,
.wMaxPacketSize = cpu_to_le16(EP0_MAX_PKT_SIZE),
};
/* Advance the srr dqp maintained by SW */
static void srr_dqp_index_advc(struct bdc *bdc, u32 srr_num)
{
struct srr *srr;
srr = &bdc->srr;
dev_dbg_ratelimited(bdc->dev, "srr->dqp_index:%d\n", srr->dqp_index);
srr->dqp_index++;
/* rollback to 0 if we are past the last */
if (srr->dqp_index == NUM_SR_ENTRIES)
srr->dqp_index = 0;
}
/* connect sr */
static void bdc_uspc_connected(struct bdc *bdc)
{
u32 speed, temp;
u32 usppms;
int ret;
temp = bdc_readl(bdc->regs, BDC_USPC);
speed = BDC_PSP(temp);
dev_dbg(bdc->dev, "%s speed=%x\n", __func__, speed);
switch (speed) {
case BDC_SPEED_SS:
bdc_gadget_ep0_desc.wMaxPacketSize =
cpu_to_le16(EP0_MAX_PKT_SIZE);
bdc->gadget.ep0->maxpacket = EP0_MAX_PKT_SIZE;
bdc->gadget.speed = USB_SPEED_SUPER;
/* Enable U1T in SS mode */
usppms = bdc_readl(bdc->regs, BDC_USPPMS);
usppms &= ~BDC_U1T(0xff);
usppms |= BDC_U1T(U1_TIMEOUT);
usppms |= BDC_PORT_W1S;
bdc_writel(bdc->regs, BDC_USPPMS, usppms);
break;
case BDC_SPEED_HS:
bdc_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
bdc->gadget.ep0->maxpacket = 64;
bdc->gadget.speed = USB_SPEED_HIGH;
break;
case BDC_SPEED_FS:
bdc_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
bdc->gadget.ep0->maxpacket = 64;
bdc->gadget.speed = USB_SPEED_FULL;
break;
case BDC_SPEED_LS:
bdc_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8);
bdc->gadget.ep0->maxpacket = 8;
bdc->gadget.speed = USB_SPEED_LOW;
break;
default:
dev_err(bdc->dev, "UNDEFINED SPEED\n");
return;
}
dev_dbg(bdc->dev, "connected at %s\n", conn_speed_str[speed]);
/* Now we know the speed, configure ep0 */
bdc->bdc_ep_array[1]->desc = &bdc_gadget_ep0_desc;
ret = bdc_config_ep(bdc, bdc->bdc_ep_array[1]);
if (ret)
dev_err(bdc->dev, "EP0 config failed\n");
bdc->bdc_ep_array[1]->usb_ep.desc = &bdc_gadget_ep0_desc;
bdc->bdc_ep_array[1]->flags |= BDC_EP_ENABLED;
usb_gadget_set_state(&bdc->gadget, USB_STATE_DEFAULT);
}
/* device got disconnected */
static void bdc_uspc_disconnected(struct bdc *bdc, bool reinit)
{
struct bdc_ep *ep;
dev_dbg(bdc->dev, "%s\n", __func__);
/*
* Only stop ep0 from here, rest of the endpoints will be disabled
* from gadget_disconnect
*/
ep = bdc->bdc_ep_array[1];
if (ep && (ep->flags & BDC_EP_ENABLED))
/* if enabled then stop and remove requests */
bdc_ep_disable(ep);
if (bdc->gadget_driver && bdc->gadget_driver->disconnect) {
spin_unlock(&bdc->lock);
bdc->gadget_driver->disconnect(&bdc->gadget);
spin_lock(&bdc->lock);
}
/* Set Unknown speed */
bdc->gadget.speed = USB_SPEED_UNKNOWN;
bdc->devstatus &= DEVSTATUS_CLEAR;
bdc->delayed_status = false;
bdc->reinit = reinit;
bdc->test_mode = false;
usb_gadget_set_state(&bdc->gadget, USB_STATE_NOTATTACHED);
}
/* TNotify wkaeup timer */
static void bdc_func_wake_timer(struct work_struct *work)
{
struct bdc *bdc = container_of(work, struct bdc, func_wake_notify.work);
unsigned long flags;
dev_dbg(bdc->dev, "%s\n", __func__);
spin_lock_irqsave(&bdc->lock, flags);
/*
* Check if host has started transferring on endpoints
* FUNC_WAKE_ISSUED is cleared when transfer has started after resume
*/
if (bdc->devstatus & FUNC_WAKE_ISSUED) {
dev_dbg(bdc->dev, "FUNC_WAKE_ISSUED FLAG IS STILL SET\n");
/* flag is still set, so again send func wake */
bdc_function_wake_fh(bdc, 0);
schedule_delayed_work(&bdc->func_wake_notify,
msecs_to_jiffies(BDC_TNOTIFY));
}
spin_unlock_irqrestore(&bdc->lock, flags);
}
/* handler for Link state change condition */
static void handle_link_state_change(struct bdc *bdc, u32 uspc)
{
u32 link_state;
dev_dbg(bdc->dev, "Link state change");
link_state = BDC_PST(uspc);
switch (link_state) {
case BDC_LINK_STATE_U3:
if ((bdc->gadget.speed != USB_SPEED_UNKNOWN) &&
bdc->gadget_driver->suspend) {
dev_dbg(bdc->dev, "Entered Suspend mode\n");
spin_unlock(&bdc->lock);
bdc->devstatus |= DEVICE_SUSPENDED;
bdc->gadget_driver->suspend(&bdc->gadget);
spin_lock(&bdc->lock);
}
break;
case BDC_LINK_STATE_U0:
if (bdc->devstatus & REMOTE_WAKEUP_ISSUED) {
bdc->devstatus &= ~REMOTE_WAKEUP_ISSUED;
if (bdc->gadget.speed == USB_SPEED_SUPER) {
bdc_function_wake_fh(bdc, 0);
bdc->devstatus |= FUNC_WAKE_ISSUED;
/*
* Start a Notification timer and check if the
* Host transferred anything on any of the EPs,
* if not then send function wake again every
* TNotification secs until host initiates
* transfer to BDC, USB3 spec Table 8.13
*/
schedule_delayed_work(
&bdc->func_wake_notify,
msecs_to_jiffies(BDC_TNOTIFY));
dev_dbg(bdc->dev, "sched func_wake_notify\n");
}
}
break;
case BDC_LINK_STATE_RESUME:
dev_dbg(bdc->dev, "Resumed from Suspend\n");
if (bdc->devstatus & DEVICE_SUSPENDED) {
bdc->gadget_driver->resume(&bdc->gadget);
bdc->devstatus &= ~DEVICE_SUSPENDED;
}
break;
default:
dev_dbg(bdc->dev, "link state:%d\n", link_state);
}
}
/* something changes on upstream port, handle it here */
void bdc_sr_uspc(struct bdc *bdc, struct bdc_sr *sreport)
{
u32 clear_flags = 0;
u32 uspc;
bool connected = false;
bool disconn = false;
uspc = bdc_readl(bdc->regs, BDC_USPC);
dev_dbg(bdc->dev, "%s uspc=0x%08x\n", __func__, uspc);
/* Port connect changed */
if (uspc & BDC_PCC) {
/* Vbus not present, and not connected to Downstream port */
if ((uspc & BDC_VBC) && !(uspc & BDC_VBS) && !(uspc & BDC_PCS))
disconn = true;
else if ((uspc & BDC_PCS) && !BDC_PST(uspc))
connected = true;
clear_flags |= BDC_PCC;
}
/* Change in VBus and VBus is present */
if ((uspc & BDC_VBC) && (uspc & BDC_VBS)) {
if (bdc->pullup) {
dev_dbg(bdc->dev, "Do a softconnect\n");
/* Attached state, do a softconnect */
bdc_softconn(bdc);
usb_gadget_set_state(&bdc->gadget, USB_STATE_POWERED);
}
clear_flags |= BDC_VBC;
} else if ((uspc & BDC_PRS) || (uspc & BDC_PRC) || disconn) {
/* Hot reset, warm reset, 2.0 bus reset or disconn */
dev_dbg(bdc->dev, "Port reset or disconn\n");
bdc_uspc_disconnected(bdc, disconn);
clear_flags |= BDC_PRC;
} else if ((uspc & BDC_PSC) && (uspc & BDC_PCS)) {
/* Change in Link state */
handle_link_state_change(bdc, uspc);
clear_flags |= BDC_PSC;
}
/*
* In SS we might not have PRC bit set before connection, but in 2.0
* the PRC bit is set before connection, so moving this condition out
* of bus reset to handle both SS/2.0 speeds.
*/
if (connected) {
/* This is the connect event for U0/L0 */
dev_dbg(bdc->dev, "Connected\n");
bdc_uspc_connected(bdc);
bdc->devstatus &= ~(DEVICE_SUSPENDED);
}
uspc = bdc_readl(bdc->regs, BDC_USPC);
uspc &= (~BDC_USPSC_RW);
dev_dbg(bdc->dev, "uspc=%x\n", uspc);
bdc_writel(bdc->regs, BDC_USPC, clear_flags);
}
/* Main interrupt handler for bdc */
static irqreturn_t bdc_udc_interrupt(int irq, void *_bdc)
{
u32 eqp_index, dqp_index, sr_type, srr_int;
struct bdc_sr *sreport;
struct bdc *bdc = _bdc;
u32 status;
int ret;
spin_lock(&bdc->lock);
status = bdc_readl(bdc->regs, BDC_BDCSC);
if (!(status & BDC_GIP)) {
spin_unlock(&bdc->lock);
return IRQ_NONE;
}
srr_int = bdc_readl(bdc->regs, BDC_SRRINT(0));
/* Check if the SRR IP bit it set? */
if (!(srr_int & BDC_SRR_IP)) {
dev_warn(bdc->dev, "Global irq pending but SRR IP is 0\n");
spin_unlock(&bdc->lock);
return IRQ_NONE;
}
eqp_index = BDC_SRR_EPI(srr_int);
dqp_index = BDC_SRR_DPI(srr_int);
dev_dbg(bdc->dev,
"%s eqp_index=%d dqp_index=%d srr.dqp_index=%d\n\n",
__func__, eqp_index, dqp_index, bdc->srr.dqp_index);
/* check for ring empty condition */
if (eqp_index == dqp_index) {
dev_dbg(bdc->dev, "SRR empty?\n");
spin_unlock(&bdc->lock);
return IRQ_HANDLED;
}
while (bdc->srr.dqp_index != eqp_index) {
sreport = &bdc->srr.sr_bds[bdc->srr.dqp_index];
/* sreport is read before using it */
rmb();
sr_type = le32_to_cpu(sreport->offset[3]) & BD_TYPE_BITMASK;
dev_dbg_ratelimited(bdc->dev, "sr_type=%d\n", sr_type);
switch (sr_type) {
case SR_XSF:
bdc->sr_handler[0](bdc, sreport);
break;
case SR_USPC:
bdc->sr_handler[1](bdc, sreport);
break;
default:
dev_warn(bdc->dev, "SR:%d not handled\n", sr_type);
}
/* Advance the srr dqp index */
srr_dqp_index_advc(bdc, 0);
}
/* update the hw dequeue pointer */
srr_int = bdc_readl(bdc->regs, BDC_SRRINT(0));
srr_int &= ~BDC_SRR_DPI_MASK;
srr_int &= ~(BDC_SRR_RWS|BDC_SRR_RST|BDC_SRR_ISR);
srr_int |= ((bdc->srr.dqp_index) << 16);
srr_int |= BDC_SRR_IP;
bdc_writel(bdc->regs, BDC_SRRINT(0), srr_int);
srr_int = bdc_readl(bdc->regs, BDC_SRRINT(0));
if (bdc->reinit) {
ret = bdc_reinit(bdc);
if (ret)
dev_err(bdc->dev, "err in bdc reinit\n");
}
spin_unlock(&bdc->lock);
return IRQ_HANDLED;
}
/* Gadget ops */
static int bdc_udc_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
struct bdc *bdc = gadget_to_bdc(gadget);
unsigned long flags;
int ret = 0;
dev_dbg(bdc->dev, "%s()\n", __func__);
spin_lock_irqsave(&bdc->lock, flags);
if (bdc->gadget_driver) {
dev_err(bdc->dev, "%s is already bound to %s\n",
bdc->gadget.name,
bdc->gadget_driver->driver.name);
ret = -EBUSY;
goto err;
}
/*
* Run the controller from here and when BDC is connected to
* Host then driver will receive a USPC SR with VBUS present
* and then driver will do a softconnect.
*/
ret = bdc_run(bdc);
if (ret) {
dev_err(bdc->dev, "%s bdc run fail\n", __func__);
goto err;
}
bdc->gadget_driver = driver;
bdc->gadget.dev.driver = &driver->driver;
err:
spin_unlock_irqrestore(&bdc->lock, flags);
return ret;
}
static int bdc_udc_stop(struct usb_gadget *gadget)
{
struct bdc *bdc = gadget_to_bdc(gadget);
unsigned long flags;
dev_dbg(bdc->dev, "%s()\n", __func__);
spin_lock_irqsave(&bdc->lock, flags);
bdc_stop(bdc);
bdc->gadget_driver = NULL;
bdc->gadget.dev.driver = NULL;
spin_unlock_irqrestore(&bdc->lock, flags);
return 0;
}
static int bdc_udc_pullup(struct usb_gadget *gadget, int is_on)
{
struct bdc *bdc = gadget_to_bdc(gadget);
unsigned long flags;
u32 uspc;
dev_dbg(bdc->dev, "%s() is_on:%d\n", __func__, is_on);
if (!gadget)
return -EINVAL;
spin_lock_irqsave(&bdc->lock, flags);
if (!is_on) {
bdc_softdisconn(bdc);
bdc->pullup = false;
} else {
/*
* For a self powered device, we need to wait till we receive
* a VBUS change and Vbus present event, then if pullup flag
* is set, then only we present the Termintation.
*/
bdc->pullup = true;
/*
* Check if BDC is already connected to Host i.e Vbus=1,
* if yes, then present TERM now, this is typical for bus
* powered devices.
*/
uspc = bdc_readl(bdc->regs, BDC_USPC);
if (uspc & BDC_VBS)
bdc_softconn(bdc);
}
spin_unlock_irqrestore(&bdc->lock, flags);
return 0;
}
static int bdc_udc_set_selfpowered(struct usb_gadget *gadget,
int is_self)
{
struct bdc *bdc = gadget_to_bdc(gadget);
unsigned long flags;
dev_dbg(bdc->dev, "%s()\n", __func__);
gadget->is_selfpowered = (is_self != 0);
spin_lock_irqsave(&bdc->lock, flags);
if (!is_self)
bdc->devstatus |= 1 << USB_DEVICE_SELF_POWERED;
else
bdc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
spin_unlock_irqrestore(&bdc->lock, flags);
return 0;
}
static int bdc_udc_wakeup(struct usb_gadget *gadget)
{
struct bdc *bdc = gadget_to_bdc(gadget);
unsigned long flags;
u8 link_state;
u32 uspc;
int ret = 0;
dev_dbg(bdc->dev,
"%s() bdc->devstatus=%08x\n",
__func__, bdc->devstatus);
if (!(bdc->devstatus & REMOTE_WAKE_ENABLE))
return -EOPNOTSUPP;
spin_lock_irqsave(&bdc->lock, flags);
uspc = bdc_readl(bdc->regs, BDC_USPC);
link_state = BDC_PST(uspc);
dev_dbg(bdc->dev, "link_state =%d portsc=%x", link_state, uspc);
if (link_state != BDC_LINK_STATE_U3) {
dev_warn(bdc->dev,
"can't wakeup from link state %d\n",
link_state);
ret = -EINVAL;
goto out;
}
if (bdc->gadget.speed == USB_SPEED_SUPER)
bdc->devstatus |= REMOTE_WAKEUP_ISSUED;
uspc &= ~BDC_PST_MASK;
uspc &= (~BDC_USPSC_RW);
uspc |= BDC_PST(BDC_LINK_STATE_U0);
uspc |= BDC_SWS;
bdc_writel(bdc->regs, BDC_USPC, uspc);
uspc = bdc_readl(bdc->regs, BDC_USPC);
link_state = BDC_PST(uspc);
dev_dbg(bdc->dev, "link_state =%d portsc=%x", link_state, uspc);
out:
spin_unlock_irqrestore(&bdc->lock, flags);
return ret;
}
static const struct usb_gadget_ops bdc_gadget_ops = {
.wakeup = bdc_udc_wakeup,
.set_selfpowered = bdc_udc_set_selfpowered,
.pullup = bdc_udc_pullup,
.udc_start = bdc_udc_start,
.udc_stop = bdc_udc_stop,
};
/* Init the gadget interface and register the udc */
int bdc_udc_init(struct bdc *bdc)
{
u32 temp;
int ret;
dev_dbg(bdc->dev, "%s()\n", __func__);
bdc->gadget.ops = &bdc_gadget_ops;
bdc->gadget.max_speed = USB_SPEED_SUPER;
bdc->gadget.speed = USB_SPEED_UNKNOWN;
bdc->gadget.dev.parent = bdc->dev;
bdc->gadget.sg_supported = false;
bdc->gadget.name = BRCM_BDC_NAME;
ret = request_irq(bdc->irq, bdc_udc_interrupt, IRQF_SHARED,
BRCM_BDC_NAME, bdc);
if (ret) {
dev_err(bdc->dev,
"failed to request irq #%d %d\n",
bdc->irq, ret);
return ret;
}
ret = bdc_init_ep(bdc);
if (ret) {
dev_err(bdc->dev, "bdc init ep fail: %d\n", ret);
goto err0;
}
ret = usb_add_gadget_udc(bdc->dev, &bdc->gadget);
if (ret) {
dev_err(bdc->dev, "failed to register udc\n");
goto err0;
}
usb_gadget_set_state(&bdc->gadget, USB_STATE_NOTATTACHED);
bdc->bdc_ep_array[1]->desc = &bdc_gadget_ep0_desc;
/*
* Allocate bd list for ep0 only, ep0 will be enabled on connect
* status report when the speed is known
*/
ret = bdc_ep_enable(bdc->bdc_ep_array[1]);
if (ret) {
dev_err(bdc->dev, "fail to enable %s\n",
bdc->bdc_ep_array[1]->name);
goto err1;
}
INIT_DELAYED_WORK(&bdc->func_wake_notify, bdc_func_wake_timer);
/* Enable Interrupts */
temp = bdc_readl(bdc->regs, BDC_BDCSC);
temp |= BDC_GIE;
bdc_writel(bdc->regs, BDC_BDCSC, temp);
return 0;
err1:
usb_del_gadget_udc(&bdc->gadget);
err0:
free_irq(bdc->irq, bdc);
bdc_free_ep(bdc);
return ret;
}
void bdc_udc_exit(struct bdc *bdc)
{
unsigned long flags;
dev_dbg(bdc->dev, "%s()\n", __func__);
spin_lock_irqsave(&bdc->lock, flags);
bdc_ep_disable(bdc->bdc_ep_array[1]);
spin_unlock_irqrestore(&bdc->lock, flags);
usb_del_gadget_udc(&bdc->gadget);
bdc_free_ep(bdc);
}
@@ -0,0 +1,11 @@
config USB_CDNS2_UDC
tristate "Cadence USBHS Device Controller"
depends on USB_PCI && ACPI && HAS_DMA
help
Cadence USBHS Device controller is a PCI based USB peripheral
controller which supports both full and high speed USB 2.0
data transfers.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "cdns2-udc-pci.ko" and to
force all gadget drivers to also be dynamically linked.
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
# define_trace.h needs to know how to find our header
CFLAGS_cdns2-trace.o := -I$(src)
obj-$(CONFIG_USB_CDNS2_UDC) += cdns2-udc-pci.o
cdns2-udc-pci-$(CONFIG_USB_CDNS2_UDC) += cdns2-pci.o cdns2-gadget.o cdns2-ep0.o
cdns2-udc-pci-$(CONFIG_TRACING) += cdns2-trace.o
@@ -0,0 +1,203 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cadence USBHS-DEV Driver.
* Debug header file.
*
* Copyright (C) 2023 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
#ifndef __LINUX_CDNS2_DEBUG
#define __LINUX_CDNS2_DEBUG
static inline const char *cdns2_decode_usb_irq(char *str, size_t size,
u8 usb_irq, u8 ext_irq)
{
int ret;
ret = scnprintf(str, size, "usbirq: 0x%02x - ", usb_irq);
if (usb_irq & USBIRQ_SOF)
ret += scnprintf(str + ret, size - ret, "SOF ");
if (usb_irq & USBIRQ_SUTOK)
ret += scnprintf(str + ret, size - ret, "SUTOK ");
if (usb_irq & USBIRQ_SUDAV)
ret += scnprintf(str + ret, size - ret, "SETUP ");
if (usb_irq & USBIRQ_SUSPEND)
ret += scnprintf(str + ret, size - ret, "Suspend ");
if (usb_irq & USBIRQ_URESET)
ret += scnprintf(str + ret, size - ret, "Reset ");
if (usb_irq & USBIRQ_HSPEED)
ret += scnprintf(str + ret, size - ret, "HS ");
if (usb_irq & USBIRQ_LPM)
ret += scnprintf(str + ret, size - ret, "LPM ");
ret += scnprintf(str + ret, size - ret, ", EXT: 0x%02x - ", ext_irq);
if (ext_irq & EXTIRQ_WAKEUP)
ret += scnprintf(str + ret, size - ret, "Wakeup ");
if (ext_irq & EXTIRQ_VBUSFAULT_FALL)
ret += scnprintf(str + ret, size - ret, "VBUS_FALL ");
if (ext_irq & EXTIRQ_VBUSFAULT_RISE)
ret += scnprintf(str + ret, size - ret, "VBUS_RISE ");
if (ret == size - 1)
pr_info("CDNS2: buffer may be truncated.\n");
return str;
}
static inline const char *cdns2_decode_dma_irq(char *str, size_t size,
u32 ep_ists, u32 ep_sts,
const char *ep_name)
{
int ret;
ret = scnprintf(str, size, "ISTS: %08x, %s: %08x ",
ep_ists, ep_name, ep_sts);
if (ep_sts & DMA_EP_STS_IOC)
ret += scnprintf(str + ret, size - ret, "IOC ");
if (ep_sts & DMA_EP_STS_ISP)
ret += scnprintf(str + ret, size - ret, "ISP ");
if (ep_sts & DMA_EP_STS_DESCMIS)
ret += scnprintf(str + ret, size - ret, "DESCMIS ");
if (ep_sts & DMA_EP_STS_TRBERR)
ret += scnprintf(str + ret, size - ret, "TRBERR ");
if (ep_sts & DMA_EP_STS_OUTSMM)
ret += scnprintf(str + ret, size - ret, "OUTSMM ");
if (ep_sts & DMA_EP_STS_ISOERR)
ret += scnprintf(str + ret, size - ret, "ISOERR ");
if (ep_sts & DMA_EP_STS_DBUSY)
ret += scnprintf(str + ret, size - ret, "DBUSY ");
if (DMA_EP_STS_CCS(ep_sts))
ret += scnprintf(str + ret, size - ret, "CCS ");
if (ret == size - 1)
pr_info("CDNS2: buffer may be truncated.\n");
return str;
}
static inline const char *cdns2_decode_epx_irq(char *str, size_t size,
char *ep_name, u32 ep_ists,
u32 ep_sts)
{
return cdns2_decode_dma_irq(str, size, ep_ists, ep_sts, ep_name);
}
static inline const char *cdns2_decode_ep0_irq(char *str, size_t size,
u32 ep_ists, u32 ep_sts,
int dir)
{
return cdns2_decode_dma_irq(str, size, ep_ists, ep_sts,
dir ? "ep0IN" : "ep0OUT");
}
static inline const char *cdns2_raw_ring(struct cdns2_endpoint *pep,
struct cdns2_trb *trbs,
char *str, size_t size)
{
struct cdns2_ring *ring = &pep->ring;
struct cdns2_trb *trb;
dma_addr_t dma;
int ret;
int i;
ret = scnprintf(str, size, "\n\t\tTR for %s:", pep->name);
trb = &trbs[ring->dequeue];
dma = cdns2_trb_virt_to_dma(pep, trb);
ret += scnprintf(str + ret, size - ret,
"\n\t\tRing deq index: %d, trb: V=%p, P=0x%pad\n",
ring->dequeue, trb, &dma);
trb = &trbs[ring->enqueue];
dma = cdns2_trb_virt_to_dma(pep, trb);
ret += scnprintf(str + ret, size - ret,
"\t\tRing enq index: %d, trb: V=%p, P=0x%pad\n",
ring->enqueue, trb, &dma);
ret += scnprintf(str + ret, size - ret,
"\t\tfree trbs: %d, CCS=%d, PCS=%d\n",
ring->free_trbs, ring->ccs, ring->pcs);
if (TRBS_PER_SEGMENT > 40) {
ret += scnprintf(str + ret, size - ret,
"\t\tTransfer ring %d too big\n", TRBS_PER_SEGMENT);
return str;
}
dma = ring->dma;
for (i = 0; i < TRBS_PER_SEGMENT; ++i) {
trb = &trbs[i];
ret += scnprintf(str + ret, size - ret,
"\t\t@%pad %08x %08x %08x\n", &dma,
le32_to_cpu(trb->buffer),
le32_to_cpu(trb->length),
le32_to_cpu(trb->control));
dma += sizeof(*trb);
}
if (ret == size - 1)
pr_info("CDNS2: buffer may be truncated.\n");
return str;
}
static inline const char *cdns2_trb_type_string(u8 type)
{
switch (type) {
case TRB_NORMAL:
return "Normal";
case TRB_LINK:
return "Link";
default:
return "UNKNOWN";
}
}
static inline const char *cdns2_decode_trb(char *str, size_t size, u32 flags,
u32 length, u32 buffer)
{
int type = TRB_FIELD_TO_TYPE(flags);
int ret;
switch (type) {
case TRB_LINK:
ret = scnprintf(str, size,
"LINK %08x type '%s' flags %c:%c:%c%c:%c",
buffer, cdns2_trb_type_string(type),
flags & TRB_CYCLE ? 'C' : 'c',
flags & TRB_TOGGLE ? 'T' : 't',
flags & TRB_CHAIN ? 'C' : 'c',
flags & TRB_CHAIN ? 'H' : 'h',
flags & TRB_IOC ? 'I' : 'i');
break;
case TRB_NORMAL:
ret = scnprintf(str, size,
"type: '%s', Buffer: %08x, length: %ld, burst len: %ld, "
"flags %c:%c:%c%c:%c",
cdns2_trb_type_string(type),
buffer, TRB_LEN(length),
TRB_FIELD_TO_BURST(length),
flags & TRB_CYCLE ? 'C' : 'c',
flags & TRB_ISP ? 'I' : 'i',
flags & TRB_CHAIN ? 'C' : 'c',
flags & TRB_CHAIN ? 'H' : 'h',
flags & TRB_IOC ? 'I' : 'i');
break;
default:
ret = scnprintf(str, size, "type '%s' -> raw %08x %08x %08x",
cdns2_trb_type_string(type),
buffer, length, flags);
}
if (ret == size - 1)
pr_info("CDNS2: buffer may be truncated.\n");
return str;
}
#endif /*__LINUX_CDNS2_DEBUG*/
@@ -0,0 +1,659 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence USBHS-DEV driver.
*
* Copyright (C) 2023 Cadence Design Systems.
*
* Authors: Pawel Laszczak <pawell@cadence.com>
*/
#include <linux/usb/composite.h>
#include <linux/unaligned.h>
#include "cdns2-gadget.h"
#include "cdns2-trace.h"
static struct usb_endpoint_descriptor cdns2_gadget_ep0_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_CONTROL,
.wMaxPacketSize = cpu_to_le16(64)
};
static int cdns2_w_index_to_ep_index(u16 wIndex)
{
if (!(wIndex & USB_ENDPOINT_NUMBER_MASK))
return 0;
return ((wIndex & USB_ENDPOINT_NUMBER_MASK) * 2) +
(wIndex & USB_ENDPOINT_DIR_MASK ? 1 : 0) - 1;
}
static bool cdns2_check_new_setup(struct cdns2_device *pdev)
{
u8 reg;
reg = readb(&pdev->ep0_regs->cs);
return !!(reg & EP0CS_CHGSET);
}
static void cdns2_ep0_enqueue(struct cdns2_device *pdev, dma_addr_t dma_addr,
unsigned int length, int zlp)
{
struct cdns2_adma_regs __iomem *regs = pdev->adma_regs;
struct cdns2_endpoint *pep = &pdev->eps[0];
struct cdns2_ring *ring = &pep->ring;
ring->trbs[0].buffer = cpu_to_le32(TRB_BUFFER(dma_addr));
ring->trbs[0].length = cpu_to_le32(TRB_LEN(length));
if (zlp) {
ring->trbs[0].control = cpu_to_le32(TRB_CYCLE |
TRB_TYPE(TRB_NORMAL));
ring->trbs[1].buffer = cpu_to_le32(TRB_BUFFER(dma_addr));
ring->trbs[1].length = cpu_to_le32(TRB_LEN(0));
ring->trbs[1].control = cpu_to_le32(TRB_CYCLE | TRB_IOC |
TRB_TYPE(TRB_NORMAL));
} else {
ring->trbs[0].control = cpu_to_le32(TRB_CYCLE | TRB_IOC |
TRB_TYPE(TRB_NORMAL));
ring->trbs[1].control = 0;
}
trace_cdns2_queue_trb(pep, ring->trbs);
if (!pep->dir)
writel(0, &pdev->ep0_regs->rxbc);
cdns2_select_ep(pdev, pep->dir);
writel(DMA_EP_STS_TRBERR, &regs->ep_sts);
writel(pep->ring.dma, &regs->ep_traddr);
trace_cdns2_doorbell_ep0(pep, readl(&regs->ep_traddr));
writel(DMA_EP_CMD_DRDY, &regs->ep_cmd);
}
static int cdns2_ep0_delegate_req(struct cdns2_device *pdev)
{
int ret;
spin_unlock(&pdev->lock);
ret = pdev->gadget_driver->setup(&pdev->gadget, &pdev->setup);
spin_lock(&pdev->lock);
return ret;
}
static void cdns2_ep0_stall(struct cdns2_device *pdev)
{
struct cdns2_endpoint *pep = &pdev->eps[0];
struct cdns2_request *preq;
preq = cdns2_next_preq(&pep->pending_list);
set_reg_bit_8(&pdev->ep0_regs->cs, EP0CS_DSTALL);
if (pdev->ep0_stage == CDNS2_DATA_STAGE && preq)
cdns2_gadget_giveback(pep, preq, -ECONNRESET);
else if (preq)
list_del_init(&preq->list);
pdev->ep0_stage = CDNS2_SETUP_STAGE;
pep->ep_state |= EP_STALLED;
}
static void cdns2_status_stage(struct cdns2_device *pdev)
{
struct cdns2_endpoint *pep = &pdev->eps[0];
struct cdns2_request *preq;
preq = cdns2_next_preq(&pep->pending_list);
if (preq)
list_del_init(&preq->list);
pdev->ep0_stage = CDNS2_SETUP_STAGE;
writeb(EP0CS_HSNAK, &pdev->ep0_regs->cs);
}
static int cdns2_req_ep0_set_configuration(struct cdns2_device *pdev,
struct usb_ctrlrequest *ctrl_req)
{
enum usb_device_state state = pdev->gadget.state;
u32 config = le16_to_cpu(ctrl_req->wValue);
int ret;
if (state < USB_STATE_ADDRESS) {
dev_err(pdev->dev, "Set Configuration - bad device state\n");
return -EINVAL;
}
ret = cdns2_ep0_delegate_req(pdev);
if (ret)
return ret;
trace_cdns2_device_state(config ? "configured" : "addressed");
if (!config)
usb_gadget_set_state(&pdev->gadget, USB_STATE_ADDRESS);
return 0;
}
static int cdns2_req_ep0_set_address(struct cdns2_device *pdev, u32 addr)
{
enum usb_device_state device_state = pdev->gadget.state;
u8 reg;
if (addr > USB_DEVICE_MAX_ADDRESS) {
dev_err(pdev->dev,
"Device address (%d) cannot be greater than %d\n",
addr, USB_DEVICE_MAX_ADDRESS);
return -EINVAL;
}
if (device_state == USB_STATE_CONFIGURED) {
dev_err(pdev->dev,
"can't set_address from configured state\n");
return -EINVAL;
}
reg = readb(&pdev->usb_regs->fnaddr);
pdev->dev_address = reg;
usb_gadget_set_state(&pdev->gadget,
(addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT));
trace_cdns2_device_state(addr ? "addressed" : "default");
return 0;
}
static int cdns2_req_ep0_handle_status(struct cdns2_device *pdev,
struct usb_ctrlrequest *ctrl)
{
struct cdns2_endpoint *pep;
__le16 *response_pkt;
u16 status = 0;
int ep_sts;
u32 recip;
recip = ctrl->bRequestType & USB_RECIP_MASK;
switch (recip) {
case USB_RECIP_DEVICE:
status = pdev->gadget.is_selfpowered;
status |= pdev->may_wakeup << USB_DEVICE_REMOTE_WAKEUP;
break;
case USB_RECIP_INTERFACE:
return cdns2_ep0_delegate_req(pdev);
case USB_RECIP_ENDPOINT:
ep_sts = cdns2_w_index_to_ep_index(le16_to_cpu(ctrl->wIndex));
pep = &pdev->eps[ep_sts];
if (pep->ep_state & EP_STALLED)
status = BIT(USB_ENDPOINT_HALT);
break;
default:
return -EINVAL;
}
put_unaligned_le16(status, (__le16 *)pdev->ep0_preq.request.buf);
cdns2_ep0_enqueue(pdev, pdev->ep0_preq.request.dma,
sizeof(*response_pkt), 0);
return 0;
}
static int cdns2_ep0_handle_feature_device(struct cdns2_device *pdev,
struct usb_ctrlrequest *ctrl,
int set)
{
enum usb_device_state state;
enum usb_device_speed speed;
int ret = 0;
u32 wValue;
u16 tmode;
wValue = le16_to_cpu(ctrl->wValue);
state = pdev->gadget.state;
speed = pdev->gadget.speed;
switch (wValue) {
case USB_DEVICE_REMOTE_WAKEUP:
pdev->may_wakeup = !!set;
break;
case USB_DEVICE_TEST_MODE:
if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH)
return -EINVAL;
tmode = le16_to_cpu(ctrl->wIndex);
if (!set || (tmode & 0xff) != 0)
return -EINVAL;
tmode >>= 8;
switch (tmode) {
case USB_TEST_J:
case USB_TEST_K:
case USB_TEST_SE0_NAK:
case USB_TEST_PACKET:
/*
* The USBHS controller automatically handles the
* Set_Feature(testmode) request. Standard test modes
* that use values of test mode selector from
* 01h to 04h (Test_J, Test_K, Test_SE0_NAK,
* Test_Packet) are supported by the
* controller(HS - ack, FS - stall).
*/
break;
default:
ret = -EINVAL;
}
break;
default:
ret = -EINVAL;
}
return ret;
}
static int cdns2_ep0_handle_feature_intf(struct cdns2_device *pdev,
struct usb_ctrlrequest *ctrl,
int set)
{
int ret = 0;
u32 wValue;
wValue = le16_to_cpu(ctrl->wValue);
switch (wValue) {
case USB_INTRF_FUNC_SUSPEND:
break;
default:
ret = -EINVAL;
}
return ret;
}
static int cdns2_ep0_handle_feature_endpoint(struct cdns2_device *pdev,
struct usb_ctrlrequest *ctrl,
int set)
{
struct cdns2_endpoint *pep;
u8 wValue;
wValue = le16_to_cpu(ctrl->wValue);
pep = &pdev->eps[cdns2_w_index_to_ep_index(le16_to_cpu(ctrl->wIndex))];
if (wValue != USB_ENDPOINT_HALT)
return -EINVAL;
if (!(le16_to_cpu(ctrl->wIndex) & ~USB_DIR_IN))
return 0;
switch (wValue) {
case USB_ENDPOINT_HALT:
if (set || !(pep->ep_state & EP_WEDGE))
return cdns2_halt_endpoint(pdev, pep, set);
break;
default:
dev_warn(pdev->dev, "WARN Incorrect wValue %04x\n", wValue);
return -EINVAL;
}
return 0;
}
static int cdns2_req_ep0_handle_feature(struct cdns2_device *pdev,
struct usb_ctrlrequest *ctrl,
int set)
{
switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_DEVICE:
return cdns2_ep0_handle_feature_device(pdev, ctrl, set);
case USB_RECIP_INTERFACE:
return cdns2_ep0_handle_feature_intf(pdev, ctrl, set);
case USB_RECIP_ENDPOINT:
return cdns2_ep0_handle_feature_endpoint(pdev, ctrl, set);
default:
return -EINVAL;
}
}
static int cdns2_ep0_std_request(struct cdns2_device *pdev)
{
struct usb_ctrlrequest *ctrl = &pdev->setup;
int ret;
switch (ctrl->bRequest) {
case USB_REQ_SET_ADDRESS:
ret = cdns2_req_ep0_set_address(pdev,
le16_to_cpu(ctrl->wValue));
break;
case USB_REQ_SET_CONFIGURATION:
ret = cdns2_req_ep0_set_configuration(pdev, ctrl);
break;
case USB_REQ_GET_STATUS:
ret = cdns2_req_ep0_handle_status(pdev, ctrl);
break;
case USB_REQ_CLEAR_FEATURE:
ret = cdns2_req_ep0_handle_feature(pdev, ctrl, 0);
break;
case USB_REQ_SET_FEATURE:
ret = cdns2_req_ep0_handle_feature(pdev, ctrl, 1);
break;
default:
ret = cdns2_ep0_delegate_req(pdev);
break;
}
return ret;
}
static void __pending_setup_status_handler(struct cdns2_device *pdev)
{
struct usb_request *request = pdev->pending_status_request;
if (pdev->status_completion_no_call && request && request->complete) {
request->complete(&pdev->eps[0].endpoint, request);
pdev->status_completion_no_call = 0;
}
}
void cdns2_pending_setup_status_handler(struct work_struct *work)
{
struct cdns2_device *pdev = container_of(work, struct cdns2_device,
pending_status_wq);
unsigned long flags;
spin_lock_irqsave(&pdev->lock, flags);
__pending_setup_status_handler(pdev);
spin_unlock_irqrestore(&pdev->lock, flags);
}
void cdns2_handle_setup_packet(struct cdns2_device *pdev)
{
struct usb_ctrlrequest *ctrl = &pdev->setup;
struct cdns2_endpoint *pep = &pdev->eps[0];
struct cdns2_request *preq;
int ret = 0;
u16 len;
u8 reg;
int i;
writeb(EP0CS_CHGSET, &pdev->ep0_regs->cs);
for (i = 0; i < 8; i++)
((u8 *)&pdev->setup)[i] = readb(&pdev->ep0_regs->setupdat[i]);
/*
* If SETUP packet was modified while reading just simple ignore it.
* The new one will be handled latter.
*/
if (cdns2_check_new_setup(pdev)) {
trace_cdns2_ep0_setup("overridden");
return;
}
trace_cdns2_ctrl_req(ctrl);
if (!pdev->gadget_driver)
goto out;
if (pdev->gadget.state == USB_STATE_NOTATTACHED) {
dev_err(pdev->dev, "ERR: Setup detected in unattached state\n");
ret = -EINVAL;
goto out;
}
pep = &pdev->eps[0];
/* Halt for Ep0 is cleared automatically when SETUP packet arrives. */
pep->ep_state &= ~EP_STALLED;
if (!list_empty(&pep->pending_list)) {
preq = cdns2_next_preq(&pep->pending_list);
cdns2_gadget_giveback(pep, preq, -ECONNRESET);
}
len = le16_to_cpu(ctrl->wLength);
if (len)
pdev->ep0_stage = CDNS2_DATA_STAGE;
else
pdev->ep0_stage = CDNS2_STATUS_STAGE;
pep->dir = ctrl->bRequestType & USB_DIR_IN;
/*
* SET_ADDRESS request is acknowledged automatically by controller and
* in the worse case driver may not notice this request. To check
* whether this request has been processed driver can use
* fnaddr register.
*/
reg = readb(&pdev->usb_regs->fnaddr);
if (pdev->setup.bRequest != USB_REQ_SET_ADDRESS &&
pdev->dev_address != reg)
cdns2_req_ep0_set_address(pdev, reg);
if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
ret = cdns2_ep0_std_request(pdev);
else
ret = cdns2_ep0_delegate_req(pdev);
if (ret == USB_GADGET_DELAYED_STATUS) {
trace_cdns2_ep0_status_stage("delayed");
return;
}
out:
if (ret < 0)
cdns2_ep0_stall(pdev);
else if (pdev->ep0_stage == CDNS2_STATUS_STAGE)
cdns2_status_stage(pdev);
}
static void cdns2_transfer_completed(struct cdns2_device *pdev)
{
struct cdns2_endpoint *pep = &pdev->eps[0];
if (!list_empty(&pep->pending_list)) {
struct cdns2_request *preq;
trace_cdns2_complete_trb(pep, pep->ring.trbs);
preq = cdns2_next_preq(&pep->pending_list);
preq->request.actual =
TRB_LEN(le32_to_cpu(pep->ring.trbs->length));
cdns2_gadget_giveback(pep, preq, 0);
}
cdns2_status_stage(pdev);
}
void cdns2_handle_ep0_interrupt(struct cdns2_device *pdev, int dir)
{
u32 ep_sts_reg;
cdns2_select_ep(pdev, dir);
trace_cdns2_ep0_irq(pdev);
ep_sts_reg = readl(&pdev->adma_regs->ep_sts);
writel(ep_sts_reg, &pdev->adma_regs->ep_sts);
__pending_setup_status_handler(pdev);
if ((ep_sts_reg & DMA_EP_STS_IOC) || (ep_sts_reg & DMA_EP_STS_ISP)) {
pdev->eps[0].dir = dir;
cdns2_transfer_completed(pdev);
}
}
/*
* Function shouldn't be called by gadget driver,
* endpoint 0 is allways active.
*/
static int cdns2_gadget_ep0_enable(struct usb_ep *ep,
const struct usb_endpoint_descriptor *desc)
{
return -EINVAL;
}
/*
* Function shouldn't be called by gadget driver,
* endpoint 0 is allways active.
*/
static int cdns2_gadget_ep0_disable(struct usb_ep *ep)
{
return -EINVAL;
}
static int cdns2_gadget_ep0_set_halt(struct usb_ep *ep, int value)
{
struct cdns2_endpoint *pep = ep_to_cdns2_ep(ep);
struct cdns2_device *pdev = pep->pdev;
unsigned long flags;
if (!value)
return 0;
spin_lock_irqsave(&pdev->lock, flags);
cdns2_ep0_stall(pdev);
spin_unlock_irqrestore(&pdev->lock, flags);
return 0;
}
static int cdns2_gadget_ep0_set_wedge(struct usb_ep *ep)
{
return cdns2_gadget_ep0_set_halt(ep, 1);
}
static int cdns2_gadget_ep0_queue(struct usb_ep *ep,
struct usb_request *request,
gfp_t gfp_flags)
{
struct cdns2_endpoint *pep = ep_to_cdns2_ep(ep);
struct cdns2_device *pdev = pep->pdev;
struct cdns2_request *preq;
unsigned long flags;
u8 zlp = 0;
int ret;
spin_lock_irqsave(&pdev->lock, flags);
preq = to_cdns2_request(request);
trace_cdns2_request_enqueue(preq);
/* Cancel the request if controller receive new SETUP packet. */
if (cdns2_check_new_setup(pdev)) {
trace_cdns2_ep0_setup("overridden");
spin_unlock_irqrestore(&pdev->lock, flags);
return -ECONNRESET;
}
/* Send STATUS stage. Should be called only for SET_CONFIGURATION. */
if (pdev->ep0_stage == CDNS2_STATUS_STAGE) {
cdns2_status_stage(pdev);
request->actual = 0;
pdev->status_completion_no_call = true;
pdev->pending_status_request = request;
usb_gadget_set_state(&pdev->gadget, USB_STATE_CONFIGURED);
spin_unlock_irqrestore(&pdev->lock, flags);
/*
* Since there is no completion interrupt for status stage,
* it needs to call ->completion in software after
* cdns2_gadget_ep0_queue is back.
*/
queue_work(system_freezable_wq, &pdev->pending_status_wq);
return 0;
}
if (!list_empty(&pep->pending_list)) {
trace_cdns2_ep0_setup("pending");
dev_err(pdev->dev,
"can't handle multiple requests for ep0\n");
spin_unlock_irqrestore(&pdev->lock, flags);
return -EBUSY;
}
ret = usb_gadget_map_request_by_dev(pdev->dev, request, pep->dir);
if (ret) {
spin_unlock_irqrestore(&pdev->lock, flags);
dev_err(pdev->dev, "failed to map request\n");
return -EINVAL;
}
request->status = -EINPROGRESS;
list_add_tail(&preq->list, &pep->pending_list);
if (request->zero && request->length &&
(request->length % ep->maxpacket == 0))
zlp = 1;
cdns2_ep0_enqueue(pdev, request->dma, request->length, zlp);
spin_unlock_irqrestore(&pdev->lock, flags);
return 0;
}
static const struct usb_ep_ops cdns2_gadget_ep0_ops = {
.enable = cdns2_gadget_ep0_enable,
.disable = cdns2_gadget_ep0_disable,
.alloc_request = cdns2_gadget_ep_alloc_request,
.free_request = cdns2_gadget_ep_free_request,
.queue = cdns2_gadget_ep0_queue,
.dequeue = cdns2_gadget_ep_dequeue,
.set_halt = cdns2_gadget_ep0_set_halt,
.set_wedge = cdns2_gadget_ep0_set_wedge,
};
void cdns2_ep0_config(struct cdns2_device *pdev)
{
struct cdns2_endpoint *pep;
pep = &pdev->eps[0];
if (!list_empty(&pep->pending_list)) {
struct cdns2_request *preq;
preq = cdns2_next_preq(&pep->pending_list);
list_del_init(&preq->list);
}
writeb(EP0_FIFO_AUTO, &pdev->ep0_regs->fifo);
cdns2_select_ep(pdev, USB_DIR_OUT);
writel(DMA_EP_CFG_ENABLE, &pdev->adma_regs->ep_cfg);
writeb(EP0_FIFO_IO_TX | EP0_FIFO_AUTO, &pdev->ep0_regs->fifo);
cdns2_select_ep(pdev, USB_DIR_IN);
writel(DMA_EP_CFG_ENABLE, &pdev->adma_regs->ep_cfg);
writeb(pdev->gadget.ep0->maxpacket, &pdev->ep0_regs->maxpack);
writel(DMA_EP_IEN_EP_OUT0 | DMA_EP_IEN_EP_IN0,
&pdev->adma_regs->ep_ien);
}
void cdns2_init_ep0(struct cdns2_device *pdev,
struct cdns2_endpoint *pep)
{
u16 maxpacket = le16_to_cpu(cdns2_gadget_ep0_desc.wMaxPacketSize);
usb_ep_set_maxpacket_limit(&pep->endpoint, maxpacket);
pep->endpoint.ops = &cdns2_gadget_ep0_ops;
pep->endpoint.desc = &cdns2_gadget_ep0_desc;
pep->endpoint.caps.type_control = true;
pep->endpoint.caps.dir_in = true;
pep->endpoint.caps.dir_out = true;
pdev->gadget.ep0 = &pep->endpoint;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,716 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* USBHS-DEV device controller driver header file
*
* Copyright (C) 2023 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
#ifndef __LINUX_CDNS2_GADGET
#define __LINUX_CDNS2_GADGET
#include <linux/usb/gadget.h>
#include <linux/dma-direction.h>
/*
* USBHS register interface.
* This corresponds to the USBHS Device Controller Interface.
*/
/**
* struct cdns2_ep0_regs - endpoint 0 related registers.
* @rxbc: receive (OUT) 0 endpoint byte count register.
* @txbc: transmit (IN) 0 endpoint byte count register.
* @cs: 0 endpoint control and status register.
* @reserved1: reserved.
* @fifo: 0 endpoint fifo register.
* @reserved2: reserved.
* @setupdat: SETUP data register.
* @reserved4: reserved.
* @maxpack: 0 endpoint max packet size.
*/
struct cdns2_ep0_regs {
__u8 rxbc;
__u8 txbc;
__u8 cs;
__u8 reserved1[4];
__u8 fifo;
__le32 reserved2[94];
__u8 setupdat[8];
__u8 reserved4[88];
__u8 maxpack;
} __packed __aligned(4);
/* EP0CS - bitmasks. */
/* Endpoint 0 stall bit for status stage. */
#define EP0CS_STALL BIT(0)
/* HSNAK bit. */
#define EP0CS_HSNAK BIT(1)
/* IN 0 endpoint busy bit. */
#define EP0CS_TXBSY_MSK BIT(2)
/* OUT 0 endpoint busy bit. */
#define EP0CS_RXBSY_MSK BIT(3)
/* Send STALL in the data stage phase. */
#define EP0CS_DSTALL BIT(4)
/* SETUP buffer content was changed. */
#define EP0CS_CHGSET BIT(7)
/* EP0FIFO - bitmasks. */
/* Direction. */
#define EP0_FIFO_IO_TX BIT(4)
/* FIFO auto bit. */
#define EP0_FIFO_AUTO BIT(5)
/* FIFO commit bit. */
#define EP0_FIFO_COMMIT BIT(6)
/* FIFO access bit. */
#define EP0_FIFO_ACCES BIT(7)
/**
* struct cdns2_epx_base - base endpoint registers.
* @rxbc: OUT endpoint byte count register.
* @rxcon: OUT endpoint control register.
* @rxcs: OUT endpoint control and status register.
* @txbc: IN endpoint byte count register.
* @txcon: IN endpoint control register.
* @txcs: IN endpoint control and status register.
*/
struct cdns2_epx_base {
__le16 rxbc;
__u8 rxcon;
__u8 rxcs;
__le16 txbc;
__u8 txcon;
__u8 txcs;
} __packed __aligned(4);
/* rxcon/txcon - endpoint control register bitmasks. */
/* Endpoint buffering: 0 - single buffering ... 3 - quad buffering. */
#define EPX_CON_BUF GENMASK(1, 0)
/* Endpoint type. */
#define EPX_CON_TYPE GENMASK(3, 2)
/* Endpoint type: isochronous. */
#define EPX_CON_TYPE_ISOC 0x4
/* Endpoint type: bulk. */
#define EPX_CON_TYPE_BULK 0x8
/* Endpoint type: interrupt. */
#define EPX_CON_TYPE_INT 0xC
/* Number of packets per microframe. */
#define EPX_CON_ISOD GENMASK(5, 4)
#define EPX_CON_ISOD_SHIFT 0x4
/* Endpoint stall bit. */
#define EPX_CON_STALL BIT(6)
/* Endpoint enable bit.*/
#define EPX_CON_VAL BIT(7)
/* rxcs/txcs - endpoint control and status bitmasks. */
/* Data sequence error for the ISO endpoint. */
#define EPX_CS_ERR(p) ((p) & BIT(0))
/**
* struct cdns2_epx_regs - endpoint 1..15 related registers.
* @reserved: reserved.
* @ep: none control endpoints array.
* @reserved2: reserved.
* @endprst: endpoint reset register.
* @reserved3: reserved.
* @isoautoarm: ISO auto-arm register.
* @reserved4: reserved.
* @isodctrl: ISO control register.
* @reserved5: reserved.
* @isoautodump: ISO auto dump enable register.
* @reserved6: reserved.
* @rxmaxpack: receive (OUT) Max packet size register.
* @reserved7: reserved.
* @rxstaddr: receive (OUT) start address endpoint buffer register.
* @reserved8: reserved.
* @txstaddr: transmit (IN) start address endpoint buffer register.
* @reserved9: reserved.
* @txmaxpack: transmit (IN) Max packet size register.
*/
struct cdns2_epx_regs {
__le32 reserved[2];
struct cdns2_epx_base ep[15];
__u8 reserved2[290];
__u8 endprst;
__u8 reserved3[41];
__le16 isoautoarm;
__u8 reserved4[10];
__le16 isodctrl;
__le16 reserved5;
__le16 isoautodump;
__le32 reserved6;
__le16 rxmaxpack[15];
__le32 reserved7[65];
__le32 rxstaddr[15];
__u8 reserved8[4];
__le32 txstaddr[15];
__u8 reserved9[98];
__le16 txmaxpack[15];
} __packed __aligned(4);
/* ENDPRST - bitmasks. */
/* Endpoint number. */
#define ENDPRST_EP GENMASK(3, 0)
/* IN direction bit. */
#define ENDPRST_IO_TX BIT(4)
/* Toggle reset bit. */
#define ENDPRST_TOGRST BIT(5)
/* FIFO reset bit. */
#define ENDPRST_FIFORST BIT(6)
/* Toggle status and reset bit. */
#define ENDPRST_TOGSETQ BIT(7)
/**
* struct cdns2_interrupt_regs - USB interrupt related registers.
* @reserved: reserved.
* @usbirq: USB interrupt request register.
* @extirq: external interrupt request register.
* @rxpngirq: external interrupt request register.
* @reserved1: reserved.
* @usbien: USB interrupt enable register.
* @extien: external interrupt enable register.
* @reserved2: reserved.
* @usbivect: USB interrupt vector register.
*/
struct cdns2_interrupt_regs {
__u8 reserved[396];
__u8 usbirq;
__u8 extirq;
__le16 rxpngirq;
__le16 reserved1[4];
__u8 usbien;
__u8 extien;
__le16 reserved2[3];
__u8 usbivect;
} __packed __aligned(4);
/* EXTIRQ and EXTIEN - bitmasks. */
/* VBUS fault fall interrupt. */
#define EXTIRQ_VBUSFAULT_FALL BIT(0)
/* VBUS fault fall interrupt. */
#define EXTIRQ_VBUSFAULT_RISE BIT(1)
/* Wake up interrupt bit. */
#define EXTIRQ_WAKEUP BIT(7)
/* USBIEN and USBIRQ - bitmasks. */
/* SETUP data valid interrupt bit.*/
#define USBIRQ_SUDAV BIT(0)
/* Start-of-frame interrupt bit. */
#define USBIRQ_SOF BIT(1)
/* SETUP token interrupt bit. */
#define USBIRQ_SUTOK BIT(2)
/* USB suspend interrupt bit. */
#define USBIRQ_SUSPEND BIT(3)
/* USB reset interrupt bit. */
#define USBIRQ_URESET BIT(4)
/* USB high-speed mode interrupt bit. */
#define USBIRQ_HSPEED BIT(5)
/* Link Power Management interrupt bit. */
#define USBIRQ_LPM BIT(7)
#define USB_IEN_INIT (USBIRQ_SUDAV | USBIRQ_SUSPEND | USBIRQ_URESET \
| USBIRQ_HSPEED | USBIRQ_LPM)
/**
* struct cdns2_usb_regs - USB controller registers.
* @reserved: reserved.
* @lpmctrl: LPM control register.
* @lpmclock: LPM clock register.
* @reserved2: reserved.
* @endprst: endpoint reset register.
* @usbcs: USB control and status register.
* @frmnr: USB frame counter register.
* @fnaddr: function Address register.
* @clkgate: clock gate register.
* @fifoctrl: FIFO control register.
* @speedctrl: speed Control register.
* @sleep_clkgate: sleep Clock Gate register.
* @reserved3: reserved.
* @cpuctrl: microprocessor control register.
*/
struct cdns2_usb_regs {
__u8 reserved[4];
__u16 lpmctrl;
__u8 lpmclock;
__u8 reserved2[411];
__u8 endprst;
__u8 usbcs;
__le16 frmnr;
__u8 fnaddr;
__u8 clkgate;
__u8 fifoctrl;
__u8 speedctrl;
__u8 sleep_clkgate;
__u8 reserved3[533];
__u8 cpuctrl;
} __packed __aligned(4);
/* LPMCTRL - bitmasks. */
/* BESL (Best Effort Service Latency). */
#define LPMCTRLLL_HIRD GENMASK(7, 4)
/* Last received Remote Wakeup field from LPM Extended Token packet. */
#define LPMCTRLLH_BREMOTEWAKEUP BIT(8)
/* Reflects value of the lpmnyet bit located in the usbcs[1] register. */
#define LPMCTRLLH_LPMNYET BIT(16)
/* LPMCLOCK - bitmasks. */
/*
* If bit is 1 the controller automatically turns off clock
* (utmisleepm goes to low), else the microprocessor should use
* sleep clock gate register to turn off clock.
*/
#define LPMCLOCK_SLEEP_ENTRY BIT(7)
/* USBCS - bitmasks. */
/* Send NYET handshake for the LPM transaction. */
#define USBCS_LPMNYET BIT(2)
/* Remote wake-up bit. */
#define USBCS_SIGRSUME BIT(5)
/* Software disconnect bit. */
#define USBCS_DISCON BIT(6)
/* Indicates that a wakeup pin resumed the controller. */
#define USBCS_WAKESRC BIT(7)
/* FIFOCTRL - bitmasks. */
/* Endpoint number. */
#define FIFOCTRL_EP GENMASK(3, 0)
/* Direction bit. */
#define FIFOCTRL_IO_TX BIT(4)
/* FIFO auto bit. */
#define FIFOCTRL_FIFOAUTO BIT(5)
/* FIFO commit bit. */
#define FIFOCTRL_FIFOCMIT BIT(6)
/* FIFO access bit. */
#define FIFOCTRL_FIFOACC BIT(7)
/* SPEEDCTRL - bitmasks. */
/* Device works in Full Speed. */
#define SPEEDCTRL_FS BIT(1)
/* Device works in High Speed. */
#define SPEEDCTRL_HS BIT(2)
/* Force FS mode. */
#define SPEEDCTRL_HSDISABLE BIT(7)
/* CPUCTRL- bitmasks. */
/* UP clock enable */
#define CPUCTRL_UPCLK BIT(0)
/* Controller reset bit. */
#define CPUCTRL_SW_RST BIT(1)
/**
* If the wuen bit is ‘1’, the upclken is automatically set to ‘1’ after
* detecting rising edge of wuintereq interrupt. If the wuen bit is ‘0’,
* the wuintereq interrupt is ignored.
*/
#define CPUCTRL_WUEN BIT(7)
/**
* struct cdns2_adma_regs - ADMA controller registers.
* @conf: DMA global configuration register.
* @sts: DMA global Status register.
* @reserved1: reserved.
* @ep_sel: DMA endpoint select register.
* @ep_traddr: DMA endpoint transfer ring address register.
* @ep_cfg: DMA endpoint configuration register.
* @ep_cmd: DMA endpoint command register.
* @ep_sts: DMA endpoint status register.
* @reserved2: reserved.
* @ep_sts_en: DMA endpoint status enable register.
* @drbl: DMA doorbell register.
* @ep_ien: DMA endpoint interrupt enable register.
* @ep_ists: DMA endpoint interrupt status register.
* @axim_ctrl: AXI Master Control register.
* @axim_id: AXI Master ID register.
* @reserved3: reserved.
* @axim_cap: AXI Master Wrapper Extended Capability.
* @reserved4: reserved.
* @axim_ctrl0: AXI Master Wrapper Extended Capability Control Register 0.
* @axim_ctrl1: AXI Master Wrapper Extended Capability Control Register 1.
*/
struct cdns2_adma_regs {
__le32 conf;
__le32 sts;
__le32 reserved1[5];
__le32 ep_sel;
__le32 ep_traddr;
__le32 ep_cfg;
__le32 ep_cmd;
__le32 ep_sts;
__le32 reserved2;
__le32 ep_sts_en;
__le32 drbl;
__le32 ep_ien;
__le32 ep_ists;
__le32 axim_ctrl;
__le32 axim_id;
__le32 reserved3;
__le32 axim_cap;
__le32 reserved4;
__le32 axim_ctrl0;
__le32 axim_ctrl1;
};
#define CDNS2_ADMA_REGS_OFFSET 0x400
/* DMA_CONF - bitmasks. */
/* Reset USB device configuration. */
#define DMA_CONF_CFGRST BIT(0)
/* Singular DMA transfer mode.*/
#define DMA_CONF_DSING BIT(8)
/* Multiple DMA transfers mode.*/
#define DMA_CONF_DMULT BIT(9)
/* DMA_EP_CFG - bitmasks. */
/* Endpoint enable. */
#define DMA_EP_CFG_ENABLE BIT(0)
/* DMA_EP_CMD - bitmasks. */
/* Endpoint reset. */
#define DMA_EP_CMD_EPRST BIT(0)
/* Transfer descriptor ready. */
#define DMA_EP_CMD_DRDY BIT(6)
/* Data flush. */
#define DMA_EP_CMD_DFLUSH BIT(7)
/* DMA_EP_STS - bitmasks. */
/* Interrupt On Complete. */
#define DMA_EP_STS_IOC BIT(2)
/* Interrupt on Short Packet. */
#define DMA_EP_STS_ISP BIT(3)
/* Transfer descriptor missing. */
#define DMA_EP_STS_DESCMIS BIT(4)
/* TRB error. */
#define DMA_EP_STS_TRBERR BIT(7)
/* DMA busy bit. */
#define DMA_EP_STS_DBUSY BIT(9)
/* Current Cycle Status. */
#define DMA_EP_STS_CCS(p) ((p) & BIT(11))
/* OUT size mismatch. */
#define DMA_EP_STS_OUTSMM BIT(14)
/* ISO transmission error. */
#define DMA_EP_STS_ISOERR BIT(15)
/* DMA_EP_STS_EN - bitmasks. */
/* OUT transfer missing descriptor enable. */
#define DMA_EP_STS_EN_DESCMISEN BIT(4)
/* TRB enable. */
#define DMA_EP_STS_EN_TRBERREN BIT(7)
/* OUT size mismatch enable. */
#define DMA_EP_STS_EN_OUTSMMEN BIT(14)
/* ISO transmission error enable. */
#define DMA_EP_STS_EN_ISOERREN BIT(15)
/* DMA_EP_IEN - bitmasks. */
#define DMA_EP_IEN(index) (1 << (index))
#define DMA_EP_IEN_EP_OUT0 BIT(0)
#define DMA_EP_IEN_EP_IN0 BIT(16)
/* DMA_EP_ISTS - bitmasks. */
#define DMA_EP_ISTS(index) (1 << (index))
#define DMA_EP_ISTS_EP_OUT0 BIT(0)
#define DMA_EP_ISTS_EP_IN0 BIT(16)
#define gadget_to_cdns2_device(g) (container_of(g, struct cdns2_device, gadget))
#define ep_to_cdns2_ep(ep) (container_of(ep, struct cdns2_endpoint, endpoint))
/*-------------------------------------------------------------------------*/
#define TRBS_PER_SEGMENT 600
#define ISO_MAX_INTERVAL 8
#define MAX_TRB_LENGTH BIT(16)
#define MAX_ISO_SIZE 3076
/*
* To improve performance the TRB buffer pointers can't cross
* 4KB boundaries.
*/
#define TRB_MAX_ISO_BUFF_SHIFT 12
#define TRB_MAX_ISO_BUFF_SIZE BIT(TRB_MAX_ISO_BUFF_SHIFT)
/* How much data is left before the 4KB boundary? */
#define TRB_BUFF_LEN_UP_TO_BOUNDARY(addr) (TRB_MAX_ISO_BUFF_SIZE - \
((addr) & (TRB_MAX_ISO_BUFF_SIZE - 1)))
#if TRBS_PER_SEGMENT < 2
#error "Incorrect TRBS_PER_SEGMENT. Minimal Transfer Ring size is 2."
#endif
/**
* struct cdns2_trb - represent Transfer Descriptor block.
* @buffer: pointer to buffer data.
* @length: length of data.
* @control: control flags.
*
* This structure describes transfer block handled by DMA module.
*/
struct cdns2_trb {
__le32 buffer;
__le32 length;
__le32 control;
};
#define TRB_SIZE (sizeof(struct cdns2_trb))
/*
* These two extra TRBs are reserved for isochronous transfer
* to inject 0 length packet and extra LINK TRB to synchronize the ISO transfer.
*/
#define TRB_ISO_RESERVED 2
#define TR_SEG_SIZE (TRB_SIZE * (TRBS_PER_SEGMENT + TRB_ISO_RESERVED))
/* TRB bit mask. */
#define TRB_TYPE_BITMASK GENMASK(15, 10)
#define TRB_TYPE(p) ((p) << 10)
#define TRB_FIELD_TO_TYPE(p) (((p) & TRB_TYPE_BITMASK) >> 10)
/* TRB type IDs. */
/* Used for Bulk, Interrupt, ISOC, and control data stage. */
#define TRB_NORMAL 1
/* TRB for linking ring segments. */
#define TRB_LINK 6
/* Cycle bit - indicates TRB ownership by driver or hw. */
#define TRB_CYCLE BIT(0)
/*
* When set to '1', the device will toggle its interpretation of the Cycle bit.
*/
#define TRB_TOGGLE BIT(1)
/* Interrupt on short packet. */
#define TRB_ISP BIT(2)
/* Chain bit associate this TRB with next one TRB. */
#define TRB_CHAIN BIT(4)
/* Interrupt on completion. */
#define TRB_IOC BIT(5)
/* Transfer_len bitmasks. */
#define TRB_LEN(p) ((p) & GENMASK(16, 0))
#define TRB_BURST(p) (((p) << 24) & GENMASK(31, 24))
#define TRB_FIELD_TO_BURST(p) (((p) & GENMASK(31, 24)) >> 24)
/* Data buffer pointer bitmasks. */
#define TRB_BUFFER(p) ((p) & GENMASK(31, 0))
/*-------------------------------------------------------------------------*/
/* Driver numeric constants. */
/* Maximum address that can be assigned to device. */
#define USB_DEVICE_MAX_ADDRESS 127
/* One control and 15 IN and 15 OUT endpoints. */
#define CDNS2_ENDPOINTS_NUM 31
#define CDNS2_EP_ZLP_BUF_SIZE 512
/*-------------------------------------------------------------------------*/
/* Used structures. */
struct cdns2_device;
/**
* struct cdns2_ring - transfer ring representation.
* @trbs: pointer to transfer ring.
* @dma: dma address of transfer ring.
* @free_trbs: number of free TRBs in transfer ring.
* @pcs: producer cycle state.
* @ccs: consumer cycle state.
* @enqueue: enqueue index in transfer ring.
* @dequeue: dequeue index in transfer ring.
*/
struct cdns2_ring {
struct cdns2_trb *trbs;
dma_addr_t dma;
int free_trbs;
u8 pcs;
u8 ccs;
int enqueue;
int dequeue;
};
/**
* struct cdns2_endpoint - extended device side representation of USB endpoint.
* @endpoint: usb endpoint.
* @pending_list: list of requests queuing on transfer ring.
* @deferred_list: list of requests waiting for queuing on transfer ring.
* @pdev: device associated with endpoint.
* @name: a human readable name e.g. ep1out.
* @ring: transfer ring associated with endpoint.
* @ep_state: state of endpoint.
* @idx: index of endpoint in pdev->eps table.
* @dir: endpoint direction.
* @num: endpoint number (1 - 15).
* @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK.
* @interval: interval between packets used for ISOC and Interrupt endpoint.
* @buffering: on-chip buffers assigned to endpoint.
* @trb_burst_size: number of burst used in TRB.
* @skip: Sometimes the controller cannot process isochronous endpoint ring
* quickly enough and it will miss some isoc tds on the ring and
* generate ISO transmition error.
* Driver sets skip flag when receive a ISO transmition error and
* process the missed TDs on the endpoint ring.
* @wa1_set: use WA1.
* @wa1_trb: TRB assigned to WA1.
* @wa1_trb_index: TRB index for WA1.
* @wa1_cycle_bit: correct cycle bit for WA1.
*/
struct cdns2_endpoint {
struct usb_ep endpoint;
struct list_head pending_list;
struct list_head deferred_list;
struct cdns2_device *pdev;
char name[20];
struct cdns2_ring ring;
#define EP_ENABLED BIT(0)
#define EP_STALLED BIT(1)
#define EP_STALL_PENDING BIT(2)
#define EP_WEDGE BIT(3)
#define EP_CLAIMED BIT(4)
#define EP_RING_FULL BIT(5)
#define EP_DEFERRED_DRDY BIT(6)
u32 ep_state;
u8 idx;
u8 dir;
u8 num;
u8 type;
int interval;
u8 buffering;
u8 trb_burst_size;
bool skip;
unsigned int wa1_set:1;
struct cdns2_trb *wa1_trb;
unsigned int wa1_trb_index;
unsigned int wa1_cycle_bit:1;
};
/**
* struct cdns2_request - extended device side representation of usb_request
* object.
* @request: generic usb_request object describing single I/O request.
* @pep: extended representation of usb_ep object.
* @trb: the first TRB association with this request.
* @start_trb: number of the first TRB in transfer ring.
* @end_trb: number of the last TRB in transfer ring.
* @list: used for queuing request in lists.
* @finished_trb: number of trb has already finished per request.
* @num_of_trb: how many trbs are associated with request.
*/
struct cdns2_request {
struct usb_request request;
struct cdns2_endpoint *pep;
struct cdns2_trb *trb;
int start_trb;
int end_trb;
struct list_head list;
int finished_trb;
int num_of_trb;
};
#define to_cdns2_request(r) (container_of(r, struct cdns2_request, request))
/* Stages used during enumeration process.*/
#define CDNS2_SETUP_STAGE 0x0
#define CDNS2_DATA_STAGE 0x1
#define CDNS2_STATUS_STAGE 0x2
/**
* struct cdns2_device - represent USB device.
* @dev: pointer to device structure associated whit this controller.
* @gadget: device side representation of the peripheral controller.
* @gadget_driver: pointer to the gadget driver.
* @lock: for synchronizing.
* @irq: interrupt line number.
* @regs: base address for registers
* @usb_regs: base address for common USB registers.
* @ep0_regs: base address for endpoint 0 related registers.
* @epx_regs: base address for all none control endpoint registers.
* @interrupt_regs: base address for interrupt handling related registers.
* @adma_regs: base address for ADMA registers.
* @eps_dma_pool: endpoint Transfer Ring pool.
* @setup: used while processing usb control requests.
* @ep0_preq: private request used while handling EP0.
* @ep0_stage: ep0 stage during enumeration process.
* @zlp_buf: zlp buffer.
* @dev_address: device address assigned by host.
* @eps: array of objects describing endpoints.
* @selected_ep: actually selected endpoint. It's used only to improve
* performance by limiting access to dma_ep_sel register.
* @is_selfpowered: device is self powered.
* @may_wakeup: allows device to remote wakeup the host.
* @status_completion_no_call: indicate that driver is waiting for status
* stage completion. It's used in deferred SET_CONFIGURATION request.
* @in_lpm: indicate the controller is in low power mode.
* @pending_status_wq: workqueue handling status stage for deferred requests.
* @pending_status_request: request for which status stage was deferred.
* @eps_supported: endpoints supported by controller in form:
* bit: 0 - ep0, 1 - epOut1, 2 - epIn1, 3 - epOut2 ...
* @burst_opt: array with the best burst size value for different TRB size.
* @onchip_tx_buf: size of transmit on-chip buffer in KB.
* @onchip_rx_buf: size of receive on-chip buffer in KB.
*/
struct cdns2_device {
struct device *dev;
struct usb_gadget gadget;
struct usb_gadget_driver *gadget_driver;
/* generic spin-lock for drivers */
spinlock_t lock;
int irq;
void __iomem *regs;
struct cdns2_usb_regs __iomem *usb_regs;
struct cdns2_ep0_regs __iomem *ep0_regs;
struct cdns2_epx_regs __iomem *epx_regs;
struct cdns2_interrupt_regs __iomem *interrupt_regs;
struct cdns2_adma_regs __iomem *adma_regs;
struct dma_pool *eps_dma_pool;
struct usb_ctrlrequest setup;
struct cdns2_request ep0_preq;
u8 ep0_stage;
void *zlp_buf;
u8 dev_address;
struct cdns2_endpoint eps[CDNS2_ENDPOINTS_NUM];
u32 selected_ep;
bool is_selfpowered;
bool may_wakeup;
bool status_completion_no_call;
bool in_lpm;
struct work_struct pending_status_wq;
struct usb_request *pending_status_request;
u32 eps_supported;
u8 burst_opt[MAX_ISO_SIZE + 1];
/*in KB */
u16 onchip_tx_buf;
u16 onchip_rx_buf;
};
#define CDNS2_IF_EP_EXIST(pdev, ep_num, dir) \
((pdev)->eps_supported & \
(BIT(ep_num) << ((dir) ? 0 : 16)))
dma_addr_t cdns2_trb_virt_to_dma(struct cdns2_endpoint *pep,
struct cdns2_trb *trb);
void cdns2_pending_setup_status_handler(struct work_struct *work);
void cdns2_select_ep(struct cdns2_device *pdev, u32 ep);
struct cdns2_request *cdns2_next_preq(struct list_head *list);
struct usb_request *cdns2_gadget_ep_alloc_request(struct usb_ep *ep,
gfp_t gfp_flags);
void cdns2_gadget_ep_free_request(struct usb_ep *ep,
struct usb_request *request);
int cdns2_gadget_ep_dequeue(struct usb_ep *ep, struct usb_request *request);
void cdns2_gadget_giveback(struct cdns2_endpoint *pep,
struct cdns2_request *priv_req,
int status);
void cdns2_init_ep0(struct cdns2_device *pdev, struct cdns2_endpoint *pep);
void cdns2_ep0_config(struct cdns2_device *pdev);
void cdns2_handle_ep0_interrupt(struct cdns2_device *pdev, int dir);
void cdns2_handle_setup_packet(struct cdns2_device *pdev);
int cdns2_gadget_resume(struct cdns2_device *pdev, bool hibernated);
int cdns2_gadget_suspend(struct cdns2_device *pdev);
void cdns2_gadget_remove(struct cdns2_device *pdev);
int cdns2_gadget_init(struct cdns2_device *pdev);
void set_reg_bit_8(void __iomem *ptr, u8 mask);
int cdns2_halt_endpoint(struct cdns2_device *pdev, struct cdns2_endpoint *pep,
int value);
#endif /* __LINUX_CDNS2_GADGET */
@@ -0,0 +1,136 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence USBHS-DEV controller - PCI Glue driver.
*
* Copyright (C) 2023 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*
*/
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include "cdns2-gadget.h"
#define PCI_DRIVER_NAME "cdns-pci-usbhs"
#define PCI_BAR_DEV 0
#define PCI_DEV_FN_DEVICE 0
static int cdns2_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
resource_size_t rsrc_start, rsrc_len;
struct device *dev = &pdev->dev;
struct cdns2_device *priv_dev;
struct resource *res;
int ret;
/* For GADGET PCI (devfn) function number is 0. */
if (!id || pdev->devfn != PCI_DEV_FN_DEVICE ||
pdev->class != PCI_CLASS_SERIAL_USB_DEVICE)
return -EINVAL;
ret = pcim_enable_device(pdev);
if (ret) {
dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", ret);
return ret;
}
pci_set_master(pdev);
priv_dev = devm_kzalloc(&pdev->dev, sizeof(*priv_dev), GFP_KERNEL);
if (!priv_dev)
return -ENOMEM;
dev_dbg(dev, "Initialize resources\n");
rsrc_start = pci_resource_start(pdev, PCI_BAR_DEV);
rsrc_len = pci_resource_len(pdev, PCI_BAR_DEV);
res = devm_request_mem_region(dev, rsrc_start, rsrc_len, "dev");
if (!res) {
dev_dbg(dev, "controller already in use\n");
return -EBUSY;
}
priv_dev->regs = devm_ioremap(dev, rsrc_start, rsrc_len);
if (!priv_dev->regs) {
dev_dbg(dev, "error mapping memory\n");
return -EFAULT;
}
priv_dev->irq = pdev->irq;
dev_dbg(dev, "USBSS-DEV physical base addr: %pa\n",
&rsrc_start);
priv_dev->dev = dev;
priv_dev->eps_supported = 0x000f000f;
priv_dev->onchip_tx_buf = 16;
priv_dev->onchip_rx_buf = 16;
ret = cdns2_gadget_init(priv_dev);
if (ret)
return ret;
pci_set_drvdata(pdev, priv_dev);
device_wakeup_enable(&pdev->dev);
if (pci_dev_run_wake(pdev))
pm_runtime_put_noidle(&pdev->dev);
return 0;
}
static void cdns2_pci_remove(struct pci_dev *pdev)
{
struct cdns2_device *priv_dev = pci_get_drvdata(pdev);
if (pci_dev_run_wake(pdev))
pm_runtime_get_noresume(&pdev->dev);
cdns2_gadget_remove(priv_dev);
}
static int cdns2_pci_suspend(struct device *dev)
{
struct cdns2_device *priv_dev = dev_get_drvdata(dev);
return cdns2_gadget_suspend(priv_dev);
}
static int cdns2_pci_resume(struct device *dev)
{
struct cdns2_device *priv_dev = dev_get_drvdata(dev);
return cdns2_gadget_resume(priv_dev, 1);
}
static const struct dev_pm_ops cdns2_pci_pm_ops = {
SYSTEM_SLEEP_PM_OPS(cdns2_pci_suspend, cdns2_pci_resume)
};
static const struct pci_device_id cdns2_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USB),
.class = PCI_CLASS_SERIAL_USB_DEVICE },
{ 0, }
};
static struct pci_driver cdns2_pci_driver = {
.name = "cdns2-pci",
.id_table = cdns2_pci_ids,
.probe = cdns2_pci_probe,
.remove = cdns2_pci_remove,
.driver = {
.pm = pm_ptr(&cdns2_pci_pm_ops),
}
};
module_pci_driver(cdns2_pci_driver);
MODULE_DEVICE_TABLE(pci, cdns2_pci_ids);
MODULE_ALIAS("pci:cdns2");
MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cadence CDNS2 PCI driver");
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
/*
* USBHS device controller driver Trace Support
*
* Copyright (C) 2023 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
#define CREATE_TRACE_POINTS
#include "cdns2-trace.h"
@@ -0,0 +1,536 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* USBHS-DEV device controller driver.
* Trace support header file.
*
* Copyright (C) 2023 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM cdns2-dev
/*
* The TRACE_SYSTEM_VAR defaults to TRACE_SYSTEM, but must be a
* legitimate C variable. It is not exported to user space.
*/
#undef TRACE_SYSTEM_VAR
#define TRACE_SYSTEM_VAR cdns2_dev
#if !defined(__LINUX_CDNS2_TRACE) || defined(TRACE_HEADER_MULTI_READ)
#define __LINUX_CDNS2_TRACE
#include <linux/types.h>
#include <linux/tracepoint.h>
#include <asm/byteorder.h>
#include <linux/usb/ch9.h>
#include "cdns2-gadget.h"
#include "cdns2-debug.h"
#define CDNS2_MSG_MAX 500
DECLARE_EVENT_CLASS(cdns2_log_enable_disable,
TP_PROTO(int set),
TP_ARGS(set),
TP_STRUCT__entry(
__field(int, set)
),
TP_fast_assign(
__entry->set = set;
),
TP_printk("%s", __entry->set ? "enabled" : "disabled")
);
DEFINE_EVENT(cdns2_log_enable_disable, cdns2_pullup,
TP_PROTO(int set),
TP_ARGS(set)
);
DECLARE_EVENT_CLASS(cdns2_log_simple,
TP_PROTO(char *msg),
TP_ARGS(msg),
TP_STRUCT__entry(
__string(text, msg)
),
TP_fast_assign(
__assign_str(text);
),
TP_printk("%s", __get_str(text))
);
DEFINE_EVENT(cdns2_log_simple, cdns2_no_room_on_ring,
TP_PROTO(char *msg),
TP_ARGS(msg)
);
DEFINE_EVENT(cdns2_log_simple, cdns2_ep0_status_stage,
TP_PROTO(char *msg),
TP_ARGS(msg)
);
DEFINE_EVENT(cdns2_log_simple, cdns2_ep0_setup,
TP_PROTO(char *msg),
TP_ARGS(msg)
);
DEFINE_EVENT(cdns2_log_simple, cdns2_device_state,
TP_PROTO(char *msg),
TP_ARGS(msg)
);
TRACE_EVENT(cdns2_ep_halt,
TP_PROTO(struct cdns2_endpoint *ep_priv, u8 halt, u8 flush),
TP_ARGS(ep_priv, halt, flush),
TP_STRUCT__entry(
__string(name, ep_priv->name)
__field(u8, halt)
__field(u8, flush)
),
TP_fast_assign(
__assign_str(name);
__entry->halt = halt;
__entry->flush = flush;
),
TP_printk("Halt %s for %s: %s", __entry->flush ? " and flush" : "",
__get_str(name), __entry->halt ? "set" : "cleared")
);
TRACE_EVENT(cdns2_wa1,
TP_PROTO(struct cdns2_endpoint *ep_priv, char *msg),
TP_ARGS(ep_priv, msg),
TP_STRUCT__entry(
__string(ep_name, ep_priv->name)
__string(msg, msg)
),
TP_fast_assign(
__assign_str(ep_name);
__assign_str(msg);
),
TP_printk("WA1: %s %s", __get_str(ep_name), __get_str(msg))
);
DECLARE_EVENT_CLASS(cdns2_log_doorbell,
TP_PROTO(struct cdns2_endpoint *pep, u32 ep_trbaddr),
TP_ARGS(pep, ep_trbaddr),
TP_STRUCT__entry(
__string(name, pep->num ? pep->name :
(pep->dir ? "ep0in" : "ep0out"))
__field(u32, ep_trbaddr)
),
TP_fast_assign(
__assign_str(name);
__entry->ep_trbaddr = ep_trbaddr;
),
TP_printk("%s, ep_trbaddr %08x", __get_str(name),
__entry->ep_trbaddr)
);
DEFINE_EVENT(cdns2_log_doorbell, cdns2_doorbell_ep0,
TP_PROTO(struct cdns2_endpoint *pep, u32 ep_trbaddr),
TP_ARGS(pep, ep_trbaddr)
);
DEFINE_EVENT(cdns2_log_doorbell, cdns2_doorbell_epx,
TP_PROTO(struct cdns2_endpoint *pep, u32 ep_trbaddr),
TP_ARGS(pep, ep_trbaddr)
);
DECLARE_EVENT_CLASS(cdns2_log_usb_irq,
TP_PROTO(u32 usb_irq, u32 ext_irq),
TP_ARGS(usb_irq, ext_irq),
TP_STRUCT__entry(
__field(u32, usb_irq)
__field(u32, ext_irq)
),
TP_fast_assign(
__entry->usb_irq = usb_irq;
__entry->ext_irq = ext_irq;
),
TP_printk("%s", cdns2_decode_usb_irq(__get_buf(CDNS2_MSG_MAX),
CDNS2_MSG_MAX,
__entry->usb_irq,
__entry->ext_irq))
);
DEFINE_EVENT(cdns2_log_usb_irq, cdns2_usb_irq,
TP_PROTO(u32 usb_irq, u32 ext_irq),
TP_ARGS(usb_irq, ext_irq)
);
TRACE_EVENT(cdns2_dma_ep_ists,
TP_PROTO(u32 dma_ep_ists),
TP_ARGS(dma_ep_ists),
TP_STRUCT__entry(
__field(u32, dma_ep_ists)
),
TP_fast_assign(
__entry->dma_ep_ists = dma_ep_ists;
),
TP_printk("OUT: 0x%04x, IN: 0x%04x", (u16)__entry->dma_ep_ists,
__entry->dma_ep_ists >> 16)
);
DECLARE_EVENT_CLASS(cdns2_log_epx_irq,
TP_PROTO(struct cdns2_device *pdev, struct cdns2_endpoint *pep),
TP_ARGS(pdev, pep),
TP_STRUCT__entry(
__string(ep_name, pep->name)
__field(u32, ep_sts)
__field(u32, ep_ists)
__field(u32, ep_traddr)
),
TP_fast_assign(
__assign_str(ep_name);
__entry->ep_sts = readl(&pdev->adma_regs->ep_sts);
__entry->ep_ists = readl(&pdev->adma_regs->ep_ists);
__entry->ep_traddr = readl(&pdev->adma_regs->ep_traddr);
),
TP_printk("%s, ep_traddr: %08x",
cdns2_decode_epx_irq(__get_buf(CDNS2_MSG_MAX), CDNS2_MSG_MAX,
__get_str(ep_name),
__entry->ep_ists, __entry->ep_sts),
__entry->ep_traddr)
);
DEFINE_EVENT(cdns2_log_epx_irq, cdns2_epx_irq,
TP_PROTO(struct cdns2_device *pdev, struct cdns2_endpoint *pep),
TP_ARGS(pdev, pep)
);
DECLARE_EVENT_CLASS(cdns2_log_ep0_irq,
TP_PROTO(struct cdns2_device *pdev),
TP_ARGS(pdev),
TP_STRUCT__entry(
__field(int, ep_dir)
__field(u32, ep_ists)
__field(u32, ep_sts)
),
TP_fast_assign(
__entry->ep_dir = pdev->selected_ep;
__entry->ep_ists = readl(&pdev->adma_regs->ep_ists);
__entry->ep_sts = readl(&pdev->adma_regs->ep_sts);
),
TP_printk("%s", cdns2_decode_ep0_irq(__get_buf(CDNS2_MSG_MAX),
CDNS2_MSG_MAX,
__entry->ep_ists, __entry->ep_sts,
__entry->ep_dir))
);
DEFINE_EVENT(cdns2_log_ep0_irq, cdns2_ep0_irq,
TP_PROTO(struct cdns2_device *pdev),
TP_ARGS(pdev)
);
DECLARE_EVENT_CLASS(cdns2_log_ctrl,
TP_PROTO(struct usb_ctrlrequest *ctrl),
TP_ARGS(ctrl),
TP_STRUCT__entry(
__field(u8, bRequestType)
__field(u8, bRequest)
__field(u16, wValue)
__field(u16, wIndex)
__field(u16, wLength)
),
TP_fast_assign(
__entry->bRequestType = ctrl->bRequestType;
__entry->bRequest = ctrl->bRequest;
__entry->wValue = le16_to_cpu(ctrl->wValue);
__entry->wIndex = le16_to_cpu(ctrl->wIndex);
__entry->wLength = le16_to_cpu(ctrl->wLength);
),
TP_printk("%s", usb_decode_ctrl(__get_buf(CDNS2_MSG_MAX), CDNS2_MSG_MAX,
__entry->bRequestType,
__entry->bRequest, __entry->wValue,
__entry->wIndex, __entry->wLength)
)
);
DEFINE_EVENT(cdns2_log_ctrl, cdns2_ctrl_req,
TP_PROTO(struct usb_ctrlrequest *ctrl),
TP_ARGS(ctrl)
);
DECLARE_EVENT_CLASS(cdns2_log_request,
TP_PROTO(struct cdns2_request *preq),
TP_ARGS(preq),
TP_STRUCT__entry(
__string(name, preq->pep->name)
__field(struct usb_request *, request)
__field(struct cdns2_request *, preq)
__field(void *, buf)
__field(unsigned int, actual)
__field(unsigned int, length)
__field(int, status)
__field(dma_addr_t, dma)
__field(int, zero)
__field(int, short_not_ok)
__field(int, no_interrupt)
__field(struct scatterlist*, sg)
__field(unsigned int, num_sgs)
__field(unsigned int, num_mapped_sgs)
__field(int, start_trb)
__field(int, end_trb)
),
TP_fast_assign(
__assign_str(name);
__entry->request = &preq->request;
__entry->preq = preq;
__entry->buf = preq->request.buf;
__entry->actual = preq->request.actual;
__entry->length = preq->request.length;
__entry->status = preq->request.status;
__entry->dma = preq->request.dma;
__entry->zero = preq->request.zero;
__entry->short_not_ok = preq->request.short_not_ok;
__entry->no_interrupt = preq->request.no_interrupt;
__entry->sg = preq->request.sg;
__entry->num_sgs = preq->request.num_sgs;
__entry->num_mapped_sgs = preq->request.num_mapped_sgs;
__entry->start_trb = preq->start_trb;
__entry->end_trb = preq->end_trb;
),
TP_printk("%s: req: %p, preq: %p, req buf: %p, length: %u/%u, status: %d,"
"buf dma: (%pad), %s%s%s, sg: %p, num_sgs: %d, num_m_sgs: %d,"
"trb: [start: %d, end: %d]",
__get_str(name), __entry->request, __entry->preq,
__entry->buf, __entry->actual, __entry->length,
__entry->status, &__entry->dma,
__entry->zero ? "Z" : "z",
__entry->short_not_ok ? "S" : "s",
__entry->no_interrupt ? "I" : "i",
__entry->sg, __entry->num_sgs, __entry->num_mapped_sgs,
__entry->start_trb,
__entry->end_trb
)
);
DEFINE_EVENT(cdns2_log_request, cdns2_request_enqueue,
TP_PROTO(struct cdns2_request *preq),
TP_ARGS(preq)
);
DEFINE_EVENT(cdns2_log_request, cdns2_request_enqueue_error,
TP_PROTO(struct cdns2_request *preq),
TP_ARGS(preq)
);
DEFINE_EVENT(cdns2_log_request, cdns2_alloc_request,
TP_PROTO(struct cdns2_request *preq),
TP_ARGS(preq)
);
DEFINE_EVENT(cdns2_log_request, cdns2_free_request,
TP_PROTO(struct cdns2_request *preq),
TP_ARGS(preq)
);
DEFINE_EVENT(cdns2_log_request, cdns2_request_dequeue,
TP_PROTO(struct cdns2_request *preq),
TP_ARGS(preq)
);
DEFINE_EVENT(cdns2_log_request, cdns2_request_giveback,
TP_PROTO(struct cdns2_request *preq),
TP_ARGS(preq)
);
DECLARE_EVENT_CLASS(cdns2_log_trb,
TP_PROTO(struct cdns2_endpoint *pep, struct cdns2_trb *trb),
TP_ARGS(pep, trb),
TP_STRUCT__entry(
__string(name, pep->name)
__field(struct cdns2_trb *, trb)
__field(u32, buffer)
__field(u32, length)
__field(u32, control)
__field(u32, type)
),
TP_fast_assign(
__assign_str(name);
__entry->trb = trb;
__entry->buffer = le32_to_cpu(trb->buffer);
__entry->length = le32_to_cpu(trb->length);
__entry->control = le32_to_cpu(trb->control);
__entry->type = usb_endpoint_type(pep->endpoint.desc);
),
TP_printk("%s: trb V: %p, dma buf: P: 0x%08x, %s",
__get_str(name), __entry->trb, __entry->buffer,
cdns2_decode_trb(__get_buf(CDNS2_MSG_MAX), CDNS2_MSG_MAX,
__entry->control, __entry->length,
__entry->buffer))
);
DEFINE_EVENT(cdns2_log_trb, cdns2_queue_trb,
TP_PROTO(struct cdns2_endpoint *pep, struct cdns2_trb *trb),
TP_ARGS(pep, trb)
);
DEFINE_EVENT(cdns2_log_trb, cdns2_complete_trb,
TP_PROTO(struct cdns2_endpoint *pep, struct cdns2_trb *trb),
TP_ARGS(pep, trb)
);
DECLARE_EVENT_CLASS(cdns2_log_ring,
TP_PROTO(struct cdns2_endpoint *pep),
TP_ARGS(pep),
TP_STRUCT__entry(
__dynamic_array(u8, tr_seg, TR_SEG_SIZE)
__dynamic_array(u8, pep, sizeof(struct cdns2_endpoint))
__dynamic_array(char, buffer,
(TRBS_PER_SEGMENT * 65) + CDNS2_MSG_MAX)
),
TP_fast_assign(
memcpy(__get_dynamic_array(pep), pep,
sizeof(struct cdns2_endpoint));
memcpy(__get_dynamic_array(tr_seg), pep->ring.trbs,
TR_SEG_SIZE);
),
TP_printk("%s",
cdns2_raw_ring((struct cdns2_endpoint *)__get_str(pep),
(struct cdns2_trb *)__get_str(tr_seg),
__get_str(buffer),
(TRBS_PER_SEGMENT * 65) + CDNS2_MSG_MAX))
);
DEFINE_EVENT(cdns2_log_ring, cdns2_ring,
TP_PROTO(struct cdns2_endpoint *pep),
TP_ARGS(pep)
);
DECLARE_EVENT_CLASS(cdns2_log_ep,
TP_PROTO(struct cdns2_endpoint *pep),
TP_ARGS(pep),
TP_STRUCT__entry(
__string(name, pep->name)
__field(unsigned int, maxpacket)
__field(unsigned int, maxpacket_limit)
__field(unsigned int, flags)
__field(unsigned int, dir)
__field(u8, enqueue)
__field(u8, dequeue)
),
TP_fast_assign(
__assign_str(name);
__entry->maxpacket = pep->endpoint.maxpacket;
__entry->maxpacket_limit = pep->endpoint.maxpacket_limit;
__entry->flags = pep->ep_state;
__entry->dir = pep->dir;
__entry->enqueue = pep->ring.enqueue;
__entry->dequeue = pep->ring.dequeue;
),
TP_printk("%s: mps: %d/%d, enq idx: %d, deq idx: %d, "
"flags: %s%s%s%s, dir: %s",
__get_str(name), __entry->maxpacket,
__entry->maxpacket_limit, __entry->enqueue,
__entry->dequeue,
__entry->flags & EP_ENABLED ? "EN | " : "",
__entry->flags & EP_STALLED ? "STALLED | " : "",
__entry->flags & EP_WEDGE ? "WEDGE | " : "",
__entry->flags & EP_RING_FULL ? "RING FULL |" : "",
__entry->dir ? "IN" : "OUT"
)
);
DEFINE_EVENT(cdns2_log_ep, cdns2_gadget_ep_enable,
TP_PROTO(struct cdns2_endpoint *pep),
TP_ARGS(pep)
);
DEFINE_EVENT(cdns2_log_ep, cdns2_gadget_ep_disable,
TP_PROTO(struct cdns2_endpoint *pep),
TP_ARGS(pep)
);
DEFINE_EVENT(cdns2_log_ep, cdns2_ep_busy_try_halt_again,
TP_PROTO(struct cdns2_endpoint *pep),
TP_ARGS(pep)
);
DECLARE_EVENT_CLASS(cdns2_log_request_handled,
TP_PROTO(struct cdns2_request *priv_req, int current_index,
int handled),
TP_ARGS(priv_req, current_index, handled),
TP_STRUCT__entry(
__field(struct cdns2_request *, priv_req)
__field(unsigned int, dma_position)
__field(unsigned int, handled)
__field(unsigned int, dequeue_idx)
__field(unsigned int, enqueue_idx)
__field(unsigned int, start_trb)
__field(unsigned int, end_trb)
),
TP_fast_assign(
__entry->priv_req = priv_req;
__entry->dma_position = current_index;
__entry->handled = handled;
__entry->dequeue_idx = priv_req->pep->ring.dequeue;
__entry->enqueue_idx = priv_req->pep->ring.enqueue;
__entry->start_trb = priv_req->start_trb;
__entry->end_trb = priv_req->end_trb;
),
TP_printk("Req: %p %s, DMA pos: %d, ep deq: %d, ep enq: %d,"
" start trb: %d, end trb: %d",
__entry->priv_req,
__entry->handled ? "handled" : "not handled",
__entry->dma_position, __entry->dequeue_idx,
__entry->enqueue_idx, __entry->start_trb,
__entry->end_trb
)
);
DEFINE_EVENT(cdns2_log_request_handled, cdns2_request_handled,
TP_PROTO(struct cdns2_request *priv_req, int current_index,
int handled),
TP_ARGS(priv_req, current_index, handled)
);
DECLARE_EVENT_CLASS(cdns2_log_epx_reg_config,
TP_PROTO(struct cdns2_device *pdev, struct cdns2_endpoint *pep),
TP_ARGS(pdev, pep),
TP_STRUCT__entry(
__string(ep_name, pep->name)
__field(u8, burst_size)
__field(__le16, maxpack_reg)
__field(__u8, con_reg)
__field(u32, ep_sel_reg)
__field(u32, ep_sts_en_reg)
__field(u32, ep_cfg_reg)
),
TP_fast_assign(
__assign_str(ep_name);
__entry->burst_size = pep->trb_burst_size;
__entry->maxpack_reg = pep->dir ? readw(&pdev->epx_regs->txmaxpack[pep->num - 1]) :
readw(&pdev->epx_regs->rxmaxpack[pep->num - 1]);
__entry->con_reg = pep->dir ? readb(&pdev->epx_regs->ep[pep->num - 1].txcon) :
readb(&pdev->epx_regs->ep[pep->num - 1].rxcon);
__entry->ep_sel_reg = readl(&pdev->adma_regs->ep_sel);
__entry->ep_sts_en_reg = readl(&pdev->adma_regs->ep_sts_en);
__entry->ep_cfg_reg = readl(&pdev->adma_regs->ep_cfg);
),
TP_printk("%s, maxpack: %d, con: %02x, dma_ep_sel: %08x, dma_ep_sts_en: %08x"
" dma_ep_cfg %08x",
__get_str(ep_name), __entry->maxpack_reg, __entry->con_reg,
__entry->ep_sel_reg, __entry->ep_sts_en_reg,
__entry->ep_cfg_reg
)
);
DEFINE_EVENT(cdns2_log_epx_reg_config, cdns2_epx_hw_cfg,
TP_PROTO(struct cdns2_device *pdev, struct cdns2_endpoint *pep),
TP_ARGS(pdev, pep)
);
#endif /* __LINUX_CDNS2_TRACE */
/* This part must be outside header guard. */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE cdns2-trace
#include <trace/define_trace.h>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,417 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* drivers/usb/gadget/qe_udc.h
*
* Copyright (C) 2006-2008 Freescale Semiconductor, Inc. All rights reserved.
*
* Xiaobo Xie <X.Xie@freescale.com>
* Li Yang <leoli@freescale.com>
*
* Description:
* Freescale USB device/endpoint management registers
*/
#ifndef __FSL_QE_UDC_H
#define __FSL_QE_UDC_H
/* SoC type */
#define PORT_CPM 0
#define PORT_QE 1
#define USB_MAX_ENDPOINTS 4
#define USB_MAX_PIPES USB_MAX_ENDPOINTS
#define USB_EP0_MAX_SIZE 64
#define USB_MAX_CTRL_PAYLOAD 0x4000
#define USB_BDRING_LEN 16
#define USB_BDRING_LEN_RX 256
#define USB_BDRING_LEN_TX 16
#define MIN_EMPTY_BDS 128
#define MAX_DATA_BDS 8
#define USB_CRC_SIZE 2
#define USB_DIR_BOTH 0x88
#define R_BUF_MAXSIZE 0x800
#define USB_EP_PARA_ALIGNMENT 32
/* USB Mode Register bit define */
#define USB_MODE_EN 0x01
#define USB_MODE_HOST 0x02
#define USB_MODE_TEST 0x04
#define USB_MODE_SFTE 0x08
#define USB_MODE_RESUME 0x40
#define USB_MODE_LSS 0x80
/* USB Slave Address Register Mask */
#define USB_SLVADDR_MASK 0x7F
/* USB Endpoint register define */
#define USB_EPNUM_MASK 0xF000
#define USB_EPNUM_SHIFT 12
#define USB_TRANS_MODE_SHIFT 8
#define USB_TRANS_CTR 0x0000
#define USB_TRANS_INT 0x0100
#define USB_TRANS_BULK 0x0200
#define USB_TRANS_ISO 0x0300
#define USB_EP_MF 0x0020
#define USB_EP_RTE 0x0010
#define USB_THS_SHIFT 2
#define USB_THS_MASK 0x000c
#define USB_THS_NORMAL 0x0
#define USB_THS_IGNORE_IN 0x0004
#define USB_THS_NACK 0x0008
#define USB_THS_STALL 0x000c
#define USB_RHS_SHIFT 0
#define USB_RHS_MASK 0x0003
#define USB_RHS_NORMAL 0x0
#define USB_RHS_IGNORE_OUT 0x0001
#define USB_RHS_NACK 0x0002
#define USB_RHS_STALL 0x0003
#define USB_RTHS_MASK 0x000f
/* USB Command Register define */
#define USB_CMD_STR_FIFO 0x80
#define USB_CMD_FLUSH_FIFO 0x40
#define USB_CMD_ISFT 0x20
#define USB_CMD_DSFT 0x10
#define USB_CMD_EP_MASK 0x03
/* USB Event and Mask Register define */
#define USB_E_MSF_MASK 0x0800
#define USB_E_SFT_MASK 0x0400
#define USB_E_RESET_MASK 0x0200
#define USB_E_IDLE_MASK 0x0100
#define USB_E_TXE4_MASK 0x0080
#define USB_E_TXE3_MASK 0x0040
#define USB_E_TXE2_MASK 0x0020
#define USB_E_TXE1_MASK 0x0010
#define USB_E_SOF_MASK 0x0008
#define USB_E_BSY_MASK 0x0004
#define USB_E_TXB_MASK 0x0002
#define USB_E_RXB_MASK 0x0001
#define USBER_ALL_CLEAR 0x0fff
#define USB_E_DEFAULT_DEVICE (USB_E_RESET_MASK | USB_E_TXE4_MASK | \
USB_E_TXE3_MASK | USB_E_TXE2_MASK | \
USB_E_TXE1_MASK | USB_E_BSY_MASK | \
USB_E_TXB_MASK | USB_E_RXB_MASK)
#define USB_E_TXE_MASK (USB_E_TXE4_MASK | USB_E_TXE3_MASK|\
USB_E_TXE2_MASK | USB_E_TXE1_MASK)
/* USB Status Register define */
#define USB_IDLE_STATUS_MASK 0x01
/* USB Start of Frame Timer */
#define USB_USSFT_MASK 0x3FFF
/* USB Frame Number Register */
#define USB_USFRN_MASK 0xFFFF
struct usb_device_para{
u16 epptr[4];
u32 rstate;
u32 rptr;
u16 frame_n;
u16 rbcnt;
u32 rtemp;
u32 rxusb_data;
u16 rxuptr;
u8 reso[2];
u32 softbl;
u8 sofucrctemp;
};
struct usb_ep_para{
u16 rbase;
u16 tbase;
u8 rbmr;
u8 tbmr;
u16 mrblr;
u16 rbptr;
u16 tbptr;
u32 tstate;
u32 tptr;
u16 tcrc;
u16 tbcnt;
u32 ttemp;
u16 txusbu_ptr;
u8 reserve[2];
};
#define USB_BUSMODE_GBL 0x20
#define USB_BUSMODE_BO_MASK 0x18
#define USB_BUSMODE_BO_SHIFT 0x3
#define USB_BUSMODE_BE 0x2
#define USB_BUSMODE_CETM 0x04
#define USB_BUSMODE_DTB 0x02
/* Endpoint basic handle */
#define ep_index(EP) ((EP)->ep.desc->bEndpointAddress & 0xF)
#define ep_maxpacket(EP) ((EP)->ep.maxpacket)
#define ep_is_in(EP) ((ep_index(EP) == 0) ? (EP->udc->ep0_dir == \
USB_DIR_IN) : ((EP)->ep.desc->bEndpointAddress \
& USB_DIR_IN) == USB_DIR_IN)
/* ep0 transfer state */
#define WAIT_FOR_SETUP 0
#define DATA_STATE_XMIT 1
#define DATA_STATE_NEED_ZLP 2
#define WAIT_FOR_OUT_STATUS 3
#define DATA_STATE_RECV 4
/* ep tramsfer mode */
#define USBP_TM_CTL 0
#define USBP_TM_ISO 1
#define USBP_TM_BULK 2
#define USBP_TM_INT 3
/*-----------------------------------------------------------------------------
USB RX And TX DATA Frame
-----------------------------------------------------------------------------*/
struct qe_frame{
u8 *data;
u32 len;
u32 status;
u32 info;
void *privdata;
struct list_head node;
};
/* Frame structure, info field. */
#define PID_DATA0 0x80000000 /* Data toggle zero */
#define PID_DATA1 0x40000000 /* Data toggle one */
#define PID_SETUP 0x20000000 /* setup bit */
#define SETUP_STATUS 0x10000000 /* setup status bit */
#define SETADDR_STATUS 0x08000000 /* setupup address status bit */
#define NO_REQ 0x04000000 /* Frame without request */
#define HOST_DATA 0x02000000 /* Host data frame */
#define FIRST_PACKET_IN_FRAME 0x01000000 /* first packet in the frame */
#define TOKEN_FRAME 0x00800000 /* Host token frame */
#define ZLP 0x00400000 /* Zero length packet */
#define IN_TOKEN_FRAME 0x00200000 /* In token package */
#define OUT_TOKEN_FRAME 0x00100000 /* Out token package */
#define SETUP_TOKEN_FRAME 0x00080000 /* Setup token package */
#define STALL_FRAME 0x00040000 /* Stall handshake */
#define NACK_FRAME 0x00020000 /* Nack handshake */
#define NO_PID 0x00010000 /* No send PID */
#define NO_CRC 0x00008000 /* No send CRC */
#define HOST_COMMAND 0x00004000 /* Host command frame */
/* Frame status field */
/* Receive side */
#define FRAME_OK 0x00000000 /* Frame transmitted or received OK */
#define FRAME_ERROR 0x80000000 /* Error occurred on frame */
#define START_FRAME_LOST 0x40000000 /* START_FRAME_LOST */
#define END_FRAME_LOST 0x20000000 /* END_FRAME_LOST */
#define RX_ER_NONOCT 0x10000000 /* Rx Non Octet Aligned Packet */
#define RX_ER_BITSTUFF 0x08000000 /* Frame Aborted --Received packet
with bit stuff error */
#define RX_ER_CRC 0x04000000 /* Received packet with CRC error */
#define RX_ER_OVERUN 0x02000000 /* Over-run occurred on reception */
#define RX_ER_PID 0x01000000 /* Wrong PID received */
/* Tranmit side */
#define TX_ER_NAK 0x00800000 /* Received NAK handshake */
#define TX_ER_STALL 0x00400000 /* Received STALL handshake */
#define TX_ER_TIMEOUT 0x00200000 /* Transmit time out */
#define TX_ER_UNDERUN 0x00100000 /* Transmit underrun */
#define FRAME_INPROGRESS 0x00080000 /* Frame is being transmitted */
#define ER_DATA_UNDERUN 0x00040000 /* Frame is shorter then expected */
#define ER_DATA_OVERUN 0x00020000 /* Frame is longer then expected */
/* QE USB frame operation functions */
#define frame_get_length(frm) (frm->len)
#define frame_set_length(frm, leng) (frm->len = leng)
#define frame_get_data(frm) (frm->data)
#define frame_set_data(frm, dat) (frm->data = dat)
#define frame_get_info(frm) (frm->info)
#define frame_set_info(frm, inf) (frm->info = inf)
#define frame_get_status(frm) (frm->status)
#define frame_set_status(frm, stat) (frm->status = stat)
#define frame_get_privdata(frm) (frm->privdata)
#define frame_set_privdata(frm, dat) (frm->privdata = dat)
static inline void qe_frame_clean(struct qe_frame *frm)
{
frame_set_data(frm, NULL);
frame_set_length(frm, 0);
frame_set_status(frm, FRAME_OK);
frame_set_info(frm, 0);
frame_set_privdata(frm, NULL);
}
static inline void qe_frame_init(struct qe_frame *frm)
{
qe_frame_clean(frm);
INIT_LIST_HEAD(&(frm->node));
}
struct qe_req {
struct usb_request req;
struct list_head queue;
/* ep_queue() func will add
a request->queue into a udc_ep->queue 'd tail */
struct qe_ep *ep;
unsigned mapped:1;
};
struct qe_ep {
struct usb_ep ep;
struct list_head queue;
struct qe_udc *udc;
struct usb_gadget *gadget;
u8 state;
struct qe_bd __iomem *rxbase;
struct qe_bd __iomem *n_rxbd;
struct qe_bd __iomem *e_rxbd;
struct qe_bd __iomem *txbase;
struct qe_bd __iomem *n_txbd;
struct qe_bd __iomem *c_txbd;
struct qe_frame *rxframe;
u8 *rxbuffer;
dma_addr_t rxbuf_d;
u8 rxbufmap;
unsigned char localnack;
int has_data;
struct qe_frame *txframe;
struct qe_req *tx_req;
int sent; /*data already sent */
int last; /*data sent in the last time*/
u8 dir;
u8 epnum;
u8 tm; /* transfer mode */
u8 data01;
u8 init;
u8 already_seen;
u8 enable_tasklet;
u8 setup_stage;
u32 last_io; /* timestamp */
char name[14];
unsigned double_buf:1;
unsigned stopped:1;
unsigned fnf:1;
unsigned has_dma:1;
u8 ackwait;
u8 dma_channel;
u16 dma_counter;
int lch;
struct timer_list timer;
};
struct qe_udc {
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
struct device *dev;
struct qe_ep eps[USB_MAX_ENDPOINTS];
struct usb_ctrlrequest local_setup_buff;
spinlock_t lock; /* lock for set/config qe_udc */
unsigned long soc_type; /* QE or CPM soc */
struct qe_req *status_req; /* ep0 status request */
/* USB and EP Parameter Block pointer */
struct usb_device_para __iomem *usb_param;
struct usb_ep_para __iomem *ep_param[4];
u32 max_pipes; /* Device max pipes */
u32 max_use_endpts; /* Max endpointes to be used */
u32 bus_reset; /* Device is bus reseting */
u32 resume_state; /* USB state to resume*/
u32 usb_state; /* USB current state */
u32 usb_next_state; /* USB next state */
u32 ep0_state; /* Endpoint zero state */
u32 ep0_dir; /* Endpoint zero direction: can be
USB_DIR_IN or USB_DIR_OUT*/
u32 usb_sof_count; /* SOF count */
u32 errors; /* USB ERRORs count */
u8 *tmpbuf;
u32 c_start;
u32 c_end;
u8 *nullbuf;
u8 *statusbuf;
dma_addr_t nullp;
u8 nullmap;
u8 device_address; /* Device USB address */
unsigned int usb_clock;
unsigned int usb_irq;
struct usb_ctlr __iomem *usb_regs;
struct tasklet_struct rx_tasklet;
struct completion *done; /* to make sure release() is done */
};
#define EP_STATE_IDLE 0
#define EP_STATE_NACK 1
#define EP_STATE_STALL 2
/*
* transmit BD's status
*/
#define T_R 0x80000000 /* ready bit */
#define T_W 0x20000000 /* wrap bit */
#define T_I 0x10000000 /* interrupt on completion */
#define T_L 0x08000000 /* last */
#define T_TC 0x04000000 /* transmit CRC */
#define T_CNF 0x02000000 /* wait for transmit confirm */
#define T_LSP 0x01000000 /* Low-speed transaction */
#define T_PID 0x00c00000 /* packet id */
#define T_NAK 0x00100000 /* No ack. */
#define T_STAL 0x00080000 /* Stall received */
#define T_TO 0x00040000 /* time out */
#define T_UN 0x00020000 /* underrun */
#define DEVICE_T_ERROR (T_UN | T_TO)
#define HOST_T_ERROR (T_UN | T_TO | T_NAK | T_STAL)
#define DEVICE_T_BD_MASK DEVICE_T_ERROR
#define HOST_T_BD_MASK HOST_T_ERROR
#define T_PID_SHIFT 6
#define T_PID_DATA0 0x00800000 /* Data 0 toggle */
#define T_PID_DATA1 0x00c00000 /* Data 1 toggle */
/*
* receive BD's status
*/
#define R_E 0x80000000 /* buffer empty */
#define R_W 0x20000000 /* wrap bit */
#define R_I 0x10000000 /* interrupt on reception */
#define R_L 0x08000000 /* last */
#define R_F 0x04000000 /* first */
#define R_PID 0x00c00000 /* packet id */
#define R_NO 0x00100000 /* Rx Non Octet Aligned Packet */
#define R_AB 0x00080000 /* Frame Aborted */
#define R_CR 0x00040000 /* CRC Error */
#define R_OV 0x00020000 /* Overrun */
#define R_ERROR (R_NO | R_AB | R_CR | R_OV)
#define R_BD_MASK R_ERROR
#define R_PID_DATA0 0x00000000
#define R_PID_DATA1 0x00400000
#define R_PID_SETUP 0x00800000
#define CPM_USB_STOP_TX 0x2e600000
#define CPM_USB_RESTART_TX 0x2e600000
#define CPM_USB_STOP_TX_OPCODE 0x0a
#define CPM_USB_RESTART_TX_OPCODE 0x0b
#define CPM_USB_EP_SHIFT 5
#endif /* __FSL_QE_UDC_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,545 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2004,2012 Freescale Semiconductor, Inc
* All rights reserved.
*
* Freescale USB device/endpoint management registers
*/
#ifndef __FSL_USB2_UDC_H
#define __FSL_USB2_UDC_H
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
/* ### define USB registers here
*/
#define USB_MAX_CTRL_PAYLOAD 64
#define USB_DR_SYS_OFFSET 0x400
/* USB DR device mode registers (Little Endian) */
struct usb_dr_device {
/* Capability register */
u8 res1[256];
u16 caplength; /* Capability Register Length */
u16 hciversion; /* Host Controller Interface Version */
u32 hcsparams; /* Host Controller Structural Parameters */
u32 hccparams; /* Host Controller Capability Parameters */
u8 res2[20];
u32 dciversion; /* Device Controller Interface Version */
u32 dccparams; /* Device Controller Capability Parameters */
u8 res3[24];
/* Operation register */
u32 usbcmd; /* USB Command Register */
u32 usbsts; /* USB Status Register */
u32 usbintr; /* USB Interrupt Enable Register */
u32 frindex; /* Frame Index Register */
u8 res4[4];
u32 deviceaddr; /* Device Address */
u32 endpointlistaddr; /* Endpoint List Address Register */
u8 res5[4];
u32 burstsize; /* Master Interface Data Burst Size Register */
u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */
u8 res6[24];
u32 configflag; /* Configure Flag Register */
u32 portsc1; /* Port 1 Status and Control Register */
u8 res7[28];
u32 otgsc; /* On-The-Go Status and Control */
u32 usbmode; /* USB Mode Register */
u32 endptsetupstat; /* Endpoint Setup Status Register */
u32 endpointprime; /* Endpoint Initialization Register */
u32 endptflush; /* Endpoint Flush Register */
u32 endptstatus; /* Endpoint Status Register */
u32 endptcomplete; /* Endpoint Complete Register */
u32 endptctrl[6]; /* Endpoint Control Registers */
};
/* USB DR host mode registers (Little Endian) */
struct usb_dr_host {
/* Capability register */
u8 res1[256];
u16 caplength; /* Capability Register Length */
u16 hciversion; /* Host Controller Interface Version */
u32 hcsparams; /* Host Controller Structural Parameters */
u32 hccparams; /* Host Controller Capability Parameters */
u8 res2[20];
u32 dciversion; /* Device Controller Interface Version */
u32 dccparams; /* Device Controller Capability Parameters */
u8 res3[24];
/* Operation register */
u32 usbcmd; /* USB Command Register */
u32 usbsts; /* USB Status Register */
u32 usbintr; /* USB Interrupt Enable Register */
u32 frindex; /* Frame Index Register */
u8 res4[4];
u32 periodiclistbase; /* Periodic Frame List Base Address Register */
u32 asynclistaddr; /* Current Asynchronous List Address Register */
u8 res5[4];
u32 burstsize; /* Master Interface Data Burst Size Register */
u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */
u8 res6[24];
u32 configflag; /* Configure Flag Register */
u32 portsc1; /* Port 1 Status and Control Register */
u8 res7[28];
u32 otgsc; /* On-The-Go Status and Control */
u32 usbmode; /* USB Mode Register */
u32 endptsetupstat; /* Endpoint Setup Status Register */
u32 endpointprime; /* Endpoint Initialization Register */
u32 endptflush; /* Endpoint Flush Register */
u32 endptstatus; /* Endpoint Status Register */
u32 endptcomplete; /* Endpoint Complete Register */
u32 endptctrl[6]; /* Endpoint Control Registers */
};
/* non-EHCI USB system interface registers (Big Endian) */
struct usb_sys_interface {
u32 snoop1;
u32 snoop2;
u32 age_cnt_thresh; /* Age Count Threshold Register */
u32 pri_ctrl; /* Priority Control Register */
u32 si_ctrl; /* System Interface Control Register */
u8 res[236];
u32 control; /* General Purpose Control Register */
};
/* ep0 transfer state */
#define WAIT_FOR_SETUP 0
#define DATA_STATE_XMIT 1
#define DATA_STATE_NEED_ZLP 2
#define WAIT_FOR_OUT_STATUS 3
#define DATA_STATE_RECV 4
/* Device Controller Capability Parameter register */
#define DCCPARAMS_DC 0x00000080
#define DCCPARAMS_DEN_MASK 0x0000001f
/* Frame Index Register Bit Masks */
#define USB_FRINDEX_MASKS 0x3fff
/* USB CMD Register Bit Masks */
#define USB_CMD_RUN_STOP 0x00000001
#define USB_CMD_CTRL_RESET 0x00000002
#define USB_CMD_PERIODIC_SCHEDULE_EN 0x00000010
#define USB_CMD_ASYNC_SCHEDULE_EN 0x00000020
#define USB_CMD_INT_AA_DOORBELL 0x00000040
#define USB_CMD_ASP 0x00000300
#define USB_CMD_ASYNC_SCH_PARK_EN 0x00000800
#define USB_CMD_SUTW 0x00002000
#define USB_CMD_ATDTW 0x00004000
#define USB_CMD_ITC 0x00FF0000
/* bit 15,3,2 are frame list size */
#define USB_CMD_FRAME_SIZE_1024 0x00000000
#define USB_CMD_FRAME_SIZE_512 0x00000004
#define USB_CMD_FRAME_SIZE_256 0x00000008
#define USB_CMD_FRAME_SIZE_128 0x0000000C
#define USB_CMD_FRAME_SIZE_64 0x00008000
#define USB_CMD_FRAME_SIZE_32 0x00008004
#define USB_CMD_FRAME_SIZE_16 0x00008008
#define USB_CMD_FRAME_SIZE_8 0x0000800C
/* bit 9-8 are async schedule park mode count */
#define USB_CMD_ASP_00 0x00000000
#define USB_CMD_ASP_01 0x00000100
#define USB_CMD_ASP_10 0x00000200
#define USB_CMD_ASP_11 0x00000300
#define USB_CMD_ASP_BIT_POS 8
/* bit 23-16 are interrupt threshold control */
#define USB_CMD_ITC_NO_THRESHOLD 0x00000000
#define USB_CMD_ITC_1_MICRO_FRM 0x00010000
#define USB_CMD_ITC_2_MICRO_FRM 0x00020000
#define USB_CMD_ITC_4_MICRO_FRM 0x00040000
#define USB_CMD_ITC_8_MICRO_FRM 0x00080000
#define USB_CMD_ITC_16_MICRO_FRM 0x00100000
#define USB_CMD_ITC_32_MICRO_FRM 0x00200000
#define USB_CMD_ITC_64_MICRO_FRM 0x00400000
#define USB_CMD_ITC_BIT_POS 16
/* USB STS Register Bit Masks */
#define USB_STS_INT 0x00000001
#define USB_STS_ERR 0x00000002
#define USB_STS_PORT_CHANGE 0x00000004
#define USB_STS_FRM_LST_ROLL 0x00000008
#define USB_STS_SYS_ERR 0x00000010
#define USB_STS_IAA 0x00000020
#define USB_STS_RESET 0x00000040
#define USB_STS_SOF 0x00000080
#define USB_STS_SUSPEND 0x00000100
#define USB_STS_HC_HALTED 0x00001000
#define USB_STS_RCL 0x00002000
#define USB_STS_PERIODIC_SCHEDULE 0x00004000
#define USB_STS_ASYNC_SCHEDULE 0x00008000
/* USB INTR Register Bit Masks */
#define USB_INTR_INT_EN 0x00000001
#define USB_INTR_ERR_INT_EN 0x00000002
#define USB_INTR_PTC_DETECT_EN 0x00000004
#define USB_INTR_FRM_LST_ROLL_EN 0x00000008
#define USB_INTR_SYS_ERR_EN 0x00000010
#define USB_INTR_ASYN_ADV_EN 0x00000020
#define USB_INTR_RESET_EN 0x00000040
#define USB_INTR_SOF_EN 0x00000080
#define USB_INTR_DEVICE_SUSPEND 0x00000100
/* Device Address bit masks */
#define USB_DEVICE_ADDRESS_MASK 0xFE000000
#define USB_DEVICE_ADDRESS_BIT_POS 25
/* endpoint list address bit masks */
#define USB_EP_LIST_ADDRESS_MASK 0xfffff800
/* PORTSCX Register Bit Masks */
#define PORTSCX_CURRENT_CONNECT_STATUS 0x00000001
#define PORTSCX_CONNECT_STATUS_CHANGE 0x00000002
#define PORTSCX_PORT_ENABLE 0x00000004
#define PORTSCX_PORT_EN_DIS_CHANGE 0x00000008
#define PORTSCX_OVER_CURRENT_ACT 0x00000010
#define PORTSCX_OVER_CURRENT_CHG 0x00000020
#define PORTSCX_PORT_FORCE_RESUME 0x00000040
#define PORTSCX_PORT_SUSPEND 0x00000080
#define PORTSCX_PORT_RESET 0x00000100
#define PORTSCX_LINE_STATUS_BITS 0x00000C00
#define PORTSCX_PORT_POWER 0x00001000
#define PORTSCX_PORT_INDICTOR_CTRL 0x0000C000
#define PORTSCX_PORT_TEST_CTRL 0x000F0000
#define PORTSCX_WAKE_ON_CONNECT_EN 0x00100000
#define PORTSCX_WAKE_ON_CONNECT_DIS 0x00200000
#define PORTSCX_WAKE_ON_OVER_CURRENT 0x00400000
#define PORTSCX_PHY_LOW_POWER_SPD 0x00800000
#define PORTSCX_PORT_FORCE_FULL_SPEED 0x01000000
#define PORTSCX_PORT_SPEED_MASK 0x0C000000
#define PORTSCX_PORT_WIDTH 0x10000000
#define PORTSCX_PHY_TYPE_SEL 0xC0000000
/* bit 11-10 are line status */
#define PORTSCX_LINE_STATUS_SE0 0x00000000
#define PORTSCX_LINE_STATUS_JSTATE 0x00000400
#define PORTSCX_LINE_STATUS_KSTATE 0x00000800
#define PORTSCX_LINE_STATUS_UNDEF 0x00000C00
#define PORTSCX_LINE_STATUS_BIT_POS 10
/* bit 15-14 are port indicator control */
#define PORTSCX_PIC_OFF 0x00000000
#define PORTSCX_PIC_AMBER 0x00004000
#define PORTSCX_PIC_GREEN 0x00008000
#define PORTSCX_PIC_UNDEF 0x0000C000
#define PORTSCX_PIC_BIT_POS 14
/* bit 19-16 are port test control */
#define PORTSCX_PTC_DISABLE 0x00000000
#define PORTSCX_PTC_JSTATE 0x00010000
#define PORTSCX_PTC_KSTATE 0x00020000
#define PORTSCX_PTC_SEQNAK 0x00030000
#define PORTSCX_PTC_PACKET 0x00040000
#define PORTSCX_PTC_FORCE_EN 0x00050000
#define PORTSCX_PTC_BIT_POS 16
/* bit 27-26 are port speed */
#define PORTSCX_PORT_SPEED_FULL 0x00000000
#define PORTSCX_PORT_SPEED_LOW 0x04000000
#define PORTSCX_PORT_SPEED_HIGH 0x08000000
#define PORTSCX_PORT_SPEED_UNDEF 0x0C000000
#define PORTSCX_SPEED_BIT_POS 26
/* bit 28 is parallel transceiver width for UTMI interface */
#define PORTSCX_PTW 0x10000000
#define PORTSCX_PTW_8BIT 0x00000000
#define PORTSCX_PTW_16BIT 0x10000000
/* bit 31-30 are port transceiver select */
#define PORTSCX_PTS_UTMI 0x00000000
#define PORTSCX_PTS_ULPI 0x80000000
#define PORTSCX_PTS_FSLS 0xC0000000
#define PORTSCX_PTS_BIT_POS 30
/* otgsc Register Bit Masks */
#define OTGSC_CTRL_VUSB_DISCHARGE 0x00000001
#define OTGSC_CTRL_VUSB_CHARGE 0x00000002
#define OTGSC_CTRL_OTG_TERM 0x00000008
#define OTGSC_CTRL_DATA_PULSING 0x00000010
#define OTGSC_STS_USB_ID 0x00000100
#define OTGSC_STS_A_VBUS_VALID 0x00000200
#define OTGSC_STS_A_SESSION_VALID 0x00000400
#define OTGSC_STS_B_SESSION_VALID 0x00000800
#define OTGSC_STS_B_SESSION_END 0x00001000
#define OTGSC_STS_1MS_TOGGLE 0x00002000
#define OTGSC_STS_DATA_PULSING 0x00004000
#define OTGSC_INTSTS_USB_ID 0x00010000
#define OTGSC_INTSTS_A_VBUS_VALID 0x00020000
#define OTGSC_INTSTS_A_SESSION_VALID 0x00040000
#define OTGSC_INTSTS_B_SESSION_VALID 0x00080000
#define OTGSC_INTSTS_B_SESSION_END 0x00100000
#define OTGSC_INTSTS_1MS 0x00200000
#define OTGSC_INTSTS_DATA_PULSING 0x00400000
#define OTGSC_INTR_USB_ID 0x01000000
#define OTGSC_INTR_A_VBUS_VALID 0x02000000
#define OTGSC_INTR_A_SESSION_VALID 0x04000000
#define OTGSC_INTR_B_SESSION_VALID 0x08000000
#define OTGSC_INTR_B_SESSION_END 0x10000000
#define OTGSC_INTR_1MS_TIMER 0x20000000
#define OTGSC_INTR_DATA_PULSING 0x40000000
/* USB MODE Register Bit Masks */
#define USB_MODE_CTRL_MODE_IDLE 0x00000000
#define USB_MODE_CTRL_MODE_DEVICE 0x00000002
#define USB_MODE_CTRL_MODE_HOST 0x00000003
#define USB_MODE_CTRL_MODE_MASK 0x00000003
#define USB_MODE_CTRL_MODE_RSV 0x00000001
#define USB_MODE_ES 0x00000004 /* Endian Select */
#define USB_MODE_SETUP_LOCK_OFF 0x00000008
#define USB_MODE_STREAM_DISABLE 0x00000010
/* Endpoint Flush Register */
#define EPFLUSH_TX_OFFSET 0x00010000
#define EPFLUSH_RX_OFFSET 0x00000000
/* Endpoint Setup Status bit masks */
#define EP_SETUP_STATUS_MASK 0x0000003F
#define EP_SETUP_STATUS_EP0 0x00000001
/* ENDPOINTCTRLx Register Bit Masks */
#define EPCTRL_TX_ENABLE 0x00800000
#define EPCTRL_TX_DATA_TOGGLE_RST 0x00400000 /* Not EP0 */
#define EPCTRL_TX_DATA_TOGGLE_INH 0x00200000 /* Not EP0 */
#define EPCTRL_TX_TYPE 0x000C0000
#define EPCTRL_TX_DATA_SOURCE 0x00020000 /* Not EP0 */
#define EPCTRL_TX_EP_STALL 0x00010000
#define EPCTRL_RX_ENABLE 0x00000080
#define EPCTRL_RX_DATA_TOGGLE_RST 0x00000040 /* Not EP0 */
#define EPCTRL_RX_DATA_TOGGLE_INH 0x00000020 /* Not EP0 */
#define EPCTRL_RX_TYPE 0x0000000C
#define EPCTRL_RX_DATA_SINK 0x00000002 /* Not EP0 */
#define EPCTRL_RX_EP_STALL 0x00000001
/* bit 19-18 and 3-2 are endpoint type */
#define EPCTRL_EP_TYPE_CONTROL 0
#define EPCTRL_EP_TYPE_ISO 1
#define EPCTRL_EP_TYPE_BULK 2
#define EPCTRL_EP_TYPE_INTERRUPT 3
#define EPCTRL_TX_EP_TYPE_SHIFT 18
#define EPCTRL_RX_EP_TYPE_SHIFT 2
/* SNOOPn Register Bit Masks */
#define SNOOP_ADDRESS_MASK 0xFFFFF000
#define SNOOP_SIZE_ZERO 0x00 /* snooping disable */
#define SNOOP_SIZE_4KB 0x0B /* 4KB snoop size */
#define SNOOP_SIZE_8KB 0x0C
#define SNOOP_SIZE_16KB 0x0D
#define SNOOP_SIZE_32KB 0x0E
#define SNOOP_SIZE_64KB 0x0F
#define SNOOP_SIZE_128KB 0x10
#define SNOOP_SIZE_256KB 0x11
#define SNOOP_SIZE_512KB 0x12
#define SNOOP_SIZE_1MB 0x13
#define SNOOP_SIZE_2MB 0x14
#define SNOOP_SIZE_4MB 0x15
#define SNOOP_SIZE_8MB 0x16
#define SNOOP_SIZE_16MB 0x17
#define SNOOP_SIZE_32MB 0x18
#define SNOOP_SIZE_64MB 0x19
#define SNOOP_SIZE_128MB 0x1A
#define SNOOP_SIZE_256MB 0x1B
#define SNOOP_SIZE_512MB 0x1C
#define SNOOP_SIZE_1GB 0x1D
#define SNOOP_SIZE_2GB 0x1E /* 2GB snoop size */
/* pri_ctrl Register Bit Masks */
#define PRI_CTRL_PRI_LVL1 0x0000000C
#define PRI_CTRL_PRI_LVL0 0x00000003
/* si_ctrl Register Bit Masks */
#define SI_CTRL_ERR_DISABLE 0x00000010
#define SI_CTRL_IDRC_DISABLE 0x00000008
#define SI_CTRL_RD_SAFE_EN 0x00000004
#define SI_CTRL_RD_PREFETCH_DISABLE 0x00000002
#define SI_CTRL_RD_PREFEFETCH_VAL 0x00000001
/* control Register Bit Masks */
#define USB_CTRL_IOENB 0x00000004
#define USB_CTRL_ULPI_INT0EN 0x00000001
#define USB_CTRL_UTMI_PHY_EN 0x00000200
#define USB_CTRL_USB_EN 0x00000004
#define USB_CTRL_ULPI_PHY_CLK_SEL 0x00000400
/* Endpoint Queue Head data struct
* Rem: all the variables of qh are LittleEndian Mode
* and NEXT_POINTER_MASK should operate on a LittleEndian, Phy Addr
*/
struct ep_queue_head {
u32 max_pkt_length; /* Mult(31-30) , Zlt(29) , Max Pkt len
and IOS(15) */
u32 curr_dtd_ptr; /* Current dTD Pointer(31-5) */
u32 next_dtd_ptr; /* Next dTD Pointer(31-5), T(0) */
u32 size_ioc_int_sts; /* Total bytes (30-16), IOC (15),
MultO(11-10), STS (7-0) */
u32 buff_ptr0; /* Buffer pointer Page 0 (31-12) */
u32 buff_ptr1; /* Buffer pointer Page 1 (31-12) */
u32 buff_ptr2; /* Buffer pointer Page 2 (31-12) */
u32 buff_ptr3; /* Buffer pointer Page 3 (31-12) */
u32 buff_ptr4; /* Buffer pointer Page 4 (31-12) */
u32 res1;
u8 setup_buffer[8]; /* Setup data 8 bytes */
u32 res2[4];
};
/* Endpoint Queue Head Bit Masks */
#define EP_QUEUE_HEAD_MULT_POS 30
#define EP_QUEUE_HEAD_ZLT_SEL 0x20000000
#define EP_QUEUE_HEAD_MAX_PKT_LEN_POS 16
#define EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info) (((ep_info)>>16)&0x07ff)
#define EP_QUEUE_HEAD_IOS 0x00008000
#define EP_QUEUE_HEAD_NEXT_TERMINATE 0x00000001
#define EP_QUEUE_HEAD_IOC 0x00008000
#define EP_QUEUE_HEAD_MULTO 0x00000C00
#define EP_QUEUE_HEAD_STATUS_HALT 0x00000040
#define EP_QUEUE_HEAD_STATUS_ACTIVE 0x00000080
#define EP_QUEUE_CURRENT_OFFSET_MASK 0x00000FFF
#define EP_QUEUE_HEAD_NEXT_POINTER_MASK 0xFFFFFFE0
#define EP_QUEUE_FRINDEX_MASK 0x000007FF
#define EP_MAX_LENGTH_TRANSFER 0x4000
/* Endpoint Transfer Descriptor data struct */
/* Rem: all the variables of td are LittleEndian Mode */
struct ep_td_struct {
u32 next_td_ptr; /* Next TD pointer(31-5), T(0) set
indicate invalid */
u32 size_ioc_sts; /* Total bytes (30-16), IOC (15),
MultO(11-10), STS (7-0) */
u32 buff_ptr0; /* Buffer pointer Page 0 */
u32 buff_ptr1; /* Buffer pointer Page 1 */
u32 buff_ptr2; /* Buffer pointer Page 2 */
u32 buff_ptr3; /* Buffer pointer Page 3 */
u32 buff_ptr4; /* Buffer pointer Page 4 */
u32 res;
/* 32 bytes */
dma_addr_t td_dma; /* dma address for this td */
/* virtual address of next td specified in next_td_ptr */
struct ep_td_struct *next_td_virt;
};
/* Endpoint Transfer Descriptor bit Masks */
#define DTD_NEXT_TERMINATE 0x00000001
#define DTD_IOC 0x00008000
#define DTD_STATUS_ACTIVE 0x00000080
#define DTD_STATUS_HALTED 0x00000040
#define DTD_STATUS_DATA_BUFF_ERR 0x00000020
#define DTD_STATUS_TRANSACTION_ERR 0x00000008
#define DTD_RESERVED_FIELDS 0x80007300
#define DTD_ADDR_MASK 0xFFFFFFE0
#define DTD_PACKET_SIZE 0x7FFF0000
#define DTD_LENGTH_BIT_POS 16
#define DTD_ERROR_MASK (DTD_STATUS_HALTED | \
DTD_STATUS_DATA_BUFF_ERR | \
DTD_STATUS_TRANSACTION_ERR)
/* Alignment requirements; must be a power of two */
#define DTD_ALIGNMENT 0x20
#define QH_ALIGNMENT 2048
/* Controller dma boundary */
#define UDC_DMA_BOUNDARY 0x1000
/*-------------------------------------------------------------------------*/
/* ### driver private data
*/
struct fsl_req {
struct usb_request req;
struct list_head queue;
/* ep_queue() func will add
a request->queue into a udc_ep->queue 'd tail */
struct fsl_ep *ep;
unsigned mapped:1;
struct ep_td_struct *head, *tail; /* For dTD List
cpu endian Virtual addr */
unsigned int dtd_count;
};
#define REQ_UNCOMPLETE 1
struct fsl_ep {
struct usb_ep ep;
struct list_head queue;
struct fsl_udc *udc;
struct ep_queue_head *qh;
struct usb_gadget *gadget;
char name[14];
unsigned stopped:1;
};
#define EP_DIR_IN 1
#define EP_DIR_OUT 0
struct fsl_udc {
struct device *dev;
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
struct fsl_usb2_platform_data *pdata;
struct completion *done; /* to make sure release() is done */
struct fsl_ep *eps;
unsigned int max_ep;
unsigned int irq;
struct usb_ctrlrequest local_setup_buff;
spinlock_t lock;
struct usb_phy *transceiver;
unsigned softconnect:1;
unsigned vbus_active:1;
unsigned stopped:1;
unsigned remote_wakeup:1;
unsigned already_stopped:1;
unsigned big_endian_desc:1;
struct ep_queue_head *ep_qh; /* Endpoints Queue-Head */
struct fsl_req *status_req; /* ep0 status request */
struct dma_pool *td_pool; /* dma pool for DTD */
enum fsl_usb2_phy_modes phy_mode;
size_t ep_qh_size; /* size after alignment adjustment*/
dma_addr_t ep_qh_dma; /* dma address of QH */
u32 max_pipes; /* Device max pipes */
u32 bus_reset; /* Device is bus resetting */
u32 resume_state; /* USB state to resume */
u32 usb_state; /* USB current state */
u32 ep0_state; /* Endpoint zero state */
u32 ep0_dir; /* Endpoint zero direction: can be
USB_DIR_IN or USB_DIR_OUT */
u8 device_address; /* Device USB address */
};
/*-------------------------------------------------------------------------*/
/* ### Add board specific defines here
*/
/*
* ### pipe direction macro from device view
*/
#define USB_RECV 0 /* OUT EP */
#define USB_SEND 1 /* IN EP */
/*
* ### internal used help routines.
*/
#define ep_index(EP) ((EP)->ep.desc->bEndpointAddress&0xF)
#define ep_maxpacket(EP) ((EP)->ep.maxpacket)
#define ep_is_in(EP) ( (ep_index(EP) == 0) ? (EP->udc->ep0_dir == \
USB_DIR_IN) : ((EP)->ep.desc->bEndpointAddress \
& USB_DIR_IN)==USB_DIR_IN)
#define get_ep_by_pipe(udc, pipe) ((pipe == 1)? &udc->eps[0]: \
&udc->eps[pipe])
#define get_pipe_by_windex(windex) ((windex & USB_ENDPOINT_NUMBER_MASK) \
* 2 + ((windex & USB_DIR_IN) ? 1 : 0))
#define get_pipe_by_ep(EP) (ep_index(EP) * 2 + ep_is_in(EP))
static inline struct ep_queue_head *get_qh_by_ep(struct fsl_ep *ep)
{
/* we only have one ep0 structure but two queue heads */
if (ep_index(ep) != 0)
return ep->qh;
else
return &ep->udc->ep_qh[(ep->udc->ep0_dir ==
USB_DIR_IN) ? 1 : 0];
}
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,289 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Toshiba TC86C001 ("Goku-S") USB Device Controller driver
*
* Copyright (C) 2000-2002 Lineo
* by Stuart Lynne, Tom Rushworth, and Bruce Balden
* Copyright (C) 2002 Toshiba Corporation
* Copyright (C) 2003 MontaVista Software (source@mvista.com)
*/
/*
* PCI BAR 0 points to these registers.
*/
struct goku_udc_regs {
/* irq management */
u32 int_status; /* 0x000 */
u32 int_enable;
#define INT_SUSPEND 0x00001 /* or resume */
#define INT_USBRESET 0x00002
#define INT_ENDPOINT0 0x00004
#define INT_SETUP 0x00008
#define INT_STATUS 0x00010
#define INT_STATUSNAK 0x00020
#define INT_EPxDATASET(n) (0x00020 << (n)) /* 0 < n < 4 */
# define INT_EP1DATASET 0x00040
# define INT_EP2DATASET 0x00080
# define INT_EP3DATASET 0x00100
#define INT_EPnNAK(n) (0x00100 << (n)) /* 0 < n < 4 */
# define INT_EP1NAK 0x00200
# define INT_EP2NAK 0x00400
# define INT_EP3NAK 0x00800
#define INT_SOF 0x01000
#define INT_ERR 0x02000
#define INT_MSTWRSET 0x04000
#define INT_MSTWREND 0x08000
#define INT_MSTWRTMOUT 0x10000
#define INT_MSTRDEND 0x20000
#define INT_SYSERROR 0x40000
#define INT_PWRDETECT 0x80000
#define INT_DEVWIDE \
(INT_PWRDETECT|INT_SYSERROR/*|INT_ERR*/|INT_USBRESET|INT_SUSPEND)
#define INT_EP0 \
(INT_SETUP|INT_ENDPOINT0/*|INT_STATUS*/|INT_STATUSNAK)
u32 dma_master;
#define MST_EOPB_DIS 0x0800
#define MST_EOPB_ENA 0x0400
#define MST_TIMEOUT_DIS 0x0200
#define MST_TIMEOUT_ENA 0x0100
#define MST_RD_EOPB 0x0080 /* write-only */
#define MST_RD_RESET 0x0040
#define MST_WR_RESET 0x0020
#define MST_RD_ENA 0x0004 /* 1:start, 0:ignore */
#define MST_WR_ENA 0x0002 /* 1:start, 0:ignore */
#define MST_CONNECTION 0x0001 /* 0 for ep1out/ep2in */
#define MST_R_BITS (MST_EOPB_DIS|MST_EOPB_ENA \
|MST_RD_ENA|MST_RD_RESET)
#define MST_W_BITS (MST_TIMEOUT_DIS|MST_TIMEOUT_ENA \
|MST_WR_ENA|MST_WR_RESET)
#define MST_RW_BITS (MST_R_BITS|MST_W_BITS \
|MST_CONNECTION)
/* these values assume (dma_master & MST_CONNECTION) == 0 */
#define UDC_MSTWR_ENDPOINT 1
#define UDC_MSTRD_ENDPOINT 2
/* dma master write */
u32 out_dma_start;
u32 out_dma_end;
u32 out_dma_current;
/* dma master read */
u32 in_dma_start;
u32 in_dma_end;
u32 in_dma_current;
u32 power_detect;
#define PW_DETECT 0x04
#define PW_RESETB 0x02
#define PW_PULLUP 0x01
u8 _reserved0 [0x1d8];
/* endpoint registers */
u32 ep_fifo [4]; /* 0x200 */
u8 _reserved1 [0x10];
u32 ep_mode [4]; /* only 1-3 valid */
u8 _reserved2 [0x10];
u32 ep_status [4];
#define EPxSTATUS_TOGGLE 0x40
#define EPxSTATUS_SUSPEND 0x20
#define EPxSTATUS_EP_MASK (0x07<<2)
# define EPxSTATUS_EP_READY (0<<2)
# define EPxSTATUS_EP_DATAIN (1<<2)
# define EPxSTATUS_EP_FULL (2<<2)
# define EPxSTATUS_EP_TX_ERR (3<<2)
# define EPxSTATUS_EP_RX_ERR (4<<2)
# define EPxSTATUS_EP_BUSY (5<<2)
# define EPxSTATUS_EP_STALL (6<<2)
# define EPxSTATUS_EP_INVALID (7<<2)
#define EPxSTATUS_FIFO_DISABLE 0x02
#define EPxSTATUS_STAGE_ERROR 0x01
u8 _reserved3 [0x10];
u32 EPxSizeLA[4];
#define PACKET_ACTIVE (1<<7)
#define DATASIZE 0x7f
u8 _reserved3a [0x10];
u32 EPxSizeLB[4]; /* only 1,2 valid */
u8 _reserved3b [0x10];
u32 EPxSizeHA[4]; /* only 1-3 valid */
u8 _reserved3c [0x10];
u32 EPxSizeHB[4]; /* only 1,2 valid */
u8 _reserved4[0x30];
/* SETUP packet contents */
u32 bRequestType; /* 0x300 */
u32 bRequest;
u32 wValueL;
u32 wValueH;
u32 wIndexL;
u32 wIndexH;
u32 wLengthL;
u32 wLengthH;
/* command interaction/handshaking */
u32 SetupRecv; /* 0x320 */
u32 CurrConfig;
u32 StdRequest;
u32 Request;
u32 DataSet;
#define DATASET_A(epnum) (1<<(2*(epnum)))
#define DATASET_B(epnum) (2<<(2*(epnum)))
#define DATASET_AB(epnum) (3<<(2*(epnum)))
u8 _reserved5[4];
u32 UsbState;
#define USBSTATE_CONFIGURED 0x04
#define USBSTATE_ADDRESSED 0x02
#define USBSTATE_DEFAULT 0x01
u32 EOP;
u32 Command; /* 0x340 */
#define COMMAND_SETDATA0 2
#define COMMAND_RESET 3
#define COMMAND_STALL 4
#define COMMAND_INVALID 5
#define COMMAND_FIFO_DISABLE 7
#define COMMAND_FIFO_ENABLE 8
#define COMMAND_INIT_DESCRIPTOR 9
#define COMMAND_FIFO_CLEAR 10 /* also stall */
#define COMMAND_STALL_CLEAR 11
#define COMMAND_EP(n) ((n) << 4)
u32 EPxSingle;
u8 _reserved6[4];
u32 EPxBCS;
u8 _reserved7[8];
u32 IntControl;
#define ICONTROL_STATUSNAK 1
u8 _reserved8[4];
u32 reqmode; // 0x360 standard request mode, low 8 bits
#define G_REQMODE_SET_INTF (1<<7)
#define G_REQMODE_GET_INTF (1<<6)
#define G_REQMODE_SET_CONF (1<<5)
#define G_REQMODE_GET_CONF (1<<4)
#define G_REQMODE_GET_DESC (1<<3)
#define G_REQMODE_SET_FEAT (1<<2)
#define G_REQMODE_CLEAR_FEAT (1<<1)
#define G_REQMODE_GET_STATUS (1<<0)
u32 ReqMode;
u8 _reserved9[0x18];
u32 PortStatus; /* 0x380 */
u8 _reserved10[8];
u32 address;
u32 buff_test;
u8 _reserved11[4];
u32 UsbReady;
u8 _reserved12[4];
u32 SetDescStall; /* 0x3a0 */
u8 _reserved13[0x45c];
/* hardware could handle limited GET_DESCRIPTOR duties */
#define DESC_LEN 0x80
u32 descriptors[DESC_LEN]; /* 0x800 */
u8 _reserved14[0x600];
} __attribute__ ((packed));
#define MAX_FIFO_SIZE 64
#define MAX_EP0_SIZE 8 /* ep0 fifo is bigger, though */
/*-------------------------------------------------------------------------*/
/* DRIVER DATA STRUCTURES and UTILITIES */
struct goku_ep {
struct usb_ep ep;
struct goku_udc *dev;
unsigned long irqs;
unsigned num:8,
dma:1,
is_in:1,
stopped:1;
/* analogous to a host-side qh */
struct list_head queue;
u32 __iomem *reg_fifo;
u32 __iomem *reg_mode;
u32 __iomem *reg_status;
};
struct goku_request {
struct usb_request req;
struct list_head queue;
unsigned mapped:1;
};
enum ep0state {
EP0_DISCONNECT, /* no host */
EP0_IDLE, /* between STATUS ack and SETUP report */
EP0_IN, EP0_OUT, /* data stage */
EP0_STATUS, /* status stage */
EP0_STALL, /* data or status stages */
EP0_SUSPEND, /* usb suspend */
};
struct goku_udc {
/* each pci device provides one gadget, several endpoints */
struct usb_gadget gadget;
spinlock_t lock;
struct goku_ep ep[4];
struct usb_gadget_driver *driver;
enum ep0state ep0state;
unsigned got_irq:1,
got_region:1,
req_config:1,
configured:1,
enabled:1;
/* pci state used to access those endpoints */
struct pci_dev *pdev;
struct goku_udc_regs __iomem *regs;
u32 int_enable;
/* statistics... */
unsigned long irqs;
};
#define to_goku_udc(g) (container_of((g), struct goku_udc, gadget))
/*-------------------------------------------------------------------------*/
#define xprintk(dev,level,fmt,args...) \
printk(level "%s %s: " fmt , driver_name , \
pci_name(dev->pdev) , ## args)
#ifdef DEBUG
#define DBG(dev,fmt,args...) \
xprintk(dev , KERN_DEBUG , fmt , ## args)
#else
#define DBG(dev,fmt,args...) \
do { } while (0)
#endif /* DEBUG */
#ifdef VERBOSE
#define VDBG DBG
#else
#define VDBG(dev,fmt,args...) \
do { } while (0)
#endif /* VERBOSE */
#define ERROR(dev,fmt,args...) \
xprintk(dev , KERN_ERR , fmt , ## args)
#define WARNING(dev,fmt,args...) \
xprintk(dev , KERN_WARNING , fmt , ## args)
#define INFO(dev,fmt,args...) \
xprintk(dev , KERN_INFO , fmt , ## args)
File diff suppressed because it is too large Load Diff
+220
View File
@@ -0,0 +1,220 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* USB Peripheral Controller driver for Aeroflex Gaisler GRUSBDC.
*
* 2013 (c) Aeroflex Gaisler AB
*
* This driver supports GRUSBDC USB Device Controller cores available in the
* GRLIB VHDL IP core library.
*
* Full documentation of the GRUSBDC core can be found here:
* https://www.gaisler.com/products/grlib/grip.pdf
*
* Contributors:
* - Andreas Larsson <andreas@gaisler.com>
* - Marko Isomaki
*/
/* Control registers on the AMBA bus */
#define GR_MAXEP 16 /* Max # endpoints for *each* direction */
struct gr_epregs {
u32 epctrl;
union {
struct { /* Slave mode*/
u32 slvctrl;
u32 slvdata;
};
struct { /* DMA mode*/
u32 dmactrl;
u32 dmaaddr;
};
};
u32 epstat;
};
struct gr_regs {
struct gr_epregs epo[GR_MAXEP]; /* 0x000 - 0x0fc */
struct gr_epregs epi[GR_MAXEP]; /* 0x100 - 0x1fc */
u32 control; /* 0x200 */
u32 status; /* 0x204 */
};
#define GR_EPCTRL_BUFSZ_SCALER 8
#define GR_EPCTRL_BUFSZ_MASK 0xffe00000
#define GR_EPCTRL_BUFSZ_POS 21
#define GR_EPCTRL_PI BIT(20)
#define GR_EPCTRL_CB BIT(19)
#define GR_EPCTRL_CS BIT(18)
#define GR_EPCTRL_MAXPL_MASK 0x0003ff80
#define GR_EPCTRL_MAXPL_POS 7
#define GR_EPCTRL_NT_MASK 0x00000060
#define GR_EPCTRL_NT_POS 5
#define GR_EPCTRL_TT_MASK 0x00000018
#define GR_EPCTRL_TT_POS 3
#define GR_EPCTRL_EH BIT(2)
#define GR_EPCTRL_ED BIT(1)
#define GR_EPCTRL_EV BIT(0)
#define GR_DMACTRL_AE BIT(10)
#define GR_DMACTRL_AD BIT(3)
#define GR_DMACTRL_AI BIT(2)
#define GR_DMACTRL_IE BIT(1)
#define GR_DMACTRL_DA BIT(0)
#define GR_EPSTAT_PT BIT(29)
#define GR_EPSTAT_PR BIT(29)
#define GR_EPSTAT_B1CNT_MASK 0x1fff0000
#define GR_EPSTAT_B1CNT_POS 16
#define GR_EPSTAT_B0CNT_MASK 0x0000fff8
#define GR_EPSTAT_B0CNT_POS 3
#define GR_EPSTAT_B1 BIT(2)
#define GR_EPSTAT_B0 BIT(1)
#define GR_EPSTAT_BS BIT(0)
#define GR_CONTROL_SI BIT(31)
#define GR_CONTROL_UI BIT(30)
#define GR_CONTROL_VI BIT(29)
#define GR_CONTROL_SP BIT(28)
#define GR_CONTROL_FI BIT(27)
#define GR_CONTROL_EP BIT(14)
#define GR_CONTROL_DH BIT(13)
#define GR_CONTROL_RW BIT(12)
#define GR_CONTROL_TS_MASK 0x00000e00
#define GR_CONTROL_TS_POS 9
#define GR_CONTROL_TM BIT(8)
#define GR_CONTROL_UA_MASK 0x000000fe
#define GR_CONTROL_UA_POS 1
#define GR_CONTROL_SU BIT(0)
#define GR_STATUS_NEPI_MASK 0xf0000000
#define GR_STATUS_NEPI_POS 28
#define GR_STATUS_NEPO_MASK 0x0f000000
#define GR_STATUS_NEPO_POS 24
#define GR_STATUS_DM BIT(23)
#define GR_STATUS_SU BIT(17)
#define GR_STATUS_UR BIT(16)
#define GR_STATUS_VB BIT(15)
#define GR_STATUS_SP BIT(14)
#define GR_STATUS_AF_MASK 0x00003800
#define GR_STATUS_AF_POS 11
#define GR_STATUS_FN_MASK 0x000007ff
#define GR_STATUS_FN_POS 0
#define MAX_CTRL_PL_SIZE 64 /* As per USB standard for full and high speed */
/*-------------------------------------------------------------------------*/
/* Driver data structures and utilities */
struct gr_dma_desc {
u32 ctrl;
u32 data;
u32 next;
/* These must be last because hw uses the previous three */
u32 paddr;
struct gr_dma_desc *next_desc;
};
#define GR_DESC_OUT_CTRL_SE BIT(17)
#define GR_DESC_OUT_CTRL_IE BIT(15)
#define GR_DESC_OUT_CTRL_NX BIT(14)
#define GR_DESC_OUT_CTRL_EN BIT(13)
#define GR_DESC_OUT_CTRL_LEN_MASK 0x00001fff
#define GR_DESC_IN_CTRL_MO BIT(18)
#define GR_DESC_IN_CTRL_PI BIT(17)
#define GR_DESC_IN_CTRL_ML BIT(16)
#define GR_DESC_IN_CTRL_IE BIT(15)
#define GR_DESC_IN_CTRL_NX BIT(14)
#define GR_DESC_IN_CTRL_EN BIT(13)
#define GR_DESC_IN_CTRL_LEN_MASK 0x00001fff
#define GR_DESC_DMAADDR_MASK 0xfffffffc
struct gr_ep {
struct usb_ep ep;
struct gr_udc *dev;
u16 bytes_per_buffer;
unsigned int dma_start;
struct gr_epregs __iomem *regs;
unsigned num:8;
unsigned is_in:1;
unsigned stopped:1;
unsigned wedged:1;
unsigned callback:1;
/* analogous to a host-side qh */
struct list_head queue;
struct list_head ep_list;
/* Bounce buffer for end of "odd" sized OUT requests */
void *tailbuf;
dma_addr_t tailbuf_paddr;
};
struct gr_request {
struct usb_request req;
struct list_head queue;
/* Chain of dma descriptors */
struct gr_dma_desc *first_desc; /* First in the chain */
struct gr_dma_desc *curr_desc; /* Current descriptor */
struct gr_dma_desc *last_desc; /* Last in the chain */
u16 evenlen; /* Size of even length head (if oddlen != 0) */
u16 oddlen; /* Size of odd length tail if buffer length is "odd" */
u8 setup; /* Setup packet */
};
enum gr_ep0state {
GR_EP0_DISCONNECT = 0, /* No host */
GR_EP0_SETUP, /* Between STATUS ack and SETUP report */
GR_EP0_IDATA, /* IN data stage */
GR_EP0_ODATA, /* OUT data stage */
GR_EP0_ISTATUS, /* Status stage after IN data stage */
GR_EP0_OSTATUS, /* Status stage after OUT data stage */
GR_EP0_STALL, /* Data or status stages */
GR_EP0_SUSPEND, /* USB suspend */
};
struct gr_udc {
struct usb_gadget gadget;
struct gr_ep epi[GR_MAXEP];
struct gr_ep epo[GR_MAXEP];
struct usb_gadget_driver *driver;
struct dma_pool *desc_pool;
struct device *dev;
enum gr_ep0state ep0state;
struct gr_request *ep0reqo;
struct gr_request *ep0reqi;
struct gr_regs __iomem *regs;
int irq;
int irqi;
int irqo;
unsigned added:1;
unsigned irq_enabled:1;
unsigned remote_wakeup:1;
u8 test_mode;
enum usb_device_state suspended_from;
unsigned int nepi;
unsigned int nepo;
struct list_head ep_list;
spinlock_t lock; /* General lock, a.k.a. "dev->lock" in comments */
};
#define to_gr_udc(gadget) (container_of((gadget), struct gr_udc, gadget))
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,603 @@
// SPDX-License-Identifier: GPL-2.0
/*
* M66592 UDC (USB gadget)
*
* Copyright (C) 2006-2007 Renesas Solutions Corp.
*
* Author : Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
*/
#ifndef __M66592_UDC_H__
#define __M66592_UDC_H__
#include <linux/clk.h>
#include <linux/usb/m66592.h>
#define M66592_SYSCFG 0x00
#define M66592_XTAL 0xC000 /* b15-14: Crystal selection */
#define M66592_XTAL48 0x8000 /* 48MHz */
#define M66592_XTAL24 0x4000 /* 24MHz */
#define M66592_XTAL12 0x0000 /* 12MHz */
#define M66592_XCKE 0x2000 /* b13: External clock enable */
#define M66592_RCKE 0x1000 /* b12: Register clock enable */
#define M66592_PLLC 0x0800 /* b11: PLL control */
#define M66592_SCKE 0x0400 /* b10: USB clock enable */
#define M66592_ATCKM 0x0100 /* b8: Automatic clock supply */
#define M66592_HSE 0x0080 /* b7: Hi-speed enable */
#define M66592_DCFM 0x0040 /* b6: Controller function select */
#define M66592_DMRPD 0x0020 /* b5: D- pull down control */
#define M66592_DPRPU 0x0010 /* b4: D+ pull up control */
#define M66592_FSRPC 0x0004 /* b2: Full-speed receiver enable */
#define M66592_PCUT 0x0002 /* b1: Low power sleep enable */
#define M66592_USBE 0x0001 /* b0: USB module operation enable */
#define M66592_SYSSTS 0x02
#define M66592_LNST 0x0003 /* b1-0: D+, D- line status */
#define M66592_SE1 0x0003 /* SE1 */
#define M66592_KSTS 0x0002 /* K State */
#define M66592_JSTS 0x0001 /* J State */
#define M66592_SE0 0x0000 /* SE0 */
#define M66592_DVSTCTR 0x04
#define M66592_WKUP 0x0100 /* b8: Remote wakeup */
#define M66592_RWUPE 0x0080 /* b7: Remote wakeup sense */
#define M66592_USBRST 0x0040 /* b6: USB reset enable */
#define M66592_RESUME 0x0020 /* b5: Resume enable */
#define M66592_UACT 0x0010 /* b4: USB bus enable */
#define M66592_RHST 0x0003 /* b1-0: Reset handshake status */
#define M66592_HSMODE 0x0003 /* Hi-Speed mode */
#define M66592_FSMODE 0x0002 /* Full-Speed mode */
#define M66592_HSPROC 0x0001 /* HS handshake is processing */
#define M66592_TESTMODE 0x06
#define M66592_UTST 0x000F /* b4-0: Test select */
#define M66592_H_TST_PACKET 0x000C /* HOST TEST Packet */
#define M66592_H_TST_SE0_NAK 0x000B /* HOST TEST SE0 NAK */
#define M66592_H_TST_K 0x000A /* HOST TEST K */
#define M66592_H_TST_J 0x0009 /* HOST TEST J */
#define M66592_H_TST_NORMAL 0x0000 /* HOST Normal Mode */
#define M66592_P_TST_PACKET 0x0004 /* PERI TEST Packet */
#define M66592_P_TST_SE0_NAK 0x0003 /* PERI TEST SE0 NAK */
#define M66592_P_TST_K 0x0002 /* PERI TEST K */
#define M66592_P_TST_J 0x0001 /* PERI TEST J */
#define M66592_P_TST_NORMAL 0x0000 /* PERI Normal Mode */
/* built-in registers */
#define M66592_CFBCFG 0x0A
#define M66592_D0FBCFG 0x0C
#define M66592_LITTLE 0x0100 /* b8: Little endian mode */
/* external chip case */
#define M66592_PINCFG 0x0A
#define M66592_LDRV 0x8000 /* b15: Drive Current Adjust */
#define M66592_BIGEND 0x0100 /* b8: Big endian mode */
#define M66592_DMA0CFG 0x0C
#define M66592_DMA1CFG 0x0E
#define M66592_DREQA 0x4000 /* b14: Dreq active select */
#define M66592_BURST 0x2000 /* b13: Burst mode */
#define M66592_DACKA 0x0400 /* b10: Dack active select */
#define M66592_DFORM 0x0380 /* b9-7: DMA mode select */
#define M66592_CPU_ADR_RD_WR 0x0000 /* Address + RD/WR mode (CPU bus) */
#define M66592_CPU_DACK_RD_WR 0x0100 /* DACK + RD/WR mode (CPU bus) */
#define M66592_CPU_DACK_ONLY 0x0180 /* DACK only mode (CPU bus) */
#define M66592_SPLIT_DACK_ONLY 0x0200 /* DACK only mode (SPLIT bus) */
#define M66592_SPLIT_DACK_DSTB 0x0300 /* DACK + DSTB0 mode (SPLIT bus) */
#define M66592_DENDA 0x0040 /* b6: Dend active select */
#define M66592_PKTM 0x0020 /* b5: Packet mode */
#define M66592_DENDE 0x0010 /* b4: Dend enable */
#define M66592_OBUS 0x0004 /* b2: OUTbus mode */
/* common case */
#define M66592_CFIFO 0x10
#define M66592_D0FIFO 0x14
#define M66592_D1FIFO 0x18
#define M66592_CFIFOSEL 0x1E
#define M66592_D0FIFOSEL 0x24
#define M66592_D1FIFOSEL 0x2A
#define M66592_RCNT 0x8000 /* b15: Read count mode */
#define M66592_REW 0x4000 /* b14: Buffer rewind */
#define M66592_DCLRM 0x2000 /* b13: DMA buffer clear mode */
#define M66592_DREQE 0x1000 /* b12: DREQ output enable */
#define M66592_MBW_8 0x0000 /* 8bit */
#define M66592_MBW_16 0x0400 /* 16bit */
#define M66592_MBW_32 0x0800 /* 32bit */
#define M66592_TRENB 0x0200 /* b9: Transaction counter enable */
#define M66592_TRCLR 0x0100 /* b8: Transaction counter clear */
#define M66592_DEZPM 0x0080 /* b7: Zero-length packet mode */
#define M66592_ISEL 0x0020 /* b5: DCP FIFO port direction select */
#define M66592_CURPIPE 0x0007 /* b2-0: PIPE select */
#define M66592_CFIFOCTR 0x20
#define M66592_D0FIFOCTR 0x26
#define M66592_D1FIFOCTR 0x2c
#define M66592_BVAL 0x8000 /* b15: Buffer valid flag */
#define M66592_BCLR 0x4000 /* b14: Buffer clear */
#define M66592_FRDY 0x2000 /* b13: FIFO ready */
#define M66592_DTLN 0x0FFF /* b11-0: FIFO received data length */
#define M66592_CFIFOSIE 0x22
#define M66592_TGL 0x8000 /* b15: Buffer toggle */
#define M66592_SCLR 0x4000 /* b14: Buffer clear */
#define M66592_SBUSY 0x2000 /* b13: SIE_FIFO busy */
#define M66592_D0FIFOTRN 0x28
#define M66592_D1FIFOTRN 0x2E
#define M66592_TRNCNT 0xFFFF /* b15-0: Transaction counter */
#define M66592_INTENB0 0x30
#define M66592_VBSE 0x8000 /* b15: VBUS interrupt */
#define M66592_RSME 0x4000 /* b14: Resume interrupt */
#define M66592_SOFE 0x2000 /* b13: Frame update interrupt */
#define M66592_DVSE 0x1000 /* b12: Device state transition interrupt */
#define M66592_CTRE 0x0800 /* b11: Control transfer stage transition irq */
#define M66592_BEMPE 0x0400 /* b10: Buffer empty interrupt */
#define M66592_NRDYE 0x0200 /* b9: Buffer not ready interrupt */
#define M66592_BRDYE 0x0100 /* b8: Buffer ready interrupt */
#define M66592_URST 0x0080 /* b7: USB reset detected interrupt */
#define M66592_SADR 0x0040 /* b6: Set address executed interrupt */
#define M66592_SCFG 0x0020 /* b5: Set configuration executed interrupt */
#define M66592_SUSP 0x0010 /* b4: Suspend detected interrupt */
#define M66592_WDST 0x0008 /* b3: Control write data stage completed irq */
#define M66592_RDST 0x0004 /* b2: Control read data stage completed irq */
#define M66592_CMPL 0x0002 /* b1: Control transfer complete interrupt */
#define M66592_SERR 0x0001 /* b0: Sequence error interrupt */
#define M66592_INTENB1 0x32
#define M66592_BCHGE 0x4000 /* b14: USB us chenge interrupt */
#define M66592_DTCHE 0x1000 /* b12: Detach sense interrupt */
#define M66592_SIGNE 0x0020 /* b5: SETUP IGNORE interrupt */
#define M66592_SACKE 0x0010 /* b4: SETUP ACK interrupt */
#define M66592_BRDYM 0x0004 /* b2: BRDY clear timing */
#define M66592_INTL 0x0002 /* b1: Interrupt sense select */
#define M66592_PCSE 0x0001 /* b0: PCUT enable by CS assert */
#define M66592_BRDYENB 0x36
#define M66592_BRDYSTS 0x46
#define M66592_BRDY7 0x0080 /* b7: PIPE7 */
#define M66592_BRDY6 0x0040 /* b6: PIPE6 */
#define M66592_BRDY5 0x0020 /* b5: PIPE5 */
#define M66592_BRDY4 0x0010 /* b4: PIPE4 */
#define M66592_BRDY3 0x0008 /* b3: PIPE3 */
#define M66592_BRDY2 0x0004 /* b2: PIPE2 */
#define M66592_BRDY1 0x0002 /* b1: PIPE1 */
#define M66592_BRDY0 0x0001 /* b1: PIPE0 */
#define M66592_NRDYENB 0x38
#define M66592_NRDYSTS 0x48
#define M66592_NRDY7 0x0080 /* b7: PIPE7 */
#define M66592_NRDY6 0x0040 /* b6: PIPE6 */
#define M66592_NRDY5 0x0020 /* b5: PIPE5 */
#define M66592_NRDY4 0x0010 /* b4: PIPE4 */
#define M66592_NRDY3 0x0008 /* b3: PIPE3 */
#define M66592_NRDY2 0x0004 /* b2: PIPE2 */
#define M66592_NRDY1 0x0002 /* b1: PIPE1 */
#define M66592_NRDY0 0x0001 /* b1: PIPE0 */
#define M66592_BEMPENB 0x3A
#define M66592_BEMPSTS 0x4A
#define M66592_BEMP7 0x0080 /* b7: PIPE7 */
#define M66592_BEMP6 0x0040 /* b6: PIPE6 */
#define M66592_BEMP5 0x0020 /* b5: PIPE5 */
#define M66592_BEMP4 0x0010 /* b4: PIPE4 */
#define M66592_BEMP3 0x0008 /* b3: PIPE3 */
#define M66592_BEMP2 0x0004 /* b2: PIPE2 */
#define M66592_BEMP1 0x0002 /* b1: PIPE1 */
#define M66592_BEMP0 0x0001 /* b0: PIPE0 */
#define M66592_SOFCFG 0x3C
#define M66592_SOFM 0x000C /* b3-2: SOF palse mode */
#define M66592_SOF_125US 0x0008 /* SOF OUT 125us uFrame Signal */
#define M66592_SOF_1MS 0x0004 /* SOF OUT 1ms Frame Signal */
#define M66592_SOF_DISABLE 0x0000 /* SOF OUT Disable */
#define M66592_INTSTS0 0x40
#define M66592_VBINT 0x8000 /* b15: VBUS interrupt */
#define M66592_RESM 0x4000 /* b14: Resume interrupt */
#define M66592_SOFR 0x2000 /* b13: SOF frame update interrupt */
#define M66592_DVST 0x1000 /* b12: Device state transition */
#define M66592_CTRT 0x0800 /* b11: Control stage transition */
#define M66592_BEMP 0x0400 /* b10: Buffer empty interrupt */
#define M66592_NRDY 0x0200 /* b9: Buffer not ready interrupt */
#define M66592_BRDY 0x0100 /* b8: Buffer ready interrupt */
#define M66592_VBSTS 0x0080 /* b7: VBUS input port */
#define M66592_DVSQ 0x0070 /* b6-4: Device state */
#define M66592_DS_SPD_CNFG 0x0070 /* Suspend Configured */
#define M66592_DS_SPD_ADDR 0x0060 /* Suspend Address */
#define M66592_DS_SPD_DFLT 0x0050 /* Suspend Default */
#define M66592_DS_SPD_POWR 0x0040 /* Suspend Powered */
#define M66592_DS_SUSP 0x0040 /* Suspend */
#define M66592_DS_CNFG 0x0030 /* Configured */
#define M66592_DS_ADDS 0x0020 /* Address */
#define M66592_DS_DFLT 0x0010 /* Default */
#define M66592_DS_POWR 0x0000 /* Powered */
#define M66592_DVSQS 0x0030 /* b5-4: Device state */
#define M66592_VALID 0x0008 /* b3: Setup packet detected flag */
#define M66592_CTSQ 0x0007 /* b2-0: Control transfer stage */
#define M66592_CS_SQER 0x0006 /* Sequence error */
#define M66592_CS_WRND 0x0005 /* Control write nodata status */
#define M66592_CS_WRSS 0x0004 /* Control write status stage */
#define M66592_CS_WRDS 0x0003 /* Control write data stage */
#define M66592_CS_RDSS 0x0002 /* Control read status stage */
#define M66592_CS_RDDS 0x0001 /* Control read data stage */
#define M66592_CS_IDST 0x0000 /* Idle or setup stage */
#define M66592_INTSTS1 0x42
#define M66592_BCHG 0x4000 /* b14: USB bus chenge interrupt */
#define M66592_DTCH 0x1000 /* b12: Detach sense interrupt */
#define M66592_SIGN 0x0020 /* b5: SETUP IGNORE interrupt */
#define M66592_SACK 0x0010 /* b4: SETUP ACK interrupt */
#define M66592_FRMNUM 0x4C
#define M66592_OVRN 0x8000 /* b15: Overrun error */
#define M66592_CRCE 0x4000 /* b14: Received data error */
#define M66592_SOFRM 0x0800 /* b11: SOF output mode */
#define M66592_FRNM 0x07FF /* b10-0: Frame number */
#define M66592_UFRMNUM 0x4E
#define M66592_UFRNM 0x0007 /* b2-0: Micro frame number */
#define M66592_RECOVER 0x50
#define M66592_STSRECOV 0x0700 /* Status recovery */
#define M66592_STSR_HI 0x0400 /* FULL(0) or HI(1) Speed */
#define M66592_STSR_DEFAULT 0x0100 /* Default state */
#define M66592_STSR_ADDRESS 0x0200 /* Address state */
#define M66592_STSR_CONFIG 0x0300 /* Configured state */
#define M66592_USBADDR 0x007F /* b6-0: USB address */
#define M66592_USBREQ 0x54
#define M66592_bRequest 0xFF00 /* b15-8: bRequest */
#define M66592_GET_STATUS 0x0000
#define M66592_CLEAR_FEATURE 0x0100
#define M66592_ReqRESERVED 0x0200
#define M66592_SET_FEATURE 0x0300
#define M66592_ReqRESERVED1 0x0400
#define M66592_SET_ADDRESS 0x0500
#define M66592_GET_DESCRIPTOR 0x0600
#define M66592_SET_DESCRIPTOR 0x0700
#define M66592_GET_CONFIGURATION 0x0800
#define M66592_SET_CONFIGURATION 0x0900
#define M66592_GET_INTERFACE 0x0A00
#define M66592_SET_INTERFACE 0x0B00
#define M66592_SYNCH_FRAME 0x0C00
#define M66592_bmRequestType 0x00FF /* b7-0: bmRequestType */
#define M66592_bmRequestTypeDir 0x0080 /* b7 : Data direction */
#define M66592_HOST_TO_DEVICE 0x0000
#define M66592_DEVICE_TO_HOST 0x0080
#define M66592_bmRequestTypeType 0x0060 /* b6-5: Type */
#define M66592_STANDARD 0x0000
#define M66592_CLASS 0x0020
#define M66592_VENDOR 0x0040
#define M66592_bmRequestTypeRecip 0x001F /* b4-0: Recipient */
#define M66592_DEVICE 0x0000
#define M66592_INTERFACE 0x0001
#define M66592_ENDPOINT 0x0002
#define M66592_USBVAL 0x56
#define M66592_wValue 0xFFFF /* b15-0: wValue */
/* Standard Feature Selector */
#define M66592_ENDPOINT_HALT 0x0000
#define M66592_DEVICE_REMOTE_WAKEUP 0x0001
#define M66592_TEST_MODE 0x0002
/* Descriptor Types */
#define M66592_DT_TYPE 0xFF00
#define M66592_GET_DT_TYPE(v) (((v) & DT_TYPE) >> 8)
#define M66592_DT_DEVICE 0x01
#define M66592_DT_CONFIGURATION 0x02
#define M66592_DT_STRING 0x03
#define M66592_DT_INTERFACE 0x04
#define M66592_DT_ENDPOINT 0x05
#define M66592_DT_DEVICE_QUALIFIER 0x06
#define M66592_DT_OTHER_SPEED_CONFIGURATION 0x07
#define M66592_DT_INTERFACE_POWER 0x08
#define M66592_DT_INDEX 0x00FF
#define M66592_CONF_NUM 0x00FF
#define M66592_ALT_SET 0x00FF
#define M66592_USBINDEX 0x58
#define M66592_wIndex 0xFFFF /* b15-0: wIndex */
#define M66592_TEST_SELECT 0xFF00 /* b15-b8: Test Mode */
#define M66592_TEST_J 0x0100 /* Test_J */
#define M66592_TEST_K 0x0200 /* Test_K */
#define M66592_TEST_SE0_NAK 0x0300 /* Test_SE0_NAK */
#define M66592_TEST_PACKET 0x0400 /* Test_Packet */
#define M66592_TEST_FORCE_ENABLE 0x0500 /* Test_Force_Enable */
#define M66592_TEST_STSelectors 0x0600 /* Standard test selectors */
#define M66592_TEST_Reserved 0x4000 /* Reserved */
#define M66592_TEST_VSTModes 0xC000 /* Vendor-specific tests */
#define M66592_EP_DIR 0x0080 /* b7: Endpoint Direction */
#define M66592_EP_DIR_IN 0x0080
#define M66592_EP_DIR_OUT 0x0000
#define M66592_USBLENG 0x5A
#define M66592_wLength 0xFFFF /* b15-0: wLength */
#define M66592_DCPCFG 0x5C
#define M66592_CNTMD 0x0100 /* b8: Continuous transfer mode */
#define M66592_DIR 0x0010 /* b4: Control transfer DIR select */
#define M66592_DCPMAXP 0x5E
#define M66592_DEVSEL 0xC000 /* b15-14: Device address select */
#define M66592_DEVICE_0 0x0000 /* Device address 0 */
#define M66592_DEVICE_1 0x4000 /* Device address 1 */
#define M66592_DEVICE_2 0x8000 /* Device address 2 */
#define M66592_DEVICE_3 0xC000 /* Device address 3 */
#define M66592_MAXP 0x007F /* b6-0: Maxpacket size of ep0 */
#define M66592_DCPCTR 0x60
#define M66592_BSTS 0x8000 /* b15: Buffer status */
#define M66592_SUREQ 0x4000 /* b14: Send USB request */
#define M66592_SQCLR 0x0100 /* b8: Sequence toggle bit clear */
#define M66592_SQSET 0x0080 /* b7: Sequence toggle bit set */
#define M66592_SQMON 0x0040 /* b6: Sequence toggle bit monitor */
#define M66592_CCPL 0x0004 /* b2: control transfer complete */
#define M66592_PID 0x0003 /* b1-0: Response PID */
#define M66592_PID_STALL 0x0002 /* STALL */
#define M66592_PID_BUF 0x0001 /* BUF */
#define M66592_PID_NAK 0x0000 /* NAK */
#define M66592_PIPESEL 0x64
#define M66592_PIPENM 0x0007 /* b2-0: Pipe select */
#define M66592_PIPE0 0x0000 /* PIPE 0 */
#define M66592_PIPE1 0x0001 /* PIPE 1 */
#define M66592_PIPE2 0x0002 /* PIPE 2 */
#define M66592_PIPE3 0x0003 /* PIPE 3 */
#define M66592_PIPE4 0x0004 /* PIPE 4 */
#define M66592_PIPE5 0x0005 /* PIPE 5 */
#define M66592_PIPE6 0x0006 /* PIPE 6 */
#define M66592_PIPE7 0x0007 /* PIPE 7 */
#define M66592_PIPECFG 0x66
#define M66592_TYP 0xC000 /* b15-14: Transfer type */
#define M66592_ISO 0xC000 /* Isochronous */
#define M66592_INT 0x8000 /* Interrupt */
#define M66592_BULK 0x4000 /* Bulk */
#define M66592_BFRE 0x0400 /* b10: Buffer ready interrupt mode */
#define M66592_DBLB 0x0200 /* b9: Double buffer mode select */
#define M66592_CNTMD 0x0100 /* b8: Continuous transfer mode */
#define M66592_SHTNAK 0x0080 /* b7: Transfer end NAK */
#define M66592_DIR 0x0010 /* b4: Transfer direction select */
#define M66592_DIR_H_OUT 0x0010 /* HOST OUT */
#define M66592_DIR_P_IN 0x0010 /* PERI IN */
#define M66592_DIR_H_IN 0x0000 /* HOST IN */
#define M66592_DIR_P_OUT 0x0000 /* PERI OUT */
#define M66592_EPNUM 0x000F /* b3-0: Eendpoint number select */
#define M66592_EP1 0x0001
#define M66592_EP2 0x0002
#define M66592_EP3 0x0003
#define M66592_EP4 0x0004
#define M66592_EP5 0x0005
#define M66592_EP6 0x0006
#define M66592_EP7 0x0007
#define M66592_EP8 0x0008
#define M66592_EP9 0x0009
#define M66592_EP10 0x000A
#define M66592_EP11 0x000B
#define M66592_EP12 0x000C
#define M66592_EP13 0x000D
#define M66592_EP14 0x000E
#define M66592_EP15 0x000F
#define M66592_PIPEBUF 0x68
#define M66592_BUFSIZE 0x7C00 /* b14-10: Pipe buffer size */
#define M66592_BUF_SIZE(x) ((((x) / 64) - 1) << 10)
#define M66592_BUFNMB 0x00FF /* b7-0: Pipe buffer number */
#define M66592_PIPEMAXP 0x6A
#define M66592_MXPS 0x07FF /* b10-0: Maxpacket size */
#define M66592_PIPEPERI 0x6C
#define M66592_IFIS 0x1000 /* b12: ISO in-buffer flush mode */
#define M66592_IITV 0x0007 /* b2-0: ISO interval */
#define M66592_PIPE1CTR 0x70
#define M66592_PIPE2CTR 0x72
#define M66592_PIPE3CTR 0x74
#define M66592_PIPE4CTR 0x76
#define M66592_PIPE5CTR 0x78
#define M66592_PIPE6CTR 0x7A
#define M66592_PIPE7CTR 0x7C
#define M66592_BSTS 0x8000 /* b15: Buffer status */
#define M66592_INBUFM 0x4000 /* b14: IN buffer monitor (PIPE 1-5) */
#define M66592_ACLRM 0x0200 /* b9: Out buffer auto clear mode */
#define M66592_SQCLR 0x0100 /* b8: Sequence toggle bit clear */
#define M66592_SQSET 0x0080 /* b7: Sequence toggle bit set */
#define M66592_SQMON 0x0040 /* b6: Sequence toggle bit monitor */
#define M66592_PID 0x0003 /* b1-0: Response PID */
#define M66592_INVALID_REG 0x7E
#define get_pipectr_addr(pipenum) (M66592_PIPE1CTR + (pipenum - 1) * 2)
#define M66592_MAX_SAMPLING 10
#define M66592_MAX_NUM_PIPE 8
#define M66592_MAX_NUM_BULK 3
#define M66592_MAX_NUM_ISOC 2
#define M66592_MAX_NUM_INT 2
#define M66592_BASE_PIPENUM_BULK 3
#define M66592_BASE_PIPENUM_ISOC 1
#define M66592_BASE_PIPENUM_INT 6
#define M66592_BASE_BUFNUM 6
#define M66592_MAX_BUFNUM 0x4F
struct m66592_pipe_info {
u16 pipe;
u16 epnum;
u16 maxpacket;
u16 type;
u16 interval;
u16 dir_in;
};
struct m66592_request {
struct usb_request req;
struct list_head queue;
};
struct m66592_ep {
struct usb_ep ep;
struct m66592 *m66592;
struct list_head queue;
unsigned busy:1;
unsigned internal_ccpl:1; /* use only control */
/* this member can able to after m66592_enable */
unsigned use_dma:1;
u16 pipenum;
u16 type;
/* register address */
unsigned long fifoaddr;
unsigned long fifosel;
unsigned long fifoctr;
unsigned long fifotrn;
unsigned long pipectr;
};
struct m66592 {
spinlock_t lock;
void __iomem *reg;
struct clk *clk;
struct m66592_platdata *pdata;
unsigned long irq_trigger;
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
struct m66592_ep ep[M66592_MAX_NUM_PIPE];
struct m66592_ep *pipenum2ep[M66592_MAX_NUM_PIPE];
struct m66592_ep *epaddr2ep[16];
struct usb_request *ep0_req; /* for internal request */
__le16 ep0_data; /* for internal request */
u16 old_vbus;
struct timer_list timer;
int scount;
int old_dvsq;
/* pipe config */
int bulk;
int interrupt;
int isochronous;
int num_dma;
};
#define to_m66592(g) (container_of((g), struct m66592, gadget))
#define gadget_to_m66592(_gadget) container_of(_gadget, struct m66592, gadget)
#define m66592_to_gadget(m66592) (&m66592->gadget)
#define is_bulk_pipe(pipenum) \
((pipenum >= M66592_BASE_PIPENUM_BULK) && \
(pipenum < (M66592_BASE_PIPENUM_BULK + M66592_MAX_NUM_BULK)))
#define is_interrupt_pipe(pipenum) \
((pipenum >= M66592_BASE_PIPENUM_INT) && \
(pipenum < (M66592_BASE_PIPENUM_INT + M66592_MAX_NUM_INT)))
#define is_isoc_pipe(pipenum) \
((pipenum >= M66592_BASE_PIPENUM_ISOC) && \
(pipenum < (M66592_BASE_PIPENUM_ISOC + M66592_MAX_NUM_ISOC)))
#define enable_irq_ready(m66592, pipenum) \
enable_pipe_irq(m66592, pipenum, M66592_BRDYENB)
#define disable_irq_ready(m66592, pipenum) \
disable_pipe_irq(m66592, pipenum, M66592_BRDYENB)
#define enable_irq_empty(m66592, pipenum) \
enable_pipe_irq(m66592, pipenum, M66592_BEMPENB)
#define disable_irq_empty(m66592, pipenum) \
disable_pipe_irq(m66592, pipenum, M66592_BEMPENB)
#define enable_irq_nrdy(m66592, pipenum) \
enable_pipe_irq(m66592, pipenum, M66592_NRDYENB)
#define disable_irq_nrdy(m66592, pipenum) \
disable_pipe_irq(m66592, pipenum, M66592_NRDYENB)
/*-------------------------------------------------------------------------*/
static inline u16 m66592_read(struct m66592 *m66592, unsigned long offset)
{
return ioread16(m66592->reg + offset);
}
static inline void m66592_read_fifo(struct m66592 *m66592,
unsigned long offset,
void *buf, unsigned long len)
{
void __iomem *fifoaddr = m66592->reg + offset;
if (m66592->pdata->on_chip) {
len = (len + 3) / 4;
ioread32_rep(fifoaddr, buf, len);
} else {
len = (len + 1) / 2;
ioread16_rep(fifoaddr, buf, len);
}
}
static inline void m66592_write(struct m66592 *m66592, u16 val,
unsigned long offset)
{
iowrite16(val, m66592->reg + offset);
}
static inline void m66592_mdfy(struct m66592 *m66592, u16 val, u16 pat,
unsigned long offset)
{
u16 tmp;
tmp = m66592_read(m66592, offset);
tmp = tmp & (~pat);
tmp = tmp | val;
m66592_write(m66592, tmp, offset);
}
#define m66592_bclr(m66592, val, offset) \
m66592_mdfy(m66592, 0, val, offset)
#define m66592_bset(m66592, val, offset) \
m66592_mdfy(m66592, val, 0, offset)
static inline void m66592_write_fifo(struct m66592 *m66592,
struct m66592_ep *ep,
void *buf, unsigned long len)
{
void __iomem *fifoaddr = m66592->reg + ep->fifoaddr;
if (m66592->pdata->on_chip) {
unsigned long count;
unsigned char *pb;
int i;
count = len / 4;
iowrite32_rep(fifoaddr, buf, count);
if (len & 0x00000003) {
pb = buf + count * 4;
for (i = 0; i < (len & 0x00000003); i++) {
if (m66592_read(m66592, M66592_CFBCFG)) /* le */
iowrite8(pb[i], fifoaddr + (3 - i));
else
iowrite8(pb[i], fifoaddr + i);
}
}
} else {
unsigned long odd = len & 0x0001;
len = len / 2;
iowrite16_rep(fifoaddr, buf, len);
if (odd) {
unsigned char *p = buf + len*2;
if (m66592->pdata->wr0_shorted_to_wr1)
m66592_bclr(m66592, M66592_MBW_16, ep->fifosel);
iowrite8(*p, fifoaddr);
if (m66592->pdata->wr0_shorted_to_wr1)
m66592_bset(m66592, M66592_MBW_16, ep->fifosel);
}
}
}
#endif /* ifndef __M66592_UDC_H__ */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,390 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* NetChip 2280 high/full speed USB device controller.
* Unlike many such controllers, this one talks PCI.
*/
/*
* Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com)
* Copyright (C) 2003 David Brownell
* Copyright (C) 2014 Ricardo Ribalda - Qtechnology/AS
*/
#include <linux/usb/net2280.h>
#include <linux/usb/usb338x.h>
/*-------------------------------------------------------------------------*/
#ifdef __KERNEL__
/* indexed registers [11.10] are accessed indirectly
* caller must own the device lock.
*/
static inline u32 get_idx_reg(struct net2280_regs __iomem *regs, u32 index)
{
writel(index, &regs->idxaddr);
/* NOTE: synchs device/cpu memory views */
return readl(&regs->idxdata);
}
static inline void
set_idx_reg(struct net2280_regs __iomem *regs, u32 index, u32 value)
{
writel(index, &regs->idxaddr);
writel(value, &regs->idxdata);
/* posted, may not be visible yet */
}
#endif /* __KERNEL__ */
#define PCI_VENDOR_ID_PLX_LEGACY 0x17cc
#define PLX_LEGACY BIT(0)
#define PLX_2280 BIT(1)
#define PLX_SUPERSPEED BIT(2)
#define PLX_PCIE BIT(3)
#define REG_DIAG 0x0
#define RETRY_COUNTER 16
#define FORCE_PCI_SERR 11
#define FORCE_PCI_INTERRUPT 10
#define FORCE_USB_INTERRUPT 9
#define FORCE_CPU_INTERRUPT 8
#define ILLEGAL_BYTE_ENABLES 5
#define FAST_TIMES 4
#define FORCE_RECEIVE_ERROR 2
#define FORCE_TRANSMIT_CRC_ERROR 0
#define REG_FRAME 0x02 /* from last sof */
#define REG_CHIPREV 0x03 /* in bcd */
#define REG_HS_NAK_RATE 0x0a /* NAK per N uframes */
#define CHIPREV_1 0x0100
#define CHIPREV_1A 0x0110
/* DEFECT 7374 */
#define DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS 200
#define DEFECT_7374_PROCESSOR_WAIT_TIME 10
/* ep0 max packet size */
#define EP0_SS_MAX_PACKET_SIZE 0x200
#define EP0_HS_MAX_PACKET_SIZE 0x40
#ifdef __KERNEL__
/*-------------------------------------------------------------------------*/
/* [8.3] for scatter/gather i/o
* use struct net2280_dma_regs bitfields
*/
struct net2280_dma {
__le32 dmacount;
__le32 dmaaddr; /* the buffer */
__le32 dmadesc; /* next dma descriptor */
__le32 _reserved;
} __aligned(16);
/*-------------------------------------------------------------------------*/
/* DRIVER DATA STRUCTURES and UTILITIES */
struct net2280_ep {
struct usb_ep ep;
struct net2280_ep_regs __iomem *cfg;
struct net2280_ep_regs __iomem *regs;
struct net2280_dma_regs __iomem *dma;
struct net2280_dma *dummy;
dma_addr_t td_dma; /* of dummy */
struct net2280 *dev;
unsigned long irqs;
/* analogous to a host-side qh */
struct list_head queue;
const struct usb_endpoint_descriptor *desc;
unsigned num : 8,
fifo_size : 12,
in_fifo_validate : 1,
out_overflow : 1,
stopped : 1,
wedged : 1,
is_in : 1,
is_iso : 1,
responded : 1;
};
static inline void allow_status(struct net2280_ep *ep)
{
/* ep0 only */
writel(BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) |
BIT(CLEAR_NAK_OUT_PACKETS) |
BIT(CLEAR_NAK_OUT_PACKETS_MODE),
&ep->regs->ep_rsp);
ep->stopped = 1;
}
static inline void allow_status_338x(struct net2280_ep *ep)
{
/*
* Control Status Phase Handshake was set by the chip when the setup
* packet arrived. While set, the chip automatically NAKs the host's
* Status Phase tokens.
*/
writel(BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE), &ep->regs->ep_rsp);
ep->stopped = 1;
/* TD 9.9 Halt Endpoint test. TD 9.22 set feature test. */
ep->responded = 0;
}
struct net2280_request {
struct usb_request req;
struct net2280_dma *td;
dma_addr_t td_dma;
struct list_head queue;
unsigned mapped : 1,
valid : 1;
};
struct net2280 {
/* each pci device provides one gadget, several endpoints */
struct usb_gadget gadget;
spinlock_t lock;
struct net2280_ep ep[9];
struct usb_gadget_driver *driver;
unsigned enabled : 1,
protocol_stall : 1,
softconnect : 1,
got_irq : 1,
region:1,
added:1,
u1_enable:1,
u2_enable:1,
ltm_enable:1,
wakeup_enable:1,
addressed_state:1,
async_callbacks:1,
bug7734_patched:1;
u16 chiprev;
int enhanced_mode;
int n_ep;
kernel_ulong_t quirks;
/* pci state used to access those endpoints */
struct pci_dev *pdev;
struct net2280_regs __iomem *regs;
struct net2280_usb_regs __iomem *usb;
struct usb338x_usb_ext_regs __iomem *usb_ext;
struct net2280_pci_regs __iomem *pci;
struct net2280_dma_regs __iomem *dma;
struct net2280_dep_regs __iomem *dep;
struct net2280_ep_regs __iomem *epregs;
struct usb338x_ll_regs __iomem *llregs;
struct usb338x_pl_regs __iomem *plregs;
struct dma_pool *requests;
/* statistics...*/
};
static inline void set_halt(struct net2280_ep *ep)
{
/* ep0 and bulk/intr endpoints */
writel(BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) |
/* set NAK_OUT for erratum 0114 */
((ep->dev->chiprev == CHIPREV_1) << SET_NAK_OUT_PACKETS) |
BIT(SET_ENDPOINT_HALT),
&ep->regs->ep_rsp);
}
static inline void clear_halt(struct net2280_ep *ep)
{
/* ep0 and bulk/intr endpoints */
writel(BIT(CLEAR_ENDPOINT_HALT) |
BIT(CLEAR_ENDPOINT_TOGGLE) |
/*
* unless the gadget driver left a short packet in the
* fifo, this reverses the erratum 0114 workaround.
*/
((ep->dev->chiprev == CHIPREV_1) << CLEAR_NAK_OUT_PACKETS),
&ep->regs->ep_rsp);
}
/*
* FSM value for Defect 7374 (U1U2 Test) is managed in
* chip's SCRATCH register:
*/
#define DEFECT7374_FSM_FIELD 28
/* Waiting for Control Read:
* - A transition to this state indicates a fresh USB connection,
* before the first Setup Packet. The connection speed is not
* known. Firmware is waiting for the first Control Read.
* - Starting state: This state can be thought of as the FSM's typical
* starting state.
* - Tip: Upon the first SS Control Read the FSM never
* returns to this state.
*/
#define DEFECT7374_FSM_WAITING_FOR_CONTROL_READ BIT(DEFECT7374_FSM_FIELD)
/* Non-SS Control Read:
* - A transition to this state indicates detection of the first HS
* or FS Control Read.
* - Tip: Upon the first SS Control Read the FSM never
* returns to this state.
*/
#define DEFECT7374_FSM_NON_SS_CONTROL_READ (2 << DEFECT7374_FSM_FIELD)
/* SS Control Read:
* - A transition to this state indicates detection of the
* first SS Control Read.
* - This state indicates workaround completion. Workarounds no longer
* need to be applied (as long as the chip remains powered up).
* - Tip: Once in this state the FSM state does not change (until
* the chip's power is lost and restored).
* - This can be thought of as the final state of the FSM;
* the FSM 'locks-up' in this state until the chip loses power.
*/
#define DEFECT7374_FSM_SS_CONTROL_READ (3 << DEFECT7374_FSM_FIELD)
#ifdef USE_RDK_LEDS
static inline void net2280_led_init(struct net2280 *dev)
{
/* LED3 (green) is on during USB activity. note erratum 0113. */
writel(BIT(GPIO3_LED_SELECT) |
BIT(GPIO3_OUTPUT_ENABLE) |
BIT(GPIO2_OUTPUT_ENABLE) |
BIT(GPIO1_OUTPUT_ENABLE) |
BIT(GPIO0_OUTPUT_ENABLE),
&dev->regs->gpioctl);
}
/* indicate speed with bi-color LED 0/1 */
static inline
void net2280_led_speed(struct net2280 *dev, enum usb_device_speed speed)
{
u32 val = readl(&dev->regs->gpioctl);
switch (speed) {
case USB_SPEED_SUPER: /* green + red */
val |= BIT(GPIO0_DATA) | BIT(GPIO1_DATA);
break;
case USB_SPEED_HIGH: /* green */
val &= ~BIT(GPIO0_DATA);
val |= BIT(GPIO1_DATA);
break;
case USB_SPEED_FULL: /* red */
val &= ~BIT(GPIO1_DATA);
val |= BIT(GPIO0_DATA);
break;
default: /* (off/black) */
val &= ~(BIT(GPIO1_DATA) | BIT(GPIO0_DATA));
break;
}
writel(val, &dev->regs->gpioctl);
}
/* indicate power with LED 2 */
static inline void net2280_led_active(struct net2280 *dev, int is_active)
{
u32 val = readl(&dev->regs->gpioctl);
/* FIXME this LED never seems to turn on.*/
if (is_active)
val |= GPIO2_DATA;
else
val &= ~GPIO2_DATA;
writel(val, &dev->regs->gpioctl);
}
static inline void net2280_led_shutdown(struct net2280 *dev)
{
/* turn off all four GPIO*_DATA bits */
writel(readl(&dev->regs->gpioctl) & ~0x0f,
&dev->regs->gpioctl);
}
#else
#define net2280_led_init(dev) do { } while (0)
#define net2280_led_speed(dev, speed) do { } while (0)
#define net2280_led_shutdown(dev) do { } while (0)
#endif
/*-------------------------------------------------------------------------*/
#define ep_dbg(ndev, fmt, args...) \
dev_dbg((&((ndev)->pdev->dev)), fmt, ##args)
#define ep_vdbg(ndev, fmt, args...) \
dev_vdbg((&((ndev)->pdev->dev)), fmt, ##args)
#define ep_info(ndev, fmt, args...) \
dev_info((&((ndev)->pdev->dev)), fmt, ##args)
#define ep_warn(ndev, fmt, args...) \
dev_warn((&((ndev)->pdev->dev)), fmt, ##args)
#define ep_err(ndev, fmt, args...) \
dev_err((&((ndev)->pdev->dev)), fmt, ##args)
/*-------------------------------------------------------------------------*/
static inline void set_fifo_bytecount(struct net2280_ep *ep, unsigned count)
{
if (ep->dev->pdev->vendor == 0x17cc)
writeb(count, 2 + (u8 __iomem *) &ep->regs->ep_cfg);
else{
u32 tmp = readl(&ep->cfg->ep_cfg) &
(~(0x07 << EP_FIFO_BYTE_COUNT));
writel(tmp | (count << EP_FIFO_BYTE_COUNT), &ep->cfg->ep_cfg);
}
}
static inline void start_out_naking(struct net2280_ep *ep)
{
/* NOTE: hardware races lurk here, and PING protocol issues */
writel(BIT(SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
/* synch with device */
readl(&ep->regs->ep_rsp);
}
static inline void stop_out_naking(struct net2280_ep *ep)
{
u32 tmp;
tmp = readl(&ep->regs->ep_stat);
if ((tmp & BIT(NAK_OUT_PACKETS)) != 0)
writel(BIT(CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
}
static inline void set_max_speed(struct net2280_ep *ep, u32 max)
{
u32 reg;
static const u32 ep_enhanced[9] = { 0x10, 0x60, 0x30, 0x80,
0x50, 0x20, 0x70, 0x40, 0x90 };
if (ep->dev->enhanced_mode) {
reg = ep_enhanced[ep->num];
switch (ep->dev->gadget.speed) {
case USB_SPEED_SUPER:
reg += 2;
break;
case USB_SPEED_FULL:
reg += 1;
break;
case USB_SPEED_HIGH:
default:
break;
}
} else {
reg = (ep->num + 1) * 0x10;
if (ep->dev->gadget.speed != USB_SPEED_HIGH)
reg += 1;
}
set_idx_reg(ep->dev->regs, reg, max);
}
#endif /* __KERNEL__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,207 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* omap_udc.h -- for omap 3.2 udc, with OTG support
*
* 2004 (C) Texas Instruments, Inc.
* 2004 (C) David Brownell
*/
/*
* USB device/endpoint management registers
*/
#define UDC_REV (UDC_BASE + 0x0) /* Revision */
#define UDC_EP_NUM (UDC_BASE + 0x4) /* Which endpoint */
# define UDC_SETUP_SEL (1 << 6)
# define UDC_EP_SEL (1 << 5)
# define UDC_EP_DIR (1 << 4)
/* low 4 bits for endpoint number */
#define UDC_DATA (UDC_BASE + 0x08) /* Endpoint FIFO */
#define UDC_CTRL (UDC_BASE + 0x0C) /* Endpoint control */
# define UDC_CLR_HALT (1 << 7)
# define UDC_SET_HALT (1 << 6)
# define UDC_CLRDATA_TOGGLE (1 << 3)
# define UDC_SET_FIFO_EN (1 << 2)
# define UDC_CLR_EP (1 << 1)
# define UDC_RESET_EP (1 << 0)
#define UDC_STAT_FLG (UDC_BASE + 0x10) /* Endpoint status */
# define UDC_NO_RXPACKET (1 << 15)
# define UDC_MISS_IN (1 << 14)
# define UDC_DATA_FLUSH (1 << 13)
# define UDC_ISO_ERR (1 << 12)
# define UDC_ISO_FIFO_EMPTY (1 << 9)
# define UDC_ISO_FIFO_FULL (1 << 8)
# define UDC_EP_HALTED (1 << 6)
# define UDC_STALL (1 << 5)
# define UDC_NAK (1 << 4)
# define UDC_ACK (1 << 3)
# define UDC_FIFO_EN (1 << 2)
# define UDC_NON_ISO_FIFO_EMPTY (1 << 1)
# define UDC_NON_ISO_FIFO_FULL (1 << 0)
#define UDC_RXFSTAT (UDC_BASE + 0x14) /* OUT bytecount */
#define UDC_SYSCON1 (UDC_BASE + 0x18) /* System config 1 */
# define UDC_CFG_LOCK (1 << 8)
# define UDC_DATA_ENDIAN (1 << 7)
# define UDC_DMA_ENDIAN (1 << 6)
# define UDC_NAK_EN (1 << 4)
# define UDC_AUTODECODE_DIS (1 << 3)
# define UDC_SELF_PWR (1 << 2)
# define UDC_SOFF_DIS (1 << 1)
# define UDC_PULLUP_EN (1 << 0)
#define UDC_SYSCON2 (UDC_BASE + 0x1C) /* System config 2 */
# define UDC_RMT_WKP (1 << 6)
# define UDC_STALL_CMD (1 << 5)
# define UDC_DEV_CFG (1 << 3)
# define UDC_CLR_CFG (1 << 2)
#define UDC_DEVSTAT (UDC_BASE + 0x20) /* Device status */
# define UDC_B_HNP_ENABLE (1 << 9)
# define UDC_A_HNP_SUPPORT (1 << 8)
# define UDC_A_ALT_HNP_SUPPORT (1 << 7)
# define UDC_R_WK_OK (1 << 6)
# define UDC_USB_RESET (1 << 5)
# define UDC_SUS (1 << 4)
# define UDC_CFG (1 << 3)
# define UDC_ADD (1 << 2)
# define UDC_DEF (1 << 1)
# define UDC_ATT (1 << 0)
#define UDC_SOF (UDC_BASE + 0x24) /* Start of frame */
# define UDC_FT_LOCK (1 << 12)
# define UDC_TS_OK (1 << 11)
# define UDC_TS 0x03ff
#define UDC_IRQ_EN (UDC_BASE + 0x28) /* Interrupt enable */
# define UDC_SOF_IE (1 << 7)
# define UDC_EPN_RX_IE (1 << 5)
# define UDC_EPN_TX_IE (1 << 4)
# define UDC_DS_CHG_IE (1 << 3)
# define UDC_EP0_IE (1 << 0)
#define UDC_DMA_IRQ_EN (UDC_BASE + 0x2C) /* DMA irq enable */
/* rx/tx dma channels numbered 1-3 not 0-2 */
# define UDC_TX_DONE_IE(n) (1 << (4 * (n) - 2))
# define UDC_RX_CNT_IE(n) (1 << (4 * (n) - 3))
# define UDC_RX_EOT_IE(n) (1 << (4 * (n) - 4))
#define UDC_IRQ_SRC (UDC_BASE + 0x30) /* Interrupt source */
# define UDC_TXN_DONE (1 << 10)
# define UDC_RXN_CNT (1 << 9)
# define UDC_RXN_EOT (1 << 8)
# define UDC_IRQ_SOF (1 << 7)
# define UDC_EPN_RX (1 << 5)
# define UDC_EPN_TX (1 << 4)
# define UDC_DS_CHG (1 << 3)
# define UDC_SETUP (1 << 2)
# define UDC_EP0_RX (1 << 1)
# define UDC_EP0_TX (1 << 0)
# define UDC_IRQ_SRC_MASK 0x7bf
#define UDC_EPN_STAT (UDC_BASE + 0x34) /* EP irq status */
#define UDC_DMAN_STAT (UDC_BASE + 0x38) /* DMA irq status */
# define UDC_DMA_RX_SB (1 << 12)
# define UDC_DMA_RX_SRC(x) (((x)>>8) & 0xf)
# define UDC_DMA_TX_SRC(x) (((x)>>0) & 0xf)
/* DMA configuration registers: up to three channels in each direction. */
#define UDC_RXDMA_CFG (UDC_BASE + 0x40) /* 3 eps for RX DMA */
# define UDC_DMA_REQ (1 << 12)
#define UDC_TXDMA_CFG (UDC_BASE + 0x44) /* 3 eps for TX DMA */
#define UDC_DATA_DMA (UDC_BASE + 0x48) /* rx/tx fifo addr */
/* rx/tx dma control, numbering channels 1-3 not 0-2 */
#define UDC_TXDMA(chan) (UDC_BASE + 0x50 - 4 + 4 * (chan))
# define UDC_TXN_EOT (1 << 15) /* bytes vs packets */
# define UDC_TXN_START (1 << 14) /* start transfer */
# define UDC_TXN_TSC 0x03ff /* units in xfer */
#define UDC_RXDMA(chan) (UDC_BASE + 0x60 - 4 + 4 * (chan))
# define UDC_RXN_STOP (1 << 15) /* enable EOT irq */
# define UDC_RXN_TC 0x00ff /* packets in xfer */
/*
* Endpoint configuration registers (used before CFG_LOCK is set)
* UDC_EP_TX(0) is unused
*/
#define UDC_EP_RX(endpoint) (UDC_BASE + 0x80 + (endpoint)*4)
# define UDC_EPN_RX_VALID (1 << 15)
# define UDC_EPN_RX_DB (1 << 14)
/* buffer size in bits 13, 12 */
# define UDC_EPN_RX_ISO (1 << 11)
/* buffer pointer in low 11 bits */
#define UDC_EP_TX(endpoint) (UDC_BASE + 0xc0 + (endpoint)*4)
/* same bitfields as in RX */
/*-------------------------------------------------------------------------*/
struct omap_req {
struct usb_request req;
struct list_head queue;
unsigned dma_bytes;
unsigned mapped:1;
};
struct omap_ep {
struct usb_ep ep;
struct list_head queue;
unsigned long irqs;
struct list_head iso;
char name[14];
u16 maxpacket;
u8 bEndpointAddress;
u8 bmAttributes;
unsigned double_buf:1;
unsigned stopped:1;
unsigned fnf:1;
unsigned has_dma:1;
u8 ackwait;
u8 dma_channel;
u16 dma_counter;
int lch;
struct omap_udc *udc;
struct timer_list timer;
};
struct omap_udc {
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
spinlock_t lock;
struct omap_ep ep[32];
u16 devstat;
u16 clr_halt;
struct usb_phy *transceiver;
struct list_head iso;
unsigned softconnect:1;
unsigned vbus_active:1;
unsigned ep0_pending:1;
unsigned ep0_in:1;
unsigned ep0_set_config:1;
unsigned ep0_reset_config:1;
unsigned ep0_setup:1;
struct completion *done;
struct clk *dc_clk;
struct clk *hhc_clk;
unsigned clk_requested:1;
};
/*-------------------------------------------------------------------------*/
#ifdef VERBOSE
# define VDBG DBG
#else
# define VDBG(stuff...) do{}while(0)
#endif
#define ERR(stuff...) pr_err("udc: " stuff)
#define WARNING(stuff...) pr_warn("udc: " stuff)
#define INFO(stuff...) pr_info("udc: " stuff)
#define DBG(stuff...) pr_debug("udc: " stuff)
/*-------------------------------------------------------------------------*/
/* MOD_CONF_CTRL_0 */
#define VBUS_W2FC_1510 (1 << 17) /* 0 gpio0, 1 dvdd2 pin */
/* FUNC_MUX_CTRL_0 */
#define VBUS_CTRL_1510 (1 << 19) /* 1 connected (software) */
#define VBUS_MODE_1510 (1 << 18) /* 0 hardware, 1 software */
#define HMC_1510 ((omap_readl(MOD_CONF_CTRL_0) >> 1) & 0x3f)
#define HMC_1610 (omap_readl(OTG_SYSCON_2) & 0x3f)
#define HMC (cpu_is_omap15xx() ? HMC_1510 : HMC_1610)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,243 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Intel PXA25x on-chip full speed USB device controller
*
* Copyright (C) 2003 Robert Schwebel <r.schwebel@pengutronix.de>, Pengutronix
* Copyright (C) 2003 David Brownell
*/
#ifndef __LINUX_USB_GADGET_PXA25X_H
#define __LINUX_USB_GADGET_PXA25X_H
#include <linux/types.h>
/*-------------------------------------------------------------------------*/
/* pxa25x has this (move to include/asm-arm/arch-pxa/pxa-regs.h) */
#define UFNRH_SIR (1 << 7) /* SOF interrupt request */
#define UFNRH_SIM (1 << 6) /* SOF interrupt mask */
#define UFNRH_IPE14 (1 << 5) /* ISO packet error, ep14 */
#define UFNRH_IPE9 (1 << 4) /* ISO packet error, ep9 */
#define UFNRH_IPE4 (1 << 3) /* ISO packet error, ep4 */
/* pxa255 has this (move to include/asm-arm/arch-pxa/pxa-regs.h) */
#define UDCCFR UDC_RES2 /* UDC Control Function Register */
#define UDCCFR_AREN (1 << 7) /* ACK response enable (now) */
#define UDCCFR_ACM (1 << 2) /* ACK control mode (wait for AREN) */
/* latest pxa255 errata define new "must be one" bits in UDCCFR */
#define UDCCFR_MB1 (0xff & ~(UDCCFR_AREN|UDCCFR_ACM))
/*-------------------------------------------------------------------------*/
struct pxa25x_udc;
struct pxa25x_ep {
struct usb_ep ep;
struct pxa25x_udc *dev;
struct list_head queue;
unsigned long pio_irqs;
unsigned short fifo_size;
u8 bEndpointAddress;
u8 bmAttributes;
unsigned stopped : 1;
unsigned dma_fixup : 1;
/* UDCCS = UDC Control/Status for this EP
* UBCR = UDC Byte Count Remaining (contents of OUT fifo)
* UDDR = UDC Endpoint Data Register (the fifo)
* DRCM = DMA Request Channel Map
*/
u32 regoff_udccs;
u32 regoff_ubcr;
u32 regoff_uddr;
};
struct pxa25x_request {
struct usb_request req;
struct list_head queue;
};
enum ep0_state {
EP0_IDLE,
EP0_IN_DATA_PHASE,
EP0_OUT_DATA_PHASE,
EP0_END_XFER,
EP0_STALL,
};
#define EP0_FIFO_SIZE ((unsigned)16)
#define BULK_FIFO_SIZE ((unsigned)64)
#define ISO_FIFO_SIZE ((unsigned)256)
#define INT_FIFO_SIZE ((unsigned)8)
struct udc_stats {
struct ep0stats {
unsigned long ops;
unsigned long bytes;
} read, write;
unsigned long irqs;
};
#ifdef CONFIG_USB_PXA25X_SMALL
/* when memory's tight, SMALL config saves code+data. */
#define PXA_UDC_NUM_ENDPOINTS 3
#endif
#ifndef PXA_UDC_NUM_ENDPOINTS
#define PXA_UDC_NUM_ENDPOINTS 16
#endif
struct pxa25x_udc {
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
enum ep0_state ep0state;
struct udc_stats stats;
unsigned got_irq : 1,
vbus : 1,
pullup : 1,
has_cfr : 1,
req_pending : 1,
req_std : 1,
req_config : 1,
suspended : 1,
active : 1;
#define start_watchdog(dev) mod_timer(&dev->timer, jiffies + (HZ/200))
struct timer_list timer;
struct device *dev;
struct clk *clk;
struct gpio_desc *pullup_gpio;
struct usb_phy *transceiver;
u64 dma_mask;
struct pxa25x_ep ep [PXA_UDC_NUM_ENDPOINTS];
void __iomem *regs;
int usb_irq;
int usb_disc_irq;
};
#define to_pxa25x(g) (container_of((g), struct pxa25x_udc, gadget))
/*-------------------------------------------------------------------------*/
static struct pxa25x_udc *the_controller;
/*-------------------------------------------------------------------------*/
/*
* Debugging support vanishes in non-debug builds. DBG_NORMAL should be
* mostly silent during normal use/testing, with no timing side-effects.
*/
#define DBG_NORMAL 1 /* error paths, device state transitions */
#define DBG_VERBOSE 2 /* add some success path trace info */
#define DBG_NOISY 3 /* ... even more: request level */
#define DBG_VERY_NOISY 4 /* ... even more: packet level */
#define DMSG(stuff...) pr_debug("udc: " stuff)
#ifdef DEBUG
static const char *state_name[] = {
"EP0_IDLE",
"EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE",
"EP0_END_XFER", "EP0_STALL"
};
#ifdef VERBOSE_DEBUG
# define UDC_DEBUG DBG_VERBOSE
#else
# define UDC_DEBUG DBG_NORMAL
#endif
static void __maybe_unused
dump_udccr(const char *label)
{
u32 udccr = UDCCR;
DMSG("%s %02X =%s%s%s%s%s%s%s%s\n",
label, udccr,
(udccr & UDCCR_REM) ? " rem" : "",
(udccr & UDCCR_RSTIR) ? " rstir" : "",
(udccr & UDCCR_SRM) ? " srm" : "",
(udccr & UDCCR_SUSIR) ? " susir" : "",
(udccr & UDCCR_RESIR) ? " resir" : "",
(udccr & UDCCR_RSM) ? " rsm" : "",
(udccr & UDCCR_UDA) ? " uda" : "",
(udccr & UDCCR_UDE) ? " ude" : "");
}
static void __maybe_unused
dump_udccs0(const char *label)
{
u32 udccs0 = UDCCS0;
DMSG("%s %s %02X =%s%s%s%s%s%s%s%s\n",
label, state_name[the_controller->ep0state], udccs0,
(udccs0 & UDCCS0_SA) ? " sa" : "",
(udccs0 & UDCCS0_RNE) ? " rne" : "",
(udccs0 & UDCCS0_FST) ? " fst" : "",
(udccs0 & UDCCS0_SST) ? " sst" : "",
(udccs0 & UDCCS0_DRWF) ? " dwrf" : "",
(udccs0 & UDCCS0_FTF) ? " ftf" : "",
(udccs0 & UDCCS0_IPR) ? " ipr" : "",
(udccs0 & UDCCS0_OPR) ? " opr" : "");
}
static inline u32 udc_ep_get_UDCCS(struct pxa25x_ep *);
static void __maybe_unused
dump_state(struct pxa25x_udc *dev)
{
u32 tmp;
unsigned i;
DMSG("%s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n",
state_name[dev->ep0state],
UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL);
dump_udccr("udccr");
if (dev->has_cfr) {
tmp = UDCCFR;
DMSG("udccfr %02X =%s%s\n", tmp,
(tmp & UDCCFR_AREN) ? " aren" : "",
(tmp & UDCCFR_ACM) ? " acm" : "");
}
if (!dev->driver) {
DMSG("no gadget driver bound\n");
return;
} else
DMSG("ep0 driver '%s'\n", dev->driver->driver.name);
dump_udccs0 ("udccs0");
DMSG("ep0 IN %lu/%lu, OUT %lu/%lu\n",
dev->stats.write.bytes, dev->stats.write.ops,
dev->stats.read.bytes, dev->stats.read.ops);
for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) {
if (dev->ep[i].ep.desc == NULL)
continue;
DMSG ("udccs%d = %02x\n", i, udc_ep_get_UDCCS(&dev->ep[i]));
}
}
#else
#define dump_udccr(x) do{}while(0)
#define dump_udccs0(x) do{}while(0)
#define dump_state(x) do{}while(0)
#define UDC_DEBUG ((unsigned)0)
#endif
#define DBG(lvl, stuff...) do{if ((lvl) <= UDC_DEBUG) DMSG(stuff);}while(0)
#define ERR(stuff...) pr_err("udc: " stuff)
#define WARNING(stuff...) pr_warn("udc: " stuff)
#define INFO(stuff...) pr_info("udc: " stuff)
#endif /* __LINUX_USB_GADGET_PXA25X_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,496 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* linux/drivers/usb/gadget/pxa27x_udc.h
* Intel PXA27x on-chip full speed USB device controller
*
* Inspired by original driver by Frank Becker, David Brownell, and others.
* Copyright (C) 2008 Robert Jarzmik
*/
#ifndef __LINUX_USB_GADGET_PXA27X_H
#define __LINUX_USB_GADGET_PXA27X_H
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/usb/otg.h>
/*
* Register definitions
*/
/* Offsets */
#define UDCCR 0x0000 /* UDC Control Register */
#define UDCICR0 0x0004 /* UDC Interrupt Control Register0 */
#define UDCICR1 0x0008 /* UDC Interrupt Control Register1 */
#define UDCISR0 0x000C /* UDC Interrupt Status Register 0 */
#define UDCISR1 0x0010 /* UDC Interrupt Status Register 1 */
#define UDCFNR 0x0014 /* UDC Frame Number Register */
#define UDCOTGICR 0x0018 /* UDC On-The-Go interrupt control */
#define UP2OCR 0x0020 /* USB Port 2 Output Control register */
#define UP3OCR 0x0024 /* USB Port 3 Output Control register */
#define UDCCSRn(x) (0x0100 + ((x)<<2)) /* UDC Control/Status register */
#define UDCBCRn(x) (0x0200 + ((x)<<2)) /* UDC Byte Count Register */
#define UDCDRn(x) (0x0300 + ((x)<<2)) /* UDC Data Register */
#define UDCCRn(x) (0x0400 + ((x)<<2)) /* UDC Control Register */
#define UDCCR_OEN (1 << 31) /* On-the-Go Enable */
#define UDCCR_AALTHNP (1 << 30) /* A-device Alternate Host Negotiation
Protocol Port Support */
#define UDCCR_AHNP (1 << 29) /* A-device Host Negotiation Protocol
Support */
#define UDCCR_BHNP (1 << 28) /* B-device Host Negotiation Protocol
Enable */
#define UDCCR_DWRE (1 << 16) /* Device Remote Wake-up Enable */
#define UDCCR_ACN (0x03 << 11) /* Active UDC configuration Number */
#define UDCCR_ACN_S 11
#define UDCCR_AIN (0x07 << 8) /* Active UDC interface Number */
#define UDCCR_AIN_S 8
#define UDCCR_AAISN (0x07 << 5) /* Active UDC Alternate Interface
Setting Number */
#define UDCCR_AAISN_S 5
#define UDCCR_SMAC (1 << 4) /* Switch Endpoint Memory to Active
Configuration */
#define UDCCR_EMCE (1 << 3) /* Endpoint Memory Configuration
Error */
#define UDCCR_UDR (1 << 2) /* UDC Resume */
#define UDCCR_UDA (1 << 1) /* UDC Active */
#define UDCCR_UDE (1 << 0) /* UDC Enable */
#define UDCICR_INT(n, intr) (((intr) & 0x03) << (((n) & 0x0F) * 2))
#define UDCICR1_IECC (1 << 31) /* IntEn - Configuration Change */
#define UDCICR1_IESOF (1 << 30) /* IntEn - Start of Frame */
#define UDCICR1_IERU (1 << 29) /* IntEn - Resume */
#define UDCICR1_IESU (1 << 28) /* IntEn - Suspend */
#define UDCICR1_IERS (1 << 27) /* IntEn - Reset */
#define UDCICR_FIFOERR (1 << 1) /* FIFO Error interrupt for EP */
#define UDCICR_PKTCOMPL (1 << 0) /* Packet Complete interrupt for EP */
#define UDCICR_INT_MASK (UDCICR_FIFOERR | UDCICR_PKTCOMPL)
#define UDCISR_INT(n, intr) (((intr) & 0x03) << (((n) & 0x0F) * 2))
#define UDCISR1_IRCC (1 << 31) /* IntReq - Configuration Change */
#define UDCISR1_IRSOF (1 << 30) /* IntReq - Start of Frame */
#define UDCISR1_IRRU (1 << 29) /* IntReq - Resume */
#define UDCISR1_IRSU (1 << 28) /* IntReq - Suspend */
#define UDCISR1_IRRS (1 << 27) /* IntReq - Reset */
#define UDCISR_INT_MASK (UDCICR_FIFOERR | UDCICR_PKTCOMPL)
#define UDCOTGICR_IESF (1 << 24) /* OTG SET_FEATURE command recvd */
#define UDCOTGICR_IEXR (1 << 17) /* Extra Transceiver Interrupt
Rising Edge Interrupt Enable */
#define UDCOTGICR_IEXF (1 << 16) /* Extra Transceiver Interrupt
Falling Edge Interrupt Enable */
#define UDCOTGICR_IEVV40R (1 << 9) /* OTG Vbus Valid 4.0V Rising Edge
Interrupt Enable */
#define UDCOTGICR_IEVV40F (1 << 8) /* OTG Vbus Valid 4.0V Falling Edge
Interrupt Enable */
#define UDCOTGICR_IEVV44R (1 << 7) /* OTG Vbus Valid 4.4V Rising Edge
Interrupt Enable */
#define UDCOTGICR_IEVV44F (1 << 6) /* OTG Vbus Valid 4.4V Falling Edge
Interrupt Enable */
#define UDCOTGICR_IESVR (1 << 5) /* OTG Session Valid Rising Edge
Interrupt Enable */
#define UDCOTGICR_IESVF (1 << 4) /* OTG Session Valid Falling Edge
Interrupt Enable */
#define UDCOTGICR_IESDR (1 << 3) /* OTG A-Device SRP Detect Rising
Edge Interrupt Enable */
#define UDCOTGICR_IESDF (1 << 2) /* OTG A-Device SRP Detect Falling
Edge Interrupt Enable */
#define UDCOTGICR_IEIDR (1 << 1) /* OTG ID Change Rising Edge
Interrupt Enable */
#define UDCOTGICR_IEIDF (1 << 0) /* OTG ID Change Falling Edge
Interrupt Enable */
/* Host Port 2 field bits */
#define UP2OCR_CPVEN (1 << 0) /* Charge Pump Vbus Enable */
#define UP2OCR_CPVPE (1 << 1) /* Charge Pump Vbus Pulse Enable */
/* Transceiver enablers */
#define UP2OCR_DPPDE (1 << 2) /* D+ Pull Down Enable */
#define UP2OCR_DMPDE (1 << 3) /* D- Pull Down Enable */
#define UP2OCR_DPPUE (1 << 4) /* D+ Pull Up Enable */
#define UP2OCR_DMPUE (1 << 5) /* D- Pull Up Enable */
#define UP2OCR_DPPUBE (1 << 6) /* D+ Pull Up Bypass Enable */
#define UP2OCR_DMPUBE (1 << 7) /* D- Pull Up Bypass Enable */
#define UP2OCR_EXSP (1 << 8) /* External Transceiver Speed Control */
#define UP2OCR_EXSUS (1 << 9) /* External Transceiver Speed Enable */
#define UP2OCR_IDON (1 << 10) /* OTG ID Read Enable */
#define UP2OCR_HXS (1 << 16) /* Transceiver Output Select */
#define UP2OCR_HXOE (1 << 17) /* Transceiver Output Enable */
#define UP2OCR_SEOS (1 << 24) /* Single-Ended Output Select */
#define UDCCSR0_ACM (1 << 9) /* Ack Control Mode */
#define UDCCSR0_AREN (1 << 8) /* Ack Response Enable */
#define UDCCSR0_SA (1 << 7) /* Setup Active */
#define UDCCSR0_RNE (1 << 6) /* Receive FIFO Not Empty */
#define UDCCSR0_FST (1 << 5) /* Force Stall */
#define UDCCSR0_SST (1 << 4) /* Sent Stall */
#define UDCCSR0_DME (1 << 3) /* DMA Enable */
#define UDCCSR0_FTF (1 << 2) /* Flush Transmit FIFO */
#define UDCCSR0_IPR (1 << 1) /* IN Packet Ready */
#define UDCCSR0_OPC (1 << 0) /* OUT Packet Complete */
#define UDCCSR_DPE (1 << 9) /* Data Packet Error */
#define UDCCSR_FEF (1 << 8) /* Flush Endpoint FIFO */
#define UDCCSR_SP (1 << 7) /* Short Packet Control/Status */
#define UDCCSR_BNE (1 << 6) /* Buffer Not Empty (IN endpoints) */
#define UDCCSR_BNF (1 << 6) /* Buffer Not Full (OUT endpoints) */
#define UDCCSR_FST (1 << 5) /* Force STALL */
#define UDCCSR_SST (1 << 4) /* Sent STALL */
#define UDCCSR_DME (1 << 3) /* DMA Enable */
#define UDCCSR_TRN (1 << 2) /* Tx/Rx NAK */
#define UDCCSR_PC (1 << 1) /* Packet Complete */
#define UDCCSR_FS (1 << 0) /* FIFO needs service */
#define UDCCONR_CN (0x03 << 25) /* Configuration Number */
#define UDCCONR_CN_S 25
#define UDCCONR_IN (0x07 << 22) /* Interface Number */
#define UDCCONR_IN_S 22
#define UDCCONR_AISN (0x07 << 19) /* Alternate Interface Number */
#define UDCCONR_AISN_S 19
#define UDCCONR_EN (0x0f << 15) /* Endpoint Number */
#define UDCCONR_EN_S 15
#define UDCCONR_ET (0x03 << 13) /* Endpoint Type: */
#define UDCCONR_ET_S 13
#define UDCCONR_ET_INT (0x03 << 13) /* Interrupt */
#define UDCCONR_ET_BULK (0x02 << 13) /* Bulk */
#define UDCCONR_ET_ISO (0x01 << 13) /* Isochronous */
#define UDCCONR_ET_NU (0x00 << 13) /* Not used */
#define UDCCONR_ED (1 << 12) /* Endpoint Direction */
#define UDCCONR_MPS (0x3ff << 2) /* Maximum Packet Size */
#define UDCCONR_MPS_S 2
#define UDCCONR_DE (1 << 1) /* Double Buffering Enable */
#define UDCCONR_EE (1 << 0) /* Endpoint Enable */
#define UDCCR_MASK_BITS (UDCCR_OEN | UDCCR_SMAC | UDCCR_UDR | UDCCR_UDE)
#define UDCCSR_WR_MASK (UDCCSR_DME | UDCCSR_FST)
#define UDC_FNR_MASK (0x7ff)
#define UDC_BCR_MASK (0x3ff)
/*
* UDCCR = UDC Endpoint Configuration Registers
* UDCCSR = UDC Control/Status Register for this EP
* UDCBCR = UDC Byte Count Remaining (contents of OUT fifo)
* UDCDR = UDC Endpoint Data Register (the fifo)
*/
#define ofs_UDCCR(ep) (UDCCRn(ep->idx))
#define ofs_UDCCSR(ep) (UDCCSRn(ep->idx))
#define ofs_UDCBCR(ep) (UDCBCRn(ep->idx))
#define ofs_UDCDR(ep) (UDCDRn(ep->idx))
/* Register access macros */
#define udc_ep_readl(ep, reg) \
__raw_readl((ep)->dev->regs + ofs_##reg(ep))
#define udc_ep_writel(ep, reg, value) \
__raw_writel((value), ep->dev->regs + ofs_##reg(ep))
#define udc_ep_readb(ep, reg) \
__raw_readb((ep)->dev->regs + ofs_##reg(ep))
#define udc_ep_writeb(ep, reg, value) \
__raw_writeb((value), ep->dev->regs + ofs_##reg(ep))
#define udc_readl(dev, reg) \
__raw_readl((dev)->regs + (reg))
#define udc_writel(udc, reg, value) \
__raw_writel((value), (udc)->regs + (reg))
#define UDCCSR_MASK (UDCCSR_FST | UDCCSR_DME)
#define UDCCISR0_EP_MASK ~0
#define UDCCISR1_EP_MASK 0xffff
#define UDCCSR0_CTRL_REQ_MASK (UDCCSR0_OPC | UDCCSR0_SA | UDCCSR0_RNE)
#define EPIDX(ep) (ep->idx)
#define EPADDR(ep) (ep->addr)
#define EPXFERTYPE(ep) (ep->type)
#define EPNAME(ep) (ep->name)
#define is_ep0(ep) (!ep->idx)
#define EPXFERTYPE_is_ISO(ep) (EPXFERTYPE(ep) == USB_ENDPOINT_XFER_ISOC)
/*
* Endpoint definitions
*
* Once enabled, pxa endpoint configuration is freezed, and cannot change
* unless a reset happens or the udc is disabled.
* Therefore, we must define all pxa potential endpoint definitions needed for
* all gadget and set them up before the udc is enabled.
*
* As the architecture chosen is fully static, meaning the pxa endpoint
* configurations are set up once and for all, we must provide a way to match
* one usb endpoint (usb_ep) to several pxa endpoints. The reason is that gadget
* layer autoconf doesn't choose the usb_ep endpoint on (config, interface, alt)
* criteria, while the pxa architecture requires that.
*
* The solution is to define several pxa endpoints matching one usb_ep. Ex:
* - "ep1-in" matches pxa endpoint EPA (which is an IN ep at addr 1, when
* the udc talks on (config=3, interface=0, alt=0)
* - "ep1-in" matches pxa endpoint EPB (which is an IN ep at addr 1, when
* the udc talks on (config=3, interface=0, alt=1)
* - "ep1-in" matches pxa endpoint EPC (which is an IN ep at addr 1, when
* the udc talks on (config=2, interface=0, alt=0)
*
* We'll define the pxa endpoint by its index (EPA => idx=1, EPB => idx=2, ...)
*/
/*
* Endpoint definition helpers
*/
#define USB_EP_DEF(addr, bname, dir, type, maxpkt, ctype, cdir) \
{ .usb_ep = { .name = bname, .ops = &pxa_ep_ops, .maxpacket = maxpkt, \
.caps = USB_EP_CAPS(ctype, cdir), }, \
.desc = { .bEndpointAddress = addr | (dir ? USB_DIR_IN : 0), \
.bmAttributes = USB_ENDPOINT_XFER_ ## type, \
.wMaxPacketSize = maxpkt, }, \
.dev = &memory \
}
#define USB_EP_BULK(addr, bname, dir, cdir) \
USB_EP_DEF(addr, bname, dir, BULK, BULK_FIFO_SIZE, \
USB_EP_CAPS_TYPE_BULK, cdir)
#define USB_EP_ISO(addr, bname, dir, cdir) \
USB_EP_DEF(addr, bname, dir, ISOC, ISO_FIFO_SIZE, \
USB_EP_CAPS_TYPE_ISO, cdir)
#define USB_EP_INT(addr, bname, dir, cdir) \
USB_EP_DEF(addr, bname, dir, INT, INT_FIFO_SIZE, \
USB_EP_CAPS_TYPE_INT, cdir)
#define USB_EP_IN_BULK(n) USB_EP_BULK(n, "ep" #n "in-bulk", 1, \
USB_EP_CAPS_DIR_IN)
#define USB_EP_OUT_BULK(n) USB_EP_BULK(n, "ep" #n "out-bulk", 0, \
USB_EP_CAPS_DIR_OUT)
#define USB_EP_IN_ISO(n) USB_EP_ISO(n, "ep" #n "in-iso", 1, \
USB_EP_CAPS_DIR_IN)
#define USB_EP_OUT_ISO(n) USB_EP_ISO(n, "ep" #n "out-iso", 0, \
USB_EP_CAPS_DIR_OUT)
#define USB_EP_IN_INT(n) USB_EP_INT(n, "ep" #n "in-int", 1, \
USB_EP_CAPS_DIR_IN)
#define USB_EP_CTRL USB_EP_DEF(0, "ep0", 0, CONTROL, EP0_FIFO_SIZE, \
USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_ALL)
#define PXA_EP_DEF(_idx, _addr, dir, _type, maxpkt, _config, iface, altset) \
{ \
.dev = &memory, \
.name = "ep" #_idx, \
.idx = _idx, .enabled = 0, \
.dir_in = dir, .addr = _addr, \
.config = _config, .interface = iface, .alternate = altset, \
.type = _type, .fifo_size = maxpkt, \
}
#define PXA_EP_BULK(_idx, addr, dir, config, iface, alt) \
PXA_EP_DEF(_idx, addr, dir, USB_ENDPOINT_XFER_BULK, BULK_FIFO_SIZE, \
config, iface, alt)
#define PXA_EP_ISO(_idx, addr, dir, config, iface, alt) \
PXA_EP_DEF(_idx, addr, dir, USB_ENDPOINT_XFER_ISOC, ISO_FIFO_SIZE, \
config, iface, alt)
#define PXA_EP_INT(_idx, addr, dir, config, iface, alt) \
PXA_EP_DEF(_idx, addr, dir, USB_ENDPOINT_XFER_INT, INT_FIFO_SIZE, \
config, iface, alt)
#define PXA_EP_IN_BULK(i, adr, c, f, a) PXA_EP_BULK(i, adr, 1, c, f, a)
#define PXA_EP_OUT_BULK(i, adr, c, f, a) PXA_EP_BULK(i, adr, 0, c, f, a)
#define PXA_EP_IN_ISO(i, adr, c, f, a) PXA_EP_ISO(i, adr, 1, c, f, a)
#define PXA_EP_OUT_ISO(i, adr, c, f, a) PXA_EP_ISO(i, adr, 0, c, f, a)
#define PXA_EP_IN_INT(i, adr, c, f, a) PXA_EP_INT(i, adr, 1, c, f, a)
#define PXA_EP_CTRL PXA_EP_DEF(0, 0, 0, 0, EP0_FIFO_SIZE, 0, 0, 0)
struct pxa27x_udc;
struct stats {
unsigned long in_ops;
unsigned long out_ops;
unsigned long in_bytes;
unsigned long out_bytes;
unsigned long irqs;
};
/**
* struct udc_usb_ep - container of each usb_ep structure
* @usb_ep: usb endpoint
* @desc: usb descriptor, especially type and address
* @dev: udc managing this endpoint
* @pxa_ep: matching pxa_ep (cache of find_pxa_ep() call)
*/
struct udc_usb_ep {
struct usb_ep usb_ep;
struct usb_endpoint_descriptor desc;
struct pxa_udc *dev;
struct pxa_ep *pxa_ep;
};
/**
* struct pxa_ep - pxa endpoint
* @dev: udc device
* @queue: requests queue
* @lock: lock to pxa_ep data (queues and stats)
* @enabled: true when endpoint enabled (not stopped by gadget layer)
* @in_handle_ep: number of recursions of handle_ep() function
* Prevents deadlocks or infinite recursions of types :
* irq->handle_ep()->req_done()->req.complete()->pxa_ep_queue()->handle_ep()
* or
* pxa_ep_queue()->handle_ep()->req_done()->req.complete()->pxa_ep_queue()
* @idx: endpoint index (1 => epA, 2 => epB, ..., 24 => epX)
* @name: endpoint name (for trace/debug purpose)
* @dir_in: 1 if IN endpoint, 0 if OUT endpoint
* @addr: usb endpoint number
* @config: configuration in which this endpoint is active
* @interface: interface in which this endpoint is active
* @alternate: altsetting in which this endpoint is active
* @fifo_size: max packet size in the endpoint fifo
* @type: endpoint type (bulk, iso, int, ...)
* @udccsr_value: save register of UDCCSR0 for suspend/resume
* @udccr_value: save register of UDCCR for suspend/resume
* @stats: endpoint statistics
*
* The *PROBLEM* is that pxa's endpoint configuration scheme is both misdesigned
* (cares about config/interface/altsetting, thus placing needless limits on
* device capability) and full of implementation bugs forcing it to be set up
* for use more or less like a pxa255.
*
* As we define the pxa_ep statically, we must guess all needed pxa_ep for all
* gadget which may work with this udc driver.
*/
struct pxa_ep {
struct pxa_udc *dev;
struct list_head queue;
spinlock_t lock; /* Protects this structure */
/* (queues, stats) */
unsigned enabled:1;
unsigned in_handle_ep:1;
unsigned idx:5;
char *name;
/*
* Specific pxa endpoint data, needed for hardware initialization
*/
unsigned dir_in:1;
unsigned addr:4;
unsigned config:2;
unsigned interface:3;
unsigned alternate:3;
unsigned fifo_size;
unsigned type;
#ifdef CONFIG_PM
u32 udccsr_value;
u32 udccr_value;
#endif
struct stats stats;
};
/**
* struct pxa27x_request - container of each usb_request structure
* @req: usb request
* @udc_usb_ep: usb endpoint the request was submitted on
* @in_use: sanity check if request already queued on an pxa_ep
* @queue: linked list of requests, linked on pxa_ep->queue
*/
struct pxa27x_request {
struct usb_request req;
struct udc_usb_ep *udc_usb_ep;
unsigned in_use:1;
struct list_head queue;
};
enum ep0_state {
WAIT_FOR_SETUP,
SETUP_STAGE,
IN_DATA_STAGE,
OUT_DATA_STAGE,
IN_STATUS_STAGE,
OUT_STATUS_STAGE,
STALL,
WAIT_ACK_SET_CONF_INTERF
};
static char *ep0_state_name[] = {
"WAIT_FOR_SETUP", "SETUP_STAGE", "IN_DATA_STAGE", "OUT_DATA_STAGE",
"IN_STATUS_STAGE", "OUT_STATUS_STAGE", "STALL",
"WAIT_ACK_SET_CONF_INTERF"
};
#define EP0_STNAME(udc) ep0_state_name[(udc)->ep0state]
#define EP0_FIFO_SIZE 16U
#define BULK_FIFO_SIZE 64U
#define ISO_FIFO_SIZE 256U
#define INT_FIFO_SIZE 16U
struct udc_stats {
unsigned long irqs_reset;
unsigned long irqs_suspend;
unsigned long irqs_resume;
unsigned long irqs_reconfig;
};
#define NR_USB_ENDPOINTS (1 + 5) /* ep0 + ep1in-bulk + .. + ep3in-iso */
#define NR_PXA_ENDPOINTS (1 + 14) /* ep0 + epA + epB + .. + epX */
/**
* struct pxa_udc - udc structure
* @regs: mapped IO space
* @irq: udc irq
* @clk: udc clock
* @usb_gadget: udc gadget structure
* @driver: bound gadget (zero, g_ether, g_mass_storage, ...)
* @dev: device
* @gpiod: gpio descriptor of gpio for D+ pullup (or NULL if none)
* @transceiver: external transceiver to handle vbus sense and D+ pullup
* @ep0state: control endpoint state machine state
* @stats: statistics on udc usage
* @udc_usb_ep: array of usb endpoints offered by the gadget
* @pxa_ep: array of pxa available endpoints
* @enabled: UDC was enabled by a previous udc_enable()
* @pullup_on: if pullup resistor connected to D+ pin
* @pullup_resume: if pullup resistor should be connected to D+ pin on resume
* @config: UDC active configuration
* @last_interface: UDC interface of the last SET_INTERFACE host request
* @last_alternate: UDC altsetting of the last SET_INTERFACE host request
* @udccsr0: save of udccsr0 in case of suspend
* @debugfs_state: debugfs entry for "udcstate"
* @debugfs_queues: debugfs entry for "queues"
* @debugfs_eps: debugfs entry for "epstate"
*/
struct pxa_udc {
void __iomem *regs;
int irq;
struct clk *clk;
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
struct device *dev;
struct gpio_desc *gpiod;
struct usb_phy *transceiver;
enum ep0_state ep0state;
struct udc_stats stats;
struct udc_usb_ep udc_usb_ep[NR_USB_ENDPOINTS];
struct pxa_ep pxa_ep[NR_PXA_ENDPOINTS];
unsigned enabled:1;
unsigned pullup_on:1;
unsigned pullup_resume:1;
unsigned vbus_sensed:1;
unsigned config:2;
unsigned last_interface:3;
unsigned last_alternate:3;
#ifdef CONFIG_PM
unsigned udccsr0;
#endif
};
#define to_pxa(g) (container_of((g), struct pxa_udc, gadget))
static inline struct pxa_udc *to_gadget_udc(struct usb_gadget *gadget)
{
return container_of(gadget, struct pxa_udc, gadget);
}
/*
* Debugging/message support
*/
#define ep_dbg(ep, fmt, arg...) \
dev_dbg(ep->dev->dev, "%s:%s: " fmt, EPNAME(ep), __func__, ## arg)
#define ep_vdbg(ep, fmt, arg...) \
dev_vdbg(ep->dev->dev, "%s:%s: " fmt, EPNAME(ep), __func__, ## arg)
#define ep_err(ep, fmt, arg...) \
dev_err(ep->dev->dev, "%s:%s: " fmt, EPNAME(ep), __func__, ## arg)
#define ep_info(ep, fmt, arg...) \
dev_info(ep->dev->dev, "%s:%s: " fmt, EPNAME(ep), __func__, ## arg)
#define ep_warn(ep, fmt, arg...) \
dev_warn(ep->dev->dev, "%s:%s:" fmt, EPNAME(ep), __func__, ## arg)
#endif /* __LINUX_USB_GADGET_PXA27X_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,287 @@
// SPDX-License-Identifier: GPL-2.0
/*
* R8A66597 UDC
*
* Copyright (C) 2007-2009 Renesas Solutions Corp.
*
* Author : Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
*/
#ifndef __R8A66597_H__
#define __R8A66597_H__
#include <linux/clk.h>
#include <linux/usb/r8a66597.h>
#define R8A66597_MAX_SAMPLING 10
#define R8A66597_MAX_NUM_PIPE 8
#define R8A66597_MAX_NUM_BULK 3
#define R8A66597_MAX_NUM_ISOC 2
#define R8A66597_MAX_NUM_INT 2
#define R8A66597_BASE_PIPENUM_BULK 3
#define R8A66597_BASE_PIPENUM_ISOC 1
#define R8A66597_BASE_PIPENUM_INT 6
#define R8A66597_BASE_BUFNUM 6
#define R8A66597_MAX_BUFNUM 0x4F
#define is_bulk_pipe(pipenum) \
((pipenum >= R8A66597_BASE_PIPENUM_BULK) && \
(pipenum < (R8A66597_BASE_PIPENUM_BULK + R8A66597_MAX_NUM_BULK)))
#define is_interrupt_pipe(pipenum) \
((pipenum >= R8A66597_BASE_PIPENUM_INT) && \
(pipenum < (R8A66597_BASE_PIPENUM_INT + R8A66597_MAX_NUM_INT)))
#define is_isoc_pipe(pipenum) \
((pipenum >= R8A66597_BASE_PIPENUM_ISOC) && \
(pipenum < (R8A66597_BASE_PIPENUM_ISOC + R8A66597_MAX_NUM_ISOC)))
#define r8a66597_is_sudmac(r8a66597) (r8a66597->pdata->sudmac)
struct r8a66597_pipe_info {
u16 pipe;
u16 epnum;
u16 maxpacket;
u16 type;
u16 interval;
u16 dir_in;
};
struct r8a66597_request {
struct usb_request req;
struct list_head queue;
};
struct r8a66597_ep {
struct usb_ep ep;
struct r8a66597 *r8a66597;
struct r8a66597_dma *dma;
struct list_head queue;
unsigned busy:1;
unsigned wedge:1;
unsigned internal_ccpl:1; /* use only control */
/* this member can able to after r8a66597_enable */
unsigned use_dma:1;
u16 pipenum;
u16 type;
/* register address */
unsigned char fifoaddr;
unsigned char fifosel;
unsigned char fifoctr;
unsigned char pipectr;
unsigned char pipetre;
unsigned char pipetrn;
};
struct r8a66597_dma {
unsigned used:1;
unsigned dir:1; /* 1 = IN(write), 0 = OUT(read) */
};
struct r8a66597 {
spinlock_t lock;
void __iomem *reg;
void __iomem *sudmac_reg;
struct clk *clk;
struct r8a66597_platdata *pdata;
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
struct r8a66597_ep ep[R8A66597_MAX_NUM_PIPE];
struct r8a66597_ep *pipenum2ep[R8A66597_MAX_NUM_PIPE];
struct r8a66597_ep *epaddr2ep[16];
struct r8a66597_dma dma;
struct timer_list timer;
struct usb_request *ep0_req; /* for internal request */
u16 ep0_data; /* for internal request */
u16 old_vbus;
u16 scount;
u16 old_dvsq;
u16 device_status; /* for GET_STATUS */
/* pipe config */
unsigned char bulk;
unsigned char interrupt;
unsigned char isochronous;
unsigned char num_dma;
unsigned irq_sense_low:1;
};
#define gadget_to_r8a66597(_gadget) \
container_of(_gadget, struct r8a66597, gadget)
#define r8a66597_to_gadget(r8a66597) (&r8a66597->gadget)
#define r8a66597_to_dev(r8a66597) (r8a66597->gadget.dev.parent)
static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset)
{
return ioread16(r8a66597->reg + offset);
}
static inline void r8a66597_read_fifo(struct r8a66597 *r8a66597,
unsigned long offset,
unsigned char *buf,
int len)
{
void __iomem *fifoaddr = r8a66597->reg + offset;
unsigned int data = 0;
int i;
if (r8a66597->pdata->on_chip) {
/* 32-bit accesses for on_chip controllers */
/* aligned buf case */
if (len >= 4 && !((unsigned long)buf & 0x03)) {
ioread32_rep(fifoaddr, buf, len / 4);
buf += len & ~0x03;
len &= 0x03;
}
/* unaligned buf case */
for (i = 0; i < len; i++) {
if (!(i & 0x03))
data = ioread32(fifoaddr);
buf[i] = (data >> ((i & 0x03) * 8)) & 0xff;
}
} else {
/* 16-bit accesses for external controllers */
/* aligned buf case */
if (len >= 2 && !((unsigned long)buf & 0x01)) {
ioread16_rep(fifoaddr, buf, len / 2);
buf += len & ~0x01;
len &= 0x01;
}
/* unaligned buf case */
for (i = 0; i < len; i++) {
if (!(i & 0x01))
data = ioread16(fifoaddr);
buf[i] = (data >> ((i & 0x01) * 8)) & 0xff;
}
}
}
static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val,
unsigned long offset)
{
iowrite16(val, r8a66597->reg + offset);
}
static inline void r8a66597_mdfy(struct r8a66597 *r8a66597,
u16 val, u16 pat, unsigned long offset)
{
u16 tmp;
tmp = r8a66597_read(r8a66597, offset);
tmp = tmp & (~pat);
tmp = tmp | val;
r8a66597_write(r8a66597, tmp, offset);
}
#define r8a66597_bclr(r8a66597, val, offset) \
r8a66597_mdfy(r8a66597, 0, val, offset)
#define r8a66597_bset(r8a66597, val, offset) \
r8a66597_mdfy(r8a66597, val, 0, offset)
static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597,
struct r8a66597_ep *ep,
unsigned char *buf,
int len)
{
void __iomem *fifoaddr = r8a66597->reg + ep->fifoaddr;
int adj = 0;
int i;
if (r8a66597->pdata->on_chip) {
/* 32-bit access only if buf is 32-bit aligned */
if (len >= 4 && !((unsigned long)buf & 0x03)) {
iowrite32_rep(fifoaddr, buf, len / 4);
buf += len & ~0x03;
len &= 0x03;
}
} else {
/* 16-bit access only if buf is 16-bit aligned */
if (len >= 2 && !((unsigned long)buf & 0x01)) {
iowrite16_rep(fifoaddr, buf, len / 2);
buf += len & ~0x01;
len &= 0x01;
}
}
/* adjust fifo address in the little endian case */
if (!(r8a66597_read(r8a66597, CFIFOSEL) & BIGEND)) {
if (r8a66597->pdata->on_chip)
adj = 0x03; /* 32-bit wide */
else
adj = 0x01; /* 16-bit wide */
}
if (r8a66597->pdata->wr0_shorted_to_wr1)
r8a66597_bclr(r8a66597, MBW_16, ep->fifosel);
for (i = 0; i < len; i++)
iowrite8(buf[i], fifoaddr + adj - (i & adj));
if (r8a66597->pdata->wr0_shorted_to_wr1)
r8a66597_bclr(r8a66597, MBW_16, ep->fifosel);
}
static inline u16 get_xtal_from_pdata(struct r8a66597_platdata *pdata)
{
u16 clock = 0;
switch (pdata->xtal) {
case R8A66597_PLATDATA_XTAL_12MHZ:
clock = XTAL12;
break;
case R8A66597_PLATDATA_XTAL_24MHZ:
clock = XTAL24;
break;
case R8A66597_PLATDATA_XTAL_48MHZ:
clock = XTAL48;
break;
default:
printk(KERN_ERR "r8a66597: platdata clock is wrong.\n");
break;
}
return clock;
}
static inline u32 r8a66597_sudmac_read(struct r8a66597 *r8a66597,
unsigned long offset)
{
return ioread32(r8a66597->sudmac_reg + offset);
}
static inline void r8a66597_sudmac_write(struct r8a66597 *r8a66597, u32 val,
unsigned long offset)
{
iowrite32(val, r8a66597->sudmac_reg + offset);
}
#define get_pipectr_addr(pipenum) (PIPE1CTR + (pipenum - 1) * 2)
#define get_pipetre_addr(pipenum) (PIPE1TRE + (pipenum - 1) * 4)
#define get_pipetrn_addr(pipenum) (PIPE1TRN + (pipenum - 1) * 4)
#define enable_irq_ready(r8a66597, pipenum) \
enable_pipe_irq(r8a66597, pipenum, BRDYENB)
#define disable_irq_ready(r8a66597, pipenum) \
disable_pipe_irq(r8a66597, pipenum, BRDYENB)
#define enable_irq_empty(r8a66597, pipenum) \
enable_pipe_irq(r8a66597, pipenum, BEMPENB)
#define disable_irq_empty(r8a66597, pipenum) \
disable_pipe_irq(r8a66597, pipenum, BEMPENB)
#define enable_irq_nrdy(r8a66597, pipenum) \
enable_pipe_irq(r8a66597, pipenum, NRDYENB)
#define disable_irq_nrdy(r8a66597, pipenum) \
disable_pipe_irq(r8a66597, pipenum, NRDYENB)
#endif /* __R8A66597_H__ */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,137 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas RZ/V2M USB3DRD driver
*
* Copyright (C) 2022 Renesas Electronics Corporation
*/
#include <linux/io.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/usb/rzv2m_usb3drd.h>
#define USB_PERI_DRD_CON 0x000
#define USB_PERI_DRD_CON_PERI_RST BIT(31)
#define USB_PERI_DRD_CON_HOST_RST BIT(30)
#define USB_PERI_DRD_CON_PERI_CON BIT(24)
static void rzv2m_usb3drd_set_bit(struct rzv2m_usb3drd *usb3, u32 bits,
u32 offs)
{
u32 val = readl(usb3->reg + offs);
val |= bits;
writel(val, usb3->reg + offs);
}
static void rzv2m_usb3drd_clear_bit(struct rzv2m_usb3drd *usb3, u32 bits,
u32 offs)
{
u32 val = readl(usb3->reg + offs);
val &= ~bits;
writel(val, usb3->reg + offs);
}
void rzv2m_usb3drd_reset(struct device *dev, bool host)
{
struct rzv2m_usb3drd *usb3 = dev_get_drvdata(dev);
if (host) {
rzv2m_usb3drd_clear_bit(usb3, USB_PERI_DRD_CON_PERI_CON,
USB_PERI_DRD_CON);
rzv2m_usb3drd_clear_bit(usb3, USB_PERI_DRD_CON_HOST_RST,
USB_PERI_DRD_CON);
rzv2m_usb3drd_set_bit(usb3, USB_PERI_DRD_CON_PERI_RST,
USB_PERI_DRD_CON);
} else {
rzv2m_usb3drd_set_bit(usb3, USB_PERI_DRD_CON_PERI_CON,
USB_PERI_DRD_CON);
rzv2m_usb3drd_set_bit(usb3, USB_PERI_DRD_CON_HOST_RST,
USB_PERI_DRD_CON);
rzv2m_usb3drd_clear_bit(usb3, USB_PERI_DRD_CON_PERI_RST,
USB_PERI_DRD_CON);
}
}
EXPORT_SYMBOL_GPL(rzv2m_usb3drd_reset);
static void rzv2m_usb3drd_remove(struct platform_device *pdev)
{
struct rzv2m_usb3drd *usb3 = platform_get_drvdata(pdev);
of_platform_depopulate(usb3->dev);
pm_runtime_put(usb3->dev);
pm_runtime_disable(&pdev->dev);
reset_control_assert(usb3->drd_rstc);
}
static int rzv2m_usb3drd_probe(struct platform_device *pdev)
{
struct rzv2m_usb3drd *usb3;
int ret;
usb3 = devm_kzalloc(&pdev->dev, sizeof(*usb3), GFP_KERNEL);
if (!usb3)
return -ENOMEM;
usb3->dev = &pdev->dev;
usb3->drd_irq = platform_get_irq_byname(pdev, "drd");
if (usb3->drd_irq < 0)
return usb3->drd_irq;
usb3->reg = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(usb3->reg))
return PTR_ERR(usb3->reg);
platform_set_drvdata(pdev, usb3);
usb3->drd_rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
if (IS_ERR(usb3->drd_rstc))
return dev_err_probe(&pdev->dev, PTR_ERR(usb3->drd_rstc),
"failed to get drd reset");
reset_control_deassert(usb3->drd_rstc);
pm_runtime_enable(&pdev->dev);
ret = pm_runtime_resume_and_get(usb3->dev);
if (ret)
goto err_rst;
ret = of_platform_populate(usb3->dev->of_node, NULL, NULL, usb3->dev);
if (ret)
goto err_pm;
return 0;
err_pm:
pm_runtime_put(usb3->dev);
err_rst:
pm_runtime_disable(&pdev->dev);
reset_control_assert(usb3->drd_rstc);
return ret;
}
static const struct of_device_id rzv2m_usb3drd_of_match[] = {
{ .compatible = "renesas,rzv2m-usb3drd", },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, rzv2m_usb3drd_of_match);
static struct platform_driver rzv2m_usb3drd_driver = {
.driver = {
.name = "rzv2m-usb3drd",
.of_match_table = rzv2m_usb3drd_of_match,
},
.probe = rzv2m_usb3drd_probe,
.remove = rzv2m_usb3drd_remove,
};
module_platform_driver(rzv2m_usb3drd_driver);
MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>");
MODULE_DESCRIPTION("Renesas RZ/V2M USB3DRD driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:rzv2m_usb3drd");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,325 @@
// SPDX-License-Identifier: GPL-2.0
/*
* snps_udc_plat.c - Synopsys UDC Platform Driver
*
* Copyright (C) 2016 Broadcom
*/
#include <linux/extcon.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/phy/phy.h>
#include <linux/module.h>
#include <linux/dmapool.h>
#include <linux/interrupt.h>
#include <linux/moduleparam.h>
#include "amd5536udc.h"
/* description */
#define UDC_MOD_DESCRIPTION "Synopsys UDC platform driver"
static void start_udc(struct udc *udc)
{
if (udc->driver) {
dev_info(udc->dev, "Connecting...\n");
udc_enable_dev_setup_interrupts(udc);
udc_basic_init(udc);
udc->connected = 1;
}
}
static void stop_udc(struct udc *udc)
{
int tmp;
u32 reg;
spin_lock(&udc->lock);
/* Flush the receieve fifo */
reg = readl(&udc->regs->ctl);
reg |= AMD_BIT(UDC_DEVCTL_SRX_FLUSH);
writel(reg, &udc->regs->ctl);
reg = readl(&udc->regs->ctl);
reg &= ~(AMD_BIT(UDC_DEVCTL_SRX_FLUSH));
writel(reg, &udc->regs->ctl);
dev_dbg(udc->dev, "ep rx queue flushed\n");
/* Mask interrupts. Required more so when the
* UDC is connected to a DRD phy.
*/
udc_mask_unused_interrupts(udc);
/* Disconnect gadget driver */
if (udc->driver) {
spin_unlock(&udc->lock);
udc->driver->disconnect(&udc->gadget);
spin_lock(&udc->lock);
/* empty queues */
for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
empty_req_queue(&udc->ep[tmp]);
}
udc->connected = 0;
spin_unlock(&udc->lock);
dev_info(udc->dev, "Device disconnected\n");
}
static void udc_drd_work(struct work_struct *work)
{
struct udc *udc;
udc = container_of(to_delayed_work(work),
struct udc, drd_work);
if (udc->conn_type) {
dev_dbg(udc->dev, "idle -> device\n");
start_udc(udc);
} else {
dev_dbg(udc->dev, "device -> idle\n");
stop_udc(udc);
}
}
static int usbd_connect_notify(struct notifier_block *self,
unsigned long event, void *ptr)
{
struct udc *udc = container_of(self, struct udc, nb);
dev_dbg(udc->dev, "%s: event: %lu\n", __func__, event);
udc->conn_type = event;
schedule_delayed_work(&udc->drd_work, 0);
return NOTIFY_OK;
}
static int udc_plat_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
struct udc *udc;
int ret;
udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL);
if (!udc)
return -ENOMEM;
spin_lock_init(&udc->lock);
udc->dev = dev;
udc->virt_addr = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(udc->virt_addr))
return PTR_ERR(udc->virt_addr);
/* udc csr registers base */
udc->csr = udc->virt_addr + UDC_CSR_ADDR;
/* dev registers base */
udc->regs = udc->virt_addr + UDC_DEVCFG_ADDR;
/* ep registers base */
udc->ep_regs = udc->virt_addr + UDC_EPREGS_ADDR;
/* fifo's base */
udc->rxfifo = (u32 __iomem *)(udc->virt_addr + UDC_RXFIFO_ADDR);
udc->txfifo = (u32 __iomem *)(udc->virt_addr + UDC_TXFIFO_ADDR);
udc->phys_addr = (unsigned long)res->start;
udc->irq = irq_of_parse_and_map(dev->of_node, 0);
if (udc->irq <= 0) {
dev_err(dev, "Can't parse and map interrupt\n");
return -EINVAL;
}
udc->udc_phy = devm_of_phy_get_by_index(dev, dev->of_node, 0);
if (IS_ERR(udc->udc_phy)) {
dev_err(dev, "Failed to obtain phy from device tree\n");
return PTR_ERR(udc->udc_phy);
}
ret = phy_init(udc->udc_phy);
if (ret) {
dev_err(dev, "UDC phy init failed");
return ret;
}
ret = phy_power_on(udc->udc_phy);
if (ret) {
dev_err(dev, "UDC phy power on failed");
phy_exit(udc->udc_phy);
return ret;
}
/* Register for extcon if supported */
if (of_property_present(dev->of_node, "extcon")) {
udc->edev = extcon_get_edev_by_phandle(dev, 0);
if (IS_ERR(udc->edev)) {
if (PTR_ERR(udc->edev) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_err(dev, "Invalid or missing extcon\n");
ret = PTR_ERR(udc->edev);
goto exit_phy;
}
udc->nb.notifier_call = usbd_connect_notify;
ret = extcon_register_notifier(udc->edev, EXTCON_USB,
&udc->nb);
if (ret < 0) {
dev_err(dev, "Can't register extcon device\n");
goto exit_phy;
}
ret = extcon_get_state(udc->edev, EXTCON_USB);
if (ret < 0) {
dev_err(dev, "Can't get cable state\n");
goto exit_extcon;
} else if (ret) {
udc->conn_type = ret;
}
INIT_DELAYED_WORK(&udc->drd_work, udc_drd_work);
}
/* init dma pools */
if (use_dma) {
ret = init_dma_pools(udc);
if (ret != 0)
goto exit_extcon;
}
ret = devm_request_irq(dev, udc->irq, udc_irq, IRQF_SHARED,
"snps-udc", udc);
if (ret < 0) {
dev_err(dev, "Request irq %d failed for UDC\n", udc->irq);
goto exit_dma;
}
platform_set_drvdata(pdev, udc);
udc->chiprev = UDC_BCM_REV;
if (udc_probe(udc)) {
ret = -ENODEV;
goto exit_dma;
}
dev_info(dev, "Synopsys UDC platform driver probe successful\n");
return 0;
exit_dma:
if (use_dma)
free_dma_pools(udc);
exit_extcon:
if (udc->edev)
extcon_unregister_notifier(udc->edev, EXTCON_USB, &udc->nb);
exit_phy:
if (udc->udc_phy) {
phy_power_off(udc->udc_phy);
phy_exit(udc->udc_phy);
}
return ret;
}
static void udc_plat_remove(struct platform_device *pdev)
{
struct udc *dev;
dev = platform_get_drvdata(pdev);
usb_del_gadget_udc(&dev->gadget);
/* gadget driver must not be registered */
if (WARN_ON(dev->driver))
return;
/* dma pool cleanup */
free_dma_pools(dev);
udc_remove(dev);
platform_set_drvdata(pdev, NULL);
phy_power_off(dev->udc_phy);
phy_exit(dev->udc_phy);
extcon_unregister_notifier(dev->edev, EXTCON_USB, &dev->nb);
dev_info(&pdev->dev, "Synopsys UDC platform driver removed\n");
}
#ifdef CONFIG_PM_SLEEP
static int udc_plat_suspend(struct device *dev)
{
struct udc *udc;
udc = dev_get_drvdata(dev);
stop_udc(udc);
if (extcon_get_state(udc->edev, EXTCON_USB) > 0) {
dev_dbg(udc->dev, "device -> idle\n");
stop_udc(udc);
}
phy_power_off(udc->udc_phy);
phy_exit(udc->udc_phy);
return 0;
}
static int udc_plat_resume(struct device *dev)
{
struct udc *udc;
int ret;
udc = dev_get_drvdata(dev);
ret = phy_init(udc->udc_phy);
if (ret) {
dev_err(udc->dev, "UDC phy init failure");
return ret;
}
ret = phy_power_on(udc->udc_phy);
if (ret) {
dev_err(udc->dev, "UDC phy power on failure");
phy_exit(udc->udc_phy);
return ret;
}
if (extcon_get_state(udc->edev, EXTCON_USB) > 0) {
dev_dbg(udc->dev, "idle -> device\n");
start_udc(udc);
}
return 0;
}
static const struct dev_pm_ops udc_plat_pm_ops = {
.suspend = udc_plat_suspend,
.resume = udc_plat_resume,
};
#endif
static const struct of_device_id of_udc_match[] = {
{ .compatible = "brcm,ns2-udc", },
{ .compatible = "brcm,cygnus-udc", },
{ .compatible = "brcm,iproc-udc", },
{ }
};
MODULE_DEVICE_TABLE(of, of_udc_match);
static struct platform_driver udc_plat_driver = {
.probe = udc_plat_probe,
.remove = udc_plat_remove,
.driver = {
.name = "snps-udc-plat",
.of_match_table = of_udc_match,
#ifdef CONFIG_PM_SLEEP
.pm = &udc_plat_pm_ops,
#endif
},
};
module_platform_driver(udc_plat_driver);
MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
MODULE_AUTHOR("Broadcom");
MODULE_LICENSE("GPL v2");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
/*
* trace.c - USB Gadget Framework Trace Support
*
* Copyright (C) 2016 Intel Corporation
* Author: Felipe Balbi <felipe.balbi@linux.intel.com>
*/
#define CREATE_TRACE_POINTS
#include "trace.h"
+299
View File
@@ -0,0 +1,299 @@
// SPDX-License-Identifier: GPL-2.0
/*
* udc.c - Core UDC Framework
*
* Copyright (C) 2016 Intel Corporation
* Author: Felipe Balbi <felipe.balbi@linux.intel.com>
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM gadget
#if !defined(__UDC_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
#define __UDC_TRACE_H
#include <linux/types.h>
#include <linux/tracepoint.h>
#include <asm/byteorder.h>
#include <linux/usb/gadget.h>
DECLARE_EVENT_CLASS(udc_log_gadget,
TP_PROTO(struct usb_gadget *g, int ret),
TP_ARGS(g, ret),
TP_STRUCT__entry(
__field(enum usb_device_speed, speed)
__field(enum usb_device_speed, max_speed)
__field(enum usb_device_state, state)
__field(unsigned, mA)
__field(unsigned, sg_supported)
__field(unsigned, is_otg)
__field(unsigned, is_a_peripheral)
__field(unsigned, b_hnp_enable)
__field(unsigned, a_hnp_support)
__field(unsigned, hnp_polling_support)
__field(unsigned, host_request_flag)
__field(unsigned, quirk_ep_out_aligned_size)
__field(unsigned, quirk_altset_not_supp)
__field(unsigned, quirk_stall_not_supp)
__field(unsigned, quirk_zlp_not_supp)
__field(unsigned, is_selfpowered)
__field(unsigned, deactivated)
__field(unsigned, connected)
__field(int, ret)
),
TP_fast_assign(
__entry->speed = g->speed;
__entry->max_speed = g->max_speed;
__entry->state = g->state;
__entry->mA = g->mA;
__entry->sg_supported = g->sg_supported;
__entry->is_otg = g->is_otg;
__entry->is_a_peripheral = g->is_a_peripheral;
__entry->b_hnp_enable = g->b_hnp_enable;
__entry->a_hnp_support = g->a_hnp_support;
__entry->hnp_polling_support = g->hnp_polling_support;
__entry->host_request_flag = g->host_request_flag;
__entry->quirk_ep_out_aligned_size = g->quirk_ep_out_aligned_size;
__entry->quirk_altset_not_supp = g->quirk_altset_not_supp;
__entry->quirk_stall_not_supp = g->quirk_stall_not_supp;
__entry->quirk_zlp_not_supp = g->quirk_zlp_not_supp;
__entry->is_selfpowered = g->is_selfpowered;
__entry->deactivated = g->deactivated;
__entry->connected = g->connected;
__entry->ret = ret;
),
TP_printk("speed %d/%d state %d %dmA [%s%s%s%s%s%s%s%s%s%s%s%s%s%s] --> %d",
__entry->speed, __entry->max_speed, __entry->state, __entry->mA,
__entry->sg_supported ? "sg:" : "",
__entry->is_otg ? "OTG:" : "",
__entry->is_a_peripheral ? "a_peripheral:" : "",
__entry->b_hnp_enable ? "b_hnp:" : "",
__entry->a_hnp_support ? "a_hnp:" : "",
__entry->hnp_polling_support ? "hnp_poll:" : "",
__entry->host_request_flag ? "hostreq:" : "",
__entry->quirk_ep_out_aligned_size ? "out_aligned:" : "",
__entry->quirk_altset_not_supp ? "no_altset:" : "",
__entry->quirk_stall_not_supp ? "no_stall:" : "",
__entry->quirk_zlp_not_supp ? "no_zlp" : "",
__entry->is_selfpowered ? "self-powered:" : "bus-powered:",
__entry->deactivated ? "deactivated:" : "activated:",
__entry->connected ? "connected" : "disconnected",
__entry->ret)
);
DEFINE_EVENT(udc_log_gadget, usb_gadget_set_state,
TP_PROTO(struct usb_gadget *g, int ret),
TP_ARGS(g, ret)
);
DEFINE_EVENT(udc_log_gadget, usb_gadget_frame_number,
TP_PROTO(struct usb_gadget *g, int ret),
TP_ARGS(g, ret)
);
DEFINE_EVENT(udc_log_gadget, usb_gadget_wakeup,
TP_PROTO(struct usb_gadget *g, int ret),
TP_ARGS(g, ret)
);
DEFINE_EVENT(udc_log_gadget, usb_gadget_set_remote_wakeup,
TP_PROTO(struct usb_gadget *g, int ret),
TP_ARGS(g, ret)
);
DEFINE_EVENT(udc_log_gadget, usb_gadget_set_selfpowered,
TP_PROTO(struct usb_gadget *g, int ret),
TP_ARGS(g, ret)
);
DEFINE_EVENT(udc_log_gadget, usb_gadget_clear_selfpowered,
TP_PROTO(struct usb_gadget *g, int ret),
TP_ARGS(g, ret)
);
DEFINE_EVENT(udc_log_gadget, usb_gadget_vbus_connect,
TP_PROTO(struct usb_gadget *g, int ret),
TP_ARGS(g, ret)
);
DEFINE_EVENT(udc_log_gadget, usb_gadget_vbus_draw,
TP_PROTO(struct usb_gadget *g, int ret),
TP_ARGS(g, ret)
);
DEFINE_EVENT(udc_log_gadget, usb_gadget_vbus_disconnect,
TP_PROTO(struct usb_gadget *g, int ret),
TP_ARGS(g, ret)
);
DEFINE_EVENT(udc_log_gadget, usb_gadget_connect,
TP_PROTO(struct usb_gadget *g, int ret),
TP_ARGS(g, ret)
);
DEFINE_EVENT(udc_log_gadget, usb_gadget_disconnect,
TP_PROTO(struct usb_gadget *g, int ret),
TP_ARGS(g, ret)
);
DEFINE_EVENT(udc_log_gadget, usb_gadget_deactivate,
TP_PROTO(struct usb_gadget *g, int ret),
TP_ARGS(g, ret)
);
DEFINE_EVENT(udc_log_gadget, usb_gadget_activate,
TP_PROTO(struct usb_gadget *g, int ret),
TP_ARGS(g, ret)
);
DECLARE_EVENT_CLASS(udc_log_ep,
TP_PROTO(struct usb_ep *ep, int ret),
TP_ARGS(ep, ret),
TP_STRUCT__entry(
__string(name, ep->name)
__field(unsigned, maxpacket)
__field(unsigned, maxpacket_limit)
__field(unsigned, max_streams)
__field(unsigned, mult)
__field(unsigned, maxburst)
__field(u8, address)
__field(bool, claimed)
__field(bool, enabled)
__field(int, ret)
),
TP_fast_assign(
__assign_str(name);
__entry->maxpacket = ep->maxpacket;
__entry->maxpacket_limit = ep->maxpacket_limit;
__entry->max_streams = ep->max_streams;
__entry->mult = ep->mult;
__entry->maxburst = ep->maxburst;
__entry->address = ep->address,
__entry->claimed = ep->claimed;
__entry->enabled = ep->enabled;
__entry->ret = ret;
),
TP_printk("%s: mps %d/%d streams %d mult %d burst %d addr %02x %s%s --> %d",
__get_str(name), __entry->maxpacket, __entry->maxpacket_limit,
__entry->max_streams, __entry->mult, __entry->maxburst,
__entry->address, __entry->claimed ? "claimed:" : "released:",
__entry->enabled ? "enabled" : "disabled", ret)
);
DEFINE_EVENT(udc_log_ep, usb_ep_set_maxpacket_limit,
TP_PROTO(struct usb_ep *ep, int ret),
TP_ARGS(ep, ret)
);
DEFINE_EVENT(udc_log_ep, usb_ep_enable,
TP_PROTO(struct usb_ep *ep, int ret),
TP_ARGS(ep, ret)
);
DEFINE_EVENT(udc_log_ep, usb_ep_disable,
TP_PROTO(struct usb_ep *ep, int ret),
TP_ARGS(ep, ret)
);
DEFINE_EVENT(udc_log_ep, usb_ep_set_halt,
TP_PROTO(struct usb_ep *ep, int ret),
TP_ARGS(ep, ret)
);
DEFINE_EVENT(udc_log_ep, usb_ep_clear_halt,
TP_PROTO(struct usb_ep *ep, int ret),
TP_ARGS(ep, ret)
);
DEFINE_EVENT(udc_log_ep, usb_ep_set_wedge,
TP_PROTO(struct usb_ep *ep, int ret),
TP_ARGS(ep, ret)
);
DEFINE_EVENT(udc_log_ep, usb_ep_fifo_status,
TP_PROTO(struct usb_ep *ep, int ret),
TP_ARGS(ep, ret)
);
DEFINE_EVENT(udc_log_ep, usb_ep_fifo_flush,
TP_PROTO(struct usb_ep *ep, int ret),
TP_ARGS(ep, ret)
);
DECLARE_EVENT_CLASS(udc_log_req,
TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret),
TP_ARGS(ep, req, ret),
TP_STRUCT__entry(
__string(name, ep->name)
__field(unsigned, length)
__field(unsigned, actual)
__field(unsigned, num_sgs)
__field(unsigned, num_mapped_sgs)
__field(unsigned, stream_id)
__field(unsigned, no_interrupt)
__field(unsigned, zero)
__field(unsigned, short_not_ok)
__field(int, status)
__field(int, ret)
__field(struct usb_request *, req)
),
TP_fast_assign(
__assign_str(name);
__entry->length = req->length;
__entry->actual = req->actual;
__entry->num_sgs = req->num_sgs;
__entry->num_mapped_sgs = req->num_mapped_sgs;
__entry->stream_id = req->stream_id;
__entry->no_interrupt = req->no_interrupt;
__entry->zero = req->zero;
__entry->short_not_ok = req->short_not_ok;
__entry->status = req->status;
__entry->ret = ret;
__entry->req = req;
),
TP_printk("%s: req %p length %d/%d sgs %d/%d stream %d %s%s%s status %d --> %d",
__get_str(name),__entry->req, __entry->actual, __entry->length,
__entry->num_mapped_sgs, __entry->num_sgs, __entry->stream_id,
__entry->zero ? "Z" : "z",
__entry->short_not_ok ? "S" : "s",
__entry->no_interrupt ? "i" : "I",
__entry->status, __entry->ret
)
);
DEFINE_EVENT(udc_log_req, usb_ep_alloc_request,
TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret),
TP_ARGS(ep, req, ret)
);
DEFINE_EVENT(udc_log_req, usb_ep_free_request,
TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret),
TP_ARGS(ep, req, ret)
);
DEFINE_EVENT(udc_log_req, usb_ep_queue,
TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret),
TP_ARGS(ep, req, ret)
);
DEFINE_EVENT(udc_log_req, usb_ep_dequeue,
TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret),
TP_ARGS(ep, req, ret)
);
DEFINE_EVENT(udc_log_req, usb_gadget_giveback_request,
TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret),
TP_ARGS(ep, req, ret)
);
#endif /* __UDC_TRACE_H */
/* this part has to be here */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace
#include <trace/define_trace.h>
File diff suppressed because it is too large Load Diff