//#define DEBUG //#define VERBOSE /* Transfer descriptors. See dump_ptd() for printout format */ //#define PTD_TRACE /* enqueuing/finishing log of urbs */ //#define URB_TRACE
/* buffer is already in 'usb data order', which is LE. */ /* When reading buffer as u16, we have to take care byte order */ /* doesn't get mixed up */
if ((unsignedlong)dp2 & 1) { /* not aligned */ for (; len > 1; len -= 2) {
w = *dp++;
w |= *dp++ << 8;
isp116x_raw_write_data16(isp116x, w);
} if (len)
isp116x_write_data16(isp116x, (u16) * dp);
} else { /* aligned */ for (; len > 1; len -= 2) { /* Keep byte order ! */
isp116x_raw_write_data16(isp116x, cpu_to_le16(*dp2++));
}
if (len)
isp116x_write_data16(isp116x, 0xff & *((u8 *) dp2));
} if (quot == 1 || quot == 2)
isp116x_raw_write_data16(isp116x, 0);
}
/* buffer is already in 'usb data order', which is LE. */ /* When reading buffer as u16, we have to take care byte order */ /* doesn't get mixed up */
if ((unsignedlong)dp2 & 1) { /* not aligned */ for (; len > 1; len -= 2) {
w = isp116x_raw_read_data16(isp116x);
*dp++ = w & 0xff;
*dp++ = (w >> 8) & 0xff;
}
if (len)
*dp = 0xff & isp116x_read_data16(isp116x);
} else { /* aligned */ for (; len > 1; len -= 2) { /* Keep byte order! */
*dp2++ = le16_to_cpu(isp116x_raw_read_data16(isp116x));
}
if (len)
*(u8 *) dp2 = 0xff & isp116x_read_data16(isp116x);
} if (quot == 1 || quot == 2)
isp116x_raw_read_data16(isp116x);
}
for (ep = isp116x->atl_active; ep; ep = ep->active) {
BUG_ON(list_empty(&ep->hep->urb_list));
urb =
container_of(ep->hep->urb_list.next, struct urb, urb_list);
udev = urb->dev;
ptd = &ep->ptd;
cc = PTD_GET_CC(ptd);
short_not_ok = 1;
status = -EINPROGRESS;
/* Data underrun is special. For allowed underrun wecleartheerrorandcontinueasnormal.For forbiddenunderrunwefinishtheDATAstage immediatelywhileforcontroltransfer,
we do a STATUS stage. */ if (cc == TD_DATAUNDERRUN) { if (!(urb->transfer_flags & URB_SHORT_NOT_OK) ||
usb_pipecontrol(urb->pipe)) {
DBG("Allowed or control data underrun\n");
cc = TD_CC_NOERROR;
short_not_ok = 0;
} else {
ep->error_count = 1;
usb_settoggle(udev, ep->epnum,
ep->nextpid == USB_PID_OUT,
PTD_GET_TOGGLE(ptd));
urb->actual_length += PTD_GET_COUNT(ptd);
status = cc_to_error[TD_DATAUNDERRUN]; goto done;
}
}
if (cc != TD_CC_NOERROR && cc != TD_NOTACCESSED
&& (++ep->error_count >= 3 || cc == TD_CC_STALL
|| cc == TD_DATAOVERRUN)) {
status = cc_to_error[cc]; if (ep->nextpid == USB_PID_ACK)
ep->nextpid = 0; goto done;
} /* According to usb spec, zero-length Int transfer signals finishingoftheurb.Hey,doesthisapplyonly
for IN endpoints? */ if (usb_pipeint(urb->pipe) && !PTD_GET_LEN(ptd)) {
status = 0; goto done;
}
/* Relax after previously failed, but later succeeded
or correctly NAK'ed retransmission attempt */ if (ep->error_count
&& (cc == TD_CC_NOERROR || cc == TD_NOTACCESSED))
ep->error_count = 0;
/* Take into account idiosyncracies of the isp116x chip
regarding toggle bit for failed transfers */ if (ep->nextpid == USB_PID_OUT)
usb_settoggle(udev, ep->epnum, 1, PTD_GET_TOGGLE(ptd)
^ (ep->error_count > 0)); elseif (ep->nextpid == USB_PID_IN)
usb_settoggle(udev, ep->epnum, 0, PTD_GET_TOGGLE(ptd)
^ (ep->error_count > 0));
switch (ep->nextpid) { case USB_PID_IN: case USB_PID_OUT:
urb->actual_length += PTD_GET_COUNT(ptd); if (PTD_GET_ACTIVE(ptd)
|| (cc != TD_CC_NOERROR && cc < 0x0E)) break; if (urb->transfer_buffer_length != urb->actual_length) { if (short_not_ok) break;
} else { if (urb->transfer_flags & URB_ZERO_PACKET
&& ep->nextpid == USB_PID_OUT
&& !(PTD_GET_COUNT(ptd) % ep->maxpacket)) {
DBG("Zero packet requested\n"); break;
}
} /* All data for this URB is transferred, let's finish */ if (usb_pipecontrol(urb->pipe))
ep->nextpid = USB_PID_ACK; else
status = 0; break; case USB_PID_SETUP: if (PTD_GET_ACTIVE(ptd)
|| (cc != TD_CC_NOERROR && cc < 0x0E)) break; if (urb->transfer_buffer_length == urb->actual_length)
ep->nextpid = USB_PID_ACK; elseif (usb_pipeout(urb->pipe)) {
usb_settoggle(udev, 0, 1, 1);
ep->nextpid = USB_PID_OUT;
} else {
usb_settoggle(udev, 0, 0, 1);
ep->nextpid = USB_PID_IN;
} break; case USB_PID_ACK: if (PTD_GET_ACTIVE(ptd)
|| (cc != TD_CC_NOERROR && cc < 0x0E)) break;
status = 0;
ep->nextpid = 0; break; default:
BUG();
}
/* Schedule int transfers */ if (isp116x->periodic_count) {
isp116x->fmindex = index =
(isp116x->fmindex + 1) & (PERIODIC_SIZE - 1);
load = isp116x->load[index]; if (load) { /* Bring all int transfers for this frame
into the active queue */
isp116x->atl_active = last_ep =
isp116x->periodic[index]; while (last_ep->next)
last_ep = (last_ep->active = last_ep->next);
last_ep->active = NULL;
}
}
if (ep->nextpid == USB_PID_SETUP) {
len = sizeof(struct usb_ctrlrequest);
} elseif (ep->nextpid == USB_PID_ACK) {
len = 0;
} else { /* Find current free length ... */
len = (MAX_LOAD_LIMIT - load) / byte_time;
/* ... then limit it to configured max size ... */
len = min(len, speed == USB_SPEED_LOW ?
MAX_TRANSFER_SIZE_LOWSPEED :
MAX_TRANSFER_SIZE_FULLSPEED);
/* ... and finally cut to the multiple of MaxPacketSize,
or to the real length if there's enough room. */ if (len <
(urb->transfer_buffer_length -
urb->actual_length)) {
len -= len % ep->maxpacket; if (!len) continue;
} else
len = urb->transfer_buffer_length -
urb->actual_length;
BUG_ON(len < 0);
}
load += len * byte_time; if (load > MAX_LOAD_LIMIT) break;
if (irqstat & (HCuPINT_ATL | HCuPINT_SOF)) {
ret = IRQ_HANDLED;
finish_atl_transfers(isp116x);
}
if (irqstat & HCuPINT_OPR) {
u32 intstat = isp116x_read_reg32(isp116x, HCINTSTAT);
isp116x_write_reg32(isp116x, HCINTSTAT, intstat); if (intstat & HCINT_UE) {
ERR("Unrecoverable error, HC is dead!\n"); /* IRQ's are off, we do no DMA,
perfectly ready to die ... */
hcd->state = HC_STATE_HALT;
usb_hc_died(hcd);
ret = IRQ_HANDLED; goto done;
} if (intstat & HCINT_RHSC) /* When root hub or any of its ports is going tocomeoutofsuspend,itmaytakemore
than 10ms for status bits to stabilize. */
mod_timer(&hcd->rh_timer, jiffies
+ msecs_to_jiffies(20) + 1); if (intstat & HCINT_RD) {
DBG("---- remote wakeup\n");
usb_hcd_resume_root_hub(hcd);
}
irqstat &= ~HCuPINT_OPR;
ret = IRQ_HANDLED;
}
if (irqstat & (HCuPINT_ATL | HCuPINT_SOF)) {
start_atl_transfers(isp116x);
}
/* usb 1.1 says max 90% of a frame is available for periodic transfers. *thisdriverdoesn'tpromisethatmuchsinceit'sgottohandlean *IRQperpacket;irqhandlinglatenciesalsouseupthattime.
*/
/* out of 1000 us */ #define MAX_PERIODIC_LOAD 600 staticint balance(struct isp116x *isp116x, u16 period, u16 load)
{ int i, branch = -ENOSPC;
/* search for the least loaded schedule branch of that period
which has enough bandwidth left unreserved. */ for (i = 0; i < period; i++) { if (branch < 0 || isp116x->load[branch] > isp116x->load[i]) { int j;
staticint isp116x_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
gfp_t mem_flags)
{ struct isp116x *isp116x = hcd_to_isp116x(hcd); struct usb_device *udev = urb->dev; unsignedint pipe = urb->pipe; int is_out = !usb_pipein(pipe); int type = usb_pipetype(pipe); int epnum = usb_pipeendpoint(pipe); struct usb_host_endpoint *hep = urb->ep; struct isp116x_ep *ep = NULL; unsignedlong flags; int i; int ret = 0;
urb_dbg(urb, "Enqueue");
if (type == PIPE_ISOCHRONOUS) {
ERR("Isochronous transfers not supported\n");
urb_dbg(urb, "Refused to enqueue"); return -ENXIO;
} /* avoid all allocations within spinlocks: request or endpoint */ if (!hep->hcpriv) {
ep = kzalloc(sizeof *ep, mem_flags); if (!ep) return -ENOMEM;
}
spin_lock_irqsave(&isp116x->lock, flags); if (!HC_IS_RUNNING(hcd->state)) {
kfree(ep);
ret = -ENODEV; goto fail_not_linked;
}
ret = usb_hcd_link_urb_to_ep(hcd, urb); if (ret) {
kfree(ep); goto fail_not_linked;
}
if (urb->interval) { /* WithINTURBssubmitted,thedriverworkswithSOF interruptenabledandATLinterruptdisabled.After thePTDsarewrittentofiforam,thechipstarts fifoprocessingandusbtransfersafterthenext SOFandcontinuesuntilthetransfersarefinished (succeededorfailed)ortheframeends.Therefore, thetransfersoccuronlyineverysecondframe, whilefiforeading/writinganddataprocessing
occur in every other second frame. */ if (urb->interval < 2)
urb->interval = 2; if (urb->interval > 2 * PERIODIC_SIZE)
urb->interval = 2 * PERIODIC_SIZE;
ep->period = urb->interval >> 1;
ep->branch = PERIODIC_SIZE;
ep->load = usb_calc_bus_time(udev->speed,
!is_out,
(type == PIPE_ISOCHRONOUS),
usb_maxpacket(udev, pipe)) / 1000;
}
hep->hcpriv = ep;
ep->hep = hep;
}
/* maybe put endpoint into schedule */ switch (type) { case PIPE_CONTROL: case PIPE_BULK: if (list_empty(&ep->schedule))
list_add_tail(&ep->schedule, &isp116x->async); break; case PIPE_INTERRUPT:
urb->interval = ep->period;
ep->length = min_t(u32, ep->maxpacket,
urb->transfer_buffer_length);
/* urb submitted for already existing endpoint */ if (ep->branch < PERIODIC_SIZE) break;
ep->branch = ret = balance(isp116x, ep->period, ep->load); if (ret < 0) goto fail;
ret = 0;
/* assume we'd just wait for the irq */ for (i = 0; i < 100 && !list_empty(&hep->urb_list); i++)
msleep(3); if (!list_empty(&hep->urb_list))
WARNING("ep %p not empty?\n", ep);
seq_printf(s, "%s\n%s version %s\n",
isp116x_to_hcd(isp116x)->product_desc, hcd_name,
DRIVER_VERSION);
if (HC_IS_SUSPENDED(isp116x_to_hcd(isp116x)->state)) {
seq_printf(s, "HCD is suspended\n"); return0;
} if (!HC_IS_RUNNING(isp116x_to_hcd(isp116x)->state)) {
seq_printf(s, "HCD not running\n"); return0;
}
ret = isp116x_sw_reset(isp116x); if (ret) return ret;
t = jiffies + msecs_to_jiffies(timeout); while (time_before_eq(jiffies, t)) {
msleep(4);
spin_lock_irq(&isp116x->lock);
clkrdy = isp116x_read_reg16(isp116x, HCuPINT) & HCuPINT_CLKRDY;
spin_unlock_irq(&isp116x->lock); if (clkrdy) break;
} if (!clkrdy) {
ERR("Clock not ready after %dms\n", timeout); /* After sw_reset the clock won't report to be ready, if
H_WAKEUP pin is high. */
ERR("Please make sure that the H_WAKEUP pin is pulled low!\n");
ret = -ENODEV;
} return ret;
}
/* Switch off ports' power, some devices don't come up
after next 'insmod' without this */
val = isp116x_read_reg32(isp116x, HCRHDESCA);
val &= ~(RH_A_NPS | RH_A_PSM);
isp116x_write_reg32(isp116x, HCRHDESCA, val);
isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPS);
spin_unlock_irqrestore(&isp116x->lock, flags);
/* ----- HW conf */
val = HCHWCFG_INT_ENABLE | HCHWCFG_DBWIDTH(1); if (board->sel15Kres)
val |= HCHWCFG_15KRSEL; /* Remote wakeup won't work without working clock */ if (board->remote_wakeup_enable)
val |= HCHWCFG_CLKNOTSTOP; if (board->oc_enable)
val |= HCHWCFG_ANALOG_OC; if (board->int_act_high)
val |= HCHWCFG_INT_POL; if (board->int_edge_triggered)
val |= HCHWCFG_INT_TRIGGER;
isp116x_write_reg16(isp116x, HCHWCFG, val);
/* ----- Root hub conf */
val = (25 << 24) & RH_A_POTPGT; /* AN10003_1.pdf recommends RH_A_NPS (no power switching) to bealwaysset.Yet,instead,werequestindividualport
power switching. */
val |= RH_A_PSM; /* Report overcurrent per port */
val |= RH_A_OCPM;
isp116x_write_reg32(isp116x, HCRHDESCA, val);
isp116x->rhdesca = isp116x_read_reg32(isp116x, HCRHDESCA);
val = RH_B_PPCM;
isp116x_write_reg32(isp116x, HCRHDESCB, val);
isp116x->rhdescb = isp116x_read_reg32(isp116x, HCRHDESCB);
val = 0; if (board->remote_wakeup_enable) { if (!device_can_wakeup(hcd->self.controller))
device_init_wakeup(hcd->self.controller, 1);
val |= RH_HS_DRWE;
}
isp116x_write_reg32(isp116x, HCRHSTATUS, val);
isp116x->rhstatus = isp116x_read_reg32(isp116x, HCRHSTATUS);
val = isp116x_read_reg32(isp116x, HCCONTROL); switch (val & HCCONTROL_HCFS) { case HCCONTROL_USB_SUSPEND:
val &= ~HCCONTROL_HCFS;
val |= HCCONTROL_USB_RESUME;
isp116x_write_reg32(isp116x, HCCONTROL, val); break; case HCCONTROL_USB_RESUME: break; case HCCONTROL_USB_OPER:
spin_unlock_irq(&isp116x->lock); return0; default: /* HCCONTROL_USB_RESET: this may happen, when during
suspension the HC lost power. Reinitialize completely */
spin_unlock_irq(&isp116x->lock);
DBG("Chip has been reset while suspended. Reinit from scratch.\n");
isp116x_reset(hcd);
isp116x_start(hcd);
isp116x_hub_control(hcd, SetPortFeature,
USB_PORT_FEAT_POWER, 1, NULL, 0); if ((isp116x->rhdesca & RH_A_NDP) == 2)
isp116x_hub_control(hcd, SetPortFeature,
USB_PORT_FEAT_POWER, 2, NULL, 0); return0;
}
val = isp116x->rhdesca & RH_A_NDP; while (val--) {
u32 stat =
isp116x_read_reg32(isp116x, val ? HCRHPORT2 : HCRHPORT1); /* force global, not selective, resume */ if (!(stat & RH_PS_PSS)) continue;
DBG("%s: Resuming port %d\n", __func__, val);
isp116x_write_reg32(isp116x, RH_PS_POCI, val
? HCRHPORT2 : HCRHPORT1);
}
spin_unlock_irq(&isp116x->lock);
if (!hcd) return;
isp116x = hcd_to_isp116x(hcd);
remove_debug_file(isp116x);
usb_remove_hcd(hcd);
iounmap(isp116x->data_reg);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (res)
release_mem_region(res->start, 2);
iounmap(isp116x->addr_reg);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res)
release_mem_region(res->start, 2);
usb_put_hcd(hcd);
}
staticint isp116x_probe(struct platform_device *pdev)
{ struct usb_hcd *hcd; struct isp116x *isp116x; struct resource *addr, *data, *ires; void __iomem *addr_reg; void __iomem *data_reg; int irq; int ret = 0; unsignedlong irqflags;
if (usb_disabled()) return -ENODEV;
if (pdev->num_resources < 3) {
ret = -ENODEV; goto err1;
}
if (!request_mem_region(addr->start, 2, hcd_name)) {
ret = -EBUSY; goto err1;
}
addr_reg = ioremap(addr->start, resource_size(addr)); if (addr_reg == NULL) {
ret = -ENOMEM; goto err2;
} if (!request_mem_region(data->start, 2, hcd_name)) {
ret = -EBUSY; goto err3;
}
data_reg = ioremap(data->start, resource_size(data)); if (data_reg == NULL) {
ret = -ENOMEM; goto err4;
}
/* allocate and initialize hcd */
hcd = usb_create_hcd(&isp116x_hc_driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) {
ret = -ENOMEM; goto err5;
} /* this rsrc_start is bogus */
hcd->rsrc_start = addr->start;
isp116x = hcd_to_isp116x(hcd);
isp116x->data_reg = data_reg;
isp116x->addr_reg = addr_reg;
spin_lock_init(&isp116x->lock);
INIT_LIST_HEAD(&isp116x->async);
isp116x->board = dev_get_platdata(&pdev->dev);
if (!isp116x->board) {
ERR("Platform data structure not initialized\n");
ret = -ENODEV; goto err6;
} if (isp116x_check_platform_delay(isp116x)) {
ERR("USE_PLATFORM_DELAY defined, but delay function not " "implemented.\n");
ERR("See comments in drivers/usb/host/isp116x-hcd.c\n");
ret = -ENODEV; goto err6;
}
ret = usb_add_hcd(hcd, irq, irqflags); if (ret) goto err6;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.