18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for the NXP ISP1760 chip
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * However, the code might contain some bugs. What doesn't work for sure is:
68c2ecf20Sopenharmony_ci * - ISO
78c2ecf20Sopenharmony_ci * - OTG
88c2ecf20Sopenharmony_ci e The interrupt line is configured as active low, level.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * (c) 2007 Sebastian Siewior <bigeasy@linutronix.de>
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * (c) 2011 Arvid Brodin <arvid.brodin@enea.com>
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/kernel.h>
188c2ecf20Sopenharmony_ci#include <linux/slab.h>
198c2ecf20Sopenharmony_ci#include <linux/list.h>
208c2ecf20Sopenharmony_ci#include <linux/usb.h>
218c2ecf20Sopenharmony_ci#include <linux/usb/hcd.h>
228c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
238c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
248c2ecf20Sopenharmony_ci#include <linux/io.h>
258c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
268c2ecf20Sopenharmony_ci#include <linux/mm.h>
278c2ecf20Sopenharmony_ci#include <linux/timer.h>
288c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
298c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include "isp1760-core.h"
328c2ecf20Sopenharmony_ci#include "isp1760-hcd.h"
338c2ecf20Sopenharmony_ci#include "isp1760-regs.h"
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic struct kmem_cache *qtd_cachep;
368c2ecf20Sopenharmony_cistatic struct kmem_cache *qh_cachep;
378c2ecf20Sopenharmony_cistatic struct kmem_cache *urb_listitem_cachep;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_citypedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh,
408c2ecf20Sopenharmony_ci		struct isp1760_qtd *qtd);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic inline struct isp1760_hcd *hcd_to_priv(struct usb_hcd *hcd)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	return *(struct isp1760_hcd **)hcd->hcd_priv;
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* urb state*/
488c2ecf20Sopenharmony_ci#define DELETE_URB		(0x0008)
498c2ecf20Sopenharmony_ci#define NO_TRANSFER_ACTIVE	(0xffffffff)
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/* Philips Proprietary Transfer Descriptor (PTD) */
528c2ecf20Sopenharmony_citypedef __u32 __bitwise __dw;
538c2ecf20Sopenharmony_cistruct ptd {
548c2ecf20Sopenharmony_ci	__dw dw0;
558c2ecf20Sopenharmony_ci	__dw dw1;
568c2ecf20Sopenharmony_ci	__dw dw2;
578c2ecf20Sopenharmony_ci	__dw dw3;
588c2ecf20Sopenharmony_ci	__dw dw4;
598c2ecf20Sopenharmony_ci	__dw dw5;
608c2ecf20Sopenharmony_ci	__dw dw6;
618c2ecf20Sopenharmony_ci	__dw dw7;
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci#define PTD_OFFSET		0x0400
648c2ecf20Sopenharmony_ci#define ISO_PTD_OFFSET		0x0400
658c2ecf20Sopenharmony_ci#define INT_PTD_OFFSET		0x0800
668c2ecf20Sopenharmony_ci#define ATL_PTD_OFFSET		0x0c00
678c2ecf20Sopenharmony_ci#define PAYLOAD_OFFSET		0x1000
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/* ATL */
718c2ecf20Sopenharmony_ci/* DW0 */
728c2ecf20Sopenharmony_ci#define DW0_VALID_BIT			1
738c2ecf20Sopenharmony_ci#define FROM_DW0_VALID(x)		((x) & 0x01)
748c2ecf20Sopenharmony_ci#define TO_DW0_LENGTH(x)		(((u32) x) << 3)
758c2ecf20Sopenharmony_ci#define TO_DW0_MAXPACKET(x)		(((u32) x) << 18)
768c2ecf20Sopenharmony_ci#define TO_DW0_MULTI(x)			(((u32) x) << 29)
778c2ecf20Sopenharmony_ci#define TO_DW0_ENDPOINT(x)		(((u32)	x) << 31)
788c2ecf20Sopenharmony_ci/* DW1 */
798c2ecf20Sopenharmony_ci#define TO_DW1_DEVICE_ADDR(x)		(((u32) x) << 3)
808c2ecf20Sopenharmony_ci#define TO_DW1_PID_TOKEN(x)		(((u32) x) << 10)
818c2ecf20Sopenharmony_ci#define DW1_TRANS_BULK			((u32) 2 << 12)
828c2ecf20Sopenharmony_ci#define DW1_TRANS_INT			((u32) 3 << 12)
838c2ecf20Sopenharmony_ci#define DW1_TRANS_SPLIT			((u32) 1 << 14)
848c2ecf20Sopenharmony_ci#define DW1_SE_USB_LOSPEED		((u32) 2 << 16)
858c2ecf20Sopenharmony_ci#define TO_DW1_PORT_NUM(x)		(((u32) x) << 18)
868c2ecf20Sopenharmony_ci#define TO_DW1_HUB_NUM(x)		(((u32) x) << 25)
878c2ecf20Sopenharmony_ci/* DW2 */
888c2ecf20Sopenharmony_ci#define TO_DW2_DATA_START_ADDR(x)	(((u32) x) << 8)
898c2ecf20Sopenharmony_ci#define TO_DW2_RL(x)			((x) << 25)
908c2ecf20Sopenharmony_ci#define FROM_DW2_RL(x)			(((x) >> 25) & 0xf)
918c2ecf20Sopenharmony_ci/* DW3 */
928c2ecf20Sopenharmony_ci#define FROM_DW3_NRBYTESTRANSFERRED(x)		((x) & 0x7fff)
938c2ecf20Sopenharmony_ci#define FROM_DW3_SCS_NRBYTESTRANSFERRED(x)	((x) & 0x07ff)
948c2ecf20Sopenharmony_ci#define TO_DW3_NAKCOUNT(x)		((x) << 19)
958c2ecf20Sopenharmony_ci#define FROM_DW3_NAKCOUNT(x)		(((x) >> 19) & 0xf)
968c2ecf20Sopenharmony_ci#define TO_DW3_CERR(x)			((x) << 23)
978c2ecf20Sopenharmony_ci#define FROM_DW3_CERR(x)		(((x) >> 23) & 0x3)
988c2ecf20Sopenharmony_ci#define TO_DW3_DATA_TOGGLE(x)		((x) << 25)
998c2ecf20Sopenharmony_ci#define FROM_DW3_DATA_TOGGLE(x)		(((x) >> 25) & 0x1)
1008c2ecf20Sopenharmony_ci#define TO_DW3_PING(x)			((x) << 26)
1018c2ecf20Sopenharmony_ci#define FROM_DW3_PING(x)		(((x) >> 26) & 0x1)
1028c2ecf20Sopenharmony_ci#define DW3_ERROR_BIT			(1 << 28)
1038c2ecf20Sopenharmony_ci#define DW3_BABBLE_BIT			(1 << 29)
1048c2ecf20Sopenharmony_ci#define DW3_HALT_BIT			(1 << 30)
1058c2ecf20Sopenharmony_ci#define DW3_ACTIVE_BIT			(1 << 31)
1068c2ecf20Sopenharmony_ci#define FROM_DW3_ACTIVE(x)		(((x) >> 31) & 0x01)
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci#define INT_UNDERRUN			(1 << 2)
1098c2ecf20Sopenharmony_ci#define INT_BABBLE			(1 << 1)
1108c2ecf20Sopenharmony_ci#define INT_EXACT			(1 << 0)
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci#define SETUP_PID	(2)
1138c2ecf20Sopenharmony_ci#define IN_PID		(1)
1148c2ecf20Sopenharmony_ci#define OUT_PID		(0)
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/* Errata 1 */
1178c2ecf20Sopenharmony_ci#define RL_COUNTER	(0)
1188c2ecf20Sopenharmony_ci#define NAK_COUNTER	(0)
1198c2ecf20Sopenharmony_ci#define ERR_COUNTER	(2)
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistruct isp1760_qtd {
1228c2ecf20Sopenharmony_ci	u8 packet_type;
1238c2ecf20Sopenharmony_ci	void *data_buffer;
1248c2ecf20Sopenharmony_ci	u32 payload_addr;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	/* the rest is HCD-private */
1278c2ecf20Sopenharmony_ci	struct list_head qtd_list;
1288c2ecf20Sopenharmony_ci	struct urb *urb;
1298c2ecf20Sopenharmony_ci	size_t length;
1308c2ecf20Sopenharmony_ci	size_t actual_length;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	/* QTD_ENQUEUED:	waiting for transfer (inactive) */
1338c2ecf20Sopenharmony_ci	/* QTD_PAYLOAD_ALLOC:	chip mem has been allocated for payload */
1348c2ecf20Sopenharmony_ci	/* QTD_XFER_STARTED:	valid ptd has been written to isp176x - only
1358c2ecf20Sopenharmony_ci				interrupt handler may touch this qtd! */
1368c2ecf20Sopenharmony_ci	/* QTD_XFER_COMPLETE:	payload has been transferred successfully */
1378c2ecf20Sopenharmony_ci	/* QTD_RETIRE:		transfer error/abort qtd */
1388c2ecf20Sopenharmony_ci#define QTD_ENQUEUED		0
1398c2ecf20Sopenharmony_ci#define QTD_PAYLOAD_ALLOC	1
1408c2ecf20Sopenharmony_ci#define QTD_XFER_STARTED	2
1418c2ecf20Sopenharmony_ci#define QTD_XFER_COMPLETE	3
1428c2ecf20Sopenharmony_ci#define QTD_RETIRE		4
1438c2ecf20Sopenharmony_ci	u32 status;
1448c2ecf20Sopenharmony_ci};
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci/* Queue head, one for each active endpoint */
1478c2ecf20Sopenharmony_cistruct isp1760_qh {
1488c2ecf20Sopenharmony_ci	struct list_head qh_list;
1498c2ecf20Sopenharmony_ci	struct list_head qtd_list;
1508c2ecf20Sopenharmony_ci	u32 toggle;
1518c2ecf20Sopenharmony_ci	u32 ping;
1528c2ecf20Sopenharmony_ci	int slot;
1538c2ecf20Sopenharmony_ci	int tt_buffer_dirty;	/* See USB2.0 spec section 11.17.5 */
1548c2ecf20Sopenharmony_ci};
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistruct urb_listitem {
1578c2ecf20Sopenharmony_ci	struct list_head urb_list;
1588c2ecf20Sopenharmony_ci	struct urb *urb;
1598c2ecf20Sopenharmony_ci};
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci/*
1628c2ecf20Sopenharmony_ci * Access functions for isp176x registers (addresses 0..0x03FF).
1638c2ecf20Sopenharmony_ci */
1648c2ecf20Sopenharmony_cistatic u32 reg_read32(void __iomem *base, u32 reg)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	return isp1760_read32(base, reg);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic void reg_write32(void __iomem *base, u32 reg, u32 val)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	isp1760_write32(base, reg, val);
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci/*
1758c2ecf20Sopenharmony_ci * Access functions for isp176x memory (offset >= 0x0400).
1768c2ecf20Sopenharmony_ci *
1778c2ecf20Sopenharmony_ci * bank_reads8() reads memory locations prefetched by an earlier write to
1788c2ecf20Sopenharmony_ci * HC_MEMORY_REG (see isp176x datasheet). Unless you want to do fancy multi-
1798c2ecf20Sopenharmony_ci * bank optimizations, you should use the more generic mem_reads8() below.
1808c2ecf20Sopenharmony_ci *
1818c2ecf20Sopenharmony_ci * For access to ptd memory, use the specialized ptd_read() and ptd_write()
1828c2ecf20Sopenharmony_ci * below.
1838c2ecf20Sopenharmony_ci *
1848c2ecf20Sopenharmony_ci * These functions copy via MMIO data to/from the device. memcpy_{to|from}io()
1858c2ecf20Sopenharmony_ci * doesn't quite work because some people have to enforce 32-bit access
1868c2ecf20Sopenharmony_ci */
1878c2ecf20Sopenharmony_cistatic void bank_reads8(void __iomem *src_base, u32 src_offset, u32 bank_addr,
1888c2ecf20Sopenharmony_ci							__u32 *dst, u32 bytes)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	__u32 __iomem *src;
1918c2ecf20Sopenharmony_ci	u32 val;
1928c2ecf20Sopenharmony_ci	__u8 *src_byteptr;
1938c2ecf20Sopenharmony_ci	__u8 *dst_byteptr;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	src = src_base + (bank_addr | src_offset);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if (src_offset < PAYLOAD_OFFSET) {
1988c2ecf20Sopenharmony_ci		while (bytes >= 4) {
1998c2ecf20Sopenharmony_ci			*dst = le32_to_cpu(__raw_readl(src));
2008c2ecf20Sopenharmony_ci			bytes -= 4;
2018c2ecf20Sopenharmony_ci			src++;
2028c2ecf20Sopenharmony_ci			dst++;
2038c2ecf20Sopenharmony_ci		}
2048c2ecf20Sopenharmony_ci	} else {
2058c2ecf20Sopenharmony_ci		while (bytes >= 4) {
2068c2ecf20Sopenharmony_ci			*dst = __raw_readl(src);
2078c2ecf20Sopenharmony_ci			bytes -= 4;
2088c2ecf20Sopenharmony_ci			src++;
2098c2ecf20Sopenharmony_ci			dst++;
2108c2ecf20Sopenharmony_ci		}
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	if (!bytes)
2148c2ecf20Sopenharmony_ci		return;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	/* in case we have 3, 2 or 1 by left. The dst buffer may not be fully
2178c2ecf20Sopenharmony_ci	 * allocated.
2188c2ecf20Sopenharmony_ci	 */
2198c2ecf20Sopenharmony_ci	if (src_offset < PAYLOAD_OFFSET)
2208c2ecf20Sopenharmony_ci		val = le32_to_cpu(__raw_readl(src));
2218c2ecf20Sopenharmony_ci	else
2228c2ecf20Sopenharmony_ci		val = __raw_readl(src);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	dst_byteptr = (void *) dst;
2258c2ecf20Sopenharmony_ci	src_byteptr = (void *) &val;
2268c2ecf20Sopenharmony_ci	while (bytes > 0) {
2278c2ecf20Sopenharmony_ci		*dst_byteptr = *src_byteptr;
2288c2ecf20Sopenharmony_ci		dst_byteptr++;
2298c2ecf20Sopenharmony_ci		src_byteptr++;
2308c2ecf20Sopenharmony_ci		bytes--;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic void mem_reads8(void __iomem *src_base, u32 src_offset, void *dst,
2358c2ecf20Sopenharmony_ci								u32 bytes)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	reg_write32(src_base, HC_MEMORY_REG, src_offset + ISP_BANK(0));
2388c2ecf20Sopenharmony_ci	ndelay(90);
2398c2ecf20Sopenharmony_ci	bank_reads8(src_base, src_offset, ISP_BANK(0), dst, bytes);
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic void mem_writes8(void __iomem *dst_base, u32 dst_offset,
2438c2ecf20Sopenharmony_ci						__u32 const *src, u32 bytes)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	__u32 __iomem *dst;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	dst = dst_base + dst_offset;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	if (dst_offset < PAYLOAD_OFFSET) {
2508c2ecf20Sopenharmony_ci		while (bytes >= 4) {
2518c2ecf20Sopenharmony_ci			__raw_writel(cpu_to_le32(*src), dst);
2528c2ecf20Sopenharmony_ci			bytes -= 4;
2538c2ecf20Sopenharmony_ci			src++;
2548c2ecf20Sopenharmony_ci			dst++;
2558c2ecf20Sopenharmony_ci		}
2568c2ecf20Sopenharmony_ci	} else {
2578c2ecf20Sopenharmony_ci		while (bytes >= 4) {
2588c2ecf20Sopenharmony_ci			__raw_writel(*src, dst);
2598c2ecf20Sopenharmony_ci			bytes -= 4;
2608c2ecf20Sopenharmony_ci			src++;
2618c2ecf20Sopenharmony_ci			dst++;
2628c2ecf20Sopenharmony_ci		}
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	if (!bytes)
2668c2ecf20Sopenharmony_ci		return;
2678c2ecf20Sopenharmony_ci	/* in case we have 3, 2 or 1 bytes left. The buffer is allocated and the
2688c2ecf20Sopenharmony_ci	 * extra bytes should not be read by the HW.
2698c2ecf20Sopenharmony_ci	 */
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	if (dst_offset < PAYLOAD_OFFSET)
2728c2ecf20Sopenharmony_ci		__raw_writel(cpu_to_le32(*src), dst);
2738c2ecf20Sopenharmony_ci	else
2748c2ecf20Sopenharmony_ci		__raw_writel(*src, dst);
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci/*
2788c2ecf20Sopenharmony_ci * Read and write ptds. 'ptd_offset' should be one of ISO_PTD_OFFSET,
2798c2ecf20Sopenharmony_ci * INT_PTD_OFFSET, and ATL_PTD_OFFSET. 'slot' should be less than 32.
2808c2ecf20Sopenharmony_ci */
2818c2ecf20Sopenharmony_cistatic void ptd_read(void __iomem *base, u32 ptd_offset, u32 slot,
2828c2ecf20Sopenharmony_ci								struct ptd *ptd)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	reg_write32(base, HC_MEMORY_REG,
2858c2ecf20Sopenharmony_ci				ISP_BANK(0) + ptd_offset + slot*sizeof(*ptd));
2868c2ecf20Sopenharmony_ci	ndelay(90);
2878c2ecf20Sopenharmony_ci	bank_reads8(base, ptd_offset + slot*sizeof(*ptd), ISP_BANK(0),
2888c2ecf20Sopenharmony_ci						(void *) ptd, sizeof(*ptd));
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic void ptd_write(void __iomem *base, u32 ptd_offset, u32 slot,
2928c2ecf20Sopenharmony_ci								struct ptd *ptd)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	mem_writes8(base, ptd_offset + slot*sizeof(*ptd) + sizeof(ptd->dw0),
2958c2ecf20Sopenharmony_ci						&ptd->dw1, 7*sizeof(ptd->dw1));
2968c2ecf20Sopenharmony_ci	/* Make sure dw0 gets written last (after other dw's and after payload)
2978c2ecf20Sopenharmony_ci	   since it contains the enable bit */
2988c2ecf20Sopenharmony_ci	wmb();
2998c2ecf20Sopenharmony_ci	mem_writes8(base, ptd_offset + slot*sizeof(*ptd), &ptd->dw0,
3008c2ecf20Sopenharmony_ci							sizeof(ptd->dw0));
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci/* memory management of the 60kb on the chip from 0x1000 to 0xffff */
3058c2ecf20Sopenharmony_cistatic void init_memory(struct isp1760_hcd *priv)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	int i, curr;
3088c2ecf20Sopenharmony_ci	u32 payload_addr;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	payload_addr = PAYLOAD_OFFSET;
3118c2ecf20Sopenharmony_ci	for (i = 0; i < BLOCK_1_NUM; i++) {
3128c2ecf20Sopenharmony_ci		priv->memory_pool[i].start = payload_addr;
3138c2ecf20Sopenharmony_ci		priv->memory_pool[i].size = BLOCK_1_SIZE;
3148c2ecf20Sopenharmony_ci		priv->memory_pool[i].free = 1;
3158c2ecf20Sopenharmony_ci		payload_addr += priv->memory_pool[i].size;
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	curr = i;
3198c2ecf20Sopenharmony_ci	for (i = 0; i < BLOCK_2_NUM; i++) {
3208c2ecf20Sopenharmony_ci		priv->memory_pool[curr + i].start = payload_addr;
3218c2ecf20Sopenharmony_ci		priv->memory_pool[curr + i].size = BLOCK_2_SIZE;
3228c2ecf20Sopenharmony_ci		priv->memory_pool[curr + i].free = 1;
3238c2ecf20Sopenharmony_ci		payload_addr += priv->memory_pool[curr + i].size;
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	curr = i;
3278c2ecf20Sopenharmony_ci	for (i = 0; i < BLOCK_3_NUM; i++) {
3288c2ecf20Sopenharmony_ci		priv->memory_pool[curr + i].start = payload_addr;
3298c2ecf20Sopenharmony_ci		priv->memory_pool[curr + i].size = BLOCK_3_SIZE;
3308c2ecf20Sopenharmony_ci		priv->memory_pool[curr + i].free = 1;
3318c2ecf20Sopenharmony_ci		payload_addr += priv->memory_pool[curr + i].size;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	WARN_ON(payload_addr - priv->memory_pool[0].start > PAYLOAD_AREA_SIZE);
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic void alloc_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	struct isp1760_hcd *priv = hcd_to_priv(hcd);
3408c2ecf20Sopenharmony_ci	int i;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	WARN_ON(qtd->payload_addr);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	if (!qtd->length)
3458c2ecf20Sopenharmony_ci		return;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	for (i = 0; i < BLOCKS; i++) {
3488c2ecf20Sopenharmony_ci		if (priv->memory_pool[i].size >= qtd->length &&
3498c2ecf20Sopenharmony_ci				priv->memory_pool[i].free) {
3508c2ecf20Sopenharmony_ci			priv->memory_pool[i].free = 0;
3518c2ecf20Sopenharmony_ci			qtd->payload_addr = priv->memory_pool[i].start;
3528c2ecf20Sopenharmony_ci			return;
3538c2ecf20Sopenharmony_ci		}
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic void free_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	struct isp1760_hcd *priv = hcd_to_priv(hcd);
3608c2ecf20Sopenharmony_ci	int i;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	if (!qtd->payload_addr)
3638c2ecf20Sopenharmony_ci		return;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	for (i = 0; i < BLOCKS; i++) {
3668c2ecf20Sopenharmony_ci		if (priv->memory_pool[i].start == qtd->payload_addr) {
3678c2ecf20Sopenharmony_ci			WARN_ON(priv->memory_pool[i].free);
3688c2ecf20Sopenharmony_ci			priv->memory_pool[i].free = 1;
3698c2ecf20Sopenharmony_ci			qtd->payload_addr = 0;
3708c2ecf20Sopenharmony_ci			return;
3718c2ecf20Sopenharmony_ci		}
3728c2ecf20Sopenharmony_ci	}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	dev_err(hcd->self.controller, "%s: Invalid pointer: %08x\n",
3758c2ecf20Sopenharmony_ci						__func__, qtd->payload_addr);
3768c2ecf20Sopenharmony_ci	WARN_ON(1);
3778c2ecf20Sopenharmony_ci	qtd->payload_addr = 0;
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic int handshake(struct usb_hcd *hcd, u32 reg,
3818c2ecf20Sopenharmony_ci		      u32 mask, u32 done, int usec)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	u32 result;
3848c2ecf20Sopenharmony_ci	int ret;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	ret = readl_poll_timeout_atomic(hcd->regs + reg, result,
3878c2ecf20Sopenharmony_ci					((result & mask) == done ||
3888c2ecf20Sopenharmony_ci					 result == U32_MAX), 1, usec);
3898c2ecf20Sopenharmony_ci	if (result == U32_MAX)
3908c2ecf20Sopenharmony_ci		return -ENODEV;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	return ret;
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci/* reset a non-running (STS_HALT == 1) controller */
3968c2ecf20Sopenharmony_cistatic int ehci_reset(struct usb_hcd *hcd)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	struct isp1760_hcd *priv = hcd_to_priv(hcd);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	u32 command = reg_read32(hcd->regs, HC_USBCMD);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	command |= CMD_RESET;
4038c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_USBCMD, command);
4048c2ecf20Sopenharmony_ci	hcd->state = HC_STATE_HALT;
4058c2ecf20Sopenharmony_ci	priv->next_statechange = jiffies;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	return handshake(hcd, HC_USBCMD, CMD_RESET, 0, 250 * 1000);
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_cistatic struct isp1760_qh *qh_alloc(gfp_t flags)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	struct isp1760_qh *qh;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	qh = kmem_cache_zalloc(qh_cachep, flags);
4158c2ecf20Sopenharmony_ci	if (!qh)
4168c2ecf20Sopenharmony_ci		return NULL;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&qh->qh_list);
4198c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&qh->qtd_list);
4208c2ecf20Sopenharmony_ci	qh->slot = -1;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	return qh;
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic void qh_free(struct isp1760_qh *qh)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	WARN_ON(!list_empty(&qh->qtd_list));
4288c2ecf20Sopenharmony_ci	WARN_ON(qh->slot > -1);
4298c2ecf20Sopenharmony_ci	kmem_cache_free(qh_cachep, qh);
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci/* one-time init, only for memory state */
4338c2ecf20Sopenharmony_cistatic int priv_init(struct usb_hcd *hcd)
4348c2ecf20Sopenharmony_ci{
4358c2ecf20Sopenharmony_ci	struct isp1760_hcd		*priv = hcd_to_priv(hcd);
4368c2ecf20Sopenharmony_ci	u32			hcc_params;
4378c2ecf20Sopenharmony_ci	int i;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	spin_lock_init(&priv->lock);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	for (i = 0; i < QH_END; i++)
4428c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&priv->qh_list[i]);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	/*
4458c2ecf20Sopenharmony_ci	 * hw default: 1K periodic list heads, one per frame.
4468c2ecf20Sopenharmony_ci	 * periodic_size can shrink by USBCMD update if hcc_params allows.
4478c2ecf20Sopenharmony_ci	 */
4488c2ecf20Sopenharmony_ci	priv->periodic_size = DEFAULT_I_TDPS;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	/* controllers may cache some of the periodic schedule ... */
4518c2ecf20Sopenharmony_ci	hcc_params = reg_read32(hcd->regs, HC_HCCPARAMS);
4528c2ecf20Sopenharmony_ci	/* full frame cache */
4538c2ecf20Sopenharmony_ci	if (HCC_ISOC_CACHE(hcc_params))
4548c2ecf20Sopenharmony_ci		priv->i_thresh = 8;
4558c2ecf20Sopenharmony_ci	else /* N microframes cached */
4568c2ecf20Sopenharmony_ci		priv->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	return 0;
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic int isp1760_hc_setup(struct usb_hcd *hcd)
4628c2ecf20Sopenharmony_ci{
4638c2ecf20Sopenharmony_ci	struct isp1760_hcd *priv = hcd_to_priv(hcd);
4648c2ecf20Sopenharmony_ci	int result;
4658c2ecf20Sopenharmony_ci	u32 scratch, hwmode;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_SCRATCH_REG, 0xdeadbabe);
4688c2ecf20Sopenharmony_ci	/* Change bus pattern */
4698c2ecf20Sopenharmony_ci	scratch = reg_read32(hcd->regs, HC_CHIP_ID_REG);
4708c2ecf20Sopenharmony_ci	scratch = reg_read32(hcd->regs, HC_SCRATCH_REG);
4718c2ecf20Sopenharmony_ci	if (scratch != 0xdeadbabe) {
4728c2ecf20Sopenharmony_ci		dev_err(hcd->self.controller, "Scratch test failed.\n");
4738c2ecf20Sopenharmony_ci		return -ENODEV;
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	/*
4778c2ecf20Sopenharmony_ci	 * The RESET_HC bit in the SW_RESET register is supposed to reset the
4788c2ecf20Sopenharmony_ci	 * host controller without touching the CPU interface registers, but at
4798c2ecf20Sopenharmony_ci	 * least on the ISP1761 it seems to behave as the RESET_ALL bit and
4808c2ecf20Sopenharmony_ci	 * reset the whole device. We thus can't use it here, so let's reset
4818c2ecf20Sopenharmony_ci	 * the host controller through the EHCI USB Command register. The device
4828c2ecf20Sopenharmony_ci	 * has been reset in core code anyway, so this shouldn't matter.
4838c2ecf20Sopenharmony_ci	 */
4848c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, 0);
4858c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE);
4868c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE);
4878c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	result = ehci_reset(hcd);
4908c2ecf20Sopenharmony_ci	if (result)
4918c2ecf20Sopenharmony_ci		return result;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	/* Step 11 passed */
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	/* ATL reset */
4968c2ecf20Sopenharmony_ci	hwmode = reg_read32(hcd->regs, HC_HW_MODE_CTRL) & ~ALL_ATX_RESET;
4978c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode | ALL_ATX_RESET);
4988c2ecf20Sopenharmony_ci	mdelay(10);
4998c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, INTERRUPT_ENABLE_MASK);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	priv->hcs_params = reg_read32(hcd->regs, HC_HCSPARAMS);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	return priv_init(hcd);
5068c2ecf20Sopenharmony_ci}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_cistatic u32 base_to_chip(u32 base)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	return ((base - 0x400) >> 3);
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_cistatic int last_qtd_of_urb(struct isp1760_qtd *qtd, struct isp1760_qh *qh)
5148c2ecf20Sopenharmony_ci{
5158c2ecf20Sopenharmony_ci	struct urb *urb;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	if (list_is_last(&qtd->qtd_list, &qh->qtd_list))
5188c2ecf20Sopenharmony_ci		return 1;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	urb = qtd->urb;
5218c2ecf20Sopenharmony_ci	qtd = list_entry(qtd->qtd_list.next, typeof(*qtd), qtd_list);
5228c2ecf20Sopenharmony_ci	return (qtd->urb != urb);
5238c2ecf20Sopenharmony_ci}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci/* magic numbers that can affect system performance */
5268c2ecf20Sopenharmony_ci#define	EHCI_TUNE_CERR		3	/* 0-3 qtd retries; 0 == don't stop */
5278c2ecf20Sopenharmony_ci#define	EHCI_TUNE_RL_HS		4	/* nak throttle; see 4.9 */
5288c2ecf20Sopenharmony_ci#define	EHCI_TUNE_RL_TT		0
5298c2ecf20Sopenharmony_ci#define	EHCI_TUNE_MULT_HS	1	/* 1-3 transactions/uframe; 4.10.3 */
5308c2ecf20Sopenharmony_ci#define	EHCI_TUNE_MULT_TT	1
5318c2ecf20Sopenharmony_ci#define	EHCI_TUNE_FLS		2	/* (small) 256 frame schedule */
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_cistatic void create_ptd_atl(struct isp1760_qh *qh,
5348c2ecf20Sopenharmony_ci			struct isp1760_qtd *qtd, struct ptd *ptd)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	u32 maxpacket;
5378c2ecf20Sopenharmony_ci	u32 multi;
5388c2ecf20Sopenharmony_ci	u32 rl = RL_COUNTER;
5398c2ecf20Sopenharmony_ci	u32 nak = NAK_COUNTER;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	memset(ptd, 0, sizeof(*ptd));
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	/* according to 3.6.2, max packet len can not be > 0x400 */
5448c2ecf20Sopenharmony_ci	maxpacket = usb_maxpacket(qtd->urb->dev, qtd->urb->pipe,
5458c2ecf20Sopenharmony_ci						usb_pipeout(qtd->urb->pipe));
5468c2ecf20Sopenharmony_ci	multi =  1 + ((maxpacket >> 11) & 0x3);
5478c2ecf20Sopenharmony_ci	maxpacket &= 0x7ff;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	/* DW0 */
5508c2ecf20Sopenharmony_ci	ptd->dw0 = DW0_VALID_BIT;
5518c2ecf20Sopenharmony_ci	ptd->dw0 |= TO_DW0_LENGTH(qtd->length);
5528c2ecf20Sopenharmony_ci	ptd->dw0 |= TO_DW0_MAXPACKET(maxpacket);
5538c2ecf20Sopenharmony_ci	ptd->dw0 |= TO_DW0_ENDPOINT(usb_pipeendpoint(qtd->urb->pipe));
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	/* DW1 */
5568c2ecf20Sopenharmony_ci	ptd->dw1 = usb_pipeendpoint(qtd->urb->pipe) >> 1;
5578c2ecf20Sopenharmony_ci	ptd->dw1 |= TO_DW1_DEVICE_ADDR(usb_pipedevice(qtd->urb->pipe));
5588c2ecf20Sopenharmony_ci	ptd->dw1 |= TO_DW1_PID_TOKEN(qtd->packet_type);
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	if (usb_pipebulk(qtd->urb->pipe))
5618c2ecf20Sopenharmony_ci		ptd->dw1 |= DW1_TRANS_BULK;
5628c2ecf20Sopenharmony_ci	else if  (usb_pipeint(qtd->urb->pipe))
5638c2ecf20Sopenharmony_ci		ptd->dw1 |= DW1_TRANS_INT;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	if (qtd->urb->dev->speed != USB_SPEED_HIGH) {
5668c2ecf20Sopenharmony_ci		/* split transaction */
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci		ptd->dw1 |= DW1_TRANS_SPLIT;
5698c2ecf20Sopenharmony_ci		if (qtd->urb->dev->speed == USB_SPEED_LOW)
5708c2ecf20Sopenharmony_ci			ptd->dw1 |= DW1_SE_USB_LOSPEED;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci		ptd->dw1 |= TO_DW1_PORT_NUM(qtd->urb->dev->ttport);
5738c2ecf20Sopenharmony_ci		ptd->dw1 |= TO_DW1_HUB_NUM(qtd->urb->dev->tt->hub->devnum);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci		/* SE bit for Split INT transfers */
5768c2ecf20Sopenharmony_ci		if (usb_pipeint(qtd->urb->pipe) &&
5778c2ecf20Sopenharmony_ci				(qtd->urb->dev->speed == USB_SPEED_LOW))
5788c2ecf20Sopenharmony_ci			ptd->dw1 |= 2 << 16;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci		rl = 0;
5818c2ecf20Sopenharmony_ci		nak = 0;
5828c2ecf20Sopenharmony_ci	} else {
5838c2ecf20Sopenharmony_ci		ptd->dw0 |= TO_DW0_MULTI(multi);
5848c2ecf20Sopenharmony_ci		if (usb_pipecontrol(qtd->urb->pipe) ||
5858c2ecf20Sopenharmony_ci						usb_pipebulk(qtd->urb->pipe))
5868c2ecf20Sopenharmony_ci			ptd->dw3 |= TO_DW3_PING(qh->ping);
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci	/* DW2 */
5898c2ecf20Sopenharmony_ci	ptd->dw2 = 0;
5908c2ecf20Sopenharmony_ci	ptd->dw2 |= TO_DW2_DATA_START_ADDR(base_to_chip(qtd->payload_addr));
5918c2ecf20Sopenharmony_ci	ptd->dw2 |= TO_DW2_RL(rl);
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	/* DW3 */
5948c2ecf20Sopenharmony_ci	ptd->dw3 |= TO_DW3_NAKCOUNT(nak);
5958c2ecf20Sopenharmony_ci	ptd->dw3 |= TO_DW3_DATA_TOGGLE(qh->toggle);
5968c2ecf20Sopenharmony_ci	if (usb_pipecontrol(qtd->urb->pipe)) {
5978c2ecf20Sopenharmony_ci		if (qtd->data_buffer == qtd->urb->setup_packet)
5988c2ecf20Sopenharmony_ci			ptd->dw3 &= ~TO_DW3_DATA_TOGGLE(1);
5998c2ecf20Sopenharmony_ci		else if (last_qtd_of_urb(qtd, qh))
6008c2ecf20Sopenharmony_ci			ptd->dw3 |= TO_DW3_DATA_TOGGLE(1);
6018c2ecf20Sopenharmony_ci	}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	ptd->dw3 |= DW3_ACTIVE_BIT;
6048c2ecf20Sopenharmony_ci	/* Cerr */
6058c2ecf20Sopenharmony_ci	ptd->dw3 |= TO_DW3_CERR(ERR_COUNTER);
6068c2ecf20Sopenharmony_ci}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_cistatic void transform_add_int(struct isp1760_qh *qh,
6098c2ecf20Sopenharmony_ci			struct isp1760_qtd *qtd, struct ptd *ptd)
6108c2ecf20Sopenharmony_ci{
6118c2ecf20Sopenharmony_ci	u32 usof;
6128c2ecf20Sopenharmony_ci	u32 period;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	/*
6158c2ecf20Sopenharmony_ci	 * Most of this is guessing. ISP1761 datasheet is quite unclear, and
6168c2ecf20Sopenharmony_ci	 * the algorithm from the original Philips driver code, which was
6178c2ecf20Sopenharmony_ci	 * pretty much used in this driver before as well, is quite horrendous
6188c2ecf20Sopenharmony_ci	 * and, i believe, incorrect. The code below follows the datasheet and
6198c2ecf20Sopenharmony_ci	 * USB2.0 spec as far as I can tell, and plug/unplug seems to be much
6208c2ecf20Sopenharmony_ci	 * more reliable this way (fingers crossed...).
6218c2ecf20Sopenharmony_ci	 */
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	if (qtd->urb->dev->speed == USB_SPEED_HIGH) {
6248c2ecf20Sopenharmony_ci		/* urb->interval is in units of microframes (1/8 ms) */
6258c2ecf20Sopenharmony_ci		period = qtd->urb->interval >> 3;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci		if (qtd->urb->interval > 4)
6288c2ecf20Sopenharmony_ci			usof = 0x01; /* One bit set =>
6298c2ecf20Sopenharmony_ci						interval 1 ms * uFrame-match */
6308c2ecf20Sopenharmony_ci		else if (qtd->urb->interval > 2)
6318c2ecf20Sopenharmony_ci			usof = 0x22; /* Two bits set => interval 1/2 ms */
6328c2ecf20Sopenharmony_ci		else if (qtd->urb->interval > 1)
6338c2ecf20Sopenharmony_ci			usof = 0x55; /* Four bits set => interval 1/4 ms */
6348c2ecf20Sopenharmony_ci		else
6358c2ecf20Sopenharmony_ci			usof = 0xff; /* All bits set => interval 1/8 ms */
6368c2ecf20Sopenharmony_ci	} else {
6378c2ecf20Sopenharmony_ci		/* urb->interval is in units of frames (1 ms) */
6388c2ecf20Sopenharmony_ci		period = qtd->urb->interval;
6398c2ecf20Sopenharmony_ci		usof = 0x0f;		/* Execute Start Split on any of the
6408c2ecf20Sopenharmony_ci					   four first uFrames */
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci		/*
6438c2ecf20Sopenharmony_ci		 * First 8 bits in dw5 is uSCS and "specifies which uSOF the
6448c2ecf20Sopenharmony_ci		 * complete split needs to be sent. Valid only for IN." Also,
6458c2ecf20Sopenharmony_ci		 * "All bits can be set to one for every transfer." (p 82,
6468c2ecf20Sopenharmony_ci		 * ISP1761 data sheet.) 0x1c is from Philips driver. Where did
6478c2ecf20Sopenharmony_ci		 * that number come from? 0xff seems to work fine...
6488c2ecf20Sopenharmony_ci		 */
6498c2ecf20Sopenharmony_ci		/* ptd->dw5 = 0x1c; */
6508c2ecf20Sopenharmony_ci		ptd->dw5 = 0xff; /* Execute Complete Split on any uFrame */
6518c2ecf20Sopenharmony_ci	}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	period = period >> 1;/* Ensure equal or shorter period than requested */
6548c2ecf20Sopenharmony_ci	period &= 0xf8; /* Mask off too large values and lowest unused 3 bits */
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	ptd->dw2 |= period;
6578c2ecf20Sopenharmony_ci	ptd->dw4 = usof;
6588c2ecf20Sopenharmony_ci}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_cistatic void create_ptd_int(struct isp1760_qh *qh,
6618c2ecf20Sopenharmony_ci			struct isp1760_qtd *qtd, struct ptd *ptd)
6628c2ecf20Sopenharmony_ci{
6638c2ecf20Sopenharmony_ci	create_ptd_atl(qh, qtd, ptd);
6648c2ecf20Sopenharmony_ci	transform_add_int(qh, qtd, ptd);
6658c2ecf20Sopenharmony_ci}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_cistatic void isp1760_urb_done(struct usb_hcd *hcd, struct urb *urb)
6688c2ecf20Sopenharmony_ci__releases(priv->lock)
6698c2ecf20Sopenharmony_ci__acquires(priv->lock)
6708c2ecf20Sopenharmony_ci{
6718c2ecf20Sopenharmony_ci	struct isp1760_hcd *priv = hcd_to_priv(hcd);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	if (!urb->unlinked) {
6748c2ecf20Sopenharmony_ci		if (urb->status == -EINPROGRESS)
6758c2ecf20Sopenharmony_ci			urb->status = 0;
6768c2ecf20Sopenharmony_ci	}
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	if (usb_pipein(urb->pipe) && usb_pipetype(urb->pipe) != PIPE_CONTROL) {
6798c2ecf20Sopenharmony_ci		void *ptr;
6808c2ecf20Sopenharmony_ci		for (ptr = urb->transfer_buffer;
6818c2ecf20Sopenharmony_ci		     ptr < urb->transfer_buffer + urb->transfer_buffer_length;
6828c2ecf20Sopenharmony_ci		     ptr += PAGE_SIZE)
6838c2ecf20Sopenharmony_ci			flush_dcache_page(virt_to_page(ptr));
6848c2ecf20Sopenharmony_ci	}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	/* complete() can reenter this HCD */
6878c2ecf20Sopenharmony_ci	usb_hcd_unlink_urb_from_ep(hcd, urb);
6888c2ecf20Sopenharmony_ci	spin_unlock(&priv->lock);
6898c2ecf20Sopenharmony_ci	usb_hcd_giveback_urb(hcd, urb, urb->status);
6908c2ecf20Sopenharmony_ci	spin_lock(&priv->lock);
6918c2ecf20Sopenharmony_ci}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_cistatic struct isp1760_qtd *qtd_alloc(gfp_t flags, struct urb *urb,
6948c2ecf20Sopenharmony_ci								u8 packet_type)
6958c2ecf20Sopenharmony_ci{
6968c2ecf20Sopenharmony_ci	struct isp1760_qtd *qtd;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	qtd = kmem_cache_zalloc(qtd_cachep, flags);
6998c2ecf20Sopenharmony_ci	if (!qtd)
7008c2ecf20Sopenharmony_ci		return NULL;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&qtd->qtd_list);
7038c2ecf20Sopenharmony_ci	qtd->urb = urb;
7048c2ecf20Sopenharmony_ci	qtd->packet_type = packet_type;
7058c2ecf20Sopenharmony_ci	qtd->status = QTD_ENQUEUED;
7068c2ecf20Sopenharmony_ci	qtd->actual_length = 0;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	return qtd;
7098c2ecf20Sopenharmony_ci}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_cistatic void qtd_free(struct isp1760_qtd *qtd)
7128c2ecf20Sopenharmony_ci{
7138c2ecf20Sopenharmony_ci	WARN_ON(qtd->payload_addr);
7148c2ecf20Sopenharmony_ci	kmem_cache_free(qtd_cachep, qtd);
7158c2ecf20Sopenharmony_ci}
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_cistatic void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot,
7188c2ecf20Sopenharmony_ci				struct isp1760_slotinfo *slots,
7198c2ecf20Sopenharmony_ci				struct isp1760_qtd *qtd, struct isp1760_qh *qh,
7208c2ecf20Sopenharmony_ci				struct ptd *ptd)
7218c2ecf20Sopenharmony_ci{
7228c2ecf20Sopenharmony_ci	struct isp1760_hcd *priv = hcd_to_priv(hcd);
7238c2ecf20Sopenharmony_ci	int skip_map;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	WARN_ON((slot < 0) || (slot > 31));
7268c2ecf20Sopenharmony_ci	WARN_ON(qtd->length && !qtd->payload_addr);
7278c2ecf20Sopenharmony_ci	WARN_ON(slots[slot].qtd);
7288c2ecf20Sopenharmony_ci	WARN_ON(slots[slot].qh);
7298c2ecf20Sopenharmony_ci	WARN_ON(qtd->status != QTD_PAYLOAD_ALLOC);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	/* Make sure done map has not triggered from some unlinked transfer */
7328c2ecf20Sopenharmony_ci	if (ptd_offset == ATL_PTD_OFFSET) {
7338c2ecf20Sopenharmony_ci		priv->atl_done_map |= reg_read32(hcd->regs,
7348c2ecf20Sopenharmony_ci						HC_ATL_PTD_DONEMAP_REG);
7358c2ecf20Sopenharmony_ci		priv->atl_done_map &= ~(1 << slot);
7368c2ecf20Sopenharmony_ci	} else {
7378c2ecf20Sopenharmony_ci		priv->int_done_map |= reg_read32(hcd->regs,
7388c2ecf20Sopenharmony_ci						HC_INT_PTD_DONEMAP_REG);
7398c2ecf20Sopenharmony_ci		priv->int_done_map &= ~(1 << slot);
7408c2ecf20Sopenharmony_ci	}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	qh->slot = slot;
7438c2ecf20Sopenharmony_ci	qtd->status = QTD_XFER_STARTED;
7448c2ecf20Sopenharmony_ci	slots[slot].timestamp = jiffies;
7458c2ecf20Sopenharmony_ci	slots[slot].qtd = qtd;
7468c2ecf20Sopenharmony_ci	slots[slot].qh = qh;
7478c2ecf20Sopenharmony_ci	ptd_write(hcd->regs, ptd_offset, slot, ptd);
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	if (ptd_offset == ATL_PTD_OFFSET) {
7508c2ecf20Sopenharmony_ci		skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG);
7518c2ecf20Sopenharmony_ci		skip_map &= ~(1 << qh->slot);
7528c2ecf20Sopenharmony_ci		reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map);
7538c2ecf20Sopenharmony_ci	} else {
7548c2ecf20Sopenharmony_ci		skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG);
7558c2ecf20Sopenharmony_ci		skip_map &= ~(1 << qh->slot);
7568c2ecf20Sopenharmony_ci		reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map);
7578c2ecf20Sopenharmony_ci	}
7588c2ecf20Sopenharmony_ci}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_cistatic int is_short_bulk(struct isp1760_qtd *qtd)
7618c2ecf20Sopenharmony_ci{
7628c2ecf20Sopenharmony_ci	return (usb_pipebulk(qtd->urb->pipe) &&
7638c2ecf20Sopenharmony_ci					(qtd->actual_length < qtd->length));
7648c2ecf20Sopenharmony_ci}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_cistatic void collect_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh,
7678c2ecf20Sopenharmony_ci						struct list_head *urb_list)
7688c2ecf20Sopenharmony_ci{
7698c2ecf20Sopenharmony_ci	int last_qtd;
7708c2ecf20Sopenharmony_ci	struct isp1760_qtd *qtd, *qtd_next;
7718c2ecf20Sopenharmony_ci	struct urb_listitem *urb_listitem;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	list_for_each_entry_safe(qtd, qtd_next, &qh->qtd_list, qtd_list) {
7748c2ecf20Sopenharmony_ci		if (qtd->status < QTD_XFER_COMPLETE)
7758c2ecf20Sopenharmony_ci			break;
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci		last_qtd = last_qtd_of_urb(qtd, qh);
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci		if ((!last_qtd) && (qtd->status == QTD_RETIRE))
7808c2ecf20Sopenharmony_ci			qtd_next->status = QTD_RETIRE;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci		if (qtd->status == QTD_XFER_COMPLETE) {
7838c2ecf20Sopenharmony_ci			if (qtd->actual_length) {
7848c2ecf20Sopenharmony_ci				switch (qtd->packet_type) {
7858c2ecf20Sopenharmony_ci				case IN_PID:
7868c2ecf20Sopenharmony_ci					mem_reads8(hcd->regs, qtd->payload_addr,
7878c2ecf20Sopenharmony_ci							qtd->data_buffer,
7888c2ecf20Sopenharmony_ci							qtd->actual_length);
7898c2ecf20Sopenharmony_ci					fallthrough;
7908c2ecf20Sopenharmony_ci				case OUT_PID:
7918c2ecf20Sopenharmony_ci					qtd->urb->actual_length +=
7928c2ecf20Sopenharmony_ci							qtd->actual_length;
7938c2ecf20Sopenharmony_ci					fallthrough;
7948c2ecf20Sopenharmony_ci				case SETUP_PID:
7958c2ecf20Sopenharmony_ci					break;
7968c2ecf20Sopenharmony_ci				}
7978c2ecf20Sopenharmony_ci			}
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci			if (is_short_bulk(qtd)) {
8008c2ecf20Sopenharmony_ci				if (qtd->urb->transfer_flags & URB_SHORT_NOT_OK)
8018c2ecf20Sopenharmony_ci					qtd->urb->status = -EREMOTEIO;
8028c2ecf20Sopenharmony_ci				if (!last_qtd)
8038c2ecf20Sopenharmony_ci					qtd_next->status = QTD_RETIRE;
8048c2ecf20Sopenharmony_ci			}
8058c2ecf20Sopenharmony_ci		}
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci		if (qtd->payload_addr)
8088c2ecf20Sopenharmony_ci			free_mem(hcd, qtd);
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci		if (last_qtd) {
8118c2ecf20Sopenharmony_ci			if ((qtd->status == QTD_RETIRE) &&
8128c2ecf20Sopenharmony_ci					(qtd->urb->status == -EINPROGRESS))
8138c2ecf20Sopenharmony_ci				qtd->urb->status = -EPIPE;
8148c2ecf20Sopenharmony_ci			/* Defer calling of urb_done() since it releases lock */
8158c2ecf20Sopenharmony_ci			urb_listitem = kmem_cache_zalloc(urb_listitem_cachep,
8168c2ecf20Sopenharmony_ci								GFP_ATOMIC);
8178c2ecf20Sopenharmony_ci			if (unlikely(!urb_listitem))
8188c2ecf20Sopenharmony_ci				break; /* Try again on next call */
8198c2ecf20Sopenharmony_ci			urb_listitem->urb = qtd->urb;
8208c2ecf20Sopenharmony_ci			list_add_tail(&urb_listitem->urb_list, urb_list);
8218c2ecf20Sopenharmony_ci		}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci		list_del(&qtd->qtd_list);
8248c2ecf20Sopenharmony_ci		qtd_free(qtd);
8258c2ecf20Sopenharmony_ci	}
8268c2ecf20Sopenharmony_ci}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci#define ENQUEUE_DEPTH	2
8298c2ecf20Sopenharmony_cistatic void enqueue_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh)
8308c2ecf20Sopenharmony_ci{
8318c2ecf20Sopenharmony_ci	struct isp1760_hcd *priv = hcd_to_priv(hcd);
8328c2ecf20Sopenharmony_ci	int ptd_offset;
8338c2ecf20Sopenharmony_ci	struct isp1760_slotinfo *slots;
8348c2ecf20Sopenharmony_ci	int curr_slot, free_slot;
8358c2ecf20Sopenharmony_ci	int n;
8368c2ecf20Sopenharmony_ci	struct ptd ptd;
8378c2ecf20Sopenharmony_ci	struct isp1760_qtd *qtd;
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	if (unlikely(list_empty(&qh->qtd_list))) {
8408c2ecf20Sopenharmony_ci		WARN_ON(1);
8418c2ecf20Sopenharmony_ci		return;
8428c2ecf20Sopenharmony_ci	}
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	/* Make sure this endpoint's TT buffer is clean before queueing ptds */
8458c2ecf20Sopenharmony_ci	if (qh->tt_buffer_dirty)
8468c2ecf20Sopenharmony_ci		return;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	if (usb_pipeint(list_entry(qh->qtd_list.next, struct isp1760_qtd,
8498c2ecf20Sopenharmony_ci							qtd_list)->urb->pipe)) {
8508c2ecf20Sopenharmony_ci		ptd_offset = INT_PTD_OFFSET;
8518c2ecf20Sopenharmony_ci		slots = priv->int_slots;
8528c2ecf20Sopenharmony_ci	} else {
8538c2ecf20Sopenharmony_ci		ptd_offset = ATL_PTD_OFFSET;
8548c2ecf20Sopenharmony_ci		slots = priv->atl_slots;
8558c2ecf20Sopenharmony_ci	}
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	free_slot = -1;
8588c2ecf20Sopenharmony_ci	for (curr_slot = 0; curr_slot < 32; curr_slot++) {
8598c2ecf20Sopenharmony_ci		if ((free_slot == -1) && (slots[curr_slot].qtd == NULL))
8608c2ecf20Sopenharmony_ci			free_slot = curr_slot;
8618c2ecf20Sopenharmony_ci		if (slots[curr_slot].qh == qh)
8628c2ecf20Sopenharmony_ci			break;
8638c2ecf20Sopenharmony_ci	}
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	n = 0;
8668c2ecf20Sopenharmony_ci	list_for_each_entry(qtd, &qh->qtd_list, qtd_list) {
8678c2ecf20Sopenharmony_ci		if (qtd->status == QTD_ENQUEUED) {
8688c2ecf20Sopenharmony_ci			WARN_ON(qtd->payload_addr);
8698c2ecf20Sopenharmony_ci			alloc_mem(hcd, qtd);
8708c2ecf20Sopenharmony_ci			if ((qtd->length) && (!qtd->payload_addr))
8718c2ecf20Sopenharmony_ci				break;
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci			if ((qtd->length) &&
8748c2ecf20Sopenharmony_ci			    ((qtd->packet_type == SETUP_PID) ||
8758c2ecf20Sopenharmony_ci			     (qtd->packet_type == OUT_PID))) {
8768c2ecf20Sopenharmony_ci				mem_writes8(hcd->regs, qtd->payload_addr,
8778c2ecf20Sopenharmony_ci						qtd->data_buffer, qtd->length);
8788c2ecf20Sopenharmony_ci			}
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci			qtd->status = QTD_PAYLOAD_ALLOC;
8818c2ecf20Sopenharmony_ci		}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci		if (qtd->status == QTD_PAYLOAD_ALLOC) {
8848c2ecf20Sopenharmony_ci/*
8858c2ecf20Sopenharmony_ci			if ((curr_slot > 31) && (free_slot == -1))
8868c2ecf20Sopenharmony_ci				dev_dbg(hcd->self.controller, "%s: No slot "
8878c2ecf20Sopenharmony_ci					"available for transfer\n", __func__);
8888c2ecf20Sopenharmony_ci*/
8898c2ecf20Sopenharmony_ci			/* Start xfer for this endpoint if not already done */
8908c2ecf20Sopenharmony_ci			if ((curr_slot > 31) && (free_slot > -1)) {
8918c2ecf20Sopenharmony_ci				if (usb_pipeint(qtd->urb->pipe))
8928c2ecf20Sopenharmony_ci					create_ptd_int(qh, qtd, &ptd);
8938c2ecf20Sopenharmony_ci				else
8948c2ecf20Sopenharmony_ci					create_ptd_atl(qh, qtd, &ptd);
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci				start_bus_transfer(hcd, ptd_offset, free_slot,
8978c2ecf20Sopenharmony_ci							slots, qtd, qh, &ptd);
8988c2ecf20Sopenharmony_ci				curr_slot = free_slot;
8998c2ecf20Sopenharmony_ci			}
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci			n++;
9028c2ecf20Sopenharmony_ci			if (n >= ENQUEUE_DEPTH)
9038c2ecf20Sopenharmony_ci				break;
9048c2ecf20Sopenharmony_ci		}
9058c2ecf20Sopenharmony_ci	}
9068c2ecf20Sopenharmony_ci}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_cistatic void schedule_ptds(struct usb_hcd *hcd)
9098c2ecf20Sopenharmony_ci{
9108c2ecf20Sopenharmony_ci	struct isp1760_hcd *priv;
9118c2ecf20Sopenharmony_ci	struct isp1760_qh *qh, *qh_next;
9128c2ecf20Sopenharmony_ci	struct list_head *ep_queue;
9138c2ecf20Sopenharmony_ci	LIST_HEAD(urb_list);
9148c2ecf20Sopenharmony_ci	struct urb_listitem *urb_listitem, *urb_listitem_next;
9158c2ecf20Sopenharmony_ci	int i;
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	if (!hcd) {
9188c2ecf20Sopenharmony_ci		WARN_ON(1);
9198c2ecf20Sopenharmony_ci		return;
9208c2ecf20Sopenharmony_ci	}
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	priv = hcd_to_priv(hcd);
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	/*
9258c2ecf20Sopenharmony_ci	 * check finished/retired xfers, transfer payloads, call urb_done()
9268c2ecf20Sopenharmony_ci	 */
9278c2ecf20Sopenharmony_ci	for (i = 0; i < QH_END; i++) {
9288c2ecf20Sopenharmony_ci		ep_queue = &priv->qh_list[i];
9298c2ecf20Sopenharmony_ci		list_for_each_entry_safe(qh, qh_next, ep_queue, qh_list) {
9308c2ecf20Sopenharmony_ci			collect_qtds(hcd, qh, &urb_list);
9318c2ecf20Sopenharmony_ci			if (list_empty(&qh->qtd_list))
9328c2ecf20Sopenharmony_ci				list_del(&qh->qh_list);
9338c2ecf20Sopenharmony_ci		}
9348c2ecf20Sopenharmony_ci	}
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	list_for_each_entry_safe(urb_listitem, urb_listitem_next, &urb_list,
9378c2ecf20Sopenharmony_ci								urb_list) {
9388c2ecf20Sopenharmony_ci		isp1760_urb_done(hcd, urb_listitem->urb);
9398c2ecf20Sopenharmony_ci		kmem_cache_free(urb_listitem_cachep, urb_listitem);
9408c2ecf20Sopenharmony_ci	}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	/*
9438c2ecf20Sopenharmony_ci	 * Schedule packets for transfer.
9448c2ecf20Sopenharmony_ci	 *
9458c2ecf20Sopenharmony_ci	 * According to USB2.0 specification:
9468c2ecf20Sopenharmony_ci	 *
9478c2ecf20Sopenharmony_ci	 * 1st prio: interrupt xfers, up to 80 % of bandwidth
9488c2ecf20Sopenharmony_ci	 * 2nd prio: control xfers
9498c2ecf20Sopenharmony_ci	 * 3rd prio: bulk xfers
9508c2ecf20Sopenharmony_ci	 *
9518c2ecf20Sopenharmony_ci	 * ... but let's use a simpler scheme here (mostly because ISP1761 doc
9528c2ecf20Sopenharmony_ci	 * is very unclear on how to prioritize traffic):
9538c2ecf20Sopenharmony_ci	 *
9548c2ecf20Sopenharmony_ci	 * 1) Enqueue any queued control transfers, as long as payload chip mem
9558c2ecf20Sopenharmony_ci	 *    and PTD ATL slots are available.
9568c2ecf20Sopenharmony_ci	 * 2) Enqueue any queued INT transfers, as long as payload chip mem
9578c2ecf20Sopenharmony_ci	 *    and PTD INT slots are available.
9588c2ecf20Sopenharmony_ci	 * 3) Enqueue any queued bulk transfers, as long as payload chip mem
9598c2ecf20Sopenharmony_ci	 *    and PTD ATL slots are available.
9608c2ecf20Sopenharmony_ci	 *
9618c2ecf20Sopenharmony_ci	 * Use double buffering (ENQUEUE_DEPTH==2) as a compromise between
9628c2ecf20Sopenharmony_ci	 * conservation of chip mem and performance.
9638c2ecf20Sopenharmony_ci	 *
9648c2ecf20Sopenharmony_ci	 * I'm sure this scheme could be improved upon!
9658c2ecf20Sopenharmony_ci	 */
9668c2ecf20Sopenharmony_ci	for (i = 0; i < QH_END; i++) {
9678c2ecf20Sopenharmony_ci		ep_queue = &priv->qh_list[i];
9688c2ecf20Sopenharmony_ci		list_for_each_entry_safe(qh, qh_next, ep_queue, qh_list)
9698c2ecf20Sopenharmony_ci			enqueue_qtds(hcd, qh);
9708c2ecf20Sopenharmony_ci	}
9718c2ecf20Sopenharmony_ci}
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci#define PTD_STATE_QTD_DONE	1
9748c2ecf20Sopenharmony_ci#define PTD_STATE_QTD_RELOAD	2
9758c2ecf20Sopenharmony_ci#define PTD_STATE_URB_RETIRE	3
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_cistatic int check_int_transfer(struct usb_hcd *hcd, struct ptd *ptd,
9788c2ecf20Sopenharmony_ci								struct urb *urb)
9798c2ecf20Sopenharmony_ci{
9808c2ecf20Sopenharmony_ci	__dw dw4;
9818c2ecf20Sopenharmony_ci	int i;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	dw4 = ptd->dw4;
9848c2ecf20Sopenharmony_ci	dw4 >>= 8;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	/* FIXME: ISP1761 datasheet does not say what to do with these. Do we
9878c2ecf20Sopenharmony_ci	   need to handle these errors? Is it done in hardware? */
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci	if (ptd->dw3 & DW3_HALT_BIT) {
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci		urb->status = -EPROTO; /* Default unknown error */
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci		for (i = 0; i < 8; i++) {
9948c2ecf20Sopenharmony_ci			switch (dw4 & 0x7) {
9958c2ecf20Sopenharmony_ci			case INT_UNDERRUN:
9968c2ecf20Sopenharmony_ci				dev_dbg(hcd->self.controller, "%s: underrun "
9978c2ecf20Sopenharmony_ci						"during uFrame %d\n",
9988c2ecf20Sopenharmony_ci						__func__, i);
9998c2ecf20Sopenharmony_ci				urb->status = -ECOMM; /* Could not write data */
10008c2ecf20Sopenharmony_ci				break;
10018c2ecf20Sopenharmony_ci			case INT_EXACT:
10028c2ecf20Sopenharmony_ci				dev_dbg(hcd->self.controller, "%s: transaction "
10038c2ecf20Sopenharmony_ci						"error during uFrame %d\n",
10048c2ecf20Sopenharmony_ci						__func__, i);
10058c2ecf20Sopenharmony_ci				urb->status = -EPROTO; /* timeout, bad CRC, PID
10068c2ecf20Sopenharmony_ci							  error etc. */
10078c2ecf20Sopenharmony_ci				break;
10088c2ecf20Sopenharmony_ci			case INT_BABBLE:
10098c2ecf20Sopenharmony_ci				dev_dbg(hcd->self.controller, "%s: babble "
10108c2ecf20Sopenharmony_ci						"error during uFrame %d\n",
10118c2ecf20Sopenharmony_ci						__func__, i);
10128c2ecf20Sopenharmony_ci				urb->status = -EOVERFLOW;
10138c2ecf20Sopenharmony_ci				break;
10148c2ecf20Sopenharmony_ci			}
10158c2ecf20Sopenharmony_ci			dw4 >>= 3;
10168c2ecf20Sopenharmony_ci		}
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci		return PTD_STATE_URB_RETIRE;
10198c2ecf20Sopenharmony_ci	}
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	return PTD_STATE_QTD_DONE;
10228c2ecf20Sopenharmony_ci}
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_cistatic int check_atl_transfer(struct usb_hcd *hcd, struct ptd *ptd,
10258c2ecf20Sopenharmony_ci								struct urb *urb)
10268c2ecf20Sopenharmony_ci{
10278c2ecf20Sopenharmony_ci	WARN_ON(!ptd);
10288c2ecf20Sopenharmony_ci	if (ptd->dw3 & DW3_HALT_BIT) {
10298c2ecf20Sopenharmony_ci		if (ptd->dw3 & DW3_BABBLE_BIT)
10308c2ecf20Sopenharmony_ci			urb->status = -EOVERFLOW;
10318c2ecf20Sopenharmony_ci		else if (FROM_DW3_CERR(ptd->dw3))
10328c2ecf20Sopenharmony_ci			urb->status = -EPIPE;  /* Stall */
10338c2ecf20Sopenharmony_ci		else
10348c2ecf20Sopenharmony_ci			urb->status = -EPROTO; /* Unknown */
10358c2ecf20Sopenharmony_ci/*
10368c2ecf20Sopenharmony_ci		dev_dbg(hcd->self.controller, "%s: ptd error:\n"
10378c2ecf20Sopenharmony_ci			"        dw0: %08x dw1: %08x dw2: %08x dw3: %08x\n"
10388c2ecf20Sopenharmony_ci			"        dw4: %08x dw5: %08x dw6: %08x dw7: %08x\n",
10398c2ecf20Sopenharmony_ci			__func__,
10408c2ecf20Sopenharmony_ci			ptd->dw0, ptd->dw1, ptd->dw2, ptd->dw3,
10418c2ecf20Sopenharmony_ci			ptd->dw4, ptd->dw5, ptd->dw6, ptd->dw7);
10428c2ecf20Sopenharmony_ci*/
10438c2ecf20Sopenharmony_ci		return PTD_STATE_URB_RETIRE;
10448c2ecf20Sopenharmony_ci	}
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	if ((ptd->dw3 & DW3_ERROR_BIT) && (ptd->dw3 & DW3_ACTIVE_BIT)) {
10478c2ecf20Sopenharmony_ci		/* Transfer Error, *but* active and no HALT -> reload */
10488c2ecf20Sopenharmony_ci		dev_dbg(hcd->self.controller, "PID error; reloading ptd\n");
10498c2ecf20Sopenharmony_ci		return PTD_STATE_QTD_RELOAD;
10508c2ecf20Sopenharmony_ci	}
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	if (!FROM_DW3_NAKCOUNT(ptd->dw3) && (ptd->dw3 & DW3_ACTIVE_BIT)) {
10538c2ecf20Sopenharmony_ci		/*
10548c2ecf20Sopenharmony_ci		 * NAKs are handled in HW by the chip. Usually if the
10558c2ecf20Sopenharmony_ci		 * device is not able to send data fast enough.
10568c2ecf20Sopenharmony_ci		 * This happens mostly on slower hardware.
10578c2ecf20Sopenharmony_ci		 */
10588c2ecf20Sopenharmony_ci		return PTD_STATE_QTD_RELOAD;
10598c2ecf20Sopenharmony_ci	}
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	return PTD_STATE_QTD_DONE;
10628c2ecf20Sopenharmony_ci}
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_cistatic void handle_done_ptds(struct usb_hcd *hcd)
10658c2ecf20Sopenharmony_ci{
10668c2ecf20Sopenharmony_ci	struct isp1760_hcd *priv = hcd_to_priv(hcd);
10678c2ecf20Sopenharmony_ci	struct ptd ptd;
10688c2ecf20Sopenharmony_ci	struct isp1760_qh *qh;
10698c2ecf20Sopenharmony_ci	int slot;
10708c2ecf20Sopenharmony_ci	int state;
10718c2ecf20Sopenharmony_ci	struct isp1760_slotinfo *slots;
10728c2ecf20Sopenharmony_ci	u32 ptd_offset;
10738c2ecf20Sopenharmony_ci	struct isp1760_qtd *qtd;
10748c2ecf20Sopenharmony_ci	int modified;
10758c2ecf20Sopenharmony_ci	int skip_map;
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG);
10788c2ecf20Sopenharmony_ci	priv->int_done_map &= ~skip_map;
10798c2ecf20Sopenharmony_ci	skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG);
10808c2ecf20Sopenharmony_ci	priv->atl_done_map &= ~skip_map;
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	modified = priv->int_done_map || priv->atl_done_map;
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	while (priv->int_done_map || priv->atl_done_map) {
10858c2ecf20Sopenharmony_ci		if (priv->int_done_map) {
10868c2ecf20Sopenharmony_ci			/* INT ptd */
10878c2ecf20Sopenharmony_ci			slot = __ffs(priv->int_done_map);
10888c2ecf20Sopenharmony_ci			priv->int_done_map &= ~(1 << slot);
10898c2ecf20Sopenharmony_ci			slots = priv->int_slots;
10908c2ecf20Sopenharmony_ci			/* This should not trigger, and could be removed if
10918c2ecf20Sopenharmony_ci			   noone have any problems with it triggering: */
10928c2ecf20Sopenharmony_ci			if (!slots[slot].qh) {
10938c2ecf20Sopenharmony_ci				WARN_ON(1);
10948c2ecf20Sopenharmony_ci				continue;
10958c2ecf20Sopenharmony_ci			}
10968c2ecf20Sopenharmony_ci			ptd_offset = INT_PTD_OFFSET;
10978c2ecf20Sopenharmony_ci			ptd_read(hcd->regs, INT_PTD_OFFSET, slot, &ptd);
10988c2ecf20Sopenharmony_ci			state = check_int_transfer(hcd, &ptd,
10998c2ecf20Sopenharmony_ci							slots[slot].qtd->urb);
11008c2ecf20Sopenharmony_ci		} else {
11018c2ecf20Sopenharmony_ci			/* ATL ptd */
11028c2ecf20Sopenharmony_ci			slot = __ffs(priv->atl_done_map);
11038c2ecf20Sopenharmony_ci			priv->atl_done_map &= ~(1 << slot);
11048c2ecf20Sopenharmony_ci			slots = priv->atl_slots;
11058c2ecf20Sopenharmony_ci			/* This should not trigger, and could be removed if
11068c2ecf20Sopenharmony_ci			   noone have any problems with it triggering: */
11078c2ecf20Sopenharmony_ci			if (!slots[slot].qh) {
11088c2ecf20Sopenharmony_ci				WARN_ON(1);
11098c2ecf20Sopenharmony_ci				continue;
11108c2ecf20Sopenharmony_ci			}
11118c2ecf20Sopenharmony_ci			ptd_offset = ATL_PTD_OFFSET;
11128c2ecf20Sopenharmony_ci			ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd);
11138c2ecf20Sopenharmony_ci			state = check_atl_transfer(hcd, &ptd,
11148c2ecf20Sopenharmony_ci							slots[slot].qtd->urb);
11158c2ecf20Sopenharmony_ci		}
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci		qtd = slots[slot].qtd;
11188c2ecf20Sopenharmony_ci		slots[slot].qtd = NULL;
11198c2ecf20Sopenharmony_ci		qh = slots[slot].qh;
11208c2ecf20Sopenharmony_ci		slots[slot].qh = NULL;
11218c2ecf20Sopenharmony_ci		qh->slot = -1;
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci		WARN_ON(qtd->status != QTD_XFER_STARTED);
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci		switch (state) {
11268c2ecf20Sopenharmony_ci		case PTD_STATE_QTD_DONE:
11278c2ecf20Sopenharmony_ci			if ((usb_pipeint(qtd->urb->pipe)) &&
11288c2ecf20Sopenharmony_ci				       (qtd->urb->dev->speed != USB_SPEED_HIGH))
11298c2ecf20Sopenharmony_ci				qtd->actual_length =
11308c2ecf20Sopenharmony_ci				       FROM_DW3_SCS_NRBYTESTRANSFERRED(ptd.dw3);
11318c2ecf20Sopenharmony_ci			else
11328c2ecf20Sopenharmony_ci				qtd->actual_length =
11338c2ecf20Sopenharmony_ci					FROM_DW3_NRBYTESTRANSFERRED(ptd.dw3);
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci			qtd->status = QTD_XFER_COMPLETE;
11368c2ecf20Sopenharmony_ci			if (list_is_last(&qtd->qtd_list, &qh->qtd_list) ||
11378c2ecf20Sopenharmony_ci							is_short_bulk(qtd))
11388c2ecf20Sopenharmony_ci				qtd = NULL;
11398c2ecf20Sopenharmony_ci			else
11408c2ecf20Sopenharmony_ci				qtd = list_entry(qtd->qtd_list.next,
11418c2ecf20Sopenharmony_ci							typeof(*qtd), qtd_list);
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci			qh->toggle = FROM_DW3_DATA_TOGGLE(ptd.dw3);
11448c2ecf20Sopenharmony_ci			qh->ping = FROM_DW3_PING(ptd.dw3);
11458c2ecf20Sopenharmony_ci			break;
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci		case PTD_STATE_QTD_RELOAD: /* QTD_RETRY, for atls only */
11488c2ecf20Sopenharmony_ci			qtd->status = QTD_PAYLOAD_ALLOC;
11498c2ecf20Sopenharmony_ci			ptd.dw0 |= DW0_VALID_BIT;
11508c2ecf20Sopenharmony_ci			/* RL counter = ERR counter */
11518c2ecf20Sopenharmony_ci			ptd.dw3 &= ~TO_DW3_NAKCOUNT(0xf);
11528c2ecf20Sopenharmony_ci			ptd.dw3 |= TO_DW3_NAKCOUNT(FROM_DW2_RL(ptd.dw2));
11538c2ecf20Sopenharmony_ci			ptd.dw3 &= ~TO_DW3_CERR(3);
11548c2ecf20Sopenharmony_ci			ptd.dw3 |= TO_DW3_CERR(ERR_COUNTER);
11558c2ecf20Sopenharmony_ci			qh->toggle = FROM_DW3_DATA_TOGGLE(ptd.dw3);
11568c2ecf20Sopenharmony_ci			qh->ping = FROM_DW3_PING(ptd.dw3);
11578c2ecf20Sopenharmony_ci			break;
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci		case PTD_STATE_URB_RETIRE:
11608c2ecf20Sopenharmony_ci			qtd->status = QTD_RETIRE;
11618c2ecf20Sopenharmony_ci			if ((qtd->urb->dev->speed != USB_SPEED_HIGH) &&
11628c2ecf20Sopenharmony_ci					(qtd->urb->status != -EPIPE) &&
11638c2ecf20Sopenharmony_ci					(qtd->urb->status != -EREMOTEIO)) {
11648c2ecf20Sopenharmony_ci				qh->tt_buffer_dirty = 1;
11658c2ecf20Sopenharmony_ci				if (usb_hub_clear_tt_buffer(qtd->urb))
11668c2ecf20Sopenharmony_ci					/* Clear failed; let's hope things work
11678c2ecf20Sopenharmony_ci					   anyway */
11688c2ecf20Sopenharmony_ci					qh->tt_buffer_dirty = 0;
11698c2ecf20Sopenharmony_ci			}
11708c2ecf20Sopenharmony_ci			qtd = NULL;
11718c2ecf20Sopenharmony_ci			qh->toggle = 0;
11728c2ecf20Sopenharmony_ci			qh->ping = 0;
11738c2ecf20Sopenharmony_ci			break;
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci		default:
11768c2ecf20Sopenharmony_ci			WARN_ON(1);
11778c2ecf20Sopenharmony_ci			continue;
11788c2ecf20Sopenharmony_ci		}
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci		if (qtd && (qtd->status == QTD_PAYLOAD_ALLOC)) {
11818c2ecf20Sopenharmony_ci			if (slots == priv->int_slots) {
11828c2ecf20Sopenharmony_ci				if (state == PTD_STATE_QTD_RELOAD)
11838c2ecf20Sopenharmony_ci					dev_err(hcd->self.controller,
11848c2ecf20Sopenharmony_ci						"%s: PTD_STATE_QTD_RELOAD on "
11858c2ecf20Sopenharmony_ci						"interrupt packet\n", __func__);
11868c2ecf20Sopenharmony_ci				if (state != PTD_STATE_QTD_RELOAD)
11878c2ecf20Sopenharmony_ci					create_ptd_int(qh, qtd, &ptd);
11888c2ecf20Sopenharmony_ci			} else {
11898c2ecf20Sopenharmony_ci				if (state != PTD_STATE_QTD_RELOAD)
11908c2ecf20Sopenharmony_ci					create_ptd_atl(qh, qtd, &ptd);
11918c2ecf20Sopenharmony_ci			}
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci			start_bus_transfer(hcd, ptd_offset, slot, slots, qtd,
11948c2ecf20Sopenharmony_ci				qh, &ptd);
11958c2ecf20Sopenharmony_ci		}
11968c2ecf20Sopenharmony_ci	}
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	if (modified)
11998c2ecf20Sopenharmony_ci		schedule_ptds(hcd);
12008c2ecf20Sopenharmony_ci}
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_cistatic irqreturn_t isp1760_irq(struct usb_hcd *hcd)
12038c2ecf20Sopenharmony_ci{
12048c2ecf20Sopenharmony_ci	struct isp1760_hcd *priv = hcd_to_priv(hcd);
12058c2ecf20Sopenharmony_ci	u32 imask;
12068c2ecf20Sopenharmony_ci	irqreturn_t irqret = IRQ_NONE;
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	spin_lock(&priv->lock);
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci	if (!(hcd->state & HC_STATE_RUNNING))
12118c2ecf20Sopenharmony_ci		goto leave;
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	imask = reg_read32(hcd->regs, HC_INTERRUPT_REG);
12148c2ecf20Sopenharmony_ci	if (unlikely(!imask))
12158c2ecf20Sopenharmony_ci		goto leave;
12168c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_INTERRUPT_REG, imask); /* Clear */
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	priv->int_done_map |= reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG);
12198c2ecf20Sopenharmony_ci	priv->atl_done_map |= reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG);
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	handle_done_ptds(hcd);
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	irqret = IRQ_HANDLED;
12248c2ecf20Sopenharmony_cileave:
12258c2ecf20Sopenharmony_ci	spin_unlock(&priv->lock);
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci	return irqret;
12288c2ecf20Sopenharmony_ci}
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci/*
12318c2ecf20Sopenharmony_ci * Workaround for problem described in chip errata 2:
12328c2ecf20Sopenharmony_ci *
12338c2ecf20Sopenharmony_ci * Sometimes interrupts are not generated when ATL (not INT?) completion occurs.
12348c2ecf20Sopenharmony_ci * One solution suggested in the errata is to use SOF interrupts _instead_of_
12358c2ecf20Sopenharmony_ci * ATL done interrupts (the "instead of" might be important since it seems
12368c2ecf20Sopenharmony_ci * enabling ATL interrupts also causes the chip to sometimes - rarely - "forget"
12378c2ecf20Sopenharmony_ci * to set the PTD's done bit in addition to not generating an interrupt!).
12388c2ecf20Sopenharmony_ci *
12398c2ecf20Sopenharmony_ci * So if we use SOF + ATL interrupts, we sometimes get stale PTDs since their
12408c2ecf20Sopenharmony_ci * done bit is not being set. This is bad - it blocks the endpoint until reboot.
12418c2ecf20Sopenharmony_ci *
12428c2ecf20Sopenharmony_ci * If we use SOF interrupts only, we get latency between ptd completion and the
12438c2ecf20Sopenharmony_ci * actual handling. This is very noticeable in testusb runs which takes several
12448c2ecf20Sopenharmony_ci * minutes longer without ATL interrupts.
12458c2ecf20Sopenharmony_ci *
12468c2ecf20Sopenharmony_ci * A better solution is to run the code below every SLOT_CHECK_PERIOD ms. If it
12478c2ecf20Sopenharmony_ci * finds active ATL slots which are older than SLOT_TIMEOUT ms, it checks the
12488c2ecf20Sopenharmony_ci * slot's ACTIVE and VALID bits. If these are not set, the ptd is considered
12498c2ecf20Sopenharmony_ci * completed and its done map bit is set.
12508c2ecf20Sopenharmony_ci *
12518c2ecf20Sopenharmony_ci * The values of SLOT_TIMEOUT and SLOT_CHECK_PERIOD have been arbitrarily chosen
12528c2ecf20Sopenharmony_ci * not to cause too much lag when this HW bug occurs, while still hopefully
12538c2ecf20Sopenharmony_ci * ensuring that the check does not falsely trigger.
12548c2ecf20Sopenharmony_ci */
12558c2ecf20Sopenharmony_ci#define SLOT_TIMEOUT 300
12568c2ecf20Sopenharmony_ci#define SLOT_CHECK_PERIOD 200
12578c2ecf20Sopenharmony_cistatic struct timer_list errata2_timer;
12588c2ecf20Sopenharmony_cistatic struct usb_hcd *errata2_timer_hcd;
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_cistatic void errata2_function(struct timer_list *unused)
12618c2ecf20Sopenharmony_ci{
12628c2ecf20Sopenharmony_ci	struct usb_hcd *hcd = errata2_timer_hcd;
12638c2ecf20Sopenharmony_ci	struct isp1760_hcd *priv = hcd_to_priv(hcd);
12648c2ecf20Sopenharmony_ci	int slot;
12658c2ecf20Sopenharmony_ci	struct ptd ptd;
12668c2ecf20Sopenharmony_ci	unsigned long spinflags;
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, spinflags);
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	for (slot = 0; slot < 32; slot++)
12718c2ecf20Sopenharmony_ci		if (priv->atl_slots[slot].qh && time_after(jiffies,
12728c2ecf20Sopenharmony_ci					priv->atl_slots[slot].timestamp +
12738c2ecf20Sopenharmony_ci					msecs_to_jiffies(SLOT_TIMEOUT))) {
12748c2ecf20Sopenharmony_ci			ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd);
12758c2ecf20Sopenharmony_ci			if (!FROM_DW0_VALID(ptd.dw0) &&
12768c2ecf20Sopenharmony_ci					!FROM_DW3_ACTIVE(ptd.dw3))
12778c2ecf20Sopenharmony_ci				priv->atl_done_map |= 1 << slot;
12788c2ecf20Sopenharmony_ci		}
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	if (priv->atl_done_map)
12818c2ecf20Sopenharmony_ci		handle_done_ptds(hcd);
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, spinflags);
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci	errata2_timer.expires = jiffies + msecs_to_jiffies(SLOT_CHECK_PERIOD);
12868c2ecf20Sopenharmony_ci	add_timer(&errata2_timer);
12878c2ecf20Sopenharmony_ci}
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_cistatic int isp1760_run(struct usb_hcd *hcd)
12908c2ecf20Sopenharmony_ci{
12918c2ecf20Sopenharmony_ci	int retval;
12928c2ecf20Sopenharmony_ci	u32 temp;
12938c2ecf20Sopenharmony_ci	u32 command;
12948c2ecf20Sopenharmony_ci	u32 chipid;
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ci	hcd->uses_new_polling = 1;
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci	hcd->state = HC_STATE_RUNNING;
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	/* Set PTD interrupt AND & OR maps */
13018c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_ATL_IRQ_MASK_AND_REG, 0);
13028c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, 0xffffffff);
13038c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_INT_IRQ_MASK_AND_REG, 0);
13048c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, 0xffffffff);
13058c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_ISO_IRQ_MASK_AND_REG, 0);
13068c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_ISO_IRQ_MASK_OR_REG, 0xffffffff);
13078c2ecf20Sopenharmony_ci	/* step 23 passed */
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	temp = reg_read32(hcd->regs, HC_HW_MODE_CTRL);
13108c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_HW_MODE_CTRL, temp | HW_GLOBAL_INTR_EN);
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci	command = reg_read32(hcd->regs, HC_USBCMD);
13138c2ecf20Sopenharmony_ci	command &= ~(CMD_LRESET|CMD_RESET);
13148c2ecf20Sopenharmony_ci	command |= CMD_RUN;
13158c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_USBCMD, command);
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci	retval = handshake(hcd, HC_USBCMD, CMD_RUN, CMD_RUN, 250 * 1000);
13188c2ecf20Sopenharmony_ci	if (retval)
13198c2ecf20Sopenharmony_ci		return retval;
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci	/*
13228c2ecf20Sopenharmony_ci	 * XXX
13238c2ecf20Sopenharmony_ci	 * Spec says to write FLAG_CF as last config action, priv code grabs
13248c2ecf20Sopenharmony_ci	 * the semaphore while doing so.
13258c2ecf20Sopenharmony_ci	 */
13268c2ecf20Sopenharmony_ci	down_write(&ehci_cf_port_reset_rwsem);
13278c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_CONFIGFLAG, FLAG_CF);
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci	retval = handshake(hcd, HC_CONFIGFLAG, FLAG_CF, FLAG_CF, 250 * 1000);
13308c2ecf20Sopenharmony_ci	up_write(&ehci_cf_port_reset_rwsem);
13318c2ecf20Sopenharmony_ci	if (retval)
13328c2ecf20Sopenharmony_ci		return retval;
13338c2ecf20Sopenharmony_ci
13348c2ecf20Sopenharmony_ci	errata2_timer_hcd = hcd;
13358c2ecf20Sopenharmony_ci	timer_setup(&errata2_timer, errata2_function, 0);
13368c2ecf20Sopenharmony_ci	errata2_timer.expires = jiffies + msecs_to_jiffies(SLOT_CHECK_PERIOD);
13378c2ecf20Sopenharmony_ci	add_timer(&errata2_timer);
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	chipid = reg_read32(hcd->regs, HC_CHIP_ID_REG);
13408c2ecf20Sopenharmony_ci	dev_info(hcd->self.controller, "USB ISP %04x HW rev. %d started\n",
13418c2ecf20Sopenharmony_ci					chipid & 0xffff, chipid >> 16);
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci	/* PTD Register Init Part 2, Step 28 */
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	/* Setup registers controlling PTD checking */
13468c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_ATL_PTD_LASTPTD_REG, 0x80000000);
13478c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_INT_PTD_LASTPTD_REG, 0x80000000);
13488c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_ISO_PTD_LASTPTD_REG, 0x00000001);
13498c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, 0xffffffff);
13508c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, 0xffffffff);
13518c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, 0xffffffff);
13528c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_BUFFER_STATUS_REG,
13538c2ecf20Sopenharmony_ci						ATL_BUF_FILL | INT_BUF_FILL);
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	/* GRR this is run-once init(), being done every time the HC starts.
13568c2ecf20Sopenharmony_ci	 * So long as they're part of class devices, we can't do it init()
13578c2ecf20Sopenharmony_ci	 * since the class device isn't created that early.
13588c2ecf20Sopenharmony_ci	 */
13598c2ecf20Sopenharmony_ci	return 0;
13608c2ecf20Sopenharmony_ci}
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_cistatic int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len)
13638c2ecf20Sopenharmony_ci{
13648c2ecf20Sopenharmony_ci	qtd->data_buffer = databuffer;
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	if (len > MAX_PAYLOAD_SIZE)
13678c2ecf20Sopenharmony_ci		len = MAX_PAYLOAD_SIZE;
13688c2ecf20Sopenharmony_ci	qtd->length = len;
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	return qtd->length;
13718c2ecf20Sopenharmony_ci}
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_cistatic void qtd_list_free(struct list_head *qtd_list)
13748c2ecf20Sopenharmony_ci{
13758c2ecf20Sopenharmony_ci	struct isp1760_qtd *qtd, *qtd_next;
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ci	list_for_each_entry_safe(qtd, qtd_next, qtd_list, qtd_list) {
13788c2ecf20Sopenharmony_ci		list_del(&qtd->qtd_list);
13798c2ecf20Sopenharmony_ci		qtd_free(qtd);
13808c2ecf20Sopenharmony_ci	}
13818c2ecf20Sopenharmony_ci}
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci/*
13848c2ecf20Sopenharmony_ci * Packetize urb->transfer_buffer into list of packets of size wMaxPacketSize.
13858c2ecf20Sopenharmony_ci * Also calculate the PID type (SETUP/IN/OUT) for each packet.
13868c2ecf20Sopenharmony_ci */
13878c2ecf20Sopenharmony_ci#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)
13888c2ecf20Sopenharmony_cistatic void packetize_urb(struct usb_hcd *hcd,
13898c2ecf20Sopenharmony_ci		struct urb *urb, struct list_head *head, gfp_t flags)
13908c2ecf20Sopenharmony_ci{
13918c2ecf20Sopenharmony_ci	struct isp1760_qtd *qtd;
13928c2ecf20Sopenharmony_ci	void *buf;
13938c2ecf20Sopenharmony_ci	int len, maxpacketsize;
13948c2ecf20Sopenharmony_ci	u8 packet_type;
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	/*
13978c2ecf20Sopenharmony_ci	 * URBs map to sequences of QTDs:  one logical transaction
13988c2ecf20Sopenharmony_ci	 */
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci	if (!urb->transfer_buffer && urb->transfer_buffer_length) {
14018c2ecf20Sopenharmony_ci		/* XXX This looks like usb storage / SCSI bug */
14028c2ecf20Sopenharmony_ci		dev_err(hcd->self.controller,
14038c2ecf20Sopenharmony_ci				"buf is null, dma is %08lx len is %d\n",
14048c2ecf20Sopenharmony_ci				(long unsigned)urb->transfer_dma,
14058c2ecf20Sopenharmony_ci				urb->transfer_buffer_length);
14068c2ecf20Sopenharmony_ci		WARN_ON(1);
14078c2ecf20Sopenharmony_ci	}
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	if (usb_pipein(urb->pipe))
14108c2ecf20Sopenharmony_ci		packet_type = IN_PID;
14118c2ecf20Sopenharmony_ci	else
14128c2ecf20Sopenharmony_ci		packet_type = OUT_PID;
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci	if (usb_pipecontrol(urb->pipe)) {
14158c2ecf20Sopenharmony_ci		qtd = qtd_alloc(flags, urb, SETUP_PID);
14168c2ecf20Sopenharmony_ci		if (!qtd)
14178c2ecf20Sopenharmony_ci			goto cleanup;
14188c2ecf20Sopenharmony_ci		qtd_fill(qtd, urb->setup_packet, sizeof(struct usb_ctrlrequest));
14198c2ecf20Sopenharmony_ci		list_add_tail(&qtd->qtd_list, head);
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci		/* for zero length DATA stages, STATUS is always IN */
14228c2ecf20Sopenharmony_ci		if (urb->transfer_buffer_length == 0)
14238c2ecf20Sopenharmony_ci			packet_type = IN_PID;
14248c2ecf20Sopenharmony_ci	}
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci	maxpacketsize = max_packet(usb_maxpacket(urb->dev, urb->pipe,
14278c2ecf20Sopenharmony_ci						usb_pipeout(urb->pipe)));
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci	/*
14308c2ecf20Sopenharmony_ci	 * buffer gets wrapped in one or more qtds;
14318c2ecf20Sopenharmony_ci	 * last one may be "short" (including zero len)
14328c2ecf20Sopenharmony_ci	 * and may serve as a control status ack
14338c2ecf20Sopenharmony_ci	 */
14348c2ecf20Sopenharmony_ci	buf = urb->transfer_buffer;
14358c2ecf20Sopenharmony_ci	len = urb->transfer_buffer_length;
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci	for (;;) {
14388c2ecf20Sopenharmony_ci		int this_qtd_len;
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_ci		qtd = qtd_alloc(flags, urb, packet_type);
14418c2ecf20Sopenharmony_ci		if (!qtd)
14428c2ecf20Sopenharmony_ci			goto cleanup;
14438c2ecf20Sopenharmony_ci		this_qtd_len = qtd_fill(qtd, buf, len);
14448c2ecf20Sopenharmony_ci		list_add_tail(&qtd->qtd_list, head);
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci		len -= this_qtd_len;
14478c2ecf20Sopenharmony_ci		buf += this_qtd_len;
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci		if (len <= 0)
14508c2ecf20Sopenharmony_ci			break;
14518c2ecf20Sopenharmony_ci	}
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	/*
14548c2ecf20Sopenharmony_ci	 * control requests may need a terminating data "status" ack;
14558c2ecf20Sopenharmony_ci	 * bulk ones may need a terminating short packet (zero length).
14568c2ecf20Sopenharmony_ci	 */
14578c2ecf20Sopenharmony_ci	if (urb->transfer_buffer_length != 0) {
14588c2ecf20Sopenharmony_ci		int one_more = 0;
14598c2ecf20Sopenharmony_ci
14608c2ecf20Sopenharmony_ci		if (usb_pipecontrol(urb->pipe)) {
14618c2ecf20Sopenharmony_ci			one_more = 1;
14628c2ecf20Sopenharmony_ci			if (packet_type == IN_PID)
14638c2ecf20Sopenharmony_ci				packet_type = OUT_PID;
14648c2ecf20Sopenharmony_ci			else
14658c2ecf20Sopenharmony_ci				packet_type = IN_PID;
14668c2ecf20Sopenharmony_ci		} else if (usb_pipebulk(urb->pipe)
14678c2ecf20Sopenharmony_ci				&& (urb->transfer_flags & URB_ZERO_PACKET)
14688c2ecf20Sopenharmony_ci				&& !(urb->transfer_buffer_length %
14698c2ecf20Sopenharmony_ci							maxpacketsize)) {
14708c2ecf20Sopenharmony_ci			one_more = 1;
14718c2ecf20Sopenharmony_ci		}
14728c2ecf20Sopenharmony_ci		if (one_more) {
14738c2ecf20Sopenharmony_ci			qtd = qtd_alloc(flags, urb, packet_type);
14748c2ecf20Sopenharmony_ci			if (!qtd)
14758c2ecf20Sopenharmony_ci				goto cleanup;
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci			/* never any data in such packets */
14788c2ecf20Sopenharmony_ci			qtd_fill(qtd, NULL, 0);
14798c2ecf20Sopenharmony_ci			list_add_tail(&qtd->qtd_list, head);
14808c2ecf20Sopenharmony_ci		}
14818c2ecf20Sopenharmony_ci	}
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci	return;
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_cicleanup:
14868c2ecf20Sopenharmony_ci	qtd_list_free(head);
14878c2ecf20Sopenharmony_ci}
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_cistatic int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
14908c2ecf20Sopenharmony_ci		gfp_t mem_flags)
14918c2ecf20Sopenharmony_ci{
14928c2ecf20Sopenharmony_ci	struct isp1760_hcd *priv = hcd_to_priv(hcd);
14938c2ecf20Sopenharmony_ci	struct list_head *ep_queue;
14948c2ecf20Sopenharmony_ci	struct isp1760_qh *qh, *qhit;
14958c2ecf20Sopenharmony_ci	unsigned long spinflags;
14968c2ecf20Sopenharmony_ci	LIST_HEAD(new_qtds);
14978c2ecf20Sopenharmony_ci	int retval;
14988c2ecf20Sopenharmony_ci	int qh_in_queue;
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci	switch (usb_pipetype(urb->pipe)) {
15018c2ecf20Sopenharmony_ci	case PIPE_CONTROL:
15028c2ecf20Sopenharmony_ci		ep_queue = &priv->qh_list[QH_CONTROL];
15038c2ecf20Sopenharmony_ci		break;
15048c2ecf20Sopenharmony_ci	case PIPE_BULK:
15058c2ecf20Sopenharmony_ci		ep_queue = &priv->qh_list[QH_BULK];
15068c2ecf20Sopenharmony_ci		break;
15078c2ecf20Sopenharmony_ci	case PIPE_INTERRUPT:
15088c2ecf20Sopenharmony_ci		if (urb->interval < 0)
15098c2ecf20Sopenharmony_ci			return -EINVAL;
15108c2ecf20Sopenharmony_ci		/* FIXME: Check bandwidth  */
15118c2ecf20Sopenharmony_ci		ep_queue = &priv->qh_list[QH_INTERRUPT];
15128c2ecf20Sopenharmony_ci		break;
15138c2ecf20Sopenharmony_ci	case PIPE_ISOCHRONOUS:
15148c2ecf20Sopenharmony_ci		dev_err(hcd->self.controller, "%s: isochronous USB packets "
15158c2ecf20Sopenharmony_ci							"not yet supported\n",
15168c2ecf20Sopenharmony_ci							__func__);
15178c2ecf20Sopenharmony_ci		return -EPIPE;
15188c2ecf20Sopenharmony_ci	default:
15198c2ecf20Sopenharmony_ci		dev_err(hcd->self.controller, "%s: unknown pipe type\n",
15208c2ecf20Sopenharmony_ci							__func__);
15218c2ecf20Sopenharmony_ci		return -EPIPE;
15228c2ecf20Sopenharmony_ci	}
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_ci	if (usb_pipein(urb->pipe))
15258c2ecf20Sopenharmony_ci		urb->actual_length = 0;
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci	packetize_urb(hcd, urb, &new_qtds, mem_flags);
15288c2ecf20Sopenharmony_ci	if (list_empty(&new_qtds))
15298c2ecf20Sopenharmony_ci		return -ENOMEM;
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_ci	retval = 0;
15328c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, spinflags);
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci	if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
15358c2ecf20Sopenharmony_ci		retval = -ESHUTDOWN;
15368c2ecf20Sopenharmony_ci		qtd_list_free(&new_qtds);
15378c2ecf20Sopenharmony_ci		goto out;
15388c2ecf20Sopenharmony_ci	}
15398c2ecf20Sopenharmony_ci	retval = usb_hcd_link_urb_to_ep(hcd, urb);
15408c2ecf20Sopenharmony_ci	if (retval) {
15418c2ecf20Sopenharmony_ci		qtd_list_free(&new_qtds);
15428c2ecf20Sopenharmony_ci		goto out;
15438c2ecf20Sopenharmony_ci	}
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_ci	qh = urb->ep->hcpriv;
15468c2ecf20Sopenharmony_ci	if (qh) {
15478c2ecf20Sopenharmony_ci		qh_in_queue = 0;
15488c2ecf20Sopenharmony_ci		list_for_each_entry(qhit, ep_queue, qh_list) {
15498c2ecf20Sopenharmony_ci			if (qhit == qh) {
15508c2ecf20Sopenharmony_ci				qh_in_queue = 1;
15518c2ecf20Sopenharmony_ci				break;
15528c2ecf20Sopenharmony_ci			}
15538c2ecf20Sopenharmony_ci		}
15548c2ecf20Sopenharmony_ci		if (!qh_in_queue)
15558c2ecf20Sopenharmony_ci			list_add_tail(&qh->qh_list, ep_queue);
15568c2ecf20Sopenharmony_ci	} else {
15578c2ecf20Sopenharmony_ci		qh = qh_alloc(GFP_ATOMIC);
15588c2ecf20Sopenharmony_ci		if (!qh) {
15598c2ecf20Sopenharmony_ci			retval = -ENOMEM;
15608c2ecf20Sopenharmony_ci			usb_hcd_unlink_urb_from_ep(hcd, urb);
15618c2ecf20Sopenharmony_ci			qtd_list_free(&new_qtds);
15628c2ecf20Sopenharmony_ci			goto out;
15638c2ecf20Sopenharmony_ci		}
15648c2ecf20Sopenharmony_ci		list_add_tail(&qh->qh_list, ep_queue);
15658c2ecf20Sopenharmony_ci		urb->ep->hcpriv = qh;
15668c2ecf20Sopenharmony_ci	}
15678c2ecf20Sopenharmony_ci
15688c2ecf20Sopenharmony_ci	list_splice_tail(&new_qtds, &qh->qtd_list);
15698c2ecf20Sopenharmony_ci	schedule_ptds(hcd);
15708c2ecf20Sopenharmony_ci
15718c2ecf20Sopenharmony_ciout:
15728c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, spinflags);
15738c2ecf20Sopenharmony_ci	return retval;
15748c2ecf20Sopenharmony_ci}
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_cistatic void kill_transfer(struct usb_hcd *hcd, struct urb *urb,
15778c2ecf20Sopenharmony_ci		struct isp1760_qh *qh)
15788c2ecf20Sopenharmony_ci{
15798c2ecf20Sopenharmony_ci	struct isp1760_hcd *priv = hcd_to_priv(hcd);
15808c2ecf20Sopenharmony_ci	int skip_map;
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci	WARN_ON(qh->slot == -1);
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci	/* We need to forcefully reclaim the slot since some transfers never
15858c2ecf20Sopenharmony_ci	   return, e.g. interrupt transfers and NAKed bulk transfers. */
15868c2ecf20Sopenharmony_ci	if (usb_pipecontrol(urb->pipe) || usb_pipebulk(urb->pipe)) {
15878c2ecf20Sopenharmony_ci		skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG);
15888c2ecf20Sopenharmony_ci		skip_map |= (1 << qh->slot);
15898c2ecf20Sopenharmony_ci		reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map);
15908c2ecf20Sopenharmony_ci		priv->atl_slots[qh->slot].qh = NULL;
15918c2ecf20Sopenharmony_ci		priv->atl_slots[qh->slot].qtd = NULL;
15928c2ecf20Sopenharmony_ci	} else {
15938c2ecf20Sopenharmony_ci		skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG);
15948c2ecf20Sopenharmony_ci		skip_map |= (1 << qh->slot);
15958c2ecf20Sopenharmony_ci		reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map);
15968c2ecf20Sopenharmony_ci		priv->int_slots[qh->slot].qh = NULL;
15978c2ecf20Sopenharmony_ci		priv->int_slots[qh->slot].qtd = NULL;
15988c2ecf20Sopenharmony_ci	}
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci	qh->slot = -1;
16018c2ecf20Sopenharmony_ci}
16028c2ecf20Sopenharmony_ci
16038c2ecf20Sopenharmony_ci/*
16048c2ecf20Sopenharmony_ci * Retire the qtds beginning at 'qtd' and belonging all to the same urb, killing
16058c2ecf20Sopenharmony_ci * any active transfer belonging to the urb in the process.
16068c2ecf20Sopenharmony_ci */
16078c2ecf20Sopenharmony_cistatic void dequeue_urb_from_qtd(struct usb_hcd *hcd, struct isp1760_qh *qh,
16088c2ecf20Sopenharmony_ci						struct isp1760_qtd *qtd)
16098c2ecf20Sopenharmony_ci{
16108c2ecf20Sopenharmony_ci	struct urb *urb;
16118c2ecf20Sopenharmony_ci	int urb_was_running;
16128c2ecf20Sopenharmony_ci
16138c2ecf20Sopenharmony_ci	urb = qtd->urb;
16148c2ecf20Sopenharmony_ci	urb_was_running = 0;
16158c2ecf20Sopenharmony_ci	list_for_each_entry_from(qtd, &qh->qtd_list, qtd_list) {
16168c2ecf20Sopenharmony_ci		if (qtd->urb != urb)
16178c2ecf20Sopenharmony_ci			break;
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci		if (qtd->status >= QTD_XFER_STARTED)
16208c2ecf20Sopenharmony_ci			urb_was_running = 1;
16218c2ecf20Sopenharmony_ci		if (last_qtd_of_urb(qtd, qh) &&
16228c2ecf20Sopenharmony_ci					(qtd->status >= QTD_XFER_COMPLETE))
16238c2ecf20Sopenharmony_ci			urb_was_running = 0;
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_ci		if (qtd->status == QTD_XFER_STARTED)
16268c2ecf20Sopenharmony_ci			kill_transfer(hcd, urb, qh);
16278c2ecf20Sopenharmony_ci		qtd->status = QTD_RETIRE;
16288c2ecf20Sopenharmony_ci	}
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_ci	if ((urb->dev->speed != USB_SPEED_HIGH) && urb_was_running) {
16318c2ecf20Sopenharmony_ci		qh->tt_buffer_dirty = 1;
16328c2ecf20Sopenharmony_ci		if (usb_hub_clear_tt_buffer(urb))
16338c2ecf20Sopenharmony_ci			/* Clear failed; let's hope things work anyway */
16348c2ecf20Sopenharmony_ci			qh->tt_buffer_dirty = 0;
16358c2ecf20Sopenharmony_ci	}
16368c2ecf20Sopenharmony_ci}
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_cistatic int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
16398c2ecf20Sopenharmony_ci		int status)
16408c2ecf20Sopenharmony_ci{
16418c2ecf20Sopenharmony_ci	struct isp1760_hcd *priv = hcd_to_priv(hcd);
16428c2ecf20Sopenharmony_ci	unsigned long spinflags;
16438c2ecf20Sopenharmony_ci	struct isp1760_qh *qh;
16448c2ecf20Sopenharmony_ci	struct isp1760_qtd *qtd;
16458c2ecf20Sopenharmony_ci	int retval = 0;
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, spinflags);
16488c2ecf20Sopenharmony_ci	retval = usb_hcd_check_unlink_urb(hcd, urb, status);
16498c2ecf20Sopenharmony_ci	if (retval)
16508c2ecf20Sopenharmony_ci		goto out;
16518c2ecf20Sopenharmony_ci
16528c2ecf20Sopenharmony_ci	qh = urb->ep->hcpriv;
16538c2ecf20Sopenharmony_ci	if (!qh) {
16548c2ecf20Sopenharmony_ci		retval = -EINVAL;
16558c2ecf20Sopenharmony_ci		goto out;
16568c2ecf20Sopenharmony_ci	}
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ci	list_for_each_entry(qtd, &qh->qtd_list, qtd_list)
16598c2ecf20Sopenharmony_ci		if (qtd->urb == urb) {
16608c2ecf20Sopenharmony_ci			dequeue_urb_from_qtd(hcd, qh, qtd);
16618c2ecf20Sopenharmony_ci			list_move(&qtd->qtd_list, &qh->qtd_list);
16628c2ecf20Sopenharmony_ci			break;
16638c2ecf20Sopenharmony_ci		}
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_ci	urb->status = status;
16668c2ecf20Sopenharmony_ci	schedule_ptds(hcd);
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ciout:
16698c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, spinflags);
16708c2ecf20Sopenharmony_ci	return retval;
16718c2ecf20Sopenharmony_ci}
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_cistatic void isp1760_endpoint_disable(struct usb_hcd *hcd,
16748c2ecf20Sopenharmony_ci		struct usb_host_endpoint *ep)
16758c2ecf20Sopenharmony_ci{
16768c2ecf20Sopenharmony_ci	struct isp1760_hcd *priv = hcd_to_priv(hcd);
16778c2ecf20Sopenharmony_ci	unsigned long spinflags;
16788c2ecf20Sopenharmony_ci	struct isp1760_qh *qh, *qh_iter;
16798c2ecf20Sopenharmony_ci	int i;
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, spinflags);
16828c2ecf20Sopenharmony_ci
16838c2ecf20Sopenharmony_ci	qh = ep->hcpriv;
16848c2ecf20Sopenharmony_ci	if (!qh)
16858c2ecf20Sopenharmony_ci		goto out;
16868c2ecf20Sopenharmony_ci
16878c2ecf20Sopenharmony_ci	WARN_ON(!list_empty(&qh->qtd_list));
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_ci	for (i = 0; i < QH_END; i++)
16908c2ecf20Sopenharmony_ci		list_for_each_entry(qh_iter, &priv->qh_list[i], qh_list)
16918c2ecf20Sopenharmony_ci			if (qh_iter == qh) {
16928c2ecf20Sopenharmony_ci				list_del(&qh_iter->qh_list);
16938c2ecf20Sopenharmony_ci				i = QH_END;
16948c2ecf20Sopenharmony_ci				break;
16958c2ecf20Sopenharmony_ci			}
16968c2ecf20Sopenharmony_ci	qh_free(qh);
16978c2ecf20Sopenharmony_ci	ep->hcpriv = NULL;
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_ci	schedule_ptds(hcd);
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ciout:
17028c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, spinflags);
17038c2ecf20Sopenharmony_ci}
17048c2ecf20Sopenharmony_ci
17058c2ecf20Sopenharmony_cistatic int isp1760_hub_status_data(struct usb_hcd *hcd, char *buf)
17068c2ecf20Sopenharmony_ci{
17078c2ecf20Sopenharmony_ci	struct isp1760_hcd *priv = hcd_to_priv(hcd);
17088c2ecf20Sopenharmony_ci	u32 temp, status = 0;
17098c2ecf20Sopenharmony_ci	u32 mask;
17108c2ecf20Sopenharmony_ci	int retval = 1;
17118c2ecf20Sopenharmony_ci	unsigned long flags;
17128c2ecf20Sopenharmony_ci
17138c2ecf20Sopenharmony_ci	/* if !PM, root hub timers won't get shut down ... */
17148c2ecf20Sopenharmony_ci	if (!HC_IS_RUNNING(hcd->state))
17158c2ecf20Sopenharmony_ci		return 0;
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci	/* init status to no-changes */
17188c2ecf20Sopenharmony_ci	buf[0] = 0;
17198c2ecf20Sopenharmony_ci	mask = PORT_CSC;
17208c2ecf20Sopenharmony_ci
17218c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
17228c2ecf20Sopenharmony_ci	temp = reg_read32(hcd->regs, HC_PORTSC1);
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci	if (temp & PORT_OWNER) {
17258c2ecf20Sopenharmony_ci		if (temp & PORT_CSC) {
17268c2ecf20Sopenharmony_ci			temp &= ~PORT_CSC;
17278c2ecf20Sopenharmony_ci			reg_write32(hcd->regs, HC_PORTSC1, temp);
17288c2ecf20Sopenharmony_ci			goto done;
17298c2ecf20Sopenharmony_ci		}
17308c2ecf20Sopenharmony_ci	}
17318c2ecf20Sopenharmony_ci
17328c2ecf20Sopenharmony_ci	/*
17338c2ecf20Sopenharmony_ci	 * Return status information even for ports with OWNER set.
17348c2ecf20Sopenharmony_ci	 * Otherwise hub_wq wouldn't see the disconnect event when a
17358c2ecf20Sopenharmony_ci	 * high-speed device is switched over to the companion
17368c2ecf20Sopenharmony_ci	 * controller by the user.
17378c2ecf20Sopenharmony_ci	 */
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_ci	if ((temp & mask) != 0
17408c2ecf20Sopenharmony_ci			|| ((temp & PORT_RESUME) != 0
17418c2ecf20Sopenharmony_ci				&& time_after_eq(jiffies,
17428c2ecf20Sopenharmony_ci					priv->reset_done))) {
17438c2ecf20Sopenharmony_ci		buf [0] |= 1 << (0 + 1);
17448c2ecf20Sopenharmony_ci		status = STS_PCD;
17458c2ecf20Sopenharmony_ci	}
17468c2ecf20Sopenharmony_ci	/* FIXME autosuspend idle root hubs */
17478c2ecf20Sopenharmony_cidone:
17488c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
17498c2ecf20Sopenharmony_ci	return status ? retval : 0;
17508c2ecf20Sopenharmony_ci}
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_cistatic void isp1760_hub_descriptor(struct isp1760_hcd *priv,
17538c2ecf20Sopenharmony_ci		struct usb_hub_descriptor *desc)
17548c2ecf20Sopenharmony_ci{
17558c2ecf20Sopenharmony_ci	int ports = HCS_N_PORTS(priv->hcs_params);
17568c2ecf20Sopenharmony_ci	u16 temp;
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_ci	desc->bDescriptorType = USB_DT_HUB;
17598c2ecf20Sopenharmony_ci	/* priv 1.0, 2.3.9 says 20ms max */
17608c2ecf20Sopenharmony_ci	desc->bPwrOn2PwrGood = 10;
17618c2ecf20Sopenharmony_ci	desc->bHubContrCurrent = 0;
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ci	desc->bNbrPorts = ports;
17648c2ecf20Sopenharmony_ci	temp = 1 + (ports / 8);
17658c2ecf20Sopenharmony_ci	desc->bDescLength = 7 + 2 * temp;
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_ci	/* ports removable, and usb 1.0 legacy PortPwrCtrlMask */
17688c2ecf20Sopenharmony_ci	memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
17698c2ecf20Sopenharmony_ci	memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
17708c2ecf20Sopenharmony_ci
17718c2ecf20Sopenharmony_ci	/* per-port overcurrent reporting */
17728c2ecf20Sopenharmony_ci	temp = HUB_CHAR_INDV_PORT_OCPM;
17738c2ecf20Sopenharmony_ci	if (HCS_PPC(priv->hcs_params))
17748c2ecf20Sopenharmony_ci		/* per-port power control */
17758c2ecf20Sopenharmony_ci		temp |= HUB_CHAR_INDV_PORT_LPSM;
17768c2ecf20Sopenharmony_ci	else
17778c2ecf20Sopenharmony_ci		/* no power switching */
17788c2ecf20Sopenharmony_ci		temp |= HUB_CHAR_NO_LPSM;
17798c2ecf20Sopenharmony_ci	desc->wHubCharacteristics = cpu_to_le16(temp);
17808c2ecf20Sopenharmony_ci}
17818c2ecf20Sopenharmony_ci
17828c2ecf20Sopenharmony_ci#define	PORT_WAKE_BITS	(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_cistatic int check_reset_complete(struct usb_hcd *hcd, int index,
17858c2ecf20Sopenharmony_ci		int port_status)
17868c2ecf20Sopenharmony_ci{
17878c2ecf20Sopenharmony_ci	if (!(port_status & PORT_CONNECT))
17888c2ecf20Sopenharmony_ci		return port_status;
17898c2ecf20Sopenharmony_ci
17908c2ecf20Sopenharmony_ci	/* if reset finished and it's still not enabled -- handoff */
17918c2ecf20Sopenharmony_ci	if (!(port_status & PORT_PE)) {
17928c2ecf20Sopenharmony_ci
17938c2ecf20Sopenharmony_ci		dev_info(hcd->self.controller,
17948c2ecf20Sopenharmony_ci					"port %d full speed --> companion\n",
17958c2ecf20Sopenharmony_ci					index + 1);
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_ci		port_status |= PORT_OWNER;
17988c2ecf20Sopenharmony_ci		port_status &= ~PORT_RWC_BITS;
17998c2ecf20Sopenharmony_ci		reg_write32(hcd->regs, HC_PORTSC1, port_status);
18008c2ecf20Sopenharmony_ci
18018c2ecf20Sopenharmony_ci	} else
18028c2ecf20Sopenharmony_ci		dev_info(hcd->self.controller, "port %d high speed\n",
18038c2ecf20Sopenharmony_ci								index + 1);
18048c2ecf20Sopenharmony_ci
18058c2ecf20Sopenharmony_ci	return port_status;
18068c2ecf20Sopenharmony_ci}
18078c2ecf20Sopenharmony_ci
18088c2ecf20Sopenharmony_cistatic int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
18098c2ecf20Sopenharmony_ci		u16 wValue, u16 wIndex, char *buf, u16 wLength)
18108c2ecf20Sopenharmony_ci{
18118c2ecf20Sopenharmony_ci	struct isp1760_hcd *priv = hcd_to_priv(hcd);
18128c2ecf20Sopenharmony_ci	int ports = HCS_N_PORTS(priv->hcs_params);
18138c2ecf20Sopenharmony_ci	u32 temp, status;
18148c2ecf20Sopenharmony_ci	unsigned long flags;
18158c2ecf20Sopenharmony_ci	int retval = 0;
18168c2ecf20Sopenharmony_ci
18178c2ecf20Sopenharmony_ci	/*
18188c2ecf20Sopenharmony_ci	 * FIXME:  support SetPortFeatures USB_PORT_FEAT_INDICATOR.
18198c2ecf20Sopenharmony_ci	 * HCS_INDICATOR may say we can change LEDs to off/amber/green.
18208c2ecf20Sopenharmony_ci	 * (track current state ourselves) ... blink for diagnostics,
18218c2ecf20Sopenharmony_ci	 * power, "this is the one", etc.  EHCI spec supports this.
18228c2ecf20Sopenharmony_ci	 */
18238c2ecf20Sopenharmony_ci
18248c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
18258c2ecf20Sopenharmony_ci	switch (typeReq) {
18268c2ecf20Sopenharmony_ci	case ClearHubFeature:
18278c2ecf20Sopenharmony_ci		switch (wValue) {
18288c2ecf20Sopenharmony_ci		case C_HUB_LOCAL_POWER:
18298c2ecf20Sopenharmony_ci		case C_HUB_OVER_CURRENT:
18308c2ecf20Sopenharmony_ci			/* no hub-wide feature/status flags */
18318c2ecf20Sopenharmony_ci			break;
18328c2ecf20Sopenharmony_ci		default:
18338c2ecf20Sopenharmony_ci			goto error;
18348c2ecf20Sopenharmony_ci		}
18358c2ecf20Sopenharmony_ci		break;
18368c2ecf20Sopenharmony_ci	case ClearPortFeature:
18378c2ecf20Sopenharmony_ci		if (!wIndex || wIndex > ports)
18388c2ecf20Sopenharmony_ci			goto error;
18398c2ecf20Sopenharmony_ci		wIndex--;
18408c2ecf20Sopenharmony_ci		temp = reg_read32(hcd->regs, HC_PORTSC1);
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_ci		/*
18438c2ecf20Sopenharmony_ci		 * Even if OWNER is set, so the port is owned by the
18448c2ecf20Sopenharmony_ci		 * companion controller, hub_wq needs to be able to clear
18458c2ecf20Sopenharmony_ci		 * the port-change status bits (especially
18468c2ecf20Sopenharmony_ci		 * USB_PORT_STAT_C_CONNECTION).
18478c2ecf20Sopenharmony_ci		 */
18488c2ecf20Sopenharmony_ci
18498c2ecf20Sopenharmony_ci		switch (wValue) {
18508c2ecf20Sopenharmony_ci		case USB_PORT_FEAT_ENABLE:
18518c2ecf20Sopenharmony_ci			reg_write32(hcd->regs, HC_PORTSC1, temp & ~PORT_PE);
18528c2ecf20Sopenharmony_ci			break;
18538c2ecf20Sopenharmony_ci		case USB_PORT_FEAT_C_ENABLE:
18548c2ecf20Sopenharmony_ci			/* XXX error? */
18558c2ecf20Sopenharmony_ci			break;
18568c2ecf20Sopenharmony_ci		case USB_PORT_FEAT_SUSPEND:
18578c2ecf20Sopenharmony_ci			if (temp & PORT_RESET)
18588c2ecf20Sopenharmony_ci				goto error;
18598c2ecf20Sopenharmony_ci
18608c2ecf20Sopenharmony_ci			if (temp & PORT_SUSPEND) {
18618c2ecf20Sopenharmony_ci				if ((temp & PORT_PE) == 0)
18628c2ecf20Sopenharmony_ci					goto error;
18638c2ecf20Sopenharmony_ci				/* resume signaling for 20 msec */
18648c2ecf20Sopenharmony_ci				temp &= ~(PORT_RWC_BITS);
18658c2ecf20Sopenharmony_ci				reg_write32(hcd->regs, HC_PORTSC1,
18668c2ecf20Sopenharmony_ci							temp | PORT_RESUME);
18678c2ecf20Sopenharmony_ci				priv->reset_done = jiffies +
18688c2ecf20Sopenharmony_ci					msecs_to_jiffies(USB_RESUME_TIMEOUT);
18698c2ecf20Sopenharmony_ci			}
18708c2ecf20Sopenharmony_ci			break;
18718c2ecf20Sopenharmony_ci		case USB_PORT_FEAT_C_SUSPEND:
18728c2ecf20Sopenharmony_ci			/* we auto-clear this feature */
18738c2ecf20Sopenharmony_ci			break;
18748c2ecf20Sopenharmony_ci		case USB_PORT_FEAT_POWER:
18758c2ecf20Sopenharmony_ci			if (HCS_PPC(priv->hcs_params))
18768c2ecf20Sopenharmony_ci				reg_write32(hcd->regs, HC_PORTSC1,
18778c2ecf20Sopenharmony_ci							temp & ~PORT_POWER);
18788c2ecf20Sopenharmony_ci			break;
18798c2ecf20Sopenharmony_ci		case USB_PORT_FEAT_C_CONNECTION:
18808c2ecf20Sopenharmony_ci			reg_write32(hcd->regs, HC_PORTSC1, temp | PORT_CSC);
18818c2ecf20Sopenharmony_ci			break;
18828c2ecf20Sopenharmony_ci		case USB_PORT_FEAT_C_OVER_CURRENT:
18838c2ecf20Sopenharmony_ci			/* XXX error ?*/
18848c2ecf20Sopenharmony_ci			break;
18858c2ecf20Sopenharmony_ci		case USB_PORT_FEAT_C_RESET:
18868c2ecf20Sopenharmony_ci			/* GetPortStatus clears reset */
18878c2ecf20Sopenharmony_ci			break;
18888c2ecf20Sopenharmony_ci		default:
18898c2ecf20Sopenharmony_ci			goto error;
18908c2ecf20Sopenharmony_ci		}
18918c2ecf20Sopenharmony_ci		reg_read32(hcd->regs, HC_USBCMD);
18928c2ecf20Sopenharmony_ci		break;
18938c2ecf20Sopenharmony_ci	case GetHubDescriptor:
18948c2ecf20Sopenharmony_ci		isp1760_hub_descriptor(priv, (struct usb_hub_descriptor *)
18958c2ecf20Sopenharmony_ci			buf);
18968c2ecf20Sopenharmony_ci		break;
18978c2ecf20Sopenharmony_ci	case GetHubStatus:
18988c2ecf20Sopenharmony_ci		/* no hub-wide feature/status flags */
18998c2ecf20Sopenharmony_ci		memset(buf, 0, 4);
19008c2ecf20Sopenharmony_ci		break;
19018c2ecf20Sopenharmony_ci	case GetPortStatus:
19028c2ecf20Sopenharmony_ci		if (!wIndex || wIndex > ports)
19038c2ecf20Sopenharmony_ci			goto error;
19048c2ecf20Sopenharmony_ci		wIndex--;
19058c2ecf20Sopenharmony_ci		status = 0;
19068c2ecf20Sopenharmony_ci		temp = reg_read32(hcd->regs, HC_PORTSC1);
19078c2ecf20Sopenharmony_ci
19088c2ecf20Sopenharmony_ci		/* wPortChange bits */
19098c2ecf20Sopenharmony_ci		if (temp & PORT_CSC)
19108c2ecf20Sopenharmony_ci			status |= USB_PORT_STAT_C_CONNECTION << 16;
19118c2ecf20Sopenharmony_ci
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_ci		/* whoever resumes must GetPortStatus to complete it!! */
19148c2ecf20Sopenharmony_ci		if (temp & PORT_RESUME) {
19158c2ecf20Sopenharmony_ci			dev_err(hcd->self.controller, "Port resume should be skipped.\n");
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_ci			/* Remote Wakeup received? */
19188c2ecf20Sopenharmony_ci			if (!priv->reset_done) {
19198c2ecf20Sopenharmony_ci				/* resume signaling for 20 msec */
19208c2ecf20Sopenharmony_ci				priv->reset_done = jiffies
19218c2ecf20Sopenharmony_ci						+ msecs_to_jiffies(20);
19228c2ecf20Sopenharmony_ci				/* check the port again */
19238c2ecf20Sopenharmony_ci				mod_timer(&hcd->rh_timer, priv->reset_done);
19248c2ecf20Sopenharmony_ci			}
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_ci			/* resume completed? */
19278c2ecf20Sopenharmony_ci			else if (time_after_eq(jiffies,
19288c2ecf20Sopenharmony_ci					priv->reset_done)) {
19298c2ecf20Sopenharmony_ci				status |= USB_PORT_STAT_C_SUSPEND << 16;
19308c2ecf20Sopenharmony_ci				priv->reset_done = 0;
19318c2ecf20Sopenharmony_ci
19328c2ecf20Sopenharmony_ci				/* stop resume signaling */
19338c2ecf20Sopenharmony_ci				temp = reg_read32(hcd->regs, HC_PORTSC1);
19348c2ecf20Sopenharmony_ci				reg_write32(hcd->regs, HC_PORTSC1,
19358c2ecf20Sopenharmony_ci					temp & ~(PORT_RWC_BITS | PORT_RESUME));
19368c2ecf20Sopenharmony_ci				retval = handshake(hcd, HC_PORTSC1,
19378c2ecf20Sopenharmony_ci					   PORT_RESUME, 0, 2000 /* 2msec */);
19388c2ecf20Sopenharmony_ci				if (retval != 0) {
19398c2ecf20Sopenharmony_ci					dev_err(hcd->self.controller,
19408c2ecf20Sopenharmony_ci						"port %d resume error %d\n",
19418c2ecf20Sopenharmony_ci						wIndex + 1, retval);
19428c2ecf20Sopenharmony_ci					goto error;
19438c2ecf20Sopenharmony_ci				}
19448c2ecf20Sopenharmony_ci				temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10));
19458c2ecf20Sopenharmony_ci			}
19468c2ecf20Sopenharmony_ci		}
19478c2ecf20Sopenharmony_ci
19488c2ecf20Sopenharmony_ci		/* whoever resets must GetPortStatus to complete it!! */
19498c2ecf20Sopenharmony_ci		if ((temp & PORT_RESET)
19508c2ecf20Sopenharmony_ci				&& time_after_eq(jiffies,
19518c2ecf20Sopenharmony_ci					priv->reset_done)) {
19528c2ecf20Sopenharmony_ci			status |= USB_PORT_STAT_C_RESET << 16;
19538c2ecf20Sopenharmony_ci			priv->reset_done = 0;
19548c2ecf20Sopenharmony_ci
19558c2ecf20Sopenharmony_ci			/* force reset to complete */
19568c2ecf20Sopenharmony_ci			reg_write32(hcd->regs, HC_PORTSC1, temp & ~PORT_RESET);
19578c2ecf20Sopenharmony_ci			/* REVISIT:  some hardware needs 550+ usec to clear
19588c2ecf20Sopenharmony_ci			 * this bit; seems too long to spin routinely...
19598c2ecf20Sopenharmony_ci			 */
19608c2ecf20Sopenharmony_ci			retval = handshake(hcd, HC_PORTSC1,
19618c2ecf20Sopenharmony_ci					PORT_RESET, 0, 750);
19628c2ecf20Sopenharmony_ci			if (retval != 0) {
19638c2ecf20Sopenharmony_ci				dev_err(hcd->self.controller, "port %d reset error %d\n",
19648c2ecf20Sopenharmony_ci						wIndex + 1, retval);
19658c2ecf20Sopenharmony_ci				goto error;
19668c2ecf20Sopenharmony_ci			}
19678c2ecf20Sopenharmony_ci
19688c2ecf20Sopenharmony_ci			/* see what we found out */
19698c2ecf20Sopenharmony_ci			temp = check_reset_complete(hcd, wIndex,
19708c2ecf20Sopenharmony_ci					reg_read32(hcd->regs, HC_PORTSC1));
19718c2ecf20Sopenharmony_ci		}
19728c2ecf20Sopenharmony_ci		/*
19738c2ecf20Sopenharmony_ci		 * Even if OWNER is set, there's no harm letting hub_wq
19748c2ecf20Sopenharmony_ci		 * see the wPortStatus values (they should all be 0 except
19758c2ecf20Sopenharmony_ci		 * for PORT_POWER anyway).
19768c2ecf20Sopenharmony_ci		 */
19778c2ecf20Sopenharmony_ci
19788c2ecf20Sopenharmony_ci		if (temp & PORT_OWNER)
19798c2ecf20Sopenharmony_ci			dev_err(hcd->self.controller, "PORT_OWNER is set\n");
19808c2ecf20Sopenharmony_ci
19818c2ecf20Sopenharmony_ci		if (temp & PORT_CONNECT) {
19828c2ecf20Sopenharmony_ci			status |= USB_PORT_STAT_CONNECTION;
19838c2ecf20Sopenharmony_ci			/* status may be from integrated TT */
19848c2ecf20Sopenharmony_ci			status |= USB_PORT_STAT_HIGH_SPEED;
19858c2ecf20Sopenharmony_ci		}
19868c2ecf20Sopenharmony_ci		if (temp & PORT_PE)
19878c2ecf20Sopenharmony_ci			status |= USB_PORT_STAT_ENABLE;
19888c2ecf20Sopenharmony_ci		if (temp & (PORT_SUSPEND|PORT_RESUME))
19898c2ecf20Sopenharmony_ci			status |= USB_PORT_STAT_SUSPEND;
19908c2ecf20Sopenharmony_ci		if (temp & PORT_RESET)
19918c2ecf20Sopenharmony_ci			status |= USB_PORT_STAT_RESET;
19928c2ecf20Sopenharmony_ci		if (temp & PORT_POWER)
19938c2ecf20Sopenharmony_ci			status |= USB_PORT_STAT_POWER;
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci		put_unaligned(cpu_to_le32(status), (__le32 *) buf);
19968c2ecf20Sopenharmony_ci		break;
19978c2ecf20Sopenharmony_ci	case SetHubFeature:
19988c2ecf20Sopenharmony_ci		switch (wValue) {
19998c2ecf20Sopenharmony_ci		case C_HUB_LOCAL_POWER:
20008c2ecf20Sopenharmony_ci		case C_HUB_OVER_CURRENT:
20018c2ecf20Sopenharmony_ci			/* no hub-wide feature/status flags */
20028c2ecf20Sopenharmony_ci			break;
20038c2ecf20Sopenharmony_ci		default:
20048c2ecf20Sopenharmony_ci			goto error;
20058c2ecf20Sopenharmony_ci		}
20068c2ecf20Sopenharmony_ci		break;
20078c2ecf20Sopenharmony_ci	case SetPortFeature:
20088c2ecf20Sopenharmony_ci		wIndex &= 0xff;
20098c2ecf20Sopenharmony_ci		if (!wIndex || wIndex > ports)
20108c2ecf20Sopenharmony_ci			goto error;
20118c2ecf20Sopenharmony_ci		wIndex--;
20128c2ecf20Sopenharmony_ci		temp = reg_read32(hcd->regs, HC_PORTSC1);
20138c2ecf20Sopenharmony_ci		if (temp & PORT_OWNER)
20148c2ecf20Sopenharmony_ci			break;
20158c2ecf20Sopenharmony_ci
20168c2ecf20Sopenharmony_ci/*		temp &= ~PORT_RWC_BITS; */
20178c2ecf20Sopenharmony_ci		switch (wValue) {
20188c2ecf20Sopenharmony_ci		case USB_PORT_FEAT_ENABLE:
20198c2ecf20Sopenharmony_ci			reg_write32(hcd->regs, HC_PORTSC1, temp | PORT_PE);
20208c2ecf20Sopenharmony_ci			break;
20218c2ecf20Sopenharmony_ci
20228c2ecf20Sopenharmony_ci		case USB_PORT_FEAT_SUSPEND:
20238c2ecf20Sopenharmony_ci			if ((temp & PORT_PE) == 0
20248c2ecf20Sopenharmony_ci					|| (temp & PORT_RESET) != 0)
20258c2ecf20Sopenharmony_ci				goto error;
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_ci			reg_write32(hcd->regs, HC_PORTSC1, temp | PORT_SUSPEND);
20288c2ecf20Sopenharmony_ci			break;
20298c2ecf20Sopenharmony_ci		case USB_PORT_FEAT_POWER:
20308c2ecf20Sopenharmony_ci			if (HCS_PPC(priv->hcs_params))
20318c2ecf20Sopenharmony_ci				reg_write32(hcd->regs, HC_PORTSC1,
20328c2ecf20Sopenharmony_ci							temp | PORT_POWER);
20338c2ecf20Sopenharmony_ci			break;
20348c2ecf20Sopenharmony_ci		case USB_PORT_FEAT_RESET:
20358c2ecf20Sopenharmony_ci			if (temp & PORT_RESUME)
20368c2ecf20Sopenharmony_ci				goto error;
20378c2ecf20Sopenharmony_ci			/* line status bits may report this as low speed,
20388c2ecf20Sopenharmony_ci			 * which can be fine if this root hub has a
20398c2ecf20Sopenharmony_ci			 * transaction translator built in.
20408c2ecf20Sopenharmony_ci			 */
20418c2ecf20Sopenharmony_ci			if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
20428c2ecf20Sopenharmony_ci					&& PORT_USB11(temp)) {
20438c2ecf20Sopenharmony_ci				temp |= PORT_OWNER;
20448c2ecf20Sopenharmony_ci			} else {
20458c2ecf20Sopenharmony_ci				temp |= PORT_RESET;
20468c2ecf20Sopenharmony_ci				temp &= ~PORT_PE;
20478c2ecf20Sopenharmony_ci
20488c2ecf20Sopenharmony_ci				/*
20498c2ecf20Sopenharmony_ci				 * caller must wait, then call GetPortStatus
20508c2ecf20Sopenharmony_ci				 * usb 2.0 spec says 50 ms resets on root
20518c2ecf20Sopenharmony_ci				 */
20528c2ecf20Sopenharmony_ci				priv->reset_done = jiffies +
20538c2ecf20Sopenharmony_ci					msecs_to_jiffies(50);
20548c2ecf20Sopenharmony_ci			}
20558c2ecf20Sopenharmony_ci			reg_write32(hcd->regs, HC_PORTSC1, temp);
20568c2ecf20Sopenharmony_ci			break;
20578c2ecf20Sopenharmony_ci		default:
20588c2ecf20Sopenharmony_ci			goto error;
20598c2ecf20Sopenharmony_ci		}
20608c2ecf20Sopenharmony_ci		reg_read32(hcd->regs, HC_USBCMD);
20618c2ecf20Sopenharmony_ci		break;
20628c2ecf20Sopenharmony_ci
20638c2ecf20Sopenharmony_ci	default:
20648c2ecf20Sopenharmony_cierror:
20658c2ecf20Sopenharmony_ci		/* "stall" on error */
20668c2ecf20Sopenharmony_ci		retval = -EPIPE;
20678c2ecf20Sopenharmony_ci	}
20688c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
20698c2ecf20Sopenharmony_ci	return retval;
20708c2ecf20Sopenharmony_ci}
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_cistatic int isp1760_get_frame(struct usb_hcd *hcd)
20738c2ecf20Sopenharmony_ci{
20748c2ecf20Sopenharmony_ci	struct isp1760_hcd *priv = hcd_to_priv(hcd);
20758c2ecf20Sopenharmony_ci	u32 fr;
20768c2ecf20Sopenharmony_ci
20778c2ecf20Sopenharmony_ci	fr = reg_read32(hcd->regs, HC_FRINDEX);
20788c2ecf20Sopenharmony_ci	return (fr >> 3) % priv->periodic_size;
20798c2ecf20Sopenharmony_ci}
20808c2ecf20Sopenharmony_ci
20818c2ecf20Sopenharmony_cistatic void isp1760_stop(struct usb_hcd *hcd)
20828c2ecf20Sopenharmony_ci{
20838c2ecf20Sopenharmony_ci	struct isp1760_hcd *priv = hcd_to_priv(hcd);
20848c2ecf20Sopenharmony_ci	u32 temp;
20858c2ecf20Sopenharmony_ci
20868c2ecf20Sopenharmony_ci	del_timer(&errata2_timer);
20878c2ecf20Sopenharmony_ci
20888c2ecf20Sopenharmony_ci	isp1760_hub_control(hcd, ClearPortFeature, USB_PORT_FEAT_POWER,	1,
20898c2ecf20Sopenharmony_ci			NULL, 0);
20908c2ecf20Sopenharmony_ci	msleep(20);
20918c2ecf20Sopenharmony_ci
20928c2ecf20Sopenharmony_ci	spin_lock_irq(&priv->lock);
20938c2ecf20Sopenharmony_ci	ehci_reset(hcd);
20948c2ecf20Sopenharmony_ci	/* Disable IRQ */
20958c2ecf20Sopenharmony_ci	temp = reg_read32(hcd->regs, HC_HW_MODE_CTRL);
20968c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_HW_MODE_CTRL, temp &= ~HW_GLOBAL_INTR_EN);
20978c2ecf20Sopenharmony_ci	spin_unlock_irq(&priv->lock);
20988c2ecf20Sopenharmony_ci
20998c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_CONFIGFLAG, 0);
21008c2ecf20Sopenharmony_ci}
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_cistatic void isp1760_shutdown(struct usb_hcd *hcd)
21038c2ecf20Sopenharmony_ci{
21048c2ecf20Sopenharmony_ci	u32 command, temp;
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_ci	isp1760_stop(hcd);
21078c2ecf20Sopenharmony_ci	temp = reg_read32(hcd->regs, HC_HW_MODE_CTRL);
21088c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_HW_MODE_CTRL, temp &= ~HW_GLOBAL_INTR_EN);
21098c2ecf20Sopenharmony_ci
21108c2ecf20Sopenharmony_ci	command = reg_read32(hcd->regs, HC_USBCMD);
21118c2ecf20Sopenharmony_ci	command &= ~CMD_RUN;
21128c2ecf20Sopenharmony_ci	reg_write32(hcd->regs, HC_USBCMD, command);
21138c2ecf20Sopenharmony_ci}
21148c2ecf20Sopenharmony_ci
21158c2ecf20Sopenharmony_cistatic void isp1760_clear_tt_buffer_complete(struct usb_hcd *hcd,
21168c2ecf20Sopenharmony_ci						struct usb_host_endpoint *ep)
21178c2ecf20Sopenharmony_ci{
21188c2ecf20Sopenharmony_ci	struct isp1760_hcd *priv = hcd_to_priv(hcd);
21198c2ecf20Sopenharmony_ci	struct isp1760_qh *qh = ep->hcpriv;
21208c2ecf20Sopenharmony_ci	unsigned long spinflags;
21218c2ecf20Sopenharmony_ci
21228c2ecf20Sopenharmony_ci	if (!qh)
21238c2ecf20Sopenharmony_ci		return;
21248c2ecf20Sopenharmony_ci
21258c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, spinflags);
21268c2ecf20Sopenharmony_ci	qh->tt_buffer_dirty = 0;
21278c2ecf20Sopenharmony_ci	schedule_ptds(hcd);
21288c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, spinflags);
21298c2ecf20Sopenharmony_ci}
21308c2ecf20Sopenharmony_ci
21318c2ecf20Sopenharmony_ci
21328c2ecf20Sopenharmony_cistatic const struct hc_driver isp1760_hc_driver = {
21338c2ecf20Sopenharmony_ci	.description		= "isp1760-hcd",
21348c2ecf20Sopenharmony_ci	.product_desc		= "NXP ISP1760 USB Host Controller",
21358c2ecf20Sopenharmony_ci	.hcd_priv_size		= sizeof(struct isp1760_hcd *),
21368c2ecf20Sopenharmony_ci	.irq			= isp1760_irq,
21378c2ecf20Sopenharmony_ci	.flags			= HCD_MEMORY | HCD_USB2,
21388c2ecf20Sopenharmony_ci	.reset			= isp1760_hc_setup,
21398c2ecf20Sopenharmony_ci	.start			= isp1760_run,
21408c2ecf20Sopenharmony_ci	.stop			= isp1760_stop,
21418c2ecf20Sopenharmony_ci	.shutdown		= isp1760_shutdown,
21428c2ecf20Sopenharmony_ci	.urb_enqueue		= isp1760_urb_enqueue,
21438c2ecf20Sopenharmony_ci	.urb_dequeue		= isp1760_urb_dequeue,
21448c2ecf20Sopenharmony_ci	.endpoint_disable	= isp1760_endpoint_disable,
21458c2ecf20Sopenharmony_ci	.get_frame_number	= isp1760_get_frame,
21468c2ecf20Sopenharmony_ci	.hub_status_data	= isp1760_hub_status_data,
21478c2ecf20Sopenharmony_ci	.hub_control		= isp1760_hub_control,
21488c2ecf20Sopenharmony_ci	.clear_tt_buffer_complete	= isp1760_clear_tt_buffer_complete,
21498c2ecf20Sopenharmony_ci};
21508c2ecf20Sopenharmony_ci
21518c2ecf20Sopenharmony_ciint __init isp1760_init_kmem_once(void)
21528c2ecf20Sopenharmony_ci{
21538c2ecf20Sopenharmony_ci	urb_listitem_cachep = kmem_cache_create("isp1760_urb_listitem",
21548c2ecf20Sopenharmony_ci			sizeof(struct urb_listitem), 0, SLAB_TEMPORARY |
21558c2ecf20Sopenharmony_ci			SLAB_MEM_SPREAD, NULL);
21568c2ecf20Sopenharmony_ci
21578c2ecf20Sopenharmony_ci	if (!urb_listitem_cachep)
21588c2ecf20Sopenharmony_ci		return -ENOMEM;
21598c2ecf20Sopenharmony_ci
21608c2ecf20Sopenharmony_ci	qtd_cachep = kmem_cache_create("isp1760_qtd",
21618c2ecf20Sopenharmony_ci			sizeof(struct isp1760_qtd), 0, SLAB_TEMPORARY |
21628c2ecf20Sopenharmony_ci			SLAB_MEM_SPREAD, NULL);
21638c2ecf20Sopenharmony_ci
21648c2ecf20Sopenharmony_ci	if (!qtd_cachep)
21658c2ecf20Sopenharmony_ci		return -ENOMEM;
21668c2ecf20Sopenharmony_ci
21678c2ecf20Sopenharmony_ci	qh_cachep = kmem_cache_create("isp1760_qh", sizeof(struct isp1760_qh),
21688c2ecf20Sopenharmony_ci			0, SLAB_TEMPORARY | SLAB_MEM_SPREAD, NULL);
21698c2ecf20Sopenharmony_ci
21708c2ecf20Sopenharmony_ci	if (!qh_cachep) {
21718c2ecf20Sopenharmony_ci		kmem_cache_destroy(qtd_cachep);
21728c2ecf20Sopenharmony_ci		return -ENOMEM;
21738c2ecf20Sopenharmony_ci	}
21748c2ecf20Sopenharmony_ci
21758c2ecf20Sopenharmony_ci	return 0;
21768c2ecf20Sopenharmony_ci}
21778c2ecf20Sopenharmony_ci
21788c2ecf20Sopenharmony_civoid isp1760_deinit_kmem_cache(void)
21798c2ecf20Sopenharmony_ci{
21808c2ecf20Sopenharmony_ci	kmem_cache_destroy(qtd_cachep);
21818c2ecf20Sopenharmony_ci	kmem_cache_destroy(qh_cachep);
21828c2ecf20Sopenharmony_ci	kmem_cache_destroy(urb_listitem_cachep);
21838c2ecf20Sopenharmony_ci}
21848c2ecf20Sopenharmony_ci
21858c2ecf20Sopenharmony_ciint isp1760_hcd_register(struct isp1760_hcd *priv, void __iomem *regs,
21868c2ecf20Sopenharmony_ci			 struct resource *mem, int irq, unsigned long irqflags,
21878c2ecf20Sopenharmony_ci			 struct device *dev)
21888c2ecf20Sopenharmony_ci{
21898c2ecf20Sopenharmony_ci	struct usb_hcd *hcd;
21908c2ecf20Sopenharmony_ci	int ret;
21918c2ecf20Sopenharmony_ci
21928c2ecf20Sopenharmony_ci	hcd = usb_create_hcd(&isp1760_hc_driver, dev, dev_name(dev));
21938c2ecf20Sopenharmony_ci	if (!hcd)
21948c2ecf20Sopenharmony_ci		return -ENOMEM;
21958c2ecf20Sopenharmony_ci
21968c2ecf20Sopenharmony_ci	*(struct isp1760_hcd **)hcd->hcd_priv = priv;
21978c2ecf20Sopenharmony_ci
21988c2ecf20Sopenharmony_ci	priv->hcd = hcd;
21998c2ecf20Sopenharmony_ci
22008c2ecf20Sopenharmony_ci	init_memory(priv);
22018c2ecf20Sopenharmony_ci
22028c2ecf20Sopenharmony_ci	hcd->irq = irq;
22038c2ecf20Sopenharmony_ci	hcd->regs = regs;
22048c2ecf20Sopenharmony_ci	hcd->rsrc_start = mem->start;
22058c2ecf20Sopenharmony_ci	hcd->rsrc_len = resource_size(mem);
22068c2ecf20Sopenharmony_ci
22078c2ecf20Sopenharmony_ci	/* This driver doesn't support wakeup requests */
22088c2ecf20Sopenharmony_ci	hcd->cant_recv_wakeups = 1;
22098c2ecf20Sopenharmony_ci
22108c2ecf20Sopenharmony_ci	ret = usb_add_hcd(hcd, irq, irqflags);
22118c2ecf20Sopenharmony_ci	if (ret)
22128c2ecf20Sopenharmony_ci		goto error;
22138c2ecf20Sopenharmony_ci
22148c2ecf20Sopenharmony_ci	device_wakeup_enable(hcd->self.controller);
22158c2ecf20Sopenharmony_ci
22168c2ecf20Sopenharmony_ci	return 0;
22178c2ecf20Sopenharmony_ci
22188c2ecf20Sopenharmony_cierror:
22198c2ecf20Sopenharmony_ci	usb_put_hcd(hcd);
22208c2ecf20Sopenharmony_ci	return ret;
22218c2ecf20Sopenharmony_ci}
22228c2ecf20Sopenharmony_ci
22238c2ecf20Sopenharmony_civoid isp1760_hcd_unregister(struct isp1760_hcd *priv)
22248c2ecf20Sopenharmony_ci{
22258c2ecf20Sopenharmony_ci	if (!priv->hcd)
22268c2ecf20Sopenharmony_ci		return;
22278c2ecf20Sopenharmony_ci
22288c2ecf20Sopenharmony_ci	usb_remove_hcd(priv->hcd);
22298c2ecf20Sopenharmony_ci	usb_put_hcd(priv->hcd);
22308c2ecf20Sopenharmony_ci}
2231