Merge branch 'master' of git://git.denx.de/u-boot-usb
This commit is contained in:
commit
748bde608a
@ -59,6 +59,28 @@ void set_usbhost_phy_ctrl(unsigned int enable)
|
||||
exynos5_set_usbhost_phy_ctrl(enable);
|
||||
}
|
||||
|
||||
static void exynos5_set_usbdrd_phy_ctrl(unsigned int enable)
|
||||
{
|
||||
struct exynos5_power *power =
|
||||
(struct exynos5_power *)samsung_get_base_power();
|
||||
|
||||
if (enable) {
|
||||
/* Enabling USBDRD_PHY */
|
||||
setbits_le32(&power->usbdrd_phy_control,
|
||||
POWER_USB_DRD_PHY_CTRL_EN);
|
||||
} else {
|
||||
/* Disabling USBDRD_PHY */
|
||||
clrbits_le32(&power->usbdrd_phy_control,
|
||||
POWER_USB_DRD_PHY_CTRL_EN);
|
||||
}
|
||||
}
|
||||
|
||||
void set_usbdrd_phy_ctrl(unsigned int enable)
|
||||
{
|
||||
if (cpu_is_exynos5())
|
||||
exynos5_set_usbdrd_phy_ctrl(enable);
|
||||
}
|
||||
|
||||
static void exynos5_dp_phy_control(unsigned int enable)
|
||||
{
|
||||
unsigned int cfg;
|
||||
|
@ -339,7 +339,7 @@ void configure_mpu_dpll(void)
|
||||
debug("MPU DPLL locked\n");
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_EHCI_OMAP
|
||||
#if defined(CONFIG_USB_EHCI_OMAP) || defined(CONFIG_USB_XHCI_OMAP)
|
||||
static void setup_usb_dpll(void)
|
||||
{
|
||||
const struct dpll_params *params;
|
||||
@ -404,7 +404,7 @@ static void setup_dplls(void)
|
||||
/* MPU dpll */
|
||||
configure_mpu_dpll();
|
||||
|
||||
#ifdef CONFIG_USB_EHCI_OMAP
|
||||
#if defined(CONFIG_USB_EHCI_OMAP) || defined(CONFIG_USB_XHCI_OMAP)
|
||||
setup_usb_dpll();
|
||||
#endif
|
||||
params = get_ddr_dpll_params(*dplls_data);
|
||||
|
@ -295,6 +295,7 @@ struct prcm_regs const omap5_es1_prcm = {
|
||||
struct omap_sys_ctrl_regs const omap5_ctrl = {
|
||||
.control_status = 0x4A002134,
|
||||
.control_std_fuse_opp_vdd_mpu_2 = 0x4A0021B4,
|
||||
.control_phy_power_usb = 0x4A002370,
|
||||
.control_padconf_core_base = 0x4A002800,
|
||||
.control_paconf_global = 0x4A002DA0,
|
||||
.control_paconf_mode = 0x4A002DA4,
|
||||
@ -567,6 +568,7 @@ struct prcm_regs const omap5_es2_prcm = {
|
||||
.cm_div_m2_dpll_unipro = 0x4a0081d0,
|
||||
.cm_ssc_deltamstep_dpll_unipro = 0x4a0081e8,
|
||||
.cm_ssc_modfreqdiv_dpll_unipro = 0x4a0081ec,
|
||||
.cm_coreaon_usb_phy_core_clkctrl = 0x4A008640,
|
||||
.cm_coreaon_bandgap_clkctrl = 0x4a008648,
|
||||
.cm_coreaon_io_srcomp_clkctrl = 0x4a008650,
|
||||
|
||||
@ -698,6 +700,8 @@ struct prcm_regs const omap5_es2_prcm = {
|
||||
.cm_l3init_p1500_clkctrl = 0x4a009678,
|
||||
.cm_l3init_fsusb_clkctrl = 0x4a0096d0,
|
||||
.cm_l3init_ocp2scp1_clkctrl = 0x4a0096e0,
|
||||
.cm_l3init_ocp2scp3_clkctrl = 0x4a0096e8,
|
||||
.cm_l3init_usb_otg_ss_clkctrl = 0x4a0096f0,
|
||||
|
||||
/* prm irqstatus regs */
|
||||
.prm_irqstatus_mpu_2 = 0x4ae06014,
|
||||
@ -789,6 +793,7 @@ struct prcm_regs const dra7xx_prcm = {
|
||||
.cm_clkmode_dpll_dsp = 0x4a005234,
|
||||
.cm_shadow_freq_config1 = 0x4a005260,
|
||||
.cm_clkmode_dpll_gmac = 0x4a0052a8,
|
||||
.cm_coreaon_usb_phy2_core_clkctrl = 0x4a008688,
|
||||
|
||||
/* cm1.mpu */
|
||||
.cm_mpu_mpu_clkctrl = 0x4a005320,
|
||||
|
@ -158,6 +158,18 @@
|
||||
interrupts = <0 130 0>;
|
||||
};
|
||||
|
||||
xhci@12000000 {
|
||||
compatible = "samsung,exynos5250-xhci";
|
||||
reg = <0x12000000 0x10000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
phy {
|
||||
compatible = "samsung,exynos5250-usb3-phy";
|
||||
reg = <0x12100000 0x100>;
|
||||
};
|
||||
};
|
||||
|
||||
ehci@12110000 {
|
||||
compatible = "samsung,exynos-ehci";
|
||||
reg = <0x12110000 0x100>;
|
||||
|
@ -51,4 +51,14 @@
|
||||
/* RTC base address */
|
||||
#define RTC_BASE 0x44E3E000
|
||||
|
||||
/* USB Clock Control */
|
||||
#define PRM_PER_USB_OTG_SS0_CLKCTRL (CM_PER + 0x260)
|
||||
#define PRM_PER_USB_OTG_SS1_CLKCTRL (CM_PER + 0x268)
|
||||
#define USBOTGSSX_CLKCTRL_MODULE_EN (1 << 2)
|
||||
#define USBOTGSSX_CLKCTRL_OPTFCLKEN_REFCLK960 (1 << 8)
|
||||
|
||||
#define PRM_PER_USBPHYOCP2SCP0_CLKCTRL (CM_PER + 0x5b8)
|
||||
#define PRM_PER_USBPHYOCP2SCP1_CLKCTRL (CM_PER + 0x5c0)
|
||||
#define USBPHYOCPSCP_MODULE_EN (1 << 2)
|
||||
|
||||
#endif /* __AM43XX_HARDWARE_AM43XX_H */
|
||||
|
@ -51,6 +51,8 @@
|
||||
#define EXYNOS4_ACE_SFR_BASE DEVICE_NOT_AVAILABLE
|
||||
#define EXYNOS4_DMC_PHY_BASE DEVICE_NOT_AVAILABLE
|
||||
#define EXYNOS4_AUDIOSS_BASE DEVICE_NOT_AVAILABLE
|
||||
#define EXYNOS4_USB_HOST_XHCI_BASE DEVICE_NOT_AVAILABLE
|
||||
#define EXYNOS4_USB3PHY_BASE DEVICE_NOT_AVAILABLE
|
||||
|
||||
/* EXYNOS4X12 */
|
||||
#define EXYNOS4X12_GPIO_PART3_BASE 0x03860000
|
||||
@ -87,6 +89,8 @@
|
||||
#define EXYNOS4X12_ACE_SFR_BASE DEVICE_NOT_AVAILABLE
|
||||
#define EXYNOS4X12_DMC_PHY_BASE DEVICE_NOT_AVAILABLE
|
||||
#define EXYNOS4X12_AUDIOSS_BASE DEVICE_NOT_AVAILABLE
|
||||
#define EXYNOS4X12_USB_HOST_XHCI_BASE DEVICE_NOT_AVAILABLE
|
||||
#define EXYNOS4X12_USB3PHY_BASE DEVICE_NOT_AVAILABLE
|
||||
|
||||
/* EXYNOS5 Common*/
|
||||
#define EXYNOS5_I2C_SPACING 0x10000
|
||||
@ -106,6 +110,8 @@
|
||||
#define EXYNOS5_DMC_CTRL_BASE 0x10DD0000
|
||||
#define EXYNOS5_GPIO_PART1_BASE 0x11400000
|
||||
#define EXYNOS5_MIPI_DSIM_BASE 0x11D00000
|
||||
#define EXYNOS5_USB_HOST_XHCI_BASE 0x12000000
|
||||
#define EXYNOS5_USB3PHY_BASE 0x12100000
|
||||
#define EXYNOS5_USB_HOST_EHCI_BASE 0x12110000
|
||||
#define EXYNOS5_USBPHY_BASE 0x12130000
|
||||
#define EXYNOS5_USBOTG_BASE 0x12140000
|
||||
@ -220,7 +226,9 @@ SAMSUNG_BASE(swreset, SWRESET)
|
||||
SAMSUNG_BASE(timer, PWMTIMER_BASE)
|
||||
SAMSUNG_BASE(uart, UART_BASE)
|
||||
SAMSUNG_BASE(usb_phy, USBPHY_BASE)
|
||||
SAMSUNG_BASE(usb3_phy, USB3PHY_BASE)
|
||||
SAMSUNG_BASE(usb_ehci, USB_HOST_EHCI_BASE)
|
||||
SAMSUNG_BASE(usb_xhci, USB_HOST_XHCI_BASE)
|
||||
SAMSUNG_BASE(usb_otg, USBOTG_BASE)
|
||||
SAMSUNG_BASE(watchdog, WATCHDOG_BASE)
|
||||
SAMSUNG_BASE(power, POWER_BASE)
|
||||
|
@ -847,6 +847,11 @@ void set_hw_thermal_trip(void);
|
||||
#define POWER_USB_HOST_PHY_CTRL_EN (1 << 0)
|
||||
#define POWER_USB_HOST_PHY_CTRL_DISABLE (0 << 0)
|
||||
|
||||
void set_usbdrd_phy_ctrl(unsigned int enable);
|
||||
|
||||
#define POWER_USB_DRD_PHY_CTRL_EN (1 << 0)
|
||||
#define POWER_USB_DRD_PHY_CTRL_DISABLE (0 << 0)
|
||||
|
||||
void set_dp_phy_ctrl(unsigned int enable);
|
||||
|
||||
#define EXYNOS_DP_PHY_ENABLE (1 << 0)
|
||||
|
88
arch/arm/include/asm/arch-exynos/xhci-exynos.h
Normal file
88
arch/arm/include/asm/arch-exynos/xhci-exynos.h
Normal file
@ -0,0 +1,88 @@
|
||||
/* Copyright (c) 2012 Samsung Electronics Co. Ltd
|
||||
*
|
||||
* Exynos Phy register definitions
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#ifndef _ASM_ARCH_XHCI_EXYNOS_H_
|
||||
#define _ASM_ARCH_XHCI_EXYNOS_H_
|
||||
|
||||
/* Phy register MACRO definitions */
|
||||
|
||||
#define LINKSYSTEM_FLADJ_MASK (0x3f << 1)
|
||||
#define LINKSYSTEM_FLADJ(_x) ((_x) << 1)
|
||||
#define LINKSYSTEM_XHCI_VERSION_CONTROL (0x1 << 27)
|
||||
|
||||
#define PHYUTMI_OTGDISABLE (1 << 6)
|
||||
#define PHYUTMI_FORCESUSPEND (1 << 1)
|
||||
#define PHYUTMI_FORCESLEEP (1 << 0)
|
||||
|
||||
#define PHYCLKRST_SSC_REFCLKSEL_MASK (0xff << 23)
|
||||
#define PHYCLKRST_SSC_REFCLKSEL(_x) ((_x) << 23)
|
||||
|
||||
#define PHYCLKRST_SSC_RANGE_MASK (0x03 << 21)
|
||||
#define PHYCLKRST_SSC_RANGE(_x) ((_x) << 21)
|
||||
|
||||
#define PHYCLKRST_SSC_EN (0x1 << 20)
|
||||
#define PHYCLKRST_REF_SSP_EN (0x1 << 19)
|
||||
#define PHYCLKRST_REF_CLKDIV2 (0x1 << 18)
|
||||
|
||||
#define PHYCLKRST_MPLL_MULTIPLIER_MASK (0x7f << 11)
|
||||
#define PHYCLKRST_MPLL_MULTIPLIER_100MHZ_REF (0x19 << 11)
|
||||
#define PHYCLKRST_MPLL_MULTIPLIER_50M_REF (0x02 << 11)
|
||||
#define PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF (0x68 << 11)
|
||||
#define PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF (0x7d << 11)
|
||||
#define PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF (0x02 << 11)
|
||||
|
||||
#define PHYCLKRST_FSEL_MASK (0x3f << 5)
|
||||
#define PHYCLKRST_FSEL(_x) ((_x) << 5)
|
||||
#define PHYCLKRST_FSEL_PAD_100MHZ (0x27 << 5)
|
||||
#define PHYCLKRST_FSEL_PAD_24MHZ (0x2a << 5)
|
||||
#define PHYCLKRST_FSEL_PAD_20MHZ (0x31 << 5)
|
||||
#define PHYCLKRST_FSEL_PAD_19_2MHZ (0x38 << 5)
|
||||
|
||||
#define PHYCLKRST_RETENABLEN (0x1 << 4)
|
||||
|
||||
#define PHYCLKRST_REFCLKSEL_MASK (0x03 << 2)
|
||||
#define PHYCLKRST_REFCLKSEL_PAD_REFCLK (0x2 << 2)
|
||||
#define PHYCLKRST_REFCLKSEL_EXT_REFCLK (0x3 << 2)
|
||||
|
||||
#define PHYCLKRST_PORTRESET (0x1 << 1)
|
||||
#define PHYCLKRST_COMMONONN (0x1 << 0)
|
||||
|
||||
#define PHYPARAM0_REF_USE_PAD (0x1 << 31)
|
||||
#define PHYPARAM0_REF_LOSLEVEL_MASK (0x1f << 26)
|
||||
#define PHYPARAM0_REF_LOSLEVEL (0x9 << 26)
|
||||
|
||||
#define PHYPARAM1_PCS_TXDEEMPH_MASK (0x1f << 0)
|
||||
#define PHYPARAM1_PCS_TXDEEMPH (0x1c)
|
||||
|
||||
#define PHYTEST_POWERDOWN_SSP (0x1 << 3)
|
||||
#define PHYTEST_POWERDOWN_HSP (0x1 << 2)
|
||||
|
||||
#define PHYBATCHG_UTMI_CLKSEL (0x1 << 2)
|
||||
|
||||
#define FSEL_CLKSEL_24M (0x5)
|
||||
|
||||
/* XHCI PHY register structure */
|
||||
struct exynos_usb3_phy {
|
||||
unsigned int reserve1;
|
||||
unsigned int link_system;
|
||||
unsigned int phy_utmi;
|
||||
unsigned int phy_pipe;
|
||||
unsigned int phy_clk_rst;
|
||||
unsigned int phy_reg0;
|
||||
unsigned int phy_reg1;
|
||||
unsigned int phy_param0;
|
||||
unsigned int phy_param1;
|
||||
unsigned int phy_term;
|
||||
unsigned int phy_test;
|
||||
unsigned int phy_adp;
|
||||
unsigned int phy_batchg;
|
||||
unsigned int phy_resume;
|
||||
unsigned int reserve2[3];
|
||||
unsigned int link_port;
|
||||
};
|
||||
|
||||
#endif /* _ASM_ARCH_XHCI_EXYNOS_H_ */
|
@ -9,6 +9,12 @@
|
||||
#define MX6_IOMUXC_GPR6 0x020e0018
|
||||
#define MX6_IOMUXC_GPR7 0x020e001c
|
||||
|
||||
/*
|
||||
* IOMUXC_GPR1 bit fields
|
||||
*/
|
||||
#define IOMUXC_GPR1_OTG_ID_ENET_RX_ERR (0<<13)
|
||||
#define IOMUXC_GPR1_OTG_ID_GPIO1 (1<<13)
|
||||
#define IOMUXC_GPR1_OTG_ID_MASK (1<<13)
|
||||
/*
|
||||
* IOMUXC_GPR13 bit fields
|
||||
*/
|
||||
|
@ -166,6 +166,16 @@
|
||||
#define OPTFCLKEN_USB_CH1_CLK_ENABLE (1 << 9)
|
||||
#define OPTFCLKEN_USB_CH2_CLK_ENABLE (1 << 10)
|
||||
|
||||
/* CM_COREAON_USB_PHY_CORE_CLKCTRL */
|
||||
#define USBPHY_CORE_CLKCTRL_OPTFCLKEN_CLK32K (1 << 8)
|
||||
|
||||
/* CM_L3INIT_USB_OTG_SS_CLKCTRL */
|
||||
#define OTG_SS_CLKCTRL_MODULEMODE_HW (1 << 0)
|
||||
#define OPTFCLKEN_REFCLK960M (1 << 8)
|
||||
|
||||
/* CM_L3INIT_OCP2SCP1_CLKCTRL */
|
||||
#define OCP2SCP1_CLKCTRL_MODULEMODE_HW (1 << 0)
|
||||
|
||||
/* CM_MPU_MPU_CLKCTRL */
|
||||
#define MPU_CLKCTRL_CLKSEL_EMIF_DIV_MODE_SHIFT 24
|
||||
#define MPU_CLKCTRL_CLKSEL_EMIF_DIV_MODE_MASK (3 << 24)
|
||||
@ -192,6 +202,10 @@
|
||||
/* PRM_VC_VAL_BYPASS */
|
||||
#define PRM_VC_I2C_CHANNEL_FREQ_KHZ 400
|
||||
|
||||
/* CTRL_CORE_SRCOMP_NORTH_SIDE */
|
||||
#define USB2PHY_DISCHGDET (1 << 29)
|
||||
#define USB2PHY_AUTORESUME_EN (1 << 30)
|
||||
|
||||
/* SMPS */
|
||||
#define SMPS_I2C_SLAVE_ADDR 0x12
|
||||
#define SMPS_REG_ADDR_12_MPU 0x23
|
||||
|
@ -131,8 +131,7 @@
|
||||
/* USB3_IF_USB_PHY_VBUS_SENSORS_0 */
|
||||
#define VBUS_VLD_STS (1 << 26)
|
||||
|
||||
|
||||
/* Setup USB on the board */
|
||||
int board_usb_init(const void *blob);
|
||||
int usb_process_devicetree(const void *blob);
|
||||
|
||||
#endif /* _TEGRA_USB_H_ */
|
||||
|
@ -145,8 +145,8 @@ struct omap_ehci {
|
||||
struct ehci_hccr;
|
||||
struct ehci_hcor;
|
||||
|
||||
int omap_ehci_hcd_init(struct omap_usbhs_board_data *usbhs_pdata,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor);
|
||||
int omap_ehci_hcd_init(int index, struct omap_usbhs_board_data *usbhs_pdata,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor);
|
||||
int omap_ehci_hcd_stop(void);
|
||||
|
||||
#endif /* _OMAP_COMMON_EHCI_H_ */
|
||||
|
@ -143,6 +143,8 @@ struct prcm_regs {
|
||||
u32 cm_div_m2_dpll_unipro;
|
||||
u32 cm_ssc_deltamstep_dpll_unipro;
|
||||
u32 cm_ssc_modfreqdiv_dpll_unipro;
|
||||
u32 cm_coreaon_usb_phy_core_clkctrl;
|
||||
u32 cm_coreaon_usb_phy2_core_clkctrl;
|
||||
|
||||
/* cm2.core */
|
||||
u32 cm_coreaon_bandgap_clkctrl;
|
||||
@ -226,6 +228,8 @@ struct prcm_regs {
|
||||
u32 cm_l3init_p1500_clkctrl;
|
||||
u32 cm_l3init_fsusb_clkctrl;
|
||||
u32 cm_l3init_ocp2scp1_clkctrl;
|
||||
u32 cm_l3init_ocp2scp3_clkctrl;
|
||||
u32 cm_l3init_usb_otg_ss_clkctrl;
|
||||
|
||||
u32 prm_irqstatus_mpu_2;
|
||||
|
||||
@ -348,6 +352,7 @@ struct omap_sys_ctrl_regs {
|
||||
u32 control_core_mac_id_1_lo;
|
||||
u32 control_core_mac_id_1_hi;
|
||||
u32 control_std_fuse_opp_vdd_mpu_2;
|
||||
u32 control_phy_power_usb;
|
||||
u32 control_core_mmr_lock1;
|
||||
u32 control_core_mmr_lock2;
|
||||
u32 control_core_mmr_lock3;
|
||||
|
@ -1548,7 +1548,7 @@ static void hc_release_ohci (ohci_t *ohci)
|
||||
*/
|
||||
static char ohci_inited = 0;
|
||||
|
||||
int usb_lowlevel_init(int index, void **controller)
|
||||
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
|
||||
{
|
||||
u32 pin_func;
|
||||
u32 sys_freqctrl, sys_clksrc;
|
||||
|
@ -1544,7 +1544,7 @@ static void hc_release_ohci (ohci_t *ohci)
|
||||
*/
|
||||
static char ohci_inited = 0;
|
||||
|
||||
int usb_lowlevel_init(int index, void **controller)
|
||||
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
|
||||
{
|
||||
|
||||
/* Set the USB Clock */
|
||||
|
@ -1549,7 +1549,7 @@ static void hc_release_ohci (ohci_t *ohci)
|
||||
*/
|
||||
static char ohci_inited = 0;
|
||||
|
||||
int usb_lowlevel_init(int index, void **controller)
|
||||
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
|
||||
{
|
||||
memset (&gohci, 0, sizeof (ohci_t));
|
||||
memset (&urb_priv, 0, sizeof (urb_priv_t));
|
||||
|
@ -688,7 +688,7 @@ void handle_usb_interrupt(void)
|
||||
|
||||
/* init uhci
|
||||
*/
|
||||
int usb_lowlevel_init(int index, void **controller)
|
||||
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
|
||||
{
|
||||
unsigned char temp;
|
||||
ambapp_ahbdev ahbdev;
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <asm/4xx_pcie.h>
|
||||
#include <asm/ppc4xx-gpio.h>
|
||||
#include <asm/errno.h>
|
||||
#include <usb.h>
|
||||
|
||||
extern flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; /* info for FLASH chips */
|
||||
|
||||
@ -188,7 +189,7 @@ int board_early_init_f(void)
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USB_OHCI_NEW) && defined(CONFIG_SYS_USB_OHCI_BOARD_INIT)
|
||||
int usb_board_init(void)
|
||||
int board_usb_init(int index, enum usb_init_type init)
|
||||
{
|
||||
struct board_bcsr *bcsr_data =
|
||||
(struct board_bcsr *)CONFIG_SYS_BCSR_BASE;
|
||||
@ -229,7 +230,7 @@ int usb_board_stop(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_board_init_fail(void)
|
||||
int board_usb_cleanup(int index, enum usb_init_type init)
|
||||
{
|
||||
return usb_board_stop();
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <asm/io.h>
|
||||
#include <spartan3.h>
|
||||
#include <command.h>
|
||||
#include <usb.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
@ -59,7 +60,7 @@ void dram_init_banksize(void)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CMD_USB
|
||||
int usb_board_init(void)
|
||||
int board_usb_init(int index, enum usb_init_type init)
|
||||
{
|
||||
writel((readl(UHCHR) | UHCHR_PCPL | UHCHR_PSPL) &
|
||||
~(UHCHR_SSEP0 | UHCHR_SSEP1 | UHCHR_SSEP2 | UHCHR_SSE),
|
||||
@ -90,9 +91,9 @@ int usb_board_init(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_board_init_fail(void)
|
||||
int board_usb_cleanup(int index, enum usb_init_type init)
|
||||
{
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_board_stop(void)
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <i2c.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
#define GP_USB_OTG_PWR IMX_GPIO_NR(3, 22)
|
||||
|
||||
#define UART_PAD_CTRL (PAD_CTL_PUS_100K_UP | \
|
||||
PAD_CTL_SPEED_MED | PAD_CTL_DSE_40ohm | \
|
||||
@ -179,6 +180,14 @@ iomux_v3_cfg_t const enet_pads2[] = {
|
||||
MX6_PAD_RGMII_RX_CTL__RGMII_RX_CTL | MUX_PAD_CTRL(ENET_PAD_CTRL),
|
||||
};
|
||||
|
||||
static iomux_v3_cfg_t const misc_pads[] = {
|
||||
MX6_PAD_GPIO_1__USB_OTG_ID | MUX_PAD_CTRL(WEAK_PULLUP),
|
||||
MX6_PAD_KEY_COL4__USBOH3_USBOTG_OC | MUX_PAD_CTRL(WEAK_PULLUP),
|
||||
MX6_PAD_EIM_D30__USBOH3_USBH1_OC | MUX_PAD_CTRL(WEAK_PULLUP),
|
||||
/* OTG Power enable */
|
||||
MX6_PAD_EIM_D22__GPIO_3_22 | MUX_PAD_CTRL(OUTPUT_40OHM),
|
||||
};
|
||||
|
||||
/* wl1271 pads on nitrogen6x */
|
||||
iomux_v3_cfg_t const wl12xx_pads[] = {
|
||||
(MX6_PAD_NANDF_CS1__GPIO_6_14 & ~MUX_PAD_CTRL_MASK)
|
||||
@ -250,6 +259,15 @@ int board_ehci_hcd_init(int port)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int board_ehci_power(int port, int on)
|
||||
{
|
||||
if (port)
|
||||
return 0;
|
||||
gpio_set_value(GP_USB_OTG_PWR, on);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FSL_ESDHC
|
||||
@ -369,6 +387,11 @@ int board_eth_init(bd_t *bis)
|
||||
free(bus);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MV_UDC
|
||||
/* For otg ethernet*/
|
||||
usb_eth_initialize(bis);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -685,6 +708,7 @@ int board_early_init_f(void)
|
||||
gpio_direction_input(WL12XX_WL_IRQ_GP);
|
||||
gpio_direction_output(WL12XX_WL_ENABLE_GP, 0);
|
||||
gpio_direction_output(WL12XX_BT_ENABLE_GP, 0);
|
||||
gpio_direction_output(GP_USB_OTG_PWR, 0); /* OTG power off */
|
||||
|
||||
imx_iomux_v3_setup_multiple_pads(wl12xx_pads, ARRAY_SIZE(wl12xx_pads));
|
||||
setup_buttons();
|
||||
@ -706,6 +730,15 @@ int overwrite_console(void)
|
||||
|
||||
int board_init(void)
|
||||
{
|
||||
struct iomuxc_base_regs *const iomuxc_regs
|
||||
= (struct iomuxc_base_regs *)IOMUXC_BASE_ADDR;
|
||||
|
||||
clrsetbits_le32(&iomuxc_regs->gpr[1],
|
||||
IOMUXC_GPR1_OTG_ID_MASK,
|
||||
IOMUXC_GPR1_OTG_ID_GPIO1);
|
||||
|
||||
imx_iomux_v3_setup_multiple_pads(misc_pads, ARRAY_SIZE(misc_pads));
|
||||
|
||||
/* address of boot parameters */
|
||||
gd->bd->bi_boot_params = PHYS_SDRAM + 0x100;
|
||||
|
||||
|
@ -565,7 +565,8 @@ struct omap_usbhs_board_data usbhs_bdata = {
|
||||
};
|
||||
|
||||
#define SB_T35_USB_HUB_RESET_GPIO 167
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
u8 val;
|
||||
int offset;
|
||||
@ -591,7 +592,7 @@ int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
twl4030_i2c_write_u8(TWL4030_CHIP_GPIO, offset, 0xC0);
|
||||
udelay(1);
|
||||
|
||||
return omap_ehci_hcd_init(&usbhs_bdata, hccr, hcor);
|
||||
return omap_ehci_hcd_init(index, &usbhs_bdata, hccr, hcor);
|
||||
}
|
||||
|
||||
int ehci_hcd_stop(void)
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <mtd/cfi_flash.h>
|
||||
#include <asm/4xx_pci.h>
|
||||
#include <pci.h>
|
||||
#include <usb.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
@ -428,7 +429,7 @@ void reset_phy(void)
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USB_OHCI_NEW) && defined(CONFIG_SYS_USB_OHCI_BOARD_INIT)
|
||||
int usb_board_init(void)
|
||||
int board_usb_init(int index, enum usb_init_type init)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -453,9 +454,8 @@ int usb_board_stop(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_board_init_fail(void)
|
||||
int board_usb_cleanup(int index, enum usb_init_type init)
|
||||
{
|
||||
usb_board_stop();
|
||||
return 0;
|
||||
return usb_board_stop();
|
||||
}
|
||||
#endif /* defined(CONFIG_USB_OHCI) && defined(CONFIG_SYS_USB_OHCI_BOARD_INIT) */
|
||||
|
@ -27,6 +27,7 @@
|
||||
#endif
|
||||
#include <serial.h>
|
||||
#include <asm/4xx_pci.h>
|
||||
#include <usb.h>
|
||||
|
||||
#include "fpga.h"
|
||||
#include "pmc440.h"
|
||||
@ -821,7 +822,7 @@ int bootstrap_eeprom_read (unsigned dev_addr, unsigned offset,
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USB_OHCI_NEW) && defined(CONFIG_SYS_USB_OHCI_BOARD_INIT)
|
||||
int usb_board_init(void)
|
||||
int board_usb_init(int index, enum usb_init_type init)
|
||||
{
|
||||
char *act = getenv("usbact");
|
||||
int i;
|
||||
@ -845,10 +846,9 @@ int usb_board_stop(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_board_init_fail(void)
|
||||
int board_usb_cleanup(int index, enum usb_init_type init)
|
||||
{
|
||||
usb_board_stop();
|
||||
return 0;
|
||||
return usb_board_stop();
|
||||
}
|
||||
#endif /* defined(CONFIG_USB_OHCI) && defined(CONFIG_SYS_USB_OHCI_BOARD_INIT) */
|
||||
|
||||
|
@ -40,9 +40,10 @@ static struct omap_usbhs_board_data usbhs_bdata = {
|
||||
.port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
|
||||
};
|
||||
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
return omap_ehci_hcd_init(&usbhs_bdata, hccr, hcor);
|
||||
return omap_ehci_hcd_init(index, &usbhs_bdata, hccr, hcor);
|
||||
}
|
||||
|
||||
int ehci_hcd_stop(int index)
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <netdev.h>
|
||||
#include <serial.h>
|
||||
#include <asm/io.h>
|
||||
#include <usb.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
@ -58,7 +59,7 @@ int board_mmc_init(bd_t *bis)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CMD_USB
|
||||
int usb_board_init(void)
|
||||
int board_usb_init(int index, enum usb_init_type init)
|
||||
{
|
||||
writel((UHCHR | UHCHR_PCPL | UHCHR_PSPL) &
|
||||
~(UHCHR_SSEP0 | UHCHR_SSEP1 | UHCHR_SSEP2 | UHCHR_SSE),
|
||||
@ -89,9 +90,9 @@ int usb_board_init(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_board_init_fail(void)
|
||||
int board_usb_cleanup(int index, enum usb_init_type init)
|
||||
{
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_board_stop(void)
|
||||
|
@ -584,7 +584,7 @@ void handle_usb_interrupt(void)
|
||||
|
||||
/* init uhci
|
||||
*/
|
||||
int usb_lowlevel_init(int index, void **controller)
|
||||
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
|
||||
{
|
||||
unsigned char temp;
|
||||
int busdevfunc;
|
||||
|
@ -32,6 +32,7 @@
|
||||
#ifdef CONFIG_USB_EHCI_TEGRA
|
||||
#include <asm/arch-tegra/usb.h>
|
||||
#include <asm/arch/usb.h>
|
||||
#include <usb.h>
|
||||
#endif
|
||||
#ifdef CONFIG_TEGRA_MMC
|
||||
#include <asm/arch-tegra/tegra_mmc.h>
|
||||
@ -153,8 +154,9 @@ int board_init(void)
|
||||
|
||||
#ifdef CONFIG_USB_EHCI_TEGRA
|
||||
pin_mux_usb();
|
||||
board_usb_init(gd->fdt_blob);
|
||||
usb_process_devicetree(gd->fdt_blob);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD
|
||||
tegra_lcd_check_next_stage(gd->fdt_blob, 0);
|
||||
#endif
|
||||
|
@ -10,6 +10,7 @@ include $(TOPDIR)/config.mk
|
||||
LIB = $(obj)libsamsung.o
|
||||
|
||||
COBJS-$(CONFIG_SOFT_I2C_MULTI_BUS) += multi_i2c.o
|
||||
COBJS-$(CONFIG_THOR_FUNCTION) += thor.o
|
||||
|
||||
SRCS := $(COBJS-y:.o=.c)
|
||||
OBJS := $(addprefix $(obj),$(COBJS-y))
|
||||
|
21
board/samsung/common/thor.c
Normal file
21
board/samsung/common/thor.c
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Samsung Electronics
|
||||
* Lukasz Majewski <l.majewski@samsung.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
|
||||
int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name)
|
||||
{
|
||||
if (!strcmp(name, "usb_dnl_thor")) {
|
||||
put_unaligned(CONFIG_G_DNL_THOR_VENDOR_NUM, &dev->idVendor);
|
||||
put_unaligned(CONFIG_G_DNL_THOR_PRODUCT_NUM, &dev->idProduct);
|
||||
} else {
|
||||
put_unaligned(CONFIG_G_DNL_VENDOR_NUM, &dev->idVendor);
|
||||
put_unaligned(CONFIG_G_DNL_PRODUCT_NUM, &dev->idProduct);
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -144,4 +144,8 @@
|
||||
mmc@12230000 {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
ehci@12110000 {
|
||||
samsung,vbus-gpio = <&gpio 0xbe 0>; /* X26 */
|
||||
};
|
||||
};
|
||||
|
@ -109,6 +109,14 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
ehci@12110000 {
|
||||
samsung,vbus-gpio = <&gpio 0xb1 0>; /* X11 */
|
||||
};
|
||||
|
||||
xhci@12000000 {
|
||||
samsung,vbus-gpio = <&gpio 0xbf 0>; /* X27 */
|
||||
};
|
||||
|
||||
tmu@10060000 {
|
||||
samsung,min-temp = <25>;
|
||||
samsung,max-temp = <125>;
|
||||
|
@ -61,22 +61,6 @@ struct local_info {
|
||||
|
||||
static struct local_info local;
|
||||
|
||||
#ifdef CONFIG_USB_EHCI_EXYNOS
|
||||
int board_usb_vbus_init(void)
|
||||
{
|
||||
struct exynos5_gpio_part1 *gpio1 = (struct exynos5_gpio_part1 *)
|
||||
samsung_get_base_gpio_part1();
|
||||
|
||||
/* Enable VBUS power switch */
|
||||
s5p_gpio_direction_output(&gpio1->x2, 6, 1);
|
||||
|
||||
/* VBUS turn ON time */
|
||||
mdelay(3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SOUND_MAX98095
|
||||
static void board_enable_audio_codec(void)
|
||||
{
|
||||
@ -122,9 +106,6 @@ int board_init(void)
|
||||
if (board_init_cros_ec_devices(gd->fdt_blob))
|
||||
return -1;
|
||||
|
||||
#ifdef CONFIG_USB_EHCI_EXYNOS
|
||||
board_usb_vbus_init();
|
||||
#endif
|
||||
#ifdef CONFIG_SOUND_MAX98095
|
||||
board_enable_audio_codec();
|
||||
#endif
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <power/max8997_muic.h>
|
||||
#include <power/battery.h>
|
||||
#include <power/max17042_fg.h>
|
||||
#include <usb.h>
|
||||
#include <usb_mass_storage.h>
|
||||
|
||||
#include "setup.h"
|
||||
@ -495,10 +496,10 @@ struct s3c_plat_otg_data s5pc210_otg_data = {
|
||||
.usb_flags = PHY0_SLEEP,
|
||||
};
|
||||
|
||||
void board_usb_init(void)
|
||||
int board_usb_init(int index, enum usb_init_type init)
|
||||
{
|
||||
debug("USB_udc_probe\n");
|
||||
s3c_udc_probe(&s5pc210_otg_data);
|
||||
return s3c_udc_probe(&s5pc210_otg_data);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -275,7 +275,7 @@ int factoryset_setenv(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int g_dnl_bind_fixup(struct usb_device_descriptor *dev)
|
||||
int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name)
|
||||
{
|
||||
put_unaligned(factory_dat.usb_vendor_id, &dev->idVendor);
|
||||
put_unaligned(factory_dat.usb_product_id, &dev->idProduct);
|
||||
|
@ -51,9 +51,10 @@ static struct omap_usbhs_board_data usbhs_bdata = {
|
||||
.port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
|
||||
};
|
||||
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
return omap_ehci_hcd_init(&usbhs_bdata, hccr, hcor);
|
||||
return omap_ehci_hcd_init(index, &usbhs_bdata, hccr, hcor);
|
||||
}
|
||||
|
||||
int ehci_hcd_stop(int index)
|
||||
|
@ -102,9 +102,10 @@ static struct omap_usbhs_board_data usbhs_bdata = {
|
||||
.port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
|
||||
};
|
||||
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
return omap_ehci_hcd_init(&usbhs_bdata, hccr, hcor);
|
||||
return omap_ehci_hcd_init(index, &usbhs_bdata, hccr, hcor);
|
||||
}
|
||||
|
||||
int ehci_hcd_stop(int index)
|
||||
|
@ -521,9 +521,10 @@ static struct omap_usbhs_board_data usbhs_bdata = {
|
||||
.port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED
|
||||
};
|
||||
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
return omap_ehci_hcd_init(&usbhs_bdata, hccr, hcor);
|
||||
return omap_ehci_hcd_init(index, &usbhs_bdata, hccr, hcor);
|
||||
}
|
||||
|
||||
int ehci_hcd_stop(int index)
|
||||
|
@ -17,12 +17,6 @@
|
||||
|
||||
#include "mux_data.h"
|
||||
|
||||
#ifdef CONFIG_USB_EHCI
|
||||
#include <usb.h>
|
||||
#include <asm/arch/ehci.h>
|
||||
#include <asm/ehci-omap.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRIVER_TI_CPSW
|
||||
#include <cpsw.h>
|
||||
#endif
|
||||
|
@ -61,5 +61,6 @@ const struct pad_conf_entry core_padconf_array_essential[] = {
|
||||
{GPMC_A4, (IEN | PDIS | M1)}, /* QSPI1_CS3 */
|
||||
{GPMC_CS2, (IEN | PTU | PDIS | M1)}, /* QSPI1_CS0 */
|
||||
{GPMC_CS3, (IEN | PTU | PDIS | M1)}, /* QSPI1_CS1*/
|
||||
{USB2_DRVVBUS, (M0 | IEN | FSC) },
|
||||
};
|
||||
#endif /* _MUX_DATA_DRA7XX_H_ */
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
#include "mux_data.h"
|
||||
|
||||
#ifdef CONFIG_USB_EHCI
|
||||
#if defined(CONFIG_USB_EHCI) || defined(CONFIG_USB_XHCI_OMAP)
|
||||
#include <usb.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <asm/arch/clock.h>
|
||||
@ -72,6 +72,35 @@ int board_eth_init(bd_t *bis)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USB_EHCI) || defined(CONFIG_USB_XHCI_OMAP)
|
||||
static void enable_host_clocks(void)
|
||||
{
|
||||
int auxclk;
|
||||
int hs_clk_ctrl_val = (OPTFCLKEN_HSIC60M_P3_CLK |
|
||||
OPTFCLKEN_HSIC480M_P3_CLK |
|
||||
OPTFCLKEN_HSIC60M_P2_CLK |
|
||||
OPTFCLKEN_HSIC480M_P2_CLK |
|
||||
OPTFCLKEN_UTMI_P3_CLK | OPTFCLKEN_UTMI_P2_CLK);
|
||||
|
||||
/* Enable port 2 and 3 clocks*/
|
||||
setbits_le32((*prcm)->cm_l3init_hsusbhost_clkctrl, hs_clk_ctrl_val);
|
||||
|
||||
/* Enable port 2 and 3 usb host ports tll clocks*/
|
||||
setbits_le32((*prcm)->cm_l3init_hsusbtll_clkctrl,
|
||||
(OPTFCLKEN_USB_CH1_CLK_ENABLE | OPTFCLKEN_USB_CH2_CLK_ENABLE));
|
||||
#ifdef CONFIG_USB_XHCI_OMAP
|
||||
/* Enable the USB OTG Super speed clocks */
|
||||
setbits_le32((*prcm)->cm_l3init_usb_otg_ss_clkctrl,
|
||||
(OPTFCLKEN_REFCLK960M | OTG_SS_CLKCTRL_MODULEMODE_HW));
|
||||
#endif
|
||||
|
||||
auxclk = readl((*prcm)->scrm_auxclk1);
|
||||
/* Request auxilary clock */
|
||||
auxclk |= AUXCLK_ENABLE_MASK;
|
||||
writel(auxclk, (*prcm)->scrm_auxclk1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief misc_init_r - Configure EVM board specific configurations
|
||||
* such as power configurations, ethernet initialization as phase2 of
|
||||
@ -81,9 +110,30 @@ int board_eth_init(bd_t *bis)
|
||||
*/
|
||||
int misc_init_r(void)
|
||||
{
|
||||
int reg;
|
||||
uint8_t device_mac[6];
|
||||
|
||||
#ifdef CONFIG_PALMAS_POWER
|
||||
palmas_init_settings();
|
||||
#endif
|
||||
|
||||
if (!getenv("usbethaddr")) {
|
||||
reg = DIE_ID_REG_BASE + DIE_ID_REG_OFFSET;
|
||||
|
||||
/*
|
||||
* create a fake MAC address from the processor ID code.
|
||||
* first byte is 0x02 to signify locally administered.
|
||||
*/
|
||||
device_mac[0] = 0x02;
|
||||
device_mac[1] = readl(reg + 0x10) & 0xff;
|
||||
device_mac[2] = readl(reg + 0xC) & 0xff;
|
||||
device_mac[3] = readl(reg + 0x8) & 0xff;
|
||||
device_mac[4] = readl(reg) & 0xff;
|
||||
device_mac[5] = (readl(reg) >> 8) & 0xff;
|
||||
|
||||
eth_setenv_enetaddr("usbethaddr", device_mac);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -129,54 +179,14 @@ static struct omap_usbhs_board_data usbhs_bdata = {
|
||||
.port_mode[2] = OMAP_EHCI_PORT_MODE_HSIC,
|
||||
};
|
||||
|
||||
static void enable_host_clocks(void)
|
||||
{
|
||||
int hs_clk_ctrl_val = (OPTFCLKEN_HSIC60M_P3_CLK |
|
||||
OPTFCLKEN_HSIC480M_P3_CLK |
|
||||
OPTFCLKEN_HSIC60M_P2_CLK |
|
||||
OPTFCLKEN_HSIC480M_P2_CLK |
|
||||
OPTFCLKEN_UTMI_P3_CLK | OPTFCLKEN_UTMI_P2_CLK);
|
||||
|
||||
/* Enable port 2 and 3 clocks*/
|
||||
setbits_le32((*prcm)->cm_l3init_hsusbhost_clkctrl, hs_clk_ctrl_val);
|
||||
|
||||
/* Enable port 2 and 3 usb host ports tll clocks*/
|
||||
setbits_le32((*prcm)->cm_l3init_hsusbtll_clkctrl,
|
||||
(OPTFCLKEN_USB_CH1_CLK_ENABLE | OPTFCLKEN_USB_CH2_CLK_ENABLE));
|
||||
}
|
||||
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
int ret;
|
||||
int auxclk;
|
||||
int reg;
|
||||
uint8_t device_mac[6];
|
||||
|
||||
enable_host_clocks();
|
||||
|
||||
if (!getenv("usbethaddr")) {
|
||||
reg = DIE_ID_REG_BASE + DIE_ID_REG_OFFSET;
|
||||
|
||||
/*
|
||||
* create a fake MAC address from the processor ID code.
|
||||
* first byte is 0x02 to signify locally administered.
|
||||
*/
|
||||
device_mac[0] = 0x02;
|
||||
device_mac[1] = readl(reg + 0x10) & 0xff;
|
||||
device_mac[2] = readl(reg + 0xC) & 0xff;
|
||||
device_mac[3] = readl(reg + 0x8) & 0xff;
|
||||
device_mac[4] = readl(reg) & 0xff;
|
||||
device_mac[5] = (readl(reg) >> 8) & 0xff;
|
||||
|
||||
eth_setenv_enetaddr("usbethaddr", device_mac);
|
||||
}
|
||||
|
||||
auxclk = readl((*prcm)->scrm_auxclk1);
|
||||
/* Request auxilary clock */
|
||||
auxclk |= AUXCLK_ENABLE_MASK;
|
||||
writel(auxclk, (*prcm)->scrm_auxclk1);
|
||||
|
||||
ret = omap_ehci_hcd_init(&usbhs_bdata, hccr, hcor);
|
||||
ret = omap_ehci_hcd_init(index, &usbhs_bdata, hccr, hcor);
|
||||
if (ret < 0) {
|
||||
puts("Failed to initialize ehci\n");
|
||||
return ret;
|
||||
@ -203,3 +213,23 @@ void usb_hub_reset_devices(int port)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_XHCI_OMAP
|
||||
/**
|
||||
* @brief board_usb_init - Configure EVM board specific configurations
|
||||
* for the LDO's and clocks for the USB blocks.
|
||||
*
|
||||
* @return 0
|
||||
*/
|
||||
int board_usb_init(int index, enum board_usb_init_type init)
|
||||
{
|
||||
int ret;
|
||||
#ifdef CONFIG_PALMAS_USB_SS_PWR
|
||||
ret = palmas_enable_ss_ldo();
|
||||
#endif
|
||||
|
||||
enable_host_clocks();
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -269,7 +269,8 @@ static struct omap_usbhs_board_data usbhs_bdata = {
|
||||
.port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
|
||||
};
|
||||
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
int ret;
|
||||
unsigned int utmi_clk;
|
||||
@ -279,7 +280,7 @@ int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
utmi_clk |= HSUSBHOST_CLKCTRL_CLKSEL_UTMI_P1_MASK;
|
||||
sr32((void *)CM_L3INIT_HSUSBHOST_CLKCTRL, 0, 32, utmi_clk);
|
||||
|
||||
ret = omap_ehci_hcd_init(&usbhs_bdata, hccr, hcor);
|
||||
ret = omap_ehci_hcd_init(index, &usbhs_bdata, hccr, hcor);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <netdev.h>
|
||||
#include <asm/io.h>
|
||||
#include <serial.h>
|
||||
#include <usb.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
@ -39,7 +40,7 @@ int dram_init(void)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CMD_USB
|
||||
int usb_board_init(void)
|
||||
int board_usb_init(int index, enum usb_init_type init)
|
||||
{
|
||||
writel((readl(UHCHR) | UHCHR_PCPL | UHCHR_PSPL) &
|
||||
~(UHCHR_SSEP0 | UHCHR_SSEP1 | UHCHR_SSEP2 | UHCHR_SSE),
|
||||
@ -70,9 +71,9 @@ int usb_board_init(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_board_init_fail(void)
|
||||
int board_usb_cleanup(int index, enum usb_init_type init)
|
||||
{
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_board_stop(void)
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <asm/arch/regs-mmc.h>
|
||||
#include <netdev.h>
|
||||
#include <asm/io.h>
|
||||
#include <usb.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
@ -42,7 +43,7 @@ extern struct serial_device serial_stuart_device;
|
||||
* Miscelaneous platform dependent initialisations
|
||||
*/
|
||||
|
||||
int usb_board_init(void)
|
||||
int board_usb_init(int index, enum usb_init_type init)
|
||||
{
|
||||
writel((readl(UHCHR) | UHCHR_PCPL | UHCHR_PSPL) &
|
||||
~(UHCHR_SSEP0 | UHCHR_SSEP1 | UHCHR_SSEP2 | UHCHR_SSE),
|
||||
@ -69,9 +70,9 @@ int usb_board_init(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_board_init_fail(void)
|
||||
int board_usb_cleanup(int index, enum usb_init_type init)
|
||||
{
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_board_stop(void)
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <netdev.h>
|
||||
#include <serial.h>
|
||||
#include <asm/io.h>
|
||||
#include <usb.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
@ -66,7 +67,7 @@ int board_mmc_init(bd_t *bis)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CMD_USB
|
||||
int usb_board_init(void)
|
||||
int board_usb_init(int index, enum usb_init_type init)
|
||||
{
|
||||
writel((UHCHR | UHCHR_PCPL | UHCHR_PSPL) &
|
||||
~(UHCHR_SSEP0 | UHCHR_SSEP1 | UHCHR_SSEP2 | UHCHR_SSE),
|
||||
@ -97,9 +98,9 @@ int usb_board_init(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_board_init_fail(void)
|
||||
int board_usb_cleanup(int index, enum usb_init_type init)
|
||||
{
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_board_stop(void)
|
||||
|
@ -168,6 +168,7 @@ COBJS-y += usb.o usb_hub.o
|
||||
COBJS-$(CONFIG_USB_STORAGE) += usb_storage.o
|
||||
endif
|
||||
COBJS-$(CONFIG_CMD_USB_MASS_STORAGE) += cmd_usb_mass_storage.o
|
||||
COBJS-$(CONFIG_CMD_THOR_DOWNLOAD) += cmd_thordown.o
|
||||
COBJS-$(CONFIG_CMD_XIMG) += cmd_ximg.o
|
||||
COBJS-$(CONFIG_YAFFS2) += cmd_yaffs2.o
|
||||
COBJS-$(CONFIG_CMD_SPL) += cmd_spl.o
|
||||
|
@ -11,27 +11,32 @@
|
||||
#include <common.h>
|
||||
#include <dfu.h>
|
||||
#include <g_dnl.h>
|
||||
#include <usb.h>
|
||||
|
||||
static int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
||||
{
|
||||
if (argc < 4)
|
||||
return CMD_RET_USAGE;
|
||||
|
||||
char *usb_controller = argv[1];
|
||||
char *interface = argv[2];
|
||||
char *devstring = argv[3];
|
||||
|
||||
char *s = "dfu";
|
||||
int ret, i = 0;
|
||||
|
||||
if (argc < 3)
|
||||
return CMD_RET_USAGE;
|
||||
|
||||
ret = dfu_init_env_entities(argv[1], simple_strtoul(argv[2], NULL, 10));
|
||||
ret = dfu_init_env_entities(interface, simple_strtoul(devstring,
|
||||
NULL, 10));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (argc > 3 && strcmp(argv[3], "list") == 0) {
|
||||
if (argc > 4 && strcmp(argv[4], "list") == 0) {
|
||||
dfu_show_entities();
|
||||
goto done;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TRATS
|
||||
board_usb_init();
|
||||
#endif
|
||||
int controller_index = simple_strtoul(usb_controller, NULL, 0);
|
||||
board_usb_init(controller_index, USB_INIT_DEVICE);
|
||||
|
||||
g_dnl_register(s);
|
||||
while (1) {
|
||||
@ -62,8 +67,9 @@ done:
|
||||
|
||||
U_BOOT_CMD(dfu, CONFIG_SYS_MAXARGS, 1, do_dfu,
|
||||
"Device Firmware Upgrade",
|
||||
"<interface> <dev> [list]\n"
|
||||
" - device firmware upgrade on a device <dev>\n"
|
||||
" attached to interface <interface>\n"
|
||||
" [list] - list available alt settings"
|
||||
"<USB_controller> <interface> <dev> [list]\n"
|
||||
" - device firmware upgrade via <USB_controller>\n"
|
||||
" on device <dev>, attached to interface\n"
|
||||
" <interface>\n"
|
||||
" [list] - list available alt settings\n"
|
||||
);
|
||||
|
72
common/cmd_thordown.c
Normal file
72
common/cmd_thordown.c
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* cmd_thordown.c -- USB TIZEN "THOR" Downloader gadget
|
||||
*
|
||||
* Copyright (C) 2013 Lukasz Majewski <l.majewski@samsung.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <thor.h>
|
||||
#include <dfu.h>
|
||||
#include <g_dnl.h>
|
||||
#include <usb.h>
|
||||
|
||||
int do_thor_down(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
||||
{
|
||||
if (argc < 4)
|
||||
return CMD_RET_USAGE;
|
||||
|
||||
char *usb_controller = argv[1];
|
||||
char *interface = argv[2];
|
||||
char *devstring = argv[3];
|
||||
|
||||
const char *s = "thor";
|
||||
int ret;
|
||||
|
||||
puts("TIZEN \"THOR\" Downloader\n");
|
||||
|
||||
ret = dfu_init_env_entities(interface, simple_strtoul(devstring,
|
||||
NULL, 10));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
int controller_index = simple_strtoul(usb_controller, NULL, 0);
|
||||
ret = board_usb_init(controller_index, USB_INIT_DEVICE);
|
||||
if (ret) {
|
||||
error("USB init failed: %d", ret);
|
||||
ret = CMD_RET_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
g_dnl_register(s);
|
||||
|
||||
ret = thor_init();
|
||||
if (ret) {
|
||||
error("THOR DOWNLOAD failed: %d", ret);
|
||||
ret = CMD_RET_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = thor_handle();
|
||||
if (ret) {
|
||||
error("THOR failed: %d", ret);
|
||||
ret = CMD_RET_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
g_dnl_unregister();
|
||||
dfu_free_entities();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
U_BOOT_CMD(thordown, CONFIG_SYS_MAXARGS, 1, do_thor_down,
|
||||
"TIZEN \"THOR\" downloader",
|
||||
"<USB_controller> <interface> <dev>\n"
|
||||
" - device software upgrade via LTHOR TIZEN dowload\n"
|
||||
" program via <USB_controller> on device <dev>,\n"
|
||||
" attached to interface <interface>\n"
|
||||
);
|
@ -8,51 +8,53 @@
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <g_dnl.h>
|
||||
#include <usb.h>
|
||||
#include <usb_mass_storage.h>
|
||||
|
||||
int do_usb_mass_storage(cmd_tbl_t *cmdtp, int flag,
|
||||
int argc, char * const argv[])
|
||||
{
|
||||
char *ep;
|
||||
unsigned int dev_num = 0, offset = 0, part_size = 0;
|
||||
int rc;
|
||||
if (argc < 3)
|
||||
return CMD_RET_USAGE;
|
||||
|
||||
struct ums_board_info *ums_info;
|
||||
static char *s = "ums";
|
||||
|
||||
if (argc < 2) {
|
||||
printf("usage: ums <dev> - e.g. ums 0\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_num = (int)simple_strtoul(argv[1], &ep, 16);
|
||||
const char *usb_controller = argv[1];
|
||||
const char *mmc_devstring = argv[2];
|
||||
|
||||
unsigned int dev_num = (unsigned int)(simple_strtoul(mmc_devstring,
|
||||
NULL, 0));
|
||||
if (dev_num) {
|
||||
puts("\nSet eMMC device to 0! - e.g. ums 0\n");
|
||||
error("Set eMMC device to 0! - e.g. ums 0");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
board_usb_init();
|
||||
ums_info = board_ums_init(dev_num, offset, part_size);
|
||||
unsigned int controller_index = (unsigned int)(simple_strtoul(
|
||||
usb_controller, NULL, 0));
|
||||
if (board_usb_init(controller_index, USB_INIT_DEVICE)) {
|
||||
error("Couldn't init USB controller.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
struct ums_board_info *ums_info = board_ums_init(dev_num, 0, 0);
|
||||
if (!ums_info) {
|
||||
printf("MMC: %d -> NOT available\n", dev_num);
|
||||
goto fail;
|
||||
}
|
||||
rc = fsg_init(ums_info);
|
||||
if (rc) {
|
||||
printf("cmd ums: fsg_init failed\n");
|
||||
error("MMC: %d -> NOT available", dev_num);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
g_dnl_register(s);
|
||||
int rc = fsg_init(ums_info);
|
||||
if (rc) {
|
||||
error("fsg_init failed");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
g_dnl_register("ums");
|
||||
|
||||
while (1) {
|
||||
/* Handle control-c and timeouts */
|
||||
if (ctrlc()) {
|
||||
printf("The remote end did not respond in time.\n");
|
||||
error("The remote end did not respond in time.");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
usb_gadget_handle_interrupts();
|
||||
/* Check if USB cable has been detached */
|
||||
if (fsg_main_thread(NULL) == EIO)
|
||||
@ -68,5 +70,5 @@ fail:
|
||||
|
||||
U_BOOT_CMD(ums, CONFIG_SYS_MAXARGS, 1, do_usb_mass_storage,
|
||||
"Use the UMS [User Mass Storage]",
|
||||
"ums - User Mass Storage Gadget"
|
||||
"<USB_controller> <mmc_dev>"
|
||||
);
|
||||
|
41
common/usb.c
41
common/usb.c
@ -33,6 +33,7 @@
|
||||
#include <linux/ctype.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <compiler.h>
|
||||
|
||||
#include <usb.h>
|
||||
#ifdef CONFIG_4xx
|
||||
@ -74,7 +75,7 @@ int usb_init(void)
|
||||
for (i = 0; i < CONFIG_USB_MAX_CONTROLLER_COUNT; i++) {
|
||||
/* init low_level USB */
|
||||
printf("USB%d: ", i);
|
||||
if (usb_lowlevel_init(i, &ctrl)) {
|
||||
if (usb_lowlevel_init(i, USB_INIT_HOST, &ctrl)) {
|
||||
puts("lowlevel init failed\n");
|
||||
continue;
|
||||
}
|
||||
@ -854,6 +855,16 @@ void usb_free_device(void)
|
||||
usb_dev[dev_index].devnum = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* XHCI issues Enable Slot command and thereafter
|
||||
* allocates device contexts. Provide a weak alias
|
||||
* function for the purpose, so that XHCI overrides it
|
||||
* and EHCI/OHCI just work out of the box.
|
||||
*/
|
||||
__weak int usb_alloc_device(struct usb_device *udev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* By the time we get here, the device has gotten a new device ID
|
||||
* and is in the default state. We need to identify the thing and
|
||||
@ -867,6 +878,17 @@ int usb_new_device(struct usb_device *dev)
|
||||
int tmp;
|
||||
ALLOC_CACHE_ALIGN_BUFFER(unsigned char, tmpbuf, USB_BUFSIZ);
|
||||
|
||||
/*
|
||||
* Allocate usb 3.0 device context.
|
||||
* USB 3.0 (xHCI) protocol tries to allocate device slot
|
||||
* and related data structures first. This call does that.
|
||||
* Refer to sec 4.3.2 in xHCI spec rev1.0
|
||||
*/
|
||||
if (usb_alloc_device(dev)) {
|
||||
printf("Cannot allocate device context to get SLOT_ID\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* We still haven't set the Address yet */
|
||||
addr = dev->devnum;
|
||||
dev->devnum = 0;
|
||||
@ -897,7 +919,7 @@ int usb_new_device(struct usb_device *dev)
|
||||
* http://sourceforge.net/mailarchive/forum.php?
|
||||
* thread_id=5729457&forum_id=5398
|
||||
*/
|
||||
struct usb_device_descriptor *desc;
|
||||
__maybe_unused struct usb_device_descriptor *desc;
|
||||
int port = -1;
|
||||
struct usb_device *parent = dev->parent;
|
||||
unsigned short portstatus;
|
||||
@ -914,6 +936,13 @@ int usb_new_device(struct usb_device *dev)
|
||||
dev->epmaxpacketin[0] = 64;
|
||||
dev->epmaxpacketout[0] = 64;
|
||||
|
||||
/*
|
||||
* XHCI needs to issue a Address device command to setup
|
||||
* proper device context structures, before it can interact
|
||||
* with the device. So a get_descriptor will fail before any
|
||||
* of that is done for XHCI unlike EHCI.
|
||||
*/
|
||||
#ifndef CONFIG_USB_XHCI
|
||||
err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, 64);
|
||||
if (err < 0) {
|
||||
debug("usb_new_device: usb_get_descriptor() failed\n");
|
||||
@ -926,11 +955,12 @@ int usb_new_device(struct usb_device *dev)
|
||||
* to differentiate between HUB and DEVICE.
|
||||
*/
|
||||
dev->descriptor.bDeviceClass = desc->bDeviceClass;
|
||||
#endif
|
||||
|
||||
/* find the port number we're at */
|
||||
if (parent) {
|
||||
int j;
|
||||
|
||||
/* find the port number we're at */
|
||||
for (j = 0; j < parent->maxchild; j++) {
|
||||
if (parent->children[j] == dev) {
|
||||
port = j;
|
||||
@ -1037,4 +1067,9 @@ int usb_new_device(struct usb_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
__weak
|
||||
int board_usb_init(int index, enum usb_init_type init)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
/* EOF */
|
||||
|
@ -67,14 +67,14 @@ int dfu_init_env_entities(char *interface, int dev)
|
||||
static unsigned char *dfu_buf;
|
||||
static unsigned long dfu_buf_size = CONFIG_SYS_DFU_DATA_BUF_SIZE;
|
||||
|
||||
static unsigned char *dfu_free_buf(void)
|
||||
unsigned char *dfu_free_buf(void)
|
||||
{
|
||||
free(dfu_buf);
|
||||
dfu_buf = NULL;
|
||||
return dfu_buf;
|
||||
}
|
||||
|
||||
static unsigned char *dfu_get_buf(void)
|
||||
unsigned char *dfu_get_buf(void)
|
||||
{
|
||||
char *s;
|
||||
|
||||
@ -330,7 +330,7 @@ int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
|
||||
}
|
||||
|
||||
static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt,
|
||||
char *interface, int num)
|
||||
char *interface, int num)
|
||||
{
|
||||
char *st;
|
||||
|
||||
@ -440,3 +440,15 @@ struct dfu_entity *dfu_get_entity(int alt)
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int dfu_get_alt(char *name)
|
||||
{
|
||||
struct dfu_entity *dfu;
|
||||
|
||||
list_for_each_entry(dfu, &dfu_list, list) {
|
||||
if (!strncmp(dfu->name, name, strlen(dfu->name)))
|
||||
return dfu->alt;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -127,6 +127,21 @@ int twl603x_audio_power(u8 on)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PALMAS_USB_SS_PWR
|
||||
/**
|
||||
* @brief palmas_enable_ss_ldo - Configure EVM board specific configurations
|
||||
* for the USB Super speed SMPS10 regulator.
|
||||
*
|
||||
* @return 0
|
||||
*/
|
||||
int palmas_enable_ss_ldo(void)
|
||||
{
|
||||
/* Enable smps10 regulator */
|
||||
return palmas_i2c_write_u8(TWL603X_CHIP_P1, SMPS10_CTRL,
|
||||
SMPS10_MODE_ACTIVE_D);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enable/disable back-up battery (or super cap) charging on TWL6035/37.
|
||||
* Please use defined BB_xxx values.
|
||||
|
@ -16,8 +16,6 @@
|
||||
#include <usb/mpc8xx_udc.h>
|
||||
#elif defined(CONFIG_OMAP1510)
|
||||
#include <usb/omap1510_udc.h>
|
||||
#elif defined(CONFIG_MUSB_UDC)
|
||||
#include <usb/musb_udc.h>
|
||||
#elif defined(CONFIG_CPU_PXA27X)
|
||||
#include <usb/pxa27x_udc.h>
|
||||
#elif defined(CONFIG_DW_UDC)
|
||||
@ -26,6 +24,7 @@
|
||||
#include <usb/mv_udc.h>
|
||||
#endif
|
||||
|
||||
#include <usb/udc.h>
|
||||
#include <version.h>
|
||||
|
||||
/* If no VendorID/ProductID is defined in config.h, pretend to be Linux
|
||||
|
@ -14,6 +14,12 @@
|
||||
|
||||
/* SMSC LAN95xx based USB 2.0 Ethernet Devices */
|
||||
|
||||
/* LED defines */
|
||||
#define LED_GPIO_CFG (0x24)
|
||||
#define LED_GPIO_CFG_SPD_LED (0x01000000)
|
||||
#define LED_GPIO_CFG_LNK_LED (0x00100000)
|
||||
#define LED_GPIO_CFG_FDX_LED (0x00010000)
|
||||
|
||||
/* Tx command words */
|
||||
#define TX_CMD_A_FIRST_SEG_ 0x00002000
|
||||
#define TX_CMD_A_LAST_SEG_ 0x00001000
|
||||
@ -591,6 +597,14 @@ static int smsc95xx_init(struct eth_device *eth, bd_t *bd)
|
||||
return ret;
|
||||
debug("ID_REV = 0x%08x\n", read_buf);
|
||||
|
||||
/* Configure GPIO pins as LED outputs */
|
||||
write_buf = LED_GPIO_CFG_SPD_LED | LED_GPIO_CFG_LNK_LED |
|
||||
LED_GPIO_CFG_FDX_LED;
|
||||
ret = smsc95xx_write_reg(dev, LED_GPIO_CFG, write_buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
debug("LED_GPIO_CFG set\n");
|
||||
|
||||
/* Init Tx */
|
||||
write_buf = 0;
|
||||
ret = smsc95xx_write_reg(dev, FLOW, write_buf);
|
||||
|
@ -21,6 +21,7 @@ ifdef CONFIG_USB_GADGET
|
||||
COBJS-$(CONFIG_USB_GADGET_ATMEL_USBA) += atmel_usba_udc.o
|
||||
COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o
|
||||
COBJS-$(CONFIG_USB_GADGET_FOTG210) += fotg210.o
|
||||
COBJS-$(CONFIG_THOR_FUNCTION) += f_thor.o
|
||||
COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o
|
||||
COBJS-$(CONFIG_DFU_FUNCTION) += f_dfu.o
|
||||
COBJS-$(CONFIG_USB_GADGET_MASS_STORAGE) += f_mass_storage.o
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <usbdevice.h>
|
||||
#include "ep0.h"
|
||||
#include <usb/designware_udc.h>
|
||||
#include <usb/udc.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
|
||||
#define UDC_INIT_MDELAY 80 /* Device settle delay */
|
||||
|
@ -635,6 +635,7 @@ fs_source_desc = {
|
||||
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = __constant_cpu_to_le16(64),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor
|
||||
@ -644,6 +645,7 @@ fs_sink_desc = {
|
||||
|
||||
.bEndpointAddress = USB_DIR_OUT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = __constant_cpu_to_le16(64),
|
||||
};
|
||||
|
||||
static const struct usb_descriptor_header *fs_eth_function[11] = {
|
||||
@ -1534,6 +1536,8 @@ static int rx_submit(struct eth_dev *dev, struct usb_request *req,
|
||||
*/
|
||||
|
||||
debug("%s\n", __func__);
|
||||
if (!req)
|
||||
return -EINVAL;
|
||||
|
||||
size = (ETHER_HDR_SIZE + dev->mtu + RX_EXTRA);
|
||||
size += dev->out_ep->maxpacket - 1;
|
||||
|
1003
drivers/usb/gadget/f_thor.c
Normal file
1003
drivers/usb/gadget/f_thor.c
Normal file
File diff suppressed because it is too large
Load Diff
124
drivers/usb/gadget/f_thor.h
Normal file
124
drivers/usb/gadget/f_thor.h
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* f_thor.h - USB TIZEN THOR - internal gadget definitions
|
||||
*
|
||||
* Copyright (C) 2013 Samsung Electronics
|
||||
* Lukasz Majewski <l.majewski@samsung.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#ifndef _USB_THOR_H_
|
||||
#define _USB_THOR_H_
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <asm/sizes.h>
|
||||
|
||||
/* THOR Composite Gadget */
|
||||
#define STRING_MANUFACTURER_IDX 0
|
||||
#define STRING_PRODUCT_IDX 1
|
||||
#define STRING_SERIAL_IDX 2
|
||||
|
||||
/* ********************************************************** */
|
||||
/* THOR protocol definitions */
|
||||
/* ********************************************************** */
|
||||
|
||||
/*
|
||||
* Attribute Vendor descriptor - necessary to prevent ZLP transmission
|
||||
* from Windows XP HOST PC
|
||||
*/
|
||||
struct usb_cdc_attribute_vendor_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubType;
|
||||
__u16 DAUType;
|
||||
__u16 DAULength;
|
||||
__u8 DAUValue;
|
||||
} __packed;
|
||||
|
||||
#define VER_PROTOCOL_MAJOR 4
|
||||
#define VER_PROTOCOL_MINOR 0
|
||||
|
||||
enum rqt {
|
||||
RQT_INFO = 200,
|
||||
RQT_CMD,
|
||||
RQT_DL,
|
||||
RQT_UL,
|
||||
};
|
||||
|
||||
enum rqt_data {
|
||||
/* RQT_INFO */
|
||||
RQT_INFO_VER_PROTOCOL = 1,
|
||||
RQT_INIT_VER_HW,
|
||||
RQT_INIT_VER_BOOT,
|
||||
RQT_INIT_VER_KERNEL,
|
||||
RQT_INIT_VER_PLATFORM,
|
||||
RQT_INIT_VER_CSC,
|
||||
|
||||
/* RQT_CMD */
|
||||
RQT_CMD_REBOOT = 1,
|
||||
RQT_CMD_POWEROFF,
|
||||
RQT_CMD_EFSCLEAR,
|
||||
|
||||
/* RQT_DL */
|
||||
RQT_DL_INIT = 1,
|
||||
RQT_DL_FILE_INFO,
|
||||
RQT_DL_FILE_START,
|
||||
RQT_DL_FILE_END,
|
||||
RQT_DL_EXIT,
|
||||
|
||||
/* RQT_UL */
|
||||
RQT_UL_INIT = 1,
|
||||
RQT_UL_START,
|
||||
RQT_UL_END,
|
||||
RQT_UL_EXIT,
|
||||
};
|
||||
|
||||
struct rqt_box { /* total: 256B */
|
||||
s32 rqt; /* request id */
|
||||
s32 rqt_data; /* request data id */
|
||||
s32 int_data[14]; /* int data */
|
||||
char str_data[5][32]; /* string data */
|
||||
char md5[32]; /* md5 checksum */
|
||||
} __packed;
|
||||
|
||||
struct rsp_box { /* total: 128B */
|
||||
s32 rsp; /* response id (= request id) */
|
||||
s32 rsp_data; /* response data id */
|
||||
s32 ack; /* ack */
|
||||
s32 int_data[5]; /* int data */
|
||||
char str_data[3][32]; /* string data */
|
||||
} __packed;
|
||||
|
||||
struct data_rsp_box { /* total: 8B */
|
||||
s32 ack; /* response id (= request id) */
|
||||
s32 count; /* response data id */
|
||||
} __packed;
|
||||
|
||||
enum {
|
||||
FILE_TYPE_NORMAL,
|
||||
FILE_TYPE_PIT,
|
||||
};
|
||||
|
||||
struct thor_dev {
|
||||
struct usb_gadget *gadget;
|
||||
struct usb_request *req; /* EP0 -> control responses */
|
||||
|
||||
/* IN/OUT EP's and correspoinding requests */
|
||||
struct usb_ep *in_ep, *out_ep, *int_ep;
|
||||
struct usb_request *in_req, *out_req;
|
||||
|
||||
/* Control flow variables */
|
||||
unsigned char configuration_done;
|
||||
unsigned char rxdata;
|
||||
unsigned char txdata;
|
||||
};
|
||||
|
||||
struct f_thor {
|
||||
struct usb_function usb_function;
|
||||
struct thor_dev *dev;
|
||||
};
|
||||
|
||||
#define F_NAME_BUF_SIZE 32
|
||||
#define THOR_PACKET_SIZE SZ_1M /* 1 MiB */
|
||||
#define THOR_STORE_UNIT_SIZE SZ_32M /* 32 MiB */
|
||||
#endif /* _USB_THOR_H_ */
|
@ -16,6 +16,7 @@
|
||||
#include <g_dnl.h>
|
||||
#include <usb_mass_storage.h>
|
||||
#include <dfu.h>
|
||||
#include <thor.h>
|
||||
|
||||
#include "gadget_chips.h"
|
||||
#include "composite.c"
|
||||
@ -79,6 +80,8 @@ static int g_dnl_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
|
||||
free(cdev->config);
|
||||
cdev->config = NULL;
|
||||
debug("%s: calling usb_gadget_disconnect for "
|
||||
"controller '%s'\n", shortname, gadget->name);
|
||||
usb_gadget_disconnect(gadget);
|
||||
@ -99,26 +102,34 @@ static int g_dnl_do_config(struct usb_configuration *c)
|
||||
ret = dfu_add(c);
|
||||
else if (!strcmp(s, "usb_dnl_ums"))
|
||||
ret = fsg_add(c);
|
||||
else if (!strcmp(s, "usb_dnl_thor"))
|
||||
ret = thor_add(c);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int g_dnl_config_register(struct usb_composite_dev *cdev)
|
||||
{
|
||||
static struct usb_configuration config = {
|
||||
.label = "usb_dnload",
|
||||
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
|
||||
.bConfigurationValue = CONFIGURATION_NUMBER,
|
||||
.iConfiguration = STRING_USBDOWN,
|
||||
struct usb_configuration *config;
|
||||
const char *name = "usb_dnload";
|
||||
|
||||
.bind = g_dnl_do_config,
|
||||
};
|
||||
config = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*config));
|
||||
if (!config)
|
||||
return -ENOMEM;
|
||||
|
||||
return usb_add_config(cdev, &config);
|
||||
memset(config, 0, sizeof(*config));
|
||||
|
||||
config->label = name;
|
||||
config->bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER;
|
||||
config->bConfigurationValue = CONFIGURATION_NUMBER;
|
||||
config->iConfiguration = STRING_USBDOWN;
|
||||
config->bind = g_dnl_do_config;
|
||||
|
||||
return usb_add_config(cdev, config);
|
||||
}
|
||||
|
||||
__weak
|
||||
int g_dnl_bind_fixup(struct usb_device_descriptor *dev)
|
||||
int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -145,7 +156,7 @@ static int g_dnl_bind(struct usb_composite_dev *cdev)
|
||||
g_dnl_string_defs[1].id = id;
|
||||
device_desc.iProduct = id;
|
||||
|
||||
g_dnl_bind_fixup(&device_desc);
|
||||
g_dnl_bind_fixup(&device_desc, cdev->driver->name);
|
||||
ret = g_dnl_config_register(cdev);
|
||||
if (ret)
|
||||
goto error;
|
||||
@ -183,8 +194,8 @@ static struct usb_composite_driver g_dnl_driver = {
|
||||
|
||||
int g_dnl_register(const char *type)
|
||||
{
|
||||
/* We only allow "dfu" atm, so 3 should be enough */
|
||||
static char name[sizeof(shortname) + 3];
|
||||
/* The largest function name is 4 */
|
||||
static char name[sizeof(shortname) + 4];
|
||||
int ret;
|
||||
|
||||
if (!strcmp(type, "dfu")) {
|
||||
@ -193,6 +204,9 @@ int g_dnl_register(const char *type)
|
||||
} else if (!strcmp(type, "ums")) {
|
||||
strcpy(name, shortname);
|
||||
strcat(name, type);
|
||||
} else if (!strcmp(type, "thor")) {
|
||||
strcpy(name, shortname);
|
||||
strcat(name, type);
|
||||
} else {
|
||||
printf("%s: unknown command: %s\n", __func__, type);
|
||||
return -EINVAL;
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include <commproc.h>
|
||||
#include <usbdevice.h>
|
||||
#include <usb/mpc8xx_udc.h>
|
||||
#include <usb/udc.h>
|
||||
|
||||
#include "ep0.h"
|
||||
|
||||
|
@ -13,13 +13,16 @@
|
||||
#include <config.h>
|
||||
#include <net.h>
|
||||
#include <malloc.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <usb/mv_udc.h>
|
||||
|
||||
#if CONFIG_USB_MAX_CONTROLLER_COUNT > 1
|
||||
#error This driver only supports one single controller.
|
||||
#endif
|
||||
#include "../host/ehci.h"
|
||||
#include "mv_udc.h"
|
||||
|
||||
/*
|
||||
* Check if the system has too long cachelines. If the cachelines are
|
||||
@ -107,6 +110,7 @@ static struct mv_drv controller = {
|
||||
.gadget = {
|
||||
.name = "mv_udc",
|
||||
.ops = &mv_udc_ops,
|
||||
.is_dualspeed = 1,
|
||||
},
|
||||
};
|
||||
|
||||
@ -210,12 +214,10 @@ static void mv_ep_free_request(struct usb_ep *ep, struct usb_request *_req)
|
||||
return;
|
||||
}
|
||||
|
||||
static void ep_enable(int num, int in)
|
||||
static void ep_enable(int num, int in, int maxpacket)
|
||||
{
|
||||
struct ept_queue_head *head;
|
||||
struct mv_udc *udc = (struct mv_udc *)controller.ctrl->hcor;
|
||||
unsigned n;
|
||||
head = mv_get_qh(num, in);
|
||||
|
||||
n = readl(&udc->epctrl[num]);
|
||||
if (in)
|
||||
@ -224,7 +226,9 @@ static void ep_enable(int num, int in)
|
||||
n |= (CTRL_RXE | CTRL_RXR | CTRL_RXT_BULK);
|
||||
|
||||
if (num != 0) {
|
||||
head->config = CONFIG_MAX_PKT(EP_MAX_PACKET_SIZE) | CONFIG_ZLT;
|
||||
struct ept_queue_head *head = mv_get_qh(num, in);
|
||||
|
||||
head->config = CONFIG_MAX_PKT(maxpacket) | CONFIG_ZLT;
|
||||
mv_flush_qh(num);
|
||||
}
|
||||
writel(n, &udc->epctrl[num]);
|
||||
@ -237,17 +241,33 @@ static int mv_ep_enable(struct usb_ep *ep,
|
||||
int num, in;
|
||||
num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
|
||||
in = (desc->bEndpointAddress & USB_DIR_IN) != 0;
|
||||
ep_enable(num, in);
|
||||
mv_ep->desc = desc;
|
||||
|
||||
if (num) {
|
||||
int max = get_unaligned_le16(&desc->wMaxPacketSize);
|
||||
|
||||
if ((max > 64) && (controller.gadget.speed == USB_SPEED_FULL))
|
||||
max = 64;
|
||||
if (ep->maxpacket != max) {
|
||||
DBG("%s: from %d to %d\n", __func__,
|
||||
ep->maxpacket, max);
|
||||
ep->maxpacket = max;
|
||||
}
|
||||
}
|
||||
ep_enable(num, in, ep->maxpacket);
|
||||
DBG("%s: num=%d maxpacket=%d\n", __func__, num, ep->maxpacket);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_ep_disable(struct usb_ep *ep)
|
||||
{
|
||||
struct mv_ep *mv_ep = container_of(ep, struct mv_ep, ep);
|
||||
|
||||
mv_ep->desc = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_bounce(struct mv_ep *ep)
|
||||
static int mv_bounce(struct mv_ep *ep, int in)
|
||||
{
|
||||
uint32_t addr = (uint32_t)ep->req.buf;
|
||||
uint32_t ba;
|
||||
@ -276,8 +296,8 @@ align:
|
||||
if (!ep->b_buf)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(ep->b_buf, ep->req.buf, ep->req.length);
|
||||
if (in)
|
||||
memcpy(ep->b_buf, ep->req.buf, ep->req.length);
|
||||
|
||||
flush:
|
||||
ba = (uint32_t)ep->b_buf;
|
||||
@ -286,29 +306,25 @@ flush:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mv_debounce(struct mv_ep *ep)
|
||||
static void mv_debounce(struct mv_ep *ep, int in)
|
||||
{
|
||||
uint32_t addr = (uint32_t)ep->req.buf;
|
||||
uint32_t ba = (uint32_t)ep->b_buf;
|
||||
|
||||
if (in) {
|
||||
if (addr == ba)
|
||||
return; /* not a bounce */
|
||||
goto free;
|
||||
}
|
||||
invalidate_dcache_range(ba, ba + ep->b_len);
|
||||
|
||||
/* Input buffer address is not aligned. */
|
||||
if (addr & (ARCH_DMA_MINALIGN - 1))
|
||||
goto copy;
|
||||
if (addr == ba)
|
||||
return; /* not a bounce */
|
||||
|
||||
/* Input buffer length is not aligned. */
|
||||
if (ep->req.length & (ARCH_DMA_MINALIGN - 1))
|
||||
goto copy;
|
||||
|
||||
/* The buffer is well aligned, only invalidate cache. */
|
||||
return;
|
||||
|
||||
copy:
|
||||
memcpy(ep->req.buf, ep->b_buf, ep->req.length);
|
||||
|
||||
free:
|
||||
/* Large payloads use allocated buffer, free it. */
|
||||
if (ep->req.length > 64)
|
||||
if (ep->b_buf != ep->b_fast)
|
||||
free(ep->b_buf);
|
||||
}
|
||||
|
||||
@ -326,7 +342,7 @@ static int mv_ep_queue(struct usb_ep *ep,
|
||||
head = mv_get_qh(num, in);
|
||||
len = req->length;
|
||||
|
||||
ret = mv_bounce(mv_ep);
|
||||
ret = mv_bounce(mv_ep, in);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -334,21 +350,20 @@ static int mv_ep_queue(struct usb_ep *ep,
|
||||
item->info = INFO_BYTES(len) | INFO_IOC | INFO_ACTIVE;
|
||||
item->page0 = (uint32_t)mv_ep->b_buf;
|
||||
item->page1 = ((uint32_t)mv_ep->b_buf & 0xfffff000) + 0x1000;
|
||||
mv_flush_qtd(num);
|
||||
|
||||
head->next = (unsigned) item;
|
||||
head->info = 0;
|
||||
|
||||
DBG("ept%d %s queue len %x, buffer %p\n",
|
||||
num, in ? "in" : "out", len, mv_ep->b_buf);
|
||||
mv_flush_qh(num);
|
||||
|
||||
if (in)
|
||||
bit = EPT_TX(num);
|
||||
else
|
||||
bit = EPT_RX(num);
|
||||
|
||||
mv_flush_qh(num);
|
||||
mv_flush_qtd(num);
|
||||
|
||||
writel(bit, &udc->epprime);
|
||||
|
||||
return 0;
|
||||
@ -366,14 +381,13 @@ static void handle_ep_complete(struct mv_ep *ep)
|
||||
mv_invalidate_qtd(num);
|
||||
|
||||
if (item->info & 0xff)
|
||||
printf("EP%d/%s FAIL nfo=%x pg0=%x\n",
|
||||
num, in ? "in" : "out", item->info, item->page0);
|
||||
printf("EP%d/%s FAIL info=%x pg0=%x\n",
|
||||
num, in ? "in" : "out", item->info, item->page0);
|
||||
|
||||
len = (item->info >> 16) & 0x7fff;
|
||||
|
||||
mv_debounce(ep);
|
||||
|
||||
ep->req.length -= len;
|
||||
mv_debounce(ep, in);
|
||||
|
||||
DBG("ept%d %s complete %x\n",
|
||||
num, in ? "in" : "out", len);
|
||||
ep->req.complete(&ep->ep, &ep->req);
|
||||
@ -411,14 +425,16 @@ static void handle_setup(void)
|
||||
if ((r.wValue == 0) && (r.wLength == 0)) {
|
||||
req->length = 0;
|
||||
for (i = 0; i < NUM_ENDPOINTS; i++) {
|
||||
if (!controller.ep[i].desc)
|
||||
struct mv_ep *ep = &controller.ep[i];
|
||||
|
||||
if (!ep->desc)
|
||||
continue;
|
||||
num = controller.ep[i].desc->bEndpointAddress
|
||||
num = ep->desc->bEndpointAddress
|
||||
& USB_ENDPOINT_NUMBER_MASK;
|
||||
in = (controller.ep[i].desc->bEndpointAddress
|
||||
in = (ep->desc->bEndpointAddress
|
||||
& USB_DIR_IN) != 0;
|
||||
if ((num == _num) && (in == _in)) {
|
||||
ep_enable(num, in);
|
||||
ep_enable(num, in, ep->ep.maxpacket);
|
||||
usb_ep_queue(controller.gadget.ep0,
|
||||
req, 0);
|
||||
break;
|
||||
@ -502,15 +518,19 @@ void udc_irq(void)
|
||||
DBG("-- suspend --\n");
|
||||
|
||||
if (n & STS_PCI) {
|
||||
DBG("-- portchange --\n");
|
||||
int max = 64;
|
||||
int speed = USB_SPEED_FULL;
|
||||
|
||||
bit = (readl(&udc->portsc) >> 26) & 3;
|
||||
DBG("-- portchange %x %s\n", bit, (bit == 2) ? "High" : "Full");
|
||||
if (bit == 2) {
|
||||
controller.gadget.speed = USB_SPEED_HIGH;
|
||||
for (i = 1; i < NUM_ENDPOINTS && n; i++)
|
||||
if (controller.ep[i].desc)
|
||||
controller.ep[i].ep.maxpacket = 512;
|
||||
} else {
|
||||
controller.gadget.speed = USB_SPEED_FULL;
|
||||
speed = USB_SPEED_HIGH;
|
||||
max = 512;
|
||||
}
|
||||
controller.gadget.speed = speed;
|
||||
for (i = 1; i < NUM_ENDPOINTS; i++) {
|
||||
if (controller.ep[i].ep.maxpacket > max)
|
||||
controller.ep[i].ep.maxpacket = max;
|
||||
}
|
||||
}
|
||||
|
||||
@ -626,6 +646,7 @@ static int mvudc_probe(void)
|
||||
free(controller.epts);
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(controller.items_mem, 0, ilist_sz);
|
||||
|
||||
for (i = 0; i < 2 * NUM_ENDPOINTS; i++) {
|
||||
/*
|
||||
@ -688,7 +709,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
|
||||
if (driver->speed != USB_SPEED_FULL && driver->speed != USB_SPEED_HIGH)
|
||||
return -EINVAL;
|
||||
|
||||
ret = usb_lowlevel_init(0, (void **)&controller.ctrl);
|
||||
ret = usb_lowlevel_init(0, USB_INIT_DEVICE, (void **)&controller.ctrl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
115
drivers/usb/gadget/mv_udc.h
Normal file
115
drivers/usb/gadget/mv_udc.h
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2011, Marvell Semiconductor Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
#ifndef __GADGET__MV_UDC_H__
|
||||
#define __GADGET__MV_UDC_H__
|
||||
|
||||
#define NUM_ENDPOINTS 6
|
||||
|
||||
struct mv_udc {
|
||||
#define MICRO_8FRAME 0x8
|
||||
#define USBCMD_ITC(x) ((((x) > 0xff) ? 0xff : x) << 16)
|
||||
#define USBCMD_FS2 (1 << 15)
|
||||
#define USBCMD_RST (1 << 1)
|
||||
#define USBCMD_RUN (1)
|
||||
u32 usbcmd; /* 0x140 */
|
||||
#define STS_SLI (1 << 8)
|
||||
#define STS_URI (1 << 6)
|
||||
#define STS_PCI (1 << 2)
|
||||
#define STS_UEI (1 << 1)
|
||||
#define STS_UI (1 << 0)
|
||||
u32 usbsts; /* 0x144 */
|
||||
u32 pad1[3];
|
||||
u32 devaddr; /* 0x154 */
|
||||
u32 epinitaddr; /* 0x158 */
|
||||
u32 pad2[10];
|
||||
#define PTS_ENABLE 2
|
||||
#define PTS(x) (((x) & 0x3) << 30)
|
||||
#define PFSC (1 << 24)
|
||||
u32 portsc; /* 0x184 */
|
||||
u32 pad3[8];
|
||||
#define USBMODE_DEVICE 2
|
||||
u32 usbmode; /* 0x1a8 */
|
||||
u32 epstat; /* 0x1ac */
|
||||
#define EPT_TX(x) (1 << (((x) & 0xffff) + 16))
|
||||
#define EPT_RX(x) (1 << ((x) & 0xffff))
|
||||
u32 epprime; /* 0x1b0 */
|
||||
u32 epflush; /* 0x1b4 */
|
||||
u32 pad4;
|
||||
u32 epcomp; /* 0x1bc */
|
||||
#define CTRL_TXE (1 << 23)
|
||||
#define CTRL_TXR (1 << 22)
|
||||
#define CTRL_RXE (1 << 7)
|
||||
#define CTRL_RXR (1 << 6)
|
||||
#define CTRL_TXT_BULK (2 << 18)
|
||||
#define CTRL_RXT_BULK (2 << 2)
|
||||
u32 epctrl[16]; /* 0x1c0 */
|
||||
};
|
||||
|
||||
struct mv_ep {
|
||||
struct usb_ep ep;
|
||||
struct list_head queue;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
|
||||
struct usb_request req;
|
||||
uint8_t *b_buf;
|
||||
uint32_t b_len;
|
||||
uint8_t b_fast[64] __aligned(ARCH_DMA_MINALIGN);
|
||||
};
|
||||
|
||||
struct mv_drv {
|
||||
struct usb_gadget gadget;
|
||||
struct usb_gadget_driver *driver;
|
||||
struct ehci_ctrl *ctrl;
|
||||
struct ept_queue_head *epts;
|
||||
struct ept_queue_item *items[2 * NUM_ENDPOINTS];
|
||||
uint8_t *items_mem;
|
||||
struct mv_ep ep[NUM_ENDPOINTS];
|
||||
};
|
||||
|
||||
struct ept_queue_head {
|
||||
unsigned config;
|
||||
unsigned current; /* read-only */
|
||||
|
||||
unsigned next;
|
||||
unsigned info;
|
||||
unsigned page0;
|
||||
unsigned page1;
|
||||
unsigned page2;
|
||||
unsigned page3;
|
||||
unsigned page4;
|
||||
unsigned reserved_0;
|
||||
|
||||
unsigned char setup_data[8];
|
||||
|
||||
unsigned reserved_1;
|
||||
unsigned reserved_2;
|
||||
unsigned reserved_3;
|
||||
unsigned reserved_4;
|
||||
};
|
||||
|
||||
#define CONFIG_MAX_PKT(n) ((n) << 16)
|
||||
#define CONFIG_ZLT (1 << 29) /* stop on zero-len xfer */
|
||||
#define CONFIG_IOS (1 << 15) /* IRQ on setup */
|
||||
|
||||
struct ept_queue_item {
|
||||
unsigned next;
|
||||
unsigned info;
|
||||
unsigned page0;
|
||||
unsigned page1;
|
||||
unsigned page2;
|
||||
unsigned page3;
|
||||
unsigned page4;
|
||||
unsigned reserved;
|
||||
};
|
||||
|
||||
#define TERMINATE 1
|
||||
#define INFO_BYTES(n) ((n) << 16)
|
||||
#define INFO_IOC (1 << 15)
|
||||
#define INFO_ACTIVE (1 << 7)
|
||||
#define INFO_HALTED (1 << 6)
|
||||
#define INFO_BUFFER_ERROR (1 << 5)
|
||||
#define INFO_TX_ERROR (1 << 3)
|
||||
#endif
|
@ -20,6 +20,7 @@
|
||||
#endif
|
||||
#include <usbdevice.h>
|
||||
#include <usb/omap1510_udc.h>
|
||||
#include <usb/udc.h>
|
||||
|
||||
#include "ep0.h"
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <asm/io.h>
|
||||
#include <usb/pxa27x_udc.h>
|
||||
#include <usb/udc.h>
|
||||
|
||||
#include "ep0.h"
|
||||
|
||||
|
@ -117,7 +117,8 @@ static int setdma_rx(struct s3c_ep *ep, struct s3c_request *req)
|
||||
|
||||
invalidate_dcache_range((unsigned long) ep->dev->dma_buf[ep_num],
|
||||
(unsigned long) ep->dev->dma_buf[ep_num]
|
||||
+ DMA_BUFFER_SIZE);
|
||||
+ ROUND(ep->ep.maxpacket,
|
||||
CONFIG_SYS_CACHELINE_SIZE));
|
||||
|
||||
if (length == 0)
|
||||
pktcnt = 1;
|
||||
|
@ -42,6 +42,11 @@ COBJS-$(CONFIG_USB_EHCI_SPEAR) += ehci-spear.o
|
||||
COBJS-$(CONFIG_USB_EHCI_TEGRA) += ehci-tegra.o
|
||||
COBJS-$(CONFIG_USB_EHCI_VCT) += ehci-vct.o
|
||||
|
||||
# xhci
|
||||
COBJS-$(CONFIG_USB_XHCI) += xhci.o xhci-mem.o xhci-ring.o
|
||||
COBJS-$(CONFIG_USB_XHCI_EXYNOS) += xhci-exynos5.o
|
||||
COBJS-$(CONFIG_USB_XHCI_OMAP) += xhci-omap.o
|
||||
|
||||
COBJS := $(COBJS-y)
|
||||
SRCS := $(COBJS:.o=.c)
|
||||
OBJS := $(addprefix $(obj),$(COBJS))
|
||||
|
@ -22,7 +22,8 @@
|
||||
/*
|
||||
* EHCI host controller init
|
||||
*/
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
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;
|
||||
|
@ -21,7 +21,8 @@
|
||||
*/
|
||||
#define EN_UPLL_TIMEOUT 500UL
|
||||
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
at91_pmc_t *pmc = (at91_pmc_t *)ATMEL_BASE_PMC;
|
||||
ulong start_time, tmp_time;
|
||||
|
@ -16,6 +16,7 @@
|
||||
#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"
|
||||
@ -30,6 +31,7 @@ DECLARE_GLOBAL_DATA_PTR;
|
||||
struct exynos_ehci {
|
||||
struct exynos_usb_phy *usb;
|
||||
struct ehci_hccr *hcd;
|
||||
struct fdt_gpio_state vbus_gpio;
|
||||
};
|
||||
|
||||
static struct exynos_ehci exynos;
|
||||
@ -58,6 +60,9 @@ static int exynos_usb_parse_dt(const void *blob, struct exynos_ehci *exynos)
|
||||
|
||||
exynos->hcd = (struct ehci_hccr *)addr;
|
||||
|
||||
/* Vbus gpio */
|
||||
fdtdec_decode_gpio(blob, node, "samsung,vbus-gpio", &exynos->vbus_gpio);
|
||||
|
||||
depth = 0;
|
||||
node = fdtdec_next_compatible_subnode(blob, node,
|
||||
COMPAT_SAMSUNG_EXYNOS_USB_PHY, &depth);
|
||||
@ -136,7 +141,8 @@ static void reset_usb_phy(struct exynos_usb_phy *usb)
|
||||
* Create the appropriate control structures to manage
|
||||
* a new EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
struct exynos_ehci *ctx = &exynos;
|
||||
|
||||
@ -150,6 +156,12 @@ int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
ctx->hcd = (struct ehci_hccr *)samsung_get_base_usb_ehci();
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF_CONTROL
|
||||
/* setup the Vbus gpio here */
|
||||
if (!fdtdec_setup_gpio(&ctx->vbus_gpio))
|
||||
gpio_direction_output(ctx->vbus_gpio.gpio, 1);
|
||||
#endif
|
||||
|
||||
setup_usb_phy(ctx->usb);
|
||||
|
||||
*hccr = ctx->hcd;
|
||||
|
@ -33,8 +33,8 @@ static inline int ehci_is_fotg2xx(union ehci_faraday_regs *regs)
|
||||
* Create the appropriate control structures to manage
|
||||
* a new EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **ret_hccr,
|
||||
struct ehci_hcor **ret_hcor)
|
||||
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;
|
||||
|
@ -35,7 +35,8 @@ static int usb_phy_clk_valid(struct usb_ehci *ehci)
|
||||
*
|
||||
* Excerpts from linux ehci fsl driver.
|
||||
*/
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
struct usb_ehci *ehci;
|
||||
const char *phy_type = NULL;
|
||||
|
@ -919,24 +919,29 @@ int usb_lowlevel_stop(int index)
|
||||
return ehci_hcd_stop(index);
|
||||
}
|
||||
|
||||
int usb_lowlevel_init(int index, void **controller)
|
||||
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
|
||||
{
|
||||
uint32_t reg;
|
||||
uint32_t cmd;
|
||||
struct QH *qh_list;
|
||||
struct QH *periodic;
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
if (ehci_hcd_init(index, &ehcic[index].hccr, &ehcic[index].hcor))
|
||||
return -1;
|
||||
rc = ehci_hcd_init(index, init, &ehcic[index].hccr, &ehcic[index].hcor);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (init == USB_INIT_DEVICE)
|
||||
goto done;
|
||||
|
||||
/* EHCI spec section 4.1 */
|
||||
if (ehci_reset(index))
|
||||
return -1;
|
||||
|
||||
#if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET)
|
||||
if (ehci_hcd_init(index, &ehcic[index].hccr, &ehcic[index].hcor))
|
||||
return -1;
|
||||
rc = ehci_hcd_init(index, init, &ehcic[index].hccr, &ehcic[index].hcor);
|
||||
if (rc)
|
||||
return rc;
|
||||
#endif
|
||||
/* Set the high address word (aka segment) for 64-bit controller */
|
||||
if (ehci_readl(&ehcic[index].hccr->cr_hccparams) & 1)
|
||||
@ -1037,7 +1042,7 @@ int usb_lowlevel_init(int index, void **controller)
|
||||
printf("USB EHCI %x.%02x\n", reg >> 8, reg & 0xff);
|
||||
|
||||
ehcic[index].rootdev = 0;
|
||||
|
||||
done:
|
||||
*controller = &ehcic[index];
|
||||
return 0;
|
||||
}
|
||||
|
@ -14,7 +14,8 @@
|
||||
* Create the appropriate control structures to manage
|
||||
* a new EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
*hccr = (struct ehci_hccr *)(0xcd000100);
|
||||
*hcor = (struct ehci_hcor *)((uint32_t) *hccr
|
||||
|
@ -74,7 +74,8 @@ static void usb_brg_adrdec_setup(void)
|
||||
* Create the appropriate control structures to manage
|
||||
* a new EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
usb_brg_adrdec_setup();
|
||||
|
||||
|
@ -32,7 +32,8 @@ static void usb_platform_dr_init(volatile struct usb_ehci *ehci);
|
||||
* This code is derived from EHCI FSL USB Linux driver for MPC5121
|
||||
*
|
||||
*/
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
volatile struct usb_ehci *ehci;
|
||||
|
||||
|
@ -218,7 +218,8 @@ void __weak board_ehci_hcd_postinit(struct usb_ehci *ehci, int port)
|
||||
{
|
||||
}
|
||||
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
struct usb_ehci *ehci;
|
||||
|
||||
|
@ -35,6 +35,7 @@
|
||||
#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
|
||||
@ -49,52 +50,84 @@
|
||||
#define UCTRL_OVER_CUR_DIS (1 << 7) /* Disable OTG Overcurrent Detection */
|
||||
|
||||
/* USBCMD */
|
||||
#define UH1_USBCMD_OFFSET 0x140
|
||||
#define UCMD_RUN_STOP (1 << 0) /* controller run/stop */
|
||||
#define UCMD_RESET (1 << 1) /* controller reset */
|
||||
|
||||
static void usbh1_internal_phy_clock_gate(int on)
|
||||
{
|
||||
void __iomem *phy_reg = (void __iomem *)USB_PHY1_BASE_ADDR;
|
||||
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;
|
||||
__raw_writel(USBPHY_CTRL_CLKGATE, phy_reg);
|
||||
}
|
||||
|
||||
static void usbh1_power_config(void)
|
||||
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 for host1
|
||||
* 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 for host 1
|
||||
* 2. The PLL's power and output to usb
|
||||
* is totally controlled by IC, so the Software only needs
|
||||
* to enable them at initializtion.
|
||||
*/
|
||||
__raw_writel(ANADIG_USB2_CHRG_DETECT_EN_B |
|
||||
ANADIG_USB2_CHRG_DETECT_CHK_CHRG_B,
|
||||
&anatop->usb2_chrg_detect);
|
||||
chrg_detect);
|
||||
|
||||
__raw_writel(ANADIG_USB2_PLL_480_CTRL_BYPASS,
|
||||
&anatop->usb2_pll_480_ctrl_clr);
|
||||
pll_480_ctrl_clr);
|
||||
|
||||
__raw_writel(ANADIG_USB2_PLL_480_CTRL_ENABLE |
|
||||
ANADIG_USB2_PLL_480_CTRL_POWER |
|
||||
ANADIG_USB2_PLL_480_CTRL_EN_USB_CLKS,
|
||||
&anatop->usb2_pll_480_ctrl_set);
|
||||
pll_480_ctrl_set);
|
||||
}
|
||||
|
||||
static int usbh1_phy_enable(void)
|
||||
/* Return 0 : host node, <>0 : device mode */
|
||||
static int usb_phy_enable(int index, struct usb_ehci *ehci)
|
||||
{
|
||||
void __iomem *phy_reg = (void __iomem *)USB_PHY1_BASE_ADDR;
|
||||
void __iomem *phy_ctrl = (void __iomem *)(phy_reg + USBPHY_CTRL);
|
||||
void __iomem *usb_cmd = (void __iomem *)(USBOH3_USB_BASE_ADDR +
|
||||
USB_H1REGS_OFFSET +
|
||||
UH1_USBCMD_OFFSET);
|
||||
void __iomem *phy_reg;
|
||||
void __iomem *phy_ctrl;
|
||||
void __iomem *usb_cmd;
|
||||
u32 val;
|
||||
|
||||
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 */
|
||||
val = __raw_readl(usb_cmd);
|
||||
val &= ~UCMD_RUN_STOP;
|
||||
@ -123,31 +156,41 @@ static int usbh1_phy_enable(void)
|
||||
/* Power up the PHY */
|
||||
__raw_writel(0, phy_reg + USBPHY_PWD);
|
||||
/* enable FS/LS device */
|
||||
val = __raw_readl(phy_reg + USBPHY_CTRL);
|
||||
val = __raw_readl(phy_ctrl);
|
||||
val |= (USBPHY_CTRL_ENUTMILEVEL2 | USBPHY_CTRL_ENUTMILEVEL3);
|
||||
__raw_writel(val, phy_reg + USBPHY_CTRL);
|
||||
__raw_writel(val, phy_ctrl);
|
||||
|
||||
return 0;
|
||||
return val & USBPHY_CTRL_OTG_ID;
|
||||
}
|
||||
|
||||
static void usbh1_oc_config(void)
|
||||
/* 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;
|
||||
};
|
||||
|
||||
static void usb_oc_config(int index)
|
||||
{
|
||||
void __iomem *usb_base = (void __iomem *)USBOH3_USB_BASE_ADDR;
|
||||
void __iomem *usbother_base = usb_base + USB_OTHERREGS_OFFSET;
|
||||
struct usbnc_regs *usbnc = (struct usbnc_regs *)(USBOH3_USB_BASE_ADDR +
|
||||
USB_OTHERREGS_OFFSET);
|
||||
void __iomem *ctrl = (void __iomem *)(&usbnc->ctrl[index]);
|
||||
u32 val;
|
||||
|
||||
val = __raw_readl(usbother_base + USB_H1_CTRL_OFFSET);
|
||||
val = __raw_readl(ctrl);
|
||||
#if CONFIG_MACH_TYPE == MACH_TYPE_MX6Q_ARM2
|
||||
/* mx6qarm2 seems to required a different setting*/
|
||||
val &= ~UCTRL_OVER_CUR_POL;
|
||||
#else
|
||||
val |= UCTRL_OVER_CUR_POL;
|
||||
#endif
|
||||
__raw_writel(val, usbother_base + USB_H1_CTRL_OFFSET);
|
||||
__raw_writel(val, ctrl);
|
||||
|
||||
val = __raw_readl(usbother_base + USB_H1_CTRL_OFFSET);
|
||||
val = __raw_readl(ctrl);
|
||||
val |= UCTRL_OVER_CUR_DIS;
|
||||
__raw_writel(val, usbother_base + USB_H1_CTRL_OFFSET);
|
||||
__raw_writel(val, ctrl);
|
||||
}
|
||||
|
||||
int __weak board_ehci_hcd_init(int port)
|
||||
@ -155,33 +198,42 @@ int __weak board_ehci_hcd_init(int port)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
int __weak board_ehci_power(int port, int on)
|
||||
{
|
||||
struct usb_ehci *ehci;
|
||||
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;
|
||||
struct usb_ehci *ehci = (struct usb_ehci *)(USBOH3_USB_BASE_ADDR +
|
||||
(0x200 * index));
|
||||
|
||||
if (index > 3)
|
||||
return -EINVAL;
|
||||
enable_usboh3_clk(1);
|
||||
mdelay(1);
|
||||
|
||||
/* Do board specific initialization */
|
||||
board_ehci_hcd_init(CONFIG_MXC_USB_PORT);
|
||||
board_ehci_hcd_init(index);
|
||||
|
||||
#if CONFIG_MXC_USB_PORT == 1
|
||||
/* USB Host 1 */
|
||||
usbh1_power_config();
|
||||
usbh1_oc_config();
|
||||
usbh1_internal_phy_clock_gate(1);
|
||||
usbh1_phy_enable();
|
||||
#else
|
||||
#error "MXC USB port not yet supported"
|
||||
#endif
|
||||
usb_power_config(index);
|
||||
usb_oc_config(index);
|
||||
usb_internal_phy_clock_gate(index, 1);
|
||||
type = usb_phy_enable(index, ehci) ? USB_INIT_DEVICE : USB_INIT_HOST;
|
||||
|
||||
ehci = (struct usb_ehci *)(USBOH3_USB_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);
|
||||
|
||||
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);
|
||||
__raw_writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc);
|
||||
setbits_le32(&ehci->portsc, USB_EN);
|
||||
|
||||
|
@ -208,7 +208,8 @@ static int mxc_set_usbcontrol(int port, unsigned int flags)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
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
|
||||
|
@ -77,7 +77,8 @@ static int ehci_mxs_toggle_clock(const struct ehci_mxs_port *port, int enable)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
|
||||
int ret;
|
||||
|
@ -96,12 +96,6 @@ static void omap_ehci_soft_phy_reset(int port)
|
||||
}
|
||||
#endif
|
||||
|
||||
inline int __board_usb_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int board_usb_init(void) __attribute__((weak, alias("__board_usb_init")));
|
||||
|
||||
#if defined(CONFIG_OMAP_EHCI_PHY1_RESET_GPIO) || \
|
||||
defined(CONFIG_OMAP_EHCI_PHY2_RESET_GPIO) || \
|
||||
defined(CONFIG_OMAP_EHCI_PHY3_RESET_GPIO)
|
||||
@ -157,15 +151,15 @@ int omap_ehci_hcd_stop(void)
|
||||
* Based on "drivers/usb/host/ehci-omap.c" from Linux 3.1
|
||||
* See there for additional Copyrights.
|
||||
*/
|
||||
int omap_ehci_hcd_init(struct omap_usbhs_board_data *usbhs_pdata,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
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();
|
||||
ret = board_usb_init(index, USB_INIT_HOST);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -69,8 +69,8 @@ static pci_dev_t ehci_find_class(int index)
|
||||
* Create the appropriate control structures to manage
|
||||
* a new EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **ret_hccr,
|
||||
struct ehci_hcor **ret_hcor)
|
||||
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;
|
||||
uint32_t cmd;
|
||||
|
@ -15,7 +15,8 @@
|
||||
* Create the appropriate control structures to manage
|
||||
* a new EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
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 +
|
||||
|
@ -20,7 +20,8 @@
|
||||
* Create the appropriate control structures to manage
|
||||
* a new EHCI host controller.
|
||||
*/
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
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_UHC0_EHCI_BASE + 0x100);
|
||||
*hcor = (struct ehci_hcor *)((uint32_t)*hccr
|
||||
|
@ -699,7 +699,7 @@ static int process_usb_nodes(const void *blob, int node_list[], int count)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int board_usb_init(const void *blob)
|
||||
int usb_process_devicetree(const void *blob)
|
||||
{
|
||||
int node_list[USB_PORTS_MAX];
|
||||
int count, err = 0;
|
||||
@ -734,7 +734,8 @@ int board_usb_init(const void *blob)
|
||||
* @param hcor returns start address of EHCI HCOR registers
|
||||
* @return 0 if ok, -1 on error (generally invalid port number)
|
||||
*/
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
struct fdt_usb *config;
|
||||
struct usb_ctlr *usbctlr;
|
||||
|
@ -15,7 +15,8 @@ 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, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
int ehci_hcd_init(int index, enum usb_init_type init,
|
||||
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
int ret;
|
||||
u32 vct_hccr;
|
||||
|
@ -28,22 +28,6 @@
|
||||
#define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS 2
|
||||
#endif
|
||||
|
||||
/* (shifted) direction/type/recipient from the USB 2.0 spec, table 9.2 */
|
||||
#define DeviceRequest \
|
||||
((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE) << 8)
|
||||
|
||||
#define DeviceOutRequest \
|
||||
((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE) << 8)
|
||||
|
||||
#define InterfaceRequest \
|
||||
((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8)
|
||||
|
||||
#define EndpointRequest \
|
||||
((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8)
|
||||
|
||||
#define EndpointOutRequest \
|
||||
((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8)
|
||||
|
||||
/*
|
||||
* Register Space.
|
||||
*/
|
||||
@ -266,7 +250,8 @@ struct ehci_ctrl {
|
||||
};
|
||||
|
||||
/* Low level init functions */
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor);
|
||||
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);
|
||||
|
||||
#endif /* USB_EHCI_H */
|
||||
|
@ -1377,7 +1377,7 @@ int isp116x_check_id(struct isp116x *isp116x)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_lowlevel_init(int index, void **controller))
|
||||
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller))
|
||||
{
|
||||
struct isp116x *isp116x = &isp116x_dev;
|
||||
|
||||
|
@ -1847,7 +1847,7 @@ static void hc_release_ohci(ohci_t *ohci)
|
||||
*/
|
||||
static char ohci_inited = 0;
|
||||
|
||||
int usb_lowlevel_init(int index, void **controller)
|
||||
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
|
||||
{
|
||||
#ifdef CONFIG_PCI_OHCI
|
||||
pci_dev_t pdev;
|
||||
@ -1861,7 +1861,7 @@ int usb_lowlevel_init(int index, void **controller)
|
||||
|
||||
#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT
|
||||
/* board dependant init */
|
||||
if (usb_board_init())
|
||||
if (board_usb_init(index, USB_INIT_HOST))
|
||||
return -1;
|
||||
#endif
|
||||
memset(&gohci, 0, sizeof(ohci_t));
|
||||
@ -1918,7 +1918,7 @@ int usb_lowlevel_init(int index, void **controller)
|
||||
err ("can't reset usb-%s", gohci.slot_name);
|
||||
#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT
|
||||
/* board dependant cleanup */
|
||||
usb_board_init_fail();
|
||||
board_usb_cleanup(index, USB_INIT_HOST);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYS_USB_OHCI_CPU_INIT
|
||||
|
@ -1642,7 +1642,7 @@ static void hc_release_ohci(struct ohci *ohci)
|
||||
*/
|
||||
static char ohci_inited = 0;
|
||||
|
||||
int usb_lowlevel_init(int index, void **controller)
|
||||
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
|
||||
{
|
||||
struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
|
||||
struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio();
|
||||
|
@ -19,14 +19,11 @@
|
||||
#endif /* CONFIG_SYS_OHCI_SWAP_REG_ACCESS */
|
||||
|
||||
/* functions for doing board or CPU specific setup/cleanup */
|
||||
extern int usb_board_init(void);
|
||||
extern int usb_board_stop(void);
|
||||
extern int usb_board_init_fail(void);
|
||||
|
||||
extern int usb_cpu_init(void);
|
||||
extern int usb_cpu_stop(void);
|
||||
extern int usb_cpu_init_fail(void);
|
||||
int usb_board_stop(void);
|
||||
|
||||
int usb_cpu_init(void);
|
||||
int usb_cpu_stop(void);
|
||||
int usb_cpu_init_fail(void);
|
||||
|
||||
static int cc_to_error[16] = {
|
||||
|
||||
|
@ -903,7 +903,7 @@ int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_lowlevel_init(int index, void **controller)
|
||||
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
|
||||
{
|
||||
struct r8a66597 *r8a66597 = &gr8a66597;
|
||||
|
||||
|
@ -194,7 +194,7 @@ static int sl811_hc_reset(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int usb_lowlevel_init(int index, void **controller)
|
||||
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
|
||||
{
|
||||
root_hub_devnum = 0;
|
||||
sl811_hc_reset();
|
||||
|
327
drivers/usb/host/xhci-exynos5.c
Normal file
327
drivers/usb/host/xhci-exynos5.c
Normal file
@ -0,0 +1,327 @@
|
||||
/*
|
||||
* 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 <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;
|
||||
|
||||
/**
|
||||
* Contains pointers to register base addresses
|
||||
* for the usb controller.
|
||||
*/
|
||||
struct exynos_xhci {
|
||||
struct exynos_usb3_phy *usb3_phy;
|
||||
struct xhci_hccr *hcd;
|
||||
struct dwc3 *dwc3_reg;
|
||||
struct fdt_gpio_state vbus_gpio;
|
||||
};
|
||||
|
||||
static struct exynos_xhci exynos;
|
||||
|
||||
#ifdef CONFIG_OF_CONTROL
|
||||
static int exynos_usb3_parse_dt(const void *blob, struct exynos_xhci *exynos)
|
||||
{
|
||||
fdt_addr_t addr;
|
||||
unsigned int node;
|
||||
int depth;
|
||||
|
||||
node = fdtdec_next_compatible(blob, 0, COMPAT_SAMSUNG_EXYNOS5_XHCI);
|
||||
if (node <= 0) {
|
||||
debug("XHCI: Can't get device node for xhci\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the base address for XHCI controller from the device node
|
||||
*/
|
||||
addr = fdtdec_get_addr(blob, node, "reg");
|
||||
if (addr == FDT_ADDR_T_NONE) {
|
||||
debug("Can't get the XHCI register base address\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
exynos->hcd = (struct xhci_hccr *)addr;
|
||||
|
||||
/* Vbus gpio */
|
||||
fdtdec_decode_gpio(blob, node, "samsung,vbus-gpio", &exynos->vbus_gpio);
|
||||
|
||||
depth = 0;
|
||||
node = fdtdec_next_compatible_subnode(blob, node,
|
||||
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
|
||||
*/
|
||||
exynos->usb3_phy = (struct exynos_usb3_phy *)fdtdec_get_addr(blob, node,
|
||||
"reg");
|
||||
if (exynos->usb3_phy == NULL) {
|
||||
debug("Can't get the usbphy register address\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
static 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);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* After PHYs are stable we can take Core out of reset state */
|
||||
clrbits_le32(&dwc3_reg->g_ctl,
|
||||
DWC3_GCTL_CORESOFTRESET);
|
||||
}
|
||||
|
||||
static 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 -EINVAL;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor)
|
||||
{
|
||||
struct exynos_xhci *ctx = &exynos;
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_OF_CONTROL
|
||||
exynos_usb3_parse_dt(gd->fdt_blob, ctx);
|
||||
#else
|
||||
ctx->usb3_phy = (struct exynos_usb3_phy *)samsung_get_base_usb3_phy();
|
||||
ctx->hcd = (struct xhci_hccr *)samsung_get_base_usb_xhci();
|
||||
#endif
|
||||
|
||||
ctx->dwc3_reg = (struct dwc3 *)((char *)(ctx->hcd) + DWC3_REG_OFFSET);
|
||||
|
||||
#ifdef CONFIG_OF_CONTROL
|
||||
/* setup the Vbus gpio here */
|
||||
if (!fdtdec_setup_gpio(&ctx->vbus_gpio))
|
||||
gpio_direction_output(ctx->vbus_gpio.gpio, 1);
|
||||
#endif
|
||||
|
||||
ret = exynos_xhci_core_init(ctx);
|
||||
if (ret) {
|
||||
puts("XHCI: failed to initialize controller\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*hccr = (ctx->hcd);
|
||||
*hcor = (struct xhci_hcor *)((uint32_t) *hccr
|
||||
+ HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));
|
||||
|
||||
debug("Exynos5-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 0;
|
||||
}
|
||||
|
||||
void xhci_hcd_stop(int index)
|
||||
{
|
||||
struct exynos_xhci *ctx = &exynos;
|
||||
|
||||
exynos_xhci_core_exit(ctx);
|
||||
}
|
720
drivers/usb/host/xhci-mem.c
Normal file
720
drivers/usb/host/xhci-mem.c
Normal file
@ -0,0 +1,720 @@
|
||||
/*
|
||||
* 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 <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(uint32_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(uint32_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((uint32_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 usb_device *udev)
|
||||
{
|
||||
u64 byte_64 = 0;
|
||||
unsigned int slot_id = udev->slot_id;
|
||||
struct xhci_virt_device *virt_dev;
|
||||
struct xhci_ctrl *ctrl = udev->controller;
|
||||
|
||||
/* 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((uint32_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((uint32_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 |= ((u32)(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 usb_device *udev)
|
||||
{
|
||||
struct usb_device *hop = udev;
|
||||
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;
|
||||
struct xhci_ctrl *ctrl = udev->controller;
|
||||
|
||||
virt_dev = ctrl->devs[udev->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 (udev->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();
|
||||
}
|
||||
|
||||
/* Extract the root hub port number */
|
||||
if (hop->parent)
|
||||
while (hop->parent->parent)
|
||||
hop = hop->parent;
|
||||
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", udev->speed);
|
||||
|
||||
switch (udev->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((uint32_t)ep0_ctx, sizeof(struct xhci_ep_ctx));
|
||||
xhci_flush_cache((uint32_t)slot_ctx, sizeof(struct xhci_slot_ctx));
|
||||
}
|
158
drivers/usb/host/xhci-omap.c
Normal file
158
drivers/usb/host/xhci-omap.c
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
inline int __board_usb_init(int index, enum board_usb_init_type init)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int board_usb_init(int index, enum board_usb_init_type init) \
|
||||
__attribute__((weak, alias("__board_usb_init")));
|
||||
|
||||
static 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));
|
||||
}
|
||||
|
||||
static 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);
|
||||
|
||||
omap_reset_usb_phy(dwc3_reg);
|
||||
|
||||
/* After PHYs are stable we can take Core out of reset state */
|
||||
clrbits_le32(&dwc3_reg->g_ctl, DWC3_GCTL_CORESOFTRESET);
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
static int omap_xhci_core_init(struct omap_xhci *omap)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
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);
|
||||
}
|
939
drivers/usb/host/xhci-ring.c
Normal file
939
drivers/usb/host/xhci-ring.c
Normal file
@ -0,0 +1,939 @@
|
||||
/*
|
||||
* USB HOST XHCI Controller stack
|
||||
*
|
||||
* Based on xHCI host controller driver in linux-kernel
|
||||
* by Sarah Sharp.
|
||||
*
|
||||
* Copyright (C) 2008 Intel Corp.
|
||||
* Author: Sarah Sharp
|
||||
*
|
||||
* Copyright (C) 2013 Samsung Electronics Co.Ltd
|
||||
* Authors: Vivek Gautam <gautam.vivek@samsung.com>
|
||||
* Vikas Sajjan <vikas.sajjan@samsung.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <usb.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <asm-generic/errno.h>
|
||||
|
||||
#include "xhci.h"
|
||||
|
||||
/**
|
||||
* Is this TRB a link TRB or was the last TRB the last TRB in this event ring
|
||||
* segment? I.e. would the updated event TRB pointer step off the end of the
|
||||
* event seg ?
|
||||
*
|
||||
* @param ctrl Host controller data structure
|
||||
* @param ring pointer to the ring
|
||||
* @param seg poniter to the segment to which TRB belongs
|
||||
* @param trb poniter to the ring trb
|
||||
* @return 1 if this TRB a link TRB else 0
|
||||
*/
|
||||
static int last_trb(struct xhci_ctrl *ctrl, struct xhci_ring *ring,
|
||||
struct xhci_segment *seg, union xhci_trb *trb)
|
||||
{
|
||||
if (ring == ctrl->event_ring)
|
||||
return trb == &seg->trbs[TRBS_PER_SEGMENT];
|
||||
else
|
||||
return TRB_TYPE_LINK_LE32(trb->link.control);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this link TRB point to the first segment in a ring,
|
||||
* or was the previous TRB the last TRB on the last segment in the ERST?
|
||||
*
|
||||
* @param ctrl Host controller data structure
|
||||
* @param ring pointer to the ring
|
||||
* @param seg poniter to the segment to which TRB belongs
|
||||
* @param trb poniter to the ring trb
|
||||
* @return 1 if this TRB is the last TRB on the last segment else 0
|
||||
*/
|
||||
static bool last_trb_on_last_seg(struct xhci_ctrl *ctrl,
|
||||
struct xhci_ring *ring,
|
||||
struct xhci_segment *seg,
|
||||
union xhci_trb *trb)
|
||||
{
|
||||
if (ring == ctrl->event_ring)
|
||||
return ((trb == &seg->trbs[TRBS_PER_SEGMENT]) &&
|
||||
(seg->next == ring->first_seg));
|
||||
else
|
||||
return le32_to_cpu(trb->link.control) & LINK_TOGGLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* See Cycle bit rules. SW is the consumer for the event ring only.
|
||||
* Don't make a ring full of link TRBs. That would be dumb and this would loop.
|
||||
*
|
||||
* If we've just enqueued a TRB that is in the middle of a TD (meaning the
|
||||
* chain bit is set), then set the chain bit in all the following link TRBs.
|
||||
* If we've enqueued the last TRB in a TD, make sure the following link TRBs
|
||||
* have their chain bit cleared (so that each Link TRB is a separate TD).
|
||||
*
|
||||
* Section 6.4.4.1 of the 0.95 spec says link TRBs cannot have the chain bit
|
||||
* set, but other sections talk about dealing with the chain bit set. This was
|
||||
* fixed in the 0.96 specification errata, but we have to assume that all 0.95
|
||||
* xHCI hardware can't handle the chain bit being cleared on a link TRB.
|
||||
*
|
||||
* @param ctrl Host controller data structure
|
||||
* @param ring pointer to the ring
|
||||
* @param more_trbs_coming flag to indicate whether more trbs
|
||||
* are expected or NOT.
|
||||
* Will you enqueue more TRBs before calling
|
||||
* prepare_ring()?
|
||||
* @return none
|
||||
*/
|
||||
static void inc_enq(struct xhci_ctrl *ctrl, struct xhci_ring *ring,
|
||||
bool more_trbs_coming)
|
||||
{
|
||||
u32 chain;
|
||||
union xhci_trb *next;
|
||||
|
||||
chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN;
|
||||
next = ++(ring->enqueue);
|
||||
|
||||
/*
|
||||
* Update the dequeue pointer further if that was a link TRB or we're at
|
||||
* the end of an event ring segment (which doesn't have link TRBS)
|
||||
*/
|
||||
while (last_trb(ctrl, ring, ring->enq_seg, next)) {
|
||||
if (ring != ctrl->event_ring) {
|
||||
/*
|
||||
* If the caller doesn't plan on enqueueing more
|
||||
* TDs before ringing the doorbell, then we
|
||||
* don't want to give the link TRB to the
|
||||
* hardware just yet. We'll give the link TRB
|
||||
* back in prepare_ring() just before we enqueue
|
||||
* the TD at the top of the ring.
|
||||
*/
|
||||
if (!chain && !more_trbs_coming)
|
||||
break;
|
||||
|
||||
/*
|
||||
* If we're not dealing with 0.95 hardware or
|
||||
* isoc rings on AMD 0.96 host,
|
||||
* carry over the chain bit of the previous TRB
|
||||
* (which may mean the chain bit is cleared).
|
||||
*/
|
||||
next->link.control &= cpu_to_le32(~TRB_CHAIN);
|
||||
next->link.control |= cpu_to_le32(chain);
|
||||
|
||||
next->link.control ^= cpu_to_le32(TRB_CYCLE);
|
||||
xhci_flush_cache((uint32_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((uint32_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((uint32_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 = udev->controller;
|
||||
|
||||
/*
|
||||
* 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((uint32_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((uint32_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 = udev->controller;
|
||||
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 -
|
||||
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 = udev->controller;
|
||||
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((uint32_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((uint32_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((uint32_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 = udev->controller;
|
||||
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((uint32_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((uint32_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((uint32_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;
|
||||
}
|
1030
drivers/usb/host/xhci.c
Normal file
1030
drivers/usb/host/xhci.c
Normal file
File diff suppressed because it is too large
Load Diff
1255
drivers/usb/host/xhci.h
Normal file
1255
drivers/usb/host/xhci.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -112,7 +112,7 @@ int submit_int_msg(struct usb_device *dev, unsigned long pipe,
|
||||
return submit_urb(&hcd, urb);
|
||||
}
|
||||
|
||||
int usb_lowlevel_init(int index, void **controller)
|
||||
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
|
||||
{
|
||||
u8 power;
|
||||
void *mbase;
|
||||
|
@ -1089,7 +1089,7 @@ int submit_bulk_msg(struct usb_device *dev, unsigned long pipe,
|
||||
/*
|
||||
* This function initializes the usb controller module.
|
||||
*/
|
||||
int usb_lowlevel_init(int index, void **controller)
|
||||
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
|
||||
{
|
||||
u8 power;
|
||||
u32 timeout;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user