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