dfu_file_buf[CONFIG_SYS_DFU_MAX_FILE_SIZE];
static long dfu_file_buf_len;
+static int mmc_access_part(struct dfu_entity *dfu, struct mmc *mmc, int part)
+{
+ int ret;
+
+ if (part == mmc->part_num)
+ return 0;
+
+ ret = mmc_switch_part(dfu->dev_num, part);
+ if (ret) {
+ error("Cannot switch to partition %d\n", part);
+ return ret;
+ }
+ mmc->part_num = part;
+
+ return 0;
+}
+
static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu,
u64 offset, void *buf, long *len)
{
struct mmc *mmc = find_mmc_device(dfu->dev_num);
u32 blk_start, blk_count, n = 0;
+ int ret, part_num_bkp = 0;
/*
* We must ensure that we work in lba_blk_size chunks, so ALIGN
return -EINVAL;
}
+ if (dfu->data.mmc.hw_partition >= 0) {
+ part_num_bkp = mmc->part_num;
+ ret = mmc_access_part(dfu, mmc, dfu->data.mmc.hw_partition);
+ if (ret)
+ return ret;
+ }
+
debug("%s: %s dev: %d start: %d cnt: %d buf: 0x%p\n", __func__,
op == DFU_OP_READ ? "MMC READ" : "MMC WRITE", dfu->dev_num,
blk_start, blk_count, buf);
if (n != blk_count) {
error("MMC operation failed");
+ if (dfu->data.mmc.hw_partition >= 0)
+ mmc_access_part(dfu, mmc, part_num_bkp);
return -EIO;
}
+ if (dfu->data.mmc.hw_partition >= 0) {
+ ret = mmc_access_part(dfu, mmc, part_num_bkp);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
* 2nd and 3rd:
* lba_start and lba_size, for raw write
* mmc_dev and mmc_part, for filesystems and part
+ * 4th (optional):
+ * mmcpart <num> (access to HW eMMC partitions)
*/
int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s)
{
return -ENODEV;
}
+ dfu->data.mmc.hw_partition = -EINVAL;
if (!strcmp(entity_type, "raw")) {
dfu->layout = DFU_RAW_ADDR;
dfu->data.mmc.lba_start = second_arg;
dfu->data.mmc.lba_size = third_arg;
dfu->data.mmc.lba_blk_size = mmc->read_bl_len;
+
+ /*
+ * Check for an extra entry at dfu_alt_info env variable
+ * specifying the mmc HW defined partition number
+ */
+ if (s)
+ if (!strcmp(strsep(&s, " "), "mmcpart"))
+ dfu->data.mmc.hw_partition =
+ simple_strtoul(s, NULL, 0);
+
} else if (!strcmp(entity_type, "part")) {
disk_partition_t partinfo;
block_dev_desc_t *blk_dev = &mmc->block_dev;
unsigned enabled:1; /* 1 to enable, 0 to disable */
unsigned has_legacy_mode:1; /* 1 if this port has legacy mode */
unsigned initialized:1; /* has this port already been initialized? */
+ enum usb_init_type init_type;
enum dr_mode dr_mode; /* dual role mode */
enum periph_id periph_id;/* peripheral id */
struct fdt_gpio_state vbus_gpio; /* GPIO for vbus enable */
return PORTSC_PSPD(reg);
}
-/* Put the port into host mode */
-static void set_host_mode(struct fdt_usb *config)
+/* Set up VBUS for host/device mode */
+static void set_up_vbus(struct fdt_usb *config, enum usb_init_type init)
{
/*
- * If we are an OTG port, check if remote host is driving VBus and
- * bail out in this case.
+ * If we are an OTG port initializing in host mode,
+ * check if remote host is driving VBus and bail out in this case.
*/
- if (config->dr_mode == DR_MODE_OTG &&
- (readl(&config->reg->phy_vbus_sensors) & VBUS_VLD_STS))
+ if (init == USB_INIT_HOST &&
+ config->dr_mode == DR_MODE_OTG &&
+ (readl(&config->reg->phy_vbus_sensors) & VBUS_VLD_STS)) {
+ printf("tegrausb: VBUS input active; not enabling as host\n");
return;
+ }
- /*
- * If not driving, we set the GPIO to enable VBUS. We assume
- * that the pinmux is set up correctly for this.
- */
if (fdt_gpio_isvalid(&config->vbus_gpio)) {
+ int vbus_value;
+
fdtdec_setup_gpio(&config->vbus_gpio);
- gpio_direction_output(config->vbus_gpio.gpio,
- (config->vbus_gpio.flags & FDT_GPIO_ACTIVE_LOW) ?
- 0 : 1);
- debug("set_host_mode: GPIO %d %s\n", config->vbus_gpio.gpio,
- (config->vbus_gpio.flags & FDT_GPIO_ACTIVE_LOW) ?
- "low" : "high");
+
+ vbus_value = (init == USB_INIT_HOST) ^
+ !!(config->vbus_gpio.flags & FDT_GPIO_ACTIVE_LOW);
+ gpio_direction_output(config->vbus_gpio.gpio, vbus_value);
+
+ debug("set_up_vbus: GPIO %d %d\n", config->vbus_gpio.gpio,
+ vbus_value);
}
}
return timing;
}
+/* select the PHY to use with a USB controller */
+static void init_phy_mux(struct fdt_usb *config, uint pts,
+ enum usb_init_type init)
+{
+ struct usb_ctlr *usbctlr = config->reg;
+
+#if defined(CONFIG_TEGRA20)
+ if (config->periph_id == PERIPH_ID_USBD) {
+ clrsetbits_le32(&usbctlr->port_sc1, PTS1_MASK,
+ PTS_UTMI << PTS1_SHIFT);
+ clrbits_le32(&usbctlr->port_sc1, STS1);
+ } else {
+ clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK,
+ PTS_UTMI << PTS_SHIFT);
+ clrbits_le32(&usbctlr->port_sc1, STS);
+ }
+#else
+ /* Set to Host mode (if applicable) after Controller Reset was done */
+ clrsetbits_le32(&usbctlr->usb_mode, USBMODE_CM_HC,
+ (init == USB_INIT_HOST) ? USBMODE_CM_HC : 0);
+ /*
+ * Select PHY interface after setting host mode.
+ * For device mode, the ordering requirement is not an issue, since
+ * only the first USB controller supports device mode, and that USB
+ * controller can only talk to a UTMI PHY, so the PHY selection is
+ * already made at reset time, so this write is a no-op.
+ */
+ clrsetbits_le32(&usbctlr->hostpc1_devlc, PTS_MASK,
+ pts << PTS_SHIFT);
+ clrbits_le32(&usbctlr->hostpc1_devlc, STS);
+#endif
+}
+
/* set up the UTMI USB controller with the parameters provided */
-static int init_utmi_usb_controller(struct fdt_usb *config)
+static int init_utmi_usb_controller(struct fdt_usb *config,
+ enum usb_init_type init)
{
- u32 val;
+ u32 b_sess_valid_mask, val;
int loop_count;
const unsigned *timing;
struct usb_ctlr *usbctlr = config->reg;
/* Follow the crystal clock disable by >100ns delay */
udelay(1);
+ b_sess_valid_mask = (VBUS_B_SESS_VLD_SW_VALUE | VBUS_B_SESS_VLD_SW_EN);
+ clrsetbits_le32(&usbctlr->phy_vbus_sensors, b_sess_valid_mask,
+ (init == USB_INIT_DEVICE) ? b_sess_valid_mask : 0);
+
/*
* To Use the A Session Valid for cable detection logic, VBUS_WAKEUP
* mux must be switched to actually use a_sess_vld threshold.
clrbits_le32(&usbctlr->icusb_ctrl, IC_ENB1);
/* Select UTMI parallel interface */
-#if defined(CONFIG_TEGRA20)
- if (config->periph_id == PERIPH_ID_USBD) {
- clrsetbits_le32(&usbctlr->port_sc1, PTS1_MASK,
- PTS_UTMI << PTS1_SHIFT);
- clrbits_le32(&usbctlr->port_sc1, STS1);
- } else {
- clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK,
- PTS_UTMI << PTS_SHIFT);
- clrbits_le32(&usbctlr->port_sc1, STS);
- }
-#else
- clrsetbits_le32(&usbctlr->hostpc1_devlc, PTS_MASK,
- PTS_UTMI << PTS_SHIFT);
- clrbits_le32(&usbctlr->hostpc1_devlc, STS);
-#endif
+ init_phy_mux(config, PTS_UTMI, init);
/* Deassert power down state */
clrbits_le32(&usbctlr->utmip_xcvr_cfg0, UTMIP_FORCE_PD_POWERDOWN |
#endif
/* set up the ULPI USB controller with the parameters provided */
-static int init_ulpi_usb_controller(struct fdt_usb *config)
+static int init_ulpi_usb_controller(struct fdt_usb *config,
+ enum usb_init_type init)
{
u32 val;
int loop_count;
ULPI_CLKOUT_PINMUX_BYP | ULPI_OUTPUT_PINMUX_BYP);
/* Select ULPI parallel interface */
-#if defined(CONFIG_TEGRA20)
- clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK,
- PTS_ULPI << PTS_SHIFT);
-#else
- clrsetbits_le32(&usbctlr->hostpc1_devlc, PTS_MASK,
- PTS_ULPI << PTS_SHIFT);
-#endif
+ init_phy_mux(config, PTS_ULPI, init);
/* enable ULPI transceiver */
setbits_le32(&usbctlr->susp_ctrl, ULPI_PHY_ENB);
return 0;
}
#else
-static int init_ulpi_usb_controller(struct fdt_usb *config)
+static int init_ulpi_usb_controller(struct fdt_usb *config,
+ enum usb_init_type init)
{
printf("No code to set up ULPI controller, please enable"
"CONFIG_USB_ULPI and CONFIG_USB_ULPI_VIEWPORT");
config = &port[index];
+ switch (init) {
+ case USB_INIT_HOST:
+ switch (config->dr_mode) {
+ case DR_MODE_HOST:
+ case DR_MODE_OTG:
+ break;
+ default:
+ printf("tegrausb: Invalid dr_mode %d for host mode\n",
+ config->dr_mode);
+ return -1;
+ }
+ break;
+ case USB_INIT_DEVICE:
+ if (config->periph_id != PERIPH_ID_USBD) {
+ printf("tegrausb: Device mode only supported on first USB controller\n");
+ return -1;
+ }
+ if (!config->utmi) {
+ printf("tegrausb: Device mode only supported with UTMI PHY\n");
+ return -1;
+ }
+ switch (config->dr_mode) {
+ case DR_MODE_DEVICE:
+ case DR_MODE_OTG:
+ break;
+ default:
+ printf("tegrausb: Invalid dr_mode %d for device mode\n",
+ config->dr_mode);
+ return -1;
+ }
+ break;
+ default:
+ printf("tegrausb: Unknown USB_INIT_* %d\n", init);
+ return -1;
+ }
+
/* skip init, if the port is already initialized */
- if (config->initialized)
+ if (config->initialized && config->init_type == init)
goto success;
- if (config->utmi && init_utmi_usb_controller(config)) {
+ if (config->utmi && init_utmi_usb_controller(config, init)) {
printf("tegrausb: Cannot init port %d\n", index);
return -1;
}
- if (config->ulpi && init_ulpi_usb_controller(config)) {
+ if (config->ulpi && init_ulpi_usb_controller(config, init)) {
printf("tegrausb: Cannot init port %d\n", index);
return -1;
}
- set_host_mode(config);
+ set_up_vbus(config, init);
config->initialized = 1;
+ config->init_type = init;
success:
usbctlr = config->reg;
*hccr = (struct ehci_hccr *)&usbctlr->cap_length;
*hcor = (struct ehci_hcor *)&usbctlr->usb_cmd;
- if (controller->has_hostpc) {
- /* Set to Host mode after Controller Reset was done */
- clrsetbits_le32(&usbctlr->usb_mode, USBMODE_CM_HC,
- USBMODE_CM_HC);
- /* Select UTMI parallel interface after setting host mode */
- if (config->utmi) {
- clrsetbits_le32((char *)&usbctlr->usb_cmd +
- HOSTPC1_DEVLC, PTS_MASK,
- PTS_UTMI << PTS_SHIFT);
- clrbits_le32((char *)&usbctlr->usb_cmd +
- HOSTPC1_DEVLC, STS);
- }
- }
return 0;
}