blob: 662e3236974e37c3aff6adb60db0bec765e8f5f4 [file] [log] [blame]
/*
* Copyright (c) 2015 Amlogic, Inc. All rights reserved.
*
* This source code is subject to the terms and conditions defined in the
* file 'LICENSE' which is part of this source code package.
*
* USB low level routines
*/
#include "usb_boot.h"
#include "usb_ch9.h"
#include "dwc_pcd.h"
#include "dwc_pcd_irq.h"
#include "platform.c"
gadget_wrapper_t gadget_wrapper;
static const struct usb_gadget_ops dwc_pcd_ops = {
.get_frame = NULL,
.wakeup = NULL,
.set_selfpowered = NULL,
};
#define USETW2(w, h, l) ((w)[0] = (u_int8_t)(l), (w)[1] = (u_int8_t)(h))
#define UCONSTW(x) { (x) & 0xff, ((x) >> 8) & 0xff }
#define UCONSTDW(x) { (x) & 0xff, ((x) >> 8) & 0xff, \
((x) >> 16) & 0xff, ((x) >> 24) & 0xff }
#define UGETW(w) ((w)[0] | ((w)[1] << 8))
#define USETW(w, v) ((w)[0] = (u_int8_t)(v), (w)[1] = (u_int8_t)((v) >> 8))
#define UGETDW(w) ((w)[0] | ((w)[1] << 8) | ((w)[2] << 16) | ((w)[3] << 24))
#define USETDW(w, v) ((w)[0] = (u_int8_t)(v), \
(w)[1] = (u_int8_t)((v) >> 8), \
(w)[2] = (u_int8_t)((v) >> 16), \
(w)[3] = (u_int8_t)((v) >> 24))
#define UE_XFERTYPE 0x03
#define UE_CONTROL 0x00
#define UE_ISOCHRONOUS 0x01
#define UE_BULK 0x02
#define UE_INTERRUPT 0x03
#define UE_GET_XFERTYPE(a) ((a) & UE_XFERTYPE)
#define UE_ISO_TYPE 0x0c
#define UE_ISO_ASYNC 0x04
#define UE_ISO_ADAPT 0x08
#define UE_ISO_SYNC 0x0c
#define UE_GET_ISO_TYPE(a) ((a) & UE_ISO_TYPE)
#define UE_GET_DIR(a) ((a) & 0x80)
#define UE_SET_DIR(a, d) ((a) | (((d)&1) << 7))
#define UE_DIR_IN 0x80
#define UE_DIR_OUT 0x00
#define UE_ADDR 0x0f
#define UE_GET_ADDR(a) ((a) & UE_ADDR)
static void dwc_otg_enable_dwc_interrupts(void)
{
gintmsk_data_t intr_mask = {0};
gahbcfg_data_t ahbcfg = {0};
/* Clear any pending OTG Interrupts */
dwc_write_reg32(DWC_REG_GOTGINT, 0xFFFFFFFF);
/* Clear any pending interrupts */
dwc_write_reg32(DWC_REG_GINTSTS, 0xFFFFFFFF);
intr_mask.b.rxstsqlvl = 1;
intr_mask.b.usbreset = 1;
intr_mask.b.enumdone = 1;
intr_mask.b.inepintr = 1;
intr_mask.b.outepintr = 1;
intr_mask.b.sofintr = 1;
intr_mask.b.usbsuspend = 1;
dwc_write_reg32(DWC_REG_GINTMSK, intr_mask.d32);
/* Enable interrupts */
ahbcfg.d32 = dwc_read_reg32(DWC_REG_GAHBCFG);
ahbcfg.b.glblintrmsk = 1;
dwc_write_reg32(DWC_REG_GAHBCFG, ahbcfg.d32);
}
static void dwc_otg_core_reset(void)
{
grstctl_t greset = {0};
u32 count = 0;
/*
* Wait for AHB master IDLE state.
*/
do {
udelay(10);
greset.d32 = dwc_read_reg32(DWC_REG_GRSTCTL);
if (++count > 100000) {
ERR("HANG! AHB Idle GRSTCTL=%0x\n", greset.d32);
return;
}
} while (0 == greset.b.ahbidle);
/*
* Core Soft Reset
*/
count = 0;
greset.b.csftrst = 1;
dwc_write_reg32(DWC_REG_GRSTCTL, greset.d32);
do {
greset.d32 = dwc_read_reg32(DWC_REG_GRSTCTL);
if (++count > 1000000) {
ERR("HANG! Soft Reset GRSTCTL=%0x\n", greset.d32);
break;
}
} while (1 == greset.b.csftrst);
/*
* Wait for 3 PHY Clocks
*/
wait_ms(10);
}
int f_dwc_core_init()
{
gahbcfg_data_t ahbcfg = {0};
gusbcfg_data_t usbcfg = {0};
grstctl_t resetctl = {0};
fifosize_data_t nptxfifosize;
int i;
DBG("\ndwc_otg core init enter!\n");
f_set_usb_phy_config();
if (0x4F543000 != (dwc_read_reg32(DWC_REG_GSNPSID) & 0xFFFFF000)) {
ERR("Bad value for SNPSID\n");
return -1;
}
dwc_otg_core_reset();
/*
* Initialize the DWC_otg core.
*/
usbcfg.d32 = dwc_read_reg32(DWC_REG_GUSBCFG);
usbcfg.b.force_dev_mode = 1;
#if (defined CONFIG_USB_DEVICE_V2)
usbcfg.b.usbtrdtim = 9;
#else
usbcfg.b.usbtrdtim = 5;
#endif
dwc_write_reg32(DWC_REG_GUSBCFG, usbcfg.d32);
ahbcfg.b.dmaenable = 0;
dwc_write_reg32(DWC_REG_GAHBCFG, ahbcfg.d32);
dwc_modify_reg32(DWC_REG_DCTL, 0, 2);
dwc_modify_reg32(DWC_REG_DCTL, 2, 0);
/*
* Do device or host intialization based on mode during PCD and HCD
* initialization
*/
if (dwc_read_reg32(DWC_REG_GINTSTS) & 0x1) {
DBG("Host Mode\n");
return -1;
}
DBG("Device Mode\n");
/* Restart the Phy Clock */
dwc_write_reg32(DWC_REG_PCGCCTL, 0);
/* Configure data FIFO sizes */
/* Rx FIFO */
dwc_write_reg32(DWC_REG_GRXFSIZ, 256);
/* Non-periodic Tx FIFO */
nptxfifosize.b.depth = 512;
nptxfifosize.b.startaddr = 256;
dwc_write_reg32(DWC_REG_GNPTXFSIZ, nptxfifosize.d32);
/* Flush the FIFOs */
dwc_otg_flush_fifo(0x10);
/* Flush the Learning Queue. */
resetctl.b.intknqflsh = 1;
dwc_write_reg32(DWC_REG_GRSTCTL, resetctl.d32);
/* Clear all pending Device Interrupts */
dwc_write_reg32(DWC_REG_DIEPMSK, 0);
dwc_write_reg32(DWC_REG_DOEPMSK, 0);
dwc_write_reg32(DWC_REG_DAINT, 0xFFFFFFFF);
dwc_write_reg32(DWC_REG_DAINTMSK, 0);
for (i = 0; i < NUM_EP; i++) {
dwc_write_reg32(DWC_REG_IN_EP_REG(i), 0);
dwc_write_reg32(DWC_REG_OUT_EP_REG(i), 0);
/* Device IN/OUT Endpoint Transfer Size */
dwc_write_reg32(DWC_REG_IN_EP_TSIZE(i), 0);
dwc_write_reg32(DWC_REG_OUT_EP_TSIZE(i), 0);
}
dwc_otg_enable_dwc_interrupts();
return 0;
}
int usb_pcd_irq_loop()
{
return f_dwc_pcd_irq();
}
struct usb_gadget* usb_pcd_get_gadget()
{
return &gadget_wrapper.gadget;
}
int pcd_queue(u32 ep_num, u32 is_in, struct usb_request *req)
{
return f_dwc_otg_ep_req_start(&gadget_wrapper.pcd, ep_num, is_in, req);
}
int dwc_otg_bind_gadget_driver(struct usb_gadget_driver *driver)
{
gadget_wrapper.driver = driver;
return 0;
}
static inline void usb_ep_set_maxpacket_limit(struct usb_ep *ep,
unsigned maxpacket_limit)
{
ep->maxpacket = maxpacket_limit;
}
static int ep_enable(struct usb_ep *usb_ep,
const struct usb_endpoint_descriptor *ep_desc)
{
int num;
int dir;
dwc_ep_t *ep;
const usb_endpoint_descriptor_t *desc;
desc = (const usb_endpoint_descriptor_t *)ep_desc;
if (!desc) {
printf("ep_desc is NULL\n");
return 0;
}
if (!usb_ep || !ep_desc || ep_desc->bDescriptorType != USB_DT_ENDPOINT) {
printf("bad ep or descriptor\n");
return -1;
}
if (usb_ep == &gadget_wrapper.ep0) {
printf("bad ep(0)\n");
return -1;
}
/* Check FIFO size? */
if (!ep_desc->wMaxPacketSize) {
printf("bad %s maxpacket\n", usb_ep->name);
return -1;
}
if (!gadget_wrapper.driver ||
gadget_wrapper.gadget.speed == USB_SPEED_UNKNOWN) {
printf("bogus device state\n");
return -1;
}
num = UE_GET_ADDR(desc->bEndpointAddress);
dir = UE_GET_DIR(desc->bEndpointAddress);
if (dir == UE_DIR_IN) {
ep = &gadget_wrapper.pcd.dwc_eps[num].dwc_ep;
gadget_wrapper.pcd.dwc_eps[num].desc = desc;
gadget_wrapper.pcd.dwc_eps[num].priv = (void *)usb_ep;
} else {
ep = &gadget_wrapper.pcd.dwc_eps[num+1].dwc_ep;
gadget_wrapper.pcd.dwc_eps[num+1].desc = desc;
gadget_wrapper.pcd.dwc_eps[num+1].priv = (void *)usb_ep;
}
dwc_otg_bulk_ep_activate(ep);
usb_ep->maxpacket = le16_to_cpu(ep_desc->wMaxPacketSize);
return 0;
}
static int ep_disable(struct usb_ep *usb_ep)
{
int retval = 0;
if (!usb_ep) {
return -1;
}
return retval;
}
static struct usb_request *dwc_otg_pcd_alloc_request(struct usb_ep *ep,
gfp_t gfp_flags)
{
struct usb_request *usb_req;
if (0 == ep) {
printf("Invalid EP!\n");
return 0;
}
usb_req = kmalloc(sizeof(*usb_req), gfp_flags);
if (0 == usb_req) {
printf("request allocation failed!\n");
return 0;
}
memset(usb_req, 0, sizeof(*usb_req));
usb_req->dma = 0xffffffff;
return usb_req;
}
static void dwc_otg_pcd_free_request(struct usb_ep *ep, struct usb_request *req)
{
if (0 == ep || 0 == req) {
printf(
"Invalid ep or req argument!\n");
return;
}
kfree(req);
}
static dwc_otg_pcd_ep_t *get_ep_from_handle(pcd_struct_t *pcd, void *handle)
{
int i;
if (pcd->dwc_eps[0].priv == handle)
return &pcd->dwc_eps[0];
for (i = 1; i < 5; i++) {
if (pcd->dwc_eps[i].priv == handle) {
return &pcd->dwc_eps[i];
}
}
return NULL;
}
static int ep_queue(struct usb_ep *usb_ep, struct usb_request *usb_req,
gfp_t gfp_flags)
{
pcd_struct_t *pcd;
struct dwc_otg_pcd_ep *ep = NULL;
int retval = 0;
if (!usb_req || !usb_req->complete || !usb_req->buf) {
printf("bad params\n");
return -1;
}
if (!usb_ep) {
printf("bad ep\n");
return -1;
}
pcd = &gadget_wrapper.pcd;
if (!gadget_wrapper.driver ||
gadget_wrapper.gadget.speed == USB_SPEED_UNKNOWN) {
printf("gadget.speed=%d\n",
gadget_wrapper.gadget.speed);
printf("bogus device state\n");
return -2;
}
usb_req->status = -EINPROGRESS;
usb_req->actual = 0;
ep = get_ep_from_handle(pcd, (void *)usb_ep);
if (!ep || (!ep->desc && ep->dwc_ep.num != 0)) {
printf("bad ep\n");
return -1;
}
ep->req = usb_req;
pcd_queue(ep->dwc_ep.num, ep->dwc_ep.is_in, usb_req);
if (retval)
return -3;
return 0;
}
static int ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req)
{
if (!usb_ep || !usb_req) {
printf("bad argument\n");
return -EINVAL;
}
if (!gadget_wrapper.driver ||
gadget_wrapper.gadget.speed == USB_SPEED_UNKNOWN) {
printf("bogus device state\n");
return -ESHUTDOWN;
}
return 0;
}
static int ep_halt(struct usb_ep *usb_ep, int value)
{
depctl_data_t depctl;
volatile u32 depctl_addr;
pcd_struct_t *pcd;
struct dwc_otg_pcd_ep *ep = NULL;
if (value) {
printf("can not halt ep\n");
return -EINVAL;
}
pcd = &gadget_wrapper.pcd;
if (!gadget_wrapper.driver ||
gadget_wrapper.gadget.speed == USB_SPEED_UNKNOWN) {
printf("gadget.speed=%d\n",
gadget_wrapper.gadget.speed);
printf("bogus device state\n");
return -EINVAL;
}
if (!usb_ep) {
printf("bad ep\n");
return -EINVAL;
}
ep = get_ep_from_handle(pcd, (void *)usb_ep);
if (!ep || (!ep->desc && ep->dwc_ep.num != 0)) {
printf("bad ep\n");
return -EINVAL;
}
if (ep->dwc_ep.is_in == 1)
depctl_addr = DWC_REG_IN_EP_REG(1);
else
depctl_addr = DWC_REG_OUT_EP_REG(1);
depctl.d32 = dwc_read_reg32(depctl_addr);
/* clear the stall bits */
depctl.b.stall = 0;
/*
* USB Spec 9.4.5: For endpoints using data toggle, regardless
* of whether an endpoint has the Halt feature set, a
* ClearFeature(ENDPOINT_HALT) request always results in the
* data toggle being reinitialized to DATA0.
*/
depctl.b.setd0pid = 1;
dwc_write_reg32(depctl_addr, depctl.d32);
return 0;
}
static struct usb_ep_ops dwc_otg_pcd_ep_ops = {
.enable = ep_enable,
.disable = ep_disable,
.alloc_request = dwc_otg_pcd_alloc_request,
.free_request = dwc_otg_pcd_free_request,
.queue = ep_queue,
.dequeue = ep_dequeue,
.set_halt = ep_halt,
.fifo_status = 0,
.fifo_flush = 0,
};
void gadget_add_eps(gadget_wrapper_t *d)
{
static const char *names[] = {
"ep0",
"ep1in",
"ep1out",
};
int i;
struct usb_ep *ep;
int8_t dev_endpoints = 1;
INIT_LIST_HEAD(&d->gadget.ep_list);
d->gadget.ep0 = &d->ep0;
d->gadget.speed = USB_SPEED_HIGH;
INIT_LIST_HEAD(&d->gadget.ep0->ep_list);
/**
* Initialize the EP0 structure.
*/
ep = &d->ep0;
/* Init the usb_ep structure. */
ep->name = names[0];
ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops;
/**
* @todo NGS: What should the max packet size be set to
* here? Before EP type is set?
*/
ep->maxpacket = 1024;
d->pcd.dwc_eps[0].priv = (void *)ep;
list_add_tail(&ep->ep_list, &d->gadget.ep_list);
for (i = 0; i < (dev_endpoints); i++) {
ep = &d->in_ep[i];
/* Init the usb_ep structure. */
ep->name = names[i+1];
ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops;
/**
* @todo NGS: What should the max packet size be set to
* here? Before EP type is set?
*/
ep->maxpacket = 512;
usb_ep_set_maxpacket_limit(ep, 512);
list_add_tail(&ep->ep_list, &d->gadget.ep_list);
}
for (i = 0; i < dev_endpoints; i++) {
ep = &d->out_ep[i];
/* Init the usb_ep structure. */
ep->name = names[i+2];
ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops;
/**
* @todo NGS: What should the max packet size be set to
* here? Before EP type is set?
*/
ep->maxpacket = 512;
usb_ep_set_maxpacket_limit(ep, 512);
list_add_tail(&ep->ep_list, &d->gadget.ep_list);
}
/* remove ep0 from the list. There is a ep0 pointer. */
list_del_init(&d->ep0.ep_list);
d->ep0.maxpacket = 64;
usb_ep_set_maxpacket_limit(&d->ep0, 64);
}
static void dwc_otg_pcd_init_ep(pcd_struct_t *pcd, dwc_otg_pcd_ep_t *pcd_ep,
uint32_t is_in, uint32_t ep_num)
{
/* Init EP structure */
pcd_ep->desc = 0;
pcd_ep->stopped = 1;
pcd_ep->queue_sof = 0;
/* Init DWC ep structure */
pcd_ep->dwc_ep.is_in = is_in;
pcd_ep->dwc_ep.num = ep_num;
pcd_ep->dwc_ep.active = 0;
pcd_ep->dwc_ep.tx_fifo_num = 0;
/* Control until ep is actvated */
pcd_ep->dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL;
pcd_ep->dwc_ep.maxpacket = 1024;
pcd_ep->dwc_ep.dma_addr = 0;
pcd_ep->dwc_ep.start_xfer_buff = 0;
pcd_ep->dwc_ep.xfer_buff = 0;
pcd_ep->dwc_ep.xfer_len = 0;
pcd_ep->dwc_ep.xfer_count = 0;
pcd_ep->dwc_ep.sent_zlp = 0;
pcd_ep->dwc_ep.total_len = 0;
}
int f_usb_pcd_init()
{
/**
* Initialized the PCD portion of the driver.
*/
return f_dwc_core_init();
}
/*
Register entry point for the peripheral controller driver.
*/
int usb_gadget_register_driver(struct usb_gadget_driver *driver)
{
gadget_wrapper_t *dev = &gadget_wrapper;
dwc_otg_pcd_ep_t *ep;
int retval = 0;
if (!driver
|| (driver->speed != USB_SPEED_FULL
&& driver->speed != USB_SPEED_HIGH)
|| !driver->bind || !driver->disconnect || !driver->setup)
return -EINVAL;
if (!dev)
return -ENODEV;
if (dev->driver)
return -EBUSY;
/* first hook up the driver ... */
dev->driver = driver;
if (retval) { /* TODO */
printf("target device_add failed, error %d\n", retval);
return retval;
}
ep = &gadget_wrapper.pcd.dwc_eps[0];
dwc_otg_pcd_init_ep(&gadget_wrapper.pcd, ep, 0, 0);
ep = &gadget_wrapper.pcd.dwc_eps[1];
dwc_otg_pcd_init_ep(&gadget_wrapper.pcd, ep, 1 /* IN */ , 1);
ep = &gadget_wrapper.pcd.dwc_eps[2];
dwc_otg_pcd_init_ep(&gadget_wrapper.pcd, ep, 0 /* OUT */ , 1);
gadget_wrapper.pcd.dwc_eps[0].dwc_ep.maxpacket = 64;
gadget_wrapper.pcd.dwc_eps[0].dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL;
gadget_wrapper.gadget.ops = &dwc_pcd_ops;
gadget_add_eps(&gadget_wrapper);
retval = driver->bind(&dev->gadget);
if (retval) {
dev->driver = 0;
return retval;
}
f_usb_pcd_init();
return 0;
}
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
gadget_wrapper_t *dev = &gadget_wrapper;
if (!dev)
return -ENODEV;
if (!driver || driver != dev->driver)
return -EINVAL;
driver->unbind(&dev->gadget);
dev->driver = NULL;
return 0;
}
int usb_gadget_handle_interrupts(void)
{
return usb_pcd_irq_loop();
}