|  | // 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 | 
|  | */ | 
|  |  | 
|  | #undef	VERBOSE | 
|  | #undef	PACKET_TRACE | 
|  |  | 
|  | #include <linux/module.h> | 
|  | #include <linux/moduleparam.h> | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/delay.h> | 
|  | #include <linux/ioport.h> | 
|  | #include <linux/sched.h> | 
|  | #include <linux/slab.h> | 
|  | #include <linux/errno.h> | 
|  | #include <linux/timer.h> | 
|  | #include <linux/list.h> | 
|  | #include <linux/interrupt.h> | 
|  | #include <linux/usb.h> | 
|  | #include <linux/usb/sl811.h> | 
|  | #include <linux/usb/hcd.h> | 
|  | #include <linux/platform_device.h> | 
|  | #include <linux/prefetch.h> | 
|  | #include <linux/string_choices.h> | 
|  | #include <linux/debugfs.h> | 
|  | #include <linux/seq_file.h> | 
|  |  | 
|  | #include <asm/io.h> | 
|  | #include <asm/irq.h> | 
|  | #include <asm/byteorder.h> | 
|  | #include <linux/unaligned.h> | 
|  |  | 
|  | #include "sl811.h" | 
|  |  | 
|  |  | 
|  | MODULE_DESCRIPTION("SL811HS USB Host Controller Driver"); | 
|  | MODULE_LICENSE("GPL"); | 
|  | MODULE_ALIAS("platform:sl811-hcd"); | 
|  |  | 
|  | #define DRIVER_VERSION	"19 May 2005" | 
|  |  | 
|  | /* for now, use only one transfer register bank */ | 
|  | #undef	USE_B | 
|  |  | 
|  | // #define	QUIRK2 | 
|  | #define	QUIRK3 | 
|  |  | 
|  | static const char hcd_name[] = "sl811-hcd"; | 
|  |  | 
|  | /*-------------------------------------------------------------------------*/ | 
|  |  | 
|  | static void port_power(struct sl811 *sl811, int is_on) | 
|  | { | 
|  | struct usb_hcd	*hcd = sl811_to_hcd(sl811); | 
|  |  | 
|  | /* hub is inactive unless the port is powered */ | 
|  | if (is_on) { | 
|  | if (sl811->port1 & USB_PORT_STAT_POWER) | 
|  | return; | 
|  |  | 
|  | sl811->port1 = USB_PORT_STAT_POWER; | 
|  | sl811->irq_enable = SL11H_INTMASK_INSRMV; | 
|  | } else { | 
|  | sl811->port1 = 0; | 
|  | sl811->irq_enable = 0; | 
|  | hcd->state = HC_STATE_HALT; | 
|  | } | 
|  | sl811->ctrl1 = 0; | 
|  | sl811_write(sl811, SL11H_IRQ_ENABLE, 0); | 
|  | sl811_write(sl811, SL11H_IRQ_STATUS, ~0); | 
|  |  | 
|  | 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); | 
|  | } | 
|  |  | 
|  | sl811_write(sl811, SL11H_IRQ_ENABLE, 0); | 
|  | sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); | 
|  | sl811_write(sl811, SL811HS_CTLREG2, SL811HS_CTL2_INIT); | 
|  | sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); | 
|  |  | 
|  | // if !is_on, put into lowpower mode now | 
|  | } | 
|  |  | 
|  | /*-------------------------------------------------------------------------*/ | 
|  |  | 
|  | /* 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. | 
|  | */ | 
|  | static void setup_packet( | 
|  | struct sl811		*sl811, | 
|  | struct sl811h_ep	*ep, | 
|  | struct urb		*urb, | 
|  | u8			bank, | 
|  | u8			control | 
|  | ) | 
|  | { | 
|  | u8			addr; | 
|  | u8			len; | 
|  | void __iomem		*data_reg; | 
|  |  | 
|  | addr = SL811HS_PACKET_BUF(bank == 0); | 
|  | len = sizeof(struct usb_ctrlrequest); | 
|  | data_reg = sl811->data_reg; | 
|  | sl811_write_buf(sl811, addr, urb->setup_packet, len); | 
|  |  | 
|  | /* autoincrementing */ | 
|  | sl811_write(sl811, bank + SL11H_BUFADDRREG, addr); | 
|  | writeb(len, data_reg); | 
|  | writeb(SL_SETUP /* | ep->epnum */, data_reg); | 
|  | writeb(usb_pipedevice(urb->pipe), 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 */ | 
|  | static void status_packet( | 
|  | struct sl811		*sl811, | 
|  | struct sl811h_ep	*ep, | 
|  | struct urb		*urb, | 
|  | u8			bank, | 
|  | u8			control | 
|  | ) | 
|  | { | 
|  | int			do_out; | 
|  | void __iomem		*data_reg; | 
|  |  | 
|  | do_out = urb->transfer_buffer_length && usb_pipein(urb->pipe); | 
|  | data_reg = sl811->data_reg; | 
|  |  | 
|  | /* autoincrementing */ | 
|  | sl811_write(sl811, bank + SL11H_BUFADDRREG, 0); | 
|  | writeb(0, data_reg); | 
|  | writeb((do_out ? SL_OUT : SL_IN) /* | ep->epnum */, data_reg); | 
|  | writeb(usb_pipedevice(urb->pipe), 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... | 
|  | */ | 
|  | static void 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; | 
|  |  | 
|  | /* autoincrementing */ | 
|  | sl811_write(sl811, bank + SL11H_BUFADDRREG, addr); | 
|  | writeb(len, data_reg); | 
|  | writeb(SL_IN | ep->epnum, data_reg); | 
|  | writeb(usb_pipedevice(urb->pipe), data_reg); | 
|  |  | 
|  | sl811_write(sl811, bank + SL11H_HOSTCTLREG, control); | 
|  | ep->length = min_t(u32, len, | 
|  | urb->transfer_buffer_length - urb->actual_length); | 
|  | PACKET("IN%s/%d qh%p len%d\n", ep->nak_count ? "/retry" : "", | 
|  | !!usb_gettoggle(urb->dev, ep->epnum, 0), ep, len); | 
|  | } | 
|  |  | 
|  | /* OUT packets can be used with any type of endpoint. | 
|  | * urb->iso_frame_desc is currently ignored here... | 
|  | */ | 
|  | static void 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; | 
|  |  | 
|  | buf = urb->transfer_buffer + urb->actual_length; | 
|  | prefetch(buf); | 
|  |  | 
|  | len = min_t(u32, ep->maxpacket, | 
|  | urb->transfer_buffer_length - urb->actual_length); | 
|  |  | 
|  | if (!(control & SL11H_HCTLMASK_ISOCH) | 
|  | && usb_gettoggle(urb->dev, ep->epnum, 1)) | 
|  | control |= SL11H_HCTLMASK_TOGGLE; | 
|  | addr = SL811HS_PACKET_BUF(bank == 0); | 
|  | data_reg = sl811->data_reg; | 
|  |  | 
|  | sl811_write_buf(sl811, addr, buf, len); | 
|  |  | 
|  | /* autoincrementing */ | 
|  | sl811_write(sl811, bank + SL11H_BUFADDRREG, addr); | 
|  | writeb(len, data_reg); | 
|  | writeb(SL_OUT | ep->epnum, data_reg); | 
|  | writeb(usb_pipedevice(urb->pipe), data_reg); | 
|  |  | 
|  | sl811_write(sl811, bank + SL11H_HOSTCTLREG, | 
|  | control | SL11H_HCTLMASK_OUT); | 
|  | ep->length = len; | 
|  | PACKET("OUT%s/%d qh%p len%d\n", ep->nak_count ? "/retry" : "", | 
|  | !!usb_gettoggle(urb->dev, ep->epnum, 1), ep, len); | 
|  | } | 
|  |  | 
|  | /*-------------------------------------------------------------------------*/ | 
|  |  | 
|  | /* caller updates on-chip enables later */ | 
|  |  | 
|  | static inline void sofirq_on(struct sl811 *sl811) | 
|  | { | 
|  | if (sl811->irq_enable & SL11H_INTMASK_SOFINTR) | 
|  | return; | 
|  | dev_dbg(sl811_to_hcd(sl811)->self.controller, "sof irq on\n"); | 
|  | sl811->irq_enable |= SL11H_INTMASK_SOFINTR; | 
|  | } | 
|  |  | 
|  | static inline void sofirq_off(struct sl811 *sl811) | 
|  | { | 
|  | if (!(sl811->irq_enable & SL11H_INTMASK_SOFINTR)) | 
|  | return; | 
|  | dev_dbg(sl811_to_hcd(sl811)->self.controller, "sof irq off\n"); | 
|  | sl811->irq_enable &= ~SL11H_INTMASK_SOFINTR; | 
|  | } | 
|  |  | 
|  | /*-------------------------------------------------------------------------*/ | 
|  |  | 
|  | /* 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. | 
|  | */ | 
|  | static struct 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; | 
|  | else if (!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; | 
|  | } | 
|  |  | 
|  | #ifdef USE_B | 
|  | if ((bank && sl811->active_b == ep) || sl811->active_a == ep) | 
|  | return NULL; | 
|  | #endif | 
|  |  | 
|  | if (ep->schedule.next == &sl811->async) | 
|  | sl811->next_async = NULL; | 
|  | else | 
|  | sl811->next_async = container_of(ep->schedule.next, | 
|  | struct sl811h_ep, schedule); | 
|  | } | 
|  |  | 
|  | if (unlikely(list_empty(&ep->hep->urb_list))) { | 
|  | dev_dbg(sl811_to_hcd(sl811)->self.controller, | 
|  | "empty %p queue?\n", ep); | 
|  | 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; | 
|  |  | 
|  | /* throttle bulk/control irq noise */ | 
|  | } else if (ep->nak_count) | 
|  | 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; | 
|  | } | 
|  |  | 
|  | #define MIN_JIFFIES	((msecs_to_jiffies(2) > 1) ? msecs_to_jiffies(2) : 2) | 
|  |  | 
|  | static inline void start_transfer(struct sl811 *sl811) | 
|  | { | 
|  | if (sl811->port1 & USB_PORT_STAT_SUSPEND) | 
|  | return; | 
|  | if (sl811->active_a == NULL) { | 
|  | sl811->active_a = start(sl811, SL811_EP_A(SL811_HOST_BUF)); | 
|  | if (sl811->active_a != NULL) | 
|  | sl811->jiffies_a = jiffies + MIN_JIFFIES; | 
|  | } | 
|  | #ifdef USE_B | 
|  | if (sl811->active_b == NULL) { | 
|  | sl811->active_b = start(sl811, SL811_EP_B(SL811_HOST_BUF)); | 
|  | if (sl811->active_b != NULL) | 
|  | sl811->jiffies_b = jiffies + MIN_JIFFIES; | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | static void finish_request( | 
|  | struct sl811		*sl811, | 
|  | struct sl811h_ep	*ep, | 
|  | struct urb		*urb, | 
|  | int			status | 
|  | ) __releases(sl811->lock) __acquires(sl811->lock) | 
|  | { | 
|  | unsigned		i; | 
|  |  | 
|  | if (usb_pipecontrol(urb->pipe)) | 
|  | ep->nextpid = USB_PID_SETUP; | 
|  |  | 
|  | usb_hcd_unlink_urb_from_ep(sl811_to_hcd(sl811), urb); | 
|  | spin_unlock(&sl811->lock); | 
|  | usb_hcd_giveback_urb(sl811_to_hcd(sl811), urb, status); | 
|  | spin_lock(&sl811->lock); | 
|  |  | 
|  | /* leave active endpoints in the schedule */ | 
|  | if (!list_empty(&ep->hep->urb_list)) | 
|  | return; | 
|  |  | 
|  | /* async deschedule? */ | 
|  | if (!list_empty(&ep->schedule)) { | 
|  | list_del_init(&ep->schedule); | 
|  | if (ep == sl811->next_async) | 
|  | sl811->next_async = NULL; | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* periodic deschedule */ | 
|  | dev_dbg(sl811_to_hcd(sl811)->self.controller, | 
|  | "deschedule qh%d/%p branch %d\n", ep->period, ep, ep->branch); | 
|  | for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) { | 
|  | struct sl811h_ep	*temp; | 
|  | struct sl811h_ep	**prev = &sl811->periodic[i]; | 
|  |  | 
|  | while (*prev && ((temp = *prev) != ep)) | 
|  | prev = &temp->next; | 
|  | if (*prev) | 
|  | *prev = ep->next; | 
|  | sl811->load[i] -= ep->load; | 
|  | } | 
|  | ep->branch = PERIODIC_SIZE; | 
|  | sl811->periodic_count--; | 
|  | sl811_to_hcd(sl811)->self.bandwidth_allocated | 
|  | -= ep->load / ep->period; | 
|  | if (ep == sl811->next_periodic) | 
|  | sl811->next_periodic = ep->next; | 
|  |  | 
|  | /* we might turn SOFs back on again for the async schedule */ | 
|  | if (sl811->periodic_count == 0) | 
|  | sofirq_off(sl811); | 
|  | } | 
|  |  | 
|  | static void | 
|  | done(struct sl811 *sl811, struct sl811h_ep *ep, u8 bank) | 
|  | { | 
|  | u8			status; | 
|  | struct urb		*urb; | 
|  | int			urbstat = -EINPROGRESS; | 
|  |  | 
|  | if (unlikely(!ep)) | 
|  | return; | 
|  |  | 
|  | status = sl811_read(sl811, bank + SL11H_PKTSTATREG); | 
|  |  | 
|  | urb = container_of(ep->hep->urb_list.next, struct urb, urb_list); | 
|  |  | 
|  | /* 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; | 
|  |  | 
|  | /* ACK advances transfer, toggle, and maybe queue */ | 
|  | } else if (status & SL11H_STATMASK_ACK) { | 
|  | struct usb_device	*udev = urb->dev; | 
|  | int			len; | 
|  | unsigned char		*buf; | 
|  |  | 
|  | /* urb->iso_frame_desc is currently ignored here... */ | 
|  |  | 
|  | ep->nak_count = ep->error_count = 0; | 
|  | switch (ep->nextpid) { | 
|  | case USB_PID_OUT: | 
|  | // PACKET("...ACK/out_%02x qh%p\n", bank, ep); | 
|  | urb->actual_length += ep->length; | 
|  | usb_dotoggle(udev, ep->epnum, 1); | 
|  | if (urb->actual_length | 
|  | == urb->transfer_buffer_length) { | 
|  | if (usb_pipecontrol(urb->pipe)) | 
|  | ep->nextpid = USB_PID_ACK; | 
|  |  | 
|  | /* some bulk protocols terminate OUT transfers | 
|  | * by a short packet, using ZLPs not padding. | 
|  | */ | 
|  | else if (ep->length < ep->maxpacket | 
|  | || !(urb->transfer_flags | 
|  | & URB_ZERO_PACKET)) | 
|  | urbstat = 0; | 
|  | } | 
|  | break; | 
|  | case USB_PID_IN: | 
|  | // PACKET("...ACK/in_%02x qh%p\n", bank, ep); | 
|  | buf = urb->transfer_buffer + urb->actual_length; | 
|  | prefetchw(buf); | 
|  | len = ep->maxpacket - sl811_read(sl811, | 
|  | bank + SL11H_XFERCNTREG); | 
|  | if (len > ep->length) { | 
|  | len = ep->length; | 
|  | urbstat = -EOVERFLOW; | 
|  | } | 
|  | urb->actual_length += len; | 
|  | sl811_read_buf(sl811, SL811HS_PACKET_BUF(bank == 0), | 
|  | buf, len); | 
|  | usb_dotoggle(udev, ep->epnum, 0); | 
|  | if (urbstat == -EINPROGRESS && | 
|  | (len < ep->maxpacket || | 
|  | urb->actual_length == | 
|  | urb->transfer_buffer_length)) { | 
|  | if (usb_pipecontrol(urb->pipe)) | 
|  | ep->nextpid = USB_PID_ACK; | 
|  | else | 
|  | urbstat = 0; | 
|  | } | 
|  | break; | 
|  | case USB_PID_SETUP: | 
|  | // PACKET("...ACK/setup_%02x qh%p\n", bank, ep); | 
|  | if (urb->transfer_buffer_length == urb->actual_length) | 
|  | ep->nextpid = USB_PID_ACK; | 
|  | else if (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: | 
|  | // PACKET("...ACK/status_%02x qh%p\n", bank, ep); | 
|  | urbstat = 0; | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* STALL stops all transfers */ | 
|  | } else if (status & SL11H_STATMASK_STALL) { | 
|  | PACKET("...STALL_%02x qh%p\n", bank, ep); | 
|  | ep->nak_count = ep->error_count = 0; | 
|  | urbstat = -EPIPE; | 
|  |  | 
|  | /* error? retry, until "3 strikes" */ | 
|  | } else if (++ep->error_count >= 3) { | 
|  | if (status & SL11H_STATMASK_TMOUT) | 
|  | urbstat = -ETIME; | 
|  | else if (status & SL11H_STATMASK_OVF) | 
|  | urbstat = -EOVERFLOW; | 
|  | else | 
|  | urbstat = -EPROTO; | 
|  | ep->error_count = 0; | 
|  | PACKET("...3STRIKES_%02x %02x qh%p stat %d\n", | 
|  | bank, status, ep, urbstat); | 
|  | } | 
|  |  | 
|  | if (urbstat != -EINPROGRESS || urb->unlinked) | 
|  | finish_request(sl811, ep, urb, urbstat); | 
|  | } | 
|  |  | 
|  | #ifdef QUIRK2 | 
|  | static inline u8 checkdone(struct sl811 *sl811) | 
|  | { | 
|  | u8	ctl; | 
|  | u8	irqstat = 0; | 
|  |  | 
|  | if (sl811->active_a && time_before_eq(sl811->jiffies_a, jiffies)) { | 
|  | ctl = sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG)); | 
|  | if (ctl & SL11H_HCTLMASK_ARM) | 
|  | sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0); | 
|  | dev_dbg(sl811_to_hcd(sl811)->self.controller, | 
|  | "%s DONE_A: ctrl %02x sts %02x\n", | 
|  | (ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost", | 
|  | ctl, | 
|  | sl811_read(sl811, SL811_EP_A(SL11H_PKTSTATREG))); | 
|  | irqstat |= SL11H_INTMASK_DONE_A; | 
|  | } | 
|  | #ifdef	USE_B | 
|  | if (sl811->active_b && time_before_eq(sl811->jiffies_b, jiffies)) { | 
|  | ctl = sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG)); | 
|  | if (ctl & SL11H_HCTLMASK_ARM) | 
|  | sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0); | 
|  | dev_dbg(sl811_to_hcd(sl811)->self.controller, | 
|  | "%s DONE_B: ctrl %02x sts %02x\n", | 
|  | (ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost", | 
|  | ctl, | 
|  | sl811_read(sl811, SL811_EP_B(SL11H_PKTSTATREG))); | 
|  | irqstat |= SL11H_INTMASK_DONE_A; | 
|  | } | 
|  | #endif | 
|  | return irqstat; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static irqreturn_t sl811h_irq(struct usb_hcd *hcd) | 
|  | { | 
|  | struct sl811	*sl811 = hcd_to_sl811(hcd); | 
|  | u8		irqstat; | 
|  | irqreturn_t	ret = IRQ_NONE; | 
|  | unsigned	retries = 5; | 
|  |  | 
|  | spin_lock(&sl811->lock); | 
|  |  | 
|  | retry: | 
|  | irqstat = sl811_read(sl811, SL11H_IRQ_STATUS) & ~SL11H_INTMASK_DP; | 
|  | if (irqstat) { | 
|  | sl811_write(sl811, SL11H_IRQ_STATUS, irqstat); | 
|  | irqstat &= sl811->irq_enable; | 
|  | } | 
|  |  | 
|  | #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; | 
|  |  | 
|  | sl811->ctrl1 = 0; | 
|  | sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); | 
|  |  | 
|  | sl811->irq_enable = SL11H_INTMASK_INSRMV; | 
|  | sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); | 
|  |  | 
|  | /* usbcore nukes other pending transactions on disconnect */ | 
|  | if (sl811->active_a) { | 
|  | sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0); | 
|  | finish_request(sl811, sl811->active_a, | 
|  | container_of(sl811->active_a | 
|  | ->hep->urb_list.next, | 
|  | struct urb, urb_list), | 
|  | -ESHUTDOWN); | 
|  | sl811->active_a = NULL; | 
|  | } | 
|  | #ifdef	USE_B | 
|  | if (sl811->active_b) { | 
|  | sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0); | 
|  | finish_request(sl811, sl811->active_b, | 
|  | container_of(sl811->active_b | 
|  | ->hep->urb_list.next, | 
|  | struct urb, urb_list), | 
|  | NULL, -ESHUTDOWN); | 
|  | sl811->active_b = NULL; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /* 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; | 
|  |  | 
|  | sl811->port1 |= USB_PORT_STAT_C_CONNECTION << 16; | 
|  |  | 
|  | } else if (irqstat & SL11H_INTMASK_RD) { | 
|  | if (sl811->port1 & USB_PORT_STAT_SUSPEND) { | 
|  | dev_dbg(hcd->self.controller, "wakeup\n"); | 
|  | sl811->port1 |= USB_PORT_STAT_C_SUSPEND << 16; | 
|  | sl811->stat_wake++; | 
|  | } else | 
|  | irqstat &= ~SL11H_INTMASK_RD; | 
|  | } | 
|  |  | 
|  | if (irqstat) { | 
|  | if (sl811->port1 & USB_PORT_STAT_ENABLE) | 
|  | start_transfer(sl811); | 
|  | ret = IRQ_HANDLED; | 
|  | if (retries--) | 
|  | goto retry; | 
|  | } | 
|  |  | 
|  | if (sl811->periodic_count == 0 && list_empty(&sl811->async)) | 
|  | sofirq_off(sl811); | 
|  | sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); | 
|  |  | 
|  | spin_unlock(&sl811->lock); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /*-------------------------------------------------------------------------*/ | 
|  |  | 
|  | /* 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 */ | 
|  |  | 
|  | static int 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; | 
|  |  | 
|  | for (j = i; j < PERIODIC_SIZE; j += period) { | 
|  | if ((sl811->load[j] + load) | 
|  | > MAX_PERIODIC_LOAD) | 
|  | break; | 
|  | } | 
|  | if (j < PERIODIC_SIZE) | 
|  | continue; | 
|  | branch = i; | 
|  | } | 
|  | } | 
|  | return branch; | 
|  | } | 
|  |  | 
|  | /*-------------------------------------------------------------------------*/ | 
|  |  | 
|  | static int 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; | 
|  | unsigned int		pipe = urb->pipe; | 
|  | int			is_out = !usb_pipein(pipe); | 
|  | int			type = usb_pipetype(pipe); | 
|  | int			epnum = usb_pipeendpoint(pipe); | 
|  | struct sl811h_ep	*ep = NULL; | 
|  | unsigned long		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; | 
|  | } else if (!ep) { | 
|  | retval = -ENOMEM; | 
|  | goto fail; | 
|  |  | 
|  | } else { | 
|  | INIT_LIST_HEAD(&ep->schedule); | 
|  | ep->udev = udev; | 
|  | ep->epnum = epnum; | 
|  | ep->maxpacket = usb_maxpacket(udev, urb->pipe); | 
|  | ep->defctrl = SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENABLE; | 
|  | usb_settoggle(udev, epnum, is_out, 0); | 
|  |  | 
|  | if (type == PIPE_CONTROL) | 
|  | ep->nextpid = USB_PID_SETUP; | 
|  | else if (is_out) | 
|  | ep->nextpid = USB_PID_OUT; | 
|  | else | 
|  | ep->nextpid = USB_PID_IN; | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | retval = balance(sl811, ep->period, ep->load); | 
|  | if (retval < 0) | 
|  | goto fail; | 
|  | ep->branch = retval; | 
|  | retval = 0; | 
|  | urb->start_frame = (sl811->frame & (PERIODIC_SIZE - 1)) | 
|  | + ep->branch; | 
|  |  | 
|  | /* 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); | 
|  | } | 
|  |  | 
|  | urb->hcpriv = hep; | 
|  | start_transfer(sl811); | 
|  | sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); | 
|  | fail: | 
|  | if (retval) | 
|  | usb_hcd_unlink_urb_from_ep(hcd, urb); | 
|  | fail_not_linked: | 
|  | spin_unlock_irqrestore(&sl811->lock, flags); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) | 
|  | { | 
|  | struct sl811		*sl811 = hcd_to_sl811(hcd); | 
|  | struct usb_host_endpoint *hep; | 
|  | unsigned long		flags; | 
|  | struct sl811h_ep	*ep; | 
|  | int			retval; | 
|  |  | 
|  | spin_lock_irqsave(&sl811->lock, flags); | 
|  | retval = usb_hcd_check_unlink_urb(hcd, urb, status); | 
|  | if (retval) | 
|  | goto fail; | 
|  |  | 
|  | 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 */ | 
|  | } else if (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 | 
|  | } else if (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 */ | 
|  | } | 
|  |  | 
|  | if (urb) | 
|  | finish_request(sl811, ep, urb, 0); | 
|  | else | 
|  | dev_dbg(sl811_to_hcd(sl811)->self.controller, | 
|  | "dequeue, urb %p active %s; wait4irq\n", urb, | 
|  | (sl811->active_a == ep) ? "A" : "B"); | 
|  | } else | 
|  | retval = -EINVAL; | 
|  | fail: | 
|  | spin_unlock_irqrestore(&sl811->lock, flags); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | static void | 
|  | sl811h_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) | 
|  | { | 
|  | struct sl811h_ep	*ep = hep->hcpriv; | 
|  |  | 
|  | if (!ep) | 
|  | return; | 
|  |  | 
|  | /* 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); | 
|  |  | 
|  | kfree(ep); | 
|  | hep->hcpriv = NULL; | 
|  | } | 
|  |  | 
|  | static int | 
|  | sl811h_get_frame(struct usb_hcd *hcd) | 
|  | { | 
|  | struct sl811 *sl811 = hcd_to_sl811(hcd); | 
|  |  | 
|  | /* wrong except while periodic transfers are scheduled; | 
|  | * never matches the on-the-wire frame; | 
|  | * subject to overruns. | 
|  | */ | 
|  | return sl811->frame; | 
|  | } | 
|  |  | 
|  |  | 
|  | /*-------------------------------------------------------------------------*/ | 
|  |  | 
|  | /* the virtual root hub timer IRQ checks for hub status */ | 
|  | static int | 
|  | sl811h_hub_status_data(struct usb_hcd *hcd, char *buf) | 
|  | { | 
|  | struct sl811 *sl811 = hcd_to_sl811(hcd); | 
|  | #ifdef	QUIRK3 | 
|  | unsigned long 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 | 
|  |  | 
|  | if (!(sl811->port1 & (0xffff << 16))) | 
|  | return 0; | 
|  |  | 
|  | /* tell hub_wq port 1 changed */ | 
|  | *buf = (1 << 1); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static void | 
|  | sl811h_hub_descriptor ( | 
|  | struct sl811			*sl811, | 
|  | struct usb_hub_descriptor	*desc | 
|  | ) { | 
|  | u16		temp = 0; | 
|  |  | 
|  | desc->bDescriptorType = USB_DT_HUB; | 
|  | desc->bHubContrCurrent = 0; | 
|  |  | 
|  | desc->bNbrPorts = 1; | 
|  | desc->bDescLength = 9; | 
|  |  | 
|  | /* per-port power switching (gang of one!), or none */ | 
|  | desc->bPwrOn2PwrGood = 0; | 
|  | if (sl811->board && sl811->board->port_power) { | 
|  | desc->bPwrOn2PwrGood = sl811->board->potpg; | 
|  | if (!desc->bPwrOn2PwrGood) | 
|  | desc->bPwrOn2PwrGood = 10; | 
|  | temp = HUB_CHAR_INDV_PORT_LPSM; | 
|  | } else | 
|  | temp = HUB_CHAR_NO_LPSM; | 
|  |  | 
|  | /* no overcurrent errors detection/handling */ | 
|  | temp |= HUB_CHAR_NO_OCPM; | 
|  |  | 
|  | desc->wHubCharacteristics = cpu_to_le16(temp); | 
|  |  | 
|  | /* ports removable, and legacy PortPwrCtrlMask */ | 
|  | desc->u.hs.DeviceRemovable[0] = 0 << 1; | 
|  | desc->u.hs.DeviceRemovable[1] = ~0; | 
|  | } | 
|  |  | 
|  | static void | 
|  | sl811h_timer(struct timer_list *t) | 
|  | { | 
|  | struct sl811 	*sl811 = timer_container_of(sl811, t, timer); | 
|  | unsigned long	flags; | 
|  | u8		irqstat; | 
|  | u8		signaling = sl811->ctrl1 & SL11H_CTL1MASK_FORCE; | 
|  | const u32	mask = USB_PORT_STAT_CONNECTION | 
|  | | USB_PORT_STAT_ENABLE | 
|  | | USB_PORT_STAT_LOW_SPEED; | 
|  |  | 
|  | spin_lock_irqsave(&sl811->lock, flags); | 
|  |  | 
|  | /* stop special signaling */ | 
|  | sl811->ctrl1 &= ~SL11H_CTL1MASK_FORCE; | 
|  | sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); | 
|  | udelay(3); | 
|  |  | 
|  | irqstat = sl811_read(sl811, SL11H_IRQ_STATUS); | 
|  |  | 
|  | switch (signaling) { | 
|  | case SL11H_CTL1MASK_SE0: | 
|  | dev_dbg(sl811_to_hcd(sl811)->self.controller, "end reset\n"); | 
|  | sl811->port1 = (USB_PORT_STAT_C_RESET << 16) | 
|  | | USB_PORT_STAT_POWER; | 
|  | sl811->ctrl1 = 0; | 
|  | /* don't wrongly ack RD */ | 
|  | if (irqstat & SL11H_INTMASK_INSRMV) | 
|  | irqstat &= ~SL11H_INTMASK_RD; | 
|  | break; | 
|  | case SL11H_CTL1MASK_K: | 
|  | dev_dbg(sl811_to_hcd(sl811)->self.controller, "end resume\n"); | 
|  | sl811->port1 &= ~USB_PORT_STAT_SUSPEND; | 
|  | break; | 
|  | default: | 
|  | dev_dbg(sl811_to_hcd(sl811)->self.controller, | 
|  | "odd timer signaling: %02x\n", signaling); | 
|  | break; | 
|  | } | 
|  | sl811_write(sl811, SL11H_IRQ_STATUS, irqstat); | 
|  |  | 
|  | if (irqstat & SL11H_INTMASK_RD) { | 
|  | /* usbcore nukes all pending transactions on disconnect */ | 
|  | if (sl811->port1 & USB_PORT_STAT_CONNECTION) | 
|  | sl811->port1 |= (USB_PORT_STAT_C_CONNECTION << 16) | 
|  | | (USB_PORT_STAT_C_ENABLE << 16); | 
|  | sl811->port1 &= ~mask; | 
|  | sl811->irq_enable = SL11H_INTMASK_INSRMV; | 
|  | } else { | 
|  | sl811->port1 |= mask; | 
|  | if (irqstat & SL11H_INTMASK_DP) | 
|  | sl811->port1 &= ~USB_PORT_STAT_LOW_SPEED; | 
|  | sl811->irq_enable = SL11H_INTMASK_INSRMV | SL11H_INTMASK_RD; | 
|  | } | 
|  |  | 
|  | if (sl811->port1 & USB_PORT_STAT_CONNECTION) { | 
|  | u8	ctrl2 = SL811HS_CTL2_INIT; | 
|  |  | 
|  | sl811->irq_enable |= SL11H_INTMASK_DONE_A; | 
|  | #ifdef USE_B | 
|  | sl811->irq_enable |= SL11H_INTMASK_DONE_B; | 
|  | #endif | 
|  | if (sl811->port1 & USB_PORT_STAT_LOW_SPEED) { | 
|  | sl811->ctrl1 |= SL11H_CTL1MASK_LSPD; | 
|  | ctrl2 |= SL811HS_CTL2MASK_DSWAP; | 
|  | } | 
|  |  | 
|  | /* start SOFs flowing, kickstarting with A registers */ | 
|  | sl811->ctrl1 |= SL11H_CTL1MASK_SOF_ENA; | 
|  | sl811_write(sl811, SL11H_SOFLOWREG, 0xe0); | 
|  | sl811_write(sl811, SL811HS_CTLREG2, ctrl2); | 
|  |  | 
|  | /* autoincrementing */ | 
|  | sl811_write(sl811, SL811_EP_A(SL11H_BUFLNTHREG), 0); | 
|  | writeb(SL_SOF, sl811->data_reg); | 
|  | writeb(0, sl811->data_reg); | 
|  | sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), | 
|  | SL11H_HCTLMASK_ARM); | 
|  |  | 
|  | /* hub_wq provides debounce delay */ | 
|  | } else { | 
|  | sl811->ctrl1 = 0; | 
|  | } | 
|  | sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); | 
|  |  | 
|  | /* reenable irqs */ | 
|  | sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); | 
|  | spin_unlock_irqrestore(&sl811->lock, flags); | 
|  | } | 
|  |  | 
|  | static int | 
|  | sl811h_hub_control( | 
|  | struct usb_hcd	*hcd, | 
|  | u16		typeReq, | 
|  | u16		wValue, | 
|  | u16		wIndex, | 
|  | char		*buf, | 
|  | u16		wLength | 
|  | ) { | 
|  | struct sl811	*sl811 = hcd_to_sl811(hcd); | 
|  | int		retval = 0; | 
|  | unsigned long	flags; | 
|  |  | 
|  | spin_lock_irqsave(&sl811->lock, flags); | 
|  |  | 
|  | switch (typeReq) { | 
|  | case ClearHubFeature: | 
|  | case SetHubFeature: | 
|  | switch (wValue) { | 
|  | case C_HUB_OVER_CURRENT: | 
|  | case C_HUB_LOCAL_POWER: | 
|  | break; | 
|  | default: | 
|  | goto error; | 
|  | } | 
|  | break; | 
|  | case ClearPortFeature: | 
|  | if (wIndex != 1 || wLength != 0) | 
|  | goto error; | 
|  |  | 
|  | switch (wValue) { | 
|  | case USB_PORT_FEAT_ENABLE: | 
|  | sl811->port1 &= USB_PORT_STAT_POWER; | 
|  | sl811->ctrl1 = 0; | 
|  | sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); | 
|  | sl811->irq_enable = SL11H_INTMASK_INSRMV; | 
|  | sl811_write(sl811, SL11H_IRQ_ENABLE, | 
|  | sl811->irq_enable); | 
|  | break; | 
|  | case USB_PORT_FEAT_SUSPEND: | 
|  | if (!(sl811->port1 & USB_PORT_STAT_SUSPEND)) | 
|  | break; | 
|  |  | 
|  | /* 20 msec of resume/K signaling, other irqs blocked */ | 
|  | dev_dbg(hcd->self.controller, "start resume...\n"); | 
|  | sl811->irq_enable = 0; | 
|  | sl811_write(sl811, SL11H_IRQ_ENABLE, | 
|  | sl811->irq_enable); | 
|  | sl811->ctrl1 |= SL11H_CTL1MASK_K; | 
|  | sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); | 
|  |  | 
|  | 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; | 
|  |  | 
|  | /* 50 msec of reset/SE0 signaling, irqs blocked */ | 
|  | sl811->irq_enable = 0; | 
|  | sl811_write(sl811, SL11H_IRQ_ENABLE, | 
|  | sl811->irq_enable); | 
|  | sl811->ctrl1 = SL11H_CTL1MASK_SE0; | 
|  | sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); | 
|  | sl811->port1 |= USB_PORT_STAT_RESET; | 
|  | mod_timer(&sl811->timer, jiffies | 
|  | + msecs_to_jiffies(50)); | 
|  | break; | 
|  | default: | 
|  | goto error; | 
|  | } | 
|  | sl811->port1 |= 1 << wValue; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | error: | 
|  | /* "protocol stall" on error */ | 
|  | retval = -EPIPE; | 
|  | } | 
|  |  | 
|  | spin_unlock_irqrestore(&sl811->lock, flags); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | #ifdef	CONFIG_PM | 
|  |  | 
|  | static int | 
|  | sl811h_bus_suspend(struct usb_hcd *hcd) | 
|  | { | 
|  | // SOFs off | 
|  | dev_dbg(hcd->self.controller, "%s\n", __func__); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | sl811h_bus_resume(struct usb_hcd *hcd) | 
|  | { | 
|  | // SOFs on | 
|  | dev_dbg(hcd->self.controller, "%s\n", __func__); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #else | 
|  |  | 
|  | #define	sl811h_bus_suspend	NULL | 
|  | #define	sl811h_bus_resume	NULL | 
|  |  | 
|  | #endif | 
|  |  | 
|  |  | 
|  | /*-------------------------------------------------------------------------*/ | 
|  |  | 
|  | static void dump_irq(struct seq_file *s, char *label, u8 mask) | 
|  | { | 
|  | seq_printf(s, "%s %02x%s%s%s%s%s%s\n", label, mask, | 
|  | (mask & SL11H_INTMASK_DONE_A) ? " done_a" : "", | 
|  | (mask & SL11H_INTMASK_DONE_B) ? " done_b" : "", | 
|  | (mask & SL11H_INTMASK_SOFINTR) ? " sof" : "", | 
|  | (mask & SL11H_INTMASK_INSRMV) ? " ins/rmv" : "", | 
|  | (mask & SL11H_INTMASK_RD) ? " rd" : "", | 
|  | (mask & SL11H_INTMASK_DP) ? " dp" : ""); | 
|  | } | 
|  |  | 
|  | static int sl811h_debug_show(struct seq_file *s, void *unused) | 
|  | { | 
|  | struct sl811		*sl811 = s->private; | 
|  | struct sl811h_ep	*ep; | 
|  | unsigned		i; | 
|  |  | 
|  | seq_printf(s, "%s\n%s version %s\nportstatus[1] = %08x\n", | 
|  | sl811_to_hcd(sl811)->product_desc, | 
|  | hcd_name, DRIVER_VERSION, | 
|  | sl811->port1); | 
|  |  | 
|  | seq_printf(s, "insert/remove: %ld\n", sl811->stat_insrmv); | 
|  | seq_printf(s, "current session:  done_a %ld done_b %ld " | 
|  | "wake %ld sof %ld overrun %ld lost %ld\n\n", | 
|  | sl811->stat_a, sl811->stat_b, | 
|  | sl811->stat_wake, sl811->stat_sof, | 
|  | sl811->stat_overrun, sl811->stat_lost); | 
|  |  | 
|  | spin_lock_irq(&sl811->lock); | 
|  |  | 
|  | if (sl811->ctrl1 & SL11H_CTL1MASK_SUSPEND) | 
|  | seq_printf(s, "(suspended)\n\n"); | 
|  | else { | 
|  | u8	t = sl811_read(sl811, SL11H_CTLREG1); | 
|  |  | 
|  | seq_printf(s, "ctrl1 %02x%s%s%s%s\n", t, | 
|  | (t & SL11H_CTL1MASK_SOF_ENA) ? " sofgen" : "", | 
|  | ({char *s; switch (t & SL11H_CTL1MASK_FORCE) { | 
|  | case SL11H_CTL1MASK_NORMAL: s = ""; break; | 
|  | case SL11H_CTL1MASK_SE0: s = " se0/reset"; break; | 
|  | case SL11H_CTL1MASK_K: s = " k/resume"; break; | 
|  | default: s = "j"; break; | 
|  | } s; }), | 
|  | (t & SL11H_CTL1MASK_LSPD) ? " lowspeed" : "", | 
|  | (t & SL11H_CTL1MASK_SUSPEND) ? " suspend" : ""); | 
|  |  | 
|  | dump_irq(s, "irq_enable", | 
|  | sl811_read(sl811, SL11H_IRQ_ENABLE)); | 
|  | dump_irq(s, "irq_status", | 
|  | sl811_read(sl811, SL11H_IRQ_STATUS)); | 
|  | seq_printf(s, "frame clocks remaining:  %d\n", | 
|  | sl811_read(sl811, SL11H_SOFTMRREG) << 6); | 
|  | } | 
|  |  | 
|  | seq_printf(s, "A: qh%p ctl %02x sts %02x\n", sl811->active_a, | 
|  | sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG)), | 
|  | sl811_read(sl811, SL811_EP_A(SL11H_PKTSTATREG))); | 
|  | seq_printf(s, "B: qh%p ctl %02x sts %02x\n", sl811->active_b, | 
|  | sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG)), | 
|  | sl811_read(sl811, SL811_EP_B(SL11H_PKTSTATREG))); | 
|  | seq_printf(s, "\n"); | 
|  | list_for_each_entry (ep, &sl811->async, schedule) { | 
|  | struct urb		*urb; | 
|  |  | 
|  | seq_printf(s, "%s%sqh%p, ep%d%s, maxpacket %d" | 
|  | " nak %d err %d\n", | 
|  | (ep == sl811->active_a) ? "(A) " : "", | 
|  | (ep == sl811->active_b) ? "(B) " : "", | 
|  | ep, ep->epnum, | 
|  | ({ char *s; switch (ep->nextpid) { | 
|  | case USB_PID_IN: s = "in"; break; | 
|  | case USB_PID_OUT: s = "out"; break; | 
|  | case USB_PID_SETUP: s = "setup"; break; | 
|  | case USB_PID_ACK: s = "status"; break; | 
|  | default: s = "?"; break; | 
|  | } s;}), | 
|  | ep->maxpacket, | 
|  | ep->nak_count, ep->error_count); | 
|  | list_for_each_entry (urb, &ep->hep->urb_list, urb_list) { | 
|  | seq_printf(s, "  urb%p, %d/%d\n", urb, | 
|  | urb->actual_length, | 
|  | urb->transfer_buffer_length); | 
|  | } | 
|  | } | 
|  | if (!list_empty(&sl811->async)) | 
|  | seq_printf(s, "\n"); | 
|  |  | 
|  | seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE); | 
|  |  | 
|  | for (i = 0; i < PERIODIC_SIZE; i++) { | 
|  | ep = sl811->periodic[i]; | 
|  | if (!ep) | 
|  | continue; | 
|  | seq_printf(s, "%2d [%3d]:\n", i, sl811->load[i]); | 
|  |  | 
|  | /* DUMB: prints shared entries multiple times */ | 
|  | do { | 
|  | seq_printf(s, | 
|  | "   %s%sqh%d/%p (%sdev%d ep%d%s max %d) " | 
|  | "err %d\n", | 
|  | (ep == sl811->active_a) ? "(A) " : "", | 
|  | (ep == sl811->active_b) ? "(B) " : "", | 
|  | ep->period, ep, | 
|  | (ep->udev->speed == USB_SPEED_FULL) | 
|  | ? "" : "ls ", | 
|  | ep->udev->devnum, ep->epnum, | 
|  | (ep->epnum == 0) ? "" | 
|  | : ((ep->nextpid == USB_PID_IN) | 
|  | ? "in" | 
|  | : "out"), | 
|  | ep->maxpacket, ep->error_count); | 
|  | ep = ep->next; | 
|  | } while (ep); | 
|  | } | 
|  |  | 
|  | spin_unlock_irq(&sl811->lock); | 
|  | seq_printf(s, "\n"); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | DEFINE_SHOW_ATTRIBUTE(sl811h_debug); | 
|  |  | 
|  | /* expect just one sl811 per system */ | 
|  | static void create_debug_file(struct sl811 *sl811) | 
|  | { | 
|  | debugfs_create_file("sl811h", S_IRUGO, usb_debug_root, sl811, | 
|  | &sl811h_debug_fops); | 
|  | } | 
|  |  | 
|  | static void remove_debug_file(struct sl811 *sl811) | 
|  | { | 
|  | debugfs_lookup_and_remove("sl811h", usb_debug_root); | 
|  | } | 
|  |  | 
|  | /*-------------------------------------------------------------------------*/ | 
|  |  | 
|  | static void | 
|  | sl811h_stop(struct usb_hcd *hcd) | 
|  | { | 
|  | struct sl811	*sl811 = hcd_to_sl811(hcd); | 
|  | unsigned long	flags; | 
|  |  | 
|  | timer_delete_sync(&hcd->rh_timer); | 
|  |  | 
|  | spin_lock_irqsave(&sl811->lock, flags); | 
|  | port_power(sl811, 0); | 
|  | spin_unlock_irqrestore(&sl811->lock, flags); | 
|  | } | 
|  |  | 
|  | static int | 
|  | sl811h_start(struct usb_hcd *hcd) | 
|  | { | 
|  | struct sl811		*sl811 = hcd_to_sl811(hcd); | 
|  |  | 
|  | /* chip has been reset, VBUS power is off */ | 
|  | hcd->state = HC_STATE_RUNNING; | 
|  |  | 
|  | if (sl811->board) { | 
|  | if (!device_can_wakeup(hcd->self.controller)) | 
|  | device_init_wakeup(hcd->self.controller, | 
|  | sl811->board->can_wakeup); | 
|  | hcd->power_budget = sl811->board->power * 2; | 
|  | } | 
|  |  | 
|  | /* enable power and interrupts */ | 
|  | port_power(sl811, 1); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /*-------------------------------------------------------------------------*/ | 
|  |  | 
|  | static const struct hc_driver sl811h_hc_driver = { | 
|  | .description =		hcd_name, | 
|  | .hcd_priv_size =	sizeof(struct sl811), | 
|  |  | 
|  | /* | 
|  | * generic hardware linkage | 
|  | */ | 
|  | .irq =			sl811h_irq, | 
|  | .flags =		HCD_USB11 | HCD_MEMORY, | 
|  |  | 
|  | /* Basic lifecycle operations */ | 
|  | .start =		sl811h_start, | 
|  | .stop =			sl811h_stop, | 
|  |  | 
|  | /* | 
|  | * managing i/o requests and associated device resources | 
|  | */ | 
|  | .urb_enqueue =		sl811h_urb_enqueue, | 
|  | .urb_dequeue =		sl811h_urb_dequeue, | 
|  | .endpoint_disable =	sl811h_endpoint_disable, | 
|  |  | 
|  | /* | 
|  | * periodic schedule support | 
|  | */ | 
|  | .get_frame_number =	sl811h_get_frame, | 
|  |  | 
|  | /* | 
|  | * root hub support | 
|  | */ | 
|  | .hub_status_data =	sl811h_hub_status_data, | 
|  | .hub_control =		sl811h_hub_control, | 
|  | .bus_suspend =		sl811h_bus_suspend, | 
|  | .bus_resume =		sl811h_bus_resume, | 
|  | }; | 
|  |  | 
|  | /*-------------------------------------------------------------------------*/ | 
|  |  | 
|  | static void | 
|  | sl811h_remove(struct platform_device *dev) | 
|  | { | 
|  | struct usb_hcd		*hcd = platform_get_drvdata(dev); | 
|  | struct sl811		*sl811 = hcd_to_sl811(hcd); | 
|  | struct resource		*res; | 
|  |  | 
|  | remove_debug_file(sl811); | 
|  | usb_remove_hcd(hcd); | 
|  |  | 
|  | /* some platforms may use IORESOURCE_IO */ | 
|  | res = platform_get_resource(dev, IORESOURCE_MEM, 1); | 
|  | if (res) | 
|  | iounmap(sl811->data_reg); | 
|  |  | 
|  | res = platform_get_resource(dev, IORESOURCE_MEM, 0); | 
|  | if (res) | 
|  | iounmap(sl811->addr_reg); | 
|  |  | 
|  | usb_put_hcd(hcd); | 
|  | } | 
|  |  | 
|  | static int | 
|  | sl811h_probe(struct platform_device *dev) | 
|  | { | 
|  | struct usb_hcd		*hcd; | 
|  | struct sl811		*sl811; | 
|  | struct resource		*addr, *data, *ires; | 
|  | int			irq; | 
|  | void __iomem		*addr_reg; | 
|  | void __iomem		*data_reg; | 
|  | int			retval; | 
|  | u8			tmp, ioaddr; | 
|  | unsigned long		irqflags; | 
|  |  | 
|  | if (usb_disabled()) | 
|  | return -ENODEV; | 
|  |  | 
|  | /* 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; | 
|  |  | 
|  | irq = ires->start; | 
|  | irqflags = ires->flags & IRQF_TRIGGER_MASK; | 
|  |  | 
|  | ioaddr = resource_type(addr) == IORESOURCE_IO; | 
|  | if (ioaddr) { | 
|  | /* | 
|  | * NOTE: 64-bit resource->start is getting truncated | 
|  | * to avoid compiler warning, assuming that ->start | 
|  | * is always 32-bit for this case | 
|  | */ | 
|  | addr_reg = (void __iomem *) (unsigned long) addr->start; | 
|  | data_reg = (void __iomem *) (unsigned long) data->start; | 
|  | } else { | 
|  | addr_reg = ioremap(addr->start, 1); | 
|  | if (addr_reg == NULL) { | 
|  | retval = -ENOMEM; | 
|  | goto err2; | 
|  | } | 
|  |  | 
|  | data_reg = ioremap(data->start, 1); | 
|  | if (data_reg == NULL) { | 
|  | retval = -ENOMEM; | 
|  | goto err4; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* allocate and initialize hcd */ | 
|  | hcd = usb_create_hcd(&sl811h_hc_driver, &dev->dev, dev_name(&dev->dev)); | 
|  | if (!hcd) { | 
|  | retval = -ENOMEM; | 
|  | goto err5; | 
|  | } | 
|  | hcd->rsrc_start = addr->start; | 
|  | sl811 = hcd_to_sl811(hcd); | 
|  |  | 
|  | spin_lock_init(&sl811->lock); | 
|  | INIT_LIST_HEAD(&sl811->async); | 
|  | sl811->board = dev_get_platdata(&dev->dev); | 
|  | timer_setup(&sl811->timer, sl811h_timer, 0); | 
|  | sl811->addr_reg = addr_reg; | 
|  | sl811->data_reg = data_reg; | 
|  |  | 
|  | spin_lock_irq(&sl811->lock); | 
|  | port_power(sl811, 0); | 
|  | spin_unlock_irq(&sl811->lock); | 
|  | msleep(200); | 
|  |  | 
|  | 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. | 
|  | */ | 
|  |  | 
|  | static int | 
|  | sl811h_suspend(struct platform_device *dev, pm_message_t state) | 
|  | { | 
|  | struct usb_hcd	*hcd = platform_get_drvdata(dev); | 
|  | struct sl811	*sl811 = hcd_to_sl811(hcd); | 
|  | int		retval = 0; | 
|  |  | 
|  | switch (state.event) { | 
|  | case PM_EVENT_FREEZE: | 
|  | retval = sl811h_bus_suspend(hcd); | 
|  | break; | 
|  | case PM_EVENT_SUSPEND: | 
|  | case PM_EVENT_HIBERNATE: | 
|  | case PM_EVENT_PRETHAW:		/* explicitly discard hw state */ | 
|  | port_power(sl811, 0); | 
|  | break; | 
|  | } | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | static int | 
|  | sl811h_resume(struct platform_device *dev) | 
|  | { | 
|  | struct usb_hcd	*hcd = platform_get_drvdata(dev); | 
|  | struct sl811	*sl811 = hcd_to_sl811(hcd); | 
|  |  | 
|  | /* 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; | 
|  | } | 
|  |  | 
|  | return sl811h_bus_resume(hcd); | 
|  | } | 
|  |  | 
|  | #else | 
|  |  | 
|  | #define	sl811h_suspend	NULL | 
|  | #define	sl811h_resume	NULL | 
|  |  | 
|  | #endif | 
|  |  | 
|  |  | 
|  | /* this driver is exported so sl811_cs can depend on it */ | 
|  | struct platform_driver sl811h_driver = { | 
|  | .probe =	sl811h_probe, | 
|  | .remove =	sl811h_remove, | 
|  |  | 
|  | .suspend =	sl811h_suspend, | 
|  | .resume =	sl811h_resume, | 
|  | .driver = { | 
|  | .name =	hcd_name, | 
|  | }, | 
|  | }; | 
|  | EXPORT_SYMBOL(sl811h_driver); | 
|  |  | 
|  | module_platform_driver(sl811h_driver); |