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:
2026-03-03 21:46:32 +02:00
parent fe3ba02c96
commit 68d74d3181
11967 changed files with 2221897 additions and 0 deletions

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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__ */

View 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;
}

View 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;
}

View 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,
};

View 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(&regs->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(&regs->otg.otgcsr));
else
spd = BMCSR_SPD(readl(&regs->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 *)&regs->usb.hccr;
hcor = (struct ehci_hcor *)&regs->usb.hcor;
if (ehci_is_fotg2xx(regs)) {
/* A-device bus reset */
/* ... Power off A-device */
setbits_le32(&regs->otg.otgcsr, OTGCSR_A_BUSDROP);
/* ... Drop vbus and bus traffic */
clrbits_le32(&regs->otg.otgcsr, OTGCSR_A_BUSREQ);
mdelay(1);
/* ... Power on A-device */
clrbits_le32(&regs->otg.otgcsr, OTGCSR_A_BUSDROP);
/* ... Drive vbus and bus traffic */
setbits_le32(&regs->otg.otgcsr, OTGCSR_A_BUSREQ);
mdelay(1);
/* Disable OTG & DEV interrupts, triggered at level-high */
writel(IMR_IRQLH | IMR_OTG | IMR_DEV, &regs->otg.imr);
/* Clear all interrupt status */
writel(ISR_HOST | ISR_OTG | ISR_DEV, &regs->otg.isr);
} else {
/* Interrupt=level-high */
setbits_le32(&regs->usb.bmcsr, BMCSR_IRQLH);
/* VBUS on */
clrbits_le32(&regs->usb.bmcsr, BMCSR_VBUS_OFF);
/* Disable all interrupts */
writel(0x00, &regs->usb.bmier);
writel(0x1f, &regs->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;
}

View 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);
}

View 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,
};

File diff suppressed because it is too large Load Diff

View 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 */

View 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 );
}

View 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,
};

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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(&reg, (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(&reg, 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(&reg, OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS);
else
setbits_le32(&reg, OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS);
if (is_ehci_phy_mode(usbhs_pdata->port_mode[1]))
clrbits_le32(&reg, OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS);
else
setbits_le32(&reg, OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS);
if (is_ehci_phy_mode(usbhs_pdata->port_mode[2]))
clrbits_le32(&reg, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS);
else
setbits_le32(&reg, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS);
} else if (rev == OMAP_USBHS_REV2) {
clrsetbits_le32(&reg, (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(&reg, OMAP_P1_MODE_HSIC);
if (is_ehci_hsic_mode(usbhs_pdata->port_mode[1]))
setbits_le32(&reg, OMAP_P2_MODE_HSIC);
} else if (rev == OMAP_USBHS_REV2_1) {
clrsetbits_le32(&reg,
(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(&reg, OMAP_P1_MODE_HSIC);
if (is_ehci_hsic_mode(usbhs_pdata->port_mode[1]))
setbits_le32(&reg, OMAP_P2_MODE_HSIC);
if (is_ehci_hsic_mode(usbhs_pdata->port_mode[2]))
setbits_le32(&reg, 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;
}

View 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 */

View 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;
}

View 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;
}

View 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;
}

View 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,
};

View 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,
};

View 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;
}

View 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;
}

View 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;
}

View 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 */

File diff suppressed because it is too large Load Diff

View 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

View 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) */

View 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();
}

View 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) */

View 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,
};

File diff suppressed because it is too large Load Diff

View 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();
}

File diff suppressed because it is too large Load Diff

View 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;
}

View 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,
};

View 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

View 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;
}

View 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__ */

View 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;
}

View 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 */

View 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,
};

View 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",
};

View 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();
}

View 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));
}

View 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,
};

View 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);
}

View 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");
}

View 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));
}

View 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);
}

View 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)
{
}

View 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;
}

View 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);
}

View 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;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff