diff options
author | Tom Rini <trini@konsulko.com> | 2020-09-03 09:48:28 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2020-09-03 09:48:28 -0400 |
commit | 9bfb567e5f1bfe7de8eb41f8c6d00f49d2b9a426 (patch) | |
tree | 50aa5bfd07887062e0d6808158a02434c9a85116 /drivers | |
parent | 7f4d3c044504668fcbc547af52e0c2c4fd715d27 (diff) | |
parent | 293a6dfeb96129abebf1ad927fa9aedf03a66d34 (diff) |
Merge branch 'master' of git://git.denx.de/u-boot-usb
- Mostly DFU fixes and r8152 fixes
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/dfu/Kconfig | 1 | ||||
-rw-r--r-- | drivers/dfu/dfu_ram.c | 7 | ||||
-rw-r--r-- | drivers/fastboot/Kconfig | 12 | ||||
-rw-r--r-- | drivers/fastboot/Makefile | 1 | ||||
-rw-r--r-- | drivers/fastboot/fb_bcb_impl.c | 43 | ||||
-rw-r--r-- | drivers/fastboot/fb_command.c | 40 | ||||
-rw-r--r-- | drivers/fastboot/fb_common.c | 2 | ||||
-rw-r--r-- | drivers/fastboot/fb_getvar.c | 4 | ||||
-rw-r--r-- | drivers/fastboot/fb_mmc.c | 75 | ||||
-rw-r--r-- | drivers/usb/eth/r8152.c | 223 | ||||
-rw-r--r-- | drivers/usb/eth/r8152.h | 59 | ||||
-rw-r--r-- | drivers/usb/eth/r8152_fw.c | 228 | ||||
-rw-r--r-- | drivers/usb/gadget/Kconfig | 6 | ||||
-rw-r--r-- | drivers/usb/gadget/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/gadget/ci_udc.c | 7 | ||||
-rw-r--r-- | drivers/usb/gadget/f_fastboot.c | 6 | ||||
-rw-r--r-- | drivers/usb/gadget/f_mass_storage.c | 6 | ||||
-rw-r--r-- | drivers/usb/gadget/f_sdp.c | 219 | ||||
-rw-r--r-- | drivers/usb/gadget/gadget_chips.h | 8 | ||||
-rw-r--r-- | drivers/usb/gadget/max3420_udc.c | 875 | ||||
-rw-r--r-- | drivers/usb/host/usb-uclass.c | 18 |
21 files changed, 1753 insertions, 88 deletions
diff --git a/drivers/dfu/Kconfig b/drivers/dfu/Kconfig index 5d45d7d7c2..0eec00ba73 100644 --- a/drivers/dfu/Kconfig +++ b/drivers/dfu/Kconfig @@ -71,6 +71,7 @@ config DFU_SF_PART config DFU_MTD bool "MTD back end for DFU" depends on DM_MTD + depends on CMD_MTDPARTS help This option enables using DFU to read and write to on any MTD device. diff --git a/drivers/dfu/dfu_ram.c b/drivers/dfu/dfu_ram.c index cc98668e7a..ab0ce9e6fa 100644 --- a/drivers/dfu/dfu_ram.c +++ b/drivers/dfu/dfu_ram.c @@ -10,6 +10,7 @@ #include <common.h> #include <malloc.h> +#include <mapmem.h> #include <errno.h> #include <dfu.h> @@ -27,9 +28,9 @@ static int dfu_transfer_medium_ram(enum dfu_op op, struct dfu_entity *dfu, } if (op == DFU_OP_WRITE) - memcpy(dfu->data.ram.start + offset, buf, *len); + memcpy(map_sysmem(dfu->data.ram.start + offset, 0), buf, *len); else - memcpy(buf, dfu->data.ram.start + offset, *len); + memcpy(buf, map_sysmem(dfu->data.ram.start + offset, 0), *len); return 0; } @@ -73,7 +74,7 @@ int dfu_fill_entity_ram(struct dfu_entity *dfu, char *devstr, char *s) } dfu->layout = DFU_RAM_ADDR; - dfu->data.ram.start = (void *)simple_strtoul(argv[1], NULL, 16); + dfu->data.ram.start = simple_strtoul(argv[1], NULL, 16); dfu->data.ram.size = simple_strtoul(argv[2], NULL, 16); dfu->write_medium = dfu_write_medium_ram; diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig index d4436dfc91..4352ba67a7 100644 --- a/drivers/fastboot/Kconfig +++ b/drivers/fastboot/Kconfig @@ -165,6 +165,18 @@ config FASTBOOT_CMD_OEM_FORMAT relies on the env variable partitions to contain the list of partitions as required by the gpt command. +config FASTBOOT_USE_BCB_SET_REBOOT_FLAG + bool "Use BCB by fastboot to set boot reason" + depends on CMD_BCB && !ARCH_MESON && !ARCH_ROCKCHIP && !TARGET_KC1 && \ + !TARGET_SNIPER && !TARGET_AM57XX_EVM && !TARGET_DRA7XX_EVM + default y + help + Fastboot could implement setting of reboot reason in a generic fashion + via BCB commands. BCB commands are able to write reboot reason into + command field of boot control block. In general case it is sufficient + implementation if your platform supports BCB commands and doesn't + require any specific reboot reason handling. + endif # FASTBOOT endmenu diff --git a/drivers/fastboot/Makefile b/drivers/fastboot/Makefile index 048af5aa82..2b2c390fe4 100644 --- a/drivers/fastboot/Makefile +++ b/drivers/fastboot/Makefile @@ -5,3 +5,4 @@ obj-y += fb_getvar.o obj-y += fb_command.o obj-$(CONFIG_FASTBOOT_FLASH_MMC) += fb_mmc.o obj-$(CONFIG_FASTBOOT_FLASH_NAND) += fb_nand.o +obj-$(CONFIG_FASTBOOT_USE_BCB_SET_REBOOT_FLAG) += fb_bcb_impl.o diff --git a/drivers/fastboot/fb_bcb_impl.c b/drivers/fastboot/fb_bcb_impl.c new file mode 100644 index 0000000000..89ec3601b6 --- /dev/null +++ b/drivers/fastboot/fb_bcb_impl.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020 GlobalLogic. + * Roman Kovalivskyi <roman.kovalivskyi@globallogic.com> + */ + +#include <common.h> +#include <fastboot.h> + +/** + * fastboot_set_reboot_flag() - Set flag to indicate reboot-bootloader + * + * Set flag which indicates that we should reboot into the bootloader + * following the reboot that fastboot executes after this function. + * + * This function should be overridden in your board file with one + * which sets whatever flag your board specific Android bootloader flow + * requires in order to re-enter the bootloader. + */ +int fastboot_set_reboot_flag(enum fastboot_reboot_reason reason) +{ + char cmd[64]; + + if (reason >= FASTBOOT_REBOOT_REASONS_COUNT) + return -EINVAL; + + snprintf(cmd, sizeof(cmd), "bcb load %d misc", + CONFIG_FASTBOOT_FLASH_MMC_DEV); + + if (run_command(cmd, 0)) + return -ENODEV; + + snprintf(cmd, sizeof(cmd), "bcb set command %s", + fastboot_boot_cmds[reason]); + + if (run_command(cmd, 0)) + return -ENOEXEC; + + if (run_command("bcb store", 0)) + return -EIO; + + return 0; +} diff --git a/drivers/fastboot/fb_command.c b/drivers/fastboot/fb_command.c index 49f6a61c37..d3c578672d 100644 --- a/drivers/fastboot/fb_command.c +++ b/drivers/fastboot/fb_command.c @@ -37,6 +37,8 @@ static void flash(char *, char *); static void erase(char *, char *); #endif static void reboot_bootloader(char *, char *); +static void reboot_fastbootd(char *, char *); +static void reboot_recovery(char *, char *); #if CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT) static void oem_format(char *, char *); #endif @@ -79,6 +81,14 @@ static const struct { .command = "reboot-bootloader", .dispatch = reboot_bootloader }, + [FASTBOOT_COMMAND_REBOOT_FASTBOOTD] = { + .command = "reboot-fastboot", + .dispatch = reboot_fastbootd + }, + [FASTBOOT_COMMAND_REBOOT_RECOVERY] = { + .command = "reboot-recovery", + .dispatch = reboot_recovery + }, [FASTBOOT_COMMAND_SET_ACTIVE] = { .command = "set_active", .dispatch = okay @@ -307,12 +317,40 @@ static void erase(char *cmd_parameter, char *response) */ static void reboot_bootloader(char *cmd_parameter, char *response) { - if (fastboot_set_reboot_flag()) + if (fastboot_set_reboot_flag(FASTBOOT_REBOOT_REASON_BOOTLOADER)) fastboot_fail("Cannot set reboot flag", response); else fastboot_okay(NULL, response); } +/** + * reboot_fastbootd() - Sets reboot fastboot flag. + * + * @cmd_parameter: Pointer to command parameter + * @response: Pointer to fastboot response buffer + */ +static void reboot_fastbootd(char *cmd_parameter, char *response) +{ + if (fastboot_set_reboot_flag(FASTBOOT_REBOOT_REASON_FASTBOOTD)) + fastboot_fail("Cannot set fastboot flag", response); + else + fastboot_okay(NULL, response); +} + +/** + * reboot_recovery() - Sets reboot recovery flag. + * + * @cmd_parameter: Pointer to command parameter + * @response: Pointer to fastboot response buffer + */ +static void reboot_recovery(char *cmd_parameter, char *response) +{ + if (fastboot_set_reboot_flag(FASTBOOT_REBOOT_REASON_RECOVERY)) + fastboot_fail("Cannot set recovery flag", response); + else + fastboot_okay(NULL, response); +} + #if CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT) /** * oem_format() - Execute the OEM format command diff --git a/drivers/fastboot/fb_common.c b/drivers/fastboot/fb_common.c index c3735a44af..736ce1cd02 100644 --- a/drivers/fastboot/fb_common.c +++ b/drivers/fastboot/fb_common.c @@ -88,7 +88,7 @@ void fastboot_okay(const char *reason, char *response) * which sets whatever flag your board specific Android bootloader flow * requires in order to re-enter the bootloader. */ -int __weak fastboot_set_reboot_flag(void) +int __weak fastboot_set_reboot_flag(enum fastboot_reboot_reason reason) { return -ENOSYS; } diff --git a/drivers/fastboot/fb_getvar.c b/drivers/fastboot/fb_getvar.c index 52da34b1e3..d43f2cfee6 100644 --- a/drivers/fastboot/fb_getvar.c +++ b/drivers/fastboot/fb_getvar.c @@ -95,7 +95,7 @@ static const struct { * * @param[in] part_name Info for which partition name to look for * @param[in,out] response Pointer to fastboot response buffer - * @param[out] size If not NULL, will contain partition size (in blocks) + * @param[out] size If not NULL, will contain partition size * @return Partition number or negative value on error */ static int getvar_get_part_info(const char *part_name, char *response, @@ -109,7 +109,7 @@ static int getvar_get_part_info(const char *part_name, char *response, r = fastboot_mmc_get_part_info(part_name, &dev_desc, &part_info, response); if (r >= 0 && size) - *size = part_info.size; + *size = part_info.size * part_info.blksz; # elif CONFIG_IS_ENABLED(FASTBOOT_FLASH_NAND) struct part_info *part_info; diff --git a/drivers/fastboot/fb_mmc.c b/drivers/fastboot/fb_mmc.c index b2f8932e1c..ae8e8e512f 100644 --- a/drivers/fastboot/fb_mmc.c +++ b/drivers/fastboot/fb_mmc.c @@ -50,6 +50,48 @@ static int part_get_info_by_name_or_alias(struct blk_desc *dev_desc, return ret; } +static int raw_part_get_info_by_name(struct blk_desc *dev_desc, + const char *name, struct disk_partition *info, int *mmcpart) +{ + /* strlen("fastboot_raw_partition_") + PART_NAME_LEN + 1 */ + char env_desc_name[23 + PART_NAME_LEN + 1]; + char *raw_part_desc; + const char *argv[2]; + const char **parg = argv; + + /* check for raw partition descriptor */ + strcpy(env_desc_name, "fastboot_raw_partition_"); + strncat(env_desc_name, name, PART_NAME_LEN); + raw_part_desc = strdup(env_get(env_desc_name)); + if (raw_part_desc == NULL) + return -ENODEV; + + /* + * parse partition descriptor + * + * <lba_start> <lba_size> [mmcpart <num>] + */ + for (; parg < argv + sizeof(argv) / sizeof(*argv); ++parg) { + *parg = strsep(&raw_part_desc, " "); + if (*parg == NULL) { + pr_err("Invalid number of arguments.\n"); + return -ENODEV; + } + } + + info->start = simple_strtoul(argv[0], NULL, 0); + info->size = simple_strtoul(argv[1], NULL, 0); + info->blksz = dev_desc->blksz; + strncpy((char *)info->name, name, PART_NAME_LEN); + + if (raw_part_desc) { + if (strcmp(strsep(&raw_part_desc, " "), "mmcpart") == 0) + *mmcpart = simple_strtoul(raw_part_desc, NULL, 0); + } + + return 0; +} + /** * fb_mmc_blk_write() - Write/erase MMC in chunks of FASTBOOT_MAX_BLK_WRITE * @@ -376,7 +418,8 @@ int fastboot_mmc_get_part_info(const char *part_name, struct blk_desc **dev_desc, struct disk_partition *part_info, char *response) { - int r; + int r = 0; + int mmcpart; *dev_desc = blk_get_dev("mmc", CONFIG_FASTBOOT_FLASH_MMC_DEV); if (!*dev_desc) { @@ -388,10 +431,12 @@ int fastboot_mmc_get_part_info(const char *part_name, return -ENOENT; } - r = part_get_info_by_name_or_alias(*dev_desc, part_name, part_info); - if (r < 0) { - fastboot_fail("partition not found", response); - return r; + if (raw_part_get_info_by_name(*dev_desc, part_name, part_info, &mmcpart) < 0) { + r = part_get_info_by_name_or_alias(*dev_desc, part_name, part_info); + if (r < 0) { + fastboot_fail("partition not found", response); + return r; + } } return r; @@ -410,6 +455,7 @@ void fastboot_mmc_flash_write(const char *cmd, void *download_buffer, { struct blk_desc *dev_desc; struct disk_partition info; + int mmcpart = 0; dev_desc = blk_get_dev("mmc", CONFIG_FASTBOOT_FLASH_MMC_DEV); if (!dev_desc || dev_desc->type == DEV_TYPE_UNKNOWN) { @@ -482,7 +528,13 @@ void fastboot_mmc_flash_write(const char *cmd, void *download_buffer, } #endif - if (part_get_info_by_name_or_alias(dev_desc, cmd, &info) < 0) { + if (raw_part_get_info_by_name(dev_desc, cmd, &info, &mmcpart) == 0) { + if (blk_dselect_hwpart(dev_desc, mmcpart)) { + pr_err("Failed to select hwpart\n"); + fastboot_fail("Failed to select hwpart", response); + return; + } + } else if (part_get_info_by_name_or_alias(dev_desc, cmd, &info) < 0) { pr_err("cannot find partition: '%s'\n", cmd); fastboot_fail("cannot find partition", response); return; @@ -524,11 +576,11 @@ void fastboot_mmc_flash_write(const char *cmd, void *download_buffer, */ void fastboot_mmc_erase(const char *cmd, char *response) { - int ret; struct blk_desc *dev_desc; struct disk_partition info; lbaint_t blks, blks_start, blks_size, grp_size; struct mmc *mmc = find_mmc_device(CONFIG_FASTBOOT_FLASH_MMC_DEV); + int mmcpart = 0; if (mmc == NULL) { pr_err("invalid mmc device\n"); @@ -562,8 +614,13 @@ void fastboot_mmc_erase(const char *cmd, char *response) } #endif - ret = part_get_info_by_name_or_alias(dev_desc, cmd, &info); - if (ret < 0) { + if (raw_part_get_info_by_name(dev_desc, cmd, &info, &mmcpart) == 0) { + if (blk_dselect_hwpart(dev_desc, mmcpart)) { + pr_err("Failed to select hwpart\n"); + fastboot_fail("Failed to select hwpart", response); + return; + } + } else if (part_get_info_by_name_or_alias(dev_desc, cmd, &info) < 0) { pr_err("cannot find partition: '%s'\n", cmd); fastboot_fail("cannot find partition", response); return; diff --git a/drivers/usb/eth/r8152.c b/drivers/usb/eth/r8152.c index 1845d957f9..215bcbbef8 100644 --- a/drivers/usb/eth/r8152.c +++ b/drivers/usb/eth/r8152.c @@ -68,6 +68,8 @@ static const struct r8152_version r8152_versions[] = { { 0x5c20, RTL_VER_05, 1 }, { 0x5c30, RTL_VER_06, 1 }, { 0x4800, RTL_VER_07, 0 }, + { 0x6000, RTL_VER_08, 1 }, + { 0x6010, RTL_VER_09, 1 }, }; static @@ -331,6 +333,12 @@ void sram_write(struct r8152 *tp, u16 addr, u16 data) ocp_reg_write(tp, OCP_SRAM_DATA, data); } +static u16 sram_read(struct r8152 *tp, u16 addr) +{ + ocp_reg_write(tp, OCP_SRAM_ADDR, addr); + return ocp_reg_read(tp, OCP_SRAM_DATA); +} + int r8152_wait_for_bit(struct r8152 *tp, bool ocp_reg, u16 type, u16 index, const u32 mask, bool set, unsigned int timeout) { @@ -467,12 +475,56 @@ static void r8153_set_rx_early_timeout(struct r8152 *tp) { u32 ocp_data = tp->coalesce / 8; - ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_TIMEOUT, ocp_data); + switch (tp->version) { + case RTL_VER_03: + case RTL_VER_04: + case RTL_VER_05: + case RTL_VER_06: + ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_TIMEOUT, + ocp_data); + break; + + case RTL_VER_08: + case RTL_VER_09: + /* The RTL8153B uses USB_RX_EXTRA_AGGR_TMR for rx timeout + * primarily. For USB_RX_EARLY_TIMEOUT, we fix it to 1264ns. + */ + ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_TIMEOUT, + RX_AUXILIARY_TIMER / 8); + ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EXTRA_AGGR_TMR, + ocp_data); + break; + + default: + debug("** %s Invalid Device\n", __func__); + break; + } } static void r8153_set_rx_early_size(struct r8152 *tp) { - u32 ocp_data = (RTL8152_AGG_BUF_SZ - RTL8153_RMS) / 4; + u32 ocp_data = (RTL8152_AGG_BUF_SZ - RTL8153_RMS - + sizeof(struct rx_desc)); + + switch (tp->version) { + case RTL_VER_03: + case RTL_VER_04: + case RTL_VER_05: + case RTL_VER_06: + ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE, + ocp_data / 4); + break; + + case RTL_VER_08: + case RTL_VER_09: + ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE, + ocp_data / 8); + break; + + default: + debug("** %s Invalid Device\n", __func__); + break; + } ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE, ocp_data); } @@ -540,6 +592,19 @@ static void r8153_u1u2en(struct r8152 *tp, bool enable) usb_ocp_write(tp, USB_TOLERANCE, BYTE_EN_SIX_BYTES, sizeof(u1u2), u1u2); } +static void r8153b_u1u2en(struct r8152 *tp, bool enable) +{ + u16 ocp_data; + + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_LPM_CONFIG); + if (enable) + ocp_data |= LPM_U1U2_EN; + else + ocp_data &= ~LPM_U1U2_EN; + + ocp_write_word(tp, MCU_TYPE_USB, USB_LPM_CONFIG, ocp_data); +} + static void r8153_u2p3en(struct r8152 *tp, bool enable) { u32 ocp_data; @@ -568,6 +633,17 @@ static void r8153_power_cut_en(struct r8152 *tp, bool enable) ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data); } +static void rtl_reset_bmu(struct r8152 *tp) +{ + u8 ocp_data; + + ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_BMU_RESET); + ocp_data &= ~(BMU_RESET_EP_IN | BMU_RESET_EP_OUT); + ocp_write_byte(tp, MCU_TYPE_USB, USB_BMU_RESET, ocp_data); + ocp_data |= BMU_RESET_EP_IN | BMU_RESET_EP_OUT; + ocp_write_byte(tp, MCU_TYPE_USB, USB_BMU_RESET, ocp_data); +} + static int r8152_read_mac(struct r8152 *tp, unsigned char *macaddr) { int ret; @@ -773,6 +849,71 @@ static void r8153_hw_phy_cfg(struct r8152 *tp) sram_write(tp, SRAM_10M_AMP2, 0x0208); } +static u32 r8152_efuse_read(struct r8152 *tp, u8 addr) +{ + u32 ocp_data; + + ocp_write_word(tp, MCU_TYPE_PLA, PLA_EFUSE_CMD, EFUSE_READ_CMD | addr); + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EFUSE_CMD); + ocp_data = (ocp_data & EFUSE_DATA_BIT16) << 9; /* data of bit16 */ + ocp_data |= ocp_read_word(tp, MCU_TYPE_PLA, PLA_EFUSE_DATA); + + return ocp_data; +} + +static void r8153b_hw_phy_cfg(struct r8152 *tp) +{ + u32 ocp_data; + u16 data; + + data = r8152_mdio_read(tp, MII_BMCR); + if (data & BMCR_PDOWN) { + data &= ~BMCR_PDOWN; + r8152_mdio_write(tp, MII_BMCR, data); + } + + /* U1/U2/L1 idle timer. 500 us */ + ocp_write_word(tp, MCU_TYPE_USB, USB_U1U2_TIMER, 500); + + r8153b_firmware(tp); + + data = sram_read(tp, SRAM_GREEN_CFG); + data |= R_TUNE_EN; + sram_write(tp, SRAM_GREEN_CFG, data); + data = ocp_reg_read(tp, OCP_NCTL_CFG); + data |= PGA_RETURN_EN; + ocp_reg_write(tp, OCP_NCTL_CFG, data); + + /* ADC Bias Calibration: + * read efuse offset 0x7d to get a 17-bit data. Remove the dummy/fake + * bit (bit3) to rebuild the real 16-bit data. Write the data to the + * ADC ioffset. + */ + ocp_data = r8152_efuse_read(tp, 0x7d); + ocp_data = ((ocp_data & 0x1fff0) >> 1) | (ocp_data & 0x7); + if (ocp_data != 0xffff) + ocp_reg_write(tp, OCP_ADC_IOFFSET, ocp_data); + + /* ups mode tx-link-pulse timing adjustment: + * rg_saw_cnt = OCP reg 0xC426 Bit[13:0] + * swr_cnt_1ms_ini = 16000000 / rg_saw_cnt + */ + ocp_data = ocp_reg_read(tp, 0xc426); + ocp_data &= 0x3fff; + if (ocp_data) { + u32 swr_cnt_1ms_ini; + + swr_cnt_1ms_ini = (16000000 / ocp_data) & SAW_CNT_1MS_MASK; + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CFG); + ocp_data = (ocp_data & ~SAW_CNT_1MS_MASK) | swr_cnt_1ms_ini; + ocp_write_word(tp, MCU_TYPE_USB, USB_UPS_CFG, ocp_data); + } + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR); + ocp_data |= PFM_PWM_SWITCH; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data); +} + static void r8153_first_init(struct r8152 *tp) { u32 ocp_data; @@ -786,6 +927,7 @@ static void r8153_first_init(struct r8152 *tp) r8153_hw_phy_cfg(tp); rtl8152_nic_reset(tp); + rtl_reset_bmu(tp); ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ocp_data &= ~NOW_IS_OOB; @@ -832,6 +974,7 @@ static void r8153_enter_oob(struct r8152 *tp) ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); rtl_disable(tp); + rtl_reset_bmu(tp); rtl8152_reinit_ll(tp); @@ -873,6 +1016,7 @@ static void rtl8153_disable(struct r8152 *tp) { r8153_disable_aldps(tp); rtl_disable(tp); + rtl_reset_bmu(tp); } static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u16 speed, u8 duplex) @@ -933,7 +1077,7 @@ static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u16 speed, u8 duplex) return -EINVAL; } - bmcr = BMCR_ANENABLE | BMCR_ANRESTART; + bmcr = BMCR_ANENABLE | BMCR_ANRESTART | BMCR_RESET; } if (tp->supports_gmii) @@ -977,6 +1121,16 @@ static void rtl8153_down(struct r8152 *tp) r8153_enter_oob(tp); } +static void rtl8153b_up(struct r8152 *tp) +{ + r8153_first_init(tp); +} + +static void rtl8153b_down(struct r8152 *tp) +{ + r8153_enter_oob(tp); +} + static void r8152b_get_version(struct r8152 *tp) { u32 ocp_data; @@ -1140,6 +1294,60 @@ static void r8153_init(struct r8152 *tp) rtl_tally_reset(tp); } +static void r8153b_init(struct r8152 *tp) +{ + u32 ocp_data; + int i; + + r8153_disable_aldps(tp); + r8153b_u1u2en(tp, false); + + r8152_wait_for_bit(tp, 0, MCU_TYPE_PLA, PLA_BOOT_CTRL, + AUTOLOAD_DONE, 1, R8152_WAIT_TIMEOUT); + + for (i = 0; i < R8152_WAIT_TIMEOUT; i++) { + ocp_data = ocp_reg_read(tp, OCP_PHY_STATUS) & PHY_STAT_MASK; + if (ocp_data == PHY_STAT_LAN_ON || ocp_data == PHY_STAT_PWRDN) + break; + + mdelay(1); + } + + r8153_u2p3en(tp, false); + + /* MSC timer = 0xfff * 8ms = 32760 ms */ + ocp_write_word(tp, MCU_TYPE_USB, USB_MSC_TIMER, 0x0fff); + + r8153_power_cut_en(tp, false); + + /* MAC clock speed down */ + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2); + ocp_data |= MAC_CLK_SPDWN_EN; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, ocp_data); + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3); + ocp_data &= ~PLA_MCU_SPDWN_EN; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, ocp_data); + + if (tp->version == RTL_VER_09) { + /* Disable Test IO for 32QFN */ + if (ocp_read_byte(tp, MCU_TYPE_PLA, 0xdc00) & BIT(5)) { + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR); + ocp_data |= TEST_IO_OFF; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data); + } + } + + /* rx aggregation */ + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL); + ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN); + ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data); + + rtl_tally_reset(tp); + r8153b_hw_phy_cfg(tp); + r8152b_enable_fc(tp); +} + static void rtl8152_unload(struct r8152 *tp) { if (tp->version != RTL_VER_01) @@ -1180,6 +1388,15 @@ static int rtl_ops_init(struct r8152 *tp) ops->unload = rtl8153_unload; break; + case RTL_VER_08: + case RTL_VER_09: + ops->init = r8153b_init; + ops->enable = rtl8153_enable; + ops->disable = rtl8153_disable; + ops->up = rtl8153b_up; + ops->down = rtl8153b_down; + break; + default: ret = -ENODEV; printf("r8152 Unknown Device\n"); diff --git a/drivers/usb/eth/r8152.h b/drivers/usb/eth/r8152.h index 10e0da8eb1..fa57e42502 100644 --- a/drivers/usb/eth/r8152.h +++ b/drivers/usb/eth/r8152.h @@ -26,6 +26,8 @@ #define PLA_TEREDO_TIMER 0xd2cc #define PLA_REALWOW_TIMER 0xd2e8 #define PLA_EXTRA_STATUS 0xd398 +#define PLA_EFUSE_DATA 0xdd00 +#define PLA_EFUSE_CMD 0xdd02 #define PLA_LEDSEL 0xdd90 #define PLA_LED_FEATURE 0xdd92 #define PLA_PHYAR 0xde00 @@ -76,8 +78,10 @@ #define USB_CSR_DUMMY2 0xb466 #define USB_DEV_STAT 0xb808 #define USB_CONNECT_TIMER 0xcbf8 +#define USB_MSC_TIMER 0xcbfc #define USB_BURST_SIZE 0xcfc0 #define USB_FW_FIX_EN1 0xcfcc +#define USB_LPM_CONFIG 0xcfd8 #define USB_USB_CTRL 0xd406 #define USB_PHY_CTRL 0xd408 #define USB_TX_AGG 0xd40a @@ -85,25 +89,23 @@ #define USB_USB_TIMER 0xd428 #define USB_RX_EARLY_TIMEOUT 0xd42c #define USB_RX_EARLY_SIZE 0xd42e -#define USB_PM_CTRL_STATUS 0xd432 +#define USB_PM_CTRL_STATUS 0xd432 /* RTL8153A */ +#define USB_RX_EXTRA_AGGR_TMR 0xd432 /* RTL8153B */ #define USB_TX_DMA 0xd434 #define USB_TOLERANCE 0xd490 #define USB_LPM_CTRL 0xd41a +#define USB_BMU_RESET 0xd4b0 +#define USB_U1U2_TIMER 0xd4da #define USB_UPS_CTRL 0xd800 -#define USB_MISC_0 0xd81a #define USB_POWER_CUT 0xd80a +#define USB_MISC_0 0xd81a #define USB_AFE_CTRL2 0xd824 +#define USB_UPS_CFG 0xd842 #define USB_WDT11_CTRL 0xe43c -#define USB_BP_BA 0xfc26 -#define USB_BP_0 0xfc28 -#define USB_BP_1 0xfc2a -#define USB_BP_2 0xfc2c -#define USB_BP_3 0xfc2e -#define USB_BP_4 0xfc30 -#define USB_BP_5 0xfc32 -#define USB_BP_6 0xfc34 -#define USB_BP_7 0xfc36 -#define USB_BP_EN 0xfc38 +#define USB_BP_BA PLA_BP_BA +#define USB_BP(n) (0xfc28 + 2 * (n)) +#define USB_BP_EN PLA_BP_EN /* RTL8153A */ +#define USB_BP2_EN 0xfc48 /* OCP Registers */ #define OCP_ALDPS_CONFIG 0x2010 @@ -114,6 +116,7 @@ #define OCP_EEE_AR 0xa41a #define OCP_EEE_DATA 0xa41c #define OCP_PHY_STATUS 0xa420 +#define OCP_NCTL_CFG 0xa42c #define OCP_POWER_CFG 0xa430 #define OCP_EEE_CFG 0xa432 #define OCP_SRAM_ADDR 0xa436 @@ -123,9 +126,11 @@ #define OCP_EEE_ADV 0xa5d0 #define OCP_EEE_LPABLE 0xa5d2 #define OCP_PHY_STATE 0xa708 /* nway state for 8153 */ +#define OCP_ADC_IOFFSET 0xbcfc #define OCP_ADC_CFG 0xbc06 /* SRAM Register */ +#define SRAM_GREEN_CFG 0x8011 #define SRAM_LPF_CFG 0x8012 #define SRAM_10M_AMP1 0x8080 #define SRAM_10M_AMP2 0x8082 @@ -207,6 +212,7 @@ /* PLA_PHY_PWR */ #define PLA_PHY_PWR_LLR (LINK_LIST_READY << 24) #define PLA_PHY_PWR_TXEMP (TXFIFO_EMPTY << 24) +#define TEST_IO_OFF BIT(4) /* PLA_MISC_1 */ #define RXDY_GATED_EN 0x0008 @@ -230,6 +236,10 @@ /* PLA_BDC_CR */ #define ALDPS_PROXY_MODE 0x0001 +/* PLA_EFUSE_CMD */ +#define EFUSE_READ_CMD BIT(15) +#define EFUSE_DATA_BIT16 BIT(7) + /* PLA_CONFIG34 */ #define LINK_ON_WAKE_EN 0x0010 #define LINK_OFF_WAKE_EN 0x0008 @@ -255,8 +265,10 @@ /* PLA_MAC_PWR_CTRL2 */ #define EEE_SPDWN_RATIO 0x8007 +#define MAC_CLK_SPDWN_EN BIT(15) /* PLA_MAC_PWR_CTRL3 */ +#define PLA_MCU_SPDWN_EN BIT(14) #define PKT_AVAIL_SPDWN_EN 0x0100 #define SUSPEND_SPDWN_EN 0x0004 #define U1U2_SPDWN_EN 0x0002 @@ -312,6 +324,9 @@ /* USB_FW_FIX_EN1 */ #define FW_IP_RESET_EN BIT(9) +/* USB_LPM_CONFIG */ +#define LPM_U1U2_EN BIT(0) + /* USB_TX_AGG */ #define TX_AGG_MAX_THRESHOLD 0x03 @@ -320,10 +335,17 @@ #define RX_THR_HIGH 0x7a120180 #define RX_THR_SLOW 0xffff0180 +/* USB_RX_EARLY_TIMEOUT */ +#define RX_AUXILIARY_TIMER 1264 + /* USB_TX_DMA */ #define TEST_MODE_DISABLE 0x00000001 #define TX_SIZE_ADJUST1 0x00000100 +/* USB_BMU_RESET */ +#define BMU_RESET_EP_IN 0x01 +#define BMU_RESET_EP_OUT 0x02 + /* USB_UPS_CTRL */ #define POWER_CUT 0x0100 @@ -366,6 +388,9 @@ #define SEN_VAL_NORMAL 0xa000 #define SEL_RXIDLE 0x0100 +/* USB_UPS_CFG */ +#define SAW_CNT_1MS_MASK 0x0fff + /* OCP_ALDPS_CONFIG */ #define ENPWRSAVE 0x8000 #define ENPDNPS 0x0200 @@ -377,6 +402,9 @@ #define PHY_STAT_LAN_ON 3 #define PHY_STAT_PWRDN 5 +/* OCP_NCTL_CFG */ +#define PGA_RETURN_EN BIT(1) + /* OCP_POWER_CFG */ #define EEE_CLKDIV_EN 0x8000 #define EN_ALDPS 0x0004 @@ -429,6 +457,10 @@ #define ADC_EN 0x0080 #define EN_EMI_L 0x0040 +/* SRAM_GREEN_CFG */ +#define GREEN_ETH_EN BIT(15) +#define R_TUNE_EN BIT(11) + /* SRAM_LPF_CFG */ #define LPF_AUTO_TUNE 0x8000 @@ -573,6 +605,8 @@ enum rtl_version { RTL_VER_05, RTL_VER_06, RTL_VER_07, + RTL_VER_08, + RTL_VER_09, RTL_VER_MAX }; @@ -640,4 +674,5 @@ int r8152_wait_for_bit(struct r8152 *tp, bool ocp_reg, u16 type, u16 index, void r8152b_firmware(struct r8152 *tp); void r8153_firmware(struct r8152 *tp); +void r8153b_firmware(struct r8152 *tp); #endif diff --git a/drivers/usb/eth/r8152_fw.c b/drivers/usb/eth/r8152_fw.c index f953b0384b..a41abed306 100644 --- a/drivers/usb/eth/r8152_fw.c +++ b/drivers/usb/eth/r8152_fw.c @@ -729,28 +729,161 @@ static u16 r8153_pla_patch_d_bp[] = { 0xfc2e, 0x0000, 0xfc30, 0x0000, 0xfc32, 0x0000, 0xfc34, 0x0000, 0xfc36, 0x0000, 0xfc38, 0x0007 }; -static void rtl_clear_bp(struct r8152 *tp) +static u8 usb_patch2_b[] = { + 0x10, 0xe0, 0x26, 0xe0, 0x3a, 0xe0, 0x58, 0xe0, + 0x6c, 0xe0, 0x85, 0xe0, 0xa5, 0xe0, 0xbe, 0xe0, + 0xd8, 0xe0, 0xdb, 0xe0, 0xf3, 0xe0, 0xf5, 0xe0, + 0xf7, 0xe0, 0xf9, 0xe0, 0xfb, 0xe0, 0xfd, 0xe0, + 0x16, 0xc0, 0x00, 0x75, 0xd1, 0x49, 0x0d, 0xf0, + 0x0f, 0xc0, 0x0f, 0xc5, 0x00, 0x1e, 0x08, 0x9e, + 0x0c, 0x9d, 0x0c, 0xc6, 0x0a, 0x9e, 0x8f, 0x1c, + 0x0e, 0x8c, 0x0e, 0x74, 0xcf, 0x49, 0xfe, 0xf1, + 0x02, 0xc0, 0x00, 0xb8, 0x96, 0x31, 0x00, 0xdc, + 0x24, 0xe4, 0x80, 0x02, 0x34, 0xd3, 0xff, 0xc3, + 0x60, 0x72, 0xa1, 0x49, 0x0d, 0xf0, 0xf8, 0xc3, + 0xf8, 0xc2, 0x00, 0x1c, 0x68, 0x9c, 0xf6, 0xc4, + 0x6a, 0x9c, 0x6c, 0x9a, 0x8f, 0x1c, 0x6e, 0x8c, + 0x6e, 0x74, 0xcf, 0x49, 0xfe, 0xf1, 0x04, 0xc0, + 0x02, 0xc2, 0x00, 0xba, 0xa8, 0x28, 0xf8, 0xc7, + 0xea, 0xc0, 0x00, 0x75, 0xd1, 0x49, 0x15, 0xf0, + 0x19, 0xc7, 0x17, 0xc2, 0xec, 0x9a, 0x00, 0x19, + 0xee, 0x89, 0xee, 0x71, 0x9f, 0x49, 0xfe, 0xf1, + 0xea, 0x71, 0x9f, 0x49, 0x0a, 0xf0, 0xd9, 0xc2, + 0xec, 0x9a, 0x00, 0x19, 0xe8, 0x99, 0x81, 0x19, + 0xee, 0x89, 0xee, 0x71, 0x9f, 0x49, 0xfe, 0xf1, + 0x06, 0xc3, 0x02, 0xc2, 0x00, 0xba, 0xf0, 0x1d, + 0x4c, 0xe8, 0x00, 0xdc, 0x00, 0xd4, 0xcb, 0xc0, + 0x00, 0x75, 0xd1, 0x49, 0x0d, 0xf0, 0xc4, 0xc0, + 0xc4, 0xc5, 0x00, 0x1e, 0x08, 0x9e, 0xc2, 0xc6, + 0x0a, 0x9e, 0x0c, 0x9d, 0x8f, 0x1c, 0x0e, 0x8c, + 0x0e, 0x74, 0xcf, 0x49, 0xfe, 0xf1, 0x04, 0xc0, + 0x02, 0xc1, 0x00, 0xb9, 0xc4, 0x16, 0x20, 0xd4, + 0xb6, 0xc0, 0x00, 0x75, 0xd1, 0x48, 0x00, 0x9d, + 0xe5, 0xc7, 0xaf, 0xc2, 0xec, 0x9a, 0x00, 0x19, + 0xe8, 0x9a, 0x81, 0x19, 0xee, 0x89, 0xee, 0x71, + 0x9f, 0x49, 0xfe, 0xf1, 0x2c, 0xc1, 0xec, 0x99, + 0x81, 0x19, 0xee, 0x89, 0xee, 0x71, 0x9f, 0x49, + 0xfe, 0xf1, 0x04, 0xc3, 0x02, 0xc2, 0x00, 0xba, + 0x96, 0x1c, 0xc0, 0xd4, 0xc0, 0x88, 0x1e, 0xc6, + 0xc0, 0x70, 0x8f, 0x49, 0x0e, 0xf0, 0x8f, 0x48, + 0x93, 0xc6, 0xca, 0x98, 0x11, 0x18, 0xc8, 0x98, + 0x16, 0xc0, 0xcc, 0x98, 0x8f, 0x18, 0xce, 0x88, + 0xce, 0x70, 0x8f, 0x49, 0xfe, 0xf1, 0x0b, 0xe0, + 0x43, 0xc6, 0x00, 0x18, 0xc8, 0x98, 0x0b, 0xc0, + 0xcc, 0x98, 0x81, 0x18, 0xce, 0x88, 0xce, 0x70, + 0x8f, 0x49, 0xfe, 0xf1, 0x02, 0xc0, 0x00, 0xb8, + 0xf2, 0x19, 0x40, 0xd3, 0x20, 0xe4, 0x33, 0xc2, + 0x40, 0x71, 0x91, 0x48, 0x40, 0x99, 0x30, 0xc2, + 0x00, 0x19, 0x48, 0x99, 0xf8, 0xc1, 0x4c, 0x99, + 0x81, 0x19, 0x4e, 0x89, 0x4e, 0x71, 0x9f, 0x49, + 0xfe, 0xf1, 0x0b, 0xc1, 0x4c, 0x99, 0x81, 0x19, + 0x4e, 0x89, 0x4e, 0x71, 0x9f, 0x49, 0xfe, 0xf1, + 0x02, 0x71, 0x02, 0xc2, 0x00, 0xba, 0x0e, 0x34, + 0x24, 0xe4, 0x19, 0xc2, 0x40, 0x71, 0x91, 0x48, + 0x40, 0x99, 0x16, 0xc2, 0x00, 0x19, 0x48, 0x99, + 0xde, 0xc1, 0x4c, 0x99, 0x81, 0x19, 0x4e, 0x89, + 0x4e, 0x71, 0x9f, 0x49, 0xfe, 0xf1, 0xf1, 0xc1, + 0x4c, 0x99, 0x81, 0x19, 0x4e, 0x89, 0x4e, 0x71, + 0x9f, 0x49, 0xfe, 0xf1, 0x02, 0x71, 0x02, 0xc2, + 0x00, 0xba, 0x60, 0x33, 0x34, 0xd3, 0x00, 0xdc, + 0x1e, 0x89, 0x02, 0xc0, 0x00, 0xb8, 0xfa, 0x12, + 0x18, 0xc0, 0x00, 0x65, 0xd1, 0x49, 0x0e, 0xf0, + 0x11, 0xc0, 0x11, 0xc5, 0x00, 0x1e, 0x08, 0x9e, + 0x0c, 0x9d, 0x0e, 0xc6, 0x0a, 0x9e, 0x8f, 0x1c, + 0x0e, 0x8c, 0x0e, 0x74, 0xcf, 0x49, 0xfe, 0xf1, + 0x04, 0xc0, 0x02, 0xc2, 0x00, 0xba, 0xa0, 0x41, + 0x06, 0xd4, 0x00, 0xdc, 0x24, 0xe4, 0x80, 0x02, + 0x34, 0xd3, 0x02, 0xc0, 0x00, 0xb8, 0x00, 0x00, + 0x02, 0xc0, 0x00, 0xb8, 0x00, 0x00, 0x02, 0xc0, + 0x00, 0xb8, 0x00, 0x00, 0x02, 0xc0, 0x00, 0xb8, + 0x00, 0x00, 0x02, 0xc0, 0x00, 0xb8, 0x00, 0x00, + 0x02, 0xc0, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00 }; + +static u16 r8153b_usb_patch_b_bp[] = { + 0xfc26, 0xa000, 0xfc28, 0x2a20, 0xfc2a, 0x28a6, 0xfc2c, 0x1dee, + 0xfc2e, 0x16c2, 0xfc30, 0x1c94, 0xfc32, 0x19f0, 0xfc34, 0x340c, + 0xfc36, 0x335e, 0xfc38, 0x12f8, 0xfc3a, 0x419e, 0xfc3c, 0x0000, + 0xfc3e, 0x0000, 0xfc40, 0x0000, 0xfc42, 0x0000, 0xfc44, 0x0000, + 0xfc46, 0x0000, 0xfc48, 0x03ff }; + +static u8 pla_patch2_b[] = { + 0x05, 0xe0, 0x1b, 0xe0, 0x2c, 0xe0, 0x60, 0xe0, + 0x73, 0xe0, 0x15, 0xc6, 0xc2, 0x64, 0xd2, 0x49, + 0x06, 0xf1, 0xc4, 0x48, 0xc5, 0x48, 0xc6, 0x48, + 0xc7, 0x48, 0x05, 0xe0, 0x44, 0x48, 0x45, 0x48, + 0x46, 0x48, 0x47, 0x48, 0xc2, 0x8c, 0xc0, 0x64, + 0x46, 0x48, 0xc0, 0x8c, 0x05, 0xc5, 0x02, 0xc4, + 0x00, 0xbc, 0x18, 0x02, 0x06, 0xdc, 0xb0, 0xc0, + 0x10, 0xc5, 0xa0, 0x77, 0xa0, 0x74, 0x46, 0x48, + 0x47, 0x48, 0xa0, 0x9c, 0x0b, 0xc5, 0xa0, 0x74, + 0x44, 0x48, 0x43, 0x48, 0xa0, 0x9c, 0x05, 0xc5, + 0xa0, 0x9f, 0x02, 0xc5, 0x00, 0xbd, 0x3c, 0x03, + 0x1c, 0xe8, 0x20, 0xe8, 0xd4, 0x49, 0x04, 0xf1, + 0xd5, 0x49, 0x20, 0xf1, 0x28, 0xe0, 0x2a, 0xc7, + 0xe0, 0x75, 0xda, 0x49, 0x14, 0xf0, 0x27, 0xc7, + 0xe0, 0x75, 0xdc, 0x49, 0x10, 0xf1, 0x24, 0xc7, + 0xe0, 0x75, 0x25, 0xc7, 0xe0, 0x74, 0x2c, 0x40, + 0x0a, 0xfa, 0x1f, 0xc7, 0xe4, 0x75, 0xd0, 0x49, + 0x09, 0xf1, 0x1c, 0xc5, 0xe6, 0x9d, 0x11, 0x1d, + 0xe4, 0x8d, 0x04, 0xe0, 0x16, 0xc7, 0x00, 0x1d, + 0xe4, 0x8d, 0xe0, 0x8e, 0x11, 0x1d, 0xe0, 0x8d, + 0x07, 0xe0, 0x0c, 0xc7, 0xe0, 0x75, 0xda, 0x48, + 0xe0, 0x9d, 0x0b, 0xc7, 0xe4, 0x8e, 0x02, 0xc4, + 0x00, 0xbc, 0x28, 0x03, 0x02, 0xc4, 0x00, 0xbc, + 0x14, 0x03, 0x12, 0xe8, 0x4e, 0xe8, 0x1c, 0xe6, + 0x20, 0xe4, 0x80, 0x02, 0xa4, 0xc0, 0x12, 0xc2, + 0x40, 0x73, 0xb0, 0x49, 0x08, 0xf0, 0xb8, 0x49, + 0x06, 0xf0, 0xb8, 0x48, 0x40, 0x9b, 0x0b, 0xc2, + 0x40, 0x76, 0x05, 0xe0, 0x02, 0x61, 0x02, 0xc3, + 0x00, 0xbb, 0x0a, 0x0a, 0x02, 0xc3, 0x00, 0xbb, + 0x1a, 0x0a, 0x98, 0xd3, 0x1e, 0xfc, 0xfe, 0xc0, + 0x02, 0x62, 0xa0, 0x48, 0x02, 0x8a, 0x00, 0x72, + 0xa0, 0x49, 0x11, 0xf0, 0x13, 0xc1, 0x20, 0x62, + 0x2e, 0x21, 0x2f, 0x25, 0x00, 0x71, 0x9f, 0x24, + 0x0a, 0x40, 0x09, 0xf0, 0x00, 0x71, 0x18, 0x48, + 0xa0, 0x49, 0x03, 0xf1, 0x9f, 0x48, 0x02, 0xe0, + 0x1f, 0x48, 0x00, 0x99, 0x02, 0xc2, 0x00, 0xba, + 0xda, 0x0e, 0x08, 0xe9 }; + +static u16 r8153b_pla_patch_b_bp[] = { + 0xfc26, 0x8000, 0xfc28, 0x0216, 0xfc2a, 0x0332, 0xfc2c, 0x030c, + 0xfc2e, 0x0a08, 0xfc30, 0x0ec0, 0xfc32, 0x0000, 0xfc34, 0x0000, + 0xfc36, 0x0000, 0xfc38, 0x001e }; + +static void rtl_clear_bp(struct r8152 *tp, u16 type) { - ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_0, 0); - ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_2, 0); - ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_4, 0); - ocp_write_dword(tp, MCU_TYPE_PLA, PLA_BP_6, 0); - ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_0, 0); - ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_2, 0); - ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_4, 0); - ocp_write_dword(tp, MCU_TYPE_USB, USB_BP_6, 0); + u8 zeros[16] = {0}; + + switch (tp->version) { + case RTL_VER_01: + case RTL_VER_02: + case RTL_VER_07: + break; + case RTL_VER_03: + case RTL_VER_04: + case RTL_VER_05: + case RTL_VER_06: + ocp_write_byte(tp, type, PLA_BP_EN, 0); + break; + case RTL_VER_08: + case RTL_VER_09: + default: + if (type == MCU_TYPE_USB) { + ocp_write_byte(tp, MCU_TYPE_USB, USB_BP2_EN, 0); + + generic_ocp_write(tp, USB_BP(8), 0xff, sizeof(zeros), + zeros, type); + } else { + ocp_write_byte(tp, MCU_TYPE_PLA, PLA_BP_EN, 0); + } + break; + } - mdelay(6); + generic_ocp_write(tp, USB_BP(0), 0xff, sizeof(zeros), zeros, type); - ocp_write_word(tp, MCU_TYPE_PLA, PLA_BP_BA, 0); - ocp_write_word(tp, MCU_TYPE_USB, USB_BP_BA, 0); -} + mdelay(6); -static void r8153_clear_bp(struct r8152 *tp) -{ - ocp_write_byte(tp, MCU_TYPE_PLA, PLA_BP_EN, 0); - ocp_write_byte(tp, MCU_TYPE_USB, USB_BP_EN, 0); - rtl_clear_bp(tp); + ocp_write_word(tp, type, PLA_BP_BA, 0); } static void r8152b_set_dq_desc(struct r8152 *tp) @@ -826,7 +959,7 @@ void r8152b_firmware(struct r8152 *tp) int i; r8152b_set_dq_desc(tp); - rtl_clear_bp(tp); + rtl_clear_bp(tp, MCU_TYPE_PLA); generic_ocp_write(tp, 0xf800, 0x3f, sizeof(r8152b_pla_patch_a), @@ -847,7 +980,7 @@ void r8152b_firmware(struct r8152 *tp) ocp_write_word(tp, MCU_TYPE_PLA, 0xb098, 0x0200); ocp_write_word(tp, MCU_TYPE_PLA, 0xb092, 0x7030); } else if (tp->version == RTL_VER_02) { - rtl_clear_bp(tp); + rtl_clear_bp(tp, MCU_TYPE_PLA); generic_ocp_write(tp, 0xf800, 0xff, sizeof(r8152b_pla_patch_a2), @@ -866,8 +999,6 @@ void r8153_firmware(struct r8152 *tp) int i; if (tp->version == RTL_VER_03) { - r8153_clear_bp(tp); - r8153_pre_ram_code(tp, 0x7000); for (i = 0; i < ARRAY_SIZE(r8153_ram_code_a); i += 2) @@ -887,7 +1018,8 @@ void r8153_firmware(struct r8152 *tp) r8153_post_ram_code(tp); r8153_wdt1_end(tp); - r8153_clear_bp(tp); + + rtl_clear_bp(tp, MCU_TYPE_USB); ocp_write_word(tp, MCU_TYPE_USB, USB_BP_EN, 0x0000); generic_ocp_write(tp, 0xf800, 0xff, @@ -904,6 +1036,8 @@ void r8153_firmware(struct r8152 *tp) ocp_write_word(tp, MCU_TYPE_PLA, 0xd38e, 0x0082); } + rtl_clear_bp(tp, MCU_TYPE_PLA); + ocp_write_word(tp, MCU_TYPE_PLA, PLA_BP_EN, 0x0000); generic_ocp_write(tp, 0xf800, 0xff, sizeof(r8153_pla_patch_b), @@ -932,7 +1066,8 @@ void r8153_firmware(struct r8152 *tp) r8153_post_ram_code(tp); r8153_wdt1_end(tp); - r8153_clear_bp(tp); + + rtl_clear_bp(tp, MCU_TYPE_USB); ocp_write_word(tp, MCU_TYPE_USB, USB_BP_EN, 0x0000); generic_ocp_write(tp, 0xf800, 0xff, @@ -951,6 +1086,8 @@ void r8153_firmware(struct r8152 *tp) ocp_write_word(tp, MCU_TYPE_USB, USB_BP_EN, 0x00ef); } + rtl_clear_bp(tp, MCU_TYPE_PLA); + ocp_write_word(tp, MCU_TYPE_PLA, PLA_BP_EN, 0x0000); generic_ocp_write(tp, 0xf800, 0xff, sizeof(r8153_pla_patch_c), @@ -985,7 +1122,7 @@ void r8153_firmware(struct r8152 *tp) r8153_post_ram_code(tp); - r8153_clear_bp(tp); + rtl_clear_bp(tp, MCU_TYPE_USB); ocp_write_word(tp, MCU_TYPE_USB, USB_BP_EN, 0x0000); generic_ocp_write(tp, 0xf800, 0xff, sizeof(usb_patch_d), @@ -996,6 +1133,8 @@ void r8153_firmware(struct r8152 *tp) r8153_usb_patch_d_bp[i], r8153_usb_patch_d_bp[i+1]); + rtl_clear_bp(tp, MCU_TYPE_PLA); + ocp_write_word(tp, MCU_TYPE_PLA, PLA_BP_EN, 0x0000); generic_ocp_write(tp, 0xf800, 0xff, sizeof(pla_patch_d), pla_patch_d, MCU_TYPE_PLA); @@ -1014,3 +1153,42 @@ void r8153_firmware(struct r8152 *tp) ocp_write_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1, ocp_data); } } + +void r8153b_firmware(struct r8152 *tp) +{ + u32 ocp_data; + int i; + + if (tp->version != RTL_VER_09) + return; + + rtl_clear_bp(tp, MCU_TYPE_USB); + + ocp_write_word(tp, MCU_TYPE_USB, USB_BP_EN, 0x0000); + generic_ocp_write(tp, 0xe600, 0xff, sizeof(usb_patch2_b), + usb_patch2_b, MCU_TYPE_USB); + + for (i = 0; i < ARRAY_SIZE(r8153b_usb_patch_b_bp); i += 2) + ocp_write_word(tp, MCU_TYPE_USB, + r8153b_usb_patch_b_bp[i], + r8153b_usb_patch_b_bp[i + 1]); + + rtl_clear_bp(tp, MCU_TYPE_PLA); + + ocp_write_word(tp, MCU_TYPE_PLA, PLA_BP_EN, 0x0000); + generic_ocp_write(tp, 0xf800, 0xff, sizeof(pla_patch2_b), + pla_patch2_b, MCU_TYPE_PLA); + + for (i = 0; i < ARRAY_SIZE(r8153b_pla_patch_b_bp); i += 2) + ocp_write_word(tp, MCU_TYPE_PLA, + r8153b_pla_patch_b_bp[i], + r8153b_pla_patch_b_bp[i + 1]); + + ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_USB2PHY); + ocp_data |= USB2PHY_L1 | USB2PHY_SUSPEND; + ocp_write_byte(tp, MCU_TYPE_USB, USB_USB2PHY, ocp_data); + + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1); + ocp_data |= FW_IP_RESET_EN; + ocp_write_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1, ocp_data); +} diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 46aa3fe954..7c0df5c264 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -105,6 +105,12 @@ config CI_UDC Say Y here to enable device controller functionality of the ChipIdea driver. +config USB_GADGET_MAX3420 + bool "MAX3420 USB Over SPI" + depends on DM_SPI + help + MAX3420, from MAXIM, implements USB-over-SPI Full-Speed device controller. + config USB_GADGET_VBUS_DRAW int "Maximum VBUS Power usage (2-500 mA)" range 2 500 diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 70f3bf43e7..f560068b41 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_USB_GADGET_BCM_UDC_OTG_PHY) += bcm_udc_otg_phy.o obj-$(CONFIG_USB_GADGET_DWC2_OTG) += dwc2_udc_otg.o obj-$(CONFIG_USB_GADGET_DWC2_OTG_PHY) += dwc2_udc_otg_phy.o obj-$(CONFIG_USB_GADGET_FOTG210) += fotg210.o +obj-$(CONFIG_USB_GADGET_MAX3420) += max3420_udc.o obj-$(CONFIG_CI_UDC) += ci_udc.o ifndef CONFIG_SPL_BUILD obj-$(CONFIG_USB_GADGET_DOWNLOAD) += g_dnl.o diff --git a/drivers/usb/gadget/ci_udc.c b/drivers/usb/gadget/ci_udc.c index cdbdbcc5ca..cdb8f6fb3d 100644 --- a/drivers/usb/gadget/ci_udc.c +++ b/drivers/usb/gadget/ci_udc.c @@ -1053,6 +1053,13 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) free(controller.items_mem); free(controller.epts); +#if CONFIG_IS_ENABLED(DM_USB) + usb_remove_ehci_gadget(&controller.ctrl); +#else + usb_lowlevel_stop(0); + controller.ctrl = NULL; +#endif + return 0; } diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index 384c0f6f6e..d1d087e12b 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/f_fastboot.c @@ -441,8 +441,6 @@ static void rx_handler_command(struct usb_ep *ep, struct usb_request *req) req->length = rx_bytes_expected(ep); } - fastboot_tx_write_str(response); - if (!strncmp("OKAY", response, 4)) { switch (cmd) { case FASTBOOT_COMMAND_BOOT: @@ -455,11 +453,15 @@ static void rx_handler_command(struct usb_ep *ep, struct usb_request *req) case FASTBOOT_COMMAND_REBOOT: case FASTBOOT_COMMAND_REBOOT_BOOTLOADER: + case FASTBOOT_COMMAND_REBOOT_FASTBOOTD: + case FASTBOOT_COMMAND_REBOOT_RECOVERY: fastboot_func->in_req->complete = compl_do_reset; break; } } + fastboot_tx_write_str(response); + *cmdbuf = '\0'; req->actual = 0; usb_ep_queue(ep, req, 0); diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 439a31c255..45f0504b6e 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -435,6 +435,7 @@ static void set_bulk_out_req_length(struct fsg_common *common, static struct ums *ums; static int ums_count; static struct fsg_common *the_fsg_common; +static unsigned int controller_index; static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) { @@ -679,7 +680,7 @@ static int sleep_thread(struct fsg_common *common) k = 0; } - usb_gadget_handle_interrupts(0); + usb_gadget_handle_interrupts(controller_index); } common->thread_wakeup_needed = 0; return rc; @@ -2764,10 +2765,11 @@ int fsg_add(struct usb_configuration *c) return fsg_bind_config(c->cdev, c, fsg_common); } -int fsg_init(struct ums *ums_devs, int count) +int fsg_init(struct ums *ums_devs, int count, unsigned int controller_idx) { ums = ums_devs; ums_count = count; + controller_index = controller_idx; return 0; } diff --git a/drivers/usb/gadget/f_sdp.c b/drivers/usb/gadget/f_sdp.c index f2fe89d2a6..e48aa2f90d 100644 --- a/drivers/usb/gadget/f_sdp.c +++ b/drivers/usb/gadget/f_sdp.c @@ -71,6 +71,10 @@ struct hid_report { #define SDP_COMMAND_LEN 16 +#define SDP_HID_PACKET_SIZE_EP1 1024 + +#define SDP_EXIT 1 + struct sdp_command { u16 cmd; u32 addr; @@ -82,8 +86,10 @@ struct sdp_command { enum sdp_state { SDP_STATE_IDLE, + SDP_STATE_RX_CMD, SDP_STATE_RX_DCD_DATA, SDP_STATE_RX_FILE_DATA, + SDP_STATE_RX_FILE_DATA_BUSY, SDP_STATE_TX_SEC_CONF, SDP_STATE_TX_SEC_CONF_BUSY, SDP_STATE_TX_REGISTER, @@ -114,8 +120,12 @@ struct f_sdp { /* EP1 IN */ struct usb_ep *in_ep; struct usb_request *in_req; + /* EP1 OUT */ + struct usb_ep *out_ep; + struct usb_request *out_req; bool configuration_done; + bool ep_int_enable; }; static struct f_sdp *sdp_func; @@ -129,7 +139,7 @@ static struct usb_interface_descriptor sdp_intf_runtime = { .bLength = sizeof(sdp_intf_runtime), .bDescriptorType = USB_DT_INTERFACE, .bAlternateSetting = 0, - .bNumEndpoints = 1, + .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_HID, .bInterfaceSubClass = 0, .bInterfaceProtocol = 0, @@ -159,10 +169,49 @@ static struct usb_endpoint_descriptor in_desc = { .bInterval = 1, }; +static struct usb_endpoint_descriptor out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, /*USB_DT_CS_ENDPOINT*/ + + .bEndpointAddress = 1 | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 64, + .bInterval = 1, +}; + +static struct usb_endpoint_descriptor in_hs_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, /*USB_DT_CS_ENDPOINT*/ + + .bEndpointAddress = 1 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 512, + .bInterval = 3, +}; + +static struct usb_endpoint_descriptor out_hs_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, /*USB_DT_CS_ENDPOINT*/ + + .bEndpointAddress = 1 | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = SDP_HID_PACKET_SIZE_EP1, + .bInterval = 3, +}; + static struct usb_descriptor_header *sdp_runtime_descs[] = { (struct usb_descriptor_header *)&sdp_intf_runtime, (struct usb_descriptor_header *)&sdp_hid_desc, (struct usb_descriptor_header *)&in_desc, + (struct usb_descriptor_header *)&out_desc, + NULL, +}; + +static struct usb_descriptor_header *sdp_runtime_hs_descs[] = { + (struct usb_descriptor_header *)&sdp_intf_runtime, + (struct usb_descriptor_header *)&sdp_hid_desc, + (struct usb_descriptor_header *)&in_hs_desc, + (struct usb_descriptor_header *)&out_hs_desc, NULL, }; @@ -328,7 +377,7 @@ static void sdp_rx_data_complete(struct usb_ep *ep, struct usb_request *req) int status = req->status; u8 *data = req->buf; u8 report = data[0]; - int datalen = req->length - 1; + int datalen = req->actual - 1; if (status != 0) { pr_err("Status: %d\n", status); @@ -351,13 +400,15 @@ static void sdp_rx_data_complete(struct usb_ep *ep, struct usb_request *req) sdp->dnl_bytes_remaining -= datalen; } - if (sdp->state == SDP_STATE_RX_FILE_DATA) { + if (sdp->state == SDP_STATE_RX_FILE_DATA_BUSY) { memcpy(sdp_ptr(sdp->dnl_address), req->buf + 1, datalen); sdp->dnl_address += datalen; } - if (sdp->dnl_bytes_remaining) + if (sdp->dnl_bytes_remaining) { + sdp->state = SDP_STATE_RX_FILE_DATA; return; + } #ifndef CONFIG_SPL_BUILD env_set_hex("filesize", sdp->dnl_bytes); @@ -365,7 +416,7 @@ static void sdp_rx_data_complete(struct usb_ep *ep, struct usb_request *req) printf("done\n"); switch (sdp->state) { - case SDP_STATE_RX_FILE_DATA: + case SDP_STATE_RX_FILE_DATA_BUSY: sdp->state = SDP_STATE_TX_SEC_CONF; break; case SDP_STATE_RX_DCD_DATA: @@ -446,10 +497,12 @@ static int sdp_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) case 1: value = SDP_COMMAND_LEN + 1; req->complete = sdp_rx_command_complete; + sdp_func->ep_int_enable = false; break; case 2: value = len; req->complete = sdp_rx_data_complete; + sdp_func->state = SDP_STATE_RX_FILE_DATA_BUSY; break; } } @@ -480,16 +533,29 @@ static int sdp_bind(struct usb_configuration *c, struct usb_function *f) return id; sdp_intf_runtime.bInterfaceNumber = id; - struct usb_ep *ep; + struct usb_ep *ep_in, *ep_out; /* allocate instance-specific endpoints */ - ep = usb_ep_autoconfig(gadget, &in_desc); - if (!ep) { + ep_in = usb_ep_autoconfig(gadget, &in_desc); + if (!ep_in) { + rv = -ENODEV; + goto error; + } + + ep_out = usb_ep_autoconfig(gadget, &out_desc); + if (!ep_out) { rv = -ENODEV; goto error; } - sdp->in_ep = ep; /* Store IN EP for enabling @ setup */ + if (gadget_is_dualspeed(gadget)) { + /* Assume endpoint addresses are the same for both speeds */ + in_hs_desc.bEndpointAddress = in_desc.bEndpointAddress; + out_hs_desc.bEndpointAddress = out_desc.bEndpointAddress; + } + + sdp->in_ep = ep_in; /* Store IN EP for enabling @ setup */ + sdp->out_ep = ep_out; cdev->req->context = sdp; @@ -522,18 +588,29 @@ static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) } -static struct usb_request *sdp_start_ep(struct usb_ep *ep) +static struct usb_request *sdp_start_ep(struct usb_ep *ep, bool in) { struct usb_request *req; - req = alloc_ep_req(ep, 64); + if (in) + req = alloc_ep_req(ep, 65); + else + req = alloc_ep_req(ep, 2048); +/* + * OUT endpoint request length should be an integral multiple of + * maxpacket size 1024, else we break on certain controllers like + * DWC3 that expect bulk OUT requests to be divisible by maxpacket size. + */ debug("%s: ep:%p req:%p\n", __func__, ep, req); if (!req) return NULL; memset(req->buf, 0, req->length); - req->complete = sdp_tx_complete; + if (in) + req->complete = sdp_tx_complete; + else + req->complete = sdp_rx_command_complete; return req; } @@ -541,20 +618,32 @@ static int sdp_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { struct f_sdp *sdp = func_to_sdp(f); struct usb_composite_dev *cdev = f->config->cdev; + struct usb_gadget *gadget = cdev->gadget; int result; debug("%s: intf: %d alt: %d\n", __func__, intf, alt); - result = usb_ep_enable(sdp->in_ep, &in_desc); + if (gadget_is_dualspeed(gadget) && gadget->speed == USB_SPEED_HIGH) { + result = usb_ep_enable(sdp->in_ep, &in_hs_desc); + result |= usb_ep_enable(sdp->out_ep, &out_hs_desc); + } else { + result = usb_ep_enable(sdp->in_ep, &in_desc); + result |= usb_ep_enable(sdp->out_ep, &out_desc); + } if (result) return result; - sdp->in_req = sdp_start_ep(sdp->in_ep); + + sdp->in_req = sdp_start_ep(sdp->in_ep, true); sdp->in_req->context = sdp; + sdp->out_req = sdp_start_ep(sdp->out_ep, false); + sdp->out_req->context = sdp; sdp->in_ep->driver_data = cdev; /* claim */ + sdp->out_ep->driver_data = cdev; /* claim */ sdp->altsetting = alt; sdp->state = SDP_STATE_IDLE; + sdp->ep_int_enable = true; return 0; } @@ -571,11 +660,18 @@ static void sdp_disable(struct usb_function *f) struct f_sdp *sdp = func_to_sdp(f); usb_ep_disable(sdp->in_ep); + usb_ep_disable(sdp->out_ep); if (sdp->in_req) { - free(sdp->in_req); + free(sdp->in_req->buf); + usb_ep_free_request(sdp->in_ep, sdp->in_req); sdp->in_req = NULL; } + if (sdp->out_req) { + free(sdp->out_req->buf); + usb_ep_free_request(sdp->out_ep, sdp->out_req); + sdp->out_req = NULL; + } } static int sdp_bind_config(struct usb_configuration *c) @@ -591,7 +687,7 @@ static int sdp_bind_config(struct usb_configuration *c) memset(sdp_func, 0, sizeof(*sdp_func)); sdp_func->usb_function.name = "sdp"; - sdp_func->usb_function.hs_descriptors = sdp_runtime_descs; + sdp_func->usb_function.hs_descriptors = sdp_runtime_hs_descs; sdp_func->usb_function.descriptors = sdp_runtime_descs; sdp_func->usb_function.bind = sdp_bind; sdp_func->usb_function.unbind = sdp_unbind; @@ -641,19 +737,43 @@ static u32 sdp_jump_imxheader(void *address) } #ifdef CONFIG_SPL_BUILD -#ifdef CONFIG_SPL_LOAD_FIT -static ulong sdp_fit_read(struct spl_load_info *load, ulong sector, - ulong count, void *buf) +static ulong sdp_load_read(struct spl_load_info *load, ulong sector, + ulong count, void *buf) { debug("%s: sector %lx, count %lx, buf %lx\n", __func__, sector, count, (ulong)buf); memcpy(buf, (void *)(load->dev + sector), count); return count; } -#endif + +static ulong search_fit_header(ulong p, int size) +{ + int i; + + for (i = 0; i < size; i += 4) { + if (genimg_get_format((const void *)(p + i)) == IMAGE_FORMAT_FIT) + return p + i; + } + + return 0; +} + +static ulong search_container_header(ulong p, int size) +{ + int i; + u8 *hdr; + + for (i = 0; i < size; i += 4) { + hdr = (u8 *)(p + i); + if (*(hdr + 3) == 0x87 && *hdr == 0) + if (*(hdr + 1) != 0 || *(hdr + 2) != 0) + return p + i; + } + return 0; +} #endif -static void sdp_handle_in_ep(struct spl_image_info *spl_image) +static int sdp_handle_in_ep(struct spl_image_info *spl_image) { u8 *data = sdp_func->in_req->buf; u32 status; @@ -705,6 +825,15 @@ static void sdp_handle_in_ep(struct spl_image_info *spl_image) /* If imx header fails, try some U-Boot specific headers */ if (status) { #ifdef CONFIG_SPL_BUILD + if (IS_ENABLED(CONFIG_SPL_LOAD_IMX_CONTAINER)) + sdp_func->jmp_address = (u32)search_container_header((ulong)sdp_func->jmp_address, sdp_func->dnl_bytes); + else if (IS_ENABLED(CONFIG_SPL_LOAD_FIT)) + sdp_func->jmp_address = (u32)search_fit_header((ulong)sdp_func->jmp_address, sdp_func->dnl_bytes); + if (sdp_func->jmp_address == 0) + panic("Error in search header, failed to jump\n"); + + printf("Found header at 0x%08x\n", sdp_func->jmp_address); + image_header_t *header = sdp_ptr(sdp_func->jmp_address); #ifdef CONFIG_SPL_LOAD_FIT @@ -714,13 +843,23 @@ static void sdp_handle_in_ep(struct spl_image_info *spl_image) debug("Found FIT\n"); load.dev = header; load.bl_len = 1; - load.read = sdp_fit_read; + load.read = sdp_load_read; spl_load_simple_fit(spl_image, &load, 0, header); - return; + return SDP_EXIT; } #endif + if (IS_ENABLED(CONFIG_SPL_LOAD_IMX_CONTAINER)) { + struct spl_load_info load; + + load.dev = header; + load.bl_len = 1; + load.read = sdp_load_read; + spl_load_imx_container(spl_image, &load, 0); + return SDP_EXIT; + } + /* In SPL, allow jumps to U-Boot images */ struct spl_image_info spl_image = {}; spl_parse_image_header(&spl_image, header); @@ -743,6 +882,29 @@ static void sdp_handle_in_ep(struct spl_image_info *spl_image) default: break; }; + + return 0; +} + +static void sdp_handle_out_ep(void) +{ + int rc; + + if (sdp_func->state == SDP_STATE_IDLE) { + sdp_func->out_req->complete = sdp_rx_command_complete; + rc = usb_ep_queue(sdp_func->out_ep, sdp_func->out_req, 0); + if (rc) + printf("error in submission: %s\n", + sdp_func->out_ep->name); + sdp_func->state = SDP_STATE_RX_CMD; + } else if (sdp_func->state == SDP_STATE_RX_FILE_DATA) { + sdp_func->out_req->complete = sdp_rx_data_complete; + rc = usb_ep_queue(sdp_func->out_ep, sdp_func->out_req, 0); + if (rc) + printf("error in submission: %s\n", + sdp_func->out_ep->name); + sdp_func->state = SDP_STATE_RX_FILE_DATA_BUSY; + } } #ifndef CONFIG_SPL_BUILD @@ -751,6 +913,7 @@ int sdp_handle(int controller_index) int spl_sdp_handle(int controller_index, struct spl_image_info *spl_image) #endif { + int flag = 0; printf("SDP: handle requests...\n"); while (1) { if (ctrlc()) { @@ -758,19 +921,19 @@ int spl_sdp_handle(int controller_index, struct spl_image_info *spl_image) return -EINVAL; } -#ifdef CONFIG_SPL_BUILD - if (spl_image->flags & SPL_FIT_FOUND) + if (flag == SDP_EXIT) return 0; -#endif WATCHDOG_RESET(); usb_gadget_handle_interrupts(controller_index); #ifdef CONFIG_SPL_BUILD - sdp_handle_in_ep(spl_image); + flag = sdp_handle_in_ep(spl_image); #else - sdp_handle_in_ep(NULL); + flag = sdp_handle_in_ep(NULL); #endif + if (sdp_func->ep_int_enable) + sdp_handle_out_ep(); } } diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index 91b0285244..587204cfb7 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -155,6 +155,12 @@ #define gadget_is_cdns3(g) 0 #endif +#ifdef CONFIG_USB_GADGET_MAX3420 +#define gadget_is_max3420(g) (!strcmp("max3420-udc", (g)->name)) +#else +#define gadget_is_max3420(g) 0 +#endif + /** * usb_gadget_controller_number - support bcdDevice id convention * @gadget: the controller being driven @@ -216,5 +222,7 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x23; else if (gadget_is_cdns3(gadget)) return 0x24; + else if (gadget_is_max3420(gadget)) + return 0x25; return -ENOENT; } diff --git a/drivers/usb/gadget/max3420_udc.c b/drivers/usb/gadget/max3420_udc.c new file mode 100644 index 0000000000..b38b9dc68f --- /dev/null +++ b/drivers/usb/gadget/max3420_udc.c @@ -0,0 +1,875 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <common.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <asm/gpio.h> +#include <linux/list.h> +#include <linux/bitfield.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <malloc.h> +#include <spi.h> +#include <dm.h> +#include <g_dnl.h> + +#define MAX3420_MAX_EPS 4 +#define EP_MAX_PACKET 64 /* Same for all Endpoints */ +#define EPNAME_SIZE 16 /* Buffer size for endpoint name */ + +#define MAX3420_SPI_DIR_RD 0 /* read register from MAX3420 */ +#define MAX3420_SPI_DIR_WR 1 /* write register to MAX3420 */ + +/* SPI commands: */ +#define MAX3420_SPI_ACK_MASK BIT(0) +#define MAX3420_SPI_DIR_MASK BIT(1) +#define MAX3420_SPI_REG_MASK GENMASK(7, 3) + +#define MAX3420_REG_EP0FIFO 0 +#define MAX3420_REG_EP1FIFO 1 +#define MAX3420_REG_EP2FIFO 2 +#define MAX3420_REG_EP3FIFO 3 +#define MAX3420_REG_SUDFIFO 4 +#define MAX3420_REG_EP0BC 5 +#define MAX3420_REG_EP1BC 6 +#define MAX3420_REG_EP2BC 7 +#define MAX3420_REG_EP3BC 8 + +#define MAX3420_REG_EPSTALLS 9 + #define bACKSTAT BIT(6) + #define bSTLSTAT BIT(5) + #define bSTLEP3IN BIT(4) + #define bSTLEP2IN BIT(3) + #define bSTLEP1OUT BIT(2) + #define bSTLEP0OUT BIT(1) + #define bSTLEP0IN BIT(0) + +#define MAX3420_REG_CLRTOGS 10 + #define bEP3DISAB BIT(7) + #define bEP2DISAB BIT(6) + #define bEP1DISAB BIT(5) + #define bCTGEP3IN BIT(4) + #define bCTGEP2IN BIT(3) + #define bCTGEP1OUT BIT(2) + +#define MAX3420_REG_EPIRQ 11 +#define MAX3420_REG_EPIEN 12 + #define bSUDAVIRQ BIT(5) + #define bIN3BAVIRQ BIT(4) + #define bIN2BAVIRQ BIT(3) + #define bOUT1DAVIRQ BIT(2) + #define bOUT0DAVIRQ BIT(1) + #define bIN0BAVIRQ BIT(0) + +#define MAX3420_REG_USBIRQ 13 +#define MAX3420_REG_USBIEN 14 + #define bOSCOKIRQ BIT(0) + #define bRWUDNIRQ BIT(1) + #define bBUSACTIRQ BIT(2) + #define bURESIRQ BIT(3) + #define bSUSPIRQ BIT(4) + #define bNOVBUSIRQ BIT(5) + #define bVBUSIRQ BIT(6) + #define bURESDNIRQ BIT(7) + +#define MAX3420_REG_USBCTL 15 + #define bHOSCSTEN BIT(7) + #define bVBGATE BIT(6) + #define bCHIPRES BIT(5) + #define bPWRDOWN BIT(4) + #define bCONNECT BIT(3) + #define bSIGRWU BIT(2) + +#define MAX3420_REG_CPUCTL 16 + #define bIE BIT(0) + +#define MAX3420_REG_PINCTL 17 + #define bEP3INAK BIT(7) + #define bEP2INAK BIT(6) + #define bEP0INAK BIT(5) + #define bFDUPSPI BIT(4) + #define bINTLEVEL BIT(3) + #define bPOSINT BIT(2) + #define bGPXB BIT(1) + #define bGPXA BIT(0) + +#define MAX3420_REG_REVISION 18 + +#define MAX3420_REG_FNADDR 19 + #define FNADDR_MASK 0x7f + +#define MAX3420_REG_IOPINS 20 +#define MAX3420_REG_IOPINS2 21 +#define MAX3420_REG_GPINIRQ 22 +#define MAX3420_REG_GPINIEN 23 +#define MAX3420_REG_GPINPOL 24 +#define MAX3420_REG_HIRQ 25 +#define MAX3420_REG_HIEN 26 +#define MAX3420_REG_MODE 27 +#define MAX3420_REG_PERADDR 28 +#define MAX3420_REG_HCTL 29 +#define MAX3420_REG_HXFR 30 +#define MAX3420_REG_HRSL 31 + +struct max3420_req { + struct usb_request usb_req; + struct list_head queue; + struct max3420_ep *ep; +}; + +struct max3420_ep { + struct max3420_udc *udc; + struct list_head queue; + char name[EPNAME_SIZE]; + unsigned int maxpacket; + struct usb_ep ep_usb; + int halted; + int id; +}; + +struct max3420_udc { + struct max3420_ep ep[MAX3420_MAX_EPS]; + struct usb_gadget_driver *driver; + bool softconnect; + struct usb_ctrlrequest setup; + struct max3420_req ep0req; + struct usb_gadget gadget; + struct spi_slave *slave; + struct udevice *dev; + u8 ep0buf[64]; + int remote_wkp; + bool suspended; +}; + +#define to_max3420_req(r) container_of((r), struct max3420_req, usb_req) +#define to_max3420_ep(e) container_of((e), struct max3420_ep, ep_usb) +#define to_udc(g) container_of((g), struct max3420_udc, gadget) + +static void spi_ack_ctrl(struct max3420_udc *udc) +{ + struct spi_slave *slave = udc->slave; + u8 txdata[1]; + + txdata[0] = FIELD_PREP(MAX3420_SPI_ACK_MASK, 1); + spi_xfer(slave, sizeof(txdata), txdata, NULL, SPI_XFER_ONCE); +} + +static u8 spi_rd8_ack(struct max3420_udc *udc, u8 reg, int ackstat) +{ + struct spi_slave *slave = udc->slave; + u8 txdata[2], rxdata[2]; + + txdata[0] = FIELD_PREP(MAX3420_SPI_REG_MASK, reg) | + FIELD_PREP(MAX3420_SPI_DIR_MASK, MAX3420_SPI_DIR_RD) | + FIELD_PREP(MAX3420_SPI_ACK_MASK, ackstat ? 1 : 0); + + rxdata[0] = 0; + rxdata[1] = 0; + spi_xfer(slave, sizeof(txdata), txdata, rxdata, SPI_XFER_ONCE); + + return rxdata[1]; +} + +static u8 spi_rd8(struct max3420_udc *udc, u8 reg) +{ + return spi_rd8_ack(udc, reg, 0); +} + +static void spi_wr8_ack(struct max3420_udc *udc, u8 reg, u8 val, int ackstat) +{ + struct spi_slave *slave = udc->slave; + u8 txdata[2]; + + txdata[0] = FIELD_PREP(MAX3420_SPI_REG_MASK, reg) | + FIELD_PREP(MAX3420_SPI_DIR_MASK, MAX3420_SPI_DIR_WR) | + FIELD_PREP(MAX3420_SPI_ACK_MASK, ackstat ? 1 : 0); + txdata[1] = val; + + spi_xfer(slave, sizeof(txdata), txdata, NULL, SPI_XFER_ONCE); +} + +static void spi_wr8(struct max3420_udc *udc, u8 reg, u8 val) +{ + spi_wr8_ack(udc, reg, val, 0); +} + +static void spi_rd_buf(struct max3420_udc *udc, u8 reg, void *buf, u8 len) +{ + struct spi_slave *slave = udc->slave; + u8 txdata[1]; + + txdata[0] = FIELD_PREP(MAX3420_SPI_REG_MASK, reg) | + FIELD_PREP(MAX3420_SPI_DIR_MASK, MAX3420_SPI_DIR_RD); + + spi_xfer(slave, sizeof(txdata), txdata, NULL, SPI_XFER_BEGIN); + spi_xfer(slave, len * 8, NULL, buf, SPI_XFER_END); +} + +static void spi_wr_buf(struct max3420_udc *udc, u8 reg, void *buf, u8 len) +{ + struct spi_slave *slave = udc->slave; + u8 txdata[1]; + + txdata[0] = FIELD_PREP(MAX3420_SPI_REG_MASK, reg) | + FIELD_PREP(MAX3420_SPI_DIR_MASK, MAX3420_SPI_DIR_WR); + + spi_xfer(slave, sizeof(txdata), txdata, NULL, SPI_XFER_BEGIN); + spi_xfer(slave, len * 8, buf, NULL, SPI_XFER_END); +} + +/* 0 if not-connected */ +int g_dnl_board_usb_cable_connected(void) +{ + return 1; +} + +static void spi_max3420_enable(struct max3420_ep *ep, int enable) +{ + struct max3420_udc *udc = ep->udc; + u8 epdis, epien; + + if (ep->id == 0) + return; + + epien = spi_rd8(udc, MAX3420_REG_EPIEN); + epdis = spi_rd8(udc, MAX3420_REG_CLRTOGS); + + if (enable) { + epdis &= ~BIT(ep->id + 4); + epien |= BIT(ep->id + 1); + } else { + epdis |= BIT(ep->id + 4); + epien &= ~BIT(ep->id + 1); + } + + spi_wr8(udc, MAX3420_REG_CLRTOGS, epdis); + spi_wr8(udc, MAX3420_REG_EPIEN, epien); +} + +static int +max3420_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct max3420_ep *ep = to_max3420_ep(_ep); + + _ep->desc = desc; + _ep->maxpacket = usb_endpoint_maxp(desc) & 0x7ff; + + spi_max3420_enable(ep, 1); + + return 0; +} + +static void max3420_req_done(struct max3420_req *req, int status) +{ + struct max3420_ep *ep = req->ep; + + if (req->usb_req.status == -EINPROGRESS) + req->usb_req.status = status; + else + status = req->usb_req.status; + + if (status && status != -ESHUTDOWN) + dev_err(ep->udc->dev, "%s done %p, status %d\n", + ep->ep_usb.name, req, status); + + if (req->usb_req.complete) + req->usb_req.complete(&ep->ep_usb, &req->usb_req); +} + +static void max3420_ep_nuke(struct max3420_ep *ep, int status) +{ + struct max3420_req *req, *r; + + list_for_each_entry_safe(req, r, &ep->queue, queue) { + list_del_init(&req->queue); + max3420_req_done(req, status); + } +} + +static int max3420_ep_disable(struct usb_ep *_ep) +{ + struct max3420_ep *ep = to_max3420_ep(_ep); + + _ep->desc = NULL; + max3420_ep_nuke(ep, -ESHUTDOWN); + spi_max3420_enable(ep, 0); + + return 0; +} + +static struct usb_request * +max3420_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct max3420_ep *ep = to_max3420_ep(_ep); + struct max3420_req *req = kzalloc(sizeof(*req), gfp_flags); + + if (!req) + return NULL; + + req->ep = ep; + INIT_LIST_HEAD(&req->queue); + + return &req->usb_req; +} + +static void +max3420_ep_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + kfree(to_max3420_req(_req)); +} + +static int +max3420_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct max3420_req *req = to_max3420_req(_req); + struct max3420_ep *ep = to_max3420_ep(_ep); + + _req->status = -EINPROGRESS; + _req->actual = 0; + list_add_tail(&req->queue, &ep->queue); + + return 0; +} + +static int max3420_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct max3420_req *req = to_max3420_req(_req); + + list_del_init(&req->queue); + max3420_req_done(req, -ECONNRESET); + + return 0; +} + +static int max3420_ep_set_halt(struct usb_ep *_ep, int halt) +{ + struct max3420_ep *ep = to_max3420_ep(_ep); + struct max3420_udc *udc = ep->udc; + u8 epstalls; + + if (ep->id == 0) /* can't stall EP0 */ + return 0; + + epstalls = spi_rd8(udc, MAX3420_REG_EPSTALLS); + if (halt) { + ep->halted = 1; + epstalls |= BIT(ep->id + 1); + } else { + u8 clrtogs; + + ep->halted = 0; + epstalls &= ~BIT(ep->id + 1); + clrtogs = spi_rd8(udc, MAX3420_REG_CLRTOGS); + clrtogs |= BIT(ep->id + 1); + spi_wr8(udc, MAX3420_REG_CLRTOGS, clrtogs); + } + spi_wr8(udc, MAX3420_REG_EPSTALLS, epstalls | bACKSTAT); + + return 0; +} + +static const struct usb_ep_ops max3420_ep_ops = { + .enable = max3420_ep_enable, + .disable = max3420_ep_disable, + .alloc_request = max3420_ep_alloc_request, + .free_request = max3420_ep_free_request, + .queue = max3420_ep_queue, + .dequeue = max3420_ep_dequeue, + .set_halt = max3420_ep_set_halt, +}; + +static void __max3420_stop(struct max3420_udc *udc) +{ + u8 val; + + /* Disable IRQ to CPU */ + spi_wr8(udc, MAX3420_REG_CPUCTL, 0); + + val = spi_rd8(udc, MAX3420_REG_USBCTL); + val |= bPWRDOWN; + val |= bHOSCSTEN; + spi_wr8(udc, MAX3420_REG_USBCTL, val); +} + +static void __max3420_start(struct max3420_udc *udc) +{ + u8 val; + + /* configure SPI */ + spi_wr8(udc, MAX3420_REG_PINCTL, bFDUPSPI); + + /* Chip Reset */ + spi_wr8(udc, MAX3420_REG_USBCTL, bCHIPRES); + mdelay(5); + spi_wr8(udc, MAX3420_REG_USBCTL, 0); + + /* Poll for OSC to stabilize */ + while (1) { + val = spi_rd8(udc, MAX3420_REG_USBIRQ); + if (val & bOSCOKIRQ) + break; + cond_resched(); + } + + /* Enable PULL-UP only when Vbus detected */ + val = spi_rd8(udc, MAX3420_REG_USBCTL); + val |= bVBGATE | bCONNECT; + spi_wr8(udc, MAX3420_REG_USBCTL, val); + + val = bURESDNIRQ | bURESIRQ; + spi_wr8(udc, MAX3420_REG_USBIEN, val); + + /* Enable only EP0 interrupts */ + val = bIN0BAVIRQ | bOUT0DAVIRQ | bSUDAVIRQ; + spi_wr8(udc, MAX3420_REG_EPIEN, val); + + /* Enable IRQ to CPU */ + spi_wr8(udc, MAX3420_REG_CPUCTL, bIE); +} + +static int max3420_udc_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct max3420_udc *udc = to_udc(gadget); + + udc->driver = driver; + udc->remote_wkp = 0; + udc->softconnect = true; + + __max3420_start(udc); + + return 0; +} + +static int max3420_udc_stop(struct usb_gadget *gadget) +{ + struct max3420_udc *udc = to_udc(gadget); + + udc->driver = NULL; + udc->softconnect = false; + + __max3420_stop(udc); + + return 0; +} + +static int max3420_wakeup(struct usb_gadget *gadget) +{ + struct max3420_udc *udc = to_udc(gadget); + u8 usbctl; + + /* Only if wakeup allowed by host */ + if (!udc->remote_wkp || !udc->suspended) + return 0; + + /* Set Remote-Wakeup Signal*/ + usbctl = spi_rd8(udc, MAX3420_REG_USBCTL); + usbctl |= bSIGRWU; + spi_wr8(udc, MAX3420_REG_USBCTL, usbctl); + + mdelay(5); + + /* Clear Remote-WkUp Signal*/ + usbctl = spi_rd8(udc, MAX3420_REG_USBCTL); + usbctl &= ~bSIGRWU; + spi_wr8(udc, MAX3420_REG_USBCTL, usbctl); + + udc->suspended = false; + + return 0; +} + +static const struct usb_gadget_ops max3420_udc_ops = { + .udc_start = max3420_udc_start, + .udc_stop = max3420_udc_stop, + .wakeup = max3420_wakeup, +}; + +static struct usb_endpoint_descriptor ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .wMaxPacketSize = cpu_to_le16(EP_MAX_PACKET), +}; + +static void max3420_getstatus(struct max3420_udc *udc) +{ + struct max3420_ep *ep; + u16 status = 0; + + switch (udc->setup.bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + /* Get device status */ + status = 0 << USB_DEVICE_SELF_POWERED; + status |= (udc->remote_wkp << USB_DEVICE_REMOTE_WAKEUP); + break; + case USB_RECIP_INTERFACE: + if (udc->driver->setup(&udc->gadget, &udc->setup) < 0) + goto stall; + break; + case USB_RECIP_ENDPOINT: + ep = &udc->ep[udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK]; + if (ep->halted) + status = 1 << USB_ENDPOINT_HALT; + break; + default: + goto stall; + } + + status = cpu_to_le16(status); + spi_wr_buf(udc, MAX3420_REG_EP0FIFO, &status, 2); + spi_wr8_ack(udc, MAX3420_REG_EP0BC, 2, 1); + return; +stall: + dev_err(udc->dev, "Can't respond to getstatus request\n"); + spi_wr8(udc, MAX3420_REG_EPSTALLS, bSTLEP0IN | bSTLEP0OUT | bSTLSTAT); +} + +static void max3420_set_clear_feature(struct max3420_udc *udc) +{ + int set = udc->setup.bRequest == USB_REQ_SET_FEATURE; + struct max3420_ep *ep; + int id; + + switch (udc->setup.bRequestType) { + case USB_RECIP_DEVICE: + if (udc->setup.wValue != USB_DEVICE_REMOTE_WAKEUP) + break; + + if (udc->setup.bRequest == USB_REQ_SET_FEATURE) + udc->remote_wkp = 1; + else + udc->remote_wkp = 0; + + return spi_ack_ctrl(udc); + + case USB_RECIP_ENDPOINT: + if (udc->setup.wValue != USB_ENDPOINT_HALT) + break; + + id = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK; + ep = &udc->ep[id]; + + max3420_ep_set_halt(&ep->ep_usb, set); + return; + default: + break; + } + + dev_err(udc->dev, "Can't respond to SET/CLEAR FEATURE\n"); + spi_wr8(udc, MAX3420_REG_EPSTALLS, bSTLEP0IN | bSTLEP0OUT | bSTLSTAT); +} + +static void max3420_handle_setup(struct max3420_udc *udc) +{ + struct usb_ctrlrequest setup; + u8 addr; + + spi_rd_buf(udc, MAX3420_REG_SUDFIFO, (void *)&setup, 8); + + udc->setup = setup; + udc->setup.wValue = cpu_to_le16(setup.wValue); + udc->setup.wIndex = cpu_to_le16(setup.wIndex); + udc->setup.wLength = cpu_to_le16(setup.wLength); + + switch (udc->setup.bRequest) { + case USB_REQ_GET_STATUS: + /* Data+Status phase form udc */ + if ((udc->setup.bRequestType & + (USB_DIR_IN | USB_TYPE_MASK)) != + (USB_DIR_IN | USB_TYPE_STANDARD)) { + break; + } + return max3420_getstatus(udc); + case USB_REQ_SET_ADDRESS: + /* Status phase from udc */ + if (udc->setup.bRequestType != (USB_DIR_OUT | + USB_TYPE_STANDARD | USB_RECIP_DEVICE)) + break; + addr = spi_rd8_ack(udc, MAX3420_REG_FNADDR, 1); + dev_dbg(udc->dev, "Assigned Address=%d/%d\n", + udc->setup.wValue, addr); + return; + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + /* Requests with no data phase, status phase from udc */ + if ((udc->setup.bRequestType & USB_TYPE_MASK) + != USB_TYPE_STANDARD) + break; + return max3420_set_clear_feature(udc); + default: + break; + } + + if (udc->driver->setup(&udc->gadget, &setup) < 0) { + /* Stall EP0 */ + spi_wr8(udc, MAX3420_REG_EPSTALLS, + bSTLEP0IN | bSTLEP0OUT | bSTLSTAT); + } +} + +static int do_data(struct max3420_udc *udc, int ep_id, int in) +{ + struct max3420_ep *ep = &udc->ep[ep_id]; + struct max3420_req *req; + int done, length, psz; + void *buf; + + if (list_empty(&ep->queue)) + return 0; + + req = list_first_entry(&ep->queue, struct max3420_req, queue); + buf = req->usb_req.buf + req->usb_req.actual; + + psz = ep->ep_usb.maxpacket; + length = req->usb_req.length - req->usb_req.actual; + length = min(length, psz); + + if (length == 0) { + done = 1; + goto xfer_done; + } + + done = 0; + if (in) { + spi_wr_buf(udc, MAX3420_REG_EP0FIFO + ep_id, buf, length); + spi_wr8(udc, MAX3420_REG_EP0BC + ep_id, length); + if (length < psz) + done = 1; + } else { + psz = spi_rd8(udc, MAX3420_REG_EP0BC + ep_id); + length = min(length, psz); + spi_rd_buf(udc, MAX3420_REG_EP0FIFO + ep_id, buf, length); + if (length < ep->ep_usb.maxpacket) + done = 1; + } + + req->usb_req.actual += length; + + if (req->usb_req.actual == req->usb_req.length) + done = 1; + +xfer_done: + if (done) { + list_del_init(&req->queue); + + if (ep_id == 0) + spi_ack_ctrl(udc); + + max3420_req_done(req, 0); + } + + return 1; +} + +static int max3420_handle_irqs(struct max3420_udc *udc) +{ + u8 epien, epirq, usbirq, usbien, reg[4]; + int ret = 0; + + spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 4); + epirq = reg[0]; + epien = reg[1]; + usbirq = reg[2]; + usbien = reg[3]; + + usbirq &= usbien; + epirq &= epien; + + if (epirq & bSUDAVIRQ) { + spi_wr8(udc, MAX3420_REG_EPIRQ, bSUDAVIRQ); + max3420_handle_setup(udc); + return 1; + } + + if (usbirq & bVBUSIRQ) { + spi_wr8(udc, MAX3420_REG_USBIRQ, bVBUSIRQ); + dev_dbg(udc->dev, "Cable plugged in\n"); + g_dnl_clear_detach(); + return 1; + } + + if (usbirq & bNOVBUSIRQ) { + spi_wr8(udc, MAX3420_REG_USBIRQ, bNOVBUSIRQ); + dev_dbg(udc->dev, "Cable pulled out\n"); + g_dnl_trigger_detach(); + return 1; + } + + if (usbirq & bURESIRQ) { + spi_wr8(udc, MAX3420_REG_USBIRQ, bURESIRQ); + return 1; + } + + if (usbirq & bURESDNIRQ) { + spi_wr8(udc, MAX3420_REG_USBIRQ, bURESDNIRQ); + spi_wr8(udc, MAX3420_REG_USBIEN, bURESDNIRQ | bURESIRQ); + spi_wr8(udc, MAX3420_REG_EPIEN, bSUDAVIRQ + | bIN0BAVIRQ | bOUT0DAVIRQ); + return 1; + } + + if (usbirq & bSUSPIRQ) { + spi_wr8(udc, MAX3420_REG_USBIRQ, bSUSPIRQ); + dev_dbg(udc->dev, "USB Suspend - Enter\n"); + udc->suspended = true; + return 1; + } + + if (usbirq & bBUSACTIRQ) { + spi_wr8(udc, MAX3420_REG_USBIRQ, bBUSACTIRQ); + dev_dbg(udc->dev, "USB Suspend - Exit\n"); + udc->suspended = false; + return 1; + } + + if (usbirq & bRWUDNIRQ) { + spi_wr8(udc, MAX3420_REG_USBIRQ, bRWUDNIRQ); + dev_dbg(udc->dev, "Asked Host to wakeup\n"); + return 1; + } + + if (usbirq & bOSCOKIRQ) { + spi_wr8(udc, MAX3420_REG_USBIRQ, bOSCOKIRQ); + dev_dbg(udc->dev, "Osc stabilized, start work\n"); + return 1; + } + + if (epirq & bOUT0DAVIRQ && do_data(udc, 0, 0)) { + spi_wr8_ack(udc, MAX3420_REG_EPIRQ, bOUT0DAVIRQ, 1); + ret = 1; + } + + if (epirq & bIN0BAVIRQ && do_data(udc, 0, 1)) + ret = 1; + + if (epirq & bOUT1DAVIRQ && do_data(udc, 1, 0)) { + spi_wr8_ack(udc, MAX3420_REG_EPIRQ, bOUT1DAVIRQ, 1); + ret = 1; + } + + if (epirq & bIN2BAVIRQ && do_data(udc, 2, 1)) + ret = 1; + + if (epirq & bIN3BAVIRQ && do_data(udc, 3, 1)) + ret = 1; + + return ret; +} + +static int max3420_irq(struct max3420_udc *udc) +{ + do_data(udc, 0, 1); /* get done with the EP0 ZLP */ + + return max3420_handle_irqs(udc); +} + +static void max3420_setup_eps(struct max3420_udc *udc) +{ + int i; + + INIT_LIST_HEAD(&udc->gadget.ep_list); + INIT_LIST_HEAD(&udc->ep[0].ep_usb.ep_list); + + for (i = 0; i < MAX3420_MAX_EPS; i++) { + struct max3420_ep *ep = &udc->ep[i]; + + INIT_LIST_HEAD(&ep->queue); + + ep->id = i; + ep->udc = udc; + ep->ep_usb.ops = &max3420_ep_ops; + ep->ep_usb.name = ep->name; + ep->ep_usb.maxpacket = EP_MAX_PACKET; + + if (i == 0) { + ep->ep_usb.desc = &ep0_desc; + snprintf(ep->name, EPNAME_SIZE, "ep0"); + continue; + } + + list_add_tail(&ep->ep_usb.ep_list, &udc->gadget.ep_list); + + if (i == 1) + snprintf(ep->name, EPNAME_SIZE, "ep1out-bulk"); + else + snprintf(ep->name, EPNAME_SIZE, "ep%din-bulk", i); + }; +} + +static void max3420_setup_spi(struct max3420_udc *udc) +{ + u8 reg[8]; + + spi_claim_bus(udc->slave); + spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 8); + /* configure SPI */ + spi_wr8(udc, MAX3420_REG_PINCTL, bFDUPSPI); +} + +int dm_usb_gadget_handle_interrupts(struct udevice *dev) +{ + struct max3420_udc *udc = dev_get_priv(dev); + + return max3420_irq(udc); +} + +static int max3420_udc_probe(struct udevice *dev) +{ + struct max3420_udc *udc = dev_get_priv(dev); + struct dm_spi_slave_platdata *slave_pdata; + struct udevice *bus = dev->parent; + int busnum = bus->seq; + unsigned int cs; + uint speed, mode; + struct udevice *spid; + + slave_pdata = dev_get_parent_platdata(dev); + cs = slave_pdata->cs; + speed = slave_pdata->max_hz; + mode = slave_pdata->mode; + spi_get_bus_and_cs(busnum, cs, speed, mode, "spi_generic_drv", + NULL, &spid, &udc->slave); + + udc->dev = dev; + udc->gadget.ep0 = &udc->ep[0].ep_usb; + udc->gadget.max_speed = USB_SPEED_FULL; + udc->gadget.speed = USB_SPEED_FULL; + udc->gadget.is_dualspeed = 0; + udc->gadget.ops = &max3420_udc_ops; + udc->gadget.name = "max3420-udc"; + + max3420_setup_eps(udc); + max3420_setup_spi(udc); + + usb_add_gadget_udc((struct device *)dev, &udc->gadget); + + return 0; +} + +static int max3420_udc_remove(struct udevice *dev) +{ + struct max3420_udc *udc = dev_get_priv(dev); + + usb_del_gadget_udc(&udc->gadget); + + spi_release_bus(udc->slave); + + return 0; +} + +static const struct udevice_id max3420_ids[] = { + { .compatible = "maxim,max3421-udc" }, + { } +}; + +U_BOOT_DRIVER(max3420_generic_udc) = { + .name = "max3420-udc", + .id = UCLASS_USB_GADGET_GENERIC, + .of_match = max3420_ids, + .probe = max3420_udc_probe, + .remove = max3420_udc_remove, + .priv_auto_alloc_size = sizeof(struct max3420_udc), +}; diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c index e5dda79b94..8773824e05 100644 --- a/drivers/usb/host/usb-uclass.c +++ b/drivers/usb/host/usb-uclass.c @@ -411,6 +411,24 @@ int usb_setup_ehci_gadget(struct ehci_ctrl **ctlrp) return 0; } +int usb_remove_ehci_gadget(struct ehci_ctrl **ctlrp) +{ + struct udevice *dev; + int ret; + + /* Find the old device and remove it */ + ret = uclass_find_device_by_seq(UCLASS_USB, 0, true, &dev); + if (ret) + return ret; + ret = device_remove(dev, DM_REMOVE_NORMAL); + if (ret) + return ret; + + *ctlrp = NULL; + + return 0; +} + /* returns 0 if no match, 1 if match */ static int usb_match_device(const struct usb_device_descriptor *desc, const struct usb_device_id *id) |