// SPDX-License-Identifier: GPL-2.0 /* * SL811HS HCD (Host Controller Driver) for USB. * * Copyright (C) 2004 Psion Teklogix (for NetBook PRO) * Copyright (C) 2004-2005 David Brownell * * Periodic scheduling is based on Roman's OHCI code * Copyright (C) 1999 Roman Weissgaerber * * The SL811HS controller handles host side USB (like the SL11H, but with * another register set and SOF generation) as well as peripheral side USB * (like the SL811S). This driver version doesn't implement the Gadget API * for the peripheral role; or OTG (that'd need much external circuitry). * * For documentation, see the SL811HS spec and the "SL811HS Embedded Host" * document (providing significant pieces missing from that spec); plus * the SL811S spec if you want peripheral side info.
*/
/* * Status: Passed basic stress testing, works with hubs, mice, keyboards, * and usb-storage. * * TODO: * - usb suspend/resume triggered by sl811 * - various issues noted in the code * - performance work; use both register banks; ... * - use urb->iso_frame_desc[] with ISO transfers
*/
if (sl811->board && sl811->board->port_power) { /* switch VBUS, at 500mA unless hub power budget gets set */
dev_dbg(hcd->self.controller, "power %s\n",
str_on_off(is_on));
sl811->board->port_power(hcd->self.controller, is_on);
}
/* reset as thoroughly as we can */ if (sl811->board && sl811->board->reset)
sl811->board->reset(hcd->self.controller); else {
sl811_write(sl811, SL11H_CTLREG1, SL11H_CTL1MASK_SE0);
mdelay(20);
}
/* This is a PIO-only HCD. Queueing appends URBs to the endpoint's queue, * and may start I/O. Endpoint queues are scanned during completion irq * handlers (one per packet: ACK, NAK, faults, etc) and urb cancellation. * * Using an external DMA engine to copy a packet at a time could work, * though setup/teardown costs may be too big to make it worthwhile.
*/
/* SETUP starts a new control request. Devices are not allowed to * STALL or NAK these; they must cancel any pending control requests.
*/ staticvoid setup_packet( struct sl811 *sl811, struct sl811h_ep *ep, struct urb *urb,
u8 bank,
u8 control
)
{
u8 addr;
u8 len; void __iomem *data_reg;
/* always OUT/data0 */
sl811_write(sl811, bank + SL11H_HOSTCTLREG,
control | SL11H_HCTLMASK_OUT);
ep->length = 0;
PACKET("SETUP qh%p\n", ep);
}
/* STATUS finishes control requests, often after IN or OUT data packets */ staticvoid status_packet( struct sl811 *sl811, struct sl811h_ep *ep, struct urb *urb,
u8 bank,
u8 control
)
{ int do_out; void __iomem *data_reg;
/* always data1; sometimes IN */
control |= SL11H_HCTLMASK_TOGGLE; if (do_out)
control |= SL11H_HCTLMASK_OUT;
sl811_write(sl811, bank + SL11H_HOSTCTLREG, control);
ep->length = 0;
PACKET("STATUS%s/%s qh%p\n", ep->nak_count ? "/retry" : "",
do_out ? "out" : "in", ep);
}
/* IN packets can be used with any type of endpoint. here we just * start the transfer, data from the peripheral may arrive later. * urb->iso_frame_desc is currently ignored here...
*/ staticvoid in_packet( struct sl811 *sl811, struct sl811h_ep *ep, struct urb *urb,
u8 bank,
u8 control
)
{
u8 addr;
u8 len; void __iomem *data_reg;
/* avoid losing data on overflow */
len = ep->maxpacket;
addr = SL811HS_PACKET_BUF(bank == 0); if (!(control & SL11H_HCTLMASK_ISOCH)
&& usb_gettoggle(urb->dev, ep->epnum, 0))
control |= SL11H_HCTLMASK_TOGGLE;
data_reg = sl811->data_reg;
/* OUT packets can be used with any type of endpoint. * urb->iso_frame_desc is currently ignored here...
*/ staticvoid out_packet( struct sl811 *sl811, struct sl811h_ep *ep, struct urb *urb,
u8 bank,
u8 control
)
{ void *buf;
u8 addr;
u8 len; void __iomem *data_reg;
/* pick the next endpoint for a transaction, and issue it. * frames start with periodic transfers (after whatever is pending * from the previous frame), and the rest of the time is async * transfers, scheduled round-robin.
*/ staticstruct sl811h_ep *start(struct sl811 *sl811, u8 bank)
{ struct sl811h_ep *ep; struct urb *urb; int fclock;
u8 control;
/* use endpoint at schedule head */ if (sl811->next_periodic) {
ep = sl811->next_periodic;
sl811->next_periodic = ep->next;
} else { if (sl811->next_async)
ep = sl811->next_async; elseif (!list_empty(&sl811->async))
ep = container_of(sl811->async.next, struct sl811h_ep, schedule); else { /* could set up the first fullspeed periodic * transfer for the next frame ...
*/ return NULL;
}
urb = container_of(ep->hep->urb_list.next, struct urb, urb_list);
control = ep->defctrl;
/* if this frame doesn't have enough time left to transfer this * packet, wait till the next frame. too-simple algorithm...
*/
fclock = sl811_read(sl811, SL11H_SOFTMRREG) << 6;
fclock -= 100; /* setup takes not much time */ if (urb->dev->speed == USB_SPEED_LOW) { if (control & SL11H_HCTLMASK_PREAMBLE) { /* also note erratum 1: some hubs won't work */
fclock -= 800;
}
fclock -= ep->maxpacket << 8;
/* erratum 2: AFTERSOF only works for fullspeed */ if (fclock < 0) { if (ep->period)
sl811->stat_overrun++;
sofirq_on(sl811); return NULL;
}
} else {
fclock -= 12000 / 19; /* 19 64byte packets/msec */ if (fclock < 0) { if (ep->period)
sl811->stat_overrun++;
control |= SL11H_HCTLMASK_AFTERSOF;
switch (ep->nextpid) { case USB_PID_IN:
in_packet(sl811, ep, urb, bank, control); break; case USB_PID_OUT:
out_packet(sl811, ep, urb, bank, control); break; case USB_PID_SETUP:
setup_packet(sl811, ep, urb, bank, control); break; case USB_PID_ACK: /* for control status */
status_packet(sl811, ep, urb, bank, control); break; default:
dev_dbg(sl811_to_hcd(sl811)->self.controller, "bad ep%p pid %02x\n", ep, ep->nextpid);
ep = NULL;
} return ep;
}
/* we can safely ignore NAKs */ if (status & SL11H_STATMASK_NAK) { // PACKET("...NAK_%02x qh%p\n", bank, ep); if (!ep->period)
ep->nak_count++;
ep->error_count = 0;
#ifdef QUIRK2 /* this may no longer be necessary ... */ if (irqstat == 0) {
irqstat = checkdone(sl811); if (irqstat)
sl811->stat_lost++;
} #endif
/* USB packets, not necessarily handled in the order they're * issued ... that's fine if they're different endpoints.
*/ if (irqstat & SL11H_INTMASK_DONE_A) {
done(sl811, sl811->active_a, SL811_EP_A(SL811_HOST_BUF));
sl811->active_a = NULL;
sl811->stat_a++;
} #ifdef USE_B if (irqstat & SL11H_INTMASK_DONE_B) {
done(sl811, sl811->active_b, SL811_EP_B(SL811_HOST_BUF));
sl811->active_b = NULL;
sl811->stat_b++;
} #endif if (irqstat & SL11H_INTMASK_SOFINTR) { unsigned index;
index = sl811->frame++ % (PERIODIC_SIZE - 1);
sl811->stat_sof++;
/* be graceful about almost-inevitable periodic schedule * overruns: continue the previous frame's transfers iff * this one has nothing scheduled.
*/ if (sl811->next_periodic) { // dev_err(hcd->self.controller, "overrun to slot %d\n", index);
sl811->stat_overrun++;
} if (sl811->periodic[index])
sl811->next_periodic = sl811->periodic[index];
}
/* hub_wq manages debouncing and wakeup */ if (irqstat & SL11H_INTMASK_INSRMV) {
sl811->stat_insrmv++;
/* most stats are reset for each VBUS session */
sl811->stat_wake = 0;
sl811->stat_sof = 0;
sl811->stat_a = 0;
sl811->stat_b = 0;
sl811->stat_lost = 0;
/* port status seems weird until after reset, so * force the reset and make hub_wq clean up later.
*/ if (irqstat & SL11H_INTMASK_RD)
sl811->port1 &= ~USB_PORT_STAT_CONNECTION; else
sl811->port1 |= USB_PORT_STAT_CONNECTION;
/* 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. * * NOTE: the periodic schedule is a sparse tree, with the load for * each branch minimized. see fig 3.5 in the OHCI spec for example.
*/ #define MAX_PERIODIC_LOAD 500 /* out of 1000 usec */
staticint balance(struct sl811 *sl811, 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 || sl811->load[branch] > sl811->load[i]) { int j;
staticint sl811h_urb_enqueue( struct usb_hcd *hcd, struct urb *urb,
gfp_t mem_flags
) { struct sl811 *sl811 = hcd_to_sl811(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 sl811h_ep *ep = NULL; unsignedlong flags; int i; int retval; struct usb_host_endpoint *hep = urb->ep;
#ifndef CONFIG_USB_SL811_HCD_ISO if (type == PIPE_ISOCHRONOUS) return -ENOSPC; #endif
/* avoid all allocations within spinlocks */ if (!hep->hcpriv) {
ep = kzalloc(sizeof *ep, mem_flags); if (ep == NULL) return -ENOMEM;
}
spin_lock_irqsave(&sl811->lock, flags);
/* don't submit to a dead or disabled port */ if (!(sl811->port1 & USB_PORT_STAT_ENABLE)
|| !HC_IS_RUNNING(hcd->state)) {
retval = -ENODEV;
kfree(ep); goto fail_not_linked;
}
retval = usb_hcd_link_urb_to_ep(hcd, urb); if (retval) {
kfree(ep); goto fail_not_linked;
}
if (hep->hcpriv) {
kfree(ep);
ep = hep->hcpriv;
} elseif (!ep) {
retval = -ENOMEM; goto fail;
if (ep->maxpacket > H_MAXPACKET) { /* iso packets up to 240 bytes could work... */
dev_dbg(hcd->self.controller, "dev %d ep%d maxpacket %d\n", udev->devnum,
epnum, ep->maxpacket);
retval = -EINVAL;
kfree(ep); goto fail;
}
if (udev->speed == USB_SPEED_LOW) { /* send preamble for external hub? */ if (!(sl811->ctrl1 & SL11H_CTL1MASK_LSPD))
ep->defctrl |= SL11H_HCTLMASK_PREAMBLE;
} switch (type) { case PIPE_ISOCHRONOUS: case PIPE_INTERRUPT: if (urb->interval > PERIODIC_SIZE)
urb->interval = PERIODIC_SIZE;
ep->period = urb->interval;
ep->branch = PERIODIC_SIZE; if (type == PIPE_ISOCHRONOUS)
ep->defctrl |= SL11H_HCTLMASK_ISOCH;
ep->load = usb_calc_bus_time(udev->speed, !is_out,
type == PIPE_ISOCHRONOUS,
usb_maxpacket(udev, pipe))
/ 1000; break;
}
ep->hep = hep;
hep->hcpriv = ep;
}
/* maybe put endpoint into schedule */ switch (type) { case PIPE_CONTROL: case PIPE_BULK: if (list_empty(&ep->schedule))
list_add_tail(&ep->schedule, &sl811->async); break; case PIPE_ISOCHRONOUS: case PIPE_INTERRUPT:
urb->interval = ep->period; if (ep->branch < PERIODIC_SIZE) { /* NOTE: the phase is correct here, but the value * needs offsetting by the transfer queue depth. * All current drivers ignore start_frame, so this * is unlikely to ever matter...
*/
urb->start_frame = (sl811->frame & (PERIODIC_SIZE - 1))
+ ep->branch; break;
}
/* sort each schedule branch by period (slow before fast) * to share the faster parts of the tree without needing * dummy/placeholder nodes
*/
dev_dbg(hcd->self.controller, "schedule qh%d/%p branch %d\n",
ep->period, ep, ep->branch); for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) { struct sl811h_ep **prev = &sl811->periodic[i]; struct sl811h_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;
}
sl811->load[i] += ep->load;
}
sl811->periodic_count++;
hcd->self.bandwidth_allocated += ep->load / ep->period;
sofirq_on(sl811);
}
hep = urb->hcpriv;
ep = hep->hcpriv; if (ep) { /* finish right away if this urb can't be active ... * note that some drivers wrongly expect delays
*/ if (ep->hep->urb_list.next != &urb->urb_list) { /* not front of queue? never active */
/* for active transfers, we expect an IRQ */
} elseif (sl811->active_a == ep) { if (time_before_eq(sl811->jiffies_a, jiffies)) { /* happens a lot with lowspeed?? */
dev_dbg(hcd->self.controller, "giveup on DONE_A: ctrl %02x sts %02x\n",
sl811_read(sl811,
SL811_EP_A(SL11H_HOSTCTLREG)),
sl811_read(sl811,
SL811_EP_A(SL11H_PKTSTATREG)));
sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG),
0);
sl811->active_a = NULL;
} else
urb = NULL; #ifdef USE_B
} elseif (sl811->active_b == ep) { if (time_before_eq(sl811->jiffies_a, jiffies)) { /* happens a lot with lowspeed?? */
dev_dbg(hcd->self.controller, "giveup on DONE_B: ctrl %02x sts %02x\n",
sl811_read(sl811,
SL811_EP_B(SL11H_HOSTCTLREG)),
sl811_read(sl811,
SL811_EP_B(SL11H_PKTSTATREG)));
sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG),
0);
sl811->active_b = NULL;
} else
urb = NULL; #endif
} else { /* front of queue for inactive endpoint */
}
/* assume we'd just wait for the irq */ if (!list_empty(&hep->urb_list))
msleep(3); if (!list_empty(&hep->urb_list))
dev_warn(hcd->self.controller, "ep %p not empty?\n", ep);
/* the virtual root hub timer IRQ checks for hub status */ staticint
sl811h_hub_status_data(struct usb_hcd *hcd, char *buf)
{ struct sl811 *sl811 = hcd_to_sl811(hcd); #ifdef QUIRK3 unsignedlong flags;
/* non-SMP HACK: use root hub timer as i/o watchdog * this seems essential when SOF IRQs aren't in use...
*/
local_irq_save(flags); if (!timer_pending(&sl811->timer)) { if (sl811h_irq( /* ~0, */ hcd) != IRQ_NONE)
sl811->stat_lost++;
}
local_irq_restore(flags); #endif
mod_timer(&sl811->timer, jiffies
+ msecs_to_jiffies(USB_RESUME_TIMEOUT)); break; case USB_PORT_FEAT_POWER:
port_power(sl811, 0); break; case USB_PORT_FEAT_C_ENABLE: case USB_PORT_FEAT_C_SUSPEND: case USB_PORT_FEAT_C_CONNECTION: case USB_PORT_FEAT_C_OVER_CURRENT: case USB_PORT_FEAT_C_RESET: break; default: goto error;
}
sl811->port1 &= ~(1 << wValue); break; case GetHubDescriptor:
sl811h_hub_descriptor(sl811, (struct usb_hub_descriptor *) buf); break; case GetHubStatus:
put_unaligned_le32(0, buf); break; case GetPortStatus: if (wIndex != 1) goto error;
put_unaligned_le32(sl811->port1, buf);
if (__is_defined(VERBOSE) ||
*(u16*)(buf+2)) /* only if wPortChange is interesting */
dev_dbg(hcd->self.controller, "GetPortStatus %08x\n",
sl811->port1); break; case SetPortFeature: if (wIndex != 1 || wLength != 0) goto error; switch (wValue) { case USB_PORT_FEAT_SUSPEND: if (sl811->port1 & USB_PORT_STAT_RESET) goto error; if (!(sl811->port1 & USB_PORT_STAT_ENABLE)) goto error;
dev_dbg(hcd->self.controller,"suspend...\n");
sl811->ctrl1 &= ~SL11H_CTL1MASK_SOF_ENA;
sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); break; case USB_PORT_FEAT_POWER:
port_power(sl811, 1); break; case USB_PORT_FEAT_RESET: if (sl811->port1 & USB_PORT_STAT_SUSPEND) goto error; if (!(sl811->port1 & USB_PORT_STAT_POWER)) break;
/* expect just one sl811 per system */ staticvoid create_debug_file(struct sl811 *sl811)
{
debugfs_create_file("sl811h", S_IRUGO, usb_debug_root, sl811,
&sl811h_debug_fops);
}
/* the chip may be wired for either kind of addressing */
addr = platform_get_mem_or_io(dev, 0);
data = platform_get_mem_or_io(dev, 1); if (!addr || !data || resource_type(addr) != resource_type(data)) return -ENODEV;
/* basic sanity checks first. board-specific init logic should * have initialized these three resources and probably board * specific platform_data. we don't probe for IRQs, and do only * minimal sanity checking.
*/
ires = platform_get_resource(dev, IORESOURCE_IRQ, 0); if (dev->num_resources < 3 || !ires) return -ENODEV;
tmp = sl811_read(sl811, SL11H_HWREVREG); switch (tmp >> 4) { case 1:
hcd->product_desc = "SL811HS v1.2"; break; case 2:
hcd->product_desc = "SL811HS v1.5"; break; default: /* reject case 0, SL11S is less functional */
dev_dbg(&dev->dev, "chiprev %02x\n", tmp);
retval = -ENXIO; goto err6;
}
/* The chip's IRQ is level triggered, active high. A requirement * for platform device setup is to cope with things like signal * inverters (e.g. CF is active low) or working only with edge * triggers (e.g. most ARM CPUs). Initial driver stress testing * was on a system with single edge triggering, so most sorts of * triggering arrangement should work. * * Use resource IRQ flags if set by platform device setup.
*/
irqflags |= IRQF_SHARED;
retval = usb_add_hcd(hcd, irq, irqflags); if (retval != 0) goto err6;
device_wakeup_enable(hcd->self.controller);
create_debug_file(sl811); return retval;
err6:
usb_put_hcd(hcd);
err5: if (!ioaddr)
iounmap(data_reg);
err4: if (!ioaddr)
iounmap(addr_reg);
err2:
dev_dbg(&dev->dev, "init error, %d\n", retval); return retval;
}
#ifdef CONFIG_PM
/* for this device there's no useful distinction between the controller * and its root hub.
*/
/* with no "check to see if VBUS is still powered" board hook, * let's assume it'd only be powered to enable remote wakeup.
*/ if (!sl811->port1 || !device_can_wakeup(&hcd->self.root_hub->dev)) {
sl811->port1 = 0;
port_power(sl811, 1);
usb_root_hub_lost_power(hcd->self.root_hub); return 0;
}
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.