diff options
75 files changed, 8147 insertions, 450 deletions
@@ -650,8 +650,10 @@ libs-y += drivers/spi/ libs-$(CONFIG_FMAN_ENET) += drivers/net/fm/ libs-$(CONFIG_SYS_FSL_DDR) += drivers/ddr/fsl/ libs-y += drivers/serial/ +libs-y += drivers/usb/dwc3/ libs-y += drivers/usb/eth/ libs-y += drivers/usb/gadget/ +libs-y += drivers/usb/gadget/udc/ libs-y += drivers/usb/host/ libs-y += drivers/usb/musb/ libs-y += drivers/usb/musb-new/ diff --git a/arch/arm/cpu/armv7/am33xx/clock_am43xx.c b/arch/arm/cpu/armv7/am33xx/clock_am43xx.c index 529a119514..b1c0025eeb 100644 --- a/arch/arm/cpu/armv7/am33xx/clock_am43xx.c +++ b/arch/arm/cpu/armv7/am33xx/clock_am43xx.c @@ -111,9 +111,21 @@ void enable_basic_clocks(void) &cmper->emifclkctrl, &cmper->otfaemifclkctrl, &cmper->qspiclkctrl, + &cmper->usb0clkctrl, + &cmper->usbphyocp2scp0clkctrl, + &cmper->usb1clkctrl, + &cmper->usbphyocp2scp1clkctrl, 0 }; + setbits_le32(&cmper->usb0clkctrl, + USBOTGSSX_CLKCTRL_OPTFCLKEN_REFCLK960); + setbits_le32(&cmwkup->usbphy0clkctrl, + USBPHY0_CLKCTRL_OPTFCLKEN_CLK32K); + setbits_le32(&cmper->usb1clkctrl, + USBOTGSSX_CLKCTRL_OPTFCLKEN_REFCLK960); + setbits_le32(&cmwkup->usbphy1clkctrl, + USBPHY0_CLKCTRL_OPTFCLKEN_CLK32K); do_enable_clocks(clk_domains, clk_modules_explicit_en, 1); /* Select the Master osc clk as Timer2 clock source */ diff --git a/arch/arm/cpu/armv7/omap5/hw_data.c b/arch/arm/cpu/armv7/omap5/hw_data.c index b9734fea8f..e4abb25fc2 100644 --- a/arch/arm/cpu/armv7/omap5/hw_data.c +++ b/arch/arm/cpu/armv7/omap5/hw_data.c @@ -460,6 +460,10 @@ void enable_basic_clocks(void) (*prcm)->cm_l4per_gpio6_clkctrl, (*prcm)->cm_l4per_gpio7_clkctrl, (*prcm)->cm_l4per_gpio8_clkctrl, +#ifdef CONFIG_USB_DWC3 + (*prcm)->cm_l3init_ocp2scp1_clkctrl, + (*prcm)->cm_l3init_usb_otg_ss1_clkctrl, +#endif 0 }; @@ -491,6 +495,16 @@ void enable_basic_clocks(void) setbits_le32((*prcm)->cm_l3init_hsmmc2_clkctrl, HSMMC_CLKCTRL_CLKSEL_MASK); +#ifdef CONFIG_USB_DWC3 + /* Enable 960 MHz clock for dwc3 */ + setbits_le32((*prcm)->cm_l3init_usb_otg_ss1_clkctrl, + OPTFCLKEN_REFCLK960M); + + /* Enable 32 KHz clock for dwc3 */ + setbits_le32((*prcm)->cm_coreaon_usb_phy1_core_clkctrl, + USBPHY_CORE_CLKCTRL_OPTFCLKEN_CLK32K); +#endif + /* Set the correct clock dividers for mmc */ setbits_le32((*prcm)->cm_l3init_hsmmc1_clkctrl, HSMMC_CLKCTRL_CLKSEL_DIV_MASK); diff --git a/arch/arm/cpu/armv7/omap5/prcm-regs.c b/arch/arm/cpu/armv7/omap5/prcm-regs.c index 0745d424e2..440bb40a14 100644 --- a/arch/arm/cpu/armv7/omap5/prcm-regs.c +++ b/arch/arm/cpu/armv7/omap5/prcm-regs.c @@ -575,7 +575,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_usb_phy1_core_clkctrl = 0x4A008640, .cm_coreaon_bandgap_clkctrl = 0x4a008648, .cm_coreaon_io_srcomp_clkctrl = 0x4a008650, @@ -709,7 +709,7 @@ struct prcm_regs const omap5_es2_prcm = { .cm_l3init_fsusb_clkctrl = 0x4a0096d0, .cm_l3init_ocp2scp1_clkctrl = 0x4a0096e0, .cm_l3init_ocp2scp3_clkctrl = 0x4a0096e8, - .cm_l3init_usb_otg_ss_clkctrl = 0x4a0096f0, + .cm_l3init_usb_otg_ss1_clkctrl = 0x4a0096f0, /* prm irqstatus regs */ .prm_irqstatus_mpu_2 = 0x4ae06014, @@ -801,8 +801,8 @@ struct prcm_regs const dra7xx_prcm = { .cm_clkmode_dpll_dsp = 0x4a005234, .cm_shadow_freq_config1 = 0x4a005260, .cm_clkmode_dpll_gmac = 0x4a0052a8, - .cm_coreaon_usb_phy_core_clkctrl = 0x4a008640, - .cm_coreaon_usb_phy2_core_clkctrl = 0x4a008688, + .cm_coreaon_usb_phy1_core_clkctrl = 0x4a008640, + .cm_coreaon_usb_phy2_core_clkctrl = 0x4a008688, /* cm1.mpu */ .cm_mpu_mpu_clkctrl = 0x4a005320, @@ -908,7 +908,7 @@ struct prcm_regs const dra7xx_prcm = { .cm_gmac_gmac_clkctrl = 0x4a0093d0, .cm_l3init_ocp2scp1_clkctrl = 0x4a0093e0, .cm_l3init_ocp2scp3_clkctrl = 0x4a0093e8, - .cm_l3init_usb_otg_ss_clkctrl = 0x4a0093f0, + .cm_l3init_usb_otg_ss1_clkctrl = 0x4a0093f0, /* cm2.l4per */ .cm_l4per_clkstctrl = 0x4a009700, diff --git a/arch/arm/include/asm/arch-am33xx/cpu.h b/arch/arm/include/asm/arch-am33xx/cpu.h index 523d22eb87..13a9cad238 100644 --- a/arch/arm/include/asm/arch-am33xx/cpu.h +++ b/arch/arm/include/asm/arch-am33xx/cpu.h @@ -230,7 +230,11 @@ struct prm_device_inst { struct cm_wkuppll { unsigned int resv0[136]; unsigned int wkl4wkclkctrl; /* offset 0x220 */ - unsigned int resv1[55]; + unsigned int resv1[7]; + unsigned int usbphy0clkctrl; /* offset 0x240 */ + unsigned int resv112; + unsigned int usbphy1clkctrl; /* offset 0x248 */ + unsigned int resv113[45]; unsigned int wkclkstctrl; /* offset 0x300 */ unsigned int resv2[15]; unsigned int wkup_i2c0ctrl; /* offset 0x340 */ @@ -289,7 +293,7 @@ struct cm_perpll { unsigned int l3clkstctrl; /* offset 0x00 */ unsigned int resv0[7]; unsigned int l3clkctrl; /* Offset 0x20 */ - unsigned int resv1[7]; + unsigned int resv112[7]; unsigned int l3instrclkctrl; /* offset 0x40 */ unsigned int resv2[3]; unsigned int ocmcramclkctrl; /* offset 0x50 */ @@ -316,7 +320,9 @@ struct cm_perpll { unsigned int qspiclkctrl; /* offset 0x258 */ unsigned int resv121; unsigned int usb0clkctrl; /* offset 0x260 */ - unsigned int resv13[103]; + unsigned int resv122; + unsigned int usb1clkctrl; /* offset 0x268 */ + unsigned int resv13[101]; unsigned int l4lsclkstctrl; /* offset 0x400 */ unsigned int resv14[7]; unsigned int l4lsclkctrl; /* offset 0x420 */ @@ -370,10 +376,14 @@ struct cm_perpll { unsigned int uart4clkctrl; /* offset 0x598 */ unsigned int resv35; unsigned int uart5clkctrl; /* offset 0x5A0 */ - unsigned int resv36[87]; + unsigned int resv36[5]; + unsigned int usbphyocp2scp0clkctrl; /* offset 0x5B8 */ + unsigned int resv361; + unsigned int usbphyocp2scp1clkctrl; /* offset 0x5C0 */ + unsigned int resv3611[79]; unsigned int emifclkstctrl; /* offset 0x700 */ - unsigned int resv361[7]; + unsigned int resv362[7]; unsigned int emifclkctrl; /* offset 0x720 */ unsigned int resv37[3]; unsigned int emiffwclkctrl; /* offset 0x730 */ diff --git a/arch/arm/include/asm/arch-am33xx/hardware_am43xx.h b/arch/arm/include/asm/arch-am33xx/hardware_am43xx.h index 29e3816c1a..479893e47e 100644 --- a/arch/arm/include/asm/arch-am33xx/hardware_am43xx.h +++ b/arch/arm/include/asm/arch-am33xx/hardware_am43xx.h @@ -61,6 +61,15 @@ /* RTC base address */ #define RTC_BASE 0x44E3E000 +/* USB OTG */ +#define USB_OTG_SS1_BASE 0x48390000 +#define USB_OTG_SS1_GLUE_BASE 0x48380000 +#define USB2_PHY1_POWER 0x44E10620 + +#define USB_OTG_SS2_BASE 0x483D0000 +#define USB_OTG_SS2_GLUE_BASE 0x483C0000 +#define USB2_PHY2_POWER 0x44E10628 + /* USB Clock Control */ #define PRM_PER_USB_OTG_SS0_CLKCTRL (CM_PER + 0x260) #define PRM_PER_USB_OTG_SS1_CLKCTRL (CM_PER + 0x268) @@ -73,6 +82,9 @@ #define CM_DEVICE_INST 0x44df4100 #define PRM_DEVICE_INST 0x44df4000 +#define USBOTGSSX_CLKCTRL_OPTFCLKEN_REFCLK960 (1 << 8) +#define USBPHY0_CLKCTRL_OPTFCLKEN_CLK32K (1 << 8) + /* Control status register */ #define CTRL_CRYSTAL_FREQ_SRC_MASK (1 << 31) #define CTRL_CRYSTAL_FREQ_SRC_SHIFT 31 diff --git a/arch/arm/include/asm/arch-omap5/omap.h b/arch/arm/include/asm/arch-omap5/omap.h index e2181598d5..e844bfb884 100644 --- a/arch/arm/include/asm/arch-omap5/omap.h +++ b/arch/arm/include/asm/arch-omap5/omap.h @@ -33,6 +33,18 @@ #define CONTROL_ID_CODE CONTROL_CORE_ID_CODE #endif +#ifdef CONFIG_DRA7XX +#define DRA7_USB_OTG_SS1_BASE 0x48890000 +#define DRA7_USB_OTG_SS1_GLUE_BASE 0x48880000 +#define DRA7_USB3_PHY1_PLL_CTRL 0x4A084C00 +#define DRA7_USB3_PHY1_POWER 0x4A002370 +#define DRA7_USB2_PHY1_POWER 0x4A002300 + +#define DRA7_USB_OTG_SS2_BASE 0x488D0000 +#define DRA7_USB_OTG_SS2_GLUE_BASE 0x488C0000 +#define DRA7_USB2_PHY2_POWER 0x4A002E74 +#endif + /* To be verified */ #define OMAP5430_CONTROL_ID_CODE_ES1_0 0x0B94202F #define OMAP5430_CONTROL_ID_CODE_ES2_0 0x1B94202F diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h index 55a4e266a0..a5821f54e5 100644 --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h @@ -8,18 +8,25 @@ #ifndef __ASM_ARM_DMA_MAPPING_H #define __ASM_ARM_DMA_MAPPING_H +#define dma_mapping_error(x, y) 0 + enum dma_data_direction { DMA_BIDIRECTIONAL = 0, DMA_TO_DEVICE = 1, DMA_FROM_DEVICE = 2, }; -static void *dma_alloc_coherent(size_t len, unsigned long *handle) +static inline void *dma_alloc_coherent(size_t len, unsigned long *handle) { *handle = (unsigned long)memalign(ARCH_DMA_MINALIGN, len); return (void *)*handle; } +static inline void dma_free_coherent(void *addr) +{ + free(addr); +} + static inline unsigned long dma_map_single(volatile void *vaddr, size_t len, enum dma_data_direction dir) { diff --git a/arch/arm/include/asm/omap_common.h b/arch/arm/include/asm/omap_common.h index 123c84ff95..c8c3e71b55 100644 --- a/arch/arm/include/asm/omap_common.h +++ b/arch/arm/include/asm/omap_common.h @@ -143,7 +143,7 @@ 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_phy1_core_clkctrl; u32 cm_coreaon_usb_phy2_core_clkctrl; /* cm2.core */ @@ -230,7 +230,7 @@ struct prcm_regs { u32 cm_l3init_fsusb_clkctrl; u32 cm_l3init_ocp2scp1_clkctrl; u32 cm_l3init_ocp2scp3_clkctrl; - u32 cm_l3init_usb_otg_ss_clkctrl; + u32 cm_l3init_usb_otg_ss1_clkctrl; u32 prm_irqstatus_mpu_2; diff --git a/arch/arm/include/asm/types.h b/arch/arm/include/asm/types.h index 2326420a7f..ee77c4179f 100644 --- a/arch/arm/include/asm/types.h +++ b/arch/arm/include/asm/types.h @@ -54,4 +54,5 @@ typedef unsigned long phys_size_t; #endif /* __KERNEL__ */ +typedef unsigned long resource_size_t; #endif diff --git a/arch/arm/mach-bcm283x/Kconfig b/arch/arm/mach-bcm283x/Kconfig index b43f2d91fd..0c04c301b2 100644 --- a/arch/arm/mach-bcm283x/Kconfig +++ b/arch/arm/mach-bcm283x/Kconfig @@ -23,6 +23,9 @@ config DM_SERIAL config DM_GPIO default y +config PHYS_TO_BUS + default y + config SYS_BOARD default "rpi" if TARGET_RPI default "rpi_2" if TARGET_RPI_2 diff --git a/arch/arm/mach-bcm283x/Makefile b/arch/arm/mach-bcm283x/Makefile index 2505428bab..ac27d00e2a 100644 --- a/arch/arm/mach-bcm283x/Makefile +++ b/arch/arm/mach-bcm283x/Makefile @@ -5,4 +5,4 @@ # obj-$(CONFIG_TARGET_RPI) += lowlevel_init.o -obj-y += init.o reset.o timer.o mbox.o +obj-y += init.o reset.o timer.o mbox.o phys2bus.o diff --git a/arch/arm/mach-bcm283x/mbox.c b/arch/arm/mach-bcm283x/mbox.c index 3b17a31eac..1af9be78c6 100644 --- a/arch/arm/mach-bcm283x/mbox.c +++ b/arch/arm/mach-bcm283x/mbox.c @@ -7,6 +7,7 @@ #include <common.h> #include <asm/io.h> #include <asm/arch/mbox.h> +#include <phys2bus.h> #define TIMEOUT 1000 /* ms */ @@ -110,10 +111,10 @@ int bcm2835_mbox_call_prop(u32 chan, struct bcm2835_mbox_hdr *buffer) dump_buf(buffer); #endif - ret = bcm2835_mbox_call_raw(chan, (u32)buffer, &rbuffer); + ret = bcm2835_mbox_call_raw(chan, phys_to_bus((u32)buffer), &rbuffer); if (ret) return ret; - if (rbuffer != (u32)buffer) { + if (rbuffer != phys_to_bus((u32)buffer)) { printf("mbox: Response buffer mismatch\n"); return -1; } diff --git a/arch/arm/mach-bcm283x/phys2bus.c b/arch/arm/mach-bcm283x/phys2bus.c new file mode 100644 index 0000000000..fc1c29905d --- /dev/null +++ b/arch/arm/mach-bcm283x/phys2bus.c @@ -0,0 +1,22 @@ +/* + * Copyright 2015 Stephen Warren + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <config.h> +#include <phys2bus.h> + +unsigned long phys_to_bus(unsigned long phys) +{ +#ifdef CONFIG_BCM2836 + return 0xc0000000 | phys; +#else + return 0x40000000 | phys; +#endif +} + +unsigned long bus_to_phys(unsigned long bus) +{ + return bus & ~0xc0000000; +} diff --git a/board/samsung/common/board.c b/board/samsung/common/board.c index 2e17da8a7a..0979868211 100644 --- a/board/samsung/common/board.c +++ b/board/samsung/common/board.c @@ -25,6 +25,7 @@ #include <asm/arch/sromc.h> #include <lcd.h> #include <samsung/misc.h> +#include <usb.h> DECLARE_GLOBAL_DATA_PTR; @@ -386,3 +387,8 @@ void reset_misc(void) dm_gpio_set_value(&gpio, 1); } } + +int board_usb_cleanup(int index, enum usb_init_type init) +{ + return 0; +} diff --git a/board/samsung/goni/goni.c b/board/samsung/goni/goni.c index 58cf96eaa8..d943d63eca 100644 --- a/board/samsung/goni/goni.c +++ b/board/samsung/goni/goni.c @@ -206,3 +206,8 @@ int misc_init_r(void) return 0; } #endif + +int board_usb_cleanup(int index, enum usb_init_type init) +{ + return 0; +} diff --git a/board/ti/am43xx/board.c b/board/ti/am43xx/board.c index 67036709f1..ddf4c5fc13 100644 --- a/board/ti/am43xx/board.c +++ b/board/ti/am43xx/board.c @@ -12,6 +12,7 @@ #include <i2c.h> #include <asm/errno.h> #include <spl.h> +#include <usb.h> #include <asm/arch/clock.h> #include <asm/arch/sys_proto.h> #include <asm/arch/mux.h> @@ -24,6 +25,10 @@ #include <power/tps62362.h> #include <miiphy.h> #include <cpsw.h> +#include <linux/usb/gadget.h> +#include <dwc3-uboot.h> +#include <dwc3-omap-uboot.h> +#include <ti-usb-phy-uboot.h> DECLARE_GLOBAL_DATA_PTR; @@ -636,6 +641,109 @@ int board_late_init(void) } #endif +#ifdef CONFIG_USB_DWC3 +static struct dwc3_device usb_otg_ss1 = { + .maximum_speed = USB_SPEED_HIGH, + .base = USB_OTG_SS1_BASE, + .tx_fifo_resize = false, + .index = 0, +}; + +static struct dwc3_omap_device usb_otg_ss1_glue = { + .base = (void *)USB_OTG_SS1_GLUE_BASE, + .utmi_mode = DWC3_OMAP_UTMI_MODE_SW, + .vbus_id_status = OMAP_DWC3_VBUS_VALID, + .index = 0, +}; + +static struct ti_usb_phy_device usb_phy1_device = { + .usb2_phy_power = (void *)USB2_PHY1_POWER, + .index = 0, +}; + +static struct dwc3_device usb_otg_ss2 = { + .maximum_speed = USB_SPEED_HIGH, + .base = USB_OTG_SS2_BASE, + .tx_fifo_resize = false, + .index = 1, +}; + +static struct dwc3_omap_device usb_otg_ss2_glue = { + .base = (void *)USB_OTG_SS2_GLUE_BASE, + .utmi_mode = DWC3_OMAP_UTMI_MODE_SW, + .vbus_id_status = OMAP_DWC3_VBUS_VALID, + .index = 1, +}; + +static struct ti_usb_phy_device usb_phy2_device = { + .usb2_phy_power = (void *)USB2_PHY2_POWER, + .index = 1, +}; + +int board_usb_init(int index, enum usb_init_type init) +{ + switch (index) { + case 0: + if (init == USB_INIT_DEVICE) { + usb_otg_ss1.dr_mode = USB_DR_MODE_PERIPHERAL; + usb_otg_ss1_glue.vbus_id_status = OMAP_DWC3_VBUS_VALID; + } else { + usb_otg_ss1.dr_mode = USB_DR_MODE_HOST; + usb_otg_ss1_glue.vbus_id_status = OMAP_DWC3_ID_GROUND; + } + + dwc3_omap_uboot_init(&usb_otg_ss1_glue); + ti_usb_phy_uboot_init(&usb_phy1_device); + dwc3_uboot_init(&usb_otg_ss1); + break; + case 1: + if (init == USB_INIT_DEVICE) { + usb_otg_ss2.dr_mode = USB_DR_MODE_PERIPHERAL; + usb_otg_ss2_glue.vbus_id_status = OMAP_DWC3_VBUS_VALID; + } else { + usb_otg_ss2.dr_mode = USB_DR_MODE_HOST; + usb_otg_ss2_glue.vbus_id_status = OMAP_DWC3_ID_GROUND; + } + + ti_usb_phy_uboot_init(&usb_phy2_device); + dwc3_omap_uboot_init(&usb_otg_ss2_glue); + dwc3_uboot_init(&usb_otg_ss2); + break; + default: + printf("Invalid Controller Index\n"); + } + + return 0; +} + +int board_usb_cleanup(int index, enum usb_init_type init) +{ + switch (index) { + case 0: + case 1: + ti_usb_phy_uboot_exit(index); + dwc3_uboot_exit(index); + dwc3_omap_uboot_exit(index); + break; + default: + printf("Invalid Controller Index\n"); + } + + return 0; +} + +int usb_gadget_handle_interrupts(int index) +{ + u32 status; + + status = dwc3_omap_uboot_interrupt_status(index); + if (status) + dwc3_uboot_handle_interrupt(index); + + return 0; +} +#endif + #ifdef CONFIG_DRIVER_TI_CPSW static void cpsw_control(int enabled) diff --git a/board/ti/dra7xx/evm.c b/board/ti/dra7xx/evm.c index 65222419eb..3089fa2334 100644 --- a/board/ti/dra7xx/evm.c +++ b/board/ti/dra7xx/evm.c @@ -14,11 +14,16 @@ #include <palmas.h> #include <sata.h> #include <asm/gpio.h> +#include <usb.h> +#include <linux/usb/gadget.h> #include <asm/arch/gpio.h> #include <asm/arch/sys_proto.h> #include <asm/arch/mmc_host_def.h> #include <asm/arch/sata.h> #include <environment.h> +#include <dwc3-uboot.h> +#include <dwc3-omap-uboot.h> +#include <ti-usb-phy-uboot.h> #include "mux_data.h" @@ -123,6 +128,110 @@ int board_mmc_init(bd_t *bis) } #endif +#ifdef CONFIG_USB_DWC3 +static struct dwc3_device usb_otg_ss1 = { + .maximum_speed = USB_SPEED_SUPER, + .base = DRA7_USB_OTG_SS1_BASE, + .tx_fifo_resize = false, + .index = 0, +}; + +static struct dwc3_omap_device usb_otg_ss1_glue = { + .base = (void *)DRA7_USB_OTG_SS1_GLUE_BASE, + .utmi_mode = DWC3_OMAP_UTMI_MODE_SW, + .vbus_id_status = OMAP_DWC3_VBUS_VALID, + .index = 0, +}; + +static struct ti_usb_phy_device usb_phy1_device = { + .pll_ctrl_base = (void *)DRA7_USB3_PHY1_PLL_CTRL, + .usb2_phy_power = (void *)DRA7_USB2_PHY1_POWER, + .usb3_phy_power = (void *)DRA7_USB3_PHY1_POWER, + .index = 0, +}; + +static struct dwc3_device usb_otg_ss2 = { + .maximum_speed = USB_SPEED_SUPER, + .base = DRA7_USB_OTG_SS2_BASE, + .tx_fifo_resize = false, + .index = 1, +}; + +static struct dwc3_omap_device usb_otg_ss2_glue = { + .base = (void *)DRA7_USB_OTG_SS2_GLUE_BASE, + .utmi_mode = DWC3_OMAP_UTMI_MODE_SW, + .vbus_id_status = OMAP_DWC3_VBUS_VALID, + .index = 1, +}; + +static struct ti_usb_phy_device usb_phy2_device = { + .usb2_phy_power = (void *)DRA7_USB2_PHY2_POWER, + .index = 1, +}; + +int board_usb_init(int index, enum usb_init_type init) +{ + switch (index) { + case 0: + if (init == USB_INIT_DEVICE) { + usb_otg_ss1.dr_mode = USB_DR_MODE_PERIPHERAL; + usb_otg_ss1_glue.vbus_id_status = OMAP_DWC3_VBUS_VALID; + } else { + usb_otg_ss1.dr_mode = USB_DR_MODE_HOST; + usb_otg_ss1_glue.vbus_id_status = OMAP_DWC3_ID_GROUND; + } + + ti_usb_phy_uboot_init(&usb_phy1_device); + dwc3_omap_uboot_init(&usb_otg_ss1_glue); + dwc3_uboot_init(&usb_otg_ss1); + break; + case 1: + if (init == USB_INIT_DEVICE) { + usb_otg_ss2.dr_mode = USB_DR_MODE_PERIPHERAL; + usb_otg_ss2_glue.vbus_id_status = OMAP_DWC3_VBUS_VALID; + } else { + usb_otg_ss2.dr_mode = USB_DR_MODE_HOST; + usb_otg_ss2_glue.vbus_id_status = OMAP_DWC3_ID_GROUND; + } + + ti_usb_phy_uboot_init(&usb_phy2_device); + dwc3_omap_uboot_init(&usb_otg_ss2_glue); + dwc3_uboot_init(&usb_otg_ss2); + break; + default: + printf("Invalid Controller Index\n"); + } + + return 0; +} + +int board_usb_cleanup(int index, enum usb_init_type init) +{ + switch (index) { + case 0: + case 1: + ti_usb_phy_uboot_exit(index); + dwc3_uboot_exit(index); + dwc3_omap_uboot_exit(index); + break; + default: + printf("Invalid Controller Index\n"); + } + return 0; +} + +int usb_gadget_handle_interrupts(int index) +{ + u32 status; + + status = dwc3_omap_uboot_interrupt_status(index); + if (status) + dwc3_uboot_handle_interrupt(index); + + return 0; +} +#endif + #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_OS_BOOT) int spl_start_uboot(void) { diff --git a/common/cmd_dfu.c b/common/cmd_dfu.c index e975abebc9..857148f8af 100644 --- a/common/cmd_dfu.c +++ b/common/cmd_dfu.c @@ -9,6 +9,7 @@ */ #include <common.h> +#include <watchdog.h> #include <dfu.h> #include <g_dnl.h> #include <usb.h> @@ -64,10 +65,12 @@ static int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if (ctrlc()) goto exit; - usb_gadget_handle_interrupts(); + WATCHDOG_RESET(); + usb_gadget_handle_interrupts(controller_index); } exit: g_dnl_unregister(); + board_usb_cleanup(controller_index, USB_INIT_DEVICE); done: dfu_free_entities(); diff --git a/common/cmd_fastboot.c b/common/cmd_fastboot.c index 346ab80454..d52ccfb310 100644 --- a/common/cmd_fastboot.c +++ b/common/cmd_fastboot.c @@ -23,6 +23,8 @@ static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) if (!g_dnl_board_usb_cable_connected()) { puts("\rUSB cable not detected.\n" \ "Command exit.\n"); + g_dnl_unregister(); + g_dnl_clear_detach(); return CMD_RET_FAILURE; } @@ -31,7 +33,7 @@ static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) break; if (ctrlc()) break; - usb_gadget_handle_interrupts(); + usb_gadget_handle_interrupts(0); } g_dnl_unregister(); diff --git a/common/cmd_thordown.c b/common/cmd_thordown.c index 8ed1dc6f9e..436b7f5631 100644 --- a/common/cmd_thordown.c +++ b/common/cmd_thordown.c @@ -56,6 +56,7 @@ int do_thor_down(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) exit: g_dnl_unregister(); + board_usb_cleanup(controller_index, USB_INIT_DEVICE); done: dfu_free_entities(); diff --git a/common/cmd_usb_mass_storage.c b/common/cmd_usb_mass_storage.c index 51c3fffb46..198dab15ba 100644 --- a/common/cmd_usb_mass_storage.c +++ b/common/cmd_usb_mass_storage.c @@ -137,7 +137,7 @@ int do_usb_mass_storage(cmd_tbl_t *cmdtp, int flag, } while (1) { - usb_gadget_handle_interrupts(); + usb_gadget_handle_interrupts(controller_index); rc = fsg_main_thread(NULL); if (rc) { @@ -154,11 +154,12 @@ int do_usb_mass_storage(cmd_tbl_t *cmdtp, int flag, } exit: g_dnl_unregister(); + board_usb_cleanup(controller_index, USB_INIT_DEVICE); return CMD_RET_SUCCESS; } U_BOOT_CMD(ums, 4, 1, do_usb_mass_storage, - "Use the UMS [User Mass Storage]", + "Use the UMS [USB Mass Storage]", "<USB_controller> [<devtype>] <devnum> e.g. ums 0 mmc 0\n" " devtype defaults to mmc" ); diff --git a/common/fb_mmc.c b/common/fb_mmc.c index 75899e4c28..0c48cf929f 100644 --- a/common/fb_mmc.c +++ b/common/fb_mmc.c @@ -33,6 +33,28 @@ void fastboot_okay(const char *s) strncat(response_str, s, RESPONSE_LEN - 4 - 1); } +static int get_partition_info_efi_by_name_or_alias(block_dev_desc_t *dev_desc, + const char *name, disk_partition_t *info) +{ + int ret; + + ret = get_partition_info_efi_by_name(dev_desc, name, info); + if (ret) { + /* strlen("fastboot_partition_alias_") + 32(part_name) + 1 */ + char env_alias_name[25 + 32 + 1]; + char *aliased_part_name; + + /* check for alias */ + strcpy(env_alias_name, "fastboot_partition_alias_"); + strncat(env_alias_name, name, 32); + aliased_part_name = getenv(env_alias_name); + if (aliased_part_name != NULL) + ret = get_partition_info_efi_by_name(dev_desc, + aliased_part_name, info); + } + return ret; +} + static void write_raw_image(block_dev_desc_t *dev_desc, disk_partition_t *info, const char *part_name, void *buffer, unsigned int download_bytes) @@ -98,7 +120,7 @@ void fb_mmc_flash_write(const char *cmd, void *download_buffer, printf("........ success\n"); fastboot_okay(""); return; - } else if (get_partition_info_efi_by_name(dev_desc, cmd, &info)) { + } else if (get_partition_info_efi_by_name_or_alias(dev_desc, cmd, &info)) { error("cannot find partition: '%s'\n", cmd); fastboot_fail("cannot find partition"); return; @@ -136,7 +158,7 @@ void fb_mmc_erase(const char *cmd, char *response) return; } - ret = get_partition_info_efi_by_name(dev_desc, cmd, &info); + ret = get_partition_info_efi_by_name_or_alias(dev_desc, cmd, &info); if (ret) { error("cannot find partition: '%s'", cmd); fastboot_fail("cannot find partition"); diff --git a/common/usb.c b/common/usb.c index 32e15cd8dd..d94640a99e 100644 --- a/common/usb.c +++ b/common/usb.c @@ -95,18 +95,24 @@ int usb_init(void) start_index = dev_index; printf("scanning bus %d for devices... ", i); dev = usb_alloc_new_device(ctrl); + if (!dev) + break; + /* * device 0 is always present * (root hub, so let it analyze) */ - if (dev) - usb_new_device(dev); + ret = usb_new_device(dev); + if (ret) + usb_free_device(); - if (start_index == dev_index) + if (start_index == dev_index) { puts("No USB Device found\n"); - else + continue; + } else { printf("%d USB Device(s) found\n", dev_index - start_index); + } usb_started = 1; } @@ -116,7 +122,7 @@ int usb_init(void) if (controllers_initialized == 0) puts("USB error: all controllers failed lowlevel init\n"); - return usb_started ? 0 : -1; + return usb_started ? 0 : -ENODEV; } /****************************************************************************** @@ -186,7 +192,7 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, if ((timeout == 0) && (!asynch_allowed)) { /* request for a asynch control pipe is not allowed */ - return -1; + return -EINVAL; } /* set setup command */ @@ -201,7 +207,7 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, dev->status = USB_ST_NOT_PROC; /*not yet processed */ if (submit_control_msg(dev, pipe, data, size, setup_packet) < 0) - return -1; + return -EIO; if (timeout == 0) return (int)size; @@ -224,17 +230,17 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, /*------------------------------------------------------------------- * submits bulk message, and waits for completion. returns 0 if Ok or - * -1 if Error. + * negative if Error. * synchronous behavior */ int usb_bulk_msg(struct usb_device *dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout) { if (len < 0) - return -1; + return -EINVAL; dev->status = USB_ST_NOT_PROC; /*not yet processed */ if (submit_bulk_msg(dev, pipe, data, len) < 0) - return -1; + return -EIO; while (timeout--) { if (!((volatile unsigned long)dev->status & USB_ST_NOT_PROC)) break; @@ -244,7 +250,7 @@ int usb_bulk_msg(struct usb_device *dev, unsigned int pipe, if (dev->status == 0) return 0; else - return -1; + return -EIO; } @@ -350,11 +356,11 @@ static int usb_parse_config(struct usb_device *dev, if (head->bDescriptorType != USB_DT_CONFIG) { printf(" ERROR: NOT USB_CONFIG_DESC %x\n", head->bDescriptorType); - return -1; + return -EINVAL; } if (head->bLength != USB_DT_CONFIG_SIZE) { printf("ERROR: Invalid USB CFG length (%d)\n", head->bLength); - return -1; + return -EINVAL; } memcpy(&dev->config, head, USB_DT_CONFIG_SIZE); dev->config.no_of_if = 0; @@ -383,7 +389,7 @@ static int usb_parse_config(struct usb_device *dev, if (ifno >= USB_MAXINTERFACES) { puts("Too many USB interfaces!\n"); /* try to go on with what we have */ - return 1; + return -EINVAL; } if_desc = &dev->config.if_desc[ifno]; dev->config.no_of_if++; @@ -421,7 +427,7 @@ static int usb_parse_config(struct usb_device *dev, if (epno > USB_MAXENDPOINTS) { printf("Interface %d has too many endpoints!\n", if_desc->desc.bInterfaceNumber); - return 1; + return -EINVAL; } /* found an endpoint */ if_desc->no_of_ep++; @@ -459,7 +465,7 @@ static int usb_parse_config(struct usb_device *dev, break; default: if (head->bLength == 0) - return 1; + return -EINVAL; debug("unknown Description Type : %x\n", head->bDescriptorType); @@ -479,7 +485,7 @@ static int usb_parse_config(struct usb_device *dev, index += head->bLength; head = (struct usb_descriptor_header *)&buffer[index]; } - return 1; + return 0; } /*********************************************************************** @@ -546,14 +552,14 @@ int usb_get_configuration_no(struct usb_device *dev, else printf("config descriptor too short " \ "(expected %i, got %i)\n", 9, result); - return -1; + return -EIO; } length = le16_to_cpu(config->wTotalLength); if (length > USB_BUFSIZ) { printf("%s: failed to get descriptor - too long: %d\n", __func__, length); - return -1; + return -EIO; } result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, length); @@ -595,7 +601,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) } if (!if_face) { printf("selecting invalid interface %d", interface); - return -1; + return -EINVAL; } /* * We should return now for devices with only one alternate setting. @@ -634,7 +640,7 @@ static int usb_set_configuration(struct usb_device *dev, int configuration) dev->toggle[1] = 0; return 0; } else - return -1; + return -EIO; } /******************************************************************** @@ -748,7 +754,7 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid, } if (rc < 2) - rc = -1; + rc = -EINVAL; return rc; } @@ -767,7 +773,7 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) unsigned int u, idx; if (size <= 0 || !buf || !index) - return -1; + return -EINVAL; buf[0] = 0; tbuf = &mybuf[0]; @@ -777,10 +783,10 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) if (err < 0) { debug("error getting string descriptor 0 " \ "(error=%lx)\n", dev->status); - return -1; + return -EIO; } else if (tbuf[0] < 4) { debug("string descriptor 0 too short\n"); - return -1; + return -EIO; } else { dev->have_langid = -1; dev->string_langid = tbuf[2] | (tbuf[3] << 8); @@ -893,7 +899,7 @@ int usb_new_device(struct usb_device *dev) */ if (usb_alloc_device(dev)) { printf("Cannot allocate device context to get SLOT_ID\n"); - return -1; + return -EINVAL; } /* We still haven't set the Address yet */ @@ -915,7 +921,7 @@ int usb_new_device(struct usb_device *dev) if (err < 8) { printf("\n USB device not responding, " \ "giving up (status=%lX)\n", dev->status); - return 1; + return -EIO; } memcpy(&dev->descriptor, tmpbuf, 8); #else @@ -950,9 +956,24 @@ int usb_new_device(struct usb_device *dev) */ #ifndef CONFIG_USB_XHCI err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, 64); - if (err < 0) { + /* + * Validate we've received only at least 8 bytes, not that we've + * received the entire descriptor. The reasoning is: + * - The code only uses fields in the first 8 bytes, so that's all we + * need to have fetched at this stage. + * - The smallest maxpacket size is 8 bytes. Before we know the actual + * maxpacket the device uses, the USB controller may only accept a + * single packet. Consequently we are only guaranteed to receive 1 + * packet (at least 8 bytes) even in a non-error case. + * + * At least the DWC2 controller needs to be programmed with the number + * of packets in addition to the number of bytes. A request for 64 + * bytes of data with the maxpacket guessed as 64 (above) yields a + * request for 1 packet. + */ + if (err < 8) { debug("usb_new_device: usb_get_descriptor() failed\n"); - return 1; + return -EIO; } dev->descriptor.bMaxPacketSize0 = desc->bMaxPacketSize0; @@ -968,7 +989,7 @@ int usb_new_device(struct usb_device *dev) err = hub_port_reset(dev->parent, dev->portnr - 1, &portstatus); if (err < 0) { printf("\n Couldn't reset port %i\n", dev->portnr); - return 1; + return -EIO; } } else { usb_reset_root_port(); @@ -990,6 +1011,9 @@ int usb_new_device(struct usb_device *dev) case 64: dev->maxpacketsize = PACKET_SIZE_64; break; + default: + printf("usb_new_device: invalid max packet size\n"); + return -EIO; } dev->devnum = addr; @@ -998,7 +1022,7 @@ int usb_new_device(struct usb_device *dev) if (err < 0) { printf("\n USB device not accepting new address " \ "(error=%lX)\n", dev->status); - return 1; + return -EIO; } mdelay(10); /* Let the SET_ADDRESS settle */ @@ -1014,7 +1038,7 @@ int usb_new_device(struct usb_device *dev) else printf("USB device descriptor short read " \ "(expected %i, got %i)\n", tmp, err); - return 1; + return -EIO; } memcpy(&dev->descriptor, tmpbuf, sizeof(dev->descriptor)); /* correct le values */ @@ -1028,7 +1052,7 @@ int usb_new_device(struct usb_device *dev) printf("usb_new_device: Cannot read configuration, " \ "skipping device %04x:%04x\n", dev->descriptor.idVendor, dev->descriptor.idProduct); - return -1; + return -EIO; } usb_parse_config(dev, tmpbuf, 0); usb_set_maxpacket(dev); @@ -1036,7 +1060,7 @@ int usb_new_device(struct usb_device *dev) if (usb_set_configuration(dev, dev->config.desc.bConfigurationValue)) { printf("failed to set default configuration " \ "len %d, status %lX\n", dev->act_len, dev->status); - return -1; + return -EIO; } debug("new device strings: Mfr=%d, Product=%d, SerialNumber=%d\n", dev->descriptor.iManufacturer, dev->descriptor.iProduct, @@ -1066,4 +1090,10 @@ int board_usb_init(int index, enum usb_init_type init) { return 0; } + +__weak +int board_usb_cleanup(int index, enum usb_init_type init) +{ + return 0; +} /* EOF */ diff --git a/common/usb_hub.c b/common/usb_hub.c index 66b4a725d1..f54a404a49 100644 --- a/common/usb_hub.c +++ b/common/usb_hub.c @@ -86,6 +86,7 @@ static void usb_hub_power_on(struct usb_hub_device *hub) int i; struct usb_device *dev; unsigned pgood_delay = hub->desc.bPwrOn2PwrGood * 2; + const char *env; dev = hub->pusb_dev; @@ -98,7 +99,14 @@ static void usb_hub_power_on(struct usb_hub_device *hub) /* * Wait for power to become stable, * plus spec-defined max time for device to connect + * but allow this time to be increased via env variable as some + * devices break the spec and require longer warm-up times */ + env = getenv("usb_pgood_delay"); + if (env) + pgood_delay = max(pgood_delay, + (unsigned)simple_strtol(env, NULL, 0)); + debug("pgood_delay=%dms\n", pgood_delay); mdelay(pgood_delay + 1000); } diff --git a/common/usb_storage.c b/common/usb_storage.c index 1411737bed..a4ca7a5957 100644 --- a/common/usb_storage.c +++ b/common/usb_storage.c @@ -208,6 +208,30 @@ static unsigned int usb_get_max_lun(struct us_data *us) return (len > 0) ? *result : 0; } +static int usb_storage_register(struct usb_device *dev, unsigned char iface) +{ + int lun, max_lun, start = usb_max_devs; + int nb_dev = 0; + + if (!usb_storage_probe(dev, iface, &usb_stor[usb_max_devs])) + return nb_dev; + + /* + * OK, it's a storage device. Iterate over its LUNs + * and populate `usb_dev_desc'. + */ + max_lun = usb_get_max_lun(&usb_stor[usb_max_devs]); + for (lun = 0; lun <= max_lun && usb_max_devs < USB_MAX_STOR_DEV; lun++) { + usb_dev_desc[usb_max_devs].lun = lun; + if (usb_stor_get_info(dev, &usb_stor[start], + &usb_dev_desc[usb_max_devs]) == 1) { + nb_dev++; + } + } + + return nb_dev; +} + /******************************************************************************* * scan the usb and reports device info * to the user if mode = 1 @@ -215,7 +239,7 @@ static unsigned int usb_get_max_lun(struct us_data *us) */ int usb_stor_scan(int mode) { - unsigned char i; + unsigned char i, iface; struct usb_device *dev; if (mode == 1) @@ -241,23 +265,10 @@ int usb_stor_scan(int mode) if (dev == NULL) break; /* no more devices available */ - if (usb_storage_probe(dev, 0, &usb_stor[usb_max_devs])) { - /* OK, it's a storage device. Iterate over its LUNs - * and populate `usb_dev_desc'. - */ - int lun, max_lun, start = usb_max_devs; - - max_lun = usb_get_max_lun(&usb_stor[usb_max_devs]); - for (lun = 0; - lun <= max_lun && usb_max_devs < USB_MAX_STOR_DEV; - lun++) { - usb_dev_desc[usb_max_devs].lun = lun; - if (usb_stor_get_info(dev, &usb_stor[start], - &usb_dev_desc[usb_max_devs]) == 1) { - usb_max_devs++; - } - } + for (iface = 0; iface < dev->config.no_of_if; iface++) { + usb_max_devs += usb_storage_register(dev, iface); } + /* if storage device */ if (usb_max_devs == USB_MAX_STOR_DEV) { printf("max USB Storage Device reached: %d stopping\n", @@ -336,8 +347,8 @@ static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length) /* set up the transfer loop */ do { /* transfer the data */ - debug("Bulk xfer 0x%x(%d) try #%d\n", - (unsigned int)buf, this_xfer, 11 - maxtry); + debug("Bulk xfer %p(%d) try #%d\n", + buf, this_xfer, 11 - maxtry); result = usb_bulk_msg(us->pusb_dev, pipe, buf, this_xfer, &partial, USB_CNTL_TIMEOUT * 5); @@ -514,6 +525,7 @@ static int usb_stor_BBB_comdat(ccb *srb, struct us_data *us) cbw->bCDBLength = srb->cmdlen; /* copy the command data into the CBW command data buffer */ /* DST SRC LEN!!! */ + memcpy(cbw->CBWCDB, srb->cmd, srb->cmdlen); result = usb_bulk_msg(us->pusb_dev, pipe, cbw, UMASS_BBB_CBW_SIZE, &actlen, USB_CNTL_TIMEOUT * 5); @@ -603,7 +615,7 @@ static int usb_stor_CBI_get_status(ccb *srb, struct us_data *us) (void *) &us->ip_data, us->irqmaxp, us->irqinterval); timeout = 1000; while (timeout--) { - if ((volatile int *) us->ip_wanted == NULL) + if (us->ip_wanted == 0) break; mdelay(10); } @@ -689,6 +701,7 @@ static int usb_stor_BBB_transport(ccb *srb, struct us_data *us) pipe = pipein; else pipe = pipeout; + result = usb_bulk_msg(us->pusb_dev, pipe, srb->pdata, srb->datalen, &data_actlen, USB_CNTL_TIMEOUT * 5); /* special handling of STALL in DATA phase */ @@ -1067,7 +1080,7 @@ unsigned long usb_stor_read(int device, lbaint_t blknr, usb_disable_asynch(1); /* asynch transfer not allowed */ srb->lun = usb_dev_desc[device].lun; - buf_addr = (unsigned long)buffer; + buf_addr = (uintptr_t)buffer; start = blknr; blks = blkcnt; @@ -1141,7 +1154,7 @@ unsigned long usb_stor_write(int device, lbaint_t blknr, usb_disable_asynch(1); /* asynch transfer not allowed */ srb->lun = usb_dev_desc[device].lun; - buf_addr = (unsigned long)buffer; + buf_addr = (uintptr_t)buffer; start = blknr; blks = blkcnt; @@ -1334,9 +1347,9 @@ int usb_stor_get_info(struct usb_device *dev, struct us_data *ss, block_dev_desc_t *dev_desc) { unsigned char perq, modi; - ALLOC_CACHE_ALIGN_BUFFER(unsigned long, cap, 2); - ALLOC_CACHE_ALIGN_BUFFER(unsigned char, usb_stor_buf, 36); - unsigned long *capacity, *blksz; + ALLOC_CACHE_ALIGN_BUFFER(u32, cap, 2); + ALLOC_CACHE_ALIGN_BUFFER(u8, usb_stor_buf, 36); + u32 capacity, blksz; ccb *pccb = &usb_ccb; pccb->pdata = usb_stor_buf; @@ -1362,9 +1375,9 @@ int usb_stor_get_info(struct usb_device *dev, struct us_data *ss, /* drive is removable */ dev_desc->removable = 1; } - memcpy(&dev_desc->vendor[0], (const void *) &usb_stor_buf[8], 8); - memcpy(&dev_desc->product[0], (const void *) &usb_stor_buf[16], 16); - memcpy(&dev_desc->revision[0], (const void *) &usb_stor_buf[32], 4); + memcpy(dev_desc->vendor, (const void *)&usb_stor_buf[8], 8); + memcpy(dev_desc->product, (const void *)&usb_stor_buf[16], 16); + memcpy(dev_desc->revision, (const void *)&usb_stor_buf[32], 4); dev_desc->vendor[8] = 0; dev_desc->product[16] = 0; dev_desc->revision[4] = 0; @@ -1385,7 +1398,7 @@ int usb_stor_get_info(struct usb_device *dev, struct us_data *ss, } return 0; } - pccb->pdata = (unsigned char *)&cap[0]; + pccb->pdata = (unsigned char *)cap; memset(pccb->pdata, 0, 8); if (usb_read_capacity(pccb, ss) != 0) { printf("READ_CAP ERROR\n"); @@ -1393,21 +1406,21 @@ int usb_stor_get_info(struct usb_device *dev, struct us_data *ss, cap[1] = 0x200; } ss->flags &= ~USB_READY; - debug("Read Capacity returns: 0x%lx, 0x%lx\n", cap[0], cap[1]); + debug("Read Capacity returns: 0x%08x, 0x%08x\n", cap[0], cap[1]); #if 0 if (cap[0] > (0x200000 * 10)) /* greater than 10 GByte */ cap[0] >>= 16; -#endif + cap[0] = cpu_to_be32(cap[0]); cap[1] = cpu_to_be32(cap[1]); +#endif + + capacity = be32_to_cpu(cap[0]) + 1; + blksz = be32_to_cpu(cap[1]); - /* this assumes bigendian! */ - cap[0] += 1; - capacity = &cap[0]; - blksz = &cap[1]; - debug("Capacity = 0x%lx, blocksz = 0x%lx\n", *capacity, *blksz); - dev_desc->lba = *capacity; - dev_desc->blksz = *blksz; + debug("Capacity = 0x%08x, blocksz = 0x%08x\n", capacity, blksz); + dev_desc->lba = capacity; + dev_desc->blksz = blksz; dev_desc->log2blksz = LOG2(dev_desc->blksz); dev_desc->type = perq; debug(" address %d\n", dev_desc->target); diff --git a/doc/README.android-fastboot b/doc/README.android-fastboot index 5526a4343c..04411e98bc 100644 --- a/doc/README.android-fastboot +++ b/doc/README.android-fastboot @@ -50,6 +50,15 @@ buffer should be as large as possible for a platform. The location of the buffer and size are set with CONFIG_USB_FASTBOOT_BUF_ADDR and CONFIG_USB_FASTBOOT_BUF_SIZE. +Fastboot partition aliases can also be defined for devices where GPT +limitations prevent user-friendly partition names such as "boot", "system" +and "cache". Or, where the actual partition name doesn't match a standard +partition name used commonly with fastboot. Current implentation checks +aliases when accessing partitions by name (flash_write and erase functions). +To define a partition alias add an environment variable similar to: +fastboot_partition_alias_<alias partition name>=<actual partition name> +Example: fastboot_partition_alias_boot=LNX + In Action ========= Enter into fastboot by executing the fastboot command in u-boot and you diff --git a/drivers/Kconfig b/drivers/Kconfig index dcce532e2d..941aa0c261 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -53,3 +53,11 @@ source "drivers/crypto/Kconfig" source "drivers/thermal/Kconfig" endmenu + +config PHYS_TO_BUS + bool + help + Some SoCs use a different address map for CPU physical addresses and + peripheral DMA master accesses. If yours does, select this option in + your platform's Kconfig, and implement the appropriate mapping + functions in your platform's support code. diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile new file mode 100644 index 0000000000..e455a5279c --- /dev/null +++ b/drivers/usb/dwc3/Makefile @@ -0,0 +1,8 @@ +obj-$(CONFIG_USB_DWC3) += dwc3.o + +dwc3-y := core.o + +dwc3-y += gadget.o ep0.o + +obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o +obj-$(CONFIG_USB_DWC3_PHY_OMAP) += ti_usb_phy.o diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c new file mode 100644 index 0000000000..ab3c94e512 --- /dev/null +++ b/drivers/usb/dwc3/core.c @@ -0,0 +1,785 @@ +/** + * core.c - DesignWare USB3 DRD Controller Core file + * + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/core.c) and ported + * to uboot. + * + * commit cd72f890d2 : usb: dwc3: core: enable phy suspend quirk on non-FPGA + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <malloc.h> +#include <dwc3-uboot.h> +#include <asm/dma-mapping.h> +#include <linux/ioport.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include "core.h" +#include "gadget.h" +#include "io.h" + +#include "linux-compat.h" + +static LIST_HEAD(dwc3_list); +/* -------------------------------------------------------------------------- */ + +static void dwc3_set_mode(struct dwc3 *dwc, u32 mode) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG)); + reg |= DWC3_GCTL_PRTCAPDIR(mode); + dwc3_writel(dwc->regs, DWC3_GCTL, reg); +} + +/** + * dwc3_core_soft_reset - Issues core soft reset and PHY reset + * @dwc: pointer to our context structure + */ +static int dwc3_core_soft_reset(struct dwc3 *dwc) +{ + u32 reg; + + /* Before Resetting PHY, put Core in Reset */ + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg |= DWC3_GCTL_CORESOFTRESET; + dwc3_writel(dwc->regs, DWC3_GCTL, reg); + + /* Assert USB3 PHY reset */ + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); + reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST; + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + + /* Assert USB2 PHY reset */ + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + + mdelay(100); + + /* Clear USB3 PHY reset */ + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); + reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST; + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + + /* Clear USB2 PHY reset */ + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + + mdelay(100); + + /* After PHYs are stable we can take Core out of reset state */ + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg &= ~DWC3_GCTL_CORESOFTRESET; + dwc3_writel(dwc->regs, DWC3_GCTL, reg); + + return 0; +} + +/** + * dwc3_free_one_event_buffer - Frees one event buffer + * @dwc: Pointer to our controller context structure + * @evt: Pointer to event buffer to be freed + */ +static void dwc3_free_one_event_buffer(struct dwc3 *dwc, + struct dwc3_event_buffer *evt) +{ + dma_free_coherent(evt->buf); +} + +/** + * dwc3_alloc_one_event_buffer - Allocates one event buffer structure + * @dwc: Pointer to our controller context structure + * @length: size of the event buffer + * + * Returns a pointer to the allocated event buffer structure on success + * otherwise ERR_PTR(errno). + */ +static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc, + unsigned length) +{ + struct dwc3_event_buffer *evt; + + evt = devm_kzalloc(dwc->dev, sizeof(*evt), GFP_KERNEL); + if (!evt) + return ERR_PTR(-ENOMEM); + + evt->dwc = dwc; + evt->length = length; + evt->buf = dma_alloc_coherent(length, + (unsigned long *)&evt->dma); + if (!evt->buf) + return ERR_PTR(-ENOMEM); + + return evt; +} + +/** + * dwc3_free_event_buffers - frees all allocated event buffers + * @dwc: Pointer to our controller context structure + */ +static void dwc3_free_event_buffers(struct dwc3 *dwc) +{ + struct dwc3_event_buffer *evt; + int i; + + for (i = 0; i < dwc->num_event_buffers; i++) { + evt = dwc->ev_buffs[i]; + if (evt) + dwc3_free_one_event_buffer(dwc, evt); + } +} + +/** + * dwc3_alloc_event_buffers - Allocates @num event buffers of size @length + * @dwc: pointer to our controller context structure + * @length: size of event buffer + * + * Returns 0 on success otherwise negative errno. In the error case, dwc + * may contain some buffers allocated but not all which were requested. + */ +static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) +{ + int num; + int i; + + num = DWC3_NUM_INT(dwc->hwparams.hwparams1); + dwc->num_event_buffers = num; + + dwc->ev_buffs = memalign(CONFIG_SYS_CACHELINE_SIZE, + sizeof(*dwc->ev_buffs) * num); + if (!dwc->ev_buffs) + return -ENOMEM; + + for (i = 0; i < num; i++) { + struct dwc3_event_buffer *evt; + + evt = dwc3_alloc_one_event_buffer(dwc, length); + if (IS_ERR(evt)) { + dev_err(dwc->dev, "can't allocate event buffer\n"); + return PTR_ERR(evt); + } + dwc->ev_buffs[i] = evt; + } + + return 0; +} + +/** + * dwc3_event_buffers_setup - setup our allocated event buffers + * @dwc: pointer to our controller context structure + * + * Returns 0 on success otherwise negative errno. + */ +static int dwc3_event_buffers_setup(struct dwc3 *dwc) +{ + struct dwc3_event_buffer *evt; + int n; + + for (n = 0; n < dwc->num_event_buffers; n++) { + evt = dwc->ev_buffs[n]; + dev_dbg(dwc->dev, "Event buf %p dma %08llx length %d\n", + evt->buf, (unsigned long long) evt->dma, + evt->length); + + evt->lpos = 0; + + dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), + lower_32_bits(evt->dma)); + dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), + upper_32_bits(evt->dma)); + dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), + DWC3_GEVNTSIZ_SIZE(evt->length)); + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0); + } + + return 0; +} + +static void dwc3_event_buffers_cleanup(struct dwc3 *dwc) +{ + struct dwc3_event_buffer *evt; + int n; + + for (n = 0; n < dwc->num_event_buffers; n++) { + evt = dwc->ev_buffs[n]; + + evt->lpos = 0; + + dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0); + dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0); + dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), DWC3_GEVNTSIZ_INTMASK + | DWC3_GEVNTSIZ_SIZE(0)); + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0); + } +} + +static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc) +{ + if (!dwc->has_hibernation) + return 0; + + if (!dwc->nr_scratch) + return 0; + + dwc->scratchbuf = kmalloc_array(dwc->nr_scratch, + DWC3_SCRATCHBUF_SIZE, GFP_KERNEL); + if (!dwc->scratchbuf) + return -ENOMEM; + + return 0; +} + +static int dwc3_setup_scratch_buffers(struct dwc3 *dwc) +{ + dma_addr_t scratch_addr; + u32 param; + int ret; + + if (!dwc->has_hibernation) + return 0; + + if (!dwc->nr_scratch) + return 0; + + scratch_addr = dma_map_single(dwc->scratchbuf, + dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(dwc->dev, scratch_addr)) { + dev_err(dwc->dev, "failed to map scratch buffer\n"); + ret = -EFAULT; + goto err0; + } + + dwc->scratch_addr = scratch_addr; + + param = lower_32_bits(scratch_addr); + + ret = dwc3_send_gadget_generic_command(dwc, + DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO, param); + if (ret < 0) + goto err1; + + param = upper_32_bits(scratch_addr); + + ret = dwc3_send_gadget_generic_command(dwc, + DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI, param); + if (ret < 0) + goto err1; + + return 0; + +err1: + dma_unmap_single((void *)dwc->scratch_addr, dwc->nr_scratch * + DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL); + +err0: + return ret; +} + +static void dwc3_free_scratch_buffers(struct dwc3 *dwc) +{ + if (!dwc->has_hibernation) + return; + + if (!dwc->nr_scratch) + return; + + dma_unmap_single((void *)dwc->scratch_addr, dwc->nr_scratch * + DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL); + kfree(dwc->scratchbuf); +} + +static void dwc3_core_num_eps(struct dwc3 *dwc) +{ + struct dwc3_hwparams *parms = &dwc->hwparams; + + dwc->num_in_eps = DWC3_NUM_IN_EPS(parms); + dwc->num_out_eps = DWC3_NUM_EPS(parms) - dwc->num_in_eps; + + dev_vdbg(dwc->dev, "found %d IN and %d OUT endpoints\n", + dwc->num_in_eps, dwc->num_out_eps); +} + +static void dwc3_cache_hwparams(struct dwc3 *dwc) +{ + struct dwc3_hwparams *parms = &dwc->hwparams; + + parms->hwparams0 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS0); + parms->hwparams1 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS1); + parms->hwparams2 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS2); + parms->hwparams3 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS3); + parms->hwparams4 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS4); + parms->hwparams5 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS5); + parms->hwparams6 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6); + parms->hwparams7 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS7); + parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8); +} + +/** + * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core + * @dwc: Pointer to our controller context structure + */ +static void dwc3_phy_setup(struct dwc3 *dwc) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); + + /* + * Above 1.94a, it is recommended to set DWC3_GUSB3PIPECTL_SUSPHY + * to '0' during coreConsultant configuration. So default value + * will be '0' when the core is reset. Application needs to set it + * to '1' after the core initialization is completed. + */ + if (dwc->revision > DWC3_REVISION_194A) + reg |= DWC3_GUSB3PIPECTL_SUSPHY; + + if (dwc->u2ss_inp3_quirk) + reg |= DWC3_GUSB3PIPECTL_U2SSINP3OK; + + if (dwc->req_p1p2p3_quirk) + reg |= DWC3_GUSB3PIPECTL_REQP1P2P3; + + if (dwc->del_p1p2p3_quirk) + reg |= DWC3_GUSB3PIPECTL_DEP1P2P3_EN; + + if (dwc->del_phy_power_chg_quirk) + reg |= DWC3_GUSB3PIPECTL_DEPOCHANGE; + + if (dwc->lfps_filter_quirk) + reg |= DWC3_GUSB3PIPECTL_LFPSFILT; + + if (dwc->rx_detect_poll_quirk) + reg |= DWC3_GUSB3PIPECTL_RX_DETOPOLL; + + if (dwc->tx_de_emphasis_quirk) + reg |= DWC3_GUSB3PIPECTL_TX_DEEPH(dwc->tx_de_emphasis); + + if (dwc->dis_u3_susphy_quirk) + reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; + + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + + mdelay(100); + + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + + /* + * Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to + * '0' during coreConsultant configuration. So default value will + * be '0' when the core is reset. Application needs to set it to + * '1' after the core initialization is completed. + */ + if (dwc->revision > DWC3_REVISION_194A) + reg |= DWC3_GUSB2PHYCFG_SUSPHY; + + if (dwc->dis_u2_susphy_quirk) + reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; + + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + + mdelay(100); +} + +/** + * dwc3_core_init - Low-level initialization of DWC3 Core + * @dwc: Pointer to our controller context structure + * + * Returns 0 on success otherwise negative errno. + */ +static int dwc3_core_init(struct dwc3 *dwc) +{ + unsigned long timeout; + u32 hwparams4 = dwc->hwparams.hwparams4; + u32 reg; + int ret; + + reg = dwc3_readl(dwc->regs, DWC3_GSNPSID); + /* This should read as U3 followed by revision number */ + if ((reg & DWC3_GSNPSID_MASK) != 0x55330000) { + dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); + ret = -ENODEV; + goto err0; + } + dwc->revision = reg; + + /* Handle USB2.0-only core configuration */ + if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == + DWC3_GHWPARAMS3_SSPHY_IFC_DIS) { + if (dwc->maximum_speed == USB_SPEED_SUPER) + dwc->maximum_speed = USB_SPEED_HIGH; + } + + /* issue device SoftReset too */ + timeout = 5000; + dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST); + while (timeout--) { + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (!(reg & DWC3_DCTL_CSFTRST)) + break; + }; + + if (!timeout) { + dev_err(dwc->dev, "Reset Timed Out\n"); + ret = -ETIMEDOUT; + goto err0; + } + + ret = dwc3_core_soft_reset(dwc); + if (ret) + goto err0; + + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg &= ~DWC3_GCTL_SCALEDOWN_MASK; + + switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) { + case DWC3_GHWPARAMS1_EN_PWROPT_CLK: + /** + * WORKAROUND: DWC3 revisions between 2.10a and 2.50a have an + * issue which would cause xHCI compliance tests to fail. + * + * Because of that we cannot enable clock gating on such + * configurations. + * + * Refers to: + * + * STAR#9000588375: Clock Gating, SOF Issues when ref_clk-Based + * SOF/ITP Mode Used + */ + if ((dwc->dr_mode == USB_DR_MODE_HOST || + dwc->dr_mode == USB_DR_MODE_OTG) && + (dwc->revision >= DWC3_REVISION_210A && + dwc->revision <= DWC3_REVISION_250A)) + reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC; + else + reg &= ~DWC3_GCTL_DSBLCLKGTNG; + break; + case DWC3_GHWPARAMS1_EN_PWROPT_HIB: + /* enable hibernation here */ + dwc->nr_scratch = DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(hwparams4); + + /* + * REVISIT Enabling this bit so that host-mode hibernation + * will work. Device-mode hibernation is not yet implemented. + */ + reg |= DWC3_GCTL_GBLHIBERNATIONEN; + break; + default: + dev_dbg(dwc->dev, "No power optimization available\n"); + } + + /* check if current dwc3 is on simulation board */ + if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) { + dev_dbg(dwc->dev, "it is on FPGA board\n"); + dwc->is_fpga = true; + } + + if(dwc->disable_scramble_quirk && !dwc->is_fpga) + WARN(true, + "disable_scramble cannot be used on non-FPGA builds\n"); + + if (dwc->disable_scramble_quirk && dwc->is_fpga) + reg |= DWC3_GCTL_DISSCRAMBLE; + else + reg &= ~DWC3_GCTL_DISSCRAMBLE; + + if (dwc->u2exit_lfps_quirk) + reg |= DWC3_GCTL_U2EXIT_LFPS; + + /* + * 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 (dwc->revision < DWC3_REVISION_190A) + reg |= DWC3_GCTL_U2RSTECN; + + dwc3_core_num_eps(dwc); + + dwc3_writel(dwc->regs, DWC3_GCTL, reg); + + dwc3_phy_setup(dwc); + + ret = dwc3_alloc_scratch_buffers(dwc); + if (ret) + goto err0; + + ret = dwc3_setup_scratch_buffers(dwc); + if (ret) + goto err1; + + return 0; + +err1: + dwc3_free_scratch_buffers(dwc); + +err0: + return ret; +} + +static void dwc3_core_exit(struct dwc3 *dwc) +{ + dwc3_free_scratch_buffers(dwc); +} + +static int dwc3_core_init_mode(struct dwc3 *dwc) +{ + int ret; + + switch (dwc->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); + ret = dwc3_gadget_init(dwc); + if (ret) { + dev_err(dev, "failed to initialize gadget\n"); + return ret; + } + break; + case USB_DR_MODE_HOST: + dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST); + ret = dwc3_host_init(dwc); + if (ret) { + dev_err(dev, "failed to initialize host\n"); + return ret; + } + break; + case USB_DR_MODE_OTG: + dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG); + ret = dwc3_host_init(dwc); + if (ret) { + dev_err(dev, "failed to initialize host\n"); + return ret; + } + + ret = dwc3_gadget_init(dwc); + if (ret) { + dev_err(dev, "failed to initialize gadget\n"); + return ret; + } + break; + default: + dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode); + return -EINVAL; + } + + return 0; +} + +static void dwc3_core_exit_mode(struct dwc3 *dwc) +{ + switch (dwc->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + dwc3_gadget_exit(dwc); + break; + case USB_DR_MODE_HOST: + dwc3_host_exit(dwc); + break; + case USB_DR_MODE_OTG: + dwc3_host_exit(dwc); + dwc3_gadget_exit(dwc); + break; + default: + /* do nothing */ + break; + } +} + +#define DWC3_ALIGN_MASK (16 - 1) + +/** + * dwc3_uboot_init - dwc3 core uboot initialization code + * @dwc3_dev: struct dwc3_device containing initialization data + * + * Entry point for dwc3 driver (equivalent to dwc3_probe in linux + * kernel driver). Pointer to dwc3_device should be passed containing + * base address and other initialization data. Returns '0' on success and + * a negative value on failure. + * + * Generally called from board_usb_init() implemented in board file. + */ +int dwc3_uboot_init(struct dwc3_device *dwc3_dev) +{ + struct dwc3 *dwc; + struct device *dev; + u8 lpm_nyet_threshold; + u8 tx_de_emphasis; + u8 hird_threshold; + + int ret; + + void *mem; + + mem = devm_kzalloc(dev, sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL); + if (!mem) + return -ENOMEM; + + dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1); + dwc->mem = mem; + + dwc->regs = (int *)(dwc3_dev->base + DWC3_GLOBALS_REGS_START); + + /* default to highest possible threshold */ + lpm_nyet_threshold = 0xff; + + /* default to -3.5dB de-emphasis */ + tx_de_emphasis = 1; + + /* + * default to assert utmi_sleep_n and use maximum allowed HIRD + * threshold value of 0b1100 + */ + hird_threshold = 12; + + dwc->maximum_speed = dwc3_dev->maximum_speed; + dwc->has_lpm_erratum = dwc3_dev->has_lpm_erratum; + if (dwc3_dev->lpm_nyet_threshold) + lpm_nyet_threshold = dwc3_dev->lpm_nyet_threshold; + dwc->is_utmi_l1_suspend = dwc3_dev->is_utmi_l1_suspend; + if (dwc3_dev->hird_threshold) + hird_threshold = dwc3_dev->hird_threshold; + + dwc->needs_fifo_resize = dwc3_dev->tx_fifo_resize; + dwc->dr_mode = dwc3_dev->dr_mode; + + dwc->disable_scramble_quirk = dwc3_dev->disable_scramble_quirk; + dwc->u2exit_lfps_quirk = dwc3_dev->u2exit_lfps_quirk; + dwc->u2ss_inp3_quirk = dwc3_dev->u2ss_inp3_quirk; + dwc->req_p1p2p3_quirk = dwc3_dev->req_p1p2p3_quirk; + dwc->del_p1p2p3_quirk = dwc3_dev->del_p1p2p3_quirk; + dwc->del_phy_power_chg_quirk = dwc3_dev->del_phy_power_chg_quirk; + dwc->lfps_filter_quirk = dwc3_dev->lfps_filter_quirk; + dwc->rx_detect_poll_quirk = dwc3_dev->rx_detect_poll_quirk; + dwc->dis_u3_susphy_quirk = dwc3_dev->dis_u3_susphy_quirk; + dwc->dis_u2_susphy_quirk = dwc3_dev->dis_u2_susphy_quirk; + + dwc->tx_de_emphasis_quirk = dwc3_dev->tx_de_emphasis_quirk; + if (dwc3_dev->tx_de_emphasis) + tx_de_emphasis = dwc3_dev->tx_de_emphasis; + + /* default to superspeed if no maximum_speed passed */ + if (dwc->maximum_speed == USB_SPEED_UNKNOWN) + dwc->maximum_speed = USB_SPEED_SUPER; + + dwc->lpm_nyet_threshold = lpm_nyet_threshold; + dwc->tx_de_emphasis = tx_de_emphasis; + + dwc->hird_threshold = hird_threshold + | (dwc->is_utmi_l1_suspend << 4); + + dwc->index = dwc3_dev->index; + + dwc3_cache_hwparams(dwc); + + ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE); + if (ret) { + dev_err(dwc->dev, "failed to allocate event buffers\n"); + return -ENOMEM; + } + + if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) + dwc->dr_mode = USB_DR_MODE_HOST; + else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) + dwc->dr_mode = USB_DR_MODE_PERIPHERAL; + + if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) + dwc->dr_mode = USB_DR_MODE_OTG; + + ret = dwc3_core_init(dwc); + if (ret) { + dev_err(dev, "failed to initialize core\n"); + goto err0; + } + + ret = dwc3_event_buffers_setup(dwc); + if (ret) { + dev_err(dwc->dev, "failed to setup event buffers\n"); + goto err1; + } + + ret = dwc3_core_init_mode(dwc); + if (ret) + goto err2; + + list_add_tail(&dwc->list, &dwc3_list); + + return 0; + +err2: + dwc3_event_buffers_cleanup(dwc); + +err1: + dwc3_core_exit(dwc); + +err0: + dwc3_free_event_buffers(dwc); + + return ret; +} + +/** + * dwc3_uboot_exit - dwc3 core uboot cleanup code + * @index: index of this controller + * + * Performs cleanup of memory allocated in dwc3_uboot_init and other misc + * cleanups (equivalent to dwc3_remove in linux). index of _this_ controller + * should be passed and should match with the index passed in + * dwc3_device during init. + * + * Generally called from board file. + */ +void dwc3_uboot_exit(int index) +{ + struct dwc3 *dwc; + + list_for_each_entry(dwc, &dwc3_list, list) { + if (dwc->index != index) + continue; + + dwc3_core_exit_mode(dwc); + dwc3_event_buffers_cleanup(dwc); + dwc3_free_event_buffers(dwc); + dwc3_core_exit(dwc); + list_del(&dwc->list); + kfree(dwc->mem); + break; + } +} + +/** + * dwc3_uboot_handle_interrupt - handle dwc3 core interrupt + * @index: index of this controller + * + * Invokes dwc3 gadget interrupts. + * + * Generally called from board file. + */ +void dwc3_uboot_handle_interrupt(int index) +{ + struct dwc3 *dwc = NULL; + + list_for_each_entry(dwc, &dwc3_list, list) { + if (dwc->index != index) + continue; + + dwc3_gadget_uboot_handle_interrupt(dwc); + break; + } +} + +MODULE_ALIAS("platform:dwc3"); +MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver"); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h new file mode 100644 index 0000000000..72d2fcdd3f --- /dev/null +++ b/drivers/usb/dwc3/core.h @@ -0,0 +1,1032 @@ +/** + * core.h - DesignWare USB3 DRD Core Header + * + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/core.h) and ported + * to uboot. + * + * commit 460d098cb6 : usb: dwc3: make HIRD threshold configurable + * + * SPDX-License-Identifier: GPL-2.0 + * + */ + +#ifndef __DRIVERS_USB_DWC3_CORE_H +#define __DRIVERS_USB_DWC3_CORE_H + +#include <linux/ioport.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/otg.h> + +#define DWC3_MSG_MAX 500 + +/* Global constants */ +#define DWC3_EP0_BOUNCE_SIZE 512 +#define DWC3_ENDPOINTS_NUM 32 +#define DWC3_XHCI_RESOURCES_NUM 2 + +#define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */ +#define DWC3_EVENT_SIZE 4 /* bytes */ +#define DWC3_EVENT_MAX_NUM 64 /* 2 events/endpoint */ +#define DWC3_EVENT_BUFFERS_SIZE (DWC3_EVENT_SIZE * DWC3_EVENT_MAX_NUM) +#define DWC3_EVENT_TYPE_MASK 0xfe + +#define DWC3_EVENT_TYPE_DEV 0 +#define DWC3_EVENT_TYPE_CARKIT 3 +#define DWC3_EVENT_TYPE_I2C 4 + +#define DWC3_DEVICE_EVENT_DISCONNECT 0 +#define DWC3_DEVICE_EVENT_RESET 1 +#define DWC3_DEVICE_EVENT_CONNECT_DONE 2 +#define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE 3 +#define DWC3_DEVICE_EVENT_WAKEUP 4 +#define DWC3_DEVICE_EVENT_HIBER_REQ 5 +#define DWC3_DEVICE_EVENT_EOPF 6 +#define DWC3_DEVICE_EVENT_SOF 7 +#define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9 +#define DWC3_DEVICE_EVENT_CMD_CMPL 10 +#define DWC3_DEVICE_EVENT_OVERFLOW 11 + +#define DWC3_GEVNTCOUNT_MASK 0xfffc +#define DWC3_GSNPSID_MASK 0xffff0000 +#define DWC3_GSNPSREV_MASK 0xffff + +/* DWC3 registers memory space boundries */ +#define DWC3_XHCI_REGS_START 0x0 +#define DWC3_XHCI_REGS_END 0x7fff +#define DWC3_GLOBALS_REGS_START 0xc100 +#define DWC3_GLOBALS_REGS_END 0xc6ff +#define DWC3_DEVICE_REGS_START 0xc700 +#define DWC3_DEVICE_REGS_END 0xcbff +#define DWC3_OTG_REGS_START 0xcc00 +#define DWC3_OTG_REGS_END 0xccff + +/* Global Registers */ +#define DWC3_GSBUSCFG0 0xc100 +#define DWC3_GSBUSCFG1 0xc104 +#define DWC3_GTXTHRCFG 0xc108 +#define DWC3_GRXTHRCFG 0xc10c +#define DWC3_GCTL 0xc110 +#define DWC3_GEVTEN 0xc114 +#define DWC3_GSTS 0xc118 +#define DWC3_GSNPSID 0xc120 +#define DWC3_GGPIO 0xc124 +#define DWC3_GUID 0xc128 +#define DWC3_GUCTL 0xc12c +#define DWC3_GBUSERRADDR0 0xc130 +#define DWC3_GBUSERRADDR1 0xc134 +#define DWC3_GPRTBIMAP0 0xc138 +#define DWC3_GPRTBIMAP1 0xc13c +#define DWC3_GHWPARAMS0 0xc140 +#define DWC3_GHWPARAMS1 0xc144 +#define DWC3_GHWPARAMS2 0xc148 +#define DWC3_GHWPARAMS3 0xc14c +#define DWC3_GHWPARAMS4 0xc150 +#define DWC3_GHWPARAMS5 0xc154 +#define DWC3_GHWPARAMS6 0xc158 +#define DWC3_GHWPARAMS7 0xc15c +#define DWC3_GDBGFIFOSPACE 0xc160 +#define DWC3_GDBGLTSSM 0xc164 +#define DWC3_GPRTBIMAP_HS0 0xc180 +#define DWC3_GPRTBIMAP_HS1 0xc184 +#define DWC3_GPRTBIMAP_FS0 0xc188 +#define DWC3_GPRTBIMAP_FS1 0xc18c + +#define DWC3_GUSB2PHYCFG(n) (0xc200 + (n * 0x04)) +#define DWC3_GUSB2I2CCTL(n) (0xc240 + (n * 0x04)) + +#define DWC3_GUSB2PHYACC(n) (0xc280 + (n * 0x04)) + +#define DWC3_GUSB3PIPECTL(n) (0xc2c0 + (n * 0x04)) + +#define DWC3_GTXFIFOSIZ(n) (0xc300 + (n * 0x04)) +#define DWC3_GRXFIFOSIZ(n) (0xc380 + (n * 0x04)) + +#define DWC3_GEVNTADRLO(n) (0xc400 + (n * 0x10)) +#define DWC3_GEVNTADRHI(n) (0xc404 + (n * 0x10)) +#define DWC3_GEVNTSIZ(n) (0xc408 + (n * 0x10)) +#define DWC3_GEVNTCOUNT(n) (0xc40c + (n * 0x10)) + +#define DWC3_GHWPARAMS8 0xc600 + +/* Device Registers */ +#define DWC3_DCFG 0xc700 +#define DWC3_DCTL 0xc704 +#define DWC3_DEVTEN 0xc708 +#define DWC3_DSTS 0xc70c +#define DWC3_DGCMDPAR 0xc710 +#define DWC3_DGCMD 0xc714 +#define DWC3_DALEPENA 0xc720 +#define DWC3_DEPCMDPAR2(n) (0xc800 + (n * 0x10)) +#define DWC3_DEPCMDPAR1(n) (0xc804 + (n * 0x10)) +#define DWC3_DEPCMDPAR0(n) (0xc808 + (n * 0x10)) +#define DWC3_DEPCMD(n) (0xc80c + (n * 0x10)) + +/* OTG Registers */ +#define DWC3_OCFG 0xcc00 +#define DWC3_OCTL 0xcc04 +#define DWC3_OEVT 0xcc08 +#define DWC3_OEVTEN 0xcc0C +#define DWC3_OSTS 0xcc10 + +/* Bit fields */ + +/* Global Configuration Register */ +#define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19) +#define DWC3_GCTL_U2RSTECN (1 << 16) +#define DWC3_GCTL_RAMCLKSEL(x) (((x) & DWC3_GCTL_CLK_MASK) << 6) +#define DWC3_GCTL_CLK_BUS (0) +#define DWC3_GCTL_CLK_PIPE (1) +#define DWC3_GCTL_CLK_PIPEHALF (2) +#define DWC3_GCTL_CLK_MASK (3) + +#define DWC3_GCTL_PRTCAP(n) (((n) & (3 << 12)) >> 12) +#define DWC3_GCTL_PRTCAPDIR(n) ((n) << 12) +#define DWC3_GCTL_PRTCAP_HOST 1 +#define DWC3_GCTL_PRTCAP_DEVICE 2 +#define DWC3_GCTL_PRTCAP_OTG 3 + +#define DWC3_GCTL_CORESOFTRESET (1 << 11) +#define DWC3_GCTL_SOFITPSYNC (1 << 10) +#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4) +#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3) +#define DWC3_GCTL_DISSCRAMBLE (1 << 3) +#define DWC3_GCTL_U2EXIT_LFPS (1 << 2) +#define DWC3_GCTL_GBLHIBERNATIONEN (1 << 1) +#define DWC3_GCTL_DSBLCLKGTNG (1 << 0) + +/* Global USB2 PHY Configuration Register */ +#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31) +#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6) + +/* Global USB3 PIPE Control Register */ +#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31) +#define DWC3_GUSB3PIPECTL_U2SSINP3OK (1 << 29) +#define DWC3_GUSB3PIPECTL_REQP1P2P3 (1 << 24) +#define DWC3_GUSB3PIPECTL_DEP1P2P3(n) ((n) << 19) +#define DWC3_GUSB3PIPECTL_DEP1P2P3_MASK DWC3_GUSB3PIPECTL_DEP1P2P3(7) +#define DWC3_GUSB3PIPECTL_DEP1P2P3_EN DWC3_GUSB3PIPECTL_DEP1P2P3(1) +#define DWC3_GUSB3PIPECTL_DEPOCHANGE (1 << 18) +#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17) +#define DWC3_GUSB3PIPECTL_LFPSFILT (1 << 9) +#define DWC3_GUSB3PIPECTL_RX_DETOPOLL (1 << 8) +#define DWC3_GUSB3PIPECTL_TX_DEEPH_MASK DWC3_GUSB3PIPECTL_TX_DEEPH(3) +#define DWC3_GUSB3PIPECTL_TX_DEEPH(n) ((n) << 1) + +/* Global TX Fifo Size Register */ +#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff) +#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000) + +/* Global Event Size Registers */ +#define DWC3_GEVNTSIZ_INTMASK (1 << 31) +#define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff) + +/* Global HWPARAMS1 Register */ +#define DWC3_GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24) +#define DWC3_GHWPARAMS1_EN_PWROPT_NO 0 +#define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1 +#define DWC3_GHWPARAMS1_EN_PWROPT_HIB 2 +#define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24) +#define DWC3_GHWPARAMS1_PWROPT_MASK DWC3_GHWPARAMS1_PWROPT(3) + +/* Global HWPARAMS3 Register */ +#define DWC3_GHWPARAMS3_SSPHY_IFC(n) ((n) & 3) +#define DWC3_GHWPARAMS3_SSPHY_IFC_DIS 0 +#define DWC3_GHWPARAMS3_SSPHY_IFC_ENA 1 +#define DWC3_GHWPARAMS3_HSPHY_IFC(n) (((n) & (3 << 2)) >> 2) +#define DWC3_GHWPARAMS3_HSPHY_IFC_DIS 0 +#define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI 1 +#define DWC3_GHWPARAMS3_HSPHY_IFC_ULPI 2 +#define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI 3 +#define DWC3_GHWPARAMS3_FSPHY_IFC(n) (((n) & (3 << 4)) >> 4) +#define DWC3_GHWPARAMS3_FSPHY_IFC_DIS 0 +#define DWC3_GHWPARAMS3_FSPHY_IFC_ENA 1 + +/* Global HWPARAMS4 Register */ +#define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13) +#define DWC3_MAX_HIBER_SCRATCHBUFS 15 + +/* Global HWPARAMS6 Register */ +#define DWC3_GHWPARAMS6_EN_FPGA (1 << 7) + +/* Device Configuration Register */ +#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) +#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) + +#define DWC3_DCFG_SPEED_MASK (7 << 0) +#define DWC3_DCFG_SUPERSPEED (4 << 0) +#define DWC3_DCFG_HIGHSPEED (0 << 0) +#define DWC3_DCFG_FULLSPEED2 (1 << 0) +#define DWC3_DCFG_LOWSPEED (2 << 0) +#define DWC3_DCFG_FULLSPEED1 (3 << 0) + +#define DWC3_DCFG_LPM_CAP (1 << 22) + +/* Device Control Register */ +#define DWC3_DCTL_RUN_STOP (1 << 31) +#define DWC3_DCTL_CSFTRST (1 << 30) +#define DWC3_DCTL_LSFTRST (1 << 29) + +#define DWC3_DCTL_HIRD_THRES_MASK (0x1f << 24) +#define DWC3_DCTL_HIRD_THRES(n) ((n) << 24) + +#define DWC3_DCTL_APPL1RES (1 << 23) + +/* These apply for core versions 1.87a and earlier */ +#define DWC3_DCTL_TRGTULST_MASK (0x0f << 17) +#define DWC3_DCTL_TRGTULST(n) ((n) << 17) +#define DWC3_DCTL_TRGTULST_U2 (DWC3_DCTL_TRGTULST(2)) +#define DWC3_DCTL_TRGTULST_U3 (DWC3_DCTL_TRGTULST(3)) +#define DWC3_DCTL_TRGTULST_SS_DIS (DWC3_DCTL_TRGTULST(4)) +#define DWC3_DCTL_TRGTULST_RX_DET (DWC3_DCTL_TRGTULST(5)) +#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6)) + +/* These apply for core versions 1.94a and later */ +#define DWC3_DCTL_LPM_ERRATA_MASK DWC3_DCTL_LPM_ERRATA(0xf) +#define DWC3_DCTL_LPM_ERRATA(n) ((n) << 20) + +#define DWC3_DCTL_KEEP_CONNECT (1 << 19) +#define DWC3_DCTL_L1_HIBER_EN (1 << 18) +#define DWC3_DCTL_CRS (1 << 17) +#define DWC3_DCTL_CSS (1 << 16) + +#define DWC3_DCTL_INITU2ENA (1 << 12) +#define DWC3_DCTL_ACCEPTU2ENA (1 << 11) +#define DWC3_DCTL_INITU1ENA (1 << 10) +#define DWC3_DCTL_ACCEPTU1ENA (1 << 9) +#define DWC3_DCTL_TSTCTRL_MASK (0xf << 1) + +#define DWC3_DCTL_ULSTCHNGREQ_MASK (0x0f << 5) +#define DWC3_DCTL_ULSTCHNGREQ(n) (((n) << 5) & DWC3_DCTL_ULSTCHNGREQ_MASK) + +#define DWC3_DCTL_ULSTCHNG_NO_ACTION (DWC3_DCTL_ULSTCHNGREQ(0)) +#define DWC3_DCTL_ULSTCHNG_SS_DISABLED (DWC3_DCTL_ULSTCHNGREQ(4)) +#define DWC3_DCTL_ULSTCHNG_RX_DETECT (DWC3_DCTL_ULSTCHNGREQ(5)) +#define DWC3_DCTL_ULSTCHNG_SS_INACTIVE (DWC3_DCTL_ULSTCHNGREQ(6)) +#define DWC3_DCTL_ULSTCHNG_RECOVERY (DWC3_DCTL_ULSTCHNGREQ(8)) +#define DWC3_DCTL_ULSTCHNG_COMPLIANCE (DWC3_DCTL_ULSTCHNGREQ(10)) +#define DWC3_DCTL_ULSTCHNG_LOOPBACK (DWC3_DCTL_ULSTCHNGREQ(11)) + +/* Device Event Enable Register */ +#define DWC3_DEVTEN_VNDRDEVTSTRCVEDEN (1 << 12) +#define DWC3_DEVTEN_EVNTOVERFLOWEN (1 << 11) +#define DWC3_DEVTEN_CMDCMPLTEN (1 << 10) +#define DWC3_DEVTEN_ERRTICERREN (1 << 9) +#define DWC3_DEVTEN_SOFEN (1 << 7) +#define DWC3_DEVTEN_EOPFEN (1 << 6) +#define DWC3_DEVTEN_HIBERNATIONREQEVTEN (1 << 5) +#define DWC3_DEVTEN_WKUPEVTEN (1 << 4) +#define DWC3_DEVTEN_ULSTCNGEN (1 << 3) +#define DWC3_DEVTEN_CONNECTDONEEN (1 << 2) +#define DWC3_DEVTEN_USBRSTEN (1 << 1) +#define DWC3_DEVTEN_DISCONNEVTEN (1 << 0) + +/* Device Status Register */ +#define DWC3_DSTS_DCNRD (1 << 29) + +/* This applies for core versions 1.87a and earlier */ +#define DWC3_DSTS_PWRUPREQ (1 << 24) + +/* These apply for core versions 1.94a and later */ +#define DWC3_DSTS_RSS (1 << 25) +#define DWC3_DSTS_SSS (1 << 24) + +#define DWC3_DSTS_COREIDLE (1 << 23) +#define DWC3_DSTS_DEVCTRLHLT (1 << 22) + +#define DWC3_DSTS_USBLNKST_MASK (0x0f << 18) +#define DWC3_DSTS_USBLNKST(n) (((n) & DWC3_DSTS_USBLNKST_MASK) >> 18) + +#define DWC3_DSTS_RXFIFOEMPTY (1 << 17) + +#define DWC3_DSTS_SOFFN_MASK (0x3fff << 3) +#define DWC3_DSTS_SOFFN(n) (((n) & DWC3_DSTS_SOFFN_MASK) >> 3) + +#define DWC3_DSTS_CONNECTSPD (7 << 0) + +#define DWC3_DSTS_SUPERSPEED (4 << 0) +#define DWC3_DSTS_HIGHSPEED (0 << 0) +#define DWC3_DSTS_FULLSPEED2 (1 << 0) +#define DWC3_DSTS_LOWSPEED (2 << 0) +#define DWC3_DSTS_FULLSPEED1 (3 << 0) + +/* Device Generic Command Register */ +#define DWC3_DGCMD_SET_LMP 0x01 +#define DWC3_DGCMD_SET_PERIODIC_PAR 0x02 +#define DWC3_DGCMD_XMIT_FUNCTION 0x03 + +/* These apply for core versions 1.94a and later */ +#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO 0x04 +#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI 0x05 + +#define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09 +#define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a +#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c +#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10 + +#define DWC3_DGCMD_STATUS(n) (((n) >> 15) & 1) +#define DWC3_DGCMD_CMDACT (1 << 10) +#define DWC3_DGCMD_CMDIOC (1 << 8) + +/* Device Generic Command Parameter Register */ +#define DWC3_DGCMDPAR_FORCE_LINKPM_ACCEPT (1 << 0) +#define DWC3_DGCMDPAR_FIFO_NUM(n) ((n) << 0) +#define DWC3_DGCMDPAR_RX_FIFO (0 << 5) +#define DWC3_DGCMDPAR_TX_FIFO (1 << 5) +#define DWC3_DGCMDPAR_LOOPBACK_DIS (0 << 0) +#define DWC3_DGCMDPAR_LOOPBACK_ENA (1 << 0) + +/* Device Endpoint Command Register */ +#define DWC3_DEPCMD_PARAM_SHIFT 16 +#define DWC3_DEPCMD_PARAM(x) ((x) << DWC3_DEPCMD_PARAM_SHIFT) +#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f) +#define DWC3_DEPCMD_STATUS(x) (((x) >> 15) & 1) +#define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11) +#define DWC3_DEPCMD_CMDACT (1 << 10) +#define DWC3_DEPCMD_CMDIOC (1 << 8) + +#define DWC3_DEPCMD_DEPSTARTCFG (0x09 << 0) +#define DWC3_DEPCMD_ENDTRANSFER (0x08 << 0) +#define DWC3_DEPCMD_UPDATETRANSFER (0x07 << 0) +#define DWC3_DEPCMD_STARTTRANSFER (0x06 << 0) +#define DWC3_DEPCMD_CLEARSTALL (0x05 << 0) +#define DWC3_DEPCMD_SETSTALL (0x04 << 0) +/* This applies for core versions 1.90a and earlier */ +#define DWC3_DEPCMD_GETSEQNUMBER (0x03 << 0) +/* This applies for core versions 1.94a and later */ +#define DWC3_DEPCMD_GETEPSTATE (0x03 << 0) +#define DWC3_DEPCMD_SETTRANSFRESOURCE (0x02 << 0) +#define DWC3_DEPCMD_SETEPCONFIG (0x01 << 0) + +/* The EP number goes 0..31 so ep0 is always out and ep1 is always in */ +#define DWC3_DALEPENA_EP(n) (1 << n) + +#define DWC3_DEPCMD_TYPE_CONTROL 0 +#define DWC3_DEPCMD_TYPE_ISOC 1 +#define DWC3_DEPCMD_TYPE_BULK 2 +#define DWC3_DEPCMD_TYPE_INTR 3 + +/* Structures */ + +struct dwc3_trb; + +/** + * struct dwc3_event_buffer - Software event buffer representation + * @buf: _THE_ buffer + * @length: size of this buffer + * @lpos: event offset + * @count: cache of last read event count register + * @flags: flags related to this event buffer + * @dma: dma_addr_t + * @dwc: pointer to DWC controller + */ +struct dwc3_event_buffer { + void *buf; + unsigned length; + unsigned int lpos; + unsigned int count; + unsigned int flags; + +#define DWC3_EVENT_PENDING (1UL << 0) + + dma_addr_t dma; + + struct dwc3 *dwc; +}; + +#define DWC3_EP_FLAG_STALLED (1 << 0) +#define DWC3_EP_FLAG_WEDGED (1 << 1) + +#define DWC3_EP_DIRECTION_TX true +#define DWC3_EP_DIRECTION_RX false + +#define DWC3_TRB_NUM 32 +#define DWC3_TRB_MASK (DWC3_TRB_NUM - 1) + +/** + * struct dwc3_ep - device side endpoint representation + * @endpoint: usb endpoint + * @request_list: list of requests for this endpoint + * @req_queued: list of requests on this ep which have TRBs setup + * @trb_pool: array of transaction buffers + * @trb_pool_dma: dma address of @trb_pool + * @free_slot: next slot which is going to be used + * @busy_slot: first slot which is owned by HW + * @desc: usb_endpoint_descriptor pointer + * @dwc: pointer to DWC controller + * @saved_state: ep state saved during hibernation + * @flags: endpoint flags (wedged, stalled, ...) + * @current_trb: index of current used trb + * @number: endpoint number (1 - 15) + * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK + * @resource_index: Resource transfer index + * @interval: the interval on which the ISOC transfer is started + * @name: a human readable name e.g. ep1out-bulk + * @direction: true for TX, false for RX + * @stream_capable: true when streams are enabled + */ +struct dwc3_ep { + struct usb_ep endpoint; + struct list_head request_list; + struct list_head req_queued; + + struct dwc3_trb *trb_pool; + dma_addr_t trb_pool_dma; + u32 free_slot; + u32 busy_slot; + const struct usb_ss_ep_comp_descriptor *comp_desc; + struct dwc3 *dwc; + + u32 saved_state; + unsigned flags; +#define DWC3_EP_ENABLED (1 << 0) +#define DWC3_EP_STALL (1 << 1) +#define DWC3_EP_WEDGE (1 << 2) +#define DWC3_EP_BUSY (1 << 4) +#define DWC3_EP_PENDING_REQUEST (1 << 5) +#define DWC3_EP_MISSED_ISOC (1 << 6) + + /* This last one is specific to EP0 */ +#define DWC3_EP0_DIR_IN (1 << 31) + + unsigned current_trb; + + u8 number; + u8 type; + u8 resource_index; + u32 interval; + + char name[20]; + + unsigned direction:1; + unsigned stream_capable:1; +}; + +enum dwc3_phy { + DWC3_PHY_UNKNOWN = 0, + DWC3_PHY_USB3, + DWC3_PHY_USB2, +}; + +enum dwc3_ep0_next { + DWC3_EP0_UNKNOWN = 0, + DWC3_EP0_COMPLETE, + DWC3_EP0_NRDY_DATA, + DWC3_EP0_NRDY_STATUS, +}; + +enum dwc3_ep0_state { + EP0_UNCONNECTED = 0, + EP0_SETUP_PHASE, + EP0_DATA_PHASE, + EP0_STATUS_PHASE, +}; + +enum dwc3_link_state { + /* In SuperSpeed */ + DWC3_LINK_STATE_U0 = 0x00, /* in HS, means ON */ + DWC3_LINK_STATE_U1 = 0x01, + DWC3_LINK_STATE_U2 = 0x02, /* in HS, means SLEEP */ + DWC3_LINK_STATE_U3 = 0x03, /* in HS, means SUSPEND */ + DWC3_LINK_STATE_SS_DIS = 0x04, + DWC3_LINK_STATE_RX_DET = 0x05, /* in HS, means Early Suspend */ + DWC3_LINK_STATE_SS_INACT = 0x06, + DWC3_LINK_STATE_POLL = 0x07, + DWC3_LINK_STATE_RECOV = 0x08, + DWC3_LINK_STATE_HRESET = 0x09, + DWC3_LINK_STATE_CMPLY = 0x0a, + DWC3_LINK_STATE_LPBK = 0x0b, + DWC3_LINK_STATE_RESET = 0x0e, + DWC3_LINK_STATE_RESUME = 0x0f, + DWC3_LINK_STATE_MASK = 0x0f, +}; + +/* TRB Length, PCM and Status */ +#define DWC3_TRB_SIZE_MASK (0x00ffffff) +#define DWC3_TRB_SIZE_LENGTH(n) ((n) & DWC3_TRB_SIZE_MASK) +#define DWC3_TRB_SIZE_PCM1(n) (((n) & 0x03) << 24) +#define DWC3_TRB_SIZE_TRBSTS(n) (((n) & (0x0f << 28)) >> 28) + +#define DWC3_TRBSTS_OK 0 +#define DWC3_TRBSTS_MISSED_ISOC 1 +#define DWC3_TRBSTS_SETUP_PENDING 2 +#define DWC3_TRB_STS_XFER_IN_PROG 4 + +/* TRB Control */ +#define DWC3_TRB_CTRL_HWO (1 << 0) +#define DWC3_TRB_CTRL_LST (1 << 1) +#define DWC3_TRB_CTRL_CHN (1 << 2) +#define DWC3_TRB_CTRL_CSP (1 << 3) +#define DWC3_TRB_CTRL_TRBCTL(n) (((n) & 0x3f) << 4) +#define DWC3_TRB_CTRL_ISP_IMI (1 << 10) +#define DWC3_TRB_CTRL_IOC (1 << 11) +#define DWC3_TRB_CTRL_SID_SOFN(n) (((n) & 0xffff) << 14) + +#define DWC3_TRBCTL_NORMAL DWC3_TRB_CTRL_TRBCTL(1) +#define DWC3_TRBCTL_CONTROL_SETUP DWC3_TRB_CTRL_TRBCTL(2) +#define DWC3_TRBCTL_CONTROL_STATUS2 DWC3_TRB_CTRL_TRBCTL(3) +#define DWC3_TRBCTL_CONTROL_STATUS3 DWC3_TRB_CTRL_TRBCTL(4) +#define DWC3_TRBCTL_CONTROL_DATA DWC3_TRB_CTRL_TRBCTL(5) +#define DWC3_TRBCTL_ISOCHRONOUS_FIRST DWC3_TRB_CTRL_TRBCTL(6) +#define DWC3_TRBCTL_ISOCHRONOUS DWC3_TRB_CTRL_TRBCTL(7) +#define DWC3_TRBCTL_LINK_TRB DWC3_TRB_CTRL_TRBCTL(8) + +/** + * struct dwc3_trb - transfer request block (hw format) + * @bpl: DW0-3 + * @bph: DW4-7 + * @size: DW8-B + * @trl: DWC-F + */ +struct dwc3_trb { + u32 bpl; + u32 bph; + u32 size; + u32 ctrl; +} __packed; + +/** + * dwc3_hwparams - copy of HWPARAMS registers + * @hwparams0 - GHWPARAMS0 + * @hwparams1 - GHWPARAMS1 + * @hwparams2 - GHWPARAMS2 + * @hwparams3 - GHWPARAMS3 + * @hwparams4 - GHWPARAMS4 + * @hwparams5 - GHWPARAMS5 + * @hwparams6 - GHWPARAMS6 + * @hwparams7 - GHWPARAMS7 + * @hwparams8 - GHWPARAMS8 + */ +struct dwc3_hwparams { + u32 hwparams0; + u32 hwparams1; + u32 hwparams2; + u32 hwparams3; + u32 hwparams4; + u32 hwparams5; + u32 hwparams6; + u32 hwparams7; + u32 hwparams8; +}; + +/* HWPARAMS0 */ +#define DWC3_MODE(n) ((n) & 0x7) + +#define DWC3_MDWIDTH(n) (((n) & 0xff00) >> 8) + +/* HWPARAMS1 */ +#define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15) + +/* HWPARAMS3 */ +#define DWC3_NUM_IN_EPS_MASK (0x1f << 18) +#define DWC3_NUM_EPS_MASK (0x3f << 12) +#define DWC3_NUM_EPS(p) (((p)->hwparams3 & \ + (DWC3_NUM_EPS_MASK)) >> 12) +#define DWC3_NUM_IN_EPS(p) (((p)->hwparams3 & \ + (DWC3_NUM_IN_EPS_MASK)) >> 18) + +/* HWPARAMS7 */ +#define DWC3_RAM1_DEPTH(n) ((n) & 0xffff) + +struct dwc3_request { + struct usb_request request; + struct list_head list; + struct dwc3_ep *dep; + u32 start_slot; + + u8 epnum; + struct dwc3_trb *trb; + dma_addr_t trb_dma; + + unsigned direction:1; + unsigned mapped:1; + unsigned queued:1; +}; + +/* + * struct dwc3_scratchpad_array - hibernation scratchpad array + * (format defined by hw) + */ +struct dwc3_scratchpad_array { + __le64 dma_adr[DWC3_MAX_HIBER_SCRATCHBUFS]; +}; + +/** + * struct dwc3 - representation of our controller + * @ctrl_req: usb control request which is used for ep0 + * @ep0_trb: trb which is used for the ctrl_req + * @ep0_bounce: bounce buffer for ep0 + * @setup_buf: used while precessing STD USB requests + * @ctrl_req_addr: dma address of ctrl_req + * @ep0_trb: dma address of ep0_trb + * @ep0_usb_req: dummy req used while handling STD USB requests + * @ep0_bounce_addr: dma address of ep0_bounce + * @scratch_addr: dma address of scratchbuf + * @lock: for synchronizing + * @dev: pointer to our struct device + * @xhci: pointer to our xHCI child + * @event_buffer_list: a list of event buffers + * @gadget: device side representation of the peripheral controller + * @gadget_driver: pointer to the gadget driver + * @regs: base address for our registers + * @regs_size: address space size + * @nr_scratch: number of scratch buffers + * @num_event_buffers: calculated number of event buffers + * @u1u2: only used on revisions <1.83a for workaround + * @maximum_speed: maximum speed requested (mainly for testing purposes) + * @revision: revision register contents + * @dr_mode: requested mode of operation + * @dcfg: saved contents of DCFG register + * @gctl: saved contents of GCTL register + * @isoch_delay: wValue from Set Isochronous Delay request; + * @u2sel: parameter from Set SEL request. + * @u2pel: parameter from Set SEL request. + * @u1sel: parameter from Set SEL request. + * @u1pel: parameter from Set SEL request. + * @num_out_eps: number of out endpoints + * @num_in_eps: number of in endpoints + * @ep0_next_event: hold the next expected event + * @ep0state: state of endpoint zero + * @link_state: link state + * @speed: device speed (super, high, full, low) + * @mem: points to start of memory which is used for this struct. + * @hwparams: copy of hwparams registers + * @root: debugfs root folder pointer + * @regset: debugfs pointer to regdump file + * @test_mode: true when we're entering a USB test mode + * @test_mode_nr: test feature selector + * @lpm_nyet_threshold: LPM NYET response threshold + * @hird_threshold: HIRD threshold + * @delayed_status: true when gadget driver asks for delayed status + * @ep0_bounced: true when we used bounce buffer + * @ep0_expect_in: true when we expect a DATA IN transfer + * @has_hibernation: true when dwc3 was configured with Hibernation + * @has_lpm_erratum: true when core was configured with LPM Erratum. Note that + * there's now way for software to detect this in runtime. + * @is_utmi_l1_suspend: the core asserts output signal + * 0 - utmi_sleep_n + * 1 - utmi_l1_suspend_n + * @is_selfpowered: true when we are selfpowered + * @is_fpga: true when we are using the FPGA board + * @needs_fifo_resize: not all users might want fifo resizing, flag it + * @pullups_connected: true when Run/Stop bit is set + * @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes. + * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround + * @start_config_issued: true when StartConfig command has been issued + * @three_stage_setup: set if we perform a three phase setup + * @disable_scramble_quirk: set if we enable the disable scramble quirk + * @u2exit_lfps_quirk: set if we enable u2exit lfps quirk + * @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk + * @req_p1p2p3_quirk: set if we enable request p1p2p3 quirk + * @del_p1p2p3_quirk: set if we enable delay p1p2p3 quirk + * @del_phy_power_chg_quirk: set if we enable delay phy power change quirk + * @lfps_filter_quirk: set if we enable LFPS filter quirk + * @rx_detect_poll_quirk: set if we enable rx_detect to polling lfps quirk + * @dis_u3_susphy_quirk: set if we disable usb3 suspend phy + * @dis_u2_susphy_quirk: set if we disable usb2 suspend phy + * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk + * @tx_de_emphasis: Tx de-emphasis value + * 0 - -6dB de-emphasis + * 1 - -3.5dB de-emphasis + * 2 - No de-emphasis + * 3 - Reserved + * @index: index of _this_ controller + * @list: to maintain the list of dwc3 controllers + */ +struct dwc3 { + struct usb_ctrlrequest *ctrl_req; + struct dwc3_trb *ep0_trb; + void *ep0_bounce; + void *scratchbuf; + u8 *setup_buf; + dma_addr_t ctrl_req_addr; + dma_addr_t ep0_trb_addr; + dma_addr_t ep0_bounce_addr; + dma_addr_t scratch_addr; + struct dwc3_request ep0_usb_req; + + /* device lock */ + spinlock_t lock; + + struct device *dev; + + struct platform_device *xhci; + struct resource xhci_resources[DWC3_XHCI_RESOURCES_NUM]; + + struct dwc3_event_buffer **ev_buffs; + struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM]; + + struct usb_gadget gadget; + struct usb_gadget_driver *gadget_driver; + + void __iomem *regs; + size_t regs_size; + + enum usb_dr_mode dr_mode; + + /* used for suspend/resume */ + u32 dcfg; + u32 gctl; + + u32 nr_scratch; + u32 num_event_buffers; + u32 u1u2; + u32 maximum_speed; + u32 revision; + +#define DWC3_REVISION_173A 0x5533173a +#define DWC3_REVISION_175A 0x5533175a +#define DWC3_REVISION_180A 0x5533180a +#define DWC3_REVISION_183A 0x5533183a +#define DWC3_REVISION_185A 0x5533185a +#define DWC3_REVISION_187A 0x5533187a +#define DWC3_REVISION_188A 0x5533188a +#define DWC3_REVISION_190A 0x5533190a +#define DWC3_REVISION_194A 0x5533194a +#define DWC3_REVISION_200A 0x5533200a +#define DWC3_REVISION_202A 0x5533202a +#define DWC3_REVISION_210A 0x5533210a +#define DWC3_REVISION_220A 0x5533220a +#define DWC3_REVISION_230A 0x5533230a +#define DWC3_REVISION_240A 0x5533240a +#define DWC3_REVISION_250A 0x5533250a +#define DWC3_REVISION_260A 0x5533260a +#define DWC3_REVISION_270A 0x5533270a +#define DWC3_REVISION_280A 0x5533280a + + enum dwc3_ep0_next ep0_next_event; + enum dwc3_ep0_state ep0state; + enum dwc3_link_state link_state; + + u16 isoch_delay; + u16 u2sel; + u16 u2pel; + u8 u1sel; + u8 u1pel; + + u8 speed; + + u8 num_out_eps; + u8 num_in_eps; + + void *mem; + + struct dwc3_hwparams hwparams; + struct dentry *root; + struct debugfs_regset32 *regset; + + u8 test_mode; + u8 test_mode_nr; + u8 lpm_nyet_threshold; + u8 hird_threshold; + + unsigned delayed_status:1; + unsigned ep0_bounced:1; + unsigned ep0_expect_in:1; + unsigned has_hibernation:1; + unsigned has_lpm_erratum:1; + unsigned is_utmi_l1_suspend:1; + unsigned is_selfpowered:1; + unsigned is_fpga:1; + unsigned needs_fifo_resize:1; + unsigned pullups_connected:1; + unsigned resize_fifos:1; + unsigned setup_packet_pending:1; + unsigned start_config_issued:1; + unsigned three_stage_setup:1; + + unsigned disable_scramble_quirk:1; + unsigned u2exit_lfps_quirk:1; + unsigned u2ss_inp3_quirk:1; + unsigned req_p1p2p3_quirk:1; + unsigned del_p1p2p3_quirk:1; + unsigned del_phy_power_chg_quirk:1; + unsigned lfps_filter_quirk:1; + unsigned rx_detect_poll_quirk:1; + unsigned dis_u3_susphy_quirk:1; + unsigned dis_u2_susphy_quirk:1; + + unsigned tx_de_emphasis_quirk:1; + unsigned tx_de_emphasis:2; + int index; + struct list_head list; +}; + +/* -------------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------------- */ + +struct dwc3_event_type { + u32 is_devspec:1; + u32 type:7; + u32 reserved8_31:24; +} __packed; + +#define DWC3_DEPEVT_XFERCOMPLETE 0x01 +#define DWC3_DEPEVT_XFERINPROGRESS 0x02 +#define DWC3_DEPEVT_XFERNOTREADY 0x03 +#define DWC3_DEPEVT_RXTXFIFOEVT 0x04 +#define DWC3_DEPEVT_STREAMEVT 0x06 +#define DWC3_DEPEVT_EPCMDCMPLT 0x07 + +/** + * dwc3_ep_event_string - returns event name + * @event: then event code + */ +static inline const char *dwc3_ep_event_string(u8 event) +{ + switch (event) { + case DWC3_DEPEVT_XFERCOMPLETE: + return "Transfer Complete"; + case DWC3_DEPEVT_XFERINPROGRESS: + return "Transfer In-Progress"; + case DWC3_DEPEVT_XFERNOTREADY: + return "Transfer Not Ready"; + case DWC3_DEPEVT_RXTXFIFOEVT: + return "FIFO"; + case DWC3_DEPEVT_STREAMEVT: + return "Stream"; + case DWC3_DEPEVT_EPCMDCMPLT: + return "Endpoint Command Complete"; + } + + return "UNKNOWN"; +} + +/** + * struct dwc3_event_depvt - Device Endpoint Events + * @one_bit: indicates this is an endpoint event (not used) + * @endpoint_number: number of the endpoint + * @endpoint_event: The event we have: + * 0x00 - Reserved + * 0x01 - XferComplete + * 0x02 - XferInProgress + * 0x03 - XferNotReady + * 0x04 - RxTxFifoEvt (IN->Underrun, OUT->Overrun) + * 0x05 - Reserved + * 0x06 - StreamEvt + * 0x07 - EPCmdCmplt + * @reserved11_10: Reserved, don't use. + * @status: Indicates the status of the event. Refer to databook for + * more information. + * @parameters: Parameters of the current event. Refer to databook for + * more information. + */ +struct dwc3_event_depevt { + u32 one_bit:1; + u32 endpoint_number:5; + u32 endpoint_event:4; + u32 reserved11_10:2; + u32 status:4; + +/* Within XferNotReady */ +#define DEPEVT_STATUS_TRANSFER_ACTIVE (1 << 3) + +/* Within XferComplete */ +#define DEPEVT_STATUS_BUSERR (1 << 0) +#define DEPEVT_STATUS_SHORT (1 << 1) +#define DEPEVT_STATUS_IOC (1 << 2) +#define DEPEVT_STATUS_LST (1 << 3) + +/* Stream event only */ +#define DEPEVT_STREAMEVT_FOUND 1 +#define DEPEVT_STREAMEVT_NOTFOUND 2 + +/* Control-only Status */ +#define DEPEVT_STATUS_CONTROL_DATA 1 +#define DEPEVT_STATUS_CONTROL_STATUS 2 + + u32 parameters:16; +} __packed; + +/** + * struct dwc3_event_devt - Device Events + * @one_bit: indicates this is a non-endpoint event (not used) + * @device_event: indicates it's a device event. Should read as 0x00 + * @type: indicates the type of device event. + * 0 - DisconnEvt + * 1 - USBRst + * 2 - ConnectDone + * 3 - ULStChng + * 4 - WkUpEvt + * 5 - Reserved + * 6 - EOPF + * 7 - SOF + * 8 - Reserved + * 9 - ErrticErr + * 10 - CmdCmplt + * 11 - EvntOverflow + * 12 - VndrDevTstRcved + * @reserved15_12: Reserved, not used + * @event_info: Information about this event + * @reserved31_25: Reserved, not used + */ +struct dwc3_event_devt { + u32 one_bit:1; + u32 device_event:7; + u32 type:4; + u32 reserved15_12:4; + u32 event_info:9; + u32 reserved31_25:7; +} __packed; + +/** + * struct dwc3_event_gevt - Other Core Events + * @one_bit: indicates this is a non-endpoint event (not used) + * @device_event: indicates it's (0x03) Carkit or (0x04) I2C event. + * @phy_port_number: self-explanatory + * @reserved31_12: Reserved, not used. + */ +struct dwc3_event_gevt { + u32 one_bit:1; + u32 device_event:7; + u32 phy_port_number:4; + u32 reserved31_12:20; +} __packed; + +/** + * union dwc3_event - representation of Event Buffer contents + * @raw: raw 32-bit event + * @type: the type of the event + * @depevt: Device Endpoint Event + * @devt: Device Event + * @gevt: Global Event + */ +union dwc3_event { + u32 raw; + struct dwc3_event_type type; + struct dwc3_event_depevt depevt; + struct dwc3_event_devt devt; + struct dwc3_event_gevt gevt; +}; + +/** + * struct dwc3_gadget_ep_cmd_params - representation of endpoint command + * parameters + * @param2: third parameter + * @param1: second parameter + * @param0: first parameter + */ +struct dwc3_gadget_ep_cmd_params { + u32 param2; + u32 param1; + u32 param0; +}; + +/* + * DWC3 Features to be used as Driver Data + */ + +#define DWC3_HAS_PERIPHERAL BIT(0) +#define DWC3_HAS_XHCI BIT(1) +#define DWC3_HAS_OTG BIT(3) + +/* prototypes */ +int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc); + +#ifdef CONFIG_USB_DWC3_HOST +int dwc3_host_init(struct dwc3 *dwc); +void dwc3_host_exit(struct dwc3 *dwc); +#else +static inline int dwc3_host_init(struct dwc3 *dwc) +{ return 0; } +static inline void dwc3_host_exit(struct dwc3 *dwc) +{ } +#endif + +#ifdef CONFIG_USB_DWC3_GADGET +int dwc3_gadget_init(struct dwc3 *dwc); +void dwc3_gadget_exit(struct dwc3 *dwc); +int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode); +int dwc3_gadget_get_link_state(struct dwc3 *dwc); +int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state); +int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, + unsigned cmd, struct dwc3_gadget_ep_cmd_params *params); +int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param); +#else +static inline int dwc3_gadget_init(struct dwc3 *dwc) +{ return 0; } +static inline void dwc3_gadget_exit(struct dwc3 *dwc) +{ } +static inline int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode) +{ return 0; } +static inline int dwc3_gadget_get_link_state(struct dwc3 *dwc) +{ return 0; } +static inline int dwc3_gadget_set_link_state(struct dwc3 *dwc, + enum dwc3_link_state state) +{ return 0; } + +static inline int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, + unsigned cmd, struct dwc3_gadget_ep_cmd_params *params) +{ return 0; } +static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc, + int cmd, u32 param) +{ return 0; } +#endif + +#endif /* __DRIVERS_USB_DWC3_CORE_H */ diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c new file mode 100644 index 0000000000..46af109141 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -0,0 +1,441 @@ +/** + * dwc3-omap.c - OMAP Specific Glue layer + * + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/dwc3-omap.c) and ported + * to uboot. + * + * commit 7ee2566ff5 : usb: dwc3: dwc3-omap: get rid of ->prepare()/->complete() + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <malloc.h> +#include <asm/io.h> +#include <dwc3-omap-uboot.h> +#include <linux/usb/dwc3-omap.h> +#include <linux/ioport.h> + +#include <linux/usb/otg.h> +#include <linux/compat.h> + +#include "linux-compat.h" + +/* + * All these registers belong to OMAP's Wrapper around the + * DesignWare USB3 Core. + */ + +#define USBOTGSS_REVISION 0x0000 +#define USBOTGSS_SYSCONFIG 0x0010 +#define USBOTGSS_IRQ_EOI 0x0020 +#define USBOTGSS_EOI_OFFSET 0x0008 +#define USBOTGSS_IRQSTATUS_RAW_0 0x0024 +#define USBOTGSS_IRQSTATUS_0 0x0028 +#define USBOTGSS_IRQENABLE_SET_0 0x002c +#define USBOTGSS_IRQENABLE_CLR_0 0x0030 +#define USBOTGSS_IRQ0_OFFSET 0x0004 +#define USBOTGSS_IRQSTATUS_RAW_1 0x0030 +#define USBOTGSS_IRQSTATUS_1 0x0034 +#define USBOTGSS_IRQENABLE_SET_1 0x0038 +#define USBOTGSS_IRQENABLE_CLR_1 0x003c +#define USBOTGSS_IRQSTATUS_RAW_2 0x0040 +#define USBOTGSS_IRQSTATUS_2 0x0044 +#define USBOTGSS_IRQENABLE_SET_2 0x0048 +#define USBOTGSS_IRQENABLE_CLR_2 0x004c +#define USBOTGSS_IRQSTATUS_RAW_3 0x0050 +#define USBOTGSS_IRQSTATUS_3 0x0054 +#define USBOTGSS_IRQENABLE_SET_3 0x0058 +#define USBOTGSS_IRQENABLE_CLR_3 0x005c +#define USBOTGSS_IRQSTATUS_EOI_MISC 0x0030 +#define USBOTGSS_IRQSTATUS_RAW_MISC 0x0034 +#define USBOTGSS_IRQSTATUS_MISC 0x0038 +#define USBOTGSS_IRQENABLE_SET_MISC 0x003c +#define USBOTGSS_IRQENABLE_CLR_MISC 0x0040 +#define USBOTGSS_IRQMISC_OFFSET 0x03fc +#define USBOTGSS_UTMI_OTG_CTRL 0x0080 +#define USBOTGSS_UTMI_OTG_STATUS 0x0084 +#define USBOTGSS_UTMI_OTG_OFFSET 0x0480 +#define USBOTGSS_TXFIFO_DEPTH 0x0508 +#define USBOTGSS_RXFIFO_DEPTH 0x050c +#define USBOTGSS_MMRAM_OFFSET 0x0100 +#define USBOTGSS_FLADJ 0x0104 +#define USBOTGSS_DEBUG_CFG 0x0108 +#define USBOTGSS_DEBUG_DATA 0x010c +#define USBOTGSS_DEV_EBC_EN 0x0110 +#define USBOTGSS_DEBUG_OFFSET 0x0600 + +/* SYSCONFIG REGISTER */ +#define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16) + +/* IRQ_EOI REGISTER */ +#define USBOTGSS_IRQ_EOI_LINE_NUMBER (1 << 0) + +/* IRQS0 BITS */ +#define USBOTGSS_IRQO_COREIRQ_ST (1 << 0) + +/* IRQMISC BITS */ +#define USBOTGSS_IRQMISC_DMADISABLECLR (1 << 17) +#define USBOTGSS_IRQMISC_OEVT (1 << 16) +#define USBOTGSS_IRQMISC_DRVVBUS_RISE (1 << 13) +#define USBOTGSS_IRQMISC_CHRGVBUS_RISE (1 << 12) +#define USBOTGSS_IRQMISC_DISCHRGVBUS_RISE (1 << 11) +#define USBOTGSS_IRQMISC_IDPULLUP_RISE (1 << 8) +#define USBOTGSS_IRQMISC_DRVVBUS_FALL (1 << 5) +#define USBOTGSS_IRQMISC_CHRGVBUS_FALL (1 << 4) +#define USBOTGSS_IRQMISC_DISCHRGVBUS_FALL (1 << 3) +#define USBOTGSS_IRQMISC_IDPULLUP_FALL (1 << 0) + +/* UTMI_OTG_CTRL REGISTER */ +#define USBOTGSS_UTMI_OTG_CTRL_DRVVBUS (1 << 5) +#define USBOTGSS_UTMI_OTG_CTRL_CHRGVBUS (1 << 4) +#define USBOTGSS_UTMI_OTG_CTRL_DISCHRGVBUS (1 << 3) +#define USBOTGSS_UTMI_OTG_CTRL_IDPULLUP (1 << 0) + +/* UTMI_OTG_STATUS REGISTER */ +#define USBOTGSS_UTMI_OTG_STATUS_SW_MODE (1 << 31) +#define USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT (1 << 9) +#define USBOTGSS_UTMI_OTG_STATUS_TXBITSTUFFENABLE (1 << 8) +#define USBOTGSS_UTMI_OTG_STATUS_IDDIG (1 << 4) +#define USBOTGSS_UTMI_OTG_STATUS_SESSEND (1 << 3) +#define USBOTGSS_UTMI_OTG_STATUS_SESSVALID (1 << 2) +#define USBOTGSS_UTMI_OTG_STATUS_VBUSVALID (1 << 1) + +struct dwc3_omap { + struct device *dev; + + void __iomem *base; + + u32 utmi_otg_status; + u32 utmi_otg_offset; + u32 irqmisc_offset; + u32 irq_eoi_offset; + u32 debug_offset; + u32 irq0_offset; + + u32 dma_status:1; + struct list_head list; + u32 index; +}; + +static LIST_HEAD(dwc3_omap_list); + +static inline u32 dwc3_omap_readl(void __iomem *base, u32 offset) +{ + return readl(base + offset); +} + +static inline void dwc3_omap_writel(void __iomem *base, u32 offset, u32 value) +{ + writel(value, base + offset); +} + +static u32 dwc3_omap_read_utmi_status(struct dwc3_omap *omap) +{ + return dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS + + omap->utmi_otg_offset); +} + +static void dwc3_omap_write_utmi_status(struct dwc3_omap *omap, u32 value) +{ + dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS + + omap->utmi_otg_offset, value); + +} + +static u32 dwc3_omap_read_irq0_status(struct dwc3_omap *omap) +{ + return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_0 - + omap->irq0_offset); +} + +static void dwc3_omap_write_irq0_status(struct dwc3_omap *omap, u32 value) +{ + dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_0 - + omap->irq0_offset, value); + +} + +static u32 dwc3_omap_read_irqmisc_status(struct dwc3_omap *omap) +{ + return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_MISC + + omap->irqmisc_offset); +} + +static void dwc3_omap_write_irqmisc_status(struct dwc3_omap *omap, u32 value) +{ + dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_MISC + + omap->irqmisc_offset, value); + +} + +static void dwc3_omap_write_irqmisc_set(struct dwc3_omap *omap, u32 value) +{ + dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_MISC + + omap->irqmisc_offset, value); + +} + +static void dwc3_omap_write_irq0_set(struct dwc3_omap *omap, u32 value) +{ + dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_0 - + omap->irq0_offset, value); +} + +static void dwc3_omap_set_mailbox(struct dwc3_omap *omap, + enum omap_dwc3_vbus_id_status status) +{ + u32 val; + + switch (status) { + case OMAP_DWC3_ID_GROUND: + dev_dbg(omap->dev, "ID GND\n"); + + val = dwc3_omap_read_utmi_status(omap); + val &= ~(USBOTGSS_UTMI_OTG_STATUS_IDDIG + | USBOTGSS_UTMI_OTG_STATUS_VBUSVALID + | USBOTGSS_UTMI_OTG_STATUS_SESSEND); + val |= USBOTGSS_UTMI_OTG_STATUS_SESSVALID + | USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT; + dwc3_omap_write_utmi_status(omap, val); + break; + + case OMAP_DWC3_VBUS_VALID: + dev_dbg(omap->dev, "VBUS Connect\n"); + + val = dwc3_omap_read_utmi_status(omap); + val &= ~USBOTGSS_UTMI_OTG_STATUS_SESSEND; + val |= USBOTGSS_UTMI_OTG_STATUS_IDDIG + | USBOTGSS_UTMI_OTG_STATUS_VBUSVALID + | USBOTGSS_UTMI_OTG_STATUS_SESSVALID + | USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT; + dwc3_omap_write_utmi_status(omap, val); + break; + + case OMAP_DWC3_ID_FLOAT: + case OMAP_DWC3_VBUS_OFF: + dev_dbg(omap->dev, "VBUS Disconnect\n"); + + val = dwc3_omap_read_utmi_status(omap); + val &= ~(USBOTGSS_UTMI_OTG_STATUS_SESSVALID + | USBOTGSS_UTMI_OTG_STATUS_VBUSVALID + | USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT); + val |= USBOTGSS_UTMI_OTG_STATUS_SESSEND + | USBOTGSS_UTMI_OTG_STATUS_IDDIG; + dwc3_omap_write_utmi_status(omap, val); + break; + + default: + dev_dbg(omap->dev, "invalid state\n"); + } +} + +static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap) +{ + struct dwc3_omap *omap = _omap; + u32 reg; + + reg = dwc3_omap_read_irqmisc_status(omap); + + if (reg & USBOTGSS_IRQMISC_DMADISABLECLR) { + dev_dbg(omap->dev, "DMA Disable was Cleared\n"); + omap->dma_status = false; + } + + if (reg & USBOTGSS_IRQMISC_OEVT) + dev_dbg(omap->dev, "OTG Event\n"); + + if (reg & USBOTGSS_IRQMISC_DRVVBUS_RISE) + dev_dbg(omap->dev, "DRVVBUS Rise\n"); + + if (reg & USBOTGSS_IRQMISC_CHRGVBUS_RISE) + dev_dbg(omap->dev, "CHRGVBUS Rise\n"); + + if (reg & USBOTGSS_IRQMISC_DISCHRGVBUS_RISE) + dev_dbg(omap->dev, "DISCHRGVBUS Rise\n"); + + if (reg & USBOTGSS_IRQMISC_IDPULLUP_RISE) + dev_dbg(omap->dev, "IDPULLUP Rise\n"); + + if (reg & USBOTGSS_IRQMISC_DRVVBUS_FALL) + dev_dbg(omap->dev, "DRVVBUS Fall\n"); + + if (reg & USBOTGSS_IRQMISC_CHRGVBUS_FALL) + dev_dbg(omap->dev, "CHRGVBUS Fall\n"); + + if (reg & USBOTGSS_IRQMISC_DISCHRGVBUS_FALL) + dev_dbg(omap->dev, "DISCHRGVBUS Fall\n"); + + if (reg & USBOTGSS_IRQMISC_IDPULLUP_FALL) + dev_dbg(omap->dev, "IDPULLUP Fall\n"); + + dwc3_omap_write_irqmisc_status(omap, reg); + + reg = dwc3_omap_read_irq0_status(omap); + + dwc3_omap_write_irq0_status(omap, reg); + + return IRQ_HANDLED; +} + +static void dwc3_omap_enable_irqs(struct dwc3_omap *omap) +{ + u32 reg; + + /* enable all IRQs */ + reg = USBOTGSS_IRQO_COREIRQ_ST; + dwc3_omap_write_irq0_set(omap, reg); + + reg = (USBOTGSS_IRQMISC_OEVT | + USBOTGSS_IRQMISC_DRVVBUS_RISE | + USBOTGSS_IRQMISC_CHRGVBUS_RISE | + USBOTGSS_IRQMISC_DISCHRGVBUS_RISE | + USBOTGSS_IRQMISC_IDPULLUP_RISE | + USBOTGSS_IRQMISC_DRVVBUS_FALL | + USBOTGSS_IRQMISC_CHRGVBUS_FALL | + USBOTGSS_IRQMISC_DISCHRGVBUS_FALL | + USBOTGSS_IRQMISC_IDPULLUP_FALL); + + dwc3_omap_write_irqmisc_set(omap, reg); +} + +static void dwc3_omap_disable_irqs(struct dwc3_omap *omap) +{ + /* disable all IRQs */ + dwc3_omap_write_irqmisc_set(omap, 0x00); + dwc3_omap_write_irq0_set(omap, 0x00); +} + +static void dwc3_omap_map_offset(struct dwc3_omap *omap) +{ + /* + * Differentiate between OMAP5 and AM437x. + * + * For OMAP5(ES2.0) and AM437x wrapper revision is same, even + * though there are changes in wrapper register offsets. + * + * Using dt compatible to differentiate AM437x. + */ +#ifdef CONFIG_AM43XX + omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET; + omap->irq0_offset = USBOTGSS_IRQ0_OFFSET; + omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET; + omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET; + omap->debug_offset = USBOTGSS_DEBUG_OFFSET; +#endif +} + +static void dwc3_omap_set_utmi_mode(struct dwc3_omap *omap, int utmi_mode) +{ + u32 reg; + + reg = dwc3_omap_read_utmi_status(omap); + + switch (utmi_mode) { + case DWC3_OMAP_UTMI_MODE_SW: + reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE; + break; + case DWC3_OMAP_UTMI_MODE_HW: + reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE; + break; + default: + dev_dbg(omap->dev, "UNKNOWN utmi mode %d\n", utmi_mode); + } + + dwc3_omap_write_utmi_status(omap, reg); +} + +/** + * dwc3_omap_uboot_init - dwc3 omap uboot initialization code + * @dev: struct dwc3_omap_device containing initialization data + * + * Entry point for dwc3 omap driver (equivalent to dwc3_omap_probe in linux + * kernel driver). Pointer to dwc3_omap_device should be passed containing + * base address and other initialization data. Returns '0' on success and + * a negative value on failure. + * + * Generally called from board_usb_init() implemented in board file. + */ +int dwc3_omap_uboot_init(struct dwc3_omap_device *omap_dev) +{ + u32 reg; + struct device *dev; + struct dwc3_omap *omap; + + omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL); + if (!omap) + return -ENOMEM; + + omap->base = omap_dev->base; + omap->index = omap_dev->index; + + dwc3_omap_map_offset(omap); + dwc3_omap_set_utmi_mode(omap, omap_dev->utmi_mode); + + /* check the DMA Status */ + reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG); + omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE); + + dwc3_omap_set_mailbox(omap, omap_dev->vbus_id_status); + + dwc3_omap_enable_irqs(omap); + list_add_tail(&omap->list, &dwc3_omap_list); + + return 0; +} + +/** + * dwc3_omap_uboot_exit - dwc3 omap uboot cleanup code + * @index: index of this controller + * + * Performs cleanup of memory allocated in dwc3_omap_uboot_init + * (equivalent to dwc3_omap_remove in linux). index of _this_ controller + * should be passed and should match with the index passed in + * dwc3_omap_device during init. + * + * Generally called from board file. + */ +void dwc3_omap_uboot_exit(int index) +{ + struct dwc3_omap *omap = NULL; + + list_for_each_entry(omap, &dwc3_omap_list, list) { + if (omap->index != index) + continue; + + dwc3_omap_disable_irqs(omap); + list_del(&omap->list); + kfree(omap); + break; + } +} + +/** + * dwc3_omap_uboot_interrupt_status - check the status of interrupt + * @index: index of this controller + * + * Checks the status of interrupts and returns true if an interrupt + * is detected or false otherwise. + * + * Generally called from board file. + */ +int dwc3_omap_uboot_interrupt_status(int index) +{ + struct dwc3_omap *omap = NULL; + + list_for_each_entry(omap, &dwc3_omap_list, list) + if (omap->index == index) + return dwc3_omap_interrupt(-1, omap); + + return 0; +} + +MODULE_ALIAS("platform:omap-dwc3"); +MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("DesignWare USB3 OMAP Glue Layer"); diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c new file mode 100644 index 0000000000..aba614fb4e --- /dev/null +++ b/drivers/usb/dwc3/ep0.c @@ -0,0 +1,1112 @@ +/** + * ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling + * + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/ep0.c) and ported + * to uboot. + * + * commit c00552ebaf : Merge 3.18-rc7 into usb-next + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <linux/kernel.h> +#include <linux/list.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h> + +#include "core.h" +#include "gadget.h" +#include "io.h" + +#include "linux-compat.h" + +static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep); +static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, + struct dwc3_ep *dep, struct dwc3_request *req); + +static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) +{ + switch (state) { + case EP0_UNCONNECTED: + return "Unconnected"; + case EP0_SETUP_PHASE: + return "Setup Phase"; + case EP0_DATA_PHASE: + return "Data Phase"; + case EP0_STATUS_PHASE: + return "Status Phase"; + default: + return "UNKNOWN"; + } +} + +static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, + u32 len, u32 type, unsigned chain) +{ + struct dwc3_gadget_ep_cmd_params params; + struct dwc3_trb *trb; + struct dwc3_ep *dep; + + int ret; + + dep = dwc->eps[epnum]; + if (dep->flags & DWC3_EP_BUSY) { + dev_vdbg(dwc->dev, "%s still busy", dep->name); + return 0; + } + + trb = &dwc->ep0_trb[dep->free_slot]; + + if (chain) + dep->free_slot++; + + trb->bpl = lower_32_bits(buf_dma); + trb->bph = upper_32_bits(buf_dma); + trb->size = len; + trb->ctrl = type; + + trb->ctrl |= (DWC3_TRB_CTRL_HWO + | DWC3_TRB_CTRL_ISP_IMI); + + if (chain) + trb->ctrl |= DWC3_TRB_CTRL_CHN; + else + trb->ctrl |= (DWC3_TRB_CTRL_IOC + | DWC3_TRB_CTRL_LST); + + dwc3_flush_cache((int)buf_dma, len); + dwc3_flush_cache((int)trb, sizeof(*trb)); + + if (chain) + return 0; + + memset(¶ms, 0, sizeof(params)); + params.param0 = upper_32_bits(dwc->ep0_trb_addr); + params.param1 = lower_32_bits(dwc->ep0_trb_addr); + + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_STARTTRANSFER, ¶ms); + if (ret < 0) { + dev_dbg(dwc->dev, "%s STARTTRANSFER failed", dep->name); + return ret; + } + + dep->flags |= DWC3_EP_BUSY; + dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc, + dep->number); + + dwc->ep0_next_event = DWC3_EP0_COMPLETE; + + return 0; +} + +static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, + struct dwc3_request *req) +{ + struct dwc3 *dwc = dep->dwc; + + req->request.actual = 0; + req->request.status = -EINPROGRESS; + req->epnum = dep->number; + + list_add_tail(&req->list, &dep->request_list); + + /* + * Gadget driver might not be quick enough to queue a request + * before we get a Transfer Not Ready event on this endpoint. + * + * In that case, we will set DWC3_EP_PENDING_REQUEST. When that + * flag is set, it's telling us that as soon as Gadget queues the + * required request, we should kick the transfer here because the + * IRQ we were waiting for is long gone. + */ + if (dep->flags & DWC3_EP_PENDING_REQUEST) { + unsigned direction; + + direction = !!(dep->flags & DWC3_EP0_DIR_IN); + + if (dwc->ep0state != EP0_DATA_PHASE) { + dev_WARN(dwc->dev, "Unexpected pending request\n"); + return 0; + } + + __dwc3_ep0_do_control_data(dwc, dwc->eps[direction], req); + + dep->flags &= ~(DWC3_EP_PENDING_REQUEST | + DWC3_EP0_DIR_IN); + + return 0; + } + + /* + * In case gadget driver asked us to delay the STATUS phase, + * handle it here. + */ + if (dwc->delayed_status) { + unsigned direction; + + direction = !dwc->ep0_expect_in; + dwc->delayed_status = false; + usb_gadget_set_state(&dwc->gadget, USB_STATE_CONFIGURED); + + if (dwc->ep0state == EP0_STATUS_PHASE) + __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]); + else + dev_dbg(dwc->dev, "too early for delayed status"); + + return 0; + } + + /* + * Unfortunately we have uncovered a limitation wrt the Data Phase. + * + * Section 9.4 says we can wait for the XferNotReady(DATA) event to + * come before issueing Start Transfer command, but if we do, we will + * miss situations where the host starts another SETUP phase instead of + * the DATA phase. Such cases happen at least on TD.7.6 of the Link + * Layer Compliance Suite. + * + * The problem surfaces due to the fact that in case of back-to-back + * SETUP packets there will be no XferNotReady(DATA) generated and we + * will be stuck waiting for XferNotReady(DATA) forever. + * + * By looking at tables 9-13 and 9-14 of the Databook, we can see that + * it tells us to start Data Phase right away. It also mentions that if + * we receive a SETUP phase instead of the DATA phase, core will issue + * XferComplete for the DATA phase, before actually initiating it in + * the wire, with the TRB's status set to "SETUP_PENDING". Such status + * can only be used to print some debugging logs, as the core expects + * us to go through to the STATUS phase and start a CONTROL_STATUS TRB, + * just so it completes right away, without transferring anything and, + * only then, we can go back to the SETUP phase. + * + * Because of this scenario, SNPS decided to change the programming + * model of control transfers and support on-demand transfers only for + * the STATUS phase. To fix the issue we have now, we will always wait + * for gadget driver to queue the DATA phase's struct usb_request, then + * start it right away. + * + * If we're actually in a 2-stage transfer, we will wait for + * XferNotReady(STATUS). + */ + if (dwc->three_stage_setup) { + unsigned direction; + + direction = dwc->ep0_expect_in; + dwc->ep0state = EP0_DATA_PHASE; + + __dwc3_ep0_do_control_data(dwc, dwc->eps[direction], req); + + dep->flags &= ~DWC3_EP0_DIR_IN; + } + + return 0; +} + +int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, + gfp_t gfp_flags) +{ + struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + unsigned long flags; + + int ret; + + spin_lock_irqsave(&dwc->lock, flags); + if (!dep->endpoint.desc) { + dev_dbg(dwc->dev, "trying to queue request %p to disabled %s", + request, dep->name); + ret = -ESHUTDOWN; + goto out; + } + + /* we share one TRB for ep0/1 */ + if (!list_empty(&dep->request_list)) { + ret = -EBUSY; + goto out; + } + + dev_vdbg(dwc->dev, "queueing request %p to %s length %d state '%s'", + request, dep->name, request->length, + dwc3_ep0_state_string(dwc->ep0state)); + + ret = __dwc3_gadget_ep0_queue(dep, req); + +out: + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) +{ + struct dwc3_ep *dep; + + /* reinitialize physical ep1 */ + dep = dwc->eps[1]; + dep->flags = DWC3_EP_ENABLED; + + /* stall is always issued on EP0 */ + dep = dwc->eps[0]; + __dwc3_gadget_ep_set_halt(dep, 1, false); + dep->flags = DWC3_EP_ENABLED; + dwc->delayed_status = false; + + if (!list_empty(&dep->request_list)) { + struct dwc3_request *req; + + req = next_request(&dep->request_list); + dwc3_gadget_giveback(dep, req, -ECONNRESET); + } + + dwc->ep0state = EP0_SETUP_PHASE; + dwc3_ep0_out_start(dwc); +} + +int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value) +{ + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + dwc3_ep0_stall_and_restart(dwc); + + return 0; +} + +int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&dwc->lock, flags); + ret = __dwc3_gadget_ep0_set_halt(ep, value); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +void dwc3_ep0_out_start(struct dwc3 *dwc) +{ + int ret; + + ret = dwc3_ep0_start_trans(dwc, 0, dwc->ctrl_req_addr, 8, + DWC3_TRBCTL_CONTROL_SETUP, 0); + WARN_ON(ret < 0); +} + +static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le) +{ + struct dwc3_ep *dep; + u32 windex = le16_to_cpu(wIndex_le); + u32 epnum; + + epnum = (windex & USB_ENDPOINT_NUMBER_MASK) << 1; + if ((windex & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) + epnum |= 1; + + dep = dwc->eps[epnum]; + if (dep->flags & DWC3_EP_ENABLED) + return dep; + + return NULL; +} + +static void dwc3_ep0_status_cmpl(struct usb_ep *ep, struct usb_request *req) +{ +} +/* + * ch 9.4.5 + */ +static int dwc3_ep0_handle_status(struct dwc3 *dwc, + struct usb_ctrlrequest *ctrl) +{ + struct dwc3_ep *dep; + u32 recip; + u32 reg; + u16 usb_status = 0; + __le16 *response_pkt; + + recip = ctrl->bRequestType & USB_RECIP_MASK; + switch (recip) { + case USB_RECIP_DEVICE: + /* + * LTM will be set once we know how to set this in HW. + */ + usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED; + + if (dwc->speed == DWC3_DSTS_SUPERSPEED) { + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (reg & DWC3_DCTL_INITU1ENA) + usb_status |= 1 << USB_DEV_STAT_U1_ENABLED; + if (reg & DWC3_DCTL_INITU2ENA) + usb_status |= 1 << USB_DEV_STAT_U2_ENABLED; + } + + break; + + case USB_RECIP_INTERFACE: + /* + * Function Remote Wake Capable D0 + * Function Remote Wakeup D1 + */ + break; + + case USB_RECIP_ENDPOINT: + dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex); + if (!dep) + return -EINVAL; + + if (dep->flags & DWC3_EP_STALL) + usb_status = 1 << USB_ENDPOINT_HALT; + break; + default: + return -EINVAL; + } + + response_pkt = (__le16 *) dwc->setup_buf; + *response_pkt = cpu_to_le16(usb_status); + + dep = dwc->eps[0]; + dwc->ep0_usb_req.dep = dep; + dwc->ep0_usb_req.request.length = sizeof(*response_pkt); + dwc->ep0_usb_req.request.buf = dwc->setup_buf; + dwc->ep0_usb_req.request.complete = dwc3_ep0_status_cmpl; + + return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req); +} + +static int dwc3_ep0_handle_feature(struct dwc3 *dwc, + struct usb_ctrlrequest *ctrl, int set) +{ + struct dwc3_ep *dep; + u32 recip; + u32 wValue; + u32 wIndex; + u32 reg; + int ret; + enum usb_device_state state; + + wValue = le16_to_cpu(ctrl->wValue); + wIndex = le16_to_cpu(ctrl->wIndex); + recip = ctrl->bRequestType & USB_RECIP_MASK; + state = dwc->gadget.state; + + switch (recip) { + case USB_RECIP_DEVICE: + + switch (wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + break; + /* + * 9.4.1 says only only for SS, in AddressState only for + * default control pipe + */ + case USB_DEVICE_U1_ENABLE: + if (state != USB_STATE_CONFIGURED) + return -EINVAL; + if (dwc->speed != DWC3_DSTS_SUPERSPEED) + return -EINVAL; + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (set) + reg |= DWC3_DCTL_INITU1ENA; + else + reg &= ~DWC3_DCTL_INITU1ENA; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + break; + + case USB_DEVICE_U2_ENABLE: + if (state != USB_STATE_CONFIGURED) + return -EINVAL; + if (dwc->speed != DWC3_DSTS_SUPERSPEED) + return -EINVAL; + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (set) + reg |= DWC3_DCTL_INITU2ENA; + else + reg &= ~DWC3_DCTL_INITU2ENA; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + break; + + case USB_DEVICE_LTM_ENABLE: + return -EINVAL; + + case USB_DEVICE_TEST_MODE: + if ((wIndex & 0xff) != 0) + return -EINVAL; + if (!set) + return -EINVAL; + + dwc->test_mode_nr = wIndex >> 8; + dwc->test_mode = true; + break; + default: + return -EINVAL; + } + break; + + case USB_RECIP_INTERFACE: + switch (wValue) { + case USB_INTRF_FUNC_SUSPEND: + if (wIndex & USB_INTRF_FUNC_SUSPEND_LP) + /* XXX enable Low power suspend */ + ; + if (wIndex & USB_INTRF_FUNC_SUSPEND_RW) + /* XXX enable remote wakeup */ + ; + break; + default: + return -EINVAL; + } + break; + + case USB_RECIP_ENDPOINT: + switch (wValue) { + case USB_ENDPOINT_HALT: + dep = dwc3_wIndex_to_dep(dwc, wIndex); + if (!dep) + return -EINVAL; + if (set == 0 && (dep->flags & DWC3_EP_WEDGE)) + break; + ret = __dwc3_gadget_ep_set_halt(dep, set, true); + if (ret) + return -EINVAL; + break; + default: + return -EINVAL; + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + enum usb_device_state state = dwc->gadget.state; + u32 addr; + u32 reg; + + addr = le16_to_cpu(ctrl->wValue); + if (addr > 127) { + dev_dbg(dwc->dev, "invalid device address %d", addr); + return -EINVAL; + } + + if (state == USB_STATE_CONFIGURED) { + dev_dbg(dwc->dev, "trying to set address when configured"); + return -EINVAL; + } + + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~(DWC3_DCFG_DEVADDR_MASK); + reg |= DWC3_DCFG_DEVADDR(addr); + dwc3_writel(dwc->regs, DWC3_DCFG, reg); + + if (addr) + usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS); + else + usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT); + + return 0; +} + +static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + int ret; + + spin_unlock(&dwc->lock); + ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl); + spin_lock(&dwc->lock); + return ret; +} + +static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + enum usb_device_state state = dwc->gadget.state; + u32 cfg; + int ret; + u32 reg; + + dwc->start_config_issued = false; + cfg = le16_to_cpu(ctrl->wValue); + + switch (state) { + case USB_STATE_DEFAULT: + return -EINVAL; + + case USB_STATE_ADDRESS: + ret = dwc3_ep0_delegate_req(dwc, ctrl); + /* if the cfg matches and the cfg is non zero */ + if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) { + + /* + * only change state if set_config has already + * been processed. If gadget driver returns + * USB_GADGET_DELAYED_STATUS, we will wait + * to change the state on the next usb_ep_queue() + */ + if (ret == 0) + usb_gadget_set_state(&dwc->gadget, + USB_STATE_CONFIGURED); + + /* + * Enable transition to U1/U2 state when + * nothing is pending from application. + */ + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA); + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + dwc->resize_fifos = true; + dev_dbg(dwc->dev, "resize FIFOs flag SET"); + } + break; + + case USB_STATE_CONFIGURED: + ret = dwc3_ep0_delegate_req(dwc, ctrl); + if (!cfg && !ret) + usb_gadget_set_state(&dwc->gadget, + USB_STATE_ADDRESS); + break; + default: + ret = -EINVAL; + } + return ret; +} + +static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req) +{ + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + u32 param = 0; + u32 reg; + + struct timing { + u8 u1sel; + u8 u1pel; + u16 u2sel; + u16 u2pel; + } __packed timing; + + int ret; + + memcpy(&timing, req->buf, sizeof(timing)); + + dwc->u1sel = timing.u1sel; + dwc->u1pel = timing.u1pel; + dwc->u2sel = le16_to_cpu(timing.u2sel); + dwc->u2pel = le16_to_cpu(timing.u2pel); + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (reg & DWC3_DCTL_INITU2ENA) + param = dwc->u2pel; + if (reg & DWC3_DCTL_INITU1ENA) + param = dwc->u1pel; + + /* + * According to Synopsys Databook, if parameter is + * greater than 125, a value of zero should be + * programmed in the register. + */ + if (param > 125) + param = 0; + + /* now that we have the time, issue DGCMD Set Sel */ + ret = dwc3_send_gadget_generic_command(dwc, + DWC3_DGCMD_SET_PERIODIC_PAR, param); + WARN_ON(ret < 0); +} + +static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + struct dwc3_ep *dep; + enum usb_device_state state = dwc->gadget.state; + u16 wLength; + + if (state == USB_STATE_DEFAULT) + return -EINVAL; + + wLength = le16_to_cpu(ctrl->wLength); + + if (wLength != 6) { + dev_err(dwc->dev, "Set SEL should be 6 bytes, got %d\n", + wLength); + return -EINVAL; + } + + /* + * To handle Set SEL we need to receive 6 bytes from Host. So let's + * queue a usb_request for 6 bytes. + * + * Remember, though, this controller can't handle non-wMaxPacketSize + * aligned transfers on the OUT direction, so we queue a request for + * wMaxPacketSize instead. + */ + dep = dwc->eps[0]; + dwc->ep0_usb_req.dep = dep; + dwc->ep0_usb_req.request.length = dep->endpoint.maxpacket; + dwc->ep0_usb_req.request.buf = dwc->setup_buf; + dwc->ep0_usb_req.request.complete = dwc3_ep0_set_sel_cmpl; + + return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req); +} + +static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + u16 wLength; + u16 wValue; + u16 wIndex; + + wValue = le16_to_cpu(ctrl->wValue); + wLength = le16_to_cpu(ctrl->wLength); + wIndex = le16_to_cpu(ctrl->wIndex); + + if (wIndex || wLength) + return -EINVAL; + + /* + * REVISIT It's unclear from Databook what to do with this + * value. For now, just cache it. + */ + dwc->isoch_delay = wValue; + + return 0; +} + +static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + int ret; + + switch (ctrl->bRequest) { + case USB_REQ_GET_STATUS: + dev_vdbg(dwc->dev, "USB_REQ_GET_STATUS"); + ret = dwc3_ep0_handle_status(dwc, ctrl); + break; + case USB_REQ_CLEAR_FEATURE: + dev_vdbg(dwc->dev, "USB_REQ_CLEAR_FEATURE"); + ret = dwc3_ep0_handle_feature(dwc, ctrl, 0); + break; + case USB_REQ_SET_FEATURE: + dev_vdbg(dwc->dev, "USB_REQ_SET_FEATURE"); + ret = dwc3_ep0_handle_feature(dwc, ctrl, 1); + break; + case USB_REQ_SET_ADDRESS: + dev_vdbg(dwc->dev, "USB_REQ_SET_ADDRESS"); + ret = dwc3_ep0_set_address(dwc, ctrl); + break; + case USB_REQ_SET_CONFIGURATION: + dev_vdbg(dwc->dev, "USB_REQ_SET_CONFIGURATION"); + ret = dwc3_ep0_set_config(dwc, ctrl); + break; + case USB_REQ_SET_SEL: + dev_vdbg(dwc->dev, "USB_REQ_SET_SEL"); + ret = dwc3_ep0_set_sel(dwc, ctrl); + break; + case USB_REQ_SET_ISOCH_DELAY: + dev_vdbg(dwc->dev, "USB_REQ_SET_ISOCH_DELAY"); + ret = dwc3_ep0_set_isoch_delay(dwc, ctrl); + break; + default: + dev_vdbg(dwc->dev, "Forwarding to gadget driver"); + ret = dwc3_ep0_delegate_req(dwc, ctrl); + break; + } + + return ret; +} + +static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct usb_ctrlrequest *ctrl = dwc->ctrl_req; + int ret = -EINVAL; + u32 len; + + if (!dwc->gadget_driver) + goto out; + + len = le16_to_cpu(ctrl->wLength); + if (!len) { + dwc->three_stage_setup = false; + dwc->ep0_expect_in = false; + dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS; + } else { + dwc->three_stage_setup = true; + dwc->ep0_expect_in = !!(ctrl->bRequestType & USB_DIR_IN); + dwc->ep0_next_event = DWC3_EP0_NRDY_DATA; + } + + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) + ret = dwc3_ep0_std_request(dwc, ctrl); + else + ret = dwc3_ep0_delegate_req(dwc, ctrl); + + if (ret == USB_GADGET_DELAYED_STATUS) + dwc->delayed_status = true; + +out: + if (ret < 0) + dwc3_ep0_stall_and_restart(dwc); +} + +static void dwc3_ep0_complete_data(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_request *r = NULL; + struct usb_request *ur; + struct dwc3_trb *trb; + struct dwc3_ep *ep0; + unsigned transfer_size = 0; + unsigned maxp; + void *buf; + u32 transferred = 0; + u32 status; + u32 length; + u8 epnum; + + epnum = event->endpoint_number; + ep0 = dwc->eps[0]; + + dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS; + + trb = dwc->ep0_trb; + + r = next_request(&ep0->request_list); + if (!r) + return; + + dwc3_flush_cache((int)trb, sizeof(*trb)); + + status = DWC3_TRB_SIZE_TRBSTS(trb->size); + if (status == DWC3_TRBSTS_SETUP_PENDING) { + dev_dbg(dwc->dev, "Setup Pending received"); + + if (r) + dwc3_gadget_giveback(ep0, r, -ECONNRESET); + + return; + } + + ur = &r->request; + buf = ur->buf; + + length = trb->size & DWC3_TRB_SIZE_MASK; + + maxp = ep0->endpoint.maxpacket; + + if (dwc->ep0_bounced) { + /* + * Handle the first TRB before handling the bounce buffer if + * the request length is greater than the bounce buffer size. + */ + if (ur->length > DWC3_EP0_BOUNCE_SIZE) { + transfer_size = (ur->length / maxp) * maxp; + transferred = transfer_size - length; + buf = (u8 *)buf + transferred; + ur->actual += transferred; + + trb++; + dwc3_flush_cache((int)trb, sizeof(*trb)); + length = trb->size & DWC3_TRB_SIZE_MASK; + + ep0->free_slot = 0; + } + + transfer_size = roundup((ur->length - transfer_size), + maxp); + transferred = min_t(u32, ur->length - transferred, + transfer_size - length); + dwc3_flush_cache((int)dwc->ep0_bounce, DWC3_EP0_BOUNCE_SIZE); + memcpy(buf, dwc->ep0_bounce, transferred); + } else { + transferred = ur->length - length; + } + + ur->actual += transferred; + + if ((epnum & 1) && ur->actual < ur->length) { + /* for some reason we did not get everything out */ + + dwc3_ep0_stall_and_restart(dwc); + } else { + dwc3_gadget_giveback(ep0, r, 0); + + if (IS_ALIGNED(ur->length, ep0->endpoint.maxpacket) && + ur->length && ur->zero) { + int ret; + + dwc->ep0_next_event = DWC3_EP0_COMPLETE; + + ret = dwc3_ep0_start_trans(dwc, epnum, + dwc->ctrl_req_addr, 0, + DWC3_TRBCTL_CONTROL_DATA, 0); + WARN_ON(ret < 0); + } + } +} + +static void dwc3_ep0_complete_status(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_request *r; + struct dwc3_ep *dep; + struct dwc3_trb *trb; + u32 status; + + dep = dwc->eps[0]; + trb = dwc->ep0_trb; + + if (!list_empty(&dep->request_list)) { + r = next_request(&dep->request_list); + + dwc3_gadget_giveback(dep, r, 0); + } + + if (dwc->test_mode) { + int ret; + + ret = dwc3_gadget_set_test_mode(dwc, dwc->test_mode_nr); + if (ret < 0) { + dev_dbg(dwc->dev, "Invalid Test #%d", + dwc->test_mode_nr); + dwc3_ep0_stall_and_restart(dwc); + return; + } + } + + status = DWC3_TRB_SIZE_TRBSTS(trb->size); + if (status == DWC3_TRBSTS_SETUP_PENDING) + dev_dbg(dwc->dev, "Setup Pending received"); + + dwc->ep0state = EP0_SETUP_PHASE; + dwc3_ep0_out_start(dwc); +} + +static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; + + dep->flags &= ~DWC3_EP_BUSY; + dep->resource_index = 0; + dwc->setup_packet_pending = false; + + switch (dwc->ep0state) { + case EP0_SETUP_PHASE: + dev_vdbg(dwc->dev, "Setup Phase"); + dwc3_ep0_inspect_setup(dwc, event); + break; + + case EP0_DATA_PHASE: + dev_vdbg(dwc->dev, "Data Phase"); + dwc3_ep0_complete_data(dwc, event); + break; + + case EP0_STATUS_PHASE: + dev_vdbg(dwc->dev, "Status Phase"); + dwc3_ep0_complete_status(dwc, event); + break; + default: + WARN(true, "UNKNOWN ep0state %d\n", dwc->ep0state); + } +} + +static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, + struct dwc3_ep *dep, struct dwc3_request *req) +{ + int ret; + + req->direction = !!dep->number; + + if (req->request.length == 0) { + ret = dwc3_ep0_start_trans(dwc, dep->number, + dwc->ctrl_req_addr, 0, + DWC3_TRBCTL_CONTROL_DATA, 0); + } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) && + (dep->number == 0)) { + u32 transfer_size = 0; + u32 maxpacket; + + ret = usb_gadget_map_request(&dwc->gadget, &req->request, + dep->number); + if (ret) { + dev_dbg(dwc->dev, "failed to map request\n"); + return; + } + + maxpacket = dep->endpoint.maxpacket; + if (req->request.length > DWC3_EP0_BOUNCE_SIZE) { + transfer_size = (req->request.length / maxpacket) * + maxpacket; + ret = dwc3_ep0_start_trans(dwc, dep->number, + req->request.dma, + transfer_size, + DWC3_TRBCTL_CONTROL_DATA, 1); + } + + transfer_size = roundup((req->request.length - transfer_size), + maxpacket); + + dwc->ep0_bounced = true; + + /* + * REVISIT in case request length is bigger than + * DWC3_EP0_BOUNCE_SIZE we will need two chained + * TRBs to handle the transfer. + */ + ret = dwc3_ep0_start_trans(dwc, dep->number, + dwc->ep0_bounce_addr, transfer_size, + DWC3_TRBCTL_CONTROL_DATA, 0); + } else { + ret = usb_gadget_map_request(&dwc->gadget, &req->request, + dep->number); + if (ret) { + dev_dbg(dwc->dev, "failed to map request\n"); + return; + } + + ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma, + req->request.length, + DWC3_TRBCTL_CONTROL_DATA, 0); + } + + WARN_ON(ret < 0); +} + +static int dwc3_ep0_start_control_status(struct dwc3_ep *dep) +{ + struct dwc3 *dwc = dep->dwc; + u32 type; + + type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3 + : DWC3_TRBCTL_CONTROL_STATUS2; + + return dwc3_ep0_start_trans(dwc, dep->number, + dwc->ctrl_req_addr, 0, type, 0); +} + +static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep) +{ + if (dwc->resize_fifos) { + dev_dbg(dwc->dev, "Resizing FIFOs"); + dwc3_gadget_resize_tx_fifos(dwc); + dwc->resize_fifos = 0; + } + + WARN_ON(dwc3_ep0_start_control_status(dep)); +} + +static void dwc3_ep0_do_control_status(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; + + __dwc3_ep0_do_control_status(dwc, dep); +} + +static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep) +{ + struct dwc3_gadget_ep_cmd_params params; + u32 cmd; + int ret; + + if (!dep->resource_index) + return; + + cmd = DWC3_DEPCMD_ENDTRANSFER; + cmd |= DWC3_DEPCMD_CMDIOC; + cmd |= DWC3_DEPCMD_PARAM(dep->resource_index); + memset(¶ms, 0, sizeof(params)); + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); + WARN_ON_ONCE(ret); + dep->resource_index = 0; +} + +static void dwc3_ep0_xfernotready(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + dwc->setup_packet_pending = true; + + switch (event->status) { + case DEPEVT_STATUS_CONTROL_DATA: + dev_vdbg(dwc->dev, "Control Data"); + + /* + * We already have a DATA transfer in the controller's cache, + * if we receive a XferNotReady(DATA) we will ignore it, unless + * it's for the wrong direction. + * + * In that case, we must issue END_TRANSFER command to the Data + * Phase we already have started and issue SetStall on the + * control endpoint. + */ + if (dwc->ep0_expect_in != event->endpoint_number) { + struct dwc3_ep *dep = dwc->eps[dwc->ep0_expect_in]; + + dev_vdbg(dwc->dev, "Wrong direction for Data phase"); + dwc3_ep0_end_control_data(dwc, dep); + dwc3_ep0_stall_and_restart(dwc); + return; + } + + break; + + case DEPEVT_STATUS_CONTROL_STATUS: + if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS) + return; + + dev_vdbg(dwc->dev, "Control Status"); + + dwc->ep0state = EP0_STATUS_PHASE; + + if (dwc->delayed_status) { + WARN_ON_ONCE(event->endpoint_number != 1); + dev_vdbg(dwc->dev, "Delayed Status"); + return; + } + + dwc3_ep0_do_control_status(dwc, event); + } +} + +void dwc3_ep0_interrupt(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + u8 epnum = event->endpoint_number; + + dev_dbg(dwc->dev, "%s while ep%d%s in state '%s'", + dwc3_ep_event_string(event->endpoint_event), + epnum >> 1, (epnum & 1) ? "in" : "out", + dwc3_ep0_state_string(dwc->ep0state)); + + switch (event->endpoint_event) { + case DWC3_DEPEVT_XFERCOMPLETE: + dwc3_ep0_xfer_complete(dwc, event); + break; + + case DWC3_DEPEVT_XFERNOTREADY: + dwc3_ep0_xfernotready(dwc, event); + break; + + case DWC3_DEPEVT_XFERINPROGRESS: + case DWC3_DEPEVT_RXTXFIFOEVT: + case DWC3_DEPEVT_STREAMEVT: + case DWC3_DEPEVT_EPCMDCMPLT: + break; + } +} diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c new file mode 100644 index 0000000000..f3d649a5ee --- /dev/null +++ b/drivers/usb/dwc3/gadget.c @@ -0,0 +1,2678 @@ +/** + * gadget.c - DesignWare USB3 DRD Controller Gadget Framework Link + * + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/gadget.c) and ported + * to uboot. + * + * commit 8e74475b0e : usb: dwc3: gadget: use udc-core's reset notifier + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <malloc.h> +#include <asm/dma-mapping.h> +#include <usb/lin_gadget_compat.h> +#include <linux/list.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <asm/arch/sys_proto.h> + +#include "core.h" +#include "gadget.h" +#include "io.h" + +#include "linux-compat.h" + +/** + * dwc3_gadget_set_test_mode - Enables USB2 Test Modes + * @dwc: pointer to our context structure + * @mode: the mode to set (J, K SE0 NAK, Force Enable) + * + * Caller should take care of locking. This function will + * return 0 on success or -EINVAL if wrong Test Selector + * is passed + */ +int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_TSTCTRL_MASK; + + switch (mode) { + case TEST_J: + case TEST_K: + case TEST_SE0_NAK: + case TEST_PACKET: + case TEST_FORCE_EN: + reg |= mode << 1; + break; + default: + return -EINVAL; + } + + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + return 0; +} + +/** + * dwc3_gadget_get_link_state - Gets current state of USB Link + * @dwc: pointer to our context structure + * + * Caller should take care of locking. This function will + * return the link state on success (>= 0) or -ETIMEDOUT. + */ +int dwc3_gadget_get_link_state(struct dwc3 *dwc) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + + return DWC3_DSTS_USBLNKST(reg); +} + +/** + * dwc3_gadget_set_link_state - Sets USB Link to a particular State + * @dwc: pointer to our context structure + * @state: the state to put link into + * + * Caller should take care of locking. This function will + * return 0 on success or -ETIMEDOUT. + */ +int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) +{ + int retries = 10000; + u32 reg; + + /* + * Wait until device controller is ready. Only applies to 1.94a and + * later RTL. + */ + if (dwc->revision >= DWC3_REVISION_194A) { + while (--retries) { + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + if (reg & DWC3_DSTS_DCNRD) + udelay(5); + else + break; + } + + if (retries <= 0) + return -ETIMEDOUT; + } + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; + + /* set requested state */ + reg |= DWC3_DCTL_ULSTCHNGREQ(state); + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + /* + * The following code is racy when called from dwc3_gadget_wakeup, + * and is not needed, at least on newer versions + */ + if (dwc->revision >= DWC3_REVISION_194A) + return 0; + + /* wait for a change in DSTS */ + retries = 10000; + while (--retries) { + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + + if (DWC3_DSTS_USBLNKST(reg) == state) + return 0; + + udelay(5); + } + + dev_vdbg(dwc->dev, "link state change request timed out\n"); + + return -ETIMEDOUT; +} + +/** + * dwc3_gadget_resize_tx_fifos - reallocate fifo spaces for current use-case + * @dwc: pointer to our context structure + * + * This function will a best effort FIFO allocation in order + * to improve FIFO usage and throughput, while still allowing + * us to enable as many endpoints as possible. + * + * Keep in mind that this operation will be highly dependent + * on the configured size for RAM1 - which contains TxFifo -, + * the amount of endpoints enabled on coreConsultant tool, and + * the width of the Master Bus. + * + * In the ideal world, we would always be able to satisfy the + * following equation: + * + * ((512 + 2 * MDWIDTH-Bytes) + (Number of IN Endpoints - 1) * \ + * (3 * (1024 + MDWIDTH-Bytes) + MDWIDTH-Bytes)) / MDWIDTH-Bytes + * + * Unfortunately, due to many variables that's not always the case. + */ +int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc) +{ + int last_fifo_depth = 0; + int fifo_size; + int mdwidth; + int num; + + if (!dwc->needs_fifo_resize) + return 0; + + mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0); + + /* MDWIDTH is represented in bits, we need it in bytes */ + mdwidth >>= 3; + + /* + * FIXME For now we will only allocate 1 wMaxPacketSize space + * for each enabled endpoint, later patches will come to + * improve this algorithm so that we better use the internal + * FIFO space + */ + for (num = 0; num < dwc->num_in_eps; num++) { + /* bit0 indicates direction; 1 means IN ep */ + struct dwc3_ep *dep = dwc->eps[(num << 1) | 1]; + int mult = 1; + int tmp; + + if (!(dep->flags & DWC3_EP_ENABLED)) + continue; + + if (usb_endpoint_xfer_bulk(dep->endpoint.desc) + || usb_endpoint_xfer_isoc(dep->endpoint.desc)) + mult = 3; + + /* + * REVISIT: the following assumes we will always have enough + * space available on the FIFO RAM for all possible use cases. + * Make sure that's true somehow and change FIFO allocation + * accordingly. + * + * If we have Bulk or Isochronous endpoints, we want + * them to be able to be very, very fast. So we're giving + * those endpoints a fifo_size which is enough for 3 full + * packets + */ + tmp = mult * (dep->endpoint.maxpacket + mdwidth); + tmp += mdwidth; + + fifo_size = DIV_ROUND_UP(tmp, mdwidth); + + fifo_size |= (last_fifo_depth << 16); + + dev_vdbg(dwc->dev, "%s: Fifo Addr %04x Size %d\n", + dep->name, last_fifo_depth, fifo_size & 0xffff); + + dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num), fifo_size); + + last_fifo_depth += (fifo_size & 0xffff); + } + + return 0; +} + +void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, + int status) +{ + struct dwc3 *dwc = dep->dwc; + + if (req->queued) { + dep->busy_slot++; + /* + * Skip LINK TRB. We can't use req->trb and check for + * DWC3_TRBCTL_LINK_TRB because it points the TRB we + * just completed (not the LINK TRB). + */ + if (((dep->busy_slot & DWC3_TRB_MASK) == + DWC3_TRB_NUM- 1) && + usb_endpoint_xfer_isoc(dep->endpoint.desc)) + dep->busy_slot++; + req->queued = false; + } + + list_del(&req->list); + req->trb = NULL; + dwc3_flush_cache((int)req->request.dma, req->request.length); + + if (req->request.status == -EINPROGRESS) + req->request.status = status; + + if (dwc->ep0_bounced && dep->number == 0) + dwc->ep0_bounced = false; + else + usb_gadget_unmap_request(&dwc->gadget, &req->request, + req->direction); + + dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n", + req, dep->name, req->request.actual, + req->request.length, status); + + spin_unlock(&dwc->lock); + usb_gadget_giveback_request(&dep->endpoint, &req->request); + spin_lock(&dwc->lock); +} + +int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) +{ + u32 timeout = 500; + u32 reg; + + dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param); + dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT); + + do { + reg = dwc3_readl(dwc->regs, DWC3_DGCMD); + if (!(reg & DWC3_DGCMD_CMDACT)) { + dev_vdbg(dwc->dev, "Command Complete --> %d\n", + DWC3_DGCMD_STATUS(reg)); + return 0; + } + + /* + * We can't sleep here, because it's also called from + * interrupt context. + */ + timeout--; + if (!timeout) + return -ETIMEDOUT; + udelay(1); + } while (1); +} + +int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, + unsigned cmd, struct dwc3_gadget_ep_cmd_params *params) +{ + u32 timeout = 500; + u32 reg; + + dwc3_writel(dwc->regs, DWC3_DEPCMDPAR0(ep), params->param0); + dwc3_writel(dwc->regs, DWC3_DEPCMDPAR1(ep), params->param1); + dwc3_writel(dwc->regs, DWC3_DEPCMDPAR2(ep), params->param2); + + dwc3_writel(dwc->regs, DWC3_DEPCMD(ep), cmd | DWC3_DEPCMD_CMDACT); + do { + reg = dwc3_readl(dwc->regs, DWC3_DEPCMD(ep)); + if (!(reg & DWC3_DEPCMD_CMDACT)) { + dev_vdbg(dwc->dev, "Command Complete --> %d\n", + DWC3_DEPCMD_STATUS(reg)); + return 0; + } + + /* + * We can't sleep here, because it is also called from + * interrupt context. + */ + timeout--; + if (!timeout) + return -ETIMEDOUT; + + udelay(1); + } while (1); +} + +static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep, + struct dwc3_trb *trb) +{ + u32 offset = (char *) trb - (char *) dep->trb_pool; + + return dep->trb_pool_dma + offset; +} + +static int dwc3_alloc_trb_pool(struct dwc3_ep *dep) +{ + if (dep->trb_pool) + return 0; + + if (dep->number == 0 || dep->number == 1) + return 0; + + dep->trb_pool = dma_alloc_coherent(sizeof(struct dwc3_trb) * + DWC3_TRB_NUM, + (unsigned long *)&dep->trb_pool_dma); + if (!dep->trb_pool) { + dev_err(dep->dwc->dev, "failed to allocate trb pool for %s\n", + dep->name); + return -ENOMEM; + } + + return 0; +} + +static void dwc3_free_trb_pool(struct dwc3_ep *dep) +{ + dma_free_coherent(dep->trb_pool); + + dep->trb_pool = NULL; + dep->trb_pool_dma = 0; +} + +static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep) +{ + struct dwc3_gadget_ep_cmd_params params; + u32 cmd; + + memset(¶ms, 0x00, sizeof(params)); + + if (dep->number != 1) { + cmd = DWC3_DEPCMD_DEPSTARTCFG; + /* XferRscIdx == 0 for ep0 and 2 for the remaining */ + if (dep->number > 1) { + if (dwc->start_config_issued) + return 0; + dwc->start_config_issued = true; + cmd |= DWC3_DEPCMD_PARAM(2); + } + + return dwc3_send_gadget_ep_cmd(dwc, 0, cmd, ¶ms); + } + + return 0; +} + +static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, + const struct usb_endpoint_descriptor *desc, + const struct usb_ss_ep_comp_descriptor *comp_desc, + bool ignore, bool restore) +{ + struct dwc3_gadget_ep_cmd_params params; + + memset(¶ms, 0x00, sizeof(params)); + + params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc)) + | DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc)); + + /* Burst size is only needed in SuperSpeed mode */ + if (dwc->gadget.speed == USB_SPEED_SUPER) { + u32 burst = dep->endpoint.maxburst - 1; + + params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst); + } + + if (ignore) + params.param0 |= DWC3_DEPCFG_IGN_SEQ_NUM; + + if (restore) { + params.param0 |= DWC3_DEPCFG_ACTION_RESTORE; + params.param2 |= dep->saved_state; + } + + params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN + | DWC3_DEPCFG_XFER_NOT_READY_EN; + + if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) { + params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE + | DWC3_DEPCFG_STREAM_EVENT_EN; + dep->stream_capable = true; + } + + if (!usb_endpoint_xfer_control(desc)) + params.param1 |= DWC3_DEPCFG_XFER_IN_PROGRESS_EN; + + /* + * We are doing 1:1 mapping for endpoints, meaning + * Physical Endpoints 2 maps to Logical Endpoint 2 and + * so on. We consider the direction bit as part of the physical + * endpoint number. So USB endpoint 0x81 is 0x03. + */ + params.param1 |= DWC3_DEPCFG_EP_NUMBER(dep->number); + + /* + * We must use the lower 16 TX FIFOs even though + * HW might have more + */ + if (dep->direction) + params.param0 |= DWC3_DEPCFG_FIFO_NUMBER(dep->number >> 1); + + if (desc->bInterval) { + params.param1 |= DWC3_DEPCFG_BINTERVAL_M1(desc->bInterval - 1); + dep->interval = 1 << (desc->bInterval - 1); + } + + return dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_SETEPCONFIG, ¶ms); +} + +static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep) +{ + struct dwc3_gadget_ep_cmd_params params; + + memset(¶ms, 0x00, sizeof(params)); + + params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1); + + return dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_SETTRANSFRESOURCE, ¶ms); +} + +/** + * __dwc3_gadget_ep_enable - Initializes a HW endpoint + * @dep: endpoint to be initialized + * @desc: USB Endpoint Descriptor + * + * Caller should take care of locking + */ +static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, + const struct usb_endpoint_descriptor *desc, + const struct usb_ss_ep_comp_descriptor *comp_desc, + bool ignore, bool restore) +{ + struct dwc3 *dwc = dep->dwc; + u32 reg; + int ret; + + dev_vdbg(dwc->dev, "Enabling %s\n", dep->name); + + if (!(dep->flags & DWC3_EP_ENABLED)) { + ret = dwc3_gadget_start_config(dwc, dep); + if (ret) + return ret; + } + + ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore, + restore); + if (ret) + return ret; + + if (!(dep->flags & DWC3_EP_ENABLED)) { + struct dwc3_trb *trb_st_hw; + struct dwc3_trb *trb_link; + + ret = dwc3_gadget_set_xfer_resource(dwc, dep); + if (ret) + return ret; + + dep->endpoint.desc = desc; + dep->comp_desc = comp_desc; + dep->type = usb_endpoint_type(desc); + dep->flags |= DWC3_EP_ENABLED; + + reg = dwc3_readl(dwc->regs, DWC3_DALEPENA); + reg |= DWC3_DALEPENA_EP(dep->number); + dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); + + if (!usb_endpoint_xfer_isoc(desc)) + return 0; + + /* Link TRB for ISOC. The HWO bit is never reset */ + trb_st_hw = &dep->trb_pool[0]; + + trb_link = &dep->trb_pool[DWC3_TRB_NUM - 1]; + memset(trb_link, 0, sizeof(*trb_link)); + + trb_link->bpl = lower_32_bits(dwc3_trb_dma_offset(dep, trb_st_hw)); + trb_link->bph = upper_32_bits(dwc3_trb_dma_offset(dep, trb_st_hw)); + trb_link->ctrl |= DWC3_TRBCTL_LINK_TRB; + trb_link->ctrl |= DWC3_TRB_CTRL_HWO; + } + + return 0; +} + +static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force); +static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep) +{ + struct dwc3_request *req; + + if (!list_empty(&dep->req_queued)) { + dwc3_stop_active_transfer(dwc, dep->number, true); + + /* - giveback all requests to gadget driver */ + while (!list_empty(&dep->req_queued)) { + req = next_request(&dep->req_queued); + + dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + } + } + + while (!list_empty(&dep->request_list)) { + req = next_request(&dep->request_list); + + dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + } +} + +/** + * __dwc3_gadget_ep_disable - Disables a HW endpoint + * @dep: the endpoint to disable + * + * This function also removes requests which are currently processed ny the + * hardware and those which are not yet scheduled. + * Caller should take care of locking. + */ +static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) +{ + struct dwc3 *dwc = dep->dwc; + u32 reg; + + dwc3_remove_requests(dwc, dep); + + /* make sure HW endpoint isn't stalled */ + if (dep->flags & DWC3_EP_STALL) + __dwc3_gadget_ep_set_halt(dep, 0, false); + + reg = dwc3_readl(dwc->regs, DWC3_DALEPENA); + reg &= ~DWC3_DALEPENA_EP(dep->number); + dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); + + dep->stream_capable = false; + dep->endpoint.desc = NULL; + dep->comp_desc = NULL; + dep->type = 0; + dep->flags = 0; + + return 0; +} + +/* -------------------------------------------------------------------------- */ + +static int dwc3_gadget_ep0_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + return -EINVAL; +} + +static int dwc3_gadget_ep0_disable(struct usb_ep *ep) +{ + return -EINVAL; +} + +/* -------------------------------------------------------------------------- */ + +static int dwc3_gadget_ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct dwc3_ep *dep; + unsigned long flags; + int ret; + + if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) { + pr_debug("dwc3: invalid parameters\n"); + return -EINVAL; + } + + if (!desc->wMaxPacketSize) { + pr_debug("dwc3: missing wMaxPacketSize\n"); + return -EINVAL; + } + + dep = to_dwc3_ep(ep); + + if (dep->flags & DWC3_EP_ENABLED) { + WARN(true, "%s is already enabled\n", + dep->name); + return 0; + } + + switch (usb_endpoint_type(desc)) { + case USB_ENDPOINT_XFER_CONTROL: + strlcat(dep->name, "-control", sizeof(dep->name)); + break; + case USB_ENDPOINT_XFER_ISOC: + strlcat(dep->name, "-isoc", sizeof(dep->name)); + break; + case USB_ENDPOINT_XFER_BULK: + strlcat(dep->name, "-bulk", sizeof(dep->name)); + break; + case USB_ENDPOINT_XFER_INT: + strlcat(dep->name, "-int", sizeof(dep->name)); + break; + default: + dev_err(dwc->dev, "invalid endpoint transfer type\n"); + } + + spin_lock_irqsave(&dwc->lock, flags); + ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false, false); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_ep_disable(struct usb_ep *ep) +{ + struct dwc3_ep *dep; + unsigned long flags; + int ret; + + if (!ep) { + pr_debug("dwc3: invalid parameters\n"); + return -EINVAL; + } + + dep = to_dwc3_ep(ep); + + if (!(dep->flags & DWC3_EP_ENABLED)) { + WARN(true, "%s is already disabled\n", + dep->name); + return 0; + } + + snprintf(dep->name, sizeof(dep->name), "ep%d%s", + dep->number >> 1, + (dep->number & 1) ? "in" : "out"); + + spin_lock_irqsave(&dwc->lock, flags); + ret = __dwc3_gadget_ep_disable(dep); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep, + gfp_t gfp_flags) +{ + struct dwc3_request *req; + struct dwc3_ep *dep = to_dwc3_ep(ep); + + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) + return NULL; + + req->epnum = dep->number; + req->dep = dep; + + return &req->request; +} + +static void dwc3_gadget_ep_free_request(struct usb_ep *ep, + struct usb_request *request) +{ + struct dwc3_request *req = to_dwc3_request(request); + + kfree(req); +} + +/** + * dwc3_prepare_one_trb - setup one TRB from one request + * @dep: endpoint for which this request is prepared + * @req: dwc3_request pointer + */ +static void dwc3_prepare_one_trb(struct dwc3_ep *dep, + struct dwc3_request *req, dma_addr_t dma, + unsigned length, unsigned last, unsigned chain, unsigned node) +{ + struct dwc3_trb *trb; + + dev_vdbg(dwc->dev, "%s: req %p dma %08llx length %d%s%s\n", + dep->name, req, (unsigned long long) dma, + length, last ? " last" : "", + chain ? " chain" : ""); + + + trb = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK]; + + if (!req->trb) { + dwc3_gadget_move_request_queued(req); + req->trb = trb; + req->trb_dma = dwc3_trb_dma_offset(dep, trb); + req->start_slot = dep->free_slot & DWC3_TRB_MASK; + } + + dep->free_slot++; + /* Skip the LINK-TRB on ISOC */ + if (((dep->free_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) && + usb_endpoint_xfer_isoc(dep->endpoint.desc)) + dep->free_slot++; + + trb->size = DWC3_TRB_SIZE_LENGTH(length); + trb->bpl = lower_32_bits(dma); + trb->bph = upper_32_bits(dma); + + switch (usb_endpoint_type(dep->endpoint.desc)) { + case USB_ENDPOINT_XFER_CONTROL: + trb->ctrl = DWC3_TRBCTL_CONTROL_SETUP; + break; + + case USB_ENDPOINT_XFER_ISOC: + if (!node) + trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST; + else + trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS; + break; + + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + trb->ctrl = DWC3_TRBCTL_NORMAL; + break; + default: + /* + * This is only possible with faulty memory because we + * checked it already :) + */ + BUG(); + } + + if (!req->request.no_interrupt && !chain) + trb->ctrl |= DWC3_TRB_CTRL_IOC; + + if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { + trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI; + trb->ctrl |= DWC3_TRB_CTRL_CSP; + } else if (last) { + trb->ctrl |= DWC3_TRB_CTRL_LST; + } + + if (chain) + trb->ctrl |= DWC3_TRB_CTRL_CHN; + + if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable) + trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(req->request.stream_id); + + trb->ctrl |= DWC3_TRB_CTRL_HWO; + + dwc3_flush_cache((int)dma, length); + dwc3_flush_cache((int)trb, sizeof(*trb)); +} + +/* + * dwc3_prepare_trbs - setup TRBs from requests + * @dep: endpoint for which requests are being prepared + * @starting: true if the endpoint is idle and no requests are queued. + * + * The function goes through the requests list and sets up TRBs for the + * transfers. The function returns once there are no more TRBs available or + * it runs out of requests. + */ +static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting) +{ + struct dwc3_request *req, *n; + u32 trbs_left; + u32 max; + + BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM); + + /* the first request must not be queued */ + trbs_left = (dep->busy_slot - dep->free_slot) & DWC3_TRB_MASK; + + /* Can't wrap around on a non-isoc EP since there's no link TRB */ + if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) { + max = DWC3_TRB_NUM - (dep->free_slot & DWC3_TRB_MASK); + if (trbs_left > max) + trbs_left = max; + } + + /* + * If busy & slot are equal than it is either full or empty. If we are + * starting to process requests then we are empty. Otherwise we are + * full and don't do anything + */ + if (!trbs_left) { + if (!starting) + return; + trbs_left = DWC3_TRB_NUM; + /* + * In case we start from scratch, we queue the ISOC requests + * starting from slot 1. This is done because we use ring + * buffer and have no LST bit to stop us. Instead, we place + * IOC bit every TRB_NUM/4. We try to avoid having an interrupt + * after the first request so we start at slot 1 and have + * 7 requests proceed before we hit the first IOC. + * Other transfer types don't use the ring buffer and are + * processed from the first TRB until the last one. Since we + * don't wrap around we have to start at the beginning. + */ + if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { + dep->busy_slot = 1; + dep->free_slot = 1; + } else { + dep->busy_slot = 0; + dep->free_slot = 0; + } + } + + /* The last TRB is a link TRB, not used for xfer */ + if ((trbs_left <= 1) && usb_endpoint_xfer_isoc(dep->endpoint.desc)) + return; + + list_for_each_entry_safe(req, n, &dep->request_list, list) { + unsigned length; + dma_addr_t dma; + + dma = req->request.dma; + length = req->request.length; + + dwc3_prepare_one_trb(dep, req, dma, length, + true, false, 0); + + break; + } +} + +static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, + int start_new) +{ + struct dwc3_gadget_ep_cmd_params params; + struct dwc3_request *req; + struct dwc3 *dwc = dep->dwc; + int ret; + u32 cmd; + + if (start_new && (dep->flags & DWC3_EP_BUSY)) { + dev_vdbg(dwc->dev, "%s: endpoint busy\n", dep->name); + return -EBUSY; + } + dep->flags &= ~DWC3_EP_PENDING_REQUEST; + + /* + * If we are getting here after a short-out-packet we don't enqueue any + * new requests as we try to set the IOC bit only on the last request. + */ + if (start_new) { + if (list_empty(&dep->req_queued)) + dwc3_prepare_trbs(dep, start_new); + + /* req points to the first request which will be sent */ + req = next_request(&dep->req_queued); + } else { + dwc3_prepare_trbs(dep, start_new); + + /* + * req points to the first request where HWO changed from 0 to 1 + */ + req = next_request(&dep->req_queued); + } + if (!req) { + dep->flags |= DWC3_EP_PENDING_REQUEST; + return 0; + } + + memset(¶ms, 0, sizeof(params)); + + if (start_new) { + params.param0 = upper_32_bits(req->trb_dma); + params.param1 = lower_32_bits(req->trb_dma); + cmd = DWC3_DEPCMD_STARTTRANSFER; + } else { + cmd = DWC3_DEPCMD_UPDATETRANSFER; + } + + cmd |= DWC3_DEPCMD_PARAM(cmd_param); + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); + if (ret < 0) { + dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n"); + + /* + * FIXME we need to iterate over the list of requests + * here and stop, unmap, free and del each of the linked + * requests instead of what we do now. + */ + usb_gadget_unmap_request(&dwc->gadget, &req->request, + req->direction); + list_del(&req->list); + return ret; + } + + dep->flags |= DWC3_EP_BUSY; + + if (start_new) { + dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc, + dep->number); + WARN_ON_ONCE(!dep->resource_index); + } + + return 0; +} + +static void __dwc3_gadget_start_isoc(struct dwc3 *dwc, + struct dwc3_ep *dep, u32 cur_uf) +{ + u32 uf; + + if (list_empty(&dep->request_list)) { + dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n", + dep->name); + dep->flags |= DWC3_EP_PENDING_REQUEST; + return; + } + + /* 4 micro frames in the future */ + uf = cur_uf + dep->interval * 4; + + __dwc3_gadget_kick_transfer(dep, uf, 1); +} + +static void dwc3_gadget_start_isoc(struct dwc3 *dwc, + struct dwc3_ep *dep, const struct dwc3_event_depevt *event) +{ + u32 cur_uf, mask; + + mask = ~(dep->interval - 1); + cur_uf = event->parameters & mask; + + __dwc3_gadget_start_isoc(dwc, dep, cur_uf); +} + +static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) +{ + struct dwc3 *dwc = dep->dwc; + int ret; + + req->request.actual = 0; + req->request.status = -EINPROGRESS; + req->direction = dep->direction; + req->epnum = dep->number; + + /* + * DWC3 hangs on OUT requests smaller than maxpacket size, + * so HACK the request length + */ + if (dep->direction == 0 && + req->request.length < dep->endpoint.maxpacket) + req->request.length = dep->endpoint.maxpacket; + + /* + * We only add to our list of requests now and + * start consuming the list once we get XferNotReady + * IRQ. + * + * That way, we avoid doing anything that we don't need + * to do now and defer it until the point we receive a + * particular token from the Host side. + * + * This will also avoid Host cancelling URBs due to too + * many NAKs. + */ + ret = usb_gadget_map_request(&dwc->gadget, &req->request, + dep->direction); + if (ret) + return ret; + + list_add_tail(&req->list, &dep->request_list); + + /* + * There are a few special cases: + * + * 1. XferNotReady with empty list of requests. We need to kick the + * transfer here in that situation, otherwise we will be NAKing + * forever. If we get XferNotReady before gadget driver has a + * chance to queue a request, we will ACK the IRQ but won't be + * able to receive the data until the next request is queued. + * The following code is handling exactly that. + * + */ + if (dep->flags & DWC3_EP_PENDING_REQUEST) { + /* + * If xfernotready is already elapsed and it is a case + * of isoc transfer, then issue END TRANSFER, so that + * you can receive xfernotready again and can have + * notion of current microframe. + */ + if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { + if (list_empty(&dep->req_queued)) { + dwc3_stop_active_transfer(dwc, dep->number, true); + dep->flags = DWC3_EP_ENABLED; + } + return 0; + } + + ret = __dwc3_gadget_kick_transfer(dep, 0, true); + if (ret && ret != -EBUSY) + dev_dbg(dwc->dev, "%s: failed to kick transfers\n", + dep->name); + return ret; + } + + /* + * 2. XferInProgress on Isoc EP with an active transfer. We need to + * kick the transfer here after queuing a request, otherwise the + * core may not see the modified TRB(s). + */ + if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && + (dep->flags & DWC3_EP_BUSY) && + !(dep->flags & DWC3_EP_MISSED_ISOC)) { + WARN_ON_ONCE(!dep->resource_index); + ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index, + false); + if (ret && ret != -EBUSY) + dev_dbg(dwc->dev, "%s: failed to kick transfers\n", + dep->name); + return ret; + } + + /* + * 4. Stream Capable Bulk Endpoints. We need to start the transfer + * right away, otherwise host will not know we have streams to be + * handled. + */ + if (dep->stream_capable) { + int ret; + + ret = __dwc3_gadget_kick_transfer(dep, 0, true); + if (ret && ret != -EBUSY) { + dev_dbg(dwc->dev, "%s: failed to kick transfers\n", + dep->name); + } + } + + return 0; +} + +static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, + gfp_t gfp_flags) +{ + struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_ep *dep = to_dwc3_ep(ep); + + unsigned long flags; + + int ret; + + spin_lock_irqsave(&dwc->lock, flags); + if (!dep->endpoint.desc) { + dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n", + request, ep->name); + ret = -ESHUTDOWN; + goto out; + } + + if (req->dep != dep) { + WARN(true, "request %p belongs to '%s'\n", + request, req->dep->name); + ret = -EINVAL; + goto out; + } + + dev_vdbg(dwc->dev, "queing request %p to %s length %d\n", + request, ep->name, request->length); + + ret = __dwc3_gadget_ep_queue(dep, req); + +out: + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, + struct usb_request *request) +{ + struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_request *r = NULL; + + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&dwc->lock, flags); + + list_for_each_entry(r, &dep->request_list, list) { + if (r == req) + break; + } + + if (r != req) { + list_for_each_entry(r, &dep->req_queued, list) { + if (r == req) + break; + } + if (r == req) { + /* wait until it is processed */ + dwc3_stop_active_transfer(dwc, dep->number, true); + goto out1; + } + dev_err(dwc->dev, "request %p was not queued to %s\n", + request, ep->name); + ret = -EINVAL; + goto out0; + } + +out1: + /* giveback the request */ + dwc3_gadget_giveback(dep, req, -ECONNRESET); + +out0: + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) +{ + struct dwc3_gadget_ep_cmd_params params; + struct dwc3 *dwc = dep->dwc; + int ret; + + if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { + dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name); + return -EINVAL; + } + + memset(¶ms, 0x00, sizeof(params)); + + if (value) { + if (!protocol && ((dep->direction && dep->flags & DWC3_EP_BUSY) || + (!list_empty(&dep->req_queued) || + !list_empty(&dep->request_list)))) { + dev_dbg(dwc->dev, "%s: pending request, cannot halt\n", + dep->name); + return -EAGAIN; + } + + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_SETSTALL, ¶ms); + if (ret) + dev_err(dwc->dev, "failed to set STALL on %s\n", + dep->name); + else + dep->flags |= DWC3_EP_STALL; + } else { + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_CLEARSTALL, ¶ms); + if (ret) + dev_err(dwc->dev, "failed to clear STALL on %s\n", + dep->name); + else + dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE); + } + + return ret; +} + +static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value) +{ + struct dwc3_ep *dep = to_dwc3_ep(ep); + + unsigned long flags; + + int ret; + + spin_lock_irqsave(&dwc->lock, flags); + ret = __dwc3_gadget_ep_set_halt(dep, value, false); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep) +{ + struct dwc3_ep *dep = to_dwc3_ep(ep); + unsigned long flags; + int ret; + + spin_lock_irqsave(&dwc->lock, flags); + dep->flags |= DWC3_EP_WEDGE; + + if (dep->number == 0 || dep->number == 1) + ret = __dwc3_gadget_ep0_set_halt(ep, 1); + else + ret = __dwc3_gadget_ep_set_halt(dep, 1, false); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +/* -------------------------------------------------------------------------- */ + +static struct usb_endpoint_descriptor dwc3_gadget_ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, +}; + +static const struct usb_ep_ops dwc3_gadget_ep0_ops = { + .enable = dwc3_gadget_ep0_enable, + .disable = dwc3_gadget_ep0_disable, + .alloc_request = dwc3_gadget_ep_alloc_request, + .free_request = dwc3_gadget_ep_free_request, + .queue = dwc3_gadget_ep0_queue, + .dequeue = dwc3_gadget_ep_dequeue, + .set_halt = dwc3_gadget_ep0_set_halt, + .set_wedge = dwc3_gadget_ep_set_wedge, +}; + +static const struct usb_ep_ops dwc3_gadget_ep_ops = { + .enable = dwc3_gadget_ep_enable, + .disable = dwc3_gadget_ep_disable, + .alloc_request = dwc3_gadget_ep_alloc_request, + .free_request = dwc3_gadget_ep_free_request, + .queue = dwc3_gadget_ep_queue, + .dequeue = dwc3_gadget_ep_dequeue, + .set_halt = dwc3_gadget_ep_set_halt, + .set_wedge = dwc3_gadget_ep_set_wedge, +}; + +/* -------------------------------------------------------------------------- */ + +static int dwc3_gadget_get_frame(struct usb_gadget *g) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + return DWC3_DSTS_SOFFN(reg); +} + +static int dwc3_gadget_wakeup(struct usb_gadget *g) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + + unsigned long timeout; + unsigned long flags; + + u32 reg; + + int ret = 0; + + u8 link_state; + u8 speed; + + spin_lock_irqsave(&dwc->lock, flags); + + /* + * According to the Databook Remote wakeup request should + * be issued only when the device is in early suspend state. + * + * We can check that via USB Link State bits in DSTS register. + */ + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + + speed = reg & DWC3_DSTS_CONNECTSPD; + if (speed == DWC3_DSTS_SUPERSPEED) { + dev_dbg(dwc->dev, "no wakeup on SuperSpeed\n"); + ret = -EINVAL; + goto out; + } + + link_state = DWC3_DSTS_USBLNKST(reg); + + switch (link_state) { + case DWC3_LINK_STATE_RX_DET: /* in HS, means Early Suspend */ + case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */ + break; + default: + dev_dbg(dwc->dev, "can't wakeup from link state %d\n", + link_state); + ret = -EINVAL; + goto out; + } + + ret = dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RECOV); + if (ret < 0) { + dev_err(dwc->dev, "failed to put link in Recovery\n"); + goto out; + } + + /* Recent versions do this automatically */ + if (dwc->revision < DWC3_REVISION_194A) { + /* write zeroes to Link Change Request */ + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + } + + /* poll until Link State changes to ON */ + timeout = 1000; + + while (timeout--) { + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + + /* in HS, means ON */ + if (DWC3_DSTS_USBLNKST(reg) == DWC3_LINK_STATE_U0) + break; + } + + if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) { + dev_err(dwc->dev, "failed to send remote wakeup\n"); + ret = -EINVAL; + } + +out: + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_set_selfpowered(struct usb_gadget *g, + int is_selfpowered) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + dwc->is_selfpowered = !!is_selfpowered; + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; +} + +static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) +{ + u32 reg; + u32 timeout = 500; + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (is_on) { + if (dwc->revision <= DWC3_REVISION_187A) { + reg &= ~DWC3_DCTL_TRGTULST_MASK; + reg |= DWC3_DCTL_TRGTULST_RX_DET; + } + + if (dwc->revision >= DWC3_REVISION_194A) + reg &= ~DWC3_DCTL_KEEP_CONNECT; + reg |= DWC3_DCTL_RUN_STOP; + + if (dwc->has_hibernation) + reg |= DWC3_DCTL_KEEP_CONNECT; + + dwc->pullups_connected = true; + } else { + reg &= ~DWC3_DCTL_RUN_STOP; + + if (dwc->has_hibernation && !suspend) + reg &= ~DWC3_DCTL_KEEP_CONNECT; + + dwc->pullups_connected = false; + } + + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + do { + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + if (is_on) { + if (!(reg & DWC3_DSTS_DEVCTRLHLT)) + break; + } else { + if (reg & DWC3_DSTS_DEVCTRLHLT) + break; + } + timeout--; + if (!timeout) + return -ETIMEDOUT; + udelay(1); + } while (1); + + dev_vdbg(dwc->dev, "gadget %s data soft-%s\n", + dwc->gadget_driver + ? dwc->gadget_driver->function : "no-function", + is_on ? "connect" : "disconnect"); + + return 0; +} + +static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + int ret; + + is_on = !!is_on; + + spin_lock_irqsave(&dwc->lock, flags); + ret = dwc3_gadget_run_stop(dwc, is_on, false); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static void dwc3_gadget_enable_irq(struct dwc3 *dwc) +{ + u32 reg; + + /* Enable all but Start and End of Frame IRQs */ + reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN | + DWC3_DEVTEN_EVNTOVERFLOWEN | + DWC3_DEVTEN_CMDCMPLTEN | + DWC3_DEVTEN_ERRTICERREN | + DWC3_DEVTEN_WKUPEVTEN | + DWC3_DEVTEN_ULSTCNGEN | + DWC3_DEVTEN_CONNECTDONEEN | + DWC3_DEVTEN_USBRSTEN | + DWC3_DEVTEN_DISCONNEVTEN); + + dwc3_writel(dwc->regs, DWC3_DEVTEN, reg); +} + +static void dwc3_gadget_disable_irq(struct dwc3 *dwc) +{ + /* mask all interrupts */ + dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); +} + +static int dwc3_gadget_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + struct dwc3_ep *dep; + unsigned long flags; + int ret = 0; + u32 reg; + + spin_lock_irqsave(&dwc->lock, flags); + + if (dwc->gadget_driver) { + dev_err(dwc->dev, "%s is already bound to %s\n", + dwc->gadget.name, + dwc->gadget_driver->function); + ret = -EBUSY; + goto err1; + } + + dwc->gadget_driver = driver; + + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~(DWC3_DCFG_SPEED_MASK); + + /** + * WORKAROUND: DWC3 revision < 2.20a have an issue + * which would cause metastability state on Run/Stop + * bit if we try to force the IP to USB2-only mode. + * + * Because of that, we cannot configure the IP to any + * speed other than the SuperSpeed + * + * Refers to: + * + * STAR#9000525659: Clock Domain Crossing on DCTL in + * USB 2.0 Mode + */ + if (dwc->revision < DWC3_REVISION_220A) { + reg |= DWC3_DCFG_SUPERSPEED; + } else { + switch (dwc->maximum_speed) { + case USB_SPEED_LOW: + reg |= DWC3_DSTS_LOWSPEED; + break; + case USB_SPEED_FULL: + reg |= DWC3_DSTS_FULLSPEED1; + break; + case USB_SPEED_HIGH: + reg |= DWC3_DSTS_HIGHSPEED; + break; + case USB_SPEED_SUPER: /* FALLTHROUGH */ + case USB_SPEED_UNKNOWN: /* FALTHROUGH */ + default: + reg |= DWC3_DSTS_SUPERSPEED; + } + } + dwc3_writel(dwc->regs, DWC3_DCFG, reg); + + dwc->start_config_issued = false; + + /* Start with SuperSpeed Default */ + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); + + dep = dwc->eps[0]; + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false, + false); + if (ret) { + dev_err(dwc->dev, "failed to enable %s\n", dep->name); + goto err2; + } + + dep = dwc->eps[1]; + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false, + false); + if (ret) { + dev_err(dwc->dev, "failed to enable %s\n", dep->name); + goto err3; + } + + /* begin to receive SETUP packets */ + dwc->ep0state = EP0_SETUP_PHASE; + dwc3_ep0_out_start(dwc); + + dwc3_gadget_enable_irq(dwc); + + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; + +err3: + __dwc3_gadget_ep_disable(dwc->eps[0]); + +err2: + dwc->gadget_driver = NULL; + +err1: + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_stop(struct usb_gadget *g) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + + dwc3_gadget_disable_irq(dwc); + __dwc3_gadget_ep_disable(dwc->eps[0]); + __dwc3_gadget_ep_disable(dwc->eps[1]); + + dwc->gadget_driver = NULL; + + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; +} + +static const struct usb_gadget_ops dwc3_gadget_ops = { + .get_frame = dwc3_gadget_get_frame, + .wakeup = dwc3_gadget_wakeup, + .set_selfpowered = dwc3_gadget_set_selfpowered, + .pullup = dwc3_gadget_pullup, + .udc_start = dwc3_gadget_start, + .udc_stop = dwc3_gadget_stop, +}; + +/* -------------------------------------------------------------------------- */ + +static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc, + u8 num, u32 direction) +{ + struct dwc3_ep *dep; + u8 i; + + for (i = 0; i < num; i++) { + u8 epnum = (i << 1) | (!!direction); + + dep = kzalloc(sizeof(*dep), GFP_KERNEL); + if (!dep) + return -ENOMEM; + + dep->dwc = dwc; + dep->number = epnum; + dep->direction = !!direction; + dwc->eps[epnum] = dep; + + snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1, + (epnum & 1) ? "in" : "out"); + + dep->endpoint.name = dep->name; + + dev_vdbg(dwc->dev, "initializing %s\n", dep->name); + + if (epnum == 0 || epnum == 1) { + usb_ep_set_maxpacket_limit(&dep->endpoint, 512); + dep->endpoint.maxburst = 1; + dep->endpoint.ops = &dwc3_gadget_ep0_ops; + if (!epnum) + dwc->gadget.ep0 = &dep->endpoint; + } else { + int ret; + + usb_ep_set_maxpacket_limit(&dep->endpoint, 512); + dep->endpoint.max_streams = 15; + dep->endpoint.ops = &dwc3_gadget_ep_ops; + list_add_tail(&dep->endpoint.ep_list, + &dwc->gadget.ep_list); + + ret = dwc3_alloc_trb_pool(dep); + if (ret) + return ret; + } + + INIT_LIST_HEAD(&dep->request_list); + INIT_LIST_HEAD(&dep->req_queued); + } + + return 0; +} + +static int dwc3_gadget_init_endpoints(struct dwc3 *dwc) +{ + int ret; + + INIT_LIST_HEAD(&dwc->gadget.ep_list); + + ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_out_eps, 0); + if (ret < 0) { + dev_vdbg(dwc->dev, "failed to allocate OUT endpoints\n"); + return ret; + } + + ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_in_eps, 1); + if (ret < 0) { + dev_vdbg(dwc->dev, "failed to allocate IN endpoints\n"); + return ret; + } + + return 0; +} + +static void dwc3_gadget_free_endpoints(struct dwc3 *dwc) +{ + struct dwc3_ep *dep; + u8 epnum; + + for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) { + dep = dwc->eps[epnum]; + if (!dep) + continue; + /* + * Physical endpoints 0 and 1 are special; they form the + * bi-directional USB endpoint 0. + * + * For those two physical endpoints, we don't allocate a TRB + * pool nor do we add them the endpoints list. Due to that, we + * shouldn't do these two operations otherwise we would end up + * with all sorts of bugs when removing dwc3.ko. + */ + if (epnum != 0 && epnum != 1) { + dwc3_free_trb_pool(dep); + list_del(&dep->endpoint.ep_list); + } + + kfree(dep); + } +} + +/* -------------------------------------------------------------------------- */ + +static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep, + struct dwc3_request *req, struct dwc3_trb *trb, + const struct dwc3_event_depevt *event, int status) +{ + unsigned int count; + unsigned int s_pkt = 0; + unsigned int trb_status; + + if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN) + /* + * We continue despite the error. There is not much we + * can do. If we don't clean it up we loop forever. If + * we skip the TRB then it gets overwritten after a + * while since we use them in a ring buffer. A BUG() + * would help. Lets hope that if this occurs, someone + * fixes the root cause instead of looking away :) + */ + dev_err(dwc->dev, "%s's TRB (%p) still owned by HW\n", + dep->name, trb); + count = trb->size & DWC3_TRB_SIZE_MASK; + + if (dep->direction) { + if (count) { + trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size); + if (trb_status == DWC3_TRBSTS_MISSED_ISOC) { + dev_dbg(dwc->dev, "incomplete IN transfer %s\n", + dep->name); + /* + * If missed isoc occurred and there is + * no request queued then issue END + * TRANSFER, so that core generates + * next xfernotready and we will issue + * a fresh START TRANSFER. + * If there are still queued request + * then wait, do not issue either END + * or UPDATE TRANSFER, just attach next + * request in request_list during + * giveback.If any future queued request + * is successfully transferred then we + * will issue UPDATE TRANSFER for all + * request in the request_list. + */ + dep->flags |= DWC3_EP_MISSED_ISOC; + } else { + dev_err(dwc->dev, "incomplete IN transfer %s\n", + dep->name); + status = -ECONNRESET; + } + } else { + dep->flags &= ~DWC3_EP_MISSED_ISOC; + } + } else { + if (count && (event->status & DEPEVT_STATUS_SHORT)) + s_pkt = 1; + } + + /* + * We assume here we will always receive the entire data block + * which we should receive. Meaning, if we program RX to + * receive 4K but we receive only 2K, we assume that's all we + * should receive and we simply bounce the request back to the + * gadget driver for further processing. + */ + req->request.actual += req->request.length - count; + if (s_pkt) + return 1; + if ((event->status & DEPEVT_STATUS_LST) && + (trb->ctrl & (DWC3_TRB_CTRL_LST | + DWC3_TRB_CTRL_HWO))) + return 1; + if ((event->status & DEPEVT_STATUS_IOC) && + (trb->ctrl & DWC3_TRB_CTRL_IOC)) + return 1; + return 0; +} + +static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, + const struct dwc3_event_depevt *event, int status) +{ + struct dwc3_request *req; + struct dwc3_trb *trb; + unsigned int slot; + + req = next_request(&dep->req_queued); + if (!req) { + WARN_ON_ONCE(1); + return 1; + } + + slot = req->start_slot; + if ((slot == DWC3_TRB_NUM - 1) && + usb_endpoint_xfer_isoc(dep->endpoint.desc)) + slot++; + slot %= DWC3_TRB_NUM; + trb = &dep->trb_pool[slot]; + + dwc3_flush_cache((int)trb, sizeof(*trb)); + __dwc3_cleanup_done_trbs(dwc, dep, req, trb, event, status); + dwc3_gadget_giveback(dep, req, status); + + if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && + list_empty(&dep->req_queued)) { + if (list_empty(&dep->request_list)) { + /* + * If there is no entry in request list then do + * not issue END TRANSFER now. Just set PENDING + * flag, so that END TRANSFER is issued when an + * entry is added into request list. + */ + dep->flags = DWC3_EP_PENDING_REQUEST; + } else { + dwc3_stop_active_transfer(dwc, dep->number, true); + dep->flags = DWC3_EP_ENABLED; + } + return 1; + } + + return 1; +} + +static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, + struct dwc3_ep *dep, const struct dwc3_event_depevt *event) +{ + unsigned status = 0; + int clean_busy; + + if (event->status & DEPEVT_STATUS_BUSERR) + status = -ECONNRESET; + + clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status); + if (clean_busy) + dep->flags &= ~DWC3_EP_BUSY; + + /* + * WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround. + * See dwc3_gadget_linksts_change_interrupt() for 1st half. + */ + if (dwc->revision < DWC3_REVISION_183A) { + u32 reg; + int i; + + for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) { + dep = dwc->eps[i]; + + if (!(dep->flags & DWC3_EP_ENABLED)) + continue; + + if (!list_empty(&dep->req_queued)) + return; + } + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg |= dwc->u1u2; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + dwc->u1u2 = 0; + } +} + +static void dwc3_endpoint_interrupt(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_ep *dep; + u8 epnum = event->endpoint_number; + + dep = dwc->eps[epnum]; + + if (!(dep->flags & DWC3_EP_ENABLED)) + return; + + if (epnum == 0 || epnum == 1) { + dwc3_ep0_interrupt(dwc, event); + return; + } + + switch (event->endpoint_event) { + case DWC3_DEPEVT_XFERCOMPLETE: + dep->resource_index = 0; + + if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { + dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n", + dep->name); + return; + } + + dwc3_endpoint_transfer_complete(dwc, dep, event); + break; + case DWC3_DEPEVT_XFERINPROGRESS: + dwc3_endpoint_transfer_complete(dwc, dep, event); + break; + case DWC3_DEPEVT_XFERNOTREADY: + if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { + dwc3_gadget_start_isoc(dwc, dep, event); + } else { + int ret; + + dev_vdbg(dwc->dev, "%s: reason %s\n", + dep->name, event->status & + DEPEVT_STATUS_TRANSFER_ACTIVE + ? "Transfer Active" + : "Transfer Not Active"); + + ret = __dwc3_gadget_kick_transfer(dep, 0, 1); + if (!ret || ret == -EBUSY) + return; + + dev_dbg(dwc->dev, "%s: failed to kick transfers\n", + dep->name); + } + + break; + case DWC3_DEPEVT_STREAMEVT: + if (!usb_endpoint_xfer_bulk(dep->endpoint.desc)) { + dev_err(dwc->dev, "Stream event for non-Bulk %s\n", + dep->name); + return; + } + + switch (event->status) { + case DEPEVT_STREAMEVT_FOUND: + dev_vdbg(dwc->dev, "Stream %d found and started\n", + event->parameters); + + break; + case DEPEVT_STREAMEVT_NOTFOUND: + /* FALLTHROUGH */ + default: + dev_dbg(dwc->dev, "Couldn't find suitable stream\n"); + } + break; + case DWC3_DEPEVT_RXTXFIFOEVT: + dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name); + break; + case DWC3_DEPEVT_EPCMDCMPLT: + dev_vdbg(dwc->dev, "Endpoint Command Complete\n"); + break; + } +} + +static void dwc3_disconnect_gadget(struct dwc3 *dwc) +{ + if (dwc->gadget_driver && dwc->gadget_driver->disconnect) { + spin_unlock(&dwc->lock); + dwc->gadget_driver->disconnect(&dwc->gadget); + spin_lock(&dwc->lock); + } +} + +static void dwc3_suspend_gadget(struct dwc3 *dwc) +{ + if (dwc->gadget_driver && dwc->gadget_driver->suspend) { + spin_unlock(&dwc->lock); + dwc->gadget_driver->suspend(&dwc->gadget); + spin_lock(&dwc->lock); + } +} + +static void dwc3_resume_gadget(struct dwc3 *dwc) +{ + if (dwc->gadget_driver && dwc->gadget_driver->resume) { + spin_unlock(&dwc->lock); + dwc->gadget_driver->resume(&dwc->gadget); + } +} + +static void dwc3_reset_gadget(struct dwc3 *dwc) +{ + if (!dwc->gadget_driver) + return; + + if (dwc->gadget.speed != USB_SPEED_UNKNOWN) { + spin_unlock(&dwc->lock); + usb_gadget_udc_reset(&dwc->gadget, dwc->gadget_driver); + spin_lock(&dwc->lock); + } +} + +static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force) +{ + struct dwc3_ep *dep; + struct dwc3_gadget_ep_cmd_params params; + u32 cmd; + int ret; + + dep = dwc->eps[epnum]; + + if (!dep->resource_index) + return; + + /* + * NOTICE: We are violating what the Databook says about the + * EndTransfer command. Ideally we would _always_ wait for the + * EndTransfer Command Completion IRQ, but that's causing too + * much trouble synchronizing between us and gadget driver. + * + * We have discussed this with the IP Provider and it was + * suggested to giveback all requests here, but give HW some + * extra time to synchronize with the interconnect. We're using + * an arbitraty 100us delay for that. + * + * Note also that a similar handling was tested by Synopsys + * (thanks a lot Paul) and nothing bad has come out of it. + * In short, what we're doing is: + * + * - Issue EndTransfer WITH CMDIOC bit set + * - Wait 100us + */ + + cmd = DWC3_DEPCMD_ENDTRANSFER; + cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0; + cmd |= DWC3_DEPCMD_CMDIOC; + cmd |= DWC3_DEPCMD_PARAM(dep->resource_index); + memset(¶ms, 0, sizeof(params)); + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); + WARN_ON_ONCE(ret); + dep->resource_index = 0; + dep->flags &= ~DWC3_EP_BUSY; + udelay(100); +} + +static void dwc3_stop_active_transfers(struct dwc3 *dwc) +{ + u32 epnum; + + for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) { + struct dwc3_ep *dep; + + dep = dwc->eps[epnum]; + if (!dep) + continue; + + if (!(dep->flags & DWC3_EP_ENABLED)) + continue; + + dwc3_remove_requests(dwc, dep); + } +} + +static void dwc3_clear_stall_all_ep(struct dwc3 *dwc) +{ + u32 epnum; + + for (epnum = 1; epnum < DWC3_ENDPOINTS_NUM; epnum++) { + struct dwc3_ep *dep; + struct dwc3_gadget_ep_cmd_params params; + int ret; + + dep = dwc->eps[epnum]; + if (!dep) + continue; + + if (!(dep->flags & DWC3_EP_STALL)) + continue; + + dep->flags &= ~DWC3_EP_STALL; + + memset(¶ms, 0, sizeof(params)); + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_CLEARSTALL, ¶ms); + WARN_ON_ONCE(ret); + } +} + +static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) +{ + int reg; + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_INITU1ENA; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + reg &= ~DWC3_DCTL_INITU2ENA; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + dwc3_disconnect_gadget(dwc); + dwc->start_config_issued = false; + + dwc->gadget.speed = USB_SPEED_UNKNOWN; + dwc->setup_packet_pending = false; + usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED); +} + +static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) +{ + u32 reg; + + /* + * WORKAROUND: DWC3 revisions <1.88a have an issue which + * would cause a missing Disconnect Event if there's a + * pending Setup Packet in the FIFO. + * + * There's no suggested workaround on the official Bug + * report, which states that "unless the driver/application + * is doing any special handling of a disconnect event, + * there is no functional issue". + * + * Unfortunately, it turns out that we _do_ some special + * handling of a disconnect event, namely complete all + * pending transfers, notify gadget driver of the + * disconnection, and so on. + * + * Our suggested workaround is to follow the Disconnect + * Event steps here, instead, based on a setup_packet_pending + * flag. Such flag gets set whenever we have a XferNotReady + * event on EP0 and gets cleared on XferComplete for the + * same endpoint. + * + * Refers to: + * + * STAR#9000466709: RTL: Device : Disconnect event not + * generated if setup packet pending in FIFO + */ + if (dwc->revision < DWC3_REVISION_188A) { + if (dwc->setup_packet_pending) + dwc3_gadget_disconnect_interrupt(dwc); + } + + dwc3_reset_gadget(dwc); + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_TSTCTRL_MASK; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc->test_mode = false; + + dwc3_stop_active_transfers(dwc); + dwc3_clear_stall_all_ep(dwc); + dwc->start_config_issued = false; + + /* Reset device address to zero */ + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~(DWC3_DCFG_DEVADDR_MASK); + dwc3_writel(dwc->regs, DWC3_DCFG, reg); +} + +static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed) +{ + u32 reg; + u32 usb30_clock = DWC3_GCTL_CLK_BUS; + + /* + * We change the clock only at SS but I dunno why I would want to do + * this. Maybe it becomes part of the power saving plan. + */ + + if (speed != DWC3_DSTS_SUPERSPEED) + return; + + /* + * RAMClkSel is reset to 0 after USB reset, so it must be reprogrammed + * each time on Connect Done. + */ + if (!usb30_clock) + return; + + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg |= DWC3_GCTL_RAMCLKSEL(usb30_clock); + dwc3_writel(dwc->regs, DWC3_GCTL, reg); +} + +static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) +{ + struct dwc3_ep *dep; + int ret; + u32 reg; + u8 speed; + + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + speed = reg & DWC3_DSTS_CONNECTSPD; + dwc->speed = speed; + + dwc3_update_ram_clk_sel(dwc, speed); + + switch (speed) { + case DWC3_DCFG_SUPERSPEED: + /* + * WORKAROUND: DWC3 revisions <1.90a have an issue which + * would cause a missing USB3 Reset event. + * + * In such situations, we should force a USB3 Reset + * event by calling our dwc3_gadget_reset_interrupt() + * routine. + * + * Refers to: + * + * STAR#9000483510: RTL: SS : USB3 reset event may + * not be generated always when the link enters poll + */ + if (dwc->revision < DWC3_REVISION_190A) + dwc3_gadget_reset_interrupt(dwc); + + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); + dwc->gadget.ep0->maxpacket = 512; + dwc->gadget.speed = USB_SPEED_SUPER; + break; + case DWC3_DCFG_HIGHSPEED: + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); + dwc->gadget.ep0->maxpacket = 64; + dwc->gadget.speed = USB_SPEED_HIGH; + break; + case DWC3_DCFG_FULLSPEED2: + case DWC3_DCFG_FULLSPEED1: + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); + dwc->gadget.ep0->maxpacket = 64; + dwc->gadget.speed = USB_SPEED_FULL; + break; + case DWC3_DCFG_LOWSPEED: + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8); + dwc->gadget.ep0->maxpacket = 8; + dwc->gadget.speed = USB_SPEED_LOW; + break; + } + + /* Enable USB2 LPM Capability */ + + if ((dwc->revision > DWC3_REVISION_194A) + && (speed != DWC3_DCFG_SUPERSPEED)) { + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg |= DWC3_DCFG_LPM_CAP; + dwc3_writel(dwc->regs, DWC3_DCFG, reg); + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN); + + reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold); + + /* + * When dwc3 revisions >= 2.40a, LPM Erratum is enabled and + * DCFG.LPMCap is set, core responses with an ACK and the + * BESL value in the LPM token is less than or equal to LPM + * NYET threshold. + */ + if (dwc->revision < DWC3_REVISION_240A && dwc->has_lpm_erratum) + WARN(true, "LPM Erratum not available on dwc3 revisisions < 2.40a\n"); + + if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A) + reg |= DWC3_DCTL_LPM_ERRATA(dwc->lpm_nyet_threshold); + + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + } else { + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_HIRD_THRES_MASK; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + } + + dep = dwc->eps[0]; + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true, + false); + if (ret) { + dev_err(dwc->dev, "failed to enable %s\n", dep->name); + return; + } + + dep = dwc->eps[1]; + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true, + false); + if (ret) { + dev_err(dwc->dev, "failed to enable %s\n", dep->name); + return; + } + + /* + * Configure PHY via GUSB3PIPECTLn if required. + * + * Update GTXFIFOSIZn + * + * In both cases reset values should be sufficient. + */ +} + +static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc) +{ + /* + * TODO take core out of low power mode when that's + * implemented. + */ + + dwc->gadget_driver->resume(&dwc->gadget); +} + +static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, + unsigned int evtinfo) +{ + enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK; + unsigned int pwropt; + + /* + * WORKAROUND: DWC3 < 2.50a have an issue when configured without + * Hibernation mode enabled which would show up when device detects + * host-initiated U3 exit. + * + * In that case, device will generate a Link State Change Interrupt + * from U3 to RESUME which is only necessary if Hibernation is + * configured in. + * + * There are no functional changes due to such spurious event and we + * just need to ignore it. + * + * Refers to: + * + * STAR#9000570034 RTL: SS Resume event generated in non-Hibernation + * operational mode + */ + pwropt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1); + if ((dwc->revision < DWC3_REVISION_250A) && + (pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) { + if ((dwc->link_state == DWC3_LINK_STATE_U3) && + (next == DWC3_LINK_STATE_RESUME)) { + dev_vdbg(dwc->dev, "ignoring transition U3 -> Resume\n"); + return; + } + } + + /* + * WORKAROUND: DWC3 Revisions <1.83a have an issue which, depending + * on the link partner, the USB session might do multiple entry/exit + * of low power states before a transfer takes place. + * + * Due to this problem, we might experience lower throughput. The + * suggested workaround is to disable DCTL[12:9] bits if we're + * transitioning from U1/U2 to U0 and enable those bits again + * after a transfer completes and there are no pending transfers + * on any of the enabled endpoints. + * + * This is the first half of that workaround. + * + * Refers to: + * + * STAR#9000446952: RTL: Device SS : if U1/U2 ->U0 takes >128us + * core send LGO_Ux entering U0 + */ + if (dwc->revision < DWC3_REVISION_183A) { + if (next == DWC3_LINK_STATE_U0) { + u32 u1u2; + u32 reg; + + switch (dwc->link_state) { + case DWC3_LINK_STATE_U1: + case DWC3_LINK_STATE_U2: + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + u1u2 = reg & (DWC3_DCTL_INITU2ENA + | DWC3_DCTL_ACCEPTU2ENA + | DWC3_DCTL_INITU1ENA + | DWC3_DCTL_ACCEPTU1ENA); + + if (!dwc->u1u2) + dwc->u1u2 = reg & u1u2; + + reg &= ~u1u2; + + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + break; + default: + /* do nothing */ + break; + } + } + } + + switch (next) { + case DWC3_LINK_STATE_U1: + if (dwc->speed == USB_SPEED_SUPER) + dwc3_suspend_gadget(dwc); + break; + case DWC3_LINK_STATE_U2: + case DWC3_LINK_STATE_U3: + dwc3_suspend_gadget(dwc); + break; + case DWC3_LINK_STATE_RESUME: + dwc3_resume_gadget(dwc); + break; + default: + /* do nothing */ + break; + } + + dwc->link_state = next; +} + +static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc, + unsigned int evtinfo) +{ + unsigned int is_ss = evtinfo & (1UL << 4); + + /** + * WORKAROUND: DWC3 revison 2.20a with hibernation support + * have a known issue which can cause USB CV TD.9.23 to fail + * randomly. + * + * Because of this issue, core could generate bogus hibernation + * events which SW needs to ignore. + * + * Refers to: + * + * STAR#9000546576: Device Mode Hibernation: Issue in USB 2.0 + * Device Fallback from SuperSpeed + */ + if (is_ss ^ (dwc->speed == USB_SPEED_SUPER)) + return; + + /* enter hibernation here */ +} + +static void dwc3_gadget_interrupt(struct dwc3 *dwc, + const struct dwc3_event_devt *event) +{ + switch (event->type) { + case DWC3_DEVICE_EVENT_DISCONNECT: + dwc3_gadget_disconnect_interrupt(dwc); + break; + case DWC3_DEVICE_EVENT_RESET: + dwc3_gadget_reset_interrupt(dwc); + break; + case DWC3_DEVICE_EVENT_CONNECT_DONE: + dwc3_gadget_conndone_interrupt(dwc); + break; + case DWC3_DEVICE_EVENT_WAKEUP: + dwc3_gadget_wakeup_interrupt(dwc); + break; + case DWC3_DEVICE_EVENT_HIBER_REQ: + if (!dwc->has_hibernation) { + WARN(1 ,"unexpected hibernation event\n"); + break; + } + dwc3_gadget_hibernation_interrupt(dwc, event->event_info); + break; + case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: + dwc3_gadget_linksts_change_interrupt(dwc, event->event_info); + break; + case DWC3_DEVICE_EVENT_EOPF: + dev_vdbg(dwc->dev, "End of Periodic Frame\n"); + break; + case DWC3_DEVICE_EVENT_SOF: + dev_vdbg(dwc->dev, "Start of Periodic Frame\n"); + break; + case DWC3_DEVICE_EVENT_ERRATIC_ERROR: + dev_vdbg(dwc->dev, "Erratic Error\n"); + break; + case DWC3_DEVICE_EVENT_CMD_CMPL: + dev_vdbg(dwc->dev, "Command Complete\n"); + break; + case DWC3_DEVICE_EVENT_OVERFLOW: + dev_vdbg(dwc->dev, "Overflow\n"); + break; + default: + dev_dbg(dwc->dev, "UNKNOWN IRQ %d\n", event->type); + } +} + +static void dwc3_process_event_entry(struct dwc3 *dwc, + const union dwc3_event *event) +{ + /* Endpoint IRQ, handle it and return early */ + if (event->type.is_devspec == 0) { + /* depevt */ + return dwc3_endpoint_interrupt(dwc, &event->depevt); + } + + switch (event->type.type) { + case DWC3_EVENT_TYPE_DEV: + dwc3_gadget_interrupt(dwc, &event->devt); + break; + /* REVISIT what to do with Carkit and I2C events ? */ + default: + dev_err(dwc->dev, "UNKNOWN IRQ type %d\n", event->raw); + } +} + +static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf) +{ + struct dwc3_event_buffer *evt; + irqreturn_t ret = IRQ_NONE; + int left; + u32 reg; + + evt = dwc->ev_buffs[buf]; + left = evt->count; + + if (!(evt->flags & DWC3_EVENT_PENDING)) + return IRQ_NONE; + + while (left > 0) { + union dwc3_event event; + + event.raw = *(u32 *) (evt->buf + evt->lpos); + + dwc3_process_event_entry(dwc, &event); + + /* + * FIXME we wrap around correctly to the next entry as + * almost all entries are 4 bytes in size. There is one + * entry which has 12 bytes which is a regular entry + * followed by 8 bytes data. ATM I don't know how + * things are organized if we get next to the a + * boundary so I worry about that once we try to handle + * that. + */ + evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE; + left -= 4; + + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), 4); + } + + evt->count = 0; + evt->flags &= ~DWC3_EVENT_PENDING; + ret = IRQ_HANDLED; + + /* Unmask interrupt */ + reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(buf)); + reg &= ~DWC3_GEVNTSIZ_INTMASK; + dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg); + + return ret; +} + +static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc) +{ + struct dwc3 *dwc = _dwc; + unsigned long flags; + irqreturn_t ret = IRQ_NONE; + int i; + + spin_lock_irqsave(&dwc->lock, flags); + + for (i = 0; i < dwc->num_event_buffers; i++) + ret |= dwc3_process_event_buf(dwc, i); + + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static irqreturn_t dwc3_check_event_buf(struct dwc3 *dwc, u32 buf) +{ + struct dwc3_event_buffer *evt; + u32 count; + u32 reg; + + evt = dwc->ev_buffs[buf]; + + count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(buf)); + count &= DWC3_GEVNTCOUNT_MASK; + if (!count) + return IRQ_NONE; + + evt->count = count; + evt->flags |= DWC3_EVENT_PENDING; + + /* Mask interrupt */ + reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(buf)); + reg |= DWC3_GEVNTSIZ_INTMASK; + dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t dwc3_interrupt(int irq, void *_dwc) +{ + struct dwc3 *dwc = _dwc; + int i; + irqreturn_t ret = IRQ_NONE; + + spin_lock(&dwc->lock); + + for (i = 0; i < dwc->num_event_buffers; i++) { + irqreturn_t status; + + status = dwc3_check_event_buf(dwc, i); + if (status == IRQ_WAKE_THREAD) + ret = status; + } + + spin_unlock(&dwc->lock); + + return ret; +} + +/** + * dwc3_gadget_init - Initializes gadget related registers + * @dwc: pointer to our controller context structure + * + * Returns 0 on success otherwise negative errno. + */ +int dwc3_gadget_init(struct dwc3 *dwc) +{ + int ret; + + dwc->ctrl_req = dma_alloc_coherent(sizeof(*dwc->ctrl_req), + (unsigned long *)&dwc->ctrl_req_addr); + if (!dwc->ctrl_req) { + dev_err(dwc->dev, "failed to allocate ctrl request\n"); + ret = -ENOMEM; + goto err0; + } + + dwc->ep0_trb = dma_alloc_coherent(sizeof(*dwc->ep0_trb) * 2, + (unsigned long *)&dwc->ep0_trb_addr); + if (!dwc->ep0_trb) { + dev_err(dwc->dev, "failed to allocate ep0 trb\n"); + ret = -ENOMEM; + goto err1; + } + + dwc->setup_buf = memalign(CONFIG_SYS_CACHELINE_SIZE, + DWC3_EP0_BOUNCE_SIZE); + if (!dwc->setup_buf) { + ret = -ENOMEM; + goto err2; + } + + dwc->ep0_bounce = dma_alloc_coherent(DWC3_EP0_BOUNCE_SIZE, + (unsigned long *)&dwc->ep0_bounce_addr); + if (!dwc->ep0_bounce) { + dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n"); + ret = -ENOMEM; + goto err3; + } + + dwc->gadget.ops = &dwc3_gadget_ops; + dwc->gadget.max_speed = USB_SPEED_SUPER; + dwc->gadget.speed = USB_SPEED_UNKNOWN; + dwc->gadget.name = "dwc3-gadget"; + + /* + * Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize + * on ep out. + */ + dwc->gadget.quirk_ep_out_aligned_size = true; + + /* + * REVISIT: Here we should clear all pending IRQs to be + * sure we're starting from a well known location. + */ + + ret = dwc3_gadget_init_endpoints(dwc); + if (ret) + goto err4; + + ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget); + if (ret) { + dev_err(dwc->dev, "failed to register udc\n"); + goto err4; + } + + return 0; + +err4: + dwc3_gadget_free_endpoints(dwc); + dma_free_coherent(dwc->ep0_bounce); + +err3: + kfree(dwc->setup_buf); + +err2: + dma_free_coherent(dwc->ep0_trb); + +err1: + dma_free_coherent(dwc->ctrl_req); + +err0: + return ret; +} + +/* -------------------------------------------------------------------------- */ + +void dwc3_gadget_exit(struct dwc3 *dwc) +{ + usb_del_gadget_udc(&dwc->gadget); + + dwc3_gadget_free_endpoints(dwc); + + dma_free_coherent(dwc->ep0_bounce); + + kfree(dwc->setup_buf); + + dma_free_coherent(dwc->ep0_trb); + + dma_free_coherent(dwc->ctrl_req); +} + +/** + * dwc3_gadget_uboot_handle_interrupt - handle dwc3 gadget interrupt + * @dwc: struct dwce * + * + * Handles ep0 and gadget interrupt + * + * Should be called from dwc3 core. + */ +void dwc3_gadget_uboot_handle_interrupt(struct dwc3 *dwc) +{ + int ret = dwc3_interrupt(0, dwc); + + if (ret == IRQ_WAKE_THREAD) { + int i; + struct dwc3_event_buffer *evt; + + for (i = 0; i < dwc->num_event_buffers; i++) { + evt = dwc->ev_buffs[i]; + dwc3_flush_cache((int)evt->buf, evt->length); + } + + dwc3_thread_interrupt(0, dwc); + } +} diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h new file mode 100644 index 0000000000..c7db219599 --- /dev/null +++ b/drivers/usb/dwc3/gadget.h @@ -0,0 +1,108 @@ +/** + * gadget.h - DesignWare USB3 DRD Gadget Header + * + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/gadget.h) and ported + * to uboot. + * + * commit 7a60855972 : usb: dwc3: gadget: fix set_halt() bug with pending + transfers + * + * SPDX-License-Identifier: GPL-2.0 + * + */ + +#ifndef __DRIVERS_USB_DWC3_GADGET_H +#define __DRIVERS_USB_DWC3_GADGET_H + +#include <linux/list.h> +#include <linux/usb/gadget.h> +#include "io.h" + +struct dwc3; +#define to_dwc3_ep(ep) (container_of(ep, struct dwc3_ep, endpoint)) +#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget)) + +/* DEPCFG parameter 1 */ +#define DWC3_DEPCFG_INT_NUM(n) ((n) << 0) +#define DWC3_DEPCFG_XFER_COMPLETE_EN (1 << 8) +#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN (1 << 9) +#define DWC3_DEPCFG_XFER_NOT_READY_EN (1 << 10) +#define DWC3_DEPCFG_FIFO_ERROR_EN (1 << 11) +#define DWC3_DEPCFG_STREAM_EVENT_EN (1 << 13) +#define DWC3_DEPCFG_BINTERVAL_M1(n) ((n) << 16) +#define DWC3_DEPCFG_STREAM_CAPABLE (1 << 24) +#define DWC3_DEPCFG_EP_NUMBER(n) ((n) << 25) +#define DWC3_DEPCFG_BULK_BASED (1 << 30) +#define DWC3_DEPCFG_FIFO_BASED (1 << 31) + +/* DEPCFG parameter 0 */ +#define DWC3_DEPCFG_EP_TYPE(n) ((n) << 1) +#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) ((n) << 3) +#define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17) +#define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22) +#define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26) +/* This applies for core versions earlier than 1.94a */ +#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31) +/* These apply for core versions 1.94a and later */ +#define DWC3_DEPCFG_ACTION_INIT (0 << 30) +#define DWC3_DEPCFG_ACTION_RESTORE (1 << 30) +#define DWC3_DEPCFG_ACTION_MODIFY (2 << 30) + +/* DEPXFERCFG parameter 0 */ +#define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff) + +/* -------------------------------------------------------------------------- */ + +#define to_dwc3_request(r) (container_of(r, struct dwc3_request, request)) + +static inline struct dwc3_request *next_request(struct list_head *list) +{ + if (list_empty(list)) + return NULL; + + return list_first_entry(list, struct dwc3_request, list); +} + +static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req) +{ + struct dwc3_ep *dep = req->dep; + + req->queued = true; + list_move_tail(&req->list, &dep->req_queued); +} + +void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, + int status); + +void dwc3_ep0_interrupt(struct dwc3 *dwc, + const struct dwc3_event_depevt *event); +void dwc3_ep0_out_start(struct dwc3 *dwc); +int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); +int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); +int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, + gfp_t gfp_flags); +int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol); +void dwc3_gadget_uboot_handle_interrupt(struct dwc3 *dwc); + +/** + * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW + * @dwc: DesignWare USB3 Pointer + * @number: DWC endpoint number + * + * Caller should take care of locking + */ +static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3 *dwc, u8 number) +{ + u32 res_id; + + res_id = dwc3_readl(dwc->regs, DWC3_DEPCMD(number)); + + return DWC3_DEPCMD_GET_RSC_IDX(res_id); +} + +#endif /* __DRIVERS_USB_DWC3_GADGET_H */ diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h new file mode 100644 index 0000000000..5042a24193 --- /dev/null +++ b/drivers/usb/dwc3/io.h @@ -0,0 +1,55 @@ +/** + * io.h - DesignWare USB3 DRD IO Header + * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/io.h) and ported + * to uboot. + * + * commit 2c4cbe6e5a : usb: dwc3: add tracepoints to aid debugging + * + * SPDX-License-Identifier: GPL-2.0 + * + */ + +#ifndef __DRIVERS_USB_DWC3_IO_H +#define __DRIVERS_USB_DWC3_IO_H + +#include <asm/io.h> + +#define CACHELINE_SIZE CONFIG_SYS_CACHELINE_SIZE +static inline u32 dwc3_readl(void __iomem *base, u32 offset) +{ + u32 offs = offset - DWC3_GLOBALS_REGS_START; + u32 value; + + /* + * We requested the mem region starting from the Globals address + * space, see dwc3_probe in core.c. + * However, the offsets are given starting from xHCI address space. + */ + value = readl(base + offs); + + return value; +} + +static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value) +{ + u32 offs = offset - DWC3_GLOBALS_REGS_START; + + /* + * We requested the mem region starting from the Globals address + * space, see dwc3_probe in core.c. + * However, the offsets are given starting from xHCI address space. + */ + writel(value, base + offs); +} + +static inline void dwc3_flush_cache(int addr, int length) +{ + flush_dcache_range(addr, addr + ROUND(length, CACHELINE_SIZE)); +} +#endif /* __DRIVERS_USB_DWC3_IO_H */ diff --git a/drivers/usb/dwc3/linux-compat.h b/drivers/usb/dwc3/linux-compat.h new file mode 100644 index 0000000000..b36f68fec8 --- /dev/null +++ b/drivers/usb/dwc3/linux-compat.h @@ -0,0 +1,38 @@ +/** + * linux-compat.h - DesignWare USB3 Linux Compatibiltiy Adapter Header + * + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Kishon Vijay Abraham I <kishon@ti.com> + * + * SPDX-License-Identifier: GPL-2.0 + * + */ + +#ifndef __DWC3_LINUX_COMPAT__ +#define __DWC3_LINUX_COMPAT__ + +#define pr_debug(format) debug(format) +#define WARN(val, format, arg...) debug(format, ##arg) +#define dev_WARN(dev, format, arg...) debug(format, ##arg) +#define WARN_ON_ONCE(val) debug("Error %d\n", val) + +#define BUILD_BUG_ON_NOT_POWER_OF_2(n) + +static inline size_t strlcat(char *dest, const char *src, size_t n) +{ + strcat(dest, src); + return strlen(dest) + strlen(src); +} + +static inline void *devm_kzalloc(struct device *dev, unsigned int size, + unsigned int flags) +{ + return kzalloc(size, flags); +} + +static inline void *kmalloc_array(size_t n, size_t size, gfp_t flags) +{ + return kzalloc(n * size, flags); +} +#endif diff --git a/drivers/usb/dwc3/ti_usb_phy.c b/drivers/usb/dwc3/ti_usb_phy.c new file mode 100644 index 0000000000..e6048ebf02 --- /dev/null +++ b/drivers/usb/dwc3/ti_usb_phy.c @@ -0,0 +1,309 @@ +/** + * ti_usb_phy.c - USB3 and USB3 PHY programming for dwc3 + * + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Kishon Vijay Abraham I <kishon@ti.com> + * + * Taken from Linux Kernel v3.16 (drivers/phy/phy-ti-pipe3.c and + * drivers/phy/phy-omap-usb2.c) and ported to uboot. + * + * "commit 56042e : phy: ti-pipe3: Fix suspend/resume and module reload" for + * phy-ti-pipe3.c + * + * "commit eb82a3 : phy: omap-usb2: Balance pm_runtime_enable() on probe failure + * and remove" for phy-omap-usb2.c + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <malloc.h> +#include <ti-usb-phy-uboot.h> +#include <usb/lin_gadget_compat.h> +#include <linux/ioport.h> +#include <asm/io.h> +#include <asm/arch/sys_proto.h> + +#include "linux-compat.h" + +#define PLL_STATUS 0x00000004 +#define PLL_GO 0x00000008 +#define PLL_CONFIGURATION1 0x0000000C +#define PLL_CONFIGURATION2 0x00000010 +#define PLL_CONFIGURATION3 0x00000014 +#define PLL_CONFIGURATION4 0x00000020 + +#define PLL_REGM_MASK 0x001FFE00 +#define PLL_REGM_SHIFT 0x9 +#define PLL_REGM_F_MASK 0x0003FFFF +#define PLL_REGM_F_SHIFT 0x0 +#define PLL_REGN_MASK 0x000001FE +#define PLL_REGN_SHIFT 0x1 +#define PLL_SELFREQDCO_MASK 0x0000000E +#define PLL_SELFREQDCO_SHIFT 0x1 +#define PLL_SD_MASK 0x0003FC00 +#define PLL_SD_SHIFT 10 +#define SET_PLL_GO 0x1 +#define PLL_LDOPWDN BIT(15) +#define PLL_TICOPWDN BIT(16) +#define PLL_LOCK 0x2 +#define PLL_IDLE 0x1 + +#define OMAP_CTRL_DEV_PHY_PD BIT(0) +#define OMAP_CTRL_USB3_PHY_PWRCTL_CLK_CMD_MASK 0x003FC000 +#define OMAP_CTRL_USB3_PHY_PWRCTL_CLK_CMD_SHIFT 0xE + +#define OMAP_CTRL_USB3_PHY_PWRCTL_CLK_FREQ_MASK 0xFFC00000 +#define OMAP_CTRL_USB3_PHY_PWRCTL_CLK_FREQ_SHIFT 0x16 + +#define OMAP_CTRL_USB3_PHY_TX_RX_POWERON 0x3 +#define OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF 0x0 + +#define OMAP_CTRL_USB2_PHY_PD BIT(28) + +#define AM437X_CTRL_USB2_PHY_PD BIT(0) +#define AM437X_CTRL_USB2_OTG_PD BIT(1) +#define AM437X_CTRL_USB2_OTGVDET_EN BIT(19) +#define AM437X_CTRL_USB2_OTGSESSEND_EN BIT(20) + +static LIST_HEAD(ti_usb_phy_list); +typedef unsigned int u32; + +struct usb3_dpll_params { + u16 m; + u8 n; + u8 freq:3; + u8 sd; + u32 mf; +}; + +struct usb3_dpll_map { + unsigned long rate; + struct usb3_dpll_params params; + struct usb3_dpll_map *dpll_map; +}; + +struct ti_usb_phy { + void __iomem *pll_ctrl_base; + void __iomem *usb2_phy_power; + void __iomem *usb3_phy_power; + struct usb3_dpll_map *dpll_map; + struct list_head list; + int index; +}; + +static struct usb3_dpll_map dpll_map_usb[] = { + {12000000, {1250, 5, 4, 20, 0} }, /* 12 MHz */ + {16800000, {3125, 20, 4, 20, 0} }, /* 16.8 MHz */ + {19200000, {1172, 8, 4, 20, 65537} }, /* 19.2 MHz */ + {20000000, {1000, 7, 4, 10, 0} }, /* 20 MHz */ + {26000000, {1250, 12, 4, 20, 0} }, /* 26 MHz */ + {38400000, {3125, 47, 4, 20, 92843} }, /* 38.4 MHz */ + { }, /* Terminator */ +}; + +static inline unsigned int ti_usb3_readl(void __iomem *base, u32 offset) +{ + return readl(base + offset); +} + +static inline void ti_usb3_writel(void __iomem *base, u32 offset, u32 value) +{ + writel(value, base + offset); +} + +#ifndef CONFIG_AM43XX +static struct usb3_dpll_params *ti_usb3_get_dpll_params(struct ti_usb_phy *phy) +{ + unsigned long rate; + struct usb3_dpll_map *dpll_map = phy->dpll_map; + + rate = get_sys_clk_freq(); + + for (; dpll_map->rate; dpll_map++) { + if (rate == dpll_map->rate) + return &dpll_map->params; + } + + dev_err(phy->dev, "No DPLL configuration for %lu Hz SYS CLK\n", rate); + + return NULL; +} + +static int ti_usb3_dpll_wait_lock(struct ti_usb_phy *phy) +{ + u32 val; + do { + val = ti_usb3_readl(phy->pll_ctrl_base, PLL_STATUS); + if (val & PLL_LOCK) + break; + } while (1); + + return 0; +} + +static int ti_usb3_dpll_program(struct ti_usb_phy *phy) +{ + u32 val; + struct usb3_dpll_params *dpll_params; + + if (!phy->pll_ctrl_base) + return -EINVAL; + + dpll_params = ti_usb3_get_dpll_params(phy); + if (!dpll_params) + return -EINVAL; + + val = ti_usb3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); + val &= ~PLL_REGN_MASK; + val |= dpll_params->n << PLL_REGN_SHIFT; + ti_usb3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); + + val = ti_usb3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); + val &= ~PLL_SELFREQDCO_MASK; + val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT; + ti_usb3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); + + val = ti_usb3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); + val &= ~PLL_REGM_MASK; + val |= dpll_params->m << PLL_REGM_SHIFT; + ti_usb3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); + + val = ti_usb3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4); + val &= ~PLL_REGM_F_MASK; + val |= dpll_params->mf << PLL_REGM_F_SHIFT; + ti_usb3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val); + + val = ti_usb3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3); + val &= ~PLL_SD_MASK; + val |= dpll_params->sd << PLL_SD_SHIFT; + ti_usb3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val); + + ti_usb3_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO); + + return ti_usb3_dpll_wait_lock(phy); +} +#endif + +void ti_usb2_phy_power(struct ti_usb_phy *phy, int on) +{ + u32 val; + + val = readl(phy->usb2_phy_power); + + if (on) { +#ifdef CONFIG_DRA7XX + val &= ~OMAP_CTRL_DEV_PHY_PD; +#elif defined(CONFIG_AM43XX) + val &= ~(AM437X_CTRL_USB2_PHY_PD | + AM437X_CTRL_USB2_OTG_PD); + val |= (AM437X_CTRL_USB2_OTGVDET_EN | + AM437X_CTRL_USB2_OTGSESSEND_EN); +#endif + } else { +#ifdef CONFIG_DRA7XX + val |= OMAP_CTRL_DEV_PHY_PD; +#elif defined(CONFIG_AM43XX) + val &= ~(AM437X_CTRL_USB2_OTGVDET_EN | + AM437X_CTRL_USB2_OTGSESSEND_EN); + val |= (AM437X_CTRL_USB2_PHY_PD | + AM437X_CTRL_USB2_OTG_PD); +#endif + } + writel(val, phy->usb2_phy_power); +} + +#ifndef CONFIG_AM43XX +void ti_usb3_phy_power(struct ti_usb_phy *phy, int on) +{ + u32 val; + u32 rate; + rate = get_sys_clk_freq(); + rate = rate/1000000; + + if (!phy->usb3_phy_power) + return; + + val = readl(phy->usb3_phy_power); + if (on) { + val &= ~(OMAP_CTRL_USB3_PHY_PWRCTL_CLK_CMD_MASK | + OMAP_CTRL_USB3_PHY_PWRCTL_CLK_FREQ_MASK); + val |= (OMAP_CTRL_USB3_PHY_TX_RX_POWERON) << + OMAP_CTRL_USB3_PHY_PWRCTL_CLK_CMD_SHIFT; + val |= rate << + OMAP_CTRL_USB3_PHY_PWRCTL_CLK_FREQ_SHIFT; + } else { + val &= ~OMAP_CTRL_USB3_PHY_PWRCTL_CLK_CMD_MASK; + val |= OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF << + OMAP_CTRL_USB3_PHY_PWRCTL_CLK_CMD_SHIFT; + } + writel(val, phy->usb3_phy_power); +} +#endif + +/** + * ti_usb_phy_uboot_init - usb phy uboot initialization code + * @dev: struct ti_usb_phy_device containing initialization data + * + * Entry point for ti usb phy driver. This driver handles initialization + * of both usb2 phy and usb3 phy. Pointer to ti_usb_phy_device should be + * passed containing base address and other initialization data. + * Returns '0' on success and a negative value on failure. + * + * Generally called from board_usb_init() implemented in board file. + */ +int ti_usb_phy_uboot_init(struct ti_usb_phy_device *dev) +{ + struct ti_usb_phy *phy; + + phy = devm_kzalloc(NULL, sizeof(*phy), GFP_KERNEL); + if (!phy) { + dev_err(NULL, "unable to alloc mem for TI USB3 PHY\n"); + return -ENOMEM; + } + + phy->dpll_map = dpll_map_usb; + phy->index = dev->index; + phy->pll_ctrl_base = dev->pll_ctrl_base; + phy->usb2_phy_power = dev->usb2_phy_power; + phy->usb3_phy_power = dev->usb3_phy_power; + +#ifndef CONFIG_AM43XX + ti_usb3_dpll_program(phy); + ti_usb3_phy_power(phy, 1); +#endif + ti_usb2_phy_power(phy, 1); + mdelay(150); + list_add_tail(&phy->list, &ti_usb_phy_list); + + return 0; +} + +/** + * ti_usb_phy_uboot_exit - usb phy uboot cleanup code + * @index: index of this controller + * + * Performs cleanup of memory allocated in ti_usb_phy_uboot_init. + * index of _this_ controller should be passed and should match with + * the index passed in ti_usb_phy_device during init. + * + * Generally called from board file. + */ +void ti_usb_phy_uboot_exit(int index) +{ + struct ti_usb_phy *phy = NULL; + + list_for_each_entry(phy, &ti_usb_phy_list, list) { + if (phy->index != index) + continue; + + ti_usb2_phy_power(phy, 0); +#ifndef CONFIG_AM43XX + ti_usb3_phy_power(phy, 0); +#endif + list_del(&phy->list); + kfree(phy); + break; + } +} diff --git a/drivers/usb/eth/asix.c b/drivers/usb/eth/asix.c index 11811094ed..1cd179baae 100644 --- a/drivers/usb/eth/asix.c +++ b/drivers/usb/eth/asix.c @@ -475,7 +475,7 @@ static int asix_send(struct eth_device *eth, void *packet, int length) length + sizeof(packet_len), &actual_len, USB_BULK_SEND_TIMEOUT); - debug("Tx: len = %u, actual = %u, err = %d\n", + debug("Tx: len = %zu, actual = %u, err = %d\n", length + sizeof(packet_len), actual_len, err); return err; diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index fbc74f3bed..1e23d09c77 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1199,7 +1199,7 @@ static struct usba_udc controller = { }, }; -int usb_gadget_handle_interrupts(void) +int usb_gadget_handle_interrupts(int index) { struct usba_udc *udc = &controller; diff --git a/drivers/usb/gadget/ci_udc.c b/drivers/usb/gadget/ci_udc.c index b0ef35e745..3b7024c498 100644 --- a/drivers/usb/gadget/ci_udc.c +++ b/drivers/usb/gadget/ci_udc.c @@ -160,8 +160,8 @@ static struct ept_queue_item *ci_get_qtd(int ep_num, int dir_in) static void ci_flush_qh(int ep_num) { struct ept_queue_head *head = ci_get_qh(ep_num, 0); - const uint32_t start = (uint32_t)head; - const uint32_t end = start + 2 * sizeof(*head); + const unsigned long start = (unsigned long)head; + const unsigned long end = start + 2 * sizeof(*head); flush_dcache_range(start, end); } @@ -175,8 +175,8 @@ static void ci_flush_qh(int ep_num) static void ci_invalidate_qh(int ep_num) { struct ept_queue_head *head = ci_get_qh(ep_num, 0); - uint32_t start = (uint32_t)head; - uint32_t end = start + 2 * sizeof(*head); + unsigned long start = (unsigned long)head; + unsigned long end = start + 2 * sizeof(*head); invalidate_dcache_range(start, end); } @@ -190,8 +190,8 @@ static void ci_invalidate_qh(int ep_num) static void ci_flush_qtd(int ep_num) { struct ept_queue_item *item = ci_get_qtd(ep_num, 0); - const uint32_t start = (uint32_t)item; - const uint32_t end = start + 2 * ILIST_ENT_SZ; + const unsigned long start = (unsigned long)item; + const unsigned long end = start + 2 * ILIST_ENT_SZ; flush_dcache_range(start, end); } @@ -205,8 +205,8 @@ static void ci_flush_qtd(int ep_num) static void ci_invalidate_qtd(int ep_num) { struct ept_queue_item *item = ci_get_qtd(ep_num, 0); - const uint32_t start = (uint32_t)item; - const uint32_t end = start + 2 * ILIST_ENT_SZ; + const unsigned long start = (unsigned long)item; + const unsigned long end = start + 2 * ILIST_ENT_SZ; invalidate_dcache_range(start, end); } @@ -308,8 +308,8 @@ static int ci_ep_disable(struct usb_ep *ep) static int ci_bounce(struct ci_req *ci_req, int in) { struct usb_request *req = &ci_req->req; - uint32_t addr = (uint32_t)req->buf; - uint32_t hwaddr; + unsigned long addr = (unsigned long)req->buf; + unsigned long hwaddr; uint32_t aligned_used_len; /* Input buffer address is not aligned. */ @@ -343,7 +343,7 @@ align: memcpy(ci_req->hw_buf, req->buf, req->length); flush: - hwaddr = (uint32_t)ci_req->hw_buf; + hwaddr = (unsigned long)ci_req->hw_buf; aligned_used_len = roundup(req->length, ARCH_DMA_MINALIGN); flush_dcache_range(hwaddr, hwaddr + aligned_used_len); @@ -353,8 +353,8 @@ flush: static void ci_debounce(struct ci_req *ci_req, int in) { struct usb_request *req = &ci_req->req; - uint32_t addr = (uint32_t)req->buf; - uint32_t hwaddr = (uint32_t)ci_req->hw_buf; + unsigned long addr = (unsigned long)req->buf; + unsigned long hwaddr = (unsigned long)ci_req->hw_buf; uint32_t aligned_used_len; if (in) @@ -388,13 +388,13 @@ static void ci_ep_submit_next_request(struct ci_ep *ci_ep) len = ci_req->req.length; item->info = INFO_BYTES(len) | INFO_ACTIVE; - item->page0 = (uint32_t)ci_req->hw_buf; - item->page1 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x1000; - item->page2 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x2000; - item->page3 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x3000; - item->page4 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x4000; + item->page0 = (unsigned long)ci_req->hw_buf; + item->page1 = ((unsigned long)ci_req->hw_buf & 0xfffff000) + 0x1000; + item->page2 = ((unsigned long)ci_req->hw_buf & 0xfffff000) + 0x2000; + item->page3 = ((unsigned long)ci_req->hw_buf & 0xfffff000) + 0x3000; + item->page4 = ((unsigned long)ci_req->hw_buf & 0xfffff000) + 0x4000; - head->next = (unsigned) item; + head->next = (unsigned long)item; head->info = 0; /* @@ -422,7 +422,7 @@ static void ci_ep_submit_next_request(struct ci_ep *ci_ep) * can use the other to transmit the extra zero-length packet. */ struct ept_queue_item *other_item = ci_get_qtd(num, 0); - item->next = (unsigned)other_item; + item->next = (unsigned long)other_item; item = other_item; item->info = INFO_ACTIVE; } @@ -741,7 +741,7 @@ void udc_irq(void) } } -int usb_gadget_handle_interrupts(void) +int usb_gadget_handle_interrupts(int index) { u32 value; struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor; @@ -772,7 +772,7 @@ static int ci_pullup(struct usb_gadget *gadget, int is_on) writel(USBCMD_ITC(MICRO_8FRAME) | USBCMD_RST, &udc->usbcmd); udelay(200); - writel((unsigned)controller.epts, &udc->epinitaddr); + writel((unsigned long)controller.epts, &udc->epinitaddr); /* select DEVICE mode */ writel(USBMODE_DEVICE, &udc->usbmode); diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 98c2da6f14..d96296cd73 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -283,7 +283,7 @@ static void device_qual(struct usb_composite_dev *cdev) qual->bDeviceSubClass = cdev->desc.bDeviceSubClass; qual->bDeviceProtocol = cdev->desc.bDeviceProtocol; /* ASSUME same EP0 fifo size at both speeds */ - qual->bMaxPacketSize0 = cdev->desc.bMaxPacketSize0; + qual->bMaxPacketSize0 = cdev->gadget->ep0->maxpacket; qual->bNumConfigurations = count_configs(cdev, USB_DT_DEVICE_QUALIFIER); qual->bRESERVED = 0; } @@ -736,6 +736,8 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) case USB_DT_DEVICE: cdev->desc.bNumConfigurations = count_configs(cdev, USB_DT_DEVICE); + cdev->desc.bMaxPacketSize0 = + cdev->gadget->ep0->maxpacket; value = min(w_length, (u16) sizeof cdev->desc); memcpy(req->buf, &cdev->desc, value); break; @@ -1050,6 +1052,7 @@ static struct usb_gadget_driver composite_driver = { .unbind = composite_unbind, .setup = composite_setup, + .reset = composite_disconnect, .disconnect = composite_disconnect, .suspend = composite_suspend, diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 0df4b2a103..6ddbe83deb 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -220,7 +220,7 @@ struct usb_ep *usb_ep_autoconfig( struct usb_endpoint_descriptor *desc ) { - struct usb_ep *ep; + struct usb_ep *ep = NULL; u8 type; type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; @@ -261,6 +261,28 @@ struct usb_ep *usb_ep_autoconfig( ep = find_ep(gadget, "ep1-bulk"); if (ep && ep_matches(gadget, ep, desc)) return ep; + } else if (gadget_is_dwc3(gadget)) { + const char *name = NULL; + /* + * First try standard, common configuration: ep1in-bulk, + * ep2out-bulk, ep3in-int to match other udc drivers to avoid + * confusion in already deployed software (endpoint numbers + * hardcoded in userspace software/drivers) + */ + if ((desc->bEndpointAddress & USB_DIR_IN) && + type == USB_ENDPOINT_XFER_BULK) + name = "ep1in"; + else if ((desc->bEndpointAddress & USB_DIR_IN) == 0 && + type == USB_ENDPOINT_XFER_BULK) + name = "ep2out"; + else if ((desc->bEndpointAddress & USB_DIR_IN) && + type == USB_ENDPOINT_XFER_INT) + name = "ep3in"; + + if (name) + ep = find_ep(gadget, name); + if (ep && ep_matches(gadget, ep, desc)) + return ep; } /* Second, look at endpoints until an unclaimed one looks usable */ diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 8f03a6bb9d..7e3b3ed859 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -1248,6 +1248,7 @@ eth_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) switch (wValue >> 8) { case USB_DT_DEVICE: + device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; value = min(wLength, (u16) sizeof device_desc); memcpy(req->buf, &device_desc, value); break; @@ -1906,7 +1907,7 @@ static int eth_stop(struct eth_dev *dev) /* Wait until host receives OID_GEN_MEDIA_CONNECT_STATUS */ ts = get_timer(0); while (get_timer(ts) < timeout) - usb_gadget_handle_interrupts(); + usb_gadget_handle_interrupts(0); #endif rndis_uninit(dev->rndis_config); @@ -2132,7 +2133,6 @@ autoconf_fail: hs_subset_descriptors(); } - device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; usb_gadget_set_selfpowered(gadget); /* For now RNDIS is always a second config */ @@ -2358,7 +2358,7 @@ static int usb_eth_init(struct eth_device *netdev, bd_t *bd) error("The remote end did not respond in time."); goto fail; } - usb_gadget_handle_interrupts(); + usb_gadget_handle_interrupts(0); } packet_received = 0; @@ -2426,7 +2426,7 @@ static int usb_eth_send(struct eth_device *netdev, void *packet, int length) printf("timeout sending packets to usb ethernet\n"); return -1; } - usb_gadget_handle_interrupts(); + usb_gadget_handle_interrupts(0); } if (rndis_pkt) free(rndis_pkt); @@ -2441,7 +2441,7 @@ static int usb_eth_recv(struct eth_device *netdev) { struct eth_dev *dev = &l_ethdev; - usb_gadget_handle_interrupts(); + usb_gadget_handle_interrupts(0); if (packet_received) { debug("%s: packet received\n", __func__); @@ -2486,7 +2486,7 @@ void usb_eth_halt(struct eth_device *netdev) /* Clear pending interrupt */ if (dev->network_started) { - usb_gadget_handle_interrupts(); + usb_gadget_handle_interrupts(0); dev->network_started = 0; } diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index 751ec9e010..206b6d17ae 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/f_fastboot.c @@ -123,6 +123,7 @@ static struct usb_gadget_strings *fastboot_strings[] = { }; static void rx_handler_command(struct usb_ep *ep, struct usb_request *req); +static int strcmp_l1(const char *s1, const char *s2); static void fastboot_complete(struct usb_ep *ep, struct usb_request *req) { @@ -326,8 +327,20 @@ static void compl_do_reset(struct usb_ep *ep, struct usb_request *req) do_reset(NULL, 0, 0, NULL); } +int __weak fb_set_reboot_flag(void) +{ + return -ENOSYS; +} + static void cb_reboot(struct usb_ep *ep, struct usb_request *req) { + char *cmd = req->buf; + if (!strcmp_l1("reboot-bootloader", cmd)) { + if (fb_set_reboot_flag()) { + fastboot_tx_write_str("FAILCannot set reboot flag"); + return; + } + } fastboot_func->in_req->complete = compl_do_reset; fastboot_tx_write_str("OKAY"); } diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index e045957d07..d1bc5efa9b 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -689,7 +689,7 @@ static int sleep_thread(struct fsg_common *common) k = 0; } - usb_gadget_handle_interrupts(); + usb_gadget_handle_interrupts(0); } common->thread_wakeup_needed = 0; return rc; @@ -973,7 +973,7 @@ static int do_write(struct fsg_common *common) /* If an error occurred, report it and its position */ if (nwritten < amount) { - printf("nwritten:%d amount:%d\n", nwritten, + printf("nwritten:%zd amount:%u\n", nwritten, amount); curlun->sense_data = SS_WRITE_ERROR; curlun->info_valid = 1; diff --git a/drivers/usb/gadget/f_thor.c b/drivers/usb/gadget/f_thor.c index 2d0410d795..6346370cd6 100644 --- a/drivers/usb/gadget/f_thor.c +++ b/drivers/usb/gadget/f_thor.c @@ -543,7 +543,7 @@ static int thor_rx_data(void) } while (!dev->rxdata) { - usb_gadget_handle_interrupts(); + usb_gadget_handle_interrupts(0); if (ctrlc()) return -1; } @@ -577,7 +577,7 @@ static void thor_tx_data(unsigned char *data, int len) /* Wait until tx interrupt received */ while (!dev->txdata) - usb_gadget_handle_interrupts(); + usb_gadget_handle_interrupts(0); dev->txdata = 0; } @@ -694,7 +694,7 @@ int thor_init(void) /* Wait for a device enumeration and configuration settings */ debug("THOR enumeration/configuration setting....\n"); while (!dev->configuration_done) - usb_gadget_handle_interrupts(); + usb_gadget_handle_interrupts(0); thor_set_dma(thor_rx_data_buf, strlen("THOR")); /* detect the download request from Host PC */ @@ -806,6 +806,7 @@ static int thor_func_bind(struct usb_configuration *c, struct usb_function *f) } dev->in_ep = ep; /* Store IN EP for enabling @ setup */ + ep->driver_data = dev; ep = usb_ep_autoconfig(gadget, &fs_out_desc); if (!ep) { @@ -818,6 +819,7 @@ static int thor_func_bind(struct usb_configuration *c, struct usb_function *f) fs_out_desc.bEndpointAddress; dev->out_ep = ep; /* Store OUT EP for enabling @ setup */ + ep->driver_data = dev; ep = usb_ep_autoconfig(gadget, &fs_int_desc); if (!ep) { @@ -826,6 +828,7 @@ static int thor_func_bind(struct usb_configuration *c, struct usb_function *f) } dev->int_ep = ep; + ep->driver_data = dev; if (gadget_is_dualspeed(gadget)) { hs_int_desc.bEndpointAddress = diff --git a/drivers/usb/gadget/fotg210.c b/drivers/usb/gadget/fotg210.c index 3acf6a1f41..1d8f58fd72 100644 --- a/drivers/usb/gadget/fotg210.c +++ b/drivers/usb/gadget/fotg210.c @@ -832,7 +832,7 @@ static struct fotg210_chip controller = { }, }; -int usb_gadget_handle_interrupts(void) +int usb_gadget_handle_interrupts(int index) { struct fotg210_chip *chip = &controller; struct fotg210_regs *regs = chip->regs; diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index cc94771e32..c859df2f7f 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -156,6 +156,14 @@ #define gadget_is_fotg210(g) 0 #endif +#ifdef CONFIG_USB_DWC3_GADGET +#define gadget_is_dwc3(g) (!strcmp("dwc3-gadget", (g)->name)) +#else +#define gadget_is_dwc3(g) 0 +#endif + + + /* * CONFIG_USB_GADGET_SX2 * CONFIG_USB_GADGET_AU1X00 diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index d4460b2dc7..6a8949da34 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -2041,7 +2041,7 @@ extern void udc_disconnect(void) /*-------------------------------------------------------------------------*/ extern int -usb_gadget_handle_interrupts(void) +usb_gadget_handle_interrupts(int index) { return pxa25x_udc_irq(); } diff --git a/drivers/usb/gadget/s3c_udc_otg.c b/drivers/usb/gadget/s3c_udc_otg.c index 7653f03949..7a2d1e7d88 100644 --- a/drivers/usb/gadget/s3c_udc_otg.c +++ b/drivers/usb/gadget/s3c_udc_otg.c @@ -833,7 +833,7 @@ int s3c_udc_probe(struct s3c_plat_otg_data *pdata) return retval; } -int usb_gadget_handle_interrupts() +int usb_gadget_handle_interrupts(int index) { u32 intr_status = readl(®->gintsts); u32 gintmsk = readl(®->gintmsk); diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile new file mode 100644 index 0000000000..12380f4e4e --- /dev/null +++ b/drivers/usb/gadget/udc/Makefile @@ -0,0 +1,4 @@ +# +# USB peripheral controller drivers +# +obj-$(CONFIG_USB_DWC3_GADGET) += udc-core.o diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c new file mode 100644 index 0000000000..875e998a82 --- /dev/null +++ b/drivers/usb/gadget/udc/udc-core.c @@ -0,0 +1,354 @@ +/** + * udc-core.c - Core UDC Framework + * + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Felipe Balbi <balbi@ti.com> + * + * Taken from Linux Kernel v3.19-rc1 (drivers/usb/gadget/udc-core.c) and ported + * to uboot. + * + * commit 02e8c96627 : usb: gadget: udc: core: prepend udc_attach_driver with + * usb_ + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <linux/compat.h> +#include <malloc.h> +#include <asm/cache.h> +#include <asm/dma-mapping.h> +#include <common.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +/** + * struct usb_udc - describes one usb device controller + * @driver - the gadget driver pointer. For use by the class code + * @dev - the child device to the actual controller + * @gadget - the gadget. For use by the class code + * @list - for use by the udc class driver + * + * This represents the internal data structure which is used by the UDC-class + * to hold information about udc driver and gadget together. + */ +struct usb_udc { + struct usb_gadget_driver *driver; + struct usb_gadget *gadget; + struct device dev; + struct list_head list; +}; + +static struct class *udc_class; +static LIST_HEAD(udc_list); +DEFINE_MUTEX(udc_lock); + +/* ------------------------------------------------------------------------- */ + +int usb_gadget_map_request(struct usb_gadget *gadget, + struct usb_request *req, int is_in) +{ + if (req->length == 0) + return 0; + + req->dma = dma_map_single(req->buf, req->length, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + return 0; +} +EXPORT_SYMBOL_GPL(usb_gadget_map_request); + +void usb_gadget_unmap_request(struct usb_gadget *gadget, + struct usb_request *req, int is_in) +{ + if (req->length == 0) + return; + + dma_unmap_single((void *)req->dma, req->length, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); +} +EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); + +/* ------------------------------------------------------------------------- */ + +/** + * usb_gadget_giveback_request - give the request back to the gadget layer + * Context: in_interrupt() + * + * This is called by device controller drivers in order to return the + * completed request back to the gadget layer. + */ +void usb_gadget_giveback_request(struct usb_ep *ep, + struct usb_request *req) +{ + req->complete(ep, req); +} +EXPORT_SYMBOL_GPL(usb_gadget_giveback_request); + +/* ------------------------------------------------------------------------- */ + +void usb_gadget_set_state(struct usb_gadget *gadget, + enum usb_device_state state) +{ + gadget->state = state; +} +EXPORT_SYMBOL_GPL(usb_gadget_set_state); + +/* ------------------------------------------------------------------------- */ + +/** + * usb_gadget_udc_reset - notifies the udc core that bus reset occurs + * @gadget: The gadget which bus reset occurs + * @driver: The gadget driver we want to notify + * + * If the udc driver has bus reset handler, it needs to call this when the bus + * reset occurs, it notifies the gadget driver that the bus reset occurs as + * well as updates gadget state. + */ +void usb_gadget_udc_reset(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + driver->reset(gadget); + usb_gadget_set_state(gadget, USB_STATE_DEFAULT); +} +EXPORT_SYMBOL_GPL(usb_gadget_udc_reset); + +/** + * usb_gadget_udc_start - tells usb device controller to start up + * @udc: The UDC to be started + * + * This call is issued by the UDC Class driver when it's about + * to register a gadget driver to the device controller, before + * calling gadget driver's bind() method. + * + * It allows the controller to be powered off until strictly + * necessary to have it powered on. + * + * Returns zero on success, else negative errno. + */ +static inline int usb_gadget_udc_start(struct usb_udc *udc) +{ + return udc->gadget->ops->udc_start(udc->gadget, udc->driver); +} + +/** + * usb_gadget_udc_stop - tells usb device controller we don't need it anymore + * @gadget: The device we want to stop activity + * @driver: The driver to unbind from @gadget + * + * This call is issued by the UDC Class driver after calling + * gadget driver's unbind() method. + * + * The details are implementation specific, but it can go as + * far as powering off UDC completely and disable its data + * line pullups. + */ +static inline void usb_gadget_udc_stop(struct usb_udc *udc) +{ + udc->gadget->ops->udc_stop(udc->gadget); +} + +/** + * usb_udc_release - release the usb_udc struct + * @dev: the dev member within usb_udc + * + * This is called by driver's core in order to free memory once the last + * reference is released. + */ +static void usb_udc_release(struct device *dev) +{ + struct usb_udc *udc; + + udc = container_of(dev, struct usb_udc, dev); + kfree(udc); +} + +/** + * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list + * @parent: the parent device to this udc. Usually the controller driver's + * device. + * @gadget: the gadget to be added to the list. + * @release: a gadget release function. + * + * Returns zero on success, negative errno otherwise. + */ +int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, + void (*release)(struct device *dev)) +{ + struct usb_udc *udc; + int ret = -ENOMEM; + + udc = kzalloc(sizeof(*udc), GFP_KERNEL); + if (!udc) + goto err1; + + dev_set_name(&gadget->dev, "gadget"); + gadget->dev.parent = parent; + + udc->dev.release = usb_udc_release; + udc->dev.class = udc_class; + udc->dev.parent = parent; + + udc->gadget = gadget; + + mutex_lock(&udc_lock); + list_add_tail(&udc->list, &udc_list); + + usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED); + + mutex_unlock(&udc_lock); + + return 0; + +err1: + return ret; +} +EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release); + +/** + * usb_add_gadget_udc - adds a new gadget to the udc class driver list + * @parent: the parent device to this udc. Usually the controller + * driver's device. + * @gadget: the gadget to be added to the list + * + * Returns zero on success, negative errno otherwise. + */ +int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) +{ + return usb_add_gadget_udc_release(parent, gadget, NULL); +} +EXPORT_SYMBOL_GPL(usb_add_gadget_udc); + +static void usb_gadget_remove_driver(struct usb_udc *udc) +{ + dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n", + udc->driver->function); + + usb_gadget_disconnect(udc->gadget); + udc->driver->disconnect(udc->gadget); + udc->driver->unbind(udc->gadget); + usb_gadget_udc_stop(udc); + + udc->driver = NULL; +} + +/** + * usb_del_gadget_udc - deletes @udc from udc_list + * @gadget: the gadget to be removed. + * + * This, will call usb_gadget_unregister_driver() if + * the @udc is still busy. + */ +void usb_del_gadget_udc(struct usb_gadget *gadget) +{ + struct usb_udc *udc = NULL; + + mutex_lock(&udc_lock); + list_for_each_entry(udc, &udc_list, list) + if (udc->gadget == gadget) + goto found; + + dev_err(gadget->dev.parent, "gadget not registered.\n"); + mutex_unlock(&udc_lock); + + return; + +found: + dev_vdbg(gadget->dev.parent, "unregistering gadget\n"); + + list_del(&udc->list); + mutex_unlock(&udc_lock); + + if (udc->driver) + usb_gadget_remove_driver(udc); +} +EXPORT_SYMBOL_GPL(usb_del_gadget_udc); + +/* ------------------------------------------------------------------------- */ + +static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver) +{ + int ret; + + dev_dbg(&udc->dev, "registering UDC driver [%s]\n", + driver->function); + + udc->driver = driver; + + ret = driver->bind(udc->gadget); + if (ret) + goto err1; + ret = usb_gadget_udc_start(udc); + if (ret) { + driver->unbind(udc->gadget); + goto err1; + } + usb_gadget_connect(udc->gadget); + + return 0; +err1: + if (ret != -EISNAM) + dev_err(&udc->dev, "failed to start %s: %d\n", + udc->driver->function, ret); + udc->driver = NULL; + return ret; +} + +int usb_gadget_probe_driver(struct usb_gadget_driver *driver) +{ + struct usb_udc *udc = NULL; + int ret; + + if (!driver || !driver->bind || !driver->setup) + return -EINVAL; + + mutex_lock(&udc_lock); + list_for_each_entry(udc, &udc_list, list) { + /* For now we take the first one */ + if (!udc->driver) + goto found; + } + + printf("couldn't find an available UDC\n"); + mutex_unlock(&udc_lock); + return -ENODEV; +found: + ret = udc_bind_to_driver(udc, driver); + mutex_unlock(&udc_lock); + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_probe_driver); + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + return usb_gadget_probe_driver(driver); +} +EXPORT_SYMBOL_GPL(usb_gadget_register_driver); + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct usb_udc *udc = NULL; + int ret = -ENODEV; + + if (!driver || !driver->unbind) + return -EINVAL; + + mutex_lock(&udc_lock); + list_for_each_entry(udc, &udc_list, list) + if (udc->driver == driver) { + usb_gadget_remove_driver(udc); + usb_gadget_set_state(udc->gadget, + USB_STATE_NOTATTACHED); + ret = 0; + break; + } + + mutex_unlock(&udc_lock); + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver); + +MODULE_DESCRIPTION("UDC Framework"); +MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c index e8142ac092..2ac00177a2 100644 --- a/drivers/usb/host/dwc2.c +++ b/drivers/usb/host/dwc2.c @@ -9,6 +9,7 @@ #include <errno.h> #include <usb.h> #include <malloc.h> +#include <phys2bus.h> #include <usbroothubdes.h> #include <asm/io.h> @@ -27,7 +28,6 @@ DEFINE_ALIGN_BUFFER(uint8_t, status_buffer, DWC2_STATUS_BUF_SIZE, 8); #define MAX_DEVICE 16 #define MAX_ENDPOINT 16 static int bulk_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; -static int control_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; static int root_hub_devnum; @@ -398,15 +398,18 @@ static void dwc_otg_core_init(struct dwc2_core_regs *regs) * @param hc Information needed to initialize the host channel */ static void dwc_otg_hc_init(struct dwc2_core_regs *regs, uint8_t hc_num, - uint8_t dev_addr, uint8_t ep_num, uint8_t ep_is_in, - uint8_t ep_type, uint16_t max_packet) + struct usb_device *dev, uint8_t dev_addr, uint8_t ep_num, + uint8_t ep_is_in, uint8_t ep_type, uint16_t max_packet) { struct dwc2_hc_regs *hc_regs = ®s->hc_regs[hc_num]; - const uint32_t hcchar = (dev_addr << DWC2_HCCHAR_DEVADDR_OFFSET) | - (ep_num << DWC2_HCCHAR_EPNUM_OFFSET) | - (ep_is_in << DWC2_HCCHAR_EPDIR_OFFSET) | - (ep_type << DWC2_HCCHAR_EPTYPE_OFFSET) | - (max_packet << DWC2_HCCHAR_MPS_OFFSET); + uint32_t hcchar = (dev_addr << DWC2_HCCHAR_DEVADDR_OFFSET) | + (ep_num << DWC2_HCCHAR_EPNUM_OFFSET) | + (ep_is_in << DWC2_HCCHAR_EPDIR_OFFSET) | + (ep_type << DWC2_HCCHAR_EPTYPE_OFFSET) | + (max_packet << DWC2_HCCHAR_MPS_OFFSET); + + if (dev->speed == USB_SPEED_LOW) + hcchar |= DWC2_HCCHAR_LSPDDEV; /* Clear old interrupt conditions for this host channel. */ writel(0x3fff, &hc_regs->hcint); @@ -463,7 +466,11 @@ static int dwc_otg_submit_rh_msg_in_status(struct usb_device *dev, void *buffer, if (hprt0 & DWC2_HPRT0_PRTPWR) port_status |= USB_PORT_STAT_POWER; - port_status |= USB_PORT_STAT_HIGH_SPEED; + if ((hprt0 & DWC2_HPRT0_PRTSPD_MASK) == DWC2_HPRT0_PRTSPD_LOW) + port_status |= USB_PORT_STAT_LOW_SPEED; + else if ((hprt0 & DWC2_HPRT0_PRTSPD_MASK) == + DWC2_HPRT0_PRTSPD_HIGH) + port_status |= USB_PORT_STAT_HIGH_SPEED; if (hprt0 & DWC2_HPRT0_PRTENCHNG) port_change |= USB_PORT_STAT_C_ENABLE; @@ -704,45 +711,76 @@ static int dwc_otg_submit_rh_msg(struct usb_device *dev, unsigned long pipe, return stat; } -/* U-Boot USB transmission interface */ -int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int len) +int wait_for_chhltd(uint32_t *sub, int *toggle, bool ignore_ack) +{ + uint32_t hcint_comp_hlt_ack = DWC2_HCINT_XFERCOMP | DWC2_HCINT_CHHLTD; + struct dwc2_hc_regs *hc_regs = ®s->hc_regs[DWC2_HC_CHANNEL]; + int ret; + uint32_t hcint, hctsiz; + + ret = wait_for_bit(&hc_regs->hcint, DWC2_HCINT_CHHLTD, true); + if (ret) + return ret; + + hcint = readl(&hc_regs->hcint); + if (hcint & (DWC2_HCINT_NAK | DWC2_HCINT_FRMOVRUN)) + return -EAGAIN; + if (ignore_ack) + hcint &= ~DWC2_HCINT_ACK; + else + hcint_comp_hlt_ack |= DWC2_HCINT_ACK; + if (hcint != hcint_comp_hlt_ack) { + debug("%s: Error (HCINT=%08x)\n", __func__, hcint); + return -EINVAL; + } + + hctsiz = readl(&hc_regs->hctsiz); + *sub = (hctsiz & DWC2_HCTSIZ_XFERSIZE_MASK) >> + DWC2_HCTSIZ_XFERSIZE_OFFSET; + *toggle = (hctsiz & DWC2_HCTSIZ_PID_MASK) >> DWC2_HCTSIZ_PID_OFFSET; + + debug("%s: sub=%u toggle=%d\n", __func__, *sub, *toggle); + + return 0; +} + +static int dwc2_eptype[] = { + DWC2_HCCHAR_EPTYPE_ISOC, + DWC2_HCCHAR_EPTYPE_INTR, + DWC2_HCCHAR_EPTYPE_CONTROL, + DWC2_HCCHAR_EPTYPE_BULK, +}; + +int chunk_msg(struct usb_device *dev, unsigned long pipe, int *pid, int in, + void *buffer, int len, bool ignore_ack) { + struct dwc2_hc_regs *hc_regs = ®s->hc_regs[DWC2_HC_CHANNEL]; int devnum = usb_pipedevice(pipe); int ep = usb_pipeendpoint(pipe); int max = usb_maxpacket(dev, pipe); + int eptype = dwc2_eptype[usb_pipetype(pipe)]; int done = 0; - uint32_t hctsiz, sub, tmp; - struct dwc2_hc_regs *hc_regs = ®s->hc_regs[DWC2_HC_CHANNEL]; - uint32_t hcint; + int ret = 0; + uint32_t sub; uint32_t xfer_len; uint32_t num_packets; int stop_transfer = 0; - unsigned int timeout = 1000000; - if (devnum == root_hub_devnum) { - dev->status = 0; - return -EINVAL; - } - - if (len > DWC2_DATA_BUF_SIZE) { - printf("%s: %d is more then available buffer size (%d)\n", - __func__, len, DWC2_DATA_BUF_SIZE); - dev->status = 0; - dev->act_len = 0; - return -EINVAL; - } + debug("%s: msg: pipe %lx pid %d in %d len %d\n", __func__, pipe, *pid, + in, len); - while ((done < len) && !stop_transfer) { + do { /* Initialize channel */ - dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, devnum, ep, - usb_pipein(pipe), DWC2_HCCHAR_EPTYPE_BULK, max); + dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, dev, devnum, ep, in, + eptype, max); xfer_len = len - done; - /* Make sure that xfer_len is a multiple of max packet size. */ if (xfer_len > CONFIG_DWC2_MAX_TRANSFER_SIZE) xfer_len = CONFIG_DWC2_MAX_TRANSFER_SIZE - max + 1; + if (xfer_len > DWC2_DATA_BUF_SIZE) + xfer_len = DWC2_DATA_BUF_SIZE - max + 1; + /* Make sure that xfer_len is a multiple of max packet size. */ if (xfer_len > 0) { num_packets = (xfer_len + max - 1) / max; if (num_packets > CONFIG_DWC2_MAX_PACKET_COUNT) { @@ -753,17 +791,22 @@ int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, num_packets = 1; } - if (usb_pipein(pipe)) + if (in) xfer_len = num_packets * max; + debug("%s: chunk: pid %d xfer_len %u pkts %u\n", __func__, + *pid, xfer_len, num_packets); + writel((xfer_len << DWC2_HCTSIZ_XFERSIZE_OFFSET) | (num_packets << DWC2_HCTSIZ_PKTCNT_OFFSET) | - (bulk_data_toggle[devnum][ep] << - DWC2_HCTSIZ_PID_OFFSET), + (*pid << DWC2_HCTSIZ_PID_OFFSET), &hc_regs->hctsiz); - memcpy(aligned_buffer, (char *)buffer + done, len - done); - writel((uint32_t)aligned_buffer, &hc_regs->hcdma); + if (!in) + memcpy(aligned_buffer, (char *)buffer + done, len); + + writel(phys_to_bus((unsigned long)aligned_buffer), + &hc_regs->hcdma); /* Set host channel enable after all other setup is complete. */ clrsetbits_le32(&hc_regs->hcchar, DWC2_HCCHAR_MULTICNT_MASK | @@ -771,55 +814,20 @@ int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, (1 << DWC2_HCCHAR_MULTICNT_OFFSET) | DWC2_HCCHAR_CHEN); - while (1) { - hcint = readl(&hc_regs->hcint); - - if (!(hcint & DWC2_HCINT_CHHLTD)) - continue; - - if (hcint & DWC2_HCINT_XFERCOMP) { - hctsiz = readl(&hc_regs->hctsiz); - done += xfer_len; - - sub = hctsiz & DWC2_HCTSIZ_XFERSIZE_MASK; - sub >>= DWC2_HCTSIZ_XFERSIZE_OFFSET; - - if (usb_pipein(pipe)) { - done -= sub; - if (hctsiz & DWC2_HCTSIZ_XFERSIZE_MASK) - stop_transfer = 1; - } - - tmp = hctsiz & DWC2_HCTSIZ_PID_MASK; - tmp >>= DWC2_HCTSIZ_PID_OFFSET; - if (tmp == DWC2_HC_PID_DATA1) { - bulk_data_toggle[devnum][ep] = - DWC2_HC_PID_DATA1; - } else { - bulk_data_toggle[devnum][ep] = - DWC2_HC_PID_DATA0; - } - break; - } - - if (hcint & DWC2_HCINT_STALL) { - puts("DWC OTG: Channel halted\n"); - bulk_data_toggle[devnum][ep] = - DWC2_HC_PID_DATA0; + ret = wait_for_chhltd(&sub, pid, ignore_ack); + if (ret) + break; + if (in) { + xfer_len -= sub; + memcpy(buffer + done, aligned_buffer, xfer_len); + if (sub) stop_transfer = 1; - break; - } - - if (!--timeout) { - printf("%s: Timeout!\n", __func__); - break; - } } - } - if (done && usb_pipein(pipe)) - memcpy(buffer, aligned_buffer, done); + done += xfer_len; + + } while ((done < len) && !stop_transfer); writel(0, &hc_regs->hcintmsk); writel(0xFFFFFFFF, &hc_regs->hcint); @@ -827,138 +835,54 @@ int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, dev->status = 0; dev->act_len = done; - return 0; + return ret; } -int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int len, struct devrequest *setup) +/* U-Boot USB transmission interface */ +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len) { - struct dwc2_hc_regs *hc_regs = ®s->hc_regs[DWC2_HC_CHANNEL]; - int done = 0; int devnum = usb_pipedevice(pipe); int ep = usb_pipeendpoint(pipe); - int max = usb_maxpacket(dev, pipe); - uint32_t hctsiz = 0, sub, tmp, ret; - uint32_t hcint; - const uint32_t hcint_comp_hlt_ack = DWC2_HCINT_XFERCOMP | - DWC2_HCINT_CHHLTD | DWC2_HCINT_ACK; - unsigned int timeout = 1000000; - - /* For CONTROL endpoint pid should start with DATA1 */ - int status_direction; if (devnum == root_hub_devnum) { dev->status = 0; - dev->speed = USB_SPEED_HIGH; - return dwc_otg_submit_rh_msg(dev, pipe, buffer, len, setup); - } - - if (len > DWC2_DATA_BUF_SIZE) { - printf("%s: %d is more then available buffer size(%d)\n", - __func__, len, DWC2_DATA_BUF_SIZE); - dev->status = 0; - dev->act_len = 0; return -EINVAL; } - /* Initialize channel, OUT for setup buffer */ - dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, devnum, ep, 0, - DWC2_HCCHAR_EPTYPE_CONTROL, max); - - /* SETUP stage */ - writel((8 << DWC2_HCTSIZ_XFERSIZE_OFFSET) | - (1 << DWC2_HCTSIZ_PKTCNT_OFFSET) | - (DWC2_HC_PID_SETUP << DWC2_HCTSIZ_PID_OFFSET), - &hc_regs->hctsiz); - - writel((uint32_t)setup, &hc_regs->hcdma); - - /* Set host channel enable after all other setup is complete. */ - clrsetbits_le32(&hc_regs->hcchar, DWC2_HCCHAR_MULTICNT_MASK | - DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS, - (1 << DWC2_HCCHAR_MULTICNT_OFFSET) | DWC2_HCCHAR_CHEN); - - ret = wait_for_bit(&hc_regs->hcint, DWC2_HCINT_CHHLTD, 1); - if (ret) - printf("%s: Timeout!\n", __func__); + return chunk_msg(dev, pipe, &bulk_data_toggle[devnum][ep], + usb_pipein(pipe), buffer, len, true); +} - hcint = readl(&hc_regs->hcint); +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len, struct devrequest *setup) +{ + int devnum = usb_pipedevice(pipe); + int pid, ret, act_len; + /* For CONTROL endpoint pid should start with DATA1 */ + int status_direction; - if (!(hcint & DWC2_HCINT_CHHLTD) || !(hcint & DWC2_HCINT_XFERCOMP)) { - printf("%s: Error (HCINT=%08x)\n", __func__, hcint); + if (devnum == root_hub_devnum) { dev->status = 0; - dev->act_len = 0; - return -EINVAL; + dev->speed = USB_SPEED_HIGH; + return dwc_otg_submit_rh_msg(dev, pipe, buffer, len, setup); } - /* Clear interrupts */ - writel(0, &hc_regs->hcintmsk); - writel(0xFFFFFFFF, &hc_regs->hcint); + pid = DWC2_HC_PID_SETUP; + ret = chunk_msg(dev, pipe, &pid, 0, setup, 8, true); + if (ret) + return ret; if (buffer) { - /* DATA stage */ - dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, devnum, ep, - usb_pipein(pipe), - DWC2_HCCHAR_EPTYPE_CONTROL, max); - - /* TODO: check if len < 64 */ - control_data_toggle[devnum][ep] = DWC2_HC_PID_DATA1; - writel((len << DWC2_HCTSIZ_XFERSIZE_OFFSET) | - (1 << DWC2_HCTSIZ_PKTCNT_OFFSET) | - (control_data_toggle[devnum][ep] << - DWC2_HCTSIZ_PID_OFFSET), - &hc_regs->hctsiz); - - writel((uint32_t)buffer, &hc_regs->hcdma); - - /* Set host channel enable after all other setup is complete */ - clrsetbits_le32(&hc_regs->hcchar, DWC2_HCCHAR_MULTICNT_MASK | - DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS, - (1 << DWC2_HCCHAR_MULTICNT_OFFSET) | - DWC2_HCCHAR_CHEN); - - while (1) { - hcint = readl(&hc_regs->hcint); - if (!(hcint & DWC2_HCINT_CHHLTD)) - continue; - - if (hcint & DWC2_HCINT_XFERCOMP) { - hctsiz = readl(&hc_regs->hctsiz); - done = len; - - sub = hctsiz & DWC2_HCTSIZ_XFERSIZE_MASK; - sub >>= DWC2_HCTSIZ_XFERSIZE_OFFSET; - - if (usb_pipein(pipe)) - done -= sub; - } - - if (hcint & DWC2_HCINT_ACK) { - tmp = hctsiz & DWC2_HCTSIZ_PID_MASK; - tmp >>= DWC2_HCTSIZ_PID_OFFSET; - if (tmp == DWC2_HC_PID_DATA0) { - control_data_toggle[devnum][ep] = - DWC2_HC_PID_DATA0; - } else { - control_data_toggle[devnum][ep] = - DWC2_HC_PID_DATA1; - } - } - - if (hcint != hcint_comp_hlt_ack) { - printf("%s: Error (HCINT=%08x)\n", - __func__, hcint); - goto out; - } - - if (!--timeout) { - printf("%s: Timeout!\n", __func__); - goto out; - } - - break; - } + pid = DWC2_HC_PID_DATA1; + ret = chunk_msg(dev, pipe, &pid, usb_pipein(pipe), buffer, + len, false); + if (ret) + return ret; + act_len = dev->act_len; } /* End of DATA stage */ + else + act_len = 0; /* STATUS stage */ if ((len == 0) || usb_pipeout(pipe)) @@ -966,42 +890,35 @@ int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, else status_direction = 0; - dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, devnum, ep, - status_direction, DWC2_HCCHAR_EPTYPE_CONTROL, max); - - writel((1 << DWC2_HCTSIZ_PKTCNT_OFFSET) | - (DWC2_HC_PID_DATA1 << DWC2_HCTSIZ_PID_OFFSET), - &hc_regs->hctsiz); - - writel((uint32_t)status_buffer, &hc_regs->hcdma); - - /* Set host channel enable after all other setup is complete. */ - clrsetbits_le32(&hc_regs->hcchar, DWC2_HCCHAR_MULTICNT_MASK | - DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS, - (1 << DWC2_HCCHAR_MULTICNT_OFFSET) | DWC2_HCCHAR_CHEN); - - while (1) { - hcint = readl(&hc_regs->hcint); - if (hcint & DWC2_HCINT_CHHLTD) - break; - } - - if (hcint != hcint_comp_hlt_ack) - printf("%s: Error (HCINT=%08x)\n", __func__, hcint); + pid = DWC2_HC_PID_DATA1; + ret = chunk_msg(dev, pipe, &pid, status_direction, status_buffer, 0, + false); + if (ret) + return ret; -out: - dev->act_len = done; - dev->status = 0; + dev->act_len = act_len; - return done; + return 0; } int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int len, int interval) { - printf("dev = %p pipe = %#lx buf = %p size = %d int = %d\n", - dev, pipe, buffer, len, interval); - return -ENOSYS; + unsigned long timeout; + int ret; + + /* FIXME: what is interval? */ + + timeout = get_timer(0) + USB_TIMEOUT_MS(pipe); + for (;;) { + if (get_timer(0) > timeout) { + printf("Timeout poll on interrupt endpoint\n"); + return -ETIMEDOUT; + } + ret = submit_bulk_msg(dev, pipe, buffer, len); + if (ret != -EAGAIN) + return ret; + } } /* U-Boot USB control interface */ @@ -1033,10 +950,8 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) DWC2_HPRT0_PRTRST); for (i = 0; i < MAX_DEVICE; i++) { - for (j = 0; j < MAX_ENDPOINT; j++) { - control_data_toggle[i][j] = DWC2_HC_PID_DATA1; + for (j = 0; j < MAX_ENDPOINT; j++) bulk_data_toggle[i][j] = DWC2_HC_PID_DATA0; - } } return 0; diff --git a/drivers/usb/host/dwc2.h b/drivers/usb/host/dwc2.h index ba08fd554f..45408c6f5c 100644 --- a/drivers/usb/host/dwc2.h +++ b/drivers/usb/host/dwc2.h @@ -536,6 +536,9 @@ struct dwc2_core_regs { #define DWC2_HPRT0_PRTPWR_OFFSET 12 #define DWC2_HPRT0_PRTTSTCTL_MASK (0xF << 13) #define DWC2_HPRT0_PRTTSTCTL_OFFSET 13 +#define DWC2_HPRT0_PRTSPD_HIGH (0 << 17) +#define DWC2_HPRT0_PRTSPD_FULL (1 << 17) +#define DWC2_HPRT0_PRTSPD_LOW (2 << 17) #define DWC2_HPRT0_PRTSPD_MASK (0x3 << 17) #define DWC2_HPRT0_PRTSPD_OFFSET 17 #define DWC2_HAINT_CH0 (1 << 0) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index f1fb190132..86f1646596 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -45,7 +45,7 @@ static struct ehci_ctrl ehcic[CONFIG_USB_MAX_CONTROLLER_COUNT]; #define ALIGN_END_ADDR(type, ptr, size) \ - ((uint32_t)(ptr) + roundup((size) * sizeof(type), USB_DMA_MINALIGN)) + ((unsigned long)(ptr) + roundup((size) * sizeof(type), USB_DMA_MINALIGN)) static struct descriptor { struct usb_hub_descriptor hub; @@ -223,7 +223,7 @@ static int ehci_shutdown(struct ehci_ctrl *ctrl) static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz) { uint32_t delta, next; - uint32_t addr = (uint32_t)buf; + uint32_t addr = (unsigned long)buf; int idx; if (addr != ALIGN(addr, ARCH_DMA_MINALIGN)) @@ -245,7 +245,7 @@ static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz) } if (idx == QT_BUFFER_CNT) { - printf("out of buffer pointers (%u bytes left)\n", sz); + printf("out of buffer pointers (%zu bytes left)\n", sz); return -1; } @@ -354,7 +354,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, * qTD transfer size will be one page shorter, and the first qTD * data buffer of each transfer will be page-unaligned. */ - if ((uint32_t)buffer & (PKT_ALIGN - 1)) + if ((unsigned long)buffer & (PKT_ALIGN - 1)) xfr_sz--; /* Convert the qTD transfer size to bytes. */ xfr_sz *= EHCI_PAGE_SIZE; @@ -394,7 +394,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, * qh_overlay.qt_next ...... 13-10 H * - qh_overlay.qt_altnext */ - qh->qh_link = cpu_to_hc32((uint32_t)&ctrl->qh_list | QH_LINK_TYPE_QH); + qh->qh_link = cpu_to_hc32((unsigned long)&ctrl->qh_list | QH_LINK_TYPE_QH); c = (dev->speed != USB_SPEED_HIGH) && !usb_pipeendpoint(pipe); maxpacket = usb_maxpacket(dev, pipe); endpt = QH_ENDPT1_RL(8) | QH_ENDPT1_C(c) | @@ -434,7 +434,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, goto fail; } /* Update previous qTD! */ - *tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]); + *tdp = cpu_to_hc32((unsigned long)&qtd[qtd_counter]); tdp = &qtd[qtd_counter++].qt_next; toggle = 1; } @@ -454,7 +454,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, * portion of the first page before the buffer start * offset within that page is unusable. */ - xfr_bytes -= (uint32_t)buf_ptr & (EHCI_PAGE_SIZE - 1); + xfr_bytes -= (unsigned long)buf_ptr & (EHCI_PAGE_SIZE - 1); /* * In order to keep each packet within a qTD transfer, * align the qTD transfer size to PKT_ALIGN. @@ -493,7 +493,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, goto fail; } /* Update previous qTD! */ - *tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]); + *tdp = cpu_to_hc32((unsigned long)&qtd[qtd_counter]); tdp = &qtd[qtd_counter++].qt_next; /* * Data toggle has to be adjusted since the qTD transfer @@ -524,21 +524,21 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE); qtd[qtd_counter].qt_token = cpu_to_hc32(token); /* Update previous qTD! */ - *tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]); + *tdp = cpu_to_hc32((unsigned long)&qtd[qtd_counter]); tdp = &qtd[qtd_counter++].qt_next; } - ctrl->qh_list.qh_link = cpu_to_hc32((uint32_t)qh | QH_LINK_TYPE_QH); + ctrl->qh_list.qh_link = cpu_to_hc32((unsigned long)qh | QH_LINK_TYPE_QH); /* Flush dcache */ - flush_dcache_range((uint32_t)&ctrl->qh_list, + flush_dcache_range((unsigned long)&ctrl->qh_list, ALIGN_END_ADDR(struct QH, &ctrl->qh_list, 1)); - flush_dcache_range((uint32_t)qh, ALIGN_END_ADDR(struct QH, qh, 1)); - flush_dcache_range((uint32_t)qtd, + flush_dcache_range((unsigned long)qh, ALIGN_END_ADDR(struct QH, qh, 1)); + flush_dcache_range((unsigned long)qtd, ALIGN_END_ADDR(struct qTD, qtd, qtd_count)); /* Set async. queue head pointer. */ - ehci_writel(&ctrl->hcor->or_asynclistaddr, (uint32_t)&ctrl->qh_list); + ehci_writel(&ctrl->hcor->or_asynclistaddr, (unsigned long)&ctrl->qh_list); usbsts = ehci_readl(&ctrl->hcor->or_usbsts); ehci_writel(&ctrl->hcor->or_usbsts, (usbsts & 0x3f)); @@ -561,11 +561,11 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, timeout = USB_TIMEOUT_MS(pipe); do { /* Invalidate dcache */ - invalidate_dcache_range((uint32_t)&ctrl->qh_list, + invalidate_dcache_range((unsigned long)&ctrl->qh_list, ALIGN_END_ADDR(struct QH, &ctrl->qh_list, 1)); - invalidate_dcache_range((uint32_t)qh, + invalidate_dcache_range((unsigned long)qh, ALIGN_END_ADDR(struct QH, qh, 1)); - invalidate_dcache_range((uint32_t)qtd, + invalidate_dcache_range((unsigned long)qtd, ALIGN_END_ADDR(struct qTD, qtd, qtd_count)); token = hc32_to_cpu(vtd->qt_token); @@ -583,8 +583,8 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, * dangerous operation, it's responsibility of the calling * code to make sure enough space is reserved. */ - invalidate_dcache_range((uint32_t)buffer, - ALIGN((uint32_t)buffer + length, ARCH_DMA_MINALIGN)); + invalidate_dcache_range((unsigned long)buffer, + ALIGN((unsigned long)buffer + length, ARCH_DMA_MINALIGN)); /* Check that the TD processing happened */ if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE) @@ -968,7 +968,7 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) /* Set head of reclaim list */ memset(qh_list, 0, sizeof(*qh_list)); - qh_list->qh_link = cpu_to_hc32((uint32_t)qh_list | QH_LINK_TYPE_QH); + qh_list->qh_link = cpu_to_hc32((unsigned long)qh_list | QH_LINK_TYPE_QH); qh_list->qh_endpt1 = cpu_to_hc32(QH_ENDPT1_H(1) | QH_ENDPT1_EPS(USB_SPEED_HIGH)); qh_list->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); @@ -976,11 +976,11 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) qh_list->qh_overlay.qt_token = cpu_to_hc32(QT_TOKEN_STATUS(QT_TOKEN_STATUS_HALTED)); - flush_dcache_range((uint32_t)qh_list, + flush_dcache_range((unsigned long)qh_list, ALIGN_END_ADDR(struct QH, qh_list, 1)); /* Set async. queue head pointer. */ - ehci_writel(&ehcic[index].hcor->or_asynclistaddr, (uint32_t)qh_list); + ehci_writel(&ehcic[index].hcor->or_asynclistaddr, (unsigned long)qh_list); /* * Set up periodic list @@ -993,7 +993,7 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) periodic->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); periodic->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); - flush_dcache_range((uint32_t)periodic, + flush_dcache_range((unsigned long)periodic, ALIGN_END_ADDR(struct QH, periodic, 1)); /* @@ -1011,17 +1011,17 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) if (!ehcic[index].periodic_list) return -ENOMEM; for (i = 0; i < 1024; i++) { - ehcic[index].periodic_list[i] = cpu_to_hc32((uint32_t)periodic + ehcic[index].periodic_list[i] = cpu_to_hc32((unsigned long)periodic | QH_LINK_TYPE_QH); } - flush_dcache_range((uint32_t)ehcic[index].periodic_list, + flush_dcache_range((unsigned long)ehcic[index].periodic_list, ALIGN_END_ADDR(uint32_t, ehcic[index].periodic_list, 1024)); /* Set periodic list base address */ ehci_writel(&ehcic[index].hcor->or_periodiclistbase, - (uint32_t)ehcic[index].periodic_list); + (unsigned long)ehcic[index].periodic_list); reg = ehci_readl(&ehcic[index].hccr->cr_hcsparams); descriptor.hub.bNbrPorts = HCS_N_PORTS(reg); @@ -1103,7 +1103,7 @@ struct int_queue { struct qTD *tds; }; -#define NEXT_QH(qh) (struct QH *)(hc32_to_cpu((qh)->qh_link) & ~0x1f) +#define NEXT_QH(qh) (struct QH *)((unsigned long)hc32_to_cpu((qh)->qh_link) & ~0x1f) static int enable_periodic(struct ehci_ctrl *ctrl) @@ -1214,11 +1214,11 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, struct qTD *td = result->tds + i; void **buf = &qh->buffer; - qh->qh_link = cpu_to_hc32((uint32_t)(qh+1) | QH_LINK_TYPE_QH); + qh->qh_link = cpu_to_hc32((unsigned long)(qh+1) | QH_LINK_TYPE_QH); if (i == queuesize - 1) qh->qh_link = cpu_to_hc32(QH_LINK_TERMINATE); - qh->qh_overlay.qt_next = cpu_to_hc32((uint32_t)td); + qh->qh_overlay.qt_next = cpu_to_hc32((unsigned long)td); qh->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); qh->qh_endpt1 = cpu_to_hc32((0 << 28) | /* No NAK reload (ehci 4.9) */ @@ -1244,7 +1244,7 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, ((usb_pipein(pipe) ? 1 : 0) << 8) | /* IN/OUT token */ 0x80); /* active */ td->qt_buffer[0] = - cpu_to_hc32((uint32_t)buffer + i * elementsize); + cpu_to_hc32((unsigned long)buffer + i * elementsize); td->qt_buffer[1] = cpu_to_hc32((td->qt_buffer[0] + 0x1000) & ~0xfff); td->qt_buffer[2] = @@ -1257,13 +1257,13 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, *buf = buffer + i * elementsize; } - flush_dcache_range((uint32_t)buffer, + flush_dcache_range((unsigned long)buffer, ALIGN_END_ADDR(char, buffer, queuesize * elementsize)); - flush_dcache_range((uint32_t)result->first, + flush_dcache_range((unsigned long)result->first, ALIGN_END_ADDR(struct QH, result->first, queuesize)); - flush_dcache_range((uint32_t)result->tds, + flush_dcache_range((unsigned long)result->tds, ALIGN_END_ADDR(struct qTD, result->tds, queuesize)); @@ -1277,11 +1277,11 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, /* hook up to periodic list */ struct QH *list = &ctrl->periodic_queue; result->last->qh_link = list->qh_link; - list->qh_link = cpu_to_hc32((uint32_t)result->first | QH_LINK_TYPE_QH); + list->qh_link = cpu_to_hc32((unsigned long)result->first | QH_LINK_TYPE_QH); - flush_dcache_range((uint32_t)result->last, + flush_dcache_range((unsigned long)result->last, ALIGN_END_ADDR(struct QH, result->last, 1)); - flush_dcache_range((uint32_t)list, + flush_dcache_range((unsigned long)list, ALIGN_END_ADDR(struct QH, list, 1)); if (enable_periodic(ctrl) < 0) { @@ -1316,7 +1316,7 @@ void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) } /* still active */ cur_td = &queue->tds[queue->current - queue->first]; - invalidate_dcache_range((uint32_t)cur_td, + invalidate_dcache_range((unsigned long)cur_td, ALIGN_END_ADDR(struct qTD, cur_td, 1)); if (QT_TOKEN_GET_STATUS(hc32_to_cpu(cur_td->qt_token)) & QT_TOKEN_STATUS_ACTIVE) { @@ -1329,7 +1329,7 @@ void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) else queue->current = NULL; - invalidate_dcache_range((uint32_t)cur->buffer, + invalidate_dcache_range((unsigned long)cur->buffer, ALIGN_END_ADDR(char, cur->buffer, queue->elementsize)); @@ -1359,7 +1359,7 @@ destroy_int_queue(struct usb_device *dev, struct int_queue *queue) if (NEXT_QH(cur) == queue->first) { debug("found candidate. removing from chain\n"); cur->qh_link = queue->last->qh_link; - flush_dcache_range((uint32_t)cur, + flush_dcache_range((unsigned long)cur, ALIGN_END_ADDR(struct QH, cur, 1)); result = 0; break; @@ -1411,8 +1411,8 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, } if (backbuffer != buffer) { - debug("got wrong buffer back (%x instead of %x)\n", - (uint32_t)backbuffer, (uint32_t)buffer); + debug("got wrong buffer back (%p instead of %p)\n", + backbuffer, buffer); return -EINVAL; } diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index b5ad1e35e5..c6bfbe3999 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -196,7 +196,7 @@ void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg) if (controller->has_hostpc) *reg |= EHCI_PS_PE; - if (((u32)status_reg & TEGRA_USB_ADDR_MASK) != port_addr_clear_csc) + if (((unsigned long)status_reg & TEGRA_USB_ADDR_MASK) != port_addr_clear_csc) return; /* For EHCI_PS_CSC to be cleared in ehci_hcd.c */ if (ehci_readl(status_reg) & EHCI_PS_CSC) @@ -434,7 +434,7 @@ static int init_utmi_usb_controller(struct fdt_usb *config, reset_set_enable(PERIPH_ID_USBD, 0); } usb1ctlr = (struct usb_ctlr *) - ((u32)config->reg & USB1_ADDR_MASK); + ((unsigned long)config->reg & USB1_ADDR_MASK); val = readl(&usb1ctlr->utmip_bias_cfg0); setbits_le32(&val, UTMIP_HSDISCON_LEVEL_MSB); clrsetbits_le32(&val, UTMIP_HSDISCON_LEVEL_MASK, @@ -537,7 +537,7 @@ static int init_utmi_usb_controller(struct fdt_usb *config, * controllers and can be controlled from USB1 only. */ usb1ctlr = (struct usb_ctlr *) - ((u32)config->reg & USB1_ADDR_MASK); + ((unsigned long)config->reg & USB1_ADDR_MASK); clrbits_le32(&usb1ctlr->utmip_bias_cfg0, UTMIP_BIASPD); udelay(25); clrbits_le32(&usb1ctlr->utmip_bias_cfg1, @@ -675,7 +675,7 @@ static int fdt_decode_usb(const void *blob, int node, struct fdt_usb *config) config->has_legacy_mode = fdtdec_get_bool(blob, node, "nvidia,has-legacy-mode"); if (config->has_legacy_mode) - port_addr_clear_csc = (u32) config->reg; + port_addr_clear_csc = (unsigned long)config->reg; config->periph_id = clock_decode_periph_id(blob, node); if (config->periph_id == PERIPH_ID_NONE) { debug("%s: Missing/invalid peripheral ID\n", __func__); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 89908e8a80..10f11cd547 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -31,7 +31,7 @@ * @param len the length of the cache line to be flushed * @return none */ -void xhci_flush_cache(uint32_t addr, u32 len) +void xhci_flush_cache(uintptr_t addr, u32 len) { BUG_ON((void *)addr == NULL || len == 0); @@ -46,7 +46,7 @@ void xhci_flush_cache(uint32_t addr, u32 len) * @param len the length of the cache line to be invalidated * @return none */ -void xhci_inval_cache(uint32_t addr, u32 len) +void xhci_inval_cache(uintptr_t addr, u32 len) { BUG_ON((void *)addr == NULL || len == 0); @@ -175,7 +175,7 @@ static void *xhci_malloc(unsigned int size) BUG_ON(!ptr); memset(ptr, '\0', size); - xhci_flush_cache((uint32_t)ptr, size); + xhci_flush_cache((uintptr_t)ptr, size); return ptr; } @@ -400,8 +400,8 @@ int xhci_alloc_virt_device(struct usb_device *udev) /* 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)); + xhci_flush_cache((uintptr_t)&ctrl->dcbaa->dev_context_ptrs[slot_id], + sizeof(__le64)); return 0; } @@ -478,8 +478,8 @@ int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr, entry->rsvd = 0; seg = seg->next; } - xhci_flush_cache((uint32_t)ctrl->erst.entries, - ERST_NUM_SEGS * sizeof(struct xhci_erst_entry)); + xhci_flush_cache((uintptr_t)ctrl->erst.entries, + ERST_NUM_SEGS * sizeof(struct xhci_erst_entry)); deq = (unsigned long)ctrl->event_ring->dequeue; @@ -496,7 +496,7 @@ int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr, /* 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); + val_64 |= ((uintptr_t)(ctrl->erst.entries) & ~ERST_PTR_MASK); xhci_writeq(&ctrl->ir_set->erst_base, val_64); @@ -715,6 +715,6 @@ void xhci_setup_addressable_virt_dev(struct usb_device *udev) /* 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)); + xhci_flush_cache((uintptr_t)ep0_ctx, sizeof(struct xhci_ep_ctx)); + xhci_flush_cache((uintptr_t)slot_ctx, sizeof(struct xhci_slot_ctx)); } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index b5aade988d..f3759d4036 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -122,8 +122,8 @@ static void inc_enq(struct xhci_ctrl *ctrl, struct xhci_ring *ring, 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)); + xhci_flush_cache((uintptr_t)next, + sizeof(union xhci_trb)); } /* Toggle the cycle bit after the last ring segment. */ if (last_trb_on_last_seg(ctrl, ring, @@ -191,7 +191,7 @@ static struct xhci_generic_trb *queue_trb(struct xhci_ctrl *ctrl, 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)); + xhci_flush_cache((uintptr_t)trb, sizeof(struct xhci_generic_trb)); inc_enq(ctrl, ring, more_trbs_coming); @@ -244,7 +244,7 @@ static int prepare_ring(struct xhci_ctrl *ctrl, struct xhci_ring *ep_ring, next->link.control ^= cpu_to_le32(TRB_CYCLE); - xhci_flush_cache((uint32_t)next, sizeof(union xhci_trb)); + xhci_flush_cache((uintptr_t)next, sizeof(union xhci_trb)); /* Toggle the cycle bit after the last ring segment. */ if (last_trb_on_last_seg(ctrl, ep_ring, @@ -364,7 +364,7 @@ static void giveback_first_trb(struct usb_device *udev, int ep_index, else start_trb->field[3] &= cpu_to_le32(~TRB_CYCLE); - xhci_flush_cache((uint32_t)start_trb, sizeof(struct xhci_generic_trb)); + xhci_flush_cache((uintptr_t)start_trb, sizeof(struct xhci_generic_trb)); /* Ringing EP doorbell here */ xhci_writel(&ctrl->dba->doorbell[udev->slot_id], @@ -403,8 +403,8 @@ 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)); + xhci_inval_cache((uintptr_t)ctrl->event_ring->dequeue, + sizeof(union xhci_trb)); event = ctrl->event_ring->dequeue; @@ -576,8 +576,8 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, 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); + xhci_inval_cache((uintptr_t)virt_dev->out_ctx->bytes, + virt_dev->out_ctx->size); ep_ctx = xhci_get_ep_ctx(ctrl, virt_dev->out_ctx, ep_index); @@ -644,7 +644,7 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, first_trb = true; /* flush the buffer before use */ - xhci_flush_cache((uint32_t)buffer, length); + xhci_flush_cache((uintptr_t)buffer, length); /* Queue the first TRB, even if it's zero-length */ do { @@ -722,7 +722,7 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, record_transfer_result(udev, event, length); xhci_acknowledge_event(ctrl); - xhci_inval_cache((uint32_t)buffer, length); + xhci_inval_cache((uintptr_t)buffer, length); return (udev->status != USB_ST_NOT_PROC) ? 0 : -1; } @@ -776,8 +776,8 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, return ret; } - xhci_inval_cache((uint32_t)virt_dev->out_ctx->bytes, - virt_dev->out_ctx->size); + xhci_inval_cache((uintptr_t)virt_dev->out_ctx->bytes, + virt_dev->out_ctx->size); struct xhci_ep_ctx *ep_ctx = NULL; ep_ctx = xhci_get_ep_ctx(ctrl, virt_dev->out_ctx, ep_index); @@ -874,7 +874,7 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, trb_fields[2] = length_field; trb_fields[3] = field | ep_ring->cycle_state; - xhci_flush_cache((uint32_t)buffer, length); + xhci_flush_cache((uintptr_t)buffer, length); queue_trb(ctrl, ep_ring, true, trb_fields); } @@ -915,7 +915,7 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, /* Invalidate buffer to make it available to usb-core */ if (length > 0) - xhci_inval_cache((uint32_t)buffer, length); + xhci_inval_cache((uintptr_t)buffer, length); if (GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len)) == COMP_SHORT_TX) { diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 87f2972cb2..f8b5ce4c36 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -256,7 +256,7 @@ static int xhci_configure_endpoints(struct usb_device *udev, bool ctx_change) virt_dev = ctrl->devs[udev->slot_id]; in_ctx = virt_dev->in_ctx; - xhci_flush_cache((uint32_t)in_ctx->bytes, in_ctx->size); + xhci_flush_cache((uintptr_t)in_ctx->bytes, in_ctx->size); xhci_queue_command(ctrl, in_ctx->bytes, udev->slot_id, 0, ctx_change ? TRB_EVAL_CONTEXT : TRB_CONFIG_EP); event = xhci_wait_for_event(ctrl, TRB_COMPLETION); @@ -325,7 +325,7 @@ static int xhci_set_configuration(struct usb_device *udev) max_ep_flag = ep_flag; } - xhci_inval_cache((uint32_t)out_ctx->bytes, out_ctx->size); + xhci_inval_cache((uintptr_t)out_ctx->bytes, out_ctx->size); /* slot context */ xhci_slot_copy(ctrl, in_ctx, out_ctx); @@ -442,8 +442,8 @@ static int xhci_address_device(struct usb_device *udev) */ return ret; - xhci_inval_cache((uint32_t)virt_dev->out_ctx->bytes, - virt_dev->out_ctx->size); + xhci_inval_cache((uintptr_t)virt_dev->out_ctx->bytes, + virt_dev->out_ctx->size); slot_ctx = xhci_get_slot_ctx(ctrl, virt_dev->out_ctx); debug("xHC internal address is: %d\n", @@ -525,7 +525,7 @@ int xhci_check_maxpacket(struct usb_device *udev) ifdesc = &udev->config.if_desc[0]; out_ctx = ctrl->devs[slot_id]->out_ctx; - xhci_inval_cache((uint32_t)out_ctx->bytes, out_ctx->size); + xhci_inval_cache((uintptr_t)out_ctx->bytes, out_ctx->size); ep_ctx = xhci_get_ep_ctx(ctrl, out_ctx, ep_index); hw_max_packet_size = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2)); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 6685ed23de..0951e87436 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -17,6 +17,7 @@ #ifndef HOST_XHCI_H_ #define HOST_XHCI_H_ +#include <asm/types.h> #include <asm/cache.h> #include <asm/io.h> #include <linux/list.h> @@ -1108,20 +1109,28 @@ static inline void xhci_writel(uint32_t volatile *regs, const unsigned int val) */ static inline u64 xhci_readq(__le64 volatile *regs) { +#if BITS_PER_LONG == 64 + return readq(regs); +#else __u32 *ptr = (__u32 *)regs; u64 val_lo = readl(ptr); u64 val_hi = readl(ptr + 1); return val_lo + (val_hi << 32); +#endif } static inline void xhci_writeq(__le64 volatile *regs, const u64 val) { +#if BITS_PER_LONG == 64 + writeq(val, regs); +#else __u32 *ptr = (__u32 *)regs; u32 val_lo = lower_32_bits(val); /* FIXME */ u32 val_hi = upper_32_bits(val); writel(val_lo, ptr); writel(val_hi, ptr + 1); +#endif } int xhci_hcd_init(int index, struct xhci_hccr **ret_hccr, @@ -1242,8 +1251,8 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, struct devrequest *req, int length, void *buffer); int xhci_check_maxpacket(struct usb_device *udev); -void xhci_flush_cache(uint32_t addr, u32 type_len); -void xhci_inval_cache(uint32_t addr, u32 type_len); +void xhci_flush_cache(uintptr_t addr, u32 type_len); +void xhci_inval_cache(uintptr_t addr, u32 type_len); void xhci_cleanup(struct xhci_ctrl *ctrl); struct xhci_ring *xhci_ring_alloc(unsigned int num_segs, bool link_trbs); int xhci_alloc_virt_device(struct usb_device *udev); diff --git a/drivers/usb/musb-new/musb_uboot.c b/drivers/usb/musb-new/musb_uboot.c index 51fb3fd7e2..053d94560d 100644 --- a/drivers/usb/musb-new/musb_uboot.c +++ b/drivers/usb/musb-new/musb_uboot.c @@ -252,7 +252,7 @@ int usb_lowlevel_stop(int index) #ifdef CONFIG_MUSB_GADGET static struct musb *gadget; -int usb_gadget_handle_interrupts(void) +int usb_gadget_handle_interrupts(int index) { WATCHDOG_RESET(); if (!gadget || !gadget->isr) diff --git a/include/configs/am43xx_evm.h b/include/configs/am43xx_evm.h index 7ccbf36b0b..6eb31e2120 100644 --- a/include/configs/am43xx_evm.h +++ b/include/configs/am43xx_evm.h @@ -109,6 +109,61 @@ #define CONFIG_OMAP_USB_PHY #define CONFIG_AM437X_USB2PHY2_HOST +/* USB GADGET */ +#if !defined(CONFIG_SPL_BUILD) || \ + (defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_USBETH_SUPPORT)) +#define CONFIG_USB_DWC3_PHY_OMAP +#define CONFIG_USB_DWC3_OMAP +#define CONFIG_USB_DWC3 +#define CONFIG_USB_DWC3_GADGET + +#define CONFIG_USB_GADGET +#define CONFIG_USBDOWNLOAD_GADGET +#define CONFIG_USB_GADGET_VBUS_DRAW 2 +#define CONFIG_G_DNL_MANUFACTURER "Texas Instruments" +#define CONFIG_G_DNL_VENDOR_NUM 0x0403 +#define CONFIG_G_DNL_PRODUCT_NUM 0xBD00 +#define CONFIG_USB_GADGET_DUALSPEED +#endif + +#ifndef CONFIG_SPL_BUILD +/* USB Device Firmware Update support */ +#define CONFIG_DFU_FUNCTION +#define CONFIG_DFU_RAM +#define CONFIG_CMD_DFU + +#define CONFIG_DFU_MMC +#define DFU_ALT_INFO_MMC \ + "dfu_alt_info_mmc=" \ + "boot part 0 1;" \ + "rootfs part 0 2;" \ + "MLO fat 0 1;" \ + "spl-os-args fat 0 1;" \ + "spl-os-image fat 0 1;" \ + "u-boot.img fat 0 1;" \ + "uEnv.txt fat 0 1\0" + +#define DFU_ALT_INFO_EMMC \ + "dfu_alt_info_emmc=" \ + "MLO raw 0x100 0x100 mmcpart 0;" \ + "u-boot.img raw 0x300 0x1000 mmcpart 0\0" + +#define CONFIG_DFU_RAM +#define DFU_ALT_INFO_RAM \ + "dfu_alt_info_ram=" \ + "kernel ram 0x80200000 0x4000000;" \ + "fdt ram 0x80f80000 0x80000;" \ + "ramdisk ram 0x81000000 0x4000000\0" + +#define DFUARGS \ + "dfu_bufsiz=0x10000\0" \ + DFU_ALT_INFO_MMC \ + DFU_ALT_INFO_EMMC \ + DFU_ALT_INFO_RAM +#else +#define DFUARGS +#endif + #ifdef CONFIG_QSPI_BOOT #define CONFIG_SYS_TEXT_BASE 0x30000000 #undef CONFIG_ENV_IS_IN_FAT @@ -239,7 +294,8 @@ "if test $board_name = AM43_IDK; then " \ "setenv fdtfile am437x-idk-evm.dtb; fi; " \ "if test $fdtfile = undefined; then " \ - "echo WARNING: Could not determine device tree; fi; \0" + "echo WARNING: Could not determine device tree; fi; \0" \ + DFUARGS \ #define CONFIG_BOOTCOMMAND \ "run findfdt; " \ diff --git a/include/configs/dra7xx_evm.h b/include/configs/dra7xx_evm.h index 8fe0e6c16a..e78cc69af6 100644 --- a/include/configs/dra7xx_evm.h +++ b/include/configs/dra7xx_evm.h @@ -43,6 +43,47 @@ "uuid_disk=${uuid_gpt_disk};" \ "name=rootfs,start=2MiB,size=-,uuid=${uuid_gpt_rootfs}" +#define DFU_ALT_INFO_MMC \ + "dfu_alt_info_mmc=" \ + "boot part 0 1;" \ + "rootfs part 0 2;" \ + "MLO fat 0 1;" \ + "MLO.raw raw 0x100 0x100;" \ + "u-boot.img.raw raw 0x300 0x400;" \ + "spl-os-args.raw raw 0x80 0x80;" \ + "spl-os-image.raw raw 0x900 0x2000;" \ + "spl-os-args fat 0 1;" \ + "spl-os-image fat 0 1;" \ + "u-boot.img fat 0 1;" \ + "uEnv.txt fat 0 1\0" + +#define DFU_ALT_INFO_EMMC \ + "dfu_alt_info_emmc=" \ + "rawemmc raw 0 3751936;" \ + "boot part 1 1;" \ + "rootfs part 1 2;" \ + "MLO fat 1 1;" \ + "MLO.raw raw 0x100 0x100;" \ + "u-boot.img.raw raw 0x300 0x400;" \ + "spl-os-args.raw raw 0x80 0x80;" \ + "spl-os-image.raw raw 0x900 0x2000;" \ + "spl-os-args fat 1 1;" \ + "spl-os-image fat 1 1;" \ + "u-boot.img fat 1 1;" \ + "uEnv.txt fat 1 1\0" + +#define DFU_ALT_INFO_RAM \ + "dfu_alt_info_ram=" \ + "kernel ram 0x80200000 0x4000000;" \ + "fdt ram 0x80f80000 0x80000;" \ + "ramdisk ram 0x81000000 0x4000000\0" + +#define DFUARGS \ + "dfu_bufsiz=0x10000\0" \ + DFU_ALT_INFO_MMC \ + DFU_ALT_INFO_EMMC \ + DFU_ALT_INFO_RAM + #include <configs/ti_omap5_common.h> /* Enhance our eMMC support / experience. */ @@ -131,6 +172,28 @@ #define CONFIG_OMAP_USB_PHY #define CONFIG_OMAP_USB2PHY2_HOST +/* USB GADGET */ +#define CONFIG_USB_DWC3_PHY_OMAP +#define CONFIG_USB_DWC3_OMAP +#define CONFIG_USB_DWC3 +#define CONFIG_USB_DWC3_GADGET + +#define CONFIG_USB_GADGET +#define CONFIG_USBDOWNLOAD_GADGET +#define CONFIG_USB_GADGET_VBUS_DRAW 2 +#define CONFIG_G_DNL_MANUFACTURER "Texas Instruments" +#define CONFIG_G_DNL_VENDOR_NUM 0x0403 +#define CONFIG_G_DNL_PRODUCT_NUM 0xBD00 +#define CONFIG_USB_GADGET_DUALSPEED + +/* USB Device Firmware Update support */ +#define CONFIG_DFU_FUNCTION +#define CONFIG_DFU_RAM +#define CONFIG_CMD_DFU + +#define CONFIG_DFU_MMC +#define CONFIG_DFU_RAM + /* SATA */ #define CONFIG_BOARD_LATE_INIT #define CONFIG_CMD_SCSI diff --git a/include/configs/ti_omap5_common.h b/include/configs/ti_omap5_common.h index 09f05f18a7..7957a73384 100644 --- a/include/configs/ti_omap5_common.h +++ b/include/configs/ti_omap5_common.h @@ -68,6 +68,10 @@ #define PARTS_DEFAULT #endif +#ifndef DFUARGS +#define DFUARGS +#endif + #define CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG #define CONFIG_EXTRA_ENV_SETTINGS \ DEFAULT_LINUX_BOOT_ENV \ @@ -138,6 +142,7 @@ "if test $fdtfile = undefined; then " \ "echo WARNING: Could not determine device tree to use; fi; \0" \ "loadfdt=load mmc ${bootpart} ${fdtaddr} ${bootdir}/${fdtfile};\0" \ + DFUARGS \ #define CONFIG_BOOTCOMMAND \ "run findfdt; " \ diff --git a/include/dwc3-omap-uboot.h b/include/dwc3-omap-uboot.h new file mode 100644 index 0000000000..db002b9b6a --- /dev/null +++ b/include/dwc3-omap-uboot.h @@ -0,0 +1,32 @@ +/* include/dwc3_omap_uboot.h + * + * Copyright (c) 2015 Texas Instruments Incorporated - http://www.ti.com + * + * Designware SuperSpeed OMAP Glue uboot init + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __DWC3_OMAP_UBOOT_H_ +#define __DWC3_OMAP_UBOOT_H_ + +#include <linux/usb/dwc3-omap.h> + +enum omap_dwc3_vbus_id_status { + OMAP_DWC3_ID_FLOAT, + OMAP_DWC3_ID_GROUND, + OMAP_DWC3_VBUS_OFF, + OMAP_DWC3_VBUS_VALID, +}; + +struct dwc3_omap_device { + void *base; + int index; + enum dwc3_omap_utmi_mode utmi_mode; + enum omap_dwc3_vbus_id_status vbus_id_status; +}; + +int dwc3_omap_uboot_init(struct dwc3_omap_device *dev); +void dwc3_omap_uboot_exit(int index); +int dwc3_omap_uboot_interrupt_status(int index); +#endif /* __DWC3_OMAP_UBOOT_H_ */ diff --git a/include/dwc3-uboot.h b/include/dwc3-uboot.h new file mode 100644 index 0000000000..09ff8a74d6 --- /dev/null +++ b/include/dwc3-uboot.h @@ -0,0 +1,42 @@ +/* include/dwc3-uboot.h + * + * Copyright (c) 2015 Texas Instruments Incorporated - http://www.ti.com + * + * Designware SuperSpeed USB uboot init + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __DWC3_UBOOT_H_ +#define __DWC3_UBOOT_H_ + +#include <linux/usb/otg.h> + +struct dwc3_device { + int base; + enum usb_dr_mode dr_mode; + u32 maximum_speed; + unsigned tx_fifo_resize:1; + unsigned has_lpm_erratum; + u8 lpm_nyet_threshold; + unsigned is_utmi_l1_suspend; + u8 hird_threshold; + unsigned disable_scramble_quirk; + unsigned u2exit_lfps_quirk; + unsigned u2ss_inp3_quirk; + unsigned req_p1p2p3_quirk; + unsigned del_p1p2p3_quirk; + unsigned del_phy_power_chg_quirk; + unsigned lfps_filter_quirk; + unsigned rx_detect_poll_quirk; + unsigned dis_u3_susphy_quirk; + unsigned dis_u2_susphy_quirk; + unsigned tx_de_emphasis_quirk; + unsigned tx_de_emphasis; + int index; +}; + +int dwc3_uboot_init(struct dwc3_device *dev); +void dwc3_uboot_exit(int index); +void dwc3_uboot_handle_interrupt(int index); +#endif /* __DWC3_UBOOT_H_ */ diff --git a/include/linux/compat.h b/include/linux/compat.h index 6eac17f0b6..6ff3915216 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -326,6 +326,7 @@ typedef unsigned long dmaaddr_t; #define IRQ_NONE 0 #define IRQ_HANDLED 1 +#define IRQ_WAKE_THREAD 2 #define dev_set_drvdata(dev, data) do {} while (0) diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index f833d10060..86e1ceac3c 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -27,6 +27,15 @@ #include <linux/usb/gadget.h> #include <usb/lin_gadget_compat.h> +/* + * USB function drivers should return USB_GADGET_DELAYED_STATUS if they + * wish to delay the data/status stages of the control transfer till they + * are ready. The control transfer will then be kept from completing till + * all the function drivers that requested for USB_GADGET_DELAYED_STAUS + * invoke usb_composite_setup_continue(). + */ +#define USB_GADGET_DELAYED_STATUS 0x7fff /* Impossibly large value */ + struct usb_configuration; /** diff --git a/include/linux/usb/dwc3-omap.h b/include/linux/usb/dwc3-omap.h new file mode 100644 index 0000000000..8bf7b55d87 --- /dev/null +++ b/include/linux/usb/dwc3-omap.h @@ -0,0 +1,19 @@ +/* include/linux/usb/dwc3-omap.h + * + * Copyright (c) 2014 Texas Instruments Incorporated - http://www.ti.com + * + * Designware SuperSpeed Glue + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __DWC3_OMAP_H_ +#define __DWC3_OMAP_H_ + +enum dwc3_omap_utmi_mode { + DWC3_OMAP_UTMI_MODE_UNKNOWN = 0, + DWC3_OMAP_UTMI_MODE_HW, + DWC3_OMAP_UTMI_MODE_SW, +}; + +#endif /* __DWC3_OMAP_H_ */ diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 9bccd451af..230f47d67e 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -31,6 +31,7 @@ struct usb_ep; * @dma: DMA address corresponding to 'buf'. If you don't set this * field, and the usb controller needs one, it is responsible * for mapping and unmapping the buffer. + * @stream_id: The stream id, when USB3.0 bulk streams are being used * @length: Length of that data * @no_interrupt: If true, hints that no completion irq is needed. * Helpful sometimes with deep request queues that are handled @@ -85,6 +86,7 @@ struct usb_request { unsigned length; dma_addr_t dma; + unsigned stream_id:16; unsigned no_interrupt:1; unsigned zero:1; unsigned short_not_ok:1; @@ -121,6 +123,7 @@ struct usb_ep_ops { int (*dequeue) (struct usb_ep *ep, struct usb_request *req); int (*set_halt) (struct usb_ep *ep, int value); + int (*set_wedge)(struct usb_ep *ep); int (*fifo_status) (struct usb_ep *ep); void (*fifo_flush) (struct usb_ep *ep); }; @@ -133,8 +136,18 @@ struct usb_ep_ops { * @maxpacket:The maximum packet size used on this endpoint. The initial * value can sometimes be reduced (hardware allowing), according to * the endpoint descriptor used to configure the endpoint. + * @maxpacket_limit:The maximum packet size value which can be handled by this + * endpoint. It's set once by UDC driver when endpoint is initialized, and + * should not be changed. Should not be confused with maxpacket. + * @max_streams: The maximum number of streams supported + * by this EP (0 - 16, actual number is 2^n) + * @maxburst: the maximum number of bursts supported by this EP (for usb3) * @driver_data:for use by the gadget driver. all other fields are * read-only to gadget drivers. + * @desc: endpoint descriptor. This pointer is set before the endpoint is + * enabled and remains valid until the endpoint is disabled. + * @comp_desc: In case of SuperSpeed support, this is the endpoint companion + * descriptor that is used to configure the endpoint * * the bus controller driver lists all the general purpose endpoints in * gadget->ep_list. the control endpoint (gadget->ep0) is not in that list, @@ -146,11 +159,31 @@ struct usb_ep { const struct usb_ep_ops *ops; struct list_head ep_list; unsigned maxpacket:16; + unsigned maxpacket_limit:16; + unsigned max_streams:16; + unsigned maxburst:5; + const struct usb_endpoint_descriptor *desc; + const struct usb_ss_ep_comp_descriptor *comp_desc; }; /*-------------------------------------------------------------------------*/ /** + * usb_ep_set_maxpacket_limit - set maximum packet size limit for endpoint + * @ep:the endpoint being configured + * @maxpacket_limit:value of maximum packet size limit + * + * This function shoud be used only in UDC drivers to initialize endpoint + * (usually in probe function). + */ +static inline void usb_ep_set_maxpacket_limit(struct usb_ep *ep, + unsigned maxpacket_limit) +{ + ep->maxpacket_limit = maxpacket_limit; + ep->maxpacket = maxpacket_limit; +} + +/** * usb_ep_enable - configure endpoint, making it usable * @ep:the endpoint being configured. may not be the endpoint named "ep0". * drivers discover endpoints through the ep_list of a usb_gadget. @@ -396,6 +429,7 @@ static inline void usb_ep_fifo_flush(struct usb_ep *ep) /*-------------------------------------------------------------------------*/ struct usb_gadget; +struct usb_gadget_driver; /* the rest of the api to the controller hardware: device operations, * which don't involve endpoints (or i/o). @@ -409,6 +443,9 @@ struct usb_gadget_ops { int (*pullup) (struct usb_gadget *, int is_on); int (*ioctl)(struct usb_gadget *, unsigned code, unsigned long param); + int (*udc_start)(struct usb_gadget *, + struct usb_gadget_driver *); + int (*udc_stop)(struct usb_gadget *); }; /** @@ -418,6 +455,8 @@ struct usb_gadget_ops { * driver setup() requests * @ep_list: List of other endpoints supported by the device. * @speed: Speed of current connection to USB host. + * @max_speed: Maximal speed the UDC can handle. UDC must support this + * and all slower speeds. * @is_dualspeed: true if the controller supports both high and full speed * operation. If it does, the gadget driver must also support both. * @is_otg: true if the USB device port uses a Mini-AB jack, so that the @@ -434,6 +473,8 @@ struct usb_gadget_ops { * @name: Identifies the controller hardware type. Used in diagnostics * and sometimes configuration. * @dev: Driver model state for this abstract device. + * @quirk_ep_out_aligned_size: epout requires buffer size to be aligned to + * MaxPacketSize. * * Gadgets have a mostly-portable "gadget driver" implementing device * functions, handling all usb configurations and interfaces. Gadget @@ -459,6 +500,8 @@ struct usb_gadget { struct usb_ep *ep0; struct list_head ep_list; /* of usb_ep */ enum usb_device_speed speed; + enum usb_device_speed max_speed; + enum usb_device_state state; unsigned is_dualspeed:1; unsigned is_otg:1; unsigned is_a_peripheral:1; @@ -467,6 +510,7 @@ struct usb_gadget { unsigned a_alt_hnp_support:1; const char *name; struct device dev; + unsigned quirk_ep_out_aligned_size:1; }; static inline void set_gadget_data(struct usb_gadget *gadget, void *data) @@ -686,6 +730,7 @@ static inline int usb_gadget_disconnect(struct usb_gadget *gadget) /** * struct usb_gadget_driver - driver for usb 'slave' devices + * @function: String describing the gadget's function * @speed: Highest speed the driver handles. * @bind: Invoked when the driver is bound to a gadget, usually * after registering the driver. @@ -707,6 +752,8 @@ static inline int usb_gadget_disconnect(struct usb_gadget *gadget) * Called in a context that permits sleeping. * @suspend: Invoked on USB suspend. May be called in_interrupt. * @resume: Invoked on USB resume. May be called in_interrupt. + * @reset: Invoked on USB bus reset. It is mandatory for all gadget drivers + * and should be called in_interrupt. * * Devices are disabled till a gadget driver successfully bind()s, which * means the driver will handle setup() requests needed to enumerate (and @@ -753,6 +800,7 @@ static inline int usb_gadget_disconnect(struct usb_gadget *gadget) * power is maintained. */ struct usb_gadget_driver { + char *function; enum usb_device_speed speed; int (*bind)(struct usb_gadget *); void (*unbind)(struct usb_gadget *); @@ -761,6 +809,7 @@ struct usb_gadget_driver { void (*disconnect)(struct usb_gadget *); void (*suspend)(struct usb_gadget *); void (*resume)(struct usb_gadget *); + void (*reset)(struct usb_gadget *); }; @@ -801,6 +850,10 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver); */ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver); +int usb_add_gadget_udc_release(struct device *parent, + struct usb_gadget *gadget, void (*release)(struct device *dev)); +int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget); +void usb_del_gadget_udc(struct usb_gadget *gadget); /*-------------------------------------------------------------------------*/ /* utility to simplify dealing with string descriptors */ @@ -847,6 +900,35 @@ int usb_gadget_config_buf(const struct usb_config_descriptor *config, void *buf, unsigned buflen, const struct usb_descriptor_header **desc); /*-------------------------------------------------------------------------*/ +/* utility to simplify map/unmap of usb_requests to/from DMA */ + +extern int usb_gadget_map_request(struct usb_gadget *gadget, + struct usb_request *req, int is_in); + +extern void usb_gadget_unmap_request(struct usb_gadget *gadget, + struct usb_request *req, int is_in); + +/*-------------------------------------------------------------------------*/ + +/* utility to set gadget state properly */ + +extern void usb_gadget_set_state(struct usb_gadget *gadget, + enum usb_device_state state); + +/*-------------------------------------------------------------------------*/ + +/* utility to tell udc core that the bus reset occurs */ +extern void usb_gadget_udc_reset(struct usb_gadget *gadget, + struct usb_gadget_driver *driver); + +/*-------------------------------------------------------------------------*/ + +/* utility to give requests back to the gadget layer */ + +extern void usb_gadget_giveback_request(struct usb_ep *ep, + struct usb_request *req); + +/*-------------------------------------------------------------------------*/ /* utility wrapping a simple endpoint selection policy */ @@ -855,6 +937,6 @@ extern struct usb_ep *usb_ep_autoconfig(struct usb_gadget *, extern void usb_ep_autoconfig_reset(struct usb_gadget *); -extern int usb_gadget_handle_interrupts(void); +extern int usb_gadget_handle_interrupts(int index); #endif /* __LINUX_USB_GADGET_H */ diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h new file mode 100644 index 0000000000..7ec5550f4b --- /dev/null +++ b/include/linux/usb/otg.h @@ -0,0 +1,20 @@ +/* include/linux/usb/otg.h + * + * Copyright (c) 2015 Texas Instruments Incorporated - http://www.ti.com + * + * USB OTG (On The Go) defines + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __LINUX_USB_OTG_H +#define __LINUX_USB_OTG_H + +enum usb_dr_mode { + USB_DR_MODE_UNKNOWN, + USB_DR_MODE_HOST, + USB_DR_MODE_PERIPHERAL, + USB_DR_MODE_OTG, +}; + +#endif /* __LINUX_USB_OTG_H */ diff --git a/include/phys2bus.h b/include/phys2bus.h new file mode 100644 index 0000000000..87b6d69aa6 --- /dev/null +++ b/include/phys2bus.h @@ -0,0 +1,25 @@ +/* + * Copyright 2015 Stephen Warren + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _BUS_ADDR_H +#define _BUS_ADDR_H + +#ifdef CONFIG_PHYS_TO_BUS +unsigned long phys_to_bus(unsigned long phys); +unsigned long bus_to_phys(unsigned long bus); +#else +static inline unsigned long phys_to_bus(unsigned long phys) +{ + return phys; +} + +static inline unsigned long bus_to_phys(unsigned long bus) +{ + return bus; +} +#endif + +#endif diff --git a/include/ti-usb-phy-uboot.h b/include/ti-usb-phy-uboot.h new file mode 100644 index 0000000000..93f7101cb1 --- /dev/null +++ b/include/ti-usb-phy-uboot.h @@ -0,0 +1,22 @@ +/* include/ti_usb_phy_uboot.h + * + * Copyright (c) 2014 Texas Instruments Incorporated - http://www.ti.com + * + * USB2 and USB3 PHY uboot init + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __TI_USB_PHY_UBOOT_H_ +#define __TI_USB_PHY_UBOOT_H_ + +struct ti_usb_phy_device { + void *pll_ctrl_base; + void *usb2_phy_power; + void *usb3_phy_power; + int index; +}; + +int ti_usb_phy_uboot_init(struct ti_usb_phy_device *dev); +void ti_usb_phy_uboot_exit(int index); +#endif /* __TI_USB_PHY_UBOOT_H_ */ diff --git a/include/usb.h b/include/usb.h index a8fee0bdb7..2c3d506345 100644 --- a/include/usb.h +++ b/include/usb.h @@ -45,20 +45,20 @@ /* device request (setup) */ struct devrequest { - unsigned char requesttype; - unsigned char request; - unsigned short value; - unsigned short index; - unsigned short length; + __u8 requesttype; + __u8 request; + __le16 value; + __le16 index; + __le16 length; } __attribute__ ((packed)); /* Interface */ struct usb_interface { struct usb_interface_descriptor desc; - unsigned char no_of_ep; - unsigned char num_altsetting; - unsigned char act_altsetting; + __u8 no_of_ep; + __u8 num_altsetting; + __u8 act_altsetting; struct usb_endpoint_descriptor ep_desc[USB_MAXENDPOINTS]; /* @@ -73,7 +73,7 @@ struct usb_interface { struct usb_config { struct usb_config_descriptor desc; - unsigned char no_of_if; /* number of interfaces */ + __u8 no_of_if; /* number of interfaces */ struct usb_interface if_desc[USB_MAXINTERFACES]; } __attribute__ ((packed)); |