// SPDX-License-Identifier: GPL-2.0 /* * ISP116x HCD (Host Controller Driver) for USB. * * Derived from the SL811 HCD, rewritten for ISP116x. * Copyright (C) 2005 Olav Kongas <ok@artecdesign.ee> * * Portions: * Copyright (C) 2004 Psion Teklogix (for NetBook PRO) * Copyright (C) 2004 David Brownell * * Periodic scheduling is based on Roman's OHCI code * Copyright (C) 1999 Roman Weissgaerber *
*/
/* * The driver basically works. A number of people have used it with a range * of devices. * * The driver passes all usbtests 1-14. * * Suspending/resuming of root hub via sysfs works. Remote wakeup works too. * And suspending/resuming of platform device works too. Suspend/resume * via HCD operations vector is not implemented. * * Iso transfer support is not implemented. Adding this would include * implementing recovery from the failure to service the processed ITL * fifo ram in time, which will involve chip reset. * * TODO: + More testing of suspend/resume.
*/
/* ISP116x chips require certain delays between accesses to its registers. The following timing options exist.
1. Configure your memory controller (the best) 2. Implement platform-specific delay function possibly combined with configuring the memory controller; see include/linux/usb-isp116x.h for more info. Some broken memory controllers line LH7A400 SMC need this. Also, uncomment for that to work the following USE_PLATFORM_DELAY macro. 3. Use ndelay (easiest, poorest). For that, uncomment the following USE_NDELAY macro.
*/ #define USE_PLATFORM_DELAY //#define USE_NDELAY
//#define DEBUG //#define VERBOSE /* Transfer descriptors. See dump_ptd() for printout format */ //#define PTD_TRACE /* enqueuing/finishing log of urbs */ //#define URB_TRACE
/* Write len bytes to fifo, pad till 32-bit boundary
*/ staticvoid write_ptddata_to_fifo(struct isp116x *isp116x, void *buf, int len)
{
u8 *dp = (u8 *) buf;
u16 *dp2 = (u16 *) buf;
u16 w; int quot = len % 4;
/* 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);
}
/* Read len bytes from fifo and then read till 32-bit boundary.
*/ staticvoid read_ptddata_from_fifo(struct isp116x *isp116x, void *buf, int len)
{
u8 *dp = (u8 *) buf;
u16 *dp2 = (u16 *) buf;
u16 w; int quot = len % 4;
/* 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);
}
/* Write ptd's and data for scheduled transfers into the fifo ram. Fifo must be empty and ready.
*/ staticvoid pack_fifo(struct isp116x *isp116x)
{ struct isp116x_ep *ep; struct ptd *ptd; int buflen = isp116x->atl_last_dir == PTD_DIR_IN
? isp116x->atl_bufshrt : isp116x->atl_buflen;
isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT);
isp116x_write_reg16(isp116x, HCXFERCTR, buflen);
isp116x_write_addr(isp116x, HCATLPORT | ISP116x_WRITE_OFFSET); for (ep = isp116x->atl_active; ep; ep = ep->active) {
ptd = &ep->ptd;
dump_ptd(ptd);
dump_ptd_out_data(ptd, ep->data);
isp116x_write_data16(isp116x, ptd->count);
isp116x_write_data16(isp116x, ptd->mps);
isp116x_write_data16(isp116x, ptd->len);
isp116x_write_data16(isp116x, ptd->faddr);
buflen -= sizeof(struct ptd); /* Skip writing data for last IN PTD */ if (ep->active || (isp116x->atl_last_dir != PTD_DIR_IN)) {
write_ptddata_to_fifo(isp116x, ep->data, ep->length);
buflen -= ALIGN(ep->length, 4);
}
}
BUG_ON(buflen);
}
/* Read the processed ptd's and data from fifo ram back to URBs' buffers. Fifo must be full and done
*/ staticvoid unpack_fifo(struct isp116x *isp116x)
{ struct isp116x_ep *ep; struct ptd *ptd; int buflen = isp116x->atl_last_dir == PTD_DIR_IN
? isp116x->atl_buflen : isp116x->atl_bufshrt;
isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT);
isp116x_write_reg16(isp116x, HCXFERCTR, buflen);
isp116x_write_addr(isp116x, HCATLPORT); for (ep = isp116x->atl_active; ep; ep = ep->active) {
ptd = &ep->ptd;
ptd->count = isp116x_read_data16(isp116x);
ptd->mps = isp116x_read_data16(isp116x);
ptd->len = isp116x_read_data16(isp116x);
ptd->faddr = isp116x_read_data16(isp116x);
buflen -= sizeof(struct ptd); /* Skip reading data for last Setup or Out PTD */ if (ep->active || (isp116x->atl_last_dir == PTD_DIR_IN)) {
read_ptddata_from_fifo(isp116x, ep->data, ep->length);
buflen -= ALIGN(ep->length, 4);
}
dump_ptd(ptd);
dump_ptd_in_data(ptd, ep->data);
}
BUG_ON(buflen);
}
/* Take done or failed requests out of schedule. Give back processed urbs.
*/ staticvoid finish_request(struct isp116x *isp116x, struct isp116x_ep *ep, struct urb *urb, int status)
__releases(isp116x->lock) __acquires(isp116x->lock)
{ unsigned i;
ep->error_count = 0;
if (usb_pipecontrol(urb->pipe))
ep->nextpid = USB_PID_SETUP;
/* Analyze transfer results, handle partial transfers and errors
*/ staticvoid postproc_atl_queue(struct isp116x *isp116x)
{ struct isp116x_ep *ep; struct urb *urb; struct usb_device *udev; struct ptd *ptd; int short_not_ok; int status;
u8 cc;
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 we clear the error and continue as normal. For forbidden underrun we finish the DATA stage immediately while for control transfer,
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 finishing of the urb. Hey, does this apply only
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 to come out of suspend, it may take more
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. * this driver doesn't promise that much since it's got to handle an * IRQ per packet; irq handling latencies also use up that time.
*/
/* 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) { /* With INT URBs submitted, the driver works with SOF interrupt enabled and ATL interrupt disabled. After the PTDs are written to fifo ram, the chip starts fifo processing and usb transfers after the next SOF and continues until the transfers are finished (succeeded or failed) or the frame ends. Therefore, the transfers occur only in every second frame, while fifo reading/writing and data processing
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;
/* sort each schedule branch by period (slow before fast) to share the faster parts of the tree without needing
dummy/placeholder nodes */
DBG("schedule qh%d/%p branch %d\n", ep->period, ep, ep->branch); for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) { struct isp116x_ep **prev = &isp116x->periodic[i]; struct isp116x_ep *here = *prev;
while (here && ep != here) { if (ep->period > here->period) break;
prev = &here->next;
here = *prev;
} if (ep != here) {
ep->next = here;
*prev = ep;
}
isp116x->load[i] += ep->load;
}
hcd->self.bandwidth_allocated += ep->load / ep->period;
/* switch over to SOFint */ if (!isp116x->periodic_count++) {
isp116x->irqenb &= ~HCuPINT_ATL;
isp116x->irqenb |= HCuPINT_SOF;
isp116x_write_reg16(isp116x, HCuPINTENB,
isp116x->irqenb);
}
}
/* 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);
/* Adapted from ohci-hub.c. Currently we don't support autosuspend.
*/ staticint isp116x_hub_status_data(struct usb_hcd *hcd, char *buf)
{ struct isp116x *isp116x = hcd_to_isp116x(hcd); int ports, i, changed = 0; unsignedlong flags;
if (!HC_IS_RUNNING(hcd->state)) return -ESHUTDOWN;
/* Report no status change now, if we are scheduled to be
called later */ if (timer_pending(&hcd->rh_timer)) return 0;
/* Perform reset of a given port. It would be great to just start the reset and let the USB core to clear the reset in due time. However, root hub ports should be reset for at least 50 ms, while our chip stays in reset for about 10 ms. I.e., we must repeatedly reset it ourself here.
*/ staticinlinevoid root_port_reset(struct isp116x *isp116x, unsigned port)
{
u32 tmp; unsignedlong flags, t;
/* Root hub reset should be 50 ms, but some devices
want it even longer. */
t = jiffies + msecs_to_jiffies(100);
while (time_before(jiffies, t)) {
spin_lock_irqsave(&isp116x->lock, flags); /* spin until any current reset finishes */ for (;;) {
tmp = isp116x_read_reg32(isp116x, port ?
HCRHPORT2 : HCRHPORT1); if (!(tmp & RH_PS_PRS)) break;
udelay(500);
} /* Don't reset a disconnected port */ if (!(tmp & RH_PS_CCS)) {
spin_unlock_irqrestore(&isp116x->lock, flags); break;
} /* Reset lasts 10ms (claims datasheet) */
isp116x_write_reg32(isp116x, port ? HCRHPORT2 :
HCRHPORT1, (RH_PS_PRS));
spin_unlock_irqrestore(&isp116x->lock, flags);
msleep(10);
}
}
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"); return 0;
} if (!HC_IS_RUNNING(isp116x_to_hcd(isp116x)->state)) {
seq_printf(s, "HCD not running\n"); return 0;
}
/* Software reset - can be called from any contect.
*/ staticint isp116x_sw_reset(struct isp116x *isp116x)
{ int retries = 15; unsignedlong flags; int ret = 0;
spin_lock_irqsave(&isp116x->lock, flags);
isp116x_write_reg16(isp116x, HCSWRES, HCSWRES_MAGIC);
isp116x_write_reg32(isp116x, HCCMDSTAT, HCCMDSTAT_HCR); while (--retries) { /* It usually resets within 1 ms */
mdelay(1); if (!(isp116x_read_reg32(isp116x, HCCMDSTAT) & HCCMDSTAT_HCR)) break;
} if (!retries) {
ERR("Software reset timeout\n");
ret = -ETIME;
}
spin_unlock_irqrestore(&isp116x->lock, flags); return ret;
}
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);
isp116x_sw_reset(isp116x);
}
/* Configure the chip. The chip must be successfully reset by now.
*/ staticint isp116x_start(struct usb_hcd *hcd)
{ struct isp116x *isp116x = hcd_to_isp116x(hcd); struct isp116x_platform_data *board = isp116x->board;
u32 val; unsignedlong flags;
spin_lock_irqsave(&isp116x->lock, flags);
/* clear interrupt status and disable all interrupt sources */
isp116x_write_reg16(isp116x, HCuPINT, 0xff);
isp116x_write_reg16(isp116x, HCuPINTENB, 0);
val = isp116x_read_reg16(isp116x, HCCHIPID); if ((val & HCCHIPID_MASK) != HCCHIPID_MAGIC) {
ERR("Invalid chip ID %04x\n", val);
spin_unlock_irqrestore(&isp116x->lock, flags); return -ENODEV;
}
/* To be removed in future */
hcd->uses_new_polling = 1;
/* ----- 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 be always set. Yet, instead, we request individual port
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); return 0; 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); return 0;
}
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.