#include <linux/usb/gadget.h>
#include <linux/usb/phy.h>
#include <linux/platform_data/s3c-hsotg.h>
+#include <linux/uaccess.h>
#include "core.h"
#include "hw.h"
int timeout;
u32 val;
+ /* Reset fifo map if not correctly cleared during previous session */
+ WARN_ON(hsotg->fifo_map);
+ hsotg->fifo_map = 0;
+
/* set RX/NPTX FIFO sizes */
writel(hsotg->g_rx_fifo_sz, hsotg->regs + GRXFSIZ);
writel((hsotg->g_rx_fifo_sz << FIFOSIZE_STARTADDR_SHIFT) |
dev_err(hsotg->dev,
"%s: timeout flushing fifos (GRSTCTL=%08x)\n",
__func__, val);
+ break;
}
udelay(1);
length = ureq->length - ureq->actual;
dev_dbg(hsotg->dev, "ureq->length:%d ureq->actual:%d\n",
ureq->length, ureq->actual);
- if (0)
- dev_dbg(hsotg->dev,
- "REQ buf %p len %d dma %pad noi=%d zp=%d snok=%d\n",
- ureq->buf, length, &ureq->dma,
- ureq->no_interrupt, ureq->zero, ureq->short_not_ok);
maxreq = get_ep_limit(hs_ep);
if (length > maxreq) {
/* Test if zlp is actually required. */
if ((ureq->length >= hs_ep->ep.maxpacket) &&
!(ureq->length % hs_ep->ep.maxpacket))
- hs_ep->sent_zlp = 1;
+ hs_ep->send_zlp = 1;
}
epsize |= DXEPTSIZ_PKTCNT(packets);
return -EIO;
}
+static int s3c_hsotg_handle_unaligned_buf_start(struct dwc2_hsotg *hsotg,
+ struct s3c_hsotg_ep *hs_ep, struct s3c_hsotg_req *hs_req)
+{
+ void *req_buf = hs_req->req.buf;
+
+ /* If dma is not being used or buffer is aligned */
+ if (!using_dma(hsotg) || !((long)req_buf & 3))
+ return 0;
+
+ WARN_ON(hs_req->saved_req_buf);
+
+ dev_dbg(hsotg->dev, "%s: %s: buf=%p length=%d\n", __func__,
+ hs_ep->ep.name, req_buf, hs_req->req.length);
+
+ hs_req->req.buf = kmalloc(hs_req->req.length, GFP_ATOMIC);
+ if (!hs_req->req.buf) {
+ hs_req->req.buf = req_buf;
+ dev_err(hsotg->dev,
+ "%s: unable to allocate memory for bounce buffer\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ /* Save actual buffer */
+ hs_req->saved_req_buf = req_buf;
+
+ if (hs_ep->dir_in)
+ memcpy(hs_req->req.buf, req_buf, hs_req->req.length);
+ return 0;
+}
+
+static void s3c_hsotg_handle_unaligned_buf_complete(struct dwc2_hsotg *hsotg,
+ struct s3c_hsotg_ep *hs_ep, struct s3c_hsotg_req *hs_req)
+{
+ /* If dma is not being used or buffer was aligned */
+ if (!using_dma(hsotg) || !hs_req->saved_req_buf)
+ return;
+
+ dev_dbg(hsotg->dev, "%s: %s: status=%d actual-length=%d\n", __func__,
+ hs_ep->ep.name, hs_req->req.status, hs_req->req.actual);
+
+ /* Copy data from bounce buffer on successful out transfer */
+ if (!hs_ep->dir_in && !hs_req->req.status)
+ memcpy(hs_req->saved_req_buf, hs_req->req.buf,
+ hs_req->req.actual);
+
+ /* Free bounce buffer */
+ kfree(hs_req->req.buf);
+
+ hs_req->req.buf = hs_req->saved_req_buf;
+ hs_req->saved_req_buf = NULL;
+}
+
static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
gfp_t gfp_flags)
{
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
struct dwc2_hsotg *hs = hs_ep->parent;
bool first;
+ int ret;
dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n",
ep->name, req, req->length, req->buf, req->no_interrupt,
req->actual = 0;
req->status = -EINPROGRESS;
+ ret = s3c_hsotg_handle_unaligned_buf_start(hs, hs_ep, hs_req);
+ if (ret)
+ return ret;
+
/* if we're using DMA, sync the buffers as necessary */
if (using_dma(hs)) {
- int ret = s3c_hsotg_map_dma(hs, hs_ep, req);
+ ret = s3c_hsotg_map_dma(hs, hs_ep, req);
if (ret)
return ret;
}
}
/**
+ * s3c_hsotg_set_test_mode - Enable usb Test Modes
+ * @hsotg: The driver state.
+ * @testmode: requested usb test mode
+ * Enable usb Test Mode requested by the Host.
+ */
+static int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode)
+{
+ int dctl = readl(hsotg->regs + DCTL);
+
+ dctl &= ~DCTL_TSTCTL_MASK;
+ switch (testmode) {
+ case TEST_J:
+ case TEST_K:
+ case TEST_SE0_NAK:
+ case TEST_PACKET:
+ case TEST_FORCE_EN:
+ dctl |= testmode << DCTL_TSTCTL_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+ writel(dctl, hsotg->regs + DCTL);
+ return 0;
+}
+
+/**
* s3c_hsotg_send_reply - send reply to control request
* @hsotg: The device state
* @ep: Endpoint 0
}
/**
- * s3c_hsotg_process_req_featire - process request {SET,CLEAR}_FEATURE
+ * s3c_hsotg_process_req_feature - process request {SET,CLEAR}_FEATURE
* @hsotg: The device state
* @ctrl: USB control request
*/
struct s3c_hsotg_ep *ep;
int ret;
bool halted;
+ u32 recip;
+ u32 wValue;
+ u32 wIndex;
dev_dbg(hsotg->dev, "%s: %s_FEATURE\n",
__func__, set ? "SET" : "CLEAR");
- if (ctrl->bRequestType == USB_RECIP_ENDPOINT) {
- ep = ep_from_windex(hsotg, le16_to_cpu(ctrl->wIndex));
+ wValue = le16_to_cpu(ctrl->wValue);
+ wIndex = le16_to_cpu(ctrl->wIndex);
+ recip = ctrl->bRequestType & USB_RECIP_MASK;
+
+ switch (recip) {
+ case USB_RECIP_DEVICE:
+ switch (wValue) {
+ case USB_DEVICE_TEST_MODE:
+ if ((wIndex & 0xff) != 0)
+ return -EINVAL;
+ if (!set)
+ return -EINVAL;
+
+ hsotg->test_mode = wIndex >> 8;
+ ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0);
+ if (ret) {
+ dev_err(hsotg->dev,
+ "%s: failed to send reply\n", __func__);
+ return ret;
+ }
+ break;
+ default:
+ return -ENOENT;
+ }
+ break;
+
+ case USB_RECIP_ENDPOINT:
+ ep = ep_from_windex(hsotg, wIndex);
if (!ep) {
dev_dbg(hsotg->dev, "%s: no endpoint for 0x%04x\n",
- __func__, le16_to_cpu(ctrl->wIndex));
+ __func__, wIndex);
return -ENOENT;
}
- switch (le16_to_cpu(ctrl->wValue)) {
+ switch (wValue) {
case USB_ENDPOINT_HALT:
halted = ep->halted;
hs_req = ep->req;
ep->req = NULL;
list_del_init(&hs_req->queue);
- usb_gadget_giveback_request(&ep->ep,
- &hs_req->req);
+ if (hs_req->req.complete) {
+ spin_unlock(&hsotg->lock);
+ usb_gadget_giveback_request(
+ &ep->ep, &hs_req->req);
+ spin_lock(&hsotg->lock);
+ }
}
/* If we have pending request, then start it */
- restart = !list_empty(&ep->queue);
- if (restart) {
- hs_req = get_ep_head(ep);
- s3c_hsotg_start_req(hsotg, ep,
- hs_req, false);
+ if (!ep->req) {
+ restart = !list_empty(&ep->queue);
+ if (restart) {
+ hs_req = get_ep_head(ep);
+ s3c_hsotg_start_req(hsotg, ep,
+ hs_req, false);
+ }
}
}
default:
return -ENOENT;
}
- } else
- return -ENOENT; /* currently only deal with endpoint */
-
+ break;
+ default:
+ return -ENOENT;
+ }
return 1;
}
if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
switch (ctrl->bRequest) {
case USB_REQ_SET_ADDRESS:
+ hsotg->connected = 1;
dcfg = readl(hsotg->regs + DCFG);
dcfg &= ~DCFG_DEVADDR_MASK;
dcfg |= (le16_to_cpu(ctrl->wValue) <<
}
hsotg->eps_out[0]->dir_in = 0;
- hsotg->eps_out[0]->sent_zlp = 0;
+ hsotg->eps_out[0]->send_zlp = 0;
hsotg->ep0_state = DWC2_EP0_SETUP;
ret = s3c_hsotg_ep_queue(&hsotg->eps_out[0]->ep, req, GFP_ATOMIC);
u32 epctl_reg = hs_ep->dir_in ? DIEPCTL(index) : DOEPCTL(index);
u32 epsiz_reg = hs_ep->dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index);
- dev_dbg(hsotg->dev, "Sending zero-length packet on ep%d\n", index);
+ if (hs_ep->dir_in)
+ dev_dbg(hsotg->dev, "Sending zero-length packet on ep%d\n",
+ index);
+ else
+ dev_dbg(hsotg->dev, "Receiving zero-length packet on ep%d\n",
+ index);
writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
DXEPTSIZ_XFERSIZE(0), hsotg->regs +
if (hs_req->req.status == -EINPROGRESS)
hs_req->req.status = result;
+ s3c_hsotg_handle_unaligned_buf_complete(hsotg, hs_ep, hs_req);
+
hs_ep->req = NULL;
list_del_init(&hs_req->queue);
size = grxstsr & GRXSTS_BYTECNT_MASK;
size >>= GRXSTS_BYTECNT_SHIFT;
- if (1)
- dev_dbg(hsotg->dev, "%s: GRXSTSP=0x%08x (%d@%d)\n",
+ dev_dbg(hsotg->dev, "%s: GRXSTSP=0x%08x (%d@%d)\n",
__func__, grxstsr, size, epnum);
switch ((status & GRXSTS_PKTSTS_MASK) >> GRXSTS_PKTSTS_SHIFT) {
if (hs_ep->index == 0 && hsotg->ep0_state == DWC2_EP0_STATUS_IN) {
dev_dbg(hsotg->dev, "zlp packet sent\n");
s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
+ if (hsotg->test_mode) {
+ int ret;
+
+ ret = s3c_hsotg_set_test_mode(hsotg, hsotg->test_mode);
+ if (ret < 0) {
+ dev_dbg(hsotg->dev, "Invalid Test #%d\n",
+ hsotg->test_mode);
+ s3c_hsotg_stall_ep0(hsotg);
+ return;
+ }
+ }
s3c_hsotg_enqueue_setup(hsotg);
return;
}
}
/* Zlp for all endpoints, for ep0 only in DATA IN stage */
- if (hs_ep->sent_zlp) {
+ if (hs_ep->send_zlp) {
s3c_hsotg_program_zlp(hsotg, hs_ep);
- hs_ep->sent_zlp = 0;
+ hs_ep->send_zlp = 0;
/* transfer will be completed on next complete interrupt */
return;
}
return;
hsotg->connected = 0;
+ hsotg->test_mode = 0;
for (ep = 0; ep < hsotg->num_of_eps; ep++) {
if (hsotg->eps_in[ep])
*
* Issue a soft reset to the core, and await the core finishing it.
*/
-void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
+void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
+ bool is_usb_reset)
{
- s3c_hsotg_corereset(hsotg);
+ u32 val;
+
+ if (!is_usb_reset)
+ s3c_hsotg_corereset(hsotg);
/*
* we must now enable ep0 ready for host detection and then
*/
/* set the PLL on, remove the HNP/SRP and set the PHY */
+ val = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5;
writel(hsotg->phyif | GUSBCFG_TOUTCAL(7) |
- (0x5 << 10), hsotg->regs + GUSBCFG);
+ (val << GUSBCFG_USBTRDTIM_SHIFT), hsotg->regs + GUSBCFG);
s3c_hsotg_init_fifo(hsotg);
- __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
+ if (!is_usb_reset)
+ __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
- writel(1 << 18 | DCFG_DEVSPD_HS, hsotg->regs + DCFG);
+ writel(DCFG_EPMISCNT(1) | DCFG_DEVSPD_HS, hsotg->regs + DCFG);
/* Clear any pending OTG interrupts */
writel(0xffffffff, hsotg->regs + GOTGINT);
s3c_hsotg_ctrl_epint(hsotg, 0, 0, 1);
s3c_hsotg_ctrl_epint(hsotg, 0, 1, 1);
- __orr32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
- udelay(10); /* see openiboot */
- __bic32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
+ if (!is_usb_reset) {
+ __orr32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
+ udelay(10); /* see openiboot */
+ __bic32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
+ }
dev_dbg(hsotg->dev, "DCTL=0x%08x\n", readl(hsotg->regs + DCTL));
readl(hsotg->regs + DOEPCTL0));
/* clear global NAKs */
- writel(DCTL_CGOUTNAK | DCTL_CGNPINNAK | DCTL_SFTDISCON,
- hsotg->regs + DCTL);
+ val = DCTL_CGOUTNAK | DCTL_CGNPINNAK;
+ if (!is_usb_reset)
+ val |= DCTL_SFTDISCON;
+ __orr32(hsotg->regs + DCTL, val);
/* must be at-least 3ms to allow bus to see disconnect */
mdelay(3);
writel(GINTSTS_ENUMDONE, hsotg->regs + GINTSTS);
s3c_hsotg_irq_enumdone(hsotg);
- hsotg->connected = 1;
}
if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) {
writel(GINTSTS_USBRST, hsotg->regs + GINTSTS);
+ /* Report disconnection if it is not already done. */
+ s3c_hsotg_disconnect(hsotg);
+
if (usb_status & GOTGCTL_BSESVLD) {
if (time_after(jiffies, hsotg->last_rst +
msecs_to_jiffies(200))) {
kill_all_requests(hsotg, hsotg->eps_out[0],
-ECONNRESET);
- s3c_hsotg_core_init_disconnected(hsotg);
- s3c_hsotg_core_connect(hsotg);
+ s3c_hsotg_core_init_disconnected(hsotg, true);
}
}
}
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
struct dwc2_hsotg *hsotg = hs_ep->parent;
unsigned long flags;
- int index = hs_ep->index;
+ unsigned int index = hs_ep->index;
u32 epctrl_reg;
u32 epctrl;
u32 mps;
- int dir_in;
- int i, val, size;
+ unsigned int dir_in;
+ unsigned int i, val, size;
int ret = 0;
dev_dbg(hsotg->dev,
break;
}
+ /* If fifo is already allocated for this ep */
+ if (hs_ep->fifo_index) {
+ size = hs_ep->ep.maxpacket * hs_ep->mc;
+ /* If bigger fifo is required deallocate current one */
+ if (size > hs_ep->fifo_size) {
+ hsotg->fifo_map &= ~(1 << hs_ep->fifo_index);
+ hs_ep->fifo_index = 0;
+ hs_ep->fifo_size = 0;
+ }
+ }
+
/*
* if the hardware has dedicated fifos, we must give each IN EP
* a unique tx-fifo even if it is non-periodic.
*/
- if (dir_in && hsotg->dedicated_fifos) {
+ if (dir_in && hsotg->dedicated_fifos && !hs_ep->fifo_index) {
+ u32 fifo_index = 0;
+ u32 fifo_size = UINT_MAX;
size = hs_ep->ep.maxpacket*hs_ep->mc;
for (i = 1; i < hsotg->num_of_eps; ++i) {
if (hsotg->fifo_map & (1<<i))
val = (val >> FIFOSIZE_DEPTH_SHIFT)*4;
if (val < size)
continue;
- hsotg->fifo_map |= 1<<i;
-
- epctrl |= DXEPCTL_TXFNUM(i);
- hs_ep->fifo_index = i;
- hs_ep->fifo_size = val;
- break;
+ /* Search for smallest acceptable fifo */
+ if (val < fifo_size) {
+ fifo_size = val;
+ fifo_index = i;
+ }
}
- if (i == hsotg->num_of_eps) {
+ if (!fifo_index) {
dev_err(hsotg->dev,
"%s: No suitable fifo found\n", __func__);
ret = -ENOMEM;
goto error;
}
+ hsotg->fifo_map |= 1 << fifo_index;
+ epctrl |= DXEPCTL_TXFNUM(fifo_index);
+ hs_ep->fifo_index = fifo_index;
+ hs_ep->fifo_size = fifo_size;
}
/* for non control endpoints, set PID to D0 */
* s3c_hsotg_ep_disable - disable given endpoint
* @ep: The endpoint to disable.
*/
-static int s3c_hsotg_ep_disable(struct usb_ep *ep)
+static int s3c_hsotg_ep_disable_force(struct usb_ep *ep, bool force)
{
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
struct dwc2_hsotg *hsotg = hs_ep->parent;
return 0;
}
+static int s3c_hsotg_ep_disable(struct usb_ep *ep)
+{
+ return s3c_hsotg_ep_disable_force(ep, false);
+}
/**
* on_list - check request is on the given endpoint
* @ep: The endpoint to check.
*/
static void s3c_hsotg_init(struct dwc2_hsotg *hsotg)
{
+ u32 trdtim;
/* unmask subset of endpoint interrupts */
writel(DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK |
/* Be in disconnected state until gadget is registered */
__orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
- if (0) {
- /* post global nak until we're ready */
- writel(DCTL_SGNPINNAK | DCTL_SGOUTNAK,
- hsotg->regs + DCTL);
- }
-
/* setup fifos */
dev_dbg(hsotg->dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n",
s3c_hsotg_init_fifo(hsotg);
/* set the PLL on, remove the HNP/SRP and set the PHY */
- writel(GUSBCFG_PHYIF16 | GUSBCFG_TOUTCAL(7) | (0x5 << 10),
- hsotg->regs + GUSBCFG);
+ trdtim = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5;
+ writel(hsotg->phyif | GUSBCFG_TOUTCAL(7) |
+ (trdtim << GUSBCFG_USBTRDTIM_SHIFT),
+ hsotg->regs + GUSBCFG);
if (using_dma(hsotg))
__orr32(hsotg->regs + GAHBCFG, GAHBCFG_DMA_EN);
spin_lock_irqsave(&hsotg->lock, flags);
s3c_hsotg_init(hsotg);
- s3c_hsotg_core_init_disconnected(hsotg);
+ s3c_hsotg_core_init_disconnected(hsotg, false);
hsotg->enabled = 0;
spin_unlock_irqrestore(&hsotg->lock, flags);
if (is_on) {
clk_enable(hsotg->clk);
hsotg->enabled = 1;
+ s3c_hsotg_core_init_disconnected(hsotg, false);
s3c_hsotg_core_connect(hsotg);
} else {
s3c_hsotg_core_disconnect(hsotg);
+ s3c_hsotg_disconnect(hsotg);
hsotg->enabled = 0;
clk_disable(hsotg->clk);
}
return 0;
}
+static int s3c_hsotg_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+ struct dwc2_hsotg *hsotg = to_hsotg(gadget);
+ unsigned long flags;
+
+ dev_dbg(hsotg->dev, "%s: is_active: %d\n", __func__, is_active);
+ spin_lock_irqsave(&hsotg->lock, flags);
+
+ if (is_active) {
+ /* Kill any ep0 requests as controller will be reinitialized */
+ kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET);
+ s3c_hsotg_core_init_disconnected(hsotg, false);
+ if (hsotg->enabled)
+ s3c_hsotg_core_connect(hsotg);
+ } else {
+ s3c_hsotg_core_disconnect(hsotg);
+ s3c_hsotg_disconnect(hsotg);
+ }
+
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+ return 0;
+}
+
+/**
+ * s3c_hsotg_vbus_draw - report bMaxPower field
+ * @gadget: The usb gadget state
+ * @mA: Amount of current
+ *
+ * Report how much power the device may consume to the phy.
+ */
+static int s3c_hsotg_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+ struct dwc2_hsotg *hsotg = to_hsotg(gadget);
+
+ if (IS_ERR_OR_NULL(hsotg->uphy))
+ return -ENOTSUPP;
+ return usb_phy_set_power(hsotg->uphy, mA);
+}
+
static const struct usb_gadget_ops s3c_hsotg_gadget_ops = {
.get_frame = s3c_hsotg_gadget_getframe,
.udc_start = s3c_hsotg_udc_start,
.udc_stop = s3c_hsotg_udc_stop,
.pullup = s3c_hsotg_pullup,
+ .vbus_session = s3c_hsotg_vbus_session,
+ .vbus_draw = s3c_hsotg_vbus_draw,
};
/**
/* check hardware configuration */
cfg = readl(hsotg->regs + GHWCFG2);
- hsotg->num_of_eps = (cfg >> 10) & 0xF;
+ hsotg->num_of_eps = (cfg >> GHWCFG2_NUM_DEV_EP_SHIFT) & 0xF;
/* Add ep0 */
hsotg->num_of_eps++;
hsotg->eps_out[0] = hsotg->eps_in[0];
cfg = readl(hsotg->regs + GHWCFG1);
- for (i = 1; i < hsotg->num_of_eps; i++, cfg >>= 2) {
+ for (i = 1, cfg >>= 2; i < hsotg->num_of_eps; i++, cfg >>= 2) {
ep_type = cfg & 3;
/* Direction in or both */
if (!(ep_type & 2)) {
}
cfg = readl(hsotg->regs + GHWCFG3);
- hsotg->fifo_mem = (cfg >> 16);
+ hsotg->fifo_mem = (cfg >> GHWCFG3_DFIFO_DEPTH_SHIFT);
cfg = readl(hsotg->regs + GHWCFG4);
- hsotg->dedicated_fifos = (cfg >> 25) & 1;
+ hsotg->dedicated_fifos = (cfg >> GHWCFG4_DED_FIFO_SHIFT) & 1;
dev_info(hsotg->dev, "EPs: %d, %s fifos, %d entries in SPRAM\n",
hsotg->num_of_eps,
readl(regs + DCFG), readl(regs + DCTL),
readl(regs + DIEPMSK));
- dev_info(dev, "GAHBCFG=0x%08x, 0x44=0x%08x\n",
- readl(regs + GAHBCFG), readl(regs + 0x44));
+ dev_info(dev, "GAHBCFG=0x%08x, GHWCFG1=0x%08x\n",
+ readl(regs + GAHBCFG), readl(regs + GHWCFG1));
dev_info(dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n",
readl(regs + GRXFSIZ), readl(regs + GNPTXFSIZ));
}
/**
+ * testmode_write - debugfs: change usb test mode
+ * @seq: The seq file to write to.
+ * @v: Unused parameter.
+ *
+ * This debugfs entry modify the current usb test mode.
+ */
+static ssize_t testmode_write(struct file *file, const char __user *ubuf, size_t
+ count, loff_t *ppos)
+{
+ struct seq_file *s = file->private_data;
+ struct dwc2_hsotg *hsotg = s->private;
+ unsigned long flags;
+ u32 testmode = 0;
+ char buf[32];
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+ return -EFAULT;
+
+ if (!strncmp(buf, "test_j", 6))
+ testmode = TEST_J;
+ else if (!strncmp(buf, "test_k", 6))
+ testmode = TEST_K;
+ else if (!strncmp(buf, "test_se0_nak", 12))
+ testmode = TEST_SE0_NAK;
+ else if (!strncmp(buf, "test_packet", 11))
+ testmode = TEST_PACKET;
+ else if (!strncmp(buf, "test_force_enable", 17))
+ testmode = TEST_FORCE_EN;
+ else
+ testmode = 0;
+
+ spin_lock_irqsave(&hsotg->lock, flags);
+ s3c_hsotg_set_test_mode(hsotg, testmode);
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+ return count;
+}
+
+/**
+ * testmode_show - debugfs: show usb test mode state
+ * @seq: The seq file to write to.
+ * @v: Unused parameter.
+ *
+ * This debugfs entry shows which usb test mode is currently enabled.
+ */
+static int testmode_show(struct seq_file *s, void *unused)
+{
+ struct dwc2_hsotg *hsotg = s->private;
+ unsigned long flags;
+ int dctl;
+
+ spin_lock_irqsave(&hsotg->lock, flags);
+ dctl = readl(hsotg->regs + DCTL);
+ dctl &= DCTL_TSTCTL_MASK;
+ dctl >>= DCTL_TSTCTL_SHIFT;
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+
+ switch (dctl) {
+ case 0:
+ seq_puts(s, "no test\n");
+ break;
+ case TEST_J:
+ seq_puts(s, "test_j\n");
+ break;
+ case TEST_K:
+ seq_puts(s, "test_k\n");
+ break;
+ case TEST_SE0_NAK:
+ seq_puts(s, "test_se0_nak\n");
+ break;
+ case TEST_PACKET:
+ seq_puts(s, "test_packet\n");
+ break;
+ case TEST_FORCE_EN:
+ seq_puts(s, "test_force_enable\n");
+ break;
+ default:
+ seq_printf(s, "UNKNOWN %d\n", dctl);
+ }
+
+ return 0;
+}
+
+static int testmode_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, testmode_show, inode->i_private);
+}
+
+static const struct file_operations testmode_fops = {
+ .owner = THIS_MODULE,
+ .open = testmode_open,
+ .write = testmode_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/**
* state_show - debugfs: show overall driver and device state.
* @seq: The seq file to write to.
* @v: Unused parameter.
/* create general state file */
- hsotg->debug_file = debugfs_create_file("state", 0444, root,
+ hsotg->debug_file = debugfs_create_file("state", S_IRUGO, root,
hsotg, &state_fops);
if (IS_ERR(hsotg->debug_file))
dev_err(hsotg->dev, "%s: failed to create state\n", __func__);
- hsotg->debug_fifo = debugfs_create_file("fifo", 0444, root,
+ hsotg->debug_testmode = debugfs_create_file("testmode",
+ S_IRUGO | S_IWUSR, root,
+ hsotg, &testmode_fops);
+
+ if (IS_ERR(hsotg->debug_testmode))
+ dev_err(hsotg->dev, "%s: failed to create testmode\n",
+ __func__);
+
+ hsotg->debug_fifo = debugfs_create_file("fifo", S_IRUGO, root,
hsotg, &fifo_fops);
if (IS_ERR(hsotg->debug_fifo))
ep = hsotg->eps_out[epidx];
if (ep) {
- ep->debugfs = debugfs_create_file(ep->name, 0444,
+ ep->debugfs = debugfs_create_file(ep->name, S_IRUGO,
root, ep, &ep_fops);
if (IS_ERR(ep->debugfs))
ep = hsotg->eps_in[epidx];
if (ep) {
- ep->debugfs = debugfs_create_file(ep->name, 0444,
+ ep->debugfs = debugfs_create_file(ep->name, S_IRUGO,
root, ep, &ep_fops);
if (IS_ERR(ep->debugfs))
}
debugfs_remove(hsotg->debug_file);
+ debugfs_remove(hsotg->debug_testmode);
debugfs_remove(hsotg->debug_fifo);
debugfs_remove(hsotg->debug_root);
}
/* usb phy enable */
s3c_hsotg_phy_enable(hsotg);
+ /*
+ * Force Device mode before initialization.
+ * This allows correctly configuring fifo for device mode.
+ */
+ __bic32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEHOSTMODE);
+ __orr32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEDEVMODE);
+
+ /*
+ * According to Synopsys databook, this sleep is needed for the force
+ * device mode to take effect.
+ */
+ msleep(25);
+
s3c_hsotg_corereset(hsotg);
ret = s3c_hsotg_hw_cfg(hsotg);
if (ret) {
s3c_hsotg_init(hsotg);
+ /* Switch back to default configuration */
+ __bic32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEDEVMODE);
+
hsotg->ctrl_buff = devm_kzalloc(hsotg->dev,
DWC2_CTRL_BUFF_SIZE, GFP_KERNEL);
if (!hsotg->ctrl_buff) {
s3c_hsotg_phy_enable(hsotg);
spin_lock_irqsave(&hsotg->lock, flags);
- s3c_hsotg_core_init_disconnected(hsotg);
+ s3c_hsotg_core_init_disconnected(hsotg, false);
if (hsotg->enabled)
s3c_hsotg_core_connect(hsotg);
spin_unlock_irqrestore(&hsotg->lock, flags);