From 9b20fe6f1affdfc7441481dc0acb55eadd80134f Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 30 Apr 2014 15:09:55 -0600 Subject: usb: tegra: fix PHY selection code The TRM for Tegra30 and later all state that USBMODE_CM_HC must be set before writing to hostpc1_devlc to select which PHY to use for a USB controller. However, neither init_{utmi,ulpi}_usb_controller() do this today, so the register writes they perform for PHY selection do not work. For the UTMI case, this was hacked around in commit 7e44d9320ed4 "ARM: Tegra: USB: EHCI: Add support for Tegra30/Tegra114" by adding code to ehci_hcd_init() which sets USBMODE_CM_HC and duplicates the PHY selection register write. This code doesn't cover the ULPI case, so I wouldn't be surprised if ULPI doesn't work with the current code, unless the ordering requirement only ends up being an issue in HW for UTMI not ULPI. This patch fixes init_{utmi,ulpi}_usb_controller() to correctly set USBMODE_CM_HC before selecting the PHY. Now that this works, we can remove the duplicate UTMI-specific code in ehci_hcd_init(), thus simplifying that function. Cc: Jim Lin Cc: Stefan Agner Signed-off-by: Stephen Warren diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 38db18e..9a9a127 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -496,6 +496,10 @@ static int init_utmi_usb_controller(struct fdt_usb *config) clrbits_le32(&usbctlr->port_sc1, STS); } #else + /* Set to Host mode after Controller Reset was done */ + clrsetbits_le32(&usbctlr->usb_mode, USBMODE_CM_HC, + USBMODE_CM_HC); + /* Select PHY interface after setting host mode */ clrsetbits_le32(&usbctlr->hostpc1_devlc, PTS_MASK, PTS_UTMI << PTS_SHIFT); clrbits_le32(&usbctlr->hostpc1_devlc, STS); @@ -561,6 +565,10 @@ static int init_ulpi_usb_controller(struct fdt_usb *config) clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK, PTS_ULPI << PTS_SHIFT); #else + /* Set to Host mode after Controller Reset was done */ + clrsetbits_le32(&usbctlr->usb_mode, USBMODE_CM_HC, + USBMODE_CM_HC); + /* Select PHY interface after setting host mode */ clrsetbits_le32(&usbctlr->hostpc1_devlc, PTS_MASK, PTS_ULPI << PTS_SHIFT); #endif @@ -788,19 +796,6 @@ success: *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; } -- cgit v0.10.2 From 2d34151f7501ddaa599897f0d89ad576126b03eb Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 30 Apr 2014 15:09:56 -0600 Subject: usb: tegra: refactor PHY type selection Both init_{utmi,ulpi}_usb_controller() have nearly identical code for PHY type selection. Pull this out into a common function to remove the duplication. Cc: Jim Lin Cc: Stefan Agner Signed-off-by: Stephen Warren diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 9a9a127..55a546e 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -293,6 +293,32 @@ static const unsigned *get_pll_timing(void) return timing; } +/* select the PHY to use with a USB controller */ +static void init_phy_mux(struct fdt_usb *config, uint pts) +{ + 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 after Controller Reset was done */ + clrsetbits_le32(&usbctlr->usb_mode, USBMODE_CM_HC, + USBMODE_CM_HC); + /* Select PHY interface after setting host mode */ + 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) { @@ -485,25 +511,7 @@ static int init_utmi_usb_controller(struct fdt_usb *config) 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 - /* Set to Host mode after Controller Reset was done */ - clrsetbits_le32(&usbctlr->usb_mode, USBMODE_CM_HC, - USBMODE_CM_HC); - /* Select PHY interface after setting host mode */ - clrsetbits_le32(&usbctlr->hostpc1_devlc, PTS_MASK, - PTS_UTMI << PTS_SHIFT); - clrbits_le32(&usbctlr->hostpc1_devlc, STS); -#endif + init_phy_mux(config, PTS_UTMI); /* Deassert power down state */ clrbits_le32(&usbctlr->utmip_xcvr_cfg0, UTMIP_FORCE_PD_POWERDOWN | @@ -561,17 +569,7 @@ static int init_ulpi_usb_controller(struct fdt_usb *config) 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 - /* Set to Host mode after Controller Reset was done */ - clrsetbits_le32(&usbctlr->usb_mode, USBMODE_CM_HC, - USBMODE_CM_HC); - /* Select PHY interface after setting host mode */ - clrsetbits_le32(&usbctlr->hostpc1_devlc, PTS_MASK, - PTS_ULPI << PTS_SHIFT); -#endif + init_phy_mux(config, PTS_ULPI); /* enable ULPI transceiver */ setbits_le32(&usbctlr->susp_ctrl, ULPI_PHY_ENB); -- cgit v0.10.2 From a4539a2aa7bde003d4318b1b2e21fd217cc2899f Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 30 Apr 2014 15:09:57 -0600 Subject: usb: tegra: support device mode A few changes are made to the Tegra EHCI driver so that it can set everything up for device-mode operation on the first USB controller. This can be used in conjunction with ci_udc.c to operate as a USB device. Detailed changes are: * Rename set_host_mode() to set_up_vbus() since that's really what it does. * Modify set_up_vbus() to know whether it's initializing in host or device mode, and: - Skip the external VBUS check in device mode, since external VBUS is expected in this case. - Disable VBUS output in device mode. * Modify init_phy_mux() to know whether it's initializing in host or device mode, and hence skip setting USBMODE_CM_HC (which enables host mode) in device mode. See the comments in that function for why this is safe w.r.t. the ordering requirements of PHY selection. * Modify init_utmi_usb_controller() to force "b session valid" in device mode, since the HW requires this. This is done in UTMI-specific code, since we only support device mode on the first USB controller, and that controller can only talk to a UTMI PHY. * Enhance ehci_hcd_init() to error-check the requested host-/device-mode vs. the dr_mode (dual-role mode) value present in device tree, and the HW configurations which support device mode. * Enhance ehci_hcd_init() not to skip HW initialization when switching between host and device mode on a controller. This requires remembering which mode the last initialization used. Cc: Jim Lin Cc: Stefan Agner Signed-off-by: Stephen Warren diff --git a/arch/arm/include/asm/arch-tegra/usb.h b/arch/arm/include/asm/arch-tegra/usb.h index ceb7bcd..c817088 100644 --- a/arch/arm/include/asm/arch-tegra/usb.h +++ b/arch/arm/include/asm/arch-tegra/usb.h @@ -349,6 +349,8 @@ struct usb_ctlr { /* USB3_IF_USB_PHY_VBUS_SENSORS_0 */ #define VBUS_VLD_STS (1 << 26) +#define VBUS_B_SESS_VLD_SW_VALUE (1 << 12) +#define VBUS_B_SESS_VLD_SW_EN (1 << 11) /* Setup USB on the board */ int usb_process_devicetree(const void *blob); diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 55a546e..33e5ea9 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -69,6 +69,7 @@ struct fdt_usb { 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 */ @@ -237,29 +238,31 @@ int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg) 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); } } @@ -294,7 +297,8 @@ static const unsigned *get_pll_timing(void) } /* select the PHY to use with a USB controller */ -static void init_phy_mux(struct fdt_usb *config, uint pts) +static void init_phy_mux(struct fdt_usb *config, uint pts, + enum usb_init_type init) { struct usb_ctlr *usbctlr = config->reg; @@ -309,10 +313,16 @@ static void init_phy_mux(struct fdt_usb *config, uint pts) clrbits_le32(&usbctlr->port_sc1, STS); } #else - /* Set to Host mode after Controller Reset was done */ + /* Set to Host mode (if applicable) after Controller Reset was done */ clrsetbits_le32(&usbctlr->usb_mode, USBMODE_CM_HC, - USBMODE_CM_HC); - /* Select PHY interface after setting host mode */ + (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); @@ -320,9 +330,10 @@ static void init_phy_mux(struct fdt_usb *config, uint pts) } /* 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; @@ -340,6 +351,10 @@ static int init_utmi_usb_controller(struct fdt_usb *config) /* 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. @@ -511,7 +526,7 @@ static int init_utmi_usb_controller(struct fdt_usb *config) clrbits_le32(&usbctlr->icusb_ctrl, IC_ENB1); /* Select UTMI parallel interface */ - init_phy_mux(config, PTS_UTMI); + init_phy_mux(config, PTS_UTMI, init); /* Deassert power down state */ clrbits_le32(&usbctlr->utmip_xcvr_cfg0, UTMIP_FORCE_PD_POWERDOWN | @@ -541,7 +556,8 @@ static int init_utmi_usb_controller(struct fdt_usb *config) #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; @@ -569,7 +585,7 @@ static int init_ulpi_usb_controller(struct fdt_usb *config) ULPI_CLKOUT_PINMUX_BYP | ULPI_OUTPUT_PINMUX_BYP); /* Select ULPI parallel interface */ - init_phy_mux(config, PTS_ULPI); + init_phy_mux(config, PTS_ULPI, init); /* enable ULPI transceiver */ setbits_le32(&usbctlr->susp_ctrl, ULPI_PHY_ENB); @@ -618,7 +634,8 @@ static int init_ulpi_usb_controller(struct fdt_usb *config) 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"); @@ -771,23 +788,60 @@ int ehci_hcd_init(int index, enum usb_init_type init, 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; -- cgit v0.10.2 From 8630c1c7e395e946ca6f98e2500e86e14f2c56bb Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 13 May 2014 10:51:54 -0600 Subject: usb: ci_udc: parse QTD before over-writing it ci_udc only allocates a single QTD structure per EP. All data needs to be extracted from the DTD prior to calling ci_ep_submit_next_request(), since that fills the QTD with next transaction's parameters. Fix handle_ep_complete() to extract the transaction (remaining) length before kicking off the next transaction. In practice, this only causes writes to UMS devices to fail for me. I may have tested the final versions of my previous ci_udc patch only with reads. More recently, I had patches applied locally that allocated a QTD per USB request rather than per USB EP, although since that doesn't give any performance benefit, I'm dropping those. Fixes: 2813006fecda ("usb: ci_udc: allow multiple buffer allocs per ep") Signed-off-by: Stephen Warren diff --git a/drivers/usb/gadget/ci_udc.c b/drivers/usb/gadget/ci_udc.c index 290863d..9cd0036 100644 --- a/drivers/usb/gadget/ci_udc.c +++ b/drivers/usb/gadget/ci_udc.c @@ -424,6 +424,7 @@ static void handle_ep_complete(struct ci_ep *ep) item = ci_get_qtd(num, in); ci_invalidate_qtd(num); + len = (item->info >> 16) & 0x7fff; if (item->info & 0xff) printf("EP%d/%s FAIL info=%x pg0=%x\n", num, in ? "in" : "out", item->info, item->page0); @@ -435,7 +436,6 @@ static void handle_ep_complete(struct ci_ep *ep) if (!list_empty(&ep->queue)) ci_ep_submit_next_request(ep); - len = (item->info >> 16) & 0x7fff; ci_req->req.actual = ci_req->req.length - len; ci_debounce(ci_req, in); -- cgit v0.10.2 From 1aa4bdc82db1e0393a461e0cd908d88418e1f632 Mon Sep 17 00:00:00 2001 From: Przemyslaw Marczak Date: Mon, 12 May 2014 12:05:33 +0200 Subject: drivers:dfu: dfu_flush(): add raw data flush to complete dfu write Before dfu write and flush operations separation, dfu write data was flushed by host download request with len of zero size. Since above change manually calling dfu write with zero size has non sense (e.g. in THOR). This should be done by flush operation. So now dfu_write_buffer_drain() is called in dfu_flush(). If there is any raw data to flush (like it can be in thor) then it will be physically written to medium. Signed-off-by: Przemyslaw Marczak Cc: Lukasz Majewski Cc: Heiko Schocher Cc: Marek Vasut diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index 51b1026..a938109 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -131,6 +131,10 @@ int dfu_flush(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) { int ret = 0; + ret = dfu_write_buffer_drain(dfu); + if (ret) + return ret; + if (dfu->flush_medium) ret = dfu->flush_medium(dfu); -- cgit v0.10.2 From 584b55b072a94de94b3fe0d54defae87795237d7 Mon Sep 17 00:00:00 2001 From: Przemyslaw Marczak Date: Mon, 12 May 2014 12:05:34 +0200 Subject: usb:gadget:f_thor: download_tail(): remove dfu_write with 0 size Since dfu_flush() can write raw data, dfu_write() with zero size can be removed from download_tail() in thor gadget. Signed-off-by: Przemyslaw Marczak Cc: Lukasz Majewski Cc: Heiko Schocher Cc: Marek Vasut diff --git a/drivers/usb/gadget/f_thor.c b/drivers/usb/gadget/f_thor.c index feef9e4..28f215e 100644 --- a/drivers/usb/gadget/f_thor.c +++ b/drivers/usb/gadget/f_thor.c @@ -219,21 +219,15 @@ static int download_tail(long long int left, int cnt) } /* - * To store last "packet" DFU storage backend requires dfu_write with - * size parameter equal to 0 + * To store last "packet" or write file from buffer to filesystem + * DFU storage backend requires dfu_flush * * This also frees memory malloc'ed by dfu_get_buf(), so no explicit * need fo call dfu_free_buf() is needed. */ - ret = dfu_write(dfu_entity, transfer_buffer, 0, cnt); - if (ret) - error("DFU write failed [%d] cnt: %d", ret, cnt); - ret = dfu_flush(dfu_entity, transfer_buffer, 0, cnt); - if (ret) { + if (ret) error("DFU flush failed!"); - return ret; - } return ret; } -- cgit v0.10.2 From c8151b4a5de136ea2c2a0e6e9aec481810ee0460 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 9 May 2014 16:58:15 +0200 Subject: dfu: mmc: Provide support for eMMC boot partition access Before this patch it was only possible to access the default eMMC HW partition. By partition selection I mean the access to eMMC via the ext_csd[179] register programming. It sometimes happens that it is necessary to write to other partitions. This patch adds extra attribute to "raw" sub type of the dfu_alt_info environment variable (e.g. boot-mmc.bin raw 0x0 0x200 mmcpart 1;) It saves the original boot value and restores it after storing the file. Signed-off-by: Lukasz Majewski diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c index 5e10ea7..63cc876 100644 --- a/drivers/dfu/dfu_mmc.c +++ b/drivers/dfu/dfu_mmc.c @@ -18,11 +18,29 @@ static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE) 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 @@ -39,6 +57,13 @@ static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu, 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); @@ -57,9 +82,17 @@ static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu, 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; } @@ -194,6 +227,8 @@ int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf, * 2nd and 3rd: * lba_start and lba_size, for raw write * mmc_dev and mmc_part, for filesystems and part + * 4th (optional): + * mmcpart (access to HW eMMC partitions) */ int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s) { @@ -233,11 +268,22 @@ 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; diff --git a/include/dfu.h b/include/dfu.h index 986598e..26ffbc8 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -43,6 +43,9 @@ struct mmc_internal_data { unsigned int lba_size; unsigned int lba_blk_size; + /* eMMC HW partition access */ + int hw_partition; + /* FAT/EXT */ unsigned int dev; unsigned int part; -- cgit v0.10.2