diff options
Diffstat (limited to 'drivers/usb/gadget/s3c-hsotg.c')
-rw-r--r-- | drivers/usb/gadget/s3c-hsotg.c | 176 |
1 files changed, 131 insertions, 45 deletions
diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 6bddf1aa234..e20bc109fdd 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -83,9 +83,12 @@ struct s3c_hsotg_req; * @dir_in: Set to true if this endpoint is of the IN direction, which * means that it is sending data to the Host. * @index: The index for the endpoint registers. + * @mc: Multi Count - number of transactions per microframe + * @interval - Interval for periodic endpoints * @name: The name array passed to the USB core. * @halted: Set if the endpoint has been halted. * @periodic: Set if this is a periodic ep, such as Interrupt + * @isochronous: Set if this is a isochronous ep * @sent_zlp: Set if we've sent a zero-length packet. * @total_data: The total number of data bytes done. * @fifo_size: The size of the FIFO (for periodic IN endpoints) @@ -121,9 +124,12 @@ struct s3c_hsotg_ep { unsigned char dir_in; unsigned char index; + unsigned char mc; + unsigned char interval; unsigned int halted:1; unsigned int periodic:1; + unsigned int isochronous:1; unsigned int sent_zlp:1; char name[10]; @@ -468,6 +474,7 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg, void *data; int can_write; int pkt_round; + int max_transfer; to_write -= (buf_pos - hs_ep->last_load); @@ -535,15 +542,17 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg, can_write *= 4; /* fifo size is in 32bit quantities. */ } - dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, mps %d\n", - __func__, gnptxsts, can_write, to_write, hs_ep->ep.maxpacket); + max_transfer = hs_ep->ep.maxpacket * hs_ep->mc; + + dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, max_transfer %d\n", + __func__, gnptxsts, can_write, to_write, max_transfer); /* * limit to 512 bytes of data, it seems at least on the non-periodic * FIFO, requests of >512 cause the endpoint to get stuck with a * fragment of the end of the transfer in it. */ - if (can_write > 512) + if (can_write > 512 && !periodic) can_write = 512; /* @@ -551,19 +560,21 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg, * the transfer to return that it did not run out of fifo space * doing it. */ - if (to_write > hs_ep->ep.maxpacket) { - to_write = hs_ep->ep.maxpacket; + if (to_write > max_transfer) { + to_write = max_transfer; - s3c_hsotg_en_gsint(hsotg, - periodic ? GINTSTS_PTxFEmp : - GINTSTS_NPTxFEmp); + /* it's needed only when we do not use dedicated fifos */ + if (!hsotg->dedicated_fifos) + s3c_hsotg_en_gsint(hsotg, + periodic ? GINTSTS_PTxFEmp : + GINTSTS_NPTxFEmp); } /* see if we can write data */ if (to_write > can_write) { to_write = can_write; - pkt_round = to_write % hs_ep->ep.maxpacket; + pkt_round = to_write % max_transfer; /* * Round the write down to an @@ -581,9 +592,11 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg, * is more room left. */ - s3c_hsotg_en_gsint(hsotg, - periodic ? GINTSTS_PTxFEmp : - GINTSTS_NPTxFEmp); + /* it's needed only when we do not use dedicated fifos */ + if (!hsotg->dedicated_fifos) + s3c_hsotg_en_gsint(hsotg, + periodic ? GINTSTS_PTxFEmp : + GINTSTS_NPTxFEmp); } dev_dbg(hsotg->dev, "write %d/%d, can_write %d, done %d\n", @@ -727,8 +740,16 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg, else packets = 1; /* send one packet if length is zero. */ + if (hs_ep->isochronous && length > (hs_ep->mc * hs_ep->ep.maxpacket)) { + dev_err(hsotg->dev, "req length > maxpacket*mc\n"); + return; + } + if (dir_in && index != 0) - epsize = DxEPTSIZ_MC(1); + if (hs_ep->isochronous) + epsize = DxEPTSIZ_MC(packets); + else + epsize = DxEPTSIZ_MC(1); else epsize = 0; @@ -820,6 +841,9 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg, dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, readl(hsotg->regs + epctrl_reg)); + + /* enable ep interrupts */ + s3c_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 1); } /** @@ -1091,6 +1115,7 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE); struct s3c_hsotg_ep *ep; int ret; + bool halted; dev_dbg(hsotg->dev, "%s: %s_FEATURE\n", __func__, set ? "SET" : "CLEAR"); @@ -1105,6 +1130,8 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, switch (le16_to_cpu(ctrl->wValue)) { case USB_ENDPOINT_HALT: + halted = ep->halted; + s3c_hsotg_ep_sethalt(&ep->ep, set); ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0); @@ -1114,7 +1141,12 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, return ret; } - if (!set) { + /* + * we have to complete all requests for ep if it was + * halted, and the halt was cleared by CLEAR_FEATURE + */ + + if (!set && halted) { /* * If we have request in progress, * then complete it @@ -1147,6 +1179,9 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, return 1; } +static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg); +static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg); + /** * s3c_hsotg_process_control - process a control request * @hsotg: The device state @@ -1187,6 +1222,7 @@ static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg, if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { switch (ctrl->bRequest) { case USB_REQ_SET_ADDRESS: + s3c_hsotg_disconnect(hsotg); dcfg = readl(hsotg->regs + DCFG); dcfg &= ~DCFG_DevAddr_MASK; dcfg |= ctrl->wValue << DCFG_DevAddr_SHIFT; @@ -1211,7 +1247,9 @@ static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg, /* as a fallback, try delivering it to the driver to deal with */ if (ret == 0 && hsotg->driver) { + spin_unlock(&hsotg->lock); ret = hsotg->driver->setup(&hsotg->gadget, ctrl); + spin_lock(&hsotg->lock); if (ret < 0) dev_dbg(hsotg->dev, "driver->setup() ret %d\n", ret); } @@ -1246,11 +1284,15 @@ static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg, * don't believe we need to anything more to get the EP * to reply with a STALL packet */ + + /* + * complete won't be called, so we enqueue + * setup request here + */ + s3c_hsotg_enqueue_setup(hsotg); } } -static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg); - /** * s3c_hsotg_complete_setup - completion of a setup transfer * @ep: The endpoint the request was on. @@ -1270,10 +1312,12 @@ static void s3c_hsotg_complete_setup(struct usb_ep *ep, return; } + spin_lock(&hsotg->lock); if (req->actual == 0) s3c_hsotg_enqueue_setup(hsotg); else s3c_hsotg_process_control(hsotg, req->buf); + spin_unlock(&hsotg->lock); } /** @@ -1698,6 +1742,7 @@ static void s3c_hsotg_set_ep_maxpacket(struct s3c_hsotg *hsotg, struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep]; void __iomem *regs = hsotg->regs; u32 mpsval; + u32 mcval; u32 reg; if (ep == 0) { @@ -1705,15 +1750,19 @@ static void s3c_hsotg_set_ep_maxpacket(struct s3c_hsotg *hsotg, mpsval = s3c_hsotg_ep0_mps(mps); if (mpsval > 3) goto bad_mps; + hs_ep->ep.maxpacket = mps; + hs_ep->mc = 1; } else { - if (mps >= DxEPCTL_MPS_LIMIT+1) + mpsval = mps & DxEPCTL_MPS_MASK; + if (mpsval > 1024) goto bad_mps; - - mpsval = mps; + mcval = ((mps >> 11) & 0x3) + 1; + hs_ep->mc = mcval; + if (mcval > 3) + goto bad_mps; + hs_ep->ep.maxpacket = mpsval; } - hs_ep->ep.maxpacket = mps; - /* * update both the in and out endpoint controldir_ registers, even * if one of the directions may not be in use. @@ -1782,8 +1831,16 @@ static int s3c_hsotg_trytx(struct s3c_hsotg *hsotg, { struct s3c_hsotg_req *hs_req = hs_ep->req; - if (!hs_ep->dir_in || !hs_req) + if (!hs_ep->dir_in || !hs_req) { + /** + * if request is not enqueued, we disable interrupts + * for endpoints, excepting ep0 + */ + if (hs_ep->index != 0) + s3c_hsotg_ctrl_epint(hsotg, hs_ep->index, + hs_ep->dir_in, 0); return 0; + } if (hs_req->req.actual < hs_req->req.length) { dev_dbg(hsotg->dev, "trying to write more for ep%d\n", @@ -1887,8 +1944,10 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx); u32 epsiz_reg = dir_in ? DIEPTSIZ(idx) : DOEPTSIZ(idx); u32 ints; + u32 ctrl; ints = readl(hsotg->regs + epint_reg); + ctrl = readl(hsotg->regs + epctl_reg); /* Clear endpoint interrupts */ writel(ints, hsotg->regs + epint_reg); @@ -1897,6 +1956,14 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, __func__, idx, dir_in ? "in" : "out", ints); if (ints & DxEPINT_XferCompl) { + if (hs_ep->isochronous && hs_ep->interval == 1) { + if (ctrl & DxEPCTL_EOFrNum) + ctrl |= DxEPCTL_SetEvenFr; + else + ctrl |= DxEPCTL_SetOddFr; + writel(ctrl, hsotg->regs + epctl_reg); + } + dev_dbg(hsotg->dev, "%s: XferCompl: DxEPCTL=0x%08x, DxEPTSIZ=%08x\n", __func__, readl(hsotg->regs + epctl_reg), @@ -1963,7 +2030,7 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, if (ints & DxEPINT_Back2BackSetup) dev_dbg(hsotg->dev, "%s: B2BSetup/INEPNakEff\n", __func__); - if (dir_in) { + if (dir_in && !hs_ep->isochronous) { /* not sure if this is important, but we'll clear it anyway */ if (ints & DIEPMSK_INTknTXFEmpMsk) { dev_dbg(hsotg->dev, "%s: ep%d: INTknTXFEmpMsk\n", @@ -2092,12 +2159,14 @@ static void kill_all_requests(struct s3c_hsotg *hsotg, } #define call_gadget(_hs, _entry) \ +do { \ if ((_hs)->gadget.speed != USB_SPEED_UNKNOWN && \ (_hs)->driver && (_hs)->driver->_entry) { \ spin_unlock(&_hs->lock); \ (_hs)->driver->_entry(&(_hs)->gadget); \ spin_lock(&_hs->lock); \ - } + } \ +} while (0) /** * s3c_hsotg_disconnect - disconnect service @@ -2241,15 +2310,19 @@ static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg) GAHBCFG_HBstLen_Incr4, hsotg->regs + GAHBCFG); else - writel(GAHBCFG_GlblIntrEn, hsotg->regs + GAHBCFG); + writel(((hsotg->dedicated_fifos) ? (GAHBCFG_NPTxFEmpLvl | + GAHBCFG_PTxFEmpLvl) : 0) | + GAHBCFG_GlblIntrEn, + hsotg->regs + GAHBCFG); /* - * Enabling INTknTXFEmpMsk here seems to be a big mistake, we end - * up being flooded with interrupts if the host is polling the - * endpoint to try and read data. + * If INTknTXFEmpMsk is enabled, it's important to disable ep interrupts + * when we have no data to transfer. Otherwise we get being flooded by + * interrupts. */ - writel(((hsotg->dedicated_fifos) ? DIEPMSK_TxFIFOEmpty : 0) | + writel(((hsotg->dedicated_fifos) ? DIEPMSK_TxFIFOEmpty | + DIEPMSK_INTknTXFEmpMsk : 0) | DIEPMSK_EPDisbldMsk | DIEPMSK_XferComplMsk | DIEPMSK_TimeOUTMsk | DIEPMSK_AHBErrMsk | DIEPMSK_INTknEPMisMsk, @@ -2378,10 +2451,14 @@ irq_retry: if (gintsts & (GINTSTS_OEPInt | GINTSTS_IEPInt)) { u32 daint = readl(hsotg->regs + DAINT); - u32 daint_out = daint >> DAINT_OutEP_SHIFT; - u32 daint_in = daint & ~(daint_out << DAINT_OutEP_SHIFT); + u32 daintmsk = readl(hsotg->regs + DAINTMSK); + u32 daint_out, daint_in; int ep; + daint &= daintmsk; + daint_out = daint >> DAINT_OutEP_SHIFT; + daint_in = daint & ~(daint_out << DAINT_OutEP_SHIFT); + dev_dbg(hsotg->dev, "%s: daint=%08x\n", __func__, daint); for (ep = 0; ep < 15 && daint_out; ep++, daint_out >>= 1) { @@ -2462,7 +2539,6 @@ irq_retry: writel(GINTSTS_USBSusp, hsotg->regs + GINTSTS); call_gadget(hsotg, suspend); - s3c_hsotg_disconnect(hsotg); } if (gintsts & GINTSTS_WkUpInt) { @@ -2577,16 +2653,25 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, epctrl |= DxEPCTL_SNAK; /* update the endpoint state */ - hs_ep->ep.maxpacket = mps; + s3c_hsotg_set_ep_maxpacket(hsotg, hs_ep->index, mps); /* default, set to non-periodic */ + hs_ep->isochronous = 0; hs_ep->periodic = 0; + hs_ep->halted = 0; + hs_ep->interval = desc->bInterval; + + if (hs_ep->interval > 1 && hs_ep->mc > 1) + dev_err(hsotg->dev, "MC > 1 when interval is not 1\n"); switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { case USB_ENDPOINT_XFER_ISOC: - dev_err(hsotg->dev, "no current ISOC support\n"); - ret = -EINVAL; - goto out; + epctrl |= DxEPCTL_EPType_Iso; + epctrl |= DxEPCTL_SetEvenFr; + hs_ep->isochronous = 1; + if (dir_in) + hs_ep->periodic = 1; + break; case USB_ENDPOINT_XFER_BULK: epctrl |= DxEPCTL_EPType_Bulk; @@ -2634,7 +2719,6 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, /* enable the endpoint interrupt */ s3c_hsotg_ctrl_epint(hsotg, index, dir_in, 1); -out: spin_unlock_irqrestore(&hsotg->lock, flags); return ret; } @@ -2776,6 +2860,8 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) writel(epctl, hs->regs + epreg); + hs_ep->halted = value; + return 0; } @@ -2903,7 +2989,7 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget, int ret; if (!hsotg) { - printk(KERN_ERR "%s: called with no device\n", __func__); + pr_err("%s: called with no device\n", __func__); return -ENODEV; } @@ -3066,7 +3152,7 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg, hs_ep->parent = hsotg; hs_ep->ep.name = hs_ep->name; - hs_ep->ep.maxpacket = epnum ? 512 : EP0_MPS_LIMIT; + hs_ep->ep.maxpacket = epnum ? 1024 : EP0_MPS_LIMIT; hs_ep->ep.ops = &s3c_hsotg_ep_ops; /* @@ -3200,7 +3286,7 @@ static int state_show(struct seq_file *seq, void *v) readl(regs + GNPTXSTS), readl(regs + GRXSTSR)); - seq_printf(seq, "\nEndpoint status:\n"); + seq_puts(seq, "\nEndpoint status:\n"); for (idx = 0; idx < 15; idx++) { u32 in, out; @@ -3217,7 +3303,7 @@ static int state_show(struct seq_file *seq, void *v) seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x", in, out); - seq_printf(seq, "\n"); + seq_puts(seq, "\n"); } return 0; @@ -3251,7 +3337,7 @@ static int fifo_show(struct seq_file *seq, void *v) u32 val; int idx; - seq_printf(seq, "Non-periodic FIFOs:\n"); + seq_puts(seq, "Non-periodic FIFOs:\n"); seq_printf(seq, "RXFIFO: Size %d\n", readl(regs + GRXFSIZ)); val = readl(regs + GNPTXFSIZ); @@ -3259,7 +3345,7 @@ static int fifo_show(struct seq_file *seq, void *v) val >> GNPTXFSIZ_NPTxFDep_SHIFT, val & GNPTXFSIZ_NPTxFStAddr_MASK); - seq_printf(seq, "\nPeriodic TXFIFOs:\n"); + seq_puts(seq, "\nPeriodic TXFIFOs:\n"); for (idx = 1; idx <= 15; idx++) { val = readl(regs + DPTXFSIZn(idx)); @@ -3330,7 +3416,7 @@ static int ep_show(struct seq_file *seq, void *v) readl(regs + DIEPTSIZ(index)), readl(regs + DOEPTSIZ(index))); - seq_printf(seq, "\n"); + seq_puts(seq, "\n"); seq_printf(seq, "mps %d\n", ep->ep.maxpacket); seq_printf(seq, "total_data=%ld\n", ep->total_data); @@ -3341,7 +3427,7 @@ static int ep_show(struct seq_file *seq, void *v) list_for_each_entry(req, &ep->queue, queue) { if (--show_limit < 0) { - seq_printf(seq, "not showing more requests...\n"); + seq_puts(seq, "not showing more requests...\n"); break; } |