mirror of
https://github.com/AsahiLinux/u-boot
synced 2025-03-15 14:26:58 +00:00
- MediaTek USB host support
This commit is contained in:
commit
f216259927
26 changed files with 4819 additions and 21 deletions
|
@ -266,6 +266,7 @@ F: arch/arm/include/asm/arch-pxa/
|
|||
ARM MEDIATEK
|
||||
M: Ryder Lee <ryder.lee@mediatek.com>
|
||||
M: Weijie Gao <weijie.gao@mediatek.com>
|
||||
M: Chunfeng Yun <chunfeng.yun@mediatek.com>
|
||||
R: GSS_MTK_Uboot_upstream <GSS_MTK_Uboot_upstream@mediatek.com>
|
||||
S: Maintained
|
||||
F: arch/arm/mach-mediatek/
|
||||
|
@ -282,6 +283,8 @@ F: drivers/power/domain/mtk-power-domain.c
|
|||
F: drivers/ram/mediatek/
|
||||
F: drivers/spi/mtk_snfi_spi.c
|
||||
F: drivers/timer/mtk_timer.c
|
||||
F: drivers/usb/host/xhci-mtk.c
|
||||
F: drivers/usb/mtu3/
|
||||
F: drivers/watchdog/mtk_wdt.c
|
||||
F: drivers/net/mtk_eth.c
|
||||
F: drivers/reset/reset-mediatek.c
|
||||
|
|
1
Makefile
1
Makefile
|
@ -795,6 +795,7 @@ libs-y += drivers/usb/eth/
|
|||
libs-$(CONFIG_USB_GADGET) += drivers/usb/gadget/
|
||||
libs-$(CONFIG_USB_GADGET) += drivers/usb/gadget/udc/
|
||||
libs-y += drivers/usb/host/
|
||||
libs-y += drivers/usb/mtu3/
|
||||
libs-y += drivers/usb/musb/
|
||||
libs-y += drivers/usb/musb-new/
|
||||
libs-y += drivers/usb/phy/
|
||||
|
|
|
@ -43,6 +43,25 @@
|
|||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
usb_p0_vbus: regulator@0 {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "p0_vbus";
|
||||
regulator-min-microvolt = <5000000>;
|
||||
regulator-max-microvolt = <5000000>;
|
||||
gpio = <&gpio 27 GPIO_ACTIVE_HIGH>;
|
||||
enable-active-high;
|
||||
};
|
||||
|
||||
usb_p1_vbus: regulator@1 {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "p1_vbus";
|
||||
regulator-min-microvolt = <5000000>;
|
||||
regulator-max-microvolt = <5000000>;
|
||||
gpio = <&gpio 32 GPIO_ACTIVE_HIGH>;
|
||||
enable-active-high;
|
||||
regulator-always-on;
|
||||
};
|
||||
};
|
||||
|
||||
&mmc0 {
|
||||
|
@ -95,6 +114,21 @@
|
|||
};
|
||||
};
|
||||
|
||||
&ssusb {
|
||||
dr_mode = "peripheral";
|
||||
maximum-speed = "high-speed";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&usb3 {
|
||||
vbus-supply = <&usb_p0_vbus>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&u3phy {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&uart0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&uart0_pins>;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/phy/phy.h>
|
||||
|
||||
/ {
|
||||
compatible = "mediatek,mt8512";
|
||||
|
@ -100,6 +101,52 @@
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
usb3: usb@11213e00 {
|
||||
compatible = "mediatek,mt8512-mtu3", "mediatek,mtu3";
|
||||
reg = <0x11213e00 0x0100>;
|
||||
reg-names = "ippc";
|
||||
phys = <&u2port0 PHY_TYPE_USB2>, <&u2port1 PHY_TYPE_USB2>;
|
||||
clocks = <&infracfg CLK_INFRA_USB_SYS>,
|
||||
<&topckgen CLK_TOP_SSUSB_TOP_CK_EN>,
|
||||
<&infracfg CLK_INFRA_ICUSB>;
|
||||
clock-names = "sys_ck", "ref_ck", "mcu_ck";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
status = "disabled";
|
||||
|
||||
ssusb: usb@11210000 {
|
||||
compatible = "mediatek,ssusb";
|
||||
reg = <0x11210000 0x3e00>;
|
||||
interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_LOW>;
|
||||
reg-names = "mac";
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
||||
u3phy: usb-phy@11cc0000 {
|
||||
compatible = "mediatek,mt8512-tphy",
|
||||
"mediatek,generic-tphy-v2";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
status = "disabled";
|
||||
|
||||
u2port0: usb-phy@11cc0000 {
|
||||
reg = <0x11cc0000 0x400>;
|
||||
clocks = <&topckgen CLK_TOP_USB20_48M_EN>;
|
||||
clock-names = "ref";
|
||||
#phy-cells = <1>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
u2port1: usb-phy@11c40000 {
|
||||
reg = <0x11c40000 0x400>;
|
||||
#phy-cells = <1>;
|
||||
status = "okay";
|
||||
};
|
||||
};
|
||||
|
||||
mmc0: mmc@11230000 {
|
||||
compatible = "mediatek,mt8512-mmc";
|
||||
reg = <0x11230000 0x1000>,
|
||||
|
@ -112,4 +159,4 @@
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
|
|
@ -9,11 +9,16 @@ CONFIG_DM_GPIO=y
|
|||
CONFIG_TARGET_MT8512=y
|
||||
CONFIG_DEFAULT_DEVICE_TREE="mt8512-bm1-emmc"
|
||||
CONFIG_FIT=y
|
||||
CONFIG_EFI_PARTITION=y
|
||||
CONFIG_FIT_SIGNATURE=y
|
||||
CONFIG_DEFAULT_FDT_FILE="mt8512-bm1-emmc.dtb"
|
||||
CONFIG_SYS_PROMPT="MT8512> "
|
||||
CONFIG_CMD_BOOTMENU=y
|
||||
CONFIG_CMD_MMC=y
|
||||
CONFIG_CMD_USB=y
|
||||
CONFIG_CMD_FAT=y
|
||||
CONFIG_CMD_FS_GENERIC=y
|
||||
# CONFIG_DOS_PARTITION is not set
|
||||
CONFIG_ENV_OVERWRITE=y
|
||||
CONFIG_REGMAP=y
|
||||
CONFIG_SYSCON=y
|
||||
|
@ -26,6 +31,16 @@ CONFIG_PINCONF=y
|
|||
CONFIG_PINCTRL_MT8512=y
|
||||
CONFIG_RAM=y
|
||||
CONFIG_BAUDRATE=921600
|
||||
CONFIG_USB_FUNCTION_FASTBOOT=y
|
||||
CONFIG_FASTBOOT_BUF_ADDR=0x56000000
|
||||
CONFIG_FASTBOOT_BUF_SIZE=0x1e00000
|
||||
CONFIG_FASTBOOT_FLASH=y
|
||||
CONFIG_FASTBOOT_FLASH_MMC_DEV=0
|
||||
CONFIG_FASTBOOT_MMC_BOOT1_SUPPORT=y
|
||||
CONFIG_PHY=y
|
||||
CONFIG_PHY_MTK_TPHY=y
|
||||
CONFIG_DM_REGULATOR=y
|
||||
CONFIG_DM_REGULATOR_FIXED=y
|
||||
CONFIG_DM_SERIAL=y
|
||||
CONFIG_MTK_SERIAL=y
|
||||
CONFIG_TIMER=y
|
||||
|
@ -33,3 +48,14 @@ CONFIG_MTK_TIMER=y
|
|||
CONFIG_WDT=y
|
||||
CONFIG_WDT_MTK=y
|
||||
CONFIG_LZO=y
|
||||
CONFIG_USB=y
|
||||
CONFIG_DM_USB=y
|
||||
CONFIG_DM_USB_GADGET=y
|
||||
CONFIG_USB_XHCI_HCD=y
|
||||
CONFIG_USB_MTU3=y
|
||||
# CONFIG_USB_MTU3_HOST is not set
|
||||
CONFIG_USB_STORAGE=y
|
||||
CONFIG_USB_GADGET=y
|
||||
CONFIG_USB_GADGET_MANUFACTURER="MediaTek"
|
||||
CONFIG_USB_GADGET_VENDOR_NUM=0x0e8d
|
||||
CONFIG_USB_GADGET_PRODUCT_NUM=0x201c
|
||||
|
|
31
doc/device-tree-bindings/usb/generic.txt
Normal file
31
doc/device-tree-bindings/usb/generic.txt
Normal file
|
@ -0,0 +1,31 @@
|
|||
Generic USB Properties
|
||||
|
||||
Optional properties:
|
||||
- maximum-speed: tells USB controllers we want to work up to a certain
|
||||
speed. Valid arguments are "super-speed-plus",
|
||||
"super-speed", "high-speed", "full-speed" and
|
||||
"low-speed". In case this isn't passed via DT, USB
|
||||
controllers should default to their maximum HW
|
||||
capability.
|
||||
- dr_mode: tells Dual-Role USB controllers that we want to work on a
|
||||
particular mode. Valid arguments are "host",
|
||||
"peripheral" and "otg". In case this attribute isn't
|
||||
passed via DT, USB DRD controllers should default to
|
||||
OTG.
|
||||
- phy_type: tells USB controllers that we want to configure the core to support
|
||||
a UTMI+ PHY with an 8- or 16-bit interface if UTMI+ is
|
||||
selected. Valid arguments are "utmi" and "utmi_wide".
|
||||
In case this isn't passed via DT, USB controllers should
|
||||
default to HW capability.
|
||||
|
||||
This is an attribute to a USB controller such as:
|
||||
|
||||
dwc3@4a030000 {
|
||||
compatible = "synopsys,dwc3";
|
||||
reg = <0x4a030000 0xcfff>;
|
||||
interrupts = <0 92 4>
|
||||
usb-phy = <&usb2_phy>, <&usb3,phy>;
|
||||
maximum-speed = "super-speed";
|
||||
dr_mode = "otg";
|
||||
phy_type = "utmi_wide";
|
||||
};
|
79
doc/device-tree-bindings/usb/mediatek,mtu3.txt
Normal file
79
doc/device-tree-bindings/usb/mediatek,mtu3.txt
Normal file
|
@ -0,0 +1,79 @@
|
|||
The device node for Mediatek USB3 DRD controller
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "mediatek,<soc-model>-mtu3", "mediatek,mtu3",
|
||||
soc-model is the name of SoC, such as mt8512 etc,
|
||||
when using "mediatek,mtu3" compatible string, you need SoC specific
|
||||
ones in addition, one of:
|
||||
- "mediatek,mt8512-mtu3"
|
||||
- reg : specifies physical base address and size of the registers
|
||||
- reg-names: should be
|
||||
- "ippc" : IP Port Control
|
||||
- power-domains : a phandle to USB power domain node to control USB's MTCMOS
|
||||
- clocks : a list of phandle + clock-specifier pairs, one for each
|
||||
entry in clock-names
|
||||
- clock-names : must contain "sys_ck" for clock of controller,
|
||||
the following clocks are optional:
|
||||
"ref_ck", "mcu_ck", "dma_ck" and "xhci_ck";
|
||||
- phys : list of all the USB PHYs on this HCD
|
||||
- #address-cells, #size-cells : used for sub-nodes with 'reg' property
|
||||
- ranges : allows valid 1:1 translation between child's address space and
|
||||
parent's address space
|
||||
|
||||
Optional properties:
|
||||
- vusb33-supply : regulator of USB AVDD3.3v
|
||||
- vbus-supply : regulator of VBUS 5v, needed when supports host mode.
|
||||
|
||||
Sub-nodes:
|
||||
Required properties:
|
||||
- compatible : should be "mediatek,ssusb"
|
||||
- reg : specifies physical base address and size of the registers
|
||||
- reg-names: should be
|
||||
- "mac" : SSUSB MAC, include xHCI and device
|
||||
- interrupts : interrupt used by xHCI or device
|
||||
- dr_mode : should be one of "host" or "peripheral",
|
||||
see : usb/generic.txt
|
||||
|
||||
Optional properties:
|
||||
- pinctrl-names : a pinctrl state named "default" is optional
|
||||
- pinctrl-0 : pin control group
|
||||
See: pinctrl/pinctrl-bindings.txt
|
||||
|
||||
- device mode:
|
||||
- maximum-speed : valid arguments are "full-speed", "high-speed",
|
||||
"super-speed" and "super-speed-plus",
|
||||
see: usb/generic.txt
|
||||
- mediatek,force-vbus : force vbus as valid by SW
|
||||
|
||||
- host mode (dr_mode is "host"):
|
||||
- mediatek,u3p-dis-msk : mask to disable u3ports, bit0 for u3port0,
|
||||
bit1 for u3port1, ... etc;
|
||||
|
||||
Example:
|
||||
usb3: usb@11213e00 {
|
||||
compatible = "mediatek,mt8512-mtu3", "mediatek,mtu3";
|
||||
reg = <0x11213e00 0x0100>;
|
||||
reg-names = "ippc";
|
||||
phys = <&u2port0 PHY_TYPE_USB2>, <&u2port1 PHY_TYPE_USB2>;
|
||||
power-domains = <&scpsys MT8512_POWER_DOMAIN_USB>;
|
||||
clocks = <&infracfg CLK_INFRA_USB_SYS>,
|
||||
<&topckgen CLK_TOP_SSUSB_TOP_CK_EN>,
|
||||
<&infracfg CLK_INFRA_ICUSB>;
|
||||
clock-names = "sys_ck", "ref_ck", "mcu_ck";
|
||||
vusb33-supply = <reg_3p3v>;
|
||||
vbus-supply = <&usb_p0_vbus>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
status = "disabled";
|
||||
|
||||
ssusb: usb@11210000 {
|
||||
compatible = "mediatek,ssusb";
|
||||
reg = <0x11210000 0x3e00>;
|
||||
interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_LOW>;
|
||||
reg-names = "mac";
|
||||
dr_mode = "peripheral";
|
||||
maximum-speed = "high-speed";
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
|
@ -72,6 +72,8 @@ source "drivers/usb/cdns3/Kconfig"
|
|||
|
||||
source "drivers/usb/dwc3/Kconfig"
|
||||
|
||||
source "drivers/usb/mtu3/Kconfig"
|
||||
|
||||
source "drivers/usb/musb/Kconfig"
|
||||
|
||||
source "drivers/usb/musb-new/Kconfig"
|
||||
|
|
|
@ -46,8 +46,16 @@ static const char *const speed_names[] = {
|
|||
[USB_SPEED_HIGH] = "high-speed",
|
||||
[USB_SPEED_WIRELESS] = "wireless",
|
||||
[USB_SPEED_SUPER] = "super-speed",
|
||||
[USB_SPEED_SUPER_PLUS] = "super-speed-plus",
|
||||
};
|
||||
|
||||
const char *usb_speed_string(enum usb_device_speed speed)
|
||||
{
|
||||
if (speed < 0 || speed >= ARRAY_SIZE(speed_names))
|
||||
speed = USB_SPEED_UNKNOWN;
|
||||
return speed_names[speed];
|
||||
}
|
||||
|
||||
enum usb_device_speed usb_get_maximum_speed(ofnode node)
|
||||
{
|
||||
const char *max_speed;
|
||||
|
|
|
@ -421,6 +421,9 @@ static void process_ep_out_intr(struct dwc2_udc *dev)
|
|||
{
|
||||
u32 ep_intr, ep_intr_status;
|
||||
u8 ep_num = 0;
|
||||
u32 ep_tsr = 0, xfer_size = 0;
|
||||
u32 epsiz_reg = reg->out_endp[ep_num].doeptsiz;
|
||||
u32 req_size = sizeof(struct usb_ctrlrequest);
|
||||
|
||||
ep_intr = readl(®->daint);
|
||||
debug_cond(DEBUG_OUT_EP != 0,
|
||||
|
@ -441,10 +444,17 @@ static void process_ep_out_intr(struct dwc2_udc *dev)
|
|||
|
||||
if (ep_num == 0) {
|
||||
if (ep_intr_status & TRANSFER_DONE) {
|
||||
if (dev->ep0state !=
|
||||
WAIT_FOR_OUT_COMPLETE)
|
||||
ep_tsr = readl(&epsiz_reg);
|
||||
xfer_size = ep_tsr &
|
||||
DOEPT_SIZ_XFER_SIZE_MAX_EP0;
|
||||
|
||||
if (xfer_size == req_size &&
|
||||
dev->ep0state == WAIT_FOR_SETUP) {
|
||||
dwc2_udc_pre_setup();
|
||||
} else if (dev->ep0state !=
|
||||
WAIT_FOR_OUT_COMPLETE) {
|
||||
complete_rx(dev, ep_num);
|
||||
else {
|
||||
} else {
|
||||
dev->ep0state = WAIT_FOR_SETUP;
|
||||
dwc2_udc_pre_setup();
|
||||
}
|
||||
|
|
|
@ -161,6 +161,12 @@
|
|||
#define gadget_is_max3420(g) 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_MTU3_GADGET
|
||||
#define gadget_is_mtu3(g) (!strcmp("mtu3-gadget", (g)->name))
|
||||
#else
|
||||
#define gadget_is_mtu3(g) 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* usb_gadget_controller_number - support bcdDevice id convention
|
||||
* @gadget: the controller being driven
|
||||
|
@ -224,5 +230,7 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
|
|||
return 0x24;
|
||||
else if (gadget_is_max3420(gadget))
|
||||
return 0x25;
|
||||
else if (gadget_is_mtu3(gadget))
|
||||
return 0x26;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
|
|
@ -279,10 +279,10 @@ static struct xhci_segment *xhci_segment_alloc(void)
|
|||
{
|
||||
struct xhci_segment *seg;
|
||||
|
||||
seg = (struct xhci_segment *)malloc(sizeof(struct xhci_segment));
|
||||
seg = malloc(sizeof(struct xhci_segment));
|
||||
BUG_ON(!seg);
|
||||
|
||||
seg->trbs = (union xhci_trb *)xhci_malloc(SEGMENT_SIZE);
|
||||
seg->trbs = xhci_malloc(SEGMENT_SIZE);
|
||||
|
||||
seg->next = NULL;
|
||||
|
||||
|
@ -309,7 +309,7 @@ struct xhci_ring *xhci_ring_alloc(unsigned int num_segs, bool link_trbs)
|
|||
struct xhci_ring *ring;
|
||||
struct xhci_segment *prev;
|
||||
|
||||
ring = (struct xhci_ring *)malloc(sizeof(struct xhci_ring));
|
||||
ring = malloc(sizeof(struct xhci_ring));
|
||||
BUG_ON(!ring);
|
||||
|
||||
if (num_segs == 0)
|
||||
|
@ -425,8 +425,7 @@ static struct xhci_container_ctx
|
|||
{
|
||||
struct xhci_container_ctx *ctx;
|
||||
|
||||
ctx = (struct xhci_container_ctx *)
|
||||
malloc(sizeof(struct xhci_container_ctx));
|
||||
ctx = malloc(sizeof(struct xhci_container_ctx));
|
||||
BUG_ON(!ctx);
|
||||
|
||||
BUG_ON((type != XHCI_CTX_TYPE_DEVICE) && (type != XHCI_CTX_TYPE_INPUT));
|
||||
|
@ -436,7 +435,7 @@ static struct xhci_container_ctx
|
|||
if (type == XHCI_CTX_TYPE_INPUT)
|
||||
ctx->size += CTX_SIZE(readl(&ctrl->hccr->cr_hccparams));
|
||||
|
||||
ctx->bytes = (u8 *)xhci_malloc(ctx->size);
|
||||
ctx->bytes = xhci_malloc(ctx->size);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
@ -458,8 +457,7 @@ int xhci_alloc_virt_device(struct xhci_ctrl *ctrl, unsigned int slot_id)
|
|||
return -EEXIST;
|
||||
}
|
||||
|
||||
ctrl->devs[slot_id] = (struct xhci_virt_device *)
|
||||
malloc(sizeof(struct xhci_virt_device));
|
||||
ctrl->devs[slot_id] = malloc(sizeof(struct xhci_virt_device));
|
||||
|
||||
if (!ctrl->devs[slot_id]) {
|
||||
puts("Failed to allocate virtual device\n");
|
||||
|
@ -518,8 +516,7 @@ int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr,
|
|||
struct xhci_segment *seg;
|
||||
|
||||
/* DCBAA initialization */
|
||||
ctrl->dcbaa = (struct xhci_device_context_array *)
|
||||
xhci_malloc(sizeof(struct xhci_device_context_array));
|
||||
ctrl->dcbaa = xhci_malloc(sizeof(struct xhci_device_context_array));
|
||||
if (ctrl->dcbaa == NULL) {
|
||||
puts("unable to allocate DCBA\n");
|
||||
return -ENOMEM;
|
||||
|
@ -555,8 +552,8 @@ int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr,
|
|||
|
||||
/* Event ring does not maintain link TRB */
|
||||
ctrl->event_ring = xhci_ring_alloc(ERST_NUM_SEGS, false);
|
||||
ctrl->erst.entries = (struct xhci_erst_entry *)
|
||||
xhci_malloc(sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS);
|
||||
ctrl->erst.entries = xhci_malloc(sizeof(struct xhci_erst_entry) *
|
||||
ERST_NUM_SEGS);
|
||||
|
||||
ctrl->erst.num_entries = ERST_NUM_SEGS;
|
||||
|
||||
|
|
44
drivers/usb/mtu3/Kconfig
Normal file
44
drivers/usb/mtu3/Kconfig
Normal file
|
@ -0,0 +1,44 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# For MTK USB3.0 IP
|
||||
|
||||
config USB_MTU3
|
||||
bool "MediaTek USB3 Dual Role controller"
|
||||
depends on USB_HOST || USB_GADGET
|
||||
depends on ARCH_MEDIATEK
|
||||
help
|
||||
Say Y here if your system runs on MediaTek SoCs with
|
||||
Dual Role SuperSpeed USB controller. You can select usb
|
||||
mode as peripheral role or host role.
|
||||
|
||||
If you don't know what this is, please say N.
|
||||
|
||||
if USB_MTU3
|
||||
choice
|
||||
bool "MTU3 Mode Selection"
|
||||
default USB_MTU3_GADGET if USB_GADGET
|
||||
default USB_MTU3_HOST if (USB_HOST && !USB_GADGET)
|
||||
|
||||
config USB_MTU3_HOST
|
||||
bool "Host only mode"
|
||||
depends on USB_XHCI_HCD
|
||||
help
|
||||
Select this when you want to use MTU3 in host mode only,
|
||||
thereby the gadget feature will be regressed.
|
||||
|
||||
config USB_MTU3_GADGET
|
||||
bool "Gadget only mode"
|
||||
depends on USB_GADGET
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
Select this when you want to use MTU3 in gadget mode only,
|
||||
thereby the host feature will be regressed.
|
||||
|
||||
endchoice
|
||||
|
||||
config USB_MTU3_DEBUG
|
||||
bool "Enable Debugging Messages"
|
||||
help
|
||||
Say Y here to enable debugging messages in the MTU3 Driver.
|
||||
|
||||
endif
|
11
drivers/usb/mtu3/Makefile
Normal file
11
drivers/usb/mtu3/Makefile
Normal file
|
@ -0,0 +1,11 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
ccflags-$(CONFIG_USB_MTU3_DEBUG) += -DDEBUG
|
||||
|
||||
obj-$(CONFIG_USB_MTU3) += mtu3.o
|
||||
|
||||
mtu3-y := mtu3_plat.o
|
||||
|
||||
obj-$(CONFIG_USB_MTU3_GADGET) += mtu3_core.o mtu3_gadget_ep0.o mtu3_gadget.o
|
||||
obj-$(CONFIG_USB_MTU3_GADGET) += mtu3_qmu.o
|
||||
obj-$(CONFIG_USB_MTU3_HOST) += mtu3_host.o
|
424
drivers/usb/mtu3/mtu3.h
Normal file
424
drivers/usb/mtu3/mtu3.h
Normal file
|
@ -0,0 +1,424 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* mtu3.h - MediaTek USB3 DRD header
|
||||
*
|
||||
* Copyright (C) 2016 MediaTek Inc.
|
||||
*
|
||||
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
|
||||
*/
|
||||
|
||||
#ifndef __MTU3_H__
|
||||
#define __MTU3_H__
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <dm/device_compat.h>
|
||||
#include <dm/devres.h>
|
||||
#include <generic-phy.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <power/regulator.h>
|
||||
#include <usb/xhci.h>
|
||||
|
||||
struct mtu3;
|
||||
struct mtu3_ep;
|
||||
struct mtu3_request;
|
||||
struct mtu3_host;
|
||||
|
||||
#include "mtu3_hw_regs.h"
|
||||
#include "mtu3_qmu.h"
|
||||
|
||||
#define MU3D_EP_TXCR0(epnum) (U3D_TX1CSR0 + (((epnum) - 1) * 0x10))
|
||||
#define MU3D_EP_TXCR1(epnum) (U3D_TX1CSR1 + (((epnum) - 1) * 0x10))
|
||||
#define MU3D_EP_TXCR2(epnum) (U3D_TX1CSR2 + (((epnum) - 1) * 0x10))
|
||||
|
||||
#define MU3D_EP_RXCR0(epnum) (U3D_RX1CSR0 + (((epnum) - 1) * 0x10))
|
||||
#define MU3D_EP_RXCR1(epnum) (U3D_RX1CSR1 + (((epnum) - 1) * 0x10))
|
||||
#define MU3D_EP_RXCR2(epnum) (U3D_RX1CSR2 + (((epnum) - 1) * 0x10))
|
||||
|
||||
#define USB_QMU_RQCSR(epnum) (U3D_RXQCSR1 + (((epnum) - 1) * 0x10))
|
||||
#define USB_QMU_RQSAR(epnum) (U3D_RXQSAR1 + (((epnum) - 1) * 0x10))
|
||||
#define USB_QMU_RQCPR(epnum) (U3D_RXQCPR1 + (((epnum) - 1) * 0x10))
|
||||
|
||||
#define USB_QMU_TQCSR(epnum) (U3D_TXQCSR1 + (((epnum) - 1) * 0x10))
|
||||
#define USB_QMU_TQSAR(epnum) (U3D_TXQSAR1 + (((epnum) - 1) * 0x10))
|
||||
#define USB_QMU_TQCPR(epnum) (U3D_TXQCPR1 + (((epnum) - 1) * 0x10))
|
||||
|
||||
#define SSUSB_U3_CTRL(p) (U3D_SSUSB_U3_CTRL_0P + ((p) * 0x08))
|
||||
#define SSUSB_U2_CTRL(p) (U3D_SSUSB_U2_CTRL_0P + ((p) * 0x08))
|
||||
|
||||
#define MTU3_DRIVER_NAME "mtu3-gadget"
|
||||
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
|
||||
|
||||
#define MTU3_EP_ENABLED BIT(0)
|
||||
#define MTU3_EP_STALL BIT(1)
|
||||
#define MTU3_EP_WEDGE BIT(2)
|
||||
#define MTU3_EP_BUSY BIT(3)
|
||||
|
||||
/* should be set as 1 */
|
||||
#define MTU3_U2_IP_SLOT_DEFAULT 1
|
||||
#define MTU3_U3_IP_SLOT_DEFAULT (MTU3_U2_IP_SLOT_DEFAULT)
|
||||
|
||||
/**
|
||||
* IP TRUNK version
|
||||
* from 0x1003 version, USB3 Gen2 is supported, two changes affect driver:
|
||||
* 1. MAXPKT and MULTI bits layout of TXCSR1 and RXCSR1 are adjusted,
|
||||
* but not backward compatible
|
||||
* 2. QMU extend buffer length supported
|
||||
*/
|
||||
#define MTU3_TRUNK_VERS_1003 0x1003
|
||||
|
||||
/**
|
||||
* Normally the device works on HS or SS, to simplify fifo management,
|
||||
* devide fifo into some 2*maxp parts, use bitmap to manage it; And
|
||||
* 32 bits size of bitmap is large enough, that means it can manage
|
||||
* up to 32KB/64KB fifo size.
|
||||
* NOTE: MTU3_U2/3IP_EP_FIFO_UNIT should be power of two;
|
||||
* FIFO size is allocated according to @slot which is 1 by default
|
||||
*/
|
||||
#define USB_HS_MAXP 512
|
||||
#define USB_SS_MAXP 1024
|
||||
#define MTU3_U2IP_EP_FIFO_UNIT \
|
||||
((USB_HS_MAXP) * ((MTU3_U2_IP_SLOT_DEFAULT) + 1))
|
||||
#define MTU3_U3IP_EP_FIFO_UNIT \
|
||||
((USB_SS_MAXP) * ((MTU3_U3_IP_SLOT_DEFAULT) + 1))
|
||||
|
||||
#define MTU3_FIFO_BIT_SIZE 32
|
||||
#define MTU3_U2_IP_EP0_FIFO_SIZE 64
|
||||
|
||||
/**
|
||||
* Maximum size of ep0 response buffer for ch9 requests,
|
||||
* the SET_SEL request uses 6 so far, and GET_STATUS is 2
|
||||
*/
|
||||
#define EP0_RESPONSE_BUF 6
|
||||
|
||||
/* device operated link and speed got from DEVICE_CONF register */
|
||||
enum mtu3_speed {
|
||||
MTU3_SPEED_INACTIVE = 0,
|
||||
MTU3_SPEED_FULL = 1,
|
||||
MTU3_SPEED_HIGH = 3,
|
||||
MTU3_SPEED_SUPER = 4,
|
||||
MTU3_SPEED_SUPER_PLUS = 5,
|
||||
};
|
||||
|
||||
/**
|
||||
* @MU3D_EP0_STATE_SETUP: waits for SETUP or received a SETUP
|
||||
* without data stage.
|
||||
* @MU3D_EP0_STATE_TX: IN data stage
|
||||
* @MU3D_EP0_STATE_RX: OUT data stage
|
||||
* @MU3D_EP0_STATE_TX_END: the last IN data is transferred, and
|
||||
* waits for its completion interrupt
|
||||
* @MU3D_EP0_STATE_STALL: ep0 is in stall status, will be auto-cleared
|
||||
* after receives a SETUP.
|
||||
*/
|
||||
enum mtu3_g_ep0_state {
|
||||
MU3D_EP0_STATE_SETUP = 1,
|
||||
MU3D_EP0_STATE_TX,
|
||||
MU3D_EP0_STATE_RX,
|
||||
MU3D_EP0_STATE_TX_END,
|
||||
MU3D_EP0_STATE_STALL,
|
||||
};
|
||||
|
||||
/**
|
||||
* MTU3_DR_FORCE_NONE: automatically switch host and peripheral mode
|
||||
* by IDPIN signal.
|
||||
* MTU3_DR_FORCE_HOST: force to enter host mode and override OTG
|
||||
* IDPIN signal.
|
||||
* MTU3_DR_FORCE_DEVICE: force to enter peripheral mode.
|
||||
*/
|
||||
enum mtu3_dr_force_mode {
|
||||
MTU3_DR_FORCE_NONE = 0,
|
||||
MTU3_DR_FORCE_HOST,
|
||||
MTU3_DR_FORCE_DEVICE,
|
||||
};
|
||||
|
||||
/**
|
||||
* @mac_base: register base address of MAC, include xHCI and device
|
||||
* @ippc_base: register base address of IP Power and Clock interface (IPPC)
|
||||
* @vusb33_supply: usb3.3V shared by device/host IP
|
||||
* @vbus_supply: vbus 5v of OTG port
|
||||
* @clks: optional clocks, include "sys_ck", "ref_ck", "mcu_ck",
|
||||
* "dma_ck" and "xhci_ck"
|
||||
* @phys: phys used
|
||||
* @dr_mode: works in which mode:
|
||||
* host only, device only or dual-role mode
|
||||
*/
|
||||
struct ssusb_mtk {
|
||||
struct udevice *dev;
|
||||
struct mtu3 *u3d;
|
||||
struct mtu3_host *u3h;
|
||||
void __iomem *mac_base;
|
||||
void __iomem *ippc_base;
|
||||
/* common power & clock */
|
||||
struct udevice *vusb33_supply;
|
||||
struct udevice *vbus_supply;
|
||||
struct clk_bulk clks;
|
||||
struct phy_bulk phys;
|
||||
/* otg */
|
||||
enum usb_dr_mode dr_mode;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ctrl: xHCI controller, needs to come first in this struct!
|
||||
* @hcd: xHCI's register base address
|
||||
* @u2_ports: number of usb2 host ports
|
||||
* @u3_ports: number of usb3 host ports
|
||||
* @u3p_dis_msk: mask of disabling usb3 ports, for example, bit0==1 to
|
||||
* disable u3port0, bit1==1 to disable u3port1,... etc
|
||||
*/
|
||||
struct mtu3_host {
|
||||
struct xhci_ctrl ctrl;
|
||||
struct xhci_hccr *hcd;
|
||||
void __iomem *ippc_base;
|
||||
struct ssusb_mtk *ssusb;
|
||||
struct udevice *dev;
|
||||
u32 u2_ports;
|
||||
u32 u3_ports;
|
||||
u32 u3p_dis_msk;
|
||||
};
|
||||
|
||||
/**
|
||||
* @base: the base address of fifo
|
||||
* @limit: the bitmap size in bits
|
||||
* @bitmap: fifo bitmap in unit of @MTU3_EP_FIFO_UNIT
|
||||
*/
|
||||
struct mtu3_fifo_info {
|
||||
u32 base;
|
||||
u32 limit;
|
||||
DECLARE_BITMAP(bitmap, MTU3_FIFO_BIT_SIZE);
|
||||
};
|
||||
|
||||
/**
|
||||
* General Purpose Descriptor (GPD):
|
||||
* The format of TX GPD is a little different from RX one.
|
||||
* And the size of GPD is 16 bytes.
|
||||
*
|
||||
* @flag:
|
||||
* bit0: Hardware Own (HWO)
|
||||
* bit1: Buffer Descriptor Present (BDP), always 0, BD is not supported
|
||||
* bit2: Bypass (BPS), 1: HW skips this GPD if HWO = 1
|
||||
* bit7: Interrupt On Completion (IOC)
|
||||
* @chksum: This is used to validate the contents of this GPD;
|
||||
* If TXQ_CS_EN / RXQ_CS_EN bit is set, an interrupt is issued
|
||||
* when checksum validation fails;
|
||||
* Checksum value is calculated over the 16 bytes of the GPD by default;
|
||||
* @data_buf_len (RX ONLY): This value indicates the length of
|
||||
* the assigned data buffer
|
||||
* @next_gpd: Physical address of the next GPD
|
||||
* @buffer: Physical address of the data buffer
|
||||
* @buf_len:
|
||||
* (TX): This value indicates the length of the assigned data buffer
|
||||
* (RX): The total length of data received
|
||||
* @ext_len: reserved
|
||||
* @ext_flag:
|
||||
* bit5 (TX ONLY): Zero Length Packet (ZLP),
|
||||
*/
|
||||
struct qmu_gpd {
|
||||
__u8 flag;
|
||||
__u8 chksum;
|
||||
__le16 data_buf_len;
|
||||
__le32 next_gpd;
|
||||
__le32 buffer;
|
||||
__le16 buf_len;
|
||||
__u8 ext_len;
|
||||
__u8 ext_flag;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* dma: physical base address of GPD segment
|
||||
* start: virtual base address of GPD segment
|
||||
* end: the last GPD element
|
||||
* enqueue: the first empty GPD to use
|
||||
* dequeue: the first completed GPD serviced by ISR
|
||||
* NOTE: the size of GPD ring should be >= 2
|
||||
*/
|
||||
struct mtu3_gpd_ring {
|
||||
dma_addr_t dma;
|
||||
struct qmu_gpd *start;
|
||||
struct qmu_gpd *end;
|
||||
struct qmu_gpd *enqueue;
|
||||
struct qmu_gpd *dequeue;
|
||||
};
|
||||
|
||||
/**
|
||||
* @fifo_size: it is (@slot + 1) * @fifo_seg_size
|
||||
* @fifo_seg_size: it is roundup_pow_of_two(@maxp)
|
||||
*/
|
||||
struct mtu3_ep {
|
||||
struct usb_ep ep;
|
||||
char name[12];
|
||||
struct mtu3 *mtu;
|
||||
u8 epnum;
|
||||
u8 type;
|
||||
u8 is_in;
|
||||
u16 maxp;
|
||||
int slot;
|
||||
u32 fifo_size;
|
||||
u32 fifo_addr;
|
||||
u32 fifo_seg_size;
|
||||
struct mtu3_fifo_info *fifo;
|
||||
|
||||
struct list_head req_list;
|
||||
struct mtu3_gpd_ring gpd_ring;
|
||||
const struct usb_ss_ep_comp_descriptor *comp_desc;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
|
||||
int flags;
|
||||
};
|
||||
|
||||
struct mtu3_request {
|
||||
struct usb_request request;
|
||||
struct list_head list;
|
||||
struct mtu3_ep *mep;
|
||||
struct mtu3 *mtu;
|
||||
struct qmu_gpd *gpd;
|
||||
int epnum;
|
||||
};
|
||||
|
||||
static inline struct ssusb_mtk *dev_to_ssusb(struct udevice *dev)
|
||||
{
|
||||
return dev_get_priv(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* struct mtu3 - device driver instance data.
|
||||
* @slot: MTU3_U2_IP_SLOT_DEFAULT for U2 IP only,
|
||||
* MTU3_U3_IP_SLOT_DEFAULT for U3 IP
|
||||
* @may_wakeup: means device's remote wakeup is enabled
|
||||
* @is_self_powered: is reported in device status and the config descriptor
|
||||
* @delayed_status: true when function drivers ask for delayed status
|
||||
* @gen2cp: compatible with USB3 Gen2 IP
|
||||
* @ep0_req: dummy request used while handling standard USB requests
|
||||
* for GET_STATUS and SET_SEL
|
||||
* @setup_buf: ep0 response buffer for GET_STATUS and SET_SEL requests
|
||||
*/
|
||||
struct mtu3 {
|
||||
spinlock_t lock;
|
||||
struct ssusb_mtk *ssusb;
|
||||
struct udevice *dev;
|
||||
void __iomem *mac_base;
|
||||
void __iomem *ippc_base;
|
||||
int irq;
|
||||
|
||||
struct mtu3_fifo_info tx_fifo;
|
||||
struct mtu3_fifo_info rx_fifo;
|
||||
|
||||
struct mtu3_ep *ep_array;
|
||||
struct mtu3_ep *in_eps;
|
||||
struct mtu3_ep *out_eps;
|
||||
struct mtu3_ep *ep0;
|
||||
int num_eps;
|
||||
int slot;
|
||||
int active_ep;
|
||||
|
||||
enum mtu3_g_ep0_state ep0_state;
|
||||
struct usb_gadget g; /* the gadget */
|
||||
struct usb_gadget_driver *gadget_driver;
|
||||
struct mtu3_request ep0_req;
|
||||
u8 setup_buf[EP0_RESPONSE_BUF];
|
||||
enum usb_device_speed max_speed;
|
||||
enum usb_device_speed speed;
|
||||
|
||||
unsigned is_active:1;
|
||||
unsigned may_wakeup:1;
|
||||
unsigned is_self_powered:1;
|
||||
unsigned test_mode:1;
|
||||
unsigned softconnect:1;
|
||||
unsigned u1_enable:1;
|
||||
unsigned u2_enable:1;
|
||||
unsigned is_u3_ip:1;
|
||||
unsigned delayed_status:1;
|
||||
unsigned gen2cp:1;
|
||||
unsigned force_vbus:1;
|
||||
|
||||
u8 address;
|
||||
u8 test_mode_nr;
|
||||
u32 hw_version;
|
||||
};
|
||||
|
||||
static inline struct mtu3 *gadget_to_mtu3(struct usb_gadget *g)
|
||||
{
|
||||
return container_of(g, struct mtu3, g);
|
||||
}
|
||||
|
||||
static inline int is_first_entry(const struct list_head *list,
|
||||
const struct list_head *head)
|
||||
{
|
||||
return list_is_last(head, list);
|
||||
}
|
||||
|
||||
static inline struct mtu3_request *to_mtu3_request(struct usb_request *req)
|
||||
{
|
||||
return req ? container_of(req, struct mtu3_request, request) : NULL;
|
||||
}
|
||||
|
||||
static inline struct mtu3_ep *to_mtu3_ep(struct usb_ep *ep)
|
||||
{
|
||||
return ep ? container_of(ep, struct mtu3_ep, ep) : NULL;
|
||||
}
|
||||
|
||||
static inline struct mtu3_request *next_request(struct mtu3_ep *mep)
|
||||
{
|
||||
if (list_empty(&mep->req_list))
|
||||
return NULL;
|
||||
|
||||
return list_first_entry(&mep->req_list, struct mtu3_request, list);
|
||||
}
|
||||
|
||||
static inline void mtu3_writel(void __iomem *base, u32 offset, u32 data)
|
||||
{
|
||||
writel(data, base + offset);
|
||||
}
|
||||
|
||||
static inline u32 mtu3_readl(void __iomem *base, u32 offset)
|
||||
{
|
||||
return readl(base + offset);
|
||||
}
|
||||
|
||||
static inline void mtu3_setbits(void __iomem *base, u32 offset, u32 bits)
|
||||
{
|
||||
void __iomem *addr = base + offset;
|
||||
u32 tmp = readl(addr);
|
||||
|
||||
writel((tmp | (bits)), addr);
|
||||
}
|
||||
|
||||
static inline void mtu3_clrbits(void __iomem *base, u32 offset, u32 bits)
|
||||
{
|
||||
void __iomem *addr = base + offset;
|
||||
u32 tmp = readl(addr);
|
||||
|
||||
writel((tmp & ~(bits)), addr);
|
||||
}
|
||||
|
||||
int ssusb_check_clocks(struct ssusb_mtk *ssusb, u32 ex_clks);
|
||||
struct usb_request *mtu3_alloc_request(struct usb_ep *ep, gfp_t gfp_flags);
|
||||
void mtu3_free_request(struct usb_ep *ep, struct usb_request *req);
|
||||
void mtu3_req_complete(struct mtu3_ep *mep,
|
||||
struct usb_request *req, int status);
|
||||
|
||||
int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
|
||||
int interval, int burst, int mult);
|
||||
void mtu3_deconfig_ep(struct mtu3 *mtu, struct mtu3_ep *mep);
|
||||
void mtu3_ep_stall_set(struct mtu3_ep *mep, bool set);
|
||||
void mtu3_ep0_setup(struct mtu3 *mtu);
|
||||
void mtu3_start(struct mtu3 *mtu);
|
||||
void mtu3_stop(struct mtu3 *mtu);
|
||||
void mtu3_dev_on_off(struct mtu3 *mtu, int is_on);
|
||||
void mtu3_set_speed(struct mtu3 *mtu, enum usb_device_speed speed);
|
||||
|
||||
int mtu3_gadget_setup(struct mtu3 *mtu);
|
||||
void mtu3_gadget_cleanup(struct mtu3 *mtu);
|
||||
void mtu3_gadget_reset(struct mtu3 *mtu);
|
||||
void mtu3_gadget_suspend(struct mtu3 *mtu);
|
||||
void mtu3_gadget_resume(struct mtu3 *mtu);
|
||||
void mtu3_gadget_disconnect(struct mtu3 *mtu);
|
||||
|
||||
irqreturn_t mtu3_ep0_isr(struct mtu3 *mtu);
|
||||
extern const struct usb_ep_ops mtu3_ep0_ops;
|
||||
|
||||
#endif
|
838
drivers/usb/mtu3/mtu3_core.c
Normal file
838
drivers/usb/mtu3/mtu3_core.c
Normal file
|
@ -0,0 +1,838 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* mtu3_core.c - hardware access layer and gadget init/exit of
|
||||
* MediaTek usb3 Dual-Role Controller Driver
|
||||
*
|
||||
* Copyright (C) 2016 MediaTek Inc.
|
||||
*
|
||||
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
|
||||
*/
|
||||
|
||||
#include <linux/log2.h>
|
||||
#include <linux/bitmap.h>
|
||||
|
||||
#include "mtu3.h"
|
||||
#include "mtu3_dr.h"
|
||||
|
||||
static int ep_fifo_alloc(struct mtu3_ep *mep, u32 seg_size)
|
||||
{
|
||||
struct mtu3_fifo_info *fifo = mep->fifo;
|
||||
struct mtu3 *mtu = mep->mtu;
|
||||
u32 fz_bit;
|
||||
|
||||
mep->fifo_seg_size = mtu->is_u3_ip ? USB_SS_MAXP : USB_HS_MAXP;
|
||||
|
||||
fz_bit = find_first_zero_bit(fifo->bitmap, fifo->limit);
|
||||
if (fz_bit >= fifo->limit)
|
||||
return -EOVERFLOW;
|
||||
|
||||
mep->fifo_size = mep->fifo_seg_size * (mep->slot + 1);
|
||||
mep->fifo_addr = fifo->base + mep->fifo_size * fz_bit;
|
||||
generic_set_bit(fz_bit, fifo->bitmap);
|
||||
|
||||
dev_dbg(mep->mtu->dev, "%s fifo:%#x/%#x, bit: %d\n",
|
||||
__func__, mep->fifo_seg_size, mep->fifo_size, fz_bit);
|
||||
|
||||
return mep->fifo_addr;
|
||||
}
|
||||
|
||||
static void ep_fifo_free(struct mtu3_ep *mep)
|
||||
{
|
||||
struct mtu3_fifo_info *fifo = mep->fifo;
|
||||
u32 addr = mep->fifo_addr;
|
||||
u32 bit;
|
||||
|
||||
if (unlikely(addr < fifo->base))
|
||||
return;
|
||||
|
||||
bit = (addr - fifo->base) / mep->fifo_size;
|
||||
generic_clear_bit(bit, fifo->bitmap);
|
||||
mep->fifo_size = 0;
|
||||
mep->fifo_seg_size = 0;
|
||||
|
||||
dev_dbg(mep->mtu->dev, "%s size:%#x/%#x, bit: %d\n",
|
||||
__func__, mep->fifo_seg_size, mep->fifo_size, bit);
|
||||
}
|
||||
|
||||
/* enable/disable U3D SS function */
|
||||
static inline void mtu3_ss_func_set(struct mtu3 *mtu, bool enable)
|
||||
{
|
||||
/* If usb3_en==0, LTSSM will go to SS.Disable state */
|
||||
if (enable)
|
||||
mtu3_setbits(mtu->mac_base, U3D_USB3_CONFIG, USB3_EN);
|
||||
else
|
||||
mtu3_clrbits(mtu->mac_base, U3D_USB3_CONFIG, USB3_EN);
|
||||
|
||||
dev_dbg(mtu->dev, "USB3_EN = %d\n", !!enable);
|
||||
}
|
||||
|
||||
/* set/clear U3D HS device soft connect */
|
||||
static inline void mtu3_hs_softconn_set(struct mtu3 *mtu, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
mtu3_setbits(mtu->mac_base, U3D_POWER_MANAGEMENT,
|
||||
SOFT_CONN | SUSPENDM_ENABLE);
|
||||
} else {
|
||||
mtu3_clrbits(mtu->mac_base, U3D_POWER_MANAGEMENT,
|
||||
SOFT_CONN | SUSPENDM_ENABLE);
|
||||
}
|
||||
dev_dbg(mtu->dev, "SOFTCONN = %d\n", !!enable);
|
||||
}
|
||||
|
||||
/* only port0 of U2/U3 supports device mode */
|
||||
static int mtu3_device_enable(struct mtu3 *mtu)
|
||||
{
|
||||
void __iomem *ibase = mtu->ippc_base;
|
||||
u32 check_clk = 0;
|
||||
|
||||
mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
|
||||
|
||||
if (mtu->is_u3_ip) {
|
||||
check_clk = SSUSB_U3_MAC_RST_B_STS;
|
||||
mtu3_clrbits(ibase, SSUSB_U3_CTRL(0),
|
||||
(SSUSB_U3_PORT_DIS | SSUSB_U3_PORT_PDN |
|
||||
SSUSB_U3_PORT_HOST_SEL));
|
||||
}
|
||||
mtu3_clrbits(ibase, SSUSB_U2_CTRL(0),
|
||||
(SSUSB_U2_PORT_DIS | SSUSB_U2_PORT_PDN |
|
||||
SSUSB_U2_PORT_HOST_SEL));
|
||||
|
||||
return ssusb_check_clocks(mtu->ssusb, check_clk);
|
||||
}
|
||||
|
||||
static void mtu3_device_disable(struct mtu3 *mtu)
|
||||
{
|
||||
void __iomem *ibase = mtu->ippc_base;
|
||||
|
||||
if (mtu->is_u3_ip)
|
||||
mtu3_setbits(ibase, SSUSB_U3_CTRL(0),
|
||||
(SSUSB_U3_PORT_DIS | SSUSB_U3_PORT_PDN));
|
||||
|
||||
mtu3_setbits(ibase, SSUSB_U2_CTRL(0),
|
||||
SSUSB_U2_PORT_DIS | SSUSB_U2_PORT_PDN);
|
||||
|
||||
mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
|
||||
}
|
||||
|
||||
/* reset U3D's device module. */
|
||||
static void mtu3_device_reset(struct mtu3 *mtu)
|
||||
{
|
||||
void __iomem *ibase = mtu->ippc_base;
|
||||
|
||||
mtu3_setbits(ibase, U3D_SSUSB_DEV_RST_CTRL, SSUSB_DEV_SW_RST);
|
||||
udelay(1);
|
||||
mtu3_clrbits(ibase, U3D_SSUSB_DEV_RST_CTRL, SSUSB_DEV_SW_RST);
|
||||
}
|
||||
|
||||
static void mtu3_intr_status_clear(struct mtu3 *mtu)
|
||||
{
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
|
||||
/* Clear EP0 and Tx/Rx EPn interrupts status */
|
||||
mtu3_writel(mbase, U3D_EPISR, ~0x0);
|
||||
/* Clear U2 USB common interrupts status */
|
||||
mtu3_writel(mbase, U3D_COMMON_USB_INTR, ~0x0);
|
||||
/* Clear U3 LTSSM interrupts status */
|
||||
mtu3_writel(mbase, U3D_LTSSM_INTR, ~0x0);
|
||||
/* Clear speed change interrupt status */
|
||||
mtu3_writel(mbase, U3D_DEV_LINK_INTR, ~0x0);
|
||||
/* Clear QMU interrupt status */
|
||||
mtu3_writel(mbase, U3D_QISAR0, ~0x0);
|
||||
}
|
||||
|
||||
/* disable all interrupts */
|
||||
static void mtu3_intr_disable(struct mtu3 *mtu)
|
||||
{
|
||||
/* Disable level 1 interrupts */
|
||||
mtu3_writel(mtu->mac_base, U3D_LV1IECR, ~0x0);
|
||||
/* Disable endpoint interrupts */
|
||||
mtu3_writel(mtu->mac_base, U3D_EPIECR, ~0x0);
|
||||
mtu3_intr_status_clear(mtu);
|
||||
}
|
||||
|
||||
/* enable system global interrupt */
|
||||
static void mtu3_intr_enable(struct mtu3 *mtu)
|
||||
{
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
u32 value;
|
||||
|
||||
/*Enable level 1 interrupts (BMU, QMU, MAC3, DMA, MAC2, EPCTL) */
|
||||
value = BMU_INTR | QMU_INTR | MAC3_INTR | MAC2_INTR | EP_CTRL_INTR;
|
||||
mtu3_writel(mbase, U3D_LV1IESR, value);
|
||||
|
||||
/* Enable U2 common USB interrupts */
|
||||
value = SUSPEND_INTR | RESUME_INTR | RESET_INTR;
|
||||
mtu3_writel(mbase, U3D_COMMON_USB_INTR_ENABLE, value);
|
||||
|
||||
if (mtu->is_u3_ip) {
|
||||
/* Enable U3 LTSSM interrupts */
|
||||
value = HOT_RST_INTR | WARM_RST_INTR |
|
||||
ENTER_U3_INTR | EXIT_U3_INTR;
|
||||
mtu3_writel(mbase, U3D_LTSSM_INTR_ENABLE, value);
|
||||
}
|
||||
|
||||
/* Enable QMU interrupts. */
|
||||
value = TXQ_CSERR_INT | TXQ_LENERR_INT | RXQ_CSERR_INT |
|
||||
RXQ_LENERR_INT | RXQ_ZLPERR_INT;
|
||||
mtu3_writel(mbase, U3D_QIESR1, value);
|
||||
|
||||
/* Enable speed change interrupt */
|
||||
mtu3_writel(mbase, U3D_DEV_LINK_INTR_ENABLE, SSUSB_DEV_SPEED_CHG_INTR);
|
||||
}
|
||||
|
||||
void mtu3_set_speed(struct mtu3 *mtu, enum usb_device_speed speed)
|
||||
{
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
|
||||
if (speed > mtu->max_speed)
|
||||
speed = mtu->max_speed;
|
||||
|
||||
switch (speed) {
|
||||
case USB_SPEED_FULL:
|
||||
/* disable U3 SS function */
|
||||
mtu3_clrbits(mbase, U3D_USB3_CONFIG, USB3_EN);
|
||||
/* disable HS function */
|
||||
mtu3_clrbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
mtu3_clrbits(mbase, U3D_USB3_CONFIG, USB3_EN);
|
||||
/* HS/FS detected by HW */
|
||||
mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
|
||||
break;
|
||||
case USB_SPEED_SUPER:
|
||||
mtu3_clrbits(mtu->ippc_base, SSUSB_U3_CTRL(0),
|
||||
SSUSB_U3_PORT_SSP_SPEED);
|
||||
break;
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
mtu3_setbits(mtu->ippc_base, SSUSB_U3_CTRL(0),
|
||||
SSUSB_U3_PORT_SSP_SPEED);
|
||||
break;
|
||||
default:
|
||||
dev_err(mtu->dev, "invalid speed: %d\n", speed);
|
||||
return;
|
||||
}
|
||||
|
||||
mtu->speed = speed;
|
||||
dev_dbg(mtu->dev, "set speed: %s\n", usb_speed_string(mtu->speed));
|
||||
}
|
||||
|
||||
/* reset: u2 - data toggle, u3 - SeqN, flow control status etc */
|
||||
static void mtu3_ep_reset(struct mtu3_ep *mep)
|
||||
{
|
||||
struct mtu3 *mtu = mep->mtu;
|
||||
u32 rst_bit = EP_RST(mep->is_in, mep->epnum);
|
||||
|
||||
mtu3_setbits(mtu->mac_base, U3D_EP_RST, rst_bit);
|
||||
mtu3_clrbits(mtu->mac_base, U3D_EP_RST, rst_bit);
|
||||
}
|
||||
|
||||
/* set/clear the stall and toggle bits for non-ep0 */
|
||||
void mtu3_ep_stall_set(struct mtu3_ep *mep, bool set)
|
||||
{
|
||||
struct mtu3 *mtu = mep->mtu;
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
u8 epnum = mep->epnum;
|
||||
u32 csr;
|
||||
|
||||
if (mep->is_in) { /* TX */
|
||||
csr = mtu3_readl(mbase, MU3D_EP_TXCR0(epnum)) & TX_W1C_BITS;
|
||||
if (set)
|
||||
csr |= TX_SENDSTALL;
|
||||
else
|
||||
csr = (csr & (~TX_SENDSTALL)) | TX_SENTSTALL;
|
||||
mtu3_writel(mbase, MU3D_EP_TXCR0(epnum), csr);
|
||||
} else { /* RX */
|
||||
csr = mtu3_readl(mbase, MU3D_EP_RXCR0(epnum)) & RX_W1C_BITS;
|
||||
if (set)
|
||||
csr |= RX_SENDSTALL;
|
||||
else
|
||||
csr = (csr & (~RX_SENDSTALL)) | RX_SENTSTALL;
|
||||
mtu3_writel(mbase, MU3D_EP_RXCR0(epnum), csr);
|
||||
}
|
||||
|
||||
if (!set) {
|
||||
mtu3_ep_reset(mep);
|
||||
mep->flags &= ~MTU3_EP_STALL;
|
||||
} else {
|
||||
mep->flags |= MTU3_EP_STALL;
|
||||
}
|
||||
|
||||
dev_dbg(mtu->dev, "%s: %s\n", mep->name,
|
||||
set ? "SEND STALL" : "CLEAR STALL, with EP RESET");
|
||||
}
|
||||
|
||||
void mtu3_dev_on_off(struct mtu3 *mtu, int is_on)
|
||||
{
|
||||
if (mtu->is_u3_ip && mtu->speed >= USB_SPEED_SUPER)
|
||||
mtu3_ss_func_set(mtu, is_on);
|
||||
else
|
||||
mtu3_hs_softconn_set(mtu, is_on);
|
||||
|
||||
dev_info(mtu->dev, "gadget (%s) pullup D%s\n",
|
||||
usb_speed_string(mtu->speed), is_on ? "+" : "-");
|
||||
}
|
||||
|
||||
void mtu3_start(struct mtu3 *mtu)
|
||||
{
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
|
||||
dev_dbg(mtu->dev, "%s devctl 0x%x\n", __func__,
|
||||
mtu3_readl(mbase, U3D_DEVICE_CONTROL));
|
||||
|
||||
/* Initialize the default interrupts */
|
||||
mtu3_intr_enable(mtu);
|
||||
mtu->is_active = 1;
|
||||
|
||||
if (mtu->softconnect)
|
||||
mtu3_dev_on_off(mtu, 1);
|
||||
}
|
||||
|
||||
void mtu3_stop(struct mtu3 *mtu)
|
||||
{
|
||||
dev_dbg(mtu->dev, "%s\n", __func__);
|
||||
|
||||
mtu3_intr_disable(mtu);
|
||||
|
||||
if (mtu->softconnect)
|
||||
mtu3_dev_on_off(mtu, 0);
|
||||
|
||||
mtu->is_active = 0;
|
||||
}
|
||||
|
||||
/* for non-ep0 */
|
||||
int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
|
||||
int interval, int burst, int mult)
|
||||
{
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
bool gen2cp = mtu->gen2cp;
|
||||
int epnum = mep->epnum;
|
||||
u32 csr0, csr1, csr2;
|
||||
int fifo_sgsz, fifo_addr;
|
||||
int num_pkts;
|
||||
|
||||
fifo_addr = ep_fifo_alloc(mep, mep->maxp);
|
||||
if (fifo_addr < 0) {
|
||||
dev_err(mtu->dev, "alloc ep fifo failed(%d)\n", mep->maxp);
|
||||
return -ENOMEM;
|
||||
}
|
||||
fifo_sgsz = ilog2(mep->fifo_seg_size);
|
||||
dev_dbg(mtu->dev, "%s fifosz: %x(%x/%x)\n", __func__, fifo_sgsz,
|
||||
mep->fifo_seg_size, mep->fifo_size);
|
||||
|
||||
if (mep->is_in) {
|
||||
csr0 = TX_TXMAXPKTSZ(mep->maxp);
|
||||
csr0 |= TX_DMAREQEN;
|
||||
|
||||
num_pkts = (burst + 1) * (mult + 1) - 1;
|
||||
csr1 = TX_SS_BURST(burst) | TX_SLOT(mep->slot);
|
||||
csr1 |= TX_MAX_PKT(gen2cp, num_pkts) | TX_MULT(gen2cp, mult);
|
||||
|
||||
csr2 = TX_FIFOADDR(fifo_addr >> 4);
|
||||
csr2 |= TX_FIFOSEGSIZE(fifo_sgsz);
|
||||
|
||||
switch (mep->type) {
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
csr1 |= TX_TYPE(TYPE_BULK);
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
csr1 |= TX_TYPE(TYPE_ISO);
|
||||
csr2 |= TX_BINTERVAL(interval);
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
csr1 |= TX_TYPE(TYPE_INT);
|
||||
csr2 |= TX_BINTERVAL(interval);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Enable QMU Done interrupt */
|
||||
mtu3_setbits(mbase, U3D_QIESR0, QMU_TX_DONE_INT(epnum));
|
||||
|
||||
mtu3_writel(mbase, MU3D_EP_TXCR0(epnum), csr0);
|
||||
mtu3_writel(mbase, MU3D_EP_TXCR1(epnum), csr1);
|
||||
mtu3_writel(mbase, MU3D_EP_TXCR2(epnum), csr2);
|
||||
|
||||
dev_dbg(mtu->dev, "U3D_TX%d CSR0:%#x, CSR1:%#x, CSR2:%#x\n",
|
||||
epnum, mtu3_readl(mbase, MU3D_EP_TXCR0(epnum)),
|
||||
mtu3_readl(mbase, MU3D_EP_TXCR1(epnum)),
|
||||
mtu3_readl(mbase, MU3D_EP_TXCR2(epnum)));
|
||||
} else {
|
||||
csr0 = RX_RXMAXPKTSZ(mep->maxp);
|
||||
csr0 |= RX_DMAREQEN;
|
||||
|
||||
num_pkts = (burst + 1) * (mult + 1) - 1;
|
||||
csr1 = RX_SS_BURST(burst) | RX_SLOT(mep->slot);
|
||||
csr1 |= RX_MAX_PKT(gen2cp, num_pkts) | RX_MULT(gen2cp, mult);
|
||||
|
||||
csr2 = RX_FIFOADDR(fifo_addr >> 4);
|
||||
csr2 |= RX_FIFOSEGSIZE(fifo_sgsz);
|
||||
|
||||
switch (mep->type) {
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
csr1 |= RX_TYPE(TYPE_BULK);
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
csr1 |= RX_TYPE(TYPE_ISO);
|
||||
csr2 |= RX_BINTERVAL(interval);
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
csr1 |= RX_TYPE(TYPE_INT);
|
||||
csr2 |= RX_BINTERVAL(interval);
|
||||
break;
|
||||
}
|
||||
|
||||
/*Enable QMU Done interrupt */
|
||||
mtu3_setbits(mbase, U3D_QIESR0, QMU_RX_DONE_INT(epnum));
|
||||
|
||||
mtu3_writel(mbase, MU3D_EP_RXCR0(epnum), csr0);
|
||||
mtu3_writel(mbase, MU3D_EP_RXCR1(epnum), csr1);
|
||||
mtu3_writel(mbase, MU3D_EP_RXCR2(epnum), csr2);
|
||||
|
||||
dev_dbg(mtu->dev, "U3D_RX%d CSR0:%#x, CSR1:%#x, CSR2:%#x\n",
|
||||
epnum, mtu3_readl(mbase, MU3D_EP_RXCR0(epnum)),
|
||||
mtu3_readl(mbase, MU3D_EP_RXCR1(epnum)),
|
||||
mtu3_readl(mbase, MU3D_EP_RXCR2(epnum)));
|
||||
}
|
||||
|
||||
dev_dbg(mtu->dev, "csr0:%#x, csr1:%#x, csr2:%#x\n", csr0, csr1, csr2);
|
||||
dev_dbg(mtu->dev, "%s: %s, fifo-addr:%#x, fifo-size:%#x(%#x/%#x)\n",
|
||||
__func__, mep->name, mep->fifo_addr, mep->fifo_size,
|
||||
fifo_sgsz, mep->fifo_seg_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* for non-ep0 */
|
||||
void mtu3_deconfig_ep(struct mtu3 *mtu, struct mtu3_ep *mep)
|
||||
{
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
int epnum = mep->epnum;
|
||||
|
||||
if (mep->is_in) {
|
||||
mtu3_writel(mbase, MU3D_EP_TXCR0(epnum), 0);
|
||||
mtu3_writel(mbase, MU3D_EP_TXCR1(epnum), 0);
|
||||
mtu3_writel(mbase, MU3D_EP_TXCR2(epnum), 0);
|
||||
mtu3_setbits(mbase, U3D_QIECR0, QMU_TX_DONE_INT(epnum));
|
||||
} else {
|
||||
mtu3_writel(mbase, MU3D_EP_RXCR0(epnum), 0);
|
||||
mtu3_writel(mbase, MU3D_EP_RXCR1(epnum), 0);
|
||||
mtu3_writel(mbase, MU3D_EP_RXCR2(epnum), 0);
|
||||
mtu3_setbits(mbase, U3D_QIECR0, QMU_RX_DONE_INT(epnum));
|
||||
}
|
||||
|
||||
mtu3_ep_reset(mep);
|
||||
ep_fifo_free(mep);
|
||||
|
||||
dev_dbg(mtu->dev, "%s: %s\n", __func__, mep->name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Two scenarios:
|
||||
* 1. when device IP supports SS, the fifo of EP0, TX EPs, RX EPs
|
||||
* are separated;
|
||||
* 2. when supports only HS, the fifo is shared for all EPs, and
|
||||
* the capability registers of @EPNTXFFSZ or @EPNRXFFSZ indicate
|
||||
* the total fifo size of non-ep0, and ep0's is fixed to 64B,
|
||||
* so the total fifo size is 64B + @EPNTXFFSZ;
|
||||
* Due to the first 64B should be reserved for EP0, non-ep0's fifo
|
||||
* starts from offset 64 and are divided into two equal parts for
|
||||
* TX or RX EPs for simplification.
|
||||
*/
|
||||
static void get_ep_fifo_config(struct mtu3 *mtu)
|
||||
{
|
||||
struct mtu3_fifo_info *tx_fifo;
|
||||
struct mtu3_fifo_info *rx_fifo;
|
||||
u32 fifosize;
|
||||
|
||||
if (mtu->is_u3_ip) {
|
||||
fifosize = mtu3_readl(mtu->mac_base, U3D_CAP_EPNTXFFSZ);
|
||||
tx_fifo = &mtu->tx_fifo;
|
||||
tx_fifo->base = 0;
|
||||
tx_fifo->limit = fifosize / MTU3_U3IP_EP_FIFO_UNIT;
|
||||
bitmap_zero(tx_fifo->bitmap, MTU3_FIFO_BIT_SIZE);
|
||||
|
||||
fifosize = mtu3_readl(mtu->mac_base, U3D_CAP_EPNRXFFSZ);
|
||||
rx_fifo = &mtu->rx_fifo;
|
||||
rx_fifo->base = 0;
|
||||
rx_fifo->limit = fifosize / MTU3_U3IP_EP_FIFO_UNIT;
|
||||
bitmap_zero(rx_fifo->bitmap, MTU3_FIFO_BIT_SIZE);
|
||||
mtu->slot = MTU3_U3_IP_SLOT_DEFAULT;
|
||||
} else {
|
||||
fifosize = mtu3_readl(mtu->mac_base, U3D_CAP_EPNTXFFSZ);
|
||||
tx_fifo = &mtu->tx_fifo;
|
||||
tx_fifo->base = MTU3_U2_IP_EP0_FIFO_SIZE;
|
||||
tx_fifo->limit = (fifosize / MTU3_U2IP_EP_FIFO_UNIT) >> 1;
|
||||
bitmap_zero(tx_fifo->bitmap, MTU3_FIFO_BIT_SIZE);
|
||||
|
||||
rx_fifo = &mtu->rx_fifo;
|
||||
rx_fifo->base = tx_fifo->base +
|
||||
tx_fifo->limit * MTU3_U2IP_EP_FIFO_UNIT;
|
||||
rx_fifo->limit = tx_fifo->limit;
|
||||
bitmap_zero(rx_fifo->bitmap, MTU3_FIFO_BIT_SIZE);
|
||||
mtu->slot = MTU3_U2_IP_SLOT_DEFAULT;
|
||||
}
|
||||
|
||||
dev_dbg(mtu->dev, "%s, TX: base-%d, limit-%d; RX: base-%d, limit-%d\n",
|
||||
__func__, tx_fifo->base, tx_fifo->limit,
|
||||
rx_fifo->base, rx_fifo->limit);
|
||||
}
|
||||
|
||||
void mtu3_ep0_setup(struct mtu3 *mtu)
|
||||
{
|
||||
u32 maxpacket = mtu->g.ep0->maxpacket;
|
||||
u32 csr;
|
||||
|
||||
dev_dbg(mtu->dev, "%s maxpacket: %d\n", __func__, maxpacket);
|
||||
|
||||
csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR);
|
||||
csr &= ~EP0_MAXPKTSZ_MSK;
|
||||
csr |= EP0_MAXPKTSZ(maxpacket);
|
||||
csr &= EP0_W1C_BITS;
|
||||
mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr);
|
||||
|
||||
/* Enable EP0 interrupt */
|
||||
mtu3_writel(mtu->mac_base, U3D_EPIESR, EP0ISR | SETUPENDISR);
|
||||
}
|
||||
|
||||
static int mtu3_mem_alloc(struct mtu3 *mtu)
|
||||
{
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
struct mtu3_ep *ep_array;
|
||||
int in_ep_num, out_ep_num;
|
||||
u32 cap_epinfo;
|
||||
int i;
|
||||
|
||||
cap_epinfo = mtu3_readl(mbase, U3D_CAP_EPINFO);
|
||||
in_ep_num = CAP_TX_EP_NUM(cap_epinfo);
|
||||
out_ep_num = CAP_RX_EP_NUM(cap_epinfo);
|
||||
|
||||
dev_info(mtu->dev, "fifosz/epnum: Tx=%#x/%d, Rx=%#x/%d\n",
|
||||
mtu3_readl(mbase, U3D_CAP_EPNTXFFSZ), in_ep_num,
|
||||
mtu3_readl(mbase, U3D_CAP_EPNRXFFSZ), out_ep_num);
|
||||
|
||||
/* one for ep0, another is reserved */
|
||||
mtu->num_eps = min(in_ep_num, out_ep_num) + 1;
|
||||
ep_array = kcalloc(mtu->num_eps * 2, sizeof(*ep_array), GFP_KERNEL);
|
||||
if (!ep_array)
|
||||
return -ENOMEM;
|
||||
|
||||
mtu->ep_array = ep_array;
|
||||
mtu->in_eps = ep_array;
|
||||
mtu->out_eps = &ep_array[mtu->num_eps];
|
||||
/* ep0 uses in_eps[0], out_eps[0] is reserved */
|
||||
mtu->ep0 = mtu->in_eps;
|
||||
mtu->ep0->mtu = mtu;
|
||||
mtu->ep0->epnum = 0;
|
||||
|
||||
for (i = 1; i < mtu->num_eps; i++) {
|
||||
struct mtu3_ep *mep = mtu->in_eps + i;
|
||||
|
||||
mep->fifo = &mtu->tx_fifo;
|
||||
mep = mtu->out_eps + i;
|
||||
mep->fifo = &mtu->rx_fifo;
|
||||
}
|
||||
|
||||
get_ep_fifo_config(mtu);
|
||||
mtu3_qmu_init(mtu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtu3_mem_free(struct mtu3 *mtu)
|
||||
{
|
||||
mtu3_qmu_exit(mtu);
|
||||
kfree(mtu->ep_array);
|
||||
}
|
||||
|
||||
static void mtu3_regs_init(struct mtu3 *mtu)
|
||||
{
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
|
||||
/* be sure interrupts are disabled before registration of ISR */
|
||||
mtu3_intr_disable(mtu);
|
||||
|
||||
if (mtu->is_u3_ip) {
|
||||
/* disable LGO_U1/U2 by default */
|
||||
mtu3_clrbits(mbase, U3D_LINK_POWER_CONTROL,
|
||||
SW_U1_REQUEST_ENABLE | SW_U2_REQUEST_ENABLE);
|
||||
/* enable accept LGO_U1/U2 link command from host */
|
||||
mtu3_setbits(mbase, U3D_LINK_POWER_CONTROL,
|
||||
SW_U1_ACCEPT_ENABLE | SW_U2_ACCEPT_ENABLE);
|
||||
/* device responses to u3_exit from host automatically */
|
||||
mtu3_clrbits(mbase, U3D_LTSSM_CTRL, SOFT_U3_EXIT_EN);
|
||||
/* automatically build U2 link when U3 detect fail */
|
||||
mtu3_setbits(mbase, U3D_USB2_TEST_MODE, U2U3_AUTO_SWITCH);
|
||||
/* auto clear SOFT_CONN when clear USB3_EN if work as HS */
|
||||
mtu3_setbits(mbase, U3D_U3U2_SWITCH_CTRL, SOFTCON_CLR_AUTO_EN);
|
||||
}
|
||||
|
||||
/* delay about 0.1us from detecting reset to send chirp-K */
|
||||
mtu3_clrbits(mbase, U3D_LINK_RESET_INFO, WTCHRP_MSK);
|
||||
/* U2/U3 detected by HW */
|
||||
mtu3_writel(mbase, U3D_DEVICE_CONF, 0);
|
||||
/* enable automatical HWRW from L1 */
|
||||
mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, LPM_HRWE);
|
||||
|
||||
mtu3_set_speed(mtu, mtu->max_speed);
|
||||
ssusb_set_force_mode(mtu->ssusb, MTU3_DR_FORCE_DEVICE);
|
||||
|
||||
if (mtu->force_vbus)
|
||||
mtu3_setbits(mbase, U3D_MISC_CTRL, VBUS_FRC_EN | VBUS_ON);
|
||||
else /* vbus detected by HW */
|
||||
mtu3_clrbits(mbase, U3D_MISC_CTRL, VBUS_FRC_EN | VBUS_ON);
|
||||
}
|
||||
|
||||
static irqreturn_t mtu3_link_isr(struct mtu3 *mtu)
|
||||
{
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
enum usb_device_speed udev_speed;
|
||||
u32 maxpkt = 64;
|
||||
u32 link;
|
||||
u32 speed;
|
||||
|
||||
link = mtu3_readl(mbase, U3D_DEV_LINK_INTR);
|
||||
link &= mtu3_readl(mbase, U3D_DEV_LINK_INTR_ENABLE);
|
||||
mtu3_writel(mbase, U3D_DEV_LINK_INTR, link); /* W1C */
|
||||
dev_dbg(mtu->dev, "=== LINK[%x] ===\n", link);
|
||||
|
||||
if (!(link & SSUSB_DEV_SPEED_CHG_INTR))
|
||||
return IRQ_NONE;
|
||||
|
||||
speed = SSUSB_DEV_SPEED(mtu3_readl(mbase, U3D_DEVICE_CONF));
|
||||
|
||||
switch (speed) {
|
||||
case MTU3_SPEED_FULL:
|
||||
udev_speed = USB_SPEED_FULL;
|
||||
/*BESLCK = 4 < BESLCK_U3 = 10 < BESLDCK = 15 */
|
||||
mtu3_writel(mbase, U3D_USB20_LPM_PARAMETER, LPM_BESLDCK(0xf)
|
||||
| LPM_BESLCK(4) | LPM_BESLCK_U3(0xa));
|
||||
mtu3_setbits(mbase, U3D_POWER_MANAGEMENT,
|
||||
LPM_BESL_STALL | LPM_BESLD_STALL);
|
||||
break;
|
||||
case MTU3_SPEED_HIGH:
|
||||
udev_speed = USB_SPEED_HIGH;
|
||||
/*BESLCK = 4 < BESLCK_U3 = 10 < BESLDCK = 15 */
|
||||
mtu3_writel(mbase, U3D_USB20_LPM_PARAMETER, LPM_BESLDCK(0xf)
|
||||
| LPM_BESLCK(4) | LPM_BESLCK_U3(0xa));
|
||||
mtu3_setbits(mbase, U3D_POWER_MANAGEMENT,
|
||||
LPM_BESL_STALL | LPM_BESLD_STALL);
|
||||
break;
|
||||
case MTU3_SPEED_SUPER:
|
||||
udev_speed = USB_SPEED_SUPER;
|
||||
maxpkt = 512;
|
||||
break;
|
||||
case MTU3_SPEED_SUPER_PLUS:
|
||||
udev_speed = USB_SPEED_SUPER_PLUS;
|
||||
maxpkt = 512;
|
||||
break;
|
||||
default:
|
||||
udev_speed = USB_SPEED_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
dev_dbg(mtu->dev, "%s: %s\n", __func__, usb_speed_string(udev_speed));
|
||||
|
||||
mtu->g.speed = udev_speed;
|
||||
mtu->g.ep0->maxpacket = maxpkt;
|
||||
mtu->ep0_state = MU3D_EP0_STATE_SETUP;
|
||||
|
||||
if (udev_speed == USB_SPEED_UNKNOWN)
|
||||
mtu3_gadget_disconnect(mtu);
|
||||
else
|
||||
mtu3_ep0_setup(mtu);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t mtu3_u3_ltssm_isr(struct mtu3 *mtu)
|
||||
{
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
u32 ltssm;
|
||||
|
||||
ltssm = mtu3_readl(mbase, U3D_LTSSM_INTR);
|
||||
ltssm &= mtu3_readl(mbase, U3D_LTSSM_INTR_ENABLE);
|
||||
mtu3_writel(mbase, U3D_LTSSM_INTR, ltssm); /* W1C */
|
||||
dev_dbg(mtu->dev, "=== LTSSM[%x] ===\n", ltssm);
|
||||
|
||||
if (ltssm & (HOT_RST_INTR | WARM_RST_INTR))
|
||||
mtu3_gadget_reset(mtu);
|
||||
|
||||
if (ltssm & VBUS_FALL_INTR) {
|
||||
mtu3_ss_func_set(mtu, false);
|
||||
mtu3_gadget_reset(mtu);
|
||||
}
|
||||
|
||||
if (ltssm & VBUS_RISE_INTR)
|
||||
mtu3_ss_func_set(mtu, true);
|
||||
|
||||
if (ltssm & EXIT_U3_INTR)
|
||||
mtu3_gadget_resume(mtu);
|
||||
|
||||
if (ltssm & ENTER_U3_INTR)
|
||||
mtu3_gadget_suspend(mtu);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t mtu3_u2_common_isr(struct mtu3 *mtu)
|
||||
{
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
u32 u2comm;
|
||||
|
||||
u2comm = mtu3_readl(mbase, U3D_COMMON_USB_INTR);
|
||||
u2comm &= mtu3_readl(mbase, U3D_COMMON_USB_INTR_ENABLE);
|
||||
mtu3_writel(mbase, U3D_COMMON_USB_INTR, u2comm); /* W1C */
|
||||
dev_dbg(mtu->dev, "=== U2COMM[%x] ===\n", u2comm);
|
||||
|
||||
if (u2comm & SUSPEND_INTR)
|
||||
mtu3_gadget_suspend(mtu);
|
||||
|
||||
if (u2comm & RESUME_INTR)
|
||||
mtu3_gadget_resume(mtu);
|
||||
|
||||
if (u2comm & RESET_INTR)
|
||||
mtu3_gadget_reset(mtu);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
irqreturn_t mtu3_irq(int irq, void *data)
|
||||
{
|
||||
struct mtu3 *mtu = (struct mtu3 *)data;
|
||||
unsigned long flags;
|
||||
u32 level1;
|
||||
|
||||
spin_lock_irqsave(&mtu->lock, flags);
|
||||
|
||||
/* U3D_LV1ISR is RU */
|
||||
level1 = mtu3_readl(mtu->mac_base, U3D_LV1ISR);
|
||||
level1 &= mtu3_readl(mtu->mac_base, U3D_LV1IER);
|
||||
|
||||
if (level1 & EP_CTRL_INTR)
|
||||
mtu3_link_isr(mtu);
|
||||
|
||||
if (level1 & MAC2_INTR)
|
||||
mtu3_u2_common_isr(mtu);
|
||||
|
||||
if (level1 & MAC3_INTR)
|
||||
mtu3_u3_ltssm_isr(mtu);
|
||||
|
||||
if (level1 & BMU_INTR)
|
||||
mtu3_ep0_isr(mtu);
|
||||
|
||||
if (level1 & QMU_INTR)
|
||||
mtu3_qmu_isr(mtu);
|
||||
|
||||
spin_unlock_irqrestore(&mtu->lock, flags);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void mtu3_check_params(struct mtu3 *mtu)
|
||||
{
|
||||
/* check the max_speed parameter */
|
||||
switch (mtu->max_speed) {
|
||||
case USB_SPEED_FULL:
|
||||
case USB_SPEED_HIGH:
|
||||
case USB_SPEED_SUPER:
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
break;
|
||||
default:
|
||||
dev_err(mtu->dev, "invalid max_speed: %d\n", mtu->max_speed);
|
||||
/* fall through */
|
||||
case USB_SPEED_UNKNOWN:
|
||||
/* default as SS */
|
||||
mtu->max_speed = USB_SPEED_SUPER;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!mtu->is_u3_ip && (mtu->max_speed > USB_SPEED_HIGH))
|
||||
mtu->max_speed = USB_SPEED_HIGH;
|
||||
|
||||
mtu->speed = mtu->max_speed;
|
||||
|
||||
dev_info(mtu->dev, "max_speed: %s\n", usb_speed_string(mtu->speed));
|
||||
}
|
||||
|
||||
static int mtu3_hw_init(struct mtu3 *mtu)
|
||||
{
|
||||
u32 value;
|
||||
int ret;
|
||||
|
||||
value = mtu3_readl(mtu->ippc_base, U3D_SSUSB_IP_TRUNK_VERS);
|
||||
mtu->hw_version = IP_TRUNK_VERS(value);
|
||||
mtu->gen2cp = !!(mtu->hw_version >= MTU3_TRUNK_VERS_1003);
|
||||
|
||||
value = mtu3_readl(mtu->ippc_base, U3D_SSUSB_IP_DEV_CAP);
|
||||
mtu->is_u3_ip = !!SSUSB_IP_DEV_U3_PORT_NUM(value);
|
||||
|
||||
dev_info(mtu->dev, "IP version 0x%x(%s IP)\n", mtu->hw_version,
|
||||
mtu->is_u3_ip ? "U3" : "U2");
|
||||
|
||||
mtu3_check_params(mtu);
|
||||
|
||||
mtu3_device_reset(mtu);
|
||||
|
||||
ret = mtu3_device_enable(mtu);
|
||||
if (ret) {
|
||||
dev_err(mtu->dev, "device enable failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mtu3_mem_alloc(mtu);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mtu3_regs_init(mtu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtu3_hw_exit(struct mtu3 *mtu)
|
||||
{
|
||||
mtu3_device_disable(mtu);
|
||||
mtu3_mem_free(mtu);
|
||||
}
|
||||
|
||||
int ssusb_gadget_init(struct ssusb_mtk *ssusb)
|
||||
{
|
||||
struct mtu3 *mtu = ssusb->u3d;
|
||||
struct udevice *dev = mtu->dev;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
spin_lock_init(&mtu->lock);
|
||||
mtu->ippc_base = ssusb->ippc_base;
|
||||
mtu->mac_base = ssusb->mac_base;
|
||||
mtu->ssusb = ssusb;
|
||||
mtu->max_speed = usb_get_maximum_speed(dev->node);
|
||||
mtu->force_vbus = dev_read_bool(dev, "mediatek,force-vbus");
|
||||
|
||||
ret = mtu3_hw_init(mtu);
|
||||
if (ret) {
|
||||
dev_err(dev, "mtu3 hw init failed:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mtu3_gadget_setup(mtu);
|
||||
if (ret) {
|
||||
dev_err(dev, "mtu3 gadget init failed:%d\n", ret);
|
||||
goto gadget_err;
|
||||
}
|
||||
|
||||
dev_info(dev, "%s() done...\n", __func__);
|
||||
|
||||
return 0;
|
||||
|
||||
gadget_err:
|
||||
mtu3_hw_exit(mtu);
|
||||
ssusb->u3d = NULL;
|
||||
dev_err(dev, "%s() fail...\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ssusb_gadget_exit(struct ssusb_mtk *ssusb)
|
||||
{
|
||||
struct mtu3 *mtu = ssusb->u3d;
|
||||
|
||||
mtu3_gadget_cleanup(mtu);
|
||||
mtu3_hw_exit(mtu);
|
||||
}
|
52
drivers/usb/mtu3/mtu3_dr.h
Normal file
52
drivers/usb/mtu3/mtu3_dr.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* mtu3_dr.h - dual role switch and host glue layer header
|
||||
*
|
||||
* Copyright (C) 2016 MediaTek Inc.
|
||||
*
|
||||
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
|
||||
*/
|
||||
|
||||
#ifndef _MTU3_DR_H_
|
||||
#define _MTU3_DR_H_
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_MTU3_HOST)
|
||||
|
||||
int ssusb_host_init(struct ssusb_mtk *ssusb);
|
||||
void ssusb_host_exit(struct ssusb_mtk *ssusb);
|
||||
|
||||
#else
|
||||
|
||||
static inline int ssusb_host_init(struct ssusb_mtk *ssusb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ssusb_host_exit(struct ssusb_mtk *ssusb)
|
||||
{}
|
||||
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_MTU3_GADGET)
|
||||
int ssusb_gadget_init(struct ssusb_mtk *ssusb);
|
||||
void ssusb_gadget_exit(struct ssusb_mtk *ssusb);
|
||||
irqreturn_t mtu3_irq(int irq, void *data);
|
||||
#else
|
||||
static inline int ssusb_gadget_init(struct ssusb_mtk *ssusb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ssusb_gadget_exit(struct ssusb_mtk *ssusb)
|
||||
{}
|
||||
|
||||
static inline irqreturn_t mtu3_irq(int irq, void *data)
|
||||
{
|
||||
return IRQ_NONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
void ssusb_set_force_mode(struct ssusb_mtk *ssusb,
|
||||
enum mtu3_dr_force_mode mode);
|
||||
|
||||
#endif /* _MTU3_DR_H_ */
|
686
drivers/usb/mtu3/mtu3_gadget.c
Normal file
686
drivers/usb/mtu3/mtu3_gadget.c
Normal file
|
@ -0,0 +1,686 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* mtu3_gadget.c - MediaTek usb3 DRD peripheral support
|
||||
*
|
||||
* Copyright (C) 2016 MediaTek Inc.
|
||||
*
|
||||
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
|
||||
*/
|
||||
|
||||
#include "mtu3.h"
|
||||
|
||||
void mtu3_req_complete(struct mtu3_ep *mep,
|
||||
struct usb_request *req, int status)
|
||||
__releases(mep->mtu->lock)
|
||||
__acquires(mep->mtu->lock)
|
||||
{
|
||||
struct mtu3_request *mreq = to_mtu3_request(req);
|
||||
struct mtu3 *mtu = mreq->mtu;
|
||||
|
||||
list_del(&mreq->list);
|
||||
if (req->status == -EINPROGRESS)
|
||||
req->status = status;
|
||||
|
||||
spin_unlock(&mtu->lock);
|
||||
|
||||
/* ep0 makes use of PIO, needn't unmap it */
|
||||
if (mep->epnum)
|
||||
usb_gadget_unmap_request(&mtu->g, req, mep->is_in);
|
||||
|
||||
dev_dbg(mtu->dev, "%s complete req: %p, sts %d, %d/%d\n",
|
||||
mep->name, req, req->status, req->actual, req->length);
|
||||
|
||||
usb_gadget_giveback_request(&mep->ep, req);
|
||||
spin_lock(&mtu->lock);
|
||||
}
|
||||
|
||||
static void nuke(struct mtu3_ep *mep, const int status)
|
||||
{
|
||||
struct mtu3_request *mreq = NULL;
|
||||
|
||||
if (list_empty(&mep->req_list))
|
||||
return;
|
||||
|
||||
dev_dbg(mep->mtu->dev, "abort %s's req: sts %d\n", mep->name, status);
|
||||
|
||||
/* exclude EP0 */
|
||||
if (mep->epnum)
|
||||
mtu3_qmu_flush(mep);
|
||||
|
||||
while (!list_empty(&mep->req_list)) {
|
||||
mreq = list_first_entry(&mep->req_list,
|
||||
struct mtu3_request, list);
|
||||
mtu3_req_complete(mep, &mreq->request, status);
|
||||
}
|
||||
}
|
||||
|
||||
static int mtu3_ep_enable(struct mtu3_ep *mep)
|
||||
{
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
const struct usb_ss_ep_comp_descriptor *comp_desc;
|
||||
struct mtu3 *mtu = mep->mtu;
|
||||
u32 interval = 0;
|
||||
u32 mult = 0;
|
||||
u32 burst = 0;
|
||||
int max_packet;
|
||||
int ret;
|
||||
|
||||
desc = mep->desc;
|
||||
comp_desc = mep->comp_desc;
|
||||
mep->type = usb_endpoint_type(desc);
|
||||
max_packet = usb_endpoint_maxp(desc);
|
||||
mep->maxp = max_packet & GENMASK(10, 0);
|
||||
|
||||
switch (mtu->g.speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
if (usb_endpoint_xfer_int(desc) ||
|
||||
usb_endpoint_xfer_isoc(desc)) {
|
||||
interval = desc->bInterval;
|
||||
interval = clamp_val(interval, 1, 16) - 1;
|
||||
if (usb_endpoint_xfer_isoc(desc) && comp_desc)
|
||||
mult = comp_desc->bmAttributes;
|
||||
}
|
||||
if (comp_desc)
|
||||
burst = comp_desc->bMaxBurst;
|
||||
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
if (usb_endpoint_xfer_isoc(desc) ||
|
||||
usb_endpoint_xfer_int(desc)) {
|
||||
interval = desc->bInterval;
|
||||
interval = clamp_val(interval, 1, 16) - 1;
|
||||
burst = (max_packet & GENMASK(12, 11)) >> 11;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break; /*others are ignored */
|
||||
}
|
||||
|
||||
dev_dbg(mtu->dev, "%s maxp:%d, interval:%d, burst:%d, mult:%d\n",
|
||||
__func__, mep->maxp, interval, burst, mult);
|
||||
|
||||
mep->ep.maxpacket = mep->maxp;
|
||||
mep->ep.desc = desc;
|
||||
mep->ep.comp_desc = comp_desc;
|
||||
mep->slot = mtu->slot;
|
||||
|
||||
ret = mtu3_config_ep(mtu, mep, interval, burst, mult);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = mtu3_gpd_ring_alloc(mep);
|
||||
if (ret < 0) {
|
||||
mtu3_deconfig_ep(mtu, mep);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mtu3_qmu_start(mep);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtu3_ep_disable(struct mtu3_ep *mep)
|
||||
{
|
||||
struct mtu3 *mtu = mep->mtu;
|
||||
|
||||
mtu3_qmu_stop(mep);
|
||||
|
||||
/* abort all pending requests */
|
||||
nuke(mep, -ESHUTDOWN);
|
||||
mtu3_deconfig_ep(mtu, mep);
|
||||
mtu3_gpd_ring_free(mep);
|
||||
|
||||
mep->desc = NULL;
|
||||
mep->ep.desc = NULL;
|
||||
mep->comp_desc = NULL;
|
||||
mep->type = 0;
|
||||
mep->flags = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtu3_gadget_ep_enable(struct usb_ep *ep,
|
||||
const struct usb_endpoint_descriptor *desc)
|
||||
{
|
||||
struct mtu3_ep *mep;
|
||||
struct mtu3 *mtu;
|
||||
unsigned long flags;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
|
||||
pr_debug("%s invalid parameters\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!desc->wMaxPacketSize) {
|
||||
pr_debug("%s missing wMaxPacketSize\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
mep = to_mtu3_ep(ep);
|
||||
mtu = mep->mtu;
|
||||
|
||||
/* check ep number and direction against endpoint */
|
||||
if (usb_endpoint_num(desc) != mep->epnum)
|
||||
return -EINVAL;
|
||||
|
||||
if (!!usb_endpoint_dir_in(desc) ^ !!mep->is_in)
|
||||
return -EINVAL;
|
||||
|
||||
dev_dbg(mtu->dev, "%s %s\n", __func__, ep->name);
|
||||
|
||||
if (mep->flags & MTU3_EP_ENABLED) {
|
||||
dev_warn(mtu->dev, "%s is already enabled\n", mep->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&mtu->lock, flags);
|
||||
mep->desc = desc;
|
||||
mep->comp_desc = ep->comp_desc;
|
||||
|
||||
ret = mtu3_ep_enable(mep);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
mep->flags = MTU3_EP_ENABLED;
|
||||
mtu->active_ep++;
|
||||
|
||||
error:
|
||||
spin_unlock_irqrestore(&mtu->lock, flags);
|
||||
|
||||
dev_dbg(mtu->dev, "%s active_ep=%d\n", __func__, mtu->active_ep);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtu3_gadget_ep_disable(struct usb_ep *ep)
|
||||
{
|
||||
struct mtu3_ep *mep = to_mtu3_ep(ep);
|
||||
struct mtu3 *mtu = mep->mtu;
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(mtu->dev, "%s %s\n", __func__, mep->name);
|
||||
|
||||
if (!(mep->flags & MTU3_EP_ENABLED)) {
|
||||
dev_warn(mtu->dev, "%s is already disabled\n", mep->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&mtu->lock, flags);
|
||||
mtu3_ep_disable(mep);
|
||||
mep->flags = 0;
|
||||
mtu->active_ep--;
|
||||
spin_unlock_irqrestore(&mtu->lock, flags);
|
||||
|
||||
dev_dbg(mtu->dev, "%s active_ep=%d, mtu3 is_active=%d\n",
|
||||
__func__, mtu->active_ep, mtu->is_active);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct usb_request *mtu3_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
|
||||
{
|
||||
struct mtu3_ep *mep = to_mtu3_ep(ep);
|
||||
struct mtu3_request *mreq;
|
||||
|
||||
mreq = kzalloc(sizeof(*mreq), gfp_flags);
|
||||
if (!mreq)
|
||||
return NULL;
|
||||
|
||||
mreq->request.dma = DMA_ADDR_INVALID;
|
||||
mreq->epnum = mep->epnum;
|
||||
mreq->mep = mep;
|
||||
|
||||
return &mreq->request;
|
||||
}
|
||||
|
||||
void mtu3_free_request(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct mtu3_request *mreq = to_mtu3_request(req);
|
||||
|
||||
kfree(mreq);
|
||||
}
|
||||
|
||||
static int mtu3_gadget_queue(struct usb_ep *ep,
|
||||
struct usb_request *req, gfp_t gfp_flags)
|
||||
{
|
||||
struct mtu3_ep *mep = to_mtu3_ep(ep);
|
||||
struct mtu3_request *mreq = to_mtu3_request(req);
|
||||
struct mtu3 *mtu = mep->mtu;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
if (!req->buf)
|
||||
return -ENODATA;
|
||||
|
||||
if (mreq->mep != mep)
|
||||
return -EINVAL;
|
||||
|
||||
dev_dbg(mtu->dev, "%s %s EP%d(%s), req=%p, maxp=%d, len#%d\n",
|
||||
__func__, mep->is_in ? "TX" : "RX", mreq->epnum, ep->name,
|
||||
mreq, ep->maxpacket, mreq->request.length);
|
||||
|
||||
if (req->length > GPD_BUF_SIZE) {
|
||||
dev_warn(mtu->dev,
|
||||
"req length > supported MAX:%d requested:%d\n",
|
||||
GPD_BUF_SIZE, req->length);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* don't queue if the ep is down */
|
||||
if (!mep->desc) {
|
||||
dev_dbg(mtu->dev, "req=%p queued to %s while it's disabled\n",
|
||||
req, ep->name);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
mreq->mtu = mtu;
|
||||
mreq->request.actual = 0;
|
||||
mreq->request.status = -EINPROGRESS;
|
||||
|
||||
ret = usb_gadget_map_request(&mtu->g, req, mep->is_in);
|
||||
if (ret) {
|
||||
dev_err(mtu->dev, "dma mapping failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&mtu->lock, flags);
|
||||
|
||||
if (mtu3_prepare_transfer(mep)) {
|
||||
ret = -EAGAIN;
|
||||
goto error;
|
||||
}
|
||||
|
||||
list_add_tail(&mreq->list, &mep->req_list);
|
||||
mtu3_insert_gpd(mep, mreq);
|
||||
mtu3_qmu_resume(mep);
|
||||
|
||||
error:
|
||||
spin_unlock_irqrestore(&mtu->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtu3_gadget_dequeue(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct mtu3_ep *mep = to_mtu3_ep(ep);
|
||||
struct mtu3_request *mreq = to_mtu3_request(req);
|
||||
struct mtu3_request *r;
|
||||
struct mtu3 *mtu = mep->mtu;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
if (mreq->mep != mep)
|
||||
return -EINVAL;
|
||||
|
||||
dev_dbg(mtu->dev, "%s : req=%p\n", __func__, req);
|
||||
|
||||
spin_lock_irqsave(&mtu->lock, flags);
|
||||
|
||||
list_for_each_entry(r, &mep->req_list, list) {
|
||||
if (r == mreq)
|
||||
break;
|
||||
}
|
||||
if (r != mreq) {
|
||||
dev_dbg(mtu->dev, "req=%p not queued to %s\n", req, ep->name);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
mtu3_qmu_flush(mep); /* REVISIT: set BPS ?? */
|
||||
mtu3_req_complete(mep, req, -ECONNRESET);
|
||||
mtu3_qmu_start(mep);
|
||||
|
||||
done:
|
||||
spin_unlock_irqrestore(&mtu->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set or clear the halt bit of an EP.
|
||||
* A halted EP won't TX/RX any data but will queue requests.
|
||||
*/
|
||||
static int mtu3_gadget_ep_set_halt(struct usb_ep *ep, int value)
|
||||
{
|
||||
struct mtu3_ep *mep = to_mtu3_ep(ep);
|
||||
struct mtu3 *mtu = mep->mtu;
|
||||
struct mtu3_request *mreq;
|
||||
unsigned long flags = 0;
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(mtu->dev, "%s : %s...", __func__, ep->name);
|
||||
|
||||
spin_lock_irqsave(&mtu->lock, flags);
|
||||
|
||||
if (mep->type == USB_ENDPOINT_XFER_ISOC) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
mreq = next_request(mep);
|
||||
if (value) {
|
||||
/*
|
||||
* If there is not request for TX-EP, QMU will not transfer
|
||||
* data to TX-FIFO, so no need check whether TX-FIFO
|
||||
* holds bytes or not here
|
||||
*/
|
||||
if (mreq) {
|
||||
dev_dbg(mtu->dev, "req in progress, cannot halt %s\n",
|
||||
ep->name);
|
||||
ret = -EAGAIN;
|
||||
goto done;
|
||||
}
|
||||
} else {
|
||||
mep->flags &= ~MTU3_EP_WEDGE;
|
||||
}
|
||||
|
||||
dev_dbg(mtu->dev, "%s %s stall\n", ep->name, value ? "set" : "clear");
|
||||
|
||||
mtu3_ep_stall_set(mep, value);
|
||||
|
||||
done:
|
||||
spin_unlock_irqrestore(&mtu->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Sets the halt feature with the clear requests ignored */
|
||||
static int mtu3_gadget_ep_set_wedge(struct usb_ep *ep)
|
||||
{
|
||||
struct mtu3_ep *mep = to_mtu3_ep(ep);
|
||||
|
||||
mep->flags |= MTU3_EP_WEDGE;
|
||||
|
||||
return usb_ep_set_halt(ep);
|
||||
}
|
||||
|
||||
static const struct usb_ep_ops mtu3_ep_ops = {
|
||||
.enable = mtu3_gadget_ep_enable,
|
||||
.disable = mtu3_gadget_ep_disable,
|
||||
.alloc_request = mtu3_alloc_request,
|
||||
.free_request = mtu3_free_request,
|
||||
.queue = mtu3_gadget_queue,
|
||||
.dequeue = mtu3_gadget_dequeue,
|
||||
.set_halt = mtu3_gadget_ep_set_halt,
|
||||
.set_wedge = mtu3_gadget_ep_set_wedge,
|
||||
};
|
||||
|
||||
static int mtu3_gadget_get_frame(struct usb_gadget *gadget)
|
||||
{
|
||||
struct mtu3 *mtu = gadget_to_mtu3(gadget);
|
||||
|
||||
return (int)mtu3_readl(mtu->mac_base, U3D_USB20_FRAME_NUM);
|
||||
}
|
||||
|
||||
static int mtu3_gadget_wakeup(struct usb_gadget *gadget)
|
||||
{
|
||||
struct mtu3 *mtu = gadget_to_mtu3(gadget);
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(mtu->dev, "%s\n", __func__);
|
||||
|
||||
/* remote wakeup feature is not enabled by host */
|
||||
if (!mtu->may_wakeup)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
spin_lock_irqsave(&mtu->lock, flags);
|
||||
if (mtu->g.speed >= USB_SPEED_SUPER) {
|
||||
mtu3_setbits(mtu->mac_base, U3D_LINK_POWER_CONTROL, UX_EXIT);
|
||||
} else {
|
||||
mtu3_setbits(mtu->mac_base, U3D_POWER_MANAGEMENT, RESUME);
|
||||
spin_unlock_irqrestore(&mtu->lock, flags);
|
||||
mdelay(10);
|
||||
spin_lock_irqsave(&mtu->lock, flags);
|
||||
mtu3_clrbits(mtu->mac_base, U3D_POWER_MANAGEMENT, RESUME);
|
||||
}
|
||||
spin_unlock_irqrestore(&mtu->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtu3_gadget_set_self_powered(struct usb_gadget *gadget,
|
||||
int is_selfpowered)
|
||||
{
|
||||
struct mtu3 *mtu = gadget_to_mtu3(gadget);
|
||||
|
||||
mtu->is_self_powered = !!is_selfpowered;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtu3_gadget_pullup(struct usb_gadget *gadget, int is_on)
|
||||
{
|
||||
struct mtu3 *mtu = gadget_to_mtu3(gadget);
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(mtu->dev, "%s (%s) for %sactive device\n", __func__,
|
||||
is_on ? "on" : "off", mtu->is_active ? "" : "in");
|
||||
|
||||
/* we'd rather not pullup unless the device is active. */
|
||||
spin_lock_irqsave(&mtu->lock, flags);
|
||||
|
||||
is_on = !!is_on;
|
||||
if (!mtu->is_active) {
|
||||
/* save it for mtu3_start() to process the request */
|
||||
mtu->softconnect = is_on;
|
||||
} else if (is_on != mtu->softconnect) {
|
||||
mtu->softconnect = is_on;
|
||||
mtu3_dev_on_off(mtu, is_on);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&mtu->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtu3_gadget_start(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct mtu3 *mtu = gadget_to_mtu3(gadget);
|
||||
unsigned long flags;
|
||||
|
||||
if (mtu->gadget_driver) {
|
||||
dev_err(mtu->dev, "%s is already bound to %s\n",
|
||||
mtu->g.name, mtu->gadget_driver->function);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
dev_dbg(mtu->dev, "bind driver %s\n", driver->function);
|
||||
|
||||
spin_lock_irqsave(&mtu->lock, flags);
|
||||
|
||||
mtu->softconnect = 0;
|
||||
mtu->gadget_driver = driver;
|
||||
mtu3_start(mtu);
|
||||
|
||||
spin_unlock_irqrestore(&mtu->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stop_activity(struct mtu3 *mtu)
|
||||
{
|
||||
int i;
|
||||
|
||||
mtu->g.speed = USB_SPEED_UNKNOWN;
|
||||
|
||||
/* deactivate the hardware */
|
||||
if (mtu->softconnect) {
|
||||
mtu->softconnect = 0;
|
||||
mtu3_dev_on_off(mtu, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* killing any outstanding requests will quiesce the driver;
|
||||
* then report disconnect
|
||||
*/
|
||||
nuke(mtu->ep0, -ESHUTDOWN);
|
||||
for (i = 1; i < mtu->num_eps; i++) {
|
||||
nuke(mtu->in_eps + i, -ESHUTDOWN);
|
||||
nuke(mtu->out_eps + i, -ESHUTDOWN);
|
||||
}
|
||||
}
|
||||
|
||||
static int mtu3_gadget_stop(struct usb_gadget *g)
|
||||
{
|
||||
struct mtu3 *mtu = gadget_to_mtu3(g);
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(mtu->dev, "%s\n", __func__);
|
||||
|
||||
spin_lock_irqsave(&mtu->lock, flags);
|
||||
|
||||
stop_activity(mtu);
|
||||
mtu->gadget_driver = NULL;
|
||||
mtu3_stop(mtu);
|
||||
|
||||
spin_unlock_irqrestore(&mtu->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mtu3_gadget_set_speed(struct usb_gadget *g, enum usb_device_speed speed)
|
||||
{
|
||||
struct mtu3 *mtu = gadget_to_mtu3(g);
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(mtu->dev, "%s %d\n", __func__, speed);
|
||||
|
||||
spin_lock_irqsave(&mtu->lock, flags);
|
||||
mtu3_set_speed(mtu, speed);
|
||||
spin_unlock_irqrestore(&mtu->lock, flags);
|
||||
}
|
||||
|
||||
static const struct usb_gadget_ops mtu3_gadget_ops = {
|
||||
.get_frame = mtu3_gadget_get_frame,
|
||||
.wakeup = mtu3_gadget_wakeup,
|
||||
.set_selfpowered = mtu3_gadget_set_self_powered,
|
||||
.pullup = mtu3_gadget_pullup,
|
||||
.udc_start = mtu3_gadget_start,
|
||||
.udc_stop = mtu3_gadget_stop,
|
||||
.udc_set_speed = mtu3_gadget_set_speed,
|
||||
};
|
||||
|
||||
static void mtu3_state_reset(struct mtu3 *mtu)
|
||||
{
|
||||
mtu->address = 0;
|
||||
mtu->ep0_state = MU3D_EP0_STATE_SETUP;
|
||||
mtu->may_wakeup = 0;
|
||||
mtu->u1_enable = 0;
|
||||
mtu->u2_enable = 0;
|
||||
mtu->delayed_status = false;
|
||||
mtu->test_mode = false;
|
||||
}
|
||||
|
||||
static void init_hw_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
|
||||
u32 epnum, u32 is_in)
|
||||
{
|
||||
mep->epnum = epnum;
|
||||
mep->mtu = mtu;
|
||||
mep->is_in = is_in;
|
||||
|
||||
INIT_LIST_HEAD(&mep->req_list);
|
||||
|
||||
sprintf(mep->name, "ep%d%s", epnum,
|
||||
!epnum ? "" : (is_in ? "in" : "out"));
|
||||
|
||||
mep->ep.name = mep->name;
|
||||
INIT_LIST_HEAD(&mep->ep.ep_list);
|
||||
|
||||
/* initialize maxpacket as SS */
|
||||
if (!epnum) {
|
||||
usb_ep_set_maxpacket_limit(&mep->ep, USB_HS_MAXP);
|
||||
mep->ep.ops = &mtu3_ep0_ops;
|
||||
mtu->g.ep0 = &mep->ep;
|
||||
} else {
|
||||
usb_ep_set_maxpacket_limit(&mep->ep, USB_SS_MAXP);
|
||||
mep->ep.ops = &mtu3_ep_ops;
|
||||
list_add_tail(&mep->ep.ep_list, &mtu->g.ep_list);
|
||||
}
|
||||
|
||||
dev_dbg(mtu->dev, "%s, name=%s, maxp=%d\n", __func__, mep->ep.name,
|
||||
mep->ep.maxpacket);
|
||||
}
|
||||
|
||||
static void mtu3_gadget_init_eps(struct mtu3 *mtu)
|
||||
{
|
||||
u8 epnum;
|
||||
|
||||
/* initialize endpoint list just once */
|
||||
INIT_LIST_HEAD(&mtu->g.ep_list);
|
||||
|
||||
dev_dbg(mtu->dev, "%s num_eps(1 for a pair of tx&rx ep)=%d\n",
|
||||
__func__, mtu->num_eps);
|
||||
|
||||
init_hw_ep(mtu, mtu->ep0, 0, 0);
|
||||
for (epnum = 1; epnum < mtu->num_eps; epnum++) {
|
||||
init_hw_ep(mtu, mtu->in_eps + epnum, epnum, 1);
|
||||
init_hw_ep(mtu, mtu->out_eps + epnum, epnum, 0);
|
||||
}
|
||||
}
|
||||
|
||||
int mtu3_gadget_setup(struct mtu3 *mtu)
|
||||
{
|
||||
mtu->g.ops = &mtu3_gadget_ops;
|
||||
mtu->g.max_speed = mtu->max_speed;
|
||||
mtu->g.speed = USB_SPEED_UNKNOWN;
|
||||
mtu->g.is_dualspeed = 1;
|
||||
mtu->g.name = MTU3_DRIVER_NAME;
|
||||
mtu->is_active = 0;
|
||||
mtu->delayed_status = false;
|
||||
|
||||
mtu3_gadget_init_eps(mtu);
|
||||
|
||||
return usb_add_gadget_udc((struct device *)mtu->dev, &mtu->g);
|
||||
}
|
||||
|
||||
void mtu3_gadget_cleanup(struct mtu3 *mtu)
|
||||
{
|
||||
usb_del_gadget_udc(&mtu->g);
|
||||
}
|
||||
|
||||
void mtu3_gadget_resume(struct mtu3 *mtu)
|
||||
{
|
||||
dev_dbg(mtu->dev, "gadget RESUME\n");
|
||||
if (mtu->gadget_driver && mtu->gadget_driver->resume) {
|
||||
spin_unlock(&mtu->lock);
|
||||
mtu->gadget_driver->resume(&mtu->g);
|
||||
spin_lock(&mtu->lock);
|
||||
}
|
||||
}
|
||||
|
||||
/* called when SOF packets stop for 3+ msec or enters U3 */
|
||||
void mtu3_gadget_suspend(struct mtu3 *mtu)
|
||||
{
|
||||
dev_dbg(mtu->dev, "gadget SUSPEND\n");
|
||||
if (mtu->gadget_driver && mtu->gadget_driver->suspend) {
|
||||
spin_unlock(&mtu->lock);
|
||||
mtu->gadget_driver->suspend(&mtu->g);
|
||||
spin_lock(&mtu->lock);
|
||||
}
|
||||
}
|
||||
|
||||
/* called when VBUS drops below session threshold, and in other cases */
|
||||
void mtu3_gadget_disconnect(struct mtu3 *mtu)
|
||||
{
|
||||
dev_dbg(mtu->dev, "gadget DISCONNECT\n");
|
||||
if (mtu->gadget_driver && mtu->gadget_driver->disconnect) {
|
||||
spin_unlock(&mtu->lock);
|
||||
mtu->gadget_driver->disconnect(&mtu->g);
|
||||
spin_lock(&mtu->lock);
|
||||
}
|
||||
|
||||
mtu3_state_reset(mtu);
|
||||
usb_gadget_set_state(&mtu->g, USB_STATE_NOTATTACHED);
|
||||
}
|
||||
|
||||
void mtu3_gadget_reset(struct mtu3 *mtu)
|
||||
{
|
||||
dev_dbg(mtu->dev, "gadget RESET\n");
|
||||
|
||||
/* report disconnect, if we didn't flush EP state */
|
||||
if (mtu->g.speed != USB_SPEED_UNKNOWN)
|
||||
mtu3_gadget_disconnect(mtu);
|
||||
else
|
||||
mtu3_state_reset(mtu);
|
||||
}
|
933
drivers/usb/mtu3/mtu3_gadget_ep0.c
Normal file
933
drivers/usb/mtu3/mtu3_gadget_ep0.c
Normal file
|
@ -0,0 +1,933 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* mtu3_gadget_ep0.c - MediaTek USB3 DRD peripheral driver ep0 handling
|
||||
*
|
||||
* Copyright (c) 2016 MediaTek Inc.
|
||||
*
|
||||
* Author: Chunfeng.Yun <chunfeng.yun@mediatek.com>
|
||||
*/
|
||||
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
#include "mtu3.h"
|
||||
|
||||
/* ep0 is always mtu3->in_eps[0] */
|
||||
#define next_ep0_request(mtu) next_request((mtu)->ep0)
|
||||
|
||||
/* for high speed test mode; see USB 2.0 spec 7.1.20 */
|
||||
static const u8 mtu3_test_packet[53] = {
|
||||
/* implicit SYNC then DATA0 to start */
|
||||
|
||||
/* JKJKJKJK x9 */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* JJKKJJKK x8 */
|
||||
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||
/* JJJJKKKK x8 */
|
||||
0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
|
||||
/* JJJJJJJKKKKKKK x8 */
|
||||
0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/* JJJJJJJK x8 */
|
||||
0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd,
|
||||
/* JKKKKKKK x10, JK */
|
||||
0xfc, 0x7e, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0x7e,
|
||||
/* implicit CRC16 then EOP to end */
|
||||
};
|
||||
|
||||
static char *decode_ep0_state(struct mtu3 *mtu)
|
||||
{
|
||||
switch (mtu->ep0_state) {
|
||||
case MU3D_EP0_STATE_SETUP:
|
||||
return "SETUP";
|
||||
case MU3D_EP0_STATE_TX:
|
||||
return "IN";
|
||||
case MU3D_EP0_STATE_RX:
|
||||
return "OUT";
|
||||
case MU3D_EP0_STATE_TX_END:
|
||||
return "TX-END";
|
||||
case MU3D_EP0_STATE_STALL:
|
||||
return "STALL";
|
||||
default:
|
||||
return "??";
|
||||
}
|
||||
}
|
||||
|
||||
static void ep0_req_giveback(struct mtu3 *mtu, struct usb_request *req)
|
||||
{
|
||||
mtu3_req_complete(mtu->ep0, req, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
forward_to_driver(struct mtu3 *mtu, const struct usb_ctrlrequest *setup)
|
||||
__releases(mtu->lock)
|
||||
__acquires(mtu->lock)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!mtu->gadget_driver)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
spin_unlock(&mtu->lock);
|
||||
ret = mtu->gadget_driver->setup(&mtu->g, setup);
|
||||
spin_lock(&mtu->lock);
|
||||
|
||||
dev_dbg(mtu->dev, "%s ret %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void writel_rep(volatile void *addr, const void *buffer,
|
||||
unsigned int count)
|
||||
{
|
||||
if (count) {
|
||||
const u32 *buf = buffer;
|
||||
|
||||
do {
|
||||
writel(*buf++, addr);
|
||||
} while (--count);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void readl_rep(const volatile void *addr, void *buffer,
|
||||
unsigned int count)
|
||||
{
|
||||
if (count) {
|
||||
u32 *buf = buffer;
|
||||
|
||||
do {
|
||||
u32 x = readl(addr);
|
||||
*buf++ = x;
|
||||
} while (--count);
|
||||
}
|
||||
}
|
||||
|
||||
static void ep0_write_fifo(struct mtu3_ep *mep, const u8 *src, u16 len)
|
||||
{
|
||||
void __iomem *fifo = mep->mtu->mac_base + U3D_FIFO0;
|
||||
u16 index = 0;
|
||||
|
||||
dev_dbg(mep->mtu->dev, "%s: ep%din, len=%d, buf=%p\n",
|
||||
__func__, mep->epnum, len, src);
|
||||
|
||||
if (len >= 4) {
|
||||
writel_rep(fifo, src, len >> 2);
|
||||
index = len & ~0x03;
|
||||
}
|
||||
if (len & 0x02) {
|
||||
writew(*(u16 *)&src[index], fifo);
|
||||
index += 2;
|
||||
}
|
||||
if (len & 0x01)
|
||||
writeb(src[index], fifo);
|
||||
}
|
||||
|
||||
static void ep0_read_fifo(struct mtu3_ep *mep, u8 *dst, u16 len)
|
||||
{
|
||||
void __iomem *fifo = mep->mtu->mac_base + U3D_FIFO0;
|
||||
u32 value;
|
||||
u16 index = 0;
|
||||
|
||||
dev_dbg(mep->mtu->dev, "%s: ep%dout len=%d buf=%p\n",
|
||||
__func__, mep->epnum, len, dst);
|
||||
|
||||
if (len >= 4) {
|
||||
readl_rep(fifo, dst, len >> 2);
|
||||
index = len & ~0x03;
|
||||
}
|
||||
if (len & 0x3) {
|
||||
value = readl(fifo);
|
||||
memcpy(&dst[index], &value, len & 0x3);
|
||||
}
|
||||
}
|
||||
|
||||
static void ep0_load_test_packet(struct mtu3 *mtu)
|
||||
{
|
||||
/*
|
||||
* because the length of test packet is less than max packet of HS ep0,
|
||||
* write it into fifo directly.
|
||||
*/
|
||||
ep0_write_fifo(mtu->ep0, mtu3_test_packet, sizeof(mtu3_test_packet));
|
||||
}
|
||||
|
||||
/*
|
||||
* A. send STALL for setup transfer without data stage:
|
||||
* set SENDSTALL and SETUPPKTRDY at the same time;
|
||||
* B. send STALL for other cases:
|
||||
* set SENDSTALL only.
|
||||
*/
|
||||
static void ep0_stall_set(struct mtu3_ep *mep0, bool set, u32 pktrdy)
|
||||
{
|
||||
struct mtu3 *mtu = mep0->mtu;
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
u32 csr;
|
||||
|
||||
/* EP0_SENTSTALL is W1C */
|
||||
csr = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS;
|
||||
if (set)
|
||||
csr |= EP0_SENDSTALL | pktrdy;
|
||||
else
|
||||
csr = (csr & ~EP0_SENDSTALL) | EP0_SENTSTALL;
|
||||
mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr);
|
||||
|
||||
mtu->delayed_status = false;
|
||||
mtu->ep0_state = MU3D_EP0_STATE_SETUP;
|
||||
|
||||
dev_dbg(mtu->dev, "ep0: %s STALL, ep0_state: %s\n",
|
||||
set ? "SEND" : "CLEAR", decode_ep0_state(mtu));
|
||||
}
|
||||
|
||||
static void ep0_do_status_stage(struct mtu3 *mtu)
|
||||
{
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
u32 value;
|
||||
|
||||
value = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS;
|
||||
mtu3_writel(mbase, U3D_EP0CSR, value | EP0_SETUPPKTRDY | EP0_DATAEND);
|
||||
}
|
||||
|
||||
static int ep0_queue(struct mtu3_ep *mep0, struct mtu3_request *mreq);
|
||||
|
||||
static void ep0_dummy_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{}
|
||||
|
||||
static void ep0_set_sel_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct mtu3_request *mreq;
|
||||
struct mtu3 *mtu;
|
||||
struct usb_set_sel_req sel;
|
||||
|
||||
memcpy(&sel, req->buf, sizeof(sel));
|
||||
|
||||
mreq = to_mtu3_request(req);
|
||||
mtu = mreq->mtu;
|
||||
dev_dbg(mtu->dev, "u1sel:%d, u1pel:%d, u2sel:%d, u2pel:%d\n",
|
||||
sel.u1_sel, sel.u1_pel, sel.u2_sel, sel.u2_pel);
|
||||
}
|
||||
|
||||
/* queue data stage to handle 6 byte SET_SEL request */
|
||||
static int ep0_set_sel(struct mtu3 *mtu, struct usb_ctrlrequest *setup)
|
||||
{
|
||||
int ret;
|
||||
u16 length = le16_to_cpu(setup->wLength);
|
||||
|
||||
if (unlikely(length != 6)) {
|
||||
dev_err(mtu->dev, "%s wrong wLength:%d\n",
|
||||
__func__, length);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mtu->ep0_req.mep = mtu->ep0;
|
||||
mtu->ep0_req.request.length = 6;
|
||||
mtu->ep0_req.request.buf = mtu->setup_buf;
|
||||
mtu->ep0_req.request.complete = ep0_set_sel_complete;
|
||||
ret = ep0_queue(mtu->ep0, &mtu->ep0_req);
|
||||
|
||||
return ret < 0 ? ret : 1;
|
||||
}
|
||||
|
||||
static int
|
||||
ep0_get_status(struct mtu3 *mtu, const struct usb_ctrlrequest *setup)
|
||||
{
|
||||
struct mtu3_ep *mep = NULL;
|
||||
int handled = 1;
|
||||
u8 result[2] = {0, 0};
|
||||
u8 epnum = 0;
|
||||
int is_in;
|
||||
|
||||
switch (setup->bRequestType & USB_RECIP_MASK) {
|
||||
case USB_RECIP_DEVICE:
|
||||
result[0] = mtu->is_self_powered << USB_DEVICE_SELF_POWERED;
|
||||
result[0] |= mtu->may_wakeup << USB_DEVICE_REMOTE_WAKEUP;
|
||||
|
||||
if (mtu->g.speed >= USB_SPEED_SUPER) {
|
||||
result[0] |= mtu->u1_enable << USB_DEV_STAT_U1_ENABLED;
|
||||
result[0] |= mtu->u2_enable << USB_DEV_STAT_U2_ENABLED;
|
||||
}
|
||||
|
||||
dev_dbg(mtu->dev, "%s result=%x, U1=%x, U2=%x\n", __func__,
|
||||
result[0], mtu->u1_enable, mtu->u2_enable);
|
||||
|
||||
break;
|
||||
case USB_RECIP_INTERFACE:
|
||||
break;
|
||||
case USB_RECIP_ENDPOINT:
|
||||
epnum = (u8)le16_to_cpu(setup->wIndex);
|
||||
is_in = epnum & USB_DIR_IN;
|
||||
epnum &= USB_ENDPOINT_NUMBER_MASK;
|
||||
|
||||
if (epnum >= mtu->num_eps) {
|
||||
handled = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (!epnum)
|
||||
break;
|
||||
|
||||
mep = (is_in ? mtu->in_eps : mtu->out_eps) + epnum;
|
||||
if (!mep->desc) {
|
||||
handled = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (mep->flags & MTU3_EP_STALL)
|
||||
result[0] |= 1 << USB_ENDPOINT_HALT;
|
||||
|
||||
break;
|
||||
default:
|
||||
/* class, vendor, etc ... delegate */
|
||||
handled = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (handled > 0) {
|
||||
int ret;
|
||||
|
||||
/* prepare a data stage for GET_STATUS */
|
||||
dev_dbg(mtu->dev, "get_status=%x\n", *(u16 *)result);
|
||||
memcpy(mtu->setup_buf, result, sizeof(result));
|
||||
mtu->ep0_req.mep = mtu->ep0;
|
||||
mtu->ep0_req.request.length = 2;
|
||||
mtu->ep0_req.request.buf = &mtu->setup_buf;
|
||||
mtu->ep0_req.request.complete = ep0_dummy_complete;
|
||||
ret = ep0_queue(mtu->ep0, &mtu->ep0_req);
|
||||
if (ret < 0)
|
||||
handled = ret;
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
|
||||
static int handle_test_mode(struct mtu3 *mtu, struct usb_ctrlrequest *setup)
|
||||
{
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
int handled = 1;
|
||||
u32 value = 0;
|
||||
|
||||
switch (le16_to_cpu(setup->wIndex) >> 8) {
|
||||
case TEST_J:
|
||||
dev_dbg(mtu->dev, "TEST_J\n");
|
||||
mtu->test_mode_nr = TEST_J_MODE;
|
||||
break;
|
||||
case TEST_K:
|
||||
dev_dbg(mtu->dev, "TEST_K\n");
|
||||
mtu->test_mode_nr = TEST_K_MODE;
|
||||
break;
|
||||
case TEST_SE0_NAK:
|
||||
dev_dbg(mtu->dev, "TEST_SE0_NAK\n");
|
||||
mtu->test_mode_nr = TEST_SE0_NAK_MODE;
|
||||
break;
|
||||
case TEST_PACKET:
|
||||
dev_dbg(mtu->dev, "TEST_PACKET\n");
|
||||
mtu->test_mode_nr = TEST_PACKET_MODE;
|
||||
break;
|
||||
default:
|
||||
handled = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mtu->test_mode = true;
|
||||
|
||||
/* no TX completion interrupt, and need restart platform after test */
|
||||
if (mtu->test_mode_nr == TEST_PACKET_MODE)
|
||||
ep0_load_test_packet(mtu);
|
||||
|
||||
/* send status before entering test mode. */
|
||||
ep0_do_status_stage(mtu);
|
||||
|
||||
/* wait for ACK status sent by host */
|
||||
readl_poll_timeout(mbase + U3D_EP0CSR, value,
|
||||
!(value & EP0_DATAEND), 5000);
|
||||
|
||||
mtu3_writel(mbase, U3D_USB2_TEST_MODE, mtu->test_mode_nr);
|
||||
|
||||
mtu->ep0_state = MU3D_EP0_STATE_SETUP;
|
||||
|
||||
out:
|
||||
return handled;
|
||||
}
|
||||
|
||||
static int ep0_handle_feature_dev(struct mtu3 *mtu,
|
||||
struct usb_ctrlrequest *setup, bool set)
|
||||
{
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
int handled = -EINVAL;
|
||||
u32 lpc;
|
||||
|
||||
switch (le16_to_cpu(setup->wValue)) {
|
||||
case USB_DEVICE_REMOTE_WAKEUP:
|
||||
mtu->may_wakeup = !!set;
|
||||
handled = 1;
|
||||
break;
|
||||
case USB_DEVICE_TEST_MODE:
|
||||
if (!set || (mtu->g.speed != USB_SPEED_HIGH) ||
|
||||
(le16_to_cpu(setup->wIndex) & 0xff))
|
||||
break;
|
||||
|
||||
handled = handle_test_mode(mtu, setup);
|
||||
break;
|
||||
case USB_DEVICE_U1_ENABLE:
|
||||
if (mtu->g.speed < USB_SPEED_SUPER ||
|
||||
mtu->g.state != USB_STATE_CONFIGURED)
|
||||
break;
|
||||
|
||||
lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL);
|
||||
if (set)
|
||||
lpc |= SW_U1_REQUEST_ENABLE;
|
||||
else
|
||||
lpc &= ~SW_U1_REQUEST_ENABLE;
|
||||
mtu3_writel(mbase, U3D_LINK_POWER_CONTROL, lpc);
|
||||
|
||||
mtu->u1_enable = !!set;
|
||||
handled = 1;
|
||||
break;
|
||||
case USB_DEVICE_U2_ENABLE:
|
||||
if (mtu->g.speed < USB_SPEED_SUPER ||
|
||||
mtu->g.state != USB_STATE_CONFIGURED)
|
||||
break;
|
||||
|
||||
lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL);
|
||||
if (set)
|
||||
lpc |= SW_U2_REQUEST_ENABLE;
|
||||
else
|
||||
lpc &= ~SW_U2_REQUEST_ENABLE;
|
||||
mtu3_writel(mbase, U3D_LINK_POWER_CONTROL, lpc);
|
||||
|
||||
mtu->u2_enable = !!set;
|
||||
handled = 1;
|
||||
break;
|
||||
default:
|
||||
handled = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
|
||||
static int ep0_handle_feature(struct mtu3 *mtu,
|
||||
struct usb_ctrlrequest *setup, bool set)
|
||||
{
|
||||
struct mtu3_ep *mep;
|
||||
int handled = -EINVAL;
|
||||
int is_in;
|
||||
u16 value;
|
||||
u16 index;
|
||||
u8 epnum;
|
||||
|
||||
value = le16_to_cpu(setup->wValue);
|
||||
index = le16_to_cpu(setup->wIndex);
|
||||
|
||||
switch (setup->bRequestType & USB_RECIP_MASK) {
|
||||
case USB_RECIP_DEVICE:
|
||||
handled = ep0_handle_feature_dev(mtu, setup, set);
|
||||
break;
|
||||
case USB_RECIP_INTERFACE:
|
||||
/* superspeed only */
|
||||
if (value == USB_INTRF_FUNC_SUSPEND &&
|
||||
mtu->g.speed >= USB_SPEED_SUPER) {
|
||||
/*
|
||||
* forward the request because function drivers
|
||||
* should handle it
|
||||
*/
|
||||
handled = 0;
|
||||
}
|
||||
break;
|
||||
case USB_RECIP_ENDPOINT:
|
||||
epnum = index & USB_ENDPOINT_NUMBER_MASK;
|
||||
if (epnum == 0 || epnum >= mtu->num_eps ||
|
||||
value != USB_ENDPOINT_HALT)
|
||||
break;
|
||||
|
||||
is_in = index & USB_DIR_IN;
|
||||
mep = (is_in ? mtu->in_eps : mtu->out_eps) + epnum;
|
||||
if (!mep->desc)
|
||||
break;
|
||||
|
||||
handled = 1;
|
||||
/* ignore request if endpoint is wedged */
|
||||
if (mep->flags & MTU3_EP_WEDGE)
|
||||
break;
|
||||
|
||||
mtu3_ep_stall_set(mep, set);
|
||||
break;
|
||||
default:
|
||||
/* class, vendor, etc ... delegate */
|
||||
handled = 0;
|
||||
break;
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
|
||||
/*
|
||||
* handle all control requests can be handled
|
||||
* returns:
|
||||
* negative errno - error happened
|
||||
* zero - need delegate SETUP to gadget driver
|
||||
* positive - already handled
|
||||
*/
|
||||
static int handle_standard_request(struct mtu3 *mtu,
|
||||
struct usb_ctrlrequest *setup)
|
||||
{
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
enum usb_device_state state = mtu->g.state;
|
||||
int handled = -EINVAL;
|
||||
u32 dev_conf;
|
||||
u16 value;
|
||||
|
||||
value = le16_to_cpu(setup->wValue);
|
||||
|
||||
/* the gadget driver handles everything except what we must handle */
|
||||
switch (setup->bRequest) {
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
/* change it after the status stage */
|
||||
mtu->address = (u8)(value & 0x7f);
|
||||
dev_dbg(mtu->dev, "set address to 0x%x\n", mtu->address);
|
||||
|
||||
dev_conf = mtu3_readl(mbase, U3D_DEVICE_CONF);
|
||||
dev_conf &= ~DEV_ADDR_MSK;
|
||||
dev_conf |= DEV_ADDR(mtu->address);
|
||||
mtu3_writel(mbase, U3D_DEVICE_CONF, dev_conf);
|
||||
|
||||
if (mtu->address)
|
||||
usb_gadget_set_state(&mtu->g, USB_STATE_ADDRESS);
|
||||
else
|
||||
usb_gadget_set_state(&mtu->g, USB_STATE_DEFAULT);
|
||||
|
||||
handled = 1;
|
||||
break;
|
||||
case USB_REQ_SET_CONFIGURATION:
|
||||
if (state == USB_STATE_ADDRESS) {
|
||||
usb_gadget_set_state(&mtu->g,
|
||||
USB_STATE_CONFIGURED);
|
||||
} else if (state == USB_STATE_CONFIGURED) {
|
||||
/*
|
||||
* USB2 spec sec 9.4.7, if wValue is 0 then dev
|
||||
* is moved to addressed state
|
||||
*/
|
||||
if (!value)
|
||||
usb_gadget_set_state(&mtu->g,
|
||||
USB_STATE_ADDRESS);
|
||||
}
|
||||
handled = 0;
|
||||
break;
|
||||
case USB_REQ_CLEAR_FEATURE:
|
||||
handled = ep0_handle_feature(mtu, setup, 0);
|
||||
break;
|
||||
case USB_REQ_SET_FEATURE:
|
||||
handled = ep0_handle_feature(mtu, setup, 1);
|
||||
break;
|
||||
case USB_REQ_GET_STATUS:
|
||||
handled = ep0_get_status(mtu, setup);
|
||||
break;
|
||||
case USB_REQ_SET_SEL:
|
||||
handled = ep0_set_sel(mtu, setup);
|
||||
break;
|
||||
case USB_REQ_SET_ISOCH_DELAY:
|
||||
handled = 1;
|
||||
break;
|
||||
default:
|
||||
/* delegate SET_CONFIGURATION, etc */
|
||||
handled = 0;
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
/* receive an data packet (OUT) */
|
||||
static void ep0_rx_state(struct mtu3 *mtu)
|
||||
{
|
||||
struct mtu3_request *mreq;
|
||||
struct usb_request *req;
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
u32 maxp;
|
||||
u32 csr;
|
||||
u16 count = 0;
|
||||
|
||||
dev_dbg(mtu->dev, "%s\n", __func__);
|
||||
|
||||
csr = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS;
|
||||
mreq = next_ep0_request(mtu);
|
||||
req = &mreq->request;
|
||||
|
||||
/* read packet and ack; or stall because of gadget driver bug */
|
||||
if (req) {
|
||||
void *buf = req->buf + req->actual;
|
||||
unsigned int len = req->length - req->actual;
|
||||
|
||||
/* read the buffer */
|
||||
count = mtu3_readl(mbase, U3D_RXCOUNT0);
|
||||
if (count > len) {
|
||||
req->status = -EOVERFLOW;
|
||||
count = len;
|
||||
}
|
||||
ep0_read_fifo(mtu->ep0, buf, count);
|
||||
req->actual += count;
|
||||
csr |= EP0_RXPKTRDY;
|
||||
|
||||
maxp = mtu->g.ep0->maxpacket;
|
||||
if (count < maxp || req->actual == req->length) {
|
||||
mtu->ep0_state = MU3D_EP0_STATE_SETUP;
|
||||
dev_dbg(mtu->dev, "ep0 state: %s\n",
|
||||
decode_ep0_state(mtu));
|
||||
|
||||
csr |= EP0_DATAEND;
|
||||
} else {
|
||||
req = NULL;
|
||||
}
|
||||
} else {
|
||||
csr |= EP0_RXPKTRDY | EP0_SENDSTALL;
|
||||
dev_dbg(mtu->dev, "%s: SENDSTALL\n", __func__);
|
||||
}
|
||||
|
||||
mtu3_writel(mbase, U3D_EP0CSR, csr);
|
||||
|
||||
/* give back the request if have received all data */
|
||||
if (req)
|
||||
ep0_req_giveback(mtu, req);
|
||||
}
|
||||
|
||||
/* transmitting to the host (IN) */
|
||||
static void ep0_tx_state(struct mtu3 *mtu)
|
||||
{
|
||||
struct mtu3_request *mreq = next_ep0_request(mtu);
|
||||
struct usb_request *req;
|
||||
u32 csr;
|
||||
u8 *src;
|
||||
u32 count;
|
||||
u32 maxp;
|
||||
|
||||
dev_dbg(mtu->dev, "%s\n", __func__);
|
||||
|
||||
if (!mreq)
|
||||
return;
|
||||
|
||||
maxp = mtu->g.ep0->maxpacket;
|
||||
req = &mreq->request;
|
||||
|
||||
/* load the data */
|
||||
src = (u8 *)req->buf + req->actual;
|
||||
count = min(maxp, req->length - req->actual);
|
||||
if (count)
|
||||
ep0_write_fifo(mtu->ep0, src, count);
|
||||
|
||||
dev_dbg(mtu->dev, "%s act=%d, len=%d, cnt=%d, maxp=%d zero=%d\n",
|
||||
__func__, req->actual, req->length, count, maxp, req->zero);
|
||||
|
||||
req->actual += count;
|
||||
|
||||
if ((count < maxp) ||
|
||||
((req->actual == req->length) && !req->zero))
|
||||
mtu->ep0_state = MU3D_EP0_STATE_TX_END;
|
||||
|
||||
/* send it out, triggering a "txpktrdy cleared" irq */
|
||||
csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR) & EP0_W1C_BITS;
|
||||
mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr | EP0_TXPKTRDY);
|
||||
|
||||
dev_dbg(mtu->dev, "%s ep0csr=0x%x\n", __func__,
|
||||
mtu3_readl(mtu->mac_base, U3D_EP0CSR));
|
||||
}
|
||||
|
||||
static void ep0_read_setup(struct mtu3 *mtu, struct usb_ctrlrequest *setup)
|
||||
{
|
||||
struct mtu3_request *mreq;
|
||||
u32 count;
|
||||
u32 csr;
|
||||
|
||||
csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR) & EP0_W1C_BITS;
|
||||
count = mtu3_readl(mtu->mac_base, U3D_RXCOUNT0);
|
||||
|
||||
ep0_read_fifo(mtu->ep0, (u8 *)setup, count);
|
||||
|
||||
dev_dbg(mtu->dev, "SETUP req%02x.%02x v%04x i%04x l%04x\n",
|
||||
setup->bRequestType, setup->bRequest,
|
||||
le16_to_cpu(setup->wValue), le16_to_cpu(setup->wIndex),
|
||||
le16_to_cpu(setup->wLength));
|
||||
|
||||
/* clean up any leftover transfers */
|
||||
mreq = next_ep0_request(mtu);
|
||||
if (mreq)
|
||||
ep0_req_giveback(mtu, &mreq->request);
|
||||
|
||||
if (le16_to_cpu(setup->wLength) == 0) {
|
||||
; /* no data stage, nothing to do */
|
||||
} else if (setup->bRequestType & USB_DIR_IN) {
|
||||
mtu3_writel(mtu->mac_base, U3D_EP0CSR,
|
||||
csr | EP0_SETUPPKTRDY | EP0_DPHTX);
|
||||
mtu->ep0_state = MU3D_EP0_STATE_TX;
|
||||
} else {
|
||||
mtu3_writel(mtu->mac_base, U3D_EP0CSR,
|
||||
(csr | EP0_SETUPPKTRDY) & (~EP0_DPHTX));
|
||||
mtu->ep0_state = MU3D_EP0_STATE_RX;
|
||||
}
|
||||
}
|
||||
|
||||
static int ep0_handle_setup(struct mtu3 *mtu)
|
||||
__releases(mtu->lock)
|
||||
__acquires(mtu->lock)
|
||||
{
|
||||
struct usb_ctrlrequest setup;
|
||||
struct mtu3_request *mreq;
|
||||
int handled = 0;
|
||||
|
||||
ep0_read_setup(mtu, &setup);
|
||||
|
||||
if ((setup.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
|
||||
handled = handle_standard_request(mtu, &setup);
|
||||
|
||||
dev_dbg(mtu->dev, "handled %d, ep0_state: %s\n",
|
||||
handled, decode_ep0_state(mtu));
|
||||
|
||||
if (handled < 0)
|
||||
goto stall;
|
||||
else if (handled > 0)
|
||||
goto finish;
|
||||
|
||||
handled = forward_to_driver(mtu, &setup);
|
||||
if (handled < 0) {
|
||||
stall:
|
||||
dev_dbg(mtu->dev, "%s stall (%d)\n", __func__, handled);
|
||||
|
||||
ep0_stall_set(mtu->ep0, true,
|
||||
le16_to_cpu(setup.wLength) ? 0 : EP0_SETUPPKTRDY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
finish:
|
||||
if (mtu->test_mode) {
|
||||
; /* nothing to do */
|
||||
} else if (handled == USB_GADGET_DELAYED_STATUS) {
|
||||
|
||||
mreq = next_ep0_request(mtu);
|
||||
if (mreq) {
|
||||
/* already asked us to continue delayed status */
|
||||
ep0_do_status_stage(mtu);
|
||||
ep0_req_giveback(mtu, &mreq->request);
|
||||
} else {
|
||||
/* do delayed STATUS stage till receive ep0_queue */
|
||||
mtu->delayed_status = true;
|
||||
}
|
||||
} else if (le16_to_cpu(setup.wLength) == 0) { /* no data stage */
|
||||
|
||||
ep0_do_status_stage(mtu);
|
||||
/* complete zlp request directly */
|
||||
mreq = next_ep0_request(mtu);
|
||||
if (mreq && !mreq->request.length)
|
||||
ep0_req_giveback(mtu, &mreq->request);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
irqreturn_t mtu3_ep0_isr(struct mtu3 *mtu)
|
||||
{
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
struct mtu3_request *mreq;
|
||||
u32 int_status;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
u32 csr;
|
||||
u32 len;
|
||||
|
||||
int_status = mtu3_readl(mbase, U3D_EPISR);
|
||||
int_status &= mtu3_readl(mbase, U3D_EPIER);
|
||||
mtu3_writel(mbase, U3D_EPISR, int_status); /* W1C */
|
||||
|
||||
/* only handle ep0's */
|
||||
if (!(int_status & (EP0ISR | SETUPENDISR)))
|
||||
return IRQ_NONE;
|
||||
|
||||
/* abort current SETUP, and process new one */
|
||||
if (int_status & SETUPENDISR)
|
||||
mtu->ep0_state = MU3D_EP0_STATE_SETUP;
|
||||
|
||||
csr = mtu3_readl(mbase, U3D_EP0CSR);
|
||||
|
||||
dev_dbg(mtu->dev, "%s csr=0x%x\n", __func__, csr);
|
||||
|
||||
/* we sent a stall.. need to clear it now.. */
|
||||
if (csr & EP0_SENTSTALL) {
|
||||
ep0_stall_set(mtu->ep0, false, 0);
|
||||
csr = mtu3_readl(mbase, U3D_EP0CSR);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
dev_dbg(mtu->dev, "ep0_state: %s\n", decode_ep0_state(mtu));
|
||||
|
||||
switch (mtu->ep0_state) {
|
||||
case MU3D_EP0_STATE_TX:
|
||||
/* irq on clearing txpktrdy */
|
||||
if ((csr & EP0_FIFOFULL) == 0) {
|
||||
ep0_tx_state(mtu);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
break;
|
||||
case MU3D_EP0_STATE_RX:
|
||||
/* irq on set rxpktrdy */
|
||||
if (csr & EP0_RXPKTRDY) {
|
||||
ep0_rx_state(mtu);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
break;
|
||||
case MU3D_EP0_STATE_TX_END:
|
||||
mtu3_writel(mbase, U3D_EP0CSR,
|
||||
(csr & EP0_W1C_BITS) | EP0_DATAEND);
|
||||
|
||||
mreq = next_ep0_request(mtu);
|
||||
if (mreq)
|
||||
ep0_req_giveback(mtu, &mreq->request);
|
||||
|
||||
mtu->ep0_state = MU3D_EP0_STATE_SETUP;
|
||||
ret = IRQ_HANDLED;
|
||||
dev_dbg(mtu->dev, "ep0_state: %s\n", decode_ep0_state(mtu));
|
||||
break;
|
||||
case MU3D_EP0_STATE_SETUP:
|
||||
if (!(csr & EP0_SETUPPKTRDY))
|
||||
break;
|
||||
|
||||
len = mtu3_readl(mbase, U3D_RXCOUNT0);
|
||||
if (len != 8) {
|
||||
dev_err(mtu->dev, "SETUP packet len %d != 8 ?\n", len);
|
||||
break;
|
||||
}
|
||||
|
||||
ep0_handle_setup(mtu);
|
||||
ret = IRQ_HANDLED;
|
||||
break;
|
||||
default:
|
||||
/* can't happen */
|
||||
ep0_stall_set(mtu->ep0, true, 0);
|
||||
WARN_ON(1);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtu3_ep0_enable(struct usb_ep *ep,
|
||||
const struct usb_endpoint_descriptor *desc)
|
||||
{
|
||||
/* always enabled */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mtu3_ep0_disable(struct usb_ep *ep)
|
||||
{
|
||||
/* always enabled */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ep0_queue(struct mtu3_ep *mep, struct mtu3_request *mreq)
|
||||
{
|
||||
struct mtu3 *mtu = mep->mtu;
|
||||
|
||||
mreq->mtu = mtu;
|
||||
mreq->request.actual = 0;
|
||||
mreq->request.status = -EINPROGRESS;
|
||||
|
||||
dev_dbg(mtu->dev, "%s %s (ep0_state: %s), len#%d\n", __func__,
|
||||
mep->name, decode_ep0_state(mtu), mreq->request.length);
|
||||
|
||||
switch (mtu->ep0_state) {
|
||||
case MU3D_EP0_STATE_SETUP:
|
||||
case MU3D_EP0_STATE_RX: /* control-OUT data */
|
||||
case MU3D_EP0_STATE_TX: /* control-IN data */
|
||||
break;
|
||||
default:
|
||||
dev_err(mtu->dev, "%s, error in ep0 state %s\n", __func__,
|
||||
decode_ep0_state(mtu));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (mtu->delayed_status) {
|
||||
mtu->delayed_status = false;
|
||||
ep0_do_status_stage(mtu);
|
||||
/* needn't giveback the request for handling delay STATUS */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!list_empty(&mep->req_list))
|
||||
return -EBUSY;
|
||||
|
||||
list_add_tail(&mreq->list, &mep->req_list);
|
||||
|
||||
/* sequence #1, IN ... start writing the data */
|
||||
if (mtu->ep0_state == MU3D_EP0_STATE_TX)
|
||||
ep0_tx_state(mtu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtu3_ep0_queue(struct usb_ep *ep,
|
||||
struct usb_request *req, gfp_t gfp)
|
||||
{
|
||||
struct mtu3_ep *mep;
|
||||
struct mtu3_request *mreq;
|
||||
struct mtu3 *mtu;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
if (!ep || !req)
|
||||
return -EINVAL;
|
||||
|
||||
mep = to_mtu3_ep(ep);
|
||||
mtu = mep->mtu;
|
||||
mreq = to_mtu3_request(req);
|
||||
|
||||
spin_lock_irqsave(&mtu->lock, flags);
|
||||
ret = ep0_queue(mep, mreq);
|
||||
spin_unlock_irqrestore(&mtu->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtu3_ep0_dequeue(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
/* we just won't support this */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mtu3_ep0_halt(struct usb_ep *ep, int value)
|
||||
{
|
||||
struct mtu3_ep *mep;
|
||||
struct mtu3 *mtu;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
if (!ep || !value)
|
||||
return -EINVAL;
|
||||
|
||||
mep = to_mtu3_ep(ep);
|
||||
mtu = mep->mtu;
|
||||
|
||||
dev_dbg(mtu->dev, "%s\n", __func__);
|
||||
|
||||
spin_lock_irqsave(&mtu->lock, flags);
|
||||
|
||||
if (!list_empty(&mep->req_list)) {
|
||||
ret = -EBUSY;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
switch (mtu->ep0_state) {
|
||||
/*
|
||||
* stalls are usually issued after parsing SETUP packet, either
|
||||
* directly in irq context from setup() or else later.
|
||||
*/
|
||||
case MU3D_EP0_STATE_TX:
|
||||
case MU3D_EP0_STATE_TX_END:
|
||||
case MU3D_EP0_STATE_RX:
|
||||
case MU3D_EP0_STATE_SETUP:
|
||||
ep0_stall_set(mtu->ep0, true, 0);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(mtu->dev, "ep0 can't halt in state %s\n",
|
||||
decode_ep0_state(mtu));
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
spin_unlock_irqrestore(&mtu->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct usb_ep_ops mtu3_ep0_ops = {
|
||||
.enable = mtu3_ep0_enable,
|
||||
.disable = mtu3_ep0_disable,
|
||||
.alloc_request = mtu3_alloc_request,
|
||||
.free_request = mtu3_free_request,
|
||||
.queue = mtu3_ep0_queue,
|
||||
.dequeue = mtu3_ep0_dequeue,
|
||||
.set_halt = mtu3_ep0_halt,
|
||||
};
|
141
drivers/usb/mtu3/mtu3_host.c
Normal file
141
drivers/usb/mtu3/mtu3_host.c
Normal file
|
@ -0,0 +1,141 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* mtu3_dr.c - dual role switch and host glue layer
|
||||
*
|
||||
* Copyright (C) 2016 MediaTek Inc.
|
||||
*
|
||||
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
|
||||
*/
|
||||
|
||||
#include <dm/lists.h>
|
||||
#include <linux/iopoll.h>
|
||||
|
||||
#include "mtu3.h"
|
||||
#include "mtu3_dr.h"
|
||||
|
||||
static void host_ports_num_get(struct mtu3_host *u3h)
|
||||
{
|
||||
u32 xhci_cap;
|
||||
|
||||
xhci_cap = mtu3_readl(u3h->ippc_base, U3D_SSUSB_IP_XHCI_CAP);
|
||||
u3h->u2_ports = SSUSB_IP_XHCI_U2_PORT_NUM(xhci_cap);
|
||||
u3h->u3_ports = SSUSB_IP_XHCI_U3_PORT_NUM(xhci_cap);
|
||||
|
||||
dev_dbg(u3h->dev, "host - u2_ports:%d, u3_ports:%d\n",
|
||||
u3h->u2_ports, u3h->u3_ports);
|
||||
}
|
||||
|
||||
/* only configure ports will be used later */
|
||||
static int ssusb_host_enable(struct mtu3_host *u3h)
|
||||
{
|
||||
void __iomem *ibase = u3h->ippc_base;
|
||||
int num_u3p = u3h->u3_ports;
|
||||
int num_u2p = u3h->u2_ports;
|
||||
int u3_ports_disabed;
|
||||
u32 check_clk;
|
||||
u32 value;
|
||||
int i;
|
||||
|
||||
/* power on host ip */
|
||||
mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN);
|
||||
|
||||
/* power on and enable u3 ports except skipped ones */
|
||||
u3_ports_disabed = 0;
|
||||
for (i = 0; i < num_u3p; i++) {
|
||||
if ((0x1 << i) & u3h->u3p_dis_msk) {
|
||||
u3_ports_disabed++;
|
||||
continue;
|
||||
}
|
||||
|
||||
value = mtu3_readl(ibase, SSUSB_U3_CTRL(i));
|
||||
value &= ~(SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS);
|
||||
value |= SSUSB_U3_PORT_HOST_SEL;
|
||||
mtu3_writel(ibase, SSUSB_U3_CTRL(i), value);
|
||||
}
|
||||
|
||||
/* power on and enable all u2 ports */
|
||||
for (i = 0; i < num_u2p; i++) {
|
||||
value = mtu3_readl(ibase, SSUSB_U2_CTRL(i));
|
||||
value &= ~(SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS);
|
||||
value |= SSUSB_U2_PORT_HOST_SEL;
|
||||
mtu3_writel(ibase, SSUSB_U2_CTRL(i), value);
|
||||
}
|
||||
|
||||
check_clk = SSUSB_XHCI_RST_B_STS;
|
||||
if (num_u3p > u3_ports_disabed)
|
||||
check_clk = SSUSB_U3_MAC_RST_B_STS;
|
||||
|
||||
return ssusb_check_clocks(u3h->ssusb, check_clk);
|
||||
}
|
||||
|
||||
static void ssusb_host_disable(struct mtu3_host *u3h)
|
||||
{
|
||||
void __iomem *ibase = u3h->ippc_base;
|
||||
int num_u3p = u3h->u3_ports;
|
||||
int num_u2p = u3h->u2_ports;
|
||||
u32 value;
|
||||
int i;
|
||||
|
||||
/* power down and disable u3 ports except skipped ones */
|
||||
for (i = 0; i < num_u3p; i++) {
|
||||
if ((0x1 << i) & u3h->u3p_dis_msk)
|
||||
continue;
|
||||
|
||||
value = mtu3_readl(ibase, SSUSB_U3_CTRL(i));
|
||||
value |= SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS;
|
||||
mtu3_writel(ibase, SSUSB_U3_CTRL(i), value);
|
||||
}
|
||||
|
||||
/* power down and disable all u2 ports */
|
||||
for (i = 0; i < num_u2p; i++) {
|
||||
value = mtu3_readl(ibase, SSUSB_U2_CTRL(i));
|
||||
value |= SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS;
|
||||
mtu3_writel(ibase, SSUSB_U2_CTRL(i), value);
|
||||
}
|
||||
|
||||
/* power down host ip */
|
||||
mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN);
|
||||
}
|
||||
|
||||
/*
|
||||
* If host supports multiple ports, the VBUSes(5V) of ports except port0
|
||||
* which supports OTG are better to be enabled by default in DTS.
|
||||
* Because the host driver will keep link with devices attached when system
|
||||
* enters suspend mode, so no need to control VBUSes after initialization.
|
||||
*/
|
||||
int ssusb_host_init(struct ssusb_mtk *ssusb)
|
||||
{
|
||||
struct mtu3_host *u3h = ssusb->u3h;
|
||||
struct udevice *dev = u3h->dev;
|
||||
int ret;
|
||||
|
||||
u3h->ssusb = ssusb;
|
||||
u3h->hcd = ssusb->mac_base;
|
||||
u3h->ippc_base = ssusb->ippc_base;
|
||||
|
||||
/* optional property, ignore the error */
|
||||
dev_read_u32(dev, "mediatek,u3p-dis-msk", &u3h->u3p_dis_msk);
|
||||
|
||||
host_ports_num_get(u3h);
|
||||
ret = ssusb_host_enable(u3h);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST);
|
||||
|
||||
ret = regulator_set_enable(ssusb->vbus_supply, true);
|
||||
if (ret < 0 && ret != -ENOSYS) {
|
||||
dev_err(dev, "failed to enable vbus %d!\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(dev, "%s done...\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ssusb_host_exit(struct ssusb_mtk *ssusb)
|
||||
{
|
||||
regulator_set_enable(ssusb->vbus_supply, false);
|
||||
ssusb_host_disable(ssusb->u3h);
|
||||
}
|
515
drivers/usb/mtu3/mtu3_hw_regs.h
Normal file
515
drivers/usb/mtu3/mtu3_hw_regs.h
Normal file
|
@ -0,0 +1,515 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* mtu3_hw_regs.h - MediaTek USB3 DRD register and field definitions
|
||||
*
|
||||
* Copyright (C) 2016 MediaTek Inc.
|
||||
*
|
||||
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
|
||||
*/
|
||||
|
||||
#ifndef _SSUSB_HW_REGS_H_
|
||||
#define _SSUSB_HW_REGS_H_
|
||||
|
||||
/* segment offset of MAC register */
|
||||
#define SSUSB_XCHI_BASE 0x0000
|
||||
#define SSUSB_DEV_BASE 0x1000
|
||||
#define SSUSB_EPCTL_CSR_BASE 0x1800
|
||||
#define SSUSB_USB3_MAC_CSR_BASE 0x2400
|
||||
#define SSUSB_USB3_SYS_CSR_BASE 0x2400
|
||||
#define SSUSB_USB2_CSR_BASE 0x3400
|
||||
|
||||
/* IPPC register in Infra */
|
||||
#define SSUSB_SIFSLV_IPPC_BASE 0x0000
|
||||
|
||||
/* --------------- SSUSB_DEV REGISTER DEFINITION --------------- */
|
||||
|
||||
#define U3D_LV1ISR (SSUSB_DEV_BASE + 0x0000)
|
||||
#define U3D_LV1IER (SSUSB_DEV_BASE + 0x0004)
|
||||
#define U3D_LV1IESR (SSUSB_DEV_BASE + 0x0008)
|
||||
#define U3D_LV1IECR (SSUSB_DEV_BASE + 0x000C)
|
||||
|
||||
#define U3D_EPISR (SSUSB_DEV_BASE + 0x0080)
|
||||
#define U3D_EPIER (SSUSB_DEV_BASE + 0x0084)
|
||||
#define U3D_EPIESR (SSUSB_DEV_BASE + 0x0088)
|
||||
#define U3D_EPIECR (SSUSB_DEV_BASE + 0x008C)
|
||||
|
||||
#define U3D_EP0CSR (SSUSB_DEV_BASE + 0x0100)
|
||||
#define U3D_RXCOUNT0 (SSUSB_DEV_BASE + 0x0108)
|
||||
#define U3D_RESERVED (SSUSB_DEV_BASE + 0x010C)
|
||||
#define U3D_TX1CSR0 (SSUSB_DEV_BASE + 0x0110)
|
||||
#define U3D_TX1CSR1 (SSUSB_DEV_BASE + 0x0114)
|
||||
#define U3D_TX1CSR2 (SSUSB_DEV_BASE + 0x0118)
|
||||
|
||||
#define U3D_RX1CSR0 (SSUSB_DEV_BASE + 0x0210)
|
||||
#define U3D_RX1CSR1 (SSUSB_DEV_BASE + 0x0214)
|
||||
#define U3D_RX1CSR2 (SSUSB_DEV_BASE + 0x0218)
|
||||
|
||||
#define U3D_FIFO0 (SSUSB_DEV_BASE + 0x0300)
|
||||
|
||||
#define U3D_QCR0 (SSUSB_DEV_BASE + 0x0400)
|
||||
#define U3D_QCR1 (SSUSB_DEV_BASE + 0x0404)
|
||||
#define U3D_QCR2 (SSUSB_DEV_BASE + 0x0408)
|
||||
#define U3D_QCR3 (SSUSB_DEV_BASE + 0x040C)
|
||||
#define U3D_QFCR (SSUSB_DEV_BASE + 0x0428)
|
||||
|
||||
#define U3D_TXQCSR1 (SSUSB_DEV_BASE + 0x0510)
|
||||
#define U3D_TXQSAR1 (SSUSB_DEV_BASE + 0x0514)
|
||||
#define U3D_TXQCPR1 (SSUSB_DEV_BASE + 0x0518)
|
||||
|
||||
#define U3D_RXQCSR1 (SSUSB_DEV_BASE + 0x0610)
|
||||
#define U3D_RXQSAR1 (SSUSB_DEV_BASE + 0x0614)
|
||||
#define U3D_RXQCPR1 (SSUSB_DEV_BASE + 0x0618)
|
||||
#define U3D_RXQLDPR1 (SSUSB_DEV_BASE + 0x061C)
|
||||
|
||||
#define U3D_QISAR0 (SSUSB_DEV_BASE + 0x0700)
|
||||
#define U3D_QIER0 (SSUSB_DEV_BASE + 0x0704)
|
||||
#define U3D_QIESR0 (SSUSB_DEV_BASE + 0x0708)
|
||||
#define U3D_QIECR0 (SSUSB_DEV_BASE + 0x070C)
|
||||
#define U3D_QISAR1 (SSUSB_DEV_BASE + 0x0710)
|
||||
#define U3D_QIER1 (SSUSB_DEV_BASE + 0x0714)
|
||||
#define U3D_QIESR1 (SSUSB_DEV_BASE + 0x0718)
|
||||
#define U3D_QIECR1 (SSUSB_DEV_BASE + 0x071C)
|
||||
|
||||
#define U3D_TQERRIR0 (SSUSB_DEV_BASE + 0x0780)
|
||||
#define U3D_TQERRIER0 (SSUSB_DEV_BASE + 0x0784)
|
||||
#define U3D_TQERRIESR0 (SSUSB_DEV_BASE + 0x0788)
|
||||
#define U3D_TQERRIECR0 (SSUSB_DEV_BASE + 0x078C)
|
||||
#define U3D_RQERRIR0 (SSUSB_DEV_BASE + 0x07C0)
|
||||
#define U3D_RQERRIER0 (SSUSB_DEV_BASE + 0x07C4)
|
||||
#define U3D_RQERRIESR0 (SSUSB_DEV_BASE + 0x07C8)
|
||||
#define U3D_RQERRIECR0 (SSUSB_DEV_BASE + 0x07CC)
|
||||
#define U3D_RQERRIR1 (SSUSB_DEV_BASE + 0x07D0)
|
||||
#define U3D_RQERRIER1 (SSUSB_DEV_BASE + 0x07D4)
|
||||
#define U3D_RQERRIESR1 (SSUSB_DEV_BASE + 0x07D8)
|
||||
#define U3D_RQERRIECR1 (SSUSB_DEV_BASE + 0x07DC)
|
||||
|
||||
#define U3D_CAP_EP0FFSZ (SSUSB_DEV_BASE + 0x0C04)
|
||||
#define U3D_CAP_EPNTXFFSZ (SSUSB_DEV_BASE + 0x0C08)
|
||||
#define U3D_CAP_EPNRXFFSZ (SSUSB_DEV_BASE + 0x0C0C)
|
||||
#define U3D_CAP_EPINFO (SSUSB_DEV_BASE + 0x0C10)
|
||||
#define U3D_MISC_CTRL (SSUSB_DEV_BASE + 0x0C84)
|
||||
|
||||
/*---------------- SSUSB_DEV FIELD DEFINITION ---------------*/
|
||||
|
||||
/* U3D_LV1ISR */
|
||||
#define EP_CTRL_INTR BIT(5)
|
||||
#define MAC2_INTR BIT(4)
|
||||
#define DMA_INTR BIT(3)
|
||||
#define MAC3_INTR BIT(2)
|
||||
#define QMU_INTR BIT(1)
|
||||
#define BMU_INTR BIT(0)
|
||||
|
||||
/* U3D_LV1IECR */
|
||||
#define LV1IECR_MSK GENMASK(31, 0)
|
||||
|
||||
/* U3D_EPISR */
|
||||
#define EPRISR(x) (BIT(16) << (x))
|
||||
#define SETUPENDISR BIT(16)
|
||||
#define EPTISR(x) (BIT(0) << (x))
|
||||
#define EP0ISR BIT(0)
|
||||
|
||||
/* U3D_EP0CSR */
|
||||
#define EP0_SENDSTALL BIT(25)
|
||||
#define EP0_FIFOFULL BIT(23)
|
||||
#define EP0_SENTSTALL BIT(22)
|
||||
#define EP0_DPHTX BIT(20)
|
||||
#define EP0_DATAEND BIT(19)
|
||||
#define EP0_TXPKTRDY BIT(18)
|
||||
#define EP0_SETUPPKTRDY BIT(17)
|
||||
#define EP0_RXPKTRDY BIT(16)
|
||||
#define EP0_MAXPKTSZ_MSK GENMASK(9, 0)
|
||||
#define EP0_MAXPKTSZ(x) ((x) & EP0_MAXPKTSZ_MSK)
|
||||
#define EP0_W1C_BITS (~(EP0_RXPKTRDY | EP0_SETUPPKTRDY | EP0_SENTSTALL))
|
||||
|
||||
/* U3D_TX1CSR0 */
|
||||
#define TX_DMAREQEN BIT(29)
|
||||
#define TX_FIFOFULL BIT(25)
|
||||
#define TX_FIFOEMPTY BIT(24)
|
||||
#define TX_SENTSTALL BIT(22)
|
||||
#define TX_SENDSTALL BIT(21)
|
||||
#define TX_TXPKTRDY BIT(16)
|
||||
#define TX_TXMAXPKTSZ_MSK GENMASK(10, 0)
|
||||
#define TX_TXMAXPKTSZ(x) ((x) & TX_TXMAXPKTSZ_MSK)
|
||||
#define TX_W1C_BITS (~(TX_SENTSTALL))
|
||||
|
||||
/* U3D_TX1CSR1 */
|
||||
#define TX_MAX_PKT_G2(x) (((x) & 0xff) << 24)
|
||||
#define TX_MULT_G2(x) (((x) & 0x7) << 21)
|
||||
#define TX_MULT_OG(x) (((x) & 0x3) << 22)
|
||||
#define TX_MAX_PKT_OG(x) (((x) & 0x3f) << 16)
|
||||
#define TX_SLOT(x) (((x) & 0x3f) << 8)
|
||||
#define TX_TYPE(x) (((x) & 0x3) << 4)
|
||||
#define TX_SS_BURST(x) (((x) & 0xf) << 0)
|
||||
#define TX_MULT(g2c, x) \
|
||||
({ \
|
||||
typeof(x) x_ = (x); \
|
||||
(g2c) ? TX_MULT_G2(x_) : TX_MULT_OG(x_); \
|
||||
})
|
||||
#define TX_MAX_PKT(g2c, x) \
|
||||
({ \
|
||||
typeof(x) x_ = (x); \
|
||||
(g2c) ? TX_MAX_PKT_G2(x_) : TX_MAX_PKT_OG(x_); \
|
||||
})
|
||||
|
||||
/* for TX_TYPE & RX_TYPE */
|
||||
#define TYPE_BULK (0x0)
|
||||
#define TYPE_INT (0x1)
|
||||
#define TYPE_ISO (0x2)
|
||||
#define TYPE_MASK (0x3)
|
||||
|
||||
/* U3D_TX1CSR2 */
|
||||
#define TX_BINTERVAL(x) (((x) & 0xff) << 24)
|
||||
#define TX_FIFOSEGSIZE(x) (((x) & 0xf) << 16)
|
||||
#define TX_FIFOADDR(x) (((x) & 0x1fff) << 0)
|
||||
|
||||
/* U3D_RX1CSR0 */
|
||||
#define RX_DMAREQEN BIT(29)
|
||||
#define RX_SENTSTALL BIT(22)
|
||||
#define RX_SENDSTALL BIT(21)
|
||||
#define RX_RXPKTRDY BIT(16)
|
||||
#define RX_RXMAXPKTSZ_MSK GENMASK(10, 0)
|
||||
#define RX_RXMAXPKTSZ(x) ((x) & RX_RXMAXPKTSZ_MSK)
|
||||
#define RX_W1C_BITS (~(RX_SENTSTALL | RX_RXPKTRDY))
|
||||
|
||||
/* U3D_RX1CSR1 */
|
||||
#define RX_MAX_PKT_G2(x) (((x) & 0xff) << 24)
|
||||
#define RX_MULT_G2(x) (((x) & 0x7) << 21)
|
||||
#define RX_MULT_OG(x) (((x) & 0x3) << 22)
|
||||
#define RX_MAX_PKT_OG(x) (((x) & 0x3f) << 16)
|
||||
#define RX_SLOT(x) (((x) & 0x3f) << 8)
|
||||
#define RX_TYPE(x) (((x) & 0x3) << 4)
|
||||
#define RX_SS_BURST(x) (((x) & 0xf) << 0)
|
||||
#define RX_MULT(g2c, x) \
|
||||
({ \
|
||||
typeof(x) x_ = (x); \
|
||||
(g2c) ? RX_MULT_G2(x_) : RX_MULT_OG(x_); \
|
||||
})
|
||||
#define RX_MAX_PKT(g2c, x) \
|
||||
({ \
|
||||
typeof(x) x_ = (x); \
|
||||
(g2c) ? RX_MAX_PKT_G2(x_) : RX_MAX_PKT_OG(x_); \
|
||||
})
|
||||
|
||||
/* U3D_RX1CSR2 */
|
||||
#define RX_BINTERVAL(x) (((x) & 0xff) << 24)
|
||||
#define RX_FIFOSEGSIZE(x) (((x) & 0xf) << 16)
|
||||
#define RX_FIFOADDR(x) (((x) & 0x1fff) << 0)
|
||||
|
||||
/* U3D_QCR0 */
|
||||
#define QMU_RX_CS_EN(x) (BIT(16) << (x))
|
||||
#define QMU_TX_CS_EN(x) (BIT(0) << (x))
|
||||
#define QMU_CS16B_EN BIT(0)
|
||||
|
||||
/* U3D_QCR1 */
|
||||
#define QMU_TX_ZLP(x) (BIT(0) << (x))
|
||||
|
||||
/* U3D_QCR3 */
|
||||
#define QMU_RX_COZ(x) (BIT(16) << (x))
|
||||
#define QMU_RX_ZLP(x) (BIT(0) << (x))
|
||||
|
||||
/* U3D_TXQCSR1 */
|
||||
/* U3D_RXQCSR1 */
|
||||
#define QMU_Q_ACTIVE BIT(15)
|
||||
#define QMU_Q_STOP BIT(2)
|
||||
#define QMU_Q_RESUME BIT(1)
|
||||
#define QMU_Q_START BIT(0)
|
||||
|
||||
/* U3D_QISAR0, U3D_QIER0, U3D_QIESR0, U3D_QIECR0 */
|
||||
#define QMU_RX_DONE_INT(x) (BIT(16) << (x))
|
||||
#define QMU_TX_DONE_INT(x) (BIT(0) << (x))
|
||||
|
||||
/* U3D_QISAR1, U3D_QIER1, U3D_QIESR1, U3D_QIECR1 */
|
||||
#define RXQ_ZLPERR_INT BIT(20)
|
||||
#define RXQ_LENERR_INT BIT(18)
|
||||
#define RXQ_CSERR_INT BIT(17)
|
||||
#define RXQ_EMPTY_INT BIT(16)
|
||||
#define TXQ_LENERR_INT BIT(2)
|
||||
#define TXQ_CSERR_INT BIT(1)
|
||||
#define TXQ_EMPTY_INT BIT(0)
|
||||
|
||||
/* U3D_TQERRIR0, U3D_TQERRIER0, U3D_TQERRIESR0, U3D_TQERRIECR0 */
|
||||
#define QMU_TX_LEN_ERR(x) (BIT(16) << (x))
|
||||
#define QMU_TX_CS_ERR(x) (BIT(0) << (x))
|
||||
|
||||
/* U3D_RQERRIR0, U3D_RQERRIER0, U3D_RQERRIESR0, U3D_RQERRIECR0 */
|
||||
#define QMU_RX_LEN_ERR(x) (BIT(16) << (x))
|
||||
#define QMU_RX_CS_ERR(x) (BIT(0) << (x))
|
||||
|
||||
/* U3D_RQERRIR1, U3D_RQERRIER1, U3D_RQERRIESR1, U3D_RQERRIECR1 */
|
||||
#define QMU_RX_ZLP_ERR(n) (BIT(16) << (n))
|
||||
|
||||
/* U3D_CAP_EPINFO */
|
||||
#define CAP_RX_EP_NUM(x) (((x) >> 8) & 0x1f)
|
||||
#define CAP_TX_EP_NUM(x) ((x) & 0x1f)
|
||||
|
||||
/* U3D_MISC_CTRL */
|
||||
#define VBUS_ON BIT(1)
|
||||
#define VBUS_FRC_EN BIT(0)
|
||||
|
||||
/*---------------- SSUSB_EPCTL_CSR REGISTER DEFINITION ----------------*/
|
||||
|
||||
#define U3D_DEVICE_CONF (SSUSB_EPCTL_CSR_BASE + 0x0000)
|
||||
#define U3D_EP_RST (SSUSB_EPCTL_CSR_BASE + 0x0004)
|
||||
|
||||
#define U3D_DEV_LINK_INTR_ENABLE (SSUSB_EPCTL_CSR_BASE + 0x0050)
|
||||
#define U3D_DEV_LINK_INTR (SSUSB_EPCTL_CSR_BASE + 0x0054)
|
||||
|
||||
/*---------------- SSUSB_EPCTL_CSR FIELD DEFINITION ----------------*/
|
||||
|
||||
/* U3D_DEVICE_CONF */
|
||||
#define DEV_ADDR_MSK GENMASK(30, 24)
|
||||
#define DEV_ADDR(x) ((0x7f & (x)) << 24)
|
||||
#define HW_USB2_3_SEL BIT(18)
|
||||
#define SW_USB2_3_SEL_EN BIT(17)
|
||||
#define SW_USB2_3_SEL BIT(16)
|
||||
#define SSUSB_DEV_SPEED(x) ((x) & 0x7)
|
||||
|
||||
/* U3D_EP_RST */
|
||||
#define EP1_IN_RST BIT(17)
|
||||
#define EP1_OUT_RST BIT(1)
|
||||
#define EP_RST(is_in, epnum) (((is_in) ? BIT(16) : BIT(0)) << (epnum))
|
||||
#define EP0_RST BIT(0)
|
||||
|
||||
/* U3D_DEV_LINK_INTR_ENABLE */
|
||||
/* U3D_DEV_LINK_INTR */
|
||||
#define SSUSB_DEV_SPEED_CHG_INTR BIT(0)
|
||||
|
||||
/*---------------- SSUSB_USB3_MAC_CSR REGISTER DEFINITION ----------------*/
|
||||
|
||||
#define U3D_LTSSM_CTRL (SSUSB_USB3_MAC_CSR_BASE + 0x0010)
|
||||
#define U3D_USB3_CONFIG (SSUSB_USB3_MAC_CSR_BASE + 0x001C)
|
||||
|
||||
#define U3D_LINK_STATE_MACHINE (SSUSB_USB3_MAC_CSR_BASE + 0x0134)
|
||||
#define U3D_LTSSM_INTR_ENABLE (SSUSB_USB3_MAC_CSR_BASE + 0x013C)
|
||||
#define U3D_LTSSM_INTR (SSUSB_USB3_MAC_CSR_BASE + 0x0140)
|
||||
|
||||
#define U3D_U3U2_SWITCH_CTRL (SSUSB_USB3_MAC_CSR_BASE + 0x0170)
|
||||
|
||||
/*---------------- SSUSB_USB3_MAC_CSR FIELD DEFINITION ----------------*/
|
||||
|
||||
/* U3D_LTSSM_CTRL */
|
||||
#define FORCE_POLLING_FAIL BIT(4)
|
||||
#define FORCE_RXDETECT_FAIL BIT(3)
|
||||
#define SOFT_U3_EXIT_EN BIT(2)
|
||||
#define COMPLIANCE_EN BIT(1)
|
||||
#define U1_GO_U2_EN BIT(0)
|
||||
|
||||
/* U3D_USB3_CONFIG */
|
||||
#define USB3_EN BIT(0)
|
||||
|
||||
/* U3D_LINK_STATE_MACHINE */
|
||||
#define LTSSM_STATE(x) ((x) & 0x1f)
|
||||
|
||||
/* U3D_LTSSM_INTR_ENABLE */
|
||||
/* U3D_LTSSM_INTR */
|
||||
#define U3_RESUME_INTR BIT(18)
|
||||
#define U3_LFPS_TMOUT_INTR BIT(17)
|
||||
#define VBUS_FALL_INTR BIT(16)
|
||||
#define VBUS_RISE_INTR BIT(15)
|
||||
#define RXDET_SUCCESS_INTR BIT(14)
|
||||
#define EXIT_U3_INTR BIT(13)
|
||||
#define EXIT_U2_INTR BIT(12)
|
||||
#define EXIT_U1_INTR BIT(11)
|
||||
#define ENTER_U3_INTR BIT(10)
|
||||
#define ENTER_U2_INTR BIT(9)
|
||||
#define ENTER_U1_INTR BIT(8)
|
||||
#define ENTER_U0_INTR BIT(7)
|
||||
#define RECOVERY_INTR BIT(6)
|
||||
#define WARM_RST_INTR BIT(5)
|
||||
#define HOT_RST_INTR BIT(4)
|
||||
#define LOOPBACK_INTR BIT(3)
|
||||
#define COMPLIANCE_INTR BIT(2)
|
||||
#define SS_DISABLE_INTR BIT(1)
|
||||
#define SS_INACTIVE_INTR BIT(0)
|
||||
|
||||
/* U3D_U3U2_SWITCH_CTRL */
|
||||
#define SOFTCON_CLR_AUTO_EN BIT(0)
|
||||
|
||||
/*---------------- SSUSB_USB3_SYS_CSR REGISTER DEFINITION ----------------*/
|
||||
|
||||
#define U3D_LINK_UX_INACT_TIMER (SSUSB_USB3_SYS_CSR_BASE + 0x020C)
|
||||
#define U3D_LINK_POWER_CONTROL (SSUSB_USB3_SYS_CSR_BASE + 0x0210)
|
||||
#define U3D_LINK_ERR_COUNT (SSUSB_USB3_SYS_CSR_BASE + 0x0214)
|
||||
|
||||
/*---------------- SSUSB_USB3_SYS_CSR FIELD DEFINITION ----------------*/
|
||||
|
||||
/* U3D_LINK_UX_INACT_TIMER */
|
||||
#define DEV_U2_INACT_TIMEOUT_MSK GENMASK(23, 16)
|
||||
#define DEV_U2_INACT_TIMEOUT_VALUE(x) (((x) & 0xff) << 16)
|
||||
#define U2_INACT_TIMEOUT_MSK GENMASK(15, 8)
|
||||
#define U1_INACT_TIMEOUT_MSK GENMASK(7, 0)
|
||||
#define U1_INACT_TIMEOUT_VALUE(x) ((x) & 0xff)
|
||||
|
||||
/* U3D_LINK_POWER_CONTROL */
|
||||
#define SW_U2_ACCEPT_ENABLE BIT(9)
|
||||
#define SW_U1_ACCEPT_ENABLE BIT(8)
|
||||
#define UX_EXIT BIT(5)
|
||||
#define LGO_U3 BIT(4)
|
||||
#define LGO_U2 BIT(3)
|
||||
#define LGO_U1 BIT(2)
|
||||
#define SW_U2_REQUEST_ENABLE BIT(1)
|
||||
#define SW_U1_REQUEST_ENABLE BIT(0)
|
||||
|
||||
/* U3D_LINK_ERR_COUNT */
|
||||
#define CLR_LINK_ERR_CNT BIT(16)
|
||||
#define LINK_ERROR_COUNT GENMASK(15, 0)
|
||||
|
||||
/*---------------- SSUSB_USB2_CSR REGISTER DEFINITION ----------------*/
|
||||
|
||||
#define U3D_POWER_MANAGEMENT (SSUSB_USB2_CSR_BASE + 0x0004)
|
||||
#define U3D_DEVICE_CONTROL (SSUSB_USB2_CSR_BASE + 0x000C)
|
||||
#define U3D_USB2_TEST_MODE (SSUSB_USB2_CSR_BASE + 0x0014)
|
||||
#define U3D_COMMON_USB_INTR_ENABLE (SSUSB_USB2_CSR_BASE + 0x0018)
|
||||
#define U3D_COMMON_USB_INTR (SSUSB_USB2_CSR_BASE + 0x001C)
|
||||
#define U3D_LINK_RESET_INFO (SSUSB_USB2_CSR_BASE + 0x0024)
|
||||
#define U3D_USB20_FRAME_NUM (SSUSB_USB2_CSR_BASE + 0x003C)
|
||||
#define U3D_USB20_LPM_PARAMETER (SSUSB_USB2_CSR_BASE + 0x0044)
|
||||
#define U3D_USB20_MISC_CONTROL (SSUSB_USB2_CSR_BASE + 0x004C)
|
||||
#define U3D_USB20_OPSTATE (SSUSB_USB2_CSR_BASE + 0x0060)
|
||||
|
||||
/*---------------- SSUSB_USB2_CSR FIELD DEFINITION ----------------*/
|
||||
|
||||
/* U3D_POWER_MANAGEMENT */
|
||||
#define LPM_BESL_STALL BIT(14)
|
||||
#define LPM_BESLD_STALL BIT(13)
|
||||
#define LPM_RWP BIT(11)
|
||||
#define LPM_HRWE BIT(10)
|
||||
#define LPM_MODE(x) (((x) & 0x3) << 8)
|
||||
#define ISO_UPDATE BIT(7)
|
||||
#define SOFT_CONN BIT(6)
|
||||
#define HS_ENABLE BIT(5)
|
||||
#define RESUME BIT(2)
|
||||
#define SUSPENDM_ENABLE BIT(0)
|
||||
|
||||
/* U3D_DEVICE_CONTROL */
|
||||
#define DC_HOSTREQ BIT(1)
|
||||
#define DC_SESSION BIT(0)
|
||||
|
||||
/* U3D_USB2_TEST_MODE */
|
||||
#define U2U3_AUTO_SWITCH BIT(10)
|
||||
#define LPM_FORCE_STALL BIT(8)
|
||||
#define FIFO_ACCESS BIT(6)
|
||||
#define FORCE_FS BIT(5)
|
||||
#define FORCE_HS BIT(4)
|
||||
#define TEST_PACKET_MODE BIT(3)
|
||||
#define TEST_K_MODE BIT(2)
|
||||
#define TEST_J_MODE BIT(1)
|
||||
#define TEST_SE0_NAK_MODE BIT(0)
|
||||
|
||||
/* U3D_COMMON_USB_INTR_ENABLE */
|
||||
/* U3D_COMMON_USB_INTR */
|
||||
#define LPM_RESUME_INTR BIT(9)
|
||||
#define LPM_INTR BIT(8)
|
||||
#define DISCONN_INTR BIT(5)
|
||||
#define CONN_INTR BIT(4)
|
||||
#define SOF_INTR BIT(3)
|
||||
#define RESET_INTR BIT(2)
|
||||
#define RESUME_INTR BIT(1)
|
||||
#define SUSPEND_INTR BIT(0)
|
||||
|
||||
/* U3D_LINK_RESET_INFO */
|
||||
#define WTCHRP_MSK GENMASK(19, 16)
|
||||
|
||||
/* U3D_USB20_LPM_PARAMETER */
|
||||
#define LPM_BESLCK_U3(x) (((x) & 0xf) << 12)
|
||||
#define LPM_BESLCK(x) (((x) & 0xf) << 8)
|
||||
#define LPM_BESLDCK(x) (((x) & 0xf) << 4)
|
||||
#define LPM_BESL GENMASK(3, 0)
|
||||
|
||||
/* U3D_USB20_MISC_CONTROL */
|
||||
#define LPM_U3_ACK_EN BIT(0)
|
||||
|
||||
/*---------------- SSUSB_SIFSLV_IPPC REGISTER DEFINITION ----------------*/
|
||||
|
||||
#define U3D_SSUSB_IP_PW_CTRL0 (SSUSB_SIFSLV_IPPC_BASE + 0x0000)
|
||||
#define U3D_SSUSB_IP_PW_CTRL1 (SSUSB_SIFSLV_IPPC_BASE + 0x0004)
|
||||
#define U3D_SSUSB_IP_PW_CTRL2 (SSUSB_SIFSLV_IPPC_BASE + 0x0008)
|
||||
#define U3D_SSUSB_IP_PW_CTRL3 (SSUSB_SIFSLV_IPPC_BASE + 0x000C)
|
||||
#define U3D_SSUSB_IP_PW_STS1 (SSUSB_SIFSLV_IPPC_BASE + 0x0010)
|
||||
#define U3D_SSUSB_IP_PW_STS2 (SSUSB_SIFSLV_IPPC_BASE + 0x0014)
|
||||
#define U3D_SSUSB_OTG_STS (SSUSB_SIFSLV_IPPC_BASE + 0x0018)
|
||||
#define U3D_SSUSB_OTG_STS_CLR (SSUSB_SIFSLV_IPPC_BASE + 0x001C)
|
||||
#define U3D_SSUSB_IP_XHCI_CAP (SSUSB_SIFSLV_IPPC_BASE + 0x0024)
|
||||
#define U3D_SSUSB_IP_DEV_CAP (SSUSB_SIFSLV_IPPC_BASE + 0x0028)
|
||||
#define U3D_SSUSB_OTG_INT_EN (SSUSB_SIFSLV_IPPC_BASE + 0x002C)
|
||||
#define U3D_SSUSB_U3_CTRL_0P (SSUSB_SIFSLV_IPPC_BASE + 0x0030)
|
||||
#define U3D_SSUSB_U2_CTRL_0P (SSUSB_SIFSLV_IPPC_BASE + 0x0050)
|
||||
#define U3D_SSUSB_REF_CK_CTRL (SSUSB_SIFSLV_IPPC_BASE + 0x008C)
|
||||
#define U3D_SSUSB_DEV_RST_CTRL (SSUSB_SIFSLV_IPPC_BASE + 0x0098)
|
||||
#define U3D_SSUSB_HW_ID (SSUSB_SIFSLV_IPPC_BASE + 0x00A0)
|
||||
#define U3D_SSUSB_HW_SUB_ID (SSUSB_SIFSLV_IPPC_BASE + 0x00A4)
|
||||
#define U3D_SSUSB_IP_TRUNK_VERS (U3D_SSUSB_HW_SUB_ID)
|
||||
#define U3D_SSUSB_PRB_CTRL0 (SSUSB_SIFSLV_IPPC_BASE + 0x00B0)
|
||||
#define U3D_SSUSB_PRB_CTRL1 (SSUSB_SIFSLV_IPPC_BASE + 0x00B4)
|
||||
#define U3D_SSUSB_PRB_CTRL2 (SSUSB_SIFSLV_IPPC_BASE + 0x00B8)
|
||||
#define U3D_SSUSB_PRB_CTRL3 (SSUSB_SIFSLV_IPPC_BASE + 0x00BC)
|
||||
#define U3D_SSUSB_PRB_CTRL4 (SSUSB_SIFSLV_IPPC_BASE + 0x00C0)
|
||||
#define U3D_SSUSB_PRB_CTRL5 (SSUSB_SIFSLV_IPPC_BASE + 0x00C4)
|
||||
#define U3D_SSUSB_IP_SPARE0 (SSUSB_SIFSLV_IPPC_BASE + 0x00C8)
|
||||
|
||||
/*---------------- SSUSB_SIFSLV_IPPC FIELD DEFINITION ----------------*/
|
||||
|
||||
/* U3D_SSUSB_IP_PW_CTRL0 */
|
||||
#define SSUSB_IP_SW_RST BIT(0)
|
||||
|
||||
/* U3D_SSUSB_IP_PW_CTRL1 */
|
||||
#define SSUSB_IP_HOST_PDN BIT(0)
|
||||
|
||||
/* U3D_SSUSB_IP_PW_CTRL2 */
|
||||
#define SSUSB_IP_DEV_PDN BIT(0)
|
||||
|
||||
/* U3D_SSUSB_IP_PW_CTRL3 */
|
||||
#define SSUSB_IP_PCIE_PDN BIT(0)
|
||||
|
||||
/* U3D_SSUSB_IP_PW_STS1 */
|
||||
#define SSUSB_IP_SLEEP_STS BIT(30)
|
||||
#define SSUSB_U3_MAC_RST_B_STS BIT(16)
|
||||
#define SSUSB_XHCI_RST_B_STS BIT(11)
|
||||
#define SSUSB_SYS125_RST_B_STS BIT(10)
|
||||
#define SSUSB_REF_RST_B_STS BIT(8)
|
||||
#define SSUSB_SYSPLL_STABLE BIT(0)
|
||||
|
||||
/* U3D_SSUSB_IP_PW_STS2 */
|
||||
#define SSUSB_U2_MAC_SYS_RST_B_STS BIT(0)
|
||||
|
||||
/* U3D_SSUSB_OTG_STS */
|
||||
#define SSUSB_VBUS_VALID BIT(9)
|
||||
|
||||
/* U3D_SSUSB_OTG_STS_CLR */
|
||||
#define SSUSB_VBUS_INTR_CLR BIT(6)
|
||||
|
||||
/* U3D_SSUSB_IP_XHCI_CAP */
|
||||
#define SSUSB_IP_XHCI_U2_PORT_NUM(x) (((x) >> 8) & 0xff)
|
||||
#define SSUSB_IP_XHCI_U3_PORT_NUM(x) ((x) & 0xff)
|
||||
|
||||
/* U3D_SSUSB_IP_DEV_CAP */
|
||||
#define SSUSB_IP_DEV_U3_PORT_NUM(x) ((x) & 0xff)
|
||||
|
||||
/* U3D_SSUSB_OTG_INT_EN */
|
||||
#define SSUSB_VBUS_CHG_INT_A_EN BIT(7)
|
||||
#define SSUSB_VBUS_CHG_INT_B_EN BIT(6)
|
||||
|
||||
/* U3D_SSUSB_U3_CTRL_0P */
|
||||
#define SSUSB_U3_PORT_SSP_SPEED BIT(9)
|
||||
#define SSUSB_U3_PORT_DUAL_MODE BIT(7)
|
||||
#define SSUSB_U3_PORT_HOST_SEL BIT(2)
|
||||
#define SSUSB_U3_PORT_PDN BIT(1)
|
||||
#define SSUSB_U3_PORT_DIS BIT(0)
|
||||
|
||||
/* U3D_SSUSB_U2_CTRL_0P */
|
||||
#define SSUSB_U2_PORT_RG_IDDIG BIT(12)
|
||||
#define SSUSB_U2_PORT_FORCE_IDDIG BIT(11)
|
||||
#define SSUSB_U2_PORT_VBUSVALID BIT(9)
|
||||
#define SSUSB_U2_PORT_OTG_SEL BIT(7)
|
||||
#define SSUSB_U2_PORT_HOST BIT(2)
|
||||
#define SSUSB_U2_PORT_PDN BIT(1)
|
||||
#define SSUSB_U2_PORT_DIS BIT(0)
|
||||
#define SSUSB_U2_PORT_HOST_SEL (SSUSB_U2_PORT_VBUSVALID | SSUSB_U2_PORT_HOST)
|
||||
|
||||
/* U3D_SSUSB_DEV_RST_CTRL */
|
||||
#define SSUSB_DEV_SW_RST BIT(0)
|
||||
|
||||
/* U3D_SSUSB_IP_TRUNK_VERS */
|
||||
#define IP_TRUNK_VERS(x) (((x) >> 16) & 0xffff)
|
||||
|
||||
#endif /* _SSUSB_HW_REGS_H_ */
|
369
drivers/usb/mtu3/mtu3_plat.c
Normal file
369
drivers/usb/mtu3/mtu3_plat.c
Normal file
|
@ -0,0 +1,369 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2016 MediaTek Inc.
|
||||
*
|
||||
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm/lists.h>
|
||||
#include <linux/iopoll.h>
|
||||
|
||||
#include "mtu3.h"
|
||||
#include "mtu3_dr.h"
|
||||
|
||||
void ssusb_set_force_mode(struct ssusb_mtk *ssusb,
|
||||
enum mtu3_dr_force_mode mode)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
value = mtu3_readl(ssusb->ippc_base, SSUSB_U2_CTRL(0));
|
||||
switch (mode) {
|
||||
case MTU3_DR_FORCE_DEVICE:
|
||||
value |= SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG;
|
||||
break;
|
||||
case MTU3_DR_FORCE_HOST:
|
||||
value |= SSUSB_U2_PORT_FORCE_IDDIG;
|
||||
value &= ~SSUSB_U2_PORT_RG_IDDIG;
|
||||
break;
|
||||
case MTU3_DR_FORCE_NONE:
|
||||
value &= ~(SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
mtu3_writel(ssusb->ippc_base, SSUSB_U2_CTRL(0), value);
|
||||
}
|
||||
|
||||
/* u2-port0 should be powered on and enabled; */
|
||||
int ssusb_check_clocks(struct ssusb_mtk *ssusb, u32 ex_clks)
|
||||
{
|
||||
void __iomem *ibase = ssusb->ippc_base;
|
||||
u32 value, check_val;
|
||||
int ret;
|
||||
|
||||
check_val = ex_clks | SSUSB_SYS125_RST_B_STS | SSUSB_SYSPLL_STABLE |
|
||||
SSUSB_REF_RST_B_STS;
|
||||
|
||||
ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS1, value,
|
||||
((value & check_val) == check_val), 10000);
|
||||
if (ret) {
|
||||
dev_err(ssusb->dev, "clks of sts1 are not stable!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS2, value,
|
||||
(value & SSUSB_U2_MAC_SYS_RST_B_STS), 10000);
|
||||
if (ret) {
|
||||
dev_err(ssusb->dev, "mac2 clock is not stable\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ssusb_phy_setup(struct ssusb_mtk *ssusb)
|
||||
{
|
||||
struct udevice *dev = ssusb->dev;
|
||||
struct phy_bulk *phys = &ssusb->phys;
|
||||
int ret;
|
||||
|
||||
ret = generic_phy_get_bulk(dev, phys);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = generic_phy_init_bulk(phys);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = generic_phy_power_on_bulk(phys);
|
||||
if (ret)
|
||||
generic_phy_exit_bulk(phys);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ssusb_phy_shutdown(struct ssusb_mtk *ssusb)
|
||||
{
|
||||
generic_phy_power_off_bulk(&ssusb->phys);
|
||||
generic_phy_exit_bulk(&ssusb->phys);
|
||||
}
|
||||
|
||||
static int ssusb_rscs_init(struct ssusb_mtk *ssusb)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = regulator_set_enable(ssusb->vusb33_supply, true);
|
||||
if (ret < 0 && ret != -ENOSYS) {
|
||||
dev_err(ssusb->dev, "failed to enable vusb33\n");
|
||||
goto vusb33_err;
|
||||
}
|
||||
|
||||
ret = clk_enable_bulk(&ssusb->clks);
|
||||
if (ret)
|
||||
goto clks_err;
|
||||
|
||||
ret = ssusb_phy_setup(ssusb);
|
||||
if (ret) {
|
||||
dev_err(ssusb->dev, "failed to setup phy\n");
|
||||
goto phy_err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
phy_err:
|
||||
clk_disable_bulk(&ssusb->clks);
|
||||
clks_err:
|
||||
regulator_set_enable(ssusb->vusb33_supply, false);
|
||||
vusb33_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ssusb_rscs_exit(struct ssusb_mtk *ssusb)
|
||||
{
|
||||
clk_disable_bulk(&ssusb->clks);
|
||||
regulator_set_enable(ssusb->vusb33_supply, false);
|
||||
ssusb_phy_shutdown(ssusb);
|
||||
}
|
||||
|
||||
static void ssusb_ip_sw_reset(struct ssusb_mtk *ssusb)
|
||||
{
|
||||
/* reset whole ip (xhci & u3d) */
|
||||
mtu3_setbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST);
|
||||
udelay(1);
|
||||
mtu3_clrbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST);
|
||||
}
|
||||
|
||||
static int get_ssusb_rscs(struct udevice *dev, struct ssusb_mtk *ssusb)
|
||||
{
|
||||
struct udevice *child;
|
||||
int ret;
|
||||
|
||||
ret = device_get_supply_regulator(dev, "vusb33-supply",
|
||||
&ssusb->vusb33_supply);
|
||||
if (ret) /* optional, ignore error */
|
||||
dev_warn(dev, "can't get optional vusb33 %d\n", ret);
|
||||
|
||||
ret = device_get_supply_regulator(dev, "vbus-supply",
|
||||
&ssusb->vbus_supply);
|
||||
if (ret) /* optional, ignore error */
|
||||
dev_warn(dev, "can't get optional vbus regulator %d!\n", ret);
|
||||
|
||||
ret = clk_get_bulk(dev, &ssusb->clks);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get clocks %d!\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssusb->ippc_base = devfdt_remap_addr_name(dev, "ippc");
|
||||
if (!ssusb->ippc_base) {
|
||||
dev_err(dev, "error mapping memory for ippc\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = device_find_first_child(dev, &child);
|
||||
if (ret || !child) {
|
||||
dev_err(dev, "failed to get child %d!\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssusb->mac_base = devfdt_remap_addr_name(child, "mac");
|
||||
if (!ssusb->mac_base) {
|
||||
dev_err(dev, "error mapping memory for mac\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ssusb->dr_mode = usb_get_dr_mode(child->node);
|
||||
|
||||
if (ssusb->dr_mode == USB_DR_MODE_UNKNOWN ||
|
||||
ssusb->dr_mode == USB_DR_MODE_OTG)
|
||||
ssusb->dr_mode = USB_DR_MODE_PERIPHERAL;
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_MTU3_GADGET))
|
||||
ssusb->dr_mode = USB_DR_MODE_PERIPHERAL;
|
||||
else if (IS_ENABLED(CONFIG_USB_MTU3_HOST))
|
||||
ssusb->dr_mode = USB_DR_MODE_HOST;
|
||||
|
||||
dev_info(dev, "dr_mode: %d, ippc: 0x%p, mac: 0x%p\n",
|
||||
ssusb->dr_mode, ssusb->ippc_base, ssusb->mac_base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtu3_probe(struct udevice *dev)
|
||||
{
|
||||
struct ssusb_mtk *ssusb = dev_get_priv(dev);
|
||||
int ret = -ENOMEM;
|
||||
|
||||
ssusb->dev = dev;
|
||||
|
||||
ret = get_ssusb_rscs(dev, ssusb);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ssusb_rscs_init(ssusb);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ssusb_ip_sw_reset(ssusb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtu3_remove(struct udevice *dev)
|
||||
{
|
||||
struct ssusb_mtk *ssusb = dev_to_ssusb(dev);
|
||||
|
||||
ssusb_rscs_exit(ssusb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id ssusb_of_match[] = {
|
||||
{.compatible = "mediatek,ssusb",},
|
||||
{},
|
||||
};
|
||||
|
||||
#if CONFIG_IS_ENABLED(DM_USB_GADGET)
|
||||
int dm_usb_gadget_handle_interrupts(struct udevice *dev)
|
||||
{
|
||||
struct mtu3 *mtu = dev_get_priv(dev);
|
||||
|
||||
mtu3_irq(0, mtu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtu3_gadget_probe(struct udevice *dev)
|
||||
{
|
||||
struct ssusb_mtk *ssusb = dev_to_ssusb(dev->parent);
|
||||
struct mtu3 *mtu = dev_get_priv(dev);
|
||||
|
||||
mtu->dev = dev;
|
||||
ssusb->u3d = mtu;
|
||||
return ssusb_gadget_init(ssusb);
|
||||
}
|
||||
|
||||
static int mtu3_gadget_remove(struct udevice *dev)
|
||||
{
|
||||
struct mtu3 *mtu = dev_get_priv(dev);
|
||||
|
||||
ssusb_gadget_exit(mtu->ssusb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(mtu3_peripheral) = {
|
||||
.name = "mtu3-peripheral",
|
||||
.id = UCLASS_USB_GADGET_GENERIC,
|
||||
.of_match = ssusb_of_match,
|
||||
.probe = mtu3_gadget_probe,
|
||||
.remove = mtu3_gadget_remove,
|
||||
.priv_auto_alloc_size = sizeof(struct mtu3),
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SPL_USB_HOST_SUPPORT) || \
|
||||
(!defined(CONFIG_SPL_BUILD) && defined(CONFIG_USB_HOST))
|
||||
static int mtu3_host_probe(struct udevice *dev)
|
||||
{
|
||||
struct ssusb_mtk *ssusb = dev_to_ssusb(dev->parent);
|
||||
struct mtu3_host *u3h = dev_get_priv(dev);
|
||||
struct xhci_hcor *hcor;
|
||||
int rc;
|
||||
|
||||
u3h->dev = dev;
|
||||
ssusb->u3h = u3h;
|
||||
rc = ssusb_host_init(ssusb);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
u3h->ctrl.quirks = XHCI_MTK_HOST;
|
||||
hcor = (struct xhci_hcor *)((uintptr_t)u3h->hcd +
|
||||
HC_LENGTH(xhci_readl(&u3h->hcd->cr_capbase)));
|
||||
|
||||
return xhci_register(dev, u3h->hcd, hcor);
|
||||
}
|
||||
|
||||
static int mtu3_host_remove(struct udevice *dev)
|
||||
{
|
||||
struct mtu3_host *u3h = dev_get_priv(dev);
|
||||
|
||||
xhci_deregister(dev);
|
||||
ssusb_host_exit(u3h->ssusb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(mtu3_host) = {
|
||||
.name = "mtu3-host",
|
||||
.id = UCLASS_USB,
|
||||
.of_match = ssusb_of_match,
|
||||
.probe = mtu3_host_probe,
|
||||
.remove = mtu3_host_remove,
|
||||
.priv_auto_alloc_size = sizeof(struct mtu3_host),
|
||||
.ops = &xhci_usb_ops,
|
||||
.flags = DM_FLAG_ALLOC_PRIV_DMA,
|
||||
};
|
||||
#endif
|
||||
|
||||
static int mtu3_glue_bind(struct udevice *parent)
|
||||
{
|
||||
struct udevice *dev;
|
||||
enum usb_dr_mode dr_mode;
|
||||
const char *driver;
|
||||
const char *name;
|
||||
ofnode node;
|
||||
int ret;
|
||||
|
||||
node = ofnode_by_compatible(parent->node, "mediatek,ssusb");
|
||||
if (!ofnode_valid(node))
|
||||
return -ENODEV;
|
||||
|
||||
name = ofnode_get_name(node);
|
||||
dr_mode = usb_get_dr_mode(node);
|
||||
|
||||
switch (dr_mode) {
|
||||
#if CONFIG_IS_ENABLED(DM_USB_GADGET)
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
case USB_DR_MODE_OTG:
|
||||
dev_dbg(parent, "%s: dr_mode: peripheral\n", __func__);
|
||||
driver = "mtu3-peripheral";
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SPL_USB_HOST_SUPPORT) || \
|
||||
(!defined(CONFIG_SPL_BUILD) && defined(CONFIG_USB_HOST))
|
||||
case USB_DR_MODE_HOST:
|
||||
dev_dbg(parent, "%s: dr_mode: host\n", __func__);
|
||||
driver = "mtu3-host";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
dev_err(parent, "%s: unsupported dr_mode %d\n",
|
||||
__func__, dr_mode);
|
||||
return -ENODEV;
|
||||
};
|
||||
|
||||
dev_dbg(parent, "%s: node name: %s, driver %s, dr_mode %d\n",
|
||||
__func__, name, driver, dr_mode);
|
||||
|
||||
ret = device_bind_driver_to_node(parent, driver, name, node, &dev);
|
||||
if (ret)
|
||||
dev_err(parent, "%s: not able to bind usb device mode\n",
|
||||
__func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct udevice_id mtu3_of_match[] = {
|
||||
{.compatible = "mediatek,mtu3",},
|
||||
{},
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(mtu3) = {
|
||||
.name = "mtu3",
|
||||
.id = UCLASS_NOP,
|
||||
.of_match = mtu3_of_match,
|
||||
.bind = mtu3_glue_bind,
|
||||
.probe = mtu3_probe,
|
||||
.remove = mtu3_remove,
|
||||
.priv_auto_alloc_size = sizeof(struct ssusb_mtk),
|
||||
};
|
505
drivers/usb/mtu3/mtu3_qmu.c
Normal file
505
drivers/usb/mtu3/mtu3_qmu.c
Normal file
|
@ -0,0 +1,505 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* mtu3_qmu.c - Queue Management Unit driver for device controller
|
||||
*
|
||||
* Copyright (C) 2016 MediaTek Inc.
|
||||
*
|
||||
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* Queue Management Unit (QMU) is designed to unload SW effort
|
||||
* to serve DMA interrupts.
|
||||
* By preparing General Purpose Descriptor (GPD) and Buffer Descriptor (BD),
|
||||
* SW links data buffers and triggers QMU to send / receive data to
|
||||
* host / from device at a time.
|
||||
* And now only GPD is supported.
|
||||
*
|
||||
* For more detailed information, please refer to QMU Programming Guide
|
||||
*/
|
||||
|
||||
#include <asm/cache.h>
|
||||
#include <cpu_func.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "mtu3.h"
|
||||
|
||||
#define QMU_CHECKSUM_LEN 16
|
||||
|
||||
#define GPD_FLAGS_HWO BIT(0)
|
||||
#define GPD_FLAGS_BDP BIT(1)
|
||||
#define GPD_FLAGS_BPS BIT(2)
|
||||
#define GPD_FLAGS_IOC BIT(7)
|
||||
|
||||
#define GPD_EXT_FLAG_ZLP BIT(5)
|
||||
|
||||
#define DCACHELINE_SIZE CONFIG_SYS_CACHELINE_SIZE
|
||||
|
||||
void mtu3_flush_cache(uintptr_t addr, u32 len)
|
||||
{
|
||||
WARN_ON(!(void *)addr || len == 0);
|
||||
|
||||
flush_dcache_range(addr & ~(DCACHELINE_SIZE - 1),
|
||||
ALIGN(addr + len, DCACHELINE_SIZE));
|
||||
}
|
||||
|
||||
void mtu3_inval_cache(uintptr_t addr, u32 len)
|
||||
{
|
||||
WARN_ON(!(void *)addr || len == 0);
|
||||
|
||||
invalidate_dcache_range(addr & ~(DCACHELINE_SIZE - 1),
|
||||
ALIGN(addr + len, DCACHELINE_SIZE));
|
||||
}
|
||||
|
||||
static struct qmu_gpd *gpd_dma_to_virt(struct mtu3_gpd_ring *ring,
|
||||
dma_addr_t dma_addr)
|
||||
{
|
||||
dma_addr_t dma_base = ring->dma;
|
||||
struct qmu_gpd *gpd_head = ring->start;
|
||||
u32 offset = (dma_addr - dma_base) / sizeof(*gpd_head);
|
||||
|
||||
if (offset >= MAX_GPD_NUM)
|
||||
return NULL;
|
||||
|
||||
return gpd_head + offset;
|
||||
}
|
||||
|
||||
static dma_addr_t gpd_virt_to_dma(struct mtu3_gpd_ring *ring,
|
||||
struct qmu_gpd *gpd)
|
||||
{
|
||||
dma_addr_t dma_base = ring->dma;
|
||||
struct qmu_gpd *gpd_head = ring->start;
|
||||
u32 offset;
|
||||
|
||||
offset = gpd - gpd_head;
|
||||
if (offset >= MAX_GPD_NUM)
|
||||
return 0;
|
||||
|
||||
return dma_base + (offset * sizeof(*gpd));
|
||||
}
|
||||
|
||||
static void gpd_ring_init(struct mtu3_gpd_ring *ring, struct qmu_gpd *gpd)
|
||||
{
|
||||
ring->start = gpd;
|
||||
ring->enqueue = gpd;
|
||||
ring->dequeue = gpd;
|
||||
ring->end = gpd + MAX_GPD_NUM - 1;
|
||||
}
|
||||
|
||||
static void reset_gpd_list(struct mtu3_ep *mep)
|
||||
{
|
||||
struct mtu3_gpd_ring *ring = &mep->gpd_ring;
|
||||
struct qmu_gpd *gpd = ring->start;
|
||||
|
||||
if (gpd) {
|
||||
gpd->flag &= ~GPD_FLAGS_HWO;
|
||||
gpd_ring_init(ring, gpd);
|
||||
mtu3_flush_cache((uintptr_t)gpd, sizeof(*gpd));
|
||||
}
|
||||
}
|
||||
|
||||
int mtu3_gpd_ring_alloc(struct mtu3_ep *mep)
|
||||
{
|
||||
struct qmu_gpd *gpd;
|
||||
struct mtu3_gpd_ring *ring = &mep->gpd_ring;
|
||||
|
||||
/* software own all gpds as default */
|
||||
gpd = memalign(DCACHELINE_SIZE, QMU_GPD_RING_SIZE);
|
||||
if (!gpd)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(gpd, 0, QMU_GPD_RING_SIZE);
|
||||
ring->dma = (dma_addr_t)gpd;
|
||||
gpd_ring_init(ring, gpd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mtu3_gpd_ring_free(struct mtu3_ep *mep)
|
||||
{
|
||||
struct mtu3_gpd_ring *ring = &mep->gpd_ring;
|
||||
|
||||
kfree(ring->start);
|
||||
memset(ring, 0, sizeof(*ring));
|
||||
}
|
||||
|
||||
void mtu3_qmu_resume(struct mtu3_ep *mep)
|
||||
{
|
||||
struct mtu3 *mtu = mep->mtu;
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
int epnum = mep->epnum;
|
||||
u32 offset;
|
||||
|
||||
offset = mep->is_in ? USB_QMU_TQCSR(epnum) : USB_QMU_RQCSR(epnum);
|
||||
|
||||
mtu3_writel(mbase, offset, QMU_Q_RESUME);
|
||||
if (!(mtu3_readl(mbase, offset) & QMU_Q_ACTIVE))
|
||||
mtu3_writel(mbase, offset, QMU_Q_RESUME);
|
||||
}
|
||||
|
||||
static struct qmu_gpd *advance_enq_gpd(struct mtu3_gpd_ring *ring)
|
||||
{
|
||||
if (ring->enqueue < ring->end)
|
||||
ring->enqueue++;
|
||||
else
|
||||
ring->enqueue = ring->start;
|
||||
|
||||
return ring->enqueue;
|
||||
}
|
||||
|
||||
static struct qmu_gpd *advance_deq_gpd(struct mtu3_gpd_ring *ring)
|
||||
{
|
||||
if (ring->dequeue < ring->end)
|
||||
ring->dequeue++;
|
||||
else
|
||||
ring->dequeue = ring->start;
|
||||
|
||||
return ring->dequeue;
|
||||
}
|
||||
|
||||
/* check if a ring is emtpy */
|
||||
static int gpd_ring_empty(struct mtu3_gpd_ring *ring)
|
||||
{
|
||||
struct qmu_gpd *enq = ring->enqueue;
|
||||
struct qmu_gpd *next;
|
||||
|
||||
if (ring->enqueue < ring->end)
|
||||
next = enq + 1;
|
||||
else
|
||||
next = ring->start;
|
||||
|
||||
/* one gpd is reserved to simplify gpd preparation */
|
||||
return next == ring->dequeue;
|
||||
}
|
||||
|
||||
int mtu3_prepare_transfer(struct mtu3_ep *mep)
|
||||
{
|
||||
return gpd_ring_empty(&mep->gpd_ring);
|
||||
}
|
||||
|
||||
static int mtu3_prepare_tx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq)
|
||||
{
|
||||
struct qmu_gpd *enq;
|
||||
struct mtu3_gpd_ring *ring = &mep->gpd_ring;
|
||||
struct qmu_gpd *gpd = ring->enqueue;
|
||||
struct usb_request *req = &mreq->request;
|
||||
|
||||
/* set all fields to zero as default value */
|
||||
memset(gpd, 0, sizeof(*gpd));
|
||||
|
||||
gpd->buffer = cpu_to_le32((u32)req->dma);
|
||||
gpd->buf_len = cpu_to_le16(req->length);
|
||||
|
||||
/* get the next GPD */
|
||||
enq = advance_enq_gpd(ring);
|
||||
dev_dbg(mep->mtu->dev, "TX-EP%d queue gpd=%p, enq=%p\n",
|
||||
mep->epnum, gpd, enq);
|
||||
|
||||
enq->flag &= ~GPD_FLAGS_HWO;
|
||||
gpd->next_gpd = cpu_to_le32((u32)gpd_virt_to_dma(ring, enq));
|
||||
|
||||
if (req->zero)
|
||||
gpd->ext_flag |= GPD_EXT_FLAG_ZLP;
|
||||
|
||||
gpd->flag |= GPD_FLAGS_IOC | GPD_FLAGS_HWO;
|
||||
|
||||
mreq->gpd = gpd;
|
||||
|
||||
if (req->length)
|
||||
mtu3_flush_cache((uintptr_t)req->buf, req->length);
|
||||
|
||||
mtu3_flush_cache((uintptr_t)gpd, sizeof(*gpd));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtu3_prepare_rx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq)
|
||||
{
|
||||
struct qmu_gpd *enq;
|
||||
struct mtu3_gpd_ring *ring = &mep->gpd_ring;
|
||||
struct qmu_gpd *gpd = ring->enqueue;
|
||||
struct usb_request *req = &mreq->request;
|
||||
|
||||
/* set all fields to zero as default value */
|
||||
memset(gpd, 0, sizeof(*gpd));
|
||||
|
||||
gpd->buffer = cpu_to_le32((u32)req->dma);
|
||||
gpd->data_buf_len = cpu_to_le16(req->length);
|
||||
|
||||
/* get the next GPD */
|
||||
enq = advance_enq_gpd(ring);
|
||||
dev_dbg(mep->mtu->dev, "RX-EP%d queue gpd=%p, enq=%p\n",
|
||||
mep->epnum, gpd, enq);
|
||||
|
||||
enq->flag &= ~GPD_FLAGS_HWO;
|
||||
gpd->next_gpd = cpu_to_le32((u32)gpd_virt_to_dma(ring, enq));
|
||||
gpd->flag |= GPD_FLAGS_IOC | GPD_FLAGS_HWO;
|
||||
|
||||
mreq->gpd = gpd;
|
||||
|
||||
mtu3_inval_cache((uintptr_t)req->buf, req->length);
|
||||
mtu3_flush_cache((uintptr_t)gpd, sizeof(*gpd));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mtu3_insert_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq)
|
||||
{
|
||||
if (mep->is_in)
|
||||
mtu3_prepare_tx_gpd(mep, mreq);
|
||||
else
|
||||
mtu3_prepare_rx_gpd(mep, mreq);
|
||||
}
|
||||
|
||||
int mtu3_qmu_start(struct mtu3_ep *mep)
|
||||
{
|
||||
struct mtu3 *mtu = mep->mtu;
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
struct mtu3_gpd_ring *ring = &mep->gpd_ring;
|
||||
u8 epnum = mep->epnum;
|
||||
|
||||
if (mep->is_in) {
|
||||
/* set QMU start address */
|
||||
mtu3_writel(mbase, USB_QMU_TQSAR(epnum), ring->dma);
|
||||
mtu3_setbits(mbase, MU3D_EP_TXCR0(epnum), TX_DMAREQEN);
|
||||
/* send zero length packet according to ZLP flag in GPD */
|
||||
mtu3_setbits(mbase, U3D_QCR1, QMU_TX_ZLP(epnum));
|
||||
mtu3_writel(mbase, U3D_TQERRIESR0,
|
||||
QMU_TX_LEN_ERR(epnum) | QMU_TX_CS_ERR(epnum));
|
||||
|
||||
if (mtu3_readl(mbase, USB_QMU_TQCSR(epnum)) & QMU_Q_ACTIVE) {
|
||||
dev_warn(mtu->dev, "Tx %d Active Now!\n", epnum);
|
||||
return 0;
|
||||
}
|
||||
mtu3_writel(mbase, USB_QMU_TQCSR(epnum), QMU_Q_START);
|
||||
|
||||
} else {
|
||||
mtu3_writel(mbase, USB_QMU_RQSAR(epnum), ring->dma);
|
||||
mtu3_setbits(mbase, MU3D_EP_RXCR0(epnum), RX_DMAREQEN);
|
||||
/* don't expect ZLP */
|
||||
mtu3_clrbits(mbase, U3D_QCR3, QMU_RX_ZLP(epnum));
|
||||
/* move to next GPD when receive ZLP */
|
||||
mtu3_setbits(mbase, U3D_QCR3, QMU_RX_COZ(epnum));
|
||||
mtu3_writel(mbase, U3D_RQERRIESR0,
|
||||
QMU_RX_LEN_ERR(epnum) | QMU_RX_CS_ERR(epnum));
|
||||
mtu3_writel(mbase, U3D_RQERRIESR1, QMU_RX_ZLP_ERR(epnum));
|
||||
|
||||
if (mtu3_readl(mbase, USB_QMU_RQCSR(epnum)) & QMU_Q_ACTIVE) {
|
||||
dev_warn(mtu->dev, "Rx %d Active Now!\n", epnum);
|
||||
return 0;
|
||||
}
|
||||
mtu3_writel(mbase, USB_QMU_RQCSR(epnum), QMU_Q_START);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* may called in atomic context */
|
||||
void mtu3_qmu_stop(struct mtu3_ep *mep)
|
||||
{
|
||||
struct mtu3 *mtu = mep->mtu;
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
int epnum = mep->epnum;
|
||||
u32 value = 0;
|
||||
u32 qcsr;
|
||||
int ret;
|
||||
|
||||
qcsr = mep->is_in ? USB_QMU_TQCSR(epnum) : USB_QMU_RQCSR(epnum);
|
||||
|
||||
if (!(mtu3_readl(mbase, qcsr) & QMU_Q_ACTIVE)) {
|
||||
dev_dbg(mtu->dev, "%s's qmu is inactive now!\n", mep->name);
|
||||
return;
|
||||
}
|
||||
mtu3_writel(mbase, qcsr, QMU_Q_STOP);
|
||||
|
||||
ret = readl_poll_timeout(mbase + qcsr, value,
|
||||
!(value & QMU_Q_ACTIVE), 1000);
|
||||
if (ret) {
|
||||
dev_err(mtu->dev, "stop %s's qmu failed\n", mep->name);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_dbg(mtu->dev, "%s's qmu stop now!\n", mep->name);
|
||||
}
|
||||
|
||||
void mtu3_qmu_flush(struct mtu3_ep *mep)
|
||||
{
|
||||
dev_dbg(mep->mtu->dev, "%s flush QMU %s\n", __func__,
|
||||
((mep->is_in) ? "TX" : "RX"));
|
||||
|
||||
/*Stop QMU */
|
||||
mtu3_qmu_stop(mep);
|
||||
reset_gpd_list(mep);
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: request list maybe is already empty as following case:
|
||||
* queue_tx --> qmu_interrupt(clear interrupt pending, schedule tasklet)-->
|
||||
* queue_tx --> process_tasklet(meanwhile, the second one is transferred,
|
||||
* tasklet process both of them)-->qmu_interrupt for second one.
|
||||
* To avoid upper case, put qmu_done_tx in ISR directly to process it.
|
||||
*/
|
||||
static void qmu_done_tx(struct mtu3 *mtu, u8 epnum)
|
||||
{
|
||||
struct mtu3_ep *mep = mtu->in_eps + epnum;
|
||||
struct mtu3_gpd_ring *ring = &mep->gpd_ring;
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
struct qmu_gpd *gpd = ring->dequeue;
|
||||
struct qmu_gpd *gpd_current = NULL;
|
||||
struct usb_request *req = NULL;
|
||||
struct mtu3_request *mreq;
|
||||
dma_addr_t cur_gpd_dma;
|
||||
|
||||
/*transfer phy address got from QMU register to virtual address */
|
||||
cur_gpd_dma = mtu3_readl(mbase, USB_QMU_TQCPR(epnum));
|
||||
gpd_current = gpd_dma_to_virt(ring, cur_gpd_dma);
|
||||
mtu3_inval_cache((uintptr_t)gpd, sizeof(*gpd));
|
||||
|
||||
dev_dbg(mtu->dev, "%s EP%d, last=%p, current=%p, enq=%p\n",
|
||||
__func__, epnum, gpd, gpd_current, ring->enqueue);
|
||||
|
||||
while (gpd != gpd_current && !(gpd->flag & GPD_FLAGS_HWO)) {
|
||||
mreq = next_request(mep);
|
||||
|
||||
if (!mreq || mreq->gpd != gpd) {
|
||||
dev_err(mtu->dev, "no correct TX req is found\n");
|
||||
break;
|
||||
}
|
||||
|
||||
req = &mreq->request;
|
||||
req->actual = le16_to_cpu(gpd->buf_len);
|
||||
mtu3_req_complete(mep, req, 0);
|
||||
|
||||
gpd = advance_deq_gpd(ring);
|
||||
mtu3_inval_cache((uintptr_t)gpd, sizeof(*gpd));
|
||||
}
|
||||
|
||||
dev_dbg(mtu->dev, "%s EP%d, deq=%p, enq=%p, complete\n",
|
||||
__func__, epnum, ring->dequeue, ring->enqueue);
|
||||
}
|
||||
|
||||
static void qmu_done_rx(struct mtu3 *mtu, u8 epnum)
|
||||
{
|
||||
struct mtu3_ep *mep = mtu->out_eps + epnum;
|
||||
struct mtu3_gpd_ring *ring = &mep->gpd_ring;
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
struct qmu_gpd *gpd = ring->dequeue;
|
||||
struct qmu_gpd *gpd_current = NULL;
|
||||
struct usb_request *req = NULL;
|
||||
struct mtu3_request *mreq;
|
||||
dma_addr_t cur_gpd_dma;
|
||||
|
||||
cur_gpd_dma = mtu3_readl(mbase, USB_QMU_RQCPR(epnum));
|
||||
gpd_current = gpd_dma_to_virt(ring, cur_gpd_dma);
|
||||
mtu3_inval_cache((uintptr_t)gpd, sizeof(*gpd));
|
||||
|
||||
dev_dbg(mtu->dev, "%s EP%d, last=%p, current=%p, enq=%p\n",
|
||||
__func__, epnum, gpd, gpd_current, ring->enqueue);
|
||||
|
||||
while (gpd != gpd_current && !(gpd->flag & GPD_FLAGS_HWO)) {
|
||||
mreq = next_request(mep);
|
||||
|
||||
if (!mreq || mreq->gpd != gpd) {
|
||||
dev_err(mtu->dev, "no correct RX req is found\n");
|
||||
break;
|
||||
}
|
||||
req = &mreq->request;
|
||||
|
||||
req->actual = le16_to_cpu(gpd->buf_len);
|
||||
mtu3_req_complete(mep, req, 0);
|
||||
|
||||
gpd = advance_deq_gpd(ring);
|
||||
mtu3_inval_cache((uintptr_t)gpd, sizeof(*gpd));
|
||||
}
|
||||
|
||||
dev_dbg(mtu->dev, "%s EP%d, deq=%p, enq=%p, complete\n",
|
||||
__func__, epnum, ring->dequeue, ring->enqueue);
|
||||
}
|
||||
|
||||
static void qmu_done_isr(struct mtu3 *mtu, u32 done_status)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i < mtu->num_eps; i++) {
|
||||
if (done_status & QMU_RX_DONE_INT(i))
|
||||
qmu_done_rx(mtu, i);
|
||||
if (done_status & QMU_TX_DONE_INT(i))
|
||||
qmu_done_tx(mtu, i);
|
||||
}
|
||||
}
|
||||
|
||||
static void qmu_exception_isr(struct mtu3 *mtu, u32 qmu_status)
|
||||
{
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
u32 errval;
|
||||
int i;
|
||||
|
||||
if ((qmu_status & RXQ_CSERR_INT) || (qmu_status & RXQ_LENERR_INT)) {
|
||||
errval = mtu3_readl(mbase, U3D_RQERRIR0);
|
||||
for (i = 1; i < mtu->num_eps; i++) {
|
||||
if (errval & QMU_RX_CS_ERR(i))
|
||||
dev_err(mtu->dev, "Rx %d CS error!\n", i);
|
||||
|
||||
if (errval & QMU_RX_LEN_ERR(i))
|
||||
dev_err(mtu->dev, "RX %d Length error\n", i);
|
||||
}
|
||||
mtu3_writel(mbase, U3D_RQERRIR0, errval);
|
||||
}
|
||||
|
||||
if (qmu_status & RXQ_ZLPERR_INT) {
|
||||
errval = mtu3_readl(mbase, U3D_RQERRIR1);
|
||||
for (i = 1; i < mtu->num_eps; i++) {
|
||||
if (errval & QMU_RX_ZLP_ERR(i))
|
||||
dev_dbg(mtu->dev, "RX EP%d Recv ZLP\n", i);
|
||||
}
|
||||
mtu3_writel(mbase, U3D_RQERRIR1, errval);
|
||||
}
|
||||
|
||||
if ((qmu_status & TXQ_CSERR_INT) || (qmu_status & TXQ_LENERR_INT)) {
|
||||
errval = mtu3_readl(mbase, U3D_TQERRIR0);
|
||||
for (i = 1; i < mtu->num_eps; i++) {
|
||||
if (errval & QMU_TX_CS_ERR(i))
|
||||
dev_err(mtu->dev, "Tx %d checksum error!\n", i);
|
||||
|
||||
if (errval & QMU_TX_LEN_ERR(i))
|
||||
dev_err(mtu->dev, "Tx %d zlp error!\n", i);
|
||||
}
|
||||
mtu3_writel(mbase, U3D_TQERRIR0, errval);
|
||||
}
|
||||
}
|
||||
|
||||
irqreturn_t mtu3_qmu_isr(struct mtu3 *mtu)
|
||||
{
|
||||
void __iomem *mbase = mtu->mac_base;
|
||||
u32 qmu_status;
|
||||
u32 qmu_done_status;
|
||||
|
||||
/* U3D_QISAR1 is read update */
|
||||
qmu_status = mtu3_readl(mbase, U3D_QISAR1);
|
||||
qmu_status &= mtu3_readl(mbase, U3D_QIER1);
|
||||
|
||||
qmu_done_status = mtu3_readl(mbase, U3D_QISAR0);
|
||||
qmu_done_status &= mtu3_readl(mbase, U3D_QIER0);
|
||||
mtu3_writel(mbase, U3D_QISAR0, qmu_done_status); /* W1C */
|
||||
dev_dbg(mtu->dev, "=== QMUdone[tx=%x, rx=%x] QMUexp[%x] ===\n",
|
||||
(qmu_done_status & 0xFFFF), qmu_done_status >> 16,
|
||||
qmu_status);
|
||||
|
||||
if (qmu_done_status)
|
||||
qmu_done_isr(mtu, qmu_done_status);
|
||||
|
||||
if (qmu_status)
|
||||
qmu_exception_isr(mtu, qmu_status);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void mtu3_qmu_init(struct mtu3 *mtu)
|
||||
{
|
||||
compiletime_assert(QMU_GPD_SIZE == 16, "QMU_GPD size SHOULD be 16B");
|
||||
}
|
||||
|
||||
void mtu3_qmu_exit(struct mtu3 *mtu)
|
||||
{
|
||||
}
|
37
drivers/usb/mtu3/mtu3_qmu.h
Normal file
37
drivers/usb/mtu3/mtu3_qmu.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* mtu3_qmu.h - Queue Management Unit driver header
|
||||
*
|
||||
* Copyright (C) 2016 MediaTek Inc.
|
||||
*
|
||||
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
|
||||
*/
|
||||
|
||||
#ifndef __MTK_QMU_H__
|
||||
#define __MTK_QMU_H__
|
||||
|
||||
#define MAX_GPD_NUM 16
|
||||
#define QMU_GPD_SIZE (sizeof(struct qmu_gpd))
|
||||
#define QMU_GPD_RING_SIZE (MAX_GPD_NUM * QMU_GPD_SIZE)
|
||||
|
||||
#define GPD_BUF_SIZE 65532
|
||||
|
||||
void mtu3_flush_cache(uintptr_t addr, u32 len);
|
||||
void mtu3_inval_cache(uintptr_t addr, u32 len);
|
||||
|
||||
void mtu3_qmu_stop(struct mtu3_ep *mep);
|
||||
int mtu3_qmu_start(struct mtu3_ep *mep);
|
||||
void mtu3_qmu_resume(struct mtu3_ep *mep);
|
||||
void mtu3_qmu_flush(struct mtu3_ep *mep);
|
||||
|
||||
void mtu3_insert_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq);
|
||||
int mtu3_prepare_transfer(struct mtu3_ep *mep);
|
||||
|
||||
int mtu3_gpd_ring_alloc(struct mtu3_ep *mep);
|
||||
void mtu3_gpd_ring_free(struct mtu3_ep *mep);
|
||||
|
||||
irqreturn_t mtu3_qmu_isr(struct mtu3 *mtu);
|
||||
void mtu3_qmu_init(struct mtu3 *mtu);
|
||||
void mtu3_qmu_exit(struct mtu3 *mtu);
|
||||
|
||||
#endif
|
|
@ -340,7 +340,7 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
|
|||
* Also, DRVVBUS pulses for SRP (but not at 5V) ...
|
||||
*/
|
||||
if ((usbintr & MUSB_INTR_BABBLE) && is_host_enabled(musb))
|
||||
pr_info("CAUTION: musb: Babble Interrupt Occured\n");
|
||||
pr_info("CAUTION: musb: Babble Interrupt Occurred\n");
|
||||
|
||||
if (usbintr & ((1 << wrp->drvvbus) << wrp->usb_shift)) {
|
||||
int drvvbus = dsps_readl(reg_base, wrp->status);
|
||||
|
|
|
@ -956,10 +956,9 @@ enum usb_device_speed {
|
|||
USB_SPEED_HIGH, /* usb 2.0 */
|
||||
USB_SPEED_WIRELESS, /* wireless (usb 2.5) */
|
||||
USB_SPEED_SUPER, /* usb 3.0 */
|
||||
USB_SPEED_SUPER_PLUS, /* usb 3.1 */
|
||||
};
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
/**
|
||||
* usb_speed_string() - Returns human readable-name of the speed.
|
||||
* @speed: The speed to return human-readable name for. If it's not
|
||||
|
@ -968,8 +967,6 @@ enum usb_device_speed {
|
|||
*/
|
||||
extern const char *usb_speed_string(enum usb_device_speed speed);
|
||||
|
||||
#endif
|
||||
|
||||
enum usb_device_state {
|
||||
/* NOTATTACHED isn't in the USB spec, and this state acts
|
||||
* the same as ATTACHED ... but it's clearer this way.
|
||||
|
|
Loading…
Add table
Reference in a new issue