summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2020-09-03 09:48:28 -0400
committerTom Rini <trini@konsulko.com>2020-09-03 09:48:28 -0400
commit9bfb567e5f1bfe7de8eb41f8c6d00f49d2b9a426 (patch)
tree50aa5bfd07887062e0d6808158a02434c9a85116 /drivers
parent7f4d3c044504668fcbc547af52e0c2c4fd715d27 (diff)
parent293a6dfeb96129abebf1ad927fa9aedf03a66d34 (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/Kconfig1
-rw-r--r--drivers/dfu/dfu_ram.c7
-rw-r--r--drivers/fastboot/Kconfig12
-rw-r--r--drivers/fastboot/Makefile1
-rw-r--r--drivers/fastboot/fb_bcb_impl.c43
-rw-r--r--drivers/fastboot/fb_command.c40
-rw-r--r--drivers/fastboot/fb_common.c2
-rw-r--r--drivers/fastboot/fb_getvar.c4
-rw-r--r--drivers/fastboot/fb_mmc.c75
-rw-r--r--drivers/usb/eth/r8152.c223
-rw-r--r--drivers/usb/eth/r8152.h59
-rw-r--r--drivers/usb/eth/r8152_fw.c228
-rw-r--r--drivers/usb/gadget/Kconfig6
-rw-r--r--drivers/usb/gadget/Makefile1
-rw-r--r--drivers/usb/gadget/ci_udc.c7
-rw-r--r--drivers/usb/gadget/f_fastboot.c6
-rw-r--r--drivers/usb/gadget/f_mass_storage.c6
-rw-r--r--drivers/usb/gadget/f_sdp.c219
-rw-r--r--drivers/usb/gadget/gadget_chips.h8
-rw-r--r--drivers/usb/gadget/max3420_udc.c875
-rw-r--r--drivers/usb/host/usb-uclass.c18
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)