162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ISP116x HCD (Host Controller Driver) for USB. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Derived from the SL811 HCD, rewritten for ISP116x. 662306a36Sopenharmony_ci * Copyright (C) 2005 Olav Kongas <ok@artecdesign.ee> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Portions: 962306a36Sopenharmony_ci * Copyright (C) 2004 Psion Teklogix (for NetBook PRO) 1062306a36Sopenharmony_ci * Copyright (C) 2004 David Brownell 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Periodic scheduling is based on Roman's OHCI code 1362306a36Sopenharmony_ci * Copyright (C) 1999 Roman Weissgaerber 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* 1862306a36Sopenharmony_ci * The driver basically works. A number of people have used it with a range 1962306a36Sopenharmony_ci * of devices. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * The driver passes all usbtests 1-14. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Suspending/resuming of root hub via sysfs works. Remote wakeup works too. 2462306a36Sopenharmony_ci * And suspending/resuming of platform device works too. Suspend/resume 2562306a36Sopenharmony_ci * via HCD operations vector is not implemented. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * Iso transfer support is not implemented. Adding this would include 2862306a36Sopenharmony_ci * implementing recovery from the failure to service the processed ITL 2962306a36Sopenharmony_ci * fifo ram in time, which will involve chip reset. 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * TODO: 3262306a36Sopenharmony_ci + More testing of suspend/resume. 3362306a36Sopenharmony_ci*/ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci ISP116x chips require certain delays between accesses to its 3762306a36Sopenharmony_ci registers. The following timing options exist. 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci 1. Configure your memory controller (the best) 4062306a36Sopenharmony_ci 2. Implement platform-specific delay function possibly 4162306a36Sopenharmony_ci combined with configuring the memory controller; see 4262306a36Sopenharmony_ci include/linux/usb-isp116x.h for more info. Some broken 4362306a36Sopenharmony_ci memory controllers line LH7A400 SMC need this. Also, 4462306a36Sopenharmony_ci uncomment for that to work the following 4562306a36Sopenharmony_ci USE_PLATFORM_DELAY macro. 4662306a36Sopenharmony_ci 3. Use ndelay (easiest, poorest). For that, uncomment 4762306a36Sopenharmony_ci the following USE_NDELAY macro. 4862306a36Sopenharmony_ci*/ 4962306a36Sopenharmony_ci#define USE_PLATFORM_DELAY 5062306a36Sopenharmony_ci//#define USE_NDELAY 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci//#define DEBUG 5362306a36Sopenharmony_ci//#define VERBOSE 5462306a36Sopenharmony_ci/* Transfer descriptors. See dump_ptd() for printout format */ 5562306a36Sopenharmony_ci//#define PTD_TRACE 5662306a36Sopenharmony_ci/* enqueuing/finishing log of urbs */ 5762306a36Sopenharmony_ci//#define URB_TRACE 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#include <linux/module.h> 6062306a36Sopenharmony_ci#include <linux/delay.h> 6162306a36Sopenharmony_ci#include <linux/debugfs.h> 6262306a36Sopenharmony_ci#include <linux/seq_file.h> 6362306a36Sopenharmony_ci#include <linux/errno.h> 6462306a36Sopenharmony_ci#include <linux/list.h> 6562306a36Sopenharmony_ci#include <linux/slab.h> 6662306a36Sopenharmony_ci#include <linux/usb.h> 6762306a36Sopenharmony_ci#include <linux/usb/isp116x.h> 6862306a36Sopenharmony_ci#include <linux/usb/hcd.h> 6962306a36Sopenharmony_ci#include <linux/platform_device.h> 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#include <asm/io.h> 7262306a36Sopenharmony_ci#include <asm/irq.h> 7362306a36Sopenharmony_ci#include <asm/byteorder.h> 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci#include "isp116x.h" 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define DRIVER_VERSION "03 Nov 2005" 7862306a36Sopenharmony_ci#define DRIVER_DESC "ISP116x USB Host Controller Driver" 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 8162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic const char hcd_name[] = "isp116x-hcd"; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/*-----------------------------------------------------------------*/ 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* 8862306a36Sopenharmony_ci Write len bytes to fifo, pad till 32-bit boundary 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_cistatic void write_ptddata_to_fifo(struct isp116x *isp116x, void *buf, int len) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci u8 *dp = (u8 *) buf; 9362306a36Sopenharmony_ci u16 *dp2 = (u16 *) buf; 9462306a36Sopenharmony_ci u16 w; 9562306a36Sopenharmony_ci int quot = len % 4; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* buffer is already in 'usb data order', which is LE. */ 9862306a36Sopenharmony_ci /* When reading buffer as u16, we have to take care byte order */ 9962306a36Sopenharmony_ci /* doesn't get mixed up */ 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if ((unsigned long)dp2 & 1) { 10262306a36Sopenharmony_ci /* not aligned */ 10362306a36Sopenharmony_ci for (; len > 1; len -= 2) { 10462306a36Sopenharmony_ci w = *dp++; 10562306a36Sopenharmony_ci w |= *dp++ << 8; 10662306a36Sopenharmony_ci isp116x_raw_write_data16(isp116x, w); 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci if (len) 10962306a36Sopenharmony_ci isp116x_write_data16(isp116x, (u16) * dp); 11062306a36Sopenharmony_ci } else { 11162306a36Sopenharmony_ci /* aligned */ 11262306a36Sopenharmony_ci for (; len > 1; len -= 2) { 11362306a36Sopenharmony_ci /* Keep byte order ! */ 11462306a36Sopenharmony_ci isp116x_raw_write_data16(isp116x, cpu_to_le16(*dp2++)); 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (len) 11862306a36Sopenharmony_ci isp116x_write_data16(isp116x, 0xff & *((u8 *) dp2)); 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci if (quot == 1 || quot == 2) 12162306a36Sopenharmony_ci isp116x_raw_write_data16(isp116x, 0); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* 12562306a36Sopenharmony_ci Read len bytes from fifo and then read till 32-bit boundary. 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_cistatic void read_ptddata_from_fifo(struct isp116x *isp116x, void *buf, int len) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci u8 *dp = (u8 *) buf; 13062306a36Sopenharmony_ci u16 *dp2 = (u16 *) buf; 13162306a36Sopenharmony_ci u16 w; 13262306a36Sopenharmony_ci int quot = len % 4; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* buffer is already in 'usb data order', which is LE. */ 13562306a36Sopenharmony_ci /* When reading buffer as u16, we have to take care byte order */ 13662306a36Sopenharmony_ci /* doesn't get mixed up */ 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if ((unsigned long)dp2 & 1) { 13962306a36Sopenharmony_ci /* not aligned */ 14062306a36Sopenharmony_ci for (; len > 1; len -= 2) { 14162306a36Sopenharmony_ci w = isp116x_raw_read_data16(isp116x); 14262306a36Sopenharmony_ci *dp++ = w & 0xff; 14362306a36Sopenharmony_ci *dp++ = (w >> 8) & 0xff; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (len) 14762306a36Sopenharmony_ci *dp = 0xff & isp116x_read_data16(isp116x); 14862306a36Sopenharmony_ci } else { 14962306a36Sopenharmony_ci /* aligned */ 15062306a36Sopenharmony_ci for (; len > 1; len -= 2) { 15162306a36Sopenharmony_ci /* Keep byte order! */ 15262306a36Sopenharmony_ci *dp2++ = le16_to_cpu(isp116x_raw_read_data16(isp116x)); 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (len) 15662306a36Sopenharmony_ci *(u8 *) dp2 = 0xff & isp116x_read_data16(isp116x); 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci if (quot == 1 || quot == 2) 15962306a36Sopenharmony_ci isp116x_raw_read_data16(isp116x); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci/* 16362306a36Sopenharmony_ci Write ptd's and data for scheduled transfers into 16462306a36Sopenharmony_ci the fifo ram. Fifo must be empty and ready. 16562306a36Sopenharmony_ci*/ 16662306a36Sopenharmony_cistatic void pack_fifo(struct isp116x *isp116x) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct isp116x_ep *ep; 16962306a36Sopenharmony_ci struct ptd *ptd; 17062306a36Sopenharmony_ci int buflen = isp116x->atl_last_dir == PTD_DIR_IN 17162306a36Sopenharmony_ci ? isp116x->atl_bufshrt : isp116x->atl_buflen; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT); 17462306a36Sopenharmony_ci isp116x_write_reg16(isp116x, HCXFERCTR, buflen); 17562306a36Sopenharmony_ci isp116x_write_addr(isp116x, HCATLPORT | ISP116x_WRITE_OFFSET); 17662306a36Sopenharmony_ci for (ep = isp116x->atl_active; ep; ep = ep->active) { 17762306a36Sopenharmony_ci ptd = &ep->ptd; 17862306a36Sopenharmony_ci dump_ptd(ptd); 17962306a36Sopenharmony_ci dump_ptd_out_data(ptd, ep->data); 18062306a36Sopenharmony_ci isp116x_write_data16(isp116x, ptd->count); 18162306a36Sopenharmony_ci isp116x_write_data16(isp116x, ptd->mps); 18262306a36Sopenharmony_ci isp116x_write_data16(isp116x, ptd->len); 18362306a36Sopenharmony_ci isp116x_write_data16(isp116x, ptd->faddr); 18462306a36Sopenharmony_ci buflen -= sizeof(struct ptd); 18562306a36Sopenharmony_ci /* Skip writing data for last IN PTD */ 18662306a36Sopenharmony_ci if (ep->active || (isp116x->atl_last_dir != PTD_DIR_IN)) { 18762306a36Sopenharmony_ci write_ptddata_to_fifo(isp116x, ep->data, ep->length); 18862306a36Sopenharmony_ci buflen -= ALIGN(ep->length, 4); 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci BUG_ON(buflen); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/* 19562306a36Sopenharmony_ci Read the processed ptd's and data from fifo ram back to 19662306a36Sopenharmony_ci URBs' buffers. Fifo must be full and done 19762306a36Sopenharmony_ci*/ 19862306a36Sopenharmony_cistatic void unpack_fifo(struct isp116x *isp116x) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci struct isp116x_ep *ep; 20162306a36Sopenharmony_ci struct ptd *ptd; 20262306a36Sopenharmony_ci int buflen = isp116x->atl_last_dir == PTD_DIR_IN 20362306a36Sopenharmony_ci ? isp116x->atl_buflen : isp116x->atl_bufshrt; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT); 20662306a36Sopenharmony_ci isp116x_write_reg16(isp116x, HCXFERCTR, buflen); 20762306a36Sopenharmony_ci isp116x_write_addr(isp116x, HCATLPORT); 20862306a36Sopenharmony_ci for (ep = isp116x->atl_active; ep; ep = ep->active) { 20962306a36Sopenharmony_ci ptd = &ep->ptd; 21062306a36Sopenharmony_ci ptd->count = isp116x_read_data16(isp116x); 21162306a36Sopenharmony_ci ptd->mps = isp116x_read_data16(isp116x); 21262306a36Sopenharmony_ci ptd->len = isp116x_read_data16(isp116x); 21362306a36Sopenharmony_ci ptd->faddr = isp116x_read_data16(isp116x); 21462306a36Sopenharmony_ci buflen -= sizeof(struct ptd); 21562306a36Sopenharmony_ci /* Skip reading data for last Setup or Out PTD */ 21662306a36Sopenharmony_ci if (ep->active || (isp116x->atl_last_dir == PTD_DIR_IN)) { 21762306a36Sopenharmony_ci read_ptddata_from_fifo(isp116x, ep->data, ep->length); 21862306a36Sopenharmony_ci buflen -= ALIGN(ep->length, 4); 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci dump_ptd(ptd); 22162306a36Sopenharmony_ci dump_ptd_in_data(ptd, ep->data); 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci BUG_ON(buflen); 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/*---------------------------------------------------------------*/ 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci/* 22962306a36Sopenharmony_ci Set up PTD's. 23062306a36Sopenharmony_ci*/ 23162306a36Sopenharmony_cistatic void preproc_atl_queue(struct isp116x *isp116x) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct isp116x_ep *ep; 23462306a36Sopenharmony_ci struct urb *urb; 23562306a36Sopenharmony_ci struct ptd *ptd; 23662306a36Sopenharmony_ci u16 len; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci for (ep = isp116x->atl_active; ep; ep = ep->active) { 23962306a36Sopenharmony_ci u16 toggle = 0, dir = PTD_DIR_SETUP; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci BUG_ON(list_empty(&ep->hep->urb_list)); 24262306a36Sopenharmony_ci urb = container_of(ep->hep->urb_list.next, 24362306a36Sopenharmony_ci struct urb, urb_list); 24462306a36Sopenharmony_ci ptd = &ep->ptd; 24562306a36Sopenharmony_ci len = ep->length; 24662306a36Sopenharmony_ci ep->data = (unsigned char *)urb->transfer_buffer 24762306a36Sopenharmony_ci + urb->actual_length; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci switch (ep->nextpid) { 25062306a36Sopenharmony_ci case USB_PID_IN: 25162306a36Sopenharmony_ci toggle = usb_gettoggle(urb->dev, ep->epnum, 0); 25262306a36Sopenharmony_ci dir = PTD_DIR_IN; 25362306a36Sopenharmony_ci break; 25462306a36Sopenharmony_ci case USB_PID_OUT: 25562306a36Sopenharmony_ci toggle = usb_gettoggle(urb->dev, ep->epnum, 1); 25662306a36Sopenharmony_ci dir = PTD_DIR_OUT; 25762306a36Sopenharmony_ci break; 25862306a36Sopenharmony_ci case USB_PID_SETUP: 25962306a36Sopenharmony_ci len = sizeof(struct usb_ctrlrequest); 26062306a36Sopenharmony_ci ep->data = urb->setup_packet; 26162306a36Sopenharmony_ci break; 26262306a36Sopenharmony_ci case USB_PID_ACK: 26362306a36Sopenharmony_ci toggle = 1; 26462306a36Sopenharmony_ci len = 0; 26562306a36Sopenharmony_ci dir = (urb->transfer_buffer_length 26662306a36Sopenharmony_ci && usb_pipein(urb->pipe)) 26762306a36Sopenharmony_ci ? PTD_DIR_OUT : PTD_DIR_IN; 26862306a36Sopenharmony_ci break; 26962306a36Sopenharmony_ci default: 27062306a36Sopenharmony_ci ERR("%s %d: ep->nextpid %d\n", __func__, __LINE__, 27162306a36Sopenharmony_ci ep->nextpid); 27262306a36Sopenharmony_ci BUG(); 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci ptd->count = PTD_CC_MSK | PTD_ACTIVE_MSK | PTD_TOGGLE(toggle); 27662306a36Sopenharmony_ci ptd->mps = PTD_MPS(ep->maxpacket) 27762306a36Sopenharmony_ci | PTD_SPD(urb->dev->speed == USB_SPEED_LOW) 27862306a36Sopenharmony_ci | PTD_EP(ep->epnum); 27962306a36Sopenharmony_ci ptd->len = PTD_LEN(len) | PTD_DIR(dir); 28062306a36Sopenharmony_ci ptd->faddr = PTD_FA(usb_pipedevice(urb->pipe)); 28162306a36Sopenharmony_ci if (!ep->active) { 28262306a36Sopenharmony_ci ptd->mps |= PTD_LAST_MSK; 28362306a36Sopenharmony_ci isp116x->atl_last_dir = dir; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci isp116x->atl_bufshrt = sizeof(struct ptd) + isp116x->atl_buflen; 28662306a36Sopenharmony_ci isp116x->atl_buflen = isp116x->atl_bufshrt + ALIGN(len, 4); 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci/* 29162306a36Sopenharmony_ci Take done or failed requests out of schedule. Give back 29262306a36Sopenharmony_ci processed urbs. 29362306a36Sopenharmony_ci*/ 29462306a36Sopenharmony_cistatic void finish_request(struct isp116x *isp116x, struct isp116x_ep *ep, 29562306a36Sopenharmony_ci struct urb *urb, int status) 29662306a36Sopenharmony_ci__releases(isp116x->lock) __acquires(isp116x->lock) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci unsigned i; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci ep->error_count = 0; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (usb_pipecontrol(urb->pipe)) 30362306a36Sopenharmony_ci ep->nextpid = USB_PID_SETUP; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci urb_dbg(urb, "Finish"); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci usb_hcd_unlink_urb_from_ep(isp116x_to_hcd(isp116x), urb); 30862306a36Sopenharmony_ci spin_unlock(&isp116x->lock); 30962306a36Sopenharmony_ci usb_hcd_giveback_urb(isp116x_to_hcd(isp116x), urb, status); 31062306a36Sopenharmony_ci spin_lock(&isp116x->lock); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* take idle endpoints out of the schedule */ 31362306a36Sopenharmony_ci if (!list_empty(&ep->hep->urb_list)) 31462306a36Sopenharmony_ci return; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* async deschedule */ 31762306a36Sopenharmony_ci if (!list_empty(&ep->schedule)) { 31862306a36Sopenharmony_ci list_del_init(&ep->schedule); 31962306a36Sopenharmony_ci return; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* periodic deschedule */ 32362306a36Sopenharmony_ci DBG("deschedule qh%d/%p branch %d\n", ep->period, ep, ep->branch); 32462306a36Sopenharmony_ci for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) { 32562306a36Sopenharmony_ci struct isp116x_ep *temp; 32662306a36Sopenharmony_ci struct isp116x_ep **prev = &isp116x->periodic[i]; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci while (*prev && ((temp = *prev) != ep)) 32962306a36Sopenharmony_ci prev = &temp->next; 33062306a36Sopenharmony_ci if (*prev) 33162306a36Sopenharmony_ci *prev = ep->next; 33262306a36Sopenharmony_ci isp116x->load[i] -= ep->load; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci ep->branch = PERIODIC_SIZE; 33562306a36Sopenharmony_ci isp116x_to_hcd(isp116x)->self.bandwidth_allocated -= 33662306a36Sopenharmony_ci ep->load / ep->period; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* switch irq type? */ 33962306a36Sopenharmony_ci if (!--isp116x->periodic_count) { 34062306a36Sopenharmony_ci isp116x->irqenb &= ~HCuPINT_SOF; 34162306a36Sopenharmony_ci isp116x->irqenb |= HCuPINT_ATL; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci/* 34662306a36Sopenharmony_ci Analyze transfer results, handle partial transfers and errors 34762306a36Sopenharmony_ci*/ 34862306a36Sopenharmony_cistatic void postproc_atl_queue(struct isp116x *isp116x) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci struct isp116x_ep *ep; 35162306a36Sopenharmony_ci struct urb *urb; 35262306a36Sopenharmony_ci struct usb_device *udev; 35362306a36Sopenharmony_ci struct ptd *ptd; 35462306a36Sopenharmony_ci int short_not_ok; 35562306a36Sopenharmony_ci int status; 35662306a36Sopenharmony_ci u8 cc; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci for (ep = isp116x->atl_active; ep; ep = ep->active) { 35962306a36Sopenharmony_ci BUG_ON(list_empty(&ep->hep->urb_list)); 36062306a36Sopenharmony_ci urb = 36162306a36Sopenharmony_ci container_of(ep->hep->urb_list.next, struct urb, urb_list); 36262306a36Sopenharmony_ci udev = urb->dev; 36362306a36Sopenharmony_ci ptd = &ep->ptd; 36462306a36Sopenharmony_ci cc = PTD_GET_CC(ptd); 36562306a36Sopenharmony_ci short_not_ok = 1; 36662306a36Sopenharmony_ci status = -EINPROGRESS; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* Data underrun is special. For allowed underrun 36962306a36Sopenharmony_ci we clear the error and continue as normal. For 37062306a36Sopenharmony_ci forbidden underrun we finish the DATA stage 37162306a36Sopenharmony_ci immediately while for control transfer, 37262306a36Sopenharmony_ci we do a STATUS stage. */ 37362306a36Sopenharmony_ci if (cc == TD_DATAUNDERRUN) { 37462306a36Sopenharmony_ci if (!(urb->transfer_flags & URB_SHORT_NOT_OK) || 37562306a36Sopenharmony_ci usb_pipecontrol(urb->pipe)) { 37662306a36Sopenharmony_ci DBG("Allowed or control data underrun\n"); 37762306a36Sopenharmony_ci cc = TD_CC_NOERROR; 37862306a36Sopenharmony_ci short_not_ok = 0; 37962306a36Sopenharmony_ci } else { 38062306a36Sopenharmony_ci ep->error_count = 1; 38162306a36Sopenharmony_ci usb_settoggle(udev, ep->epnum, 38262306a36Sopenharmony_ci ep->nextpid == USB_PID_OUT, 38362306a36Sopenharmony_ci PTD_GET_TOGGLE(ptd)); 38462306a36Sopenharmony_ci urb->actual_length += PTD_GET_COUNT(ptd); 38562306a36Sopenharmony_ci status = cc_to_error[TD_DATAUNDERRUN]; 38662306a36Sopenharmony_ci goto done; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (cc != TD_CC_NOERROR && cc != TD_NOTACCESSED 39162306a36Sopenharmony_ci && (++ep->error_count >= 3 || cc == TD_CC_STALL 39262306a36Sopenharmony_ci || cc == TD_DATAOVERRUN)) { 39362306a36Sopenharmony_ci status = cc_to_error[cc]; 39462306a36Sopenharmony_ci if (ep->nextpid == USB_PID_ACK) 39562306a36Sopenharmony_ci ep->nextpid = 0; 39662306a36Sopenharmony_ci goto done; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci /* According to usb spec, zero-length Int transfer signals 39962306a36Sopenharmony_ci finishing of the urb. Hey, does this apply only 40062306a36Sopenharmony_ci for IN endpoints? */ 40162306a36Sopenharmony_ci if (usb_pipeint(urb->pipe) && !PTD_GET_LEN(ptd)) { 40262306a36Sopenharmony_ci status = 0; 40362306a36Sopenharmony_ci goto done; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* Relax after previously failed, but later succeeded 40762306a36Sopenharmony_ci or correctly NAK'ed retransmission attempt */ 40862306a36Sopenharmony_ci if (ep->error_count 40962306a36Sopenharmony_ci && (cc == TD_CC_NOERROR || cc == TD_NOTACCESSED)) 41062306a36Sopenharmony_ci ep->error_count = 0; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* Take into account idiosyncracies of the isp116x chip 41362306a36Sopenharmony_ci regarding toggle bit for failed transfers */ 41462306a36Sopenharmony_ci if (ep->nextpid == USB_PID_OUT) 41562306a36Sopenharmony_ci usb_settoggle(udev, ep->epnum, 1, PTD_GET_TOGGLE(ptd) 41662306a36Sopenharmony_ci ^ (ep->error_count > 0)); 41762306a36Sopenharmony_ci else if (ep->nextpid == USB_PID_IN) 41862306a36Sopenharmony_ci usb_settoggle(udev, ep->epnum, 0, PTD_GET_TOGGLE(ptd) 41962306a36Sopenharmony_ci ^ (ep->error_count > 0)); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci switch (ep->nextpid) { 42262306a36Sopenharmony_ci case USB_PID_IN: 42362306a36Sopenharmony_ci case USB_PID_OUT: 42462306a36Sopenharmony_ci urb->actual_length += PTD_GET_COUNT(ptd); 42562306a36Sopenharmony_ci if (PTD_GET_ACTIVE(ptd) 42662306a36Sopenharmony_ci || (cc != TD_CC_NOERROR && cc < 0x0E)) 42762306a36Sopenharmony_ci break; 42862306a36Sopenharmony_ci if (urb->transfer_buffer_length != urb->actual_length) { 42962306a36Sopenharmony_ci if (short_not_ok) 43062306a36Sopenharmony_ci break; 43162306a36Sopenharmony_ci } else { 43262306a36Sopenharmony_ci if (urb->transfer_flags & URB_ZERO_PACKET 43362306a36Sopenharmony_ci && ep->nextpid == USB_PID_OUT 43462306a36Sopenharmony_ci && !(PTD_GET_COUNT(ptd) % ep->maxpacket)) { 43562306a36Sopenharmony_ci DBG("Zero packet requested\n"); 43662306a36Sopenharmony_ci break; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci /* All data for this URB is transferred, let's finish */ 44062306a36Sopenharmony_ci if (usb_pipecontrol(urb->pipe)) 44162306a36Sopenharmony_ci ep->nextpid = USB_PID_ACK; 44262306a36Sopenharmony_ci else 44362306a36Sopenharmony_ci status = 0; 44462306a36Sopenharmony_ci break; 44562306a36Sopenharmony_ci case USB_PID_SETUP: 44662306a36Sopenharmony_ci if (PTD_GET_ACTIVE(ptd) 44762306a36Sopenharmony_ci || (cc != TD_CC_NOERROR && cc < 0x0E)) 44862306a36Sopenharmony_ci break; 44962306a36Sopenharmony_ci if (urb->transfer_buffer_length == urb->actual_length) 45062306a36Sopenharmony_ci ep->nextpid = USB_PID_ACK; 45162306a36Sopenharmony_ci else if (usb_pipeout(urb->pipe)) { 45262306a36Sopenharmony_ci usb_settoggle(udev, 0, 1, 1); 45362306a36Sopenharmony_ci ep->nextpid = USB_PID_OUT; 45462306a36Sopenharmony_ci } else { 45562306a36Sopenharmony_ci usb_settoggle(udev, 0, 0, 1); 45662306a36Sopenharmony_ci ep->nextpid = USB_PID_IN; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci break; 45962306a36Sopenharmony_ci case USB_PID_ACK: 46062306a36Sopenharmony_ci if (PTD_GET_ACTIVE(ptd) 46162306a36Sopenharmony_ci || (cc != TD_CC_NOERROR && cc < 0x0E)) 46262306a36Sopenharmony_ci break; 46362306a36Sopenharmony_ci status = 0; 46462306a36Sopenharmony_ci ep->nextpid = 0; 46562306a36Sopenharmony_ci break; 46662306a36Sopenharmony_ci default: 46762306a36Sopenharmony_ci BUG(); 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci done: 47162306a36Sopenharmony_ci if (status != -EINPROGRESS || urb->unlinked) 47262306a36Sopenharmony_ci finish_request(isp116x, ep, urb, status); 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci/* 47762306a36Sopenharmony_ci Scan transfer lists, schedule transfers, send data off 47862306a36Sopenharmony_ci to chip. 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_cistatic void start_atl_transfers(struct isp116x *isp116x) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci struct isp116x_ep *last_ep = NULL, *ep; 48362306a36Sopenharmony_ci struct urb *urb; 48462306a36Sopenharmony_ci u16 load = 0; 48562306a36Sopenharmony_ci int len, index, speed, byte_time; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (atomic_read(&isp116x->atl_finishing)) 48862306a36Sopenharmony_ci return; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (!HC_IS_RUNNING(isp116x_to_hcd(isp116x)->state)) 49162306a36Sopenharmony_ci return; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* FIFO not empty? */ 49462306a36Sopenharmony_ci if (isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_FULL) 49562306a36Sopenharmony_ci return; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci isp116x->atl_active = NULL; 49862306a36Sopenharmony_ci isp116x->atl_buflen = isp116x->atl_bufshrt = 0; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* Schedule int transfers */ 50162306a36Sopenharmony_ci if (isp116x->periodic_count) { 50262306a36Sopenharmony_ci isp116x->fmindex = index = 50362306a36Sopenharmony_ci (isp116x->fmindex + 1) & (PERIODIC_SIZE - 1); 50462306a36Sopenharmony_ci load = isp116x->load[index]; 50562306a36Sopenharmony_ci if (load) { 50662306a36Sopenharmony_ci /* Bring all int transfers for this frame 50762306a36Sopenharmony_ci into the active queue */ 50862306a36Sopenharmony_ci isp116x->atl_active = last_ep = 50962306a36Sopenharmony_ci isp116x->periodic[index]; 51062306a36Sopenharmony_ci while (last_ep->next) 51162306a36Sopenharmony_ci last_ep = (last_ep->active = last_ep->next); 51262306a36Sopenharmony_ci last_ep->active = NULL; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* Schedule control/bulk transfers */ 51762306a36Sopenharmony_ci list_for_each_entry(ep, &isp116x->async, schedule) { 51862306a36Sopenharmony_ci urb = container_of(ep->hep->urb_list.next, 51962306a36Sopenharmony_ci struct urb, urb_list); 52062306a36Sopenharmony_ci speed = urb->dev->speed; 52162306a36Sopenharmony_ci byte_time = speed == USB_SPEED_LOW 52262306a36Sopenharmony_ci ? BYTE_TIME_LOWSPEED : BYTE_TIME_FULLSPEED; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (ep->nextpid == USB_PID_SETUP) { 52562306a36Sopenharmony_ci len = sizeof(struct usb_ctrlrequest); 52662306a36Sopenharmony_ci } else if (ep->nextpid == USB_PID_ACK) { 52762306a36Sopenharmony_ci len = 0; 52862306a36Sopenharmony_ci } else { 52962306a36Sopenharmony_ci /* Find current free length ... */ 53062306a36Sopenharmony_ci len = (MAX_LOAD_LIMIT - load) / byte_time; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* ... then limit it to configured max size ... */ 53362306a36Sopenharmony_ci len = min(len, speed == USB_SPEED_LOW ? 53462306a36Sopenharmony_ci MAX_TRANSFER_SIZE_LOWSPEED : 53562306a36Sopenharmony_ci MAX_TRANSFER_SIZE_FULLSPEED); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci /* ... and finally cut to the multiple of MaxPacketSize, 53862306a36Sopenharmony_ci or to the real length if there's enough room. */ 53962306a36Sopenharmony_ci if (len < 54062306a36Sopenharmony_ci (urb->transfer_buffer_length - 54162306a36Sopenharmony_ci urb->actual_length)) { 54262306a36Sopenharmony_ci len -= len % ep->maxpacket; 54362306a36Sopenharmony_ci if (!len) 54462306a36Sopenharmony_ci continue; 54562306a36Sopenharmony_ci } else 54662306a36Sopenharmony_ci len = urb->transfer_buffer_length - 54762306a36Sopenharmony_ci urb->actual_length; 54862306a36Sopenharmony_ci BUG_ON(len < 0); 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci load += len * byte_time; 55262306a36Sopenharmony_ci if (load > MAX_LOAD_LIMIT) 55362306a36Sopenharmony_ci break; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci ep->active = NULL; 55662306a36Sopenharmony_ci ep->length = len; 55762306a36Sopenharmony_ci if (last_ep) 55862306a36Sopenharmony_ci last_ep->active = ep; 55962306a36Sopenharmony_ci else 56062306a36Sopenharmony_ci isp116x->atl_active = ep; 56162306a36Sopenharmony_ci last_ep = ep; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* Avoid starving of endpoints */ 56562306a36Sopenharmony_ci if ((&isp116x->async)->next != (&isp116x->async)->prev) 56662306a36Sopenharmony_ci list_move(&isp116x->async, (&isp116x->async)->next); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (isp116x->atl_active) { 56962306a36Sopenharmony_ci preproc_atl_queue(isp116x); 57062306a36Sopenharmony_ci pack_fifo(isp116x); 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci/* 57562306a36Sopenharmony_ci Finish the processed transfers 57662306a36Sopenharmony_ci*/ 57762306a36Sopenharmony_cistatic void finish_atl_transfers(struct isp116x *isp116x) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci if (!isp116x->atl_active) 58062306a36Sopenharmony_ci return; 58162306a36Sopenharmony_ci /* Fifo not ready? */ 58262306a36Sopenharmony_ci if (!(isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_DONE)) 58362306a36Sopenharmony_ci return; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci atomic_inc(&isp116x->atl_finishing); 58662306a36Sopenharmony_ci unpack_fifo(isp116x); 58762306a36Sopenharmony_ci postproc_atl_queue(isp116x); 58862306a36Sopenharmony_ci atomic_dec(&isp116x->atl_finishing); 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic irqreturn_t isp116x_irq(struct usb_hcd *hcd) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci struct isp116x *isp116x = hcd_to_isp116x(hcd); 59462306a36Sopenharmony_ci u16 irqstat; 59562306a36Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci spin_lock(&isp116x->lock); 59862306a36Sopenharmony_ci isp116x_write_reg16(isp116x, HCuPINTENB, 0); 59962306a36Sopenharmony_ci irqstat = isp116x_read_reg16(isp116x, HCuPINT); 60062306a36Sopenharmony_ci isp116x_write_reg16(isp116x, HCuPINT, irqstat); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (irqstat & (HCuPINT_ATL | HCuPINT_SOF)) { 60362306a36Sopenharmony_ci ret = IRQ_HANDLED; 60462306a36Sopenharmony_ci finish_atl_transfers(isp116x); 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (irqstat & HCuPINT_OPR) { 60862306a36Sopenharmony_ci u32 intstat = isp116x_read_reg32(isp116x, HCINTSTAT); 60962306a36Sopenharmony_ci isp116x_write_reg32(isp116x, HCINTSTAT, intstat); 61062306a36Sopenharmony_ci if (intstat & HCINT_UE) { 61162306a36Sopenharmony_ci ERR("Unrecoverable error, HC is dead!\n"); 61262306a36Sopenharmony_ci /* IRQ's are off, we do no DMA, 61362306a36Sopenharmony_ci perfectly ready to die ... */ 61462306a36Sopenharmony_ci hcd->state = HC_STATE_HALT; 61562306a36Sopenharmony_ci usb_hc_died(hcd); 61662306a36Sopenharmony_ci ret = IRQ_HANDLED; 61762306a36Sopenharmony_ci goto done; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci if (intstat & HCINT_RHSC) 62062306a36Sopenharmony_ci /* When root hub or any of its ports is going 62162306a36Sopenharmony_ci to come out of suspend, it may take more 62262306a36Sopenharmony_ci than 10ms for status bits to stabilize. */ 62362306a36Sopenharmony_ci mod_timer(&hcd->rh_timer, jiffies 62462306a36Sopenharmony_ci + msecs_to_jiffies(20) + 1); 62562306a36Sopenharmony_ci if (intstat & HCINT_RD) { 62662306a36Sopenharmony_ci DBG("---- remote wakeup\n"); 62762306a36Sopenharmony_ci usb_hcd_resume_root_hub(hcd); 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci irqstat &= ~HCuPINT_OPR; 63062306a36Sopenharmony_ci ret = IRQ_HANDLED; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (irqstat & (HCuPINT_ATL | HCuPINT_SOF)) { 63462306a36Sopenharmony_ci start_atl_transfers(isp116x); 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci isp116x_write_reg16(isp116x, HCuPINTENB, isp116x->irqenb); 63862306a36Sopenharmony_ci done: 63962306a36Sopenharmony_ci spin_unlock(&isp116x->lock); 64062306a36Sopenharmony_ci return ret; 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci/*-----------------------------------------------------------------*/ 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci/* usb 1.1 says max 90% of a frame is available for periodic transfers. 64662306a36Sopenharmony_ci * this driver doesn't promise that much since it's got to handle an 64762306a36Sopenharmony_ci * IRQ per packet; irq handling latencies also use up that time. 64862306a36Sopenharmony_ci */ 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci/* out of 1000 us */ 65162306a36Sopenharmony_ci#define MAX_PERIODIC_LOAD 600 65262306a36Sopenharmony_cistatic int balance(struct isp116x *isp116x, u16 period, u16 load) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci int i, branch = -ENOSPC; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci /* search for the least loaded schedule branch of that period 65762306a36Sopenharmony_ci which has enough bandwidth left unreserved. */ 65862306a36Sopenharmony_ci for (i = 0; i < period; i++) { 65962306a36Sopenharmony_ci if (branch < 0 || isp116x->load[branch] > isp116x->load[i]) { 66062306a36Sopenharmony_ci int j; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci for (j = i; j < PERIODIC_SIZE; j += period) { 66362306a36Sopenharmony_ci if ((isp116x->load[j] + load) 66462306a36Sopenharmony_ci > MAX_PERIODIC_LOAD) 66562306a36Sopenharmony_ci break; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci if (j < PERIODIC_SIZE) 66862306a36Sopenharmony_ci continue; 66962306a36Sopenharmony_ci branch = i; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci return branch; 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci/* NB! ALL the code above this point runs with isp116x->lock 67662306a36Sopenharmony_ci held, irqs off 67762306a36Sopenharmony_ci*/ 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci/*-----------------------------------------------------------------*/ 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic int isp116x_urb_enqueue(struct usb_hcd *hcd, 68262306a36Sopenharmony_ci struct urb *urb, 68362306a36Sopenharmony_ci gfp_t mem_flags) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci struct isp116x *isp116x = hcd_to_isp116x(hcd); 68662306a36Sopenharmony_ci struct usb_device *udev = urb->dev; 68762306a36Sopenharmony_ci unsigned int pipe = urb->pipe; 68862306a36Sopenharmony_ci int is_out = !usb_pipein(pipe); 68962306a36Sopenharmony_ci int type = usb_pipetype(pipe); 69062306a36Sopenharmony_ci int epnum = usb_pipeendpoint(pipe); 69162306a36Sopenharmony_ci struct usb_host_endpoint *hep = urb->ep; 69262306a36Sopenharmony_ci struct isp116x_ep *ep = NULL; 69362306a36Sopenharmony_ci unsigned long flags; 69462306a36Sopenharmony_ci int i; 69562306a36Sopenharmony_ci int ret = 0; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci urb_dbg(urb, "Enqueue"); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci if (type == PIPE_ISOCHRONOUS) { 70062306a36Sopenharmony_ci ERR("Isochronous transfers not supported\n"); 70162306a36Sopenharmony_ci urb_dbg(urb, "Refused to enqueue"); 70262306a36Sopenharmony_ci return -ENXIO; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci /* avoid all allocations within spinlocks: request or endpoint */ 70562306a36Sopenharmony_ci if (!hep->hcpriv) { 70662306a36Sopenharmony_ci ep = kzalloc(sizeof *ep, mem_flags); 70762306a36Sopenharmony_ci if (!ep) 70862306a36Sopenharmony_ci return -ENOMEM; 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci spin_lock_irqsave(&isp116x->lock, flags); 71262306a36Sopenharmony_ci if (!HC_IS_RUNNING(hcd->state)) { 71362306a36Sopenharmony_ci kfree(ep); 71462306a36Sopenharmony_ci ret = -ENODEV; 71562306a36Sopenharmony_ci goto fail_not_linked; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci ret = usb_hcd_link_urb_to_ep(hcd, urb); 71862306a36Sopenharmony_ci if (ret) { 71962306a36Sopenharmony_ci kfree(ep); 72062306a36Sopenharmony_ci goto fail_not_linked; 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (hep->hcpriv) 72462306a36Sopenharmony_ci ep = hep->hcpriv; 72562306a36Sopenharmony_ci else { 72662306a36Sopenharmony_ci INIT_LIST_HEAD(&ep->schedule); 72762306a36Sopenharmony_ci ep->udev = udev; 72862306a36Sopenharmony_ci ep->epnum = epnum; 72962306a36Sopenharmony_ci ep->maxpacket = usb_maxpacket(udev, urb->pipe); 73062306a36Sopenharmony_ci usb_settoggle(udev, epnum, is_out, 0); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (type == PIPE_CONTROL) { 73362306a36Sopenharmony_ci ep->nextpid = USB_PID_SETUP; 73462306a36Sopenharmony_ci } else if (is_out) { 73562306a36Sopenharmony_ci ep->nextpid = USB_PID_OUT; 73662306a36Sopenharmony_ci } else { 73762306a36Sopenharmony_ci ep->nextpid = USB_PID_IN; 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (urb->interval) { 74162306a36Sopenharmony_ci /* 74262306a36Sopenharmony_ci With INT URBs submitted, the driver works with SOF 74362306a36Sopenharmony_ci interrupt enabled and ATL interrupt disabled. After 74462306a36Sopenharmony_ci the PTDs are written to fifo ram, the chip starts 74562306a36Sopenharmony_ci fifo processing and usb transfers after the next 74662306a36Sopenharmony_ci SOF and continues until the transfers are finished 74762306a36Sopenharmony_ci (succeeded or failed) or the frame ends. Therefore, 74862306a36Sopenharmony_ci the transfers occur only in every second frame, 74962306a36Sopenharmony_ci while fifo reading/writing and data processing 75062306a36Sopenharmony_ci occur in every other second frame. */ 75162306a36Sopenharmony_ci if (urb->interval < 2) 75262306a36Sopenharmony_ci urb->interval = 2; 75362306a36Sopenharmony_ci if (urb->interval > 2 * PERIODIC_SIZE) 75462306a36Sopenharmony_ci urb->interval = 2 * PERIODIC_SIZE; 75562306a36Sopenharmony_ci ep->period = urb->interval >> 1; 75662306a36Sopenharmony_ci ep->branch = PERIODIC_SIZE; 75762306a36Sopenharmony_ci ep->load = usb_calc_bus_time(udev->speed, 75862306a36Sopenharmony_ci !is_out, 75962306a36Sopenharmony_ci (type == PIPE_ISOCHRONOUS), 76062306a36Sopenharmony_ci usb_maxpacket(udev, pipe)) / 76162306a36Sopenharmony_ci 1000; 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci hep->hcpriv = ep; 76462306a36Sopenharmony_ci ep->hep = hep; 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci /* maybe put endpoint into schedule */ 76862306a36Sopenharmony_ci switch (type) { 76962306a36Sopenharmony_ci case PIPE_CONTROL: 77062306a36Sopenharmony_ci case PIPE_BULK: 77162306a36Sopenharmony_ci if (list_empty(&ep->schedule)) 77262306a36Sopenharmony_ci list_add_tail(&ep->schedule, &isp116x->async); 77362306a36Sopenharmony_ci break; 77462306a36Sopenharmony_ci case PIPE_INTERRUPT: 77562306a36Sopenharmony_ci urb->interval = ep->period; 77662306a36Sopenharmony_ci ep->length = min_t(u32, ep->maxpacket, 77762306a36Sopenharmony_ci urb->transfer_buffer_length); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci /* urb submitted for already existing endpoint */ 78062306a36Sopenharmony_ci if (ep->branch < PERIODIC_SIZE) 78162306a36Sopenharmony_ci break; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci ep->branch = ret = balance(isp116x, ep->period, ep->load); 78462306a36Sopenharmony_ci if (ret < 0) 78562306a36Sopenharmony_ci goto fail; 78662306a36Sopenharmony_ci ret = 0; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci urb->start_frame = (isp116x->fmindex & (PERIODIC_SIZE - 1)) 78962306a36Sopenharmony_ci + ep->branch; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* sort each schedule branch by period (slow before fast) 79262306a36Sopenharmony_ci to share the faster parts of the tree without needing 79362306a36Sopenharmony_ci dummy/placeholder nodes */ 79462306a36Sopenharmony_ci DBG("schedule qh%d/%p branch %d\n", ep->period, ep, ep->branch); 79562306a36Sopenharmony_ci for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) { 79662306a36Sopenharmony_ci struct isp116x_ep **prev = &isp116x->periodic[i]; 79762306a36Sopenharmony_ci struct isp116x_ep *here = *prev; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci while (here && ep != here) { 80062306a36Sopenharmony_ci if (ep->period > here->period) 80162306a36Sopenharmony_ci break; 80262306a36Sopenharmony_ci prev = &here->next; 80362306a36Sopenharmony_ci here = *prev; 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci if (ep != here) { 80662306a36Sopenharmony_ci ep->next = here; 80762306a36Sopenharmony_ci *prev = ep; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci isp116x->load[i] += ep->load; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci hcd->self.bandwidth_allocated += ep->load / ep->period; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci /* switch over to SOFint */ 81462306a36Sopenharmony_ci if (!isp116x->periodic_count++) { 81562306a36Sopenharmony_ci isp116x->irqenb &= ~HCuPINT_ATL; 81662306a36Sopenharmony_ci isp116x->irqenb |= HCuPINT_SOF; 81762306a36Sopenharmony_ci isp116x_write_reg16(isp116x, HCuPINTENB, 81862306a36Sopenharmony_ci isp116x->irqenb); 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci urb->hcpriv = hep; 82362306a36Sopenharmony_ci start_atl_transfers(isp116x); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci fail: 82662306a36Sopenharmony_ci if (ret) 82762306a36Sopenharmony_ci usb_hcd_unlink_urb_from_ep(hcd, urb); 82862306a36Sopenharmony_ci fail_not_linked: 82962306a36Sopenharmony_ci spin_unlock_irqrestore(&isp116x->lock, flags); 83062306a36Sopenharmony_ci return ret; 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci/* 83462306a36Sopenharmony_ci Dequeue URBs. 83562306a36Sopenharmony_ci*/ 83662306a36Sopenharmony_cistatic int isp116x_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, 83762306a36Sopenharmony_ci int status) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci struct isp116x *isp116x = hcd_to_isp116x(hcd); 84062306a36Sopenharmony_ci struct usb_host_endpoint *hep; 84162306a36Sopenharmony_ci struct isp116x_ep *ep, *ep_act; 84262306a36Sopenharmony_ci unsigned long flags; 84362306a36Sopenharmony_ci int rc; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci spin_lock_irqsave(&isp116x->lock, flags); 84662306a36Sopenharmony_ci rc = usb_hcd_check_unlink_urb(hcd, urb, status); 84762306a36Sopenharmony_ci if (rc) 84862306a36Sopenharmony_ci goto done; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci hep = urb->hcpriv; 85162306a36Sopenharmony_ci ep = hep->hcpriv; 85262306a36Sopenharmony_ci WARN_ON(hep != ep->hep); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci /* In front of queue? */ 85562306a36Sopenharmony_ci if (ep->hep->urb_list.next == &urb->urb_list) 85662306a36Sopenharmony_ci /* active? */ 85762306a36Sopenharmony_ci for (ep_act = isp116x->atl_active; ep_act; 85862306a36Sopenharmony_ci ep_act = ep_act->active) 85962306a36Sopenharmony_ci if (ep_act == ep) { 86062306a36Sopenharmony_ci VDBG("dequeue, urb %p active; wait for irq\n", 86162306a36Sopenharmony_ci urb); 86262306a36Sopenharmony_ci urb = NULL; 86362306a36Sopenharmony_ci break; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci if (urb) 86762306a36Sopenharmony_ci finish_request(isp116x, ep, urb, status); 86862306a36Sopenharmony_ci done: 86962306a36Sopenharmony_ci spin_unlock_irqrestore(&isp116x->lock, flags); 87062306a36Sopenharmony_ci return rc; 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_cistatic void isp116x_endpoint_disable(struct usb_hcd *hcd, 87462306a36Sopenharmony_ci struct usb_host_endpoint *hep) 87562306a36Sopenharmony_ci{ 87662306a36Sopenharmony_ci int i; 87762306a36Sopenharmony_ci struct isp116x_ep *ep = hep->hcpriv; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci if (!ep) 88062306a36Sopenharmony_ci return; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci /* assume we'd just wait for the irq */ 88362306a36Sopenharmony_ci for (i = 0; i < 100 && !list_empty(&hep->urb_list); i++) 88462306a36Sopenharmony_ci msleep(3); 88562306a36Sopenharmony_ci if (!list_empty(&hep->urb_list)) 88662306a36Sopenharmony_ci WARNING("ep %p not empty?\n", ep); 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci kfree(ep); 88962306a36Sopenharmony_ci hep->hcpriv = NULL; 89062306a36Sopenharmony_ci} 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_cistatic int isp116x_get_frame(struct usb_hcd *hcd) 89362306a36Sopenharmony_ci{ 89462306a36Sopenharmony_ci struct isp116x *isp116x = hcd_to_isp116x(hcd); 89562306a36Sopenharmony_ci u32 fmnum; 89662306a36Sopenharmony_ci unsigned long flags; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci spin_lock_irqsave(&isp116x->lock, flags); 89962306a36Sopenharmony_ci fmnum = isp116x_read_reg32(isp116x, HCFMNUM); 90062306a36Sopenharmony_ci spin_unlock_irqrestore(&isp116x->lock, flags); 90162306a36Sopenharmony_ci return (int)fmnum; 90262306a36Sopenharmony_ci} 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci/* 90562306a36Sopenharmony_ci Adapted from ohci-hub.c. Currently we don't support autosuspend. 90662306a36Sopenharmony_ci*/ 90762306a36Sopenharmony_cistatic int isp116x_hub_status_data(struct usb_hcd *hcd, char *buf) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci struct isp116x *isp116x = hcd_to_isp116x(hcd); 91062306a36Sopenharmony_ci int ports, i, changed = 0; 91162306a36Sopenharmony_ci unsigned long flags; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci if (!HC_IS_RUNNING(hcd->state)) 91462306a36Sopenharmony_ci return -ESHUTDOWN; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci /* Report no status change now, if we are scheduled to be 91762306a36Sopenharmony_ci called later */ 91862306a36Sopenharmony_ci if (timer_pending(&hcd->rh_timer)) 91962306a36Sopenharmony_ci return 0; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci ports = isp116x->rhdesca & RH_A_NDP; 92262306a36Sopenharmony_ci spin_lock_irqsave(&isp116x->lock, flags); 92362306a36Sopenharmony_ci isp116x->rhstatus = isp116x_read_reg32(isp116x, HCRHSTATUS); 92462306a36Sopenharmony_ci if (isp116x->rhstatus & (RH_HS_LPSC | RH_HS_OCIC)) 92562306a36Sopenharmony_ci buf[0] = changed = 1; 92662306a36Sopenharmony_ci else 92762306a36Sopenharmony_ci buf[0] = 0; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci for (i = 0; i < ports; i++) { 93062306a36Sopenharmony_ci u32 status = isp116x_read_reg32(isp116x, i ? HCRHPORT2 : HCRHPORT1); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC 93362306a36Sopenharmony_ci | RH_PS_OCIC | RH_PS_PRSC)) { 93462306a36Sopenharmony_ci changed = 1; 93562306a36Sopenharmony_ci buf[0] |= 1 << (i + 1); 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci } 93862306a36Sopenharmony_ci spin_unlock_irqrestore(&isp116x->lock, flags); 93962306a36Sopenharmony_ci return changed; 94062306a36Sopenharmony_ci} 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_cistatic void isp116x_hub_descriptor(struct isp116x *isp116x, 94362306a36Sopenharmony_ci struct usb_hub_descriptor *desc) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci u32 reg = isp116x->rhdesca; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci desc->bDescriptorType = USB_DT_HUB; 94862306a36Sopenharmony_ci desc->bDescLength = 9; 94962306a36Sopenharmony_ci desc->bHubContrCurrent = 0; 95062306a36Sopenharmony_ci desc->bNbrPorts = (u8) (reg & 0x3); 95162306a36Sopenharmony_ci /* Power switching, device type, overcurrent. */ 95262306a36Sopenharmony_ci desc->wHubCharacteristics = cpu_to_le16((u16) ((reg >> 8) & 95362306a36Sopenharmony_ci (HUB_CHAR_LPSM | 95462306a36Sopenharmony_ci HUB_CHAR_COMPOUND | 95562306a36Sopenharmony_ci HUB_CHAR_OCPM))); 95662306a36Sopenharmony_ci desc->bPwrOn2PwrGood = (u8) ((reg >> 24) & 0xff); 95762306a36Sopenharmony_ci /* ports removable, and legacy PortPwrCtrlMask */ 95862306a36Sopenharmony_ci desc->u.hs.DeviceRemovable[0] = 0; 95962306a36Sopenharmony_ci desc->u.hs.DeviceRemovable[1] = ~0; 96062306a36Sopenharmony_ci} 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci/* Perform reset of a given port. 96362306a36Sopenharmony_ci It would be great to just start the reset and let the 96462306a36Sopenharmony_ci USB core to clear the reset in due time. However, 96562306a36Sopenharmony_ci root hub ports should be reset for at least 50 ms, while 96662306a36Sopenharmony_ci our chip stays in reset for about 10 ms. I.e., we must 96762306a36Sopenharmony_ci repeatedly reset it ourself here. 96862306a36Sopenharmony_ci*/ 96962306a36Sopenharmony_cistatic inline void root_port_reset(struct isp116x *isp116x, unsigned port) 97062306a36Sopenharmony_ci{ 97162306a36Sopenharmony_ci u32 tmp; 97262306a36Sopenharmony_ci unsigned long flags, t; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci /* Root hub reset should be 50 ms, but some devices 97562306a36Sopenharmony_ci want it even longer. */ 97662306a36Sopenharmony_ci t = jiffies + msecs_to_jiffies(100); 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci while (time_before(jiffies, t)) { 97962306a36Sopenharmony_ci spin_lock_irqsave(&isp116x->lock, flags); 98062306a36Sopenharmony_ci /* spin until any current reset finishes */ 98162306a36Sopenharmony_ci for (;;) { 98262306a36Sopenharmony_ci tmp = isp116x_read_reg32(isp116x, port ? 98362306a36Sopenharmony_ci HCRHPORT2 : HCRHPORT1); 98462306a36Sopenharmony_ci if (!(tmp & RH_PS_PRS)) 98562306a36Sopenharmony_ci break; 98662306a36Sopenharmony_ci udelay(500); 98762306a36Sopenharmony_ci } 98862306a36Sopenharmony_ci /* Don't reset a disconnected port */ 98962306a36Sopenharmony_ci if (!(tmp & RH_PS_CCS)) { 99062306a36Sopenharmony_ci spin_unlock_irqrestore(&isp116x->lock, flags); 99162306a36Sopenharmony_ci break; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci /* Reset lasts 10ms (claims datasheet) */ 99462306a36Sopenharmony_ci isp116x_write_reg32(isp116x, port ? HCRHPORT2 : 99562306a36Sopenharmony_ci HCRHPORT1, (RH_PS_PRS)); 99662306a36Sopenharmony_ci spin_unlock_irqrestore(&isp116x->lock, flags); 99762306a36Sopenharmony_ci msleep(10); 99862306a36Sopenharmony_ci } 99962306a36Sopenharmony_ci} 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci/* Adapted from ohci-hub.c */ 100262306a36Sopenharmony_cistatic int isp116x_hub_control(struct usb_hcd *hcd, 100362306a36Sopenharmony_ci u16 typeReq, 100462306a36Sopenharmony_ci u16 wValue, u16 wIndex, char *buf, u16 wLength) 100562306a36Sopenharmony_ci{ 100662306a36Sopenharmony_ci struct isp116x *isp116x = hcd_to_isp116x(hcd); 100762306a36Sopenharmony_ci int ret = 0; 100862306a36Sopenharmony_ci unsigned long flags; 100962306a36Sopenharmony_ci int ports = isp116x->rhdesca & RH_A_NDP; 101062306a36Sopenharmony_ci u32 tmp = 0; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci switch (typeReq) { 101362306a36Sopenharmony_ci case ClearHubFeature: 101462306a36Sopenharmony_ci DBG("ClearHubFeature: "); 101562306a36Sopenharmony_ci switch (wValue) { 101662306a36Sopenharmony_ci case C_HUB_OVER_CURRENT: 101762306a36Sopenharmony_ci DBG("C_HUB_OVER_CURRENT\n"); 101862306a36Sopenharmony_ci spin_lock_irqsave(&isp116x->lock, flags); 101962306a36Sopenharmony_ci isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_OCIC); 102062306a36Sopenharmony_ci spin_unlock_irqrestore(&isp116x->lock, flags); 102162306a36Sopenharmony_ci fallthrough; 102262306a36Sopenharmony_ci case C_HUB_LOCAL_POWER: 102362306a36Sopenharmony_ci DBG("C_HUB_LOCAL_POWER\n"); 102462306a36Sopenharmony_ci break; 102562306a36Sopenharmony_ci default: 102662306a36Sopenharmony_ci goto error; 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci break; 102962306a36Sopenharmony_ci case SetHubFeature: 103062306a36Sopenharmony_ci DBG("SetHubFeature: "); 103162306a36Sopenharmony_ci switch (wValue) { 103262306a36Sopenharmony_ci case C_HUB_OVER_CURRENT: 103362306a36Sopenharmony_ci case C_HUB_LOCAL_POWER: 103462306a36Sopenharmony_ci DBG("C_HUB_OVER_CURRENT or C_HUB_LOCAL_POWER\n"); 103562306a36Sopenharmony_ci break; 103662306a36Sopenharmony_ci default: 103762306a36Sopenharmony_ci goto error; 103862306a36Sopenharmony_ci } 103962306a36Sopenharmony_ci break; 104062306a36Sopenharmony_ci case GetHubDescriptor: 104162306a36Sopenharmony_ci DBG("GetHubDescriptor\n"); 104262306a36Sopenharmony_ci isp116x_hub_descriptor(isp116x, 104362306a36Sopenharmony_ci (struct usb_hub_descriptor *)buf); 104462306a36Sopenharmony_ci break; 104562306a36Sopenharmony_ci case GetHubStatus: 104662306a36Sopenharmony_ci DBG("GetHubStatus\n"); 104762306a36Sopenharmony_ci *(__le32 *) buf = 0; 104862306a36Sopenharmony_ci break; 104962306a36Sopenharmony_ci case GetPortStatus: 105062306a36Sopenharmony_ci DBG("GetPortStatus\n"); 105162306a36Sopenharmony_ci if (!wIndex || wIndex > ports) 105262306a36Sopenharmony_ci goto error; 105362306a36Sopenharmony_ci spin_lock_irqsave(&isp116x->lock, flags); 105462306a36Sopenharmony_ci tmp = isp116x_read_reg32(isp116x, (--wIndex) ? HCRHPORT2 : HCRHPORT1); 105562306a36Sopenharmony_ci spin_unlock_irqrestore(&isp116x->lock, flags); 105662306a36Sopenharmony_ci *(__le32 *) buf = cpu_to_le32(tmp); 105762306a36Sopenharmony_ci DBG("GetPortStatus: port[%d] %08x\n", wIndex + 1, tmp); 105862306a36Sopenharmony_ci break; 105962306a36Sopenharmony_ci case ClearPortFeature: 106062306a36Sopenharmony_ci DBG("ClearPortFeature: "); 106162306a36Sopenharmony_ci if (!wIndex || wIndex > ports) 106262306a36Sopenharmony_ci goto error; 106362306a36Sopenharmony_ci wIndex--; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci switch (wValue) { 106662306a36Sopenharmony_ci case USB_PORT_FEAT_ENABLE: 106762306a36Sopenharmony_ci DBG("USB_PORT_FEAT_ENABLE\n"); 106862306a36Sopenharmony_ci tmp = RH_PS_CCS; 106962306a36Sopenharmony_ci break; 107062306a36Sopenharmony_ci case USB_PORT_FEAT_C_ENABLE: 107162306a36Sopenharmony_ci DBG("USB_PORT_FEAT_C_ENABLE\n"); 107262306a36Sopenharmony_ci tmp = RH_PS_PESC; 107362306a36Sopenharmony_ci break; 107462306a36Sopenharmony_ci case USB_PORT_FEAT_SUSPEND: 107562306a36Sopenharmony_ci DBG("USB_PORT_FEAT_SUSPEND\n"); 107662306a36Sopenharmony_ci tmp = RH_PS_POCI; 107762306a36Sopenharmony_ci break; 107862306a36Sopenharmony_ci case USB_PORT_FEAT_C_SUSPEND: 107962306a36Sopenharmony_ci DBG("USB_PORT_FEAT_C_SUSPEND\n"); 108062306a36Sopenharmony_ci tmp = RH_PS_PSSC; 108162306a36Sopenharmony_ci break; 108262306a36Sopenharmony_ci case USB_PORT_FEAT_POWER: 108362306a36Sopenharmony_ci DBG("USB_PORT_FEAT_POWER\n"); 108462306a36Sopenharmony_ci tmp = RH_PS_LSDA; 108562306a36Sopenharmony_ci break; 108662306a36Sopenharmony_ci case USB_PORT_FEAT_C_CONNECTION: 108762306a36Sopenharmony_ci DBG("USB_PORT_FEAT_C_CONNECTION\n"); 108862306a36Sopenharmony_ci tmp = RH_PS_CSC; 108962306a36Sopenharmony_ci break; 109062306a36Sopenharmony_ci case USB_PORT_FEAT_C_OVER_CURRENT: 109162306a36Sopenharmony_ci DBG("USB_PORT_FEAT_C_OVER_CURRENT\n"); 109262306a36Sopenharmony_ci tmp = RH_PS_OCIC; 109362306a36Sopenharmony_ci break; 109462306a36Sopenharmony_ci case USB_PORT_FEAT_C_RESET: 109562306a36Sopenharmony_ci DBG("USB_PORT_FEAT_C_RESET\n"); 109662306a36Sopenharmony_ci tmp = RH_PS_PRSC; 109762306a36Sopenharmony_ci break; 109862306a36Sopenharmony_ci default: 109962306a36Sopenharmony_ci goto error; 110062306a36Sopenharmony_ci } 110162306a36Sopenharmony_ci spin_lock_irqsave(&isp116x->lock, flags); 110262306a36Sopenharmony_ci isp116x_write_reg32(isp116x, wIndex 110362306a36Sopenharmony_ci ? HCRHPORT2 : HCRHPORT1, tmp); 110462306a36Sopenharmony_ci spin_unlock_irqrestore(&isp116x->lock, flags); 110562306a36Sopenharmony_ci break; 110662306a36Sopenharmony_ci case SetPortFeature: 110762306a36Sopenharmony_ci DBG("SetPortFeature: "); 110862306a36Sopenharmony_ci if (!wIndex || wIndex > ports) 110962306a36Sopenharmony_ci goto error; 111062306a36Sopenharmony_ci wIndex--; 111162306a36Sopenharmony_ci switch (wValue) { 111262306a36Sopenharmony_ci case USB_PORT_FEAT_SUSPEND: 111362306a36Sopenharmony_ci DBG("USB_PORT_FEAT_SUSPEND\n"); 111462306a36Sopenharmony_ci spin_lock_irqsave(&isp116x->lock, flags); 111562306a36Sopenharmony_ci isp116x_write_reg32(isp116x, wIndex 111662306a36Sopenharmony_ci ? HCRHPORT2 : HCRHPORT1, RH_PS_PSS); 111762306a36Sopenharmony_ci spin_unlock_irqrestore(&isp116x->lock, flags); 111862306a36Sopenharmony_ci break; 111962306a36Sopenharmony_ci case USB_PORT_FEAT_POWER: 112062306a36Sopenharmony_ci DBG("USB_PORT_FEAT_POWER\n"); 112162306a36Sopenharmony_ci spin_lock_irqsave(&isp116x->lock, flags); 112262306a36Sopenharmony_ci isp116x_write_reg32(isp116x, wIndex 112362306a36Sopenharmony_ci ? HCRHPORT2 : HCRHPORT1, RH_PS_PPS); 112462306a36Sopenharmony_ci spin_unlock_irqrestore(&isp116x->lock, flags); 112562306a36Sopenharmony_ci break; 112662306a36Sopenharmony_ci case USB_PORT_FEAT_RESET: 112762306a36Sopenharmony_ci DBG("USB_PORT_FEAT_RESET\n"); 112862306a36Sopenharmony_ci root_port_reset(isp116x, wIndex); 112962306a36Sopenharmony_ci break; 113062306a36Sopenharmony_ci default: 113162306a36Sopenharmony_ci goto error; 113262306a36Sopenharmony_ci } 113362306a36Sopenharmony_ci break; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci default: 113662306a36Sopenharmony_ci error: 113762306a36Sopenharmony_ci /* "protocol stall" on error */ 113862306a36Sopenharmony_ci DBG("PROTOCOL STALL\n"); 113962306a36Sopenharmony_ci ret = -EPIPE; 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci return ret; 114262306a36Sopenharmony_ci} 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci/*-----------------------------------------------------------------*/ 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_cistatic void dump_irq(struct seq_file *s, char *label, u16 mask) 114962306a36Sopenharmony_ci{ 115062306a36Sopenharmony_ci seq_printf(s, "%s %04x%s%s%s%s%s%s\n", label, mask, 115162306a36Sopenharmony_ci mask & HCuPINT_CLKRDY ? " clkrdy" : "", 115262306a36Sopenharmony_ci mask & HCuPINT_SUSP ? " susp" : "", 115362306a36Sopenharmony_ci mask & HCuPINT_OPR ? " opr" : "", 115462306a36Sopenharmony_ci mask & HCuPINT_AIIEOT ? " eot" : "", 115562306a36Sopenharmony_ci mask & HCuPINT_ATL ? " atl" : "", 115662306a36Sopenharmony_ci mask & HCuPINT_SOF ? " sof" : ""); 115762306a36Sopenharmony_ci} 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_cistatic void dump_int(struct seq_file *s, char *label, u32 mask) 116062306a36Sopenharmony_ci{ 116162306a36Sopenharmony_ci seq_printf(s, "%s %08x%s%s%s%s%s%s%s\n", label, mask, 116262306a36Sopenharmony_ci mask & HCINT_MIE ? " MIE" : "", 116362306a36Sopenharmony_ci mask & HCINT_RHSC ? " rhsc" : "", 116462306a36Sopenharmony_ci mask & HCINT_FNO ? " fno" : "", 116562306a36Sopenharmony_ci mask & HCINT_UE ? " ue" : "", 116662306a36Sopenharmony_ci mask & HCINT_RD ? " rd" : "", 116762306a36Sopenharmony_ci mask & HCINT_SF ? " sof" : "", mask & HCINT_SO ? " so" : ""); 116862306a36Sopenharmony_ci} 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_cistatic int isp116x_debug_show(struct seq_file *s, void *unused) 117162306a36Sopenharmony_ci{ 117262306a36Sopenharmony_ci struct isp116x *isp116x = s->private; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci seq_printf(s, "%s\n%s version %s\n", 117562306a36Sopenharmony_ci isp116x_to_hcd(isp116x)->product_desc, hcd_name, 117662306a36Sopenharmony_ci DRIVER_VERSION); 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci if (HC_IS_SUSPENDED(isp116x_to_hcd(isp116x)->state)) { 117962306a36Sopenharmony_ci seq_printf(s, "HCD is suspended\n"); 118062306a36Sopenharmony_ci return 0; 118162306a36Sopenharmony_ci } 118262306a36Sopenharmony_ci if (!HC_IS_RUNNING(isp116x_to_hcd(isp116x)->state)) { 118362306a36Sopenharmony_ci seq_printf(s, "HCD not running\n"); 118462306a36Sopenharmony_ci return 0; 118562306a36Sopenharmony_ci } 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci spin_lock_irq(&isp116x->lock); 118862306a36Sopenharmony_ci dump_irq(s, "hc_irq_enable", isp116x_read_reg16(isp116x, HCuPINTENB)); 118962306a36Sopenharmony_ci dump_irq(s, "hc_irq_status", isp116x_read_reg16(isp116x, HCuPINT)); 119062306a36Sopenharmony_ci dump_int(s, "hc_int_enable", isp116x_read_reg32(isp116x, HCINTENB)); 119162306a36Sopenharmony_ci dump_int(s, "hc_int_status", isp116x_read_reg32(isp116x, HCINTSTAT)); 119262306a36Sopenharmony_ci isp116x_show_regs_seq(isp116x, s); 119362306a36Sopenharmony_ci spin_unlock_irq(&isp116x->lock); 119462306a36Sopenharmony_ci seq_printf(s, "\n"); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci return 0; 119762306a36Sopenharmony_ci} 119862306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(isp116x_debug); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_cistatic void create_debug_file(struct isp116x *isp116x) 120162306a36Sopenharmony_ci{ 120262306a36Sopenharmony_ci debugfs_create_file(hcd_name, S_IRUGO, usb_debug_root, isp116x, 120362306a36Sopenharmony_ci &isp116x_debug_fops); 120462306a36Sopenharmony_ci} 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_cistatic void remove_debug_file(struct isp116x *isp116x) 120762306a36Sopenharmony_ci{ 120862306a36Sopenharmony_ci debugfs_lookup_and_remove(hcd_name, usb_debug_root); 120962306a36Sopenharmony_ci} 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci#else 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_cistatic inline void create_debug_file(struct isp116x *isp116x) { } 121462306a36Sopenharmony_cistatic inline void remove_debug_file(struct isp116x *isp116x) { } 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci#endif /* CONFIG_DEBUG_FS */ 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci/*-----------------------------------------------------------------*/ 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci/* 122162306a36Sopenharmony_ci Software reset - can be called from any contect. 122262306a36Sopenharmony_ci*/ 122362306a36Sopenharmony_cistatic int isp116x_sw_reset(struct isp116x *isp116x) 122462306a36Sopenharmony_ci{ 122562306a36Sopenharmony_ci int retries = 15; 122662306a36Sopenharmony_ci unsigned long flags; 122762306a36Sopenharmony_ci int ret = 0; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci spin_lock_irqsave(&isp116x->lock, flags); 123062306a36Sopenharmony_ci isp116x_write_reg16(isp116x, HCSWRES, HCSWRES_MAGIC); 123162306a36Sopenharmony_ci isp116x_write_reg32(isp116x, HCCMDSTAT, HCCMDSTAT_HCR); 123262306a36Sopenharmony_ci while (--retries) { 123362306a36Sopenharmony_ci /* It usually resets within 1 ms */ 123462306a36Sopenharmony_ci mdelay(1); 123562306a36Sopenharmony_ci if (!(isp116x_read_reg32(isp116x, HCCMDSTAT) & HCCMDSTAT_HCR)) 123662306a36Sopenharmony_ci break; 123762306a36Sopenharmony_ci } 123862306a36Sopenharmony_ci if (!retries) { 123962306a36Sopenharmony_ci ERR("Software reset timeout\n"); 124062306a36Sopenharmony_ci ret = -ETIME; 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci spin_unlock_irqrestore(&isp116x->lock, flags); 124362306a36Sopenharmony_ci return ret; 124462306a36Sopenharmony_ci} 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_cistatic int isp116x_reset(struct usb_hcd *hcd) 124762306a36Sopenharmony_ci{ 124862306a36Sopenharmony_ci struct isp116x *isp116x = hcd_to_isp116x(hcd); 124962306a36Sopenharmony_ci unsigned long t; 125062306a36Sopenharmony_ci u16 clkrdy = 0; 125162306a36Sopenharmony_ci int ret, timeout = 15 /* ms */ ; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci ret = isp116x_sw_reset(isp116x); 125462306a36Sopenharmony_ci if (ret) 125562306a36Sopenharmony_ci return ret; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci t = jiffies + msecs_to_jiffies(timeout); 125862306a36Sopenharmony_ci while (time_before_eq(jiffies, t)) { 125962306a36Sopenharmony_ci msleep(4); 126062306a36Sopenharmony_ci spin_lock_irq(&isp116x->lock); 126162306a36Sopenharmony_ci clkrdy = isp116x_read_reg16(isp116x, HCuPINT) & HCuPINT_CLKRDY; 126262306a36Sopenharmony_ci spin_unlock_irq(&isp116x->lock); 126362306a36Sopenharmony_ci if (clkrdy) 126462306a36Sopenharmony_ci break; 126562306a36Sopenharmony_ci } 126662306a36Sopenharmony_ci if (!clkrdy) { 126762306a36Sopenharmony_ci ERR("Clock not ready after %dms\n", timeout); 126862306a36Sopenharmony_ci /* After sw_reset the clock won't report to be ready, if 126962306a36Sopenharmony_ci H_WAKEUP pin is high. */ 127062306a36Sopenharmony_ci ERR("Please make sure that the H_WAKEUP pin is pulled low!\n"); 127162306a36Sopenharmony_ci ret = -ENODEV; 127262306a36Sopenharmony_ci } 127362306a36Sopenharmony_ci return ret; 127462306a36Sopenharmony_ci} 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_cistatic void isp116x_stop(struct usb_hcd *hcd) 127762306a36Sopenharmony_ci{ 127862306a36Sopenharmony_ci struct isp116x *isp116x = hcd_to_isp116x(hcd); 127962306a36Sopenharmony_ci unsigned long flags; 128062306a36Sopenharmony_ci u32 val; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci spin_lock_irqsave(&isp116x->lock, flags); 128362306a36Sopenharmony_ci isp116x_write_reg16(isp116x, HCuPINTENB, 0); 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci /* Switch off ports' power, some devices don't come up 128662306a36Sopenharmony_ci after next 'insmod' without this */ 128762306a36Sopenharmony_ci val = isp116x_read_reg32(isp116x, HCRHDESCA); 128862306a36Sopenharmony_ci val &= ~(RH_A_NPS | RH_A_PSM); 128962306a36Sopenharmony_ci isp116x_write_reg32(isp116x, HCRHDESCA, val); 129062306a36Sopenharmony_ci isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPS); 129162306a36Sopenharmony_ci spin_unlock_irqrestore(&isp116x->lock, flags); 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci isp116x_sw_reset(isp116x); 129462306a36Sopenharmony_ci} 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci/* 129762306a36Sopenharmony_ci Configure the chip. The chip must be successfully reset by now. 129862306a36Sopenharmony_ci*/ 129962306a36Sopenharmony_cistatic int isp116x_start(struct usb_hcd *hcd) 130062306a36Sopenharmony_ci{ 130162306a36Sopenharmony_ci struct isp116x *isp116x = hcd_to_isp116x(hcd); 130262306a36Sopenharmony_ci struct isp116x_platform_data *board = isp116x->board; 130362306a36Sopenharmony_ci u32 val; 130462306a36Sopenharmony_ci unsigned long flags; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci spin_lock_irqsave(&isp116x->lock, flags); 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci /* clear interrupt status and disable all interrupt sources */ 130962306a36Sopenharmony_ci isp116x_write_reg16(isp116x, HCuPINT, 0xff); 131062306a36Sopenharmony_ci isp116x_write_reg16(isp116x, HCuPINTENB, 0); 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci val = isp116x_read_reg16(isp116x, HCCHIPID); 131362306a36Sopenharmony_ci if ((val & HCCHIPID_MASK) != HCCHIPID_MAGIC) { 131462306a36Sopenharmony_ci ERR("Invalid chip ID %04x\n", val); 131562306a36Sopenharmony_ci spin_unlock_irqrestore(&isp116x->lock, flags); 131662306a36Sopenharmony_ci return -ENODEV; 131762306a36Sopenharmony_ci } 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci /* To be removed in future */ 132062306a36Sopenharmony_ci hcd->uses_new_polling = 1; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci isp116x_write_reg16(isp116x, HCITLBUFLEN, ISP116x_ITL_BUFSIZE); 132362306a36Sopenharmony_ci isp116x_write_reg16(isp116x, HCATLBUFLEN, ISP116x_ATL_BUFSIZE); 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci /* ----- HW conf */ 132662306a36Sopenharmony_ci val = HCHWCFG_INT_ENABLE | HCHWCFG_DBWIDTH(1); 132762306a36Sopenharmony_ci if (board->sel15Kres) 132862306a36Sopenharmony_ci val |= HCHWCFG_15KRSEL; 132962306a36Sopenharmony_ci /* Remote wakeup won't work without working clock */ 133062306a36Sopenharmony_ci if (board->remote_wakeup_enable) 133162306a36Sopenharmony_ci val |= HCHWCFG_CLKNOTSTOP; 133262306a36Sopenharmony_ci if (board->oc_enable) 133362306a36Sopenharmony_ci val |= HCHWCFG_ANALOG_OC; 133462306a36Sopenharmony_ci if (board->int_act_high) 133562306a36Sopenharmony_ci val |= HCHWCFG_INT_POL; 133662306a36Sopenharmony_ci if (board->int_edge_triggered) 133762306a36Sopenharmony_ci val |= HCHWCFG_INT_TRIGGER; 133862306a36Sopenharmony_ci isp116x_write_reg16(isp116x, HCHWCFG, val); 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci /* ----- Root hub conf */ 134162306a36Sopenharmony_ci val = (25 << 24) & RH_A_POTPGT; 134262306a36Sopenharmony_ci /* AN10003_1.pdf recommends RH_A_NPS (no power switching) to 134362306a36Sopenharmony_ci be always set. Yet, instead, we request individual port 134462306a36Sopenharmony_ci power switching. */ 134562306a36Sopenharmony_ci val |= RH_A_PSM; 134662306a36Sopenharmony_ci /* Report overcurrent per port */ 134762306a36Sopenharmony_ci val |= RH_A_OCPM; 134862306a36Sopenharmony_ci isp116x_write_reg32(isp116x, HCRHDESCA, val); 134962306a36Sopenharmony_ci isp116x->rhdesca = isp116x_read_reg32(isp116x, HCRHDESCA); 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci val = RH_B_PPCM; 135262306a36Sopenharmony_ci isp116x_write_reg32(isp116x, HCRHDESCB, val); 135362306a36Sopenharmony_ci isp116x->rhdescb = isp116x_read_reg32(isp116x, HCRHDESCB); 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci val = 0; 135662306a36Sopenharmony_ci if (board->remote_wakeup_enable) { 135762306a36Sopenharmony_ci if (!device_can_wakeup(hcd->self.controller)) 135862306a36Sopenharmony_ci device_init_wakeup(hcd->self.controller, 1); 135962306a36Sopenharmony_ci val |= RH_HS_DRWE; 136062306a36Sopenharmony_ci } 136162306a36Sopenharmony_ci isp116x_write_reg32(isp116x, HCRHSTATUS, val); 136262306a36Sopenharmony_ci isp116x->rhstatus = isp116x_read_reg32(isp116x, HCRHSTATUS); 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci isp116x_write_reg32(isp116x, HCFMINTVL, 0x27782edf); 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci hcd->state = HC_STATE_RUNNING; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci /* Set up interrupts */ 136962306a36Sopenharmony_ci isp116x->intenb = HCINT_MIE | HCINT_RHSC | HCINT_UE; 137062306a36Sopenharmony_ci if (board->remote_wakeup_enable) 137162306a36Sopenharmony_ci isp116x->intenb |= HCINT_RD; 137262306a36Sopenharmony_ci isp116x->irqenb = HCuPINT_ATL | HCuPINT_OPR; /* | HCuPINT_SUSP; */ 137362306a36Sopenharmony_ci isp116x_write_reg32(isp116x, HCINTENB, isp116x->intenb); 137462306a36Sopenharmony_ci isp116x_write_reg16(isp116x, HCuPINTENB, isp116x->irqenb); 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci /* Go operational */ 137762306a36Sopenharmony_ci val = HCCONTROL_USB_OPER; 137862306a36Sopenharmony_ci if (board->remote_wakeup_enable) 137962306a36Sopenharmony_ci val |= HCCONTROL_RWE; 138062306a36Sopenharmony_ci isp116x_write_reg32(isp116x, HCCONTROL, val); 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci /* Disable ports to avoid race in device enumeration */ 138362306a36Sopenharmony_ci isp116x_write_reg32(isp116x, HCRHPORT1, RH_PS_CCS); 138462306a36Sopenharmony_ci isp116x_write_reg32(isp116x, HCRHPORT2, RH_PS_CCS); 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci isp116x_show_regs_log(isp116x); 138762306a36Sopenharmony_ci spin_unlock_irqrestore(&isp116x->lock, flags); 138862306a36Sopenharmony_ci return 0; 138962306a36Sopenharmony_ci} 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci#ifdef CONFIG_PM 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_cistatic int isp116x_bus_suspend(struct usb_hcd *hcd) 139462306a36Sopenharmony_ci{ 139562306a36Sopenharmony_ci struct isp116x *isp116x = hcd_to_isp116x(hcd); 139662306a36Sopenharmony_ci unsigned long flags; 139762306a36Sopenharmony_ci u32 val; 139862306a36Sopenharmony_ci int ret = 0; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci spin_lock_irqsave(&isp116x->lock, flags); 140162306a36Sopenharmony_ci val = isp116x_read_reg32(isp116x, HCCONTROL); 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci switch (val & HCCONTROL_HCFS) { 140462306a36Sopenharmony_ci case HCCONTROL_USB_OPER: 140562306a36Sopenharmony_ci spin_unlock_irqrestore(&isp116x->lock, flags); 140662306a36Sopenharmony_ci val &= (~HCCONTROL_HCFS & ~HCCONTROL_RWE); 140762306a36Sopenharmony_ci val |= HCCONTROL_USB_SUSPEND; 140862306a36Sopenharmony_ci if (hcd->self.root_hub->do_remote_wakeup) 140962306a36Sopenharmony_ci val |= HCCONTROL_RWE; 141062306a36Sopenharmony_ci /* Wait for usb transfers to finish */ 141162306a36Sopenharmony_ci msleep(2); 141262306a36Sopenharmony_ci spin_lock_irqsave(&isp116x->lock, flags); 141362306a36Sopenharmony_ci isp116x_write_reg32(isp116x, HCCONTROL, val); 141462306a36Sopenharmony_ci spin_unlock_irqrestore(&isp116x->lock, flags); 141562306a36Sopenharmony_ci /* Wait for devices to suspend */ 141662306a36Sopenharmony_ci msleep(5); 141762306a36Sopenharmony_ci break; 141862306a36Sopenharmony_ci case HCCONTROL_USB_RESUME: 141962306a36Sopenharmony_ci isp116x_write_reg32(isp116x, HCCONTROL, 142062306a36Sopenharmony_ci (val & ~HCCONTROL_HCFS) | 142162306a36Sopenharmony_ci HCCONTROL_USB_RESET); 142262306a36Sopenharmony_ci fallthrough; 142362306a36Sopenharmony_ci case HCCONTROL_USB_RESET: 142462306a36Sopenharmony_ci ret = -EBUSY; 142562306a36Sopenharmony_ci fallthrough; 142662306a36Sopenharmony_ci default: /* HCCONTROL_USB_SUSPEND */ 142762306a36Sopenharmony_ci spin_unlock_irqrestore(&isp116x->lock, flags); 142862306a36Sopenharmony_ci break; 142962306a36Sopenharmony_ci } 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci return ret; 143262306a36Sopenharmony_ci} 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_cistatic int isp116x_bus_resume(struct usb_hcd *hcd) 143562306a36Sopenharmony_ci{ 143662306a36Sopenharmony_ci struct isp116x *isp116x = hcd_to_isp116x(hcd); 143762306a36Sopenharmony_ci u32 val; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci msleep(5); 144062306a36Sopenharmony_ci spin_lock_irq(&isp116x->lock); 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci val = isp116x_read_reg32(isp116x, HCCONTROL); 144362306a36Sopenharmony_ci switch (val & HCCONTROL_HCFS) { 144462306a36Sopenharmony_ci case HCCONTROL_USB_SUSPEND: 144562306a36Sopenharmony_ci val &= ~HCCONTROL_HCFS; 144662306a36Sopenharmony_ci val |= HCCONTROL_USB_RESUME; 144762306a36Sopenharmony_ci isp116x_write_reg32(isp116x, HCCONTROL, val); 144862306a36Sopenharmony_ci break; 144962306a36Sopenharmony_ci case HCCONTROL_USB_RESUME: 145062306a36Sopenharmony_ci break; 145162306a36Sopenharmony_ci case HCCONTROL_USB_OPER: 145262306a36Sopenharmony_ci spin_unlock_irq(&isp116x->lock); 145362306a36Sopenharmony_ci return 0; 145462306a36Sopenharmony_ci default: 145562306a36Sopenharmony_ci /* HCCONTROL_USB_RESET: this may happen, when during 145662306a36Sopenharmony_ci suspension the HC lost power. Reinitialize completely */ 145762306a36Sopenharmony_ci spin_unlock_irq(&isp116x->lock); 145862306a36Sopenharmony_ci DBG("Chip has been reset while suspended. Reinit from scratch.\n"); 145962306a36Sopenharmony_ci isp116x_reset(hcd); 146062306a36Sopenharmony_ci isp116x_start(hcd); 146162306a36Sopenharmony_ci isp116x_hub_control(hcd, SetPortFeature, 146262306a36Sopenharmony_ci USB_PORT_FEAT_POWER, 1, NULL, 0); 146362306a36Sopenharmony_ci if ((isp116x->rhdesca & RH_A_NDP) == 2) 146462306a36Sopenharmony_ci isp116x_hub_control(hcd, SetPortFeature, 146562306a36Sopenharmony_ci USB_PORT_FEAT_POWER, 2, NULL, 0); 146662306a36Sopenharmony_ci return 0; 146762306a36Sopenharmony_ci } 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci val = isp116x->rhdesca & RH_A_NDP; 147062306a36Sopenharmony_ci while (val--) { 147162306a36Sopenharmony_ci u32 stat = 147262306a36Sopenharmony_ci isp116x_read_reg32(isp116x, val ? HCRHPORT2 : HCRHPORT1); 147362306a36Sopenharmony_ci /* force global, not selective, resume */ 147462306a36Sopenharmony_ci if (!(stat & RH_PS_PSS)) 147562306a36Sopenharmony_ci continue; 147662306a36Sopenharmony_ci DBG("%s: Resuming port %d\n", __func__, val); 147762306a36Sopenharmony_ci isp116x_write_reg32(isp116x, RH_PS_POCI, val 147862306a36Sopenharmony_ci ? HCRHPORT2 : HCRHPORT1); 147962306a36Sopenharmony_ci } 148062306a36Sopenharmony_ci spin_unlock_irq(&isp116x->lock); 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci hcd->state = HC_STATE_RESUMING; 148362306a36Sopenharmony_ci msleep(USB_RESUME_TIMEOUT); 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci /* Go operational */ 148662306a36Sopenharmony_ci spin_lock_irq(&isp116x->lock); 148762306a36Sopenharmony_ci val = isp116x_read_reg32(isp116x, HCCONTROL); 148862306a36Sopenharmony_ci isp116x_write_reg32(isp116x, HCCONTROL, 148962306a36Sopenharmony_ci (val & ~HCCONTROL_HCFS) | HCCONTROL_USB_OPER); 149062306a36Sopenharmony_ci spin_unlock_irq(&isp116x->lock); 149162306a36Sopenharmony_ci hcd->state = HC_STATE_RUNNING; 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci return 0; 149462306a36Sopenharmony_ci} 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci#else 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci#define isp116x_bus_suspend NULL 149962306a36Sopenharmony_ci#define isp116x_bus_resume NULL 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci#endif 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_cistatic const struct hc_driver isp116x_hc_driver = { 150462306a36Sopenharmony_ci .description = hcd_name, 150562306a36Sopenharmony_ci .product_desc = "ISP116x Host Controller", 150662306a36Sopenharmony_ci .hcd_priv_size = sizeof(struct isp116x), 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci .irq = isp116x_irq, 150962306a36Sopenharmony_ci .flags = HCD_USB11, 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci .reset = isp116x_reset, 151262306a36Sopenharmony_ci .start = isp116x_start, 151362306a36Sopenharmony_ci .stop = isp116x_stop, 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci .urb_enqueue = isp116x_urb_enqueue, 151662306a36Sopenharmony_ci .urb_dequeue = isp116x_urb_dequeue, 151762306a36Sopenharmony_ci .endpoint_disable = isp116x_endpoint_disable, 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci .get_frame_number = isp116x_get_frame, 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci .hub_status_data = isp116x_hub_status_data, 152262306a36Sopenharmony_ci .hub_control = isp116x_hub_control, 152362306a36Sopenharmony_ci .bus_suspend = isp116x_bus_suspend, 152462306a36Sopenharmony_ci .bus_resume = isp116x_bus_resume, 152562306a36Sopenharmony_ci}; 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci/*----------------------------------------------------------------*/ 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_cistatic void isp116x_remove(struct platform_device *pdev) 153062306a36Sopenharmony_ci{ 153162306a36Sopenharmony_ci struct usb_hcd *hcd = platform_get_drvdata(pdev); 153262306a36Sopenharmony_ci struct isp116x *isp116x; 153362306a36Sopenharmony_ci struct resource *res; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci if (!hcd) 153662306a36Sopenharmony_ci return; 153762306a36Sopenharmony_ci isp116x = hcd_to_isp116x(hcd); 153862306a36Sopenharmony_ci remove_debug_file(isp116x); 153962306a36Sopenharmony_ci usb_remove_hcd(hcd); 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci iounmap(isp116x->data_reg); 154262306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 154362306a36Sopenharmony_ci if (res) 154462306a36Sopenharmony_ci release_mem_region(res->start, 2); 154562306a36Sopenharmony_ci iounmap(isp116x->addr_reg); 154662306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 154762306a36Sopenharmony_ci if (res) 154862306a36Sopenharmony_ci release_mem_region(res->start, 2); 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci usb_put_hcd(hcd); 155162306a36Sopenharmony_ci} 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_cistatic int isp116x_probe(struct platform_device *pdev) 155462306a36Sopenharmony_ci{ 155562306a36Sopenharmony_ci struct usb_hcd *hcd; 155662306a36Sopenharmony_ci struct isp116x *isp116x; 155762306a36Sopenharmony_ci struct resource *addr, *data, *ires; 155862306a36Sopenharmony_ci void __iomem *addr_reg; 155962306a36Sopenharmony_ci void __iomem *data_reg; 156062306a36Sopenharmony_ci int irq; 156162306a36Sopenharmony_ci int ret = 0; 156262306a36Sopenharmony_ci unsigned long irqflags; 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci if (usb_disabled()) 156562306a36Sopenharmony_ci return -ENODEV; 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci if (pdev->num_resources < 3) { 156862306a36Sopenharmony_ci ret = -ENODEV; 156962306a36Sopenharmony_ci goto err1; 157062306a36Sopenharmony_ci } 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci data = platform_get_resource(pdev, IORESOURCE_MEM, 0); 157362306a36Sopenharmony_ci addr = platform_get_resource(pdev, IORESOURCE_MEM, 1); 157462306a36Sopenharmony_ci ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci if (!addr || !data || !ires) { 157762306a36Sopenharmony_ci ret = -ENODEV; 157862306a36Sopenharmony_ci goto err1; 157962306a36Sopenharmony_ci } 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci irq = ires->start; 158262306a36Sopenharmony_ci irqflags = ires->flags & IRQF_TRIGGER_MASK; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci if (!request_mem_region(addr->start, 2, hcd_name)) { 158562306a36Sopenharmony_ci ret = -EBUSY; 158662306a36Sopenharmony_ci goto err1; 158762306a36Sopenharmony_ci } 158862306a36Sopenharmony_ci addr_reg = ioremap(addr->start, resource_size(addr)); 158962306a36Sopenharmony_ci if (addr_reg == NULL) { 159062306a36Sopenharmony_ci ret = -ENOMEM; 159162306a36Sopenharmony_ci goto err2; 159262306a36Sopenharmony_ci } 159362306a36Sopenharmony_ci if (!request_mem_region(data->start, 2, hcd_name)) { 159462306a36Sopenharmony_ci ret = -EBUSY; 159562306a36Sopenharmony_ci goto err3; 159662306a36Sopenharmony_ci } 159762306a36Sopenharmony_ci data_reg = ioremap(data->start, resource_size(data)); 159862306a36Sopenharmony_ci if (data_reg == NULL) { 159962306a36Sopenharmony_ci ret = -ENOMEM; 160062306a36Sopenharmony_ci goto err4; 160162306a36Sopenharmony_ci } 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci /* allocate and initialize hcd */ 160462306a36Sopenharmony_ci hcd = usb_create_hcd(&isp116x_hc_driver, &pdev->dev, dev_name(&pdev->dev)); 160562306a36Sopenharmony_ci if (!hcd) { 160662306a36Sopenharmony_ci ret = -ENOMEM; 160762306a36Sopenharmony_ci goto err5; 160862306a36Sopenharmony_ci } 160962306a36Sopenharmony_ci /* this rsrc_start is bogus */ 161062306a36Sopenharmony_ci hcd->rsrc_start = addr->start; 161162306a36Sopenharmony_ci isp116x = hcd_to_isp116x(hcd); 161262306a36Sopenharmony_ci isp116x->data_reg = data_reg; 161362306a36Sopenharmony_ci isp116x->addr_reg = addr_reg; 161462306a36Sopenharmony_ci spin_lock_init(&isp116x->lock); 161562306a36Sopenharmony_ci INIT_LIST_HEAD(&isp116x->async); 161662306a36Sopenharmony_ci isp116x->board = dev_get_platdata(&pdev->dev); 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci if (!isp116x->board) { 161962306a36Sopenharmony_ci ERR("Platform data structure not initialized\n"); 162062306a36Sopenharmony_ci ret = -ENODEV; 162162306a36Sopenharmony_ci goto err6; 162262306a36Sopenharmony_ci } 162362306a36Sopenharmony_ci if (isp116x_check_platform_delay(isp116x)) { 162462306a36Sopenharmony_ci ERR("USE_PLATFORM_DELAY defined, but delay function not " 162562306a36Sopenharmony_ci "implemented.\n"); 162662306a36Sopenharmony_ci ERR("See comments in drivers/usb/host/isp116x-hcd.c\n"); 162762306a36Sopenharmony_ci ret = -ENODEV; 162862306a36Sopenharmony_ci goto err6; 162962306a36Sopenharmony_ci } 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci ret = usb_add_hcd(hcd, irq, irqflags); 163262306a36Sopenharmony_ci if (ret) 163362306a36Sopenharmony_ci goto err6; 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci device_wakeup_enable(hcd->self.controller); 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci create_debug_file(isp116x); 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci return 0; 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci err6: 164262306a36Sopenharmony_ci usb_put_hcd(hcd); 164362306a36Sopenharmony_ci err5: 164462306a36Sopenharmony_ci iounmap(data_reg); 164562306a36Sopenharmony_ci err4: 164662306a36Sopenharmony_ci release_mem_region(data->start, 2); 164762306a36Sopenharmony_ci err3: 164862306a36Sopenharmony_ci iounmap(addr_reg); 164962306a36Sopenharmony_ci err2: 165062306a36Sopenharmony_ci release_mem_region(addr->start, 2); 165162306a36Sopenharmony_ci err1: 165262306a36Sopenharmony_ci ERR("init error, %d\n", ret); 165362306a36Sopenharmony_ci return ret; 165462306a36Sopenharmony_ci} 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci#ifdef CONFIG_PM 165762306a36Sopenharmony_ci/* 165862306a36Sopenharmony_ci Suspend of platform device 165962306a36Sopenharmony_ci*/ 166062306a36Sopenharmony_cistatic int isp116x_suspend(struct platform_device *dev, pm_message_t state) 166162306a36Sopenharmony_ci{ 166262306a36Sopenharmony_ci VDBG("%s: state %x\n", __func__, state.event); 166362306a36Sopenharmony_ci return 0; 166462306a36Sopenharmony_ci} 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci/* 166762306a36Sopenharmony_ci Resume platform device 166862306a36Sopenharmony_ci*/ 166962306a36Sopenharmony_cistatic int isp116x_resume(struct platform_device *dev) 167062306a36Sopenharmony_ci{ 167162306a36Sopenharmony_ci VDBG("%s\n", __func__); 167262306a36Sopenharmony_ci return 0; 167362306a36Sopenharmony_ci} 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci#else 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci#define isp116x_suspend NULL 167862306a36Sopenharmony_ci#define isp116x_resume NULL 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci#endif 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci/* work with hotplug and coldplug */ 168362306a36Sopenharmony_ciMODULE_ALIAS("platform:isp116x-hcd"); 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_cistatic struct platform_driver isp116x_driver = { 168662306a36Sopenharmony_ci .probe = isp116x_probe, 168762306a36Sopenharmony_ci .remove_new = isp116x_remove, 168862306a36Sopenharmony_ci .suspend = isp116x_suspend, 168962306a36Sopenharmony_ci .resume = isp116x_resume, 169062306a36Sopenharmony_ci .driver = { 169162306a36Sopenharmony_ci .name = hcd_name, 169262306a36Sopenharmony_ci }, 169362306a36Sopenharmony_ci}; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_cimodule_platform_driver(isp116x_driver); 1696