/* 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((int)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); }
int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int len) { int dir_out = usb_pipeout(pipe); int ep = usb_pipeendpoint(pipe); int max = usb_maxpacket(dev, pipe); int done = 0; PDEBUG(7, "dev = %ld pipe = %ld buf = %p size = %d dir_out = %d\n", usb_pipedevice(pipe), usb_pipeendpoint(pipe), buffer, len, dir_out); dev->status = 0; sl811_write(SL811_DEV_A, usb_pipedevice(pipe)); sl811_write(SL811_PIDEP_A, PIDEP(!dir_out ? USB_PID_IN : USB_PID_OUT, ep)); while (done < len) { int res = sl811_send_packet(dev, pipe, (__u8*)buffer+done, max > len - done ? len - done : max); if (res < 0) { dev->status = -res; return res; } if (!dir_out && res < max) /* short packet */ break; done += res; usb_dotoggle(dev, ep, dir_out); } dev->act_len = done; return 0; }
/* 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); }
/* 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); }
int usb_init_kup4x (void) { volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; volatile memctl8xx_t *memctl = &immap->im_memctl; int i; unsigned char tmp; memctl = &immap->im_memctl; memctl->memc_or7 = 0xFFFF8726; memctl->memc_br7 = 0x50000401; /* start at 0x50000000 */ /* BP 14 low = USB ON */ immap->im_cpm.cp_pbdat &= ~(BP_USB_VCC); /* PB 14 nomal port */ immap->im_cpm.cp_pbpar &= ~(BP_USB_VCC); /* output */ immap->im_cpm.cp_pbdir |= (BP_USB_VCC); puts ("USB: "); for (i = 0x10; i < 0xff; i++) { sl811_write(i, i); tmp = (sl811_read(i)); if (tmp != i) { printf ("SL811 compare error index=0x%02x read=0x%02x\n", i, tmp); return (-1); } } printf ("SL811 ready\n"); return (0); }
/* 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((int)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); }
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 & (1 << USB_PORT_FEAT_POWER)) return; sl811->port1 = (1 << USB_PORT_FEAT_POWER); sl811->irq_enable = SL11H_INTMASK_INSRMV; hcd->self.controller->power.power_state = PMSG_ON; } else { sl811->port1 = 0; sl811->irq_enable = 0; hcd->state = HC_STATE_HALT; hcd->self.controller->power.power_state = PMSG_SUSPEND; } 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 */ DBG("power %s\n", is_on ? "on" : "off"); 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 }
static void port_power(struct sl811 *sl811, int is_on) { struct usb_hcd *hcd = sl811_to_hcd(sl811); 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) { DBG("power %s\n", is_on ? "on" : "off"); sl811->board->port_power(hcd->self.controller, is_on); } 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); }
int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int len,struct devrequest *setup) { int done = 0; int devnum = usb_pipedevice(pipe); int ep = usb_pipeendpoint(pipe); dev->status = 0; if (devnum == root_hub_devnum) return sl811_rh_submit_urb(dev, pipe, buffer, len, setup); PDEBUG(7, "dev = %d pipe = %ld buf = %p size = %d rt = %#x req = %#x bus = %i\n", devnum, ep, buffer, len, (int)setup->requesttype, (int)setup->request, sl811_read(SL811_SOFCNTDIV)*64); sl811_write(SL811_DEV_A, devnum); sl811_write(SL811_PIDEP_A, PIDEP(USB_PID_SETUP, ep)); /* setup phase */ usb_settoggle(dev, ep, 1, 0); if (sl811_send_packet(dev, usb_sndctrlpipe(dev, ep), (__u8*)setup, sizeof(*setup)) == sizeof(*setup)) { int dir_in = usb_pipein(pipe); int max = usb_maxpacket(dev, pipe); /* data phase */ sl811_write(SL811_PIDEP_A, PIDEP(dir_in ? USB_PID_IN : USB_PID_OUT, ep)); usb_settoggle(dev, ep, usb_pipeout(pipe), 1); while (done < len) { int res = sl811_send_packet(dev, pipe, (__u8*)buffer+done, max > len - done ? len - done : max); if (res < 0) { PDEBUG(0, "status data failed!\n"); dev->status = -res; return 0; } done += res; usb_dotoggle(dev, ep, usb_pipeout(pipe)); if (dir_in && res < max) /* short packet */ break; } /* status phase */ sl811_write(SL811_PIDEP_A, PIDEP(!dir_in ? USB_PID_IN : USB_PID_OUT, ep)); usb_settoggle(dev, ep, !usb_pipeout(pipe), 1); if (sl811_send_packet(dev, !dir_in ? usb_rcvctrlpipe(dev, ep) : usb_sndctrlpipe(dev, ep), 0, 0) < 0) { PDEBUG(0, "status phase failed!\n"); dev->status = -1; } } else { PDEBUG(0, "setup phase failed!\n"); dev->status = -1; } dev->act_len = done; return done; }
static int sl811_send_packet(struct usb_device *dev, unsigned long pipe, __u8 *buffer, int len) { __u8 ctrl = SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE; __u16 status = 0; int err = 0, time_start = get_timer(0); int need_preamble = !(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) && (dev->speed == USB_SPEED_LOW); if (len > 239) return -1; if (usb_pipeout(pipe)) ctrl |= SL811_USB_CTRL_DIR_OUT; if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) ctrl |= SL811_USB_CTRL_TOGGLE_1; if (need_preamble) ctrl |= SL811_USB_CTRL_PREAMBLE; sl811_write(SL811_INTRSTS, 0xff); while (err < 3) { sl811_write(SL811_ADDR_A, 0x10); sl811_write(SL811_LEN_A, len); if (usb_pipeout(pipe) && len) sl811_write_buf(0x10, buffer, len); if (!(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) && sl811_read(SL811_SOFCNTDIV)*64 < calc_needed_buswidth(len, need_preamble)) ctrl |= SL811_USB_CTRL_SOF; else ctrl &= ~SL811_USB_CTRL_SOF; sl811_write(SL811_CTRL_A, ctrl); while (!(sl811_read(SL811_INTRSTS) & SL811_INTR_DONE_A)) { if (5*CONFIG_SYS_HZ < get_timer(time_start)) { printf("USB transmit timed out\n"); return -USB_ST_CRC_ERR; } } sl811_write(SL811_INTRSTS, 0xff); status = sl811_read(SL811_STS_A); if (status & SL811_USB_STS_ACK) { int remainder = sl811_read(SL811_CNT_A); if (remainder) { PDEBUG(0, "usb transfer remainder = %d\n", remainder); len -= remainder; } if (usb_pipein(pipe) && len) sl811_read_buf(0x10, buffer, len); return len; } if ((status & SL811_USB_STS_NAK) == SL811_USB_STS_NAK) continue; PDEBUG(0, "usb transfer error %#x\n", (int)status); err++; } err = 0; if (status & SL811_USB_STS_ERROR) err |= USB_ST_BUF_ERR; if (status & SL811_USB_STS_TIMEOUT) err |= USB_ST_CRC_ERR; if (status & SL811_USB_STS_STALL) err |= USB_ST_STALLED; return -err; }
/* * This function resets SL811HS controller and detects the speed of * the connecting device * * Return: 0 = no device attached; 1 = USB device attached */ static int sl811_hc_reset(void) { int status ; sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI); sl811_write(SL811_CTRL1, SL811_CTRL1_RESET); mdelay(20); /* Disable hardware SOF generation, clear all irq status. */ sl811_write(SL811_CTRL1, 0); mdelay(2); sl811_write(SL811_INTRSTS, 0xff); status = sl811_read(SL811_INTRSTS); if (status & SL811_INTR_NOTPRESENT) { /* Device is not present */ PDEBUG(0, "Device not present\n"); rh_status.wPortStatus &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE); rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION; sl811_write(SL811_INTR, SL811_INTR_INSRMV); return 0; } /* Send SOF to address 0, endpoint 0. */ sl811_write(SL811_LEN_B, 0); sl811_write(SL811_PIDEP_B, PIDEP(USB_PID_SOF, 0)); sl811_write(SL811_DEV_B, 0x00); sl811_write(SL811_SOFLOW, SL811_12M_LOW); if (status & SL811_INTR_SPEED_FULL) { /* full speed device connect directly to root hub */ PDEBUG (0, "Full speed Device attached\n"); sl811_write(SL811_CTRL1, SL811_CTRL1_RESET); mdelay(20); sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI); sl811_write(SL811_CTRL1, SL811_CTRL1_SOF); /* start the SOF or EOP */ sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM); rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION; rh_status.wPortStatus &= ~USB_PORT_STAT_LOW_SPEED; mdelay(2); sl811_write(SL811_INTRSTS, 0xff); } else { /* slow speed device connect directly to root-hub */ PDEBUG(0, "Low speed Device attached\n"); sl811_write(SL811_CTRL1, SL811_CTRL1_RESET); mdelay(20); sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_CTL2_DSWAP | SL811_12M_HI); sl811_write(SL811_CTRL1, SL811_CTRL1_SPEED_LOW | SL811_CTRL1_SOF); /* start the SOF or EOP */ sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM); rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION | USB_PORT_STAT_LOW_SPEED; mdelay(2); sl811_write(SL811_INTRSTS, 0xff); } rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION; sl811_write(SL811_INTR, /*SL811_INTR_INSRMV*/SL811_INTR_DONE_A); return 1; }