[U-Boot] [PATCH 2/4] usb: add 'bcm_udc_otg' support
Steve Rae
srae at broadcom.com
Tue Jan 20 23:42:08 CET 2015
Implement the UDC support for the USB OTG interface.
Signed-off-by: Steve Rae <srae at 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
--
1.8.5
More information about the U-Boot
mailing list