
Implement the UDC support for the USB OTG interface.
Signed-off-by: Steve Rae srae@broadcom.com --- This commit is not checkpatch clean - however, there seems to be no way to remove this warning: "warning: drivers/usb/gadget/bcm_udc_otg.c,97: Adding new packed members is to be done with care"
drivers/usb/gadget/bcm_udc_otg.c | 1193 ++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/bcm_udc_otg.h | 19 + 2 files changed, 1212 insertions(+) create mode 100644 drivers/usb/gadget/bcm_udc_otg.c create mode 100644 drivers/usb/gadget/bcm_udc_otg.h
diff --git a/drivers/usb/gadget/bcm_udc_otg.c b/drivers/usb/gadget/bcm_udc_otg.c new file mode 100644 index 0000000..706e003 --- /dev/null +++ b/drivers/usb/gadget/bcm_udc_otg.c @@ -0,0 +1,1193 @@ +/* + * Copyright 2015 Broadcom Corporation. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <config.h> +#include <asm/io.h> +#include <asm/arch/sysmap.h> +#include <asm/kona-common/clk.h> +#include "bcm_udc_otg.h" + +#ifdef CONFIG_CMD_SERIALNO +#include "serial_no.h" +#endif + +#include <usbdevice.h> + +#define DEVICE_STRING_LANGUAGE_ID_INDEX 0 +#define DEVICE_STRING_PRODUCT_INDEX 1 +#define DEVICE_STRING_SERIAL_NUMBER_INDEX 2 +#define DEVICE_STRING_CONFIG_INDEX 3 +#define DEVICE_STRING_INTERFACE_INDEX 4 +#define DEVICE_STRING_MANUFACTURER_INDEX 5 +#define DEVICE_STRING_MAX_INDEX DEVICE_STRING_MANUFACTURER_INDEX + +#define FASTBOOT_INTERFACE_CLASS 0xff +#define FASTBOOT_INTERFACE_SUB_CLASS 0x42 +#define FASTBOOT_INTERFACE_PROTOCOL 0x03 + +#define wfld_set(addr, fld_val, fld_mask) \ + (writel(((readl(addr) & ~(fld_mask)) | (fld_val)), (addr))) +#define wfld_clear(addr, fld_mask) \ + (writel((readl(addr) & ~(fld_mask)), (addr))) + +#define DEVICE_STRING_LANGUAGE_ID 0x0409 /* English (United States) */ + +/* + * In high speed mode rx packets are 512 + * In full speed mode rx packets are 64 + */ +#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE (0x0200) +#define TX_ENDPOINT_MAXIMUM_PACKET_SIZE (0x0040) + +#ifndef CONFIG_USB_BOARDNAME +#define CONFIG_USB_BOARDNAME "Board" +#endif + +#ifndef CONFIG_USB_CONFIGURATION +#define CONFIG_USB_CONFIGURATION "Fastboot" +#endif + +#ifndef CONFIG_USB_INTERFACE +#define CONFIG_USB_INTERFACE "Fastboot" +#endif + +#ifndef CONFIG_USB_SERIALNO +#define CONFIG_USB_SERIALNO "1234567890" +#endif + +#ifndef CONFIG_USBID_ADDR +#error CONFIG_USBID_ADDR must be defined! +#endif + +static char *usb_device_strings[DEVICE_STRING_MANUFACTURER_INDEX + 1] = { + /* Language = */ "", + /* Product = */ CONFIG_USB_BOARDNAME, + /* Serial = */ CONFIG_USB_SERIALNO, + /* Config = */ CONFIG_USB_CONFIGURATION, + /* Interface = */ CONFIG_USB_INTERFACE, + /* Manufacturer = */ CONFIG_G_DNL_MANUFACTURER, +}; + +static struct usb_device_descriptor dfu_dev_descriptor + __aligned(4) = { + .bLength = 0x12, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = 0x100, + .bDeviceClass = 0xff, + .bDeviceSubClass = 0xff, + .bDeviceProtocol = 0xff, + .bMaxPacketSize0 = 64, /* depends on enum speed */ + .idVendor = CONFIG_G_DNL_VENDOR_NUM, + .idProduct = CONFIG_G_DNL_PRODUCT_NUM, + .bcdDevice = 0x0001, + .iManufacturer = DEVICE_STRING_MANUFACTURER_INDEX, + .iProduct = DEVICE_STRING_PRODUCT_INDEX, + .iSerialNumber = DEVICE_STRING_SERIAL_NUMBER_INDEX, + .bNumConfigurations = 0x1, +}; + +struct full_configuration_descriptor { + struct usb_configuration_descriptor c; + struct usb_interface_descriptor i; + struct usb_endpoint_descriptor e1; + struct usb_endpoint_descriptor e2; +} __packed; + +static struct full_configuration_descriptor full_desc + __aligned(4) = { +.c = { + .bLength = 0x9, + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = sizeof(struct usb_configuration_descriptor) + + sizeof(struct usb_interface_descriptor) + + sizeof(struct usb_endpoint_descriptor) + + sizeof(struct usb_endpoint_descriptor), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = DEVICE_STRING_CONFIG_INDEX, + .bmAttributes = BMATTRIBUTE_RESERVED | + BMATTRIBUTE_SELF_POWERED, + .bMaxPower = 0, +}, +.i = { + .bLength = 0x9, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0x0, + .bAlternateSetting = 0x0, /* CheckMe */ + .bNumEndpoints = 0x2, + .bInterfaceClass = FASTBOOT_INTERFACE_CLASS, + .bInterfaceSubClass = FASTBOOT_INTERFACE_SUB_CLASS, + .bInterfaceProtocol = FASTBOOT_INTERFACE_PROTOCOL, + .iInterface = DEVICE_STRING_INTERFACE_INDEX, +}, +.e1 = { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN | 1, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = TX_ENDPOINT_MAXIMUM_PACKET_SIZE, + .bInterval = 0x00, +}, +.e2 = { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT | 1, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = RX_ENDPOINT_MAXIMUM_PACKET_SIZE, + .bInterval = 0x00, +}, +}; + +static rx_type rx_callback; + +#define EP0_SETUP_BUF_SIZE 16 /* hold 2 setup packets */ +#define EP0_IN_BUF_SIZE 128 +#define EP0_OUT_BUF_SIZE 16 + +#define RX_FIFO_SIZE 512 /* 512 x 4 bytes */ +#define TX_FIFO_SIZE 256 /* 256 x 4 bytes */ +#define TX_FIFO_OFFSET RX_FIFO_SIZE +#define TX_FIFO1_SIZE 256 /* 256 x 4 bytes */ +#define TX_FIFO1_OFFSET (TX_FIFO_SIZE + TX_FIFO_OFFSET) + +#define TURN_AROUND_TIME 9 + +#define EP_MISMATCH_CNT 1 +#define PERIODIC_FRM_INTERVAL 1 +#define BC11_CFG_VDP_OFF 0x55570000 + +#define EP1_IN_BUF_SIZE 512 +#define EP1_OUT_BUF_SIZE 512 + +#define HSOTG_CTRL_STATUS_OFFSET HSOTG_CTRL_BC_STATUS_OFFSET +#define HSOTG_CTRL_STATUS_SHP_MASK HSOTG_CTRL_BC_STATUS_SDP_MASK +#define HSOTG_CTRL_CFG_OFFSET HSOTG_CTRL_BC_CFG_OFFSET +#define HSOTG_CTRL_CFG_OVWR_KEY_MASK HSOTG_CTRL_BC_CFG_BC_OVWR_KEY_MASK +#define HSOTG_CTRL_CFG_SW_OVWR_EN_MASK HSOTG_CTRL_BC_CFG_SW_OVWR_EN_MASK +#define HSOTG_CTRL_CFG_OVWR_SET_M0_MASK HSOTG_CTRL_BC_CFG_BC_OVWR_SET_M0_MASK +#define HSOTG_CTRL_CFG_OVWR_SET_P0_MASK HSOTG_CTRL_BC_CFG_BC_OVWR_SET_P0_MASK + +/* Align buffers to 32 bytes so cache invalidate/flush routines work. */ +static uint32_t ep0_setup_buf[EP0_SETUP_BUF_SIZE] __aligned(32); +static uint32_t ep0_out_buf[EP0_OUT_BUF_SIZE] __aligned(32); +static uint32_t ep0_in_buf[EP0_IN_BUF_SIZE] __aligned(32); + +static uint8_t ep1_out_buf[2][EP1_OUT_BUF_SIZE] __aligned(32); +static uint8_t ep1_in_buf[EP1_IN_BUF_SIZE] __aligned(32); + +static int ep1_out_buf_sel; +static uint32_t usb_speed; + +/** + * @brief: usb_soft_reset - Soft Reset USB Core. + * @param[in]: none + * @param[out]: none + * @return: 0 + */ +static int usb_soft_reset(void) +{ + uint32_t val; + + /* Add hclk soft reset after interface setting */ + writel(HSOTG_RSTCTL_CSFTRST_MASK, + HSOTG_BASE_ADDR + HSOTG_RSTCTL_OFFSET); + + udelay(1000); + + /* Poll until Reset complete and AHB idle */ + do { + val = readl(HSOTG_BASE_ADDR + HSOTG_RSTCTL_OFFSET); + } while ((val & HSOTG_RSTCTL_CSFTRST_MASK) || + (!(val & HSOTG_RSTCTL_AHBIDLE_MASK))); + + return 0; +} + +/** + * @brief: setup_device_fifo - Configure USB FIFO. + * @param[in]: none + * @param[out]: none + * @return: none + */ +static void setup_device_fifo(void) +{ + /* Receive FIFO size register; +512*4bytes, number is in terms of 32bits */ + writel((RX_FIFO_SIZE << HSOTG_RXFSIZ_RXFDEP_SHIFT), + HSOTG_BASE_ADDR + HSOTG_RXFSIZ_OFFSET); + + /* + * Receive FIFO size register + * 256*4bytes -- Tx FIFO depth + * 512*4bytes -- TX RAM start address + * (number is in terms of 32bits) + */ + writel((TX_FIFO_SIZE << HSOTG_NPTXFSIZ_NPTXFDEP_SHIFT) | + (TX_FIFO_OFFSET << HSOTG_NPTXFSIZ_NPTXFSTADDR_SHIFT), + HSOTG_BASE_ADDR + HSOTG_NPTXFSIZ_OFFSET); + + writel((TX_FIFO1_SIZE << HSOTG_DIEPTXF1_INEPNTXFDEP_SHIFT) | + (TX_FIFO1_OFFSET << HSOTG_DIEPTXF1_INEPNTXFSTADDR_SHIFT), + HSOTG_BASE_ADDR + HSOTG_DIEPTXF1_OFFSET); +} + +/** + * @brief: dfu_setup_device_mode - Configure USB device mode. + * @param[in]: none + * @param[out]: none + * @return: 0 or ERROR ID + */ +static int dfu_setup_device_mode(void) +{ + setup_device_fifo(); + + writel(HSOTG_AHBCFG_NPTXFEMPLVL_MASK | + /* int indicates TXFIFO empty */ + HSOTG_AHBCFG_DMAEN_MASK | + /* DMA mode */ + HSOTG_AHBCFG_GLBLINTRMSK_MASK, + /* Unmask the interrupt assertion */ + HSOTG_BASE_ADDR + HSOTG_AHBCFG_OFFSET); + + writel(TURN_AROUND_TIME << HSOTG_CFG_USBTRDTIM_SHIFT, + HSOTG_BASE_ADDR + HSOTG_CFG_OFFSET); + + /* UTMI+ 8bit interface */ + wfld_clear(HSOTG_BASE_ADDR + HSOTG_CFG_OFFSET, + HSOTG_CFG_ULPI_UTMI_SEL_MASK); + + writel((EP_MISMATCH_CNT << HSOTG_DCFG_EPMISCNT_SHIFT) | + /* IN Mismatch Cnt */ + (PERIODIC_FRM_INTERVAL << HSOTG_DCFG_PERFRINT_SHIFT), + /* Frame Interval */ + HSOTG_BASE_ADDR + HSOTG_DCFG_OFFSET); + + /* check if OTG is in device mode */ + if (readl(HSOTG_BASE_ADDR + HSOTG_INTSTS_OFFSET) & + HSOTG_INTSTS_CURMOD_MASK) { + error("Not in device mode"); + return 1; + } + + return 0; +} + +/** + * @brief: usb_clr_interrupt - Clear USB interrupt. + * @param[in]: none + * @param[out]: none + * @return: 0 + */ +static int usb_clr_interrupt(void) +{ + /* clear all interrupts */ + writel(0xffffffff, HSOTG_BASE_ADDR + HSOTG_INTSTS_OFFSET); + return 0; +} + +/** + * @brief: usb_phy_connect - Connect USB Phy to host. + * @param[in]: none + * @param[out]: none + * @return: 0 + */ +static int usb_phy_connect(void) +{ + /* set Phy to driving mode */ + wfld_clear(HSOTG_CTRL_BASE_ADDR + HSOTG_CTRL_PHY_P1CTL_OFFSET, + HSOTG_CTRL_PHY_P1CTL_NON_DRIVING_MASK); + + udelay(100); + + /* Clear Soft Disconnect */ + wfld_clear(HSOTG_BASE_ADDR + HSOTG_DCTL_OFFSET, + HSOTG_DCTL_SFTDISCON_MASK); + + /* software reset Phy, active low */ + wfld_clear(HSOTG_CTRL_BASE_ADDR + HSOTG_CTRL_PHY_P1CTL_OFFSET, + HSOTG_CTRL_PHY_P1CTL_SOFT_RESET_MASK); + + udelay(10000); + + /* */ + wfld_set(HSOTG_CTRL_BASE_ADDR + HSOTG_CTRL_PHY_P1CTL_OFFSET, + HSOTG_CTRL_PHY_P1CTL_SOFT_RESET_MASK, + HSOTG_CTRL_PHY_P1CTL_SOFT_RESET_MASK); + + return 0; +} + +/** + * @brief: usb_phy_disconnect - disconnect USB Phy from Host. + * @param[in]: none + * @param[out]: none + * @return: 0 + */ +static int usb_phy_disconnect(void) +{ + /* Soft Disconnect */ + wfld_set(HSOTG_BASE_ADDR + HSOTG_DCTL_OFFSET, + HSOTG_DCTL_SFTDISCON_MASK, + HSOTG_DCTL_SFTDISCON_MASK); + + /* set Phy to non-driving (reset) mode */ + wfld_set(HSOTG_CTRL_BASE_ADDR + HSOTG_CTRL_PHY_P1CTL_OFFSET, + HSOTG_CTRL_PHY_P1CTL_NON_DRIVING_MASK, + HSOTG_CTRL_PHY_P1CTL_NON_DRIVING_MASK); + return 0; +} + +/** + * @brief: usb_wait_for_vbus - wait for vbus turning 5.0v by polling STAT2. + * @param[in]: none + * @param[out]: none + * @return: none + */ +static void usb_wait_for_vbus(void) +{ + uint32_t val; + + /* + * If there is no PMU, then the VBUS signal from the connector will + * not necessarily be connected to STAT2. We can get around this by + * telling the usb core to proceed irregardless by triggering the + * core with internal STAT1/STAT2 signals in software. + */ + val = readl(HSOTG_CTRL_BASE_ADDR + HSOTG_CTRL_USBOTGCONTROL_OFFSET); + val |= (HSOTG_CTRL_USBOTGCONTROL_REG_OTGSTAT2_MASK | + HSOTG_CTRL_USBOTGCONTROL_REG_OTGSTAT1_MASK | + HSOTG_CTRL_USBOTGCONTROL_OTGSTAT_CTRL_MASK); + writel(val, HSOTG_CTRL_BASE_ADDR + HSOTG_CTRL_USBOTGCONTROL_OFFSET); +} + +/** + * @brief: usb_turn_off_vdp - disable vdp. + * @param[in]: none + * @param[out]: none + * @return: none + */ +static void usb_turn_off_vdp(void) +{ + /* Check if it is standard host port (SHP) */ + if (readl(HSOTG_CTRL_BASE_ADDR + HSOTG_CTRL_STATUS_OFFSET) & + HSOTG_CTRL_STATUS_SHP_MASK) { + udelay(60000); /* 50 ms + 20 % */ + + /* + * force turn off VDP, enable sw_ovwr_set to take over the + * bc11 switches directly + */ + wfld_set(HSOTG_CTRL_BASE_ADDR + HSOTG_CTRL_CFG_OFFSET, + BC11_CFG_VDP_OFF, + HSOTG_CTRL_CFG_OVWR_KEY_MASK | + HSOTG_CTRL_CFG_SW_OVWR_EN_MASK | + HSOTG_CTRL_CFG_OVWR_SET_M0_MASK | + HSOTG_CTRL_CFG_OVWR_SET_P0_MASK); + + udelay(160); /* Allow time for switches to disengage */ + } else { + udelay(120000); /* 100 ms + 20 % */ + } +} + +/** + * @brief: usb_ep0_setup_prime - Prepare receiving EP0 Setup packet. + * @param[in]: none + * @param[out]: none + * @return: 0 + */ +static int usb_ep0_setup_prime(void) +{ + uint32_t mask; + + invalidate_dcache_range((uint32_t)ep0_setup_buf, + ((uint32_t)ep0_setup_buf + + sizeof(ep0_setup_buf))); + + /* Device OUT Endpoint 0 DMA Address Register */ + writel((uint32_t)ep0_setup_buf, + HSOTG_BASE_ADDR + HSOTG_DOEPDMA0_OFFSET); + + /* Device OUT Endpoint 0 Transfer Size Register */ + writel((0x3 << HSOTG_DOEPTSIZ0_SUPCNT_SHIFT) | /* 3 SUPCnt */ + (0x3 << HSOTG_DOEPTSIZ0_PKTCNT_SHIFT) | /* 3 PktCnt */ + (0x8 << HSOTG_DOEPTSIZ0_XFERSIZE_SHIFT), /* XferSize 8 bytes */ + HSOTG_BASE_ADDR + HSOTG_DOEPTSIZ0_OFFSET); + + /* Device OUT Endpoint 0 Control Register */ + mask = HSOTG_DOEPCTL0_EPENA_MASK | /* Endpoint Enable */ + HSOTG_DOEPCTL0_CNAK_MASK; /* clear NAK bit */ + wfld_set(HSOTG_BASE_ADDR + HSOTG_DOEPCTL0_OFFSET, mask, mask); + + return 0; +} + +/** + * @brief: usb_ep0_recv_data - Receive packet on EP0-OUT endpoint. + * @param[in]: uint8_t *data_buf - Data packet buffer + * @param[in]: uint32_t cnt - Number of bytes to receive + * @param[out]: none + * @return: 0 or ERROR ID + */ +static int usb_ep0_recv_data(uint8_t *data_buf, uint32_t cnt) +{ + uint32_t mask; + + if (cnt > sizeof(ep0_out_buf)) { + printf("EP0_OUT overflow\n"); + return 1; + } + + invalidate_dcache_range((uint32_t)ep0_out_buf, + (uint32_t)ep0_out_buf + sizeof(ep0_out_buf)); + + /* Device OUT Endpoint 0 DMA Address Register */ + writel((uint32_t)ep0_out_buf, + HSOTG_BASE_ADDR + HSOTG_DOEPDMA0_OFFSET); + + /* Device OUT Endpoint 0 Transfer Size Register */ + writel((1 << HSOTG_DOEPTSIZ0_PKTCNT_SHIFT) | cnt, + HSOTG_BASE_ADDR + HSOTG_DOEPTSIZ0_OFFSET); + + /* Device OUT Endpoint 0 Control Register */ + mask = HSOTG_DOEPCTL0_EPENA_MASK | /* Endpoint Enable */ + HSOTG_DOEPCTL0_CNAK_MASK; /* clear NAK bit */ + wfld_set(HSOTG_BASE_ADDR + HSOTG_DOEPCTL0_OFFSET, mask, mask); + + if (cnt > 0) + memcpy(data_buf, ep0_out_buf, cnt); + + return 0; +} + +/** + * @brief: usb_ep0_recv_zlp - Receive zero length packet on EP0-OUT endpoint. + * @param[in]: none + * @param[out]: none + * @return: 0 + */ +static int usb_ep0_recv_zlp(void) +{ + usb_ep0_recv_data(NULL, 0); + return 0; +} + +/** + * @brief: usb_ep0_send_data - Send packet on EP0-IN endpoint. + * @param[in]: uint8_t *data_buf - Data packet to send + * @param[in]: uint32_t cnt - Number of bytes to send + * @param[out]: none + * @return: 0 or ERROR ID + */ +static int usb_ep0_send_data(uint8_t *data_buf, uint32_t cnt) +{ + if (cnt > sizeof(ep0_in_buf)) { + printf("EP0_IN overflow\n"); + return 1; + } else if (cnt > 0) { + /* use local DMA buffer */ + memcpy(ep0_in_buf, data_buf, cnt); + } + + flush_dcache_range((uint32_t)ep0_in_buf, + (uint32_t)ep0_in_buf + sizeof(ep0_in_buf)); + + /* Device IN Endpoint 0 DMA Address Register */ + writel((uint32_t)ep0_in_buf, + HSOTG_BASE_ADDR + HSOTG_DIEPDMA0_OFFSET); + + /* Device IN Endpoint 0 Transfer Size Register */ + writel((1 << HSOTG_DIEPTSIZ0_PKTCNT_SHIFT) | /* 1 pakcnt */ + (cnt << HSOTG_DIEPTSIZ0_XFERSIZE_SHIFT), /* XferSize cnt */ + HSOTG_BASE_ADDR + HSOTG_DIEPTSIZ0_OFFSET); + + /* Device IN Endpoint 0 Control Register */ + writel(HSOTG_DIEPCTL0_EPENA_MASK | /* Endpoint Enable */ + HSOTG_DIEPCTL0_CNAK_MASK | /* clear NAK BIT */ + HSOTG_DIEPCTL0_USBACTEP_MASK | /* USB ACTIVE EP */ + (0 << HSOTG_DIEPCTL0_NEXTEP_SHIFT), /* next EP */ + HSOTG_BASE_ADDR + HSOTG_DIEPCTL0_OFFSET); + + return 0; +} + +/** + * @brief: usb_ep0_send_zlp - Send zero length packet on EP0-IN endpoint. + * @param[in]: none + * @param[out]: none + * @return: 0 + */ +static int usb_ep0_send_zlp(void) +{ + usb_ep0_send_data(NULL, 0); + return 0; +} + +/** + * @brief: usb_ep0_send_stall - Send STALL condition on EP0-IN endpoint. + * @param[in]: none + * @param[out]: none + * @return: 0 + */ +static int usb_ep0_send_stall(void) +{ + /* Device Control IN Endpoint 0 Control Register */ + wfld_set(HSOTG_BASE_ADDR + HSOTG_DIEPCTL0_OFFSET, + HSOTG_DIEPCTL0_STALL_MASK, HSOTG_DIEPCTL0_STALL_MASK); + usb_ep0_setup_prime(); + return 0; +} + +/** + * @brief: usb_ep1_out_prime - Prepare receiving EP1-OUT packet. + * @param[in]: none + * @param[out]: none + * @return: 0 + */ +static int usb_ep1_out_prime(void) +{ + /* toggle which buffer is used */ + ep1_out_buf_sel = ep1_out_buf_sel ? 0 : 1; + + invalidate_dcache_range((uint32_t)ep1_out_buf[ep1_out_buf_sel], + (uint32_t)ep1_out_buf[ep1_out_buf_sel] + + sizeof(ep1_out_buf[ep1_out_buf_sel])); + + /* Device OUT Endpoint 1 DMA Address Register */ + writel((uint32_t)ep1_out_buf[ep1_out_buf_sel], + HSOTG_BASE_ADDR + HSOTG_DOEPDMA1_OFFSET); + + /* Device OUT Endpoint 1 Transfer Size Register */ + writel((0x1 << HSOTG_DOEPTSIZ1_PKTCNT_SHIFT) | + /* 1 PktCnt */ + (EP1_OUT_BUF_SIZE << HSOTG_DOEPTSIZ1_XFERSIZE_SHIFT), + /* XferSize */ + HSOTG_BASE_ADDR + HSOTG_DOEPTSIZ1_OFFSET); + + /* Device OUT Endpoint 1 Control Register */ + writel((1 << HSOTG_DOEPCTL1_EPENA_SHIFT) | /* Endpoint Enable */ + (2 << HSOTG_DOEPCTL1_EPTYPE_SHIFT) | /* Bulk Endpoint */ + (1 << HSOTG_DOEPCTL1_CNAK_SHIFT) | /* clear NAK bit */ + (1 << HSOTG_DOEPCTL1_USBACTEP_SHIFT) | /* USB ACTIVE EP */ + (RX_ENDPOINT_MAXIMUM_PACKET_SIZE << HSOTG_DOEPCTL1_MPS_SHIFT), + HSOTG_BASE_ADDR + HSOTG_DOEPCTL1_OFFSET); + + /* */ + writel(0x00030003, HSOTG_BASE_ADDR + HSOTG_DAINTMSK_OFFSET); + + return 0; +} + +/** + * @brief: usb_ep1_in_prime - Prepare for sending EP1-IN packet. + * @param[in]: none + * @param[out]: none + * @return: 0 + */ +static int usb_ep1_in_prime(void) +{ + /* Device Control IN Endpoint 1 Control Register */ + writel((2 << HSOTG_DIEPCTL1_EPTYPE_SHIFT) | /* Bulk Endpoint */ + (1 << HSOTG_DIEPCTL1_CNAK_SHIFT) | /* clear NAK BIT */ + (1 << HSOTG_DIEPCTL1_USBACTEP_SHIFT) | /* USB ACTIVE EP */ + (1 << HSOTG_DIEPCTL1_NEXTEP_SHIFT) | /* next EP */ + (1 << HSOTG_DIEPCTL1_TXFNUM_SHIFT) | /* use FIFO1 for ep1 */ + (TX_ENDPOINT_MAXIMUM_PACKET_SIZE << HSOTG_DIEPCTL1_MPS_SHIFT), + HSOTG_BASE_ADDR + HSOTG_DIEPCTL1_OFFSET); + + return 0; +} + +/** + * @brief: usb_send_data_ep1_in - Send packet on EP1-IN endpoint. + * @param[in]: uint8_t *data_buf - Data packet to send + * @param[in]: uint32_t cnt - Number of bytes to send + * @param[out]: none + * @return: 0 or ERROR ID + */ +static int usb_send_data_ep1_in(uint8_t *data_buf, uint32_t cnt) +{ + if (cnt > sizeof(ep1_in_buf)) { + printf("EP1_IN overflow\n"); + return 1; + } else if (cnt > 0) { + /* use local DMA buffer */ + memcpy(ep1_in_buf, data_buf, cnt); + } + + flush_dcache_range((uint32_t)ep1_in_buf, + (uint32_t)ep1_in_buf + sizeof(ep1_in_buf)); + + /* Device IN Endpoint 1 DMA Address Register */ + writel((uint32_t)ep1_in_buf, + HSOTG_BASE_ADDR + HSOTG_DIEPDMA1_OFFSET); + + /* Device IN Endpoint 1 Transfer Size Register */ + writel((1 << HSOTG_DIEPTSIZ1_PKTCNT_SHIFT) | /* 1 pakcnt */ + (cnt << HSOTG_DIEPTSIZ1_XFERSIZE_SHIFT), /* XferSize cnt */ + HSOTG_BASE_ADDR + HSOTG_DIEPTSIZ1_OFFSET); + + /* Device IN Endpoint 1 Control Register */ + writel((1 << HSOTG_DIEPCTL1_EPENA_SHIFT) | /* Enable EP */ + (2 << HSOTG_DIEPCTL1_EPTYPE_SHIFT) | /* Bulk Endpoint */ + (1 << HSOTG_DIEPCTL1_CNAK_SHIFT) | /* clear NAK BIT */ + (1 << HSOTG_DIEPCTL1_USBACTEP_SHIFT) | /* USB ACTIVE EP */ + (1 << HSOTG_DIEPCTL1_NEXTEP_SHIFT) | /* next EP */ + (1 << HSOTG_DIEPCTL1_TXFNUM_SHIFT) | /* use FIFO1 for ep1 */ + (TX_ENDPOINT_MAXIMUM_PACKET_SIZE << HSOTG_DIEPCTL1_MPS_SHIFT), + HSOTG_BASE_ADDR + HSOTG_DIEPCTL1_OFFSET); + + return 0; +} + +static void process_usb_req_get_descriptor(uint16_t desc_type, + uint16_t desc_index, uint16_t wLength) +{ + switch (desc_type) { + case USB_DT_DEVICE: + /* Reply Device Descriptor */ + debug("DEVICE DESC\n"); + usb_ep0_send_data((uint8_t *)&dfu_dev_descriptor, + min(sizeof(dfu_dev_descriptor), + (size_t)wLength)); + usb_ep0_recv_zlp(); + break; + case USB_DT_CONFIG: + /* Reply Configuration Descriptor */ + debug("CONFIG DESC\n"); + usb_ep0_send_data((uint8_t *)&full_desc, + min(sizeof(full_desc), + (size_t)wLength)); + usb_ep0_recv_zlp(); + break; + case USB_DT_STRING: + debug("STRING DESC\n"); + if (desc_index == DEVICE_STRING_LANGUAGE_ID_INDEX) { + uint8_t temp[100]; + temp[0] = 4; + temp[1] = USB_DT_STRING; + temp[2] = DEVICE_STRING_LANGUAGE_ID & 0xFF; + temp[3] = DEVICE_STRING_LANGUAGE_ID >> 8; + + usb_ep0_send_data(temp, 4); + usb_ep0_recv_zlp(); + } else if (desc_index <= DEVICE_STRING_MAX_INDEX) { + int index; + int sl = strlen(&usb_device_strings[desc_index][0]); + uint8_t temp[100]; + temp[0] = 2 + (2*sl); + temp[1] = USB_DT_STRING; + + for (index = 0; index < sl; index++) { + int i = 2 + (2*index); + temp[i] = usb_device_strings[desc_index][index]; + temp[i + 1] = 0; + } + + usb_ep0_send_data(temp, 2 + (2*sl)); + usb_ep0_recv_zlp(); + } else if (desc_index == 238) { + debug("string index [%d] is ignored\n", desc_index); + usb_ep0_send_stall(); + } else { + error("bad string index [%d]", desc_index); + usb_ep0_send_stall(); + } + break; + default: + error("bad descriptor request"); + usb_ep0_send_stall(); + break; + } +} + +static int process_usb_set_addr(uint32_t dev_addr) +{ + /* Device Configuration Register */ + writel((EP_MISMATCH_CNT << HSOTG_DCFG_EPMISCNT_SHIFT) | + /* IN Mismatch Cnt */ + (PERIODIC_FRM_INTERVAL << HSOTG_DCFG_PERFRINT_SHIFT) | + /* Frame Interval */ + (dev_addr << HSOTG_DCFG_DEVADDR_SHIFT), + /* Device Address */ + HSOTG_BASE_ADDR + HSOTG_DCFG_OFFSET); + + return 0; +} + +/** + * @brief: dfu_ep0_handler - Decode EP0 setup commands and process CH9 requests. + * @param[in]: uint8_t *setup_buf - EP0 packet buffer. + * @param[out]: none + * @return: none + */ +static void dfu_ep0_handler(u8 *setup_buf) +{ + uint16_t wLength; + uint16_t desc_type; + uint16_t desc_index; + struct usb_device_request ctrl; + + memcpy(&ctrl, setup_buf, sizeof(ctrl)); + + wLength = ctrl.wLength; + + if (ctrl.bmRequestType & USB_TYPE_VENDOR) { + debug("USB_TYPE_VENDOR\n"); + if (ctrl.bmRequestType & 0x80) { + /* VENDOR_REQ_GET_USBID */ + if (ctrl.bRequest == 0x3) { + u8 temp[2]; + memcpy(temp, (void *)CONFIG_USBID_ADDR, 2); + usb_ep0_send_data(temp, 2); + usb_ep0_recv_zlp(); + } else { + u8 temp = 0; + usb_ep0_send_data(&temp, 1); + usb_ep0_recv_zlp(); + } + } else { + usb_ep0_setup_prime(); + usb_ep0_send_zlp(); + } + } else if (ctrl.bmRequestType & USB_TYPE_CLASS) { + debug("USB_TYPE_CLASS\n"); + usb_ep0_send_stall(); + } else { + /* CH9 command */ + switch (ctrl.bRequest) { + case USB_REQ_SET_ADDRESS: + debug("SET_ADDR = %d\n", ctrl.wValue); + process_usb_set_addr(ctrl.wValue); + usb_ep0_setup_prime(); + usb_ep0_send_zlp(); + return; + + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + debug("USB_SET/CLR_FEATURE\n"); + if (ctrl.wValue == USB_TEST_MODE) { + unsigned int testctl = ctrl.wIndex >> 8; + + usb_ep0_setup_prime(); + usb_ep0_send_zlp(); + + /* + * Wait till after status phase to set test mode + */ + wfld_set(HSOTG_BASE_ADDR + HSOTG_DCTL_OFFSET, + testctl, HSOTG_DCTL_TSTCTL_MASK); + } else { + usb_ep0_setup_prime(); + usb_ep0_send_zlp(); + } + return; + + case USB_REQ_GET_CONFIGURATION: + debug("GET_CONFIG\n"); + usb_ep0_setup_prime(); + usb_ep0_send_zlp(); + break; + + case USB_REQ_SET_CONFIGURATION: + debug("SET_CONFIG\n"); + usb_ep0_setup_prime(); + usb_ep1_out_prime(); + usb_ep1_in_prime(); + usb_ep0_send_zlp(); + break; + + case USB_REQ_GET_DESCRIPTOR: + debug("GET_DESC -> "); + desc_type = ctrl.wValue >> 8; + desc_index = ctrl.wValue & 0xff; + process_usb_req_get_descriptor(desc_type, + desc_index, + wLength); + break; + + case USB_REQ_SET_INTERFACE: + case USB_REQ_GET_INTERFACE: + debug("USB_SET/GET_INTERFACE\n"); + usb_ep0_setup_prime(); + usb_ep0_send_zlp(); + break; + + case USB_REQ_GET_STATUS: + case USB_REQ_SET_DESCRIPTOR: + case USB_REQ_SYNCH_FRAME: + debug("USB misc CH9 cmd: ignore\n"); + usb_ep0_setup_prime(); + usb_ep0_send_zlp(); + break; + + default: + error("Unknown EP0 cmd"); + usb_ep0_send_stall(); + break; + } + } +} + +/** + * @brief: usb_handle_ep0_in_int - Process USB EP0-IN interrupt. + * @param[in]: none + * @param[out]: none + * @return: 0 + */ +static int usb_handle_ep0_in_int(void) +{ + uint32_t diep0int; + + /* clear Device IN Endpoint 0 Interrupt Register */ + diep0int = readl(HSOTG_BASE_ADDR + HSOTG_DIEPINT0_OFFSET); + writel(diep0int, HSOTG_BASE_ADDR + HSOTG_DIEPINT0_OFFSET); + + return 0; +} + +/** + * @brief: usb_handle_ep1_in_int - Process USB EP1-IN interrupt. + * @param[in]: none + * @param[out]: none + * @return: 0 + */ +static int usb_handle_ep1_in_int(void) +{ + uint32_t diep1int; + + /* clear Device IN Endpoint 1 Interrupt Register */ + diep1int = readl(HSOTG_BASE_ADDR + HSOTG_DIEPINT1_OFFSET); + writel(diep1int, HSOTG_BASE_ADDR + HSOTG_DIEPINT1_OFFSET); + + return 0; +} + +/** + * @brief: usb_handle_ep0_out_int - Process USB EP0-OUT interrupt. + * @param[in]: none + * @param[out]: none + * @return: 0 or ERROR ID + */ +static int usb_handle_ep0_out_int(void) +{ + uint32_t doep0int; + + /* clear Device OUT Endpoint 0 Interrupt Register */ + doep0int = readl(HSOTG_BASE_ADDR + HSOTG_DOEPINT0_OFFSET); + writel(doep0int, HSOTG_BASE_ADDR + HSOTG_DOEPINT0_OFFSET); + + if (doep0int & HSOTG_DOEPINT0_TIMEOUT_MASK) { + /* Timeout Condition */ + debug("timeout\n"); + + /* Reset EP0 buffer */ + dfu_ep0_handler((uint8_t *)ep0_setup_buf); + } else if (doep0int & HSOTG_DOEPINT0_XFERCOMPL_MASK) { + /* Transfer Completed Interrupt */ + + /* re-arm EP0-Setup */ + usb_ep0_setup_prime(); + } else { + debug(" ... not timeout or transfer complete?\n"); + return 1; + } + return 0; +} + +/** + * @brief: usb_handle_ep1_out_int - Process USB EP1-OUT interrupt. + * @param[in]: none + * @param[out]: none + * @return: 0 or ERROR ID + */ +static int usb_handle_ep1_out_int(void) +{ + uint32_t doepint1; + + /* clear Device OUT Endpoint 1 Interrupt Register */ + doepint1 = readl(HSOTG_BASE_ADDR + HSOTG_DOEPINT1_OFFSET); + writel(doepint1, HSOTG_BASE_ADDR + HSOTG_DOEPINT1_OFFSET); + + if (doepint1 & HSOTG_DOEPINT1_TIMEOUT_MASK) { + /* Timeout Condition */ + debug("timeout\n"); + } else if (doepint1 & HSOTG_DOEPINT1_XFERCOMPL_MASK) { + /* Transfer Completed Interrupt */ + + /* Get pointer to current buffer */ + uint8_t *buffer = ep1_out_buf[ep1_out_buf_sel]; + + /* Compute size of transfer */ + int transfer_size = EP1_OUT_BUF_SIZE - + (readl(HSOTG_BASE_ADDR + HSOTG_DOEPTSIZ1_OFFSET) & + HSOTG_DOEPTSIZ1_XFERSIZE_MASK); + + /* Stuff a NULL in to help terminate command strings */ + if (transfer_size != EP1_OUT_BUF_SIZE) + buffer[transfer_size] = 0; + + /* + * Before processing received data, re-arm EP1-OUT + * (toggles active buffer) + */ + usb_ep1_out_prime(); + + /* Now, process data */ + if (rx_callback) + rx_callback(buffer, transfer_size); + + } else { + debug(" ... not timeout or transfer complete?\n"); + return 1; + } + return 0; +} + +/** + * @brief: usb_chirp_enum - Perform USB CHIRP. + * @param[in]: none + * @param[out]: none + * @return: 0 + */ +static inline int usb_chirp_enum(void) +{ + /* + * Enumerated Speed: + * 00: High speed (PHY clock is running at 30 or 60 MHz) + * 01: Full speed (PHY clock is running at 30 or 60 MHz) + * 10: Low speed (PHY clock is running at 6 MHz) + * 11: Full speed (PHY clock is running at 48 MHz) + */ + usb_speed = readl(HSOTG_BASE_ADDR + HSOTG_DSTS_OFFSET); + usb_speed = ((usb_speed & HSOTG_DSTS_ENUMSPD_MASK) >> + HSOTG_DSTS_ENUMSPD_SHIFT); + + printf("USB speed: %s\n", (usb_speed == 0) ? "High" : "Full/Low"); + + return 0; +} + +/** + * @brief: usb_power_up - Power-up USB. + * @param[in]: none + * @param[out]: none + * @return: none + */ +static inline void usb_power_up(void) +{ + /* clear "stop PHY clock" */ + writel((0 << HSOTG_PCGCR_STOPPCLK_SHIFT), + HSOTG_BASE_ADDR + HSOTG_PCGCR_OFFSET); + printf("Enable USB PHY clock!\n"); +} + +/* + * UDC interface + */ + +int usb_init_otg(rx_type rx) +{ + uint32_t mask; + + rx_callback = rx; + + clk_usb_otg_enable((void *)HSOTG_BASE_ADDR); + +#ifdef CONFIG_CMD_SERIALNO + if (get_serial_no()) + usb_device_strings[DEVICE_STRING_SERIAL_NUMBER_INDEX] = + (char *)get_serial_no(); +#endif + + mask = HSOTG_CTRL_USBOTGCONTROL_UTMIOTG_IDDIG_SW_MASK | + /* SW sets device mode bit 26 */ + HSOTG_CTRL_USBOTGCONTROL_USB_ON_MASK | + /* turn on OTG core */ + HSOTG_CTRL_USBOTGCONTROL_USB_ON_IS_HCLK_EN_MASK | + /* use usb_on as source of AHB clock enable */ + HSOTG_CTRL_USBOTGCONTROL_USB_HCLK_EN_DIRECT_MASK; + /* explicit source of AHB clock enable */ + + wfld_set(HSOTG_CTRL_BASE_ADDR + HSOTG_CTRL_USBOTGCONTROL_OFFSET, + mask, mask); + + udelay(1000); + usb_soft_reset(); + udelay(1000); + + /* Initialize OTG device core */ + if (dfu_setup_device_mode()) { + usb_phy_disconnect(); + return 1; + } + + /* Device All Endpoints Interrupt Mask Register */ + writel(HSOTG_INTMSK_WKUPINTMSK_MASK | + /* Resume/Remote Wakeup Detected */ + HSOTG_INTMSK_DISCONNINTMSK_MASK | + /* Disconnect Detected */ + HSOTG_INTMSK_OEPINTMSK_MASK | /* OUT Endpoints */ + HSOTG_INTMSK_INEPINTMSK_MASK | /* IN Endpoints */ + HSOTG_INTMSK_ENUMDONEMSK_MASK | /* Enumeration Done */ + HSOTG_INTMSK_USBRSTMSK_MASK | /* USB Reset */ + HSOTG_INTMSK_USBSUSPMSK_MASK | /* USB Suspend */ + HSOTG_INTMSK_ERLYSUSPMSK_MASK | /* Early Suspend */ + HSOTG_INTMSK_OTGINTMSK_MASK, /* OTG Interrupt */ + HSOTG_BASE_ADDR + HSOTG_INTMSK_OFFSET); + + /* Device IN Endpoint Common Interrupt Mask Register */ + writel(HSOTG_DIEPMSK_TIMEOUTMSK_MASK | /* Timeout Condition */ + HSOTG_DIEPMSK_AHBERRMSK_MASK | /* AHB Error */ + HSOTG_DIEPMSK_EPDISBLDMSK_MASK | /* Endpoint Disabled */ + HSOTG_DIEPMSK_XFERCOMPLMSK_MASK, /* Transfer Completed */ + HSOTG_BASE_ADDR + HSOTG_DIEPMSK_OFFSET); + + /* Device OUT Endpoint Common Interrupt Mask Register */ + writel(HSOTG_DOEPMSK_SETUPMSK_MASK | /* SETUP Phase Done */ + HSOTG_DOEPMSK_AHBERRMSK_MASK | /* AHB Error */ + HSOTG_DOEPMSK_EPDISBLDMSK_MASK | /* Endpoint Disabled */ + HSOTG_DOEPMSK_XFERCOMPLMSK_MASK, /* Transfer Completed */ + HSOTG_BASE_ADDR + HSOTG_DOEPMSK_OFFSET); + + memset(ep0_setup_buf, 0, sizeof(ep0_setup_buf)); + + usb_ep0_setup_prime(); + usb_clr_interrupt(); + + usb_phy_connect(); + + /* dataline or Vbus Pulsing */ + mask = HSOTG_CFG_SRPCAP_MASK | /* SRP-Capable */ + HSOTG_CFG_HNPCAP_MASK; /* HNP-Capable */ + wfld_set(HSOTG_BASE_ADDR + HSOTG_CFG_OFFSET, mask, mask); + + mask = HSOTG_OTGCTL_SESREQ_MASK; + wfld_set(HSOTG_BASE_ADDR + HSOTG_OTGCTL_OFFSET, mask, mask); + + usb_speed = 0; + + return 0; +} + +void usb_handle_ints_otg(void) +{ + uint32_t regval; + uint32_t intsts; + + intsts = readl(HSOTG_BASE_ADDR + HSOTG_INTSTS_OFFSET); + + if (intsts) { + /* IN Endpoints Interrupt */ + if (intsts & HSOTG_INTSTS_IEPINT_MASK) { + uint32_t daint = readl(HSOTG_BASE_ADDR + + HSOTG_DAINT_OFFSET); + +#ifdef CONFIG_DEBUG_IEPINT + debug("daint = 0x%08X - I\n", daint); +#endif + + if (daint & (1 << (0 + HSOTG_DAINTMSK_INEPMSK_SHIFT))) + usb_handle_ep0_in_int(); + if (daint & (1 << (1 + HSOTG_DAINTMSK_INEPMSK_SHIFT))) + usb_handle_ep1_in_int(); + } + + /* OUT Endpoints Interrupt */ + if (intsts & HSOTG_INTSTS_OEPINT_MASK) { + uint32_t daint = readl(HSOTG_BASE_ADDR + + HSOTG_DAINT_OFFSET); + +#ifdef CONFIG_DEBUG_OEPINT + debug("daint = 0x%08X - O\n", daint); +#endif + + if (daint & (1 << (0 + HSOTG_DAINTMSK_OUTEPMSK_SHIFT))) + usb_handle_ep0_out_int(); + if (daint & (1 << (1 + HSOTG_DAINTMSK_OUTEPMSK_SHIFT))) + usb_handle_ep1_out_int(); + } + + /* OTG Interrupt */ + if (intsts & HSOTG_INTSTS_OTGINT_MASK) { + debug("G\n"); + /* clear OTG Interrupt Register */ + regval = readl(HSOTG_BASE_ADDR + HSOTG_OTGINT_OFFSET); + writel(regval, HSOTG_BASE_ADDR + HSOTG_OTGINT_OFFSET); + debug("OTG: 0x%08X\n", regval); + + /* Session Request Success Status Change */ + if (regval & HSOTG_OTGINT_SESREQSUCSTSCHNG_MASK) + debug("r\n"); + + /* Session End Detected */ + if (regval & HSOTG_OTGINT_SESENDDET_MASK) { + debug("e\n"); + udelay(2500); + wfld_set(HSOTG_BASE_ADDR + HSOTG_OTGCTL_OFFSET, + HSOTG_OTGCTL_SESREQ_MASK, + HSOTG_OTGCTL_SESREQ_MASK); + } + + /* Debounce Done */ + if (regval & HSOTG_OTGINT_DBNCEDONE_MASK) + debug("d\n"); + + /* A-Device Timeout Change */ + if (regval & HSOTG_OTGINT_ADEVTOUTCHG_MASK) + debug("a\n"); + } + + /* Enumeration Done */ + if (intsts & HSOTG_INTSTS_ENUMDONE_MASK) { + debug("E\n"); + usb_chirp_enum(); + writel(HSOTG_INTSTS_ENUMDONE_MASK, + HSOTG_BASE_ADDR + HSOTG_INTSTS_OFFSET); + } + + /* Early Suspend */ + if (intsts & HSOTG_INTSTS_ERLYSUSP_MASK) { + debug("eS\n"); + writel(HSOTG_INTSTS_ERLYSUSP_MASK, + HSOTG_BASE_ADDR + HSOTG_INTSTS_OFFSET); + } + + /* USB Suspend */ + if (intsts & HSOTG_INTSTS_USBSUSP_MASK) { + debug("Su\n"); + writel(HSOTG_INTSTS_USBSUSP_MASK, + HSOTG_BASE_ADDR + HSOTG_INTSTS_OFFSET); + } + + /* Wake Up */ + if (intsts & HSOTG_INTSTS_WKUPINT_MASK) { + usb_power_up(); + debug("Rm\n"); + writel(HSOTG_INTSTS_WKUPINT_MASK, + HSOTG_BASE_ADDR + HSOTG_INTSTS_OFFSET); + } + } +} + +int usb_send_bulk_otg(uint8_t *buffer, uint32_t len) +{ + return usb_send_data_ep1_in(buffer, len); +} + +void usb_shutdown_otg(void) +{ + /* */ + writel(0x04008C4C, HSOTG_CTRL_BASE_ADDR + + HSOTG_CTRL_USBOTGCONTROL_OFFSET); + + /* wait for clock to settle down before checking vbus */ + udelay(32); + + usb_wait_for_vbus(); + + usb_turn_off_vdp(); +} diff --git a/drivers/usb/gadget/bcm_udc_otg.h b/drivers/usb/gadget/bcm_udc_otg.h new file mode 100644 index 0000000..d5b1805 --- /dev/null +++ b/drivers/usb/gadget/bcm_udc_otg.h @@ -0,0 +1,19 @@ +/* + * Copyright 2015 Broadcom Corporation. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __BCM_UDC_OTG_H +#define __BCM_UDC_OTG_H + +#include <linux/types.h> + +typedef int (*rx_type)(const unsigned char *buffer, unsigned int buffer_size); + +int usb_init_otg(rx_type rx); +void usb_handle_ints_otg(void); +int usb_send_bulk_otg(uint8_t *buffer, uint32_t len); +void usb_shutdown_otg(void); + +#endif