From 213fa47dacf07d11f094ff58a5695cd0c425e164 Mon Sep 17 00:00:00 2001 From: Ye Li Date: Mon, 29 Jun 2020 10:12:26 +0800 Subject: usb: gadget: Fix controller index in UMS The usb mass storage (f_mass_storage.c) uses fixed usb index 0, this causes problem while CDNS3 USB controller index is 1. Modify the API of fsg to pass the controller index. Reviewed-by: Jun Li Signed-off-by: Ye Li Signed-off-by: Peng Fan --- drivers/usb/gadget/f_mass_storage.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/usb/gadget') 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; } -- cgit From 1468a1cc72afa210c35a4d0ed533de29110de648 Mon Sep 17 00:00:00 2001 From: Ye Li Date: Mon, 29 Jun 2020 10:12:59 +0800 Subject: usb: ci_udc: Add function to remove usb device When unregister gadget driver in ci_udc, the usb device is not removed or stop. This causes next "usb start" fails to work. Add a new interface "usb_remove_ehci_gadget" in usb-uclass to remove the usb device for DM driver. Using "usb_lowlevel_stop" for non-DM driver. Signed-off-by: Ye Li Signed-off-by: Peng Fan --- drivers/usb/gadget/ci_udc.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/usb/gadget') 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; } -- cgit From 2b2a771b40876c3db456705d5dcc5b60249d4075 Mon Sep 17 00:00:00 2001 From: Roman Kovalivskyi Date: Tue, 28 Jul 2020 23:35:33 +0300 Subject: fastboot: Add support for 'reboot fastboot' command Android 10 adds support for dynamic partitions and in order to support this userspace fastboot must be used[1]. New tool fastbootd is included into recovery. Userspace fastboot works from recovery and is launched if: 1) - Dynamic partitioning is enabled 2) - Boot control block has 'boot-fastboot' value into command field The bootloader is expected to load and boot into the recovery image upon seeing boot-fastboot in the BCB command. Recovery then parses the BCB message and switches to fastbootd mode[2]. Please note that boot script is expected to handle 'boot-fastboot' command in BCB and load into recovery mode. Bootloader must support 'reboot fastboot' command which should reboot device into userspace fastboot to accomodate those changes[3]. Another command that bootloader must support[3] is 'reboot recovery'. This command should simply reboot device into recovery mode. [1] - https://source.android.com/devices/bootloader/fastbootd [2] - https://source.android.com/devices/bootloader/fastbootd#unified_fastboot_and_recovery [3] - https://source.android.com/devices/bootloader/fastbootd#modifications_to_the_bootloader Signed-off-by: Roman Kovalivskyi Signed-off-by: Roman Stratiienko Change-Id: I9d2bdc9a6f6f31ea98572fe155e1cc8341e9af76 --- drivers/usb/gadget/f_fastboot.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index 384c0f6f6e..30f7a52087 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/f_fastboot.c @@ -455,6 +455,8 @@ 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; } -- cgit From 7ed4eac43d672b8f6cefa6bf92fcaacf7dda0f7c Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Wed, 29 Jul 2020 20:51:27 -0500 Subject: usb: max3420: add the gadget driver MAX3420 implements FullSpeed USB Device over SPI. Another version MAX3421, also implements USB Host mode. This driver should be good for the device mode of max3421 as well. Signed-off-by: Jassi Brar --- drivers/usb/gadget/Kconfig | 6 + drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/gadget_chips.h | 8 + drivers/usb/gadget/max3420_udc.c | 875 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 890 insertions(+) create mode 100644 drivers/usb/gadget/max3420_udc.c (limited to 'drivers/usb/gadget') 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/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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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), +}; -- cgit From d10d429112b78c69099c57fa219230539502e543 Mon Sep 17 00:00:00 2001 From: Ye Li Date: Tue, 18 Aug 2020 18:16:44 +0800 Subject: f_sdp: Add high speed endpoint descriptor Add HS endpoint descriptor for SDP. So that we can use high speed endpoint, and the SDP device can send packet with 512 byte size. Signed-off-by: Ye Li Signed-off-by: Peng Fan Reviewed-by: Lukasz Majewski --- drivers/usb/gadget/f_sdp.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/f_sdp.c b/drivers/usb/gadget/f_sdp.c index f2fe89d2a6..f971ccdeca 100644 --- a/drivers/usb/gadget/f_sdp.c +++ b/drivers/usb/gadget/f_sdp.c @@ -159,6 +159,16 @@ static struct usb_endpoint_descriptor in_desc = { .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 = 1, +}; + static struct usb_descriptor_header *sdp_runtime_descs[] = { (struct usb_descriptor_header *)&sdp_intf_runtime, (struct usb_descriptor_header *)&sdp_hid_desc, @@ -166,6 +176,13 @@ static struct usb_descriptor_header *sdp_runtime_descs[] = { 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, + NULL, +}; + /* This is synchronized with what the SoC implementation reports */ static struct hid_report sdp_hid_report = { .usage_page = { @@ -489,6 +506,11 @@ static int sdp_bind(struct usb_configuration *c, struct usb_function *f) goto error; } + if (gadget_is_dualspeed(gadget)) { + /* Assume endpoint addresses are the same for both speeds */ + in_hs_desc.bEndpointAddress = in_desc.bEndpointAddress; + } + sdp->in_ep = ep; /* Store IN EP for enabling @ setup */ cdev->req->context = sdp; @@ -541,11 +563,15 @@ 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); + else + result = usb_ep_enable(sdp->in_ep, &in_desc); if (result) return result; sdp->in_req = sdp_start_ep(sdp->in_ep); @@ -591,7 +617,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; -- cgit From 5dee7f0b0233f8b4e43e1212a6a96cdee835a8e6 Mon Sep 17 00:00:00 2001 From: Ye Li Date: Tue, 18 Aug 2020 18:16:45 +0800 Subject: f_sdp: Fix wrong usb request size Because the buffer length of sdp usb request is 65, we have to allocate 65 bytes not 64 bytes. Otherwise there is potential buffer overflow. Signed-off-by: Ye Li Signed-off-by: Peng Fan Reviewed-by: Lukasz Majewski --- drivers/usb/gadget/f_sdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/f_sdp.c b/drivers/usb/gadget/f_sdp.c index f971ccdeca..eec7560fc2 100644 --- a/drivers/usb/gadget/f_sdp.c +++ b/drivers/usb/gadget/f_sdp.c @@ -548,7 +548,7 @@ static struct usb_request *sdp_start_ep(struct usb_ep *ep) { struct usb_request *req; - req = alloc_ep_req(ep, 64); + req = alloc_ep_req(ep, 65); debug("%s: ep:%p req:%p\n", __func__, ep, req); if (!req) -- cgit From b0e9f3e593c0ed1356ff05d99d7a562dc4bf228d Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Tue, 18 Aug 2020 18:16:46 +0800 Subject: f_sdp: Support searching and loading FIT or container image Add support to f_sdp to search and load iMX8 container image or iMX8M FIT image by new UUU command SDPV. When using the SDPV, the uuu will continue to send out data after first level boot loader used by ROM. This means uuu won't skip to the offset of the second boot loader, and the padding data before second boot loader will be sent out. So we have to search the FIT header or container header in the buffer that SDP received. Also change to more common method to exit f_sdp handler not depending on SPL_FIT_FOUND flag because container loader won't set this. Signed-off-by: Peng Fan --- drivers/usb/gadget/f_sdp.c | 70 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 12 deletions(-) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/f_sdp.c b/drivers/usb/gadget/f_sdp.c index eec7560fc2..9b7372815d 100644 --- a/drivers/usb/gadget/f_sdp.c +++ b/drivers/usb/gadget/f_sdp.c @@ -71,6 +71,8 @@ struct hid_report { #define SDP_COMMAND_LEN 16 +#define SDP_EXIT 1 + struct sdp_command { u16 cmd; u32 addr; @@ -667,19 +669,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; @@ -731,6 +757,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 @@ -740,13 +775,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); @@ -769,6 +814,8 @@ static void sdp_handle_in_ep(struct spl_image_info *spl_image) default: break; }; + + return 0; } #ifndef CONFIG_SPL_BUILD @@ -777,6 +824,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()) { @@ -784,18 +832,16 @@ 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 } } -- cgit From 9e06c5c55a60bea001c82ad32f48177eddda0d38 Mon Sep 17 00:00:00 2001 From: Sherry Sun Date: Tue, 18 Aug 2020 18:16:48 +0800 Subject: f_sdp: Add EP1_OUT as default data receive pipe in sdp EP0 has been used to transfer file data in sdp before, but the max packetsize of ep0 is 64 bytes. So in order to improve the file transfer speed, here add the EP1_OUT interrupt endpoint which max packetsize is set to 1024 byte. After testing, it turns out that using ep1out is twice as fast as using ep0 while receiving data in sdp. Signed-off-by: Sherry Sun Reviewed-by: Ye Li Signed-off-by: Peng Fan --- drivers/usb/gadget/f_sdp.c | 123 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 107 insertions(+), 16 deletions(-) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/f_sdp.c b/drivers/usb/gadget/f_sdp.c index 9b7372815d..c5b35945c1 100644 --- a/drivers/usb/gadget/f_sdp.c +++ b/drivers/usb/gadget/f_sdp.c @@ -71,6 +71,8 @@ struct hid_report { #define SDP_COMMAND_LEN 16 +#define SDP_HID_PACKET_SIZE_EP1 1024 + #define SDP_EXIT 1 struct sdp_command { @@ -84,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, @@ -116,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; @@ -131,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, @@ -161,6 +169,16 @@ 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*/ @@ -171,10 +189,21 @@ static struct usb_endpoint_descriptor in_hs_desc = { .bInterval = 1, }; +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 = 1, +}; + 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, }; @@ -182,6 +211,7 @@ 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, }; @@ -347,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); @@ -370,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); @@ -384,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: @@ -465,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; } } @@ -499,11 +533,17 @@ 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; } @@ -511,9 +551,11 @@ static int sdp_bind(struct usb_configuration *c, struct usb_function *f) 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; /* Store IN EP for enabling @ setup */ + sdp->in_ep = ep_in; /* Store IN EP for enabling @ setup */ + sdp->out_ep = ep_out; cdev->req->context = sdp; @@ -546,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, 65); + 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; } @@ -570,19 +623,27 @@ static int sdp_set_alt(struct usb_function *f, unsigned intf, unsigned alt) debug("%s: intf: %d alt: %d\n", __func__, intf, alt); - if (gadget_is_dualspeed(gadget) && gadget->speed == USB_SPEED_HIGH) + if (gadget_is_dualspeed(gadget) && gadget->speed == USB_SPEED_HIGH) { result = usb_ep_enable(sdp->in_ep, &in_hs_desc); - else + 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; } @@ -599,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) @@ -818,6 +886,27 @@ static int sdp_handle_in_ep(struct spl_image_info *spl_image) 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 int sdp_handle(int controller_index) #else @@ -843,6 +932,8 @@ int spl_sdp_handle(int controller_index, struct spl_image_info *spl_image) #else flag = sdp_handle_in_ep(NULL); #endif + if (sdp_func->ep_int_enable) + sdp_handle_out_ep(); } } -- cgit From 405217a0332aa33e33fb579d75bc7f420c27bcd1 Mon Sep 17 00:00:00 2001 From: Sherry Sun Date: Tue, 18 Aug 2020 18:16:49 +0800 Subject: f_sdp: Change bInterval of interrupt endpoint to 3 Since the USB HID limits the maximum bandwidth(3072) for interrupt endpoint transfers, when the bInterval set to 1, we can only support 3 boards to run sdp at the same time. In order to support more boards, change the bInterval of interrupt endpoint to 3, which will not affect the transmission speed. Reviewed-by: Ye Li Signed-off-by: Sherry Sun Signed-off-by: Peng Fan --- drivers/usb/gadget/f_sdp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/f_sdp.c b/drivers/usb/gadget/f_sdp.c index c5b35945c1..e48aa2f90d 100644 --- a/drivers/usb/gadget/f_sdp.c +++ b/drivers/usb/gadget/f_sdp.c @@ -186,7 +186,7 @@ static struct usb_endpoint_descriptor in_hs_desc = { .bEndpointAddress = 1 | USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, .wMaxPacketSize = 512, - .bInterval = 1, + .bInterval = 3, }; static struct usb_endpoint_descriptor out_hs_desc = { @@ -196,7 +196,7 @@ static struct usb_endpoint_descriptor out_hs_desc = { .bEndpointAddress = 1 | USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_INT, .wMaxPacketSize = SDP_HID_PACKET_SIZE_EP1, - .bInterval = 1, + .bInterval = 3, }; static struct usb_descriptor_header *sdp_runtime_descs[] = { -- cgit From 64af06ce91d1b2f7819a273e56f7c41186a7588b Mon Sep 17 00:00:00 2001 From: "yurii.pidhornyi" Date: Thu, 20 Aug 2020 18:41:18 +0300 Subject: fastboot: Fix fastboot reboot fail by changing functions order It was revealed that when the fastboot_tx_write_str function is called without the previously initialized fastboot_func->in_req->complete field, a copy of in_req will be sent to the I/O requests queue without an initialized field. Moving a piece of code with the initializing of the fastboot_func->in_req->complete field above transmit_tx allows to solve this problem. Fixes: 65c96757fe9 "usb: fastboot: Convert USB f_fastboot to shared fastboot" Signed-off-by: yurii.pidhornyi --- drivers/usb/gadget/f_fastboot.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index 30f7a52087..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: @@ -462,6 +460,8 @@ static void rx_handler_command(struct usb_ep *ep, struct usb_request *req) } } + fastboot_tx_write_str(response); + *cmdbuf = '\0'; req->actual = 0; usb_ep_queue(ep, req, 0); -- cgit