avionic design with actual uboot and tooling
submodule of avionic design uboot bootloader and with included tools to get you started , read readme.md and readme-tk1-loader.md
This commit is contained in:
96
u-boot/drivers/usb/host/Kconfig
Normal file
96
u-boot/drivers/usb/host/Kconfig
Normal file
@@ -0,0 +1,96 @@
|
||||
#
|
||||
# USB Host Controller Drivers
|
||||
#
|
||||
comment "USB Host Controller Drivers"
|
||||
|
||||
config USB_XHCI_HCD
|
||||
bool "xHCI HCD (USB 3.0) support"
|
||||
---help---
|
||||
The eXtensible Host Controller Interface (xHCI) is standard for USB 3.0
|
||||
"SuperSpeed" host controller hardware.
|
||||
|
||||
if USB_XHCI_HCD
|
||||
|
||||
config USB_XHCI_UNIPHIER
|
||||
bool "Support for UniPhier on-chip xHCI USB controller"
|
||||
depends on ARCH_UNIPHIER
|
||||
default y
|
||||
---help---
|
||||
Enables support for the on-chip xHCI controller on UniPhier SoCs.
|
||||
|
||||
config USB_XHCI_DWC3
|
||||
bool "DesignWare USB3 DRD Core Support"
|
||||
help
|
||||
Say Y or if your system has a Dual Role SuperSpeed
|
||||
USB controller based on the DesignWare USB3 IP Core.
|
||||
|
||||
endif
|
||||
|
||||
config USB_OHCI_GENERIC
|
||||
bool "Support for generic OHCI USB controller"
|
||||
depends on OF_CONTROL
|
||||
depends on DM_USB
|
||||
default n
|
||||
---help---
|
||||
Enables support for generic OHCI controller.
|
||||
|
||||
config USB_EHCI_HCD
|
||||
bool "EHCI HCD (USB 2.0) support"
|
||||
---help---
|
||||
The Enhanced Host Controller Interface (EHCI) is standard for USB 2.0
|
||||
"high speed" (480 Mbit/sec, 60 Mbyte/sec) host controller hardware.
|
||||
If your USB host controller supports USB 2.0, you will likely want to
|
||||
configure this Host Controller Driver.
|
||||
|
||||
EHCI controllers are packaged with "companion" host controllers (OHCI
|
||||
or UHCI) to handle USB 1.1 devices connected to root hub ports. Ports
|
||||
will connect to EHCI if the device is high speed, otherwise they
|
||||
connect to a companion controller. If you configure EHCI, you should
|
||||
probably configure the OHCI (for NEC and some other vendors) USB Host
|
||||
Controller Driver or UHCI (for Via motherboards) Host Controller
|
||||
Driver too.
|
||||
|
||||
You may want to read <file:Documentation/usb/ehci.txt>.
|
||||
|
||||
config USB_EHCI
|
||||
bool
|
||||
default USB_EHCI_HCD
|
||||
---help---
|
||||
TODO: rename after most boards switch to Kconfig
|
||||
|
||||
if USB_EHCI_HCD
|
||||
|
||||
config USB_EHCI_MARVELL
|
||||
bool "Support for MVEBU (AXP / A38x) on-chip EHCI USB controller"
|
||||
depends on ARCH_MVEBU
|
||||
default y
|
||||
---help---
|
||||
Enables support for the on-chip EHCI controller on MVEBU SoCs.
|
||||
|
||||
config USB_EHCI_MX6
|
||||
bool "Support for i.MX6 on-chip EHCI USB controller"
|
||||
depends on ARCH_MX6
|
||||
default y
|
||||
---help---
|
||||
Enables support for the on-chip EHCI controller on i.MX6 SoCs.
|
||||
|
||||
config USB_EHCI_MSM
|
||||
bool "Support for Qualcomm on-chip EHCI USB controller"
|
||||
depends on DM_USB
|
||||
select USB_ULPI_VIEWPORT
|
||||
default n
|
||||
---help---
|
||||
Enables support for the on-chip EHCI controller on Qualcomm
|
||||
Snapdragon SoCs.
|
||||
This driver supports combination of Chipidea USB controller
|
||||
and Synapsys USB PHY in host mode only.
|
||||
|
||||
config USB_EHCI_GENERIC
|
||||
bool "Support for generic EHCI USB controller"
|
||||
depends on OF_CONTROL
|
||||
depends on DM_USB
|
||||
default n
|
||||
---help---
|
||||
Enables support for generic EHCI controller.
|
||||
|
||||
endif
|
||||
68
u-boot/drivers/usb/host/Makefile
Normal file
68
u-boot/drivers/usb/host/Makefile
Normal file
@@ -0,0 +1,68 @@
|
||||
#
|
||||
# (C) Copyright 2000-2007
|
||||
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
ifdef CONFIG_DM_USB
|
||||
obj-$(CONFIG_CMD_USB) += usb-uclass.o
|
||||
obj-$(CONFIG_SANDBOX) += usb-sandbox.o
|
||||
endif
|
||||
|
||||
# ohci
|
||||
obj-$(CONFIG_USB_OHCI_NEW) += ohci-hcd.o
|
||||
obj-$(CONFIG_USB_ATMEL) += ohci-at91.o
|
||||
obj-$(CONFIG_USB_OHCI_DA8XX) += ohci-da8xx.o
|
||||
obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o
|
||||
obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o
|
||||
obj-$(CONFIG_USB_SL811HS) += sl811-hcd.o
|
||||
obj-$(CONFIG_USB_OHCI_S3C24XX) += ohci-s3c24xx.o
|
||||
obj-$(CONFIG_USB_OHCI_EP93XX) += ohci-ep93xx.o
|
||||
obj-$(CONFIG_USB_OHCI_SUNXI) += ohci-sunxi.o
|
||||
obj-$(CONFIG_USB_OHCI_LPC32XX) += ohci-lpc32xx.o
|
||||
obj-$(CONFIG_USB_OHCI_GENERIC) += ohci-generic.o
|
||||
|
||||
# echi
|
||||
obj-$(CONFIG_USB_EHCI) += ehci-hcd.o
|
||||
obj-$(CONFIG_USB_EHCI_ARMADA100) += ehci-armada100.o utmi-armada100.o
|
||||
obj-$(CONFIG_USB_EHCI_ATMEL) += ehci-atmel.o
|
||||
ifdef CONFIG_MPC512X
|
||||
obj-$(CONFIG_USB_EHCI_FSL) += ehci-mpc512x.o
|
||||
else
|
||||
obj-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o
|
||||
endif
|
||||
obj-$(CONFIG_USB_EHCI_FARADAY) += ehci-faraday.o
|
||||
obj-$(CONFIG_USB_EHCI_GENERIC) += ehci-generic.o
|
||||
obj-$(CONFIG_USB_EHCI_EXYNOS) += ehci-exynos.o
|
||||
obj-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o
|
||||
obj-$(CONFIG_USB_EHCI_MXS) += ehci-mxs.o
|
||||
obj-$(CONFIG_USB_EHCI_MX5) += ehci-mx5.o
|
||||
obj-$(CONFIG_USB_EHCI_MX6) += ehci-mx6.o
|
||||
obj-$(CONFIG_USB_EHCI_MX7) += ehci-mx6.o
|
||||
obj-$(CONFIG_USB_EHCI_OMAP) += ehci-omap.o
|
||||
obj-$(CONFIG_USB_EHCI_PPC4XX) += ehci-ppc4xx.o
|
||||
obj-$(CONFIG_USB_EHCI_MARVELL) += ehci-marvell.o
|
||||
obj-$(CONFIG_USB_EHCI_MSM) += ehci-msm.o
|
||||
obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o
|
||||
obj-$(CONFIG_USB_EHCI_SPEAR) += ehci-spear.o
|
||||
obj-$(CONFIG_USB_EHCI_SUNXI) += ehci-sunxi.o
|
||||
obj-$(CONFIG_USB_EHCI_TEGRA) += ehci-tegra.o
|
||||
obj-$(CONFIG_USB_EHCI_VCT) += ehci-vct.o
|
||||
obj-$(CONFIG_USB_EHCI_VF) += ehci-vf.o
|
||||
obj-$(CONFIG_USB_EHCI_RMOBILE) += ehci-rmobile.o
|
||||
obj-$(CONFIG_USB_EHCI_ZYNQ) += ehci-zynq.o
|
||||
|
||||
# xhci
|
||||
obj-$(CONFIG_USB_XHCI_HCD) += xhci.o xhci-mem.o xhci-ring.o
|
||||
obj-$(CONFIG_USB_XHCI_DWC3) += xhci-dwc3.o
|
||||
obj-$(CONFIG_USB_XHCI_ZYNQMP) += xhci-zynqmp.o
|
||||
obj-$(CONFIG_USB_XHCI_KEYSTONE) += xhci-keystone.o
|
||||
obj-$(CONFIG_USB_XHCI_EXYNOS) += xhci-exynos5.o
|
||||
obj-$(CONFIG_USB_XHCI_FSL) += xhci-fsl.o
|
||||
obj-$(CONFIG_USB_XHCI_OMAP) += xhci-omap.o
|
||||
obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o
|
||||
obj-$(CONFIG_USB_XHCI_UNIPHIER) += xhci-uniphier.o
|
||||
|
||||
# designware
|
||||
obj-$(CONFIG_USB_DWC2) += dwc2.o
|
||||
1252
u-boot/drivers/usb/host/dwc2.c
Normal file
1252
u-boot/drivers/usb/host/dwc2.c
Normal file
File diff suppressed because it is too large
Load Diff
789
u-boot/drivers/usb/host/dwc2.h
Normal file
789
u-boot/drivers/usb/host/dwc2.h
Normal file
@@ -0,0 +1,789 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Marek Vasut <marex@denx.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#ifndef __DWC2_H__
|
||||
#define __DWC2_H__
|
||||
|
||||
struct dwc2_hc_regs {
|
||||
u32 hcchar; /* 0x00 */
|
||||
u32 hcsplt;
|
||||
u32 hcint;
|
||||
u32 hcintmsk;
|
||||
u32 hctsiz; /* 0x10 */
|
||||
u32 hcdma;
|
||||
u32 reserved;
|
||||
u32 hcdmab;
|
||||
};
|
||||
|
||||
struct dwc2_host_regs {
|
||||
u32 hcfg; /* 0x00 */
|
||||
u32 hfir;
|
||||
u32 hfnum;
|
||||
u32 _pad_0x40c;
|
||||
u32 hptxsts; /* 0x10 */
|
||||
u32 haint;
|
||||
u32 haintmsk;
|
||||
u32 hflbaddr;
|
||||
};
|
||||
|
||||
struct dwc2_core_regs {
|
||||
u32 gotgctl; /* 0x000 */
|
||||
u32 gotgint;
|
||||
u32 gahbcfg;
|
||||
u32 gusbcfg;
|
||||
u32 grstctl; /* 0x010 */
|
||||
u32 gintsts;
|
||||
u32 gintmsk;
|
||||
u32 grxstsr;
|
||||
u32 grxstsp; /* 0x020 */
|
||||
u32 grxfsiz;
|
||||
u32 gnptxfsiz;
|
||||
u32 gnptxsts;
|
||||
u32 gi2cctl; /* 0x030 */
|
||||
u32 gpvndctl;
|
||||
u32 ggpio;
|
||||
u32 guid;
|
||||
u32 gsnpsid; /* 0x040 */
|
||||
u32 ghwcfg1;
|
||||
u32 ghwcfg2;
|
||||
u32 ghwcfg3;
|
||||
u32 ghwcfg4; /* 0x050 */
|
||||
u32 glpmcfg;
|
||||
u32 _pad_0x58_0x9c[42];
|
||||
u32 hptxfsiz; /* 0x100 */
|
||||
u32 dptxfsiz_dieptxf[15];
|
||||
u32 _pad_0x140_0x3fc[176];
|
||||
struct dwc2_host_regs host_regs; /* 0x400 */
|
||||
u32 _pad_0x420_0x43c[8];
|
||||
u32 hprt0; /* 0x440 */
|
||||
u32 _pad_0x444_0x4fc[47];
|
||||
struct dwc2_hc_regs hc_regs[16]; /* 0x500 */
|
||||
u32 _pad_0x700_0xe00[448];
|
||||
u32 pcgcctl; /* 0xe00 */
|
||||
};
|
||||
|
||||
#define DWC2_GOTGCTL_SESREQSCS (1 << 0)
|
||||
#define DWC2_GOTGCTL_SESREQSCS_OFFSET 0
|
||||
#define DWC2_GOTGCTL_SESREQ (1 << 1)
|
||||
#define DWC2_GOTGCTL_SESREQ_OFFSET 1
|
||||
#define DWC2_GOTGCTL_HSTNEGSCS (1 << 8)
|
||||
#define DWC2_GOTGCTL_HSTNEGSCS_OFFSET 8
|
||||
#define DWC2_GOTGCTL_HNPREQ (1 << 9)
|
||||
#define DWC2_GOTGCTL_HNPREQ_OFFSET 9
|
||||
#define DWC2_GOTGCTL_HSTSETHNPEN (1 << 10)
|
||||
#define DWC2_GOTGCTL_HSTSETHNPEN_OFFSET 10
|
||||
#define DWC2_GOTGCTL_DEVHNPEN (1 << 11)
|
||||
#define DWC2_GOTGCTL_DEVHNPEN_OFFSET 11
|
||||
#define DWC2_GOTGCTL_CONIDSTS (1 << 16)
|
||||
#define DWC2_GOTGCTL_CONIDSTS_OFFSET 16
|
||||
#define DWC2_GOTGCTL_DBNCTIME (1 << 17)
|
||||
#define DWC2_GOTGCTL_DBNCTIME_OFFSET 17
|
||||
#define DWC2_GOTGCTL_ASESVLD (1 << 18)
|
||||
#define DWC2_GOTGCTL_ASESVLD_OFFSET 18
|
||||
#define DWC2_GOTGCTL_BSESVLD (1 << 19)
|
||||
#define DWC2_GOTGCTL_BSESVLD_OFFSET 19
|
||||
#define DWC2_GOTGCTL_OTGVER (1 << 20)
|
||||
#define DWC2_GOTGCTL_OTGVER_OFFSET 20
|
||||
#define DWC2_GOTGINT_SESENDDET (1 << 2)
|
||||
#define DWC2_GOTGINT_SESENDDET_OFFSET 2
|
||||
#define DWC2_GOTGINT_SESREQSUCSTSCHNG (1 << 8)
|
||||
#define DWC2_GOTGINT_SESREQSUCSTSCHNG_OFFSET 8
|
||||
#define DWC2_GOTGINT_HSTNEGSUCSTSCHNG (1 << 9)
|
||||
#define DWC2_GOTGINT_HSTNEGSUCSTSCHNG_OFFSET 9
|
||||
#define DWC2_GOTGINT_RESERVER10_16_MASK (0x7F << 10)
|
||||
#define DWC2_GOTGINT_RESERVER10_16_OFFSET 10
|
||||
#define DWC2_GOTGINT_HSTNEGDET (1 << 17)
|
||||
#define DWC2_GOTGINT_HSTNEGDET_OFFSET 17
|
||||
#define DWC2_GOTGINT_ADEVTOUTCHNG (1 << 18)
|
||||
#define DWC2_GOTGINT_ADEVTOUTCHNG_OFFSET 18
|
||||
#define DWC2_GOTGINT_DEBDONE (1 << 19)
|
||||
#define DWC2_GOTGINT_DEBDONE_OFFSET 19
|
||||
#define DWC2_GAHBCFG_GLBLINTRMSK (1 << 0)
|
||||
#define DWC2_GAHBCFG_GLBLINTRMSK_OFFSET 0
|
||||
#define DWC2_GAHBCFG_HBURSTLEN_SINGLE (0 << 1)
|
||||
#define DWC2_GAHBCFG_HBURSTLEN_INCR (1 << 1)
|
||||
#define DWC2_GAHBCFG_HBURSTLEN_INCR4 (3 << 1)
|
||||
#define DWC2_GAHBCFG_HBURSTLEN_INCR8 (5 << 1)
|
||||
#define DWC2_GAHBCFG_HBURSTLEN_INCR16 (7 << 1)
|
||||
#define DWC2_GAHBCFG_HBURSTLEN_MASK (0xF << 1)
|
||||
#define DWC2_GAHBCFG_HBURSTLEN_OFFSET 1
|
||||
#define DWC2_GAHBCFG_DMAENABLE (1 << 5)
|
||||
#define DWC2_GAHBCFG_DMAENABLE_OFFSET 5
|
||||
#define DWC2_GAHBCFG_NPTXFEMPLVL_TXFEMPLVL (1 << 7)
|
||||
#define DWC2_GAHBCFG_NPTXFEMPLVL_TXFEMPLVL_OFFSET 7
|
||||
#define DWC2_GAHBCFG_PTXFEMPLVL (1 << 8)
|
||||
#define DWC2_GAHBCFG_PTXFEMPLVL_OFFSET 8
|
||||
#define DWC2_GUSBCFG_TOUTCAL_MASK (0x7 << 0)
|
||||
#define DWC2_GUSBCFG_TOUTCAL_OFFSET 0
|
||||
#define DWC2_GUSBCFG_PHYIF (1 << 3)
|
||||
#define DWC2_GUSBCFG_PHYIF_OFFSET 3
|
||||
#define DWC2_GUSBCFG_ULPI_UTMI_SEL (1 << 4)
|
||||
#define DWC2_GUSBCFG_ULPI_UTMI_SEL_OFFSET 4
|
||||
#define DWC2_GUSBCFG_FSINTF (1 << 5)
|
||||
#define DWC2_GUSBCFG_FSINTF_OFFSET 5
|
||||
#define DWC2_GUSBCFG_PHYSEL (1 << 6)
|
||||
#define DWC2_GUSBCFG_PHYSEL_OFFSET 6
|
||||
#define DWC2_GUSBCFG_DDRSEL (1 << 7)
|
||||
#define DWC2_GUSBCFG_DDRSEL_OFFSET 7
|
||||
#define DWC2_GUSBCFG_SRPCAP (1 << 8)
|
||||
#define DWC2_GUSBCFG_SRPCAP_OFFSET 8
|
||||
#define DWC2_GUSBCFG_HNPCAP (1 << 9)
|
||||
#define DWC2_GUSBCFG_HNPCAP_OFFSET 9
|
||||
#define DWC2_GUSBCFG_USBTRDTIM_MASK (0xF << 10)
|
||||
#define DWC2_GUSBCFG_USBTRDTIM_OFFSET 10
|
||||
#define DWC2_GUSBCFG_NPTXFRWNDEN (1 << 14)
|
||||
#define DWC2_GUSBCFG_NPTXFRWNDEN_OFFSET 14
|
||||
#define DWC2_GUSBCFG_PHYLPWRCLKSEL (1 << 15)
|
||||
#define DWC2_GUSBCFG_PHYLPWRCLKSEL_OFFSET 15
|
||||
#define DWC2_GUSBCFG_OTGUTMIFSSEL (1 << 16)
|
||||
#define DWC2_GUSBCFG_OTGUTMIFSSEL_OFFSET 16
|
||||
#define DWC2_GUSBCFG_ULPI_FSLS (1 << 17)
|
||||
#define DWC2_GUSBCFG_ULPI_FSLS_OFFSET 17
|
||||
#define DWC2_GUSBCFG_ULPI_AUTO_RES (1 << 18)
|
||||
#define DWC2_GUSBCFG_ULPI_AUTO_RES_OFFSET 18
|
||||
#define DWC2_GUSBCFG_ULPI_CLK_SUS_M (1 << 19)
|
||||
#define DWC2_GUSBCFG_ULPI_CLK_SUS_M_OFFSET 19
|
||||
#define DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV (1 << 20)
|
||||
#define DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV_OFFSET 20
|
||||
#define DWC2_GUSBCFG_ULPI_INT_VBUS_INDICATOR (1 << 21)
|
||||
#define DWC2_GUSBCFG_ULPI_INT_VBUS_INDICATOR_OFFSET 21
|
||||
#define DWC2_GUSBCFG_TERM_SEL_DL_PULSE (1 << 22)
|
||||
#define DWC2_GUSBCFG_TERM_SEL_DL_PULSE_OFFSET 22
|
||||
#define DWC2_GUSBCFG_INDICATOR_PASSTHROUGH (1 << 24)
|
||||
#define DWC2_GUSBCFG_INDICATOR_PASSTHROUGH_OFFSET 24
|
||||
#define DWC2_GUSBCFG_IC_USB_CAP (1 << 26)
|
||||
#define DWC2_GUSBCFG_IC_USB_CAP_OFFSET 26
|
||||
#define DWC2_GUSBCFG_IC_TRAFFIC_PULL_REMOVE (1 << 27)
|
||||
#define DWC2_GUSBCFG_IC_TRAFFIC_PULL_REMOVE_OFFSET 27
|
||||
#define DWC2_GUSBCFG_TX_END_DELAY (1 << 28)
|
||||
#define DWC2_GUSBCFG_TX_END_DELAY_OFFSET 28
|
||||
#define DWC2_GUSBCFG_FORCEHOSTMODE (1 << 29)
|
||||
#define DWC2_GUSBCFG_FORCEHOSTMODE_OFFSET 29
|
||||
#define DWC2_GUSBCFG_FORCEDEVMODE (1 << 30)
|
||||
#define DWC2_GUSBCFG_FORCEDEVMODE_OFFSET 30
|
||||
#define DWC2_GLPMCTL_LPM_CAP_EN (1 << 0)
|
||||
#define DWC2_GLPMCTL_LPM_CAP_EN_OFFSET 0
|
||||
#define DWC2_GLPMCTL_APPL_RESP (1 << 1)
|
||||
#define DWC2_GLPMCTL_APPL_RESP_OFFSET 1
|
||||
#define DWC2_GLPMCTL_HIRD_MASK (0xF << 2)
|
||||
#define DWC2_GLPMCTL_HIRD_OFFSET 2
|
||||
#define DWC2_GLPMCTL_REM_WKUP_EN (1 << 6)
|
||||
#define DWC2_GLPMCTL_REM_WKUP_EN_OFFSET 6
|
||||
#define DWC2_GLPMCTL_EN_UTMI_SLEEP (1 << 7)
|
||||
#define DWC2_GLPMCTL_EN_UTMI_SLEEP_OFFSET 7
|
||||
#define DWC2_GLPMCTL_HIRD_THRES_MASK (0x1F << 8)
|
||||
#define DWC2_GLPMCTL_HIRD_THRES_OFFSET 8
|
||||
#define DWC2_GLPMCTL_LPM_RESP_MASK (0x3 << 13)
|
||||
#define DWC2_GLPMCTL_LPM_RESP_OFFSET 13
|
||||
#define DWC2_GLPMCTL_PRT_SLEEP_STS (1 << 15)
|
||||
#define DWC2_GLPMCTL_PRT_SLEEP_STS_OFFSET 15
|
||||
#define DWC2_GLPMCTL_SLEEP_STATE_RESUMEOK (1 << 16)
|
||||
#define DWC2_GLPMCTL_SLEEP_STATE_RESUMEOK_OFFSET 16
|
||||
#define DWC2_GLPMCTL_LPM_CHAN_INDEX_MASK (0xF << 17)
|
||||
#define DWC2_GLPMCTL_LPM_CHAN_INDEX_OFFSET 17
|
||||
#define DWC2_GLPMCTL_RETRY_COUNT_MASK (0x7 << 21)
|
||||
#define DWC2_GLPMCTL_RETRY_COUNT_OFFSET 21
|
||||
#define DWC2_GLPMCTL_SEND_LPM (1 << 24)
|
||||
#define DWC2_GLPMCTL_SEND_LPM_OFFSET 24
|
||||
#define DWC2_GLPMCTL_RETRY_COUNT_STS_MASK (0x7 << 25)
|
||||
#define DWC2_GLPMCTL_RETRY_COUNT_STS_OFFSET 25
|
||||
#define DWC2_GLPMCTL_HSIC_CONNECT (1 << 30)
|
||||
#define DWC2_GLPMCTL_HSIC_CONNECT_OFFSET 30
|
||||
#define DWC2_GLPMCTL_INV_SEL_HSIC (1 << 31)
|
||||
#define DWC2_GLPMCTL_INV_SEL_HSIC_OFFSET 31
|
||||
#define DWC2_GRSTCTL_CSFTRST (1 << 0)
|
||||
#define DWC2_GRSTCTL_CSFTRST_OFFSET 0
|
||||
#define DWC2_GRSTCTL_HSFTRST (1 << 1)
|
||||
#define DWC2_GRSTCTL_HSFTRST_OFFSET 1
|
||||
#define DWC2_GRSTCTL_HSTFRM (1 << 2)
|
||||
#define DWC2_GRSTCTL_HSTFRM_OFFSET 2
|
||||
#define DWC2_GRSTCTL_INTKNQFLSH (1 << 3)
|
||||
#define DWC2_GRSTCTL_INTKNQFLSH_OFFSET 3
|
||||
#define DWC2_GRSTCTL_RXFFLSH (1 << 4)
|
||||
#define DWC2_GRSTCTL_RXFFLSH_OFFSET 4
|
||||
#define DWC2_GRSTCTL_TXFFLSH (1 << 5)
|
||||
#define DWC2_GRSTCTL_TXFFLSH_OFFSET 5
|
||||
#define DWC2_GRSTCTL_TXFNUM_MASK (0x1F << 6)
|
||||
#define DWC2_GRSTCTL_TXFNUM_OFFSET 6
|
||||
#define DWC2_GRSTCTL_DMAREQ (1 << 30)
|
||||
#define DWC2_GRSTCTL_DMAREQ_OFFSET 30
|
||||
#define DWC2_GRSTCTL_AHBIDLE (1 << 31)
|
||||
#define DWC2_GRSTCTL_AHBIDLE_OFFSET 31
|
||||
#define DWC2_GINTMSK_MODEMISMATCH (1 << 1)
|
||||
#define DWC2_GINTMSK_MODEMISMATCH_OFFSET 1
|
||||
#define DWC2_GINTMSK_OTGINTR (1 << 2)
|
||||
#define DWC2_GINTMSK_OTGINTR_OFFSET 2
|
||||
#define DWC2_GINTMSK_SOFINTR (1 << 3)
|
||||
#define DWC2_GINTMSK_SOFINTR_OFFSET 3
|
||||
#define DWC2_GINTMSK_RXSTSQLVL (1 << 4)
|
||||
#define DWC2_GINTMSK_RXSTSQLVL_OFFSET 4
|
||||
#define DWC2_GINTMSK_NPTXFEMPTY (1 << 5)
|
||||
#define DWC2_GINTMSK_NPTXFEMPTY_OFFSET 5
|
||||
#define DWC2_GINTMSK_GINNAKEFF (1 << 6)
|
||||
#define DWC2_GINTMSK_GINNAKEFF_OFFSET 6
|
||||
#define DWC2_GINTMSK_GOUTNAKEFF (1 << 7)
|
||||
#define DWC2_GINTMSK_GOUTNAKEFF_OFFSET 7
|
||||
#define DWC2_GINTMSK_I2CINTR (1 << 9)
|
||||
#define DWC2_GINTMSK_I2CINTR_OFFSET 9
|
||||
#define DWC2_GINTMSK_ERLYSUSPEND (1 << 10)
|
||||
#define DWC2_GINTMSK_ERLYSUSPEND_OFFSET 10
|
||||
#define DWC2_GINTMSK_USBSUSPEND (1 << 11)
|
||||
#define DWC2_GINTMSK_USBSUSPEND_OFFSET 11
|
||||
#define DWC2_GINTMSK_USBRESET (1 << 12)
|
||||
#define DWC2_GINTMSK_USBRESET_OFFSET 12
|
||||
#define DWC2_GINTMSK_ENUMDONE (1 << 13)
|
||||
#define DWC2_GINTMSK_ENUMDONE_OFFSET 13
|
||||
#define DWC2_GINTMSK_ISOOUTDROP (1 << 14)
|
||||
#define DWC2_GINTMSK_ISOOUTDROP_OFFSET 14
|
||||
#define DWC2_GINTMSK_EOPFRAME (1 << 15)
|
||||
#define DWC2_GINTMSK_EOPFRAME_OFFSET 15
|
||||
#define DWC2_GINTMSK_EPMISMATCH (1 << 17)
|
||||
#define DWC2_GINTMSK_EPMISMATCH_OFFSET 17
|
||||
#define DWC2_GINTMSK_INEPINTR (1 << 18)
|
||||
#define DWC2_GINTMSK_INEPINTR_OFFSET 18
|
||||
#define DWC2_GINTMSK_OUTEPINTR (1 << 19)
|
||||
#define DWC2_GINTMSK_OUTEPINTR_OFFSET 19
|
||||
#define DWC2_GINTMSK_INCOMPLISOIN (1 << 20)
|
||||
#define DWC2_GINTMSK_INCOMPLISOIN_OFFSET 20
|
||||
#define DWC2_GINTMSK_INCOMPLISOOUT (1 << 21)
|
||||
#define DWC2_GINTMSK_INCOMPLISOOUT_OFFSET 21
|
||||
#define DWC2_GINTMSK_PORTINTR (1 << 24)
|
||||
#define DWC2_GINTMSK_PORTINTR_OFFSET 24
|
||||
#define DWC2_GINTMSK_HCINTR (1 << 25)
|
||||
#define DWC2_GINTMSK_HCINTR_OFFSET 25
|
||||
#define DWC2_GINTMSK_PTXFEMPTY (1 << 26)
|
||||
#define DWC2_GINTMSK_PTXFEMPTY_OFFSET 26
|
||||
#define DWC2_GINTMSK_LPMTRANRCVD (1 << 27)
|
||||
#define DWC2_GINTMSK_LPMTRANRCVD_OFFSET 27
|
||||
#define DWC2_GINTMSK_CONIDSTSCHNG (1 << 28)
|
||||
#define DWC2_GINTMSK_CONIDSTSCHNG_OFFSET 28
|
||||
#define DWC2_GINTMSK_DISCONNECT (1 << 29)
|
||||
#define DWC2_GINTMSK_DISCONNECT_OFFSET 29
|
||||
#define DWC2_GINTMSK_SESSREQINTR (1 << 30)
|
||||
#define DWC2_GINTMSK_SESSREQINTR_OFFSET 30
|
||||
#define DWC2_GINTMSK_WKUPINTR (1 << 31)
|
||||
#define DWC2_GINTMSK_WKUPINTR_OFFSET 31
|
||||
#define DWC2_GINTSTS_CURMODE_DEVICE (0 << 0)
|
||||
#define DWC2_GINTSTS_CURMODE_HOST (1 << 0)
|
||||
#define DWC2_GINTSTS_CURMODE (1 << 0)
|
||||
#define DWC2_GINTSTS_CURMODE_OFFSET 0
|
||||
#define DWC2_GINTSTS_MODEMISMATCH (1 << 1)
|
||||
#define DWC2_GINTSTS_MODEMISMATCH_OFFSET 1
|
||||
#define DWC2_GINTSTS_OTGINTR (1 << 2)
|
||||
#define DWC2_GINTSTS_OTGINTR_OFFSET 2
|
||||
#define DWC2_GINTSTS_SOFINTR (1 << 3)
|
||||
#define DWC2_GINTSTS_SOFINTR_OFFSET 3
|
||||
#define DWC2_GINTSTS_RXSTSQLVL (1 << 4)
|
||||
#define DWC2_GINTSTS_RXSTSQLVL_OFFSET 4
|
||||
#define DWC2_GINTSTS_NPTXFEMPTY (1 << 5)
|
||||
#define DWC2_GINTSTS_NPTXFEMPTY_OFFSET 5
|
||||
#define DWC2_GINTSTS_GINNAKEFF (1 << 6)
|
||||
#define DWC2_GINTSTS_GINNAKEFF_OFFSET 6
|
||||
#define DWC2_GINTSTS_GOUTNAKEFF (1 << 7)
|
||||
#define DWC2_GINTSTS_GOUTNAKEFF_OFFSET 7
|
||||
#define DWC2_GINTSTS_I2CINTR (1 << 9)
|
||||
#define DWC2_GINTSTS_I2CINTR_OFFSET 9
|
||||
#define DWC2_GINTSTS_ERLYSUSPEND (1 << 10)
|
||||
#define DWC2_GINTSTS_ERLYSUSPEND_OFFSET 10
|
||||
#define DWC2_GINTSTS_USBSUSPEND (1 << 11)
|
||||
#define DWC2_GINTSTS_USBSUSPEND_OFFSET 11
|
||||
#define DWC2_GINTSTS_USBRESET (1 << 12)
|
||||
#define DWC2_GINTSTS_USBRESET_OFFSET 12
|
||||
#define DWC2_GINTSTS_ENUMDONE (1 << 13)
|
||||
#define DWC2_GINTSTS_ENUMDONE_OFFSET 13
|
||||
#define DWC2_GINTSTS_ISOOUTDROP (1 << 14)
|
||||
#define DWC2_GINTSTS_ISOOUTDROP_OFFSET 14
|
||||
#define DWC2_GINTSTS_EOPFRAME (1 << 15)
|
||||
#define DWC2_GINTSTS_EOPFRAME_OFFSET 15
|
||||
#define DWC2_GINTSTS_INTOKENRX (1 << 16)
|
||||
#define DWC2_GINTSTS_INTOKENRX_OFFSET 16
|
||||
#define DWC2_GINTSTS_EPMISMATCH (1 << 17)
|
||||
#define DWC2_GINTSTS_EPMISMATCH_OFFSET 17
|
||||
#define DWC2_GINTSTS_INEPINT (1 << 18)
|
||||
#define DWC2_GINTSTS_INEPINT_OFFSET 18
|
||||
#define DWC2_GINTSTS_OUTEPINTR (1 << 19)
|
||||
#define DWC2_GINTSTS_OUTEPINTR_OFFSET 19
|
||||
#define DWC2_GINTSTS_INCOMPLISOIN (1 << 20)
|
||||
#define DWC2_GINTSTS_INCOMPLISOIN_OFFSET 20
|
||||
#define DWC2_GINTSTS_INCOMPLISOOUT (1 << 21)
|
||||
#define DWC2_GINTSTS_INCOMPLISOOUT_OFFSET 21
|
||||
#define DWC2_GINTSTS_PORTINTR (1 << 24)
|
||||
#define DWC2_GINTSTS_PORTINTR_OFFSET 24
|
||||
#define DWC2_GINTSTS_HCINTR (1 << 25)
|
||||
#define DWC2_GINTSTS_HCINTR_OFFSET 25
|
||||
#define DWC2_GINTSTS_PTXFEMPTY (1 << 26)
|
||||
#define DWC2_GINTSTS_PTXFEMPTY_OFFSET 26
|
||||
#define DWC2_GINTSTS_LPMTRANRCVD (1 << 27)
|
||||
#define DWC2_GINTSTS_LPMTRANRCVD_OFFSET 27
|
||||
#define DWC2_GINTSTS_CONIDSTSCHNG (1 << 28)
|
||||
#define DWC2_GINTSTS_CONIDSTSCHNG_OFFSET 28
|
||||
#define DWC2_GINTSTS_DISCONNECT (1 << 29)
|
||||
#define DWC2_GINTSTS_DISCONNECT_OFFSET 29
|
||||
#define DWC2_GINTSTS_SESSREQINTR (1 << 30)
|
||||
#define DWC2_GINTSTS_SESSREQINTR_OFFSET 30
|
||||
#define DWC2_GINTSTS_WKUPINTR (1 << 31)
|
||||
#define DWC2_GINTSTS_WKUPINTR_OFFSET 31
|
||||
#define DWC2_GRXSTS_EPNUM_MASK (0xF << 0)
|
||||
#define DWC2_GRXSTS_EPNUM_OFFSET 0
|
||||
#define DWC2_GRXSTS_BCNT_MASK (0x7FF << 4)
|
||||
#define DWC2_GRXSTS_BCNT_OFFSET 4
|
||||
#define DWC2_GRXSTS_DPID_MASK (0x3 << 15)
|
||||
#define DWC2_GRXSTS_DPID_OFFSET 15
|
||||
#define DWC2_GRXSTS_PKTSTS_MASK (0xF << 17)
|
||||
#define DWC2_GRXSTS_PKTSTS_OFFSET 17
|
||||
#define DWC2_GRXSTS_FN_MASK (0xF << 21)
|
||||
#define DWC2_GRXSTS_FN_OFFSET 21
|
||||
#define DWC2_FIFOSIZE_STARTADDR_MASK (0xFFFF << 0)
|
||||
#define DWC2_FIFOSIZE_STARTADDR_OFFSET 0
|
||||
#define DWC2_FIFOSIZE_DEPTH_MASK (0xFFFF << 16)
|
||||
#define DWC2_FIFOSIZE_DEPTH_OFFSET 16
|
||||
#define DWC2_GNPTXSTS_NPTXFSPCAVAIL_MASK (0xFFFF << 0)
|
||||
#define DWC2_GNPTXSTS_NPTXFSPCAVAIL_OFFSET 0
|
||||
#define DWC2_GNPTXSTS_NPTXQSPCAVAIL_MASK (0xFF << 16)
|
||||
#define DWC2_GNPTXSTS_NPTXQSPCAVAIL_OFFSET 16
|
||||
#define DWC2_GNPTXSTS_NPTXQTOP_TERMINATE (1 << 24)
|
||||
#define DWC2_GNPTXSTS_NPTXQTOP_TERMINATE_OFFSET 24
|
||||
#define DWC2_GNPTXSTS_NPTXQTOP_TOKEN_MASK (0x3 << 25)
|
||||
#define DWC2_GNPTXSTS_NPTXQTOP_TOKEN_OFFSET 25
|
||||
#define DWC2_GNPTXSTS_NPTXQTOP_CHNEP_MASK (0xF << 27)
|
||||
#define DWC2_GNPTXSTS_NPTXQTOP_CHNEP_OFFSET 27
|
||||
#define DWC2_DTXFSTS_TXFSPCAVAIL_MASK (0xFFFF << 0)
|
||||
#define DWC2_DTXFSTS_TXFSPCAVAIL_OFFSET 0
|
||||
#define DWC2_GI2CCTL_RWDATA_MASK (0xFF << 0)
|
||||
#define DWC2_GI2CCTL_RWDATA_OFFSET 0
|
||||
#define DWC2_GI2CCTL_REGADDR_MASK (0xFF << 8)
|
||||
#define DWC2_GI2CCTL_REGADDR_OFFSET 8
|
||||
#define DWC2_GI2CCTL_ADDR_MASK (0x7F << 16)
|
||||
#define DWC2_GI2CCTL_ADDR_OFFSET 16
|
||||
#define DWC2_GI2CCTL_I2CEN (1 << 23)
|
||||
#define DWC2_GI2CCTL_I2CEN_OFFSET 23
|
||||
#define DWC2_GI2CCTL_ACK (1 << 24)
|
||||
#define DWC2_GI2CCTL_ACK_OFFSET 24
|
||||
#define DWC2_GI2CCTL_I2CSUSPCTL (1 << 25)
|
||||
#define DWC2_GI2CCTL_I2CSUSPCTL_OFFSET 25
|
||||
#define DWC2_GI2CCTL_I2CDEVADDR_MASK (0x3 << 26)
|
||||
#define DWC2_GI2CCTL_I2CDEVADDR_OFFSET 26
|
||||
#define DWC2_GI2CCTL_RW (1 << 30)
|
||||
#define DWC2_GI2CCTL_RW_OFFSET 30
|
||||
#define DWC2_GI2CCTL_BSYDNE (1 << 31)
|
||||
#define DWC2_GI2CCTL_BSYDNE_OFFSET 31
|
||||
#define DWC2_HWCFG1_EP_DIR0_MASK (0x3 << 0)
|
||||
#define DWC2_HWCFG1_EP_DIR0_OFFSET 0
|
||||
#define DWC2_HWCFG1_EP_DIR1_MASK (0x3 << 2)
|
||||
#define DWC2_HWCFG1_EP_DIR1_OFFSET 2
|
||||
#define DWC2_HWCFG1_EP_DIR2_MASK (0x3 << 4)
|
||||
#define DWC2_HWCFG1_EP_DIR2_OFFSET 4
|
||||
#define DWC2_HWCFG1_EP_DIR3_MASK (0x3 << 6)
|
||||
#define DWC2_HWCFG1_EP_DIR3_OFFSET 6
|
||||
#define DWC2_HWCFG1_EP_DIR4_MASK (0x3 << 8)
|
||||
#define DWC2_HWCFG1_EP_DIR4_OFFSET 8
|
||||
#define DWC2_HWCFG1_EP_DIR5_MASK (0x3 << 10)
|
||||
#define DWC2_HWCFG1_EP_DIR5_OFFSET 10
|
||||
#define DWC2_HWCFG1_EP_DIR6_MASK (0x3 << 12)
|
||||
#define DWC2_HWCFG1_EP_DIR6_OFFSET 12
|
||||
#define DWC2_HWCFG1_EP_DIR7_MASK (0x3 << 14)
|
||||
#define DWC2_HWCFG1_EP_DIR7_OFFSET 14
|
||||
#define DWC2_HWCFG1_EP_DIR8_MASK (0x3 << 16)
|
||||
#define DWC2_HWCFG1_EP_DIR8_OFFSET 16
|
||||
#define DWC2_HWCFG1_EP_DIR9_MASK (0x3 << 18)
|
||||
#define DWC2_HWCFG1_EP_DIR9_OFFSET 18
|
||||
#define DWC2_HWCFG1_EP_DIR10_MASK (0x3 << 20)
|
||||
#define DWC2_HWCFG1_EP_DIR10_OFFSET 20
|
||||
#define DWC2_HWCFG1_EP_DIR11_MASK (0x3 << 22)
|
||||
#define DWC2_HWCFG1_EP_DIR11_OFFSET 22
|
||||
#define DWC2_HWCFG1_EP_DIR12_MASK (0x3 << 24)
|
||||
#define DWC2_HWCFG1_EP_DIR12_OFFSET 24
|
||||
#define DWC2_HWCFG1_EP_DIR13_MASK (0x3 << 26)
|
||||
#define DWC2_HWCFG1_EP_DIR13_OFFSET 26
|
||||
#define DWC2_HWCFG1_EP_DIR14_MASK (0x3 << 28)
|
||||
#define DWC2_HWCFG1_EP_DIR14_OFFSET 28
|
||||
#define DWC2_HWCFG1_EP_DIR15_MASK (0x3 << 30)
|
||||
#define DWC2_HWCFG1_EP_DIR15_OFFSET 30
|
||||
#define DWC2_HWCFG2_OP_MODE_MASK (0x7 << 0)
|
||||
#define DWC2_HWCFG2_OP_MODE_OFFSET 0
|
||||
#define DWC2_HWCFG2_ARCHITECTURE_SLAVE_ONLY (0x0 << 3)
|
||||
#define DWC2_HWCFG2_ARCHITECTURE_EXT_DMA (0x1 << 3)
|
||||
#define DWC2_HWCFG2_ARCHITECTURE_INT_DMA (0x2 << 3)
|
||||
#define DWC2_HWCFG2_ARCHITECTURE_MASK (0x3 << 3)
|
||||
#define DWC2_HWCFG2_ARCHITECTURE_OFFSET 3
|
||||
#define DWC2_HWCFG2_POINT2POINT (1 << 5)
|
||||
#define DWC2_HWCFG2_POINT2POINT_OFFSET 5
|
||||
#define DWC2_HWCFG2_HS_PHY_TYPE_MASK (0x3 << 6)
|
||||
#define DWC2_HWCFG2_HS_PHY_TYPE_OFFSET 6
|
||||
#define DWC2_HWCFG2_FS_PHY_TYPE_MASK (0x3 << 8)
|
||||
#define DWC2_HWCFG2_FS_PHY_TYPE_OFFSET 8
|
||||
#define DWC2_HWCFG2_NUM_DEV_EP_MASK (0xF << 10)
|
||||
#define DWC2_HWCFG2_NUM_DEV_EP_OFFSET 10
|
||||
#define DWC2_HWCFG2_NUM_HOST_CHAN_MASK (0xF << 14)
|
||||
#define DWC2_HWCFG2_NUM_HOST_CHAN_OFFSET 14
|
||||
#define DWC2_HWCFG2_PERIO_EP_SUPPORTED (1 << 18)
|
||||
#define DWC2_HWCFG2_PERIO_EP_SUPPORTED_OFFSET 18
|
||||
#define DWC2_HWCFG2_DYNAMIC_FIFO (1 << 19)
|
||||
#define DWC2_HWCFG2_DYNAMIC_FIFO_OFFSET 19
|
||||
#define DWC2_HWCFG2_MULTI_PROC_INT (1 << 20)
|
||||
#define DWC2_HWCFG2_MULTI_PROC_INT_OFFSET 20
|
||||
#define DWC2_HWCFG2_NONPERIO_TX_Q_DEPTH_MASK (0x3 << 22)
|
||||
#define DWC2_HWCFG2_NONPERIO_TX_Q_DEPTH_OFFSET 22
|
||||
#define DWC2_HWCFG2_HOST_PERIO_TX_Q_DEPTH_MASK (0x3 << 24)
|
||||
#define DWC2_HWCFG2_HOST_PERIO_TX_Q_DEPTH_OFFSET 24
|
||||
#define DWC2_HWCFG2_DEV_TOKEN_Q_DEPTH_MASK (0x1F << 26)
|
||||
#define DWC2_HWCFG2_DEV_TOKEN_Q_DEPTH_OFFSET 26
|
||||
#define DWC2_HWCFG3_XFER_SIZE_CNTR_WIDTH_MASK (0xF << 0)
|
||||
#define DWC2_HWCFG3_XFER_SIZE_CNTR_WIDTH_OFFSET 0
|
||||
#define DWC2_HWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK (0x7 << 4)
|
||||
#define DWC2_HWCFG3_PACKET_SIZE_CNTR_WIDTH_OFFSET 4
|
||||
#define DWC2_HWCFG3_OTG_FUNC (1 << 7)
|
||||
#define DWC2_HWCFG3_OTG_FUNC_OFFSET 7
|
||||
#define DWC2_HWCFG3_I2C (1 << 8)
|
||||
#define DWC2_HWCFG3_I2C_OFFSET 8
|
||||
#define DWC2_HWCFG3_VENDOR_CTRL_IF (1 << 9)
|
||||
#define DWC2_HWCFG3_VENDOR_CTRL_IF_OFFSET 9
|
||||
#define DWC2_HWCFG3_OPTIONAL_FEATURES (1 << 10)
|
||||
#define DWC2_HWCFG3_OPTIONAL_FEATURES_OFFSET 10
|
||||
#define DWC2_HWCFG3_SYNCH_RESET_TYPE (1 << 11)
|
||||
#define DWC2_HWCFG3_SYNCH_RESET_TYPE_OFFSET 11
|
||||
#define DWC2_HWCFG3_OTG_ENABLE_IC_USB (1 << 12)
|
||||
#define DWC2_HWCFG3_OTG_ENABLE_IC_USB_OFFSET 12
|
||||
#define DWC2_HWCFG3_OTG_ENABLE_HSIC (1 << 13)
|
||||
#define DWC2_HWCFG3_OTG_ENABLE_HSIC_OFFSET 13
|
||||
#define DWC2_HWCFG3_OTG_LPM_EN (1 << 15)
|
||||
#define DWC2_HWCFG3_OTG_LPM_EN_OFFSET 15
|
||||
#define DWC2_HWCFG3_DFIFO_DEPTH_MASK (0xFFFF << 16)
|
||||
#define DWC2_HWCFG3_DFIFO_DEPTH_OFFSET 16
|
||||
#define DWC2_HWCFG4_NUM_DEV_PERIO_IN_EP_MASK (0xF << 0)
|
||||
#define DWC2_HWCFG4_NUM_DEV_PERIO_IN_EP_OFFSET 0
|
||||
#define DWC2_HWCFG4_POWER_OPTIMIZ (1 << 4)
|
||||
#define DWC2_HWCFG4_POWER_OPTIMIZ_OFFSET 4
|
||||
#define DWC2_HWCFG4_MIN_AHB_FREQ_MASK (0x1FF << 5)
|
||||
#define DWC2_HWCFG4_MIN_AHB_FREQ_OFFSET 5
|
||||
#define DWC2_HWCFG4_UTMI_PHY_DATA_WIDTH_MASK (0x3 << 14)
|
||||
#define DWC2_HWCFG4_UTMI_PHY_DATA_WIDTH_OFFSET 14
|
||||
#define DWC2_HWCFG4_NUM_DEV_MODE_CTRL_EP_MASK (0xF << 16)
|
||||
#define DWC2_HWCFG4_NUM_DEV_MODE_CTRL_EP_OFFSET 16
|
||||
#define DWC2_HWCFG4_IDDIG_FILT_EN (1 << 20)
|
||||
#define DWC2_HWCFG4_IDDIG_FILT_EN_OFFSET 20
|
||||
#define DWC2_HWCFG4_VBUS_VALID_FILT_EN (1 << 21)
|
||||
#define DWC2_HWCFG4_VBUS_VALID_FILT_EN_OFFSET 21
|
||||
#define DWC2_HWCFG4_A_VALID_FILT_EN (1 << 22)
|
||||
#define DWC2_HWCFG4_A_VALID_FILT_EN_OFFSET 22
|
||||
#define DWC2_HWCFG4_B_VALID_FILT_EN (1 << 23)
|
||||
#define DWC2_HWCFG4_B_VALID_FILT_EN_OFFSET 23
|
||||
#define DWC2_HWCFG4_SESSION_END_FILT_EN (1 << 24)
|
||||
#define DWC2_HWCFG4_SESSION_END_FILT_EN_OFFSET 24
|
||||
#define DWC2_HWCFG4_DED_FIFO_EN (1 << 25)
|
||||
#define DWC2_HWCFG4_DED_FIFO_EN_OFFSET 25
|
||||
#define DWC2_HWCFG4_NUM_IN_EPS_MASK (0xF << 26)
|
||||
#define DWC2_HWCFG4_NUM_IN_EPS_OFFSET 26
|
||||
#define DWC2_HWCFG4_DESC_DMA (1 << 30)
|
||||
#define DWC2_HWCFG4_DESC_DMA_OFFSET 30
|
||||
#define DWC2_HWCFG4_DESC_DMA_DYN (1 << 31)
|
||||
#define DWC2_HWCFG4_DESC_DMA_DYN_OFFSET 31
|
||||
#define DWC2_HCFG_FSLSPCLKSEL_30_60_MHZ 0
|
||||
#define DWC2_HCFG_FSLSPCLKSEL_48_MHZ 1
|
||||
#define DWC2_HCFG_FSLSPCLKSEL_6_MHZ 2
|
||||
#define DWC2_HCFG_FSLSPCLKSEL_MASK (0x3 << 0)
|
||||
#define DWC2_HCFG_FSLSPCLKSEL_OFFSET 0
|
||||
#define DWC2_HCFG_FSLSSUPP (1 << 2)
|
||||
#define DWC2_HCFG_FSLSSUPP_OFFSET 2
|
||||
#define DWC2_HCFG_DESCDMA (1 << 23)
|
||||
#define DWC2_HCFG_DESCDMA_OFFSET 23
|
||||
#define DWC2_HCFG_FRLISTEN_MASK (0x3 << 24)
|
||||
#define DWC2_HCFG_FRLISTEN_OFFSET 24
|
||||
#define DWC2_HCFG_PERSCHEDENA (1 << 26)
|
||||
#define DWC2_HCFG_PERSCHEDENA_OFFSET 26
|
||||
#define DWC2_HCFG_PERSCHEDSTAT (1 << 27)
|
||||
#define DWC2_HCFG_PERSCHEDSTAT_OFFSET 27
|
||||
#define DWC2_HFIR_FRINT_MASK (0xFFFF << 0)
|
||||
#define DWC2_HFIR_FRINT_OFFSET 0
|
||||
#define DWC2_HFNUM_FRNUM_MASK (0xFFFF << 0)
|
||||
#define DWC2_HFNUM_FRNUM_OFFSET 0
|
||||
#define DWC2_HFNUM_FRREM_MASK (0xFFFF << 16)
|
||||
#define DWC2_HFNUM_FRREM_OFFSET 16
|
||||
#define DWC2_HFNUM_MAX_FRNUM 0x3FFF
|
||||
#define DWC2_HPTXSTS_PTXFSPCAVAIL_MASK (0xFFFF << 0)
|
||||
#define DWC2_HPTXSTS_PTXFSPCAVAIL_OFFSET 0
|
||||
#define DWC2_HPTXSTS_PTXQSPCAVAIL_MASK (0xFF << 16)
|
||||
#define DWC2_HPTXSTS_PTXQSPCAVAIL_OFFSET 16
|
||||
#define DWC2_HPTXSTS_PTXQTOP_TERMINATE (1 << 24)
|
||||
#define DWC2_HPTXSTS_PTXQTOP_TERMINATE_OFFSET 24
|
||||
#define DWC2_HPTXSTS_PTXQTOP_TOKEN_MASK (0x3 << 25)
|
||||
#define DWC2_HPTXSTS_PTXQTOP_TOKEN_OFFSET 25
|
||||
#define DWC2_HPTXSTS_PTXQTOP_CHNUM_MASK (0xF << 27)
|
||||
#define DWC2_HPTXSTS_PTXQTOP_CHNUM_OFFSET 27
|
||||
#define DWC2_HPTXSTS_PTXQTOP_ODD (1 << 31)
|
||||
#define DWC2_HPTXSTS_PTXQTOP_ODD_OFFSET 31
|
||||
#define DWC2_HPRT0_PRTCONNSTS (1 << 0)
|
||||
#define DWC2_HPRT0_PRTCONNSTS_OFFSET 0
|
||||
#define DWC2_HPRT0_PRTCONNDET (1 << 1)
|
||||
#define DWC2_HPRT0_PRTCONNDET_OFFSET 1
|
||||
#define DWC2_HPRT0_PRTENA (1 << 2)
|
||||
#define DWC2_HPRT0_PRTENA_OFFSET 2
|
||||
#define DWC2_HPRT0_PRTENCHNG (1 << 3)
|
||||
#define DWC2_HPRT0_PRTENCHNG_OFFSET 3
|
||||
#define DWC2_HPRT0_PRTOVRCURRACT (1 << 4)
|
||||
#define DWC2_HPRT0_PRTOVRCURRACT_OFFSET 4
|
||||
#define DWC2_HPRT0_PRTOVRCURRCHNG (1 << 5)
|
||||
#define DWC2_HPRT0_PRTOVRCURRCHNG_OFFSET 5
|
||||
#define DWC2_HPRT0_PRTRES (1 << 6)
|
||||
#define DWC2_HPRT0_PRTRES_OFFSET 6
|
||||
#define DWC2_HPRT0_PRTSUSP (1 << 7)
|
||||
#define DWC2_HPRT0_PRTSUSP_OFFSET 7
|
||||
#define DWC2_HPRT0_PRTRST (1 << 8)
|
||||
#define DWC2_HPRT0_PRTRST_OFFSET 8
|
||||
#define DWC2_HPRT0_PRTLNSTS_MASK (0x3 << 10)
|
||||
#define DWC2_HPRT0_PRTLNSTS_OFFSET 10
|
||||
#define DWC2_HPRT0_PRTPWR (1 << 12)
|
||||
#define DWC2_HPRT0_PRTPWR_OFFSET 12
|
||||
#define DWC2_HPRT0_PRTTSTCTL_MASK (0xF << 13)
|
||||
#define DWC2_HPRT0_PRTTSTCTL_OFFSET 13
|
||||
#define DWC2_HPRT0_PRTSPD_HIGH (0 << 17)
|
||||
#define DWC2_HPRT0_PRTSPD_FULL (1 << 17)
|
||||
#define DWC2_HPRT0_PRTSPD_LOW (2 << 17)
|
||||
#define DWC2_HPRT0_PRTSPD_MASK (0x3 << 17)
|
||||
#define DWC2_HPRT0_PRTSPD_OFFSET 17
|
||||
#define DWC2_HAINT_CH0 (1 << 0)
|
||||
#define DWC2_HAINT_CH0_OFFSET 0
|
||||
#define DWC2_HAINT_CH1 (1 << 1)
|
||||
#define DWC2_HAINT_CH1_OFFSET 1
|
||||
#define DWC2_HAINT_CH2 (1 << 2)
|
||||
#define DWC2_HAINT_CH2_OFFSET 2
|
||||
#define DWC2_HAINT_CH3 (1 << 3)
|
||||
#define DWC2_HAINT_CH3_OFFSET 3
|
||||
#define DWC2_HAINT_CH4 (1 << 4)
|
||||
#define DWC2_HAINT_CH4_OFFSET 4
|
||||
#define DWC2_HAINT_CH5 (1 << 5)
|
||||
#define DWC2_HAINT_CH5_OFFSET 5
|
||||
#define DWC2_HAINT_CH6 (1 << 6)
|
||||
#define DWC2_HAINT_CH6_OFFSET 6
|
||||
#define DWC2_HAINT_CH7 (1 << 7)
|
||||
#define DWC2_HAINT_CH7_OFFSET 7
|
||||
#define DWC2_HAINT_CH8 (1 << 8)
|
||||
#define DWC2_HAINT_CH8_OFFSET 8
|
||||
#define DWC2_HAINT_CH9 (1 << 9)
|
||||
#define DWC2_HAINT_CH9_OFFSET 9
|
||||
#define DWC2_HAINT_CH10 (1 << 10)
|
||||
#define DWC2_HAINT_CH10_OFFSET 10
|
||||
#define DWC2_HAINT_CH11 (1 << 11)
|
||||
#define DWC2_HAINT_CH11_OFFSET 11
|
||||
#define DWC2_HAINT_CH12 (1 << 12)
|
||||
#define DWC2_HAINT_CH12_OFFSET 12
|
||||
#define DWC2_HAINT_CH13 (1 << 13)
|
||||
#define DWC2_HAINT_CH13_OFFSET 13
|
||||
#define DWC2_HAINT_CH14 (1 << 14)
|
||||
#define DWC2_HAINT_CH14_OFFSET 14
|
||||
#define DWC2_HAINT_CH15 (1 << 15)
|
||||
#define DWC2_HAINT_CH15_OFFSET 15
|
||||
#define DWC2_HAINT_CHINT_MASK 0xffff
|
||||
#define DWC2_HAINT_CHINT_OFFSET 0
|
||||
#define DWC2_HAINTMSK_CH0 (1 << 0)
|
||||
#define DWC2_HAINTMSK_CH0_OFFSET 0
|
||||
#define DWC2_HAINTMSK_CH1 (1 << 1)
|
||||
#define DWC2_HAINTMSK_CH1_OFFSET 1
|
||||
#define DWC2_HAINTMSK_CH2 (1 << 2)
|
||||
#define DWC2_HAINTMSK_CH2_OFFSET 2
|
||||
#define DWC2_HAINTMSK_CH3 (1 << 3)
|
||||
#define DWC2_HAINTMSK_CH3_OFFSET 3
|
||||
#define DWC2_HAINTMSK_CH4 (1 << 4)
|
||||
#define DWC2_HAINTMSK_CH4_OFFSET 4
|
||||
#define DWC2_HAINTMSK_CH5 (1 << 5)
|
||||
#define DWC2_HAINTMSK_CH5_OFFSET 5
|
||||
#define DWC2_HAINTMSK_CH6 (1 << 6)
|
||||
#define DWC2_HAINTMSK_CH6_OFFSET 6
|
||||
#define DWC2_HAINTMSK_CH7 (1 << 7)
|
||||
#define DWC2_HAINTMSK_CH7_OFFSET 7
|
||||
#define DWC2_HAINTMSK_CH8 (1 << 8)
|
||||
#define DWC2_HAINTMSK_CH8_OFFSET 8
|
||||
#define DWC2_HAINTMSK_CH9 (1 << 9)
|
||||
#define DWC2_HAINTMSK_CH9_OFFSET 9
|
||||
#define DWC2_HAINTMSK_CH10 (1 << 10)
|
||||
#define DWC2_HAINTMSK_CH10_OFFSET 10
|
||||
#define DWC2_HAINTMSK_CH11 (1 << 11)
|
||||
#define DWC2_HAINTMSK_CH11_OFFSET 11
|
||||
#define DWC2_HAINTMSK_CH12 (1 << 12)
|
||||
#define DWC2_HAINTMSK_CH12_OFFSET 12
|
||||
#define DWC2_HAINTMSK_CH13 (1 << 13)
|
||||
#define DWC2_HAINTMSK_CH13_OFFSET 13
|
||||
#define DWC2_HAINTMSK_CH14 (1 << 14)
|
||||
#define DWC2_HAINTMSK_CH14_OFFSET 14
|
||||
#define DWC2_HAINTMSK_CH15 (1 << 15)
|
||||
#define DWC2_HAINTMSK_CH15_OFFSET 15
|
||||
#define DWC2_HAINTMSK_CHINT_MASK 0xffff
|
||||
#define DWC2_HAINTMSK_CHINT_OFFSET 0
|
||||
#define DWC2_HCCHAR_MPS_MASK (0x7FF << 0)
|
||||
#define DWC2_HCCHAR_MPS_OFFSET 0
|
||||
#define DWC2_HCCHAR_EPNUM_MASK (0xF << 11)
|
||||
#define DWC2_HCCHAR_EPNUM_OFFSET 11
|
||||
#define DWC2_HCCHAR_EPDIR (1 << 15)
|
||||
#define DWC2_HCCHAR_EPDIR_OFFSET 15
|
||||
#define DWC2_HCCHAR_LSPDDEV (1 << 17)
|
||||
#define DWC2_HCCHAR_LSPDDEV_OFFSET 17
|
||||
#define DWC2_HCCHAR_EPTYPE_CONTROL 0
|
||||
#define DWC2_HCCHAR_EPTYPE_ISOC 1
|
||||
#define DWC2_HCCHAR_EPTYPE_BULK 2
|
||||
#define DWC2_HCCHAR_EPTYPE_INTR 3
|
||||
#define DWC2_HCCHAR_EPTYPE_MASK (0x3 << 18)
|
||||
#define DWC2_HCCHAR_EPTYPE_OFFSET 18
|
||||
#define DWC2_HCCHAR_MULTICNT_MASK (0x3 << 20)
|
||||
#define DWC2_HCCHAR_MULTICNT_OFFSET 20
|
||||
#define DWC2_HCCHAR_DEVADDR_MASK (0x7F << 22)
|
||||
#define DWC2_HCCHAR_DEVADDR_OFFSET 22
|
||||
#define DWC2_HCCHAR_ODDFRM (1 << 29)
|
||||
#define DWC2_HCCHAR_ODDFRM_OFFSET 29
|
||||
#define DWC2_HCCHAR_CHDIS (1 << 30)
|
||||
#define DWC2_HCCHAR_CHDIS_OFFSET 30
|
||||
#define DWC2_HCCHAR_CHEN (1 << 31)
|
||||
#define DWC2_HCCHAR_CHEN_OFFSET 31
|
||||
#define DWC2_HCSPLT_PRTADDR_MASK (0x7F << 0)
|
||||
#define DWC2_HCSPLT_PRTADDR_OFFSET 0
|
||||
#define DWC2_HCSPLT_HUBADDR_MASK (0x7F << 7)
|
||||
#define DWC2_HCSPLT_HUBADDR_OFFSET 7
|
||||
#define DWC2_HCSPLT_XACTPOS_MASK (0x3 << 14)
|
||||
#define DWC2_HCSPLT_XACTPOS_OFFSET 14
|
||||
#define DWC2_HCSPLT_COMPSPLT (1 << 16)
|
||||
#define DWC2_HCSPLT_COMPSPLT_OFFSET 16
|
||||
#define DWC2_HCSPLT_SPLTENA (1 << 31)
|
||||
#define DWC2_HCSPLT_SPLTENA_OFFSET 31
|
||||
#define DWC2_HCINT_XFERCOMP (1 << 0)
|
||||
#define DWC2_HCINT_XFERCOMP_OFFSET 0
|
||||
#define DWC2_HCINT_CHHLTD (1 << 1)
|
||||
#define DWC2_HCINT_CHHLTD_OFFSET 1
|
||||
#define DWC2_HCINT_AHBERR (1 << 2)
|
||||
#define DWC2_HCINT_AHBERR_OFFSET 2
|
||||
#define DWC2_HCINT_STALL (1 << 3)
|
||||
#define DWC2_HCINT_STALL_OFFSET 3
|
||||
#define DWC2_HCINT_NAK (1 << 4)
|
||||
#define DWC2_HCINT_NAK_OFFSET 4
|
||||
#define DWC2_HCINT_ACK (1 << 5)
|
||||
#define DWC2_HCINT_ACK_OFFSET 5
|
||||
#define DWC2_HCINT_NYET (1 << 6)
|
||||
#define DWC2_HCINT_NYET_OFFSET 6
|
||||
#define DWC2_HCINT_XACTERR (1 << 7)
|
||||
#define DWC2_HCINT_XACTERR_OFFSET 7
|
||||
#define DWC2_HCINT_BBLERR (1 << 8)
|
||||
#define DWC2_HCINT_BBLERR_OFFSET 8
|
||||
#define DWC2_HCINT_FRMOVRUN (1 << 9)
|
||||
#define DWC2_HCINT_FRMOVRUN_OFFSET 9
|
||||
#define DWC2_HCINT_DATATGLERR (1 << 10)
|
||||
#define DWC2_HCINT_DATATGLERR_OFFSET 10
|
||||
#define DWC2_HCINT_BNA (1 << 11)
|
||||
#define DWC2_HCINT_BNA_OFFSET 11
|
||||
#define DWC2_HCINT_XCS_XACT (1 << 12)
|
||||
#define DWC2_HCINT_XCS_XACT_OFFSET 12
|
||||
#define DWC2_HCINT_FRM_LIST_ROLL (1 << 13)
|
||||
#define DWC2_HCINT_FRM_LIST_ROLL_OFFSET 13
|
||||
#define DWC2_HCINTMSK_XFERCOMPL (1 << 0)
|
||||
#define DWC2_HCINTMSK_XFERCOMPL_OFFSET 0
|
||||
#define DWC2_HCINTMSK_CHHLTD (1 << 1)
|
||||
#define DWC2_HCINTMSK_CHHLTD_OFFSET 1
|
||||
#define DWC2_HCINTMSK_AHBERR (1 << 2)
|
||||
#define DWC2_HCINTMSK_AHBERR_OFFSET 2
|
||||
#define DWC2_HCINTMSK_STALL (1 << 3)
|
||||
#define DWC2_HCINTMSK_STALL_OFFSET 3
|
||||
#define DWC2_HCINTMSK_NAK (1 << 4)
|
||||
#define DWC2_HCINTMSK_NAK_OFFSET 4
|
||||
#define DWC2_HCINTMSK_ACK (1 << 5)
|
||||
#define DWC2_HCINTMSK_ACK_OFFSET 5
|
||||
#define DWC2_HCINTMSK_NYET (1 << 6)
|
||||
#define DWC2_HCINTMSK_NYET_OFFSET 6
|
||||
#define DWC2_HCINTMSK_XACTERR (1 << 7)
|
||||
#define DWC2_HCINTMSK_XACTERR_OFFSET 7
|
||||
#define DWC2_HCINTMSK_BBLERR (1 << 8)
|
||||
#define DWC2_HCINTMSK_BBLERR_OFFSET 8
|
||||
#define DWC2_HCINTMSK_FRMOVRUN (1 << 9)
|
||||
#define DWC2_HCINTMSK_FRMOVRUN_OFFSET 9
|
||||
#define DWC2_HCINTMSK_DATATGLERR (1 << 10)
|
||||
#define DWC2_HCINTMSK_DATATGLERR_OFFSET 10
|
||||
#define DWC2_HCINTMSK_BNA (1 << 11)
|
||||
#define DWC2_HCINTMSK_BNA_OFFSET 11
|
||||
#define DWC2_HCINTMSK_XCS_XACT (1 << 12)
|
||||
#define DWC2_HCINTMSK_XCS_XACT_OFFSET 12
|
||||
#define DWC2_HCINTMSK_FRM_LIST_ROLL (1 << 13)
|
||||
#define DWC2_HCINTMSK_FRM_LIST_ROLL_OFFSET 13
|
||||
#define DWC2_HCTSIZ_XFERSIZE_MASK 0x7ffff
|
||||
#define DWC2_HCTSIZ_XFERSIZE_OFFSET 0
|
||||
#define DWC2_HCTSIZ_SCHINFO_MASK 0xff
|
||||
#define DWC2_HCTSIZ_SCHINFO_OFFSET 0
|
||||
#define DWC2_HCTSIZ_NTD_MASK (0xff << 8)
|
||||
#define DWC2_HCTSIZ_NTD_OFFSET 8
|
||||
#define DWC2_HCTSIZ_PKTCNT_MASK (0x3ff << 19)
|
||||
#define DWC2_HCTSIZ_PKTCNT_OFFSET 19
|
||||
#define DWC2_HCTSIZ_PID_MASK (0x3 << 29)
|
||||
#define DWC2_HCTSIZ_PID_OFFSET 29
|
||||
#define DWC2_HCTSIZ_DOPNG (1 << 31)
|
||||
#define DWC2_HCTSIZ_DOPNG_OFFSET 31
|
||||
#define DWC2_HCDMA_CTD_MASK (0xFF << 3)
|
||||
#define DWC2_HCDMA_CTD_OFFSET 3
|
||||
#define DWC2_HCDMA_DMA_ADDR_MASK (0x1FFFFF << 11)
|
||||
#define DWC2_HCDMA_DMA_ADDR_OFFSET 11
|
||||
#define DWC2_PCGCCTL_STOPPCLK (1 << 0)
|
||||
#define DWC2_PCGCCTL_STOPPCLK_OFFSET 0
|
||||
#define DWC2_PCGCCTL_GATEHCLK (1 << 1)
|
||||
#define DWC2_PCGCCTL_GATEHCLK_OFFSET 1
|
||||
#define DWC2_PCGCCTL_PWRCLMP (1 << 2)
|
||||
#define DWC2_PCGCCTL_PWRCLMP_OFFSET 2
|
||||
#define DWC2_PCGCCTL_RSTPDWNMODULE (1 << 3)
|
||||
#define DWC2_PCGCCTL_RSTPDWNMODULE_OFFSET 3
|
||||
#define DWC2_PCGCCTL_PHYSUSPENDED (1 << 4)
|
||||
#define DWC2_PCGCCTL_PHYSUSPENDED_OFFSET 4
|
||||
#define DWC2_PCGCCTL_ENBL_SLEEP_GATING (1 << 5)
|
||||
#define DWC2_PCGCCTL_ENBL_SLEEP_GATING_OFFSET 5
|
||||
#define DWC2_PCGCCTL_PHY_IN_SLEEP (1 << 6)
|
||||
#define DWC2_PCGCCTL_PHY_IN_SLEEP_OFFSET 6
|
||||
#define DWC2_PCGCCTL_DEEP_SLEEP (1 << 7)
|
||||
#define DWC2_PCGCCTL_DEEP_SLEEP_OFFSET 7
|
||||
#define DWC2_SNPSID_DEVID_VER_2xx (0x4f542 << 12)
|
||||
#define DWC2_SNPSID_DEVID_VER_3xx (0x4f543 << 12)
|
||||
#define DWC2_SNPSID_DEVID_MASK (0xfffff << 12)
|
||||
#define DWC2_SNPSID_DEVID_OFFSET 12
|
||||
|
||||
/* Host controller specific */
|
||||
#define DWC2_HC_PID_DATA0 0
|
||||
#define DWC2_HC_PID_DATA2 1
|
||||
#define DWC2_HC_PID_DATA1 2
|
||||
#define DWC2_HC_PID_MDATA 3
|
||||
#define DWC2_HC_PID_SETUP 3
|
||||
|
||||
/* roothub.a masks */
|
||||
#define RH_A_NDP (0xff << 0) /* number of downstream ports */
|
||||
#define RH_A_PSM (1 << 8) /* power switching mode */
|
||||
#define RH_A_NPS (1 << 9) /* no power switching */
|
||||
#define RH_A_DT (1 << 10) /* device type (mbz) */
|
||||
#define RH_A_OCPM (1 << 11) /* over current protection mode */
|
||||
#define RH_A_NOCP (1 << 12) /* no over current protection */
|
||||
#define RH_A_POTPGT (0xff << 24) /* power on to power good time */
|
||||
|
||||
/* roothub.b masks */
|
||||
#define RH_B_DR 0x0000ffff /* device removable flags */
|
||||
#define RH_B_PPCM 0xffff0000 /* port power control mask */
|
||||
|
||||
/* Default driver configuration */
|
||||
#define CONFIG_DWC2_DMA_ENABLE
|
||||
#define CONFIG_DWC2_DMA_BURST_SIZE 32 /* DMA burst len */
|
||||
#undef CONFIG_DWC2_DFLT_SPEED_FULL /* Do not force DWC2 to FS */
|
||||
#define CONFIG_DWC2_ENABLE_DYNAMIC_FIFO /* Runtime FIFO size detect */
|
||||
#define CONFIG_DWC2_MAX_CHANNELS 16 /* Max # of EPs */
|
||||
#define CONFIG_DWC2_HOST_RX_FIFO_SIZE (516 + CONFIG_DWC2_MAX_CHANNELS)
|
||||
#define CONFIG_DWC2_HOST_NPERIO_TX_FIFO_SIZE 0x100 /* nPeriodic TX FIFO */
|
||||
#define CONFIG_DWC2_HOST_PERIO_TX_FIFO_SIZE 0x200 /* Periodic TX FIFO */
|
||||
#define CONFIG_DWC2_MAX_TRANSFER_SIZE 65535
|
||||
#define CONFIG_DWC2_MAX_PACKET_COUNT 511
|
||||
|
||||
#define DWC2_PHY_TYPE_FS 0
|
||||
#define DWC2_PHY_TYPE_UTMI 1
|
||||
#define DWC2_PHY_TYPE_ULPI 2
|
||||
#define CONFIG_DWC2_PHY_TYPE DWC2_PHY_TYPE_UTMI /* PHY type */
|
||||
#define CONFIG_DWC2_UTMI_WIDTH 8 /* UTMI bus width (8/16) */
|
||||
|
||||
#undef CONFIG_DWC2_PHY_ULPI_DDR /* ULPI PHY uses DDR mode */
|
||||
#define CONFIG_DWC2_PHY_ULPI_EXT_VBUS /* ULPI PHY controls VBUS */
|
||||
#undef CONFIG_DWC2_I2C_ENABLE /* Enable I2C */
|
||||
#undef CONFIG_DWC2_ULPI_FS_LS /* ULPI is FS/LS */
|
||||
#undef CONFIG_DWC2_TS_DLINE /* External DLine pulsing */
|
||||
#undef CONFIG_DWC2_THR_CTL /* Threshold control */
|
||||
#define CONFIG_DWC2_TX_THR_LENGTH 64
|
||||
#undef CONFIG_DWC2_IC_USB_CAP /* IC Cap */
|
||||
|
||||
#endif /* __DWC2_H__ */
|
||||
48
u-boot/drivers/usb/host/ehci-armada100.c
Normal file
48
u-boot/drivers/usb/host/ehci-armada100.c
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* (C) Copyright 2012
|
||||
* eInfochips Ltd. <www.einfochips.com>
|
||||
* Written-by: Ajay Bhargav <ajay.bhargav@einfochips.com>
|
||||
*
|
||||
* This driver is based on Kirkwood echi driver
|
||||
* (C) Copyright 2009
|
||||
* Marvell Semiconductor <www.marvell.com>
|
||||
* Written-by: Prafulla Wadaskar <prafulla@marvell.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <usb.h>
|
||||
#include "ehci.h"
|
||||
#include <asm/arch/cpu.h>
|
||||
#include <asm/arch/armada100.h>
|
||||
#include <asm/arch/utmi-armada100.h>
|
||||
|
||||
/*
|
||||
* EHCI host controller init
|
||||
*/
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
if (utmi_init() < 0)
|
||||
return -1;
|
||||
|
||||
*hccr = (struct ehci_hccr *)(ARMD1_USB_HOST_BASE + 0x100);
|
||||
*hcor = (struct ehci_hcor *)((uint32_t) *hccr
|
||||
+ HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
|
||||
|
||||
debug("armada100-ehci: init hccr %x and hcor %x hc_length %d\n",
|
||||
(uint32_t)*hccr, (uint32_t)*hcor,
|
||||
(uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* EHCI host controller stop
|
||||
*/
|
||||
int ehci_hcd_stop(int index)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
43
u-boot/drivers/usb/host/ehci-atmel.c
Normal file
43
u-boot/drivers/usb/host/ehci-atmel.c
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* (C) Copyright 2012
|
||||
* Atmel Semiconductor <www.atmel.com>
|
||||
* Written-by: Bo Shen <voice.shen@atmel.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <usb.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/clk.h>
|
||||
|
||||
#include "ehci.h"
|
||||
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
/* Enable UTMI PLL */
|
||||
if (at91_upll_clk_enable())
|
||||
return -1;
|
||||
|
||||
/* Enable USB Host clock */
|
||||
at91_periph_clk_enable(ATMEL_ID_UHPHS);
|
||||
|
||||
*hccr = (struct ehci_hccr *)ATMEL_BASE_EHCI;
|
||||
*hcor = (struct ehci_hcor *)((uint32_t)*hccr +
|
||||
HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ehci_hcd_stop(int index)
|
||||
{
|
||||
/* Disable USB Host Clock */
|
||||
at91_periph_clk_disable(ATMEL_ID_UHPHS);
|
||||
|
||||
/* Disable UTMI PLL */
|
||||
if (at91_upll_clk_disable())
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
263
u-boot/drivers/usb/host/ehci-exynos.c
Normal file
263
u-boot/drivers/usb/host/ehci-exynos.c
Normal file
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* SAMSUNG EXYNOS USB HOST EHCI Controller
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics Co.Ltd
|
||||
* Vivek Gautam <gautam.vivek@samsung.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <fdtdec.h>
|
||||
#include <libfdt.h>
|
||||
#include <malloc.h>
|
||||
#include <usb.h>
|
||||
#include <asm/arch/cpu.h>
|
||||
#include <asm/arch/ehci.h>
|
||||
#include <asm/arch/system.h>
|
||||
#include <asm/arch/power.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <asm-generic/errno.h>
|
||||
#include <linux/compat.h>
|
||||
#include "ehci.h"
|
||||
|
||||
/* Declare global data pointer */
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
struct exynos_ehci_platdata {
|
||||
struct usb_platdata usb_plat;
|
||||
fdt_addr_t hcd_base;
|
||||
fdt_addr_t phy_base;
|
||||
struct gpio_desc vbus_gpio;
|
||||
};
|
||||
|
||||
/**
|
||||
* Contains pointers to register base addresses
|
||||
* for the usb controller.
|
||||
*/
|
||||
struct exynos_ehci {
|
||||
struct ehci_ctrl ctrl;
|
||||
struct exynos_usb_phy *usb;
|
||||
struct ehci_hccr *hcd;
|
||||
};
|
||||
|
||||
static int ehci_usb_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct exynos_ehci_platdata *plat = dev_get_platdata(dev);
|
||||
const void *blob = gd->fdt_blob;
|
||||
unsigned int node;
|
||||
int depth;
|
||||
|
||||
/*
|
||||
* Get the base address for XHCI controller from the device node
|
||||
*/
|
||||
plat->hcd_base = dev_get_addr(dev);
|
||||
if (plat->hcd_base == FDT_ADDR_T_NONE) {
|
||||
debug("Can't get the XHCI register base address\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
depth = 0;
|
||||
node = fdtdec_next_compatible_subnode(blob, dev->of_offset,
|
||||
COMPAT_SAMSUNG_EXYNOS_USB_PHY, &depth);
|
||||
if (node <= 0) {
|
||||
debug("XHCI: Can't get device node for usb3-phy controller\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the base address for usbphy from the device node
|
||||
*/
|
||||
plat->phy_base = fdtdec_get_addr(blob, node, "reg");
|
||||
if (plat->phy_base == FDT_ADDR_T_NONE) {
|
||||
debug("Can't get the usbphy register address\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* Vbus gpio */
|
||||
gpio_request_by_name(dev, "samsung,vbus-gpio", 0,
|
||||
&plat->vbus_gpio, GPIOD_IS_OUT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exynos5_setup_usb_phy(struct exynos_usb_phy *usb)
|
||||
{
|
||||
u32 hsic_ctrl;
|
||||
|
||||
clrbits_le32(&usb->usbphyctrl0,
|
||||
HOST_CTRL0_FSEL_MASK |
|
||||
HOST_CTRL0_COMMONON_N |
|
||||
/* HOST Phy setting */
|
||||
HOST_CTRL0_PHYSWRST |
|
||||
HOST_CTRL0_PHYSWRSTALL |
|
||||
HOST_CTRL0_SIDDQ |
|
||||
HOST_CTRL0_FORCESUSPEND |
|
||||
HOST_CTRL0_FORCESLEEP);
|
||||
|
||||
setbits_le32(&usb->usbphyctrl0,
|
||||
/* Setting up the ref freq */
|
||||
(CLK_24MHZ << 16) |
|
||||
/* HOST Phy setting */
|
||||
HOST_CTRL0_LINKSWRST |
|
||||
HOST_CTRL0_UTMISWRST);
|
||||
udelay(10);
|
||||
clrbits_le32(&usb->usbphyctrl0,
|
||||
HOST_CTRL0_LINKSWRST |
|
||||
HOST_CTRL0_UTMISWRST);
|
||||
|
||||
/* HSIC Phy Setting */
|
||||
hsic_ctrl = (HSIC_CTRL_FORCESUSPEND |
|
||||
HSIC_CTRL_FORCESLEEP |
|
||||
HSIC_CTRL_SIDDQ);
|
||||
|
||||
clrbits_le32(&usb->hsicphyctrl1, hsic_ctrl);
|
||||
clrbits_le32(&usb->hsicphyctrl2, hsic_ctrl);
|
||||
|
||||
hsic_ctrl = (((HSIC_CTRL_REFCLKDIV_12 & HSIC_CTRL_REFCLKDIV_MASK)
|
||||
<< HSIC_CTRL_REFCLKDIV_SHIFT)
|
||||
| ((HSIC_CTRL_REFCLKSEL & HSIC_CTRL_REFCLKSEL_MASK)
|
||||
<< HSIC_CTRL_REFCLKSEL_SHIFT)
|
||||
| HSIC_CTRL_UTMISWRST);
|
||||
|
||||
setbits_le32(&usb->hsicphyctrl1, hsic_ctrl);
|
||||
setbits_le32(&usb->hsicphyctrl2, hsic_ctrl);
|
||||
|
||||
udelay(10);
|
||||
|
||||
clrbits_le32(&usb->hsicphyctrl1, HSIC_CTRL_PHYSWRST |
|
||||
HSIC_CTRL_UTMISWRST);
|
||||
|
||||
clrbits_le32(&usb->hsicphyctrl2, HSIC_CTRL_PHYSWRST |
|
||||
HSIC_CTRL_UTMISWRST);
|
||||
|
||||
udelay(20);
|
||||
|
||||
/* EHCI Ctrl setting */
|
||||
setbits_le32(&usb->ehcictrl,
|
||||
EHCICTRL_ENAINCRXALIGN |
|
||||
EHCICTRL_ENAINCR4 |
|
||||
EHCICTRL_ENAINCR8 |
|
||||
EHCICTRL_ENAINCR16);
|
||||
}
|
||||
|
||||
static void exynos4412_setup_usb_phy(struct exynos4412_usb_phy *usb)
|
||||
{
|
||||
writel(CLK_24MHZ, &usb->usbphyclk);
|
||||
|
||||
clrbits_le32(&usb->usbphyctrl, (PHYPWR_NORMAL_MASK_HSIC0 |
|
||||
PHYPWR_NORMAL_MASK_HSIC1 | PHYPWR_NORMAL_MASK_PHY1 |
|
||||
PHYPWR_NORMAL_MASK_PHY0));
|
||||
|
||||
setbits_le32(&usb->usbphyrstcon, (RSTCON_HOSTPHY_SWRST | RSTCON_SWRST));
|
||||
udelay(10);
|
||||
clrbits_le32(&usb->usbphyrstcon, (RSTCON_HOSTPHY_SWRST | RSTCON_SWRST));
|
||||
}
|
||||
|
||||
static void setup_usb_phy(struct exynos_usb_phy *usb)
|
||||
{
|
||||
set_usbhost_mode(USB20_PHY_CFG_HOST_LINK_EN);
|
||||
|
||||
set_usbhost_phy_ctrl(POWER_USB_HOST_PHY_CTRL_EN);
|
||||
|
||||
if (cpu_is_exynos5())
|
||||
exynos5_setup_usb_phy(usb);
|
||||
else if (cpu_is_exynos4())
|
||||
if (proid_is_exynos4412())
|
||||
exynos4412_setup_usb_phy((struct exynos4412_usb_phy *)
|
||||
usb);
|
||||
}
|
||||
|
||||
static void exynos5_reset_usb_phy(struct exynos_usb_phy *usb)
|
||||
{
|
||||
u32 hsic_ctrl;
|
||||
|
||||
/* HOST_PHY reset */
|
||||
setbits_le32(&usb->usbphyctrl0,
|
||||
HOST_CTRL0_PHYSWRST |
|
||||
HOST_CTRL0_PHYSWRSTALL |
|
||||
HOST_CTRL0_SIDDQ |
|
||||
HOST_CTRL0_FORCESUSPEND |
|
||||
HOST_CTRL0_FORCESLEEP);
|
||||
|
||||
/* HSIC Phy reset */
|
||||
hsic_ctrl = (HSIC_CTRL_FORCESUSPEND |
|
||||
HSIC_CTRL_FORCESLEEP |
|
||||
HSIC_CTRL_SIDDQ |
|
||||
HSIC_CTRL_PHYSWRST);
|
||||
|
||||
setbits_le32(&usb->hsicphyctrl1, hsic_ctrl);
|
||||
setbits_le32(&usb->hsicphyctrl2, hsic_ctrl);
|
||||
}
|
||||
|
||||
static void exynos4412_reset_usb_phy(struct exynos4412_usb_phy *usb)
|
||||
{
|
||||
setbits_le32(&usb->usbphyctrl, (PHYPWR_NORMAL_MASK_HSIC0 |
|
||||
PHYPWR_NORMAL_MASK_HSIC1 | PHYPWR_NORMAL_MASK_PHY1 |
|
||||
PHYPWR_NORMAL_MASK_PHY0));
|
||||
}
|
||||
|
||||
/* Reset the EHCI host controller. */
|
||||
static void reset_usb_phy(struct exynos_usb_phy *usb)
|
||||
{
|
||||
if (cpu_is_exynos5())
|
||||
exynos5_reset_usb_phy(usb);
|
||||
else if (cpu_is_exynos4())
|
||||
if (proid_is_exynos4412())
|
||||
exynos4412_reset_usb_phy((struct exynos4412_usb_phy *)
|
||||
usb);
|
||||
|
||||
set_usbhost_phy_ctrl(POWER_USB_HOST_PHY_CTRL_DISABLE);
|
||||
}
|
||||
|
||||
static int ehci_usb_probe(struct udevice *dev)
|
||||
{
|
||||
struct exynos_ehci_platdata *plat = dev_get_platdata(dev);
|
||||
struct exynos_ehci *ctx = dev_get_priv(dev);
|
||||
struct ehci_hcor *hcor;
|
||||
|
||||
ctx->hcd = (struct ehci_hccr *)plat->hcd_base;
|
||||
ctx->usb = (struct exynos_usb_phy *)plat->phy_base;
|
||||
|
||||
/* setup the Vbus gpio here */
|
||||
if (dm_gpio_is_valid(&plat->vbus_gpio))
|
||||
dm_gpio_set_value(&plat->vbus_gpio, 1);
|
||||
|
||||
setup_usb_phy(ctx->usb);
|
||||
hcor = (struct ehci_hcor *)((uint32_t)ctx->hcd +
|
||||
HC_LENGTH(ehci_readl(&ctx->hcd->cr_capbase)));
|
||||
|
||||
return ehci_register(dev, ctx->hcd, hcor, NULL, 0, USB_INIT_HOST);
|
||||
}
|
||||
|
||||
static int ehci_usb_remove(struct udevice *dev)
|
||||
{
|
||||
struct exynos_ehci *ctx = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = ehci_deregister(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
reset_usb_phy(ctx->usb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id ehci_usb_ids[] = {
|
||||
{ .compatible = "samsung,exynos-ehci" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(usb_ehci) = {
|
||||
.name = "ehci_exynos",
|
||||
.id = UCLASS_USB,
|
||||
.of_match = ehci_usb_ids,
|
||||
.ofdata_to_platdata = ehci_usb_ofdata_to_platdata,
|
||||
.probe = ehci_usb_probe,
|
||||
.remove = ehci_usb_remove,
|
||||
.ops = &ehci_usb_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct exynos_ehci),
|
||||
.platdata_auto_alloc_size = sizeof(struct exynos_ehci_platdata),
|
||||
.flags = DM_FLAG_ALLOC_PRIV_DMA,
|
||||
};
|
||||
143
u-boot/drivers/usb/host/ehci-faraday.c
Normal file
143
u-boot/drivers/usb/host/ehci-faraday.c
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Faraday USB 2.0 EHCI Controller
|
||||
*
|
||||
* (C) Copyright 2010 Faraday Technology
|
||||
* Dante Su <dantesu@faraday-tech.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <usb.h>
|
||||
#include <usb/fusbh200.h>
|
||||
#include <usb/fotg210.h>
|
||||
|
||||
#include "ehci.h"
|
||||
|
||||
#ifndef CONFIG_USB_EHCI_BASE_LIST
|
||||
#define CONFIG_USB_EHCI_BASE_LIST { CONFIG_USB_EHCI_BASE }
|
||||
#endif
|
||||
|
||||
union ehci_faraday_regs {
|
||||
struct fusbh200_regs usb;
|
||||
struct fotg210_regs otg;
|
||||
};
|
||||
|
||||
static inline int ehci_is_fotg2xx(union ehci_faraday_regs *regs)
|
||||
{
|
||||
return !readl(®s->usb.easstr);
|
||||
}
|
||||
|
||||
void faraday_ehci_set_usbmode(struct ehci_ctrl *ctrl)
|
||||
{
|
||||
/* nothing needs to be done */
|
||||
}
|
||||
|
||||
int faraday_ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg)
|
||||
{
|
||||
int spd, ret = PORTSC_PSPD_HS;
|
||||
union ehci_faraday_regs *regs;
|
||||
|
||||
ret = (void __iomem *)((ulong)ctrl->hcor - 0x10);
|
||||
if (ehci_is_fotg2xx(regs))
|
||||
spd = OTGCSR_SPD(readl(®s->otg.otgcsr));
|
||||
else
|
||||
spd = BMCSR_SPD(readl(®s->usb.bmcsr));
|
||||
|
||||
switch (spd) {
|
||||
case 0: /* full speed */
|
||||
ret = PORTSC_PSPD_FS;
|
||||
break;
|
||||
case 1: /* low speed */
|
||||
ret = PORTSC_PSPD_LS;
|
||||
break;
|
||||
case 2: /* high speed */
|
||||
ret = PORTSC_PSPD_HS;
|
||||
break;
|
||||
default:
|
||||
printf("ehci-faraday: invalid device speed\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t *faraday_ehci_get_portsc_register(struct ehci_ctrl *ctrl, int port)
|
||||
{
|
||||
/* Faraday EHCI has one and only one portsc register */
|
||||
if (port) {
|
||||
/* Printing the message would cause a scan failure! */
|
||||
debug("The request port(%d) is not configured\n", port);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Faraday EHCI PORTSC register offset is 0x20 from hcor */
|
||||
return (uint32_t *)((uint8_t *)ctrl->hcor + 0x20);
|
||||
}
|
||||
|
||||
static const struct ehci_ops faraday_ehci_ops = {
|
||||
.set_usb_mode = faraday_ehci_set_usbmode,
|
||||
.get_port_speed = faraday_ehci_get_port_speed,
|
||||
.get_portsc_register = faraday_ehci_get_portsc_register,
|
||||
};
|
||||
|
||||
/*
|
||||
* Create the appropriate control structures to manage
|
||||
* a new EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **ret_hccr, struct ehci_hcor **ret_hcor)
|
||||
{
|
||||
struct ehci_hccr *hccr;
|
||||
struct ehci_hcor *hcor;
|
||||
union ehci_faraday_regs *regs;
|
||||
uint32_t base_list[] = CONFIG_USB_EHCI_BASE_LIST;
|
||||
|
||||
if (index < 0 || index >= ARRAY_SIZE(base_list))
|
||||
return -1;
|
||||
ehci_set_controller_priv(index, NULL, &faraday_ehci_ops);
|
||||
regs = (void __iomem *)base_list[index];
|
||||
hccr = (struct ehci_hccr *)®s->usb.hccr;
|
||||
hcor = (struct ehci_hcor *)®s->usb.hcor;
|
||||
|
||||
if (ehci_is_fotg2xx(regs)) {
|
||||
/* A-device bus reset */
|
||||
/* ... Power off A-device */
|
||||
setbits_le32(®s->otg.otgcsr, OTGCSR_A_BUSDROP);
|
||||
/* ... Drop vbus and bus traffic */
|
||||
clrbits_le32(®s->otg.otgcsr, OTGCSR_A_BUSREQ);
|
||||
mdelay(1);
|
||||
/* ... Power on A-device */
|
||||
clrbits_le32(®s->otg.otgcsr, OTGCSR_A_BUSDROP);
|
||||
/* ... Drive vbus and bus traffic */
|
||||
setbits_le32(®s->otg.otgcsr, OTGCSR_A_BUSREQ);
|
||||
mdelay(1);
|
||||
/* Disable OTG & DEV interrupts, triggered at level-high */
|
||||
writel(IMR_IRQLH | IMR_OTG | IMR_DEV, ®s->otg.imr);
|
||||
/* Clear all interrupt status */
|
||||
writel(ISR_HOST | ISR_OTG | ISR_DEV, ®s->otg.isr);
|
||||
} else {
|
||||
/* Interrupt=level-high */
|
||||
setbits_le32(®s->usb.bmcsr, BMCSR_IRQLH);
|
||||
/* VBUS on */
|
||||
clrbits_le32(®s->usb.bmcsr, BMCSR_VBUS_OFF);
|
||||
/* Disable all interrupts */
|
||||
writel(0x00, ®s->usb.bmier);
|
||||
writel(0x1f, ®s->usb.bmisr);
|
||||
}
|
||||
|
||||
*ret_hccr = hccr;
|
||||
*ret_hcor = hcor;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy the appropriate control structures corresponding
|
||||
* the the EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_stop(int index)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
176
u-boot/drivers/usb/host/ehci-fsl.c
Normal file
176
u-boot/drivers/usb/host/ehci-fsl.c
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* (C) Copyright 2009, 2011 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* (C) Copyright 2008, Excito Elektronik i Sk=E5ne AB
|
||||
*
|
||||
* Author: Tor Krill tor@excito.com
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <pci.h>
|
||||
#include <usb.h>
|
||||
#include <asm/io.h>
|
||||
#include <usb/ehci-ci.h>
|
||||
#include <hwconfig.h>
|
||||
#include <fsl_usb.h>
|
||||
#include <fdt_support.h>
|
||||
|
||||
#include "ehci.h"
|
||||
|
||||
#ifndef CONFIG_USB_MAX_CONTROLLER_COUNT
|
||||
#define CONFIG_USB_MAX_CONTROLLER_COUNT 1
|
||||
#endif
|
||||
|
||||
static void set_txfifothresh(struct usb_ehci *, u32);
|
||||
|
||||
/* Check USB PHY clock valid */
|
||||
static int usb_phy_clk_valid(struct usb_ehci *ehci)
|
||||
{
|
||||
if (!((in_be32(&ehci->control) & PHY_CLK_VALID) ||
|
||||
in_be32(&ehci->prictrl))) {
|
||||
printf("USB PHY clock invalid!\n");
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the appropriate control structures to manage
|
||||
* a new EHCI host controller.
|
||||
*
|
||||
* Excerpts from linux ehci fsl driver.
|
||||
*/
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
struct usb_ehci *ehci = NULL;
|
||||
const char *phy_type = NULL;
|
||||
size_t len;
|
||||
char current_usb_controller[5];
|
||||
#ifdef CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY
|
||||
char usb_phy[5];
|
||||
|
||||
usb_phy[0] = '\0';
|
||||
#endif
|
||||
if (has_erratum_a007075()) {
|
||||
/*
|
||||
* A 5ms delay is needed after applying soft-reset to the
|
||||
* controller to let external ULPI phy come out of reset.
|
||||
* This delay needs to be added before re-initializing
|
||||
* the controller after soft-resetting completes
|
||||
*/
|
||||
mdelay(5);
|
||||
}
|
||||
memset(current_usb_controller, '\0', 5);
|
||||
snprintf(current_usb_controller, sizeof(current_usb_controller),
|
||||
"usb%d", index+1);
|
||||
|
||||
switch (index) {
|
||||
case 0:
|
||||
ehci = (struct usb_ehci *)CONFIG_SYS_FSL_USB1_ADDR;
|
||||
break;
|
||||
case 1:
|
||||
ehci = (struct usb_ehci *)CONFIG_SYS_FSL_USB2_ADDR;
|
||||
break;
|
||||
default:
|
||||
printf("ERROR: wrong controller index!!\n");
|
||||
return -EINVAL;
|
||||
};
|
||||
|
||||
*hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength);
|
||||
*hcor = (struct ehci_hcor *)((uint32_t) *hccr +
|
||||
HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
|
||||
|
||||
/* Set to Host mode */
|
||||
setbits_le32(&ehci->usbmode, CM_HOST);
|
||||
|
||||
out_be32(&ehci->snoop1, SNOOP_SIZE_2GB);
|
||||
out_be32(&ehci->snoop2, 0x80000000 | SNOOP_SIZE_2GB);
|
||||
|
||||
/* Init phy */
|
||||
if (hwconfig_sub(current_usb_controller, "phy_type"))
|
||||
phy_type = hwconfig_subarg(current_usb_controller,
|
||||
"phy_type", &len);
|
||||
else
|
||||
phy_type = getenv("usb_phy_type");
|
||||
|
||||
if (!phy_type) {
|
||||
#ifdef CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY
|
||||
/* if none specified assume internal UTMI */
|
||||
strcpy(usb_phy, "utmi");
|
||||
phy_type = usb_phy;
|
||||
#else
|
||||
printf("WARNING: USB phy type not defined !!\n");
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!strncmp(phy_type, "utmi", 4)) {
|
||||
#if defined(CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY)
|
||||
clrsetbits_be32(&ehci->control, CONTROL_REGISTER_W1C_MASK,
|
||||
PHY_CLK_SEL_UTMI);
|
||||
clrsetbits_be32(&ehci->control, CONTROL_REGISTER_W1C_MASK,
|
||||
UTMI_PHY_EN);
|
||||
udelay(1000); /* delay required for PHY Clk to appear */
|
||||
#endif
|
||||
out_le32(&(*hcor)->or_portsc[0], PORT_PTS_UTMI);
|
||||
clrsetbits_be32(&ehci->control, CONTROL_REGISTER_W1C_MASK,
|
||||
USB_EN);
|
||||
} else {
|
||||
clrsetbits_be32(&ehci->control, CONTROL_REGISTER_W1C_MASK,
|
||||
PHY_CLK_SEL_ULPI);
|
||||
clrsetbits_be32(&ehci->control, UTMI_PHY_EN |
|
||||
CONTROL_REGISTER_W1C_MASK, USB_EN);
|
||||
udelay(1000); /* delay required for PHY Clk to appear */
|
||||
if (!usb_phy_clk_valid(ehci))
|
||||
return -EINVAL;
|
||||
out_le32(&(*hcor)->or_portsc[0], PORT_PTS_ULPI);
|
||||
}
|
||||
|
||||
out_be32(&ehci->prictrl, 0x0000000c);
|
||||
out_be32(&ehci->age_cnt_limit, 0x00000040);
|
||||
out_be32(&ehci->sictrl, 0x00000001);
|
||||
|
||||
in_le32(&ehci->usbmode);
|
||||
|
||||
if (has_erratum_a007798())
|
||||
set_txfifothresh(ehci, TXFIFOTHRESH);
|
||||
|
||||
if (has_erratum_a004477()) {
|
||||
/*
|
||||
* When reset is issued while any ULPI transaction is ongoing
|
||||
* then it may result to corruption of ULPI Function Control
|
||||
* Register which eventually causes phy clock to enter low
|
||||
* power mode which stops the clock. Thus delay is required
|
||||
* before reset to let ongoing ULPI transaction complete.
|
||||
*/
|
||||
udelay(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy the appropriate control structures corresponding
|
||||
* the the EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_stop(int index)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setting the value of TXFIFO_THRESH field in TXFILLTUNING register
|
||||
* to counter DDR latencies in writing data into Tx buffer.
|
||||
* This prevents Tx buffer from getting underrun
|
||||
*/
|
||||
static void set_txfifothresh(struct usb_ehci *ehci, u32 txfifo_thresh)
|
||||
{
|
||||
u32 cmd;
|
||||
cmd = ehci_readl(&ehci->txfilltuning);
|
||||
cmd &= ~TXFIFO_THRESH_MASK;
|
||||
cmd |= TXFIFO_THRESH(txfifo_thresh);
|
||||
ehci_writel(&ehci->txfilltuning, cmd);
|
||||
}
|
||||
66
u-boot/drivers/usb/host/ehci-generic.c
Normal file
66
u-boot/drivers/usb/host/ehci-generic.c
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Alexey Brodkin <abrodkin@synopsys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <clk.h>
|
||||
#include <asm/io.h>
|
||||
#include <dm.h>
|
||||
#include "ehci.h"
|
||||
|
||||
/*
|
||||
* Even though here we don't explicitly use "struct ehci_ctrl"
|
||||
* ehci_register() expects it to be the first thing that resides in
|
||||
* device's private data.
|
||||
*/
|
||||
struct generic_ehci {
|
||||
struct ehci_ctrl ctrl;
|
||||
};
|
||||
|
||||
static int ehci_usb_probe(struct udevice *dev)
|
||||
{
|
||||
struct ehci_hccr *hccr;
|
||||
struct ehci_hcor *hcor;
|
||||
int i;
|
||||
|
||||
for (i = 0; ; i++) {
|
||||
struct clk clk;
|
||||
int ret;
|
||||
|
||||
ret = clk_get_by_index(dev, i, &clk);
|
||||
if (ret < 0)
|
||||
break;
|
||||
if (clk_enable(&clk))
|
||||
printf("failed to enable clock %d\n", i);
|
||||
clk_free(&clk);
|
||||
}
|
||||
|
||||
hccr = map_physmem(dev_get_addr(dev), 0x100, MAP_NOCACHE);
|
||||
hcor = (struct ehci_hcor *)((uintptr_t)hccr +
|
||||
HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
|
||||
|
||||
return ehci_register(dev, hccr, hcor, NULL, 0, USB_INIT_HOST);
|
||||
}
|
||||
|
||||
static int ehci_usb_remove(struct udevice *dev)
|
||||
{
|
||||
return ehci_deregister(dev);
|
||||
}
|
||||
|
||||
static const struct udevice_id ehci_usb_ids[] = {
|
||||
{ .compatible = "generic-ehci" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(ehci_generic) = {
|
||||
.name = "ehci_generic",
|
||||
.id = UCLASS_USB,
|
||||
.of_match = ehci_usb_ids,
|
||||
.probe = ehci_usb_probe,
|
||||
.remove = ehci_usb_remove,
|
||||
.ops = &ehci_usb_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct generic_ehci),
|
||||
.flags = DM_FLAG_ALLOC_PRIV_DMA,
|
||||
};
|
||||
1659
u-boot/drivers/usb/host/ehci-hcd.c
Normal file
1659
u-boot/drivers/usb/host/ehci-hcd.c
Normal file
File diff suppressed because it is too large
Load Diff
198
u-boot/drivers/usb/host/ehci-marvell.c
Normal file
198
u-boot/drivers/usb/host/ehci-marvell.c
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* (C) Copyright 2009
|
||||
* Marvell Semiconductor <www.marvell.com>
|
||||
* Written-by: Prafulla Wadaskar <prafulla@marvell.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <usb.h>
|
||||
#include "ehci.h"
|
||||
#include <linux/mbus.h>
|
||||
#include <asm/arch/cpu.h>
|
||||
#include <dm.h>
|
||||
|
||||
#if defined(CONFIG_KIRKWOOD)
|
||||
#include <asm/arch/soc.h>
|
||||
#elif defined(CONFIG_ORION5X)
|
||||
#include <asm/arch/orion5x.h>
|
||||
#endif
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define USB_WINDOW_CTRL(i) (0x320 + ((i) << 4))
|
||||
#define USB_WINDOW_BASE(i) (0x324 + ((i) << 4))
|
||||
#define USB_TARGET_DRAM 0x0
|
||||
|
||||
/*
|
||||
* USB 2.0 Bridge Address Decoding registers setup
|
||||
*/
|
||||
#ifdef CONFIG_DM_USB
|
||||
|
||||
struct ehci_mvebu_priv {
|
||||
struct ehci_ctrl ehci;
|
||||
fdt_addr_t hcd_base;
|
||||
};
|
||||
|
||||
/*
|
||||
* Once all the older Marvell SoC's (Orion, Kirkwood) are converted
|
||||
* to the common mvebu archticture including the mbus setup, this
|
||||
* will be the only function needed to configure the access windows
|
||||
*/
|
||||
static void usb_brg_adrdec_setup(u32 base)
|
||||
{
|
||||
const struct mbus_dram_target_info *dram;
|
||||
int i;
|
||||
|
||||
dram = mvebu_mbus_dram_info();
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
writel(0, base + USB_WINDOW_CTRL(i));
|
||||
writel(0, base + USB_WINDOW_BASE(i));
|
||||
}
|
||||
|
||||
for (i = 0; i < dram->num_cs; i++) {
|
||||
const struct mbus_dram_window *cs = dram->cs + i;
|
||||
|
||||
/* Write size, attributes and target id to control register */
|
||||
writel(((cs->size - 1) & 0xffff0000) | (cs->mbus_attr << 8) |
|
||||
(dram->mbus_dram_target_id << 4) | 1,
|
||||
base + USB_WINDOW_CTRL(i));
|
||||
|
||||
/* Write base address to base register */
|
||||
writel(cs->base, base + USB_WINDOW_BASE(i));
|
||||
}
|
||||
}
|
||||
|
||||
static int ehci_mvebu_probe(struct udevice *dev)
|
||||
{
|
||||
struct ehci_mvebu_priv *priv = dev_get_priv(dev);
|
||||
struct ehci_hccr *hccr;
|
||||
struct ehci_hcor *hcor;
|
||||
|
||||
/*
|
||||
* Get the base address for EHCI controller from the device node
|
||||
*/
|
||||
priv->hcd_base = dev_get_addr(dev);
|
||||
if (priv->hcd_base == FDT_ADDR_T_NONE) {
|
||||
debug("Can't get the EHCI register base address\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
usb_brg_adrdec_setup(priv->hcd_base);
|
||||
|
||||
hccr = (struct ehci_hccr *)(priv->hcd_base + 0x100);
|
||||
hcor = (struct ehci_hcor *)
|
||||
((u32)hccr + HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
|
||||
|
||||
debug("ehci-marvell: init hccr %x and hcor %x hc_length %d\n",
|
||||
(u32)hccr, (u32)hcor,
|
||||
(u32)HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
|
||||
|
||||
return ehci_register(dev, hccr, hcor, NULL, 0, USB_INIT_HOST);
|
||||
}
|
||||
|
||||
static int ehci_mvebu_remove(struct udevice *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ehci_deregister(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id ehci_usb_ids[] = {
|
||||
{ .compatible = "marvell,orion-ehci", },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(ehci_mvebu) = {
|
||||
.name = "ehci_mvebu",
|
||||
.id = UCLASS_USB,
|
||||
.of_match = ehci_usb_ids,
|
||||
.probe = ehci_mvebu_probe,
|
||||
.remove = ehci_mvebu_remove,
|
||||
.ops = &ehci_usb_ops,
|
||||
.platdata_auto_alloc_size = sizeof(struct usb_platdata),
|
||||
.priv_auto_alloc_size = sizeof(struct ehci_mvebu_priv),
|
||||
.flags = DM_FLAG_ALLOC_PRIV_DMA,
|
||||
};
|
||||
|
||||
#else
|
||||
#define MVUSB_BASE(port) MVUSB0_BASE
|
||||
|
||||
static void usb_brg_adrdec_setup(int index)
|
||||
{
|
||||
int i;
|
||||
u32 size, base, attrib;
|
||||
|
||||
for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
|
||||
|
||||
/* Enable DRAM bank */
|
||||
switch (i) {
|
||||
case 0:
|
||||
attrib = MVUSB0_CPU_ATTR_DRAM_CS0;
|
||||
break;
|
||||
case 1:
|
||||
attrib = MVUSB0_CPU_ATTR_DRAM_CS1;
|
||||
break;
|
||||
case 2:
|
||||
attrib = MVUSB0_CPU_ATTR_DRAM_CS2;
|
||||
break;
|
||||
case 3:
|
||||
attrib = MVUSB0_CPU_ATTR_DRAM_CS3;
|
||||
break;
|
||||
default:
|
||||
/* invalide bank, disable access */
|
||||
attrib = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
size = gd->bd->bi_dram[i].size;
|
||||
base = gd->bd->bi_dram[i].start;
|
||||
if ((size) && (attrib))
|
||||
writel(MVCPU_WIN_CTRL_DATA(size, USB_TARGET_DRAM,
|
||||
attrib, MVCPU_WIN_ENABLE),
|
||||
MVUSB0_BASE + USB_WINDOW_CTRL(i));
|
||||
else
|
||||
writel(MVCPU_WIN_DISABLE,
|
||||
MVUSB0_BASE + USB_WINDOW_CTRL(i));
|
||||
|
||||
writel(base, MVUSB0_BASE + USB_WINDOW_BASE(i));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the appropriate control structures to manage
|
||||
* a new EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
usb_brg_adrdec_setup(index);
|
||||
|
||||
*hccr = (struct ehci_hccr *)(MVUSB_BASE(index) + 0x100);
|
||||
*hcor = (struct ehci_hcor *)((uint32_t) *hccr
|
||||
+ HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
|
||||
|
||||
debug("ehci-marvell: init hccr %x and hcor %x hc_length %d\n",
|
||||
(uint32_t)*hccr, (uint32_t)*hcor,
|
||||
(uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy the appropriate control structures corresponding
|
||||
* the the EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_stop(int index)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_DM_USB */
|
||||
140
u-boot/drivers/usb/host/ehci-mpc512x.c
Normal file
140
u-boot/drivers/usb/host/ehci-mpc512x.c
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* (C) Copyright 2010, Damien Dusha, <d.dusha@gmail.com>
|
||||
*
|
||||
* (C) Copyright 2009, Value Team S.p.A.
|
||||
* Francesco Rendine, <francesco.rendine@valueteam.com>
|
||||
*
|
||||
* (C) Copyright 2009 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* (C) Copyright 2008, Excito Elektronik i Sk=E5ne AB
|
||||
*
|
||||
* Author: Tor Krill tor@excito.com
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <pci.h>
|
||||
#include <usb.h>
|
||||
#include <asm/io.h>
|
||||
#include <usb/ehci-ci.h>
|
||||
|
||||
#include "ehci.h"
|
||||
|
||||
static void fsl_setup_phy(volatile struct ehci_hcor *);
|
||||
static void fsl_platform_set_host_mode(volatile struct usb_ehci *ehci);
|
||||
static int reset_usb_controller(volatile struct usb_ehci *ehci);
|
||||
static void usb_platform_dr_init(volatile struct usb_ehci *ehci);
|
||||
|
||||
/*
|
||||
* Initialize SOC FSL EHCI Controller
|
||||
*
|
||||
* This code is derived from EHCI FSL USB Linux driver for MPC5121
|
||||
*
|
||||
*/
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
volatile struct usb_ehci *ehci;
|
||||
|
||||
/* Hook the memory mapped registers for EHCI-Controller */
|
||||
ehci = (struct usb_ehci *)CONFIG_SYS_FSL_USB1_ADDR;
|
||||
*hccr = (struct ehci_hccr *)((uint32_t)&(ehci->caplength));
|
||||
*hcor = (struct ehci_hcor *)((uint32_t) *hccr +
|
||||
HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
|
||||
|
||||
/* configure interface for UTMI_WIDE */
|
||||
usb_platform_dr_init(ehci);
|
||||
|
||||
/* Init Phy USB0 to UTMI+ */
|
||||
fsl_setup_phy(*hcor);
|
||||
|
||||
/* Set to host mode */
|
||||
fsl_platform_set_host_mode(ehci);
|
||||
|
||||
/*
|
||||
* Setting the burst size seems to be required to prevent the
|
||||
* USB from hanging when communicating with certain USB Mass
|
||||
* storage devices. This was determined by analysing the
|
||||
* EHCI registers under Linux vs U-Boot and burstsize was the
|
||||
* major non-interrupt related difference between the two
|
||||
* implementations.
|
||||
*
|
||||
* Some USB sticks behave better than others. In particular,
|
||||
* the following USB stick is especially problematic:
|
||||
* 0930:6545 Toshiba Corp
|
||||
*
|
||||
* The burstsize is set here to match the Linux implementation.
|
||||
*/
|
||||
out_be32(&ehci->burstsize, FSL_EHCI_TXPBURST(8) |
|
||||
FSL_EHCI_RXPBURST(8));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy the appropriate control structures corresponding
|
||||
* the the EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_stop(int index)
|
||||
{
|
||||
volatile struct usb_ehci *ehci;
|
||||
int exit_status = 0;
|
||||
|
||||
/* Reset the USB controller */
|
||||
ehci = (struct usb_ehci *)CONFIG_SYS_FSL_USB1_ADDR;
|
||||
exit_status = reset_usb_controller(ehci);
|
||||
|
||||
return exit_status;
|
||||
}
|
||||
|
||||
static int reset_usb_controller(volatile struct usb_ehci *ehci)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/* Command a reset of the USB Controller */
|
||||
out_be32(&(ehci->usbcmd), CMD_RESET);
|
||||
|
||||
/* Wait for the reset process to finish */
|
||||
for (i = 65535 ; i > 0 ; i--) {
|
||||
/*
|
||||
* The host will set this bit to zero once the
|
||||
* reset process is complete
|
||||
*/
|
||||
if ((in_be32(&(ehci->usbcmd)) & CMD_RESET) == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Hub did not reset in time */
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void fsl_setup_phy(volatile struct ehci_hcor *hcor)
|
||||
{
|
||||
uint32_t portsc;
|
||||
|
||||
portsc = ehci_readl(&hcor->or_portsc[0]);
|
||||
portsc &= ~(PORT_PTS_MSK | PORT_PTS_PTW);
|
||||
|
||||
/* Enable the phy mode to UTMI Wide */
|
||||
portsc |= PORT_PTS_PTW;
|
||||
portsc |= PORT_PTS_UTMI;
|
||||
|
||||
ehci_writel(&hcor->or_portsc[0], portsc);
|
||||
}
|
||||
|
||||
static void fsl_platform_set_host_mode(volatile struct usb_ehci *ehci)
|
||||
{
|
||||
uint32_t temp;
|
||||
|
||||
temp = in_le32(&ehci->usbmode);
|
||||
temp |= CM_HOST | ES_BE;
|
||||
out_le32(&ehci->usbmode, temp);
|
||||
}
|
||||
|
||||
static void usb_platform_dr_init(volatile struct usb_ehci *ehci)
|
||||
{
|
||||
/* Configure interface for UTMI_WIDE */
|
||||
out_be32(&ehci->isiphyctrl, PHYCTRL_PHYE | PHYCTRL_PXE);
|
||||
out_be32(&ehci->usbgenctrl, GC_PPP | GC_PFP );
|
||||
}
|
||||
178
u-boot/drivers/usb/host/ehci-msm.c
Normal file
178
u-boot/drivers/usb/host/ehci-msm.c
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Qualcomm EHCI driver
|
||||
*
|
||||
* (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
|
||||
*
|
||||
* Based on Linux driver
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <libfdt.h>
|
||||
#include <usb.h>
|
||||
#include <usb/ehci-ci.h>
|
||||
#include <usb/ulpi.h>
|
||||
#include <wait_bit.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/compat.h>
|
||||
#include "ehci.h"
|
||||
|
||||
/* PHY viewport regs */
|
||||
#define ULPI_MISC_A_READ 0x96
|
||||
#define ULPI_MISC_A_SET 0x97
|
||||
#define ULPI_MISC_A_CLEAR 0x98
|
||||
#define ULPI_MISC_A_VBUSVLDEXTSEL (1 << 1)
|
||||
#define ULPI_MISC_A_VBUSVLDEXT (1 << 0)
|
||||
|
||||
#define GEN2_SESS_VLD_CTRL_EN (1 << 7)
|
||||
|
||||
#define SESS_VLD_CTRL (1 << 25)
|
||||
|
||||
struct msm_ehci_priv {
|
||||
struct ehci_ctrl ctrl; /* Needed by EHCI */
|
||||
struct usb_ehci *ehci; /* Start of IP core*/
|
||||
struct ulpi_viewport ulpi_vp; /* ULPI Viewport */
|
||||
};
|
||||
|
||||
int __weak board_prepare_usb(enum usb_init_type type)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void setup_usb_phy(struct msm_ehci_priv *priv)
|
||||
{
|
||||
/* Select and enable external configuration with USB PHY */
|
||||
ulpi_write(&priv->ulpi_vp, (u8 *)ULPI_MISC_A_SET,
|
||||
ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT);
|
||||
}
|
||||
|
||||
static void reset_usb_phy(struct msm_ehci_priv *priv)
|
||||
{
|
||||
/* Disable VBUS mimicing in the controller. */
|
||||
ulpi_write(&priv->ulpi_vp, (u8 *)ULPI_MISC_A_CLEAR,
|
||||
ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT);
|
||||
}
|
||||
|
||||
|
||||
static int msm_init_after_reset(struct ehci_ctrl *dev)
|
||||
{
|
||||
struct msm_ehci_priv *p = container_of(dev, struct msm_ehci_priv, ctrl);
|
||||
struct usb_ehci *ehci = p->ehci;
|
||||
|
||||
/* select ULPI phy */
|
||||
writel(PORT_PTS_ULPI, &ehci->portsc);
|
||||
setup_usb_phy(p);
|
||||
|
||||
/* Enable sess_vld */
|
||||
setbits_le32(&ehci->genconfig2, GEN2_SESS_VLD_CTRL_EN);
|
||||
|
||||
/* Enable external vbus configuration in the LINK */
|
||||
setbits_le32(&ehci->usbcmd, SESS_VLD_CTRL);
|
||||
|
||||
/* USB_OTG_HS_AHB_BURST */
|
||||
writel(0x0, &ehci->sbuscfg);
|
||||
|
||||
/* USB_OTG_HS_AHB_MODE: HPROT_MODE */
|
||||
/* Bus access related config. */
|
||||
writel(0x08, &ehci->sbusmode);
|
||||
|
||||
/* set mode to host controller */
|
||||
writel(CM_HOST, &ehci->usbmode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ehci_ops msm_ehci_ops = {
|
||||
.init_after_reset = msm_init_after_reset
|
||||
};
|
||||
|
||||
static int ehci_usb_probe(struct udevice *dev)
|
||||
{
|
||||
struct msm_ehci_priv *p = dev_get_priv(dev);
|
||||
struct usb_ehci *ehci = p->ehci;
|
||||
struct ehci_hccr *hccr;
|
||||
struct ehci_hcor *hcor;
|
||||
int ret;
|
||||
|
||||
hccr = (struct ehci_hccr *)((phys_addr_t)&ehci->caplength);
|
||||
hcor = (struct ehci_hcor *)((phys_addr_t)hccr +
|
||||
HC_LENGTH(ehci_readl(&(hccr)->cr_capbase)));
|
||||
|
||||
ret = board_prepare_usb(USB_INIT_HOST);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ehci_register(dev, hccr, hcor, &msm_ehci_ops, 0, USB_INIT_HOST);
|
||||
}
|
||||
|
||||
static int ehci_usb_remove(struct udevice *dev)
|
||||
{
|
||||
struct msm_ehci_priv *p = dev_get_priv(dev);
|
||||
struct usb_ehci *ehci = p->ehci;
|
||||
int ret;
|
||||
|
||||
ret = ehci_deregister(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Stop controller. */
|
||||
clrbits_le32(&ehci->usbcmd, CMD_RUN);
|
||||
|
||||
reset_usb_phy(p);
|
||||
|
||||
ret = board_prepare_usb(USB_INIT_DEVICE); /* Board specific hook */
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Reset controller */
|
||||
setbits_le32(&ehci->usbcmd, CMD_RESET);
|
||||
|
||||
/* Wait for reset */
|
||||
if (wait_for_bit(__func__, &ehci->usbcmd, CMD_RESET, false, 30,
|
||||
false)) {
|
||||
printf("Stuck on USB reset.\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ehci_usb_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct msm_ehci_priv *priv = dev_get_priv(dev);
|
||||
|
||||
priv->ulpi_vp.port_num = 0;
|
||||
priv->ehci = (void *)dev_get_addr(dev);
|
||||
|
||||
if (priv->ehci == (void *)FDT_ADDR_T_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
/* Warning: this will not work if viewport address is > 64 bit due to
|
||||
* ULPI design.
|
||||
*/
|
||||
priv->ulpi_vp.viewport_addr = (phys_addr_t)&priv->ehci->ulpi_viewpoint;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id ehci_usb_ids[] = {
|
||||
{ .compatible = "qcom,ehci-host", },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(usb_ehci) = {
|
||||
.name = "ehci_msm",
|
||||
.id = UCLASS_USB,
|
||||
.of_match = ehci_usb_ids,
|
||||
.ofdata_to_platdata = ehci_usb_ofdata_to_platdata,
|
||||
.probe = ehci_usb_probe,
|
||||
.remove = ehci_usb_remove,
|
||||
.ops = &ehci_usb_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct msm_ehci_priv),
|
||||
.flags = DM_FLAG_ALLOC_PRIV_DMA,
|
||||
};
|
||||
270
u-boot/drivers/usb/host/ehci-mx5.c
Normal file
270
u-boot/drivers/usb/host/ehci-mx5.c
Normal file
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
|
||||
* Copyright (C) 2010 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <usb.h>
|
||||
#include <errno.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <usb/ehci-ci.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/imx-regs.h>
|
||||
#include <asm/arch/clock.h>
|
||||
|
||||
#include "ehci.h"
|
||||
|
||||
#define MX5_USBOTHER_REGS_OFFSET 0x800
|
||||
|
||||
|
||||
#define MXC_OTG_OFFSET 0
|
||||
#define MXC_H1_OFFSET 0x200
|
||||
#define MXC_H2_OFFSET 0x400
|
||||
#define MXC_H3_OFFSET 0x600
|
||||
|
||||
#define MXC_USBCTRL_OFFSET 0
|
||||
#define MXC_USB_PHY_CTR_FUNC_OFFSET 0x8
|
||||
#define MXC_USB_PHY_CTR_FUNC2_OFFSET 0xc
|
||||
#define MXC_USB_CTRL_1_OFFSET 0x10
|
||||
#define MXC_USBH2CTRL_OFFSET 0x14
|
||||
#define MXC_USBH3CTRL_OFFSET 0x18
|
||||
|
||||
/* USB_CTRL */
|
||||
/* OTG wakeup intr enable */
|
||||
#define MXC_OTG_UCTRL_OWIE_BIT (1 << 27)
|
||||
/* OTG power mask */
|
||||
#define MXC_OTG_UCTRL_OPM_BIT (1 << 24)
|
||||
/* OTG power pin polarity */
|
||||
#define MXC_OTG_UCTRL_O_PWR_POL_BIT (1 << 24)
|
||||
/* Host1 ULPI interrupt enable */
|
||||
#define MXC_H1_UCTRL_H1UIE_BIT (1 << 12)
|
||||
/* HOST1 wakeup intr enable */
|
||||
#define MXC_H1_UCTRL_H1WIE_BIT (1 << 11)
|
||||
/* HOST1 power mask */
|
||||
#define MXC_H1_UCTRL_H1PM_BIT (1 << 8)
|
||||
/* HOST1 power pin polarity */
|
||||
#define MXC_H1_UCTRL_H1_PWR_POL_BIT (1 << 8)
|
||||
|
||||
/* USB_PHY_CTRL_FUNC */
|
||||
/* OTG Polarity of Overcurrent */
|
||||
#define MXC_OTG_PHYCTRL_OC_POL_BIT (1 << 9)
|
||||
/* OTG Disable Overcurrent Event */
|
||||
#define MXC_OTG_PHYCTRL_OC_DIS_BIT (1 << 8)
|
||||
/* UH1 Polarity of Overcurrent */
|
||||
#define MXC_H1_OC_POL_BIT (1 << 6)
|
||||
/* UH1 Disable Overcurrent Event */
|
||||
#define MXC_H1_OC_DIS_BIT (1 << 5)
|
||||
/* OTG Power Pin Polarity */
|
||||
#define MXC_OTG_PHYCTRL_PWR_POL_BIT (1 << 3)
|
||||
|
||||
/* USBH2CTRL */
|
||||
#define MXC_H2_UCTRL_H2_OC_POL_BIT (1 << 31)
|
||||
#define MXC_H2_UCTRL_H2_OC_DIS_BIT (1 << 30)
|
||||
#define MXC_H2_UCTRL_H2UIE_BIT (1 << 8)
|
||||
#define MXC_H2_UCTRL_H2WIE_BIT (1 << 7)
|
||||
#define MXC_H2_UCTRL_H2PM_BIT (1 << 4)
|
||||
#define MXC_H2_UCTRL_H2_PWR_POL_BIT (1 << 4)
|
||||
|
||||
/* USBH3CTRL */
|
||||
#define MXC_H3_UCTRL_H3_OC_POL_BIT (1 << 31)
|
||||
#define MXC_H3_UCTRL_H3_OC_DIS_BIT (1 << 30)
|
||||
#define MXC_H3_UCTRL_H3UIE_BIT (1 << 8)
|
||||
#define MXC_H3_UCTRL_H3WIE_BIT (1 << 7)
|
||||
#define MXC_H3_UCTRL_H3_PWR_POL_BIT (1 << 4)
|
||||
|
||||
/* USB_CTRL_1 */
|
||||
#define MXC_USB_CTRL_UH1_EXT_CLK_EN (1 << 25)
|
||||
|
||||
int mxc_set_usbcontrol(int port, unsigned int flags)
|
||||
{
|
||||
unsigned int v;
|
||||
void __iomem *usb_base = (void __iomem *)OTG_BASE_ADDR;
|
||||
void __iomem *usbother_base;
|
||||
int ret = 0;
|
||||
|
||||
usbother_base = usb_base + MX5_USBOTHER_REGS_OFFSET;
|
||||
|
||||
switch (port) {
|
||||
case 0: /* OTG port */
|
||||
if (flags & MXC_EHCI_INTERNAL_PHY) {
|
||||
v = __raw_readl(usbother_base +
|
||||
MXC_USB_PHY_CTR_FUNC_OFFSET);
|
||||
if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)
|
||||
v |= MXC_OTG_PHYCTRL_OC_POL_BIT;
|
||||
else
|
||||
v &= ~MXC_OTG_PHYCTRL_OC_POL_BIT;
|
||||
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
|
||||
/* OC/USBPWR is used */
|
||||
v &= ~MXC_OTG_PHYCTRL_OC_DIS_BIT;
|
||||
else
|
||||
/* OC/USBPWR is not used */
|
||||
v |= MXC_OTG_PHYCTRL_OC_DIS_BIT;
|
||||
#ifdef CONFIG_MX51
|
||||
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
|
||||
v |= MXC_OTG_PHYCTRL_PWR_POL_BIT;
|
||||
else
|
||||
v &= ~MXC_OTG_PHYCTRL_PWR_POL_BIT;
|
||||
#endif
|
||||
__raw_writel(v, usbother_base +
|
||||
MXC_USB_PHY_CTR_FUNC_OFFSET);
|
||||
|
||||
v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET);
|
||||
#ifdef CONFIG_MX51
|
||||
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
|
||||
v &= ~MXC_OTG_UCTRL_OPM_BIT;
|
||||
else
|
||||
v |= MXC_OTG_UCTRL_OPM_BIT;
|
||||
#endif
|
||||
#ifdef CONFIG_MX53
|
||||
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
|
||||
v |= MXC_OTG_UCTRL_O_PWR_POL_BIT;
|
||||
else
|
||||
v &= ~MXC_OTG_UCTRL_O_PWR_POL_BIT;
|
||||
#endif
|
||||
__raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET);
|
||||
}
|
||||
break;
|
||||
case 1: /* Host 1 ULPI */
|
||||
#ifdef CONFIG_MX51
|
||||
/* The clock for the USBH1 ULPI port will come externally
|
||||
from the PHY. */
|
||||
v = __raw_readl(usbother_base + MXC_USB_CTRL_1_OFFSET);
|
||||
__raw_writel(v | MXC_USB_CTRL_UH1_EXT_CLK_EN, usbother_base +
|
||||
MXC_USB_CTRL_1_OFFSET);
|
||||
#endif
|
||||
|
||||
v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET);
|
||||
#ifdef CONFIG_MX51
|
||||
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
|
||||
v &= ~MXC_H1_UCTRL_H1PM_BIT; /* H1 power mask unused */
|
||||
else
|
||||
v |= MXC_H1_UCTRL_H1PM_BIT; /* H1 power mask used */
|
||||
#endif
|
||||
#ifdef CONFIG_MX53
|
||||
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
|
||||
v |= MXC_H1_UCTRL_H1_PWR_POL_BIT;
|
||||
else
|
||||
v &= ~MXC_H1_UCTRL_H1_PWR_POL_BIT;
|
||||
#endif
|
||||
__raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET);
|
||||
|
||||
v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET);
|
||||
if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)
|
||||
v |= MXC_H1_OC_POL_BIT;
|
||||
else
|
||||
v &= ~MXC_H1_OC_POL_BIT;
|
||||
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
|
||||
v &= ~MXC_H1_OC_DIS_BIT; /* OC is used */
|
||||
else
|
||||
v |= MXC_H1_OC_DIS_BIT; /* OC is not used */
|
||||
__raw_writel(v, usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET);
|
||||
|
||||
break;
|
||||
case 2: /* Host 2 ULPI */
|
||||
v = __raw_readl(usbother_base + MXC_USBH2CTRL_OFFSET);
|
||||
#ifdef CONFIG_MX51
|
||||
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
|
||||
v &= ~MXC_H2_UCTRL_H2PM_BIT; /* H2 power mask unused */
|
||||
else
|
||||
v |= MXC_H2_UCTRL_H2PM_BIT; /* H2 power mask used */
|
||||
#endif
|
||||
#ifdef CONFIG_MX53
|
||||
if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)
|
||||
v |= MXC_H2_UCTRL_H2_OC_POL_BIT;
|
||||
else
|
||||
v &= ~MXC_H2_UCTRL_H2_OC_POL_BIT;
|
||||
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
|
||||
v &= ~MXC_H2_UCTRL_H2_OC_DIS_BIT; /* OC is used */
|
||||
else
|
||||
v |= MXC_H2_UCTRL_H2_OC_DIS_BIT; /* OC is not used */
|
||||
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
|
||||
v |= MXC_H2_UCTRL_H2_PWR_POL_BIT;
|
||||
else
|
||||
v &= ~MXC_H2_UCTRL_H2_PWR_POL_BIT;
|
||||
#endif
|
||||
__raw_writel(v, usbother_base + MXC_USBH2CTRL_OFFSET);
|
||||
break;
|
||||
#ifdef CONFIG_MX53
|
||||
case 3: /* Host 3 ULPI */
|
||||
v = __raw_readl(usbother_base + MXC_USBH3CTRL_OFFSET);
|
||||
if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)
|
||||
v |= MXC_H3_UCTRL_H3_OC_POL_BIT;
|
||||
else
|
||||
v &= ~MXC_H3_UCTRL_H3_OC_POL_BIT;
|
||||
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
|
||||
v &= ~MXC_H3_UCTRL_H3_OC_DIS_BIT; /* OC is used */
|
||||
else
|
||||
v |= MXC_H3_UCTRL_H3_OC_DIS_BIT; /* OC is not used */
|
||||
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
|
||||
v |= MXC_H3_UCTRL_H3_PWR_POL_BIT;
|
||||
else
|
||||
v &= ~MXC_H3_UCTRL_H3_PWR_POL_BIT;
|
||||
__raw_writel(v, usbother_base + MXC_USBH3CTRL_OFFSET);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __weak board_ehci_hcd_init(int port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __weak board_ehci_hcd_postinit(struct usb_ehci *ehci, int port)
|
||||
{
|
||||
}
|
||||
|
||||
__weak void mx5_ehci_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg,
|
||||
uint32_t *reg)
|
||||
{
|
||||
mdelay(50);
|
||||
}
|
||||
|
||||
static const struct ehci_ops mx5_ehci_ops = {
|
||||
.powerup_fixup = mx5_ehci_powerup_fixup,
|
||||
};
|
||||
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
struct usb_ehci *ehci;
|
||||
|
||||
/* The only user for this is efikamx-usb */
|
||||
ehci_set_controller_priv(index, NULL, &mx5_ehci_ops);
|
||||
set_usboh3_clk();
|
||||
enable_usboh3_clk(true);
|
||||
set_usb_phy_clk();
|
||||
enable_usb_phy1_clk(true);
|
||||
enable_usb_phy2_clk(true);
|
||||
mdelay(1);
|
||||
|
||||
/* Do board specific initialization */
|
||||
board_ehci_hcd_init(CONFIG_MXC_USB_PORT);
|
||||
|
||||
ehci = (struct usb_ehci *)(OTG_BASE_ADDR +
|
||||
(0x200 * CONFIG_MXC_USB_PORT));
|
||||
*hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength);
|
||||
*hcor = (struct ehci_hcor *)((uint32_t)*hccr +
|
||||
HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
|
||||
setbits_le32(&ehci->usbmode, CM_HOST);
|
||||
|
||||
__raw_writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc);
|
||||
setbits_le32(&ehci->portsc, USB_EN);
|
||||
|
||||
mxc_set_usbcontrol(CONFIG_MXC_USB_PORT, CONFIG_MXC_USB_FLAGS);
|
||||
mdelay(10);
|
||||
|
||||
/* Do board specific post-initialization */
|
||||
board_ehci_hcd_postinit(ehci, CONFIG_MXC_USB_PORT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ehci_hcd_stop(int index)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
361
u-boot/drivers/usb/host/ehci-mx6.c
Normal file
361
u-boot/drivers/usb/host/ehci-mx6.c
Normal file
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
|
||||
* Copyright (C) 2010 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <usb.h>
|
||||
#include <errno.h>
|
||||
#include <wait_bit.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <usb/ehci-ci.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/imx-regs.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/imx-common/iomux-v3.h>
|
||||
|
||||
#include "ehci.h"
|
||||
|
||||
#define USB_OTGREGS_OFFSET 0x000
|
||||
#define USB_H1REGS_OFFSET 0x200
|
||||
#define USB_H2REGS_OFFSET 0x400
|
||||
#define USB_H3REGS_OFFSET 0x600
|
||||
#define USB_OTHERREGS_OFFSET 0x800
|
||||
|
||||
#define USB_H1_CTRL_OFFSET 0x04
|
||||
|
||||
#define USBPHY_CTRL 0x00000030
|
||||
#define USBPHY_CTRL_SET 0x00000034
|
||||
#define USBPHY_CTRL_CLR 0x00000038
|
||||
#define USBPHY_CTRL_TOG 0x0000003c
|
||||
|
||||
#define USBPHY_PWD 0x00000000
|
||||
#define USBPHY_CTRL_SFTRST 0x80000000
|
||||
#define USBPHY_CTRL_CLKGATE 0x40000000
|
||||
#define USBPHY_CTRL_ENUTMILEVEL3 0x00008000
|
||||
#define USBPHY_CTRL_ENUTMILEVEL2 0x00004000
|
||||
#define USBPHY_CTRL_OTG_ID 0x08000000
|
||||
|
||||
#define ANADIG_USB2_CHRG_DETECT_EN_B 0x00100000
|
||||
#define ANADIG_USB2_CHRG_DETECT_CHK_CHRG_B 0x00080000
|
||||
|
||||
#define ANADIG_USB2_PLL_480_CTRL_BYPASS 0x00010000
|
||||
#define ANADIG_USB2_PLL_480_CTRL_ENABLE 0x00002000
|
||||
#define ANADIG_USB2_PLL_480_CTRL_POWER 0x00001000
|
||||
#define ANADIG_USB2_PLL_480_CTRL_EN_USB_CLKS 0x00000040
|
||||
|
||||
#define USBNC_OFFSET 0x200
|
||||
#define USBNC_PHYSTATUS_ID_DIG (1 << 4) /* otg_id status */
|
||||
#define USBNC_PHYCFG2_ACAENB (1 << 4) /* otg_id detection enable */
|
||||
#define UCTRL_PM (1 << 9) /* OTG Power Mask */
|
||||
#define UCTRL_OVER_CUR_POL (1 << 8) /* OTG Polarity of Overcurrent */
|
||||
#define UCTRL_OVER_CUR_DIS (1 << 7) /* Disable OTG Overcurrent Detection */
|
||||
|
||||
/* USBCMD */
|
||||
#define UCMD_RUN_STOP (1 << 0) /* controller run/stop */
|
||||
#define UCMD_RESET (1 << 1) /* controller reset */
|
||||
|
||||
#if defined(CONFIG_MX6)
|
||||
static const unsigned phy_bases[] = {
|
||||
USB_PHY0_BASE_ADDR,
|
||||
USB_PHY1_BASE_ADDR,
|
||||
};
|
||||
|
||||
static void usb_internal_phy_clock_gate(int index, int on)
|
||||
{
|
||||
void __iomem *phy_reg;
|
||||
|
||||
if (index >= ARRAY_SIZE(phy_bases))
|
||||
return;
|
||||
|
||||
phy_reg = (void __iomem *)phy_bases[index];
|
||||
phy_reg += on ? USBPHY_CTRL_CLR : USBPHY_CTRL_SET;
|
||||
writel(USBPHY_CTRL_CLKGATE, phy_reg);
|
||||
}
|
||||
|
||||
static void usb_power_config(int index)
|
||||
{
|
||||
struct anatop_regs __iomem *anatop =
|
||||
(struct anatop_regs __iomem *)ANATOP_BASE_ADDR;
|
||||
void __iomem *chrg_detect;
|
||||
void __iomem *pll_480_ctrl_clr;
|
||||
void __iomem *pll_480_ctrl_set;
|
||||
|
||||
switch (index) {
|
||||
case 0:
|
||||
chrg_detect = &anatop->usb1_chrg_detect;
|
||||
pll_480_ctrl_clr = &anatop->usb1_pll_480_ctrl_clr;
|
||||
pll_480_ctrl_set = &anatop->usb1_pll_480_ctrl_set;
|
||||
break;
|
||||
case 1:
|
||||
chrg_detect = &anatop->usb2_chrg_detect;
|
||||
pll_480_ctrl_clr = &anatop->usb2_pll_480_ctrl_clr;
|
||||
pll_480_ctrl_set = &anatop->usb2_pll_480_ctrl_set;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Some phy and power's special controls
|
||||
* 1. The external charger detector needs to be disabled
|
||||
* or the signal at DP will be poor
|
||||
* 2. The PLL's power and output to usb
|
||||
* is totally controlled by IC, so the Software only needs
|
||||
* to enable them at initializtion.
|
||||
*/
|
||||
writel(ANADIG_USB2_CHRG_DETECT_EN_B |
|
||||
ANADIG_USB2_CHRG_DETECT_CHK_CHRG_B,
|
||||
chrg_detect);
|
||||
|
||||
writel(ANADIG_USB2_PLL_480_CTRL_BYPASS,
|
||||
pll_480_ctrl_clr);
|
||||
|
||||
writel(ANADIG_USB2_PLL_480_CTRL_ENABLE |
|
||||
ANADIG_USB2_PLL_480_CTRL_POWER |
|
||||
ANADIG_USB2_PLL_480_CTRL_EN_USB_CLKS,
|
||||
pll_480_ctrl_set);
|
||||
}
|
||||
|
||||
/* Return 0 : host node, <>0 : device mode */
|
||||
static int usb_phy_enable(int index, struct usb_ehci *ehci)
|
||||
{
|
||||
void __iomem *phy_reg;
|
||||
void __iomem *phy_ctrl;
|
||||
void __iomem *usb_cmd;
|
||||
int ret;
|
||||
|
||||
if (index >= ARRAY_SIZE(phy_bases))
|
||||
return 0;
|
||||
|
||||
phy_reg = (void __iomem *)phy_bases[index];
|
||||
phy_ctrl = (void __iomem *)(phy_reg + USBPHY_CTRL);
|
||||
usb_cmd = (void __iomem *)&ehci->usbcmd;
|
||||
|
||||
/* Stop then Reset */
|
||||
clrbits_le32(usb_cmd, UCMD_RUN_STOP);
|
||||
ret = wait_for_bit(__func__, usb_cmd, UCMD_RUN_STOP, false, 10000,
|
||||
false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
setbits_le32(usb_cmd, UCMD_RESET);
|
||||
ret = wait_for_bit(__func__, usb_cmd, UCMD_RESET, false, 10000, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Reset USBPHY module */
|
||||
setbits_le32(phy_ctrl, USBPHY_CTRL_SFTRST);
|
||||
udelay(10);
|
||||
|
||||
/* Remove CLKGATE and SFTRST */
|
||||
clrbits_le32(phy_ctrl, USBPHY_CTRL_CLKGATE | USBPHY_CTRL_SFTRST);
|
||||
udelay(10);
|
||||
|
||||
/* Power up the PHY */
|
||||
writel(0, phy_reg + USBPHY_PWD);
|
||||
/* enable FS/LS device */
|
||||
setbits_le32(phy_ctrl, USBPHY_CTRL_ENUTMILEVEL2 |
|
||||
USBPHY_CTRL_ENUTMILEVEL3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_phy_mode(int port)
|
||||
{
|
||||
void __iomem *phy_reg;
|
||||
void __iomem *phy_ctrl;
|
||||
u32 val;
|
||||
|
||||
phy_reg = (void __iomem *)phy_bases[port];
|
||||
phy_ctrl = (void __iomem *)(phy_reg + USBPHY_CTRL);
|
||||
|
||||
val = readl(phy_ctrl);
|
||||
|
||||
if (val & USBPHY_CTRL_OTG_ID)
|
||||
return USB_INIT_DEVICE;
|
||||
else
|
||||
return USB_INIT_HOST;
|
||||
}
|
||||
|
||||
/* Base address for this IP block is 0x02184800 */
|
||||
struct usbnc_regs {
|
||||
u32 ctrl[4]; /* otg/host1-3 */
|
||||
u32 uh2_hsic_ctrl;
|
||||
u32 uh3_hsic_ctrl;
|
||||
u32 otg_phy_ctrl_0;
|
||||
u32 uh1_phy_ctrl_0;
|
||||
};
|
||||
#elif defined(CONFIG_MX7)
|
||||
struct usbnc_regs {
|
||||
u32 ctrl1;
|
||||
u32 ctrl2;
|
||||
u32 reserve1[10];
|
||||
u32 phy_cfg1;
|
||||
u32 phy_cfg2;
|
||||
u32 reserve2;
|
||||
u32 phy_status;
|
||||
u32 reserve3[4];
|
||||
u32 adp_cfg1;
|
||||
u32 adp_cfg2;
|
||||
u32 adp_status;
|
||||
};
|
||||
|
||||
static void usb_power_config(int index)
|
||||
{
|
||||
struct usbnc_regs *usbnc = (struct usbnc_regs *)(USB_BASE_ADDR +
|
||||
(0x10000 * index) + USBNC_OFFSET);
|
||||
void __iomem *phy_cfg2 = (void __iomem *)(&usbnc->phy_cfg2);
|
||||
|
||||
/*
|
||||
* Clear the ACAENB to enable usb_otg_id detection,
|
||||
* otherwise it is the ACA detection enabled.
|
||||
*/
|
||||
clrbits_le32(phy_cfg2, USBNC_PHYCFG2_ACAENB);
|
||||
}
|
||||
|
||||
int usb_phy_mode(int port)
|
||||
{
|
||||
struct usbnc_regs *usbnc = (struct usbnc_regs *)(USB_BASE_ADDR +
|
||||
(0x10000 * port) + USBNC_OFFSET);
|
||||
void __iomem *status = (void __iomem *)(&usbnc->phy_status);
|
||||
u32 val;
|
||||
|
||||
val = readl(status);
|
||||
|
||||
if (val & USBNC_PHYSTATUS_ID_DIG)
|
||||
return USB_INIT_DEVICE;
|
||||
else
|
||||
return USB_INIT_HOST;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void usb_oc_config(int index)
|
||||
{
|
||||
#if defined(CONFIG_MX6)
|
||||
struct usbnc_regs *usbnc = (struct usbnc_regs *)(USB_BASE_ADDR +
|
||||
USB_OTHERREGS_OFFSET);
|
||||
void __iomem *ctrl = (void __iomem *)(&usbnc->ctrl[index]);
|
||||
#elif defined(CONFIG_MX7)
|
||||
struct usbnc_regs *usbnc = (struct usbnc_regs *)(USB_BASE_ADDR +
|
||||
(0x10000 * index) + USBNC_OFFSET);
|
||||
void __iomem *ctrl = (void __iomem *)(&usbnc->ctrl1);
|
||||
#endif
|
||||
|
||||
#if CONFIG_MACH_TYPE == MACH_TYPE_MX6Q_ARM2
|
||||
/* mx6qarm2 seems to required a different setting*/
|
||||
clrbits_le32(ctrl, UCTRL_OVER_CUR_POL);
|
||||
#else
|
||||
setbits_le32(ctrl, UCTRL_OVER_CUR_POL);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_MX6)
|
||||
setbits_le32(ctrl, UCTRL_OVER_CUR_DIS);
|
||||
#elif defined(CONFIG_MX7)
|
||||
setbits_le32(ctrl, UCTRL_OVER_CUR_DIS | UCTRL_PM);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* board_usb_phy_mode - override usb phy mode
|
||||
* @port: usb host/otg port
|
||||
*
|
||||
* Target board specific, override usb_phy_mode.
|
||||
* When usb-otg is used as usb host port, iomux pad usb_otg_id can be
|
||||
* left disconnected in this case usb_phy_mode will not be able to identify
|
||||
* the phy mode that usb port is used.
|
||||
* Machine file overrides board_usb_phy_mode.
|
||||
*
|
||||
* Return: USB_INIT_DEVICE or USB_INIT_HOST
|
||||
*/
|
||||
int __weak board_usb_phy_mode(int port)
|
||||
{
|
||||
return usb_phy_mode(port);
|
||||
}
|
||||
|
||||
/**
|
||||
* board_ehci_hcd_init - set usb vbus voltage
|
||||
* @port: usb otg port
|
||||
*
|
||||
* Target board specific, setup iomux pad to setup supply vbus voltage
|
||||
* for usb otg port. Machine board file overrides board_ehci_hcd_init
|
||||
*
|
||||
* Return: 0 Success
|
||||
*/
|
||||
int __weak board_ehci_hcd_init(int port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* board_ehci_power - enables/disables usb vbus voltage
|
||||
* @port: usb otg port
|
||||
* @on: on/off vbus voltage
|
||||
*
|
||||
* Enables/disables supply vbus voltage for usb otg port.
|
||||
* Machine board file overrides board_ehci_power
|
||||
*
|
||||
* Return: 0 Success
|
||||
*/
|
||||
int __weak board_ehci_power(int port, int on)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
enum usb_init_type type;
|
||||
#if defined(CONFIG_MX6)
|
||||
u32 controller_spacing = 0x200;
|
||||
#elif defined(CONFIG_MX7)
|
||||
u32 controller_spacing = 0x10000;
|
||||
#endif
|
||||
struct usb_ehci *ehci = (struct usb_ehci *)(USB_BASE_ADDR +
|
||||
(controller_spacing * index));
|
||||
int ret;
|
||||
|
||||
if (index > 3)
|
||||
return -EINVAL;
|
||||
enable_usboh3_clk(1);
|
||||
mdelay(1);
|
||||
|
||||
/* Do board specific initialization */
|
||||
ret = board_ehci_hcd_init(index);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
usb_power_config(index);
|
||||
usb_oc_config(index);
|
||||
|
||||
#if defined(CONFIG_MX6)
|
||||
usb_internal_phy_clock_gate(index, 1);
|
||||
usb_phy_enable(index, ehci);
|
||||
#endif
|
||||
type = board_usb_phy_mode(index);
|
||||
|
||||
*hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength);
|
||||
*hcor = (struct ehci_hcor *)((uint32_t)*hccr +
|
||||
HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
|
||||
|
||||
if ((type == init) || (type == USB_INIT_DEVICE))
|
||||
board_ehci_power(index, (type == USB_INIT_DEVICE) ? 0 : 1);
|
||||
if (type != init)
|
||||
return -ENODEV;
|
||||
if (type == USB_INIT_DEVICE)
|
||||
return 0;
|
||||
|
||||
setbits_le32(&ehci->usbmode, CM_HOST);
|
||||
writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc);
|
||||
setbits_le32(&ehci->portsc, USB_EN);
|
||||
|
||||
mdelay(10);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ehci_hcd_stop(int index)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
250
u-boot/drivers/usb/host/ehci-mxc.c
Normal file
250
u-boot/drivers/usb/host/ehci-mxc.c
Normal file
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
|
||||
#include <common.h>
|
||||
#include <usb.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/imx-regs.h>
|
||||
#include <usb/ehci-ci.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "ehci.h"
|
||||
|
||||
#define USBCTRL_OTGBASE_OFFSET 0x600
|
||||
|
||||
#define MX25_OTG_SIC_SHIFT 29
|
||||
#define MX25_OTG_SIC_MASK (0x3 << MX25_OTG_SIC_SHIFT)
|
||||
#define MX25_OTG_PM_BIT (1 << 24)
|
||||
#define MX25_OTG_PP_BIT (1 << 11)
|
||||
#define MX25_OTG_OCPOL_BIT (1 << 3)
|
||||
|
||||
#define MX25_H1_SIC_SHIFT 21
|
||||
#define MX25_H1_SIC_MASK (0x3 << MX25_H1_SIC_SHIFT)
|
||||
#define MX25_H1_PP_BIT (1 << 18)
|
||||
#define MX25_H1_PM_BIT (1 << 16)
|
||||
#define MX25_H1_IPPUE_UP_BIT (1 << 7)
|
||||
#define MX25_H1_IPPUE_DOWN_BIT (1 << 6)
|
||||
#define MX25_H1_TLL_BIT (1 << 5)
|
||||
#define MX25_H1_USBTE_BIT (1 << 4)
|
||||
#define MX25_H1_OCPOL_BIT (1 << 2)
|
||||
|
||||
#define MX31_OTG_SIC_SHIFT 29
|
||||
#define MX31_OTG_SIC_MASK (0x3 << MX31_OTG_SIC_SHIFT)
|
||||
#define MX31_OTG_PM_BIT (1 << 24)
|
||||
|
||||
#define MX31_H2_SIC_SHIFT 21
|
||||
#define MX31_H2_SIC_MASK (0x3 << MX31_H2_SIC_SHIFT)
|
||||
#define MX31_H2_PM_BIT (1 << 16)
|
||||
#define MX31_H2_DT_BIT (1 << 5)
|
||||
|
||||
#define MX31_H1_SIC_SHIFT 13
|
||||
#define MX31_H1_SIC_MASK (0x3 << MX31_H1_SIC_SHIFT)
|
||||
#define MX31_H1_PM_BIT (1 << 8)
|
||||
#define MX31_H1_DT_BIT (1 << 4)
|
||||
|
||||
#define MX35_OTG_SIC_SHIFT 29
|
||||
#define MX35_OTG_SIC_MASK (0x3 << MX35_OTG_SIC_SHIFT)
|
||||
#define MX35_OTG_PM_BIT (1 << 24)
|
||||
#define MX35_OTG_PP_BIT (1 << 11)
|
||||
#define MX35_OTG_OCPOL_BIT (1 << 3)
|
||||
|
||||
#define MX35_H1_SIC_SHIFT 21
|
||||
#define MX35_H1_SIC_MASK (0x3 << MX35_H1_SIC_SHIFT)
|
||||
#define MX35_H1_PP_BIT (1 << 18)
|
||||
#define MX35_H1_PM_BIT (1 << 16)
|
||||
#define MX35_H1_IPPUE_UP_BIT (1 << 7)
|
||||
#define MX35_H1_IPPUE_DOWN_BIT (1 << 6)
|
||||
#define MX35_H1_TLL_BIT (1 << 5)
|
||||
#define MX35_H1_USBTE_BIT (1 << 4)
|
||||
#define MX35_H1_OCPOL_BIT (1 << 2)
|
||||
|
||||
static int mxc_set_usbcontrol(int port, unsigned int flags)
|
||||
{
|
||||
unsigned int v;
|
||||
|
||||
v = readl(IMX_USB_BASE + USBCTRL_OTGBASE_OFFSET);
|
||||
#if defined(CONFIG_MX25)
|
||||
switch (port) {
|
||||
case 0: /* OTG port */
|
||||
v &= ~(MX25_OTG_SIC_MASK | MX25_OTG_PM_BIT | MX25_OTG_PP_BIT |
|
||||
MX25_OTG_OCPOL_BIT);
|
||||
v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX25_OTG_SIC_SHIFT;
|
||||
|
||||
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
|
||||
v |= MX25_OTG_PM_BIT;
|
||||
|
||||
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
|
||||
v |= MX25_OTG_PP_BIT;
|
||||
|
||||
if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW))
|
||||
v |= MX25_OTG_OCPOL_BIT;
|
||||
|
||||
break;
|
||||
case 1: /* H1 port */
|
||||
v &= ~(MX25_H1_SIC_MASK | MX25_H1_PM_BIT | MX25_H1_PP_BIT |
|
||||
MX25_H1_OCPOL_BIT | MX25_H1_TLL_BIT |
|
||||
MX25_H1_USBTE_BIT | MX25_H1_IPPUE_DOWN_BIT |
|
||||
MX25_H1_IPPUE_UP_BIT);
|
||||
v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX25_H1_SIC_SHIFT;
|
||||
|
||||
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
|
||||
v |= MX25_H1_PM_BIT;
|
||||
|
||||
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
|
||||
v |= MX25_H1_PP_BIT;
|
||||
|
||||
if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW))
|
||||
v |= MX25_H1_OCPOL_BIT;
|
||||
|
||||
if (!(flags & MXC_EHCI_TTL_ENABLED))
|
||||
v |= MX25_H1_TLL_BIT;
|
||||
|
||||
if (flags & MXC_EHCI_INTERNAL_PHY)
|
||||
v |= MX25_H1_USBTE_BIT;
|
||||
|
||||
if (flags & MXC_EHCI_IPPUE_DOWN)
|
||||
v |= MX25_H1_IPPUE_DOWN_BIT;
|
||||
|
||||
if (flags & MXC_EHCI_IPPUE_UP)
|
||||
v |= MX25_H1_IPPUE_UP_BIT;
|
||||
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
#elif defined(CONFIG_MX31)
|
||||
switch (port) {
|
||||
case 0: /* OTG port */
|
||||
v &= ~(MX31_OTG_SIC_MASK | MX31_OTG_PM_BIT);
|
||||
v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX31_OTG_SIC_SHIFT;
|
||||
|
||||
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
|
||||
v |= MX31_OTG_PM_BIT;
|
||||
|
||||
break;
|
||||
case 1: /* H1 port */
|
||||
v &= ~(MX31_H1_SIC_MASK | MX31_H1_PM_BIT | MX31_H1_DT_BIT);
|
||||
v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX31_H1_SIC_SHIFT;
|
||||
|
||||
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
|
||||
v |= MX31_H1_PM_BIT;
|
||||
|
||||
if (!(flags & MXC_EHCI_TTL_ENABLED))
|
||||
v |= MX31_H1_DT_BIT;
|
||||
|
||||
break;
|
||||
case 2: /* H2 port */
|
||||
v &= ~(MX31_H2_SIC_MASK | MX31_H2_PM_BIT | MX31_H2_DT_BIT);
|
||||
v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX31_H2_SIC_SHIFT;
|
||||
|
||||
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
|
||||
v |= MX31_H2_PM_BIT;
|
||||
|
||||
if (!(flags & MXC_EHCI_TTL_ENABLED))
|
||||
v |= MX31_H2_DT_BIT;
|
||||
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
#elif defined(CONFIG_MX35)
|
||||
switch (port) {
|
||||
case 0: /* OTG port */
|
||||
v &= ~(MX35_OTG_SIC_MASK | MX35_OTG_PM_BIT | MX35_OTG_PP_BIT |
|
||||
MX35_OTG_OCPOL_BIT);
|
||||
v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX35_OTG_SIC_SHIFT;
|
||||
|
||||
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
|
||||
v |= MX35_OTG_PM_BIT;
|
||||
|
||||
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
|
||||
v |= MX35_OTG_PP_BIT;
|
||||
|
||||
if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW))
|
||||
v |= MX35_OTG_OCPOL_BIT;
|
||||
|
||||
break;
|
||||
case 1: /* H1 port */
|
||||
v &= ~(MX35_H1_SIC_MASK | MX35_H1_PM_BIT | MX35_H1_PP_BIT |
|
||||
MX35_H1_OCPOL_BIT | MX35_H1_TLL_BIT |
|
||||
MX35_H1_USBTE_BIT | MX35_H1_IPPUE_DOWN_BIT |
|
||||
MX35_H1_IPPUE_UP_BIT);
|
||||
v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX35_H1_SIC_SHIFT;
|
||||
|
||||
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
|
||||
v |= MX35_H1_PM_BIT;
|
||||
|
||||
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
|
||||
v |= MX35_H1_PP_BIT;
|
||||
|
||||
if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW))
|
||||
v |= MX35_H1_OCPOL_BIT;
|
||||
|
||||
if (!(flags & MXC_EHCI_TTL_ENABLED))
|
||||
v |= MX35_H1_TLL_BIT;
|
||||
|
||||
if (flags & MXC_EHCI_INTERNAL_PHY)
|
||||
v |= MX35_H1_USBTE_BIT;
|
||||
|
||||
if (flags & MXC_EHCI_IPPUE_DOWN)
|
||||
v |= MX35_H1_IPPUE_DOWN_BIT;
|
||||
|
||||
if (flags & MXC_EHCI_IPPUE_UP)
|
||||
v |= MX35_H1_IPPUE_UP_BIT;
|
||||
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
#else
|
||||
#error MXC EHCI USB driver not supported on this platform
|
||||
#endif
|
||||
writel(v, IMX_USB_BASE + USBCTRL_OTGBASE_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
struct usb_ehci *ehci;
|
||||
#ifdef CONFIG_MX31
|
||||
struct clock_control_regs *sc_regs =
|
||||
(struct clock_control_regs *)CCM_BASE;
|
||||
|
||||
__raw_readl(&sc_regs->ccmr);
|
||||
__raw_writel(__raw_readl(&sc_regs->ccmr) | (1 << 9), &sc_regs->ccmr) ;
|
||||
#endif
|
||||
|
||||
udelay(80);
|
||||
|
||||
ehci = (struct usb_ehci *)(IMX_USB_BASE +
|
||||
IMX_USB_PORT_OFFSET * CONFIG_MXC_USB_PORT);
|
||||
*hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength);
|
||||
*hcor = (struct ehci_hcor *)((uint32_t) *hccr +
|
||||
HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
|
||||
setbits_le32(&ehci->usbmode, CM_HOST);
|
||||
__raw_writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc);
|
||||
mxc_set_usbcontrol(CONFIG_MXC_USB_PORT, CONFIG_MXC_USB_FLAGS);
|
||||
#ifdef CONFIG_MX35
|
||||
/* Workaround for ENGcm11601 */
|
||||
__raw_writel(0, &ehci->sbuscfg);
|
||||
#endif
|
||||
|
||||
udelay(10000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy the appropriate control structures corresponding
|
||||
* the the EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_stop(int index)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
174
u-boot/drivers/usb/host/ehci-mxs.c
Normal file
174
u-boot/drivers/usb/host/ehci-mxs.c
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Freescale i.MX28 USB Host driver
|
||||
*
|
||||
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
|
||||
* on behalf of DENX Software Engineering GmbH
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/imx-regs.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "ehci.h"
|
||||
|
||||
/* This DIGCTL register ungates clock to USB */
|
||||
#define HW_DIGCTL_CTRL 0x8001c000
|
||||
#define HW_DIGCTL_CTRL_USB0_CLKGATE (1 << 2)
|
||||
#define HW_DIGCTL_CTRL_USB1_CLKGATE (1 << 16)
|
||||
|
||||
struct ehci_mxs_port {
|
||||
uint32_t usb_regs;
|
||||
struct mxs_usbphy_regs *phy_regs;
|
||||
|
||||
struct mxs_register_32 *pll;
|
||||
uint32_t pll_en_bits;
|
||||
uint32_t pll_dis_bits;
|
||||
uint32_t gate_bits;
|
||||
};
|
||||
|
||||
static const struct ehci_mxs_port mxs_port[] = {
|
||||
#ifdef CONFIG_EHCI_MXS_PORT0
|
||||
{
|
||||
MXS_USBCTRL0_BASE,
|
||||
(struct mxs_usbphy_regs *)MXS_USBPHY0_BASE,
|
||||
(struct mxs_register_32 *)(MXS_CLKCTRL_BASE +
|
||||
offsetof(struct mxs_clkctrl_regs,
|
||||
hw_clkctrl_pll0ctrl0_reg)),
|
||||
CLKCTRL_PLL0CTRL0_EN_USB_CLKS | CLKCTRL_PLL0CTRL0_POWER,
|
||||
CLKCTRL_PLL0CTRL0_EN_USB_CLKS,
|
||||
HW_DIGCTL_CTRL_USB0_CLKGATE,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_EHCI_MXS_PORT1
|
||||
{
|
||||
MXS_USBCTRL1_BASE,
|
||||
(struct mxs_usbphy_regs *)MXS_USBPHY1_BASE,
|
||||
(struct mxs_register_32 *)(MXS_CLKCTRL_BASE +
|
||||
offsetof(struct mxs_clkctrl_regs,
|
||||
hw_clkctrl_pll1ctrl0_reg)),
|
||||
CLKCTRL_PLL1CTRL0_EN_USB_CLKS | CLKCTRL_PLL1CTRL0_POWER,
|
||||
CLKCTRL_PLL1CTRL0_EN_USB_CLKS,
|
||||
HW_DIGCTL_CTRL_USB1_CLKGATE,
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
static int ehci_mxs_toggle_clock(const struct ehci_mxs_port *port, int enable)
|
||||
{
|
||||
struct mxs_register_32 *digctl_ctrl =
|
||||
(struct mxs_register_32 *)HW_DIGCTL_CTRL;
|
||||
int pll_offset, dig_offset;
|
||||
|
||||
if (enable) {
|
||||
pll_offset = offsetof(struct mxs_register_32, reg_set);
|
||||
dig_offset = offsetof(struct mxs_register_32, reg_clr);
|
||||
writel(port->gate_bits, (u32)&digctl_ctrl->reg + dig_offset);
|
||||
writel(port->pll_en_bits, (u32)port->pll + pll_offset);
|
||||
} else {
|
||||
pll_offset = offsetof(struct mxs_register_32, reg_clr);
|
||||
dig_offset = offsetof(struct mxs_register_32, reg_set);
|
||||
writel(port->pll_dis_bits, (u32)port->pll + pll_offset);
|
||||
writel(port->gate_bits, (u32)&digctl_ctrl->reg + dig_offset);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __weak board_ehci_hcd_init(int port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __weak board_ehci_hcd_exit(int port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
|
||||
int ret;
|
||||
uint32_t usb_base, cap_base;
|
||||
const struct ehci_mxs_port *port;
|
||||
|
||||
if ((index < 0) || (index >= ARRAY_SIZE(mxs_port))) {
|
||||
printf("Invalid port index (index = %d)!\n", index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = board_ehci_hcd_init(index);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
port = &mxs_port[index];
|
||||
|
||||
/* Reset the PHY block */
|
||||
writel(USBPHY_CTRL_SFTRST, &port->phy_regs->hw_usbphy_ctrl_set);
|
||||
udelay(10);
|
||||
writel(USBPHY_CTRL_SFTRST | USBPHY_CTRL_CLKGATE,
|
||||
&port->phy_regs->hw_usbphy_ctrl_clr);
|
||||
|
||||
/* Enable USB clock */
|
||||
ret = ehci_mxs_toggle_clock(port, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Start USB PHY */
|
||||
writel(0, &port->phy_regs->hw_usbphy_pwd);
|
||||
|
||||
/* Enable UTMI+ Level 2 and Level 3 compatibility */
|
||||
writel(USBPHY_CTRL_ENUTMILEVEL3 | USBPHY_CTRL_ENUTMILEVEL2 | 1,
|
||||
&port->phy_regs->hw_usbphy_ctrl_set);
|
||||
|
||||
usb_base = port->usb_regs + 0x100;
|
||||
*hccr = (struct ehci_hccr *)usb_base;
|
||||
|
||||
cap_base = ehci_readl(&(*hccr)->cr_capbase);
|
||||
*hcor = (struct ehci_hcor *)(usb_base + HC_LENGTH(cap_base));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ehci_hcd_stop(int index)
|
||||
{
|
||||
int ret;
|
||||
uint32_t usb_base, cap_base, tmp;
|
||||
struct ehci_hccr *hccr;
|
||||
struct ehci_hcor *hcor;
|
||||
const struct ehci_mxs_port *port;
|
||||
|
||||
if ((index < 0) || (index >= ARRAY_SIZE(mxs_port))) {
|
||||
printf("Invalid port index (index = %d)!\n", index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
port = &mxs_port[index];
|
||||
|
||||
/* Stop the USB port */
|
||||
usb_base = port->usb_regs + 0x100;
|
||||
hccr = (struct ehci_hccr *)usb_base;
|
||||
cap_base = ehci_readl(&hccr->cr_capbase);
|
||||
hcor = (struct ehci_hcor *)(usb_base + HC_LENGTH(cap_base));
|
||||
|
||||
tmp = ehci_readl(&hcor->or_usbcmd);
|
||||
tmp &= ~CMD_RUN;
|
||||
ehci_writel(tmp, &hcor->or_usbcmd);
|
||||
|
||||
/* Disable the PHY */
|
||||
tmp = USBPHY_PWD_RXPWDRX | USBPHY_PWD_RXPWDDIFF |
|
||||
USBPHY_PWD_RXPWD1PT1 | USBPHY_PWD_RXPWDENV |
|
||||
USBPHY_PWD_TXPWDV2I | USBPHY_PWD_TXPWDIBIAS |
|
||||
USBPHY_PWD_TXPWDFS;
|
||||
writel(tmp, &port->phy_regs->hw_usbphy_pwd);
|
||||
|
||||
/* Disable USB clock */
|
||||
ret = ehci_mxs_toggle_clock(port, 0);
|
||||
|
||||
board_ehci_hcd_exit(index);
|
||||
|
||||
return ret;
|
||||
}
|
||||
295
u-boot/drivers/usb/host/ehci-omap.c
Normal file
295
u-boot/drivers/usb/host/ehci-omap.c
Normal file
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* (C) Copyright 2011 Ilya Yanok, Emcraft Systems
|
||||
* (C) Copyright 2004-2008
|
||||
* Texas Instruments, <www.ti.com>
|
||||
*
|
||||
* Derived from Beagle Board code by
|
||||
* Sunil Kumar <sunilsaini05@gmail.com>
|
||||
* Shashi Ranjan <shashiranjanmca05@gmail.com>
|
||||
*
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <usb.h>
|
||||
#include <usb/ulpi.h>
|
||||
#include <errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <asm/arch/ehci.h>
|
||||
#include <asm/ehci-omap.h>
|
||||
|
||||
#include "ehci.h"
|
||||
|
||||
static struct omap_uhh *const uhh = (struct omap_uhh *)OMAP_UHH_BASE;
|
||||
static struct omap_usbtll *const usbtll = (struct omap_usbtll *)OMAP_USBTLL_BASE;
|
||||
static struct omap_ehci *const ehci = (struct omap_ehci *)OMAP_EHCI_BASE;
|
||||
|
||||
static int omap_uhh_reset(void)
|
||||
{
|
||||
int timeout = 0;
|
||||
u32 rev;
|
||||
|
||||
rev = readl(&uhh->rev);
|
||||
|
||||
/* Soft RESET */
|
||||
writel(OMAP_UHH_SYSCONFIG_SOFTRESET, &uhh->sysc);
|
||||
|
||||
switch (rev) {
|
||||
case OMAP_USBHS_REV1:
|
||||
/* Wait for soft RESET to complete */
|
||||
while (!(readl(&uhh->syss) & 0x1)) {
|
||||
if (timeout > 100) {
|
||||
printf("%s: RESET timeout\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
udelay(10);
|
||||
timeout++;
|
||||
}
|
||||
|
||||
/* Set No-Idle, No-Standby */
|
||||
writel(OMAP_UHH_SYSCONFIG_VAL, &uhh->sysc);
|
||||
break;
|
||||
|
||||
default: /* Rev. 2 onwards */
|
||||
|
||||
udelay(2); /* Need to wait before accessing SYSCONFIG back */
|
||||
|
||||
/* Wait for soft RESET to complete */
|
||||
while ((readl(&uhh->sysc) & 0x1)) {
|
||||
if (timeout > 100) {
|
||||
printf("%s: RESET timeout\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
udelay(10);
|
||||
timeout++;
|
||||
}
|
||||
|
||||
writel(OMAP_UHH_SYSCONFIG_VAL, &uhh->sysc);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_ehci_tll_reset(void)
|
||||
{
|
||||
unsigned long init = get_timer(0);
|
||||
|
||||
/* perform TLL soft reset, and wait until reset is complete */
|
||||
writel(OMAP_USBTLL_SYSCONFIG_SOFTRESET, &usbtll->sysc);
|
||||
|
||||
/* Wait for TLL reset to complete */
|
||||
while (!(readl(&usbtll->syss) & OMAP_USBTLL_SYSSTATUS_RESETDONE))
|
||||
if (get_timer(init) > CONFIG_SYS_HZ) {
|
||||
debug("OMAP EHCI error: timeout resetting TLL\n");
|
||||
return -EL3RST;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void omap_usbhs_hsic_init(int port)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* Enable channels now */
|
||||
reg = readl(&usbtll->channel_conf + port);
|
||||
|
||||
setbits_le32(®, (OMAP_TLL_CHANNEL_CONF_CHANMODE_TRANSPARENT_UTMI
|
||||
| OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF
|
||||
| OMAP_TLL_CHANNEL_CONF_DRVVBUS
|
||||
| OMAP_TLL_CHANNEL_CONF_CHRGVBUS
|
||||
| OMAP_TLL_CHANNEL_CONF_CHANEN));
|
||||
|
||||
writel(reg, &usbtll->channel_conf + port);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_ULPI
|
||||
static void omap_ehci_soft_phy_reset(int port)
|
||||
{
|
||||
struct ulpi_viewport ulpi_vp;
|
||||
|
||||
ulpi_vp.viewport_addr = (u32)&ehci->insreg05_utmi_ulpi;
|
||||
ulpi_vp.port_num = port;
|
||||
|
||||
ulpi_reset(&ulpi_vp);
|
||||
}
|
||||
#else
|
||||
static void omap_ehci_soft_phy_reset(int port)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_OMAP_EHCI_PHY1_RESET_GPIO) || \
|
||||
defined(CONFIG_OMAP_EHCI_PHY2_RESET_GPIO) || \
|
||||
defined(CONFIG_OMAP_EHCI_PHY3_RESET_GPIO)
|
||||
/* controls PHY(s) reset signal(s) */
|
||||
static inline void omap_ehci_phy_reset(int on, int delay)
|
||||
{
|
||||
/*
|
||||
* Refer ISSUE1:
|
||||
* Hold the PHY in RESET for enough time till
|
||||
* PHY is settled and ready
|
||||
*/
|
||||
if (delay && !on)
|
||||
udelay(delay);
|
||||
#ifdef CONFIG_OMAP_EHCI_PHY1_RESET_GPIO
|
||||
gpio_request(CONFIG_OMAP_EHCI_PHY1_RESET_GPIO, "USB PHY1 reset");
|
||||
gpio_direction_output(CONFIG_OMAP_EHCI_PHY1_RESET_GPIO, !on);
|
||||
#endif
|
||||
#ifdef CONFIG_OMAP_EHCI_PHY2_RESET_GPIO
|
||||
gpio_request(CONFIG_OMAP_EHCI_PHY2_RESET_GPIO, "USB PHY2 reset");
|
||||
gpio_direction_output(CONFIG_OMAP_EHCI_PHY2_RESET_GPIO, !on);
|
||||
#endif
|
||||
#ifdef CONFIG_OMAP_EHCI_PHY3_RESET_GPIO
|
||||
gpio_request(CONFIG_OMAP_EHCI_PHY3_RESET_GPIO, "USB PHY3 reset");
|
||||
gpio_direction_output(CONFIG_OMAP_EHCI_PHY3_RESET_GPIO, !on);
|
||||
#endif
|
||||
|
||||
/* Hold the PHY in RESET for enough time till DIR is high */
|
||||
/* Refer: ISSUE1 */
|
||||
if (delay && on)
|
||||
udelay(delay);
|
||||
}
|
||||
#else
|
||||
#define omap_ehci_phy_reset(on, delay) do {} while (0)
|
||||
#endif
|
||||
|
||||
/* Reset is needed otherwise the kernel-driver will throw an error. */
|
||||
int omap_ehci_hcd_stop(void)
|
||||
{
|
||||
debug("Resetting OMAP EHCI\n");
|
||||
omap_ehci_phy_reset(1, 0);
|
||||
|
||||
if (omap_uhh_reset() < 0)
|
||||
return -1;
|
||||
|
||||
if (omap_ehci_tll_reset() < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the OMAP EHCI controller and PHY.
|
||||
* Based on "drivers/usb/host/ehci-omap.c" from Linux 3.1
|
||||
* See there for additional Copyrights.
|
||||
*/
|
||||
int omap_ehci_hcd_init(int index, struct omap_usbhs_board_data *usbhs_pdata,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
int ret;
|
||||
unsigned int i, reg = 0, rev = 0;
|
||||
|
||||
debug("Initializing OMAP EHCI\n");
|
||||
|
||||
ret = board_usb_init(index, USB_INIT_HOST);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Put the PHY in RESET */
|
||||
omap_ehci_phy_reset(1, 10);
|
||||
|
||||
ret = omap_uhh_reset();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = omap_ehci_tll_reset();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(OMAP_USBTLL_SYSCONFIG_ENAWAKEUP |
|
||||
OMAP_USBTLL_SYSCONFIG_SIDLEMODE |
|
||||
OMAP_USBTLL_SYSCONFIG_CACTIVITY, &usbtll->sysc);
|
||||
|
||||
/* Put UHH in NoIdle/NoStandby mode */
|
||||
writel(OMAP_UHH_SYSCONFIG_VAL, &uhh->sysc);
|
||||
|
||||
/* setup ULPI bypass and burst configurations */
|
||||
clrsetbits_le32(®, OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN,
|
||||
(OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN |
|
||||
OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN |
|
||||
OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN));
|
||||
|
||||
rev = readl(&uhh->rev);
|
||||
if (rev == OMAP_USBHS_REV1) {
|
||||
if (is_ehci_phy_mode(usbhs_pdata->port_mode[0]))
|
||||
clrbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS);
|
||||
else
|
||||
setbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS);
|
||||
|
||||
if (is_ehci_phy_mode(usbhs_pdata->port_mode[1]))
|
||||
clrbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS);
|
||||
else
|
||||
setbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS);
|
||||
|
||||
if (is_ehci_phy_mode(usbhs_pdata->port_mode[2]))
|
||||
clrbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS);
|
||||
else
|
||||
setbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS);
|
||||
} else if (rev == OMAP_USBHS_REV2) {
|
||||
|
||||
clrsetbits_le32(®, (OMAP_P1_MODE_CLEAR | OMAP_P2_MODE_CLEAR),
|
||||
OMAP4_UHH_HOSTCONFIG_APP_START_CLK);
|
||||
|
||||
/* Clear port mode fields for PHY mode */
|
||||
|
||||
if (is_ehci_hsic_mode(usbhs_pdata->port_mode[0]))
|
||||
setbits_le32(®, OMAP_P1_MODE_HSIC);
|
||||
|
||||
if (is_ehci_hsic_mode(usbhs_pdata->port_mode[1]))
|
||||
setbits_le32(®, OMAP_P2_MODE_HSIC);
|
||||
|
||||
} else if (rev == OMAP_USBHS_REV2_1) {
|
||||
|
||||
clrsetbits_le32(®,
|
||||
(OMAP_P1_MODE_CLEAR |
|
||||
OMAP_P2_MODE_CLEAR |
|
||||
OMAP_P3_MODE_CLEAR),
|
||||
OMAP4_UHH_HOSTCONFIG_APP_START_CLK);
|
||||
|
||||
/* Clear port mode fields for PHY mode */
|
||||
|
||||
if (is_ehci_hsic_mode(usbhs_pdata->port_mode[0]))
|
||||
setbits_le32(®, OMAP_P1_MODE_HSIC);
|
||||
|
||||
if (is_ehci_hsic_mode(usbhs_pdata->port_mode[1]))
|
||||
setbits_le32(®, OMAP_P2_MODE_HSIC);
|
||||
|
||||
if (is_ehci_hsic_mode(usbhs_pdata->port_mode[2]))
|
||||
setbits_le32(®, OMAP_P3_MODE_HSIC);
|
||||
}
|
||||
|
||||
debug("OMAP UHH_REVISION 0x%x\n", rev);
|
||||
writel(reg, &uhh->hostconfig);
|
||||
|
||||
for (i = 0; i < OMAP_HS_USB_PORTS; i++)
|
||||
if (is_ehci_hsic_mode(usbhs_pdata->port_mode[i]))
|
||||
omap_usbhs_hsic_init(i);
|
||||
|
||||
omap_ehci_phy_reset(0, 10);
|
||||
|
||||
/*
|
||||
* An undocumented "feature" in the OMAP3 EHCI controller,
|
||||
* causes suspended ports to be taken out of suspend when
|
||||
* the USBCMD.Run/Stop bit is cleared (for example when
|
||||
* we do ehci_bus_suspend).
|
||||
* This breaks suspend-resume if the root-hub is allowed
|
||||
* to suspend. Writing 1 to this undocumented register bit
|
||||
* disables this feature and restores normal behavior.
|
||||
*/
|
||||
writel(EHCI_INSNREG04_DISABLE_UNSUSPEND, &ehci->insreg04);
|
||||
|
||||
for (i = 0; i < OMAP_HS_USB_PORTS; i++)
|
||||
if (is_ehci_phy_mode(usbhs_pdata->port_mode[i]))
|
||||
omap_ehci_soft_phy_reset(i);
|
||||
|
||||
*hccr = (struct ehci_hccr *)(OMAP_EHCI_BASE);
|
||||
*hcor = (struct ehci_hcor *)(OMAP_EHCI_BASE + 0x10);
|
||||
|
||||
debug("OMAP EHCI init done\n");
|
||||
return 0;
|
||||
}
|
||||
164
u-boot/drivers/usb/host/ehci-pci.c
Normal file
164
u-boot/drivers/usb/host/ehci-pci.c
Normal file
@@ -0,0 +1,164 @@
|
||||
/*-
|
||||
* Copyright (c) 2007-2008, Juniper Networks, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <pci.h>
|
||||
#include <usb.h>
|
||||
|
||||
#include "ehci.h"
|
||||
|
||||
/* Information about a USB port */
|
||||
struct ehci_pci_priv {
|
||||
struct ehci_ctrl ehci;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DM_USB
|
||||
|
||||
static void ehci_pci_init(struct udevice *dev, struct ehci_hccr **ret_hccr,
|
||||
struct ehci_hcor **ret_hcor)
|
||||
{
|
||||
struct ehci_hccr *hccr;
|
||||
struct ehci_hcor *hcor;
|
||||
u32 cmd;
|
||||
|
||||
hccr = (struct ehci_hccr *)dm_pci_map_bar(dev,
|
||||
PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
|
||||
hcor = (struct ehci_hcor *)((uintptr_t) hccr +
|
||||
HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
|
||||
|
||||
debug("EHCI-PCI init hccr 0x%x and hcor 0x%x hc_length %d\n",
|
||||
(u32)hccr, (u32)hcor,
|
||||
(u32)HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
|
||||
|
||||
*ret_hccr = hccr;
|
||||
*ret_hcor = hcor;
|
||||
|
||||
/* enable busmaster */
|
||||
dm_pci_read_config32(dev, PCI_COMMAND, &cmd);
|
||||
cmd |= PCI_COMMAND_MASTER;
|
||||
dm_pci_write_config32(dev, PCI_COMMAND, cmd);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#ifdef CONFIG_PCI_EHCI_DEVICE
|
||||
static struct pci_device_id ehci_pci_ids[] = {
|
||||
/* Please add supported PCI EHCI controller ids here */
|
||||
{0x1033, 0x00E0}, /* NEC */
|
||||
{0x10B9, 0x5239}, /* ULI1575 PCI EHCI module ids */
|
||||
{0x12D8, 0x400F}, /* Pericom */
|
||||
{0, 0}
|
||||
};
|
||||
#endif
|
||||
|
||||
static void ehci_pci_legacy_init(pci_dev_t pdev, struct ehci_hccr **ret_hccr,
|
||||
struct ehci_hcor **ret_hcor)
|
||||
{
|
||||
struct ehci_hccr *hccr;
|
||||
struct ehci_hcor *hcor;
|
||||
u32 cmd;
|
||||
|
||||
hccr = (struct ehci_hccr *)pci_map_bar(pdev,
|
||||
PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
|
||||
hcor = (struct ehci_hcor *)((uintptr_t) hccr +
|
||||
HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
|
||||
|
||||
debug("EHCI-PCI init hccr 0x%x and hcor 0x%x hc_length %d\n",
|
||||
(u32)hccr, (u32)hcor,
|
||||
(u32)HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
|
||||
|
||||
*ret_hccr = hccr;
|
||||
*ret_hcor = hcor;
|
||||
|
||||
/* enable busmaster */
|
||||
pci_read_config_dword(pdev, PCI_COMMAND, &cmd);
|
||||
cmd |= PCI_COMMAND_MASTER;
|
||||
pci_write_config_dword(pdev, PCI_COMMAND, cmd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the appropriate control structures to manage
|
||||
* a new EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **ret_hccr, struct ehci_hcor **ret_hcor)
|
||||
{
|
||||
pci_dev_t pdev;
|
||||
|
||||
#ifdef CONFIG_PCI_EHCI_DEVICE
|
||||
pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVICE);
|
||||
#else
|
||||
pdev = pci_find_class(PCI_CLASS_SERIAL_USB_EHCI, index);
|
||||
#endif
|
||||
if (pdev < 0) {
|
||||
printf("EHCI host controller not found\n");
|
||||
return -1;
|
||||
}
|
||||
ehci_pci_legacy_init(pdev, ret_hccr, ret_hcor);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy the appropriate control structures corresponding
|
||||
* the the EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_stop(int index)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* nCONFIG_DM_USB */
|
||||
|
||||
#ifdef CONFIG_DM_USB
|
||||
static int ehci_pci_probe(struct udevice *dev)
|
||||
{
|
||||
struct ehci_hccr *hccr;
|
||||
struct ehci_hcor *hcor;
|
||||
|
||||
ehci_pci_init(dev, &hccr, &hcor);
|
||||
|
||||
return ehci_register(dev, hccr, hcor, NULL, 0, USB_INIT_HOST);
|
||||
}
|
||||
|
||||
static int ehci_pci_remove(struct udevice *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ehci_deregister(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id ehci_pci_ids[] = {
|
||||
{ .compatible = "ehci-pci" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(ehci_pci) = {
|
||||
.name = "ehci_pci",
|
||||
.id = UCLASS_USB,
|
||||
.probe = ehci_pci_probe,
|
||||
.remove = ehci_pci_remove,
|
||||
.of_match = ehci_pci_ids,
|
||||
.ops = &ehci_usb_ops,
|
||||
.platdata_auto_alloc_size = sizeof(struct usb_platdata),
|
||||
.priv_auto_alloc_size = sizeof(struct ehci_pci_priv),
|
||||
.flags = DM_FLAG_ALLOC_PRIV_DMA,
|
||||
};
|
||||
|
||||
static struct pci_device_id ehci_pci_supported[] = {
|
||||
{ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_EHCI, ~0) },
|
||||
{},
|
||||
};
|
||||
|
||||
U_BOOT_PCI_DEVICE(ehci_pci, ehci_pci_supported);
|
||||
|
||||
#endif /* CONFIG_DM_USB */
|
||||
34
u-boot/drivers/usb/host/ehci-ppc4xx.c
Normal file
34
u-boot/drivers/usb/host/ehci-ppc4xx.c
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* (C) Copyright 2010, Chris Zhang <chris@seamicro.com>
|
||||
*
|
||||
* Author: Chris Zhang <chris@seamicro.com>
|
||||
* This code is based on ehci freescale driver
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <usb.h>
|
||||
|
||||
#include "ehci.h"
|
||||
|
||||
/*
|
||||
* Create the appropriate control structures to manage
|
||||
* a new EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
*hccr = (struct ehci_hccr *)(CONFIG_SYS_PPC4XX_USB_ADDR);
|
||||
*hcor = (struct ehci_hcor *)((uint32_t) *hccr +
|
||||
HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy the appropriate control structures corresponding
|
||||
* the the EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_stop(int index)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
129
u-boot/drivers/usb/host/ehci-rmobile.c
Normal file
129
u-boot/drivers/usb/host/ehci-rmobile.c
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* EHCI HCD (Host Controller Driver) for USB.
|
||||
*
|
||||
* Copyright (C) 2013,2014 Renesas Electronics Corporation
|
||||
* Copyright (C) 2014 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/ehci-rmobile.h>
|
||||
#include "ehci.h"
|
||||
|
||||
#if defined(CONFIG_R8A7740)
|
||||
static u32 usb_base_address[] = {
|
||||
0xC6700000
|
||||
};
|
||||
#elif defined(CONFIG_R8A7790)
|
||||
static u32 usb_base_address[] = {
|
||||
0xEE080000, /* USB0 (EHCI) */
|
||||
0xEE0A0000, /* USB1 */
|
||||
0xEE0C0000, /* USB2 */
|
||||
};
|
||||
#elif defined(CONFIG_R8A7791) || defined(CONFIG_R8A7793) || \
|
||||
defined(CONFIG_R8A7794)
|
||||
static u32 usb_base_address[] = {
|
||||
0xEE080000, /* USB0 (EHCI) */
|
||||
0xEE0C0000, /* USB1 */
|
||||
};
|
||||
#else
|
||||
#error rmobile EHCI USB driver not supported on this platform
|
||||
#endif
|
||||
|
||||
int ehci_hcd_stop(int index)
|
||||
{
|
||||
int i;
|
||||
u32 base;
|
||||
struct ahbcom_pci_bridge *ahbcom_pci;
|
||||
|
||||
base = usb_base_address[index];
|
||||
ahbcom_pci = (struct ahbcom_pci_bridge *)(base + AHBPCI_OFFSET);
|
||||
writel(0, &ahbcom_pci->ahb_bus_ctr);
|
||||
|
||||
/* reset ehci */
|
||||
setbits_le32(base + EHCI_USBCMD, CMD_RESET);
|
||||
for (i = 100; i > 0; i--) {
|
||||
if (!(readl(base + EHCI_USBCMD) & CMD_RESET))
|
||||
break;
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
if (!i)
|
||||
printf("error : ehci(%d) reset failed.\n", index);
|
||||
|
||||
if (index == (ARRAY_SIZE(usb_base_address) - 1))
|
||||
setbits_le32(SMSTPCR7, SMSTPCR703);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
u32 base;
|
||||
u32 phys_base;
|
||||
struct rmobile_ehci_reg *rehci;
|
||||
struct ahbcom_pci_bridge *ahbcom_pci;
|
||||
struct ahbconf_pci_bridge *ahbconf_pci;
|
||||
struct ahb_pciconf *ahb_pciconf_ohci;
|
||||
struct ahb_pciconf *ahb_pciconf_ehci;
|
||||
uint32_t cap_base;
|
||||
|
||||
base = usb_base_address[index];
|
||||
phys_base = base;
|
||||
if (index == 0)
|
||||
clrbits_le32(SMSTPCR7, SMSTPCR703);
|
||||
|
||||
rehci = (struct rmobile_ehci_reg *)(base + EHCI_OFFSET);
|
||||
ahbcom_pci = (struct ahbcom_pci_bridge *)(base + AHBPCI_OFFSET);
|
||||
ahbconf_pci =
|
||||
(struct ahbconf_pci_bridge *)(base + PCI_CONF_AHBPCI_OFFSET);
|
||||
ahb_pciconf_ohci = (struct ahb_pciconf *)(base + PCI_CONF_OHCI_OFFSET);
|
||||
ahb_pciconf_ehci = (struct ahb_pciconf *)(base + PCI_CONF_EHCI_OFFSET);
|
||||
|
||||
/* Clock & Reset & Direct Power Down */
|
||||
clrsetbits_le32(&ahbcom_pci->usbctr,
|
||||
(DIRPD | PCICLK_MASK | USBH_RST), USBCTR_WIN_SIZE_1GB);
|
||||
clrbits_le32(&ahbcom_pci->usbctr, PLL_RST);
|
||||
|
||||
/* AHB-PCI Bridge Communication Registers */
|
||||
writel(AHB_BUS_CTR_INIT, &ahbcom_pci->ahb_bus_ctr);
|
||||
writel((CONFIG_SYS_SDRAM_BASE & 0xf0000000) | PCIAHB_WIN_PREFETCH,
|
||||
&ahbcom_pci->pciahb_win1_ctr);
|
||||
writel(0xf0000000 | PCIAHB_WIN_PREFETCH,
|
||||
&ahbcom_pci->pciahb_win2_ctr);
|
||||
writel(phys_base | PCIWIN2_PCICMD, &ahbcom_pci->ahbpci_win2_ctr);
|
||||
|
||||
setbits_le32(&ahbcom_pci->pci_arbiter_ctr,
|
||||
PCIBP_MODE | PCIREQ1 | PCIREQ0);
|
||||
|
||||
/* PCI Configuration Registers for AHBPCI */
|
||||
writel(PCIWIN1_PCICMD | AHB_CFG_AHBPCI,
|
||||
&ahbcom_pci->ahbpci_win1_ctr);
|
||||
writel(phys_base + AHBPCI_OFFSET, &ahbconf_pci->basead);
|
||||
writel(CONFIG_SYS_SDRAM_BASE & 0xf0000000, &ahbconf_pci->win1_basead);
|
||||
writel(0xf0000000, &ahbconf_pci->win2_basead);
|
||||
writel(SERREN | PERREN | MASTEREN | MEMEN,
|
||||
&ahbconf_pci->cmnd_sts);
|
||||
|
||||
/* PCI Configuration Registers for EHCI */
|
||||
writel(PCIWIN1_PCICMD | AHB_CFG_HOST, &ahbcom_pci->ahbpci_win1_ctr);
|
||||
writel(phys_base + OHCI_OFFSET, &ahb_pciconf_ohci->basead);
|
||||
writel(phys_base + EHCI_OFFSET, &ahb_pciconf_ehci->basead);
|
||||
writel(SERREN | PERREN | MASTEREN | MEMEN,
|
||||
&ahb_pciconf_ohci->cmnd_sts);
|
||||
writel(SERREN | PERREN | MASTEREN | MEMEN,
|
||||
&ahb_pciconf_ehci->cmnd_sts);
|
||||
|
||||
/* Enable PCI interrupt */
|
||||
setbits_le32(&ahbcom_pci->pci_int_enable,
|
||||
USBH_PMEEN | USBH_INTBEN | USBH_INTAEN);
|
||||
|
||||
*hccr = (struct ehci_hccr *)((uint32_t)&rehci->hciversion);
|
||||
cap_base = ehci_readl(&(*hccr)->cr_capbase);
|
||||
*hcor = (struct ehci_hcor *)((uint32_t)*hccr + HC_LENGTH(cap_base));
|
||||
|
||||
return 0;
|
||||
}
|
||||
76
u-boot/drivers/usb/host/ehci-spear.c
Normal file
76
u-boot/drivers/usb/host/ehci-spear.c
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* (C) Copyright 2010
|
||||
* Armando Visconti, ST Micoelectronics, <armando.visconti@st.com>.
|
||||
*
|
||||
* (C) Copyright 2009
|
||||
* Marvell Semiconductor <www.marvell.com>
|
||||
* Written-by: Prafulla Wadaskar <prafulla@marvell.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <usb.h>
|
||||
#include "ehci.h"
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <asm/arch/spr_misc.h>
|
||||
|
||||
static void spear6xx_usbh_stop(void)
|
||||
{
|
||||
struct misc_regs *const misc_p =
|
||||
(struct misc_regs *)CONFIG_SPEAR_MISCBASE;
|
||||
u32 periph1_rst = readl(misc_p->periph1_rst);
|
||||
|
||||
periph1_rst |= PERIPH_USBH1 | PERIPH_USBH2;
|
||||
writel(periph1_rst, misc_p->periph1_rst);
|
||||
|
||||
udelay(1000);
|
||||
periph1_rst &= ~(PERIPH_USBH1 | PERIPH_USBH2);
|
||||
writel(periph1_rst, misc_p->periph1_rst);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the appropriate control structures to manage
|
||||
* a new EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
u32 ehci = 0;
|
||||
|
||||
switch (index) {
|
||||
case 0:
|
||||
ehci = CONFIG_SYS_UHC0_EHCI_BASE;
|
||||
break;
|
||||
case 1:
|
||||
ehci = CONFIG_SYS_UHC1_EHCI_BASE;
|
||||
break;
|
||||
default:
|
||||
printf("ERROR: wrong controller index!\n");
|
||||
break;
|
||||
};
|
||||
|
||||
*hccr = (struct ehci_hccr *)(ehci + 0x100);
|
||||
*hcor = (struct ehci_hcor *)((uint32_t) *hccr +
|
||||
HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
|
||||
|
||||
debug("SPEAr-ehci: init hccr %x and hcor %x hc_length %d\n",
|
||||
(uint32_t)*hccr, (uint32_t)*hcor,
|
||||
(uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy the appropriate control structures corresponding
|
||||
* the the EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_stop(int index)
|
||||
{
|
||||
#if defined(CONFIG_SPEAR600)
|
||||
spear6xx_usbh_stop();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
114
u-boot/drivers/usb/host/ehci-sunxi.c
Normal file
114
u-boot/drivers/usb/host/ehci-sunxi.c
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Sunxi ehci glue
|
||||
*
|
||||
* Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com>
|
||||
* Copyright (C) 2014 Roman Byshko <rbyshko@gmail.com>
|
||||
*
|
||||
* Based on code from
|
||||
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/arch/usb_phy.h>
|
||||
#include <asm/io.h>
|
||||
#include <dm.h>
|
||||
#include "ehci.h"
|
||||
|
||||
#ifdef CONFIG_SUNXI_GEN_SUN4I
|
||||
#define BASE_DIST 0x8000
|
||||
#define AHB_CLK_DIST 2
|
||||
#else
|
||||
#define BASE_DIST 0x1000
|
||||
#define AHB_CLK_DIST 1
|
||||
#endif
|
||||
|
||||
struct ehci_sunxi_priv {
|
||||
struct ehci_ctrl ehci;
|
||||
int ahb_gate_mask; /* Mask of ahb_gate0 clk gate bits for this hcd */
|
||||
int phy_index; /* Index of the usb-phy attached to this hcd */
|
||||
};
|
||||
|
||||
static int ehci_usb_probe(struct udevice *dev)
|
||||
{
|
||||
struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
|
||||
struct usb_platdata *plat = dev_get_platdata(dev);
|
||||
struct ehci_sunxi_priv *priv = dev_get_priv(dev);
|
||||
struct ehci_hccr *hccr = (struct ehci_hccr *)dev_get_addr(dev);
|
||||
struct ehci_hcor *hcor;
|
||||
int extra_ahb_gate_mask = 0;
|
||||
|
||||
/*
|
||||
* This should go away once we've moved to the driver model for
|
||||
* clocks resp. phys.
|
||||
*/
|
||||
priv->ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0;
|
||||
#ifdef CONFIG_MACH_SUN8I_H3
|
||||
extra_ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_OHCI0;
|
||||
#endif
|
||||
priv->phy_index = ((u32)hccr - SUNXI_USB1_BASE) / BASE_DIST;
|
||||
priv->ahb_gate_mask <<= priv->phy_index * AHB_CLK_DIST;
|
||||
extra_ahb_gate_mask <<= priv->phy_index * AHB_CLK_DIST;
|
||||
priv->phy_index++; /* Non otg phys start at 1 */
|
||||
|
||||
setbits_le32(&ccm->ahb_gate0,
|
||||
priv->ahb_gate_mask | extra_ahb_gate_mask);
|
||||
#ifdef CONFIG_SUNXI_GEN_SUN6I
|
||||
setbits_le32(&ccm->ahb_reset0_cfg,
|
||||
priv->ahb_gate_mask | extra_ahb_gate_mask);
|
||||
#endif
|
||||
|
||||
sunxi_usb_phy_init(priv->phy_index);
|
||||
sunxi_usb_phy_power_on(priv->phy_index);
|
||||
|
||||
hcor = (struct ehci_hcor *)((uint32_t)hccr +
|
||||
HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
|
||||
|
||||
return ehci_register(dev, hccr, hcor, NULL, 0, plat->init_type);
|
||||
}
|
||||
|
||||
static int ehci_usb_remove(struct udevice *dev)
|
||||
{
|
||||
struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
|
||||
struct ehci_sunxi_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = ehci_deregister(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sunxi_usb_phy_exit(priv->phy_index);
|
||||
|
||||
#ifdef CONFIG_SUNXI_GEN_SUN6I
|
||||
clrbits_le32(&ccm->ahb_reset0_cfg, priv->ahb_gate_mask);
|
||||
#endif
|
||||
clrbits_le32(&ccm->ahb_gate0, priv->ahb_gate_mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id ehci_usb_ids[] = {
|
||||
{ .compatible = "allwinner,sun4i-a10-ehci", },
|
||||
{ .compatible = "allwinner,sun5i-a13-ehci", },
|
||||
{ .compatible = "allwinner,sun6i-a31-ehci", },
|
||||
{ .compatible = "allwinner,sun7i-a20-ehci", },
|
||||
{ .compatible = "allwinner,sun8i-a23-ehci", },
|
||||
{ .compatible = "allwinner,sun8i-a83t-ehci", },
|
||||
{ .compatible = "allwinner,sun8i-h3-ehci", },
|
||||
{ .compatible = "allwinner,sun9i-a80-ehci", },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(ehci_sunxi) = {
|
||||
.name = "ehci_sunxi",
|
||||
.id = UCLASS_USB,
|
||||
.of_match = ehci_usb_ids,
|
||||
.probe = ehci_usb_probe,
|
||||
.remove = ehci_usb_remove,
|
||||
.ops = &ehci_usb_ops,
|
||||
.platdata_auto_alloc_size = sizeof(struct usb_platdata),
|
||||
.priv_auto_alloc_size = sizeof(struct ehci_sunxi_priv),
|
||||
.flags = DM_FLAG_ALLOC_PRIV_DMA,
|
||||
};
|
||||
879
u-boot/drivers/usb/host/ehci-tegra.c
Normal file
879
u-boot/drivers/usb/host/ehci-tegra.c
Normal file
@@ -0,0 +1,879 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The Chromium OS Authors.
|
||||
* Copyright (c) 2009-2015 NVIDIA Corporation
|
||||
* Copyright (c) 2013 Lucas Stach
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm-generic/gpio.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/arch-tegra/usb.h>
|
||||
#include <asm/arch-tegra/clk_rst.h>
|
||||
#include <usb.h>
|
||||
#include <usb/ulpi.h>
|
||||
#include <libfdt.h>
|
||||
#include <fdtdec.h>
|
||||
|
||||
#include "ehci.h"
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define USB1_ADDR_MASK 0xFFFF0000
|
||||
|
||||
#define HOSTPC1_DEVLC 0x84
|
||||
#define HOSTPC1_PSPD(x) (((x) >> 25) & 0x3)
|
||||
|
||||
#ifdef CONFIG_USB_ULPI
|
||||
#ifndef CONFIG_USB_ULPI_VIEWPORT
|
||||
#error "To use CONFIG_USB_ULPI on Tegra Boards you have to also \
|
||||
define CONFIG_USB_ULPI_VIEWPORT"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Parameters we need for USB */
|
||||
enum {
|
||||
PARAM_DIVN, /* PLL FEEDBACK DIVIDer */
|
||||
PARAM_DIVM, /* PLL INPUT DIVIDER */
|
||||
PARAM_DIVP, /* POST DIVIDER (2^N) */
|
||||
PARAM_CPCON, /* BASE PLLC CHARGE Pump setup ctrl */
|
||||
PARAM_LFCON, /* BASE PLLC LOOP FILter setup ctrl */
|
||||
PARAM_ENABLE_DELAY_COUNT, /* PLL-U Enable Delay Count */
|
||||
PARAM_STABLE_COUNT, /* PLL-U STABLE count */
|
||||
PARAM_ACTIVE_DELAY_COUNT, /* PLL-U Active delay count */
|
||||
PARAM_XTAL_FREQ_COUNT, /* PLL-U XTAL frequency count */
|
||||
PARAM_DEBOUNCE_A_TIME, /* 10MS DELAY for BIAS_DEBOUNCE_A */
|
||||
PARAM_BIAS_TIME, /* 20US DELAY AFter bias cell op */
|
||||
|
||||
PARAM_COUNT
|
||||
};
|
||||
|
||||
/* Possible port types (dual role mode) */
|
||||
enum dr_mode {
|
||||
DR_MODE_NONE = 0,
|
||||
DR_MODE_HOST, /* supports host operation */
|
||||
DR_MODE_DEVICE, /* supports device operation */
|
||||
DR_MODE_OTG, /* supports both */
|
||||
};
|
||||
|
||||
enum usb_ctlr_type {
|
||||
USB_CTLR_T20,
|
||||
USB_CTLR_T30,
|
||||
USB_CTLR_T114,
|
||||
USB_CTLR_T210,
|
||||
|
||||
USB_CTRL_COUNT,
|
||||
};
|
||||
|
||||
/* Information about a USB port */
|
||||
struct fdt_usb {
|
||||
struct ehci_ctrl ehci;
|
||||
struct usb_ctlr *reg; /* address of registers in physical memory */
|
||||
unsigned utmi:1; /* 1 if port has external tranceiver, else 0 */
|
||||
unsigned ulpi:1; /* 1 if port has external ULPI transceiver */
|
||||
unsigned enabled:1; /* 1 to enable, 0 to disable */
|
||||
unsigned has_legacy_mode:1; /* 1 if this port has legacy mode */
|
||||
enum usb_ctlr_type type;
|
||||
enum usb_init_type init_type;
|
||||
enum dr_mode dr_mode; /* dual role mode */
|
||||
enum periph_id periph_id;/* peripheral id */
|
||||
struct gpio_desc vbus_gpio; /* GPIO for vbus enable */
|
||||
struct gpio_desc phy_reset_gpio; /* GPIO to reset ULPI phy */
|
||||
};
|
||||
|
||||
/*
|
||||
* This table has USB timing parameters for each Oscillator frequency we
|
||||
* support. There are four sets of values:
|
||||
*
|
||||
* 1. PLLU configuration information (reference clock is osc/clk_m and
|
||||
* PLLU-FOs are fixed at 12MHz/60MHz/480MHz).
|
||||
*
|
||||
* Reference frequency 13.0MHz 19.2MHz 12.0MHz 26.0MHz
|
||||
* ----------------------------------------------------------------------
|
||||
* DIVN 960 (0x3c0) 200 (0c8) 960 (3c0h) 960 (3c0)
|
||||
* DIVM 13 (0d) 4 (04) 12 (0c) 26 (1a)
|
||||
* Filter frequency (MHz) 1 4.8 6 2
|
||||
* CPCON 1100b 0011b 1100b 1100b
|
||||
* LFCON0 0 0 0 0
|
||||
*
|
||||
* 2. PLL CONFIGURATION & PARAMETERS for different clock generators:
|
||||
*
|
||||
* Reference frequency 13.0MHz 19.2MHz 12.0MHz 26.0MHz
|
||||
* ---------------------------------------------------------------------------
|
||||
* PLLU_ENABLE_DLY_COUNT 02 (0x02) 03 (03) 02 (02) 04 (04)
|
||||
* PLLU_STABLE_COUNT 51 (33) 75 (4B) 47 (2F) 102 (66)
|
||||
* PLL_ACTIVE_DLY_COUNT 05 (05) 06 (06) 04 (04) 09 (09)
|
||||
* XTAL_FREQ_COUNT 127 (7F) 187 (BB) 118 (76) 254 (FE)
|
||||
*
|
||||
* 3. Debounce values IdDig, Avalid, Bvalid, VbusValid, VbusWakeUp, and
|
||||
* SessEnd. Each of these signals have their own debouncer and for each of
|
||||
* those one out of two debouncing times can be chosen (BIAS_DEBOUNCE_A or
|
||||
* BIAS_DEBOUNCE_B).
|
||||
*
|
||||
* The values of DEBOUNCE_A and DEBOUNCE_B are calculated as follows:
|
||||
* 0xffff -> No debouncing at all
|
||||
* <n> ms = <n> *1000 / (1/19.2MHz) / 4
|
||||
*
|
||||
* So to program a 1 ms debounce for BIAS_DEBOUNCE_A, we have:
|
||||
* BIAS_DEBOUNCE_A[15:0] = 1000 * 19.2 / 4 = 4800 = 0x12c0
|
||||
*
|
||||
* We need to use only DebounceA for BOOTROM. We don't need the DebounceB
|
||||
* values, so we can keep those to default.
|
||||
*
|
||||
* 4. The 20 microsecond delay after bias cell operation.
|
||||
*/
|
||||
static const unsigned T20_usb_pll[CLOCK_OSC_FREQ_COUNT][PARAM_COUNT] = {
|
||||
/* DivN, DivM, DivP, CPCON, LFCON, Delays Debounce, Bias */
|
||||
{ 0x3C0, 0x0D, 0x00, 0xC, 0, 0x02, 0x33, 0x05, 0x7F, 0x7EF4, 5 },
|
||||
{ 0x0C8, 0x04, 0x00, 0x3, 0, 0x03, 0x4B, 0x06, 0xBB, 0xBB80, 7 },
|
||||
{ 0x3C0, 0x0C, 0x00, 0xC, 0, 0x02, 0x2F, 0x04, 0x76, 0x7530, 5 },
|
||||
{ 0x3C0, 0x1A, 0x00, 0xC, 0, 0x04, 0x66, 0x09, 0xFE, 0xFDE8, 9 },
|
||||
{ 0x000, 0x00, 0x00, 0x0, 0, 0x00, 0x00, 0x00, 0x00, 0x0000, 0 },
|
||||
{ 0x000, 0x00, 0x00, 0x0, 0, 0x00, 0x00, 0x00, 0x00, 0x0000, 0 }
|
||||
};
|
||||
|
||||
static const unsigned T30_usb_pll[CLOCK_OSC_FREQ_COUNT][PARAM_COUNT] = {
|
||||
/* DivN, DivM, DivP, CPCON, LFCON, Delays Debounce, Bias */
|
||||
{ 0x3C0, 0x0D, 0x00, 0xC, 1, 0x02, 0x33, 0x09, 0x7F, 0x7EF4, 5 },
|
||||
{ 0x0C8, 0x04, 0x00, 0x3, 0, 0x03, 0x4B, 0x0C, 0xBB, 0xBB80, 7 },
|
||||
{ 0x3C0, 0x0C, 0x00, 0xC, 1, 0x02, 0x2F, 0x08, 0x76, 0x7530, 5 },
|
||||
{ 0x3C0, 0x1A, 0x00, 0xC, 1, 0x04, 0x66, 0x09, 0xFE, 0xFDE8, 9 },
|
||||
{ 0x000, 0x00, 0x00, 0x0, 0, 0x00, 0x00, 0x00, 0x00, 0x0000, 0 },
|
||||
{ 0x000, 0x00, 0x00, 0x0, 0, 0x00, 0x00, 0x00, 0x00, 0x0000, 0 }
|
||||
};
|
||||
|
||||
static const unsigned T114_usb_pll[CLOCK_OSC_FREQ_COUNT][PARAM_COUNT] = {
|
||||
/* DivN, DivM, DivP, CPCON, LFCON, Delays Debounce, Bias */
|
||||
{ 0x3C0, 0x0D, 0x00, 0xC, 2, 0x02, 0x33, 0x09, 0x7F, 0x7EF4, 6 },
|
||||
{ 0x0C8, 0x04, 0x00, 0x3, 2, 0x03, 0x4B, 0x0C, 0xBB, 0xBB80, 8 },
|
||||
{ 0x3C0, 0x0C, 0x00, 0xC, 2, 0x02, 0x2F, 0x08, 0x76, 0x7530, 5 },
|
||||
{ 0x3C0, 0x1A, 0x00, 0xC, 2, 0x04, 0x66, 0x09, 0xFE, 0xFDE8, 11 },
|
||||
{ 0x000, 0x00, 0x00, 0x0, 0, 0x00, 0x00, 0x00, 0x00, 0x0000, 0 },
|
||||
{ 0x000, 0x00, 0x00, 0x0, 0, 0x00, 0x00, 0x00, 0x00, 0x0000, 0 }
|
||||
};
|
||||
|
||||
/* NOTE: 13/26MHz settings are N/A for T210, so dupe 12MHz settings for now */
|
||||
static const unsigned T210_usb_pll[CLOCK_OSC_FREQ_COUNT][PARAM_COUNT] = {
|
||||
/* DivN, DivM, DivP, KCP, KVCO, Delays Debounce, Bias */
|
||||
{ 0x028, 0x01, 0x01, 0x0, 0, 0x02, 0x2F, 0x08, 0x76, 32500, 5 },
|
||||
{ 0x019, 0x01, 0x01, 0x0, 0, 0x03, 0x4B, 0x0C, 0xBB, 48000, 8 },
|
||||
{ 0x028, 0x01, 0x01, 0x0, 0, 0x02, 0x2F, 0x08, 0x76, 30000, 5 },
|
||||
{ 0x028, 0x01, 0x01, 0x0, 0, 0x02, 0x2F, 0x08, 0x76, 65000, 5 },
|
||||
{ 0x019, 0x02, 0x01, 0x0, 0, 0x05, 0x96, 0x18, 0x177, 96000, 15 },
|
||||
{ 0x028, 0x04, 0x01, 0x0, 0, 0x04, 0x66, 0x09, 0xFE, 120000, 20 }
|
||||
};
|
||||
|
||||
/* UTMIP Idle Wait Delay */
|
||||
static const u8 utmip_idle_wait_delay = 17;
|
||||
|
||||
/* UTMIP Elastic limit */
|
||||
static const u8 utmip_elastic_limit = 16;
|
||||
|
||||
/* UTMIP High Speed Sync Start Delay */
|
||||
static const u8 utmip_hs_sync_start_delay = 9;
|
||||
|
||||
struct fdt_usb_controller {
|
||||
/* flag to determine whether controller supports hostpc register */
|
||||
u32 has_hostpc:1;
|
||||
const unsigned *pll_parameter;
|
||||
};
|
||||
|
||||
static struct fdt_usb_controller fdt_usb_controllers[USB_CTRL_COUNT] = {
|
||||
{
|
||||
.has_hostpc = 0,
|
||||
.pll_parameter = (const unsigned *)T20_usb_pll,
|
||||
},
|
||||
{
|
||||
.has_hostpc = 1,
|
||||
.pll_parameter = (const unsigned *)T30_usb_pll,
|
||||
},
|
||||
{
|
||||
.has_hostpc = 1,
|
||||
.pll_parameter = (const unsigned *)T114_usb_pll,
|
||||
},
|
||||
{
|
||||
.has_hostpc = 1,
|
||||
.pll_parameter = (const unsigned *)T210_usb_pll,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* A known hardware issue where Connect Status Change bit of PORTSC register
|
||||
* of USB1 controller will be set after Port Reset.
|
||||
* We have to clear it in order for later device enumeration to proceed.
|
||||
*/
|
||||
static void tegra_ehci_powerup_fixup(struct ehci_ctrl *ctrl,
|
||||
uint32_t *status_reg, uint32_t *reg)
|
||||
{
|
||||
struct fdt_usb *config = ctrl->priv;
|
||||
struct fdt_usb_controller *controller;
|
||||
|
||||
controller = &fdt_usb_controllers[config->type];
|
||||
mdelay(50);
|
||||
/* This is to avoid PORT_ENABLE bit to be cleared in "ehci-hcd.c". */
|
||||
if (controller->has_hostpc)
|
||||
*reg |= EHCI_PS_PE;
|
||||
|
||||
if (!config->has_legacy_mode)
|
||||
return;
|
||||
/* For EHCI_PS_CSC to be cleared in ehci_hcd.c */
|
||||
if (ehci_readl(status_reg) & EHCI_PS_CSC)
|
||||
*reg |= EHCI_PS_CSC;
|
||||
}
|
||||
|
||||
static void tegra_ehci_set_usbmode(struct ehci_ctrl *ctrl)
|
||||
{
|
||||
struct fdt_usb *config = ctrl->priv;
|
||||
struct usb_ctlr *usbctlr;
|
||||
uint32_t tmp;
|
||||
|
||||
usbctlr = config->reg;
|
||||
|
||||
tmp = ehci_readl(&usbctlr->usb_mode);
|
||||
tmp |= USBMODE_CM_HC;
|
||||
ehci_writel(&usbctlr->usb_mode, tmp);
|
||||
}
|
||||
|
||||
static int tegra_ehci_get_port_speed(struct ehci_ctrl *ctrl, uint32_t reg)
|
||||
{
|
||||
struct fdt_usb *config = ctrl->priv;
|
||||
struct fdt_usb_controller *controller;
|
||||
uint32_t tmp;
|
||||
uint32_t *reg_ptr;
|
||||
|
||||
controller = &fdt_usb_controllers[config->type];
|
||||
if (controller->has_hostpc) {
|
||||
reg_ptr = (uint32_t *)((u8 *)&ctrl->hcor->or_usbcmd +
|
||||
HOSTPC1_DEVLC);
|
||||
tmp = ehci_readl(reg_ptr);
|
||||
return HOSTPC1_PSPD(tmp);
|
||||
} else
|
||||
return PORTSC_PSPD(reg);
|
||||
}
|
||||
|
||||
/* Set up VBUS for host/device mode */
|
||||
static void set_up_vbus(struct fdt_usb *config, enum usb_init_type init)
|
||||
{
|
||||
/*
|
||||
* If we are an OTG port initializing in host mode,
|
||||
* check if remote host is driving VBus and bail out in this case.
|
||||
*/
|
||||
if (init == USB_INIT_HOST &&
|
||||
config->dr_mode == DR_MODE_OTG &&
|
||||
(readl(&config->reg->phy_vbus_sensors) & VBUS_VLD_STS)) {
|
||||
printf("tegrausb: VBUS input active; not enabling as host\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dm_gpio_is_valid(&config->vbus_gpio)) {
|
||||
int vbus_value;
|
||||
|
||||
vbus_value = (init == USB_INIT_HOST);
|
||||
dm_gpio_set_value(&config->vbus_gpio, vbus_value);
|
||||
|
||||
debug("set_up_vbus: GPIO %d %d\n",
|
||||
gpio_get_number(&config->vbus_gpio), vbus_value);
|
||||
}
|
||||
}
|
||||
|
||||
static void usbf_reset_controller(struct fdt_usb *config,
|
||||
struct usb_ctlr *usbctlr)
|
||||
{
|
||||
/* Reset the USB controller with 2us delay */
|
||||
reset_periph(config->periph_id, 2);
|
||||
|
||||
/*
|
||||
* Set USB1_NO_LEGACY_MODE to 1, Registers are accessible under
|
||||
* base address
|
||||
*/
|
||||
if (config->has_legacy_mode)
|
||||
setbits_le32(&usbctlr->usb1_legacy_ctrl, USB1_NO_LEGACY_MODE);
|
||||
|
||||
/* Put UTMIP1/3 in reset */
|
||||
setbits_le32(&usbctlr->susp_ctrl, UTMIP_RESET);
|
||||
|
||||
/* Enable the UTMIP PHY */
|
||||
if (config->utmi)
|
||||
setbits_le32(&usbctlr->susp_ctrl, UTMIP_PHY_ENB);
|
||||
}
|
||||
|
||||
static const unsigned *get_pll_timing(struct fdt_usb_controller *controller)
|
||||
{
|
||||
const unsigned *timing;
|
||||
|
||||
timing = controller->pll_parameter +
|
||||
clock_get_osc_freq() * PARAM_COUNT;
|
||||
|
||||
return timing;
|
||||
}
|
||||
|
||||
/* select the PHY to use with a USB controller */
|
||||
static void init_phy_mux(struct fdt_usb *config, uint pts,
|
||||
enum usb_init_type init)
|
||||
{
|
||||
struct usb_ctlr *usbctlr = config->reg;
|
||||
|
||||
#if defined(CONFIG_TEGRA20)
|
||||
if (config->periph_id == PERIPH_ID_USBD) {
|
||||
clrsetbits_le32(&usbctlr->port_sc1, PTS1_MASK,
|
||||
pts << PTS1_SHIFT);
|
||||
clrbits_le32(&usbctlr->port_sc1, STS1);
|
||||
} else {
|
||||
clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK,
|
||||
pts << PTS_SHIFT);
|
||||
clrbits_le32(&usbctlr->port_sc1, STS);
|
||||
}
|
||||
#else
|
||||
/* Set to Host mode (if applicable) after Controller Reset was done */
|
||||
clrsetbits_le32(&usbctlr->usb_mode, USBMODE_CM_HC,
|
||||
(init == USB_INIT_HOST) ? USBMODE_CM_HC : 0);
|
||||
/*
|
||||
* Select PHY interface after setting host mode.
|
||||
* For device mode, the ordering requirement is not an issue, since
|
||||
* only the first USB controller supports device mode, and that USB
|
||||
* controller can only talk to a UTMI PHY, so the PHY selection is
|
||||
* already made at reset time, so this write is a no-op.
|
||||
*/
|
||||
clrsetbits_le32(&usbctlr->hostpc1_devlc, PTS_MASK,
|
||||
pts << PTS_SHIFT);
|
||||
clrbits_le32(&usbctlr->hostpc1_devlc, STS);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* set up the UTMI USB controller with the parameters provided */
|
||||
static int init_utmi_usb_controller(struct fdt_usb *config,
|
||||
enum usb_init_type init)
|
||||
{
|
||||
struct fdt_usb_controller *controller;
|
||||
u32 b_sess_valid_mask, val;
|
||||
int loop_count;
|
||||
const unsigned *timing;
|
||||
struct usb_ctlr *usbctlr = config->reg;
|
||||
struct clk_rst_ctlr *clkrst;
|
||||
struct usb_ctlr *usb1ctlr;
|
||||
|
||||
clock_enable(config->periph_id);
|
||||
|
||||
/* Reset the usb controller */
|
||||
usbf_reset_controller(config, usbctlr);
|
||||
|
||||
/* Stop crystal clock by setting UTMIP_PHY_XTAL_CLOCKEN low */
|
||||
clrbits_le32(&usbctlr->utmip_misc_cfg1, UTMIP_PHY_XTAL_CLOCKEN);
|
||||
|
||||
/* Follow the crystal clock disable by >100ns delay */
|
||||
udelay(1);
|
||||
|
||||
b_sess_valid_mask = (VBUS_B_SESS_VLD_SW_VALUE | VBUS_B_SESS_VLD_SW_EN);
|
||||
clrsetbits_le32(&usbctlr->phy_vbus_sensors, b_sess_valid_mask,
|
||||
(init == USB_INIT_DEVICE) ? b_sess_valid_mask : 0);
|
||||
|
||||
/*
|
||||
* To Use the A Session Valid for cable detection logic, VBUS_WAKEUP
|
||||
* mux must be switched to actually use a_sess_vld threshold.
|
||||
*/
|
||||
if (config->dr_mode == DR_MODE_OTG &&
|
||||
dm_gpio_is_valid(&config->vbus_gpio))
|
||||
clrsetbits_le32(&usbctlr->usb1_legacy_ctrl,
|
||||
VBUS_SENSE_CTL_MASK,
|
||||
VBUS_SENSE_CTL_A_SESS_VLD << VBUS_SENSE_CTL_SHIFT);
|
||||
|
||||
controller = &fdt_usb_controllers[config->type];
|
||||
debug("controller=%p, type=%d\n", controller, config->type);
|
||||
|
||||
/*
|
||||
* PLL Delay CONFIGURATION settings. The following parameters control
|
||||
* the bring up of the plls.
|
||||
*/
|
||||
timing = get_pll_timing(controller);
|
||||
|
||||
if (!controller->has_hostpc) {
|
||||
val = readl(&usbctlr->utmip_misc_cfg1);
|
||||
clrsetbits_le32(&val, UTMIP_PLLU_STABLE_COUNT_MASK,
|
||||
timing[PARAM_STABLE_COUNT] <<
|
||||
UTMIP_PLLU_STABLE_COUNT_SHIFT);
|
||||
clrsetbits_le32(&val, UTMIP_PLL_ACTIVE_DLY_COUNT_MASK,
|
||||
timing[PARAM_ACTIVE_DELAY_COUNT] <<
|
||||
UTMIP_PLL_ACTIVE_DLY_COUNT_SHIFT);
|
||||
writel(val, &usbctlr->utmip_misc_cfg1);
|
||||
|
||||
/* Set PLL enable delay count and crystal frequency count */
|
||||
val = readl(&usbctlr->utmip_pll_cfg1);
|
||||
clrsetbits_le32(&val, UTMIP_PLLU_ENABLE_DLY_COUNT_MASK,
|
||||
timing[PARAM_ENABLE_DELAY_COUNT] <<
|
||||
UTMIP_PLLU_ENABLE_DLY_COUNT_SHIFT);
|
||||
clrsetbits_le32(&val, UTMIP_XTAL_FREQ_COUNT_MASK,
|
||||
timing[PARAM_XTAL_FREQ_COUNT] <<
|
||||
UTMIP_XTAL_FREQ_COUNT_SHIFT);
|
||||
writel(val, &usbctlr->utmip_pll_cfg1);
|
||||
} else {
|
||||
clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
|
||||
|
||||
val = readl(&clkrst->crc_utmip_pll_cfg2);
|
||||
clrsetbits_le32(&val, UTMIP_PLLU_STABLE_COUNT_MASK,
|
||||
timing[PARAM_STABLE_COUNT] <<
|
||||
UTMIP_PLLU_STABLE_COUNT_SHIFT);
|
||||
clrsetbits_le32(&val, UTMIP_PLL_ACTIVE_DLY_COUNT_MASK,
|
||||
timing[PARAM_ACTIVE_DELAY_COUNT] <<
|
||||
UTMIP_PLL_ACTIVE_DLY_COUNT_SHIFT);
|
||||
writel(val, &clkrst->crc_utmip_pll_cfg2);
|
||||
|
||||
/* Set PLL enable delay count and crystal frequency count */
|
||||
val = readl(&clkrst->crc_utmip_pll_cfg1);
|
||||
clrsetbits_le32(&val, UTMIP_PLLU_ENABLE_DLY_COUNT_MASK,
|
||||
timing[PARAM_ENABLE_DELAY_COUNT] <<
|
||||
UTMIP_PLLU_ENABLE_DLY_COUNT_SHIFT);
|
||||
clrsetbits_le32(&val, UTMIP_XTAL_FREQ_COUNT_MASK,
|
||||
timing[PARAM_XTAL_FREQ_COUNT] <<
|
||||
UTMIP_XTAL_FREQ_COUNT_SHIFT);
|
||||
writel(val, &clkrst->crc_utmip_pll_cfg1);
|
||||
|
||||
/* Disable Power Down state for PLL */
|
||||
clrbits_le32(&clkrst->crc_utmip_pll_cfg1,
|
||||
PLLU_POWERDOWN | PLL_ENABLE_POWERDOWN |
|
||||
PLL_ACTIVE_POWERDOWN);
|
||||
|
||||
/* Recommended PHY settings for EYE diagram */
|
||||
val = readl(&usbctlr->utmip_xcvr_cfg0);
|
||||
clrsetbits_le32(&val, UTMIP_XCVR_SETUP_MASK,
|
||||
0x4 << UTMIP_XCVR_SETUP_SHIFT);
|
||||
clrsetbits_le32(&val, UTMIP_XCVR_SETUP_MSB_MASK,
|
||||
0x3 << UTMIP_XCVR_SETUP_MSB_SHIFT);
|
||||
clrsetbits_le32(&val, UTMIP_XCVR_HSSLEW_MSB_MASK,
|
||||
0x8 << UTMIP_XCVR_HSSLEW_MSB_SHIFT);
|
||||
writel(val, &usbctlr->utmip_xcvr_cfg0);
|
||||
clrsetbits_le32(&usbctlr->utmip_xcvr_cfg1,
|
||||
UTMIP_XCVR_TERM_RANGE_ADJ_MASK,
|
||||
0x7 << UTMIP_XCVR_TERM_RANGE_ADJ_SHIFT);
|
||||
|
||||
/* Some registers can be controlled from USB1 only. */
|
||||
if (config->periph_id != PERIPH_ID_USBD) {
|
||||
clock_enable(PERIPH_ID_USBD);
|
||||
/* Disable Reset if in Reset state */
|
||||
reset_set_enable(PERIPH_ID_USBD, 0);
|
||||
}
|
||||
usb1ctlr = (struct usb_ctlr *)
|
||||
((unsigned long)config->reg & USB1_ADDR_MASK);
|
||||
val = readl(&usb1ctlr->utmip_bias_cfg0);
|
||||
setbits_le32(&val, UTMIP_HSDISCON_LEVEL_MSB);
|
||||
clrsetbits_le32(&val, UTMIP_HSDISCON_LEVEL_MASK,
|
||||
0x1 << UTMIP_HSDISCON_LEVEL_SHIFT);
|
||||
clrsetbits_le32(&val, UTMIP_HSSQUELCH_LEVEL_MASK,
|
||||
0x2 << UTMIP_HSSQUELCH_LEVEL_SHIFT);
|
||||
writel(val, &usb1ctlr->utmip_bias_cfg0);
|
||||
|
||||
/* Miscellaneous setting mentioned in Programming Guide */
|
||||
clrbits_le32(&usbctlr->utmip_misc_cfg0,
|
||||
UTMIP_SUSPEND_EXIT_ON_EDGE);
|
||||
}
|
||||
|
||||
/* Setting the tracking length time */
|
||||
clrsetbits_le32(&usbctlr->utmip_bias_cfg1,
|
||||
UTMIP_BIAS_PDTRK_COUNT_MASK,
|
||||
timing[PARAM_BIAS_TIME] << UTMIP_BIAS_PDTRK_COUNT_SHIFT);
|
||||
|
||||
/* Program debounce time for VBUS to become valid */
|
||||
clrsetbits_le32(&usbctlr->utmip_debounce_cfg0,
|
||||
UTMIP_DEBOUNCE_CFG0_MASK,
|
||||
timing[PARAM_DEBOUNCE_A_TIME] << UTMIP_DEBOUNCE_CFG0_SHIFT);
|
||||
|
||||
if (timing[PARAM_DEBOUNCE_A_TIME] > 0xFFFF) {
|
||||
clrsetbits_le32(&usbctlr->utmip_debounce_cfg0,
|
||||
UTMIP_DEBOUNCE_CFG0_MASK,
|
||||
(timing[PARAM_DEBOUNCE_A_TIME] >> 1)
|
||||
<< UTMIP_DEBOUNCE_CFG0_SHIFT);
|
||||
clrsetbits_le32(&usbctlr->utmip_bias_cfg1,
|
||||
UTMIP_BIAS_DEBOUNCE_TIMESCALE_MASK,
|
||||
1 << UTMIP_BIAS_DEBOUNCE_TIMESCALE_SHIFT);
|
||||
}
|
||||
|
||||
setbits_le32(&usbctlr->utmip_tx_cfg0, UTMIP_FS_PREAMBLE_J);
|
||||
|
||||
/* Disable battery charge enabling bit */
|
||||
setbits_le32(&usbctlr->utmip_bat_chrg_cfg0, UTMIP_PD_CHRG);
|
||||
|
||||
clrbits_le32(&usbctlr->utmip_xcvr_cfg0, UTMIP_XCVR_LSBIAS_SE);
|
||||
setbits_le32(&usbctlr->utmip_spare_cfg0, FUSE_SETUP_SEL);
|
||||
|
||||
/*
|
||||
* Configure the UTMIP_IDLE_WAIT and UTMIP_ELASTIC_LIMIT
|
||||
* Setting these fields, together with default values of the
|
||||
* other fields, results in programming the registers below as
|
||||
* follows:
|
||||
* UTMIP_HSRX_CFG0 = 0x9168c000
|
||||
* UTMIP_HSRX_CFG1 = 0x13
|
||||
*/
|
||||
|
||||
/* Set PLL enable delay count and Crystal frequency count */
|
||||
val = readl(&usbctlr->utmip_hsrx_cfg0);
|
||||
clrsetbits_le32(&val, UTMIP_IDLE_WAIT_MASK,
|
||||
utmip_idle_wait_delay << UTMIP_IDLE_WAIT_SHIFT);
|
||||
clrsetbits_le32(&val, UTMIP_ELASTIC_LIMIT_MASK,
|
||||
utmip_elastic_limit << UTMIP_ELASTIC_LIMIT_SHIFT);
|
||||
writel(val, &usbctlr->utmip_hsrx_cfg0);
|
||||
|
||||
/* Configure the UTMIP_HS_SYNC_START_DLY */
|
||||
clrsetbits_le32(&usbctlr->utmip_hsrx_cfg1,
|
||||
UTMIP_HS_SYNC_START_DLY_MASK,
|
||||
utmip_hs_sync_start_delay << UTMIP_HS_SYNC_START_DLY_SHIFT);
|
||||
|
||||
/* Preceed the crystal clock disable by >100ns delay. */
|
||||
udelay(1);
|
||||
|
||||
/* Resuscitate crystal clock by setting UTMIP_PHY_XTAL_CLOCKEN */
|
||||
setbits_le32(&usbctlr->utmip_misc_cfg1, UTMIP_PHY_XTAL_CLOCKEN);
|
||||
|
||||
if (controller->has_hostpc) {
|
||||
if (config->periph_id == PERIPH_ID_USBD)
|
||||
clrbits_le32(&clkrst->crc_utmip_pll_cfg2,
|
||||
UTMIP_FORCE_PD_SAMP_A_POWERDOWN);
|
||||
if (config->periph_id == PERIPH_ID_USB2)
|
||||
clrbits_le32(&clkrst->crc_utmip_pll_cfg2,
|
||||
UTMIP_FORCE_PD_SAMP_B_POWERDOWN);
|
||||
if (config->periph_id == PERIPH_ID_USB3)
|
||||
clrbits_le32(&clkrst->crc_utmip_pll_cfg2,
|
||||
UTMIP_FORCE_PD_SAMP_C_POWERDOWN);
|
||||
}
|
||||
/* Finished the per-controller init. */
|
||||
|
||||
/* De-assert UTMIP_RESET to bring out of reset. */
|
||||
clrbits_le32(&usbctlr->susp_ctrl, UTMIP_RESET);
|
||||
|
||||
/* Wait for the phy clock to become valid in 100 ms */
|
||||
for (loop_count = 100000; loop_count != 0; loop_count--) {
|
||||
if (readl(&usbctlr->susp_ctrl) & USB_PHY_CLK_VALID)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
if (!loop_count)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
/* Disable ICUSB FS/LS transceiver */
|
||||
clrbits_le32(&usbctlr->icusb_ctrl, IC_ENB1);
|
||||
|
||||
/* Select UTMI parallel interface */
|
||||
init_phy_mux(config, PTS_UTMI, init);
|
||||
|
||||
/* Deassert power down state */
|
||||
clrbits_le32(&usbctlr->utmip_xcvr_cfg0, UTMIP_FORCE_PD_POWERDOWN |
|
||||
UTMIP_FORCE_PD2_POWERDOWN | UTMIP_FORCE_PDZI_POWERDOWN);
|
||||
clrbits_le32(&usbctlr->utmip_xcvr_cfg1, UTMIP_FORCE_PDDISC_POWERDOWN |
|
||||
UTMIP_FORCE_PDCHRP_POWERDOWN | UTMIP_FORCE_PDDR_POWERDOWN);
|
||||
|
||||
if (controller->has_hostpc) {
|
||||
/*
|
||||
* BIAS Pad Power Down is common among all 3 USB
|
||||
* controllers and can be controlled from USB1 only.
|
||||
*/
|
||||
usb1ctlr = (struct usb_ctlr *)
|
||||
((unsigned long)config->reg & USB1_ADDR_MASK);
|
||||
clrbits_le32(&usb1ctlr->utmip_bias_cfg0, UTMIP_BIASPD);
|
||||
udelay(25);
|
||||
clrbits_le32(&usb1ctlr->utmip_bias_cfg1,
|
||||
UTMIP_FORCE_PDTRK_POWERDOWN);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_ULPI
|
||||
/* if board file does not set a ULPI reference frequency we default to 24MHz */
|
||||
#ifndef CONFIG_ULPI_REF_CLK
|
||||
#define CONFIG_ULPI_REF_CLK 24000000
|
||||
#endif
|
||||
|
||||
/* set up the ULPI USB controller with the parameters provided */
|
||||
static int init_ulpi_usb_controller(struct fdt_usb *config,
|
||||
enum usb_init_type init)
|
||||
{
|
||||
u32 val;
|
||||
int loop_count;
|
||||
struct ulpi_viewport ulpi_vp;
|
||||
struct usb_ctlr *usbctlr = config->reg;
|
||||
int ret;
|
||||
|
||||
/* set up ULPI reference clock on pllp_out4 */
|
||||
clock_enable(PERIPH_ID_DEV2_OUT);
|
||||
clock_set_pllout(CLOCK_ID_PERIPH, PLL_OUT4, CONFIG_ULPI_REF_CLK);
|
||||
|
||||
/* reset ULPI phy */
|
||||
if (dm_gpio_is_valid(&config->phy_reset_gpio)) {
|
||||
dm_gpio_set_value(&config->phy_reset_gpio, 0);
|
||||
mdelay(5);
|
||||
dm_gpio_set_value(&config->phy_reset_gpio, 1);
|
||||
}
|
||||
|
||||
/* Reset the usb controller */
|
||||
clock_enable(config->periph_id);
|
||||
usbf_reset_controller(config, usbctlr);
|
||||
|
||||
/* enable pinmux bypass */
|
||||
setbits_le32(&usbctlr->ulpi_timing_ctrl_0,
|
||||
ULPI_CLKOUT_PINMUX_BYP | ULPI_OUTPUT_PINMUX_BYP);
|
||||
|
||||
/* Select ULPI parallel interface */
|
||||
init_phy_mux(config, PTS_ULPI, init);
|
||||
|
||||
/* enable ULPI transceiver */
|
||||
setbits_le32(&usbctlr->susp_ctrl, ULPI_PHY_ENB);
|
||||
|
||||
/* configure ULPI transceiver timings */
|
||||
val = 0;
|
||||
writel(val, &usbctlr->ulpi_timing_ctrl_1);
|
||||
|
||||
val |= ULPI_DATA_TRIMMER_SEL(4);
|
||||
val |= ULPI_STPDIRNXT_TRIMMER_SEL(4);
|
||||
val |= ULPI_DIR_TRIMMER_SEL(4);
|
||||
writel(val, &usbctlr->ulpi_timing_ctrl_1);
|
||||
udelay(10);
|
||||
|
||||
val |= ULPI_DATA_TRIMMER_LOAD;
|
||||
val |= ULPI_STPDIRNXT_TRIMMER_LOAD;
|
||||
val |= ULPI_DIR_TRIMMER_LOAD;
|
||||
writel(val, &usbctlr->ulpi_timing_ctrl_1);
|
||||
|
||||
/* set up phy for host operation with external vbus supply */
|
||||
ulpi_vp.port_num = 0;
|
||||
ulpi_vp.viewport_addr = (u32)&usbctlr->ulpi_viewport;
|
||||
|
||||
ret = ulpi_init(&ulpi_vp);
|
||||
if (ret) {
|
||||
printf("Tegra ULPI viewport init failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ulpi_set_vbus(&ulpi_vp, 1, 1);
|
||||
ulpi_set_vbus_indicator(&ulpi_vp, 1, 1, 0);
|
||||
|
||||
/* enable wakeup events */
|
||||
setbits_le32(&usbctlr->port_sc1, WKCN | WKDS | WKOC);
|
||||
|
||||
/* Enable and wait for the phy clock to become valid in 100 ms */
|
||||
setbits_le32(&usbctlr->susp_ctrl, USB_SUSP_CLR);
|
||||
for (loop_count = 100000; loop_count != 0; loop_count--) {
|
||||
if (readl(&usbctlr->susp_ctrl) & USB_PHY_CLK_VALID)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
if (!loop_count)
|
||||
return -ETIMEDOUT;
|
||||
clrbits_le32(&usbctlr->susp_ctrl, USB_SUSP_CLR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int init_ulpi_usb_controller(struct fdt_usb *config,
|
||||
enum usb_init_type init)
|
||||
{
|
||||
printf("No code to set up ULPI controller, please enable"
|
||||
"CONFIG_USB_ULPI and CONFIG_USB_ULPI_VIEWPORT");
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void config_clock(const u32 timing[])
|
||||
{
|
||||
debug("%s: DIVM = %d, DIVN = %d, DIVP = %d, cpcon/lfcon = %d/%d\n",
|
||||
__func__, timing[PARAM_DIVM], timing[PARAM_DIVN],
|
||||
timing[PARAM_DIVP], timing[PARAM_CPCON], timing[PARAM_LFCON]);
|
||||
|
||||
clock_start_pll(CLOCK_ID_USB,
|
||||
timing[PARAM_DIVM], timing[PARAM_DIVN], timing[PARAM_DIVP],
|
||||
timing[PARAM_CPCON], timing[PARAM_LFCON]);
|
||||
}
|
||||
|
||||
static int fdt_decode_usb(struct udevice *dev, struct fdt_usb *config)
|
||||
{
|
||||
const void *blob = gd->fdt_blob;
|
||||
int node = dev->of_offset;
|
||||
const char *phy, *mode;
|
||||
|
||||
config->reg = (struct usb_ctlr *)dev_get_addr(dev);
|
||||
mode = fdt_getprop(blob, node, "dr_mode", NULL);
|
||||
if (mode) {
|
||||
if (0 == strcmp(mode, "host"))
|
||||
config->dr_mode = DR_MODE_HOST;
|
||||
else if (0 == strcmp(mode, "peripheral"))
|
||||
config->dr_mode = DR_MODE_DEVICE;
|
||||
else if (0 == strcmp(mode, "otg"))
|
||||
config->dr_mode = DR_MODE_OTG;
|
||||
else {
|
||||
debug("%s: Cannot decode dr_mode '%s'\n", __func__,
|
||||
mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
config->dr_mode = DR_MODE_HOST;
|
||||
}
|
||||
|
||||
phy = fdt_getprop(blob, node, "phy_type", NULL);
|
||||
config->utmi = phy && 0 == strcmp("utmi", phy);
|
||||
config->ulpi = phy && 0 == strcmp("ulpi", phy);
|
||||
config->enabled = fdtdec_get_is_enabled(blob, node);
|
||||
config->has_legacy_mode = fdtdec_get_bool(blob, node,
|
||||
"nvidia,has-legacy-mode");
|
||||
config->periph_id = clock_decode_periph_id(blob, node);
|
||||
if (config->periph_id == PERIPH_ID_NONE) {
|
||||
debug("%s: Missing/invalid peripheral ID\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
gpio_request_by_name_nodev(blob, node, "nvidia,vbus-gpio", 0,
|
||||
&config->vbus_gpio, GPIOD_IS_OUT);
|
||||
gpio_request_by_name_nodev(blob, node, "nvidia,phy-reset-gpio", 0,
|
||||
&config->phy_reset_gpio, GPIOD_IS_OUT);
|
||||
debug("enabled=%d, legacy_mode=%d, utmi=%d, ulpi=%d, periph_id=%d, "
|
||||
"vbus=%d, phy_reset=%d, dr_mode=%d\n",
|
||||
config->enabled, config->has_legacy_mode, config->utmi,
|
||||
config->ulpi, config->periph_id,
|
||||
gpio_get_number(&config->vbus_gpio),
|
||||
gpio_get_number(&config->phy_reset_gpio), config->dr_mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_common_init(struct fdt_usb *config, enum usb_init_type init)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (init) {
|
||||
case USB_INIT_HOST:
|
||||
switch (config->dr_mode) {
|
||||
case DR_MODE_HOST:
|
||||
case DR_MODE_OTG:
|
||||
break;
|
||||
default:
|
||||
printf("tegrausb: Invalid dr_mode %d for host mode\n",
|
||||
config->dr_mode);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case USB_INIT_DEVICE:
|
||||
if (config->periph_id != PERIPH_ID_USBD) {
|
||||
printf("tegrausb: Device mode only supported on first USB controller\n");
|
||||
return -1;
|
||||
}
|
||||
if (!config->utmi) {
|
||||
printf("tegrausb: Device mode only supported with UTMI PHY\n");
|
||||
return -1;
|
||||
}
|
||||
switch (config->dr_mode) {
|
||||
case DR_MODE_DEVICE:
|
||||
case DR_MODE_OTG:
|
||||
break;
|
||||
default:
|
||||
printf("tegrausb: Invalid dr_mode %d for device mode\n",
|
||||
config->dr_mode);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("tegrausb: Unknown USB_INIT_* %d\n", init);
|
||||
return -1;
|
||||
}
|
||||
|
||||
debug("%d, %d\n", config->utmi, config->ulpi);
|
||||
if (config->utmi)
|
||||
ret = init_utmi_usb_controller(config, init);
|
||||
else if (config->ulpi)
|
||||
ret = init_ulpi_usb_controller(config, init);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
set_up_vbus(config, init);
|
||||
|
||||
config->init_type = init;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_common_uninit(struct fdt_usb *priv)
|
||||
{
|
||||
struct usb_ctlr *usbctlr;
|
||||
|
||||
usbctlr = priv->reg;
|
||||
|
||||
/* Stop controller */
|
||||
writel(0, &usbctlr->usb_cmd);
|
||||
udelay(1000);
|
||||
|
||||
/* Initiate controller reset */
|
||||
writel(2, &usbctlr->usb_cmd);
|
||||
udelay(1000);
|
||||
}
|
||||
|
||||
static const struct ehci_ops tegra_ehci_ops = {
|
||||
.set_usb_mode = tegra_ehci_set_usbmode,
|
||||
.get_port_speed = tegra_ehci_get_port_speed,
|
||||
.powerup_fixup = tegra_ehci_powerup_fixup,
|
||||
};
|
||||
|
||||
static int ehci_usb_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct fdt_usb *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = fdt_decode_usb(dev, priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->type = dev_get_driver_data(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ehci_usb_probe(struct udevice *dev)
|
||||
{
|
||||
struct usb_platdata *plat = dev_get_platdata(dev);
|
||||
struct fdt_usb *priv = dev_get_priv(dev);
|
||||
struct ehci_hccr *hccr;
|
||||
struct ehci_hcor *hcor;
|
||||
static bool clk_done;
|
||||
int ret;
|
||||
|
||||
ret = usb_common_init(priv, plat->init_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
hccr = (struct ehci_hccr *)&priv->reg->cap_length;
|
||||
hcor = (struct ehci_hcor *)&priv->reg->usb_cmd;
|
||||
if (!clk_done) {
|
||||
config_clock(get_pll_timing(&fdt_usb_controllers[priv->type]));
|
||||
clk_done = true;
|
||||
}
|
||||
|
||||
return ehci_register(dev, hccr, hcor, &tegra_ehci_ops, 0,
|
||||
plat->init_type);
|
||||
}
|
||||
|
||||
static int ehci_usb_remove(struct udevice *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ehci_deregister(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id ehci_usb_ids[] = {
|
||||
{ .compatible = "nvidia,tegra20-ehci", .data = USB_CTLR_T20 },
|
||||
{ .compatible = "nvidia,tegra30-ehci", .data = USB_CTLR_T30 },
|
||||
{ .compatible = "nvidia,tegra114-ehci", .data = USB_CTLR_T114 },
|
||||
{ .compatible = "nvidia,tegra210-ehci", .data = USB_CTLR_T210 },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(usb_ehci) = {
|
||||
.name = "ehci_tegra",
|
||||
.id = UCLASS_USB,
|
||||
.of_match = ehci_usb_ids,
|
||||
.ofdata_to_platdata = ehci_usb_ofdata_to_platdata,
|
||||
.probe = ehci_usb_probe,
|
||||
.remove = ehci_usb_remove,
|
||||
.ops = &ehci_usb_ops,
|
||||
.platdata_auto_alloc_size = sizeof(struct usb_platdata),
|
||||
.priv_auto_alloc_size = sizeof(struct fdt_usb),
|
||||
.flags = DM_FLAG_ALLOC_PRIV_DMA,
|
||||
};
|
||||
45
u-boot/drivers/usb/host/ehci-vct.c
Normal file
45
u-boot/drivers/usb/host/ehci-vct.c
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* (C) Copyright 2009 Stefan Roese <sr@denx.de>, DENX Software Engineering
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <usb.h>
|
||||
|
||||
#include "ehci.h"
|
||||
|
||||
int vct_ehci_hcd_init(u32 *hccr, u32 *hcor);
|
||||
|
||||
/*
|
||||
* Create the appropriate control structures to manage
|
||||
* a new EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
int ret;
|
||||
u32 vct_hccr;
|
||||
u32 vct_hcor;
|
||||
|
||||
/*
|
||||
* Init VCT specific stuff
|
||||
*/
|
||||
ret = vct_ehci_hcd_init(&vct_hccr, &vct_hcor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*hccr = (struct ehci_hccr *)vct_hccr;
|
||||
*hcor = (struct ehci_hcor *)vct_hcor;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy the appropriate control structures corresponding
|
||||
* the the EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_stop(int index)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
177
u-boot/drivers/usb/host/ehci-vf.c
Normal file
177
u-boot/drivers/usb/host/ehci-vf.c
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Sanchayan Maity <sanchayan.maity@toradex.com>
|
||||
* Copyright (C) 2015 Toradex AG
|
||||
*
|
||||
* Based on ehci-mx6 driver
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <usb.h>
|
||||
#include <errno.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/arch/imx-regs.h>
|
||||
#include <asm/arch/crm_regs.h>
|
||||
#include <asm/imx-common/iomux-v3.h>
|
||||
#include <asm/imx-common/regs-usbphy.h>
|
||||
#include <usb/ehci-ci.h>
|
||||
|
||||
#include "ehci.h"
|
||||
|
||||
#define USB_NC_REG_OFFSET 0x00000800
|
||||
|
||||
#define ANADIG_PLL_CTRL_EN_USB_CLKS (1 << 6)
|
||||
|
||||
#define UCTRL_OVER_CUR_POL (1 << 8) /* OTG Polarity of Overcurrent */
|
||||
#define UCTRL_OVER_CUR_DIS (1 << 7) /* Disable OTG Overcurrent Detection */
|
||||
|
||||
/* USBCMD */
|
||||
#define UCMD_RUN_STOP (1 << 0) /* controller run/stop */
|
||||
#define UCMD_RESET (1 << 1) /* controller reset */
|
||||
|
||||
static const unsigned phy_bases[] = {
|
||||
USB_PHY0_BASE_ADDR,
|
||||
USB_PHY1_BASE_ADDR,
|
||||
};
|
||||
|
||||
static const unsigned nc_reg_bases[] = {
|
||||
USBC0_BASE_ADDR,
|
||||
USBC1_BASE_ADDR,
|
||||
};
|
||||
|
||||
static void usb_internal_phy_clock_gate(int index)
|
||||
{
|
||||
void __iomem *phy_reg;
|
||||
|
||||
phy_reg = (void __iomem *)phy_bases[index];
|
||||
clrbits_le32(phy_reg + USBPHY_CTRL, USBPHY_CTRL_CLKGATE);
|
||||
}
|
||||
|
||||
static void usb_power_config(int index)
|
||||
{
|
||||
struct anadig_reg __iomem *anadig =
|
||||
(struct anadig_reg __iomem *)ANADIG_BASE_ADDR;
|
||||
void __iomem *pll_ctrl;
|
||||
|
||||
switch (index) {
|
||||
case 0:
|
||||
pll_ctrl = &anadig->pll3_ctrl;
|
||||
clrbits_le32(pll_ctrl, ANADIG_PLL3_CTRL_BYPASS);
|
||||
setbits_le32(pll_ctrl, ANADIG_PLL3_CTRL_ENABLE
|
||||
| ANADIG_PLL3_CTRL_POWERDOWN
|
||||
| ANADIG_PLL_CTRL_EN_USB_CLKS);
|
||||
break;
|
||||
case 1:
|
||||
pll_ctrl = &anadig->pll7_ctrl;
|
||||
clrbits_le32(pll_ctrl, ANADIG_PLL7_CTRL_BYPASS);
|
||||
setbits_le32(pll_ctrl, ANADIG_PLL7_CTRL_ENABLE
|
||||
| ANADIG_PLL7_CTRL_POWERDOWN
|
||||
| ANADIG_PLL_CTRL_EN_USB_CLKS);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_phy_enable(int index, struct usb_ehci *ehci)
|
||||
{
|
||||
void __iomem *phy_reg;
|
||||
void __iomem *phy_ctrl;
|
||||
void __iomem *usb_cmd;
|
||||
|
||||
phy_reg = (void __iomem *)phy_bases[index];
|
||||
phy_ctrl = (void __iomem *)(phy_reg + USBPHY_CTRL);
|
||||
usb_cmd = (void __iomem *)&ehci->usbcmd;
|
||||
|
||||
/* Stop then Reset */
|
||||
clrbits_le32(usb_cmd, UCMD_RUN_STOP);
|
||||
while (readl(usb_cmd) & UCMD_RUN_STOP)
|
||||
;
|
||||
|
||||
setbits_le32(usb_cmd, UCMD_RESET);
|
||||
while (readl(usb_cmd) & UCMD_RESET)
|
||||
;
|
||||
|
||||
/* Reset USBPHY module */
|
||||
setbits_le32(phy_ctrl, USBPHY_CTRL_SFTRST);
|
||||
udelay(10);
|
||||
|
||||
/* Remove CLKGATE and SFTRST */
|
||||
clrbits_le32(phy_ctrl, USBPHY_CTRL_CLKGATE | USBPHY_CTRL_SFTRST);
|
||||
udelay(10);
|
||||
|
||||
/* Power up the PHY */
|
||||
writel(0, phy_reg + USBPHY_PWD);
|
||||
|
||||
/* Enable FS/LS device */
|
||||
setbits_le32(phy_ctrl, USBPHY_CTRL_ENUTMILEVEL2 |
|
||||
USBPHY_CTRL_ENUTMILEVEL3);
|
||||
}
|
||||
|
||||
static void usb_oc_config(int index)
|
||||
{
|
||||
void __iomem *ctrl;
|
||||
|
||||
ctrl = (void __iomem *)(nc_reg_bases[index] + USB_NC_REG_OFFSET);
|
||||
|
||||
setbits_le32(ctrl, UCTRL_OVER_CUR_POL);
|
||||
setbits_le32(ctrl, UCTRL_OVER_CUR_DIS);
|
||||
}
|
||||
|
||||
int __weak board_usb_phy_mode(int port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __weak board_ehci_hcd_init(int port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
struct usb_ehci *ehci;
|
||||
enum usb_init_type type;
|
||||
|
||||
if (index >= ARRAY_SIZE(nc_reg_bases))
|
||||
return -EINVAL;
|
||||
|
||||
ehci = (struct usb_ehci *)nc_reg_bases[index];
|
||||
|
||||
/* Do board specific initialisation */
|
||||
board_ehci_hcd_init(index);
|
||||
|
||||
usb_power_config(index);
|
||||
usb_oc_config(index);
|
||||
usb_internal_phy_clock_gate(index);
|
||||
usb_phy_enable(index, ehci);
|
||||
|
||||
*hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength);
|
||||
*hcor = (struct ehci_hcor *)((uint32_t)*hccr +
|
||||
HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
|
||||
|
||||
type = board_usb_phy_mode(index);
|
||||
if (type != init)
|
||||
return -ENODEV;
|
||||
|
||||
if (init == USB_INIT_DEVICE) {
|
||||
setbits_le32(&ehci->usbmode, CM_DEVICE);
|
||||
writel((PORT_PTS_UTMI | PORT_PTS_PTW), &ehci->portsc);
|
||||
setbits_le32(&ehci->portsc, USB_EN);
|
||||
} else if (init == USB_INIT_HOST) {
|
||||
setbits_le32(&ehci->usbmode, CM_HOST);
|
||||
writel((PORT_PTS_UTMI | PORT_PTS_PTW), &ehci->portsc);
|
||||
setbits_le32(&ehci->portsc, USB_EN);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ehci_hcd_stop(int index)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
104
u-boot/drivers/usb/host/ehci-zynq.c
Normal file
104
u-boot/drivers/usb/host/ehci-zynq.c
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* (C) Copyright 2014, Xilinx, Inc
|
||||
*
|
||||
* USB Low level initialization(Specific to zynq)
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <asm/arch/sys_proto.h>
|
||||
#include <asm/io.h>
|
||||
#include <usb.h>
|
||||
#include <usb/ehci-ci.h>
|
||||
#include <usb/ulpi.h>
|
||||
|
||||
#include "ehci.h"
|
||||
|
||||
#define ZYNQ_USB_USBCMD_RST 0x0000002
|
||||
#define ZYNQ_USB_USBCMD_STOP 0x0000000
|
||||
#define ZYNQ_USB_NUM_MIO 12
|
||||
|
||||
/*
|
||||
* Create the appropriate control structures to manage
|
||||
* a new EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_init(int index, enum usb_init_type init, struct ehci_hccr **hccr,
|
||||
struct ehci_hcor **hcor)
|
||||
{
|
||||
struct usb_ehci *ehci;
|
||||
struct ulpi_viewport ulpi_vp;
|
||||
int ret, mio_usb;
|
||||
/* Used for writing the ULPI data address */
|
||||
struct ulpi_regs *ulpi = (struct ulpi_regs *)0;
|
||||
|
||||
if (!index) {
|
||||
mio_usb = zynq_slcr_get_mio_pin_status("usb0");
|
||||
if (mio_usb != ZYNQ_USB_NUM_MIO) {
|
||||
printf("usb0 wrong num MIO: %d, Index %d\n", mio_usb,
|
||||
index);
|
||||
return -1;
|
||||
}
|
||||
ehci = (struct usb_ehci *)ZYNQ_USB_BASEADDR0;
|
||||
} else {
|
||||
mio_usb = zynq_slcr_get_mio_pin_status("usb1");
|
||||
if (mio_usb != ZYNQ_USB_NUM_MIO) {
|
||||
printf("usb1 wrong num MIO: %d, Index %d\n", mio_usb,
|
||||
index);
|
||||
return -1;
|
||||
}
|
||||
ehci = (struct usb_ehci *)ZYNQ_USB_BASEADDR1;
|
||||
}
|
||||
|
||||
*hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength);
|
||||
*hcor = (struct ehci_hcor *)((uint32_t) *hccr +
|
||||
HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
|
||||
|
||||
ulpi_vp.viewport_addr = (u32)&ehci->ulpi_viewpoint;
|
||||
ulpi_vp.port_num = 0;
|
||||
|
||||
ret = ulpi_init(&ulpi_vp);
|
||||
if (ret) {
|
||||
puts("zynq ULPI viewport init failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ULPI set flags */
|
||||
ulpi_write(&ulpi_vp, &ulpi->otg_ctrl,
|
||||
ULPI_OTG_DP_PULLDOWN | ULPI_OTG_DM_PULLDOWN |
|
||||
ULPI_OTG_EXTVBUSIND);
|
||||
ulpi_write(&ulpi_vp, &ulpi->function_ctrl,
|
||||
ULPI_FC_FULL_SPEED | ULPI_FC_OPMODE_NORMAL |
|
||||
ULPI_FC_SUSPENDM);
|
||||
ulpi_write(&ulpi_vp, &ulpi->iface_ctrl, 0);
|
||||
|
||||
/* Set VBus */
|
||||
ulpi_write(&ulpi_vp, &ulpi->otg_ctrl_set,
|
||||
ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy the appropriate control structures corresponding
|
||||
* the the EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_stop(int index)
|
||||
{
|
||||
struct usb_ehci *ehci;
|
||||
|
||||
if (!index)
|
||||
ehci = (struct usb_ehci *)ZYNQ_USB_BASEADDR0;
|
||||
else
|
||||
ehci = (struct usb_ehci *)ZYNQ_USB_BASEADDR1;
|
||||
|
||||
/* Stop controller */
|
||||
writel(ZYNQ_USB_USBCMD_STOP, &ehci->usbcmd);
|
||||
udelay(1000);
|
||||
|
||||
/* Initiate controller reset */
|
||||
writel(ZYNQ_USB_USBCMD_RST, &ehci->usbcmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
295
u-boot/drivers/usb/host/ehci.h
Normal file
295
u-boot/drivers/usb/host/ehci.h
Normal file
@@ -0,0 +1,295 @@
|
||||
/*-
|
||||
* Copyright (c) 2007-2008, Juniper Networks, Inc.
|
||||
* Copyright (c) 2008, Michael Trimarchi <trimarchimichael@yahoo.it>
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#ifndef USB_EHCI_H
|
||||
#define USB_EHCI_H
|
||||
|
||||
#include <usb.h>
|
||||
|
||||
#if !defined(CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS)
|
||||
#define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS 2
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Register Space.
|
||||
*/
|
||||
struct ehci_hccr {
|
||||
uint32_t cr_capbase;
|
||||
#define HC_LENGTH(p) (((p) >> 0) & 0x00ff)
|
||||
#define HC_VERSION(p) (((p) >> 16) & 0xffff)
|
||||
uint32_t cr_hcsparams;
|
||||
#define HCS_PPC(p) ((p) & (1 << 4))
|
||||
#define HCS_INDICATOR(p) ((p) & (1 << 16)) /* Port indicators */
|
||||
#define HCS_N_PORTS(p) (((p) >> 0) & 0xf)
|
||||
uint32_t cr_hccparams;
|
||||
uint8_t cr_hcsp_portrt[8];
|
||||
} __attribute__ ((packed, aligned(4)));
|
||||
|
||||
struct ehci_hcor {
|
||||
uint32_t or_usbcmd;
|
||||
#define CMD_PARK (1 << 11) /* enable "park" */
|
||||
#define CMD_PARK_CNT(c) (((c) >> 8) & 3) /* how many transfers to park */
|
||||
#define CMD_LRESET (1 << 7) /* partial reset */
|
||||
#define CMD_IAAD (1 << 6) /* "doorbell" interrupt */
|
||||
#define CMD_ASE (1 << 5) /* async schedule enable */
|
||||
#define CMD_PSE (1 << 4) /* periodic schedule enable */
|
||||
#define CMD_RESET (1 << 1) /* reset HC not bus */
|
||||
#define CMD_RUN (1 << 0) /* start/stop HC */
|
||||
uint32_t or_usbsts;
|
||||
#define STS_ASS (1 << 15)
|
||||
#define STS_PSS (1 << 14)
|
||||
#define STS_HALT (1 << 12)
|
||||
uint32_t or_usbintr;
|
||||
#define INTR_UE (1 << 0) /* USB interrupt enable */
|
||||
#define INTR_UEE (1 << 1) /* USB error interrupt enable */
|
||||
#define INTR_PCE (1 << 2) /* Port change detect enable */
|
||||
#define INTR_SEE (1 << 4) /* system error enable */
|
||||
#define INTR_AAE (1 << 5) /* Interrupt on async adavance enable */
|
||||
uint32_t or_frindex;
|
||||
uint32_t or_ctrldssegment;
|
||||
uint32_t or_periodiclistbase;
|
||||
uint32_t or_asynclistaddr;
|
||||
uint32_t _reserved_0_;
|
||||
uint32_t or_burstsize;
|
||||
uint32_t or_txfilltuning;
|
||||
#define TXFIFO_THRESH_MASK (0x3f << 16)
|
||||
#define TXFIFO_THRESH(p) ((p & 0x3f) << 16)
|
||||
uint32_t _reserved_1_[6];
|
||||
uint32_t or_configflag;
|
||||
#define FLAG_CF (1 << 0) /* true: we'll support "high speed" */
|
||||
uint32_t or_portsc[CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS];
|
||||
#define PORTSC_PSPD(x) (((x) >> 26) & 0x3)
|
||||
#define PORTSC_PSPD_FS 0x0
|
||||
#define PORTSC_PSPD_LS 0x1
|
||||
#define PORTSC_PSPD_HS 0x2
|
||||
uint32_t or_systune;
|
||||
} __attribute__ ((packed, aligned(4)));
|
||||
|
||||
#define USBMODE 0x68 /* USB Device mode */
|
||||
#define USBMODE_SDIS (1 << 3) /* Stream disable */
|
||||
#define USBMODE_BE (1 << 2) /* BE/LE endiannes select */
|
||||
#define USBMODE_CM_HC (3 << 0) /* host controller mode */
|
||||
#define USBMODE_CM_IDLE (0 << 0) /* idle state */
|
||||
|
||||
/* Interface descriptor */
|
||||
struct usb_linux_interface_descriptor {
|
||||
unsigned char bLength;
|
||||
unsigned char bDescriptorType;
|
||||
unsigned char bInterfaceNumber;
|
||||
unsigned char bAlternateSetting;
|
||||
unsigned char bNumEndpoints;
|
||||
unsigned char bInterfaceClass;
|
||||
unsigned char bInterfaceSubClass;
|
||||
unsigned char bInterfaceProtocol;
|
||||
unsigned char iInterface;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Configuration descriptor information.. */
|
||||
struct usb_linux_config_descriptor {
|
||||
unsigned char bLength;
|
||||
unsigned char bDescriptorType;
|
||||
unsigned short wTotalLength;
|
||||
unsigned char bNumInterfaces;
|
||||
unsigned char bConfigurationValue;
|
||||
unsigned char iConfiguration;
|
||||
unsigned char bmAttributes;
|
||||
unsigned char MaxPower;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#if defined CONFIG_EHCI_DESC_BIG_ENDIAN
|
||||
#define ehci_readl(x) cpu_to_be32((*((volatile u32 *)(x))))
|
||||
#define ehci_writel(a, b) (*((volatile u32 *)(a)) = \
|
||||
cpu_to_be32(((volatile u32)b)))
|
||||
#else
|
||||
#define ehci_readl(x) cpu_to_le32((*((volatile u32 *)(x))))
|
||||
#define ehci_writel(a, b) (*((volatile u32 *)(a)) = \
|
||||
cpu_to_le32(((volatile u32)b)))
|
||||
#endif
|
||||
|
||||
#if defined CONFIG_EHCI_MMIO_BIG_ENDIAN
|
||||
#define hc32_to_cpu(x) be32_to_cpu((x))
|
||||
#define cpu_to_hc32(x) cpu_to_be32((x))
|
||||
#else
|
||||
#define hc32_to_cpu(x) le32_to_cpu((x))
|
||||
#define cpu_to_hc32(x) cpu_to_le32((x))
|
||||
#endif
|
||||
|
||||
#define EHCI_PS_WKOC_E (1 << 22) /* RW wake on over current */
|
||||
#define EHCI_PS_WKDSCNNT_E (1 << 21) /* RW wake on disconnect */
|
||||
#define EHCI_PS_WKCNNT_E (1 << 20) /* RW wake on connect */
|
||||
#define EHCI_PS_PO (1 << 13) /* RW port owner */
|
||||
#define EHCI_PS_PP (1 << 12) /* RW,RO port power */
|
||||
#define EHCI_PS_LS (3 << 10) /* RO line status */
|
||||
#define EHCI_PS_PR (1 << 8) /* RW port reset */
|
||||
#define EHCI_PS_SUSP (1 << 7) /* RW suspend */
|
||||
#define EHCI_PS_FPR (1 << 6) /* RW force port resume */
|
||||
#define EHCI_PS_OCC (1 << 5) /* RWC over current change */
|
||||
#define EHCI_PS_OCA (1 << 4) /* RO over current active */
|
||||
#define EHCI_PS_PEC (1 << 3) /* RWC port enable change */
|
||||
#define EHCI_PS_PE (1 << 2) /* RW port enable */
|
||||
#define EHCI_PS_CSC (1 << 1) /* RWC connect status change */
|
||||
#define EHCI_PS_CS (1 << 0) /* RO connect status */
|
||||
#define EHCI_PS_CLEAR (EHCI_PS_OCC | EHCI_PS_PEC | EHCI_PS_CSC)
|
||||
|
||||
#define EHCI_PS_IS_LOWSPEED(x) (((x) & EHCI_PS_LS) == (1 << 10))
|
||||
|
||||
/*
|
||||
* Schedule Interface Space.
|
||||
*
|
||||
* IMPORTANT: Software must ensure that no interface data structure
|
||||
* reachable by the EHCI host controller spans a 4K page boundary!
|
||||
*
|
||||
* Periodic transfers (i.e. isochronous and interrupt transfers) are
|
||||
* not supported.
|
||||
*/
|
||||
|
||||
/* Queue Element Transfer Descriptor (qTD). */
|
||||
struct qTD {
|
||||
/* this part defined by EHCI spec */
|
||||
uint32_t qt_next; /* see EHCI 3.5.1 */
|
||||
#define QT_NEXT_TERMINATE 1
|
||||
uint32_t qt_altnext; /* see EHCI 3.5.2 */
|
||||
uint32_t qt_token; /* see EHCI 3.5.3 */
|
||||
#define QT_TOKEN_DT(x) (((x) & 0x1) << 31) /* Data Toggle */
|
||||
#define QT_TOKEN_GET_DT(x) (((x) >> 31) & 0x1)
|
||||
#define QT_TOKEN_TOTALBYTES(x) (((x) & 0x7fff) << 16) /* Total Bytes to Transfer */
|
||||
#define QT_TOKEN_GET_TOTALBYTES(x) (((x) >> 16) & 0x7fff)
|
||||
#define QT_TOKEN_IOC(x) (((x) & 0x1) << 15) /* Interrupt On Complete */
|
||||
#define QT_TOKEN_CPAGE(x) (((x) & 0x7) << 12) /* Current Page */
|
||||
#define QT_TOKEN_CERR(x) (((x) & 0x3) << 10) /* Error Counter */
|
||||
#define QT_TOKEN_PID(x) (((x) & 0x3) << 8) /* PID Code */
|
||||
#define QT_TOKEN_PID_OUT 0x0
|
||||
#define QT_TOKEN_PID_IN 0x1
|
||||
#define QT_TOKEN_PID_SETUP 0x2
|
||||
#define QT_TOKEN_STATUS(x) (((x) & 0xff) << 0) /* Status */
|
||||
#define QT_TOKEN_GET_STATUS(x) (((x) >> 0) & 0xff)
|
||||
#define QT_TOKEN_STATUS_ACTIVE 0x80
|
||||
#define QT_TOKEN_STATUS_HALTED 0x40
|
||||
#define QT_TOKEN_STATUS_DATBUFERR 0x20
|
||||
#define QT_TOKEN_STATUS_BABBLEDET 0x10
|
||||
#define QT_TOKEN_STATUS_XACTERR 0x08
|
||||
#define QT_TOKEN_STATUS_MISSEDUFRAME 0x04
|
||||
#define QT_TOKEN_STATUS_SPLITXSTATE 0x02
|
||||
#define QT_TOKEN_STATUS_PERR 0x01
|
||||
#define QT_BUFFER_CNT 5
|
||||
uint32_t qt_buffer[QT_BUFFER_CNT]; /* see EHCI 3.5.4 */
|
||||
uint32_t qt_buffer_hi[QT_BUFFER_CNT]; /* Appendix B */
|
||||
/* pad struct for 32 byte alignment */
|
||||
uint32_t unused[3];
|
||||
};
|
||||
|
||||
#define EHCI_PAGE_SIZE 4096
|
||||
|
||||
/* Queue Head (QH). */
|
||||
struct QH {
|
||||
uint32_t qh_link;
|
||||
#define QH_LINK_TERMINATE 1
|
||||
#define QH_LINK_TYPE_ITD 0
|
||||
#define QH_LINK_TYPE_QH 2
|
||||
#define QH_LINK_TYPE_SITD 4
|
||||
#define QH_LINK_TYPE_FSTN 6
|
||||
uint32_t qh_endpt1;
|
||||
#define QH_ENDPT1_RL(x) (((x) & 0xf) << 28) /* NAK Count Reload */
|
||||
#define QH_ENDPT1_C(x) (((x) & 0x1) << 27) /* Control Endpoint Flag */
|
||||
#define QH_ENDPT1_MAXPKTLEN(x) (((x) & 0x7ff) << 16) /* Maximum Packet Length */
|
||||
#define QH_ENDPT1_H(x) (((x) & 0x1) << 15) /* Head of Reclamation List Flag */
|
||||
#define QH_ENDPT1_DTC(x) (((x) & 0x1) << 14) /* Data Toggle Control */
|
||||
#define QH_ENDPT1_DTC_IGNORE_QTD_TD 0x0
|
||||
#define QH_ENDPT1_DTC_DT_FROM_QTD 0x1
|
||||
#define QH_ENDPT1_EPS(x) (((x) & 0x3) << 12) /* Endpoint Speed */
|
||||
#define QH_ENDPT1_EPS_FS 0x0
|
||||
#define QH_ENDPT1_EPS_LS 0x1
|
||||
#define QH_ENDPT1_EPS_HS 0x2
|
||||
#define QH_ENDPT1_ENDPT(x) (((x) & 0xf) << 8) /* Endpoint Number */
|
||||
#define QH_ENDPT1_I(x) (((x) & 0x1) << 7) /* Inactivate on Next Transaction */
|
||||
#define QH_ENDPT1_DEVADDR(x) (((x) & 0x7f) << 0) /* Device Address */
|
||||
uint32_t qh_endpt2;
|
||||
#define QH_ENDPT2_MULT(x) (((x) & 0x3) << 30) /* High-Bandwidth Pipe Multiplier */
|
||||
#define QH_ENDPT2_PORTNUM(x) (((x) & 0x7f) << 23) /* Port Number */
|
||||
#define QH_ENDPT2_HUBADDR(x) (((x) & 0x7f) << 16) /* Hub Address */
|
||||
#define QH_ENDPT2_UFCMASK(x) (((x) & 0xff) << 8) /* Split Completion Mask */
|
||||
#define QH_ENDPT2_UFSMASK(x) (((x) & 0xff) << 0) /* Interrupt Schedule Mask */
|
||||
uint32_t qh_curtd;
|
||||
struct qTD qh_overlay;
|
||||
/*
|
||||
* Add dummy fill value to make the size of this struct
|
||||
* aligned to 32 bytes
|
||||
*/
|
||||
union {
|
||||
uint32_t fill[4];
|
||||
void *buffer;
|
||||
};
|
||||
};
|
||||
|
||||
/* Tweak flags for EHCI, used to control operation */
|
||||
enum {
|
||||
/* don't use or_configflag in init */
|
||||
EHCI_TWEAK_NO_INIT_CF = 1 << 0,
|
||||
};
|
||||
|
||||
struct ehci_ctrl;
|
||||
|
||||
struct ehci_ops {
|
||||
void (*set_usb_mode)(struct ehci_ctrl *ctrl);
|
||||
int (*get_port_speed)(struct ehci_ctrl *ctrl, uint32_t reg);
|
||||
void (*powerup_fixup)(struct ehci_ctrl *ctrl, uint32_t *status_reg,
|
||||
uint32_t *reg);
|
||||
uint32_t *(*get_portsc_register)(struct ehci_ctrl *ctrl, int port);
|
||||
int (*init_after_reset)(struct ehci_ctrl *ctrl);
|
||||
};
|
||||
|
||||
struct ehci_ctrl {
|
||||
enum usb_init_type init;
|
||||
struct ehci_hccr *hccr; /* R/O registers, not need for volatile */
|
||||
struct ehci_hcor *hcor;
|
||||
int rootdev;
|
||||
uint16_t portreset;
|
||||
struct QH qh_list __aligned(USB_DMA_MINALIGN);
|
||||
struct QH periodic_queue __aligned(USB_DMA_MINALIGN);
|
||||
uint32_t *periodic_list;
|
||||
int periodic_schedules;
|
||||
int ntds;
|
||||
struct ehci_ops ops;
|
||||
void *priv; /* client's private data */
|
||||
};
|
||||
|
||||
/**
|
||||
* ehci_set_controller_info() - Set up private data for the controller
|
||||
*
|
||||
* This function can be called in ehci_hcd_init() to tell the EHCI layer
|
||||
* about the controller's private data pointer. Then in the above functions
|
||||
* this can be accessed given the struct ehci_ctrl pointer. Also special
|
||||
* EHCI operation methods can be provided if required
|
||||
*
|
||||
* @index: Controller number to set
|
||||
* @priv: Controller pointer
|
||||
* @ops: Controller operations, or NULL to use default
|
||||
*/
|
||||
void ehci_set_controller_priv(int index, void *priv,
|
||||
const struct ehci_ops *ops);
|
||||
|
||||
/**
|
||||
* ehci_get_controller_priv() - Get controller private data
|
||||
*
|
||||
* @index Controller number to get
|
||||
* @return controller pointer for this index
|
||||
*/
|
||||
void *ehci_get_controller_priv(int index);
|
||||
|
||||
/* Low level init functions */
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor);
|
||||
int ehci_hcd_stop(int index);
|
||||
|
||||
int ehci_register(struct udevice *dev, struct ehci_hccr *hccr,
|
||||
struct ehci_hcor *hcor, const struct ehci_ops *ops,
|
||||
uint tweaks, enum usb_init_type init);
|
||||
int ehci_deregister(struct udevice *dev);
|
||||
extern struct dm_usb_ops ehci_usb_ops;
|
||||
|
||||
#endif /* USB_EHCI_H */
|
||||
1323
u-boot/drivers/usb/host/isp116x-hcd.c
Normal file
1323
u-boot/drivers/usb/host/isp116x-hcd.c
Normal file
File diff suppressed because it is too large
Load Diff
476
u-boot/drivers/usb/host/isp116x.h
Normal file
476
u-boot/drivers/usb/host/isp116x.h
Normal file
@@ -0,0 +1,476 @@
|
||||
/*
|
||||
* ISP116x register declarations and HCD data structures
|
||||
*
|
||||
* Copyright (C) 2007 Rodolfo Giometti <giometti@linux.it>
|
||||
* Copyright (C) 2007 Eurotech S.p.A. <info@eurotech.it>
|
||||
* Copyright (C) 2005 Olav Kongas <ok@artecdesign.ee>
|
||||
* Portions:
|
||||
* Copyright (C) 2004 Lothar Wassmann
|
||||
* Copyright (C) 2004 Psion Teklogix
|
||||
* Copyright (C) 2004 David Brownell
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(fmt, args...) \
|
||||
printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args)
|
||||
#else
|
||||
#define DBG(fmt, args...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifdef VERBOSE
|
||||
# define VDBG DBG
|
||||
#else
|
||||
# define VDBG(fmt, args...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#define ERR(fmt, args...) \
|
||||
printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args)
|
||||
#define WARN(fmt, args...) \
|
||||
printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args)
|
||||
#define INFO(fmt, args...) \
|
||||
printf("isp116x: " fmt "\n" , ## args)
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/* us of 1ms frame */
|
||||
#define MAX_LOAD_LIMIT 850
|
||||
|
||||
/* Full speed: max # of bytes to transfer for a single urb
|
||||
at a time must be < 1024 && must be multiple of 64.
|
||||
832 allows transfering 4kiB within 5 frames. */
|
||||
#define MAX_TRANSFER_SIZE_FULLSPEED 832
|
||||
|
||||
/* Low speed: there is no reason to schedule in very big
|
||||
chunks; often the requested long transfers are for
|
||||
string descriptors containing short strings. */
|
||||
#define MAX_TRANSFER_SIZE_LOWSPEED 64
|
||||
|
||||
/* Bytetime (us), a rough indication of how much time it
|
||||
would take to transfer a byte of useful data over USB */
|
||||
#define BYTE_TIME_FULLSPEED 1
|
||||
#define BYTE_TIME_LOWSPEED 20
|
||||
|
||||
/* Buffer sizes */
|
||||
#define ISP116x_BUF_SIZE 4096
|
||||
#define ISP116x_ITL_BUFSIZE 0
|
||||
#define ISP116x_ATL_BUFSIZE ((ISP116x_BUF_SIZE) - 2*(ISP116x_ITL_BUFSIZE))
|
||||
|
||||
#define ISP116x_WRITE_OFFSET 0x80
|
||||
|
||||
/* --- ISP116x registers/bits ---------------------------------------------- */
|
||||
|
||||
#define HCREVISION 0x00
|
||||
#define HCCONTROL 0x01
|
||||
#define HCCONTROL_HCFS (3 << 6) /* host controller
|
||||
functional state */
|
||||
#define HCCONTROL_USB_RESET (0 << 6)
|
||||
#define HCCONTROL_USB_RESUME (1 << 6)
|
||||
#define HCCONTROL_USB_OPER (2 << 6)
|
||||
#define HCCONTROL_USB_SUSPEND (3 << 6)
|
||||
#define HCCONTROL_RWC (1 << 9) /* remote wakeup connected */
|
||||
#define HCCONTROL_RWE (1 << 10) /* remote wakeup enable */
|
||||
#define HCCMDSTAT 0x02
|
||||
#define HCCMDSTAT_HCR (1 << 0) /* host controller reset */
|
||||
#define HCCMDSTAT_SOC (3 << 16) /* scheduling overrun count */
|
||||
#define HCINTSTAT 0x03
|
||||
#define HCINT_SO (1 << 0) /* scheduling overrun */
|
||||
#define HCINT_WDH (1 << 1) /* writeback of done_head */
|
||||
#define HCINT_SF (1 << 2) /* start frame */
|
||||
#define HCINT_RD (1 << 3) /* resume detect */
|
||||
#define HCINT_UE (1 << 4) /* unrecoverable error */
|
||||
#define HCINT_FNO (1 << 5) /* frame number overflow */
|
||||
#define HCINT_RHSC (1 << 6) /* root hub status change */
|
||||
#define HCINT_OC (1 << 30) /* ownership change */
|
||||
#define HCINT_MIE (1 << 31) /* master interrupt enable */
|
||||
#define HCINTENB 0x04
|
||||
#define HCINTDIS 0x05
|
||||
#define HCFMINTVL 0x0d
|
||||
#define HCFMREM 0x0e
|
||||
#define HCFMNUM 0x0f
|
||||
#define HCLSTHRESH 0x11
|
||||
#define HCRHDESCA 0x12
|
||||
#define RH_A_NDP (0x3 << 0) /* # downstream ports */
|
||||
#define RH_A_PSM (1 << 8) /* power switching mode */
|
||||
#define RH_A_NPS (1 << 9) /* no power switching */
|
||||
#define RH_A_DT (1 << 10) /* device type (mbz) */
|
||||
#define RH_A_OCPM (1 << 11) /* overcurrent protection
|
||||
mode */
|
||||
#define RH_A_NOCP (1 << 12) /* no overcurrent protection */
|
||||
#define RH_A_POTPGT (0xff << 24) /* power on -> power good
|
||||
time */
|
||||
#define HCRHDESCB 0x13
|
||||
#define RH_B_DR (0xffff << 0) /* device removable flags */
|
||||
#define RH_B_PPCM (0xffff << 16) /* port power control mask */
|
||||
#define HCRHSTATUS 0x14
|
||||
#define RH_HS_LPS (1 << 0) /* local power status */
|
||||
#define RH_HS_OCI (1 << 1) /* over current indicator */
|
||||
#define RH_HS_DRWE (1 << 15) /* device remote wakeup
|
||||
enable */
|
||||
#define RH_HS_LPSC (1 << 16) /* local power status change */
|
||||
#define RH_HS_OCIC (1 << 17) /* over current indicator
|
||||
change */
|
||||
#define RH_HS_CRWE (1 << 31) /* clear remote wakeup
|
||||
enable */
|
||||
#define HCRHPORT1 0x15
|
||||
#define RH_PS_CCS (1 << 0) /* current connect status */
|
||||
#define RH_PS_PES (1 << 1) /* port enable status */
|
||||
#define RH_PS_PSS (1 << 2) /* port suspend status */
|
||||
#define RH_PS_POCI (1 << 3) /* port over current
|
||||
indicator */
|
||||
#define RH_PS_PRS (1 << 4) /* port reset status */
|
||||
#define RH_PS_PPS (1 << 8) /* port power status */
|
||||
#define RH_PS_LSDA (1 << 9) /* low speed device attached */
|
||||
#define RH_PS_CSC (1 << 16) /* connect status change */
|
||||
#define RH_PS_PESC (1 << 17) /* port enable status change */
|
||||
#define RH_PS_PSSC (1 << 18) /* port suspend status
|
||||
change */
|
||||
#define RH_PS_OCIC (1 << 19) /* over current indicator
|
||||
change */
|
||||
#define RH_PS_PRSC (1 << 20) /* port reset status change */
|
||||
#define HCRHPORT_CLRMASK (0x1f << 16)
|
||||
#define HCRHPORT2 0x16
|
||||
#define HCHWCFG 0x20
|
||||
#define HCHWCFG_15KRSEL (1 << 12)
|
||||
#define HCHWCFG_CLKNOTSTOP (1 << 11)
|
||||
#define HCHWCFG_ANALOG_OC (1 << 10)
|
||||
#define HCHWCFG_DACK_MODE (1 << 8)
|
||||
#define HCHWCFG_EOT_POL (1 << 7)
|
||||
#define HCHWCFG_DACK_POL (1 << 6)
|
||||
#define HCHWCFG_DREQ_POL (1 << 5)
|
||||
#define HCHWCFG_DBWIDTH_MASK (0x03 << 3)
|
||||
#define HCHWCFG_DBWIDTH(n) (((n) << 3) & HCHWCFG_DBWIDTH_MASK)
|
||||
#define HCHWCFG_INT_POL (1 << 2)
|
||||
#define HCHWCFG_INT_TRIGGER (1 << 1)
|
||||
#define HCHWCFG_INT_ENABLE (1 << 0)
|
||||
#define HCDMACFG 0x21
|
||||
#define HCDMACFG_BURST_LEN_MASK (0x03 << 5)
|
||||
#define HCDMACFG_BURST_LEN(n) (((n) << 5) & HCDMACFG_BURST_LEN_MASK)
|
||||
#define HCDMACFG_BURST_LEN_1 HCDMACFG_BURST_LEN(0)
|
||||
#define HCDMACFG_BURST_LEN_4 HCDMACFG_BURST_LEN(1)
|
||||
#define HCDMACFG_BURST_LEN_8 HCDMACFG_BURST_LEN(2)
|
||||
#define HCDMACFG_DMA_ENABLE (1 << 4)
|
||||
#define HCDMACFG_BUF_TYPE_MASK (0x07 << 1)
|
||||
#define HCDMACFG_CTR_SEL (1 << 2)
|
||||
#define HCDMACFG_ITLATL_SEL (1 << 1)
|
||||
#define HCDMACFG_DMA_RW_SELECT (1 << 0)
|
||||
#define HCXFERCTR 0x22
|
||||
#define HCuPINT 0x24
|
||||
#define HCuPINT_SOF (1 << 0)
|
||||
#define HCuPINT_ATL (1 << 1)
|
||||
#define HCuPINT_AIIEOT (1 << 2)
|
||||
#define HCuPINT_OPR (1 << 4)
|
||||
#define HCuPINT_SUSP (1 << 5)
|
||||
#define HCuPINT_CLKRDY (1 << 6)
|
||||
#define HCuPINTENB 0x25
|
||||
#define HCCHIPID 0x27
|
||||
#define HCCHIPID_MASK 0xff00
|
||||
#define HCCHIPID_MAGIC 0x6100
|
||||
#define HCSCRATCH 0x28
|
||||
#define HCSWRES 0x29
|
||||
#define HCSWRES_MAGIC 0x00f6
|
||||
#define HCITLBUFLEN 0x2a
|
||||
#define HCATLBUFLEN 0x2b
|
||||
#define HCBUFSTAT 0x2c
|
||||
#define HCBUFSTAT_ITL0_FULL (1 << 0)
|
||||
#define HCBUFSTAT_ITL1_FULL (1 << 1)
|
||||
#define HCBUFSTAT_ATL_FULL (1 << 2)
|
||||
#define HCBUFSTAT_ITL0_DONE (1 << 3)
|
||||
#define HCBUFSTAT_ITL1_DONE (1 << 4)
|
||||
#define HCBUFSTAT_ATL_DONE (1 << 5)
|
||||
#define HCRDITL0LEN 0x2d
|
||||
#define HCRDITL1LEN 0x2e
|
||||
#define HCITLPORT 0x40
|
||||
#define HCATLPORT 0x41
|
||||
|
||||
/* PTD accessor macros. */
|
||||
#define PTD_GET_COUNT(p) (((p)->count & PTD_COUNT_MSK) >> 0)
|
||||
#define PTD_COUNT(v) (((v) << 0) & PTD_COUNT_MSK)
|
||||
#define PTD_GET_TOGGLE(p) (((p)->count & PTD_TOGGLE_MSK) >> 10)
|
||||
#define PTD_TOGGLE(v) (((v) << 10) & PTD_TOGGLE_MSK)
|
||||
#define PTD_GET_ACTIVE(p) (((p)->count & PTD_ACTIVE_MSK) >> 11)
|
||||
#define PTD_ACTIVE(v) (((v) << 11) & PTD_ACTIVE_MSK)
|
||||
#define PTD_GET_CC(p) (((p)->count & PTD_CC_MSK) >> 12)
|
||||
#define PTD_CC(v) (((v) << 12) & PTD_CC_MSK)
|
||||
#define PTD_GET_MPS(p) (((p)->mps & PTD_MPS_MSK) >> 0)
|
||||
#define PTD_MPS(v) (((v) << 0) & PTD_MPS_MSK)
|
||||
#define PTD_GET_SPD(p) (((p)->mps & PTD_SPD_MSK) >> 10)
|
||||
#define PTD_SPD(v) (((v) << 10) & PTD_SPD_MSK)
|
||||
#define PTD_GET_LAST(p) (((p)->mps & PTD_LAST_MSK) >> 11)
|
||||
#define PTD_LAST(v) (((v) << 11) & PTD_LAST_MSK)
|
||||
#define PTD_GET_EP(p) (((p)->mps & PTD_EP_MSK) >> 12)
|
||||
#define PTD_EP(v) (((v) << 12) & PTD_EP_MSK)
|
||||
#define PTD_GET_LEN(p) (((p)->len & PTD_LEN_MSK) >> 0)
|
||||
#define PTD_LEN(v) (((v) << 0) & PTD_LEN_MSK)
|
||||
#define PTD_GET_DIR(p) (((p)->len & PTD_DIR_MSK) >> 10)
|
||||
#define PTD_DIR(v) (((v) << 10) & PTD_DIR_MSK)
|
||||
#define PTD_GET_B5_5(p) (((p)->len & PTD_B5_5_MSK) >> 13)
|
||||
#define PTD_B5_5(v) (((v) << 13) & PTD_B5_5_MSK)
|
||||
#define PTD_GET_FA(p) (((p)->faddr & PTD_FA_MSK) >> 0)
|
||||
#define PTD_FA(v) (((v) << 0) & PTD_FA_MSK)
|
||||
#define PTD_GET_FMT(p) (((p)->faddr & PTD_FMT_MSK) >> 7)
|
||||
#define PTD_FMT(v) (((v) << 7) & PTD_FMT_MSK)
|
||||
|
||||
/* Hardware transfer status codes -- CC from ptd->count */
|
||||
#define TD_CC_NOERROR 0x00
|
||||
#define TD_CC_CRC 0x01
|
||||
#define TD_CC_BITSTUFFING 0x02
|
||||
#define TD_CC_DATATOGGLEM 0x03
|
||||
#define TD_CC_STALL 0x04
|
||||
#define TD_DEVNOTRESP 0x05
|
||||
#define TD_PIDCHECKFAIL 0x06
|
||||
#define TD_UNEXPECTEDPID 0x07
|
||||
#define TD_DATAOVERRUN 0x08
|
||||
#define TD_DATAUNDERRUN 0x09
|
||||
/* 0x0A, 0x0B reserved for hardware */
|
||||
#define TD_BUFFEROVERRUN 0x0C
|
||||
#define TD_BUFFERUNDERRUN 0x0D
|
||||
/* 0x0E, 0x0F reserved for HCD */
|
||||
#define TD_NOTACCESSED 0x0F
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */
|
||||
#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE)
|
||||
|
||||
/* Philips transfer descriptor */
|
||||
struct ptd {
|
||||
u16 count;
|
||||
#define PTD_COUNT_MSK (0x3ff << 0)
|
||||
#define PTD_TOGGLE_MSK (1 << 10)
|
||||
#define PTD_ACTIVE_MSK (1 << 11)
|
||||
#define PTD_CC_MSK (0xf << 12)
|
||||
u16 mps;
|
||||
#define PTD_MPS_MSK (0x3ff << 0)
|
||||
#define PTD_SPD_MSK (1 << 10)
|
||||
#define PTD_LAST_MSK (1 << 11)
|
||||
#define PTD_EP_MSK (0xf << 12)
|
||||
u16 len;
|
||||
#define PTD_LEN_MSK (0x3ff << 0)
|
||||
#define PTD_DIR_MSK (3 << 10)
|
||||
#define PTD_DIR_SETUP (0)
|
||||
#define PTD_DIR_OUT (1)
|
||||
#define PTD_DIR_IN (2)
|
||||
#define PTD_B5_5_MSK (1 << 13)
|
||||
u16 faddr;
|
||||
#define PTD_FA_MSK (0x7f << 0)
|
||||
#define PTD_FMT_MSK (1 << 7)
|
||||
} __attribute__ ((packed, aligned(2)));
|
||||
|
||||
struct isp116x_ep {
|
||||
struct usb_device *udev;
|
||||
struct ptd ptd;
|
||||
|
||||
u8 maxpacket;
|
||||
u8 epnum;
|
||||
u8 nextpid;
|
||||
|
||||
u16 length; /* of current packet */
|
||||
unsigned char *data; /* to databuf */
|
||||
|
||||
u16 error_count;
|
||||
};
|
||||
|
||||
/* URB struct */
|
||||
#define N_URB_TD 48
|
||||
#define URB_DEL 1
|
||||
typedef struct {
|
||||
struct isp116x_ep *ed;
|
||||
void *transfer_buffer; /* (in) associated data buffer */
|
||||
int actual_length; /* (return) actual transfer length */
|
||||
unsigned long pipe; /* (in) pipe information */
|
||||
#if 0
|
||||
int state;
|
||||
#endif
|
||||
} urb_priv_t;
|
||||
|
||||
struct isp116x_platform_data {
|
||||
/* Enable internal resistors on downstream ports */
|
||||
unsigned sel15Kres:1;
|
||||
/* On-chip overcurrent detection */
|
||||
unsigned oc_enable:1;
|
||||
/* Enable wakeup by devices on usb bus (e.g. wakeup
|
||||
by attachment/detachment or by device activity
|
||||
such as moving a mouse). When chosen, this option
|
||||
prevents stopping internal clock, increasing
|
||||
thereby power consumption in suspended state. */
|
||||
unsigned remote_wakeup_enable:1;
|
||||
};
|
||||
|
||||
struct isp116x {
|
||||
u16 *addr_reg;
|
||||
u16 *data_reg;
|
||||
|
||||
struct isp116x_platform_data *board;
|
||||
|
||||
struct dentry *dentry;
|
||||
unsigned long stat1, stat2, stat4, stat8, stat16;
|
||||
|
||||
/* Status flags */
|
||||
unsigned disabled:1;
|
||||
unsigned sleeping:1;
|
||||
|
||||
/* Root hub registers */
|
||||
u32 rhdesca;
|
||||
u32 rhdescb;
|
||||
u32 rhstatus;
|
||||
u32 rhport[2];
|
||||
|
||||
/* Schedule for the current frame */
|
||||
struct isp116x_ep *atl_active;
|
||||
int atl_buflen;
|
||||
int atl_bufshrt;
|
||||
int atl_last_dir;
|
||||
int atl_finishing;
|
||||
};
|
||||
|
||||
/* ------------------------------------------------- */
|
||||
|
||||
/* Inter-io delay (ns). The chip is picky about access timings; it
|
||||
* expects at least:
|
||||
* 150ns delay between consecutive accesses to DATA_REG,
|
||||
* 300ns delay between access to ADDR_REG and DATA_REG
|
||||
* OE, WE MUST NOT be changed during these intervals
|
||||
*/
|
||||
#if defined(UDELAY)
|
||||
#define isp116x_delay(h,d) udelay(d)
|
||||
#else
|
||||
#define isp116x_delay(h,d) do {} while (0)
|
||||
#endif
|
||||
|
||||
static inline void isp116x_write_addr(struct isp116x *isp116x, unsigned reg)
|
||||
{
|
||||
writew(reg & 0xff, isp116x->addr_reg);
|
||||
isp116x_delay(isp116x, UDELAY);
|
||||
}
|
||||
|
||||
static inline void isp116x_write_data16(struct isp116x *isp116x, u16 val)
|
||||
{
|
||||
writew(val, isp116x->data_reg);
|
||||
isp116x_delay(isp116x, UDELAY);
|
||||
}
|
||||
|
||||
static inline void isp116x_raw_write_data16(struct isp116x *isp116x, u16 val)
|
||||
{
|
||||
__raw_writew(val, isp116x->data_reg);
|
||||
isp116x_delay(isp116x, UDELAY);
|
||||
}
|
||||
|
||||
static inline u16 isp116x_read_data16(struct isp116x *isp116x)
|
||||
{
|
||||
u16 val;
|
||||
|
||||
val = readw(isp116x->data_reg);
|
||||
isp116x_delay(isp116x, UDELAY);
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline u16 isp116x_raw_read_data16(struct isp116x *isp116x)
|
||||
{
|
||||
u16 val;
|
||||
|
||||
val = __raw_readw(isp116x->data_reg);
|
||||
isp116x_delay(isp116x, UDELAY);
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void isp116x_write_data32(struct isp116x *isp116x, u32 val)
|
||||
{
|
||||
writew(val & 0xffff, isp116x->data_reg);
|
||||
isp116x_delay(isp116x, UDELAY);
|
||||
writew(val >> 16, isp116x->data_reg);
|
||||
isp116x_delay(isp116x, UDELAY);
|
||||
}
|
||||
|
||||
static inline u32 isp116x_read_data32(struct isp116x *isp116x)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = (u32) readw(isp116x->data_reg);
|
||||
isp116x_delay(isp116x, UDELAY);
|
||||
val |= ((u32) readw(isp116x->data_reg)) << 16;
|
||||
isp116x_delay(isp116x, UDELAY);
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Let's keep register access functions out of line. Hint:
|
||||
we wait at least 150 ns at every access.
|
||||
*/
|
||||
static u16 isp116x_read_reg16(struct isp116x *isp116x, unsigned reg)
|
||||
{
|
||||
isp116x_write_addr(isp116x, reg);
|
||||
return isp116x_read_data16(isp116x);
|
||||
}
|
||||
|
||||
static u32 isp116x_read_reg32(struct isp116x *isp116x, unsigned reg)
|
||||
{
|
||||
isp116x_write_addr(isp116x, reg);
|
||||
return isp116x_read_data32(isp116x);
|
||||
}
|
||||
|
||||
static void isp116x_write_reg16(struct isp116x *isp116x, unsigned reg,
|
||||
unsigned val)
|
||||
{
|
||||
isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET);
|
||||
isp116x_write_data16(isp116x, (u16) (val & 0xffff));
|
||||
}
|
||||
|
||||
static void isp116x_write_reg32(struct isp116x *isp116x, unsigned reg,
|
||||
unsigned val)
|
||||
{
|
||||
isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET);
|
||||
isp116x_write_data32(isp116x, (u32) val);
|
||||
}
|
||||
|
||||
/* --- USB HUB constants (not OHCI-specific; see hub.h) -------------------- */
|
||||
|
||||
/* destination of request */
|
||||
#define RH_INTERFACE 0x01
|
||||
#define RH_ENDPOINT 0x02
|
||||
#define RH_OTHER 0x03
|
||||
|
||||
#define RH_CLASS 0x20
|
||||
#define RH_VENDOR 0x40
|
||||
|
||||
/* Requests: bRequest << 8 | bmRequestType */
|
||||
#define RH_GET_STATUS 0x0080
|
||||
#define RH_CLEAR_FEATURE 0x0100
|
||||
#define RH_SET_FEATURE 0x0300
|
||||
#define RH_SET_ADDRESS 0x0500
|
||||
#define RH_GET_DESCRIPTOR 0x0680
|
||||
#define RH_SET_DESCRIPTOR 0x0700
|
||||
#define RH_GET_CONFIGURATION 0x0880
|
||||
#define RH_SET_CONFIGURATION 0x0900
|
||||
#define RH_GET_STATE 0x0280
|
||||
#define RH_GET_INTERFACE 0x0A80
|
||||
#define RH_SET_INTERFACE 0x0B00
|
||||
#define RH_SYNC_FRAME 0x0C80
|
||||
/* Our Vendor Specific Request */
|
||||
#define RH_SET_EP 0x2000
|
||||
|
||||
/* Hub port features */
|
||||
#define RH_PORT_CONNECTION 0x00
|
||||
#define RH_PORT_ENABLE 0x01
|
||||
#define RH_PORT_SUSPEND 0x02
|
||||
#define RH_PORT_OVER_CURRENT 0x03
|
||||
#define RH_PORT_RESET 0x04
|
||||
#define RH_PORT_POWER 0x08
|
||||
#define RH_PORT_LOW_SPEED 0x09
|
||||
|
||||
#define RH_C_PORT_CONNECTION 0x10
|
||||
#define RH_C_PORT_ENABLE 0x11
|
||||
#define RH_C_PORT_SUSPEND 0x12
|
||||
#define RH_C_PORT_OVER_CURRENT 0x13
|
||||
#define RH_C_PORT_RESET 0x14
|
||||
|
||||
/* Hub features */
|
||||
#define RH_C_HUB_LOCAL_POWER 0x00
|
||||
#define RH_C_HUB_OVER_CURRENT 0x01
|
||||
|
||||
#define RH_DEVICE_REMOTE_WAKEUP 0x00
|
||||
#define RH_ENDPOINT_STALL 0x01
|
||||
|
||||
#define RH_ACK 0x01
|
||||
#define RH_REQ_ERR -1
|
||||
#define RH_NACK 0x00
|
||||
70
u-boot/drivers/usb/host/ohci-at91.c
Normal file
70
u-boot/drivers/usb/host/ohci-at91.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* (C) Copyright 2006
|
||||
* DENX Software Engineering <mk@denx.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
|
||||
#if defined(CONFIG_USB_OHCI_NEW) && defined(CONFIG_SYS_USB_OHCI_CPU_INIT)
|
||||
|
||||
#include <asm/arch/clk.h>
|
||||
|
||||
int usb_cpu_init(void)
|
||||
{
|
||||
#ifdef CONFIG_USB_ATMEL_CLK_SEL_PLLB
|
||||
if (at91_pllb_clk_enable(get_pllb_init()))
|
||||
return -1;
|
||||
|
||||
#ifdef CONFIG_AT91SAM9N12
|
||||
at91_usb_clk_init(AT91_PMC_USBS_USB_PLLB | AT91_PMC_USB_DIV_2);
|
||||
#endif
|
||||
#elif defined(CONFIG_USB_ATMEL_CLK_SEL_UPLL)
|
||||
if (at91_upll_clk_enable())
|
||||
return -1;
|
||||
|
||||
at91_usb_clk_init(AT91_PMC_USBS_USB_UPLL | AT91_PMC_USBDIV_10);
|
||||
#endif
|
||||
|
||||
at91_periph_clk_enable(ATMEL_ID_UHP);
|
||||
|
||||
at91_system_clk_enable(ATMEL_PMC_UHP);
|
||||
#if defined(CONFIG_AT91SAM9261) || defined(CONFIG_AT91SAM9G10)
|
||||
at91_system_clk_enable(AT91_PMC_HCK0);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_cpu_stop(void)
|
||||
{
|
||||
at91_periph_clk_disable(ATMEL_ID_UHP);
|
||||
|
||||
at91_system_clk_disable(ATMEL_PMC_UHP);
|
||||
#if defined(CONFIG_AT91SAM9261) || defined(CONFIG_AT91SAM9G10)
|
||||
at91_system_clk_disable(AT91_PMC_HCK0);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_ATMEL_CLK_SEL_PLLB
|
||||
#ifdef CONFIG_AT91SAM9N12
|
||||
at91_usb_clk_init(0);
|
||||
#endif
|
||||
|
||||
if (at91_pllb_clk_disable())
|
||||
return -1;
|
||||
|
||||
#elif defined(CONFIG_USB_ATMEL_CLK_SEL_UPLL)
|
||||
if (at91_upll_clk_disable())
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_cpu_init_fail(void)
|
||||
{
|
||||
return usb_cpu_stop();
|
||||
}
|
||||
|
||||
#endif /* defined(CONFIG_USB_OHCI) && defined(CONFIG_SYS_USB_OHCI_CPU_INIT) */
|
||||
40
u-boot/drivers/usb/host/ohci-da8xx.c
Normal file
40
u-boot/drivers/usb/host/ohci-da8xx.c
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Sughosh Ganu <urwithsughosh@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
|
||||
#include <asm/arch/da8xx-usb.h>
|
||||
|
||||
int usb_cpu_init(void)
|
||||
{
|
||||
/* enable psc for usb2.0 */
|
||||
lpsc_on(DAVINCI_LPSC_USB20);
|
||||
|
||||
/* enable psc for usb1.0 */
|
||||
lpsc_on(DAVINCI_LPSC_USB11);
|
||||
|
||||
/* start the on-chip usb phy and its pll */
|
||||
if (usb_phy_on())
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int usb_cpu_stop(void)
|
||||
{
|
||||
usb_phy_off();
|
||||
|
||||
/* turn off the usb clock and assert the module reset */
|
||||
lpsc_disable(DAVINCI_LPSC_USB11);
|
||||
lpsc_disable(DAVINCI_LPSC_USB20);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_cpu_init_fail(void)
|
||||
{
|
||||
return usb_cpu_stop();
|
||||
}
|
||||
38
u-boot/drivers/usb/host/ohci-ep93xx.c
Normal file
38
u-boot/drivers/usb/host/ohci-ep93xx.c
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* (C) Copyright 2013
|
||||
* Sergey Kostanbaev < sergey.kostanbaev <at> fairwaves.ru >
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <common.h>
|
||||
|
||||
#if defined(CONFIG_USB_OHCI_NEW) && defined(CONFIG_SYS_USB_OHCI_CPU_INIT)
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/ep93xx.h>
|
||||
|
||||
int usb_cpu_init(void)
|
||||
{
|
||||
struct syscon_regs *syscon = (struct syscon_regs *)SYSCON_BASE;
|
||||
unsigned long pwr = readl(&syscon->pwrcnt);
|
||||
writel(pwr | SYSCON_PWRCNT_USH_EN, &syscon->pwrcnt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_cpu_stop(void)
|
||||
{
|
||||
struct syscon_regs *syscon = (struct syscon_regs *)SYSCON_BASE;
|
||||
unsigned long pwr = readl(&syscon->pwrcnt);
|
||||
writel(pwr & ~SYSCON_PWRCNT_USH_EN, &syscon->pwrcnt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_cpu_init_fail(void)
|
||||
{
|
||||
return usb_cpu_stop();
|
||||
}
|
||||
|
||||
#endif /* defined(CONFIG_USB_OHCI) && defined(CONFIG_SYS_USB_OHCI_CPU_INIT) */
|
||||
45
u-boot/drivers/usb/host/ohci-generic.c
Normal file
45
u-boot/drivers/usb/host/ohci-generic.c
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Alexey Brodkin <abrodkin@synopsys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include "ohci.h"
|
||||
|
||||
#if !defined(CONFIG_USB_OHCI_NEW)
|
||||
# error "Generic OHCI driver requires CONFIG_USB_OHCI_NEW"
|
||||
#endif
|
||||
|
||||
struct generic_ohci {
|
||||
ohci_t ohci;
|
||||
};
|
||||
|
||||
static int ohci_usb_probe(struct udevice *dev)
|
||||
{
|
||||
struct ohci_regs *regs = (struct ohci_regs *)dev_get_addr(dev);
|
||||
|
||||
return ohci_register(dev, regs);
|
||||
}
|
||||
|
||||
static int ohci_usb_remove(struct udevice *dev)
|
||||
{
|
||||
return ohci_deregister(dev);
|
||||
}
|
||||
|
||||
static const struct udevice_id ohci_usb_ids[] = {
|
||||
{ .compatible = "generic-ohci" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(ohci_generic) = {
|
||||
.name = "ohci_generic",
|
||||
.id = UCLASS_USB,
|
||||
.of_match = ohci_usb_ids,
|
||||
.probe = ohci_usb_probe,
|
||||
.remove = ohci_usb_remove,
|
||||
.ops = &ohci_usb_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct generic_ohci),
|
||||
.flags = DM_FLAG_ALLOC_PRIV_DMA,
|
||||
};
|
||||
2243
u-boot/drivers/usb/host/ohci-hcd.c
Normal file
2243
u-boot/drivers/usb/host/ohci-hcd.c
Normal file
File diff suppressed because it is too large
Load Diff
211
u-boot/drivers/usb/host/ohci-lpc32xx.c
Normal file
211
u-boot/drivers/usb/host/ohci-lpc32xx.c
Normal file
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright (C) 2008 by NXP Semiconductors
|
||||
* @Author: Based on code by Kevin Wells
|
||||
* @Descr: USB driver - Embedded Artists LPC3250 OEM Board support functions
|
||||
*
|
||||
* Copyright (c) 2015 Tyco Fire Protection Products.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <errno.h>
|
||||
#include <wait_bit.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/cpu.h>
|
||||
#include <asm/arch/clk.h>
|
||||
#include <usb.h>
|
||||
#include <i2c.h>
|
||||
|
||||
/* OTG I2C controller module register structures */
|
||||
struct otgi2c_regs {
|
||||
u32 otg_i2c_txrx; /* OTG I2C Tx/Rx Data FIFO */
|
||||
u32 otg_i2c_stat; /* OTG I2C Status Register */
|
||||
u32 otg_i2c_ctrl; /* OTG I2C Control Register */
|
||||
u32 otg_i2c_clk_hi; /* OTG I2C Clock Divider high */
|
||||
u32 otg_i2c_clk_lo; /* OTG I2C Clock Divider low */
|
||||
};
|
||||
|
||||
/* OTG controller module register structures */
|
||||
struct otg_regs {
|
||||
u32 reserved1[64];
|
||||
u32 otg_int_sts; /* OTG int status register */
|
||||
u32 otg_int_enab; /* OTG int enable register */
|
||||
u32 otg_int_set; /* OTG int set register */
|
||||
u32 otg_int_clr; /* OTG int clear register */
|
||||
u32 otg_sts_ctrl; /* OTG status/control register */
|
||||
u32 otg_timer; /* OTG timer register */
|
||||
u32 reserved2[122];
|
||||
struct otgi2c_regs otg_i2c;
|
||||
u32 reserved3[824];
|
||||
u32 otg_clk_ctrl; /* OTG clock control reg */
|
||||
u32 otg_clk_sts; /* OTG clock status reg */
|
||||
};
|
||||
|
||||
/* otg_sts_ctrl register definitions */
|
||||
#define OTG_HOST_EN (1 << 0) /* Enable host mode */
|
||||
|
||||
/* otg_clk_ctrl and otg_clk_sts register definitions */
|
||||
#define OTG_CLK_AHB_EN (1 << 4) /* Enable AHB clock */
|
||||
#define OTG_CLK_OTG_EN (1 << 3) /* Enable OTG clock */
|
||||
#define OTG_CLK_I2C_EN (1 << 2) /* Enable I2C clock */
|
||||
#define OTG_CLK_HOST_EN (1 << 0) /* Enable host clock */
|
||||
|
||||
/* ISP1301 USB transceiver I2C registers */
|
||||
#define MC1_SPEED_REG (1 << 0)
|
||||
#define MC1_DAT_SE0 (1 << 2)
|
||||
#define MC1_UART_EN (1 << 6)
|
||||
|
||||
#define MC2_SPD_SUSP_CTRL (1 << 1)
|
||||
#define MC2_BI_DI (1 << 2)
|
||||
#define MC2_PSW_EN (1 << 6)
|
||||
|
||||
#define OTG1_DP_PULLUP (1 << 0)
|
||||
#define OTG1_DM_PULLUP (1 << 1)
|
||||
#define OTG1_DP_PULLDOWN (1 << 2)
|
||||
#define OTG1_DM_PULLDOWN (1 << 3)
|
||||
#define OTG1_VBUS_DRV (1 << 5)
|
||||
|
||||
#define ISP1301_I2C_ADDR CONFIG_USB_ISP1301_I2C_ADDR
|
||||
|
||||
#define ISP1301_I2C_MODE_CONTROL_1_SET 0x04
|
||||
#define ISP1301_I2C_MODE_CONTROL_1_CLR 0x05
|
||||
#define ISP1301_I2C_MODE_CONTROL_2_SET 0x12
|
||||
#define ISP1301_I2C_MODE_CONTROL_2_CLR 0x13
|
||||
#define ISP1301_I2C_OTG_CONTROL_1_SET 0x06
|
||||
#define ISP1301_I2C_OTG_CONTROL_1_CLR 0x07
|
||||
#define ISP1301_I2C_INTERRUPT_LATCH_CLR 0x0B
|
||||
#define ISP1301_I2C_INTERRUPT_FALLING_CLR 0x0D
|
||||
#define ISP1301_I2C_INTERRUPT_RISING_CLR 0x0F
|
||||
|
||||
static struct otg_regs *otg = (struct otg_regs *)USB_BASE;
|
||||
static struct clk_pm_regs *clk_pwr = (struct clk_pm_regs *)CLK_PM_BASE;
|
||||
|
||||
static int isp1301_set_value(int reg, u8 value)
|
||||
{
|
||||
return i2c_write(ISP1301_I2C_ADDR, reg, 1, &value, 1);
|
||||
}
|
||||
|
||||
static void isp1301_configure(void)
|
||||
{
|
||||
i2c_set_bus_num(I2C_2);
|
||||
|
||||
/*
|
||||
* LPC32XX only supports DAT_SE0 USB mode
|
||||
* This sequence is important
|
||||
*/
|
||||
|
||||
/* Disable transparent UART mode first */
|
||||
isp1301_set_value(ISP1301_I2C_MODE_CONTROL_1_CLR, MC1_UART_EN);
|
||||
|
||||
isp1301_set_value(ISP1301_I2C_MODE_CONTROL_1_CLR, ~MC1_SPEED_REG);
|
||||
isp1301_set_value(ISP1301_I2C_MODE_CONTROL_1_SET, MC1_SPEED_REG);
|
||||
isp1301_set_value(ISP1301_I2C_MODE_CONTROL_2_CLR, ~0);
|
||||
isp1301_set_value(ISP1301_I2C_MODE_CONTROL_2_SET,
|
||||
MC2_BI_DI | MC2_PSW_EN | MC2_SPD_SUSP_CTRL);
|
||||
|
||||
isp1301_set_value(ISP1301_I2C_OTG_CONTROL_1_CLR, ~0);
|
||||
isp1301_set_value(ISP1301_I2C_MODE_CONTROL_1_SET, MC1_DAT_SE0);
|
||||
isp1301_set_value(ISP1301_I2C_OTG_CONTROL_1_SET,
|
||||
OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN);
|
||||
isp1301_set_value(ISP1301_I2C_OTG_CONTROL_1_CLR,
|
||||
OTG1_DM_PULLUP | OTG1_DP_PULLUP);
|
||||
isp1301_set_value(ISP1301_I2C_INTERRUPT_LATCH_CLR, ~0);
|
||||
isp1301_set_value(ISP1301_I2C_INTERRUPT_FALLING_CLR, ~0);
|
||||
isp1301_set_value(ISP1301_I2C_INTERRUPT_RISING_CLR, ~0);
|
||||
|
||||
/* Enable usb_need_clk clock after transceiver is initialized */
|
||||
setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_USBDVND_EN);
|
||||
}
|
||||
|
||||
static int usbpll_setup(void)
|
||||
{
|
||||
u32 ret;
|
||||
|
||||
/* make sure clocks are disabled */
|
||||
clrbits_le32(&clk_pwr->usb_ctrl,
|
||||
CLK_USBCTRL_CLK_EN1 | CLK_USBCTRL_CLK_EN2);
|
||||
|
||||
/* start PLL clock input */
|
||||
setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_CLK_EN1);
|
||||
|
||||
/* Setup PLL. */
|
||||
setbits_le32(&clk_pwr->usb_ctrl,
|
||||
CLK_USBCTRL_FDBK_PLUS1(192 - 1));
|
||||
setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_POSTDIV_2POW(0x01));
|
||||
setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_PLL_PWRUP);
|
||||
|
||||
ret = wait_for_bit(__func__, &clk_pwr->usb_ctrl, CLK_USBCTRL_PLL_STS,
|
||||
true, CONFIG_SYS_HZ, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* enable PLL output */
|
||||
setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_CLK_EN2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_cpu_init(void)
|
||||
{
|
||||
u32 ret;
|
||||
|
||||
/*
|
||||
* USB pins routing setup is done by "lpc32xx_usb_init()" and should
|
||||
* be call by board "board_init()" or "misc_init_r()" functions.
|
||||
*/
|
||||
|
||||
/* enable AHB slave USB clock */
|
||||
setbits_le32(&clk_pwr->usb_ctrl,
|
||||
CLK_USBCTRL_HCLK_EN | CLK_USBCTRL_BUS_KEEPER);
|
||||
|
||||
/* enable I2C clock */
|
||||
writel(OTG_CLK_I2C_EN, &otg->otg_clk_ctrl);
|
||||
ret = wait_for_bit(__func__, &otg->otg_clk_sts, OTG_CLK_I2C_EN, true,
|
||||
CONFIG_SYS_HZ, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Configure ISP1301 */
|
||||
isp1301_configure();
|
||||
|
||||
/* setup USB clocks and PLL */
|
||||
ret = usbpll_setup();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* enable usb_host_need_clk */
|
||||
setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_USBHSTND_EN);
|
||||
|
||||
/* enable all needed USB clocks */
|
||||
const u32 mask = OTG_CLK_AHB_EN | OTG_CLK_OTG_EN |
|
||||
OTG_CLK_I2C_EN | OTG_CLK_HOST_EN;
|
||||
writel(mask, &otg->otg_clk_ctrl);
|
||||
|
||||
ret = wait_for_bit(__func__, &otg->otg_clk_sts, mask, true,
|
||||
CONFIG_SYS_HZ, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
setbits_le32(&otg->otg_sts_ctrl, OTG_HOST_EN);
|
||||
isp1301_set_value(ISP1301_I2C_OTG_CONTROL_1_SET, OTG1_VBUS_DRV);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_cpu_stop(void)
|
||||
{
|
||||
/* vbus off */
|
||||
isp1301_set_value(ISP1301_I2C_OTG_CONTROL_1_SET, OTG1_VBUS_DRV);
|
||||
|
||||
clrbits_le32(&otg->otg_sts_ctrl, OTG_HOST_EN);
|
||||
|
||||
clrbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_HCLK_EN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_cpu_init_fail(void)
|
||||
{
|
||||
return usb_cpu_stop();
|
||||
}
|
||||
1688
u-boot/drivers/usb/host/ohci-s3c24xx.c
Normal file
1688
u-boot/drivers/usb/host/ohci-s3c24xx.c
Normal file
File diff suppressed because it is too large
Load Diff
409
u-boot/drivers/usb/host/ohci-s3c24xx.h
Normal file
409
u-boot/drivers/usb/host/ohci-s3c24xx.h
Normal file
@@ -0,0 +1,409 @@
|
||||
/*
|
||||
* URB OHCI HCD (Host Controller Driver) for USB.
|
||||
*
|
||||
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
|
||||
* (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
|
||||
*
|
||||
* usb-ohci.h
|
||||
*/
|
||||
|
||||
|
||||
static int cc_to_error[16] = {
|
||||
|
||||
/* mapping of the OHCI CC status to error codes */
|
||||
/* No Error */ 0,
|
||||
/* CRC Error */ USB_ST_CRC_ERR,
|
||||
/* Bit Stuff */ USB_ST_BIT_ERR,
|
||||
/* Data Togg */ USB_ST_CRC_ERR,
|
||||
/* Stall */ USB_ST_STALLED,
|
||||
/* DevNotResp */ -1,
|
||||
/* PIDCheck */ USB_ST_BIT_ERR,
|
||||
/* UnExpPID */ USB_ST_BIT_ERR,
|
||||
/* DataOver */ USB_ST_BUF_ERR,
|
||||
/* DataUnder */ USB_ST_BUF_ERR,
|
||||
/* reservd */ -1,
|
||||
/* reservd */ -1,
|
||||
/* BufferOver */ USB_ST_BUF_ERR,
|
||||
/* BuffUnder */ USB_ST_BUF_ERR,
|
||||
/* Not Access */ -1,
|
||||
/* Not Access */ -1
|
||||
};
|
||||
|
||||
/* ED States */
|
||||
#define ED_NEW 0x00
|
||||
#define ED_UNLINK 0x01
|
||||
#define ED_OPER 0x02
|
||||
#define ED_DEL 0x04
|
||||
#define ED_URB_DEL 0x08
|
||||
|
||||
/* usb_ohci_ed */
|
||||
struct ed {
|
||||
__u32 hwINFO;
|
||||
__u32 hwTailP;
|
||||
__u32 hwHeadP;
|
||||
__u32 hwNextED;
|
||||
|
||||
struct ed *ed_prev;
|
||||
__u8 int_period;
|
||||
__u8 int_branch;
|
||||
__u8 int_load;
|
||||
__u8 int_interval;
|
||||
__u8 state;
|
||||
__u8 type;
|
||||
__u16 last_iso;
|
||||
struct ed *ed_rm_list;
|
||||
|
||||
struct usb_device *usb_dev;
|
||||
__u32 unused[3];
|
||||
} __attribute__ ((aligned(16)));
|
||||
|
||||
/* TD info field */
|
||||
#define TD_CC 0xf0000000
|
||||
#define TD_CC_GET(td_p) (((td_p) >> 28) & 0x0f)
|
||||
#define TD_CC_SET(td_p, cc) \
|
||||
{(td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28)}
|
||||
#define TD_EC 0x0C000000
|
||||
#define TD_T 0x03000000
|
||||
#define TD_T_DATA0 0x02000000
|
||||
#define TD_T_DATA1 0x03000000
|
||||
#define TD_T_TOGGLE 0x00000000
|
||||
#define TD_R 0x00040000
|
||||
#define TD_DI 0x00E00000
|
||||
#define TD_DI_SET(X) (((X) & 0x07)<< 21)
|
||||
#define TD_DP 0x00180000
|
||||
#define TD_DP_SETUP 0x00000000
|
||||
#define TD_DP_IN 0x00100000
|
||||
#define TD_DP_OUT 0x00080000
|
||||
|
||||
#define TD_ISO 0x00010000
|
||||
#define TD_DEL 0x00020000
|
||||
|
||||
/* CC Codes */
|
||||
#define TD_CC_NOERROR 0x00
|
||||
#define TD_CC_CRC 0x01
|
||||
#define TD_CC_BITSTUFFING 0x02
|
||||
#define TD_CC_DATATOGGLEM 0x03
|
||||
#define TD_CC_STALL 0x04
|
||||
#define TD_DEVNOTRESP 0x05
|
||||
#define TD_PIDCHECKFAIL 0x06
|
||||
#define TD_UNEXPECTEDPID 0x07
|
||||
#define TD_DATAOVERRUN 0x08
|
||||
#define TD_DATAUNDERRUN 0x09
|
||||
#define TD_BUFFEROVERRUN 0x0C
|
||||
#define TD_BUFFERUNDERRUN 0x0D
|
||||
#define TD_NOTACCESSED 0x0F
|
||||
|
||||
|
||||
#define MAXPSW 1
|
||||
|
||||
struct td {
|
||||
__u32 hwINFO;
|
||||
__u32 hwCBP; /* Current Buffer Pointer */
|
||||
__u32 hwNextTD; /* Next TD Pointer */
|
||||
__u32 hwBE; /* Memory Buffer End Pointer */
|
||||
|
||||
__u8 unused;
|
||||
__u8 index;
|
||||
struct ed *ed;
|
||||
struct td *next_dl_td;
|
||||
struct usb_device *usb_dev;
|
||||
int transfer_len;
|
||||
__u32 data;
|
||||
|
||||
__u32 unused2[2];
|
||||
} __attribute__ ((aligned(32)));
|
||||
|
||||
#define OHCI_ED_SKIP (1 << 14)
|
||||
|
||||
/*
|
||||
* The HCCA (Host Controller Communications Area) is a 256 byte
|
||||
* structure defined in the OHCI spec. that the host controller is
|
||||
* told the base address of. It must be 256-byte aligned.
|
||||
*/
|
||||
|
||||
#define NUM_INTS 32 /* part of the OHCI standard */
|
||||
struct ohci_hcca {
|
||||
__u32 int_table[NUM_INTS]; /* Interrupt ED table */
|
||||
__u16 frame_no; /* current frame number */
|
||||
__u16 pad1; /* set to 0 on each frame_no change */
|
||||
__u32 done_head; /* info returned for an interrupt */
|
||||
u8 reserved_for_hc[116];
|
||||
} __attribute__ ((aligned(256)));
|
||||
|
||||
/*
|
||||
* Maximum number of root hub ports.
|
||||
*/
|
||||
#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */
|
||||
|
||||
/*
|
||||
* This is the structure of the OHCI controller's memory mapped I/O
|
||||
* region. This is Memory Mapped I/O. You must use the readl() and
|
||||
* writel() macros defined in asm/io.h to access these!!
|
||||
*/
|
||||
struct ohci_regs {
|
||||
/* control and status registers */
|
||||
__u32 revision;
|
||||
__u32 control;
|
||||
__u32 cmdstatus;
|
||||
__u32 intrstatus;
|
||||
__u32 intrenable;
|
||||
__u32 intrdisable;
|
||||
/* memory pointers */
|
||||
__u32 hcca;
|
||||
__u32 ed_periodcurrent;
|
||||
__u32 ed_controlhead;
|
||||
__u32 ed_controlcurrent;
|
||||
__u32 ed_bulkhead;
|
||||
__u32 ed_bulkcurrent;
|
||||
__u32 donehead;
|
||||
/* frame counters */
|
||||
__u32 fminterval;
|
||||
__u32 fmremaining;
|
||||
__u32 fmnumber;
|
||||
__u32 periodicstart;
|
||||
__u32 lsthresh;
|
||||
/* Root hub ports */
|
||||
struct ohci_roothub_regs {
|
||||
__u32 a;
|
||||
__u32 b;
|
||||
__u32 status;
|
||||
__u32 portstatus[MAX_ROOT_PORTS];
|
||||
} roothub;
|
||||
} __attribute__ ((aligned(32)));
|
||||
|
||||
/* OHCI CONTROL AND STATUS REGISTER MASKS */
|
||||
|
||||
/*
|
||||
* HcControl (control) register masks
|
||||
*/
|
||||
#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */
|
||||
#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */
|
||||
#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */
|
||||
#define OHCI_CTRL_CLE (1 << 4) /* control list enable */
|
||||
#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */
|
||||
#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */
|
||||
#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */
|
||||
#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
|
||||
#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */
|
||||
|
||||
/* pre-shifted values for HCFS */
|
||||
# define OHCI_USB_RESET (0 << 6)
|
||||
# define OHCI_USB_RESUME (1 << 6)
|
||||
# define OHCI_USB_OPER (2 << 6)
|
||||
# define OHCI_USB_SUSPEND (3 << 6)
|
||||
|
||||
/*
|
||||
* HcCommandStatus (cmdstatus) register masks
|
||||
*/
|
||||
#define OHCI_HCR (1 << 0) /* host controller reset */
|
||||
#define OHCI_CLF (1 << 1) /* control list filled */
|
||||
#define OHCI_BLF (1 << 2) /* bulk list filled */
|
||||
#define OHCI_OCR (1 << 3) /* ownership change request */
|
||||
#define OHCI_SOC (3 << 16) /* scheduling overrun count */
|
||||
|
||||
/*
|
||||
* masks used with interrupt registers:
|
||||
* HcInterruptStatus (intrstatus)
|
||||
* HcInterruptEnable (intrenable)
|
||||
* HcInterruptDisable (intrdisable)
|
||||
*/
|
||||
#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */
|
||||
#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */
|
||||
#define OHCI_INTR_SF (1 << 2) /* start frame */
|
||||
#define OHCI_INTR_RD (1 << 3) /* resume detect */
|
||||
#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */
|
||||
#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */
|
||||
#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */
|
||||
#define OHCI_INTR_OC (1 << 30) /* ownership change */
|
||||
#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */
|
||||
|
||||
/* Virtual Root HUB */
|
||||
struct virt_root_hub {
|
||||
int devnum; /* Address of Root Hub endpoint */
|
||||
void *dev; /* was urb */
|
||||
void *int_addr;
|
||||
int send;
|
||||
int interval;
|
||||
};
|
||||
|
||||
/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */
|
||||
|
||||
/* destination of request */
|
||||
#define RH_INTERFACE 0x01
|
||||
#define RH_ENDPOINT 0x02
|
||||
#define RH_OTHER 0x03
|
||||
|
||||
#define RH_CLASS 0x20
|
||||
#define RH_VENDOR 0x40
|
||||
|
||||
/* Requests: bRequest << 8 | bmRequestType */
|
||||
#define RH_GET_STATUS 0x0080
|
||||
#define RH_CLEAR_FEATURE 0x0100
|
||||
#define RH_SET_FEATURE 0x0300
|
||||
#define RH_SET_ADDRESS 0x0500
|
||||
#define RH_GET_DESCRIPTOR 0x0680
|
||||
#define RH_SET_DESCRIPTOR 0x0700
|
||||
#define RH_GET_CONFIGURATION 0x0880
|
||||
#define RH_SET_CONFIGURATION 0x0900
|
||||
#define RH_GET_STATE 0x0280
|
||||
#define RH_GET_INTERFACE 0x0A80
|
||||
#define RH_SET_INTERFACE 0x0B00
|
||||
#define RH_SYNC_FRAME 0x0C80
|
||||
/* Our Vendor Specific Request */
|
||||
#define RH_SET_EP 0x2000
|
||||
|
||||
|
||||
/* Hub port features */
|
||||
#define RH_PORT_CONNECTION 0x00
|
||||
#define RH_PORT_ENABLE 0x01
|
||||
#define RH_PORT_SUSPEND 0x02
|
||||
#define RH_PORT_OVER_CURRENT 0x03
|
||||
#define RH_PORT_RESET 0x04
|
||||
#define RH_PORT_POWER 0x08
|
||||
#define RH_PORT_LOW_SPEED 0x09
|
||||
|
||||
#define RH_C_PORT_CONNECTION 0x10
|
||||
#define RH_C_PORT_ENABLE 0x11
|
||||
#define RH_C_PORT_SUSPEND 0x12
|
||||
#define RH_C_PORT_OVER_CURRENT 0x13
|
||||
#define RH_C_PORT_RESET 0x14
|
||||
|
||||
/* Hub features */
|
||||
#define RH_C_HUB_LOCAL_POWER 0x00
|
||||
#define RH_C_HUB_OVER_CURRENT 0x01
|
||||
|
||||
#define RH_DEVICE_REMOTE_WAKEUP 0x00
|
||||
#define RH_ENDPOINT_STALL 0x01
|
||||
|
||||
#define RH_ACK 0x01
|
||||
#define RH_REQ_ERR -1
|
||||
#define RH_NACK 0x00
|
||||
|
||||
|
||||
/* OHCI ROOT HUB REGISTER MASKS */
|
||||
|
||||
/* roothub.portstatus [i] bits */
|
||||
#define RH_PS_CCS 0x00000001 /* current connect status */
|
||||
#define RH_PS_PES 0x00000002 /* port enable status */
|
||||
#define RH_PS_PSS 0x00000004 /* port suspend status */
|
||||
#define RH_PS_POCI 0x00000008 /* port over current indicator */
|
||||
#define RH_PS_PRS 0x00000010 /* port reset status */
|
||||
#define RH_PS_PPS 0x00000100 /* port power status */
|
||||
#define RH_PS_LSDA 0x00000200 /* low speed device attached */
|
||||
#define RH_PS_CSC 0x00010000 /* connect status change */
|
||||
#define RH_PS_PESC 0x00020000 /* port enable status change */
|
||||
#define RH_PS_PSSC 0x00040000 /* port suspend status change */
|
||||
#define RH_PS_OCIC 0x00080000 /* over current indicator change */
|
||||
#define RH_PS_PRSC 0x00100000 /* port reset status change */
|
||||
|
||||
/* roothub.status bits */
|
||||
#define RH_HS_LPS 0x00000001 /* local power status */
|
||||
#define RH_HS_OCI 0x00000002 /* over current indicator */
|
||||
#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */
|
||||
#define RH_HS_LPSC 0x00010000 /* local power status change */
|
||||
#define RH_HS_OCIC 0x00020000 /* over current indicator change */
|
||||
#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */
|
||||
|
||||
/* roothub.b masks */
|
||||
#define RH_B_DR 0x0000ffff /* device removable flags */
|
||||
#define RH_B_PPCM 0xffff0000 /* port power control mask */
|
||||
|
||||
/* roothub.a masks */
|
||||
#define RH_A_NDP (0xff << 0) /* number of downstream ports */
|
||||
#define RH_A_PSM (1 << 8) /* power switching mode */
|
||||
#define RH_A_NPS (1 << 9) /* no power switching */
|
||||
#define RH_A_DT (1 << 10) /* device type (mbz) */
|
||||
#define RH_A_OCPM (1 << 11) /* over current protection mode */
|
||||
#define RH_A_NOCP (1 << 12) /* no over current protection */
|
||||
#define RH_A_POTPGT (0xff << 24) /* power on to power good time */
|
||||
|
||||
/* urb */
|
||||
#define N_URB_TD 48
|
||||
struct urb_priv {
|
||||
struct ed *ed;
|
||||
__u16 length; /* number of tds associated with this request */
|
||||
__u16 td_cnt; /* number of tds already serviced */
|
||||
int state;
|
||||
unsigned long pipe;
|
||||
int actual_length;
|
||||
struct td *td[N_URB_TD]; /* list pointer to all corresponding TDs
|
||||
associated with this request */
|
||||
};
|
||||
#define URB_DEL 1
|
||||
|
||||
/*
|
||||
* This is the full ohci controller description
|
||||
*
|
||||
* Note how the "proper" USB information is just
|
||||
* a subset of what the full implementation needs. (Linus)
|
||||
*/
|
||||
|
||||
|
||||
struct ohci {
|
||||
struct ohci_hcca *hcca; /* hcca */
|
||||
/*dma_addr_t hcca_dma; */
|
||||
|
||||
int irq;
|
||||
int disabled; /* e.g. got a UE, we're hung */
|
||||
int sleeping;
|
||||
unsigned long flags; /* for HC bugs */
|
||||
|
||||
struct ohci_regs *regs; /* OHCI controller's memory */
|
||||
|
||||
struct ed *ed_rm_list[2]; /* lists of all endpoints to be removed */
|
||||
struct ed *ed_bulktail; /* last endpoint of bulk list */
|
||||
struct ed *ed_controltail; /* last endpoint of control list */
|
||||
int intrstatus;
|
||||
__u32 hc_control; /* copy of the hc control reg */
|
||||
struct usb_device *dev[32];
|
||||
struct virt_root_hub rh;
|
||||
|
||||
const char *slot_name;
|
||||
};
|
||||
|
||||
#define NUM_EDS 8 /* num of preallocated endpoint descriptors */
|
||||
|
||||
struct ohci_device {
|
||||
struct ed ed[NUM_EDS];
|
||||
int ed_cnt;
|
||||
};
|
||||
|
||||
/* hcd */
|
||||
/* endpoint */
|
||||
static int ep_link(struct ohci *ohci, struct ed *ed);
|
||||
static int ep_unlink(struct ohci *ohci, struct ed *ed);
|
||||
static struct ed *ep_add_ed(struct usb_device *usb_dev, unsigned long pipe);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* we need more TDs than EDs */
|
||||
#define NUM_TD 64
|
||||
|
||||
/* +1 so we can align the storage */
|
||||
struct td gtd[NUM_TD + 1];
|
||||
|
||||
/* pointers to aligned storage */
|
||||
struct td *ptd;
|
||||
|
||||
/* TDs ... */
|
||||
static inline struct td *td_alloc(struct usb_device *usb_dev)
|
||||
{
|
||||
int i;
|
||||
struct td *td;
|
||||
|
||||
td = NULL;
|
||||
for (i = 0; i < NUM_TD; i++) {
|
||||
if (ptd[i].usb_dev == NULL) {
|
||||
td = &ptd[i];
|
||||
td->usb_dev = usb_dev;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return td;
|
||||
}
|
||||
|
||||
static inline void ed_free(struct ed *ed)
|
||||
{
|
||||
ed->usb_dev = NULL;
|
||||
}
|
||||
117
u-boot/drivers/usb/host/ohci-sunxi.c
Normal file
117
u-boot/drivers/usb/host/ohci-sunxi.c
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Sunxi ohci glue
|
||||
*
|
||||
* Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com>
|
||||
*
|
||||
* Based on code from
|
||||
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/arch/usb_phy.h>
|
||||
#include <asm/io.h>
|
||||
#include <dm.h>
|
||||
#include <usb.h>
|
||||
#include "ohci.h"
|
||||
|
||||
#ifdef CONFIG_SUNXI_GEN_SUN4I
|
||||
#define BASE_DIST 0x8000
|
||||
#define AHB_CLK_DIST 2
|
||||
#else
|
||||
#define BASE_DIST 0x1000
|
||||
#define AHB_CLK_DIST 1
|
||||
#endif
|
||||
|
||||
struct ohci_sunxi_priv {
|
||||
ohci_t ohci;
|
||||
int ahb_gate_mask; /* Mask of ahb_gate0 clk gate bits for this hcd */
|
||||
int usb_gate_mask; /* Mask of usb_clk_cfg clk gate bits for this hcd */
|
||||
int phy_index; /* Index of the usb-phy attached to this hcd */
|
||||
};
|
||||
|
||||
static int ohci_usb_probe(struct udevice *dev)
|
||||
{
|
||||
struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
|
||||
struct usb_bus_priv *bus_priv = dev_get_uclass_priv(dev);
|
||||
struct ohci_sunxi_priv *priv = dev_get_priv(dev);
|
||||
struct ohci_regs *regs = (struct ohci_regs *)dev_get_addr(dev);
|
||||
int extra_ahb_gate_mask = 0;
|
||||
|
||||
bus_priv->companion = true;
|
||||
|
||||
/*
|
||||
* This should go away once we've moved to the driver model for
|
||||
* clocks resp. phys.
|
||||
*/
|
||||
priv->ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_OHCI0;
|
||||
#ifdef CONFIG_MACH_SUN8I_H3
|
||||
extra_ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0;
|
||||
#endif
|
||||
priv->usb_gate_mask = CCM_USB_CTRL_OHCI0_CLK;
|
||||
priv->phy_index = ((u32)regs - (SUNXI_USB1_BASE + 0x400)) / BASE_DIST;
|
||||
priv->ahb_gate_mask <<= priv->phy_index * AHB_CLK_DIST;
|
||||
extra_ahb_gate_mask <<= priv->phy_index * AHB_CLK_DIST;
|
||||
priv->usb_gate_mask <<= priv->phy_index;
|
||||
priv->phy_index++; /* Non otg phys start at 1 */
|
||||
|
||||
setbits_le32(&ccm->ahb_gate0,
|
||||
priv->ahb_gate_mask | extra_ahb_gate_mask);
|
||||
setbits_le32(&ccm->usb_clk_cfg, priv->usb_gate_mask);
|
||||
#ifdef CONFIG_SUNXI_GEN_SUN6I
|
||||
setbits_le32(&ccm->ahb_reset0_cfg,
|
||||
priv->ahb_gate_mask | extra_ahb_gate_mask);
|
||||
#endif
|
||||
|
||||
sunxi_usb_phy_init(priv->phy_index);
|
||||
sunxi_usb_phy_power_on(priv->phy_index);
|
||||
|
||||
return ohci_register(dev, regs);
|
||||
}
|
||||
|
||||
static int ohci_usb_remove(struct udevice *dev)
|
||||
{
|
||||
struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
|
||||
struct ohci_sunxi_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = ohci_deregister(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sunxi_usb_phy_exit(priv->phy_index);
|
||||
|
||||
#ifdef CONFIG_SUNXI_GEN_SUN6I
|
||||
clrbits_le32(&ccm->ahb_reset0_cfg, priv->ahb_gate_mask);
|
||||
#endif
|
||||
clrbits_le32(&ccm->usb_clk_cfg, priv->usb_gate_mask);
|
||||
clrbits_le32(&ccm->ahb_gate0, priv->ahb_gate_mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id ohci_usb_ids[] = {
|
||||
{ .compatible = "allwinner,sun4i-a10-ohci", },
|
||||
{ .compatible = "allwinner,sun5i-a13-ohci", },
|
||||
{ .compatible = "allwinner,sun6i-a31-ohci", },
|
||||
{ .compatible = "allwinner,sun7i-a20-ohci", },
|
||||
{ .compatible = "allwinner,sun8i-a23-ohci", },
|
||||
{ .compatible = "allwinner,sun8i-a83t-ohci", },
|
||||
{ .compatible = "allwinner,sun8i-h3-ohci", },
|
||||
{ .compatible = "allwinner,sun9i-a80-ohci", },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(usb_ohci) = {
|
||||
.name = "ohci_sunxi",
|
||||
.id = UCLASS_USB,
|
||||
.of_match = ohci_usb_ids,
|
||||
.probe = ohci_usb_probe,
|
||||
.remove = ohci_usb_remove,
|
||||
.ops = &ohci_usb_ops,
|
||||
.platdata_auto_alloc_size = sizeof(struct usb_platdata),
|
||||
.priv_auto_alloc_size = sizeof(struct ohci_sunxi_priv),
|
||||
.flags = DM_FLAG_ALLOC_PRIV_DMA,
|
||||
};
|
||||
418
u-boot/drivers/usb/host/ohci.h
Normal file
418
u-boot/drivers/usb/host/ohci.h
Normal file
@@ -0,0 +1,418 @@
|
||||
/*
|
||||
* URB OHCI HCD (Host Controller Driver) for USB.
|
||||
*
|
||||
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
|
||||
* (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
|
||||
*
|
||||
* usb-ohci.h
|
||||
*/
|
||||
|
||||
/*
|
||||
* e.g. PCI controllers need this
|
||||
*/
|
||||
#ifdef CONFIG_SYS_OHCI_SWAP_REG_ACCESS
|
||||
# define ohci_readl(a) __swap_32(*((volatile u32 *)(a)))
|
||||
# define ohci_writel(a, b) (*((volatile u32 *)(b)) = __swap_32((volatile u32)a))
|
||||
#else
|
||||
# define ohci_readl(a) (*((volatile u32 *)(a)))
|
||||
# define ohci_writel(a, b) (*((volatile u32 *)(b)) = ((volatile u32)a))
|
||||
#endif /* CONFIG_SYS_OHCI_SWAP_REG_ACCESS */
|
||||
|
||||
#if ARCH_DMA_MINALIGN > 16
|
||||
#define ED_ALIGNMENT ARCH_DMA_MINALIGN
|
||||
#else
|
||||
#define ED_ALIGNMENT 16
|
||||
#endif
|
||||
|
||||
#if defined CONFIG_DM_USB && ARCH_DMA_MINALIGN > 32
|
||||
#define TD_ALIGNMENT ARCH_DMA_MINALIGN
|
||||
#else
|
||||
#define TD_ALIGNMENT 32
|
||||
#endif
|
||||
|
||||
/* functions for doing board or CPU specific setup/cleanup */
|
||||
int usb_board_stop(void);
|
||||
|
||||
int usb_cpu_init(void);
|
||||
int usb_cpu_stop(void);
|
||||
int usb_cpu_init_fail(void);
|
||||
|
||||
/* ED States */
|
||||
#define ED_NEW 0x00
|
||||
#define ED_UNLINK 0x01
|
||||
#define ED_OPER 0x02
|
||||
#define ED_DEL 0x04
|
||||
#define ED_URB_DEL 0x08
|
||||
|
||||
/* usb_ohci_ed */
|
||||
struct ed {
|
||||
__u32 hwINFO;
|
||||
__u32 hwTailP;
|
||||
__u32 hwHeadP;
|
||||
__u32 hwNextED;
|
||||
|
||||
struct ed *ed_prev;
|
||||
__u8 int_period;
|
||||
__u8 int_branch;
|
||||
__u8 int_load;
|
||||
__u8 int_interval;
|
||||
__u8 state;
|
||||
__u8 type;
|
||||
__u16 last_iso;
|
||||
struct ed *ed_rm_list;
|
||||
|
||||
struct usb_device *usb_dev;
|
||||
void *purb;
|
||||
__u32 unused[2];
|
||||
} __attribute__((aligned(ED_ALIGNMENT)));
|
||||
typedef struct ed ed_t;
|
||||
|
||||
|
||||
/* TD info field */
|
||||
#define TD_CC 0xf0000000
|
||||
#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f)
|
||||
#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28)
|
||||
#define TD_EC 0x0C000000
|
||||
#define TD_T 0x03000000
|
||||
#define TD_T_DATA0 0x02000000
|
||||
#define TD_T_DATA1 0x03000000
|
||||
#define TD_T_TOGGLE 0x00000000
|
||||
#define TD_R 0x00040000
|
||||
#define TD_DI 0x00E00000
|
||||
#define TD_DI_SET(X) (((X) & 0x07)<< 21)
|
||||
#define TD_DP 0x00180000
|
||||
#define TD_DP_SETUP 0x00000000
|
||||
#define TD_DP_IN 0x00100000
|
||||
#define TD_DP_OUT 0x00080000
|
||||
|
||||
#define TD_ISO 0x00010000
|
||||
#define TD_DEL 0x00020000
|
||||
|
||||
/* CC Codes */
|
||||
#define TD_CC_NOERROR 0x00
|
||||
#define TD_CC_CRC 0x01
|
||||
#define TD_CC_BITSTUFFING 0x02
|
||||
#define TD_CC_DATATOGGLEM 0x03
|
||||
#define TD_CC_STALL 0x04
|
||||
#define TD_DEVNOTRESP 0x05
|
||||
#define TD_PIDCHECKFAIL 0x06
|
||||
#define TD_UNEXPECTEDPID 0x07
|
||||
#define TD_DATAOVERRUN 0x08
|
||||
#define TD_DATAUNDERRUN 0x09
|
||||
#define TD_BUFFEROVERRUN 0x0C
|
||||
#define TD_BUFFERUNDERRUN 0x0D
|
||||
#define TD_NOTACCESSED 0x0F
|
||||
|
||||
|
||||
#define MAXPSW 1
|
||||
|
||||
struct td {
|
||||
__u32 hwINFO;
|
||||
__u32 hwCBP; /* Current Buffer Pointer */
|
||||
__u32 hwNextTD; /* Next TD Pointer */
|
||||
__u32 hwBE; /* Memory Buffer End Pointer */
|
||||
|
||||
/* #ifndef CONFIG_MPC5200 /\* this seems wrong *\/ */
|
||||
__u16 hwPSW[MAXPSW];
|
||||
/* #endif */
|
||||
__u8 unused;
|
||||
__u8 index;
|
||||
struct ed *ed;
|
||||
struct td *next_dl_td;
|
||||
struct usb_device *usb_dev;
|
||||
int transfer_len;
|
||||
__u32 data;
|
||||
|
||||
__u32 unused2[2];
|
||||
} __attribute__((aligned(TD_ALIGNMENT)));
|
||||
typedef struct td td_t;
|
||||
|
||||
#define OHCI_ED_SKIP (1 << 14)
|
||||
|
||||
/*
|
||||
* The HCCA (Host Controller Communications Area) is a 256 byte
|
||||
* structure defined in the OHCI spec. that the host controller is
|
||||
* told the base address of. It must be 256-byte aligned.
|
||||
*/
|
||||
|
||||
#define NUM_INTS 32 /* part of the OHCI standard */
|
||||
struct ohci_hcca {
|
||||
__u32 int_table[NUM_INTS]; /* Interrupt ED table */
|
||||
#if defined(CONFIG_MPC5200)
|
||||
__u16 pad1; /* set to 0 on each frame_no change */
|
||||
__u16 frame_no; /* current frame number */
|
||||
#else
|
||||
__u16 frame_no; /* current frame number */
|
||||
__u16 pad1; /* set to 0 on each frame_no change */
|
||||
#endif
|
||||
__u32 done_head; /* info returned for an interrupt */
|
||||
u8 reserved_for_hc[116];
|
||||
} __attribute__((aligned(256)));
|
||||
|
||||
|
||||
/*
|
||||
* Maximum number of root hub ports.
|
||||
*/
|
||||
#ifndef CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS
|
||||
# error "CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS undefined!"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is the structure of the OHCI controller's memory mapped I/O
|
||||
* region. This is Memory Mapped I/O. You must use the ohci_readl() and
|
||||
* ohci_writel() macros defined in this file to access these!!
|
||||
*/
|
||||
struct ohci_regs {
|
||||
/* control and status registers */
|
||||
__u32 revision;
|
||||
__u32 control;
|
||||
__u32 cmdstatus;
|
||||
__u32 intrstatus;
|
||||
__u32 intrenable;
|
||||
__u32 intrdisable;
|
||||
/* memory pointers */
|
||||
__u32 hcca;
|
||||
__u32 ed_periodcurrent;
|
||||
__u32 ed_controlhead;
|
||||
__u32 ed_controlcurrent;
|
||||
__u32 ed_bulkhead;
|
||||
__u32 ed_bulkcurrent;
|
||||
__u32 donehead;
|
||||
/* frame counters */
|
||||
__u32 fminterval;
|
||||
__u32 fmremaining;
|
||||
__u32 fmnumber;
|
||||
__u32 periodicstart;
|
||||
__u32 lsthresh;
|
||||
/* Root hub ports */
|
||||
struct ohci_roothub_regs {
|
||||
__u32 a;
|
||||
__u32 b;
|
||||
__u32 status;
|
||||
__u32 portstatus[CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS];
|
||||
} roothub;
|
||||
} __attribute__((aligned(32)));
|
||||
|
||||
/* Some EHCI controls */
|
||||
#define EHCI_USBCMD_OFF 0x20
|
||||
#define EHCI_USBCMD_HCRESET (1 << 1)
|
||||
|
||||
/* OHCI CONTROL AND STATUS REGISTER MASKS */
|
||||
|
||||
/*
|
||||
* HcControl (control) register masks
|
||||
*/
|
||||
#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */
|
||||
#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */
|
||||
#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */
|
||||
#define OHCI_CTRL_CLE (1 << 4) /* control list enable */
|
||||
#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */
|
||||
#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */
|
||||
#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */
|
||||
#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
|
||||
#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */
|
||||
|
||||
/* pre-shifted values for HCFS */
|
||||
# define OHCI_USB_RESET (0 << 6)
|
||||
# define OHCI_USB_RESUME (1 << 6)
|
||||
# define OHCI_USB_OPER (2 << 6)
|
||||
# define OHCI_USB_SUSPEND (3 << 6)
|
||||
|
||||
/*
|
||||
* HcCommandStatus (cmdstatus) register masks
|
||||
*/
|
||||
#define OHCI_HCR (1 << 0) /* host controller reset */
|
||||
#define OHCI_CLF (1 << 1) /* control list filled */
|
||||
#define OHCI_BLF (1 << 2) /* bulk list filled */
|
||||
#define OHCI_OCR (1 << 3) /* ownership change request */
|
||||
#define OHCI_SOC (3 << 16) /* scheduling overrun count */
|
||||
|
||||
/*
|
||||
* masks used with interrupt registers:
|
||||
* HcInterruptStatus (intrstatus)
|
||||
* HcInterruptEnable (intrenable)
|
||||
* HcInterruptDisable (intrdisable)
|
||||
*/
|
||||
#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */
|
||||
#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */
|
||||
#define OHCI_INTR_SF (1 << 2) /* start frame */
|
||||
#define OHCI_INTR_RD (1 << 3) /* resume detect */
|
||||
#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */
|
||||
#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */
|
||||
#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */
|
||||
#define OHCI_INTR_OC (1 << 30) /* ownership change */
|
||||
#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */
|
||||
|
||||
|
||||
/* Virtual Root HUB */
|
||||
struct virt_root_hub {
|
||||
int devnum; /* Address of Root Hub endpoint */
|
||||
void *dev; /* was urb */
|
||||
void *int_addr;
|
||||
int send;
|
||||
int interval;
|
||||
};
|
||||
|
||||
/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */
|
||||
|
||||
/* destination of request */
|
||||
#define RH_INTERFACE 0x01
|
||||
#define RH_ENDPOINT 0x02
|
||||
#define RH_OTHER 0x03
|
||||
|
||||
#define RH_CLASS 0x20
|
||||
#define RH_VENDOR 0x40
|
||||
|
||||
/* Requests: bRequest << 8 | bmRequestType */
|
||||
#define RH_GET_STATUS 0x0080
|
||||
#define RH_CLEAR_FEATURE 0x0100
|
||||
#define RH_SET_FEATURE 0x0300
|
||||
#define RH_SET_ADDRESS 0x0500
|
||||
#define RH_GET_DESCRIPTOR 0x0680
|
||||
#define RH_SET_DESCRIPTOR 0x0700
|
||||
#define RH_GET_CONFIGURATION 0x0880
|
||||
#define RH_SET_CONFIGURATION 0x0900
|
||||
#define RH_GET_STATE 0x0280
|
||||
#define RH_GET_INTERFACE 0x0A80
|
||||
#define RH_SET_INTERFACE 0x0B00
|
||||
#define RH_SYNC_FRAME 0x0C80
|
||||
/* Our Vendor Specific Request */
|
||||
#define RH_SET_EP 0x2000
|
||||
|
||||
|
||||
/* Hub port features */
|
||||
#define RH_PORT_CONNECTION 0x00
|
||||
#define RH_PORT_ENABLE 0x01
|
||||
#define RH_PORT_SUSPEND 0x02
|
||||
#define RH_PORT_OVER_CURRENT 0x03
|
||||
#define RH_PORT_RESET 0x04
|
||||
#define RH_PORT_POWER 0x08
|
||||
#define RH_PORT_LOW_SPEED 0x09
|
||||
|
||||
#define RH_C_PORT_CONNECTION 0x10
|
||||
#define RH_C_PORT_ENABLE 0x11
|
||||
#define RH_C_PORT_SUSPEND 0x12
|
||||
#define RH_C_PORT_OVER_CURRENT 0x13
|
||||
#define RH_C_PORT_RESET 0x14
|
||||
|
||||
/* Hub features */
|
||||
#define RH_C_HUB_LOCAL_POWER 0x00
|
||||
#define RH_C_HUB_OVER_CURRENT 0x01
|
||||
|
||||
#define RH_DEVICE_REMOTE_WAKEUP 0x00
|
||||
#define RH_ENDPOINT_STALL 0x01
|
||||
|
||||
#define RH_ACK 0x01
|
||||
#define RH_REQ_ERR -1
|
||||
#define RH_NACK 0x00
|
||||
|
||||
|
||||
/* OHCI ROOT HUB REGISTER MASKS */
|
||||
|
||||
/* roothub.portstatus [i] bits */
|
||||
#define RH_PS_CCS 0x00000001 /* current connect status */
|
||||
#define RH_PS_PES 0x00000002 /* port enable status*/
|
||||
#define RH_PS_PSS 0x00000004 /* port suspend status */
|
||||
#define RH_PS_POCI 0x00000008 /* port over current indicator */
|
||||
#define RH_PS_PRS 0x00000010 /* port reset status */
|
||||
#define RH_PS_PPS 0x00000100 /* port power status */
|
||||
#define RH_PS_LSDA 0x00000200 /* low speed device attached */
|
||||
#define RH_PS_CSC 0x00010000 /* connect status change */
|
||||
#define RH_PS_PESC 0x00020000 /* port enable status change */
|
||||
#define RH_PS_PSSC 0x00040000 /* port suspend status change */
|
||||
#define RH_PS_OCIC 0x00080000 /* over current indicator change */
|
||||
#define RH_PS_PRSC 0x00100000 /* port reset status change */
|
||||
|
||||
/* roothub.status bits */
|
||||
#define RH_HS_LPS 0x00000001 /* local power status */
|
||||
#define RH_HS_OCI 0x00000002 /* over current indicator */
|
||||
#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */
|
||||
#define RH_HS_LPSC 0x00010000 /* local power status change */
|
||||
#define RH_HS_OCIC 0x00020000 /* over current indicator change */
|
||||
#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */
|
||||
|
||||
/* roothub.b masks */
|
||||
#define RH_B_DR 0x0000ffff /* device removable flags */
|
||||
#define RH_B_PPCM 0xffff0000 /* port power control mask */
|
||||
|
||||
/* roothub.a masks */
|
||||
#define RH_A_NDP (0xff << 0) /* number of downstream ports */
|
||||
#define RH_A_PSM (1 << 8) /* power switching mode */
|
||||
#define RH_A_NPS (1 << 9) /* no power switching */
|
||||
#define RH_A_DT (1 << 10) /* device type (mbz) */
|
||||
#define RH_A_OCPM (1 << 11) /* over current protection mode */
|
||||
#define RH_A_NOCP (1 << 12) /* no over current protection */
|
||||
#define RH_A_POTPGT (0xff << 24) /* power on to power good time */
|
||||
|
||||
/* urb */
|
||||
#define N_URB_TD 48
|
||||
typedef struct
|
||||
{
|
||||
ed_t *ed;
|
||||
__u16 length; /* number of tds associated with this request */
|
||||
__u16 td_cnt; /* number of tds already serviced */
|
||||
struct usb_device *dev;
|
||||
int state;
|
||||
unsigned long pipe;
|
||||
void *transfer_buffer;
|
||||
int transfer_buffer_length;
|
||||
int interval;
|
||||
int actual_length;
|
||||
int finished;
|
||||
td_t *td[N_URB_TD]; /* list pointer to all corresponding TDs associated with this request */
|
||||
} urb_priv_t;
|
||||
#define URB_DEL 1
|
||||
|
||||
#define NUM_EDS 8 /* num of preallocated endpoint descriptors */
|
||||
|
||||
#define NUM_TD 64 /* we need more TDs than EDs */
|
||||
|
||||
#define NUM_INT_DEVS 8 /* num of ohci_dev structs for int endpoints */
|
||||
|
||||
typedef struct ohci_device {
|
||||
ed_t ed[NUM_EDS] __aligned(ED_ALIGNMENT);
|
||||
td_t tds[NUM_TD] __aligned(TD_ALIGNMENT);
|
||||
int ed_cnt;
|
||||
int devnum;
|
||||
} ohci_dev_t;
|
||||
|
||||
/*
|
||||
* This is the full ohci controller description
|
||||
*
|
||||
* Note how the "proper" USB information is just
|
||||
* a subset of what the full implementation needs. (Linus)
|
||||
*/
|
||||
|
||||
|
||||
typedef struct ohci {
|
||||
/* this allocates EDs for all possible endpoints */
|
||||
struct ohci_device ohci_dev __aligned(TD_ALIGNMENT);
|
||||
struct ohci_device int_dev[NUM_INT_DEVS] __aligned(TD_ALIGNMENT);
|
||||
struct ohci_hcca *hcca; /* hcca */
|
||||
/*dma_addr_t hcca_dma;*/
|
||||
|
||||
int irq;
|
||||
int disabled; /* e.g. got a UE, we're hung */
|
||||
int sleeping;
|
||||
unsigned long flags; /* for HC bugs */
|
||||
|
||||
struct ohci_regs *regs; /* OHCI controller's memory */
|
||||
|
||||
int ohci_int_load[32]; /* load of the 32 Interrupt Chains (for load balancing)*/
|
||||
ed_t *ed_rm_list[2]; /* lists of all endpoints to be removed */
|
||||
ed_t *ed_bulktail; /* last endpoint of bulk list */
|
||||
ed_t *ed_controltail; /* last endpoint of control list */
|
||||
int intrstatus;
|
||||
__u32 hc_control; /* copy of the hc control reg */
|
||||
struct usb_device *dev[32];
|
||||
struct virt_root_hub rh;
|
||||
|
||||
const char *slot_name;
|
||||
} ohci_t;
|
||||
|
||||
#ifdef CONFIG_DM_USB
|
||||
extern struct dm_usb_ops ohci_usb_ops;
|
||||
|
||||
int ohci_register(struct udevice *dev, struct ohci_regs *regs);
|
||||
int ohci_deregister(struct udevice *dev);
|
||||
#endif
|
||||
818
u-boot/drivers/usb/host/r8a66597-hcd.c
Normal file
818
u-boot/drivers/usb/host/r8a66597-hcd.c
Normal file
@@ -0,0 +1,818 @@
|
||||
/*
|
||||
* R8A66597 HCD (Host Controller Driver) for u-boot
|
||||
*
|
||||
* Copyright (C) 2008 Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <console.h>
|
||||
#include <usb.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "r8a66597.h"
|
||||
|
||||
#ifdef R8A66597_DEBUG
|
||||
#define R8A66597_DPRINT printf
|
||||
#else
|
||||
#define R8A66597_DPRINT(...)
|
||||
#endif
|
||||
|
||||
static const char hcd_name[] = "r8a66597_hcd";
|
||||
static struct r8a66597 gr8a66597;
|
||||
|
||||
static void get_hub_data(struct usb_device *dev, u16 *hub_devnum, u16 *hubport)
|
||||
{
|
||||
int i;
|
||||
|
||||
*hub_devnum = 0;
|
||||
*hubport = 0;
|
||||
|
||||
/* check a device connected to root_hub */
|
||||
if ((dev->parent && dev->parent->devnum == 1) ||
|
||||
(dev->devnum == 1))
|
||||
return;
|
||||
|
||||
for (i = 0; i < USB_MAXCHILDREN; i++) {
|
||||
if (dev->parent->children[i] == dev) {
|
||||
*hub_devnum = (u8)dev->parent->devnum;
|
||||
*hubport = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
printf("get_hub_data error.\n");
|
||||
}
|
||||
|
||||
static void set_devadd(struct r8a66597 *r8a66597, u8 r8a66597_address,
|
||||
struct usb_device *dev, int port)
|
||||
{
|
||||
u16 val, usbspd, upphub, hubport;
|
||||
unsigned long devadd_reg = get_devadd_addr(r8a66597_address);
|
||||
|
||||
get_hub_data(dev, &upphub, &hubport);
|
||||
usbspd = r8a66597->speed;
|
||||
val = (upphub << 11) | (hubport << 8) | (usbspd << 6) | (port & 0x0001);
|
||||
r8a66597_write(r8a66597, val, devadd_reg);
|
||||
}
|
||||
|
||||
static int r8a66597_clock_enable(struct r8a66597 *r8a66597)
|
||||
{
|
||||
u16 tmp;
|
||||
int i = 0;
|
||||
|
||||
#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
|
||||
do {
|
||||
r8a66597_write(r8a66597, SCKE, SYSCFG0);
|
||||
tmp = r8a66597_read(r8a66597, SYSCFG0);
|
||||
if (i++ > 1000) {
|
||||
printf("register access fail.\n");
|
||||
return -1;
|
||||
}
|
||||
} while ((tmp & SCKE) != SCKE);
|
||||
r8a66597_write(r8a66597, 0x04, 0x02);
|
||||
#else
|
||||
do {
|
||||
r8a66597_write(r8a66597, USBE, SYSCFG0);
|
||||
tmp = r8a66597_read(r8a66597, SYSCFG0);
|
||||
if (i++ > 1000) {
|
||||
printf("register access fail.\n");
|
||||
return -1;
|
||||
}
|
||||
} while ((tmp & USBE) != USBE);
|
||||
r8a66597_bclr(r8a66597, USBE, SYSCFG0);
|
||||
r8a66597_mdfy(r8a66597, CONFIG_R8A66597_XTAL, XTAL, SYSCFG0);
|
||||
|
||||
i = 0;
|
||||
r8a66597_bset(r8a66597, XCKE, SYSCFG0);
|
||||
do {
|
||||
udelay(1000);
|
||||
tmp = r8a66597_read(r8a66597, SYSCFG0);
|
||||
if (i++ > 500) {
|
||||
printf("register access fail.\n");
|
||||
return -1;
|
||||
}
|
||||
} while ((tmp & SCKE) != SCKE);
|
||||
#endif /* #if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void r8a66597_clock_disable(struct r8a66597 *r8a66597)
|
||||
{
|
||||
r8a66597_bclr(r8a66597, SCKE, SYSCFG0);
|
||||
udelay(1);
|
||||
#if !defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
|
||||
r8a66597_bclr(r8a66597, PLLC, SYSCFG0);
|
||||
r8a66597_bclr(r8a66597, XCKE, SYSCFG0);
|
||||
r8a66597_bclr(r8a66597, USBE, SYSCFG0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void r8a66597_enable_port(struct r8a66597 *r8a66597, int port)
|
||||
{
|
||||
u16 val;
|
||||
|
||||
val = port ? DRPD : DCFM | DRPD;
|
||||
r8a66597_bset(r8a66597, val, get_syscfg_reg(port));
|
||||
r8a66597_bset(r8a66597, HSE, get_syscfg_reg(port));
|
||||
|
||||
r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR, get_dmacfg_reg(port));
|
||||
}
|
||||
|
||||
static void r8a66597_disable_port(struct r8a66597 *r8a66597, int port)
|
||||
{
|
||||
u16 val, tmp;
|
||||
|
||||
r8a66597_write(r8a66597, 0, get_intenb_reg(port));
|
||||
r8a66597_write(r8a66597, 0, get_intsts_reg(port));
|
||||
|
||||
r8a66597_port_power(r8a66597, port, 0);
|
||||
|
||||
do {
|
||||
tmp = r8a66597_read(r8a66597, SOFCFG) & EDGESTS;
|
||||
udelay(640);
|
||||
} while (tmp == EDGESTS);
|
||||
|
||||
val = port ? DRPD : DCFM | DRPD;
|
||||
r8a66597_bclr(r8a66597, val, get_syscfg_reg(port));
|
||||
r8a66597_bclr(r8a66597, HSE, get_syscfg_reg(port));
|
||||
}
|
||||
|
||||
static int enable_controller(struct r8a66597 *r8a66597)
|
||||
{
|
||||
int ret, port;
|
||||
|
||||
ret = r8a66597_clock_enable(r8a66597);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
r8a66597_bset(r8a66597, CONFIG_R8A66597_LDRV & LDRV, PINCFG);
|
||||
r8a66597_bset(r8a66597, USBE, SYSCFG0);
|
||||
|
||||
r8a66597_bset(r8a66597, INTL, SOFCFG);
|
||||
r8a66597_write(r8a66597, 0, INTENB0);
|
||||
for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++)
|
||||
r8a66597_write(r8a66597, 0, get_intenb_reg(port));
|
||||
|
||||
r8a66597_bset(r8a66597, CONFIG_R8A66597_ENDIAN & BIGEND, CFIFOSEL);
|
||||
r8a66597_bset(r8a66597, CONFIG_R8A66597_ENDIAN & BIGEND, D0FIFOSEL);
|
||||
r8a66597_bset(r8a66597, CONFIG_R8A66597_ENDIAN & BIGEND, D1FIFOSEL);
|
||||
r8a66597_bset(r8a66597, TRNENSEL, SOFCFG);
|
||||
|
||||
for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++)
|
||||
r8a66597_enable_port(r8a66597, port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void disable_controller(struct r8a66597 *r8a66597)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!(r8a66597_read(r8a66597, SYSCFG0) & USBE))
|
||||
return;
|
||||
|
||||
r8a66597_write(r8a66597, 0, INTENB0);
|
||||
r8a66597_write(r8a66597, 0, INTSTS0);
|
||||
|
||||
r8a66597_write(r8a66597, 0, D0FIFOSEL);
|
||||
r8a66597_write(r8a66597, 0, D1FIFOSEL);
|
||||
r8a66597_write(r8a66597, 0, DCPCFG);
|
||||
r8a66597_write(r8a66597, 0x40, DCPMAXP);
|
||||
r8a66597_write(r8a66597, 0, DCPCTR);
|
||||
|
||||
for (i = 0; i <= 10; i++)
|
||||
r8a66597_write(r8a66597, 0, get_devadd_addr(i));
|
||||
for (i = 1; i <= 5; i++) {
|
||||
r8a66597_write(r8a66597, 0, get_pipetre_addr(i));
|
||||
r8a66597_write(r8a66597, 0, get_pipetrn_addr(i));
|
||||
}
|
||||
for (i = 1; i < R8A66597_MAX_NUM_PIPE; i++) {
|
||||
r8a66597_write(r8a66597, 0, get_pipectr_addr(i));
|
||||
r8a66597_write(r8a66597, i, PIPESEL);
|
||||
r8a66597_write(r8a66597, 0, PIPECFG);
|
||||
r8a66597_write(r8a66597, 0, PIPEBUF);
|
||||
r8a66597_write(r8a66597, 0, PIPEMAXP);
|
||||
r8a66597_write(r8a66597, 0, PIPEPERI);
|
||||
}
|
||||
|
||||
for (i = 0; i < R8A66597_MAX_ROOT_HUB; i++)
|
||||
r8a66597_disable_port(r8a66597, i);
|
||||
|
||||
r8a66597_clock_disable(r8a66597);
|
||||
}
|
||||
|
||||
static void r8a66597_reg_wait(struct r8a66597 *r8a66597, unsigned long reg,
|
||||
u16 mask, u16 loop)
|
||||
{
|
||||
u16 tmp;
|
||||
int i = 0;
|
||||
|
||||
do {
|
||||
tmp = r8a66597_read(r8a66597, reg);
|
||||
if (i++ > 1000000) {
|
||||
printf("register%lx, loop %x is timeout\n", reg, loop);
|
||||
break;
|
||||
}
|
||||
} while ((tmp & mask) != loop);
|
||||
}
|
||||
|
||||
static void pipe_buffer_setting(struct r8a66597 *r8a66597,
|
||||
struct usb_device *dev, unsigned long pipe)
|
||||
{
|
||||
u16 val = 0;
|
||||
u16 pipenum, bufnum, maxpacket;
|
||||
|
||||
if (usb_pipein(pipe)) {
|
||||
pipenum = BULK_IN_PIPENUM;
|
||||
bufnum = BULK_IN_BUFNUM;
|
||||
maxpacket = dev->epmaxpacketin[usb_pipeendpoint(pipe)];
|
||||
} else {
|
||||
pipenum = BULK_OUT_PIPENUM;
|
||||
bufnum = BULK_OUT_BUFNUM;
|
||||
maxpacket = dev->epmaxpacketout[usb_pipeendpoint(pipe)];
|
||||
}
|
||||
|
||||
if (r8a66597->pipe_config & (1 << pipenum))
|
||||
return;
|
||||
r8a66597->pipe_config |= (1 << pipenum);
|
||||
|
||||
r8a66597_bset(r8a66597, ACLRM, get_pipectr_addr(pipenum));
|
||||
r8a66597_bclr(r8a66597, ACLRM, get_pipectr_addr(pipenum));
|
||||
r8a66597_write(r8a66597, pipenum, PIPESEL);
|
||||
|
||||
/* FIXME: This driver support bulk transfer only. */
|
||||
if (!usb_pipein(pipe))
|
||||
val |= R8A66597_DIR;
|
||||
else
|
||||
val |= R8A66597_SHTNAK;
|
||||
val |= R8A66597_BULK | R8A66597_DBLB | usb_pipeendpoint(pipe);
|
||||
r8a66597_write(r8a66597, val, PIPECFG);
|
||||
|
||||
r8a66597_write(r8a66597, (8 << 10) | bufnum, PIPEBUF);
|
||||
r8a66597_write(r8a66597, make_devsel(usb_pipedevice(pipe)) |
|
||||
maxpacket, PIPEMAXP);
|
||||
r8a66597_write(r8a66597, 0, PIPEPERI);
|
||||
r8a66597_write(r8a66597, SQCLR, get_pipectr_addr(pipenum));
|
||||
}
|
||||
|
||||
static int send_setup_packet(struct r8a66597 *r8a66597, struct usb_device *dev,
|
||||
struct devrequest *setup)
|
||||
{
|
||||
int i;
|
||||
unsigned short *p = (unsigned short *)setup;
|
||||
unsigned long setup_addr = USBREQ;
|
||||
u16 intsts1;
|
||||
int timeout = 3000;
|
||||
u16 devsel = setup->request == USB_REQ_SET_ADDRESS ? 0 : dev->devnum;
|
||||
|
||||
r8a66597_write(r8a66597, make_devsel(devsel) |
|
||||
(8 << dev->maxpacketsize), DCPMAXP);
|
||||
r8a66597_write(r8a66597, ~(SIGN | SACK), INTSTS1);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
r8a66597_write(r8a66597, le16_to_cpu(p[i]), setup_addr);
|
||||
setup_addr += 2;
|
||||
}
|
||||
r8a66597_write(r8a66597, ~0x0001, BRDYSTS);
|
||||
r8a66597_write(r8a66597, SUREQ, DCPCTR);
|
||||
|
||||
while (1) {
|
||||
intsts1 = r8a66597_read(r8a66597, INTSTS1);
|
||||
if (intsts1 & SACK)
|
||||
break;
|
||||
if (intsts1 & SIGN) {
|
||||
printf("setup packet send error\n");
|
||||
return -1;
|
||||
}
|
||||
if (timeout-- < 0) {
|
||||
printf("setup packet timeout\n");
|
||||
return -1;
|
||||
}
|
||||
udelay(500);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_bulk_packet(struct r8a66597 *r8a66597, struct usb_device *dev,
|
||||
unsigned long pipe, void *buffer, int transfer_len)
|
||||
{
|
||||
u16 tmp, bufsize;
|
||||
u16 *buf;
|
||||
size_t size;
|
||||
|
||||
R8A66597_DPRINT("%s\n", __func__);
|
||||
|
||||
r8a66597_mdfy(r8a66597, MBW | BULK_OUT_PIPENUM,
|
||||
MBW | CURPIPE, CFIFOSEL);
|
||||
r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, BULK_OUT_PIPENUM);
|
||||
tmp = r8a66597_read(r8a66597, CFIFOCTR);
|
||||
if ((tmp & FRDY) == 0) {
|
||||
printf("%s FRDY is not set (%x)\n", __func__, tmp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* prepare parameters */
|
||||
bufsize = dev->epmaxpacketout[usb_pipeendpoint(pipe)];
|
||||
buf = (u16 *)(buffer + dev->act_len);
|
||||
size = min((int)bufsize, transfer_len - dev->act_len);
|
||||
|
||||
/* write fifo */
|
||||
r8a66597_write(r8a66597, ~(1 << BULK_OUT_PIPENUM), BEMPSTS);
|
||||
if (buffer) {
|
||||
r8a66597_write_fifo(r8a66597, CFIFO, buf, size);
|
||||
r8a66597_write(r8a66597, BVAL, CFIFOCTR);
|
||||
}
|
||||
|
||||
/* update parameters */
|
||||
dev->act_len += size;
|
||||
|
||||
r8a66597_mdfy(r8a66597, PID_BUF, PID,
|
||||
get_pipectr_addr(BULK_OUT_PIPENUM));
|
||||
|
||||
while (!(r8a66597_read(r8a66597, BEMPSTS) & (1 << BULK_OUT_PIPENUM)))
|
||||
if (ctrlc())
|
||||
return -1;
|
||||
r8a66597_write(r8a66597, ~(1 << BULK_OUT_PIPENUM), BEMPSTS);
|
||||
|
||||
if (dev->act_len >= transfer_len)
|
||||
r8a66597_mdfy(r8a66597, PID_NAK, PID,
|
||||
get_pipectr_addr(BULK_OUT_PIPENUM));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int receive_bulk_packet(struct r8a66597 *r8a66597,
|
||||
struct usb_device *dev,
|
||||
unsigned long pipe,
|
||||
void *buffer, int transfer_len)
|
||||
{
|
||||
u16 tmp;
|
||||
u16 *buf;
|
||||
const u16 pipenum = BULK_IN_PIPENUM;
|
||||
int rcv_len;
|
||||
int maxpacket = dev->epmaxpacketin[usb_pipeendpoint(pipe)];
|
||||
|
||||
R8A66597_DPRINT("%s\n", __func__);
|
||||
|
||||
/* prepare */
|
||||
if (dev->act_len == 0) {
|
||||
r8a66597_mdfy(r8a66597, PID_NAK, PID,
|
||||
get_pipectr_addr(pipenum));
|
||||
r8a66597_write(r8a66597, ~(1 << pipenum), BRDYSTS);
|
||||
|
||||
r8a66597_write(r8a66597, TRCLR, get_pipetre_addr(pipenum));
|
||||
r8a66597_write(r8a66597,
|
||||
(transfer_len + maxpacket - 1) / maxpacket,
|
||||
get_pipetrn_addr(pipenum));
|
||||
r8a66597_bset(r8a66597, TRENB, get_pipetre_addr(pipenum));
|
||||
|
||||
r8a66597_mdfy(r8a66597, PID_BUF, PID,
|
||||
get_pipectr_addr(pipenum));
|
||||
}
|
||||
|
||||
r8a66597_mdfy(r8a66597, MBW | pipenum, MBW | CURPIPE, CFIFOSEL);
|
||||
r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, pipenum);
|
||||
|
||||
while (!(r8a66597_read(r8a66597, BRDYSTS) & (1 << pipenum)))
|
||||
if (ctrlc())
|
||||
return -1;
|
||||
r8a66597_write(r8a66597, ~(1 << pipenum), BRDYSTS);
|
||||
|
||||
tmp = r8a66597_read(r8a66597, CFIFOCTR);
|
||||
if ((tmp & FRDY) == 0) {
|
||||
printf("%s FRDY is not set. (%x)\n", __func__, tmp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf = (u16 *)(buffer + dev->act_len);
|
||||
rcv_len = tmp & DTLN;
|
||||
dev->act_len += rcv_len;
|
||||
|
||||
if (buffer) {
|
||||
if (rcv_len == 0)
|
||||
r8a66597_write(r8a66597, BCLR, CFIFOCTR);
|
||||
else
|
||||
r8a66597_read_fifo(r8a66597, CFIFO, buf, rcv_len);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int receive_control_packet(struct r8a66597 *r8a66597,
|
||||
struct usb_device *dev,
|
||||
void *buffer, int transfer_len)
|
||||
{
|
||||
u16 tmp;
|
||||
int rcv_len;
|
||||
|
||||
/* FIXME: limit transfer size : 64byte or less */
|
||||
|
||||
r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG);
|
||||
r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL);
|
||||
r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
|
||||
r8a66597_bset(r8a66597, SQSET, DCPCTR);
|
||||
r8a66597_write(r8a66597, BCLR, CFIFOCTR);
|
||||
r8a66597_mdfy(r8a66597, PID_BUF, PID, DCPCTR);
|
||||
|
||||
while (!(r8a66597_read(r8a66597, BRDYSTS) & 0x0001))
|
||||
if (ctrlc())
|
||||
return -1;
|
||||
r8a66597_write(r8a66597, ~0x0001, BRDYSTS);
|
||||
|
||||
r8a66597_mdfy(r8a66597, MBW, MBW | CURPIPE, CFIFOSEL);
|
||||
r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
|
||||
|
||||
tmp = r8a66597_read(r8a66597, CFIFOCTR);
|
||||
if ((tmp & FRDY) == 0) {
|
||||
printf("%s FRDY is not set. (%x)\n", __func__, tmp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rcv_len = tmp & DTLN;
|
||||
dev->act_len += rcv_len;
|
||||
|
||||
r8a66597_mdfy(r8a66597, PID_NAK, PID, DCPCTR);
|
||||
|
||||
if (buffer) {
|
||||
if (rcv_len == 0)
|
||||
r8a66597_write(r8a66597, BCLR, DCPCTR);
|
||||
else
|
||||
r8a66597_read_fifo(r8a66597, CFIFO, buffer, rcv_len);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_status_packet(struct r8a66597 *r8a66597,
|
||||
unsigned long pipe)
|
||||
{
|
||||
r8a66597_bset(r8a66597, SQSET, DCPCTR);
|
||||
r8a66597_mdfy(r8a66597, PID_NAK, PID, DCPCTR);
|
||||
|
||||
if (usb_pipein(pipe)) {
|
||||
r8a66597_bset(r8a66597, R8A66597_DIR, DCPCFG);
|
||||
r8a66597_mdfy(r8a66597, ISEL, ISEL | CURPIPE, CFIFOSEL);
|
||||
r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
|
||||
r8a66597_write(r8a66597, ~BEMP0, BEMPSTS);
|
||||
r8a66597_write(r8a66597, BCLR | BVAL, CFIFOCTR);
|
||||
} else {
|
||||
r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG);
|
||||
r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL);
|
||||
r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
|
||||
r8a66597_write(r8a66597, BCLR, CFIFOCTR);
|
||||
}
|
||||
r8a66597_mdfy(r8a66597, PID_BUF, PID, DCPCTR);
|
||||
|
||||
while (!(r8a66597_read(r8a66597, BEMPSTS) & 0x0001))
|
||||
if (ctrlc())
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void r8a66597_check_syssts(struct r8a66597 *r8a66597, int port)
|
||||
{
|
||||
int count = R8A66597_MAX_SAMPLING;
|
||||
unsigned short syssts, old_syssts;
|
||||
|
||||
R8A66597_DPRINT("%s\n", __func__);
|
||||
|
||||
old_syssts = r8a66597_read(r8a66597, get_syssts_reg(port) & LNST);
|
||||
while (count > 0) {
|
||||
mdelay(R8A66597_RH_POLL_TIME);
|
||||
|
||||
syssts = r8a66597_read(r8a66597, get_syssts_reg(port) & LNST);
|
||||
if (syssts == old_syssts) {
|
||||
count--;
|
||||
} else {
|
||||
count = R8A66597_MAX_SAMPLING;
|
||||
old_syssts = syssts;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void r8a66597_bus_reset(struct r8a66597 *r8a66597, int port)
|
||||
{
|
||||
mdelay(10);
|
||||
r8a66597_mdfy(r8a66597, USBRST, USBRST | UACT, get_dvstctr_reg(port));
|
||||
mdelay(50);
|
||||
r8a66597_mdfy(r8a66597, UACT, USBRST | UACT, get_dvstctr_reg(port));
|
||||
mdelay(50);
|
||||
}
|
||||
|
||||
static int check_usb_device_connecting(struct r8a66597 *r8a66597)
|
||||
{
|
||||
int timeout = 10000; /* 100usec * 10000 = 1sec */
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
/* check a usb cable connect */
|
||||
while (!(r8a66597_read(r8a66597, INTSTS1) & ATTCH)) {
|
||||
if (timeout-- < 0) {
|
||||
printf("%s timeout.\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
/* check a data line */
|
||||
r8a66597_check_syssts(r8a66597, 0);
|
||||
|
||||
r8a66597_bus_reset(r8a66597, 0);
|
||||
r8a66597->speed = get_rh_usb_speed(r8a66597, 0);
|
||||
|
||||
if (!(r8a66597_read(r8a66597, INTSTS1) & DTCH)) {
|
||||
r8a66597->port_change = USB_PORT_STAT_C_CONNECTION;
|
||||
r8a66597->port_status = USB_PORT_STAT_CONNECTION |
|
||||
USB_PORT_STAT_ENABLE;
|
||||
return 0; /* success */
|
||||
}
|
||||
|
||||
R8A66597_DPRINT("USB device has detached. retry = %d\n", i);
|
||||
r8a66597_write(r8a66597, ~DTCH, INTSTS1);
|
||||
}
|
||||
|
||||
return -1; /* fail */
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*
|
||||
* Virtual Root Hub
|
||||
*-------------------------------------------------------------------------*/
|
||||
|
||||
#include <usbroothubdes.h>
|
||||
|
||||
static int r8a66597_submit_rh_msg(struct usb_device *dev, unsigned long pipe,
|
||||
void *buffer, int transfer_len, struct devrequest *cmd)
|
||||
{
|
||||
struct r8a66597 *r8a66597 = &gr8a66597;
|
||||
int leni = transfer_len;
|
||||
int len = 0;
|
||||
int stat = 0;
|
||||
__u16 bmRType_bReq;
|
||||
__u16 wValue;
|
||||
__u16 wLength;
|
||||
unsigned char data[32];
|
||||
|
||||
R8A66597_DPRINT("%s\n", __func__);
|
||||
|
||||
if (usb_pipeint(pipe)) {
|
||||
printf("Root-Hub submit IRQ: NOT implemented");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bmRType_bReq = cmd->requesttype | (cmd->request << 8);
|
||||
wValue = cpu_to_le16 (cmd->value);
|
||||
wLength = cpu_to_le16 (cmd->length);
|
||||
|
||||
switch (bmRType_bReq) {
|
||||
case RH_GET_STATUS:
|
||||
*(__u16 *)buffer = cpu_to_le16(1);
|
||||
len = 2;
|
||||
break;
|
||||
case RH_GET_STATUS | RH_INTERFACE:
|
||||
*(__u16 *)buffer = cpu_to_le16(0);
|
||||
len = 2;
|
||||
break;
|
||||
case RH_GET_STATUS | RH_ENDPOINT:
|
||||
*(__u16 *)buffer = cpu_to_le16(0);
|
||||
len = 2;
|
||||
break;
|
||||
case RH_GET_STATUS | RH_CLASS:
|
||||
*(__u32 *)buffer = cpu_to_le32(0);
|
||||
len = 4;
|
||||
break;
|
||||
case RH_GET_STATUS | RH_OTHER | RH_CLASS:
|
||||
*(__u32 *)buffer = cpu_to_le32(r8a66597->port_status |
|
||||
(r8a66597->port_change << 16));
|
||||
len = 4;
|
||||
break;
|
||||
case RH_CLEAR_FEATURE | RH_ENDPOINT:
|
||||
case RH_CLEAR_FEATURE | RH_CLASS:
|
||||
break;
|
||||
|
||||
case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
|
||||
switch (wValue) {
|
||||
case RH_C_PORT_CONNECTION:
|
||||
r8a66597->port_change &= ~USB_PORT_STAT_C_CONNECTION;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
|
||||
switch (wValue) {
|
||||
case (RH_PORT_SUSPEND):
|
||||
break;
|
||||
case (RH_PORT_RESET):
|
||||
r8a66597_bus_reset(r8a66597, 0);
|
||||
break;
|
||||
case (RH_PORT_POWER):
|
||||
break;
|
||||
case (RH_PORT_ENABLE):
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case RH_SET_ADDRESS:
|
||||
gr8a66597.rh_devnum = wValue;
|
||||
break;
|
||||
case RH_GET_DESCRIPTOR:
|
||||
switch ((wValue & 0xff00) >> 8) {
|
||||
case (0x01): /* device descriptor */
|
||||
len = min_t(unsigned int,
|
||||
leni,
|
||||
min_t(unsigned int,
|
||||
sizeof(root_hub_dev_des),
|
||||
wLength));
|
||||
memcpy(buffer, root_hub_dev_des, len);
|
||||
break;
|
||||
case (0x02): /* configuration descriptor */
|
||||
len = min_t(unsigned int,
|
||||
leni,
|
||||
min_t(unsigned int,
|
||||
sizeof(root_hub_config_des),
|
||||
wLength));
|
||||
memcpy(buffer, root_hub_config_des, len);
|
||||
break;
|
||||
case (0x03): /* string descriptors */
|
||||
if (wValue == 0x0300) {
|
||||
len = min_t(unsigned int,
|
||||
leni,
|
||||
min_t(unsigned int,
|
||||
sizeof(root_hub_str_index0),
|
||||
wLength));
|
||||
memcpy(buffer, root_hub_str_index0, len);
|
||||
}
|
||||
if (wValue == 0x0301) {
|
||||
len = min_t(unsigned int,
|
||||
leni,
|
||||
min_t(unsigned int,
|
||||
sizeof(root_hub_str_index1),
|
||||
wLength));
|
||||
memcpy(buffer, root_hub_str_index1, len);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
stat = USB_ST_STALLED;
|
||||
}
|
||||
break;
|
||||
|
||||
case RH_GET_DESCRIPTOR | RH_CLASS:
|
||||
{
|
||||
__u32 temp = 0x00000001;
|
||||
|
||||
data[0] = 9; /* min length; */
|
||||
data[1] = 0x29;
|
||||
data[2] = temp & RH_A_NDP;
|
||||
data[3] = 0;
|
||||
if (temp & RH_A_PSM)
|
||||
data[3] |= 0x1;
|
||||
if (temp & RH_A_NOCP)
|
||||
data[3] |= 0x10;
|
||||
else if (temp & RH_A_OCPM)
|
||||
data[3] |= 0x8;
|
||||
|
||||
/* corresponds to data[4-7] */
|
||||
data[5] = (temp & RH_A_POTPGT) >> 24;
|
||||
data[7] = temp & RH_B_DR;
|
||||
if (data[2] < 7) {
|
||||
data[8] = 0xff;
|
||||
} else {
|
||||
data[0] += 2;
|
||||
data[8] = (temp & RH_B_DR) >> 8;
|
||||
data[10] = data[9] = 0xff;
|
||||
}
|
||||
|
||||
len = min_t(unsigned int, leni,
|
||||
min_t(unsigned int, data[0], wLength));
|
||||
memcpy(buffer, data, len);
|
||||
break;
|
||||
}
|
||||
|
||||
case RH_GET_CONFIGURATION:
|
||||
*(__u8 *) buffer = 0x01;
|
||||
len = 1;
|
||||
break;
|
||||
case RH_SET_CONFIGURATION:
|
||||
break;
|
||||
default:
|
||||
R8A66597_DPRINT("unsupported root hub command");
|
||||
stat = USB_ST_STALLED;
|
||||
}
|
||||
|
||||
mdelay(1);
|
||||
|
||||
len = min_t(int, len, leni);
|
||||
|
||||
dev->act_len = len;
|
||||
dev->status = stat;
|
||||
|
||||
return stat;
|
||||
}
|
||||
|
||||
int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
|
||||
int transfer_len)
|
||||
{
|
||||
struct r8a66597 *r8a66597 = &gr8a66597;
|
||||
int ret = 0;
|
||||
|
||||
R8A66597_DPRINT("%s\n", __func__);
|
||||
R8A66597_DPRINT("pipe = %08x, buffer = %p, len = %d, devnum = %d\n",
|
||||
pipe, buffer, transfer_len, dev->devnum);
|
||||
|
||||
set_devadd(r8a66597, dev->devnum, dev, 0);
|
||||
|
||||
pipe_buffer_setting(r8a66597, dev, pipe);
|
||||
|
||||
dev->act_len = 0;
|
||||
while (dev->act_len < transfer_len && ret == 0) {
|
||||
if (ctrlc())
|
||||
return -1;
|
||||
|
||||
if (usb_pipein(pipe))
|
||||
ret = receive_bulk_packet(r8a66597, dev, pipe, buffer,
|
||||
transfer_len);
|
||||
else
|
||||
ret = send_bulk_packet(r8a66597, dev, pipe, buffer,
|
||||
transfer_len);
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
dev->status = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int submit_control_msg(struct usb_device *dev, unsigned long pipe,
|
||||
void *buffer, int transfer_len, struct devrequest *setup)
|
||||
{
|
||||
struct r8a66597 *r8a66597 = &gr8a66597;
|
||||
u16 r8a66597_address = setup->request == USB_REQ_SET_ADDRESS ?
|
||||
0 : dev->devnum;
|
||||
|
||||
R8A66597_DPRINT("%s\n", __func__);
|
||||
if (usb_pipedevice(pipe) == r8a66597->rh_devnum)
|
||||
return r8a66597_submit_rh_msg(dev, pipe, buffer, transfer_len,
|
||||
setup);
|
||||
|
||||
R8A66597_DPRINT("%s: setup\n", __func__);
|
||||
set_devadd(r8a66597, r8a66597_address, dev, 0);
|
||||
|
||||
if (send_setup_packet(r8a66597, dev, setup) < 0) {
|
||||
printf("setup packet send error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dev->act_len = 0;
|
||||
if (usb_pipein(pipe))
|
||||
if (receive_control_packet(r8a66597, dev, buffer,
|
||||
transfer_len) < 0)
|
||||
return -1;
|
||||
|
||||
if (send_status_packet(r8a66597, pipe) < 0)
|
||||
return -1;
|
||||
|
||||
dev->status = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
|
||||
int transfer_len, int interval)
|
||||
{
|
||||
/* no implement */
|
||||
R8A66597_DPRINT("%s\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
|
||||
{
|
||||
struct r8a66597 *r8a66597 = &gr8a66597;
|
||||
|
||||
R8A66597_DPRINT("%s\n", __func__);
|
||||
|
||||
memset(r8a66597, 0, sizeof(*r8a66597));
|
||||
r8a66597->reg = CONFIG_R8A66597_BASE_ADDR;
|
||||
|
||||
disable_controller(r8a66597);
|
||||
mdelay(100);
|
||||
|
||||
enable_controller(r8a66597);
|
||||
r8a66597_port_power(r8a66597, 0 , 1);
|
||||
|
||||
/* check usb device */
|
||||
check_usb_device_connecting(r8a66597);
|
||||
|
||||
mdelay(50);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_lowlevel_stop(int index)
|
||||
{
|
||||
disable_controller(&gr8a66597);
|
||||
|
||||
return 0;
|
||||
}
|
||||
647
u-boot/drivers/usb/host/r8a66597.h
Normal file
647
u-boot/drivers/usb/host/r8a66597.h
Normal file
@@ -0,0 +1,647 @@
|
||||
/*
|
||||
* R8A66597 HCD (Host Controller Driver) for u-boot
|
||||
*
|
||||
* Copyright (C) 2008 Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#ifndef __R8A66597_H__
|
||||
#define __R8A66597_H__
|
||||
|
||||
#define SYSCFG0 0x00
|
||||
#define SYSCFG1 0x02
|
||||
#define SYSSTS0 0x04
|
||||
#define SYSSTS1 0x06
|
||||
#define DVSTCTR0 0x08
|
||||
#define DVSTCTR1 0x0A
|
||||
#define TESTMODE 0x0C
|
||||
#define PINCFG 0x0E
|
||||
#define DMA0CFG 0x10
|
||||
#define DMA1CFG 0x12
|
||||
#define CFIFO 0x14
|
||||
#define D0FIFO 0x18
|
||||
#define D1FIFO 0x1C
|
||||
#define CFIFOSEL 0x20
|
||||
#define CFIFOCTR 0x22
|
||||
#define CFIFOSIE 0x24
|
||||
#define D0FIFOSEL 0x28
|
||||
#define D0FIFOCTR 0x2A
|
||||
#define D1FIFOSEL 0x2C
|
||||
#define D1FIFOCTR 0x2E
|
||||
#define INTENB0 0x30
|
||||
#define INTENB1 0x32
|
||||
#define INTENB2 0x34
|
||||
#define BRDYENB 0x36
|
||||
#define NRDYENB 0x38
|
||||
#define BEMPENB 0x3A
|
||||
#define SOFCFG 0x3C
|
||||
#define INTSTS0 0x40
|
||||
#define INTSTS1 0x42
|
||||
#define INTSTS2 0x44
|
||||
#define BRDYSTS 0x46
|
||||
#define NRDYSTS 0x48
|
||||
#define BEMPSTS 0x4A
|
||||
#define FRMNUM 0x4C
|
||||
#define UFRMNUM 0x4E
|
||||
#define USBADDR 0x50
|
||||
#define USBREQ 0x54
|
||||
#define USBVAL 0x56
|
||||
#define USBINDX 0x58
|
||||
#define USBLENG 0x5A
|
||||
#define DCPCFG 0x5C
|
||||
#define DCPMAXP 0x5E
|
||||
#define DCPCTR 0x60
|
||||
#define PIPESEL 0x64
|
||||
#define PIPECFG 0x68
|
||||
#define PIPEBUF 0x6A
|
||||
#define PIPEMAXP 0x6C
|
||||
#define PIPEPERI 0x6E
|
||||
#define PIPE1CTR 0x70
|
||||
#define PIPE2CTR 0x72
|
||||
#define PIPE3CTR 0x74
|
||||
#define PIPE4CTR 0x76
|
||||
#define PIPE5CTR 0x78
|
||||
#define PIPE6CTR 0x7A
|
||||
#define PIPE7CTR 0x7C
|
||||
#define PIPE8CTR 0x7E
|
||||
#define PIPE9CTR 0x80
|
||||
#define PIPE1TRE 0x90
|
||||
#define PIPE1TRN 0x92
|
||||
#define PIPE2TRE 0x94
|
||||
#define PIPE2TRN 0x96
|
||||
#define PIPE3TRE 0x98
|
||||
#define PIPE3TRN 0x9A
|
||||
#define PIPE4TRE 0x9C
|
||||
#define PIPE4TRN 0x9E
|
||||
#define PIPE5TRE 0xA0
|
||||
#define PIPE5TRN 0xA2
|
||||
#define DEVADD0 0xD0
|
||||
#define DEVADD1 0xD2
|
||||
#define DEVADD2 0xD4
|
||||
#define DEVADD3 0xD6
|
||||
#define DEVADD4 0xD8
|
||||
#define DEVADD5 0xDA
|
||||
#define DEVADD6 0xDC
|
||||
#define DEVADD7 0xDE
|
||||
#define DEVADD8 0xE0
|
||||
#define DEVADD9 0xE2
|
||||
#define DEVADDA 0xE4
|
||||
|
||||
/* System Configuration Control Register */
|
||||
#define XTAL 0xC000 /* b15-14: Crystal selection */
|
||||
#define XTAL48 0x8000 /* 48MHz */
|
||||
#define XTAL24 0x4000 /* 24MHz */
|
||||
#define XTAL12 0x0000 /* 12MHz */
|
||||
#define XCKE 0x2000 /* b13: External clock enable */
|
||||
#define PLLC 0x0800 /* b11: PLL control */
|
||||
#define SCKE 0x0400 /* b10: USB clock enable */
|
||||
#define PCSDIS 0x0200 /* b9: not CS wakeup */
|
||||
#define LPSME 0x0100 /* b8: Low power sleep mode */
|
||||
#define HSE 0x0080 /* b7: Hi-speed enable */
|
||||
#define DCFM 0x0040 /* b6: Controller function select */
|
||||
#define DRPD 0x0020 /* b5: D+/- pull down control */
|
||||
#define DPRPU 0x0010 /* b4: D+ pull up control */
|
||||
#define USBE 0x0001 /* b0: USB module operation enable */
|
||||
|
||||
/* System Configuration Status Register */
|
||||
#define OVCBIT 0x8000 /* b15-14: Over-current bit */
|
||||
#define OVCMON 0xC000 /* b15-14: Over-current monitor */
|
||||
#define SOFEA 0x0020 /* b5: SOF monitor */
|
||||
#define IDMON 0x0004 /* b3: ID-pin monitor */
|
||||
#define LNST 0x0003 /* b1-0: D+, D- line status */
|
||||
#define SE1 0x0003 /* SE1 */
|
||||
#define FS_KSTS 0x0002 /* Full-Speed K State */
|
||||
#define FS_JSTS 0x0001 /* Full-Speed J State */
|
||||
#define LS_JSTS 0x0002 /* Low-Speed J State */
|
||||
#define LS_KSTS 0x0001 /* Low-Speed K State */
|
||||
#define SE0 0x0000 /* SE0 */
|
||||
|
||||
/* Device State Control Register */
|
||||
#define EXTLP0 0x0400 /* b10: External port */
|
||||
#define VBOUT 0x0200 /* b9: VBUS output */
|
||||
#define WKUP 0x0100 /* b8: Remote wakeup */
|
||||
#define RWUPE 0x0080 /* b7: Remote wakeup sense */
|
||||
#define USBRST 0x0040 /* b6: USB reset enable */
|
||||
#define RESUME 0x0020 /* b5: Resume enable */
|
||||
#define UACT 0x0010 /* b4: USB bus enable */
|
||||
#define RHST 0x0007 /* b1-0: Reset handshake status */
|
||||
#define HSPROC 0x0004 /* HS handshake is processing */
|
||||
#define HSMODE 0x0003 /* Hi-Speed mode */
|
||||
#define FSMODE 0x0002 /* Full-Speed mode */
|
||||
#define LSMODE 0x0001 /* Low-Speed mode */
|
||||
#define UNDECID 0x0000 /* Undecided */
|
||||
|
||||
/* Test Mode Register */
|
||||
#define UTST 0x000F /* b3-0: Test select */
|
||||
#define H_TST_PACKET 0x000C /* HOST TEST Packet */
|
||||
#define H_TST_SE0_NAK 0x000B /* HOST TEST SE0 NAK */
|
||||
#define H_TST_K 0x000A /* HOST TEST K */
|
||||
#define H_TST_J 0x0009 /* HOST TEST J */
|
||||
#define H_TST_NORMAL 0x0000 /* HOST Normal Mode */
|
||||
#define P_TST_PACKET 0x0004 /* PERI TEST Packet */
|
||||
#define P_TST_SE0_NAK 0x0003 /* PERI TEST SE0 NAK */
|
||||
#define P_TST_K 0x0002 /* PERI TEST K */
|
||||
#define P_TST_J 0x0001 /* PERI TEST J */
|
||||
#define P_TST_NORMAL 0x0000 /* PERI Normal Mode */
|
||||
|
||||
/* Data Pin Configuration Register */
|
||||
#define LDRV 0x8000 /* b15: Drive Current Adjust */
|
||||
#define VIF1 0x0000 /* VIF = 1.8V */
|
||||
#define VIF3 0x8000 /* VIF = 3.3V */
|
||||
#define INTA 0x0001 /* b1: USB INT-pin active */
|
||||
|
||||
/* DMAx Pin Configuration Register */
|
||||
#define DREQA 0x4000 /* b14: Dreq active select */
|
||||
#define BURST 0x2000 /* b13: Burst mode */
|
||||
#define DACKA 0x0400 /* b10: Dack active select */
|
||||
#define DFORM 0x0380 /* b9-7: DMA mode select */
|
||||
#define CPU_ADR_RD_WR 0x0000 /* Address + RD/WR mode (CPU bus) */
|
||||
#define CPU_DACK_RD_WR 0x0100 /* DACK + RD/WR mode (CPU bus) */
|
||||
#define CPU_DACK_ONLY 0x0180 /* DACK only mode (CPU bus) */
|
||||
#define SPLIT_DACK_ONLY 0x0200 /* DACK only mode (SPLIT bus) */
|
||||
#define DENDA 0x0040 /* b6: Dend active select */
|
||||
#define PKTM 0x0020 /* b5: Packet mode */
|
||||
#define DENDE 0x0010 /* b4: Dend enable */
|
||||
#define OBUS 0x0004 /* b2: OUTbus mode */
|
||||
|
||||
/* CFIFO/DxFIFO Port Select Register */
|
||||
#define RCNT 0x8000 /* b15: Read count mode */
|
||||
#define REW 0x4000 /* b14: Buffer rewind */
|
||||
#define DCLRM 0x2000 /* b13: DMA buffer clear mode */
|
||||
#define DREQE 0x1000 /* b12: DREQ output enable */
|
||||
#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
|
||||
#define MBW 0x0800
|
||||
#else
|
||||
#define MBW 0x0400 /* b10: Maximum bit width for FIFO access */
|
||||
#endif
|
||||
#define MBW_8 0x0000 /* 8bit */
|
||||
#define MBW_16 0x0400 /* 16bit */
|
||||
#define BIGEND 0x0100 /* b8: Big endian mode */
|
||||
#define BYTE_LITTLE 0x0000 /* little dendian */
|
||||
#define BYTE_BIG 0x0100 /* big endifan */
|
||||
#define ISEL 0x0020 /* b5: DCP FIFO port direction select */
|
||||
#define CURPIPE 0x000F /* b2-0: PIPE select */
|
||||
|
||||
/* CFIFO/DxFIFO Port Control Register */
|
||||
#define BVAL 0x8000 /* b15: Buffer valid flag */
|
||||
#define BCLR 0x4000 /* b14: Buffer clear */
|
||||
#define FRDY 0x2000 /* b13: FIFO ready */
|
||||
#define DTLN 0x0FFF /* b11-0: FIFO received data length */
|
||||
|
||||
/* Interrupt Enable Register 0 */
|
||||
#define VBSE 0x8000 /* b15: VBUS interrupt */
|
||||
#define RSME 0x4000 /* b14: Resume interrupt */
|
||||
#define SOFE 0x2000 /* b13: Frame update interrupt */
|
||||
#define DVSE 0x1000 /* b12: Device state transition interrupt */
|
||||
#define CTRE 0x0800 /* b11: Control transfer stage transition interrupt */
|
||||
#define BEMPE 0x0400 /* b10: Buffer empty interrupt */
|
||||
#define NRDYE 0x0200 /* b9: Buffer not ready interrupt */
|
||||
#define BRDYE 0x0100 /* b8: Buffer ready interrupt */
|
||||
|
||||
/* Interrupt Enable Register 1 */
|
||||
#define OVRCRE 0x8000 /* b15: Over-current interrupt */
|
||||
#define BCHGE 0x4000 /* b14: USB us chenge interrupt */
|
||||
#define DTCHE 0x1000 /* b12: Detach sense interrupt */
|
||||
#define ATTCHE 0x0800 /* b11: Attach sense interrupt */
|
||||
#define EOFERRE 0x0040 /* b6: EOF error interrupt */
|
||||
#define SIGNE 0x0020 /* b5: SETUP IGNORE interrupt */
|
||||
#define SACKE 0x0010 /* b4: SETUP ACK interrupt */
|
||||
|
||||
/* BRDY Interrupt Enable/Status Register */
|
||||
#define BRDY9 0x0200 /* b9: PIPE9 */
|
||||
#define BRDY8 0x0100 /* b8: PIPE8 */
|
||||
#define BRDY7 0x0080 /* b7: PIPE7 */
|
||||
#define BRDY6 0x0040 /* b6: PIPE6 */
|
||||
#define BRDY5 0x0020 /* b5: PIPE5 */
|
||||
#define BRDY4 0x0010 /* b4: PIPE4 */
|
||||
#define BRDY3 0x0008 /* b3: PIPE3 */
|
||||
#define BRDY2 0x0004 /* b2: PIPE2 */
|
||||
#define BRDY1 0x0002 /* b1: PIPE1 */
|
||||
#define BRDY0 0x0001 /* b1: PIPE0 */
|
||||
|
||||
/* NRDY Interrupt Enable/Status Register */
|
||||
#define NRDY9 0x0200 /* b9: PIPE9 */
|
||||
#define NRDY8 0x0100 /* b8: PIPE8 */
|
||||
#define NRDY7 0x0080 /* b7: PIPE7 */
|
||||
#define NRDY6 0x0040 /* b6: PIPE6 */
|
||||
#define NRDY5 0x0020 /* b5: PIPE5 */
|
||||
#define NRDY4 0x0010 /* b4: PIPE4 */
|
||||
#define NRDY3 0x0008 /* b3: PIPE3 */
|
||||
#define NRDY2 0x0004 /* b2: PIPE2 */
|
||||
#define NRDY1 0x0002 /* b1: PIPE1 */
|
||||
#define NRDY0 0x0001 /* b1: PIPE0 */
|
||||
|
||||
/* BEMP Interrupt Enable/Status Register */
|
||||
#define BEMP9 0x0200 /* b9: PIPE9 */
|
||||
#define BEMP8 0x0100 /* b8: PIPE8 */
|
||||
#define BEMP7 0x0080 /* b7: PIPE7 */
|
||||
#define BEMP6 0x0040 /* b6: PIPE6 */
|
||||
#define BEMP5 0x0020 /* b5: PIPE5 */
|
||||
#define BEMP4 0x0010 /* b4: PIPE4 */
|
||||
#define BEMP3 0x0008 /* b3: PIPE3 */
|
||||
#define BEMP2 0x0004 /* b2: PIPE2 */
|
||||
#define BEMP1 0x0002 /* b1: PIPE1 */
|
||||
#define BEMP0 0x0001 /* b0: PIPE0 */
|
||||
|
||||
/* SOF Pin Configuration Register */
|
||||
#define TRNENSEL 0x0100 /* b8: Select transaction enable period */
|
||||
#define BRDYM 0x0040 /* b6: BRDY clear timing */
|
||||
#define INTL 0x0020 /* b5: Interrupt sense select */
|
||||
#define EDGESTS 0x0010 /* b4: */
|
||||
#define SOFMODE 0x000C /* b3-2: SOF pin select */
|
||||
#define SOF_125US 0x0008 /* SOF OUT 125us Frame Signal */
|
||||
#define SOF_1MS 0x0004 /* SOF OUT 1ms Frame Signal */
|
||||
#define SOF_DISABLE 0x0000 /* SOF OUT Disable */
|
||||
|
||||
/* Interrupt Status Register 0 */
|
||||
#define VBINT 0x8000 /* b15: VBUS interrupt */
|
||||
#define RESM 0x4000 /* b14: Resume interrupt */
|
||||
#define SOFR 0x2000 /* b13: SOF frame update interrupt */
|
||||
#define DVST 0x1000 /* b12: Device state transition interrupt */
|
||||
#define CTRT 0x0800 /* b11: Control transfer stage transition interrupt */
|
||||
#define BEMP 0x0400 /* b10: Buffer empty interrupt */
|
||||
#define NRDY 0x0200 /* b9: Buffer not ready interrupt */
|
||||
#define BRDY 0x0100 /* b8: Buffer ready interrupt */
|
||||
#define VBSTS 0x0080 /* b7: VBUS input port */
|
||||
#define DVSQ 0x0070 /* b6-4: Device state */
|
||||
#define DS_SPD_CNFG 0x0070 /* Suspend Configured */
|
||||
#define DS_SPD_ADDR 0x0060 /* Suspend Address */
|
||||
#define DS_SPD_DFLT 0x0050 /* Suspend Default */
|
||||
#define DS_SPD_POWR 0x0040 /* Suspend Powered */
|
||||
#define DS_SUSP 0x0040 /* Suspend */
|
||||
#define DS_CNFG 0x0030 /* Configured */
|
||||
#define DS_ADDS 0x0020 /* Address */
|
||||
#define DS_DFLT 0x0010 /* Default */
|
||||
#define DS_POWR 0x0000 /* Powered */
|
||||
#define DVSQS 0x0030 /* b5-4: Device state */
|
||||
#define VALID 0x0008 /* b3: Setup packet detected flag */
|
||||
#define CTSQ 0x0007 /* b2-0: Control transfer stage */
|
||||
#define CS_SQER 0x0006 /* Sequence error */
|
||||
#define CS_WRND 0x0005 /* Control write nodata status stage */
|
||||
#define CS_WRSS 0x0004 /* Control write status stage */
|
||||
#define CS_WRDS 0x0003 /* Control write data stage */
|
||||
#define CS_RDSS 0x0002 /* Control read status stage */
|
||||
#define CS_RDDS 0x0001 /* Control read data stage */
|
||||
#define CS_IDST 0x0000 /* Idle or setup stage */
|
||||
|
||||
/* Interrupt Status Register 1 */
|
||||
#define OVRCR 0x8000 /* b15: Over-current interrupt */
|
||||
#define BCHG 0x4000 /* b14: USB bus chenge interrupt */
|
||||
#define DTCH 0x1000 /* b12: Detach sense interrupt */
|
||||
#define ATTCH 0x0800 /* b11: Attach sense interrupt */
|
||||
#define EOFERR 0x0040 /* b6: EOF-error interrupt */
|
||||
#define SIGN 0x0020 /* b5: Setup ignore interrupt */
|
||||
#define SACK 0x0010 /* b4: Setup acknowledge interrupt */
|
||||
|
||||
/* Frame Number Register */
|
||||
#define OVRN 0x8000 /* b15: Overrun error */
|
||||
#define CRCE 0x4000 /* b14: Received data error */
|
||||
#define FRNM 0x07FF /* b10-0: Frame number */
|
||||
|
||||
/* Micro Frame Number Register */
|
||||
#define UFRNM 0x0007 /* b2-0: Micro frame number */
|
||||
|
||||
/* Default Control Pipe Maxpacket Size Register */
|
||||
/* Pipe Maxpacket Size Register */
|
||||
#define DEVSEL 0xF000 /* b15-14: Device address select */
|
||||
#define MAXP 0x007F /* b6-0: Maxpacket size of default control pipe */
|
||||
|
||||
/* Default Control Pipe Control Register */
|
||||
#define BSTS 0x8000 /* b15: Buffer status */
|
||||
#define SUREQ 0x4000 /* b14: Send USB request */
|
||||
#define CSCLR 0x2000 /* b13: complete-split status clear */
|
||||
#define CSSTS 0x1000 /* b12: complete-split status */
|
||||
#define SUREQCLR 0x0800 /* b11: stop setup request */
|
||||
#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */
|
||||
#define SQSET 0x0080 /* b7: Sequence toggle bit set */
|
||||
#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */
|
||||
#define PBUSY 0x0020 /* b5: pipe busy */
|
||||
#define PINGE 0x0010 /* b4: ping enable */
|
||||
#define CCPL 0x0004 /* b2: Enable control transfer complete */
|
||||
#define PID 0x0003 /* b1-0: Response PID */
|
||||
#define PID_STALL11 0x0003 /* STALL */
|
||||
#define PID_STALL 0x0002 /* STALL */
|
||||
#define PID_BUF 0x0001 /* BUF */
|
||||
#define PID_NAK 0x0000 /* NAK */
|
||||
|
||||
/* Pipe Window Select Register */
|
||||
#define PIPENM 0x0007 /* b2-0: Pipe select */
|
||||
|
||||
/* Pipe Configuration Register */
|
||||
#define R8A66597_TYP 0xC000 /* b15-14: Transfer type */
|
||||
#define R8A66597_ISO 0xC000 /* Isochronous */
|
||||
#define R8A66597_INT 0x8000 /* Interrupt */
|
||||
#define R8A66597_BULK 0x4000 /* Bulk */
|
||||
#define R8A66597_BFRE 0x0400 /* b10: Buffer ready interrupt mode select */
|
||||
#define R8A66597_DBLB 0x0200 /* b9: Double buffer mode select */
|
||||
#define R8A66597_CNTMD 0x0100 /* b8: Continuous transfer mode select */
|
||||
#define R8A66597_SHTNAK 0x0080 /* b7: Transfer end NAK */
|
||||
#define R8A66597_DIR 0x0010 /* b4: Transfer direction select */
|
||||
#define R8A66597_EPNUM 0x000F /* b3-0: Eendpoint number select */
|
||||
|
||||
/* Pipe Buffer Configuration Register */
|
||||
#define BUFSIZE 0x7C00 /* b14-10: Pipe buffer size */
|
||||
#define BUFNMB 0x007F /* b6-0: Pipe buffer number */
|
||||
#define PIPE0BUF 256
|
||||
#define PIPExBUF 64
|
||||
|
||||
/* Pipe Maxpacket Size Register */
|
||||
#define MXPS 0x07FF /* b10-0: Maxpacket size */
|
||||
|
||||
/* Pipe Cycle Configuration Register */
|
||||
#define IFIS 0x1000 /* b12: Isochronous in-buffer flush mode select */
|
||||
#define IITV 0x0007 /* b2-0: Isochronous interval */
|
||||
|
||||
/* Pipex Control Register */
|
||||
#define BSTS 0x8000 /* b15: Buffer status */
|
||||
#define INBUFM 0x4000 /* b14: IN buffer monitor (Only for PIPE1 to 5) */
|
||||
#define CSCLR 0x2000 /* b13: complete-split status clear */
|
||||
#define CSSTS 0x1000 /* b12: complete-split status */
|
||||
#define ATREPM 0x0400 /* b10: Auto repeat mode */
|
||||
#define ACLRM 0x0200 /* b9: Out buffer auto clear mode */
|
||||
#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */
|
||||
#define SQSET 0x0080 /* b7: Sequence toggle bit set */
|
||||
#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */
|
||||
#define PBUSY 0x0020 /* b5: pipe busy */
|
||||
#define PID 0x0003 /* b1-0: Response PID */
|
||||
|
||||
/* PIPExTRE */
|
||||
#define TRENB 0x0200 /* b9: Transaction counter enable */
|
||||
#define TRCLR 0x0100 /* b8: Transaction counter clear */
|
||||
|
||||
/* PIPExTRN */
|
||||
#define TRNCNT 0xFFFF /* b15-0: Transaction counter */
|
||||
|
||||
/* DEVADDx */
|
||||
#define UPPHUB 0x7800
|
||||
#define HUBPORT 0x0700
|
||||
#define USBSPD 0x00C0
|
||||
#define RTPORT 0x0001
|
||||
|
||||
#define R8A66597_MAX_NUM_PIPE 10
|
||||
#define R8A66597_BUF_BSIZE 8
|
||||
#define R8A66597_MAX_DEVICE 10
|
||||
#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
|
||||
#define R8A66597_MAX_ROOT_HUB 1
|
||||
#else
|
||||
#define R8A66597_MAX_ROOT_HUB 2
|
||||
#endif
|
||||
#define R8A66597_MAX_SAMPLING 5
|
||||
#define R8A66597_RH_POLL_TIME 10
|
||||
|
||||
#define BULK_IN_PIPENUM 3
|
||||
#define BULK_IN_BUFNUM 8
|
||||
|
||||
#define BULK_OUT_PIPENUM 4
|
||||
#define BULK_OUT_BUFNUM 40
|
||||
|
||||
#define check_bulk_or_isoc(pipenum) ((pipenum >= 1 && pipenum <= 5))
|
||||
#define check_interrupt(pipenum) ((pipenum >= 6 && pipenum <= 9))
|
||||
#define make_devsel(addr) (addr << 12)
|
||||
|
||||
struct r8a66597 {
|
||||
unsigned long reg;
|
||||
unsigned short pipe_config; /* bit field */
|
||||
unsigned short port_status;
|
||||
unsigned short port_change;
|
||||
u16 speed; /* HSMODE or FSMODE or LSMODE */
|
||||
unsigned char rh_devnum;
|
||||
};
|
||||
|
||||
static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset)
|
||||
{
|
||||
return inw(r8a66597->reg + offset);
|
||||
}
|
||||
|
||||
static inline void r8a66597_read_fifo(struct r8a66597 *r8a66597,
|
||||
unsigned long offset, void *buf,
|
||||
int len)
|
||||
{
|
||||
int i;
|
||||
#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
|
||||
unsigned long fifoaddr = r8a66597->reg + offset;
|
||||
unsigned long count;
|
||||
unsigned long *p = buf;
|
||||
|
||||
count = len / 4;
|
||||
for (i = 0; i < count; i++)
|
||||
p[i] = inl(r8a66597->reg + offset);
|
||||
|
||||
if (len & 0x00000003) {
|
||||
unsigned long tmp = inl(fifoaddr);
|
||||
memcpy((unsigned char *)buf + count * 4, &tmp, len & 0x03);
|
||||
}
|
||||
#else
|
||||
unsigned short *p = buf;
|
||||
|
||||
len = (len + 1) / 2;
|
||||
for (i = 0; i < len; i++)
|
||||
p[i] = inw(r8a66597->reg + offset);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val,
|
||||
unsigned long offset)
|
||||
{
|
||||
outw(val, r8a66597->reg + offset);
|
||||
}
|
||||
|
||||
static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597,
|
||||
unsigned long offset, void *buf,
|
||||
int len)
|
||||
{
|
||||
int i;
|
||||
unsigned long fifoaddr = r8a66597->reg + offset;
|
||||
#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
|
||||
unsigned long count;
|
||||
unsigned char *pb;
|
||||
unsigned long *p = buf;
|
||||
|
||||
count = len / 4;
|
||||
for (i = 0; i < count; i++)
|
||||
outl(p[i], fifoaddr);
|
||||
|
||||
if (len & 0x00000003) {
|
||||
pb = (unsigned char *)buf + count * 4;
|
||||
for (i = 0; i < (len & 0x00000003); i++) {
|
||||
if (r8a66597_read(r8a66597, CFIFOSEL) & BIGEND)
|
||||
outb(pb[i], fifoaddr + i);
|
||||
else
|
||||
outb(pb[i], fifoaddr + 3 - i);
|
||||
}
|
||||
}
|
||||
#else
|
||||
int odd = len & 0x0001;
|
||||
unsigned short *p = buf;
|
||||
|
||||
len = len / 2;
|
||||
for (i = 0; i < len; i++)
|
||||
outw(p[i], fifoaddr);
|
||||
|
||||
if (odd) {
|
||||
unsigned char *pb = (unsigned char *)(buf + len);
|
||||
outb(*pb, fifoaddr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void r8a66597_mdfy(struct r8a66597 *r8a66597,
|
||||
u16 val, u16 pat, unsigned long offset)
|
||||
{
|
||||
u16 tmp;
|
||||
tmp = r8a66597_read(r8a66597, offset);
|
||||
tmp = tmp & (~pat);
|
||||
tmp = tmp | val;
|
||||
r8a66597_write(r8a66597, tmp, offset);
|
||||
}
|
||||
|
||||
#define r8a66597_bclr(r8a66597, val, offset) \
|
||||
r8a66597_mdfy(r8a66597, 0, val, offset)
|
||||
#define r8a66597_bset(r8a66597, val, offset) \
|
||||
r8a66597_mdfy(r8a66597, val, 0, offset)
|
||||
|
||||
static inline unsigned long get_syscfg_reg(int port)
|
||||
{
|
||||
return port == 0 ? SYSCFG0 : SYSCFG1;
|
||||
}
|
||||
|
||||
static inline unsigned long get_syssts_reg(int port)
|
||||
{
|
||||
return port == 0 ? SYSSTS0 : SYSSTS1;
|
||||
}
|
||||
|
||||
static inline unsigned long get_dvstctr_reg(int port)
|
||||
{
|
||||
return port == 0 ? DVSTCTR0 : DVSTCTR1;
|
||||
}
|
||||
|
||||
static inline unsigned long get_dmacfg_reg(int port)
|
||||
{
|
||||
return port == 0 ? DMA0CFG : DMA1CFG;
|
||||
}
|
||||
|
||||
static inline unsigned long get_intenb_reg(int port)
|
||||
{
|
||||
return port == 0 ? INTENB1 : INTENB2;
|
||||
}
|
||||
|
||||
static inline unsigned long get_intsts_reg(int port)
|
||||
{
|
||||
return port == 0 ? INTSTS1 : INTSTS2;
|
||||
}
|
||||
|
||||
static inline u16 get_rh_usb_speed(struct r8a66597 *r8a66597, int port)
|
||||
{
|
||||
unsigned long dvstctr_reg = get_dvstctr_reg(port);
|
||||
|
||||
return r8a66597_read(r8a66597, dvstctr_reg) & RHST;
|
||||
}
|
||||
|
||||
static inline void r8a66597_port_power(struct r8a66597 *r8a66597, int port,
|
||||
int power)
|
||||
{
|
||||
unsigned long dvstctr_reg = get_dvstctr_reg(port);
|
||||
|
||||
if (power)
|
||||
r8a66597_bset(r8a66597, VBOUT, dvstctr_reg);
|
||||
else
|
||||
r8a66597_bclr(r8a66597, VBOUT, dvstctr_reg);
|
||||
}
|
||||
|
||||
#define get_pipectr_addr(pipenum) (PIPE1CTR + (pipenum - 1) * 2)
|
||||
#define get_pipetre_addr(pipenum) (PIPE1TRE + (pipenum - 1) * 4)
|
||||
#define get_pipetrn_addr(pipenum) (PIPE1TRN + (pipenum - 1) * 4)
|
||||
#define get_devadd_addr(address) (DEVADD0 + address * 2)
|
||||
|
||||
|
||||
/* USB HUB CONSTANTS (not OHCI-specific; see hub.h, based on usb_ohci.h) */
|
||||
|
||||
/* destination of request */
|
||||
#define RH_INTERFACE 0x01
|
||||
#define RH_ENDPOINT 0x02
|
||||
#define RH_OTHER 0x03
|
||||
|
||||
#define RH_CLASS 0x20
|
||||
#define RH_VENDOR 0x40
|
||||
|
||||
/* Requests: bRequest << 8 | bmRequestType */
|
||||
#define RH_GET_STATUS 0x0080
|
||||
#define RH_CLEAR_FEATURE 0x0100
|
||||
#define RH_SET_FEATURE 0x0300
|
||||
#define RH_SET_ADDRESS 0x0500
|
||||
#define RH_GET_DESCRIPTOR 0x0680
|
||||
#define RH_SET_DESCRIPTOR 0x0700
|
||||
#define RH_GET_CONFIGURATION 0x0880
|
||||
#define RH_SET_CONFIGURATION 0x0900
|
||||
#define RH_GET_STATE 0x0280
|
||||
#define RH_GET_INTERFACE 0x0A80
|
||||
#define RH_SET_INTERFACE 0x0B00
|
||||
#define RH_SYNC_FRAME 0x0C80
|
||||
/* Our Vendor Specific Request */
|
||||
#define RH_SET_EP 0x2000
|
||||
|
||||
/* Hub port features */
|
||||
#define RH_PORT_CONNECTION 0x00
|
||||
#define RH_PORT_ENABLE 0x01
|
||||
#define RH_PORT_SUSPEND 0x02
|
||||
#define RH_PORT_OVER_CURRENT 0x03
|
||||
#define RH_PORT_RESET 0x04
|
||||
#define RH_PORT_POWER 0x08
|
||||
#define RH_PORT_LOW_SPEED 0x09
|
||||
|
||||
#define RH_C_PORT_CONNECTION 0x10
|
||||
#define RH_C_PORT_ENABLE 0x11
|
||||
#define RH_C_PORT_SUSPEND 0x12
|
||||
#define RH_C_PORT_OVER_CURRENT 0x13
|
||||
#define RH_C_PORT_RESET 0x14
|
||||
|
||||
/* Hub features */
|
||||
#define RH_C_HUB_LOCAL_POWER 0x00
|
||||
#define RH_C_HUB_OVER_CURRENT 0x01
|
||||
|
||||
#define RH_DEVICE_REMOTE_WAKEUP 0x00
|
||||
#define RH_ENDPOINT_STALL 0x01
|
||||
|
||||
#define RH_ACK 0x01
|
||||
#define RH_REQ_ERR -1
|
||||
#define RH_NACK 0x00
|
||||
|
||||
/* OHCI ROOT HUB REGISTER MASKS */
|
||||
|
||||
/* roothub.portstatus [i] bits */
|
||||
#define RH_PS_CCS 0x00000001 /* current connect status */
|
||||
#define RH_PS_PES 0x00000002 /* port enable status*/
|
||||
#define RH_PS_PSS 0x00000004 /* port suspend status */
|
||||
#define RH_PS_POCI 0x00000008 /* port over current indicator */
|
||||
#define RH_PS_PRS 0x00000010 /* port reset status */
|
||||
#define RH_PS_PPS 0x00000100 /* port power status */
|
||||
#define RH_PS_LSDA 0x00000200 /* low speed device attached */
|
||||
#define RH_PS_CSC 0x00010000 /* connect status change */
|
||||
#define RH_PS_PESC 0x00020000 /* port enable status change */
|
||||
#define RH_PS_PSSC 0x00040000 /* port suspend status change */
|
||||
#define RH_PS_OCIC 0x00080000 /* over current indicator change */
|
||||
#define RH_PS_PRSC 0x00100000 /* port reset status change */
|
||||
|
||||
/* roothub.status bits */
|
||||
#define RH_HS_LPS 0x00000001 /* local power status */
|
||||
#define RH_HS_OCI 0x00000002 /* over current indicator */
|
||||
#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */
|
||||
#define RH_HS_LPSC 0x00010000 /* local power status change */
|
||||
#define RH_HS_OCIC 0x00020000 /* over current indicator change */
|
||||
#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */
|
||||
|
||||
/* roothub.b masks */
|
||||
#define RH_B_DR 0x0000ffff /* device removable flags */
|
||||
#define RH_B_PPCM 0xffff0000 /* port power control mask */
|
||||
|
||||
/* roothub.a masks */
|
||||
#define RH_A_NDP (0xff << 0) /* number of downstream ports */
|
||||
#define RH_A_PSM (1 << 8) /* power switching mode */
|
||||
#define RH_A_NPS (1 << 9) /* no power switching */
|
||||
#define RH_A_DT (1 << 10) /* device type (mbz) */
|
||||
#define RH_A_OCPM (1 << 11) /* over current protection mode */
|
||||
#define RH_A_NOCP (1 << 12) /* no over current protection */
|
||||
#define RH_A_POTPGT (0xff << 24) /* power on to power good time */
|
||||
|
||||
#endif /* __R8A66597_H__ */
|
||||
714
u-boot/drivers/usb/host/sl811-hcd.c
Normal file
714
u-boot/drivers/usb/host/sl811-hcd.c
Normal file
@@ -0,0 +1,714 @@
|
||||
/*
|
||||
* (C) Copyright 2004
|
||||
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
*
|
||||
* This code is based on linux driver for sl811hs chip, source at
|
||||
* drivers/usb/host/sl811.c:
|
||||
*
|
||||
* SL811 Host Controller Interface driver for USB.
|
||||
*
|
||||
* Copyright (c) 2003/06, Courage Co., Ltd.
|
||||
*
|
||||
* Based on:
|
||||
* 1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap,
|
||||
* Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber,
|
||||
* Adam Richter, Gregory P. Smith;
|
||||
* 2.Original SL811 driver (hc_sl811.o) by Pei Liu <pbl@cypress.com>
|
||||
* 3.Rewrited as sl811.o by Yin Aihua <yinah:couragetech.com.cn>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <mpc8xx.h>
|
||||
#include <usb.h>
|
||||
#include "sl811.h"
|
||||
|
||||
#include "../../../board/kup/common/kup.h"
|
||||
|
||||
#ifdef __PPC__
|
||||
# define EIEIO __asm__ volatile ("eieio")
|
||||
#else
|
||||
# define EIEIO /* nothing */
|
||||
#endif
|
||||
|
||||
#define SL811_ADR (0x50000000)
|
||||
#define SL811_DAT (0x50000001)
|
||||
|
||||
#ifdef SL811_DEBUG
|
||||
static int debug = 9;
|
||||
#endif
|
||||
|
||||
static int root_hub_devnum = 0;
|
||||
static struct usb_port_status rh_status = { 0 };/* root hub port status */
|
||||
|
||||
static int sl811_rh_submit_urb(struct usb_device *usb_dev, unsigned long pipe,
|
||||
void *data, int buf_len, struct devrequest *cmd);
|
||||
|
||||
static void sl811_write (__u8 index, __u8 data)
|
||||
{
|
||||
*(volatile unsigned char *) (SL811_ADR) = index;
|
||||
EIEIO;
|
||||
*(volatile unsigned char *) (SL811_DAT) = data;
|
||||
EIEIO;
|
||||
}
|
||||
|
||||
static __u8 sl811_read (__u8 index)
|
||||
{
|
||||
__u8 data;
|
||||
|
||||
*(volatile unsigned char *) (SL811_ADR) = index;
|
||||
EIEIO;
|
||||
data = *(volatile unsigned char *) (SL811_DAT);
|
||||
EIEIO;
|
||||
return (data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read consecutive bytes of data from the SL811H/SL11H buffer
|
||||
*/
|
||||
static void inline sl811_read_buf(__u8 offset, __u8 *buf, __u8 size)
|
||||
{
|
||||
*(volatile unsigned char *) (SL811_ADR) = offset;
|
||||
EIEIO;
|
||||
while (size--) {
|
||||
*buf++ = *(volatile unsigned char *) (SL811_DAT);
|
||||
EIEIO;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Write consecutive bytes of data to the SL811H/SL11H buffer
|
||||
*/
|
||||
static void inline sl811_write_buf(__u8 offset, __u8 *buf, __u8 size)
|
||||
{
|
||||
*(volatile unsigned char *) (SL811_ADR) = offset;
|
||||
EIEIO;
|
||||
while (size--) {
|
||||
*(volatile unsigned char *) (SL811_DAT) = *buf++;
|
||||
EIEIO;
|
||||
}
|
||||
}
|
||||
|
||||
int usb_init_kup4x (void)
|
||||
{
|
||||
volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR;
|
||||
volatile memctl8xx_t *memctl = &immap->im_memctl;
|
||||
int i;
|
||||
unsigned char tmp;
|
||||
|
||||
memctl = &immap->im_memctl;
|
||||
memctl->memc_or7 = 0xFFFF8726;
|
||||
memctl->memc_br7 = 0x50000401; /* start at 0x50000000 */
|
||||
/* BP 14 low = USB ON */
|
||||
immap->im_cpm.cp_pbdat &= ~(BP_USB_VCC);
|
||||
/* PB 14 nomal port */
|
||||
immap->im_cpm.cp_pbpar &= ~(BP_USB_VCC);
|
||||
/* output */
|
||||
immap->im_cpm.cp_pbdir |= (BP_USB_VCC);
|
||||
|
||||
puts ("USB: ");
|
||||
|
||||
for (i = 0x10; i < 0xff; i++) {
|
||||
sl811_write(i, i);
|
||||
tmp = (sl811_read(i));
|
||||
if (tmp != i) {
|
||||
printf ("SL811 compare error index=0x%02x read=0x%02x\n", i, tmp);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
printf ("SL811 ready\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function resets SL811HS controller and detects the speed of
|
||||
* the connecting device
|
||||
*
|
||||
* Return: 0 = no device attached; 1 = USB device attached
|
||||
*/
|
||||
static int sl811_hc_reset(void)
|
||||
{
|
||||
int status ;
|
||||
|
||||
sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI);
|
||||
sl811_write(SL811_CTRL1, SL811_CTRL1_RESET);
|
||||
|
||||
mdelay(20);
|
||||
|
||||
/* Disable hardware SOF generation, clear all irq status. */
|
||||
sl811_write(SL811_CTRL1, 0);
|
||||
mdelay(2);
|
||||
sl811_write(SL811_INTRSTS, 0xff);
|
||||
status = sl811_read(SL811_INTRSTS);
|
||||
|
||||
if (status & SL811_INTR_NOTPRESENT) {
|
||||
/* Device is not present */
|
||||
PDEBUG(0, "Device not present\n");
|
||||
rh_status.wPortStatus &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE);
|
||||
rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION;
|
||||
sl811_write(SL811_INTR, SL811_INTR_INSRMV);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Send SOF to address 0, endpoint 0. */
|
||||
sl811_write(SL811_LEN_B, 0);
|
||||
sl811_write(SL811_PIDEP_B, PIDEP(USB_PID_SOF, 0));
|
||||
sl811_write(SL811_DEV_B, 0x00);
|
||||
sl811_write(SL811_SOFLOW, SL811_12M_LOW);
|
||||
|
||||
if (status & SL811_INTR_SPEED_FULL) {
|
||||
/* full speed device connect directly to root hub */
|
||||
PDEBUG (0, "Full speed Device attached\n");
|
||||
|
||||
sl811_write(SL811_CTRL1, SL811_CTRL1_RESET);
|
||||
mdelay(20);
|
||||
sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI);
|
||||
sl811_write(SL811_CTRL1, SL811_CTRL1_SOF);
|
||||
|
||||
/* start the SOF or EOP */
|
||||
sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM);
|
||||
rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION;
|
||||
rh_status.wPortStatus &= ~USB_PORT_STAT_LOW_SPEED;
|
||||
mdelay(2);
|
||||
sl811_write(SL811_INTRSTS, 0xff);
|
||||
} else {
|
||||
/* slow speed device connect directly to root-hub */
|
||||
PDEBUG(0, "Low speed Device attached\n");
|
||||
|
||||
sl811_write(SL811_CTRL1, SL811_CTRL1_RESET);
|
||||
mdelay(20);
|
||||
sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_CTL2_DSWAP | SL811_12M_HI);
|
||||
sl811_write(SL811_CTRL1, SL811_CTRL1_SPEED_LOW | SL811_CTRL1_SOF);
|
||||
|
||||
/* start the SOF or EOP */
|
||||
sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM);
|
||||
rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION | USB_PORT_STAT_LOW_SPEED;
|
||||
mdelay(2);
|
||||
sl811_write(SL811_INTRSTS, 0xff);
|
||||
}
|
||||
|
||||
rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION;
|
||||
sl811_write(SL811_INTR, /*SL811_INTR_INSRMV*/SL811_INTR_DONE_A);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
|
||||
{
|
||||
root_hub_devnum = 0;
|
||||
sl811_hc_reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_lowlevel_stop(int index)
|
||||
{
|
||||
sl811_hc_reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int calc_needed_buswidth(int bytes, int need_preamble)
|
||||
{
|
||||
return !need_preamble ? bytes * 8 + 256 : 8 * 8 * bytes + 2048;
|
||||
}
|
||||
|
||||
static int sl811_send_packet(struct usb_device *dev, unsigned long pipe, __u8 *buffer, int len)
|
||||
{
|
||||
__u8 ctrl = SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE;
|
||||
__u16 status = 0;
|
||||
int err = 0, time_start = get_timer(0);
|
||||
int need_preamble = !(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) &&
|
||||
(dev->speed == USB_SPEED_LOW);
|
||||
|
||||
if (len > 239)
|
||||
return -1;
|
||||
|
||||
if (usb_pipeout(pipe))
|
||||
ctrl |= SL811_USB_CTRL_DIR_OUT;
|
||||
if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)))
|
||||
ctrl |= SL811_USB_CTRL_TOGGLE_1;
|
||||
if (need_preamble)
|
||||
ctrl |= SL811_USB_CTRL_PREAMBLE;
|
||||
|
||||
sl811_write(SL811_INTRSTS, 0xff);
|
||||
|
||||
while (err < 3) {
|
||||
sl811_write(SL811_ADDR_A, 0x10);
|
||||
sl811_write(SL811_LEN_A, len);
|
||||
if (usb_pipeout(pipe) && len)
|
||||
sl811_write_buf(0x10, buffer, len);
|
||||
|
||||
if (!(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) &&
|
||||
sl811_read(SL811_SOFCNTDIV)*64 < calc_needed_buswidth(len, need_preamble))
|
||||
ctrl |= SL811_USB_CTRL_SOF;
|
||||
else
|
||||
ctrl &= ~SL811_USB_CTRL_SOF;
|
||||
|
||||
sl811_write(SL811_CTRL_A, ctrl);
|
||||
while (!(sl811_read(SL811_INTRSTS) & SL811_INTR_DONE_A)) {
|
||||
if (5*CONFIG_SYS_HZ < get_timer(time_start)) {
|
||||
printf("USB transmit timed out\n");
|
||||
return -USB_ST_CRC_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
sl811_write(SL811_INTRSTS, 0xff);
|
||||
status = sl811_read(SL811_STS_A);
|
||||
|
||||
if (status & SL811_USB_STS_ACK) {
|
||||
int remainder = sl811_read(SL811_CNT_A);
|
||||
if (remainder) {
|
||||
PDEBUG(0, "usb transfer remainder = %d\n", remainder);
|
||||
len -= remainder;
|
||||
}
|
||||
if (usb_pipein(pipe) && len)
|
||||
sl811_read_buf(0x10, buffer, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
if ((status & SL811_USB_STS_NAK) == SL811_USB_STS_NAK)
|
||||
continue;
|
||||
|
||||
PDEBUG(0, "usb transfer error %#x\n", (int)status);
|
||||
err++;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
|
||||
if (status & SL811_USB_STS_ERROR)
|
||||
err |= USB_ST_BUF_ERR;
|
||||
if (status & SL811_USB_STS_TIMEOUT)
|
||||
err |= USB_ST_CRC_ERR;
|
||||
if (status & SL811_USB_STS_STALL)
|
||||
err |= USB_ST_STALLED;
|
||||
|
||||
return -err;
|
||||
}
|
||||
|
||||
int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
|
||||
int len)
|
||||
{
|
||||
int dir_out = usb_pipeout(pipe);
|
||||
int ep = usb_pipeendpoint(pipe);
|
||||
int max = usb_maxpacket(dev, pipe);
|
||||
int done = 0;
|
||||
|
||||
PDEBUG(7, "dev = %ld pipe = %ld buf = %p size = %d dir_out = %d\n",
|
||||
usb_pipedevice(pipe), usb_pipeendpoint(pipe), buffer, len, dir_out);
|
||||
|
||||
dev->status = 0;
|
||||
|
||||
sl811_write(SL811_DEV_A, usb_pipedevice(pipe));
|
||||
sl811_write(SL811_PIDEP_A, PIDEP(!dir_out ? USB_PID_IN : USB_PID_OUT, ep));
|
||||
while (done < len) {
|
||||
int res = sl811_send_packet(dev, pipe, (__u8*)buffer+done,
|
||||
max > len - done ? len - done : max);
|
||||
if (res < 0) {
|
||||
dev->status = -res;
|
||||
return res;
|
||||
}
|
||||
|
||||
if (!dir_out && res < max) /* short packet */
|
||||
break;
|
||||
|
||||
done += res;
|
||||
usb_dotoggle(dev, ep, dir_out);
|
||||
}
|
||||
|
||||
dev->act_len = done;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
|
||||
int len,struct devrequest *setup)
|
||||
{
|
||||
int done = 0;
|
||||
int devnum = usb_pipedevice(pipe);
|
||||
int ep = usb_pipeendpoint(pipe);
|
||||
|
||||
dev->status = 0;
|
||||
|
||||
if (devnum == root_hub_devnum)
|
||||
return sl811_rh_submit_urb(dev, pipe, buffer, len, setup);
|
||||
|
||||
PDEBUG(7, "dev = %d pipe = %ld buf = %p size = %d rt = %#x req = %#x bus = %i\n",
|
||||
devnum, ep, buffer, len, (int)setup->requesttype,
|
||||
(int)setup->request, sl811_read(SL811_SOFCNTDIV)*64);
|
||||
|
||||
sl811_write(SL811_DEV_A, devnum);
|
||||
sl811_write(SL811_PIDEP_A, PIDEP(USB_PID_SETUP, ep));
|
||||
/* setup phase */
|
||||
usb_settoggle(dev, ep, 1, 0);
|
||||
if (sl811_send_packet(dev, usb_sndctrlpipe(dev, ep),
|
||||
(__u8*)setup, sizeof(*setup)) == sizeof(*setup)) {
|
||||
int dir_in = usb_pipein(pipe);
|
||||
int max = usb_maxpacket(dev, pipe);
|
||||
|
||||
/* data phase */
|
||||
sl811_write(SL811_PIDEP_A,
|
||||
PIDEP(dir_in ? USB_PID_IN : USB_PID_OUT, ep));
|
||||
usb_settoggle(dev, ep, usb_pipeout(pipe), 1);
|
||||
while (done < len) {
|
||||
int res = sl811_send_packet(dev, pipe, (__u8*)buffer+done,
|
||||
max > len - done ? len - done : max);
|
||||
if (res < 0) {
|
||||
PDEBUG(0, "status data failed!\n");
|
||||
dev->status = -res;
|
||||
return 0;
|
||||
}
|
||||
done += res;
|
||||
usb_dotoggle(dev, ep, usb_pipeout(pipe));
|
||||
if (dir_in && res < max) /* short packet */
|
||||
break;
|
||||
}
|
||||
|
||||
/* status phase */
|
||||
sl811_write(SL811_PIDEP_A,
|
||||
PIDEP(!dir_in ? USB_PID_IN : USB_PID_OUT, ep));
|
||||
usb_settoggle(dev, ep, !usb_pipeout(pipe), 1);
|
||||
if (sl811_send_packet(dev,
|
||||
!dir_in ? usb_rcvctrlpipe(dev, ep) :
|
||||
usb_sndctrlpipe(dev, ep),
|
||||
0, 0) < 0) {
|
||||
PDEBUG(0, "status phase failed!\n");
|
||||
dev->status = -1;
|
||||
}
|
||||
} else {
|
||||
PDEBUG(0, "setup phase failed!\n");
|
||||
dev->status = -1;
|
||||
}
|
||||
|
||||
dev->act_len = done;
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
|
||||
int len, int interval)
|
||||
{
|
||||
PDEBUG(0, "dev = %p pipe = %#lx buf = %p size = %d int = %d\n", dev, pipe,
|
||||
buffer, len, interval);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* SL811 Virtual Root Hub
|
||||
*/
|
||||
|
||||
/* Device descriptor */
|
||||
static __u8 sl811_rh_dev_des[] =
|
||||
{
|
||||
0x12, /* __u8 bLength; */
|
||||
0x01, /* __u8 bDescriptorType; Device */
|
||||
0x10, /* __u16 bcdUSB; v1.1 */
|
||||
0x01,
|
||||
0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
|
||||
0x00, /* __u8 bDeviceSubClass; */
|
||||
0x00, /* __u8 bDeviceProtocol; */
|
||||
0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
|
||||
0x00, /* __u16 idVendor; */
|
||||
0x00,
|
||||
0x00, /* __u16 idProduct; */
|
||||
0x00,
|
||||
0x00, /* __u16 bcdDevice; */
|
||||
0x00,
|
||||
0x00, /* __u8 iManufacturer; */
|
||||
0x02, /* __u8 iProduct; */
|
||||
0x01, /* __u8 iSerialNumber; */
|
||||
0x01 /* __u8 bNumConfigurations; */
|
||||
};
|
||||
|
||||
/* Configuration descriptor */
|
||||
static __u8 sl811_rh_config_des[] =
|
||||
{
|
||||
0x09, /* __u8 bLength; */
|
||||
0x02, /* __u8 bDescriptorType; Configuration */
|
||||
0x19, /* __u16 wTotalLength; */
|
||||
0x00,
|
||||
0x01, /* __u8 bNumInterfaces; */
|
||||
0x01, /* __u8 bConfigurationValue; */
|
||||
0x00, /* __u8 iConfiguration; */
|
||||
0x40, /* __u8 bmAttributes;
|
||||
Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup,
|
||||
4..0: resvd */
|
||||
0x00, /* __u8 MaxPower; */
|
||||
|
||||
/* interface */
|
||||
0x09, /* __u8 if_bLength; */
|
||||
0x04, /* __u8 if_bDescriptorType; Interface */
|
||||
0x00, /* __u8 if_bInterfaceNumber; */
|
||||
0x00, /* __u8 if_bAlternateSetting; */
|
||||
0x01, /* __u8 if_bNumEndpoints; */
|
||||
0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
|
||||
0x00, /* __u8 if_bInterfaceSubClass; */
|
||||
0x00, /* __u8 if_bInterfaceProtocol; */
|
||||
0x00, /* __u8 if_iInterface; */
|
||||
|
||||
/* endpoint */
|
||||
0x07, /* __u8 ep_bLength; */
|
||||
0x05, /* __u8 ep_bDescriptorType; Endpoint */
|
||||
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
|
||||
0x03, /* __u8 ep_bmAttributes; Interrupt */
|
||||
0x08, /* __u16 ep_wMaxPacketSize; */
|
||||
0x00,
|
||||
0xff /* __u8 ep_bInterval; 255 ms */
|
||||
};
|
||||
|
||||
/* root hub class descriptor*/
|
||||
static __u8 sl811_rh_hub_des[] =
|
||||
{
|
||||
0x09, /* __u8 bLength; */
|
||||
0x29, /* __u8 bDescriptorType; Hub-descriptor */
|
||||
0x01, /* __u8 bNbrPorts; */
|
||||
0x00, /* __u16 wHubCharacteristics; */
|
||||
0x00,
|
||||
0x50, /* __u8 bPwrOn2pwrGood; 2ms */
|
||||
0x00, /* __u8 bHubContrCurrent; 0 mA */
|
||||
0xfc, /* __u8 DeviceRemovable; *** 7 Ports max *** */
|
||||
0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */
|
||||
};
|
||||
|
||||
/*
|
||||
* helper routine for returning string descriptors in UTF-16LE
|
||||
* input can actually be ISO-8859-1; ASCII is its 7-bit subset
|
||||
*/
|
||||
static int ascii2utf (char *s, u8 *utf, int utfmax)
|
||||
{
|
||||
int retval;
|
||||
|
||||
for (retval = 0; *s && utfmax > 1; utfmax -= 2, retval += 2) {
|
||||
*utf++ = *s++;
|
||||
*utf++ = 0;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* root_hub_string is used by each host controller's root hub code,
|
||||
* so that they're identified consistently throughout the system.
|
||||
*/
|
||||
static int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len)
|
||||
{
|
||||
char buf [30];
|
||||
|
||||
/* assert (len > (2 * (sizeof (buf) + 1)));
|
||||
assert (strlen (type) <= 8);*/
|
||||
|
||||
/* language ids */
|
||||
if (id == 0) {
|
||||
*data++ = 4; *data++ = 3; /* 4 bytes data */
|
||||
*data++ = 0; *data++ = 0; /* some language id */
|
||||
return 4;
|
||||
|
||||
/* serial number */
|
||||
} else if (id == 1) {
|
||||
sprintf (buf, "%#x", serial);
|
||||
|
||||
/* product description */
|
||||
} else if (id == 2) {
|
||||
sprintf (buf, "USB %s Root Hub", type);
|
||||
|
||||
/* id 3 == vendor description */
|
||||
|
||||
/* unsupported IDs --> "stall" */
|
||||
} else
|
||||
return 0;
|
||||
|
||||
ascii2utf (buf, data + 2, len - 2);
|
||||
data [0] = 2 + strlen(buf) * 2;
|
||||
data [1] = 3;
|
||||
return data [0];
|
||||
}
|
||||
|
||||
/* helper macro */
|
||||
#define OK(x) len = (x); break
|
||||
|
||||
/*
|
||||
* This function handles all USB request to the the virtual root hub
|
||||
*/
|
||||
static int sl811_rh_submit_urb(struct usb_device *usb_dev, unsigned long pipe,
|
||||
void *data, int buf_len, struct devrequest *cmd)
|
||||
{
|
||||
__u8 data_buf[16];
|
||||
__u8 *bufp = data_buf;
|
||||
int len = 0;
|
||||
int status = 0;
|
||||
__u16 bmRType_bReq;
|
||||
__u16 wValue = le16_to_cpu (cmd->value);
|
||||
__u16 wLength = le16_to_cpu (cmd->length);
|
||||
#ifdef SL811_DEBUG
|
||||
__u16 wIndex = le16_to_cpu (cmd->index);
|
||||
#endif
|
||||
|
||||
if (usb_pipeint(pipe)) {
|
||||
PDEBUG(0, "interrupt transfer unimplemented!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bmRType_bReq = cmd->requesttype | (cmd->request << 8);
|
||||
|
||||
PDEBUG(5, "submit rh urb, req = %d(%x) val = %#x index = %#x len=%d\n",
|
||||
bmRType_bReq, bmRType_bReq, wValue, wIndex, wLength);
|
||||
|
||||
/* Request Destination:
|
||||
without flags: Device,
|
||||
USB_RECIP_INTERFACE: interface,
|
||||
USB_RECIP_ENDPOINT: endpoint,
|
||||
USB_TYPE_CLASS means HUB here,
|
||||
USB_RECIP_OTHER | USB_TYPE_CLASS almost ever means HUB_PORT here
|
||||
*/
|
||||
switch (bmRType_bReq) {
|
||||
case RH_GET_STATUS:
|
||||
*(__u16 *)bufp = cpu_to_le16(1);
|
||||
OK(2);
|
||||
|
||||
case RH_GET_STATUS | USB_RECIP_INTERFACE:
|
||||
*(__u16 *)bufp = cpu_to_le16(0);
|
||||
OK(2);
|
||||
|
||||
case RH_GET_STATUS | USB_RECIP_ENDPOINT:
|
||||
*(__u16 *)bufp = cpu_to_le16(0);
|
||||
OK(2);
|
||||
|
||||
case RH_GET_STATUS | USB_TYPE_CLASS:
|
||||
*(__u32 *)bufp = cpu_to_le32(0);
|
||||
OK(4);
|
||||
|
||||
case RH_GET_STATUS | USB_RECIP_OTHER | USB_TYPE_CLASS:
|
||||
*(__u32 *)bufp = cpu_to_le32(rh_status.wPortChange<<16 | rh_status.wPortStatus);
|
||||
OK(4);
|
||||
|
||||
case RH_CLEAR_FEATURE | USB_RECIP_ENDPOINT:
|
||||
switch (wValue) {
|
||||
case 1:
|
||||
OK(0);
|
||||
}
|
||||
break;
|
||||
|
||||
case RH_CLEAR_FEATURE | USB_TYPE_CLASS:
|
||||
switch (wValue) {
|
||||
case C_HUB_LOCAL_POWER:
|
||||
OK(0);
|
||||
|
||||
case C_HUB_OVER_CURRENT:
|
||||
OK(0);
|
||||
}
|
||||
break;
|
||||
|
||||
case RH_CLEAR_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS:
|
||||
switch (wValue) {
|
||||
case USB_PORT_FEAT_ENABLE:
|
||||
rh_status.wPortStatus &= ~USB_PORT_STAT_ENABLE;
|
||||
OK(0);
|
||||
|
||||
case USB_PORT_FEAT_SUSPEND:
|
||||
rh_status.wPortStatus &= ~USB_PORT_STAT_SUSPEND;
|
||||
OK(0);
|
||||
|
||||
case USB_PORT_FEAT_POWER:
|
||||
rh_status.wPortStatus &= ~USB_PORT_STAT_POWER;
|
||||
OK(0);
|
||||
|
||||
case USB_PORT_FEAT_C_CONNECTION:
|
||||
rh_status.wPortChange &= ~USB_PORT_STAT_C_CONNECTION;
|
||||
OK(0);
|
||||
|
||||
case USB_PORT_FEAT_C_ENABLE:
|
||||
rh_status.wPortChange &= ~USB_PORT_STAT_C_ENABLE;
|
||||
OK(0);
|
||||
|
||||
case USB_PORT_FEAT_C_SUSPEND:
|
||||
rh_status.wPortChange &= ~USB_PORT_STAT_C_SUSPEND;
|
||||
OK(0);
|
||||
|
||||
case USB_PORT_FEAT_C_OVER_CURRENT:
|
||||
rh_status.wPortChange &= ~USB_PORT_STAT_C_OVERCURRENT;
|
||||
OK(0);
|
||||
|
||||
case USB_PORT_FEAT_C_RESET:
|
||||
rh_status.wPortChange &= ~USB_PORT_STAT_C_RESET;
|
||||
OK(0);
|
||||
}
|
||||
break;
|
||||
|
||||
case RH_SET_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS:
|
||||
switch (wValue) {
|
||||
case USB_PORT_FEAT_SUSPEND:
|
||||
rh_status.wPortStatus |= USB_PORT_STAT_SUSPEND;
|
||||
OK(0);
|
||||
|
||||
case USB_PORT_FEAT_RESET:
|
||||
rh_status.wPortStatus |= USB_PORT_STAT_RESET;
|
||||
rh_status.wPortChange = 0;
|
||||
rh_status.wPortChange |= USB_PORT_STAT_C_RESET;
|
||||
rh_status.wPortStatus &= ~USB_PORT_STAT_RESET;
|
||||
rh_status.wPortStatus |= USB_PORT_STAT_ENABLE;
|
||||
OK(0);
|
||||
|
||||
case USB_PORT_FEAT_POWER:
|
||||
rh_status.wPortStatus |= USB_PORT_STAT_POWER;
|
||||
OK(0);
|
||||
|
||||
case USB_PORT_FEAT_ENABLE:
|
||||
rh_status.wPortStatus |= USB_PORT_STAT_ENABLE;
|
||||
OK(0);
|
||||
}
|
||||
break;
|
||||
|
||||
case RH_SET_ADDRESS:
|
||||
root_hub_devnum = wValue;
|
||||
OK(0);
|
||||
|
||||
case RH_GET_DESCRIPTOR:
|
||||
switch ((wValue & 0xff00) >> 8) {
|
||||
case USB_DT_DEVICE:
|
||||
len = sizeof(sl811_rh_dev_des);
|
||||
bufp = sl811_rh_dev_des;
|
||||
OK(len);
|
||||
|
||||
case USB_DT_CONFIG:
|
||||
len = sizeof(sl811_rh_config_des);
|
||||
bufp = sl811_rh_config_des;
|
||||
OK(len);
|
||||
|
||||
case USB_DT_STRING:
|
||||
len = usb_root_hub_string(wValue & 0xff, (int)(long)0, "SL811HS", data, wLength);
|
||||
if (len > 0) {
|
||||
bufp = data;
|
||||
OK(len);
|
||||
}
|
||||
|
||||
default:
|
||||
status = -32;
|
||||
}
|
||||
break;
|
||||
|
||||
case RH_GET_DESCRIPTOR | USB_TYPE_CLASS:
|
||||
len = sizeof(sl811_rh_hub_des);
|
||||
bufp = sl811_rh_hub_des;
|
||||
OK(len);
|
||||
|
||||
case RH_GET_CONFIGURATION:
|
||||
bufp[0] = 0x01;
|
||||
OK(1);
|
||||
|
||||
case RH_SET_CONFIGURATION:
|
||||
OK(0);
|
||||
|
||||
default:
|
||||
PDEBUG(1, "unsupported root hub command\n");
|
||||
status = -32;
|
||||
}
|
||||
|
||||
len = min(len, buf_len);
|
||||
if (data != bufp)
|
||||
memcpy(data, bufp, len);
|
||||
|
||||
PDEBUG(5, "len = %d, status = %d\n", len, status);
|
||||
|
||||
usb_dev->status = status;
|
||||
usb_dev->act_len = len;
|
||||
|
||||
return status == 0 ? len : status;
|
||||
}
|
||||
104
u-boot/drivers/usb/host/sl811.h
Normal file
104
u-boot/drivers/usb/host/sl811.h
Normal file
@@ -0,0 +1,104 @@
|
||||
#ifndef __UBOOT_SL811_H
|
||||
#define __UBOOT_SL811_H
|
||||
|
||||
#undef SL811_DEBUG
|
||||
|
||||
#ifdef SL811_DEBUG
|
||||
#define PDEBUG(level, fmt, args...) \
|
||||
if (debug >= (level)) printf("[%s:%d] " fmt, \
|
||||
__PRETTY_FUNCTION__, __LINE__ , ## args)
|
||||
#else
|
||||
#define PDEBUG(level, fmt, args...) do {} while(0)
|
||||
#endif
|
||||
|
||||
/* Sl811 host control register */
|
||||
#define SL811_CTRL_A 0x00
|
||||
#define SL811_ADDR_A 0x01
|
||||
#define SL811_LEN_A 0x02
|
||||
#define SL811_STS_A 0x03 /* read */
|
||||
#define SL811_PIDEP_A 0x03 /* write */
|
||||
#define SL811_CNT_A 0x04 /* read */
|
||||
#define SL811_DEV_A 0x04 /* write */
|
||||
#define SL811_CTRL1 0x05
|
||||
#define SL811_INTR 0x06
|
||||
#define SL811_CTRL_B 0x08
|
||||
#define SL811_ADDR_B 0x09
|
||||
#define SL811_LEN_B 0x0A
|
||||
#define SL811_STS_B 0x0B /* read */
|
||||
#define SL811_PIDEP_B 0x0B /* write */
|
||||
#define SL811_CNT_B 0x0C /* read */
|
||||
#define SL811_DEV_B 0x0C /* write */
|
||||
#define SL811_INTRSTS 0x0D /* write clears bitwise */
|
||||
#define SL811_HWREV 0x0E /* read */
|
||||
#define SL811_SOFLOW 0x0E /* write */
|
||||
#define SL811_SOFCNTDIV 0x0F /* read */
|
||||
#define SL811_CTRL2 0x0F /* write */
|
||||
|
||||
/* USB control register bits (addr 0x00 and addr 0x08) */
|
||||
#define SL811_USB_CTRL_ARM 0x01
|
||||
#define SL811_USB_CTRL_ENABLE 0x02
|
||||
#define SL811_USB_CTRL_DIR_OUT 0x04
|
||||
#define SL811_USB_CTRL_ISO 0x10
|
||||
#define SL811_USB_CTRL_SOF 0x20
|
||||
#define SL811_USB_CTRL_TOGGLE_1 0x40
|
||||
#define SL811_USB_CTRL_PREAMBLE 0x80
|
||||
|
||||
/* USB status register bits (addr 0x03 and addr 0x0B) */
|
||||
#define SL811_USB_STS_ACK 0x01
|
||||
#define SL811_USB_STS_ERROR 0x02
|
||||
#define SL811_USB_STS_TIMEOUT 0x04
|
||||
#define SL811_USB_STS_TOGGLE_1 0x08
|
||||
#define SL811_USB_STS_SETUP 0x10
|
||||
#define SL811_USB_STS_OVERFLOW 0x20
|
||||
#define SL811_USB_STS_NAK 0x40
|
||||
#define SL811_USB_STS_STALL 0x80
|
||||
|
||||
/* Control register 1 bits (addr 0x05) */
|
||||
#define SL811_CTRL1_SOF 0x01
|
||||
#define SL811_CTRL1_RESET 0x08
|
||||
#define SL811_CTRL1_JKSTATE 0x10
|
||||
#define SL811_CTRL1_SPEED_LOW 0x20
|
||||
#define SL811_CTRL1_SUSPEND 0x40
|
||||
|
||||
/* Interrut enable (addr 0x06) and interrupt status register bits (addr 0x0D) */
|
||||
#define SL811_INTR_DONE_A 0x01
|
||||
#define SL811_INTR_DONE_B 0x02
|
||||
#define SL811_INTR_SOF 0x10
|
||||
#define SL811_INTR_INSRMV 0x20
|
||||
#define SL811_INTR_DETECT 0x40
|
||||
#define SL811_INTR_NOTPRESENT 0x40
|
||||
#define SL811_INTR_SPEED_FULL 0x80 /* only in status reg */
|
||||
|
||||
/* HW rev and SOF lo register bits (addr 0x0E) */
|
||||
#define SL811_HWR_HWREV 0xF0
|
||||
|
||||
/* SOF counter and control reg 2 (addr 0x0F) */
|
||||
#define SL811_CTL2_SOFHI 0x3F
|
||||
#define SL811_CTL2_DSWAP 0x40
|
||||
#define SL811_CTL2_HOST 0x80
|
||||
|
||||
/* Set up for 1-ms SOF time. */
|
||||
#define SL811_12M_LOW 0xE0
|
||||
#define SL811_12M_HI 0x2E
|
||||
|
||||
#define SL811_DATA_START 0x10
|
||||
#define SL811_DATA_LIMIT 240
|
||||
|
||||
/* Requests: bRequest << 8 | bmRequestType */
|
||||
#define RH_GET_STATUS 0x0080
|
||||
#define RH_CLEAR_FEATURE 0x0100
|
||||
#define RH_SET_FEATURE 0x0300
|
||||
#define RH_SET_ADDRESS 0x0500
|
||||
#define RH_GET_DESCRIPTOR 0x0680
|
||||
#define RH_SET_DESCRIPTOR 0x0700
|
||||
#define RH_GET_CONFIGURATION 0x0880
|
||||
#define RH_SET_CONFIGURATION 0x0900
|
||||
#define RH_GET_STATE 0x0280
|
||||
#define RH_GET_INTERFACE 0x0A80
|
||||
#define RH_SET_INTERFACE 0x0B00
|
||||
#define RH_SYNC_FRAME 0x0C80
|
||||
|
||||
|
||||
#define PIDEP(pid, ep) (((pid) & 0x0f) << 4 | (ep))
|
||||
|
||||
#endif /* __UBOOT_SL811_H */
|
||||
136
u-boot/drivers/usb/host/usb-sandbox.c
Normal file
136
u-boot/drivers/usb/host/usb-sandbox.c
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* (C) Copyright 2015 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <usb.h>
|
||||
#include <dm/root.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
static void usbmon_trace(struct udevice *bus, ulong pipe,
|
||||
struct devrequest *setup, struct udevice *emul)
|
||||
{
|
||||
static const char types[] = "ZICB";
|
||||
int type;
|
||||
|
||||
type = (pipe & USB_PIPE_TYPE_MASK) >> USB_PIPE_TYPE_SHIFT;
|
||||
debug("0 0 S %c%c:%d:%03ld:%ld", types[type],
|
||||
pipe & USB_DIR_IN ? 'i' : 'o',
|
||||
bus->seq,
|
||||
(pipe & USB_PIPE_DEV_MASK) >> USB_PIPE_DEV_SHIFT,
|
||||
(pipe & USB_PIPE_EP_MASK) >> USB_PIPE_EP_SHIFT);
|
||||
if (setup) {
|
||||
debug(" s %02x %02x %04x %04x %04x", setup->requesttype,
|
||||
setup->request, setup->value, setup->index,
|
||||
setup->length);
|
||||
}
|
||||
debug(" %s", emul ? emul->name : "(no emul found)");
|
||||
|
||||
debug("\n");
|
||||
}
|
||||
|
||||
static int sandbox_submit_control(struct udevice *bus,
|
||||
struct usb_device *udev,
|
||||
unsigned long pipe,
|
||||
void *buffer, int length,
|
||||
struct devrequest *setup)
|
||||
{
|
||||
struct udevice *emul;
|
||||
int ret;
|
||||
|
||||
/* Just use child of dev as emulator? */
|
||||
debug("%s: bus=%s\n", __func__, bus->name);
|
||||
ret = usb_emul_find(bus, pipe, &emul);
|
||||
usbmon_trace(bus, pipe, setup, emul);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = usb_emul_control(emul, udev, pipe, buffer, length, setup);
|
||||
if (ret < 0) {
|
||||
debug("ret=%d\n", ret);
|
||||
udev->status = ret;
|
||||
udev->act_len = 0;
|
||||
} else {
|
||||
udev->status = 0;
|
||||
udev->act_len = ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sandbox_submit_bulk(struct udevice *bus, struct usb_device *udev,
|
||||
unsigned long pipe, void *buffer, int length)
|
||||
{
|
||||
struct udevice *emul;
|
||||
int ret;
|
||||
|
||||
/* Just use child of dev as emulator? */
|
||||
debug("%s: bus=%s\n", __func__, bus->name);
|
||||
ret = usb_emul_find(bus, pipe, &emul);
|
||||
usbmon_trace(bus, pipe, NULL, emul);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = usb_emul_bulk(emul, udev, pipe, buffer, length);
|
||||
if (ret < 0) {
|
||||
debug("ret=%d\n", ret);
|
||||
udev->status = ret;
|
||||
udev->act_len = 0;
|
||||
} else {
|
||||
udev->status = 0;
|
||||
udev->act_len = ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sandbox_submit_int(struct udevice *bus, struct usb_device *udev,
|
||||
unsigned long pipe, void *buffer, int length,
|
||||
int interval)
|
||||
{
|
||||
struct udevice *emul;
|
||||
int ret;
|
||||
|
||||
/* Just use child of dev as emulator? */
|
||||
debug("%s: bus=%s\n", __func__, bus->name);
|
||||
ret = usb_emul_find(bus, pipe, &emul);
|
||||
usbmon_trace(bus, pipe, NULL, emul);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = usb_emul_int(emul, udev, pipe, buffer, length, interval);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sandbox_alloc_device(struct udevice *dev, struct usb_device *udev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sandbox_usb_probe(struct udevice *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_usb_ops sandbox_usb_ops = {
|
||||
.control = sandbox_submit_control,
|
||||
.bulk = sandbox_submit_bulk,
|
||||
.interrupt = sandbox_submit_int,
|
||||
.alloc_device = sandbox_alloc_device,
|
||||
};
|
||||
|
||||
static const struct udevice_id sandbox_usb_ids[] = {
|
||||
{ .compatible = "sandbox,usb" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(usb_sandbox) = {
|
||||
.name = "usb_sandbox",
|
||||
.id = UCLASS_USB,
|
||||
.of_match = sandbox_usb_ids,
|
||||
.probe = sandbox_usb_probe,
|
||||
.ops = &sandbox_usb_ops,
|
||||
};
|
||||
788
u-boot/drivers/usb/host/usb-uclass.c
Normal file
788
u-boot/drivers/usb/host/usb-uclass.c
Normal file
@@ -0,0 +1,788 @@
|
||||
/*
|
||||
* (C) Copyright 2015 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*
|
||||
* usb_match_device() modified from Linux kernel v4.0.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <memalign.h>
|
||||
#include <usb.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/root.h>
|
||||
#include <dm/uclass-internal.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
extern bool usb_started; /* flag for the started/stopped USB status */
|
||||
static bool asynch_allowed;
|
||||
|
||||
struct usb_uclass_priv {
|
||||
int companion_device_count;
|
||||
};
|
||||
|
||||
int usb_disable_asynch(int disable)
|
||||
{
|
||||
int old_value = asynch_allowed;
|
||||
|
||||
asynch_allowed = !disable;
|
||||
return old_value;
|
||||
}
|
||||
|
||||
int submit_int_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
|
||||
int length, int interval)
|
||||
{
|
||||
struct udevice *bus = udev->controller_dev;
|
||||
struct dm_usb_ops *ops = usb_get_ops(bus);
|
||||
|
||||
if (!ops->interrupt)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->interrupt(bus, udev, pipe, buffer, length, interval);
|
||||
}
|
||||
|
||||
int submit_control_msg(struct usb_device *udev, unsigned long pipe,
|
||||
void *buffer, int length, struct devrequest *setup)
|
||||
{
|
||||
struct udevice *bus = udev->controller_dev;
|
||||
struct dm_usb_ops *ops = usb_get_ops(bus);
|
||||
struct usb_uclass_priv *uc_priv = bus->uclass->priv;
|
||||
int err;
|
||||
|
||||
if (!ops->control)
|
||||
return -ENOSYS;
|
||||
|
||||
err = ops->control(bus, udev, pipe, buffer, length, setup);
|
||||
if (setup->request == USB_REQ_SET_FEATURE &&
|
||||
setup->requesttype == USB_RT_PORT &&
|
||||
setup->value == cpu_to_le16(USB_PORT_FEAT_RESET) &&
|
||||
err == -ENXIO) {
|
||||
/* Device handed over to companion after port reset */
|
||||
uc_priv->companion_device_count++;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int submit_bulk_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
|
||||
int length)
|
||||
{
|
||||
struct udevice *bus = udev->controller_dev;
|
||||
struct dm_usb_ops *ops = usb_get_ops(bus);
|
||||
|
||||
if (!ops->bulk)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->bulk(bus, udev, pipe, buffer, length);
|
||||
}
|
||||
|
||||
struct int_queue *create_int_queue(struct usb_device *udev,
|
||||
unsigned long pipe, int queuesize, int elementsize,
|
||||
void *buffer, int interval)
|
||||
{
|
||||
struct udevice *bus = udev->controller_dev;
|
||||
struct dm_usb_ops *ops = usb_get_ops(bus);
|
||||
|
||||
if (!ops->create_int_queue)
|
||||
return NULL;
|
||||
|
||||
return ops->create_int_queue(bus, udev, pipe, queuesize, elementsize,
|
||||
buffer, interval);
|
||||
}
|
||||
|
||||
void *poll_int_queue(struct usb_device *udev, struct int_queue *queue)
|
||||
{
|
||||
struct udevice *bus = udev->controller_dev;
|
||||
struct dm_usb_ops *ops = usb_get_ops(bus);
|
||||
|
||||
if (!ops->poll_int_queue)
|
||||
return NULL;
|
||||
|
||||
return ops->poll_int_queue(bus, udev, queue);
|
||||
}
|
||||
|
||||
int destroy_int_queue(struct usb_device *udev, struct int_queue *queue)
|
||||
{
|
||||
struct udevice *bus = udev->controller_dev;
|
||||
struct dm_usb_ops *ops = usb_get_ops(bus);
|
||||
|
||||
if (!ops->destroy_int_queue)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->destroy_int_queue(bus, udev, queue);
|
||||
}
|
||||
|
||||
int usb_alloc_device(struct usb_device *udev)
|
||||
{
|
||||
struct udevice *bus = udev->controller_dev;
|
||||
struct dm_usb_ops *ops = usb_get_ops(bus);
|
||||
|
||||
/* This is only requird by some controllers - current XHCI */
|
||||
if (!ops->alloc_device)
|
||||
return 0;
|
||||
|
||||
return ops->alloc_device(bus, udev);
|
||||
}
|
||||
|
||||
int usb_reset_root_port(struct usb_device *udev)
|
||||
{
|
||||
struct udevice *bus = udev->controller_dev;
|
||||
struct dm_usb_ops *ops = usb_get_ops(bus);
|
||||
|
||||
if (!ops->reset_root_port)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->reset_root_port(bus, udev);
|
||||
}
|
||||
|
||||
int usb_stop(void)
|
||||
{
|
||||
struct udevice *bus;
|
||||
struct uclass *uc;
|
||||
struct usb_uclass_priv *uc_priv;
|
||||
int err = 0, ret;
|
||||
|
||||
/* De-activate any devices that have been activated */
|
||||
ret = uclass_get(UCLASS_USB, &uc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
uc_priv = uc->priv;
|
||||
|
||||
uclass_foreach_dev(bus, uc) {
|
||||
ret = device_remove(bus);
|
||||
if (ret && !err)
|
||||
err = ret;
|
||||
}
|
||||
#ifdef CONFIG_BLK
|
||||
ret = blk_unbind_all(IF_TYPE_USB);
|
||||
if (ret && !err)
|
||||
err = ret;
|
||||
#endif
|
||||
#ifdef CONFIG_SANDBOX
|
||||
struct udevice *dev;
|
||||
|
||||
/* Reset all enulation devices */
|
||||
ret = uclass_get(UCLASS_USB_EMUL, &uc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
uclass_foreach_dev(dev, uc)
|
||||
usb_emul_reset(dev);
|
||||
#endif
|
||||
#ifdef CONFIG_USB_STORAGE
|
||||
usb_stor_reset();
|
||||
#endif
|
||||
usb_hub_reset();
|
||||
uc_priv->companion_device_count = 0;
|
||||
usb_started = 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void usb_scan_bus(struct udevice *bus, bool recurse)
|
||||
{
|
||||
struct usb_bus_priv *priv;
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
priv = dev_get_uclass_priv(bus);
|
||||
|
||||
assert(recurse); /* TODO: Support non-recusive */
|
||||
|
||||
printf("scanning bus %d for devices... ", bus->seq);
|
||||
debug("\n");
|
||||
ret = usb_scan_device(bus, 0, USB_SPEED_FULL, &dev);
|
||||
if (ret)
|
||||
printf("failed, error %d\n", ret);
|
||||
else if (priv->next_addr == 0)
|
||||
printf("No USB Device found\n");
|
||||
else
|
||||
printf("%d USB Device(s) found\n", priv->next_addr);
|
||||
}
|
||||
|
||||
static void remove_inactive_children(struct uclass *uc, struct udevice *bus)
|
||||
{
|
||||
uclass_foreach_dev(bus, uc) {
|
||||
struct udevice *dev, *next;
|
||||
|
||||
if (!device_active(bus))
|
||||
continue;
|
||||
device_foreach_child_safe(dev, next, bus) {
|
||||
if (!device_active(dev))
|
||||
device_unbind(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int usb_init(void)
|
||||
{
|
||||
int controllers_initialized = 0;
|
||||
struct usb_uclass_priv *uc_priv;
|
||||
struct usb_bus_priv *priv;
|
||||
struct udevice *bus;
|
||||
struct uclass *uc;
|
||||
int count = 0;
|
||||
int ret;
|
||||
|
||||
asynch_allowed = 1;
|
||||
usb_hub_reset();
|
||||
|
||||
ret = uclass_get(UCLASS_USB, &uc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
uc_priv = uc->priv;
|
||||
|
||||
uclass_foreach_dev(bus, uc) {
|
||||
/* init low_level USB */
|
||||
printf("USB%d: ", count);
|
||||
count++;
|
||||
ret = device_probe(bus);
|
||||
if (ret == -ENODEV) { /* No such device. */
|
||||
puts("Port not available.\n");
|
||||
controllers_initialized++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ret) { /* Other error. */
|
||||
printf("probe failed, error %d\n", ret);
|
||||
continue;
|
||||
}
|
||||
controllers_initialized++;
|
||||
usb_started = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* lowlevel init done, now scan the bus for devices i.e. search HUBs
|
||||
* and configure them, first scan primary controllers.
|
||||
*/
|
||||
uclass_foreach_dev(bus, uc) {
|
||||
if (!device_active(bus))
|
||||
continue;
|
||||
|
||||
priv = dev_get_uclass_priv(bus);
|
||||
if (!priv->companion)
|
||||
usb_scan_bus(bus, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now that the primary controllers have been scanned and have handed
|
||||
* over any devices they do not understand to their companions, scan
|
||||
* the companions if necessary.
|
||||
*/
|
||||
if (uc_priv->companion_device_count) {
|
||||
uclass_foreach_dev(bus, uc) {
|
||||
if (!device_active(bus))
|
||||
continue;
|
||||
|
||||
priv = dev_get_uclass_priv(bus);
|
||||
if (priv->companion)
|
||||
usb_scan_bus(bus, true);
|
||||
}
|
||||
}
|
||||
|
||||
debug("scan end\n");
|
||||
|
||||
/* Remove any devices that were not found on this scan */
|
||||
remove_inactive_children(uc, bus);
|
||||
|
||||
ret = uclass_get(UCLASS_USB_HUB, &uc);
|
||||
if (ret)
|
||||
return ret;
|
||||
remove_inactive_children(uc, bus);
|
||||
|
||||
/* if we were not able to find at least one working bus, bail out */
|
||||
if (!count)
|
||||
printf("No controllers found\n");
|
||||
else if (controllers_initialized == 0)
|
||||
printf("USB error: all controllers failed lowlevel init\n");
|
||||
|
||||
return usb_started ? 0 : -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO(sjg@chromium.org): Remove this legacy function. At present it is needed
|
||||
* to support boards which use driver model for USB but not Ethernet, and want
|
||||
* to use USB Ethernet.
|
||||
*
|
||||
* The #if clause is here to ensure that remains the only case.
|
||||
*/
|
||||
#if !defined(CONFIG_DM_ETH) && defined(CONFIG_USB_HOST_ETHER)
|
||||
static struct usb_device *find_child_devnum(struct udevice *parent, int devnum)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
struct udevice *dev;
|
||||
|
||||
if (!device_active(parent))
|
||||
return NULL;
|
||||
udev = dev_get_parent_priv(parent);
|
||||
if (udev->devnum == devnum)
|
||||
return udev;
|
||||
|
||||
for (device_find_first_child(parent, &dev);
|
||||
dev;
|
||||
device_find_next_child(&dev)) {
|
||||
udev = find_child_devnum(dev, devnum);
|
||||
if (udev)
|
||||
return udev;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct usb_device *usb_get_dev_index(struct udevice *bus, int index)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int devnum = index + 1; /* Addresses are allocated from 1 on USB */
|
||||
|
||||
device_find_first_child(bus, &dev);
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
return find_child_devnum(dev, devnum);
|
||||
}
|
||||
#endif
|
||||
|
||||
int usb_post_bind(struct udevice *dev)
|
||||
{
|
||||
/* Scan the bus for devices */
|
||||
return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false);
|
||||
}
|
||||
|
||||
int usb_setup_ehci_gadget(struct ehci_ctrl **ctlrp)
|
||||
{
|
||||
struct usb_platdata *plat;
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
/* Find the old device and remove it */
|
||||
ret = uclass_find_device_by_seq(UCLASS_USB, 0, true, &dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = device_remove(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
plat = dev_get_platdata(dev);
|
||||
plat->init_type = USB_INIT_DEVICE;
|
||||
ret = device_probe(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
*ctlrp = dev_get_priv(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* returns 0 if no match, 1 if match */
|
||||
int usb_match_device(const struct usb_device_descriptor *desc,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
|
||||
id->idVendor != le16_to_cpu(desc->idVendor))
|
||||
return 0;
|
||||
|
||||
if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
|
||||
id->idProduct != le16_to_cpu(desc->idProduct))
|
||||
return 0;
|
||||
|
||||
/* No need to test id->bcdDevice_lo != 0, since 0 is never
|
||||
greater than any unsigned number. */
|
||||
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&
|
||||
(id->bcdDevice_lo > le16_to_cpu(desc->bcdDevice)))
|
||||
return 0;
|
||||
|
||||
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&
|
||||
(id->bcdDevice_hi < le16_to_cpu(desc->bcdDevice)))
|
||||
return 0;
|
||||
|
||||
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
|
||||
(id->bDeviceClass != desc->bDeviceClass))
|
||||
return 0;
|
||||
|
||||
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&
|
||||
(id->bDeviceSubClass != desc->bDeviceSubClass))
|
||||
return 0;
|
||||
|
||||
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&
|
||||
(id->bDeviceProtocol != desc->bDeviceProtocol))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns 0 if no match, 1 if match */
|
||||
int usb_match_one_id_intf(const struct usb_device_descriptor *desc,
|
||||
const struct usb_interface_descriptor *int_desc,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
/* The interface class, subclass, protocol and number should never be
|
||||
* checked for a match if the device class is Vendor Specific,
|
||||
* unless the match record specifies the Vendor ID. */
|
||||
if (desc->bDeviceClass == USB_CLASS_VENDOR_SPEC &&
|
||||
!(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
|
||||
(id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS |
|
||||
USB_DEVICE_ID_MATCH_INT_SUBCLASS |
|
||||
USB_DEVICE_ID_MATCH_INT_PROTOCOL |
|
||||
USB_DEVICE_ID_MATCH_INT_NUMBER)))
|
||||
return 0;
|
||||
|
||||
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
|
||||
(id->bInterfaceClass != int_desc->bInterfaceClass))
|
||||
return 0;
|
||||
|
||||
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) &&
|
||||
(id->bInterfaceSubClass != int_desc->bInterfaceSubClass))
|
||||
return 0;
|
||||
|
||||
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) &&
|
||||
(id->bInterfaceProtocol != int_desc->bInterfaceProtocol))
|
||||
return 0;
|
||||
|
||||
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER) &&
|
||||
(id->bInterfaceNumber != int_desc->bInterfaceNumber))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns 0 if no match, 1 if match */
|
||||
int usb_match_one_id(struct usb_device_descriptor *desc,
|
||||
struct usb_interface_descriptor *int_desc,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
if (!usb_match_device(desc, id))
|
||||
return 0;
|
||||
|
||||
return usb_match_one_id_intf(desc, int_desc, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_find_and_bind_driver() - Find and bind the right USB driver
|
||||
*
|
||||
* This only looks at certain fields in the descriptor.
|
||||
*/
|
||||
static int usb_find_and_bind_driver(struct udevice *parent,
|
||||
struct usb_device_descriptor *desc,
|
||||
struct usb_interface_descriptor *iface,
|
||||
int bus_seq, int devnum,
|
||||
struct udevice **devp)
|
||||
{
|
||||
struct usb_driver_entry *start, *entry;
|
||||
int n_ents;
|
||||
int ret;
|
||||
char name[30], *str;
|
||||
|
||||
*devp = NULL;
|
||||
debug("%s: Searching for driver\n", __func__);
|
||||
start = ll_entry_start(struct usb_driver_entry, usb_driver_entry);
|
||||
n_ents = ll_entry_count(struct usb_driver_entry, usb_driver_entry);
|
||||
for (entry = start; entry != start + n_ents; entry++) {
|
||||
const struct usb_device_id *id;
|
||||
struct udevice *dev;
|
||||
const struct driver *drv;
|
||||
struct usb_dev_platdata *plat;
|
||||
|
||||
for (id = entry->match; id->match_flags; id++) {
|
||||
if (!usb_match_one_id(desc, iface, id))
|
||||
continue;
|
||||
|
||||
drv = entry->driver;
|
||||
/*
|
||||
* We could pass the descriptor to the driver as
|
||||
* platdata (instead of NULL) and allow its bind()
|
||||
* method to return -ENOENT if it doesn't support this
|
||||
* device. That way we could continue the search to
|
||||
* find another driver. For now this doesn't seem
|
||||
* necesssary, so just bind the first match.
|
||||
*/
|
||||
ret = device_bind(parent, drv, drv->name, NULL, -1,
|
||||
&dev);
|
||||
if (ret)
|
||||
goto error;
|
||||
debug("%s: Match found: %s\n", __func__, drv->name);
|
||||
dev->driver_data = id->driver_info;
|
||||
plat = dev_get_parent_platdata(dev);
|
||||
plat->id = *id;
|
||||
*devp = dev;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Bind a generic driver so that the device can be used */
|
||||
snprintf(name, sizeof(name), "generic_bus_%x_dev_%x", bus_seq, devnum);
|
||||
str = strdup(name);
|
||||
if (!str)
|
||||
return -ENOMEM;
|
||||
ret = device_bind_driver(parent, "usb_dev_generic_drv", str, devp);
|
||||
|
||||
error:
|
||||
debug("%s: No match found: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_find_child() - Find an existing device which matches our needs
|
||||
*
|
||||
*
|
||||
*/
|
||||
static int usb_find_child(struct udevice *parent,
|
||||
struct usb_device_descriptor *desc,
|
||||
struct usb_interface_descriptor *iface,
|
||||
struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev;
|
||||
|
||||
*devp = NULL;
|
||||
for (device_find_first_child(parent, &dev);
|
||||
dev;
|
||||
device_find_next_child(&dev)) {
|
||||
struct usb_dev_platdata *plat = dev_get_parent_platdata(dev);
|
||||
|
||||
/* If this device is already in use, skip it */
|
||||
if (device_active(dev))
|
||||
continue;
|
||||
debug(" %s: name='%s', plat=%d, desc=%d\n", __func__,
|
||||
dev->name, plat->id.bDeviceClass, desc->bDeviceClass);
|
||||
if (usb_match_one_id(desc, iface, &plat->id)) {
|
||||
*devp = dev;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int usb_scan_device(struct udevice *parent, int port,
|
||||
enum usb_device_speed speed, struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev;
|
||||
bool created = false;
|
||||
struct usb_dev_platdata *plat;
|
||||
struct usb_bus_priv *priv;
|
||||
struct usb_device *parent_udev;
|
||||
int ret;
|
||||
ALLOC_CACHE_ALIGN_BUFFER(struct usb_device, udev, 1);
|
||||
struct usb_interface_descriptor *iface = &udev->config.if_desc[0].desc;
|
||||
|
||||
*devp = NULL;
|
||||
memset(udev, '\0', sizeof(*udev));
|
||||
udev->controller_dev = usb_get_bus(parent);
|
||||
priv = dev_get_uclass_priv(udev->controller_dev);
|
||||
|
||||
/*
|
||||
* Somewhat nasty, this. We create a local device and use the normal
|
||||
* USB stack to read its descriptor. Then we know what type of device
|
||||
* to create for real.
|
||||
*
|
||||
* udev->dev is set to the parent, since we don't have a real device
|
||||
* yet. The USB stack should not access udev.dev anyway, except perhaps
|
||||
* to find the controller, and the controller will either be @parent,
|
||||
* or some parent of @parent.
|
||||
*
|
||||
* Another option might be to create the device as a generic USB
|
||||
* device, then morph it into the correct one when we know what it
|
||||
* should be. This means that a generic USB device would morph into
|
||||
* a network controller, or a USB flash stick, for example. However,
|
||||
* we don't support such morphing and it isn't clear that it would
|
||||
* be easy to do.
|
||||
*
|
||||
* Yet another option is to split out the USB stack parts of udev
|
||||
* into something like a 'struct urb' (as Linux does) which can exist
|
||||
* independently of any device. This feels cleaner, but calls for quite
|
||||
* a big change to the USB stack.
|
||||
*
|
||||
* For now, the approach is to set up an empty udev, read its
|
||||
* descriptor and assign it an address, then bind a real device and
|
||||
* stash the resulting information into the device's parent
|
||||
* platform data. Then when we probe it, usb_child_pre_probe() is called
|
||||
* and it will pull the information out of the stash.
|
||||
*/
|
||||
udev->dev = parent;
|
||||
udev->speed = speed;
|
||||
udev->devnum = priv->next_addr + 1;
|
||||
udev->portnr = port;
|
||||
debug("Calling usb_setup_device(), portnr=%d\n", udev->portnr);
|
||||
parent_udev = device_get_uclass_id(parent) == UCLASS_USB_HUB ?
|
||||
dev_get_parent_priv(parent) : NULL;
|
||||
ret = usb_setup_device(udev, priv->desc_before_addr, parent_udev);
|
||||
debug("read_descriptor for '%s': ret=%d\n", parent->name, ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = usb_find_child(parent, &udev->descriptor, iface, &dev);
|
||||
debug("** usb_find_child returns %d\n", ret);
|
||||
if (ret) {
|
||||
if (ret != -ENOENT)
|
||||
return ret;
|
||||
ret = usb_find_and_bind_driver(parent, &udev->descriptor, iface,
|
||||
udev->controller_dev->seq,
|
||||
udev->devnum, &dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
created = true;
|
||||
}
|
||||
plat = dev_get_parent_platdata(dev);
|
||||
debug("%s: Probing '%s', plat=%p\n", __func__, dev->name, plat);
|
||||
plat->devnum = udev->devnum;
|
||||
plat->udev = udev;
|
||||
priv->next_addr++;
|
||||
ret = device_probe(dev);
|
||||
if (ret) {
|
||||
debug("%s: Device '%s' probe failed\n", __func__, dev->name);
|
||||
priv->next_addr--;
|
||||
if (created)
|
||||
device_unbind(dev);
|
||||
return ret;
|
||||
}
|
||||
*devp = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect if a USB device has been plugged or unplugged.
|
||||
*/
|
||||
int usb_detect_change(void)
|
||||
{
|
||||
struct udevice *hub;
|
||||
struct uclass *uc;
|
||||
int change = 0;
|
||||
int ret;
|
||||
|
||||
ret = uclass_get(UCLASS_USB_HUB, &uc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
uclass_foreach_dev(hub, uc) {
|
||||
struct usb_device *udev;
|
||||
struct udevice *dev;
|
||||
|
||||
if (!device_active(hub))
|
||||
continue;
|
||||
for (device_find_first_child(hub, &dev);
|
||||
dev;
|
||||
device_find_next_child(&dev)) {
|
||||
struct usb_port_status status;
|
||||
|
||||
if (!device_active(dev))
|
||||
continue;
|
||||
|
||||
udev = dev_get_parent_priv(dev);
|
||||
if (usb_get_port_status(udev, udev->portnr, &status)
|
||||
< 0)
|
||||
/* USB request failed */
|
||||
continue;
|
||||
|
||||
if (le16_to_cpu(status.wPortChange) &
|
||||
USB_PORT_STAT_C_CONNECTION)
|
||||
change++;
|
||||
}
|
||||
}
|
||||
|
||||
return change;
|
||||
}
|
||||
|
||||
int usb_child_post_bind(struct udevice *dev)
|
||||
{
|
||||
struct usb_dev_platdata *plat = dev_get_parent_platdata(dev);
|
||||
const void *blob = gd->fdt_blob;
|
||||
int val;
|
||||
|
||||
if (dev->of_offset == -1)
|
||||
return 0;
|
||||
|
||||
/* We only support matching a few things */
|
||||
val = fdtdec_get_int(blob, dev->of_offset, "usb,device-class", -1);
|
||||
if (val != -1) {
|
||||
plat->id.match_flags |= USB_DEVICE_ID_MATCH_DEV_CLASS;
|
||||
plat->id.bDeviceClass = val;
|
||||
}
|
||||
val = fdtdec_get_int(blob, dev->of_offset, "usb,interface-class", -1);
|
||||
if (val != -1) {
|
||||
plat->id.match_flags |= USB_DEVICE_ID_MATCH_INT_CLASS;
|
||||
plat->id.bInterfaceClass = val;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct udevice *usb_get_bus(struct udevice *dev)
|
||||
{
|
||||
struct udevice *bus;
|
||||
|
||||
for (bus = dev; bus && device_get_uclass_id(bus) != UCLASS_USB; )
|
||||
bus = bus->parent;
|
||||
if (!bus) {
|
||||
/* By design this cannot happen */
|
||||
assert(bus);
|
||||
debug("USB HUB '%s' does not have a controller\n", dev->name);
|
||||
}
|
||||
|
||||
return bus;
|
||||
}
|
||||
|
||||
int usb_child_pre_probe(struct udevice *dev)
|
||||
{
|
||||
struct usb_device *udev = dev_get_parent_priv(dev);
|
||||
struct usb_dev_platdata *plat = dev_get_parent_platdata(dev);
|
||||
int ret;
|
||||
|
||||
if (plat->udev) {
|
||||
/*
|
||||
* Copy over all the values set in the on stack struct
|
||||
* usb_device in usb_scan_device() to our final struct
|
||||
* usb_device for this dev.
|
||||
*/
|
||||
*udev = *(plat->udev);
|
||||
/* And clear plat->udev as it will not be valid for long */
|
||||
plat->udev = NULL;
|
||||
udev->dev = dev;
|
||||
} else {
|
||||
/*
|
||||
* This happens with devices which are explicitly bound
|
||||
* instead of being discovered through usb_scan_device()
|
||||
* such as sandbox emul devices.
|
||||
*/
|
||||
udev->dev = dev;
|
||||
udev->controller_dev = usb_get_bus(dev);
|
||||
udev->devnum = plat->devnum;
|
||||
|
||||
/*
|
||||
* udev did not go through usb_scan_device(), so we need to
|
||||
* select the config and read the config descriptors.
|
||||
*/
|
||||
ret = usb_select_config(udev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(usb) = {
|
||||
.id = UCLASS_USB,
|
||||
.name = "usb",
|
||||
.flags = DM_UC_FLAG_SEQ_ALIAS,
|
||||
.post_bind = usb_post_bind,
|
||||
.priv_auto_alloc_size = sizeof(struct usb_uclass_priv),
|
||||
.per_child_auto_alloc_size = sizeof(struct usb_device),
|
||||
.per_device_auto_alloc_size = sizeof(struct usb_bus_priv),
|
||||
.child_post_bind = usb_child_post_bind,
|
||||
.child_pre_probe = usb_child_pre_probe,
|
||||
.per_child_platdata_auto_alloc_size = sizeof(struct usb_dev_platdata),
|
||||
};
|
||||
|
||||
UCLASS_DRIVER(usb_dev_generic) = {
|
||||
.id = UCLASS_USB_DEV_GENERIC,
|
||||
.name = "usb_dev_generic",
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(usb_dev_generic_drv) = {
|
||||
.id = UCLASS_USB_DEV_GENERIC,
|
||||
.name = "usb_dev_generic_drv",
|
||||
};
|
||||
80
u-boot/drivers/usb/host/utmi-armada100.c
Normal file
80
u-boot/drivers/usb/host/utmi-armada100.c
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* (C) Copyright 2012
|
||||
* eInfochips Ltd. <www.einfochips.com>
|
||||
* Written-by: Ajay Bhargav <ajay.bhargav@einfochips.com>
|
||||
*
|
||||
* (C) Copyright 2009
|
||||
* Marvell Semiconductor <www.marvell.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <usb.h>
|
||||
#include <asm/arch/cpu.h>
|
||||
#include <asm/arch/armada100.h>
|
||||
#include <asm/arch/utmi-armada100.h>
|
||||
|
||||
static int utmi_phy_init(void)
|
||||
{
|
||||
struct armd1usb_phy_reg *phy_regs =
|
||||
(struct armd1usb_phy_reg *)UTMI_PHY_BASE;
|
||||
int timeout;
|
||||
|
||||
setbits_le32(&phy_regs->utmi_ctrl, INPKT_DELAY_SOF | PLL_PWR_UP);
|
||||
udelay(1000);
|
||||
setbits_le32(&phy_regs->utmi_ctrl, PHY_PWR_UP);
|
||||
|
||||
clrbits_le32(&phy_regs->utmi_pll, PLL_FBDIV_MASK | PLL_REFDIV_MASK);
|
||||
setbits_le32(&phy_regs->utmi_pll, N_DIVIDER << PLL_FBDIV | M_DIVIDER);
|
||||
|
||||
setbits_le32(&phy_regs->utmi_tx, PHSEL_VAL << CK60_PHSEL);
|
||||
|
||||
/* Calibrate pll */
|
||||
timeout = 10000;
|
||||
while (--timeout && ((readl(&phy_regs->utmi_pll) & PLL_READY) == 0))
|
||||
;
|
||||
if (!timeout)
|
||||
return -1;
|
||||
|
||||
udelay(200);
|
||||
setbits_le32(&phy_regs->utmi_pll, VCOCAL_START);
|
||||
udelay(400);
|
||||
clrbits_le32(&phy_regs->utmi_pll, VCOCAL_START);
|
||||
|
||||
udelay(200);
|
||||
setbits_le32(&phy_regs->utmi_tx, RCAL_START);
|
||||
udelay(400);
|
||||
clrbits_le32(&phy_regs->utmi_tx, RCAL_START);
|
||||
|
||||
timeout = 10000;
|
||||
while (--timeout && ((readl(&phy_regs->utmi_pll) & PLL_READY) == 0))
|
||||
;
|
||||
if (!timeout)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize USB host controller's UTMI Physical interface
|
||||
*/
|
||||
int utmi_init(void)
|
||||
{
|
||||
struct armd1mpmu_registers *mpmu_regs =
|
||||
(struct armd1mpmu_registers *)ARMD1_MPMU_BASE;
|
||||
|
||||
struct armd1apmu_registers *apmu_regs =
|
||||
(struct armd1apmu_registers *)ARMD1_APMU_BASE;
|
||||
|
||||
/* Turn on 26Mhz ref clock for UTMI PLL */
|
||||
setbits_le32(&mpmu_regs->acgr, APB2_26M_EN | AP_26M);
|
||||
|
||||
/* USB Clock reset */
|
||||
writel(USB_SPH_AXICLK_EN, &apmu_regs->usbcrc);
|
||||
writel(USB_SPH_AXICLK_EN | USB_SPH_AXI_RST, &apmu_regs->usbcrc);
|
||||
|
||||
/* Initialize UTMI transceiver */
|
||||
return utmi_phy_init();
|
||||
}
|
||||
99
u-boot/drivers/usb/host/xhci-dwc3.c
Normal file
99
u-boot/drivers/usb/host/xhci-dwc3.c
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2015 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* DWC3 controller driver
|
||||
*
|
||||
* Author: Ramneek Mehresh<ramneek.mehresh@freescale.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/usb/dwc3.h>
|
||||
|
||||
void dwc3_set_mode(struct dwc3 *dwc3_reg, u32 mode)
|
||||
{
|
||||
clrsetbits_le32(&dwc3_reg->g_ctl,
|
||||
DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG),
|
||||
DWC3_GCTL_PRTCAPDIR(mode));
|
||||
}
|
||||
|
||||
void dwc3_phy_reset(struct dwc3 *dwc3_reg)
|
||||
{
|
||||
/* Assert USB3 PHY reset */
|
||||
setbits_le32(&dwc3_reg->g_usb3pipectl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST);
|
||||
|
||||
/* Assert USB2 PHY reset */
|
||||
setbits_le32(&dwc3_reg->g_usb2phycfg, DWC3_GUSB2PHYCFG_PHYSOFTRST);
|
||||
|
||||
mdelay(100);
|
||||
|
||||
/* Clear USB3 PHY reset */
|
||||
clrbits_le32(&dwc3_reg->g_usb3pipectl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST);
|
||||
|
||||
/* Clear USB2 PHY reset */
|
||||
clrbits_le32(&dwc3_reg->g_usb2phycfg, DWC3_GUSB2PHYCFG_PHYSOFTRST);
|
||||
}
|
||||
|
||||
void dwc3_core_soft_reset(struct dwc3 *dwc3_reg)
|
||||
{
|
||||
/* Before Resetting PHY, put Core in Reset */
|
||||
setbits_le32(&dwc3_reg->g_ctl, DWC3_GCTL_CORESOFTRESET);
|
||||
|
||||
/* reset USB3 phy - if required */
|
||||
dwc3_phy_reset(dwc3_reg);
|
||||
|
||||
mdelay(100);
|
||||
|
||||
/* After PHYs are stable we can take Core out of reset state */
|
||||
clrbits_le32(&dwc3_reg->g_ctl, DWC3_GCTL_CORESOFTRESET);
|
||||
}
|
||||
|
||||
int dwc3_core_init(struct dwc3 *dwc3_reg)
|
||||
{
|
||||
u32 reg;
|
||||
u32 revision;
|
||||
unsigned int dwc3_hwparams1;
|
||||
|
||||
revision = readl(&dwc3_reg->g_snpsid);
|
||||
/* This should read as U3 followed by revision number */
|
||||
if ((revision & DWC3_GSNPSID_MASK) != 0x55330000) {
|
||||
puts("this is not a DesignWare USB3 DRD Core\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dwc3_core_soft_reset(dwc3_reg);
|
||||
|
||||
dwc3_hwparams1 = readl(&dwc3_reg->g_hwparams1);
|
||||
|
||||
reg = readl(&dwc3_reg->g_ctl);
|
||||
reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
|
||||
reg &= ~DWC3_GCTL_DISSCRAMBLE;
|
||||
switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc3_hwparams1)) {
|
||||
case DWC3_GHWPARAMS1_EN_PWROPT_CLK:
|
||||
reg &= ~DWC3_GCTL_DSBLCLKGTNG;
|
||||
break;
|
||||
default:
|
||||
debug("No power optimization available\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* WORKAROUND: DWC3 revisions <1.90a have a bug
|
||||
* where the device can fail to connect at SuperSpeed
|
||||
* and falls back to high-speed mode which causes
|
||||
* the device to enter a Connect/Disconnect loop
|
||||
*/
|
||||
if ((revision & DWC3_REVISION_MASK) < 0x190a)
|
||||
reg |= DWC3_GCTL_U2RSTECN;
|
||||
|
||||
writel(reg, &dwc3_reg->g_ctl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dwc3_set_fladj(struct dwc3 *dwc3_reg, u32 val)
|
||||
{
|
||||
setbits_le32(&dwc3_reg->g_fladj, GFLADJ_30MHZ_REG_SEL |
|
||||
GFLADJ_30MHZ(val));
|
||||
}
|
||||
260
u-boot/drivers/usb/host/xhci-exynos5.c
Normal file
260
u-boot/drivers/usb/host/xhci-exynos5.c
Normal file
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* SAMSUNG EXYNOS5 USB HOST XHCI Controller
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics Co.Ltd
|
||||
* Vivek Gautam <gautam.vivek@samsung.com>
|
||||
* Vikas Sajjan <vikas.sajjan@samsung.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is a conglomeration for DWC3-init sequence and further
|
||||
* exynos5 specific PHY-init sequence.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <fdtdec.h>
|
||||
#include <libfdt.h>
|
||||
#include <malloc.h>
|
||||
#include <usb.h>
|
||||
#include <watchdog.h>
|
||||
#include <asm/arch/cpu.h>
|
||||
#include <asm/arch/power.h>
|
||||
#include <asm/arch/xhci-exynos.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <asm-generic/errno.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/usb/dwc3.h>
|
||||
|
||||
#include "xhci.h"
|
||||
|
||||
/* Declare global data pointer */
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
struct exynos_xhci_platdata {
|
||||
fdt_addr_t hcd_base;
|
||||
fdt_addr_t phy_base;
|
||||
struct gpio_desc vbus_gpio;
|
||||
};
|
||||
|
||||
/**
|
||||
* Contains pointers to register base addresses
|
||||
* for the usb controller.
|
||||
*/
|
||||
struct exynos_xhci {
|
||||
struct usb_platdata usb_plat;
|
||||
struct xhci_ctrl ctrl;
|
||||
struct exynos_usb3_phy *usb3_phy;
|
||||
struct xhci_hccr *hcd;
|
||||
struct dwc3 *dwc3_reg;
|
||||
};
|
||||
|
||||
static int xhci_usb_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct exynos_xhci_platdata *plat = dev_get_platdata(dev);
|
||||
const void *blob = gd->fdt_blob;
|
||||
unsigned int node;
|
||||
int depth;
|
||||
|
||||
/*
|
||||
* Get the base address for XHCI controller from the device node
|
||||
*/
|
||||
plat->hcd_base = dev_get_addr(dev);
|
||||
if (plat->hcd_base == FDT_ADDR_T_NONE) {
|
||||
debug("Can't get the XHCI register base address\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
depth = 0;
|
||||
node = fdtdec_next_compatible_subnode(blob, dev->of_offset,
|
||||
COMPAT_SAMSUNG_EXYNOS5_USB3_PHY, &depth);
|
||||
if (node <= 0) {
|
||||
debug("XHCI: Can't get device node for usb3-phy controller\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the base address for usbphy from the device node
|
||||
*/
|
||||
plat->phy_base = fdtdec_get_addr(blob, node, "reg");
|
||||
if (plat->phy_base == FDT_ADDR_T_NONE) {
|
||||
debug("Can't get the usbphy register address\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* Vbus gpio */
|
||||
gpio_request_by_name(dev, "samsung,vbus-gpio", 0,
|
||||
&plat->vbus_gpio, GPIOD_IS_OUT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exynos5_usb3_phy_init(struct exynos_usb3_phy *phy)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
/* enabling usb_drd phy */
|
||||
set_usbdrd_phy_ctrl(POWER_USB_DRD_PHY_CTRL_EN);
|
||||
|
||||
/* Reset USB 3.0 PHY */
|
||||
writel(0x0, &phy->phy_reg0);
|
||||
|
||||
clrbits_le32(&phy->phy_param0,
|
||||
/* Select PHY CLK source */
|
||||
PHYPARAM0_REF_USE_PAD |
|
||||
/* Set Loss-of-Signal Detector sensitivity */
|
||||
PHYPARAM0_REF_LOSLEVEL_MASK);
|
||||
setbits_le32(&phy->phy_param0, PHYPARAM0_REF_LOSLEVEL);
|
||||
|
||||
writel(0x0, &phy->phy_resume);
|
||||
|
||||
/*
|
||||
* Setting the Frame length Adj value[6:1] to default 0x20
|
||||
* See xHCI 1.0 spec, 5.2.4
|
||||
*/
|
||||
setbits_le32(&phy->link_system,
|
||||
LINKSYSTEM_XHCI_VERSION_CONTROL |
|
||||
LINKSYSTEM_FLADJ(0x20));
|
||||
|
||||
/* Set Tx De-Emphasis level */
|
||||
clrbits_le32(&phy->phy_param1, PHYPARAM1_PCS_TXDEEMPH_MASK);
|
||||
setbits_le32(&phy->phy_param1, PHYPARAM1_PCS_TXDEEMPH);
|
||||
|
||||
setbits_le32(&phy->phy_batchg, PHYBATCHG_UTMI_CLKSEL);
|
||||
|
||||
/* PHYTEST POWERDOWN Control */
|
||||
clrbits_le32(&phy->phy_test,
|
||||
PHYTEST_POWERDOWN_SSP |
|
||||
PHYTEST_POWERDOWN_HSP);
|
||||
|
||||
/* UTMI Power Control */
|
||||
writel(PHYUTMI_OTGDISABLE, &phy->phy_utmi);
|
||||
|
||||
/* Use core clock from main PLL */
|
||||
reg = PHYCLKRST_REFCLKSEL_EXT_REFCLK |
|
||||
/* Default 24Mhz crystal clock */
|
||||
PHYCLKRST_FSEL(FSEL_CLKSEL_24M) |
|
||||
PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF |
|
||||
PHYCLKRST_SSC_REFCLKSEL(0x88) |
|
||||
/* Force PortReset of PHY */
|
||||
PHYCLKRST_PORTRESET |
|
||||
/* Digital power supply in normal operating mode */
|
||||
PHYCLKRST_RETENABLEN |
|
||||
/* Enable ref clock for SS function */
|
||||
PHYCLKRST_REF_SSP_EN |
|
||||
/* Enable spread spectrum */
|
||||
PHYCLKRST_SSC_EN |
|
||||
/* Power down HS Bias and PLL blocks in suspend mode */
|
||||
PHYCLKRST_COMMONONN;
|
||||
|
||||
writel(reg, &phy->phy_clk_rst);
|
||||
|
||||
/* giving time to Phy clock to settle before resetting */
|
||||
udelay(10);
|
||||
|
||||
reg &= ~PHYCLKRST_PORTRESET;
|
||||
writel(reg, &phy->phy_clk_rst);
|
||||
}
|
||||
|
||||
static void exynos5_usb3_phy_exit(struct exynos_usb3_phy *phy)
|
||||
{
|
||||
setbits_le32(&phy->phy_utmi,
|
||||
PHYUTMI_OTGDISABLE |
|
||||
PHYUTMI_FORCESUSPEND |
|
||||
PHYUTMI_FORCESLEEP);
|
||||
|
||||
clrbits_le32(&phy->phy_clk_rst,
|
||||
PHYCLKRST_REF_SSP_EN |
|
||||
PHYCLKRST_SSC_EN |
|
||||
PHYCLKRST_COMMONONN);
|
||||
|
||||
/* PHYTEST POWERDOWN Control to remove leakage current */
|
||||
setbits_le32(&phy->phy_test,
|
||||
PHYTEST_POWERDOWN_SSP |
|
||||
PHYTEST_POWERDOWN_HSP);
|
||||
|
||||
/* disabling usb_drd phy */
|
||||
set_usbdrd_phy_ctrl(POWER_USB_DRD_PHY_CTRL_DISABLE);
|
||||
}
|
||||
|
||||
static int exynos_xhci_core_init(struct exynos_xhci *exynos)
|
||||
{
|
||||
int ret;
|
||||
|
||||
exynos5_usb3_phy_init(exynos->usb3_phy);
|
||||
|
||||
ret = dwc3_core_init(exynos->dwc3_reg);
|
||||
if (ret) {
|
||||
debug("failed to initialize core\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* We are hard-coding DWC3 core to Host Mode */
|
||||
dwc3_set_mode(exynos->dwc3_reg, DWC3_GCTL_PRTCAP_HOST);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exynos_xhci_core_exit(struct exynos_xhci *exynos)
|
||||
{
|
||||
exynos5_usb3_phy_exit(exynos->usb3_phy);
|
||||
}
|
||||
|
||||
static int xhci_usb_probe(struct udevice *dev)
|
||||
{
|
||||
struct exynos_xhci_platdata *plat = dev_get_platdata(dev);
|
||||
struct exynos_xhci *ctx = dev_get_priv(dev);
|
||||
struct xhci_hcor *hcor;
|
||||
int ret;
|
||||
|
||||
ctx->hcd = (struct xhci_hccr *)plat->hcd_base;
|
||||
ctx->usb3_phy = (struct exynos_usb3_phy *)plat->phy_base;
|
||||
ctx->dwc3_reg = (struct dwc3 *)((char *)(ctx->hcd) + DWC3_REG_OFFSET);
|
||||
hcor = (struct xhci_hcor *)((uint32_t)ctx->hcd +
|
||||
HC_LENGTH(xhci_readl(&ctx->hcd->cr_capbase)));
|
||||
|
||||
/* setup the Vbus gpio here */
|
||||
if (dm_gpio_is_valid(&plat->vbus_gpio))
|
||||
dm_gpio_set_value(&plat->vbus_gpio, 1);
|
||||
|
||||
ret = exynos_xhci_core_init(ctx);
|
||||
if (ret) {
|
||||
puts("XHCI: failed to initialize controller\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return xhci_register(dev, ctx->hcd, hcor);
|
||||
}
|
||||
|
||||
static int xhci_usb_remove(struct udevice *dev)
|
||||
{
|
||||
struct exynos_xhci *ctx = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = xhci_deregister(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
exynos_xhci_core_exit(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id xhci_usb_ids[] = {
|
||||
{ .compatible = "samsung,exynos5250-xhci" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(usb_xhci) = {
|
||||
.name = "xhci_exynos",
|
||||
.id = UCLASS_USB,
|
||||
.of_match = xhci_usb_ids,
|
||||
.ofdata_to_platdata = xhci_usb_ofdata_to_platdata,
|
||||
.probe = xhci_usb_probe,
|
||||
.remove = xhci_usb_remove,
|
||||
.ops = &xhci_usb_ops,
|
||||
.platdata_auto_alloc_size = sizeof(struct exynos_xhci_platdata),
|
||||
.priv_auto_alloc_size = sizeof(struct exynos_xhci),
|
||||
.flags = DM_FLAG_ALLOC_PRIV_DMA,
|
||||
};
|
||||
118
u-boot/drivers/usb/host/xhci-fsl.c
Normal file
118
u-boot/drivers/usb/host/xhci-fsl.c
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2015 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* FSL USB HOST xHCI Controller
|
||||
*
|
||||
* Author: Ramneek Mehresh<ramneek.mehresh@freescale.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <usb.h>
|
||||
#include <asm-generic/errno.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/usb/xhci-fsl.h>
|
||||
#include <linux/usb/dwc3.h>
|
||||
#include "xhci.h"
|
||||
#include <fsl_errata.h>
|
||||
#include <fsl_usb.h>
|
||||
|
||||
/* Declare global data pointer */
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
static struct fsl_xhci fsl_xhci;
|
||||
unsigned long ctr_addr[] = FSL_USB_XHCI_ADDR;
|
||||
|
||||
__weak int __board_usb_init(int index, enum usb_init_type init)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int erratum_a008751(void)
|
||||
{
|
||||
#if defined(CONFIG_TARGET_LS2080AQDS) || defined(CONFIG_TARGET_LS2080ARDB)
|
||||
u32 __iomem *scfg = (u32 __iomem *)SCFG_BASE;
|
||||
writel(SCFG_USB3PRM1CR_INIT, scfg + SCFG_USB3PRM1CR / 4);
|
||||
return 0;
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void fsl_apply_xhci_errata(void)
|
||||
{
|
||||
int ret;
|
||||
if (has_erratum_a008751()) {
|
||||
ret = erratum_a008751();
|
||||
if (ret != 0)
|
||||
puts("Failed to apply erratum a008751\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int fsl_xhci_core_init(struct fsl_xhci *fsl_xhci)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = dwc3_core_init(fsl_xhci->dwc3_reg);
|
||||
if (ret) {
|
||||
debug("%s:failed to initialize core\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* We are hard-coding DWC3 core to Host Mode */
|
||||
dwc3_set_mode(fsl_xhci->dwc3_reg, DWC3_GCTL_PRTCAP_HOST);
|
||||
|
||||
/* Set GFLADJ_30MHZ as 20h as per XHCI spec default value */
|
||||
dwc3_set_fladj(fsl_xhci->dwc3_reg, GFLADJ_30MHZ_DEFAULT);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsl_xhci_core_exit(struct fsl_xhci *fsl_xhci)
|
||||
{
|
||||
/*
|
||||
* Currently fsl socs do not support PHY shutdown from
|
||||
* sw. But this support may be added in future socs.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor)
|
||||
{
|
||||
struct fsl_xhci *ctx = &fsl_xhci;
|
||||
int ret = 0;
|
||||
|
||||
ctx->hcd = (struct xhci_hccr *)ctr_addr[index];
|
||||
ctx->dwc3_reg = (struct dwc3 *)((char *)(ctx->hcd) + DWC3_REG_OFFSET);
|
||||
|
||||
ret = board_usb_init(index, USB_INIT_HOST);
|
||||
if (ret != 0) {
|
||||
puts("Failed to initialize board for USB\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
fsl_apply_xhci_errata();
|
||||
|
||||
ret = fsl_xhci_core_init(ctx);
|
||||
if (ret < 0) {
|
||||
puts("Failed to initialize xhci\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
*hccr = (struct xhci_hccr *)ctx->hcd;
|
||||
*hcor = (struct xhci_hcor *)((uintptr_t) *hccr
|
||||
+ HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));
|
||||
|
||||
debug("fsl-xhci: init hccr %lx and hcor %lx hc_length %lx\n",
|
||||
(uintptr_t)*hccr, (uintptr_t)*hcor,
|
||||
(uintptr_t)HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void xhci_hcd_stop(int index)
|
||||
{
|
||||
struct fsl_xhci *ctx = &fsl_xhci;
|
||||
|
||||
fsl_xhci_core_exit(ctx);
|
||||
}
|
||||
241
u-boot/drivers/usb/host/xhci-keystone.c
Normal file
241
u-boot/drivers/usb/host/xhci-keystone.c
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* USB 3.0 DRD Controller
|
||||
*
|
||||
* (C) Copyright 2012-2014
|
||||
* Texas Instruments Incorporated, <www.ti.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <watchdog.h>
|
||||
#include <usb.h>
|
||||
#include <asm/arch/psc_defs.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/usb/dwc3.h>
|
||||
#include <asm/arch/xhci-keystone.h>
|
||||
#include <asm-generic/errno.h>
|
||||
#include <linux/list.h>
|
||||
#include "xhci.h"
|
||||
|
||||
struct kdwc3_irq_regs {
|
||||
u32 revision; /* 0x000 */
|
||||
u32 rsvd0[3];
|
||||
u32 sysconfig; /* 0x010 */
|
||||
u32 rsvd1[1];
|
||||
u32 irq_eoi;
|
||||
u32 rsvd2[1];
|
||||
struct {
|
||||
u32 raw_status;
|
||||
u32 status;
|
||||
u32 enable_set;
|
||||
u32 enable_clr;
|
||||
} irqs[16];
|
||||
};
|
||||
|
||||
struct keystone_xhci {
|
||||
struct xhci_hccr *hcd;
|
||||
struct dwc3 *dwc3_reg;
|
||||
struct xhci_hcor *hcor;
|
||||
struct kdwc3_irq_regs *usbss;
|
||||
struct keystone_xhci_phy *phy;
|
||||
};
|
||||
|
||||
struct keystone_xhci keystone;
|
||||
|
||||
static void keystone_xhci_phy_set(struct keystone_xhci_phy *phy)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
* VBUSVLDEXTSEL has a default value of 1 in BootCfg but shouldn't.
|
||||
* It should always be cleared because our USB PHY has an onchip VBUS
|
||||
* analog comparator.
|
||||
*/
|
||||
val = readl(&phy->phy_clock);
|
||||
/* quit selecting the vbusvldextsel by default! */
|
||||
val &= ~USB3_PHY_OTG_VBUSVLDECTSEL;
|
||||
writel(val, &phy->phy_clock);
|
||||
}
|
||||
|
||||
static void keystone_xhci_phy_unset(struct keystone_xhci_phy *phy)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* Disable the PHY REFCLK clock gate */
|
||||
val = readl(&phy->phy_clock);
|
||||
val &= ~USB3_PHY_REF_SSP_EN;
|
||||
writel(val, &phy->phy_clock);
|
||||
}
|
||||
|
||||
static int keystone_xhci_core_init(struct dwc3 *dwc3_reg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = dwc3_core_init(dwc3_reg);
|
||||
if (ret) {
|
||||
debug("failed to initialize core\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* We are hard-coding DWC3 core to Host Mode */
|
||||
dwc3_set_mode(dwc3_reg, DWC3_GCTL_PRTCAP_HOST);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xhci_hcd_init(int index,
|
||||
struct xhci_hccr **ret_hccr, struct xhci_hcor **ret_hcor)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
struct xhci_hccr *hcd;
|
||||
struct xhci_hcor *hcor;
|
||||
struct kdwc3_irq_regs *usbss;
|
||||
struct keystone_xhci_phy *phy;
|
||||
|
||||
usbss = (struct kdwc3_irq_regs *)CONFIG_USB_SS_BASE;
|
||||
phy = (struct keystone_xhci_phy *)CONFIG_DEV_USB_PHY_BASE;
|
||||
|
||||
/* Enable the PHY REFCLK clock gate with phy_ref_ssp_en = 1 */
|
||||
val = readl(&(phy->phy_clock));
|
||||
val |= USB3_PHY_REF_SSP_EN;
|
||||
writel(val, &phy->phy_clock);
|
||||
|
||||
mdelay(100);
|
||||
|
||||
/* Release USB from reset */
|
||||
ret = psc_enable_module(KS2_LPSC_USB);
|
||||
if (ret) {
|
||||
puts("Cannot enable USB module");
|
||||
return -1;
|
||||
}
|
||||
|
||||
mdelay(100);
|
||||
|
||||
/* Initialize usb phy */
|
||||
keystone_xhci_phy_set(phy);
|
||||
|
||||
/* soft reset usbss */
|
||||
writel(1, &usbss->sysconfig);
|
||||
while (readl(&usbss->sysconfig) & 1)
|
||||
;
|
||||
|
||||
val = readl(&usbss->revision);
|
||||
debug("usbss revision %x\n", val);
|
||||
|
||||
/* Initialize usb core */
|
||||
hcd = (struct xhci_hccr *)CONFIG_USB_HOST_XHCI_BASE;
|
||||
keystone.dwc3_reg = (struct dwc3 *)(CONFIG_USB_HOST_XHCI_BASE +
|
||||
DWC3_REG_OFFSET);
|
||||
|
||||
keystone_xhci_core_init(keystone.dwc3_reg);
|
||||
|
||||
/* set register addresses */
|
||||
hcor = (struct xhci_hcor *)((uint32_t)hcd +
|
||||
HC_LENGTH(readl(&hcd->cr_capbase)));
|
||||
|
||||
debug("Keystone2-xhci: init hccr %08x and hcor %08x hc_length %d\n",
|
||||
(u32)hcd, (u32)hcor,
|
||||
(u32)HC_LENGTH(xhci_readl(&hcd->cr_capbase)));
|
||||
|
||||
keystone.usbss = usbss;
|
||||
keystone.phy = phy;
|
||||
keystone.hcd = hcd;
|
||||
keystone.hcor = hcor;
|
||||
|
||||
*ret_hccr = hcd;
|
||||
*ret_hcor = hcor;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int keystone_xhci_phy_suspend(void)
|
||||
{
|
||||
int loop_cnt = 0;
|
||||
struct xhci_hcor *hcor;
|
||||
uint32_t *portsc_1 = NULL;
|
||||
uint32_t *portsc_2 = NULL;
|
||||
u32 val, usb2_pls, usb3_pls, event_q;
|
||||
struct dwc3 *dwc3_reg = keystone.dwc3_reg;
|
||||
|
||||
/* set register addresses */
|
||||
hcor = keystone.hcor;
|
||||
|
||||
/* Bypass Scrambling and Set Shorter Training sequence for simulation */
|
||||
val = DWC3_GCTL_PWRDNSCALE(0x4b0) | DWC3_GCTL_PRTCAPDIR(0x2);
|
||||
writel(val, &dwc3_reg->g_ctl);
|
||||
|
||||
/* GUSB2PHYCFG */
|
||||
val = readl(&dwc3_reg->g_usb2phycfg[0]);
|
||||
|
||||
/* assert bit 6 (SusPhy) */
|
||||
val |= DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
writel(val, &dwc3_reg->g_usb2phycfg[0]);
|
||||
|
||||
/* GUSB3PIPECTL */
|
||||
val = readl(&dwc3_reg->g_usb3pipectl[0]);
|
||||
|
||||
/*
|
||||
* assert bit 29 to allow PHY to go to suspend when idle
|
||||
* and cause the USB3 SS PHY to enter suspend mode
|
||||
*/
|
||||
val |= (BIT(29) | DWC3_GUSB3PIPECTL_SUSPHY);
|
||||
writel(val, &dwc3_reg->g_usb3pipectl[0]);
|
||||
|
||||
/*
|
||||
* Steps necessary to allow controller to suspend even when
|
||||
* VBUS is HIGH:
|
||||
* - Init DCFG[2:0] (DevSpd) to: 1=FS
|
||||
* - Init GEVNTADR0 to point to an eventQ
|
||||
* - Init GEVNTSIZ0 to 0x0100 to specify the size of the eventQ
|
||||
* - Init DCTL::Run_nStop = 1
|
||||
*/
|
||||
writel(0x00020001, &dwc3_reg->d_cfg);
|
||||
/* TODO: local2global( (Uint32) eventQ )? */
|
||||
writel((u32)&event_q, &dwc3_reg->g_evnt_buf[0].g_evntadrlo);
|
||||
writel(0, &dwc3_reg->g_evnt_buf[0].g_evntadrhi);
|
||||
writel(0x4, &dwc3_reg->g_evnt_buf[0].g_evntsiz);
|
||||
/* Run */
|
||||
writel(DWC3_DCTL_RUN_STOP, &dwc3_reg->d_ctl);
|
||||
|
||||
mdelay(100);
|
||||
|
||||
/* Wait for USB2 & USB3 PORTSC::PortLinkState to indicate suspend */
|
||||
portsc_1 = (uint32_t *)(&hcor->portregs[0].or_portsc);
|
||||
portsc_2 = (uint32_t *)(&hcor->portregs[1].or_portsc);
|
||||
usb2_pls = 0;
|
||||
usb3_pls = 0;
|
||||
do {
|
||||
++loop_cnt;
|
||||
usb2_pls = (readl(portsc_1) & PORT_PLS_MASK) >> 5;
|
||||
usb3_pls = (readl(portsc_2) & PORT_PLS_MASK) >> 5;
|
||||
} while (((usb2_pls != 0x4) || (usb3_pls != 0x4)) && loop_cnt < 1000);
|
||||
|
||||
if (usb2_pls != 0x4 || usb3_pls != 0x4) {
|
||||
debug("USB suspend failed - PLS USB2=%02x, USB3=%02x\n",
|
||||
usb2_pls, usb3_pls);
|
||||
return -1;
|
||||
}
|
||||
|
||||
debug("USB2 and USB3 PLS - Disabled, loop_cnt=%d\n", loop_cnt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xhci_hcd_stop(int index)
|
||||
{
|
||||
/* Disable USB */
|
||||
if (keystone_xhci_phy_suspend())
|
||||
return;
|
||||
|
||||
if (psc_disable_module(KS2_LPSC_USB)) {
|
||||
debug("PSC disable module USB failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Disable PHY */
|
||||
keystone_xhci_phy_unset(keystone.phy);
|
||||
|
||||
/* memset(&keystone, 0, sizeof(struct keystone_xhci)); */
|
||||
debug("xhci_hcd_stop OK.\n");
|
||||
}
|
||||
714
u-boot/drivers/usb/host/xhci-mem.c
Normal file
714
u-boot/drivers/usb/host/xhci-mem.c
Normal file
@@ -0,0 +1,714 @@
|
||||
/*
|
||||
* USB HOST XHCI Controller stack
|
||||
*
|
||||
* Based on xHCI host controller driver in linux-kernel
|
||||
* by Sarah Sharp.
|
||||
*
|
||||
* Copyright (C) 2008 Intel Corp.
|
||||
* Author: Sarah Sharp
|
||||
*
|
||||
* Copyright (C) 2013 Samsung Electronics Co.Ltd
|
||||
* Authors: Vivek Gautam <gautam.vivek@samsung.com>
|
||||
* Vikas Sajjan <vikas.sajjan@samsung.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <usb.h>
|
||||
#include <malloc.h>
|
||||
#include <asm/cache.h>
|
||||
#include <asm-generic/errno.h>
|
||||
|
||||
#include "xhci.h"
|
||||
|
||||
#define CACHELINE_SIZE CONFIG_SYS_CACHELINE_SIZE
|
||||
/**
|
||||
* flushes the address passed till the length
|
||||
*
|
||||
* @param addr pointer to memory region to be flushed
|
||||
* @param len the length of the cache line to be flushed
|
||||
* @return none
|
||||
*/
|
||||
void xhci_flush_cache(uintptr_t addr, u32 len)
|
||||
{
|
||||
BUG_ON((void *)addr == NULL || len == 0);
|
||||
|
||||
flush_dcache_range(addr & ~(CACHELINE_SIZE - 1),
|
||||
ALIGN(addr + len, CACHELINE_SIZE));
|
||||
}
|
||||
|
||||
/**
|
||||
* invalidates the address passed till the length
|
||||
*
|
||||
* @param addr pointer to memory region to be invalidates
|
||||
* @param len the length of the cache line to be invalidated
|
||||
* @return none
|
||||
*/
|
||||
void xhci_inval_cache(uintptr_t addr, u32 len)
|
||||
{
|
||||
BUG_ON((void *)addr == NULL || len == 0);
|
||||
|
||||
invalidate_dcache_range(addr & ~(CACHELINE_SIZE - 1),
|
||||
ALIGN(addr + len, CACHELINE_SIZE));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* frees the "segment" pointer passed
|
||||
*
|
||||
* @param ptr pointer to "segement" to be freed
|
||||
* @return none
|
||||
*/
|
||||
static void xhci_segment_free(struct xhci_segment *seg)
|
||||
{
|
||||
free(seg->trbs);
|
||||
seg->trbs = NULL;
|
||||
|
||||
free(seg);
|
||||
}
|
||||
|
||||
/**
|
||||
* frees the "ring" pointer passed
|
||||
*
|
||||
* @param ptr pointer to "ring" to be freed
|
||||
* @return none
|
||||
*/
|
||||
static void xhci_ring_free(struct xhci_ring *ring)
|
||||
{
|
||||
struct xhci_segment *seg;
|
||||
struct xhci_segment *first_seg;
|
||||
|
||||
BUG_ON(!ring);
|
||||
|
||||
first_seg = ring->first_seg;
|
||||
seg = first_seg->next;
|
||||
while (seg != first_seg) {
|
||||
struct xhci_segment *next = seg->next;
|
||||
xhci_segment_free(seg);
|
||||
seg = next;
|
||||
}
|
||||
xhci_segment_free(first_seg);
|
||||
|
||||
free(ring);
|
||||
}
|
||||
|
||||
/**
|
||||
* frees the "xhci_container_ctx" pointer passed
|
||||
*
|
||||
* @param ptr pointer to "xhci_container_ctx" to be freed
|
||||
* @return none
|
||||
*/
|
||||
static void xhci_free_container_ctx(struct xhci_container_ctx *ctx)
|
||||
{
|
||||
free(ctx->bytes);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* frees the virtual devices for "xhci_ctrl" pointer passed
|
||||
*
|
||||
* @param ptr pointer to "xhci_ctrl" whose virtual devices are to be freed
|
||||
* @return none
|
||||
*/
|
||||
static void xhci_free_virt_devices(struct xhci_ctrl *ctrl)
|
||||
{
|
||||
int i;
|
||||
int slot_id;
|
||||
struct xhci_virt_device *virt_dev;
|
||||
|
||||
/*
|
||||
* refactored here to loop through all virt_dev
|
||||
* Slot ID 0 is reserved
|
||||
*/
|
||||
for (slot_id = 0; slot_id < MAX_HC_SLOTS; slot_id++) {
|
||||
virt_dev = ctrl->devs[slot_id];
|
||||
if (!virt_dev)
|
||||
continue;
|
||||
|
||||
ctrl->dcbaa->dev_context_ptrs[slot_id] = 0;
|
||||
|
||||
for (i = 0; i < 31; ++i)
|
||||
if (virt_dev->eps[i].ring)
|
||||
xhci_ring_free(virt_dev->eps[i].ring);
|
||||
|
||||
if (virt_dev->in_ctx)
|
||||
xhci_free_container_ctx(virt_dev->in_ctx);
|
||||
if (virt_dev->out_ctx)
|
||||
xhci_free_container_ctx(virt_dev->out_ctx);
|
||||
|
||||
free(virt_dev);
|
||||
/* make sure we are pointing to NULL */
|
||||
ctrl->devs[slot_id] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* frees all the memory allocated
|
||||
*
|
||||
* @param ptr pointer to "xhci_ctrl" to be cleaned up
|
||||
* @return none
|
||||
*/
|
||||
void xhci_cleanup(struct xhci_ctrl *ctrl)
|
||||
{
|
||||
xhci_ring_free(ctrl->event_ring);
|
||||
xhci_ring_free(ctrl->cmd_ring);
|
||||
xhci_free_virt_devices(ctrl);
|
||||
free(ctrl->erst.entries);
|
||||
free(ctrl->dcbaa);
|
||||
memset(ctrl, '\0', sizeof(struct xhci_ctrl));
|
||||
}
|
||||
|
||||
/**
|
||||
* Malloc the aligned memory
|
||||
*
|
||||
* @param size size of memory to be allocated
|
||||
* @return allocates the memory and returns the aligned pointer
|
||||
*/
|
||||
static void *xhci_malloc(unsigned int size)
|
||||
{
|
||||
void *ptr;
|
||||
size_t cacheline_size = max(XHCI_ALIGNMENT, CACHELINE_SIZE);
|
||||
|
||||
ptr = memalign(cacheline_size, ALIGN(size, cacheline_size));
|
||||
BUG_ON(!ptr);
|
||||
memset(ptr, '\0', size);
|
||||
|
||||
xhci_flush_cache((uintptr_t)ptr, size);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the prev segment point to the next segment.
|
||||
* Change the last TRB in the prev segment to be a Link TRB which points to the
|
||||
* address of the next segment. The caller needs to set any Link TRB
|
||||
* related flags, such as End TRB, Toggle Cycle, and no snoop.
|
||||
*
|
||||
* @param prev pointer to the previous segment
|
||||
* @param next pointer to the next segment
|
||||
* @param link_trbs flag to indicate whether to link the trbs or NOT
|
||||
* @return none
|
||||
*/
|
||||
static void xhci_link_segments(struct xhci_segment *prev,
|
||||
struct xhci_segment *next, bool link_trbs)
|
||||
{
|
||||
u32 val;
|
||||
u64 val_64 = 0;
|
||||
|
||||
if (!prev || !next)
|
||||
return;
|
||||
prev->next = next;
|
||||
if (link_trbs) {
|
||||
val_64 = (uintptr_t)next->trbs;
|
||||
prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr = val_64;
|
||||
|
||||
/*
|
||||
* Set the last TRB in the segment to
|
||||
* have a TRB type ID of Link TRB
|
||||
*/
|
||||
val = le32_to_cpu(prev->trbs[TRBS_PER_SEGMENT-1].link.control);
|
||||
val &= ~TRB_TYPE_BITMASK;
|
||||
val |= (TRB_LINK << TRB_TYPE_SHIFT);
|
||||
|
||||
prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the Ring's enqueue,dequeue,enq_seg pointers
|
||||
*
|
||||
* @param ring pointer to the RING to be intialised
|
||||
* @return none
|
||||
*/
|
||||
static void xhci_initialize_ring_info(struct xhci_ring *ring)
|
||||
{
|
||||
/*
|
||||
* The ring is empty, so the enqueue pointer == dequeue pointer
|
||||
*/
|
||||
ring->enqueue = ring->first_seg->trbs;
|
||||
ring->enq_seg = ring->first_seg;
|
||||
ring->dequeue = ring->enqueue;
|
||||
ring->deq_seg = ring->first_seg;
|
||||
|
||||
/*
|
||||
* The ring is initialized to 0. The producer must write 1 to the
|
||||
* cycle bit to handover ownership of the TRB, so PCS = 1.
|
||||
* The consumer must compare CCS to the cycle bit to
|
||||
* check ownership, so CCS = 1.
|
||||
*/
|
||||
ring->cycle_state = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates a generic ring segment from the ring pool, sets the dma address,
|
||||
* initializes the segment to zero, and sets the private next pointer to NULL.
|
||||
* Section 4.11.1.1:
|
||||
* "All components of all Command and Transfer TRBs shall be initialized to '0'"
|
||||
*
|
||||
* @param none
|
||||
* @return pointer to the newly allocated SEGMENT
|
||||
*/
|
||||
static struct xhci_segment *xhci_segment_alloc(void)
|
||||
{
|
||||
struct xhci_segment *seg;
|
||||
|
||||
seg = (struct xhci_segment *)malloc(sizeof(struct xhci_segment));
|
||||
BUG_ON(!seg);
|
||||
|
||||
seg->trbs = (union xhci_trb *)xhci_malloc(SEGMENT_SIZE);
|
||||
|
||||
seg->next = NULL;
|
||||
|
||||
return seg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ring with zero or more segments.
|
||||
* TODO: current code only uses one-time-allocated single-segment rings
|
||||
* of 1KB anyway, so we might as well get rid of all the segment and
|
||||
* linking code (and maybe increase the size a bit, e.g. 4KB).
|
||||
*
|
||||
*
|
||||
* Link each segment together into a ring.
|
||||
* Set the end flag and the cycle toggle bit on the last segment.
|
||||
* See section 4.9.2 and figures 15 and 16 of XHCI spec rev1.0.
|
||||
*
|
||||
* @param num_segs number of segments in the ring
|
||||
* @param link_trbs flag to indicate whether to link the trbs or NOT
|
||||
* @return pointer to the newly created RING
|
||||
*/
|
||||
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));
|
||||
BUG_ON(!ring);
|
||||
|
||||
if (num_segs == 0)
|
||||
return ring;
|
||||
|
||||
ring->first_seg = xhci_segment_alloc();
|
||||
BUG_ON(!ring->first_seg);
|
||||
|
||||
num_segs--;
|
||||
|
||||
prev = ring->first_seg;
|
||||
while (num_segs > 0) {
|
||||
struct xhci_segment *next;
|
||||
|
||||
next = xhci_segment_alloc();
|
||||
BUG_ON(!next);
|
||||
|
||||
xhci_link_segments(prev, next, link_trbs);
|
||||
|
||||
prev = next;
|
||||
num_segs--;
|
||||
}
|
||||
xhci_link_segments(prev, ring->first_seg, link_trbs);
|
||||
if (link_trbs) {
|
||||
/* See section 4.9.2.1 and 6.4.4.1 */
|
||||
prev->trbs[TRBS_PER_SEGMENT-1].link.control |=
|
||||
cpu_to_le32(LINK_TOGGLE);
|
||||
}
|
||||
xhci_initialize_ring_info(ring);
|
||||
|
||||
return ring;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates the Container context
|
||||
*
|
||||
* @param ctrl Host controller data structure
|
||||
* @param type type of XHCI Container Context
|
||||
* @return NULL if failed else pointer to the context on success
|
||||
*/
|
||||
static struct xhci_container_ctx
|
||||
*xhci_alloc_container_ctx(struct xhci_ctrl *ctrl, int type)
|
||||
{
|
||||
struct xhci_container_ctx *ctx;
|
||||
|
||||
ctx = (struct xhci_container_ctx *)
|
||||
malloc(sizeof(struct xhci_container_ctx));
|
||||
BUG_ON(!ctx);
|
||||
|
||||
BUG_ON((type != XHCI_CTX_TYPE_DEVICE) && (type != XHCI_CTX_TYPE_INPUT));
|
||||
ctx->type = type;
|
||||
ctx->size = (MAX_EP_CTX_NUM + 1) *
|
||||
CTX_SIZE(readl(&ctrl->hccr->cr_hccparams));
|
||||
if (type == XHCI_CTX_TYPE_INPUT)
|
||||
ctx->size += CTX_SIZE(readl(&ctrl->hccr->cr_hccparams));
|
||||
|
||||
ctx->bytes = (u8 *)xhci_malloc(ctx->size);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocating virtual device
|
||||
*
|
||||
* @param udev pointer to USB deivce structure
|
||||
* @return 0 on success else -1 on failure
|
||||
*/
|
||||
int xhci_alloc_virt_device(struct xhci_ctrl *ctrl, unsigned int slot_id)
|
||||
{
|
||||
u64 byte_64 = 0;
|
||||
struct xhci_virt_device *virt_dev;
|
||||
|
||||
/* Slot ID 0 is reserved */
|
||||
if (ctrl->devs[slot_id]) {
|
||||
printf("Virt dev for slot[%d] already allocated\n", slot_id);
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
ctrl->devs[slot_id] = (struct xhci_virt_device *)
|
||||
malloc(sizeof(struct xhci_virt_device));
|
||||
|
||||
if (!ctrl->devs[slot_id]) {
|
||||
puts("Failed to allocate virtual device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(ctrl->devs[slot_id], 0, sizeof(struct xhci_virt_device));
|
||||
virt_dev = ctrl->devs[slot_id];
|
||||
|
||||
/* Allocate the (output) device context that will be used in the HC. */
|
||||
virt_dev->out_ctx = xhci_alloc_container_ctx(ctrl,
|
||||
XHCI_CTX_TYPE_DEVICE);
|
||||
if (!virt_dev->out_ctx) {
|
||||
puts("Failed to allocate out context for virt dev\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Allocate the (input) device context for address device command */
|
||||
virt_dev->in_ctx = xhci_alloc_container_ctx(ctrl,
|
||||
XHCI_CTX_TYPE_INPUT);
|
||||
if (!virt_dev->in_ctx) {
|
||||
puts("Failed to allocate in context for virt dev\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Allocate endpoint 0 ring */
|
||||
virt_dev->eps[0].ring = xhci_ring_alloc(1, true);
|
||||
|
||||
byte_64 = (uintptr_t)(virt_dev->out_ctx->bytes);
|
||||
|
||||
/* Point to output device context in dcbaa. */
|
||||
ctrl->dcbaa->dev_context_ptrs[slot_id] = byte_64;
|
||||
|
||||
xhci_flush_cache((uintptr_t)&ctrl->dcbaa->dev_context_ptrs[slot_id],
|
||||
sizeof(__le64));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates the necessary data structures
|
||||
* for XHCI host controller
|
||||
*
|
||||
* @param ctrl Host controller data structure
|
||||
* @param hccr pointer to HOST Controller Control Registers
|
||||
* @param hcor pointer to HOST Controller Operational Registers
|
||||
* @return 0 if successful else -1 on failure
|
||||
*/
|
||||
int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr,
|
||||
struct xhci_hcor *hcor)
|
||||
{
|
||||
uint64_t val_64;
|
||||
uint64_t trb_64;
|
||||
uint32_t val;
|
||||
unsigned long deq;
|
||||
int i;
|
||||
struct xhci_segment *seg;
|
||||
|
||||
/* DCBAA initialization */
|
||||
ctrl->dcbaa = (struct xhci_device_context_array *)
|
||||
xhci_malloc(sizeof(struct xhci_device_context_array));
|
||||
if (ctrl->dcbaa == NULL) {
|
||||
puts("unable to allocate DCBA\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
val_64 = (uintptr_t)ctrl->dcbaa;
|
||||
/* Set the pointer in DCBAA register */
|
||||
xhci_writeq(&hcor->or_dcbaap, val_64);
|
||||
|
||||
/* Command ring control pointer register initialization */
|
||||
ctrl->cmd_ring = xhci_ring_alloc(1, true);
|
||||
|
||||
/* Set the address in the Command Ring Control register */
|
||||
trb_64 = (uintptr_t)ctrl->cmd_ring->first_seg->trbs;
|
||||
val_64 = xhci_readq(&hcor->or_crcr);
|
||||
val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) |
|
||||
(trb_64 & (u64) ~CMD_RING_RSVD_BITS) |
|
||||
ctrl->cmd_ring->cycle_state;
|
||||
xhci_writeq(&hcor->or_crcr, val_64);
|
||||
|
||||
/* write the address of db register */
|
||||
val = xhci_readl(&hccr->cr_dboff);
|
||||
val &= DBOFF_MASK;
|
||||
ctrl->dba = (struct xhci_doorbell_array *)((char *)hccr + val);
|
||||
|
||||
/* write the address of runtime register */
|
||||
val = xhci_readl(&hccr->cr_rtsoff);
|
||||
val &= RTSOFF_MASK;
|
||||
ctrl->run_regs = (struct xhci_run_regs *)((char *)hccr + val);
|
||||
|
||||
/* writting the address of ir_set structure */
|
||||
ctrl->ir_set = &ctrl->run_regs->ir_set[0];
|
||||
|
||||
/* 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.num_entries = ERST_NUM_SEGS;
|
||||
|
||||
for (val = 0, seg = ctrl->event_ring->first_seg;
|
||||
val < ERST_NUM_SEGS;
|
||||
val++) {
|
||||
trb_64 = 0;
|
||||
trb_64 = (uintptr_t)seg->trbs;
|
||||
struct xhci_erst_entry *entry = &ctrl->erst.entries[val];
|
||||
xhci_writeq(&entry->seg_addr, trb_64);
|
||||
entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT);
|
||||
entry->rsvd = 0;
|
||||
seg = seg->next;
|
||||
}
|
||||
xhci_flush_cache((uintptr_t)ctrl->erst.entries,
|
||||
ERST_NUM_SEGS * sizeof(struct xhci_erst_entry));
|
||||
|
||||
deq = (unsigned long)ctrl->event_ring->dequeue;
|
||||
|
||||
/* Update HC event ring dequeue pointer */
|
||||
xhci_writeq(&ctrl->ir_set->erst_dequeue,
|
||||
(u64)deq & (u64)~ERST_PTR_MASK);
|
||||
|
||||
/* set ERST count with the number of entries in the segment table */
|
||||
val = xhci_readl(&ctrl->ir_set->erst_size);
|
||||
val &= ERST_SIZE_MASK;
|
||||
val |= ERST_NUM_SEGS;
|
||||
xhci_writel(&ctrl->ir_set->erst_size, val);
|
||||
|
||||
/* this is the event ring segment table pointer */
|
||||
val_64 = xhci_readq(&ctrl->ir_set->erst_base);
|
||||
val_64 &= ERST_PTR_MASK;
|
||||
val_64 |= ((uintptr_t)(ctrl->erst.entries) & ~ERST_PTR_MASK);
|
||||
|
||||
xhci_writeq(&ctrl->ir_set->erst_base, val_64);
|
||||
|
||||
/* initializing the virtual devices to NULL */
|
||||
for (i = 0; i < MAX_HC_SLOTS; ++i)
|
||||
ctrl->devs[i] = NULL;
|
||||
|
||||
/*
|
||||
* Just Zero'ing this register completely,
|
||||
* or some spurious Device Notification Events
|
||||
* might screw things here.
|
||||
*/
|
||||
xhci_writel(&hcor->or_dnctrl, 0x0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Give the input control context for the passed container context
|
||||
*
|
||||
* @param ctx pointer to the context
|
||||
* @return pointer to the Input control context data
|
||||
*/
|
||||
struct xhci_input_control_ctx
|
||||
*xhci_get_input_control_ctx(struct xhci_container_ctx *ctx)
|
||||
{
|
||||
BUG_ON(ctx->type != XHCI_CTX_TYPE_INPUT);
|
||||
return (struct xhci_input_control_ctx *)ctx->bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Give the slot context for the passed container context
|
||||
*
|
||||
* @param ctrl Host controller data structure
|
||||
* @param ctx pointer to the context
|
||||
* @return pointer to the slot control context data
|
||||
*/
|
||||
struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_ctrl *ctrl,
|
||||
struct xhci_container_ctx *ctx)
|
||||
{
|
||||
if (ctx->type == XHCI_CTX_TYPE_DEVICE)
|
||||
return (struct xhci_slot_ctx *)ctx->bytes;
|
||||
|
||||
return (struct xhci_slot_ctx *)
|
||||
(ctx->bytes + CTX_SIZE(readl(&ctrl->hccr->cr_hccparams)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the EP context from based on the ep_index
|
||||
*
|
||||
* @param ctrl Host controller data structure
|
||||
* @param ctx context container
|
||||
* @param ep_index index of the endpoint
|
||||
* @return pointer to the End point context
|
||||
*/
|
||||
struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_ctrl *ctrl,
|
||||
struct xhci_container_ctx *ctx,
|
||||
unsigned int ep_index)
|
||||
{
|
||||
/* increment ep index by offset of start of ep ctx array */
|
||||
ep_index++;
|
||||
if (ctx->type == XHCI_CTX_TYPE_INPUT)
|
||||
ep_index++;
|
||||
|
||||
return (struct xhci_ep_ctx *)
|
||||
(ctx->bytes +
|
||||
(ep_index * CTX_SIZE(readl(&ctrl->hccr->cr_hccparams))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy output xhci_ep_ctx to the input xhci_ep_ctx copy.
|
||||
* Useful when you want to change one particular aspect of the endpoint
|
||||
* and then issue a configure endpoint command.
|
||||
*
|
||||
* @param ctrl Host controller data structure
|
||||
* @param in_ctx contains the input context
|
||||
* @param out_ctx contains the input context
|
||||
* @param ep_index index of the end point
|
||||
* @return none
|
||||
*/
|
||||
void xhci_endpoint_copy(struct xhci_ctrl *ctrl,
|
||||
struct xhci_container_ctx *in_ctx,
|
||||
struct xhci_container_ctx *out_ctx,
|
||||
unsigned int ep_index)
|
||||
{
|
||||
struct xhci_ep_ctx *out_ep_ctx;
|
||||
struct xhci_ep_ctx *in_ep_ctx;
|
||||
|
||||
out_ep_ctx = xhci_get_ep_ctx(ctrl, out_ctx, ep_index);
|
||||
in_ep_ctx = xhci_get_ep_ctx(ctrl, in_ctx, ep_index);
|
||||
|
||||
in_ep_ctx->ep_info = out_ep_ctx->ep_info;
|
||||
in_ep_ctx->ep_info2 = out_ep_ctx->ep_info2;
|
||||
in_ep_ctx->deq = out_ep_ctx->deq;
|
||||
in_ep_ctx->tx_info = out_ep_ctx->tx_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy output xhci_slot_ctx to the input xhci_slot_ctx.
|
||||
* Useful when you want to change one particular aspect of the endpoint
|
||||
* and then issue a configure endpoint command.
|
||||
* Only the context entries field matters, but
|
||||
* we'll copy the whole thing anyway.
|
||||
*
|
||||
* @param ctrl Host controller data structure
|
||||
* @param in_ctx contains the inpout context
|
||||
* @param out_ctx contains the inpout context
|
||||
* @return none
|
||||
*/
|
||||
void xhci_slot_copy(struct xhci_ctrl *ctrl, struct xhci_container_ctx *in_ctx,
|
||||
struct xhci_container_ctx *out_ctx)
|
||||
{
|
||||
struct xhci_slot_ctx *in_slot_ctx;
|
||||
struct xhci_slot_ctx *out_slot_ctx;
|
||||
|
||||
in_slot_ctx = xhci_get_slot_ctx(ctrl, in_ctx);
|
||||
out_slot_ctx = xhci_get_slot_ctx(ctrl, out_ctx);
|
||||
|
||||
in_slot_ctx->dev_info = out_slot_ctx->dev_info;
|
||||
in_slot_ctx->dev_info2 = out_slot_ctx->dev_info2;
|
||||
in_slot_ctx->tt_info = out_slot_ctx->tt_info;
|
||||
in_slot_ctx->dev_state = out_slot_ctx->dev_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup an xHCI virtual device for a Set Address command
|
||||
*
|
||||
* @param udev pointer to the Device Data Structure
|
||||
* @return returns negative value on failure else 0 on success
|
||||
*/
|
||||
void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl, int slot_id,
|
||||
int speed, int hop_portnr)
|
||||
{
|
||||
struct xhci_virt_device *virt_dev;
|
||||
struct xhci_ep_ctx *ep0_ctx;
|
||||
struct xhci_slot_ctx *slot_ctx;
|
||||
u32 port_num = 0;
|
||||
u64 trb_64 = 0;
|
||||
|
||||
virt_dev = ctrl->devs[slot_id];
|
||||
|
||||
BUG_ON(!virt_dev);
|
||||
|
||||
/* Extract the EP0 and Slot Ctrl */
|
||||
ep0_ctx = xhci_get_ep_ctx(ctrl, virt_dev->in_ctx, 0);
|
||||
slot_ctx = xhci_get_slot_ctx(ctrl, virt_dev->in_ctx);
|
||||
|
||||
/* Only the control endpoint is valid - one endpoint context */
|
||||
slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1) | 0);
|
||||
|
||||
switch (speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_SS);
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_HS);
|
||||
break;
|
||||
case USB_SPEED_FULL:
|
||||
slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_FS);
|
||||
break;
|
||||
case USB_SPEED_LOW:
|
||||
slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_LS);
|
||||
break;
|
||||
default:
|
||||
/* Speed was set earlier, this shouldn't happen. */
|
||||
BUG();
|
||||
}
|
||||
|
||||
port_num = hop_portnr;
|
||||
debug("port_num = %d\n", port_num);
|
||||
|
||||
slot_ctx->dev_info2 |=
|
||||
cpu_to_le32(((port_num & ROOT_HUB_PORT_MASK) <<
|
||||
ROOT_HUB_PORT_SHIFT));
|
||||
|
||||
/* Step 4 - ring already allocated */
|
||||
/* Step 5 */
|
||||
ep0_ctx->ep_info2 = cpu_to_le32(CTRL_EP << EP_TYPE_SHIFT);
|
||||
debug("SPEED = %d\n", speed);
|
||||
|
||||
switch (speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
ep0_ctx->ep_info2 |= cpu_to_le32(((512 & MAX_PACKET_MASK) <<
|
||||
MAX_PACKET_SHIFT));
|
||||
debug("Setting Packet size = 512bytes\n");
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
/* USB core guesses at a 64-byte max packet first for FS devices */
|
||||
case USB_SPEED_FULL:
|
||||
ep0_ctx->ep_info2 |= cpu_to_le32(((64 & MAX_PACKET_MASK) <<
|
||||
MAX_PACKET_SHIFT));
|
||||
debug("Setting Packet size = 64bytes\n");
|
||||
break;
|
||||
case USB_SPEED_LOW:
|
||||
ep0_ctx->ep_info2 |= cpu_to_le32(((8 & MAX_PACKET_MASK) <<
|
||||
MAX_PACKET_SHIFT));
|
||||
debug("Setting Packet size = 8bytes\n");
|
||||
break;
|
||||
default:
|
||||
/* New speed? */
|
||||
BUG();
|
||||
}
|
||||
|
||||
/* EP 0 can handle "burst" sizes of 1, so Max Burst Size field is 0 */
|
||||
ep0_ctx->ep_info2 |=
|
||||
cpu_to_le32(((0 & MAX_BURST_MASK) << MAX_BURST_SHIFT) |
|
||||
((3 & ERROR_COUNT_MASK) << ERROR_COUNT_SHIFT));
|
||||
|
||||
trb_64 = (uintptr_t)virt_dev->eps[0].ring->first_seg->trbs;
|
||||
ep0_ctx->deq = cpu_to_le64(trb_64 | virt_dev->eps[0].ring->cycle_state);
|
||||
|
||||
/* Steps 7 and 8 were done in xhci_alloc_virt_device() */
|
||||
|
||||
xhci_flush_cache((uintptr_t)ep0_ctx, sizeof(struct xhci_ep_ctx));
|
||||
xhci_flush_cache((uintptr_t)slot_ctx, sizeof(struct xhci_slot_ctx));
|
||||
}
|
||||
100
u-boot/drivers/usb/host/xhci-omap.c
Normal file
100
u-boot/drivers/usb/host/xhci-omap.c
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* OMAP USB HOST xHCI Controller
|
||||
*
|
||||
* (C) Copyright 2013
|
||||
* Texas Instruments, <www.ti.com>
|
||||
*
|
||||
* Author: Dan Murphy <dmurphy@ti.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <usb.h>
|
||||
#include <asm-generic/errno.h>
|
||||
#include <asm/omap_common.h>
|
||||
#include <asm/arch/cpu.h>
|
||||
#include <asm/arch/sys_proto.h>
|
||||
|
||||
#include <linux/compat.h>
|
||||
#include <linux/usb/dwc3.h>
|
||||
#include <linux/usb/xhci-omap.h>
|
||||
|
||||
#include "xhci.h"
|
||||
|
||||
/* Declare global data pointer */
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
static struct omap_xhci omap;
|
||||
|
||||
__weak int __board_usb_init(int index, enum usb_init_type init)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int board_usb_init(int index, enum usb_init_type init)
|
||||
__attribute__((weak, alias("__board_usb_init")));
|
||||
|
||||
static int omap_xhci_core_init(struct omap_xhci *omap)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
usb_phy_power(1);
|
||||
omap_enable_phy(omap);
|
||||
|
||||
ret = dwc3_core_init(omap->dwc3_reg);
|
||||
if (ret) {
|
||||
debug("%s:failed to initialize core\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* We are hard-coding DWC3 core to Host Mode */
|
||||
dwc3_set_mode(omap->dwc3_reg, DWC3_GCTL_PRTCAP_HOST);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void omap_xhci_core_exit(struct omap_xhci *omap)
|
||||
{
|
||||
usb_phy_power(0);
|
||||
}
|
||||
|
||||
int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor)
|
||||
{
|
||||
struct omap_xhci *ctx = &omap;
|
||||
int ret = 0;
|
||||
|
||||
ctx->hcd = (struct xhci_hccr *)OMAP_XHCI_BASE;
|
||||
ctx->dwc3_reg = (struct dwc3 *)((char *)(ctx->hcd) + DWC3_REG_OFFSET);
|
||||
ctx->usb3_phy = (struct omap_usb3_phy *)OMAP_OCP1_SCP_BASE;
|
||||
ctx->otg_wrapper = (struct omap_dwc_wrapper *)OMAP_OTG_WRAPPER_BASE;
|
||||
|
||||
ret = board_usb_init(index, USB_INIT_HOST);
|
||||
if (ret != 0) {
|
||||
puts("Failed to initialize board for USB\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = omap_xhci_core_init(ctx);
|
||||
if (ret < 0) {
|
||||
puts("Failed to initialize xhci\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
*hccr = (struct xhci_hccr *)(OMAP_XHCI_BASE);
|
||||
*hcor = (struct xhci_hcor *)((uint32_t) *hccr
|
||||
+ HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));
|
||||
|
||||
debug("omap-xhci: init hccr %x and hcor %x hc_length %d\n",
|
||||
(uint32_t)*hccr, (uint32_t)*hcor,
|
||||
(uint32_t)HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void xhci_hcd_stop(int index)
|
||||
{
|
||||
struct omap_xhci *ctx = &omap;
|
||||
|
||||
omap_xhci_core_exit(ctx);
|
||||
board_usb_cleanup(index, USB_INIT_HOST);
|
||||
}
|
||||
60
u-boot/drivers/usb/host/xhci-pci.c
Normal file
60
u-boot/drivers/usb/host/xhci-pci.c
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <errno.h>
|
||||
#include <pci.h>
|
||||
#include <usb.h>
|
||||
|
||||
#include "xhci.h"
|
||||
|
||||
/*
|
||||
* Create the appropriate control structures to manage a new XHCI host
|
||||
* controller.
|
||||
*/
|
||||
int xhci_hcd_init(int index, struct xhci_hccr **ret_hccr,
|
||||
struct xhci_hcor **ret_hcor)
|
||||
{
|
||||
struct xhci_hccr *hccr;
|
||||
struct xhci_hcor *hcor;
|
||||
pci_dev_t pdev;
|
||||
uint32_t cmd;
|
||||
int len;
|
||||
|
||||
pdev = pci_find_class(PCI_CLASS_SERIAL_USB_XHCI, index);
|
||||
if (pdev < 0) {
|
||||
printf("XHCI host controller not found\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
hccr = (struct xhci_hccr *)pci_map_bar(pdev,
|
||||
PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
|
||||
len = HC_LENGTH(xhci_readl(&hccr->cr_capbase));
|
||||
hcor = (struct xhci_hcor *)((uint32_t)hccr + len);
|
||||
|
||||
debug("XHCI-PCI init hccr 0x%x and hcor 0x%x hc_length %d\n",
|
||||
(uint32_t)hccr, (uint32_t)hcor, len);
|
||||
|
||||
*ret_hccr = hccr;
|
||||
*ret_hcor = hcor;
|
||||
|
||||
/* enable busmaster */
|
||||
pci_read_config_dword(pdev, PCI_COMMAND, &cmd);
|
||||
cmd |= PCI_COMMAND_MASTER;
|
||||
pci_write_config_dword(pdev, PCI_COMMAND, cmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy the appropriate control structures corresponding * to the XHCI host
|
||||
* controller
|
||||
*/
|
||||
void xhci_hcd_stop(int index)
|
||||
{
|
||||
}
|
||||
939
u-boot/drivers/usb/host/xhci-ring.c
Normal file
939
u-boot/drivers/usb/host/xhci-ring.c
Normal file
@@ -0,0 +1,939 @@
|
||||
/*
|
||||
* USB HOST XHCI Controller stack
|
||||
*
|
||||
* Based on xHCI host controller driver in linux-kernel
|
||||
* by Sarah Sharp.
|
||||
*
|
||||
* Copyright (C) 2008 Intel Corp.
|
||||
* Author: Sarah Sharp
|
||||
*
|
||||
* Copyright (C) 2013 Samsung Electronics Co.Ltd
|
||||
* Authors: Vivek Gautam <gautam.vivek@samsung.com>
|
||||
* Vikas Sajjan <vikas.sajjan@samsung.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <usb.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <asm-generic/errno.h>
|
||||
|
||||
#include "xhci.h"
|
||||
|
||||
/**
|
||||
* Is this TRB a link TRB or was the last TRB the last TRB in this event ring
|
||||
* segment? I.e. would the updated event TRB pointer step off the end of the
|
||||
* event seg ?
|
||||
*
|
||||
* @param ctrl Host controller data structure
|
||||
* @param ring pointer to the ring
|
||||
* @param seg poniter to the segment to which TRB belongs
|
||||
* @param trb poniter to the ring trb
|
||||
* @return 1 if this TRB a link TRB else 0
|
||||
*/
|
||||
static int last_trb(struct xhci_ctrl *ctrl, struct xhci_ring *ring,
|
||||
struct xhci_segment *seg, union xhci_trb *trb)
|
||||
{
|
||||
if (ring == ctrl->event_ring)
|
||||
return trb == &seg->trbs[TRBS_PER_SEGMENT];
|
||||
else
|
||||
return TRB_TYPE_LINK_LE32(trb->link.control);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this link TRB point to the first segment in a ring,
|
||||
* or was the previous TRB the last TRB on the last segment in the ERST?
|
||||
*
|
||||
* @param ctrl Host controller data structure
|
||||
* @param ring pointer to the ring
|
||||
* @param seg poniter to the segment to which TRB belongs
|
||||
* @param trb poniter to the ring trb
|
||||
* @return 1 if this TRB is the last TRB on the last segment else 0
|
||||
*/
|
||||
static bool last_trb_on_last_seg(struct xhci_ctrl *ctrl,
|
||||
struct xhci_ring *ring,
|
||||
struct xhci_segment *seg,
|
||||
union xhci_trb *trb)
|
||||
{
|
||||
if (ring == ctrl->event_ring)
|
||||
return ((trb == &seg->trbs[TRBS_PER_SEGMENT]) &&
|
||||
(seg->next == ring->first_seg));
|
||||
else
|
||||
return le32_to_cpu(trb->link.control) & LINK_TOGGLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* See Cycle bit rules. SW is the consumer for the event ring only.
|
||||
* Don't make a ring full of link TRBs. That would be dumb and this would loop.
|
||||
*
|
||||
* If we've just enqueued a TRB that is in the middle of a TD (meaning the
|
||||
* chain bit is set), then set the chain bit in all the following link TRBs.
|
||||
* If we've enqueued the last TRB in a TD, make sure the following link TRBs
|
||||
* have their chain bit cleared (so that each Link TRB is a separate TD).
|
||||
*
|
||||
* Section 6.4.4.1 of the 0.95 spec says link TRBs cannot have the chain bit
|
||||
* set, but other sections talk about dealing with the chain bit set. This was
|
||||
* fixed in the 0.96 specification errata, but we have to assume that all 0.95
|
||||
* xHCI hardware can't handle the chain bit being cleared on a link TRB.
|
||||
*
|
||||
* @param ctrl Host controller data structure
|
||||
* @param ring pointer to the ring
|
||||
* @param more_trbs_coming flag to indicate whether more trbs
|
||||
* are expected or NOT.
|
||||
* Will you enqueue more TRBs before calling
|
||||
* prepare_ring()?
|
||||
* @return none
|
||||
*/
|
||||
static void inc_enq(struct xhci_ctrl *ctrl, struct xhci_ring *ring,
|
||||
bool more_trbs_coming)
|
||||
{
|
||||
u32 chain;
|
||||
union xhci_trb *next;
|
||||
|
||||
chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN;
|
||||
next = ++(ring->enqueue);
|
||||
|
||||
/*
|
||||
* Update the dequeue pointer further if that was a link TRB or we're at
|
||||
* the end of an event ring segment (which doesn't have link TRBS)
|
||||
*/
|
||||
while (last_trb(ctrl, ring, ring->enq_seg, next)) {
|
||||
if (ring != ctrl->event_ring) {
|
||||
/*
|
||||
* If the caller doesn't plan on enqueueing more
|
||||
* TDs before ringing the doorbell, then we
|
||||
* don't want to give the link TRB to the
|
||||
* hardware just yet. We'll give the link TRB
|
||||
* back in prepare_ring() just before we enqueue
|
||||
* the TD at the top of the ring.
|
||||
*/
|
||||
if (!chain && !more_trbs_coming)
|
||||
break;
|
||||
|
||||
/*
|
||||
* If we're not dealing with 0.95 hardware or
|
||||
* isoc rings on AMD 0.96 host,
|
||||
* carry over the chain bit of the previous TRB
|
||||
* (which may mean the chain bit is cleared).
|
||||
*/
|
||||
next->link.control &= cpu_to_le32(~TRB_CHAIN);
|
||||
next->link.control |= cpu_to_le32(chain);
|
||||
|
||||
next->link.control ^= cpu_to_le32(TRB_CYCLE);
|
||||
xhci_flush_cache((uintptr_t)next,
|
||||
sizeof(union xhci_trb));
|
||||
}
|
||||
/* Toggle the cycle bit after the last ring segment. */
|
||||
if (last_trb_on_last_seg(ctrl, ring,
|
||||
ring->enq_seg, next))
|
||||
ring->cycle_state = (ring->cycle_state ? 0 : 1);
|
||||
|
||||
ring->enq_seg = ring->enq_seg->next;
|
||||
ring->enqueue = ring->enq_seg->trbs;
|
||||
next = ring->enqueue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See Cycle bit rules. SW is the consumer for the event ring only.
|
||||
* Don't make a ring full of link TRBs. That would be dumb and this would loop.
|
||||
*
|
||||
* @param ctrl Host controller data structure
|
||||
* @param ring Ring whose Dequeue TRB pointer needs to be incremented.
|
||||
* return none
|
||||
*/
|
||||
static void inc_deq(struct xhci_ctrl *ctrl, struct xhci_ring *ring)
|
||||
{
|
||||
do {
|
||||
/*
|
||||
* Update the dequeue pointer further if that was a link TRB or
|
||||
* we're at the end of an event ring segment (which doesn't have
|
||||
* link TRBS)
|
||||
*/
|
||||
if (last_trb(ctrl, ring, ring->deq_seg, ring->dequeue)) {
|
||||
if (ring == ctrl->event_ring &&
|
||||
last_trb_on_last_seg(ctrl, ring,
|
||||
ring->deq_seg, ring->dequeue)) {
|
||||
ring->cycle_state = (ring->cycle_state ? 0 : 1);
|
||||
}
|
||||
ring->deq_seg = ring->deq_seg->next;
|
||||
ring->dequeue = ring->deq_seg->trbs;
|
||||
} else {
|
||||
ring->dequeue++;
|
||||
}
|
||||
} while (last_trb(ctrl, ring, ring->deq_seg, ring->dequeue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic function for queueing a TRB on a ring.
|
||||
* The caller must have checked to make sure there's room on the ring.
|
||||
*
|
||||
* @param more_trbs_coming: Will you enqueue more TRBs before calling
|
||||
* prepare_ring()?
|
||||
* @param ctrl Host controller data structure
|
||||
* @param ring pointer to the ring
|
||||
* @param more_trbs_coming flag to indicate whether more trbs
|
||||
* @param trb_fields pointer to trb field array containing TRB contents
|
||||
* @return pointer to the enqueued trb
|
||||
*/
|
||||
static struct xhci_generic_trb *queue_trb(struct xhci_ctrl *ctrl,
|
||||
struct xhci_ring *ring,
|
||||
bool more_trbs_coming,
|
||||
unsigned int *trb_fields)
|
||||
{
|
||||
struct xhci_generic_trb *trb;
|
||||
int i;
|
||||
|
||||
trb = &ring->enqueue->generic;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
trb->field[i] = cpu_to_le32(trb_fields[i]);
|
||||
|
||||
xhci_flush_cache((uintptr_t)trb, sizeof(struct xhci_generic_trb));
|
||||
|
||||
inc_enq(ctrl, ring, more_trbs_coming);
|
||||
|
||||
return trb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does various checks on the endpoint ring, and makes it ready
|
||||
* to queue num_trbs.
|
||||
*
|
||||
* @param ctrl Host controller data structure
|
||||
* @param ep_ring pointer to the EP Transfer Ring
|
||||
* @param ep_state State of the End Point
|
||||
* @return error code in case of invalid ep_state, 0 on success
|
||||
*/
|
||||
static int prepare_ring(struct xhci_ctrl *ctrl, struct xhci_ring *ep_ring,
|
||||
u32 ep_state)
|
||||
{
|
||||
union xhci_trb *next = ep_ring->enqueue;
|
||||
|
||||
/* Make sure the endpoint has been added to xHC schedule */
|
||||
switch (ep_state) {
|
||||
case EP_STATE_DISABLED:
|
||||
/*
|
||||
* USB core changed config/interfaces without notifying us,
|
||||
* or hardware is reporting the wrong state.
|
||||
*/
|
||||
puts("WARN urb submitted to disabled ep\n");
|
||||
return -ENOENT;
|
||||
case EP_STATE_ERROR:
|
||||
puts("WARN waiting for error on ep to be cleared\n");
|
||||
return -EINVAL;
|
||||
case EP_STATE_HALTED:
|
||||
puts("WARN halted endpoint, queueing URB anyway.\n");
|
||||
case EP_STATE_STOPPED:
|
||||
case EP_STATE_RUNNING:
|
||||
debug("EP STATE RUNNING.\n");
|
||||
break;
|
||||
default:
|
||||
puts("ERROR unknown endpoint state for ep\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
while (last_trb(ctrl, ep_ring, ep_ring->enq_seg, next)) {
|
||||
/*
|
||||
* If we're not dealing with 0.95 hardware or isoc rings
|
||||
* on AMD 0.96 host, clear the chain bit.
|
||||
*/
|
||||
next->link.control &= cpu_to_le32(~TRB_CHAIN);
|
||||
|
||||
next->link.control ^= cpu_to_le32(TRB_CYCLE);
|
||||
|
||||
xhci_flush_cache((uintptr_t)next, sizeof(union xhci_trb));
|
||||
|
||||
/* Toggle the cycle bit after the last ring segment. */
|
||||
if (last_trb_on_last_seg(ctrl, ep_ring,
|
||||
ep_ring->enq_seg, next))
|
||||
ep_ring->cycle_state = (ep_ring->cycle_state ? 0 : 1);
|
||||
ep_ring->enq_seg = ep_ring->enq_seg->next;
|
||||
ep_ring->enqueue = ep_ring->enq_seg->trbs;
|
||||
next = ep_ring->enqueue;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic function for queueing a command TRB on the command ring.
|
||||
* Check to make sure there's room on the command ring for one command TRB.
|
||||
*
|
||||
* @param ctrl Host controller data structure
|
||||
* @param ptr Pointer address to write in the first two fields (opt.)
|
||||
* @param slot_id Slot ID to encode in the flags field (opt.)
|
||||
* @param ep_index Endpoint index to encode in the flags field (opt.)
|
||||
* @param cmd Command type to enqueue
|
||||
* @return none
|
||||
*/
|
||||
void xhci_queue_command(struct xhci_ctrl *ctrl, u8 *ptr, u32 slot_id,
|
||||
u32 ep_index, trb_type cmd)
|
||||
{
|
||||
u32 fields[4];
|
||||
u64 val_64 = (uintptr_t)ptr;
|
||||
|
||||
BUG_ON(prepare_ring(ctrl, ctrl->cmd_ring, EP_STATE_RUNNING));
|
||||
|
||||
fields[0] = lower_32_bits(val_64);
|
||||
fields[1] = upper_32_bits(val_64);
|
||||
fields[2] = 0;
|
||||
fields[3] = TRB_TYPE(cmd) | EP_ID_FOR_TRB(ep_index) |
|
||||
SLOT_ID_FOR_TRB(slot_id) | ctrl->cmd_ring->cycle_state;
|
||||
|
||||
queue_trb(ctrl, ctrl->cmd_ring, false, fields);
|
||||
|
||||
/* Ring the command ring doorbell */
|
||||
xhci_writel(&ctrl->dba->doorbell[0], DB_VALUE_HOST);
|
||||
}
|
||||
|
||||
/**
|
||||
* The TD size is the number of bytes remaining in the TD (including this TRB),
|
||||
* right shifted by 10.
|
||||
* It must fit in bits 21:17, so it can't be bigger than 31.
|
||||
*
|
||||
* @param remainder remaining packets to be sent
|
||||
* @return remainder if remainder is less than max else max
|
||||
*/
|
||||
static u32 xhci_td_remainder(unsigned int remainder)
|
||||
{
|
||||
u32 max = (1 << (21 - 17 + 1)) - 1;
|
||||
|
||||
if ((remainder >> 10) >= max)
|
||||
return max << 17;
|
||||
else
|
||||
return (remainder >> 10) << 17;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds out the remanining packets to be sent
|
||||
*
|
||||
* @param running_total total size sent so far
|
||||
* @param trb_buff_len length of the TRB Buffer
|
||||
* @param total_packet_count total packet count
|
||||
* @param maxpacketsize max packet size of current pipe
|
||||
* @param num_trbs_left number of TRBs left to be processed
|
||||
* @return 0 if running_total or trb_buff_len is 0, else remainder
|
||||
*/
|
||||
static u32 xhci_v1_0_td_remainder(int running_total,
|
||||
int trb_buff_len,
|
||||
unsigned int total_packet_count,
|
||||
int maxpacketsize,
|
||||
unsigned int num_trbs_left)
|
||||
{
|
||||
int packets_transferred;
|
||||
|
||||
/* One TRB with a zero-length data packet. */
|
||||
if (num_trbs_left == 0 || (running_total == 0 && trb_buff_len == 0))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* All the TRB queueing functions don't count the current TRB in
|
||||
* running_total.
|
||||
*/
|
||||
packets_transferred = (running_total + trb_buff_len) / maxpacketsize;
|
||||
|
||||
if ((total_packet_count - packets_transferred) > 31)
|
||||
return 31 << 17;
|
||||
return (total_packet_count - packets_transferred) << 17;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ring the doorbell of the End Point
|
||||
*
|
||||
* @param udev pointer to the USB device structure
|
||||
* @param ep_index index of the endpoint
|
||||
* @param start_cycle cycle flag of the first TRB
|
||||
* @param start_trb pionter to the first TRB
|
||||
* @return none
|
||||
*/
|
||||
static void giveback_first_trb(struct usb_device *udev, int ep_index,
|
||||
int start_cycle,
|
||||
struct xhci_generic_trb *start_trb)
|
||||
{
|
||||
struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
|
||||
|
||||
/*
|
||||
* Pass all the TRBs to the hardware at once and make sure this write
|
||||
* isn't reordered.
|
||||
*/
|
||||
if (start_cycle)
|
||||
start_trb->field[3] |= cpu_to_le32(start_cycle);
|
||||
else
|
||||
start_trb->field[3] &= cpu_to_le32(~TRB_CYCLE);
|
||||
|
||||
xhci_flush_cache((uintptr_t)start_trb, sizeof(struct xhci_generic_trb));
|
||||
|
||||
/* Ringing EP doorbell here */
|
||||
xhci_writel(&ctrl->dba->doorbell[udev->slot_id],
|
||||
DB_VALUE(ep_index, 0));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**** POLLING mechanism for XHCI ****/
|
||||
|
||||
/**
|
||||
* Finalizes a handled event TRB by advancing our dequeue pointer and giving
|
||||
* the TRB back to the hardware for recycling. Must call this exactly once at
|
||||
* the end of each event handler, and not touch the TRB again afterwards.
|
||||
*
|
||||
* @param ctrl Host controller data structure
|
||||
* @return none
|
||||
*/
|
||||
void xhci_acknowledge_event(struct xhci_ctrl *ctrl)
|
||||
{
|
||||
/* Advance our dequeue pointer to the next event */
|
||||
inc_deq(ctrl, ctrl->event_ring);
|
||||
|
||||
/* Inform the hardware */
|
||||
xhci_writeq(&ctrl->ir_set->erst_dequeue,
|
||||
(uintptr_t)ctrl->event_ring->dequeue | ERST_EHB);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there is a new event to handle on the event ring.
|
||||
*
|
||||
* @param ctrl Host controller data structure
|
||||
* @return 0 if failure else 1 on success
|
||||
*/
|
||||
static int event_ready(struct xhci_ctrl *ctrl)
|
||||
{
|
||||
union xhci_trb *event;
|
||||
|
||||
xhci_inval_cache((uintptr_t)ctrl->event_ring->dequeue,
|
||||
sizeof(union xhci_trb));
|
||||
|
||||
event = ctrl->event_ring->dequeue;
|
||||
|
||||
/* Does the HC or OS own the TRB? */
|
||||
if ((le32_to_cpu(event->event_cmd.flags) & TRB_CYCLE) !=
|
||||
ctrl->event_ring->cycle_state)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a specific type of event and returns it. Discards unexpected
|
||||
* events. Caller *must* call xhci_acknowledge_event() after it is finished
|
||||
* processing the event, and must not access the returned pointer afterwards.
|
||||
*
|
||||
* @param ctrl Host controller data structure
|
||||
* @param expected TRB type expected from Event TRB
|
||||
* @return pointer to event trb
|
||||
*/
|
||||
union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected)
|
||||
{
|
||||
trb_type type;
|
||||
unsigned long ts = get_timer(0);
|
||||
|
||||
do {
|
||||
union xhci_trb *event = ctrl->event_ring->dequeue;
|
||||
|
||||
if (!event_ready(ctrl))
|
||||
continue;
|
||||
|
||||
type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->event_cmd.flags));
|
||||
if (type == expected)
|
||||
return event;
|
||||
|
||||
if (type == TRB_PORT_STATUS)
|
||||
/* TODO: remove this once enumeration has been reworked */
|
||||
/*
|
||||
* Port status change events always have a
|
||||
* successful completion code
|
||||
*/
|
||||
BUG_ON(GET_COMP_CODE(
|
||||
le32_to_cpu(event->generic.field[2])) !=
|
||||
COMP_SUCCESS);
|
||||
else
|
||||
printf("Unexpected XHCI event TRB, skipping... "
|
||||
"(%08x %08x %08x %08x)\n",
|
||||
le32_to_cpu(event->generic.field[0]),
|
||||
le32_to_cpu(event->generic.field[1]),
|
||||
le32_to_cpu(event->generic.field[2]),
|
||||
le32_to_cpu(event->generic.field[3]));
|
||||
|
||||
xhci_acknowledge_event(ctrl);
|
||||
} while (get_timer(ts) < XHCI_TIMEOUT);
|
||||
|
||||
if (expected == TRB_TRANSFER)
|
||||
return NULL;
|
||||
|
||||
printf("XHCI timeout on event type %d... cannot recover.\n", expected);
|
||||
BUG();
|
||||
}
|
||||
|
||||
/*
|
||||
* Stops transfer processing for an endpoint and throws away all unprocessed
|
||||
* TRBs by setting the xHC's dequeue pointer to our enqueue pointer. The next
|
||||
* xhci_bulk_tx/xhci_ctrl_tx on this enpoint will add new transfers there and
|
||||
* ring the doorbell, causing this endpoint to start working again.
|
||||
* (Careful: This will BUG() when there was no transfer in progress. Shouldn't
|
||||
* happen in practice for current uses and is too complicated to fix right now.)
|
||||
*/
|
||||
static void abort_td(struct usb_device *udev, int ep_index)
|
||||
{
|
||||
struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
|
||||
struct xhci_ring *ring = ctrl->devs[udev->slot_id]->eps[ep_index].ring;
|
||||
union xhci_trb *event;
|
||||
u32 field;
|
||||
|
||||
xhci_queue_command(ctrl, NULL, udev->slot_id, ep_index, TRB_STOP_RING);
|
||||
|
||||
event = xhci_wait_for_event(ctrl, TRB_TRANSFER);
|
||||
field = le32_to_cpu(event->trans_event.flags);
|
||||
BUG_ON(TRB_TO_SLOT_ID(field) != udev->slot_id);
|
||||
BUG_ON(TRB_TO_EP_INDEX(field) != ep_index);
|
||||
BUG_ON(GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len
|
||||
!= COMP_STOP)));
|
||||
xhci_acknowledge_event(ctrl);
|
||||
|
||||
event = xhci_wait_for_event(ctrl, TRB_COMPLETION);
|
||||
BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags))
|
||||
!= udev->slot_id || GET_COMP_CODE(le32_to_cpu(
|
||||
event->event_cmd.status)) != COMP_SUCCESS);
|
||||
xhci_acknowledge_event(ctrl);
|
||||
|
||||
xhci_queue_command(ctrl, (void *)((uintptr_t)ring->enqueue |
|
||||
ring->cycle_state), udev->slot_id, ep_index, TRB_SET_DEQ);
|
||||
event = xhci_wait_for_event(ctrl, TRB_COMPLETION);
|
||||
BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags))
|
||||
!= udev->slot_id || GET_COMP_CODE(le32_to_cpu(
|
||||
event->event_cmd.status)) != COMP_SUCCESS);
|
||||
xhci_acknowledge_event(ctrl);
|
||||
}
|
||||
|
||||
static void record_transfer_result(struct usb_device *udev,
|
||||
union xhci_trb *event, int length)
|
||||
{
|
||||
udev->act_len = min(length, length -
|
||||
(int)EVENT_TRB_LEN(le32_to_cpu(event->trans_event.transfer_len)));
|
||||
|
||||
switch (GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len))) {
|
||||
case COMP_SUCCESS:
|
||||
BUG_ON(udev->act_len != length);
|
||||
/* fallthrough */
|
||||
case COMP_SHORT_TX:
|
||||
udev->status = 0;
|
||||
break;
|
||||
case COMP_STALL:
|
||||
udev->status = USB_ST_STALLED;
|
||||
break;
|
||||
case COMP_DB_ERR:
|
||||
case COMP_TRB_ERR:
|
||||
udev->status = USB_ST_BUF_ERR;
|
||||
break;
|
||||
case COMP_BABBLE:
|
||||
udev->status = USB_ST_BABBLE_DET;
|
||||
break;
|
||||
default:
|
||||
udev->status = 0x80; /* USB_ST_TOO_LAZY_TO_MAKE_A_NEW_MACRO */
|
||||
}
|
||||
}
|
||||
|
||||
/**** Bulk and Control transfer methods ****/
|
||||
/**
|
||||
* Queues up the BULK Request
|
||||
*
|
||||
* @param udev pointer to the USB device structure
|
||||
* @param pipe contains the DIR_IN or OUT , devnum
|
||||
* @param length length of the buffer
|
||||
* @param buffer buffer to be read/written based on the request
|
||||
* @return returns 0 if successful else -1 on failure
|
||||
*/
|
||||
int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe,
|
||||
int length, void *buffer)
|
||||
{
|
||||
int num_trbs = 0;
|
||||
struct xhci_generic_trb *start_trb;
|
||||
bool first_trb = 0;
|
||||
int start_cycle;
|
||||
u32 field = 0;
|
||||
u32 length_field = 0;
|
||||
struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
|
||||
int slot_id = udev->slot_id;
|
||||
int ep_index;
|
||||
struct xhci_virt_device *virt_dev;
|
||||
struct xhci_ep_ctx *ep_ctx;
|
||||
struct xhci_ring *ring; /* EP transfer ring */
|
||||
union xhci_trb *event;
|
||||
|
||||
int running_total, trb_buff_len;
|
||||
unsigned int total_packet_count;
|
||||
int maxpacketsize;
|
||||
u64 addr;
|
||||
int ret;
|
||||
u32 trb_fields[4];
|
||||
u64 val_64 = (uintptr_t)buffer;
|
||||
|
||||
debug("dev=%p, pipe=%lx, buffer=%p, length=%d\n",
|
||||
udev, pipe, buffer, length);
|
||||
|
||||
ep_index = usb_pipe_ep_index(pipe);
|
||||
virt_dev = ctrl->devs[slot_id];
|
||||
|
||||
xhci_inval_cache((uintptr_t)virt_dev->out_ctx->bytes,
|
||||
virt_dev->out_ctx->size);
|
||||
|
||||
ep_ctx = xhci_get_ep_ctx(ctrl, virt_dev->out_ctx, ep_index);
|
||||
|
||||
ring = virt_dev->eps[ep_index].ring;
|
||||
/*
|
||||
* How much data is (potentially) left before the 64KB boundary?
|
||||
* XHCI Spec puts restriction( TABLE 49 and 6.4.1 section of XHCI Spec)
|
||||
* that the buffer should not span 64KB boundary. if so
|
||||
* we send request in more than 1 TRB by chaining them.
|
||||
*/
|
||||
running_total = TRB_MAX_BUFF_SIZE -
|
||||
(lower_32_bits(val_64) & (TRB_MAX_BUFF_SIZE - 1));
|
||||
trb_buff_len = running_total;
|
||||
running_total &= TRB_MAX_BUFF_SIZE - 1;
|
||||
|
||||
/*
|
||||
* If there's some data on this 64KB chunk, or we have to send a
|
||||
* zero-length transfer, we need at least one TRB
|
||||
*/
|
||||
if (running_total != 0 || length == 0)
|
||||
num_trbs++;
|
||||
|
||||
/* How many more 64KB chunks to transfer, how many more TRBs? */
|
||||
while (running_total < length) {
|
||||
num_trbs++;
|
||||
running_total += TRB_MAX_BUFF_SIZE;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: Calling routine prepare_ring() called in place of
|
||||
* prepare_trasfer() as there in 'Linux' since we are not
|
||||
* maintaining multiple TDs/transfer at the same time.
|
||||
*/
|
||||
ret = prepare_ring(ctrl, ring,
|
||||
le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Don't give the first TRB to the hardware (by toggling the cycle bit)
|
||||
* until we've finished creating all the other TRBs. The ring's cycle
|
||||
* state may change as we enqueue the other TRBs, so save it too.
|
||||
*/
|
||||
start_trb = &ring->enqueue->generic;
|
||||
start_cycle = ring->cycle_state;
|
||||
|
||||
running_total = 0;
|
||||
maxpacketsize = usb_maxpacket(udev, pipe);
|
||||
|
||||
total_packet_count = DIV_ROUND_UP(length, maxpacketsize);
|
||||
|
||||
/* How much data is in the first TRB? */
|
||||
/*
|
||||
* How much data is (potentially) left before the 64KB boundary?
|
||||
* XHCI Spec puts restriction( TABLE 49 and 6.4.1 section of XHCI Spec)
|
||||
* that the buffer should not span 64KB boundary. if so
|
||||
* we send request in more than 1 TRB by chaining them.
|
||||
*/
|
||||
addr = val_64;
|
||||
|
||||
if (trb_buff_len > length)
|
||||
trb_buff_len = length;
|
||||
|
||||
first_trb = true;
|
||||
|
||||
/* flush the buffer before use */
|
||||
xhci_flush_cache((uintptr_t)buffer, length);
|
||||
|
||||
/* Queue the first TRB, even if it's zero-length */
|
||||
do {
|
||||
u32 remainder = 0;
|
||||
field = 0;
|
||||
/* Don't change the cycle bit of the first TRB until later */
|
||||
if (first_trb) {
|
||||
first_trb = false;
|
||||
if (start_cycle == 0)
|
||||
field |= TRB_CYCLE;
|
||||
} else {
|
||||
field |= ring->cycle_state;
|
||||
}
|
||||
|
||||
/*
|
||||
* Chain all the TRBs together; clear the chain bit in the last
|
||||
* TRB to indicate it's the last TRB in the chain.
|
||||
*/
|
||||
if (num_trbs > 1)
|
||||
field |= TRB_CHAIN;
|
||||
else
|
||||
field |= TRB_IOC;
|
||||
|
||||
/* Only set interrupt on short packet for IN endpoints */
|
||||
if (usb_pipein(pipe))
|
||||
field |= TRB_ISP;
|
||||
|
||||
/* Set the TRB length, TD size, and interrupter fields. */
|
||||
if (HC_VERSION(xhci_readl(&ctrl->hccr->cr_capbase)) < 0x100)
|
||||
remainder = xhci_td_remainder(length - running_total);
|
||||
else
|
||||
remainder = xhci_v1_0_td_remainder(running_total,
|
||||
trb_buff_len,
|
||||
total_packet_count,
|
||||
maxpacketsize,
|
||||
num_trbs - 1);
|
||||
|
||||
length_field = ((trb_buff_len & TRB_LEN_MASK) |
|
||||
remainder |
|
||||
((0 & TRB_INTR_TARGET_MASK) <<
|
||||
TRB_INTR_TARGET_SHIFT));
|
||||
|
||||
trb_fields[0] = lower_32_bits(addr);
|
||||
trb_fields[1] = upper_32_bits(addr);
|
||||
trb_fields[2] = length_field;
|
||||
trb_fields[3] = field | (TRB_NORMAL << TRB_TYPE_SHIFT);
|
||||
|
||||
queue_trb(ctrl, ring, (num_trbs > 1), trb_fields);
|
||||
|
||||
--num_trbs;
|
||||
|
||||
running_total += trb_buff_len;
|
||||
|
||||
/* Calculate length for next transfer */
|
||||
addr += trb_buff_len;
|
||||
trb_buff_len = min((length - running_total), TRB_MAX_BUFF_SIZE);
|
||||
} while (running_total < length);
|
||||
|
||||
giveback_first_trb(udev, ep_index, start_cycle, start_trb);
|
||||
|
||||
event = xhci_wait_for_event(ctrl, TRB_TRANSFER);
|
||||
if (!event) {
|
||||
debug("XHCI bulk transfer timed out, aborting...\n");
|
||||
abort_td(udev, ep_index);
|
||||
udev->status = USB_ST_NAK_REC; /* closest thing to a timeout */
|
||||
udev->act_len = 0;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
field = le32_to_cpu(event->trans_event.flags);
|
||||
|
||||
BUG_ON(TRB_TO_SLOT_ID(field) != slot_id);
|
||||
BUG_ON(TRB_TO_EP_INDEX(field) != ep_index);
|
||||
BUG_ON(*(void **)(uintptr_t)le64_to_cpu(event->trans_event.buffer) -
|
||||
buffer > (size_t)length);
|
||||
|
||||
record_transfer_result(udev, event, length);
|
||||
xhci_acknowledge_event(ctrl);
|
||||
xhci_inval_cache((uintptr_t)buffer, length);
|
||||
|
||||
return (udev->status != USB_ST_NOT_PROC) ? 0 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues up the Control Transfer Request
|
||||
*
|
||||
* @param udev pointer to the USB device structure
|
||||
* @param pipe contains the DIR_IN or OUT , devnum
|
||||
* @param req request type
|
||||
* @param length length of the buffer
|
||||
* @param buffer buffer to be read/written based on the request
|
||||
* @return returns 0 if successful else error code on failure
|
||||
*/
|
||||
int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe,
|
||||
struct devrequest *req, int length,
|
||||
void *buffer)
|
||||
{
|
||||
int ret;
|
||||
int start_cycle;
|
||||
int num_trbs;
|
||||
u32 field;
|
||||
u32 length_field;
|
||||
u64 buf_64 = 0;
|
||||
struct xhci_generic_trb *start_trb;
|
||||
struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
|
||||
int slot_id = udev->slot_id;
|
||||
int ep_index;
|
||||
u32 trb_fields[4];
|
||||
struct xhci_virt_device *virt_dev = ctrl->devs[slot_id];
|
||||
struct xhci_ring *ep_ring;
|
||||
union xhci_trb *event;
|
||||
|
||||
debug("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u\n",
|
||||
req->request, req->request,
|
||||
req->requesttype, req->requesttype,
|
||||
le16_to_cpu(req->value), le16_to_cpu(req->value),
|
||||
le16_to_cpu(req->index));
|
||||
|
||||
ep_index = usb_pipe_ep_index(pipe);
|
||||
|
||||
ep_ring = virt_dev->eps[ep_index].ring;
|
||||
|
||||
/*
|
||||
* Check to see if the max packet size for the default control
|
||||
* endpoint changed during FS device enumeration
|
||||
*/
|
||||
if (udev->speed == USB_SPEED_FULL) {
|
||||
ret = xhci_check_maxpacket(udev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
xhci_inval_cache((uintptr_t)virt_dev->out_ctx->bytes,
|
||||
virt_dev->out_ctx->size);
|
||||
|
||||
struct xhci_ep_ctx *ep_ctx = NULL;
|
||||
ep_ctx = xhci_get_ep_ctx(ctrl, virt_dev->out_ctx, ep_index);
|
||||
|
||||
/* 1 TRB for setup, 1 for status */
|
||||
num_trbs = 2;
|
||||
/*
|
||||
* Don't need to check if we need additional event data and normal TRBs,
|
||||
* since data in control transfers will never get bigger than 16MB
|
||||
* XXX: can we get a buffer that crosses 64KB boundaries?
|
||||
*/
|
||||
|
||||
if (length > 0)
|
||||
num_trbs++;
|
||||
/*
|
||||
* XXX: Calling routine prepare_ring() called in place of
|
||||
* prepare_trasfer() as there in 'Linux' since we are not
|
||||
* maintaining multiple TDs/transfer at the same time.
|
||||
*/
|
||||
ret = prepare_ring(ctrl, ep_ring,
|
||||
le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Don't give the first TRB to the hardware (by toggling the cycle bit)
|
||||
* until we've finished creating all the other TRBs. The ring's cycle
|
||||
* state may change as we enqueue the other TRBs, so save it too.
|
||||
*/
|
||||
start_trb = &ep_ring->enqueue->generic;
|
||||
start_cycle = ep_ring->cycle_state;
|
||||
|
||||
debug("start_trb %p, start_cycle %d\n", start_trb, start_cycle);
|
||||
|
||||
/* Queue setup TRB - see section 6.4.1.2.1 */
|
||||
/* FIXME better way to translate setup_packet into two u32 fields? */
|
||||
field = 0;
|
||||
field |= TRB_IDT | (TRB_SETUP << TRB_TYPE_SHIFT);
|
||||
if (start_cycle == 0)
|
||||
field |= 0x1;
|
||||
|
||||
/* xHCI 1.0 6.4.1.2.1: Transfer Type field */
|
||||
if (HC_VERSION(xhci_readl(&ctrl->hccr->cr_capbase)) == 0x100) {
|
||||
if (length > 0) {
|
||||
if (req->requesttype & USB_DIR_IN)
|
||||
field |= (TRB_DATA_IN << TRB_TX_TYPE_SHIFT);
|
||||
else
|
||||
field |= (TRB_DATA_OUT << TRB_TX_TYPE_SHIFT);
|
||||
}
|
||||
}
|
||||
|
||||
debug("req->requesttype = %d, req->request = %d,"
|
||||
"le16_to_cpu(req->value) = %d,"
|
||||
"le16_to_cpu(req->index) = %d,"
|
||||
"le16_to_cpu(req->length) = %d\n",
|
||||
req->requesttype, req->request, le16_to_cpu(req->value),
|
||||
le16_to_cpu(req->index), le16_to_cpu(req->length));
|
||||
|
||||
trb_fields[0] = req->requesttype | req->request << 8 |
|
||||
le16_to_cpu(req->value) << 16;
|
||||
trb_fields[1] = le16_to_cpu(req->index) |
|
||||
le16_to_cpu(req->length) << 16;
|
||||
/* TRB_LEN | (TRB_INTR_TARGET) */
|
||||
trb_fields[2] = (8 | ((0 & TRB_INTR_TARGET_MASK) <<
|
||||
TRB_INTR_TARGET_SHIFT));
|
||||
/* Immediate data in pointer */
|
||||
trb_fields[3] = field;
|
||||
queue_trb(ctrl, ep_ring, true, trb_fields);
|
||||
|
||||
/* Re-initializing field to zero */
|
||||
field = 0;
|
||||
/* If there's data, queue data TRBs */
|
||||
/* Only set interrupt on short packet for IN endpoints */
|
||||
if (usb_pipein(pipe))
|
||||
field = TRB_ISP | (TRB_DATA << TRB_TYPE_SHIFT);
|
||||
else
|
||||
field = (TRB_DATA << TRB_TYPE_SHIFT);
|
||||
|
||||
length_field = (length & TRB_LEN_MASK) | xhci_td_remainder(length) |
|
||||
((0 & TRB_INTR_TARGET_MASK) << TRB_INTR_TARGET_SHIFT);
|
||||
debug("length_field = %d, length = %d,"
|
||||
"xhci_td_remainder(length) = %d , TRB_INTR_TARGET(0) = %d\n",
|
||||
length_field, (length & TRB_LEN_MASK),
|
||||
xhci_td_remainder(length), 0);
|
||||
|
||||
if (length > 0) {
|
||||
if (req->requesttype & USB_DIR_IN)
|
||||
field |= TRB_DIR_IN;
|
||||
buf_64 = (uintptr_t)buffer;
|
||||
|
||||
trb_fields[0] = lower_32_bits(buf_64);
|
||||
trb_fields[1] = upper_32_bits(buf_64);
|
||||
trb_fields[2] = length_field;
|
||||
trb_fields[3] = field | ep_ring->cycle_state;
|
||||
|
||||
xhci_flush_cache((uintptr_t)buffer, length);
|
||||
queue_trb(ctrl, ep_ring, true, trb_fields);
|
||||
}
|
||||
|
||||
/*
|
||||
* Queue status TRB -
|
||||
* see Table 7 and sections 4.11.2.2 and 6.4.1.2.3
|
||||
*/
|
||||
|
||||
/* If the device sent data, the status stage is an OUT transfer */
|
||||
field = 0;
|
||||
if (length > 0 && req->requesttype & USB_DIR_IN)
|
||||
field = 0;
|
||||
else
|
||||
field = TRB_DIR_IN;
|
||||
|
||||
trb_fields[0] = 0;
|
||||
trb_fields[1] = 0;
|
||||
trb_fields[2] = ((0 & TRB_INTR_TARGET_MASK) << TRB_INTR_TARGET_SHIFT);
|
||||
/* Event on completion */
|
||||
trb_fields[3] = field | TRB_IOC |
|
||||
(TRB_STATUS << TRB_TYPE_SHIFT) |
|
||||
ep_ring->cycle_state;
|
||||
|
||||
queue_trb(ctrl, ep_ring, false, trb_fields);
|
||||
|
||||
giveback_first_trb(udev, ep_index, start_cycle, start_trb);
|
||||
|
||||
event = xhci_wait_for_event(ctrl, TRB_TRANSFER);
|
||||
if (!event)
|
||||
goto abort;
|
||||
field = le32_to_cpu(event->trans_event.flags);
|
||||
|
||||
BUG_ON(TRB_TO_SLOT_ID(field) != slot_id);
|
||||
BUG_ON(TRB_TO_EP_INDEX(field) != ep_index);
|
||||
|
||||
record_transfer_result(udev, event, length);
|
||||
xhci_acknowledge_event(ctrl);
|
||||
|
||||
/* Invalidate buffer to make it available to usb-core */
|
||||
if (length > 0)
|
||||
xhci_inval_cache((uintptr_t)buffer, length);
|
||||
|
||||
if (GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len))
|
||||
== COMP_SHORT_TX) {
|
||||
/* Short data stage, clear up additional status stage event */
|
||||
event = xhci_wait_for_event(ctrl, TRB_TRANSFER);
|
||||
if (!event)
|
||||
goto abort;
|
||||
BUG_ON(TRB_TO_SLOT_ID(field) != slot_id);
|
||||
BUG_ON(TRB_TO_EP_INDEX(field) != ep_index);
|
||||
xhci_acknowledge_event(ctrl);
|
||||
}
|
||||
|
||||
return (udev->status != USB_ST_NOT_PROC) ? 0 : -1;
|
||||
|
||||
abort:
|
||||
debug("XHCI control transfer timed out, aborting...\n");
|
||||
abort_td(udev, ep_index);
|
||||
udev->status = USB_ST_NAK_REC;
|
||||
udev->act_len = 0;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
85
u-boot/drivers/usb/host/xhci-uniphier.c
Normal file
85
u-boot/drivers/usb/host/xhci-uniphier.c
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <usb.h>
|
||||
#include <fdtdec.h>
|
||||
#include "xhci.h"
|
||||
|
||||
static int get_uniphier_xhci_base(int index, struct xhci_hccr **base)
|
||||
{
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
int node_list[2];
|
||||
fdt_addr_t addr;
|
||||
int count;
|
||||
|
||||
count = fdtdec_find_aliases_for_id(gd->fdt_blob, "usb",
|
||||
COMPAT_SOCIONEXT_XHCI, node_list,
|
||||
ARRAY_SIZE(node_list));
|
||||
|
||||
if (index >= count)
|
||||
return -ENODEV;
|
||||
|
||||
addr = fdtdec_get_addr(gd->fdt_blob, node_list[index], "reg");
|
||||
if (addr == FDT_ADDR_T_NONE)
|
||||
return -ENODEV;
|
||||
|
||||
*base = (struct xhci_hccr *)addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define USB3_RST_CTRL 0x00100040
|
||||
#define IOMMU_RST_N (1 << 5)
|
||||
#define LINK_RST_N (1 << 4)
|
||||
|
||||
static void uniphier_xhci_reset(void __iomem *base, int on)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
tmp = readl(base + USB3_RST_CTRL);
|
||||
|
||||
if (on)
|
||||
tmp &= ~(IOMMU_RST_N | LINK_RST_N);
|
||||
else
|
||||
tmp |= IOMMU_RST_N | LINK_RST_N;
|
||||
|
||||
writel(tmp, base + USB3_RST_CTRL);
|
||||
}
|
||||
|
||||
int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor)
|
||||
{
|
||||
int ret;
|
||||
struct xhci_hccr *cr;
|
||||
struct xhci_hcor *or;
|
||||
|
||||
ret = get_uniphier_xhci_base(index, &cr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
uniphier_xhci_reset(cr, 0);
|
||||
|
||||
or = (void *)cr + HC_LENGTH(xhci_readl(&cr->cr_capbase));
|
||||
|
||||
*hccr = cr;
|
||||
*hcor = or;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xhci_hcd_stop(int index)
|
||||
{
|
||||
int ret;
|
||||
struct xhci_hccr *cr;
|
||||
|
||||
ret = get_uniphier_xhci_base(index, &cr);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
uniphier_xhci_reset(cr, 1);
|
||||
}
|
||||
126
u-boot/drivers/usb/host/xhci-zynqmp.c
Normal file
126
u-boot/drivers/usb/host/xhci-zynqmp.c
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2015 Xilinx, Inc.
|
||||
*
|
||||
* Zynq USB HOST xHCI Controller
|
||||
*
|
||||
* Author: Siva Durga Prasad Paladugu<sivadur@xilinx.com>
|
||||
*
|
||||
* This file was reused from Freescale USB xHCI
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <usb.h>
|
||||
#include <asm-generic/errno.h>
|
||||
#include <asm/arch-zynqmp/hardware.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/usb/dwc3.h>
|
||||
#include "xhci.h"
|
||||
|
||||
/* Declare global data pointer */
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/* Default to the ZYNQMP XHCI defines */
|
||||
#define USB3_PWRCTL_CLK_CMD_MASK 0x3FE000
|
||||
#define USB3_PWRCTL_CLK_FREQ_MASK 0xFFC
|
||||
#define USB3_PHY_PARTIAL_RX_POWERON BIT(6)
|
||||
#define USB3_PHY_RX_POWERON BIT(14)
|
||||
#define USB3_PHY_TX_POWERON BIT(15)
|
||||
#define USB3_PHY_TX_RX_POWERON (USB3_PHY_RX_POWERON | USB3_PHY_TX_POWERON)
|
||||
#define USB3_PWRCTL_CLK_CMD_SHIFT 14
|
||||
#define USB3_PWRCTL_CLK_FREQ_SHIFT 22
|
||||
|
||||
/* USBOTGSS_WRAPPER definitions */
|
||||
#define USBOTGSS_WRAPRESET BIT(17)
|
||||
#define USBOTGSS_DMADISABLE BIT(16)
|
||||
#define USBOTGSS_STANDBYMODE_NO_STANDBY BIT(4)
|
||||
#define USBOTGSS_STANDBYMODE_SMRT BIT(5)
|
||||
#define USBOTGSS_STANDBYMODE_SMRT_WKUP (0x3 << 4)
|
||||
#define USBOTGSS_IDLEMODE_NOIDLE BIT(2)
|
||||
#define USBOTGSS_IDLEMODE_SMRT BIT(3)
|
||||
#define USBOTGSS_IDLEMODE_SMRT_WKUP (0x3 << 2)
|
||||
|
||||
/* USBOTGSS_IRQENABLE_SET_0 bit */
|
||||
#define USBOTGSS_COREIRQ_EN BIT(1)
|
||||
|
||||
/* USBOTGSS_IRQENABLE_SET_1 bits */
|
||||
#define USBOTGSS_IRQ_SET_1_IDPULLUP_FALL_EN BIT(1)
|
||||
#define USBOTGSS_IRQ_SET_1_DISCHRGVBUS_FALL_EN BIT(3)
|
||||
#define USBOTGSS_IRQ_SET_1_CHRGVBUS_FALL_EN BIT(4)
|
||||
#define USBOTGSS_IRQ_SET_1_DRVVBUS_FALL_EN BIT(5)
|
||||
#define USBOTGSS_IRQ_SET_1_IDPULLUP_RISE_EN BIT(8)
|
||||
#define USBOTGSS_IRQ_SET_1_DISCHRGVBUS_RISE_EN BIT(11)
|
||||
#define USBOTGSS_IRQ_SET_1_CHRGVBUS_RISE_EN BIT(12)
|
||||
#define USBOTGSS_IRQ_SET_1_DRVVBUS_RISE_EN BIT(13)
|
||||
#define USBOTGSS_IRQ_SET_1_OEVT_EN BIT(16)
|
||||
#define USBOTGSS_IRQ_SET_1_DMADISABLECLR_EN BIT(17)
|
||||
|
||||
struct zynqmp_xhci {
|
||||
struct xhci_hccr *hcd;
|
||||
struct dwc3 *dwc3_reg;
|
||||
};
|
||||
|
||||
static struct zynqmp_xhci zynqmp_xhci;
|
||||
|
||||
unsigned long ctr_addr[] = CONFIG_ZYNQMP_XHCI_LIST;
|
||||
|
||||
static int zynqmp_xhci_core_init(struct zynqmp_xhci *zynqmp_xhci)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = dwc3_core_init(zynqmp_xhci->dwc3_reg);
|
||||
if (ret) {
|
||||
debug("%s:failed to initialize core\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* We are hard-coding DWC3 core to Host Mode */
|
||||
dwc3_set_mode(zynqmp_xhci->dwc3_reg, DWC3_GCTL_PRTCAP_HOST);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor)
|
||||
{
|
||||
struct zynqmp_xhci *ctx = &zynqmp_xhci;
|
||||
int ret = 0;
|
||||
uint32_t hclen;
|
||||
|
||||
if (index < 0 || index >= ARRAY_SIZE(ctr_addr))
|
||||
return -EINVAL;
|
||||
|
||||
ctx->hcd = (struct xhci_hccr *)ctr_addr[index];
|
||||
ctx->dwc3_reg = (struct dwc3 *)((void *)ctx->hcd + DWC3_REG_OFFSET);
|
||||
|
||||
ret = board_usb_init(index, USB_INIT_HOST);
|
||||
if (ret != 0) {
|
||||
puts("Failed to initialize board for USB\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = zynqmp_xhci_core_init(ctx);
|
||||
if (ret < 0) {
|
||||
puts("Failed to initialize xhci\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
*hccr = (struct xhci_hccr *)ctx->hcd;
|
||||
hclen = HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase));
|
||||
*hcor = (struct xhci_hcor *)((uintptr_t) *hccr + hclen);
|
||||
|
||||
debug("zynqmp-xhci: init hccr %p and hcor %p hc_length %d\n",
|
||||
*hccr, *hcor, hclen);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void xhci_hcd_stop(int index)
|
||||
{
|
||||
/*
|
||||
* Currently zynqmp socs do not support PHY shutdown from
|
||||
* sw. But this support may be added in future socs.
|
||||
*/
|
||||
|
||||
return;
|
||||
}
|
||||
1245
u-boot/drivers/usb/host/xhci.c
Normal file
1245
u-boot/drivers/usb/host/xhci.c
Normal file
File diff suppressed because it is too large
Load Diff
1289
u-boot/drivers/usb/host/xhci.h
Normal file
1289
u-boot/drivers/usb/host/xhci.h
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user