xref: /linux/drivers/usb/isp1760/isp1760-hcd.c (revision 7ec462100ef9142344ddbf86f2c3008b97acddbe)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
27ef077a8SLaurent Pinchart /*
37ef077a8SLaurent Pinchart  * Driver for the NXP ISP1760 chip
47ef077a8SLaurent Pinchart  *
57ef077a8SLaurent Pinchart  * However, the code might contain some bugs. What doesn't work for sure is:
67ef077a8SLaurent Pinchart  * - ISO
77ef077a8SLaurent Pinchart  * - OTG
87ef077a8SLaurent Pinchart  e The interrupt line is configured as active low, level.
97ef077a8SLaurent Pinchart  *
107ef077a8SLaurent Pinchart  * (c) 2007 Sebastian Siewior <bigeasy@linutronix.de>
117ef077a8SLaurent Pinchart  *
127ef077a8SLaurent Pinchart  * (c) 2011 Arvid Brodin <arvid.brodin@enea.com>
137ef077a8SLaurent Pinchart  *
1460d789f3SRui Miguel Silva  * Copyright 2021 Linaro, Rui Miguel Silva <rui.silva@linaro.org>
1560d789f3SRui Miguel Silva  *
167ef077a8SLaurent Pinchart  */
177ef077a8SLaurent Pinchart #include <linux/gpio/consumer.h>
187ef077a8SLaurent Pinchart #include <linux/module.h>
197ef077a8SLaurent Pinchart #include <linux/kernel.h>
207ef077a8SLaurent Pinchart #include <linux/slab.h>
217ef077a8SLaurent Pinchart #include <linux/list.h>
227ef077a8SLaurent Pinchart #include <linux/usb.h>
237ef077a8SLaurent Pinchart #include <linux/usb/hcd.h>
247ef077a8SLaurent Pinchart #include <linux/debugfs.h>
257ef077a8SLaurent Pinchart #include <linux/uaccess.h>
267ef077a8SLaurent Pinchart #include <linux/io.h>
2708305b45SChunfeng Yun #include <linux/iopoll.h>
287ef077a8SLaurent Pinchart #include <linux/mm.h>
297ef077a8SLaurent Pinchart #include <linux/timer.h>
30*5f60d5f6SAl Viro #include <linux/unaligned.h>
317ef077a8SLaurent Pinchart #include <asm/cacheflush.h>
327ef077a8SLaurent Pinchart 
337ef077a8SLaurent Pinchart #include "isp1760-core.h"
347ef077a8SLaurent Pinchart #include "isp1760-hcd.h"
357ef077a8SLaurent Pinchart #include "isp1760-regs.h"
367ef077a8SLaurent Pinchart 
377ef077a8SLaurent Pinchart static struct kmem_cache *qtd_cachep;
387ef077a8SLaurent Pinchart static struct kmem_cache *qh_cachep;
397ef077a8SLaurent Pinchart static struct kmem_cache *urb_listitem_cachep;
407ef077a8SLaurent Pinchart 
417ef077a8SLaurent Pinchart typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh,
427ef077a8SLaurent Pinchart 		struct isp1760_qtd *qtd);
437ef077a8SLaurent Pinchart 
hcd_to_priv(struct usb_hcd * hcd)447ef077a8SLaurent Pinchart static inline struct isp1760_hcd *hcd_to_priv(struct usb_hcd *hcd)
457ef077a8SLaurent Pinchart {
467ef077a8SLaurent Pinchart 	return *(struct isp1760_hcd **)hcd->hcd_priv;
477ef077a8SLaurent Pinchart }
487ef077a8SLaurent Pinchart 
4960d789f3SRui Miguel Silva #define dw_to_le32(x)	(cpu_to_le32((__force u32)x))
5060d789f3SRui Miguel Silva #define le32_to_dw(x)	((__force __dw)(le32_to_cpu(x)))
5160d789f3SRui Miguel Silva 
527ef077a8SLaurent Pinchart /* urb state*/
537ef077a8SLaurent Pinchart #define DELETE_URB		(0x0008)
547ef077a8SLaurent Pinchart #define NO_TRANSFER_ACTIVE	(0xffffffff)
557ef077a8SLaurent Pinchart 
567ef077a8SLaurent Pinchart /* Philips Proprietary Transfer Descriptor (PTD) */
577ef077a8SLaurent Pinchart typedef __u32 __bitwise __dw;
587ef077a8SLaurent Pinchart struct ptd {
597ef077a8SLaurent Pinchart 	__dw dw0;
607ef077a8SLaurent Pinchart 	__dw dw1;
617ef077a8SLaurent Pinchart 	__dw dw2;
627ef077a8SLaurent Pinchart 	__dw dw3;
637ef077a8SLaurent Pinchart 	__dw dw4;
647ef077a8SLaurent Pinchart 	__dw dw5;
657ef077a8SLaurent Pinchart 	__dw dw6;
667ef077a8SLaurent Pinchart 	__dw dw7;
677ef077a8SLaurent Pinchart };
6860d789f3SRui Miguel Silva 
6960d789f3SRui Miguel Silva struct ptd_le32 {
7060d789f3SRui Miguel Silva 	__le32 dw0;
7160d789f3SRui Miguel Silva 	__le32 dw1;
7260d789f3SRui Miguel Silva 	__le32 dw2;
7360d789f3SRui Miguel Silva 	__le32 dw3;
7460d789f3SRui Miguel Silva 	__le32 dw4;
7560d789f3SRui Miguel Silva 	__le32 dw5;
7660d789f3SRui Miguel Silva 	__le32 dw6;
7760d789f3SRui Miguel Silva 	__le32 dw7;
7860d789f3SRui Miguel Silva };
7960d789f3SRui Miguel Silva 
807ef077a8SLaurent Pinchart #define PTD_OFFSET		0x0400
817ef077a8SLaurent Pinchart #define ISO_PTD_OFFSET		0x0400
827ef077a8SLaurent Pinchart #define INT_PTD_OFFSET		0x0800
837ef077a8SLaurent Pinchart #define ATL_PTD_OFFSET		0x0c00
847ef077a8SLaurent Pinchart #define PAYLOAD_OFFSET		0x1000
857ef077a8SLaurent Pinchart 
861da9e1c0SRui Miguel Silva #define ISP_BANK_0		0x00
871da9e1c0SRui Miguel Silva #define ISP_BANK_1		0x01
881da9e1c0SRui Miguel Silva #define ISP_BANK_2		0x02
891da9e1c0SRui Miguel Silva #define ISP_BANK_3		0x03
901da9e1c0SRui Miguel Silva 
91abfabc8aSRui Miguel Silva #define TO_DW(x)	((__force __dw)x)
92abfabc8aSRui Miguel Silva #define TO_U32(x)	((__force u32)x)
937ef077a8SLaurent Pinchart 
947ef077a8SLaurent Pinchart  /* ATL */
957ef077a8SLaurent Pinchart  /* DW0 */
96abfabc8aSRui Miguel Silva #define DW0_VALID_BIT			TO_DW(1)
97abfabc8aSRui Miguel Silva #define FROM_DW0_VALID(x)		(TO_U32(x) & 0x01)
98abfabc8aSRui Miguel Silva #define TO_DW0_LENGTH(x)		TO_DW((((u32)x) << 3))
99abfabc8aSRui Miguel Silva #define TO_DW0_MAXPACKET(x)		TO_DW((((u32)x) << 18))
100abfabc8aSRui Miguel Silva #define TO_DW0_MULTI(x)			TO_DW((((u32)x) << 29))
101abfabc8aSRui Miguel Silva #define TO_DW0_ENDPOINT(x)		TO_DW((((u32)x) << 31))
1027ef077a8SLaurent Pinchart /* DW1 */
103abfabc8aSRui Miguel Silva #define TO_DW1_DEVICE_ADDR(x)		TO_DW((((u32)x) << 3))
104abfabc8aSRui Miguel Silva #define TO_DW1_PID_TOKEN(x)		TO_DW((((u32)x) << 10))
105abfabc8aSRui Miguel Silva #define DW1_TRANS_BULK			TO_DW(((u32)2 << 12))
106abfabc8aSRui Miguel Silva #define DW1_TRANS_INT			TO_DW(((u32)3 << 12))
107abfabc8aSRui Miguel Silva #define DW1_TRANS_SPLIT			TO_DW(((u32)1 << 14))
108abfabc8aSRui Miguel Silva #define DW1_SE_USB_LOSPEED		TO_DW(((u32)2 << 16))
109abfabc8aSRui Miguel Silva #define TO_DW1_PORT_NUM(x)		TO_DW((((u32)x) << 18))
110abfabc8aSRui Miguel Silva #define TO_DW1_HUB_NUM(x)		TO_DW((((u32)x) << 25))
1117ef077a8SLaurent Pinchart /* DW2 */
112abfabc8aSRui Miguel Silva #define TO_DW2_DATA_START_ADDR(x)	TO_DW((((u32)x) << 8))
113abfabc8aSRui Miguel Silva #define TO_DW2_RL(x)			TO_DW(((x) << 25))
114abfabc8aSRui Miguel Silva #define FROM_DW2_RL(x)			((TO_U32(x) >> 25) & 0xf)
1157ef077a8SLaurent Pinchart /* DW3 */
11660d789f3SRui Miguel Silva #define FROM_DW3_NRBYTESTRANSFERRED(x)		TO_U32((x) & 0x3fff)
117abfabc8aSRui Miguel Silva #define FROM_DW3_SCS_NRBYTESTRANSFERRED(x)	TO_U32((x) & 0x07ff)
118abfabc8aSRui Miguel Silva #define TO_DW3_NAKCOUNT(x)		TO_DW(((x) << 19))
119abfabc8aSRui Miguel Silva #define FROM_DW3_NAKCOUNT(x)		((TO_U32(x) >> 19) & 0xf)
120abfabc8aSRui Miguel Silva #define TO_DW3_CERR(x)			TO_DW(((x) << 23))
121abfabc8aSRui Miguel Silva #define FROM_DW3_CERR(x)		((TO_U32(x) >> 23) & 0x3)
122abfabc8aSRui Miguel Silva #define TO_DW3_DATA_TOGGLE(x)		TO_DW(((x) << 25))
123abfabc8aSRui Miguel Silva #define FROM_DW3_DATA_TOGGLE(x)		((TO_U32(x) >> 25) & 0x1)
124abfabc8aSRui Miguel Silva #define TO_DW3_PING(x)			TO_DW(((x) << 26))
125abfabc8aSRui Miguel Silva #define FROM_DW3_PING(x)		((TO_U32(x) >> 26) & 0x1)
126abfabc8aSRui Miguel Silva #define DW3_ERROR_BIT			TO_DW((1 << 28))
127abfabc8aSRui Miguel Silva #define DW3_BABBLE_BIT			TO_DW((1 << 29))
128abfabc8aSRui Miguel Silva #define DW3_HALT_BIT			TO_DW((1 << 30))
129abfabc8aSRui Miguel Silva #define DW3_ACTIVE_BIT			TO_DW((1 << 31))
130abfabc8aSRui Miguel Silva #define FROM_DW3_ACTIVE(x)		((TO_U32(x) >> 31) & 0x01)
1317ef077a8SLaurent Pinchart 
1327ef077a8SLaurent Pinchart #define INT_UNDERRUN			(1 << 2)
1337ef077a8SLaurent Pinchart #define INT_BABBLE			(1 << 1)
1347ef077a8SLaurent Pinchart #define INT_EXACT			(1 << 0)
1357ef077a8SLaurent Pinchart 
1367ef077a8SLaurent Pinchart #define SETUP_PID	(2)
1377ef077a8SLaurent Pinchart #define IN_PID		(1)
1387ef077a8SLaurent Pinchart #define OUT_PID		(0)
1397ef077a8SLaurent Pinchart 
1407ef077a8SLaurent Pinchart /* Errata 1 */
1417ef077a8SLaurent Pinchart #define RL_COUNTER	(0)
1427ef077a8SLaurent Pinchart #define NAK_COUNTER	(0)
14360d789f3SRui Miguel Silva #define ERR_COUNTER	(3)
1447ef077a8SLaurent Pinchart 
1457ef077a8SLaurent Pinchart struct isp1760_qtd {
1467ef077a8SLaurent Pinchart 	u8 packet_type;
1477ef077a8SLaurent Pinchart 	void *data_buffer;
1487ef077a8SLaurent Pinchart 	u32 payload_addr;
1497ef077a8SLaurent Pinchart 
1507ef077a8SLaurent Pinchart 	/* the rest is HCD-private */
1517ef077a8SLaurent Pinchart 	struct list_head qtd_list;
1527ef077a8SLaurent Pinchart 	struct urb *urb;
1537ef077a8SLaurent Pinchart 	size_t length;
1547ef077a8SLaurent Pinchart 	size_t actual_length;
1557ef077a8SLaurent Pinchart 
1567ef077a8SLaurent Pinchart 	/* QTD_ENQUEUED:	waiting for transfer (inactive) */
1577ef077a8SLaurent Pinchart 	/* QTD_PAYLOAD_ALLOC:	chip mem has been allocated for payload */
1587ef077a8SLaurent Pinchart 	/* QTD_XFER_STARTED:	valid ptd has been written to isp176x - only
1597ef077a8SLaurent Pinchart 				interrupt handler may touch this qtd! */
1607ef077a8SLaurent Pinchart 	/* QTD_XFER_COMPLETE:	payload has been transferred successfully */
1617ef077a8SLaurent Pinchart 	/* QTD_RETIRE:		transfer error/abort qtd */
1627ef077a8SLaurent Pinchart #define QTD_ENQUEUED		0
1637ef077a8SLaurent Pinchart #define QTD_PAYLOAD_ALLOC	1
1647ef077a8SLaurent Pinchart #define QTD_XFER_STARTED	2
1657ef077a8SLaurent Pinchart #define QTD_XFER_COMPLETE	3
1667ef077a8SLaurent Pinchart #define QTD_RETIRE		4
1677ef077a8SLaurent Pinchart 	u32 status;
1687ef077a8SLaurent Pinchart };
1697ef077a8SLaurent Pinchart 
1707ef077a8SLaurent Pinchart /* Queue head, one for each active endpoint */
1717ef077a8SLaurent Pinchart struct isp1760_qh {
1727ef077a8SLaurent Pinchart 	struct list_head qh_list;
1737ef077a8SLaurent Pinchart 	struct list_head qtd_list;
1747ef077a8SLaurent Pinchart 	u32 toggle;
1757ef077a8SLaurent Pinchart 	u32 ping;
1767ef077a8SLaurent Pinchart 	int slot;
1777ef077a8SLaurent Pinchart 	int tt_buffer_dirty;	/* See USB2.0 spec section 11.17.5 */
1787ef077a8SLaurent Pinchart };
1797ef077a8SLaurent Pinchart 
1807ef077a8SLaurent Pinchart struct urb_listitem {
1817ef077a8SLaurent Pinchart 	struct list_head urb_list;
1827ef077a8SLaurent Pinchart 	struct urb *urb;
1837ef077a8SLaurent Pinchart };
1847ef077a8SLaurent Pinchart 
18536815a4aSRui Miguel Silva static const u32 isp176x_hc_portsc1_fields[] = {
18660d789f3SRui Miguel Silva 	[PORT_OWNER]		= BIT(13),
18760d789f3SRui Miguel Silva 	[PORT_POWER]		= BIT(12),
18860d789f3SRui Miguel Silva 	[PORT_LSTATUS]		= BIT(10),
18960d789f3SRui Miguel Silva 	[PORT_RESET]		= BIT(8),
19060d789f3SRui Miguel Silva 	[PORT_SUSPEND]		= BIT(7),
19160d789f3SRui Miguel Silva 	[PORT_RESUME]		= BIT(6),
19260d789f3SRui Miguel Silva 	[PORT_PE]		= BIT(2),
19360d789f3SRui Miguel Silva 	[PORT_CSC]		= BIT(1),
19460d789f3SRui Miguel Silva 	[PORT_CONNECT]		= BIT(0),
19560d789f3SRui Miguel Silva };
19660d789f3SRui Miguel Silva 
1977ef077a8SLaurent Pinchart /*
1981da9e1c0SRui Miguel Silva  * Access functions for isp176x registers regmap fields
1997ef077a8SLaurent Pinchart  */
isp1760_hcd_read(struct usb_hcd * hcd,u32 field)2001da9e1c0SRui Miguel Silva static u32 isp1760_hcd_read(struct usb_hcd *hcd, u32 field)
2017ef077a8SLaurent Pinchart {
2021da9e1c0SRui Miguel Silva 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
2031da9e1c0SRui Miguel Silva 
2041da9e1c0SRui Miguel Silva 	return isp1760_field_read(priv->fields, field);
2057ef077a8SLaurent Pinchart }
2067ef077a8SLaurent Pinchart 
20760d789f3SRui Miguel Silva /*
20836815a4aSRui Miguel Silva  * We need, in isp176x, to write directly the values to the portsc1
20960d789f3SRui Miguel Silva  * register so it will make the other values to trigger.
21060d789f3SRui Miguel Silva  */
isp1760_hcd_portsc1_set_clear(struct isp1760_hcd * priv,u32 field,u32 val)21160d789f3SRui Miguel Silva static void isp1760_hcd_portsc1_set_clear(struct isp1760_hcd *priv, u32 field,
21260d789f3SRui Miguel Silva 					  u32 val)
21360d789f3SRui Miguel Silva {
21436815a4aSRui Miguel Silva 	u32 bit = isp176x_hc_portsc1_fields[field];
21536815a4aSRui Miguel Silva 	u16 portsc1_reg = priv->is_isp1763 ? ISP1763_HC_PORTSC1 :
21636815a4aSRui Miguel Silva 		ISP176x_HC_PORTSC1;
21736815a4aSRui Miguel Silva 	u32 port_status = readl(priv->base + portsc1_reg);
21860d789f3SRui Miguel Silva 
21960d789f3SRui Miguel Silva 	if (val)
22036815a4aSRui Miguel Silva 		writel(port_status | bit, priv->base + portsc1_reg);
22160d789f3SRui Miguel Silva 	else
22236815a4aSRui Miguel Silva 		writel(port_status & ~bit, priv->base + portsc1_reg);
22360d789f3SRui Miguel Silva }
22460d789f3SRui Miguel Silva 
isp1760_hcd_write(struct usb_hcd * hcd,u32 field,u32 val)2251da9e1c0SRui Miguel Silva static void isp1760_hcd_write(struct usb_hcd *hcd, u32 field, u32 val)
2267ef077a8SLaurent Pinchart {
2271da9e1c0SRui Miguel Silva 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
2281da9e1c0SRui Miguel Silva 
22936815a4aSRui Miguel Silva 	if (unlikely((field >= PORT_OWNER && field <= PORT_CONNECT)))
23060d789f3SRui Miguel Silva 		return isp1760_hcd_portsc1_set_clear(priv, field, val);
23160d789f3SRui Miguel Silva 
2321da9e1c0SRui Miguel Silva 	isp1760_field_write(priv->fields, field, val);
2331da9e1c0SRui Miguel Silva }
2341da9e1c0SRui Miguel Silva 
isp1760_hcd_set(struct usb_hcd * hcd,u32 field)2351da9e1c0SRui Miguel Silva static void isp1760_hcd_set(struct usb_hcd *hcd, u32 field)
2361da9e1c0SRui Miguel Silva {
2371da9e1c0SRui Miguel Silva 	isp1760_hcd_write(hcd, field, 0xFFFFFFFF);
2381da9e1c0SRui Miguel Silva }
2391da9e1c0SRui Miguel Silva 
isp1760_hcd_clear(struct usb_hcd * hcd,u32 field)2401da9e1c0SRui Miguel Silva static void isp1760_hcd_clear(struct usb_hcd *hcd, u32 field)
2411da9e1c0SRui Miguel Silva {
2421da9e1c0SRui Miguel Silva 	isp1760_hcd_write(hcd, field, 0);
2431da9e1c0SRui Miguel Silva }
2441da9e1c0SRui Miguel Silva 
isp1760_hcd_set_and_wait(struct usb_hcd * hcd,u32 field,u32 timeout_us)24560d789f3SRui Miguel Silva static int isp1760_hcd_set_and_wait(struct usb_hcd *hcd, u32 field,
2461da9e1c0SRui Miguel Silva 				    u32 timeout_us)
2471da9e1c0SRui Miguel Silva {
2481da9e1c0SRui Miguel Silva 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
24960d789f3SRui Miguel Silva 	u32 val;
2501da9e1c0SRui Miguel Silva 
2511da9e1c0SRui Miguel Silva 	isp1760_hcd_set(hcd, field);
2521da9e1c0SRui Miguel Silva 
25360d789f3SRui Miguel Silva 	return regmap_field_read_poll_timeout(priv->fields[field], val,
25441f67318SRui Miguel Silva 					      val, 0, timeout_us);
2551da9e1c0SRui Miguel Silva }
2561da9e1c0SRui Miguel Silva 
isp1760_hcd_set_and_wait_swap(struct usb_hcd * hcd,u32 field,u32 timeout_us)25760d789f3SRui Miguel Silva static int isp1760_hcd_set_and_wait_swap(struct usb_hcd *hcd, u32 field,
2581da9e1c0SRui Miguel Silva 					 u32 timeout_us)
2591da9e1c0SRui Miguel Silva {
2601da9e1c0SRui Miguel Silva 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
26160d789f3SRui Miguel Silva 	u32 val;
26260d789f3SRui Miguel Silva 
26360d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, field);
26460d789f3SRui Miguel Silva 
26560d789f3SRui Miguel Silva 	return regmap_field_read_poll_timeout(priv->fields[field], val,
26641f67318SRui Miguel Silva 					      !val, 0, timeout_us);
26760d789f3SRui Miguel Silva }
26860d789f3SRui Miguel Silva 
isp1760_hcd_clear_and_wait(struct usb_hcd * hcd,u32 field,u32 timeout_us)26960d789f3SRui Miguel Silva static int isp1760_hcd_clear_and_wait(struct usb_hcd *hcd, u32 field,
27060d789f3SRui Miguel Silva 				      u32 timeout_us)
27160d789f3SRui Miguel Silva {
27260d789f3SRui Miguel Silva 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
27360d789f3SRui Miguel Silva 	u32 val;
2741da9e1c0SRui Miguel Silva 
2751da9e1c0SRui Miguel Silva 	isp1760_hcd_clear(hcd, field);
2761da9e1c0SRui Miguel Silva 
27760d789f3SRui Miguel Silva 	return regmap_field_read_poll_timeout(priv->fields[field], val,
27841f67318SRui Miguel Silva 					      !val, 0, timeout_us);
2791da9e1c0SRui Miguel Silva }
2801da9e1c0SRui Miguel Silva 
isp1760_hcd_is_set(struct usb_hcd * hcd,u32 field)2811da9e1c0SRui Miguel Silva static bool isp1760_hcd_is_set(struct usb_hcd *hcd, u32 field)
2821da9e1c0SRui Miguel Silva {
2831da9e1c0SRui Miguel Silva 	return !!isp1760_hcd_read(hcd, field);
2847ef077a8SLaurent Pinchart }
2857ef077a8SLaurent Pinchart 
isp1760_hcd_ppc_is_set(struct usb_hcd * hcd)28660d789f3SRui Miguel Silva static bool isp1760_hcd_ppc_is_set(struct usb_hcd *hcd)
28760d789f3SRui Miguel Silva {
28860d789f3SRui Miguel Silva 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
28960d789f3SRui Miguel Silva 
29060d789f3SRui Miguel Silva 	if (priv->is_isp1763)
29160d789f3SRui Miguel Silva 		return true;
29260d789f3SRui Miguel Silva 
29360d789f3SRui Miguel Silva 	return isp1760_hcd_is_set(hcd, HCS_PPC);
29460d789f3SRui Miguel Silva }
29560d789f3SRui Miguel Silva 
isp1760_hcd_n_ports(struct usb_hcd * hcd)29660d789f3SRui Miguel Silva static u32 isp1760_hcd_n_ports(struct usb_hcd *hcd)
29760d789f3SRui Miguel Silva {
29860d789f3SRui Miguel Silva 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
29960d789f3SRui Miguel Silva 
30060d789f3SRui Miguel Silva 	if (priv->is_isp1763)
30160d789f3SRui Miguel Silva 		return 1;
30260d789f3SRui Miguel Silva 
30360d789f3SRui Miguel Silva 	return isp1760_hcd_read(hcd, HCS_N_PORTS);
30460d789f3SRui Miguel Silva }
30560d789f3SRui Miguel Silva 
3067ef077a8SLaurent Pinchart /*
3077ef077a8SLaurent Pinchart  * Access functions for isp176x memory (offset >= 0x0400).
3087ef077a8SLaurent Pinchart  *
3097ef077a8SLaurent Pinchart  * bank_reads8() reads memory locations prefetched by an earlier write to
3107ef077a8SLaurent Pinchart  * HC_MEMORY_REG (see isp176x datasheet). Unless you want to do fancy multi-
31160d789f3SRui Miguel Silva  * bank optimizations, you should use the more generic mem_read() below.
3127ef077a8SLaurent Pinchart  *
3137ef077a8SLaurent Pinchart  * For access to ptd memory, use the specialized ptd_read() and ptd_write()
3147ef077a8SLaurent Pinchart  * below.
3157ef077a8SLaurent Pinchart  *
3167ef077a8SLaurent Pinchart  * These functions copy via MMIO data to/from the device. memcpy_{to|from}io()
3177ef077a8SLaurent Pinchart  * doesn't quite work because some people have to enforce 32-bit access
3187ef077a8SLaurent Pinchart  */
bank_reads8(void __iomem * src_base,u32 src_offset,u32 bank_addr,__u32 * dst,u32 bytes)3197ef077a8SLaurent Pinchart static void bank_reads8(void __iomem *src_base, u32 src_offset, u32 bank_addr,
3207ef077a8SLaurent Pinchart 							__u32 *dst, u32 bytes)
3217ef077a8SLaurent Pinchart {
3227ef077a8SLaurent Pinchart 	__u32 __iomem *src;
3237ef077a8SLaurent Pinchart 	u32 val;
3247ef077a8SLaurent Pinchart 	__u8 *src_byteptr;
3257ef077a8SLaurent Pinchart 	__u8 *dst_byteptr;
3267ef077a8SLaurent Pinchart 
3277ef077a8SLaurent Pinchart 	src = src_base + (bank_addr | src_offset);
3287ef077a8SLaurent Pinchart 
3297ef077a8SLaurent Pinchart 	if (src_offset < PAYLOAD_OFFSET) {
3307ef077a8SLaurent Pinchart 		while (bytes >= 4) {
33103e28d52SRui Miguel Silva 			*dst = readl_relaxed(src);
3327ef077a8SLaurent Pinchart 			bytes -= 4;
3337ef077a8SLaurent Pinchart 			src++;
3347ef077a8SLaurent Pinchart 			dst++;
3357ef077a8SLaurent Pinchart 		}
3367ef077a8SLaurent Pinchart 	} else {
3377ef077a8SLaurent Pinchart 		while (bytes >= 4) {
3387ef077a8SLaurent Pinchart 			*dst = __raw_readl(src);
3397ef077a8SLaurent Pinchart 			bytes -= 4;
3407ef077a8SLaurent Pinchart 			src++;
3417ef077a8SLaurent Pinchart 			dst++;
3427ef077a8SLaurent Pinchart 		}
3437ef077a8SLaurent Pinchart 	}
3447ef077a8SLaurent Pinchart 
3457ef077a8SLaurent Pinchart 	if (!bytes)
3467ef077a8SLaurent Pinchart 		return;
3477ef077a8SLaurent Pinchart 
3487ef077a8SLaurent Pinchart 	/* in case we have 3, 2 or 1 by left. The dst buffer may not be fully
3497ef077a8SLaurent Pinchart 	 * allocated.
3507ef077a8SLaurent Pinchart 	 */
3517ef077a8SLaurent Pinchart 	if (src_offset < PAYLOAD_OFFSET)
35203e28d52SRui Miguel Silva 		val = readl_relaxed(src);
3537ef077a8SLaurent Pinchart 	else
3547ef077a8SLaurent Pinchart 		val = __raw_readl(src);
3557ef077a8SLaurent Pinchart 
3567ef077a8SLaurent Pinchart 	dst_byteptr = (void *) dst;
3577ef077a8SLaurent Pinchart 	src_byteptr = (void *) &val;
3587ef077a8SLaurent Pinchart 	while (bytes > 0) {
3597ef077a8SLaurent Pinchart 		*dst_byteptr = *src_byteptr;
3607ef077a8SLaurent Pinchart 		dst_byteptr++;
3617ef077a8SLaurent Pinchart 		src_byteptr++;
3627ef077a8SLaurent Pinchart 		bytes--;
3637ef077a8SLaurent Pinchart 	}
3647ef077a8SLaurent Pinchart }
3657ef077a8SLaurent Pinchart 
isp1760_mem_read(struct usb_hcd * hcd,u32 src_offset,void * dst,u32 bytes)36660d789f3SRui Miguel Silva static void isp1760_mem_read(struct usb_hcd *hcd, u32 src_offset, void *dst,
36760d789f3SRui Miguel Silva 			     u32 bytes)
3687ef077a8SLaurent Pinchart {
36960d789f3SRui Miguel Silva 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
37060d789f3SRui Miguel Silva 
37136815a4aSRui Miguel Silva 	isp1760_reg_write(priv->regs, ISP176x_HC_MEMORY, src_offset);
37260d789f3SRui Miguel Silva 	ndelay(100);
3731da9e1c0SRui Miguel Silva 
37460d789f3SRui Miguel Silva 	bank_reads8(priv->base, src_offset, ISP_BANK_0, dst, bytes);
3757ef077a8SLaurent Pinchart }
3767ef077a8SLaurent Pinchart 
37760d789f3SRui Miguel Silva /*
37860d789f3SRui Miguel Silva  * ISP1763 does not have the banks direct host controller memory access,
37960d789f3SRui Miguel Silva  * needs to use the HC_DATA register. Add data read/write according to this,
38060d789f3SRui Miguel Silva  * and also adjust 16bit access.
38160d789f3SRui Miguel Silva  */
isp1763_mem_read(struct usb_hcd * hcd,u16 srcaddr,u16 * dstptr,u32 bytes)38260d789f3SRui Miguel Silva static void isp1763_mem_read(struct usb_hcd *hcd, u16 srcaddr,
38360d789f3SRui Miguel Silva 			     u16 *dstptr, u32 bytes)
38460d789f3SRui Miguel Silva {
38560d789f3SRui Miguel Silva 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
38660d789f3SRui Miguel Silva 
38760d789f3SRui Miguel Silva 	/* Write the starting device address to the hcd memory register */
38860d789f3SRui Miguel Silva 	isp1760_reg_write(priv->regs, ISP1763_HC_MEMORY, srcaddr);
38960d789f3SRui Miguel Silva 	ndelay(100); /* Delay between consecutive access */
39060d789f3SRui Miguel Silva 
39160d789f3SRui Miguel Silva 	/* As long there are at least 16-bit to read ... */
39260d789f3SRui Miguel Silva 	while (bytes >= 2) {
39360d789f3SRui Miguel Silva 		*dstptr = __raw_readw(priv->base + ISP1763_HC_DATA);
39460d789f3SRui Miguel Silva 		bytes -= 2;
39560d789f3SRui Miguel Silva 		dstptr++;
39660d789f3SRui Miguel Silva 	}
39760d789f3SRui Miguel Silva 
39860d789f3SRui Miguel Silva 	/* If there are no more bytes to read, return */
39960d789f3SRui Miguel Silva 	if (bytes <= 0)
40060d789f3SRui Miguel Silva 		return;
40160d789f3SRui Miguel Silva 
40260d789f3SRui Miguel Silva 	*((u8 *)dstptr) = (u8)(readw(priv->base + ISP1763_HC_DATA) & 0xFF);
40360d789f3SRui Miguel Silva }
40460d789f3SRui Miguel Silva 
mem_read(struct usb_hcd * hcd,u32 src_offset,__u32 * dst,u32 bytes)40560d789f3SRui Miguel Silva static void mem_read(struct usb_hcd *hcd, u32 src_offset, __u32 *dst,
40660d789f3SRui Miguel Silva 		     u32 bytes)
40760d789f3SRui Miguel Silva {
40860d789f3SRui Miguel Silva 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
40960d789f3SRui Miguel Silva 
41060d789f3SRui Miguel Silva 	if (!priv->is_isp1763)
41160d789f3SRui Miguel Silva 		return isp1760_mem_read(hcd, src_offset, (u16 *)dst, bytes);
41260d789f3SRui Miguel Silva 
41360d789f3SRui Miguel Silva 	isp1763_mem_read(hcd, (u16)src_offset, (u16 *)dst, bytes);
41460d789f3SRui Miguel Silva }
41560d789f3SRui Miguel Silva 
isp1760_mem_write(void __iomem * dst_base,u32 dst_offset,__u32 const * src,u32 bytes)41660d789f3SRui Miguel Silva static void isp1760_mem_write(void __iomem *dst_base, u32 dst_offset,
4177ef077a8SLaurent Pinchart 			      __u32 const *src, u32 bytes)
4187ef077a8SLaurent Pinchart {
4197ef077a8SLaurent Pinchart 	__u32 __iomem *dst;
4207ef077a8SLaurent Pinchart 
4217ef077a8SLaurent Pinchart 	dst = dst_base + dst_offset;
4227ef077a8SLaurent Pinchart 
4237ef077a8SLaurent Pinchart 	if (dst_offset < PAYLOAD_OFFSET) {
4247ef077a8SLaurent Pinchart 		while (bytes >= 4) {
42503e28d52SRui Miguel Silva 			writel_relaxed(*src, dst);
4267ef077a8SLaurent Pinchart 			bytes -= 4;
4277ef077a8SLaurent Pinchart 			src++;
4287ef077a8SLaurent Pinchart 			dst++;
4297ef077a8SLaurent Pinchart 		}
4307ef077a8SLaurent Pinchart 	} else {
4317ef077a8SLaurent Pinchart 		while (bytes >= 4) {
4327ef077a8SLaurent Pinchart 			__raw_writel(*src, dst);
4337ef077a8SLaurent Pinchart 			bytes -= 4;
4347ef077a8SLaurent Pinchart 			src++;
4357ef077a8SLaurent Pinchart 			dst++;
4367ef077a8SLaurent Pinchart 		}
4377ef077a8SLaurent Pinchart 	}
4387ef077a8SLaurent Pinchart 
4397ef077a8SLaurent Pinchart 	if (!bytes)
4407ef077a8SLaurent Pinchart 		return;
4417ef077a8SLaurent Pinchart 	/* in case we have 3, 2 or 1 bytes left. The buffer is allocated and the
4427ef077a8SLaurent Pinchart 	 * extra bytes should not be read by the HW.
4437ef077a8SLaurent Pinchart 	 */
4447ef077a8SLaurent Pinchart 
4457ef077a8SLaurent Pinchart 	if (dst_offset < PAYLOAD_OFFSET)
44603e28d52SRui Miguel Silva 		writel_relaxed(*src, dst);
4477ef077a8SLaurent Pinchart 	else
4487ef077a8SLaurent Pinchart 		__raw_writel(*src, dst);
4497ef077a8SLaurent Pinchart }
4507ef077a8SLaurent Pinchart 
isp1763_mem_write(struct usb_hcd * hcd,u16 dstaddr,u16 * src,u32 bytes)45160d789f3SRui Miguel Silva static void isp1763_mem_write(struct usb_hcd *hcd, u16 dstaddr, u16 *src,
45260d789f3SRui Miguel Silva 			      u32 bytes)
45360d789f3SRui Miguel Silva {
45460d789f3SRui Miguel Silva 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
45560d789f3SRui Miguel Silva 
45660d789f3SRui Miguel Silva 	/* Write the starting device address to the hcd memory register */
45760d789f3SRui Miguel Silva 	isp1760_reg_write(priv->regs, ISP1763_HC_MEMORY, dstaddr);
45860d789f3SRui Miguel Silva 	ndelay(100); /* Delay between consecutive access */
45960d789f3SRui Miguel Silva 
46060d789f3SRui Miguel Silva 	while (bytes >= 2) {
46160d789f3SRui Miguel Silva 		/* Get and write the data; then adjust the data ptr and len */
46260d789f3SRui Miguel Silva 		__raw_writew(*src, priv->base + ISP1763_HC_DATA);
46360d789f3SRui Miguel Silva 		bytes -= 2;
46460d789f3SRui Miguel Silva 		src++;
46560d789f3SRui Miguel Silva 	}
46660d789f3SRui Miguel Silva 
46760d789f3SRui Miguel Silva 	/* If there are no more bytes to process, return */
46860d789f3SRui Miguel Silva 	if (bytes <= 0)
46960d789f3SRui Miguel Silva 		return;
47060d789f3SRui Miguel Silva 
47160d789f3SRui Miguel Silva 	/*
47260d789f3SRui Miguel Silva 	 * The only way to get here is if there is a single byte left,
47360d789f3SRui Miguel Silva 	 * get it and write it to the data reg;
47460d789f3SRui Miguel Silva 	 */
47560d789f3SRui Miguel Silva 	writew(*((u8 *)src), priv->base + ISP1763_HC_DATA);
47660d789f3SRui Miguel Silva }
47760d789f3SRui Miguel Silva 
mem_write(struct usb_hcd * hcd,u32 dst_offset,__u32 * src,u32 bytes)47860d789f3SRui Miguel Silva static void mem_write(struct usb_hcd *hcd, u32 dst_offset, __u32 *src,
47960d789f3SRui Miguel Silva 		      u32 bytes)
48060d789f3SRui Miguel Silva {
48160d789f3SRui Miguel Silva 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
48260d789f3SRui Miguel Silva 
48360d789f3SRui Miguel Silva 	if (!priv->is_isp1763)
48460d789f3SRui Miguel Silva 		return isp1760_mem_write(priv->base, dst_offset, src, bytes);
48560d789f3SRui Miguel Silva 
48660d789f3SRui Miguel Silva 	isp1763_mem_write(hcd, dst_offset, (u16 *)src, bytes);
48760d789f3SRui Miguel Silva }
48860d789f3SRui Miguel Silva 
4897ef077a8SLaurent Pinchart /*
4907ef077a8SLaurent Pinchart  * Read and write ptds. 'ptd_offset' should be one of ISO_PTD_OFFSET,
4917ef077a8SLaurent Pinchart  * INT_PTD_OFFSET, and ATL_PTD_OFFSET. 'slot' should be less than 32.
4927ef077a8SLaurent Pinchart  */
isp1760_ptd_read(struct usb_hcd * hcd,u32 ptd_offset,u32 slot,struct ptd * ptd)49360d789f3SRui Miguel Silva static void isp1760_ptd_read(struct usb_hcd *hcd, u32 ptd_offset, u32 slot,
4947ef077a8SLaurent Pinchart 			     struct ptd *ptd)
4957ef077a8SLaurent Pinchart {
49660d789f3SRui Miguel Silva 	u16 src_offset = ptd_offset + slot * sizeof(*ptd);
49760d789f3SRui Miguel Silva 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
49860d789f3SRui Miguel Silva 
49936815a4aSRui Miguel Silva 	isp1760_reg_write(priv->regs, ISP176x_HC_MEMORY, src_offset);
50060d789f3SRui Miguel Silva 	ndelay(90);
50160d789f3SRui Miguel Silva 
50260d789f3SRui Miguel Silva 	bank_reads8(priv->base, src_offset, ISP_BANK_0, (void *)ptd,
50360d789f3SRui Miguel Silva 		    sizeof(*ptd));
5047ef077a8SLaurent Pinchart }
5057ef077a8SLaurent Pinchart 
isp1763_ptd_read(struct usb_hcd * hcd,u32 ptd_offset,u32 slot,struct ptd * ptd)50660d789f3SRui Miguel Silva static void isp1763_ptd_read(struct usb_hcd *hcd, u32 ptd_offset, u32 slot,
50760d789f3SRui Miguel Silva 			     struct ptd *ptd)
50860d789f3SRui Miguel Silva {
50960d789f3SRui Miguel Silva 	u16 src_offset = ptd_offset + slot * sizeof(*ptd);
51060d789f3SRui Miguel Silva 	struct ptd_le32 le32_ptd;
51160d789f3SRui Miguel Silva 
51260d789f3SRui Miguel Silva 	isp1763_mem_read(hcd, src_offset, (u16 *)&le32_ptd, sizeof(le32_ptd));
51360d789f3SRui Miguel Silva 	/* Normalize the data obtained */
51460d789f3SRui Miguel Silva 	ptd->dw0 = le32_to_dw(le32_ptd.dw0);
51560d789f3SRui Miguel Silva 	ptd->dw1 = le32_to_dw(le32_ptd.dw1);
51660d789f3SRui Miguel Silva 	ptd->dw2 = le32_to_dw(le32_ptd.dw2);
51760d789f3SRui Miguel Silva 	ptd->dw3 = le32_to_dw(le32_ptd.dw3);
51860d789f3SRui Miguel Silva 	ptd->dw4 = le32_to_dw(le32_ptd.dw4);
51960d789f3SRui Miguel Silva 	ptd->dw5 = le32_to_dw(le32_ptd.dw5);
52060d789f3SRui Miguel Silva 	ptd->dw6 = le32_to_dw(le32_ptd.dw6);
52160d789f3SRui Miguel Silva 	ptd->dw7 = le32_to_dw(le32_ptd.dw7);
52260d789f3SRui Miguel Silva }
52360d789f3SRui Miguel Silva 
ptd_read(struct usb_hcd * hcd,u32 ptd_offset,u32 slot,struct ptd * ptd)52460d789f3SRui Miguel Silva static void ptd_read(struct usb_hcd *hcd, u32 ptd_offset, u32 slot,
52560d789f3SRui Miguel Silva 		     struct ptd *ptd)
52660d789f3SRui Miguel Silva {
52760d789f3SRui Miguel Silva 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
52860d789f3SRui Miguel Silva 
52960d789f3SRui Miguel Silva 	if (!priv->is_isp1763)
53060d789f3SRui Miguel Silva 		return isp1760_ptd_read(hcd, ptd_offset, slot, ptd);
53160d789f3SRui Miguel Silva 
53260d789f3SRui Miguel Silva 	isp1763_ptd_read(hcd, ptd_offset, slot, ptd);
53360d789f3SRui Miguel Silva }
53460d789f3SRui Miguel Silva 
isp1763_ptd_write(struct usb_hcd * hcd,u32 ptd_offset,u32 slot,struct ptd * cpu_ptd)53560d789f3SRui Miguel Silva static void isp1763_ptd_write(struct usb_hcd *hcd, u32 ptd_offset, u32 slot,
53660d789f3SRui Miguel Silva 			      struct ptd *cpu_ptd)
53760d789f3SRui Miguel Silva {
53860d789f3SRui Miguel Silva 	u16 dst_offset = ptd_offset + slot * sizeof(*cpu_ptd);
53960d789f3SRui Miguel Silva 	struct ptd_le32 ptd;
54060d789f3SRui Miguel Silva 
54160d789f3SRui Miguel Silva 	ptd.dw0 = dw_to_le32(cpu_ptd->dw0);
54260d789f3SRui Miguel Silva 	ptd.dw1 = dw_to_le32(cpu_ptd->dw1);
54360d789f3SRui Miguel Silva 	ptd.dw2 = dw_to_le32(cpu_ptd->dw2);
54460d789f3SRui Miguel Silva 	ptd.dw3 = dw_to_le32(cpu_ptd->dw3);
54560d789f3SRui Miguel Silva 	ptd.dw4 = dw_to_le32(cpu_ptd->dw4);
54660d789f3SRui Miguel Silva 	ptd.dw5 = dw_to_le32(cpu_ptd->dw5);
54760d789f3SRui Miguel Silva 	ptd.dw6 = dw_to_le32(cpu_ptd->dw6);
54860d789f3SRui Miguel Silva 	ptd.dw7 = dw_to_le32(cpu_ptd->dw7);
54960d789f3SRui Miguel Silva 
55060d789f3SRui Miguel Silva 	isp1763_mem_write(hcd, dst_offset,  (u16 *)&ptd.dw0,
55160d789f3SRui Miguel Silva 			  8 * sizeof(ptd.dw0));
55260d789f3SRui Miguel Silva }
55360d789f3SRui Miguel Silva 
isp1760_ptd_write(void __iomem * base,u32 ptd_offset,u32 slot,struct ptd * ptd)55460d789f3SRui Miguel Silva static void isp1760_ptd_write(void __iomem *base, u32 ptd_offset, u32 slot,
55560d789f3SRui Miguel Silva 			      struct ptd *ptd)
55660d789f3SRui Miguel Silva {
55760d789f3SRui Miguel Silva 	u32 dst_offset = ptd_offset + slot * sizeof(*ptd);
55860d789f3SRui Miguel Silva 
55960d789f3SRui Miguel Silva 	/*
56060d789f3SRui Miguel Silva 	 * Make sure dw0 gets written last (after other dw's and after payload)
56160d789f3SRui Miguel Silva 	 *  since it contains the enable bit
56260d789f3SRui Miguel Silva 	 */
56360d789f3SRui Miguel Silva 	isp1760_mem_write(base, dst_offset + sizeof(ptd->dw0),
56460d789f3SRui Miguel Silva 			  (__force u32 *)&ptd->dw1, 7 * sizeof(ptd->dw1));
56560d789f3SRui Miguel Silva 	wmb();
56660d789f3SRui Miguel Silva 	isp1760_mem_write(base, dst_offset, (__force u32 *)&ptd->dw0,
56760d789f3SRui Miguel Silva 			  sizeof(ptd->dw0));
56860d789f3SRui Miguel Silva }
56960d789f3SRui Miguel Silva 
ptd_write(struct usb_hcd * hcd,u32 ptd_offset,u32 slot,struct ptd * ptd)57060d789f3SRui Miguel Silva static void ptd_write(struct usb_hcd *hcd, u32 ptd_offset, u32 slot,
57160d789f3SRui Miguel Silva 		      struct ptd *ptd)
57260d789f3SRui Miguel Silva {
57360d789f3SRui Miguel Silva 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
57460d789f3SRui Miguel Silva 
57560d789f3SRui Miguel Silva 	if (!priv->is_isp1763)
57660d789f3SRui Miguel Silva 		return isp1760_ptd_write(priv->base, ptd_offset, slot, ptd);
57760d789f3SRui Miguel Silva 
57860d789f3SRui Miguel Silva 	isp1763_ptd_write(hcd, ptd_offset, slot, ptd);
57960d789f3SRui Miguel Silva }
5807ef077a8SLaurent Pinchart 
5817ef077a8SLaurent Pinchart /* memory management of the 60kb on the chip from 0x1000 to 0xffff */
init_memory(struct isp1760_hcd * priv)5827ef077a8SLaurent Pinchart static void init_memory(struct isp1760_hcd *priv)
5837ef077a8SLaurent Pinchart {
584a74f639cSRui Miguel Silva 	const struct isp1760_memory_layout *mem = priv->memory_layout;
585a74f639cSRui Miguel Silva 	int i, j, curr;
5867ef077a8SLaurent Pinchart 	u32 payload_addr;
5877ef077a8SLaurent Pinchart 
5887ef077a8SLaurent Pinchart 	payload_addr = PAYLOAD_OFFSET;
589a74f639cSRui Miguel Silva 
590f757f929SRui Miguel Silva 	for (i = 0, curr = 0; i < ARRAY_SIZE(mem->blocks); i++, curr += j) {
591f757f929SRui Miguel Silva 		for (j = 0; j < mem->blocks[i]; j++) {
592a74f639cSRui Miguel Silva 			priv->memory_pool[curr + j].start = payload_addr;
593a74f639cSRui Miguel Silva 			priv->memory_pool[curr + j].size = mem->blocks_size[i];
594a74f639cSRui Miguel Silva 			priv->memory_pool[curr + j].free = 1;
595a74f639cSRui Miguel Silva 			payload_addr += priv->memory_pool[curr + j].size;
596a74f639cSRui Miguel Silva 		}
5977ef077a8SLaurent Pinchart 	}
5987ef077a8SLaurent Pinchart 
599a74f639cSRui Miguel Silva 	WARN_ON(payload_addr - priv->memory_pool[0].start >
600a74f639cSRui Miguel Silva 		mem->payload_area_size);
6017ef077a8SLaurent Pinchart }
6027ef077a8SLaurent Pinchart 
alloc_mem(struct usb_hcd * hcd,struct isp1760_qtd * qtd)6037ef077a8SLaurent Pinchart static void alloc_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd)
6047ef077a8SLaurent Pinchart {
6057ef077a8SLaurent Pinchart 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
606a74f639cSRui Miguel Silva 	const struct isp1760_memory_layout *mem = priv->memory_layout;
6077ef077a8SLaurent Pinchart 	int i;
6087ef077a8SLaurent Pinchart 
6097ef077a8SLaurent Pinchart 	WARN_ON(qtd->payload_addr);
6107ef077a8SLaurent Pinchart 
6117ef077a8SLaurent Pinchart 	if (!qtd->length)
6127ef077a8SLaurent Pinchart 		return;
6137ef077a8SLaurent Pinchart 
614a74f639cSRui Miguel Silva 	for (i = 0; i < mem->payload_blocks; i++) {
6157ef077a8SLaurent Pinchart 		if (priv->memory_pool[i].size >= qtd->length &&
6167ef077a8SLaurent Pinchart 				priv->memory_pool[i].free) {
6177ef077a8SLaurent Pinchart 			priv->memory_pool[i].free = 0;
6187ef077a8SLaurent Pinchart 			qtd->payload_addr = priv->memory_pool[i].start;
6197ef077a8SLaurent Pinchart 			return;
6207ef077a8SLaurent Pinchart 		}
6217ef077a8SLaurent Pinchart 	}
6227ef077a8SLaurent Pinchart }
6237ef077a8SLaurent Pinchart 
free_mem(struct usb_hcd * hcd,struct isp1760_qtd * qtd)6247ef077a8SLaurent Pinchart static void free_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd)
6257ef077a8SLaurent Pinchart {
6267ef077a8SLaurent Pinchart 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
627a74f639cSRui Miguel Silva 	const struct isp1760_memory_layout *mem = priv->memory_layout;
6287ef077a8SLaurent Pinchart 	int i;
6297ef077a8SLaurent Pinchart 
6307ef077a8SLaurent Pinchart 	if (!qtd->payload_addr)
6317ef077a8SLaurent Pinchart 		return;
6327ef077a8SLaurent Pinchart 
633a74f639cSRui Miguel Silva 	for (i = 0; i < mem->payload_blocks; i++) {
6347ef077a8SLaurent Pinchart 		if (priv->memory_pool[i].start == qtd->payload_addr) {
6357ef077a8SLaurent Pinchart 			WARN_ON(priv->memory_pool[i].free);
6367ef077a8SLaurent Pinchart 			priv->memory_pool[i].free = 1;
6377ef077a8SLaurent Pinchart 			qtd->payload_addr = 0;
6387ef077a8SLaurent Pinchart 			return;
6397ef077a8SLaurent Pinchart 		}
6407ef077a8SLaurent Pinchart 	}
6417ef077a8SLaurent Pinchart 
6427ef077a8SLaurent Pinchart 	dev_err(hcd->self.controller, "%s: Invalid pointer: %08x\n",
6437ef077a8SLaurent Pinchart 						__func__, qtd->payload_addr);
6447ef077a8SLaurent Pinchart 	WARN_ON(1);
6457ef077a8SLaurent Pinchart 	qtd->payload_addr = 0;
6467ef077a8SLaurent Pinchart }
6477ef077a8SLaurent Pinchart 
6487ef077a8SLaurent Pinchart /* reset a non-running (STS_HALT == 1) controller */
ehci_reset(struct usb_hcd * hcd)6497ef077a8SLaurent Pinchart static int ehci_reset(struct usb_hcd *hcd)
6507ef077a8SLaurent Pinchart {
6517ef077a8SLaurent Pinchart 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
6527ef077a8SLaurent Pinchart 
6537ef077a8SLaurent Pinchart 	hcd->state = HC_STATE_HALT;
6547ef077a8SLaurent Pinchart 	priv->next_statechange = jiffies;
655992510f3SGustavo A. R. Silva 
65660d789f3SRui Miguel Silva 	return isp1760_hcd_set_and_wait_swap(hcd, CMD_RESET, 250 * 1000);
6577ef077a8SLaurent Pinchart }
6587ef077a8SLaurent Pinchart 
qh_alloc(gfp_t flags)6597ef077a8SLaurent Pinchart static struct isp1760_qh *qh_alloc(gfp_t flags)
6607ef077a8SLaurent Pinchart {
6617ef077a8SLaurent Pinchart 	struct isp1760_qh *qh;
6627ef077a8SLaurent Pinchart 
6637ef077a8SLaurent Pinchart 	qh = kmem_cache_zalloc(qh_cachep, flags);
6647ef077a8SLaurent Pinchart 	if (!qh)
6657ef077a8SLaurent Pinchart 		return NULL;
6667ef077a8SLaurent Pinchart 
6677ef077a8SLaurent Pinchart 	INIT_LIST_HEAD(&qh->qh_list);
6687ef077a8SLaurent Pinchart 	INIT_LIST_HEAD(&qh->qtd_list);
6697ef077a8SLaurent Pinchart 	qh->slot = -1;
6707ef077a8SLaurent Pinchart 
6717ef077a8SLaurent Pinchart 	return qh;
6727ef077a8SLaurent Pinchart }
6737ef077a8SLaurent Pinchart 
qh_free(struct isp1760_qh * qh)6747ef077a8SLaurent Pinchart static void qh_free(struct isp1760_qh *qh)
6757ef077a8SLaurent Pinchart {
6767ef077a8SLaurent Pinchart 	WARN_ON(!list_empty(&qh->qtd_list));
6777ef077a8SLaurent Pinchart 	WARN_ON(qh->slot > -1);
6787ef077a8SLaurent Pinchart 	kmem_cache_free(qh_cachep, qh);
6797ef077a8SLaurent Pinchart }
6807ef077a8SLaurent Pinchart 
6817ef077a8SLaurent Pinchart /* one-time init, only for memory state */
priv_init(struct usb_hcd * hcd)6827ef077a8SLaurent Pinchart static int priv_init(struct usb_hcd *hcd)
6837ef077a8SLaurent Pinchart {
6847ef077a8SLaurent Pinchart 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
6851da9e1c0SRui Miguel Silva 	u32 isoc_cache;
6861da9e1c0SRui Miguel Silva 	u32 isoc_thres;
6877ef077a8SLaurent Pinchart 	int i;
6887ef077a8SLaurent Pinchart 
6897ef077a8SLaurent Pinchart 	spin_lock_init(&priv->lock);
6907ef077a8SLaurent Pinchart 
6917ef077a8SLaurent Pinchart 	for (i = 0; i < QH_END; i++)
6927ef077a8SLaurent Pinchart 		INIT_LIST_HEAD(&priv->qh_list[i]);
6937ef077a8SLaurent Pinchart 
6947ef077a8SLaurent Pinchart 	/*
6957ef077a8SLaurent Pinchart 	 * hw default: 1K periodic list heads, one per frame.
6967ef077a8SLaurent Pinchart 	 * periodic_size can shrink by USBCMD update if hcc_params allows.
6977ef077a8SLaurent Pinchart 	 */
6987ef077a8SLaurent Pinchart 	priv->periodic_size = DEFAULT_I_TDPS;
6997ef077a8SLaurent Pinchart 
70060d789f3SRui Miguel Silva 	if (priv->is_isp1763) {
70160d789f3SRui Miguel Silva 		priv->i_thresh = 2;
70260d789f3SRui Miguel Silva 		return 0;
70360d789f3SRui Miguel Silva 	}
70460d789f3SRui Miguel Silva 
7057ef077a8SLaurent Pinchart 	/* controllers may cache some of the periodic schedule ... */
7061da9e1c0SRui Miguel Silva 	isoc_cache = isp1760_hcd_read(hcd, HCC_ISOC_CACHE);
7071da9e1c0SRui Miguel Silva 	isoc_thres = isp1760_hcd_read(hcd, HCC_ISOC_THRES);
7081da9e1c0SRui Miguel Silva 
7097ef077a8SLaurent Pinchart 	/* full frame cache */
7101da9e1c0SRui Miguel Silva 	if (isoc_cache)
7117ef077a8SLaurent Pinchart 		priv->i_thresh = 8;
7127ef077a8SLaurent Pinchart 	else /* N microframes cached */
7131da9e1c0SRui Miguel Silva 		priv->i_thresh = 2 + isoc_thres;
7147ef077a8SLaurent Pinchart 
7157ef077a8SLaurent Pinchart 	return 0;
7167ef077a8SLaurent Pinchart }
7177ef077a8SLaurent Pinchart 
isp1760_hc_setup(struct usb_hcd * hcd)7187ef077a8SLaurent Pinchart static int isp1760_hc_setup(struct usb_hcd *hcd)
7197ef077a8SLaurent Pinchart {
7207ef077a8SLaurent Pinchart 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
72160d789f3SRui Miguel Silva 	u32 atx_reset;
7227ef077a8SLaurent Pinchart 	int result;
7231da9e1c0SRui Miguel Silva 	u32 scratch;
72460d789f3SRui Miguel Silva 	u32 pattern;
7257ef077a8SLaurent Pinchart 
72660d789f3SRui Miguel Silva 	if (priv->is_isp1763)
72760d789f3SRui Miguel Silva 		pattern = 0xcafe;
72860d789f3SRui Miguel Silva 	else
72960d789f3SRui Miguel Silva 		pattern = 0xdeadcafe;
73060d789f3SRui Miguel Silva 
73160d789f3SRui Miguel Silva 	isp1760_hcd_write(hcd, HC_SCRATCH, pattern);
7321da9e1c0SRui Miguel Silva 
7338472896fSRui Miguel Silva 	/*
7348472896fSRui Miguel Silva 	 * we do not care about the read value here we just want to
7358472896fSRui Miguel Silva 	 * change bus pattern.
7368472896fSRui Miguel Silva 	 */
7378472896fSRui Miguel Silva 	isp1760_hcd_read(hcd, HC_CHIP_ID_HIGH);
73860d789f3SRui Miguel Silva 	scratch = isp1760_hcd_read(hcd, HC_SCRATCH);
73960d789f3SRui Miguel Silva 	if (scratch != pattern) {
7408472896fSRui Miguel Silva 		dev_err(hcd->self.controller, "Scratch test failed. 0x%08x\n",
7418472896fSRui Miguel Silva 			scratch);
7427ef077a8SLaurent Pinchart 		return -ENODEV;
7437ef077a8SLaurent Pinchart 	}
7447ef077a8SLaurent Pinchart 
7457ef077a8SLaurent Pinchart 	/*
7467ef077a8SLaurent Pinchart 	 * The RESET_HC bit in the SW_RESET register is supposed to reset the
7477ef077a8SLaurent Pinchart 	 * host controller without touching the CPU interface registers, but at
7487ef077a8SLaurent Pinchart 	 * least on the ISP1761 it seems to behave as the RESET_ALL bit and
7497ef077a8SLaurent Pinchart 	 * reset the whole device. We thus can't use it here, so let's reset
7507ef077a8SLaurent Pinchart 	 * the host controller through the EHCI USB Command register. The device
7517ef077a8SLaurent Pinchart 	 * has been reset in core code anyway, so this shouldn't matter.
7527ef077a8SLaurent Pinchart 	 */
75360d789f3SRui Miguel Silva 	isp1760_hcd_clear(hcd, ISO_BUF_FILL);
75460d789f3SRui Miguel Silva 	isp1760_hcd_clear(hcd, INT_BUF_FILL);
75560d789f3SRui Miguel Silva 	isp1760_hcd_clear(hcd, ATL_BUF_FILL);
75660d789f3SRui Miguel Silva 
75760d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, HC_ATL_PTD_SKIPMAP);
75860d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, HC_INT_PTD_SKIPMAP);
75960d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, HC_ISO_PTD_SKIPMAP);
7607ef077a8SLaurent Pinchart 
7617ef077a8SLaurent Pinchart 	result = ehci_reset(hcd);
7627ef077a8SLaurent Pinchart 	if (result)
7637ef077a8SLaurent Pinchart 		return result;
7647ef077a8SLaurent Pinchart 
7657ef077a8SLaurent Pinchart 	/* Step 11 passed */
7667ef077a8SLaurent Pinchart 
7677ef077a8SLaurent Pinchart 	/* ATL reset */
76860d789f3SRui Miguel Silva 	if (priv->is_isp1763)
76960d789f3SRui Miguel Silva 		atx_reset = SW_RESET_RESET_ATX;
77060d789f3SRui Miguel Silva 	else
77160d789f3SRui Miguel Silva 		atx_reset = ALL_ATX_RESET;
7727ef077a8SLaurent Pinchart 
77360d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, atx_reset);
77460d789f3SRui Miguel Silva 	mdelay(10);
77560d789f3SRui Miguel Silva 	isp1760_hcd_clear(hcd, atx_reset);
77660d789f3SRui Miguel Silva 
77760d789f3SRui Miguel Silva 	if (priv->is_isp1763) {
77860d789f3SRui Miguel Silva 		isp1760_hcd_set(hcd, HW_OTG_DISABLE);
77960d789f3SRui Miguel Silva 		isp1760_hcd_set(hcd, HW_SW_SEL_HC_DC_CLEAR);
78060d789f3SRui Miguel Silva 		isp1760_hcd_set(hcd, HW_HC_2_DIS_CLEAR);
78160d789f3SRui Miguel Silva 		mdelay(10);
78260d789f3SRui Miguel Silva 
78360d789f3SRui Miguel Silva 		isp1760_hcd_set(hcd, HW_INTF_LOCK);
78460d789f3SRui Miguel Silva 	}
78560d789f3SRui Miguel Silva 
78660d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, HC_INT_IRQ_ENABLE);
78760d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, HC_ATL_IRQ_ENABLE);
7887ef077a8SLaurent Pinchart 
7897ef077a8SLaurent Pinchart 	return priv_init(hcd);
7907ef077a8SLaurent Pinchart }
7917ef077a8SLaurent Pinchart 
base_to_chip(u32 base)7927ef077a8SLaurent Pinchart static u32 base_to_chip(u32 base)
7937ef077a8SLaurent Pinchart {
7947ef077a8SLaurent Pinchart 	return ((base - 0x400) >> 3);
7957ef077a8SLaurent Pinchart }
7967ef077a8SLaurent Pinchart 
last_qtd_of_urb(struct isp1760_qtd * qtd,struct isp1760_qh * qh)7977ef077a8SLaurent Pinchart static int last_qtd_of_urb(struct isp1760_qtd *qtd, struct isp1760_qh *qh)
7987ef077a8SLaurent Pinchart {
7997ef077a8SLaurent Pinchart 	struct urb *urb;
8007ef077a8SLaurent Pinchart 
8017ef077a8SLaurent Pinchart 	if (list_is_last(&qtd->qtd_list, &qh->qtd_list))
8027ef077a8SLaurent Pinchart 		return 1;
8037ef077a8SLaurent Pinchart 
8047ef077a8SLaurent Pinchart 	urb = qtd->urb;
8057ef077a8SLaurent Pinchart 	qtd = list_entry(qtd->qtd_list.next, typeof(*qtd), qtd_list);
8067ef077a8SLaurent Pinchart 	return (qtd->urb != urb);
8077ef077a8SLaurent Pinchart }
8087ef077a8SLaurent Pinchart 
8097ef077a8SLaurent Pinchart /* magic numbers that can affect system performance */
8107ef077a8SLaurent Pinchart #define	EHCI_TUNE_CERR		3	/* 0-3 qtd retries; 0 == don't stop */
8117ef077a8SLaurent Pinchart #define	EHCI_TUNE_RL_HS		4	/* nak throttle; see 4.9 */
8127ef077a8SLaurent Pinchart #define	EHCI_TUNE_RL_TT		0
8137ef077a8SLaurent Pinchart #define	EHCI_TUNE_MULT_HS	1	/* 1-3 transactions/uframe; 4.10.3 */
8147ef077a8SLaurent Pinchart #define	EHCI_TUNE_MULT_TT	1
8157ef077a8SLaurent Pinchart #define	EHCI_TUNE_FLS		2	/* (small) 256 frame schedule */
8167ef077a8SLaurent Pinchart 
create_ptd_atl(struct isp1760_qh * qh,struct isp1760_qtd * qtd,struct ptd * ptd)8177ef077a8SLaurent Pinchart static void create_ptd_atl(struct isp1760_qh *qh,
8187ef077a8SLaurent Pinchart 			struct isp1760_qtd *qtd, struct ptd *ptd)
8197ef077a8SLaurent Pinchart {
8207ef077a8SLaurent Pinchart 	u32 maxpacket;
8217ef077a8SLaurent Pinchart 	u32 multi;
8227ef077a8SLaurent Pinchart 	u32 rl = RL_COUNTER;
8237ef077a8SLaurent Pinchart 	u32 nak = NAK_COUNTER;
8247ef077a8SLaurent Pinchart 
8257ef077a8SLaurent Pinchart 	memset(ptd, 0, sizeof(*ptd));
8267ef077a8SLaurent Pinchart 
8277ef077a8SLaurent Pinchart 	/* according to 3.6.2, max packet len can not be > 0x400 */
828dcd2e49bSVincent Mailhol 	maxpacket = usb_maxpacket(qtd->urb->dev, qtd->urb->pipe);
8297ef077a8SLaurent Pinchart 	multi =  1 + ((maxpacket >> 11) & 0x3);
8307ef077a8SLaurent Pinchart 	maxpacket &= 0x7ff;
8317ef077a8SLaurent Pinchart 
8327ef077a8SLaurent Pinchart 	/* DW0 */
8337ef077a8SLaurent Pinchart 	ptd->dw0 = DW0_VALID_BIT;
8347ef077a8SLaurent Pinchart 	ptd->dw0 |= TO_DW0_LENGTH(qtd->length);
8357ef077a8SLaurent Pinchart 	ptd->dw0 |= TO_DW0_MAXPACKET(maxpacket);
8367ef077a8SLaurent Pinchart 	ptd->dw0 |= TO_DW0_ENDPOINT(usb_pipeendpoint(qtd->urb->pipe));
8377ef077a8SLaurent Pinchart 
8387ef077a8SLaurent Pinchart 	/* DW1 */
839abfabc8aSRui Miguel Silva 	ptd->dw1 = TO_DW((usb_pipeendpoint(qtd->urb->pipe) >> 1));
8407ef077a8SLaurent Pinchart 	ptd->dw1 |= TO_DW1_DEVICE_ADDR(usb_pipedevice(qtd->urb->pipe));
8417ef077a8SLaurent Pinchart 	ptd->dw1 |= TO_DW1_PID_TOKEN(qtd->packet_type);
8427ef077a8SLaurent Pinchart 
8437ef077a8SLaurent Pinchart 	if (usb_pipebulk(qtd->urb->pipe))
8447ef077a8SLaurent Pinchart 		ptd->dw1 |= DW1_TRANS_BULK;
8457ef077a8SLaurent Pinchart 	else if  (usb_pipeint(qtd->urb->pipe))
8467ef077a8SLaurent Pinchart 		ptd->dw1 |= DW1_TRANS_INT;
8477ef077a8SLaurent Pinchart 
8487ef077a8SLaurent Pinchart 	if (qtd->urb->dev->speed != USB_SPEED_HIGH) {
8497ef077a8SLaurent Pinchart 		/* split transaction */
8507ef077a8SLaurent Pinchart 
8517ef077a8SLaurent Pinchart 		ptd->dw1 |= DW1_TRANS_SPLIT;
8527ef077a8SLaurent Pinchart 		if (qtd->urb->dev->speed == USB_SPEED_LOW)
8537ef077a8SLaurent Pinchart 			ptd->dw1 |= DW1_SE_USB_LOSPEED;
8547ef077a8SLaurent Pinchart 
8557ef077a8SLaurent Pinchart 		ptd->dw1 |= TO_DW1_PORT_NUM(qtd->urb->dev->ttport);
8567ef077a8SLaurent Pinchart 		ptd->dw1 |= TO_DW1_HUB_NUM(qtd->urb->dev->tt->hub->devnum);
8577ef077a8SLaurent Pinchart 
8587ef077a8SLaurent Pinchart 		/* SE bit for Split INT transfers */
8597ef077a8SLaurent Pinchart 		if (usb_pipeint(qtd->urb->pipe) &&
8607ef077a8SLaurent Pinchart 				(qtd->urb->dev->speed == USB_SPEED_LOW))
861abfabc8aSRui Miguel Silva 			ptd->dw1 |= DW1_SE_USB_LOSPEED;
8627ef077a8SLaurent Pinchart 
8637ef077a8SLaurent Pinchart 		rl = 0;
8647ef077a8SLaurent Pinchart 		nak = 0;
8657ef077a8SLaurent Pinchart 	} else {
8667ef077a8SLaurent Pinchart 		ptd->dw0 |= TO_DW0_MULTI(multi);
8677ef077a8SLaurent Pinchart 		if (usb_pipecontrol(qtd->urb->pipe) ||
8687ef077a8SLaurent Pinchart 						usb_pipebulk(qtd->urb->pipe))
8697ef077a8SLaurent Pinchart 			ptd->dw3 |= TO_DW3_PING(qh->ping);
8707ef077a8SLaurent Pinchart 	}
8717ef077a8SLaurent Pinchart 	/* DW2 */
8727ef077a8SLaurent Pinchart 	ptd->dw2 = 0;
8737ef077a8SLaurent Pinchart 	ptd->dw2 |= TO_DW2_DATA_START_ADDR(base_to_chip(qtd->payload_addr));
8747ef077a8SLaurent Pinchart 	ptd->dw2 |= TO_DW2_RL(rl);
8757ef077a8SLaurent Pinchart 
8767ef077a8SLaurent Pinchart 	/* DW3 */
8777ef077a8SLaurent Pinchart 	ptd->dw3 |= TO_DW3_NAKCOUNT(nak);
8787ef077a8SLaurent Pinchart 	ptd->dw3 |= TO_DW3_DATA_TOGGLE(qh->toggle);
8797ef077a8SLaurent Pinchart 	if (usb_pipecontrol(qtd->urb->pipe)) {
8807ef077a8SLaurent Pinchart 		if (qtd->data_buffer == qtd->urb->setup_packet)
8817ef077a8SLaurent Pinchart 			ptd->dw3 &= ~TO_DW3_DATA_TOGGLE(1);
8827ef077a8SLaurent Pinchart 		else if (last_qtd_of_urb(qtd, qh))
8837ef077a8SLaurent Pinchart 			ptd->dw3 |= TO_DW3_DATA_TOGGLE(1);
8847ef077a8SLaurent Pinchart 	}
8857ef077a8SLaurent Pinchart 
8867ef077a8SLaurent Pinchart 	ptd->dw3 |= DW3_ACTIVE_BIT;
8877ef077a8SLaurent Pinchart 	/* Cerr */
8887ef077a8SLaurent Pinchart 	ptd->dw3 |= TO_DW3_CERR(ERR_COUNTER);
8897ef077a8SLaurent Pinchart }
8907ef077a8SLaurent Pinchart 
transform_add_int(struct isp1760_qh * qh,struct isp1760_qtd * qtd,struct ptd * ptd)8917ef077a8SLaurent Pinchart static void transform_add_int(struct isp1760_qh *qh,
8927ef077a8SLaurent Pinchart 			struct isp1760_qtd *qtd, struct ptd *ptd)
8937ef077a8SLaurent Pinchart {
8947ef077a8SLaurent Pinchart 	u32 usof;
8957ef077a8SLaurent Pinchart 	u32 period;
8967ef077a8SLaurent Pinchart 
8977ef077a8SLaurent Pinchart 	/*
8987ef077a8SLaurent Pinchart 	 * Most of this is guessing. ISP1761 datasheet is quite unclear, and
8997ef077a8SLaurent Pinchart 	 * the algorithm from the original Philips driver code, which was
9007ef077a8SLaurent Pinchart 	 * pretty much used in this driver before as well, is quite horrendous
9017ef077a8SLaurent Pinchart 	 * and, i believe, incorrect. The code below follows the datasheet and
9027ef077a8SLaurent Pinchart 	 * USB2.0 spec as far as I can tell, and plug/unplug seems to be much
9037ef077a8SLaurent Pinchart 	 * more reliable this way (fingers crossed...).
9047ef077a8SLaurent Pinchart 	 */
9057ef077a8SLaurent Pinchart 
9067ef077a8SLaurent Pinchart 	if (qtd->urb->dev->speed == USB_SPEED_HIGH) {
9077ef077a8SLaurent Pinchart 		/* urb->interval is in units of microframes (1/8 ms) */
9087ef077a8SLaurent Pinchart 		period = qtd->urb->interval >> 3;
9097ef077a8SLaurent Pinchart 
9107ef077a8SLaurent Pinchart 		if (qtd->urb->interval > 4)
9117ef077a8SLaurent Pinchart 			usof = 0x01; /* One bit set =>
9127ef077a8SLaurent Pinchart 						interval 1 ms * uFrame-match */
9137ef077a8SLaurent Pinchart 		else if (qtd->urb->interval > 2)
9147ef077a8SLaurent Pinchart 			usof = 0x22; /* Two bits set => interval 1/2 ms */
9157ef077a8SLaurent Pinchart 		else if (qtd->urb->interval > 1)
9167ef077a8SLaurent Pinchart 			usof = 0x55; /* Four bits set => interval 1/4 ms */
9177ef077a8SLaurent Pinchart 		else
9187ef077a8SLaurent Pinchart 			usof = 0xff; /* All bits set => interval 1/8 ms */
9197ef077a8SLaurent Pinchart 	} else {
9207ef077a8SLaurent Pinchart 		/* urb->interval is in units of frames (1 ms) */
9217ef077a8SLaurent Pinchart 		period = qtd->urb->interval;
9227ef077a8SLaurent Pinchart 		usof = 0x0f;		/* Execute Start Split on any of the
9237ef077a8SLaurent Pinchart 					   four first uFrames */
9247ef077a8SLaurent Pinchart 
9257ef077a8SLaurent Pinchart 		/*
9267ef077a8SLaurent Pinchart 		 * First 8 bits in dw5 is uSCS and "specifies which uSOF the
9277ef077a8SLaurent Pinchart 		 * complete split needs to be sent. Valid only for IN." Also,
9287ef077a8SLaurent Pinchart 		 * "All bits can be set to one for every transfer." (p 82,
9297ef077a8SLaurent Pinchart 		 * ISP1761 data sheet.) 0x1c is from Philips driver. Where did
9307ef077a8SLaurent Pinchart 		 * that number come from? 0xff seems to work fine...
9317ef077a8SLaurent Pinchart 		 */
9327ef077a8SLaurent Pinchart 		/* ptd->dw5 = 0x1c; */
933abfabc8aSRui Miguel Silva 		ptd->dw5 = TO_DW(0xff); /* Execute Complete Split on any uFrame */
9347ef077a8SLaurent Pinchart 	}
9357ef077a8SLaurent Pinchart 
9367ef077a8SLaurent Pinchart 	period = period >> 1;/* Ensure equal or shorter period than requested */
9377ef077a8SLaurent Pinchart 	period &= 0xf8; /* Mask off too large values and lowest unused 3 bits */
9387ef077a8SLaurent Pinchart 
939abfabc8aSRui Miguel Silva 	ptd->dw2 |= TO_DW(period);
940abfabc8aSRui Miguel Silva 	ptd->dw4 = TO_DW(usof);
9417ef077a8SLaurent Pinchart }
9427ef077a8SLaurent Pinchart 
create_ptd_int(struct isp1760_qh * qh,struct isp1760_qtd * qtd,struct ptd * ptd)9437ef077a8SLaurent Pinchart static void create_ptd_int(struct isp1760_qh *qh,
9447ef077a8SLaurent Pinchart 			struct isp1760_qtd *qtd, struct ptd *ptd)
9457ef077a8SLaurent Pinchart {
9467ef077a8SLaurent Pinchart 	create_ptd_atl(qh, qtd, ptd);
9477ef077a8SLaurent Pinchart 	transform_add_int(qh, qtd, ptd);
9487ef077a8SLaurent Pinchart }
9497ef077a8SLaurent Pinchart 
isp1760_urb_done(struct usb_hcd * hcd,struct urb * urb)9507ef077a8SLaurent Pinchart static void isp1760_urb_done(struct usb_hcd *hcd, struct urb *urb)
9517ef077a8SLaurent Pinchart __releases(priv->lock)
9527ef077a8SLaurent Pinchart __acquires(priv->lock)
9537ef077a8SLaurent Pinchart {
9547ef077a8SLaurent Pinchart 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
9557ef077a8SLaurent Pinchart 
9567ef077a8SLaurent Pinchart 	if (!urb->unlinked) {
9577ef077a8SLaurent Pinchart 		if (urb->status == -EINPROGRESS)
9587ef077a8SLaurent Pinchart 			urb->status = 0;
9597ef077a8SLaurent Pinchart 	}
9607ef077a8SLaurent Pinchart 
9617ef077a8SLaurent Pinchart 	if (usb_pipein(urb->pipe) && usb_pipetype(urb->pipe) != PIPE_CONTROL) {
9627ef077a8SLaurent Pinchart 		void *ptr;
9637ef077a8SLaurent Pinchart 		for (ptr = urb->transfer_buffer;
9647ef077a8SLaurent Pinchart 		     ptr < urb->transfer_buffer + urb->transfer_buffer_length;
9657ef077a8SLaurent Pinchart 		     ptr += PAGE_SIZE)
9667ef077a8SLaurent Pinchart 			flush_dcache_page(virt_to_page(ptr));
9677ef077a8SLaurent Pinchart 	}
9687ef077a8SLaurent Pinchart 
9697ef077a8SLaurent Pinchart 	/* complete() can reenter this HCD */
9707ef077a8SLaurent Pinchart 	usb_hcd_unlink_urb_from_ep(hcd, urb);
9717ef077a8SLaurent Pinchart 	spin_unlock(&priv->lock);
9727ef077a8SLaurent Pinchart 	usb_hcd_giveback_urb(hcd, urb, urb->status);
9737ef077a8SLaurent Pinchart 	spin_lock(&priv->lock);
9747ef077a8SLaurent Pinchart }
9757ef077a8SLaurent Pinchart 
qtd_alloc(gfp_t flags,struct urb * urb,u8 packet_type)9767ef077a8SLaurent Pinchart static struct isp1760_qtd *qtd_alloc(gfp_t flags, struct urb *urb,
9777ef077a8SLaurent Pinchart 								u8 packet_type)
9787ef077a8SLaurent Pinchart {
9797ef077a8SLaurent Pinchart 	struct isp1760_qtd *qtd;
9807ef077a8SLaurent Pinchart 
9817ef077a8SLaurent Pinchart 	qtd = kmem_cache_zalloc(qtd_cachep, flags);
9827ef077a8SLaurent Pinchart 	if (!qtd)
9837ef077a8SLaurent Pinchart 		return NULL;
9847ef077a8SLaurent Pinchart 
9857ef077a8SLaurent Pinchart 	INIT_LIST_HEAD(&qtd->qtd_list);
9867ef077a8SLaurent Pinchart 	qtd->urb = urb;
9877ef077a8SLaurent Pinchart 	qtd->packet_type = packet_type;
9887ef077a8SLaurent Pinchart 	qtd->status = QTD_ENQUEUED;
9897ef077a8SLaurent Pinchart 	qtd->actual_length = 0;
9907ef077a8SLaurent Pinchart 
9917ef077a8SLaurent Pinchart 	return qtd;
9927ef077a8SLaurent Pinchart }
9937ef077a8SLaurent Pinchart 
qtd_free(struct isp1760_qtd * qtd)9947ef077a8SLaurent Pinchart static void qtd_free(struct isp1760_qtd *qtd)
9957ef077a8SLaurent Pinchart {
9967ef077a8SLaurent Pinchart 	WARN_ON(qtd->payload_addr);
9977ef077a8SLaurent Pinchart 	kmem_cache_free(qtd_cachep, qtd);
9987ef077a8SLaurent Pinchart }
9997ef077a8SLaurent Pinchart 
start_bus_transfer(struct usb_hcd * hcd,u32 ptd_offset,int slot,struct isp1760_slotinfo * slots,struct isp1760_qtd * qtd,struct isp1760_qh * qh,struct ptd * ptd)10007ef077a8SLaurent Pinchart static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot,
10017ef077a8SLaurent Pinchart 				struct isp1760_slotinfo *slots,
10027ef077a8SLaurent Pinchart 				struct isp1760_qtd *qtd, struct isp1760_qh *qh,
10037ef077a8SLaurent Pinchart 				struct ptd *ptd)
10047ef077a8SLaurent Pinchart {
10057ef077a8SLaurent Pinchart 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
100660d789f3SRui Miguel Silva 	const struct isp1760_memory_layout *mem = priv->memory_layout;
10077ef077a8SLaurent Pinchart 	int skip_map;
10087ef077a8SLaurent Pinchart 
100960d789f3SRui Miguel Silva 	WARN_ON((slot < 0) || (slot > mem->slot_num - 1));
10107ef077a8SLaurent Pinchart 	WARN_ON(qtd->length && !qtd->payload_addr);
10117ef077a8SLaurent Pinchart 	WARN_ON(slots[slot].qtd);
10127ef077a8SLaurent Pinchart 	WARN_ON(slots[slot].qh);
10137ef077a8SLaurent Pinchart 	WARN_ON(qtd->status != QTD_PAYLOAD_ALLOC);
10147ef077a8SLaurent Pinchart 
101560d789f3SRui Miguel Silva 	if (priv->is_isp1763)
101660d789f3SRui Miguel Silva 		ndelay(100);
101760d789f3SRui Miguel Silva 
10187ef077a8SLaurent Pinchart 	/* Make sure done map has not triggered from some unlinked transfer */
10197ef077a8SLaurent Pinchart 	if (ptd_offset == ATL_PTD_OFFSET) {
102060d789f3SRui Miguel Silva 		skip_map = isp1760_hcd_read(hcd, HC_ATL_PTD_SKIPMAP);
102160d789f3SRui Miguel Silva 		isp1760_hcd_write(hcd, HC_ATL_PTD_SKIPMAP,
102260d789f3SRui Miguel Silva 				  skip_map | (1 << slot));
102360d789f3SRui Miguel Silva 		priv->atl_done_map |= isp1760_hcd_read(hcd, HC_ATL_PTD_DONEMAP);
10247ef077a8SLaurent Pinchart 		priv->atl_done_map &= ~(1 << slot);
10257ef077a8SLaurent Pinchart 	} else {
102660d789f3SRui Miguel Silva 		skip_map = isp1760_hcd_read(hcd, HC_INT_PTD_SKIPMAP);
102760d789f3SRui Miguel Silva 		isp1760_hcd_write(hcd, HC_INT_PTD_SKIPMAP,
102860d789f3SRui Miguel Silva 				  skip_map | (1 << slot));
102960d789f3SRui Miguel Silva 		priv->int_done_map |= isp1760_hcd_read(hcd, HC_INT_PTD_DONEMAP);
10307ef077a8SLaurent Pinchart 		priv->int_done_map &= ~(1 << slot);
10317ef077a8SLaurent Pinchart 	}
10327ef077a8SLaurent Pinchart 
103360d789f3SRui Miguel Silva 	skip_map &= ~(1 << slot);
10347ef077a8SLaurent Pinchart 	qh->slot = slot;
10357ef077a8SLaurent Pinchart 	qtd->status = QTD_XFER_STARTED;
10367ef077a8SLaurent Pinchart 	slots[slot].timestamp = jiffies;
10377ef077a8SLaurent Pinchart 	slots[slot].qtd = qtd;
10387ef077a8SLaurent Pinchart 	slots[slot].qh = qh;
103960d789f3SRui Miguel Silva 	ptd_write(hcd, ptd_offset, slot, ptd);
10407ef077a8SLaurent Pinchart 
104160d789f3SRui Miguel Silva 	if (ptd_offset == ATL_PTD_OFFSET)
104260d789f3SRui Miguel Silva 		isp1760_hcd_write(hcd, HC_ATL_PTD_SKIPMAP, skip_map);
104360d789f3SRui Miguel Silva 	else
104460d789f3SRui Miguel Silva 		isp1760_hcd_write(hcd, HC_INT_PTD_SKIPMAP, skip_map);
10457ef077a8SLaurent Pinchart }
10467ef077a8SLaurent Pinchart 
is_short_bulk(struct isp1760_qtd * qtd)10477ef077a8SLaurent Pinchart static int is_short_bulk(struct isp1760_qtd *qtd)
10487ef077a8SLaurent Pinchart {
10497ef077a8SLaurent Pinchart 	return (usb_pipebulk(qtd->urb->pipe) &&
10507ef077a8SLaurent Pinchart 					(qtd->actual_length < qtd->length));
10517ef077a8SLaurent Pinchart }
10527ef077a8SLaurent Pinchart 
collect_qtds(struct usb_hcd * hcd,struct isp1760_qh * qh,struct list_head * urb_list)10537ef077a8SLaurent Pinchart static void collect_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh,
10547ef077a8SLaurent Pinchart 						struct list_head *urb_list)
10557ef077a8SLaurent Pinchart {
10567ef077a8SLaurent Pinchart 	struct isp1760_qtd *qtd, *qtd_next;
10577ef077a8SLaurent Pinchart 	struct urb_listitem *urb_listitem;
10581da9e1c0SRui Miguel Silva 	int last_qtd;
10597ef077a8SLaurent Pinchart 
10607ef077a8SLaurent Pinchart 	list_for_each_entry_safe(qtd, qtd_next, &qh->qtd_list, qtd_list) {
10617ef077a8SLaurent Pinchart 		if (qtd->status < QTD_XFER_COMPLETE)
10627ef077a8SLaurent Pinchart 			break;
10637ef077a8SLaurent Pinchart 
10647ef077a8SLaurent Pinchart 		last_qtd = last_qtd_of_urb(qtd, qh);
10657ef077a8SLaurent Pinchart 
10667ef077a8SLaurent Pinchart 		if ((!last_qtd) && (qtd->status == QTD_RETIRE))
10677ef077a8SLaurent Pinchart 			qtd_next->status = QTD_RETIRE;
10687ef077a8SLaurent Pinchart 
10697ef077a8SLaurent Pinchart 		if (qtd->status == QTD_XFER_COMPLETE) {
10707ef077a8SLaurent Pinchart 			if (qtd->actual_length) {
10717ef077a8SLaurent Pinchart 				switch (qtd->packet_type) {
10727ef077a8SLaurent Pinchart 				case IN_PID:
107360d789f3SRui Miguel Silva 					mem_read(hcd, qtd->payload_addr,
10747ef077a8SLaurent Pinchart 						 qtd->data_buffer,
10757ef077a8SLaurent Pinchart 						 qtd->actual_length);
10760d9b6d49SGustavo A. R. Silva 					fallthrough;
10777ef077a8SLaurent Pinchart 				case OUT_PID:
10787ef077a8SLaurent Pinchart 					qtd->urb->actual_length +=
10797ef077a8SLaurent Pinchart 							qtd->actual_length;
10800d9b6d49SGustavo A. R. Silva 					fallthrough;
10817ef077a8SLaurent Pinchart 				case SETUP_PID:
10827ef077a8SLaurent Pinchart 					break;
10837ef077a8SLaurent Pinchart 				}
10847ef077a8SLaurent Pinchart 			}
10857ef077a8SLaurent Pinchart 
10867ef077a8SLaurent Pinchart 			if (is_short_bulk(qtd)) {
10877ef077a8SLaurent Pinchart 				if (qtd->urb->transfer_flags & URB_SHORT_NOT_OK)
10887ef077a8SLaurent Pinchart 					qtd->urb->status = -EREMOTEIO;
10897ef077a8SLaurent Pinchart 				if (!last_qtd)
10907ef077a8SLaurent Pinchart 					qtd_next->status = QTD_RETIRE;
10917ef077a8SLaurent Pinchart 			}
10927ef077a8SLaurent Pinchart 		}
10937ef077a8SLaurent Pinchart 
10947ef077a8SLaurent Pinchart 		if (qtd->payload_addr)
10957ef077a8SLaurent Pinchart 			free_mem(hcd, qtd);
10967ef077a8SLaurent Pinchart 
10977ef077a8SLaurent Pinchart 		if (last_qtd) {
10987ef077a8SLaurent Pinchart 			if ((qtd->status == QTD_RETIRE) &&
10997ef077a8SLaurent Pinchart 					(qtd->urb->status == -EINPROGRESS))
11007ef077a8SLaurent Pinchart 				qtd->urb->status = -EPIPE;
11017ef077a8SLaurent Pinchart 			/* Defer calling of urb_done() since it releases lock */
11027ef077a8SLaurent Pinchart 			urb_listitem = kmem_cache_zalloc(urb_listitem_cachep,
11037ef077a8SLaurent Pinchart 								GFP_ATOMIC);
11047ef077a8SLaurent Pinchart 			if (unlikely(!urb_listitem))
11057ef077a8SLaurent Pinchart 				break; /* Try again on next call */
11067ef077a8SLaurent Pinchart 			urb_listitem->urb = qtd->urb;
11077ef077a8SLaurent Pinchart 			list_add_tail(&urb_listitem->urb_list, urb_list);
11087ef077a8SLaurent Pinchart 		}
11097ef077a8SLaurent Pinchart 
11107ef077a8SLaurent Pinchart 		list_del(&qtd->qtd_list);
11117ef077a8SLaurent Pinchart 		qtd_free(qtd);
11127ef077a8SLaurent Pinchart 	}
11137ef077a8SLaurent Pinchart }
11147ef077a8SLaurent Pinchart 
11157ef077a8SLaurent Pinchart #define ENQUEUE_DEPTH	2
enqueue_qtds(struct usb_hcd * hcd,struct isp1760_qh * qh)11167ef077a8SLaurent Pinchart static void enqueue_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh)
11177ef077a8SLaurent Pinchart {
11187ef077a8SLaurent Pinchart 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
111960d789f3SRui Miguel Silva 	const struct isp1760_memory_layout *mem = priv->memory_layout;
112060d789f3SRui Miguel Silva 	int slot_num = mem->slot_num;
11217ef077a8SLaurent Pinchart 	int ptd_offset;
11227ef077a8SLaurent Pinchart 	struct isp1760_slotinfo *slots;
11237ef077a8SLaurent Pinchart 	int curr_slot, free_slot;
11247ef077a8SLaurent Pinchart 	int n;
11257ef077a8SLaurent Pinchart 	struct ptd ptd;
11267ef077a8SLaurent Pinchart 	struct isp1760_qtd *qtd;
11277ef077a8SLaurent Pinchart 
11287ef077a8SLaurent Pinchart 	if (unlikely(list_empty(&qh->qtd_list))) {
11297ef077a8SLaurent Pinchart 		WARN_ON(1);
11307ef077a8SLaurent Pinchart 		return;
11317ef077a8SLaurent Pinchart 	}
11327ef077a8SLaurent Pinchart 
11337ef077a8SLaurent Pinchart 	/* Make sure this endpoint's TT buffer is clean before queueing ptds */
11347ef077a8SLaurent Pinchart 	if (qh->tt_buffer_dirty)
11357ef077a8SLaurent Pinchart 		return;
11367ef077a8SLaurent Pinchart 
11377ef077a8SLaurent Pinchart 	if (usb_pipeint(list_entry(qh->qtd_list.next, struct isp1760_qtd,
11387ef077a8SLaurent Pinchart 							qtd_list)->urb->pipe)) {
11397ef077a8SLaurent Pinchart 		ptd_offset = INT_PTD_OFFSET;
11407ef077a8SLaurent Pinchart 		slots = priv->int_slots;
11417ef077a8SLaurent Pinchart 	} else {
11427ef077a8SLaurent Pinchart 		ptd_offset = ATL_PTD_OFFSET;
11437ef077a8SLaurent Pinchart 		slots = priv->atl_slots;
11447ef077a8SLaurent Pinchart 	}
11457ef077a8SLaurent Pinchart 
11467ef077a8SLaurent Pinchart 	free_slot = -1;
114760d789f3SRui Miguel Silva 	for (curr_slot = 0; curr_slot < slot_num; curr_slot++) {
11487ef077a8SLaurent Pinchart 		if ((free_slot == -1) && (slots[curr_slot].qtd == NULL))
11497ef077a8SLaurent Pinchart 			free_slot = curr_slot;
11507ef077a8SLaurent Pinchart 		if (slots[curr_slot].qh == qh)
11517ef077a8SLaurent Pinchart 			break;
11527ef077a8SLaurent Pinchart 	}
11537ef077a8SLaurent Pinchart 
11547ef077a8SLaurent Pinchart 	n = 0;
11557ef077a8SLaurent Pinchart 	list_for_each_entry(qtd, &qh->qtd_list, qtd_list) {
11567ef077a8SLaurent Pinchart 		if (qtd->status == QTD_ENQUEUED) {
11577ef077a8SLaurent Pinchart 			WARN_ON(qtd->payload_addr);
11587ef077a8SLaurent Pinchart 			alloc_mem(hcd, qtd);
11597ef077a8SLaurent Pinchart 			if ((qtd->length) && (!qtd->payload_addr))
11607ef077a8SLaurent Pinchart 				break;
11617ef077a8SLaurent Pinchart 
116260d789f3SRui Miguel Silva 			if (qtd->length && (qtd->packet_type == SETUP_PID ||
116360d789f3SRui Miguel Silva 					    qtd->packet_type == OUT_PID)) {
116460d789f3SRui Miguel Silva 				mem_write(hcd, qtd->payload_addr,
11657ef077a8SLaurent Pinchart 					  qtd->data_buffer, qtd->length);
11667ef077a8SLaurent Pinchart 			}
11677ef077a8SLaurent Pinchart 
11687ef077a8SLaurent Pinchart 			qtd->status = QTD_PAYLOAD_ALLOC;
11697ef077a8SLaurent Pinchart 		}
11707ef077a8SLaurent Pinchart 
11717ef077a8SLaurent Pinchart 		if (qtd->status == QTD_PAYLOAD_ALLOC) {
11727ef077a8SLaurent Pinchart /*
11737ef077a8SLaurent Pinchart 			if ((curr_slot > 31) && (free_slot == -1))
11747ef077a8SLaurent Pinchart 				dev_dbg(hcd->self.controller, "%s: No slot "
11757ef077a8SLaurent Pinchart 					"available for transfer\n", __func__);
11767ef077a8SLaurent Pinchart */
11777ef077a8SLaurent Pinchart 			/* Start xfer for this endpoint if not already done */
117860d789f3SRui Miguel Silva 			if ((curr_slot > slot_num - 1) && (free_slot > -1)) {
11797ef077a8SLaurent Pinchart 				if (usb_pipeint(qtd->urb->pipe))
11807ef077a8SLaurent Pinchart 					create_ptd_int(qh, qtd, &ptd);
11817ef077a8SLaurent Pinchart 				else
11827ef077a8SLaurent Pinchart 					create_ptd_atl(qh, qtd, &ptd);
11837ef077a8SLaurent Pinchart 
11847ef077a8SLaurent Pinchart 				start_bus_transfer(hcd, ptd_offset, free_slot,
11857ef077a8SLaurent Pinchart 							slots, qtd, qh, &ptd);
11867ef077a8SLaurent Pinchart 				curr_slot = free_slot;
11877ef077a8SLaurent Pinchart 			}
11887ef077a8SLaurent Pinchart 
11897ef077a8SLaurent Pinchart 			n++;
11907ef077a8SLaurent Pinchart 			if (n >= ENQUEUE_DEPTH)
11917ef077a8SLaurent Pinchart 				break;
11927ef077a8SLaurent Pinchart 		}
11937ef077a8SLaurent Pinchart 	}
11947ef077a8SLaurent Pinchart }
11957ef077a8SLaurent Pinchart 
schedule_ptds(struct usb_hcd * hcd)11967ef077a8SLaurent Pinchart static void schedule_ptds(struct usb_hcd *hcd)
11977ef077a8SLaurent Pinchart {
11987ef077a8SLaurent Pinchart 	struct isp1760_hcd *priv;
11997ef077a8SLaurent Pinchart 	struct isp1760_qh *qh, *qh_next;
12007ef077a8SLaurent Pinchart 	struct list_head *ep_queue;
12017ef077a8SLaurent Pinchart 	LIST_HEAD(urb_list);
12027ef077a8SLaurent Pinchart 	struct urb_listitem *urb_listitem, *urb_listitem_next;
12037ef077a8SLaurent Pinchart 	int i;
12047ef077a8SLaurent Pinchart 
12057ef077a8SLaurent Pinchart 	if (!hcd) {
12067ef077a8SLaurent Pinchart 		WARN_ON(1);
12077ef077a8SLaurent Pinchart 		return;
12087ef077a8SLaurent Pinchart 	}
12097ef077a8SLaurent Pinchart 
12107ef077a8SLaurent Pinchart 	priv = hcd_to_priv(hcd);
12117ef077a8SLaurent Pinchart 
12127ef077a8SLaurent Pinchart 	/*
12137ef077a8SLaurent Pinchart 	 * check finished/retired xfers, transfer payloads, call urb_done()
12147ef077a8SLaurent Pinchart 	 */
12157ef077a8SLaurent Pinchart 	for (i = 0; i < QH_END; i++) {
12167ef077a8SLaurent Pinchart 		ep_queue = &priv->qh_list[i];
12177ef077a8SLaurent Pinchart 		list_for_each_entry_safe(qh, qh_next, ep_queue, qh_list) {
12187ef077a8SLaurent Pinchart 			collect_qtds(hcd, qh, &urb_list);
12197ef077a8SLaurent Pinchart 			if (list_empty(&qh->qtd_list))
12207ef077a8SLaurent Pinchart 				list_del(&qh->qh_list);
12217ef077a8SLaurent Pinchart 		}
12227ef077a8SLaurent Pinchart 	}
12237ef077a8SLaurent Pinchart 
12247ef077a8SLaurent Pinchart 	list_for_each_entry_safe(urb_listitem, urb_listitem_next, &urb_list,
12257ef077a8SLaurent Pinchart 								urb_list) {
12267ef077a8SLaurent Pinchart 		isp1760_urb_done(hcd, urb_listitem->urb);
12277ef077a8SLaurent Pinchart 		kmem_cache_free(urb_listitem_cachep, urb_listitem);
12287ef077a8SLaurent Pinchart 	}
12297ef077a8SLaurent Pinchart 
12307ef077a8SLaurent Pinchart 	/*
12317ef077a8SLaurent Pinchart 	 * Schedule packets for transfer.
12327ef077a8SLaurent Pinchart 	 *
12337ef077a8SLaurent Pinchart 	 * According to USB2.0 specification:
12347ef077a8SLaurent Pinchart 	 *
12357ef077a8SLaurent Pinchart 	 * 1st prio: interrupt xfers, up to 80 % of bandwidth
12367ef077a8SLaurent Pinchart 	 * 2nd prio: control xfers
12377ef077a8SLaurent Pinchart 	 * 3rd prio: bulk xfers
12387ef077a8SLaurent Pinchart 	 *
12397ef077a8SLaurent Pinchart 	 * ... but let's use a simpler scheme here (mostly because ISP1761 doc
12407ef077a8SLaurent Pinchart 	 * is very unclear on how to prioritize traffic):
12417ef077a8SLaurent Pinchart 	 *
12427ef077a8SLaurent Pinchart 	 * 1) Enqueue any queued control transfers, as long as payload chip mem
12437ef077a8SLaurent Pinchart 	 *    and PTD ATL slots are available.
12447ef077a8SLaurent Pinchart 	 * 2) Enqueue any queued INT transfers, as long as payload chip mem
12457ef077a8SLaurent Pinchart 	 *    and PTD INT slots are available.
12467ef077a8SLaurent Pinchart 	 * 3) Enqueue any queued bulk transfers, as long as payload chip mem
12477ef077a8SLaurent Pinchart 	 *    and PTD ATL slots are available.
12487ef077a8SLaurent Pinchart 	 *
12497ef077a8SLaurent Pinchart 	 * Use double buffering (ENQUEUE_DEPTH==2) as a compromise between
12507ef077a8SLaurent Pinchart 	 * conservation of chip mem and performance.
12517ef077a8SLaurent Pinchart 	 *
12527ef077a8SLaurent Pinchart 	 * I'm sure this scheme could be improved upon!
12537ef077a8SLaurent Pinchart 	 */
12547ef077a8SLaurent Pinchart 	for (i = 0; i < QH_END; i++) {
12557ef077a8SLaurent Pinchart 		ep_queue = &priv->qh_list[i];
12567ef077a8SLaurent Pinchart 		list_for_each_entry_safe(qh, qh_next, ep_queue, qh_list)
12577ef077a8SLaurent Pinchart 			enqueue_qtds(hcd, qh);
12587ef077a8SLaurent Pinchart 	}
12597ef077a8SLaurent Pinchart }
12607ef077a8SLaurent Pinchart 
12617ef077a8SLaurent Pinchart #define PTD_STATE_QTD_DONE	1
12627ef077a8SLaurent Pinchart #define PTD_STATE_QTD_RELOAD	2
12637ef077a8SLaurent Pinchart #define PTD_STATE_URB_RETIRE	3
12647ef077a8SLaurent Pinchart 
check_int_transfer(struct usb_hcd * hcd,struct ptd * ptd,struct urb * urb)12657ef077a8SLaurent Pinchart static int check_int_transfer(struct usb_hcd *hcd, struct ptd *ptd,
12667ef077a8SLaurent Pinchart 								struct urb *urb)
12677ef077a8SLaurent Pinchart {
1268abfabc8aSRui Miguel Silva 	u32 dw4;
12697ef077a8SLaurent Pinchart 	int i;
12707ef077a8SLaurent Pinchart 
1271abfabc8aSRui Miguel Silva 	dw4 = TO_U32(ptd->dw4);
12727ef077a8SLaurent Pinchart 	dw4 >>= 8;
12737ef077a8SLaurent Pinchart 
12747ef077a8SLaurent Pinchart 	/* FIXME: ISP1761 datasheet does not say what to do with these. Do we
12757ef077a8SLaurent Pinchart 	   need to handle these errors? Is it done in hardware? */
12767ef077a8SLaurent Pinchart 
12777ef077a8SLaurent Pinchart 	if (ptd->dw3 & DW3_HALT_BIT) {
12787ef077a8SLaurent Pinchart 
12797ef077a8SLaurent Pinchart 		urb->status = -EPROTO; /* Default unknown error */
12807ef077a8SLaurent Pinchart 
12817ef077a8SLaurent Pinchart 		for (i = 0; i < 8; i++) {
12827ef077a8SLaurent Pinchart 			switch (dw4 & 0x7) {
12837ef077a8SLaurent Pinchart 			case INT_UNDERRUN:
12847ef077a8SLaurent Pinchart 				dev_dbg(hcd->self.controller, "%s: underrun "
12857ef077a8SLaurent Pinchart 						"during uFrame %d\n",
12867ef077a8SLaurent Pinchart 						__func__, i);
12877ef077a8SLaurent Pinchart 				urb->status = -ECOMM; /* Could not write data */
12887ef077a8SLaurent Pinchart 				break;
12897ef077a8SLaurent Pinchart 			case INT_EXACT:
12907ef077a8SLaurent Pinchart 				dev_dbg(hcd->self.controller, "%s: transaction "
12917ef077a8SLaurent Pinchart 						"error during uFrame %d\n",
12927ef077a8SLaurent Pinchart 						__func__, i);
12937ef077a8SLaurent Pinchart 				urb->status = -EPROTO; /* timeout, bad CRC, PID
12947ef077a8SLaurent Pinchart 							  error etc. */
12957ef077a8SLaurent Pinchart 				break;
12967ef077a8SLaurent Pinchart 			case INT_BABBLE:
12977ef077a8SLaurent Pinchart 				dev_dbg(hcd->self.controller, "%s: babble "
12987ef077a8SLaurent Pinchart 						"error during uFrame %d\n",
12997ef077a8SLaurent Pinchart 						__func__, i);
13007ef077a8SLaurent Pinchart 				urb->status = -EOVERFLOW;
13017ef077a8SLaurent Pinchart 				break;
13027ef077a8SLaurent Pinchart 			}
13037ef077a8SLaurent Pinchart 			dw4 >>= 3;
13047ef077a8SLaurent Pinchart 		}
13057ef077a8SLaurent Pinchart 
13067ef077a8SLaurent Pinchart 		return PTD_STATE_URB_RETIRE;
13077ef077a8SLaurent Pinchart 	}
13087ef077a8SLaurent Pinchart 
13097ef077a8SLaurent Pinchart 	return PTD_STATE_QTD_DONE;
13107ef077a8SLaurent Pinchart }
13117ef077a8SLaurent Pinchart 
check_atl_transfer(struct usb_hcd * hcd,struct ptd * ptd,struct urb * urb)13127ef077a8SLaurent Pinchart static int check_atl_transfer(struct usb_hcd *hcd, struct ptd *ptd,
13137ef077a8SLaurent Pinchart 								struct urb *urb)
13147ef077a8SLaurent Pinchart {
13157ef077a8SLaurent Pinchart 	WARN_ON(!ptd);
13167ef077a8SLaurent Pinchart 	if (ptd->dw3 & DW3_HALT_BIT) {
13177ef077a8SLaurent Pinchart 		if (ptd->dw3 & DW3_BABBLE_BIT)
13187ef077a8SLaurent Pinchart 			urb->status = -EOVERFLOW;
13197ef077a8SLaurent Pinchart 		else if (FROM_DW3_CERR(ptd->dw3))
13207ef077a8SLaurent Pinchart 			urb->status = -EPIPE;  /* Stall */
13217ef077a8SLaurent Pinchart 		else
13227ef077a8SLaurent Pinchart 			urb->status = -EPROTO; /* Unknown */
13237ef077a8SLaurent Pinchart /*
13247ef077a8SLaurent Pinchart 		dev_dbg(hcd->self.controller, "%s: ptd error:\n"
13257ef077a8SLaurent Pinchart 			"        dw0: %08x dw1: %08x dw2: %08x dw3: %08x\n"
13267ef077a8SLaurent Pinchart 			"        dw4: %08x dw5: %08x dw6: %08x dw7: %08x\n",
13277ef077a8SLaurent Pinchart 			__func__,
13287ef077a8SLaurent Pinchart 			ptd->dw0, ptd->dw1, ptd->dw2, ptd->dw3,
13297ef077a8SLaurent Pinchart 			ptd->dw4, ptd->dw5, ptd->dw6, ptd->dw7);
13307ef077a8SLaurent Pinchart */
13317ef077a8SLaurent Pinchart 		return PTD_STATE_URB_RETIRE;
13327ef077a8SLaurent Pinchart 	}
13337ef077a8SLaurent Pinchart 
13347ef077a8SLaurent Pinchart 	if ((ptd->dw3 & DW3_ERROR_BIT) && (ptd->dw3 & DW3_ACTIVE_BIT)) {
13357ef077a8SLaurent Pinchart 		/* Transfer Error, *but* active and no HALT -> reload */
13367ef077a8SLaurent Pinchart 		dev_dbg(hcd->self.controller, "PID error; reloading ptd\n");
13377ef077a8SLaurent Pinchart 		return PTD_STATE_QTD_RELOAD;
13387ef077a8SLaurent Pinchart 	}
13397ef077a8SLaurent Pinchart 
13407ef077a8SLaurent Pinchart 	if (!FROM_DW3_NAKCOUNT(ptd->dw3) && (ptd->dw3 & DW3_ACTIVE_BIT)) {
13417ef077a8SLaurent Pinchart 		/*
13427ef077a8SLaurent Pinchart 		 * NAKs are handled in HW by the chip. Usually if the
13437ef077a8SLaurent Pinchart 		 * device is not able to send data fast enough.
13447ef077a8SLaurent Pinchart 		 * This happens mostly on slower hardware.
13457ef077a8SLaurent Pinchart 		 */
13467ef077a8SLaurent Pinchart 		return PTD_STATE_QTD_RELOAD;
13477ef077a8SLaurent Pinchart 	}
13487ef077a8SLaurent Pinchart 
13497ef077a8SLaurent Pinchart 	return PTD_STATE_QTD_DONE;
13507ef077a8SLaurent Pinchart }
13517ef077a8SLaurent Pinchart 
handle_done_ptds(struct usb_hcd * hcd)13527ef077a8SLaurent Pinchart static void handle_done_ptds(struct usb_hcd *hcd)
13537ef077a8SLaurent Pinchart {
13547ef077a8SLaurent Pinchart 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
13557ef077a8SLaurent Pinchart 	struct ptd ptd;
13567ef077a8SLaurent Pinchart 	struct isp1760_qh *qh;
13577ef077a8SLaurent Pinchart 	int slot;
13587ef077a8SLaurent Pinchart 	int state;
13597ef077a8SLaurent Pinchart 	struct isp1760_slotinfo *slots;
13607ef077a8SLaurent Pinchart 	u32 ptd_offset;
13617ef077a8SLaurent Pinchart 	struct isp1760_qtd *qtd;
13627ef077a8SLaurent Pinchart 	int modified;
13637ef077a8SLaurent Pinchart 	int skip_map;
13647ef077a8SLaurent Pinchart 
136560d789f3SRui Miguel Silva 	skip_map = isp1760_hcd_read(hcd, HC_INT_PTD_SKIPMAP);
13667ef077a8SLaurent Pinchart 	priv->int_done_map &= ~skip_map;
136760d789f3SRui Miguel Silva 	skip_map = isp1760_hcd_read(hcd, HC_ATL_PTD_SKIPMAP);
13687ef077a8SLaurent Pinchart 	priv->atl_done_map &= ~skip_map;
13697ef077a8SLaurent Pinchart 
13707ef077a8SLaurent Pinchart 	modified = priv->int_done_map || priv->atl_done_map;
13717ef077a8SLaurent Pinchart 
13727ef077a8SLaurent Pinchart 	while (priv->int_done_map || priv->atl_done_map) {
13737ef077a8SLaurent Pinchart 		if (priv->int_done_map) {
13747ef077a8SLaurent Pinchart 			/* INT ptd */
13757ef077a8SLaurent Pinchart 			slot = __ffs(priv->int_done_map);
13767ef077a8SLaurent Pinchart 			priv->int_done_map &= ~(1 << slot);
13777ef077a8SLaurent Pinchart 			slots = priv->int_slots;
13787ef077a8SLaurent Pinchart 			/* This should not trigger, and could be removed if
13797ef077a8SLaurent Pinchart 			   noone have any problems with it triggering: */
13807ef077a8SLaurent Pinchart 			if (!slots[slot].qh) {
13817ef077a8SLaurent Pinchart 				WARN_ON(1);
13827ef077a8SLaurent Pinchart 				continue;
13837ef077a8SLaurent Pinchart 			}
13847ef077a8SLaurent Pinchart 			ptd_offset = INT_PTD_OFFSET;
138560d789f3SRui Miguel Silva 			ptd_read(hcd, INT_PTD_OFFSET, slot, &ptd);
13867ef077a8SLaurent Pinchart 			state = check_int_transfer(hcd, &ptd,
13877ef077a8SLaurent Pinchart 							slots[slot].qtd->urb);
13887ef077a8SLaurent Pinchart 		} else {
13897ef077a8SLaurent Pinchart 			/* ATL ptd */
13907ef077a8SLaurent Pinchart 			slot = __ffs(priv->atl_done_map);
13917ef077a8SLaurent Pinchart 			priv->atl_done_map &= ~(1 << slot);
13927ef077a8SLaurent Pinchart 			slots = priv->atl_slots;
13937ef077a8SLaurent Pinchart 			/* This should not trigger, and could be removed if
13947ef077a8SLaurent Pinchart 			   noone have any problems with it triggering: */
13957ef077a8SLaurent Pinchart 			if (!slots[slot].qh) {
13967ef077a8SLaurent Pinchart 				WARN_ON(1);
13977ef077a8SLaurent Pinchart 				continue;
13987ef077a8SLaurent Pinchart 			}
13997ef077a8SLaurent Pinchart 			ptd_offset = ATL_PTD_OFFSET;
140060d789f3SRui Miguel Silva 			ptd_read(hcd, ATL_PTD_OFFSET, slot, &ptd);
14017ef077a8SLaurent Pinchart 			state = check_atl_transfer(hcd, &ptd,
14027ef077a8SLaurent Pinchart 							slots[slot].qtd->urb);
14037ef077a8SLaurent Pinchart 		}
14047ef077a8SLaurent Pinchart 
14057ef077a8SLaurent Pinchart 		qtd = slots[slot].qtd;
14067ef077a8SLaurent Pinchart 		slots[slot].qtd = NULL;
14077ef077a8SLaurent Pinchart 		qh = slots[slot].qh;
14087ef077a8SLaurent Pinchart 		slots[slot].qh = NULL;
14097ef077a8SLaurent Pinchart 		qh->slot = -1;
14107ef077a8SLaurent Pinchart 
14117ef077a8SLaurent Pinchart 		WARN_ON(qtd->status != QTD_XFER_STARTED);
14127ef077a8SLaurent Pinchart 
14137ef077a8SLaurent Pinchart 		switch (state) {
14147ef077a8SLaurent Pinchart 		case PTD_STATE_QTD_DONE:
14157ef077a8SLaurent Pinchart 			if ((usb_pipeint(qtd->urb->pipe)) &&
14167ef077a8SLaurent Pinchart 				       (qtd->urb->dev->speed != USB_SPEED_HIGH))
14177ef077a8SLaurent Pinchart 				qtd->actual_length =
14187ef077a8SLaurent Pinchart 				       FROM_DW3_SCS_NRBYTESTRANSFERRED(ptd.dw3);
14197ef077a8SLaurent Pinchart 			else
14207ef077a8SLaurent Pinchart 				qtd->actual_length =
14217ef077a8SLaurent Pinchart 					FROM_DW3_NRBYTESTRANSFERRED(ptd.dw3);
14227ef077a8SLaurent Pinchart 
14237ef077a8SLaurent Pinchart 			qtd->status = QTD_XFER_COMPLETE;
14247ef077a8SLaurent Pinchart 			if (list_is_last(&qtd->qtd_list, &qh->qtd_list) ||
14257ef077a8SLaurent Pinchart 			    is_short_bulk(qtd))
14267ef077a8SLaurent Pinchart 				qtd = NULL;
14277ef077a8SLaurent Pinchart 			else
14287ef077a8SLaurent Pinchart 				qtd = list_entry(qtd->qtd_list.next,
14297ef077a8SLaurent Pinchart 							typeof(*qtd), qtd_list);
14307ef077a8SLaurent Pinchart 
14317ef077a8SLaurent Pinchart 			qh->toggle = FROM_DW3_DATA_TOGGLE(ptd.dw3);
14327ef077a8SLaurent Pinchart 			qh->ping = FROM_DW3_PING(ptd.dw3);
14337ef077a8SLaurent Pinchart 			break;
14347ef077a8SLaurent Pinchart 
14357ef077a8SLaurent Pinchart 		case PTD_STATE_QTD_RELOAD: /* QTD_RETRY, for atls only */
14367ef077a8SLaurent Pinchart 			qtd->status = QTD_PAYLOAD_ALLOC;
14377ef077a8SLaurent Pinchart 			ptd.dw0 |= DW0_VALID_BIT;
14387ef077a8SLaurent Pinchart 			/* RL counter = ERR counter */
14397ef077a8SLaurent Pinchart 			ptd.dw3 &= ~TO_DW3_NAKCOUNT(0xf);
14407ef077a8SLaurent Pinchart 			ptd.dw3 |= TO_DW3_NAKCOUNT(FROM_DW2_RL(ptd.dw2));
14417ef077a8SLaurent Pinchart 			ptd.dw3 &= ~TO_DW3_CERR(3);
14427ef077a8SLaurent Pinchart 			ptd.dw3 |= TO_DW3_CERR(ERR_COUNTER);
14437ef077a8SLaurent Pinchart 			qh->toggle = FROM_DW3_DATA_TOGGLE(ptd.dw3);
14447ef077a8SLaurent Pinchart 			qh->ping = FROM_DW3_PING(ptd.dw3);
14457ef077a8SLaurent Pinchart 			break;
14467ef077a8SLaurent Pinchart 
14477ef077a8SLaurent Pinchart 		case PTD_STATE_URB_RETIRE:
14487ef077a8SLaurent Pinchart 			qtd->status = QTD_RETIRE;
14497ef077a8SLaurent Pinchart 			if ((qtd->urb->dev->speed != USB_SPEED_HIGH) &&
14507ef077a8SLaurent Pinchart 					(qtd->urb->status != -EPIPE) &&
14517ef077a8SLaurent Pinchart 					(qtd->urb->status != -EREMOTEIO)) {
14527ef077a8SLaurent Pinchart 				qh->tt_buffer_dirty = 1;
14537ef077a8SLaurent Pinchart 				if (usb_hub_clear_tt_buffer(qtd->urb))
14547ef077a8SLaurent Pinchart 					/* Clear failed; let's hope things work
14557ef077a8SLaurent Pinchart 					   anyway */
14567ef077a8SLaurent Pinchart 					qh->tt_buffer_dirty = 0;
14577ef077a8SLaurent Pinchart 			}
14587ef077a8SLaurent Pinchart 			qtd = NULL;
14597ef077a8SLaurent Pinchart 			qh->toggle = 0;
14607ef077a8SLaurent Pinchart 			qh->ping = 0;
14617ef077a8SLaurent Pinchart 			break;
14627ef077a8SLaurent Pinchart 
14637ef077a8SLaurent Pinchart 		default:
14647ef077a8SLaurent Pinchart 			WARN_ON(1);
14657ef077a8SLaurent Pinchart 			continue;
14667ef077a8SLaurent Pinchart 		}
14677ef077a8SLaurent Pinchart 
14687ef077a8SLaurent Pinchart 		if (qtd && (qtd->status == QTD_PAYLOAD_ALLOC)) {
14697ef077a8SLaurent Pinchart 			if (slots == priv->int_slots) {
14707ef077a8SLaurent Pinchart 				if (state == PTD_STATE_QTD_RELOAD)
14717ef077a8SLaurent Pinchart 					dev_err(hcd->self.controller,
14727ef077a8SLaurent Pinchart 						"%s: PTD_STATE_QTD_RELOAD on "
14737ef077a8SLaurent Pinchart 						"interrupt packet\n", __func__);
14747ef077a8SLaurent Pinchart 				if (state != PTD_STATE_QTD_RELOAD)
14757ef077a8SLaurent Pinchart 					create_ptd_int(qh, qtd, &ptd);
14767ef077a8SLaurent Pinchart 			} else {
14777ef077a8SLaurent Pinchart 				if (state != PTD_STATE_QTD_RELOAD)
14787ef077a8SLaurent Pinchart 					create_ptd_atl(qh, qtd, &ptd);
14797ef077a8SLaurent Pinchart 			}
14807ef077a8SLaurent Pinchart 
14817ef077a8SLaurent Pinchart 			start_bus_transfer(hcd, ptd_offset, slot, slots, qtd,
14827ef077a8SLaurent Pinchart 				qh, &ptd);
14837ef077a8SLaurent Pinchart 		}
14847ef077a8SLaurent Pinchart 	}
14857ef077a8SLaurent Pinchart 
14867ef077a8SLaurent Pinchart 	if (modified)
14877ef077a8SLaurent Pinchart 		schedule_ptds(hcd);
14887ef077a8SLaurent Pinchart }
14897ef077a8SLaurent Pinchart 
isp1760_irq(struct usb_hcd * hcd)14907ef077a8SLaurent Pinchart static irqreturn_t isp1760_irq(struct usb_hcd *hcd)
14917ef077a8SLaurent Pinchart {
14927ef077a8SLaurent Pinchart 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
14937ef077a8SLaurent Pinchart 	irqreturn_t irqret = IRQ_NONE;
149460d789f3SRui Miguel Silva 	u32 int_reg;
149560d789f3SRui Miguel Silva 	u32 imask;
14967ef077a8SLaurent Pinchart 
14977ef077a8SLaurent Pinchart 	spin_lock(&priv->lock);
14987ef077a8SLaurent Pinchart 
14997ef077a8SLaurent Pinchart 	if (!(hcd->state & HC_STATE_RUNNING))
15007ef077a8SLaurent Pinchart 		goto leave;
15017ef077a8SLaurent Pinchart 
150260d789f3SRui Miguel Silva 	imask = isp1760_hcd_read(hcd, HC_INTERRUPT);
15037ef077a8SLaurent Pinchart 	if (unlikely(!imask))
15047ef077a8SLaurent Pinchart 		goto leave;
15057ef077a8SLaurent Pinchart 
150660d789f3SRui Miguel Silva 	int_reg = priv->is_isp1763 ? ISP1763_HC_INTERRUPT :
150760d789f3SRui Miguel Silva 		ISP176x_HC_INTERRUPT;
150860d789f3SRui Miguel Silva 	isp1760_reg_write(priv->regs, int_reg, imask);
150960d789f3SRui Miguel Silva 
151060d789f3SRui Miguel Silva 	priv->int_done_map |= isp1760_hcd_read(hcd, HC_INT_PTD_DONEMAP);
151160d789f3SRui Miguel Silva 	priv->atl_done_map |= isp1760_hcd_read(hcd, HC_ATL_PTD_DONEMAP);
15127ef077a8SLaurent Pinchart 
15137ef077a8SLaurent Pinchart 	handle_done_ptds(hcd);
15147ef077a8SLaurent Pinchart 
15157ef077a8SLaurent Pinchart 	irqret = IRQ_HANDLED;
151660d789f3SRui Miguel Silva 
15177ef077a8SLaurent Pinchart leave:
15187ef077a8SLaurent Pinchart 	spin_unlock(&priv->lock);
15197ef077a8SLaurent Pinchart 
15207ef077a8SLaurent Pinchart 	return irqret;
15217ef077a8SLaurent Pinchart }
15227ef077a8SLaurent Pinchart 
15237ef077a8SLaurent Pinchart /*
15247ef077a8SLaurent Pinchart  * Workaround for problem described in chip errata 2:
15257ef077a8SLaurent Pinchart  *
15267ef077a8SLaurent Pinchart  * Sometimes interrupts are not generated when ATL (not INT?) completion occurs.
15277ef077a8SLaurent Pinchart  * One solution suggested in the errata is to use SOF interrupts _instead_of_
15287ef077a8SLaurent Pinchart  * ATL done interrupts (the "instead of" might be important since it seems
15297ef077a8SLaurent Pinchart  * enabling ATL interrupts also causes the chip to sometimes - rarely - "forget"
15307ef077a8SLaurent Pinchart  * to set the PTD's done bit in addition to not generating an interrupt!).
15317ef077a8SLaurent Pinchart  *
15327ef077a8SLaurent Pinchart  * So if we use SOF + ATL interrupts, we sometimes get stale PTDs since their
15337ef077a8SLaurent Pinchart  * done bit is not being set. This is bad - it blocks the endpoint until reboot.
15347ef077a8SLaurent Pinchart  *
15357ef077a8SLaurent Pinchart  * If we use SOF interrupts only, we get latency between ptd completion and the
15367ef077a8SLaurent Pinchart  * actual handling. This is very noticeable in testusb runs which takes several
15377ef077a8SLaurent Pinchart  * minutes longer without ATL interrupts.
15387ef077a8SLaurent Pinchart  *
15397ef077a8SLaurent Pinchart  * A better solution is to run the code below every SLOT_CHECK_PERIOD ms. If it
15407ef077a8SLaurent Pinchart  * finds active ATL slots which are older than SLOT_TIMEOUT ms, it checks the
15417ef077a8SLaurent Pinchart  * slot's ACTIVE and VALID bits. If these are not set, the ptd is considered
15427ef077a8SLaurent Pinchart  * completed and its done map bit is set.
15437ef077a8SLaurent Pinchart  *
15447ef077a8SLaurent Pinchart  * The values of SLOT_TIMEOUT and SLOT_CHECK_PERIOD have been arbitrarily chosen
15457ef077a8SLaurent Pinchart  * not to cause too much lag when this HW bug occurs, while still hopefully
15467ef077a8SLaurent Pinchart  * ensuring that the check does not falsely trigger.
15477ef077a8SLaurent Pinchart  */
15487ef077a8SLaurent Pinchart #define SLOT_TIMEOUT 300
15497ef077a8SLaurent Pinchart #define SLOT_CHECK_PERIOD 200
15507ef077a8SLaurent Pinchart static struct timer_list errata2_timer;
15517e33da59SKees Cook static struct usb_hcd *errata2_timer_hcd;
15527ef077a8SLaurent Pinchart 
errata2_function(struct timer_list * unused)15537e33da59SKees Cook static void errata2_function(struct timer_list *unused)
15547ef077a8SLaurent Pinchart {
15557e33da59SKees Cook 	struct usb_hcd *hcd = errata2_timer_hcd;
15567ef077a8SLaurent Pinchart 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
155760d789f3SRui Miguel Silva 	const struct isp1760_memory_layout *mem = priv->memory_layout;
15587ef077a8SLaurent Pinchart 	int slot;
15597ef077a8SLaurent Pinchart 	struct ptd ptd;
15607ef077a8SLaurent Pinchart 	unsigned long spinflags;
15617ef077a8SLaurent Pinchart 
15627ef077a8SLaurent Pinchart 	spin_lock_irqsave(&priv->lock, spinflags);
15637ef077a8SLaurent Pinchart 
156460d789f3SRui Miguel Silva 	for (slot = 0; slot < mem->slot_num; slot++)
15657ef077a8SLaurent Pinchart 		if (priv->atl_slots[slot].qh && time_after(jiffies,
15667ef077a8SLaurent Pinchart 					priv->atl_slots[slot].timestamp +
15674d3db7d7SNicholas Mc Guire 					msecs_to_jiffies(SLOT_TIMEOUT))) {
156860d789f3SRui Miguel Silva 			ptd_read(hcd, ATL_PTD_OFFSET, slot, &ptd);
15697ef077a8SLaurent Pinchart 			if (!FROM_DW0_VALID(ptd.dw0) &&
15707ef077a8SLaurent Pinchart 					!FROM_DW3_ACTIVE(ptd.dw3))
15717ef077a8SLaurent Pinchart 				priv->atl_done_map |= 1 << slot;
15727ef077a8SLaurent Pinchart 		}
15737ef077a8SLaurent Pinchart 
15747ef077a8SLaurent Pinchart 	if (priv->atl_done_map)
15757ef077a8SLaurent Pinchart 		handle_done_ptds(hcd);
15767ef077a8SLaurent Pinchart 
15777ef077a8SLaurent Pinchart 	spin_unlock_irqrestore(&priv->lock, spinflags);
15787ef077a8SLaurent Pinchart 
15794d3db7d7SNicholas Mc Guire 	errata2_timer.expires = jiffies + msecs_to_jiffies(SLOT_CHECK_PERIOD);
15807ef077a8SLaurent Pinchart 	add_timer(&errata2_timer);
15817ef077a8SLaurent Pinchart }
15827ef077a8SLaurent Pinchart 
isp1763_run(struct usb_hcd * hcd)158360d789f3SRui Miguel Silva static int isp1763_run(struct usb_hcd *hcd)
158460d789f3SRui Miguel Silva {
158560d789f3SRui Miguel Silva 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
158660d789f3SRui Miguel Silva 	int retval;
158760d789f3SRui Miguel Silva 	u32 chipid_h;
158860d789f3SRui Miguel Silva 	u32 chipid_l;
158960d789f3SRui Miguel Silva 	u32 chip_rev;
159060d789f3SRui Miguel Silva 	u32 ptd_atl_int;
159160d789f3SRui Miguel Silva 	u32 ptd_iso;
159260d789f3SRui Miguel Silva 
159360d789f3SRui Miguel Silva 	hcd->uses_new_polling = 1;
159460d789f3SRui Miguel Silva 	hcd->state = HC_STATE_RUNNING;
159560d789f3SRui Miguel Silva 
159660d789f3SRui Miguel Silva 	chipid_h = isp1760_hcd_read(hcd, HC_CHIP_ID_HIGH);
159760d789f3SRui Miguel Silva 	chipid_l = isp1760_hcd_read(hcd, HC_CHIP_ID_LOW);
159860d789f3SRui Miguel Silva 	chip_rev = isp1760_hcd_read(hcd, HC_CHIP_REV);
159960d789f3SRui Miguel Silva 	dev_info(hcd->self.controller, "USB ISP %02x%02x HW rev. %d started\n",
160060d789f3SRui Miguel Silva 		 chipid_h, chipid_l, chip_rev);
160160d789f3SRui Miguel Silva 
160260d789f3SRui Miguel Silva 	isp1760_hcd_clear(hcd, ISO_BUF_FILL);
160360d789f3SRui Miguel Silva 	isp1760_hcd_clear(hcd, INT_BUF_FILL);
160460d789f3SRui Miguel Silva 	isp1760_hcd_clear(hcd, ATL_BUF_FILL);
160560d789f3SRui Miguel Silva 
160660d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, HC_ATL_PTD_SKIPMAP);
160760d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, HC_INT_PTD_SKIPMAP);
160860d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, HC_ISO_PTD_SKIPMAP);
160960d789f3SRui Miguel Silva 	ndelay(100);
161060d789f3SRui Miguel Silva 	isp1760_hcd_clear(hcd, HC_ATL_PTD_DONEMAP);
161160d789f3SRui Miguel Silva 	isp1760_hcd_clear(hcd, HC_INT_PTD_DONEMAP);
161260d789f3SRui Miguel Silva 	isp1760_hcd_clear(hcd, HC_ISO_PTD_DONEMAP);
161360d789f3SRui Miguel Silva 
161460d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, HW_OTG_DISABLE);
161560d789f3SRui Miguel Silva 	isp1760_reg_write(priv->regs, ISP1763_HC_OTG_CTRL_CLEAR, BIT(7));
161660d789f3SRui Miguel Silva 	isp1760_reg_write(priv->regs, ISP1763_HC_OTG_CTRL_CLEAR, BIT(15));
161760d789f3SRui Miguel Silva 	mdelay(10);
161860d789f3SRui Miguel Silva 
161960d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, HC_INT_IRQ_ENABLE);
162060d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, HC_ATL_IRQ_ENABLE);
162160d789f3SRui Miguel Silva 
162260d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, HW_GLOBAL_INTR_EN);
162360d789f3SRui Miguel Silva 
162460d789f3SRui Miguel Silva 	isp1760_hcd_clear(hcd, HC_ATL_IRQ_MASK_AND);
162560d789f3SRui Miguel Silva 	isp1760_hcd_clear(hcd, HC_INT_IRQ_MASK_AND);
162660d789f3SRui Miguel Silva 	isp1760_hcd_clear(hcd, HC_ISO_IRQ_MASK_AND);
162760d789f3SRui Miguel Silva 
162860d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, HC_ATL_IRQ_MASK_OR);
162960d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, HC_INT_IRQ_MASK_OR);
163060d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, HC_ISO_IRQ_MASK_OR);
163160d789f3SRui Miguel Silva 
163260d789f3SRui Miguel Silva 	ptd_atl_int = 0x8000;
163360d789f3SRui Miguel Silva 	ptd_iso = 0x0001;
163460d789f3SRui Miguel Silva 
163560d789f3SRui Miguel Silva 	isp1760_hcd_write(hcd, HC_ATL_PTD_LASTPTD, ptd_atl_int);
163660d789f3SRui Miguel Silva 	isp1760_hcd_write(hcd, HC_INT_PTD_LASTPTD, ptd_atl_int);
163760d789f3SRui Miguel Silva 	isp1760_hcd_write(hcd, HC_ISO_PTD_LASTPTD, ptd_iso);
163860d789f3SRui Miguel Silva 
163960d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, ATL_BUF_FILL);
164060d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, INT_BUF_FILL);
164160d789f3SRui Miguel Silva 
164260d789f3SRui Miguel Silva 	isp1760_hcd_clear(hcd, CMD_LRESET);
164360d789f3SRui Miguel Silva 	isp1760_hcd_clear(hcd, CMD_RESET);
164460d789f3SRui Miguel Silva 
164560d789f3SRui Miguel Silva 	retval = isp1760_hcd_set_and_wait(hcd, CMD_RUN, 250 * 1000);
164660d789f3SRui Miguel Silva 	if (retval)
164760d789f3SRui Miguel Silva 		return retval;
164860d789f3SRui Miguel Silva 
164960d789f3SRui Miguel Silva 	down_write(&ehci_cf_port_reset_rwsem);
165060d789f3SRui Miguel Silva 	retval = isp1760_hcd_set_and_wait(hcd, FLAG_CF, 250 * 1000);
165160d789f3SRui Miguel Silva 	up_write(&ehci_cf_port_reset_rwsem);
165260d789f3SRui Miguel Silva 	if (retval)
165360d789f3SRui Miguel Silva 		return retval;
165460d789f3SRui Miguel Silva 
165560d789f3SRui Miguel Silva 	return 0;
165660d789f3SRui Miguel Silva }
165760d789f3SRui Miguel Silva 
isp1760_run(struct usb_hcd * hcd)16587ef077a8SLaurent Pinchart static int isp1760_run(struct usb_hcd *hcd)
16597ef077a8SLaurent Pinchart {
16601da9e1c0SRui Miguel Silva 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
16617ef077a8SLaurent Pinchart 	int retval;
166260d789f3SRui Miguel Silva 	u32 chipid_h;
166360d789f3SRui Miguel Silva 	u32 chipid_l;
166460d789f3SRui Miguel Silva 	u32 chip_rev;
166560d789f3SRui Miguel Silva 	u32 ptd_atl_int;
166660d789f3SRui Miguel Silva 	u32 ptd_iso;
166760d789f3SRui Miguel Silva 
166860d789f3SRui Miguel Silva 	/*
166960d789f3SRui Miguel Silva 	 * ISP1763 have some differences in the setup and order to enable
167060d789f3SRui Miguel Silva 	 * the ports, disable otg, setup buffers, and ATL, INT, ISO status.
167160d789f3SRui Miguel Silva 	 * So, just handle it a separate sequence.
167260d789f3SRui Miguel Silva 	 */
167360d789f3SRui Miguel Silva 	if (priv->is_isp1763)
167460d789f3SRui Miguel Silva 		return isp1763_run(hcd);
16757ef077a8SLaurent Pinchart 
16767ef077a8SLaurent Pinchart 	hcd->uses_new_polling = 1;
16777ef077a8SLaurent Pinchart 
16787ef077a8SLaurent Pinchart 	hcd->state = HC_STATE_RUNNING;
16797ef077a8SLaurent Pinchart 
16807ef077a8SLaurent Pinchart 	/* Set PTD interrupt AND & OR maps */
168160d789f3SRui Miguel Silva 	isp1760_hcd_clear(hcd, HC_ATL_IRQ_MASK_AND);
168260d789f3SRui Miguel Silva 	isp1760_hcd_clear(hcd, HC_INT_IRQ_MASK_AND);
168360d789f3SRui Miguel Silva 	isp1760_hcd_clear(hcd, HC_ISO_IRQ_MASK_AND);
168460d789f3SRui Miguel Silva 
168560d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, HC_ATL_IRQ_MASK_OR);
168660d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, HC_INT_IRQ_MASK_OR);
168760d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, HC_ISO_IRQ_MASK_OR);
168860d789f3SRui Miguel Silva 
16897ef077a8SLaurent Pinchart 	/* step 23 passed */
16907ef077a8SLaurent Pinchart 
16911da9e1c0SRui Miguel Silva 	isp1760_hcd_set(hcd, HW_GLOBAL_INTR_EN);
16927ef077a8SLaurent Pinchart 
16931da9e1c0SRui Miguel Silva 	isp1760_hcd_clear(hcd, CMD_LRESET);
16941da9e1c0SRui Miguel Silva 	isp1760_hcd_clear(hcd, CMD_RESET);
16957ef077a8SLaurent Pinchart 
169660d789f3SRui Miguel Silva 	retval = isp1760_hcd_set_and_wait(hcd, CMD_RUN, 250 * 1000);
16977ef077a8SLaurent Pinchart 	if (retval)
16987ef077a8SLaurent Pinchart 		return retval;
16997ef077a8SLaurent Pinchart 
17007ef077a8SLaurent Pinchart 	/*
17017ef077a8SLaurent Pinchart 	 * XXX
17027ef077a8SLaurent Pinchart 	 * Spec says to write FLAG_CF as last config action, priv code grabs
17037ef077a8SLaurent Pinchart 	 * the semaphore while doing so.
17047ef077a8SLaurent Pinchart 	 */
17057ef077a8SLaurent Pinchart 	down_write(&ehci_cf_port_reset_rwsem);
17067ef077a8SLaurent Pinchart 
170760d789f3SRui Miguel Silva 	retval = isp1760_hcd_set_and_wait(hcd, FLAG_CF, 250 * 1000);
17087ef077a8SLaurent Pinchart 	up_write(&ehci_cf_port_reset_rwsem);
17097ef077a8SLaurent Pinchart 	if (retval)
17107ef077a8SLaurent Pinchart 		return retval;
17117ef077a8SLaurent Pinchart 
17127e33da59SKees Cook 	errata2_timer_hcd = hcd;
17137e33da59SKees Cook 	timer_setup(&errata2_timer, errata2_function, 0);
17144d3db7d7SNicholas Mc Guire 	errata2_timer.expires = jiffies + msecs_to_jiffies(SLOT_CHECK_PERIOD);
17157ef077a8SLaurent Pinchart 	add_timer(&errata2_timer);
17167ef077a8SLaurent Pinchart 
171760d789f3SRui Miguel Silva 	chipid_h = isp1760_hcd_read(hcd, HC_CHIP_ID_HIGH);
171860d789f3SRui Miguel Silva 	chipid_l = isp1760_hcd_read(hcd, HC_CHIP_ID_LOW);
171960d789f3SRui Miguel Silva 	chip_rev = isp1760_hcd_read(hcd, HC_CHIP_REV);
172060d789f3SRui Miguel Silva 	dev_info(hcd->self.controller, "USB ISP %02x%02x HW rev. %d started\n",
172160d789f3SRui Miguel Silva 		 chipid_h, chipid_l, chip_rev);
17227ef077a8SLaurent Pinchart 
17237ef077a8SLaurent Pinchart 	/* PTD Register Init Part 2, Step 28 */
17247ef077a8SLaurent Pinchart 
17257ef077a8SLaurent Pinchart 	/* Setup registers controlling PTD checking */
172660d789f3SRui Miguel Silva 	ptd_atl_int = 0x80000000;
172760d789f3SRui Miguel Silva 	ptd_iso = 0x00000001;
172860d789f3SRui Miguel Silva 
172960d789f3SRui Miguel Silva 	isp1760_hcd_write(hcd, HC_ATL_PTD_LASTPTD, ptd_atl_int);
173060d789f3SRui Miguel Silva 	isp1760_hcd_write(hcd, HC_INT_PTD_LASTPTD, ptd_atl_int);
173160d789f3SRui Miguel Silva 	isp1760_hcd_write(hcd, HC_ISO_PTD_LASTPTD, ptd_iso);
173260d789f3SRui Miguel Silva 
173360d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, HC_ATL_PTD_SKIPMAP);
173460d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, HC_INT_PTD_SKIPMAP);
173560d789f3SRui Miguel Silva 	isp1760_hcd_set(hcd, HC_ISO_PTD_SKIPMAP);
17361da9e1c0SRui Miguel Silva 
17371da9e1c0SRui Miguel Silva 	isp1760_hcd_set(hcd, ATL_BUF_FILL);
17381da9e1c0SRui Miguel Silva 	isp1760_hcd_set(hcd, INT_BUF_FILL);
17397ef077a8SLaurent Pinchart 
17407ef077a8SLaurent Pinchart 	/* GRR this is run-once init(), being done every time the HC starts.
17417ef077a8SLaurent Pinchart 	 * So long as they're part of class devices, we can't do it init()
17427ef077a8SLaurent Pinchart 	 * since the class device isn't created that early.
17437ef077a8SLaurent Pinchart 	 */
17447ef077a8SLaurent Pinchart 	return 0;
17457ef077a8SLaurent Pinchart }
17467ef077a8SLaurent Pinchart 
qtd_fill(struct isp1760_qtd * qtd,void * databuffer,size_t len)17477ef077a8SLaurent Pinchart static int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len)
17487ef077a8SLaurent Pinchart {
17497ef077a8SLaurent Pinchart 	qtd->data_buffer = databuffer;
17507ef077a8SLaurent Pinchart 
17517ef077a8SLaurent Pinchart 	qtd->length = len;
17527ef077a8SLaurent Pinchart 
17537ef077a8SLaurent Pinchart 	return qtd->length;
17547ef077a8SLaurent Pinchart }
17557ef077a8SLaurent Pinchart 
qtd_list_free(struct list_head * qtd_list)17567ef077a8SLaurent Pinchart static void qtd_list_free(struct list_head *qtd_list)
17577ef077a8SLaurent Pinchart {
17587ef077a8SLaurent Pinchart 	struct isp1760_qtd *qtd, *qtd_next;
17597ef077a8SLaurent Pinchart 
17607ef077a8SLaurent Pinchart 	list_for_each_entry_safe(qtd, qtd_next, qtd_list, qtd_list) {
17617ef077a8SLaurent Pinchart 		list_del(&qtd->qtd_list);
17627ef077a8SLaurent Pinchart 		qtd_free(qtd);
17637ef077a8SLaurent Pinchart 	}
17647ef077a8SLaurent Pinchart }
17657ef077a8SLaurent Pinchart 
17667ef077a8SLaurent Pinchart /*
17677ef077a8SLaurent Pinchart  * Packetize urb->transfer_buffer into list of packets of size wMaxPacketSize.
17687ef077a8SLaurent Pinchart  * Also calculate the PID type (SETUP/IN/OUT) for each packet.
17697ef077a8SLaurent Pinchart  */
packetize_urb(struct usb_hcd * hcd,struct urb * urb,struct list_head * head,gfp_t flags)17707ef077a8SLaurent Pinchart static void packetize_urb(struct usb_hcd *hcd,
17717ef077a8SLaurent Pinchart 		struct urb *urb, struct list_head *head, gfp_t flags)
17727ef077a8SLaurent Pinchart {
1773a74f639cSRui Miguel Silva 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
1774a74f639cSRui Miguel Silva 	const struct isp1760_memory_layout *mem = priv->memory_layout;
17757ef077a8SLaurent Pinchart 	struct isp1760_qtd *qtd;
17767ef077a8SLaurent Pinchart 	void *buf;
17777ef077a8SLaurent Pinchart 	int len, maxpacketsize;
17787ef077a8SLaurent Pinchart 	u8 packet_type;
17797ef077a8SLaurent Pinchart 
17807ef077a8SLaurent Pinchart 	/*
17817ef077a8SLaurent Pinchart 	 * URBs map to sequences of QTDs:  one logical transaction
17827ef077a8SLaurent Pinchart 	 */
17837ef077a8SLaurent Pinchart 
17847ef077a8SLaurent Pinchart 	if (!urb->transfer_buffer && urb->transfer_buffer_length) {
17857ef077a8SLaurent Pinchart 		/* XXX This looks like usb storage / SCSI bug */
17867ef077a8SLaurent Pinchart 		dev_err(hcd->self.controller,
17877ef077a8SLaurent Pinchart 				"buf is null, dma is %08lx len is %d\n",
17887ef077a8SLaurent Pinchart 				(long unsigned)urb->transfer_dma,
17897ef077a8SLaurent Pinchart 				urb->transfer_buffer_length);
17907ef077a8SLaurent Pinchart 		WARN_ON(1);
17917ef077a8SLaurent Pinchart 	}
17927ef077a8SLaurent Pinchart 
17937ef077a8SLaurent Pinchart 	if (usb_pipein(urb->pipe))
17947ef077a8SLaurent Pinchart 		packet_type = IN_PID;
17957ef077a8SLaurent Pinchart 	else
17967ef077a8SLaurent Pinchart 		packet_type = OUT_PID;
17977ef077a8SLaurent Pinchart 
17987ef077a8SLaurent Pinchart 	if (usb_pipecontrol(urb->pipe)) {
17997ef077a8SLaurent Pinchart 		qtd = qtd_alloc(flags, urb, SETUP_PID);
18007ef077a8SLaurent Pinchart 		if (!qtd)
18017ef077a8SLaurent Pinchart 			goto cleanup;
18027ef077a8SLaurent Pinchart 		qtd_fill(qtd, urb->setup_packet, sizeof(struct usb_ctrlrequest));
18037ef077a8SLaurent Pinchart 		list_add_tail(&qtd->qtd_list, head);
18047ef077a8SLaurent Pinchart 
18057ef077a8SLaurent Pinchart 		/* for zero length DATA stages, STATUS is always IN */
18067ef077a8SLaurent Pinchart 		if (urb->transfer_buffer_length == 0)
18077ef077a8SLaurent Pinchart 			packet_type = IN_PID;
18087ef077a8SLaurent Pinchart 	}
18097ef077a8SLaurent Pinchart 
1810dcd2e49bSVincent Mailhol 	maxpacketsize = usb_maxpacket(urb->dev, urb->pipe);
18117ef077a8SLaurent Pinchart 
18127ef077a8SLaurent Pinchart 	/*
18137ef077a8SLaurent Pinchart 	 * buffer gets wrapped in one or more qtds;
18147ef077a8SLaurent Pinchart 	 * last one may be "short" (including zero len)
18157ef077a8SLaurent Pinchart 	 * and may serve as a control status ack
18167ef077a8SLaurent Pinchart 	 */
18177ef077a8SLaurent Pinchart 	buf = urb->transfer_buffer;
18187ef077a8SLaurent Pinchart 	len = urb->transfer_buffer_length;
18197ef077a8SLaurent Pinchart 
18207ef077a8SLaurent Pinchart 	for (;;) {
18217ef077a8SLaurent Pinchart 		int this_qtd_len;
18227ef077a8SLaurent Pinchart 
18237ef077a8SLaurent Pinchart 		qtd = qtd_alloc(flags, urb, packet_type);
18247ef077a8SLaurent Pinchart 		if (!qtd)
18257ef077a8SLaurent Pinchart 			goto cleanup;
1826a74f639cSRui Miguel Silva 
1827a74f639cSRui Miguel Silva 		if (len > mem->blocks_size[ISP176x_BLOCK_NUM - 1])
1828cbfa3effSRui Miguel Silva 			this_qtd_len = mem->blocks_size[ISP176x_BLOCK_NUM - 1];
1829cbfa3effSRui Miguel Silva 		else
1830cbfa3effSRui Miguel Silva 			this_qtd_len = len;
1831a74f639cSRui Miguel Silva 
1832cbfa3effSRui Miguel Silva 		this_qtd_len = qtd_fill(qtd, buf, this_qtd_len);
18337ef077a8SLaurent Pinchart 		list_add_tail(&qtd->qtd_list, head);
18347ef077a8SLaurent Pinchart 
18357ef077a8SLaurent Pinchart 		len -= this_qtd_len;
18367ef077a8SLaurent Pinchart 		buf += this_qtd_len;
18377ef077a8SLaurent Pinchart 
18387ef077a8SLaurent Pinchart 		if (len <= 0)
18397ef077a8SLaurent Pinchart 			break;
18407ef077a8SLaurent Pinchart 	}
18417ef077a8SLaurent Pinchart 
18427ef077a8SLaurent Pinchart 	/*
18437ef077a8SLaurent Pinchart 	 * control requests may need a terminating data "status" ack;
18447ef077a8SLaurent Pinchart 	 * bulk ones may need a terminating short packet (zero length).
18457ef077a8SLaurent Pinchart 	 */
18467ef077a8SLaurent Pinchart 	if (urb->transfer_buffer_length != 0) {
18477ef077a8SLaurent Pinchart 		int one_more = 0;
18487ef077a8SLaurent Pinchart 
18497ef077a8SLaurent Pinchart 		if (usb_pipecontrol(urb->pipe)) {
18507ef077a8SLaurent Pinchart 			one_more = 1;
18517ef077a8SLaurent Pinchart 			if (packet_type == IN_PID)
18527ef077a8SLaurent Pinchart 				packet_type = OUT_PID;
18537ef077a8SLaurent Pinchart 			else
18547ef077a8SLaurent Pinchart 				packet_type = IN_PID;
18558e58b771SRui Miguel Silva 		} else if (usb_pipebulk(urb->pipe) && maxpacketsize
18567ef077a8SLaurent Pinchart 				&& (urb->transfer_flags & URB_ZERO_PACKET)
18577ef077a8SLaurent Pinchart 				&& !(urb->transfer_buffer_length %
18587ef077a8SLaurent Pinchart 							maxpacketsize)) {
18597ef077a8SLaurent Pinchart 			one_more = 1;
18607ef077a8SLaurent Pinchart 		}
18617ef077a8SLaurent Pinchart 		if (one_more) {
18627ef077a8SLaurent Pinchart 			qtd = qtd_alloc(flags, urb, packet_type);
18637ef077a8SLaurent Pinchart 			if (!qtd)
18647ef077a8SLaurent Pinchart 				goto cleanup;
18657ef077a8SLaurent Pinchart 
18667ef077a8SLaurent Pinchart 			/* never any data in such packets */
18677ef077a8SLaurent Pinchart 			qtd_fill(qtd, NULL, 0);
18687ef077a8SLaurent Pinchart 			list_add_tail(&qtd->qtd_list, head);
18697ef077a8SLaurent Pinchart 		}
18707ef077a8SLaurent Pinchart 	}
18717ef077a8SLaurent Pinchart 
18727ef077a8SLaurent Pinchart 	return;
18737ef077a8SLaurent Pinchart 
18747ef077a8SLaurent Pinchart cleanup:
18757ef077a8SLaurent Pinchart 	qtd_list_free(head);
18767ef077a8SLaurent Pinchart }
18777ef077a8SLaurent Pinchart 
isp1760_urb_enqueue(struct usb_hcd * hcd,struct urb * urb,gfp_t mem_flags)18787ef077a8SLaurent Pinchart static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
18797ef077a8SLaurent Pinchart 		gfp_t mem_flags)
18807ef077a8SLaurent Pinchart {
18817ef077a8SLaurent Pinchart 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
18827ef077a8SLaurent Pinchart 	struct list_head *ep_queue;
18837ef077a8SLaurent Pinchart 	struct isp1760_qh *qh, *qhit;
18847ef077a8SLaurent Pinchart 	unsigned long spinflags;
18857ef077a8SLaurent Pinchart 	LIST_HEAD(new_qtds);
18867ef077a8SLaurent Pinchart 	int retval;
18877ef077a8SLaurent Pinchart 	int qh_in_queue;
18887ef077a8SLaurent Pinchart 
18897ef077a8SLaurent Pinchart 	switch (usb_pipetype(urb->pipe)) {
18907ef077a8SLaurent Pinchart 	case PIPE_CONTROL:
18917ef077a8SLaurent Pinchart 		ep_queue = &priv->qh_list[QH_CONTROL];
18927ef077a8SLaurent Pinchart 		break;
18937ef077a8SLaurent Pinchart 	case PIPE_BULK:
18947ef077a8SLaurent Pinchart 		ep_queue = &priv->qh_list[QH_BULK];
18957ef077a8SLaurent Pinchart 		break;
18967ef077a8SLaurent Pinchart 	case PIPE_INTERRUPT:
18977ef077a8SLaurent Pinchart 		if (urb->interval < 0)
18987ef077a8SLaurent Pinchart 			return -EINVAL;
18997ef077a8SLaurent Pinchart 		/* FIXME: Check bandwidth  */
19007ef077a8SLaurent Pinchart 		ep_queue = &priv->qh_list[QH_INTERRUPT];
19017ef077a8SLaurent Pinchart 		break;
19027ef077a8SLaurent Pinchart 	case PIPE_ISOCHRONOUS:
19037ef077a8SLaurent Pinchart 		dev_err(hcd->self.controller, "%s: isochronous USB packets "
19047ef077a8SLaurent Pinchart 							"not yet supported\n",
19057ef077a8SLaurent Pinchart 							__func__);
19067ef077a8SLaurent Pinchart 		return -EPIPE;
19077ef077a8SLaurent Pinchart 	default:
19087ef077a8SLaurent Pinchart 		dev_err(hcd->self.controller, "%s: unknown pipe type\n",
19097ef077a8SLaurent Pinchart 							__func__);
19107ef077a8SLaurent Pinchart 		return -EPIPE;
19117ef077a8SLaurent Pinchart 	}
19127ef077a8SLaurent Pinchart 
19137ef077a8SLaurent Pinchart 	if (usb_pipein(urb->pipe))
19147ef077a8SLaurent Pinchart 		urb->actual_length = 0;
19157ef077a8SLaurent Pinchart 
19167ef077a8SLaurent Pinchart 	packetize_urb(hcd, urb, &new_qtds, mem_flags);
19177ef077a8SLaurent Pinchart 	if (list_empty(&new_qtds))
19187ef077a8SLaurent Pinchart 		return -ENOMEM;
19197ef077a8SLaurent Pinchart 
19207ef077a8SLaurent Pinchart 	spin_lock_irqsave(&priv->lock, spinflags);
19217ef077a8SLaurent Pinchart 
19227ef077a8SLaurent Pinchart 	if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
19237ef077a8SLaurent Pinchart 		retval = -ESHUTDOWN;
19247ef077a8SLaurent Pinchart 		qtd_list_free(&new_qtds);
19257ef077a8SLaurent Pinchart 		goto out;
19267ef077a8SLaurent Pinchart 	}
19277ef077a8SLaurent Pinchart 	retval = usb_hcd_link_urb_to_ep(hcd, urb);
19287ef077a8SLaurent Pinchart 	if (retval) {
19297ef077a8SLaurent Pinchart 		qtd_list_free(&new_qtds);
19307ef077a8SLaurent Pinchart 		goto out;
19317ef077a8SLaurent Pinchart 	}
19327ef077a8SLaurent Pinchart 
19337ef077a8SLaurent Pinchart 	qh = urb->ep->hcpriv;
19347ef077a8SLaurent Pinchart 	if (qh) {
19357ef077a8SLaurent Pinchart 		qh_in_queue = 0;
19367ef077a8SLaurent Pinchart 		list_for_each_entry(qhit, ep_queue, qh_list) {
19377ef077a8SLaurent Pinchart 			if (qhit == qh) {
19387ef077a8SLaurent Pinchart 				qh_in_queue = 1;
19397ef077a8SLaurent Pinchart 				break;
19407ef077a8SLaurent Pinchart 			}
19417ef077a8SLaurent Pinchart 		}
19427ef077a8SLaurent Pinchart 		if (!qh_in_queue)
19437ef077a8SLaurent Pinchart 			list_add_tail(&qh->qh_list, ep_queue);
19447ef077a8SLaurent Pinchart 	} else {
19457ef077a8SLaurent Pinchart 		qh = qh_alloc(GFP_ATOMIC);
19467ef077a8SLaurent Pinchart 		if (!qh) {
19477ef077a8SLaurent Pinchart 			retval = -ENOMEM;
19487ef077a8SLaurent Pinchart 			usb_hcd_unlink_urb_from_ep(hcd, urb);
19497ef077a8SLaurent Pinchart 			qtd_list_free(&new_qtds);
19507ef077a8SLaurent Pinchart 			goto out;
19517ef077a8SLaurent Pinchart 		}
19527ef077a8SLaurent Pinchart 		list_add_tail(&qh->qh_list, ep_queue);
19537ef077a8SLaurent Pinchart 		urb->ep->hcpriv = qh;
19547ef077a8SLaurent Pinchart 	}
19557ef077a8SLaurent Pinchart 
19567ef077a8SLaurent Pinchart 	list_splice_tail(&new_qtds, &qh->qtd_list);
19577ef077a8SLaurent Pinchart 	schedule_ptds(hcd);
19587ef077a8SLaurent Pinchart 
19597ef077a8SLaurent Pinchart out:
19607ef077a8SLaurent Pinchart 	spin_unlock_irqrestore(&priv->lock, spinflags);
19617ef077a8SLaurent Pinchart 	return retval;
19627ef077a8SLaurent Pinchart }
19637ef077a8SLaurent Pinchart 
kill_transfer(struct usb_hcd * hcd,struct urb * urb,struct isp1760_qh * qh)19647ef077a8SLaurent Pinchart static void kill_transfer(struct usb_hcd *hcd, struct urb *urb,
19657ef077a8SLaurent Pinchart 		struct isp1760_qh *qh)
19667ef077a8SLaurent Pinchart {
19677ef077a8SLaurent Pinchart 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
19687ef077a8SLaurent Pinchart 	int skip_map;
19697ef077a8SLaurent Pinchart 
19707ef077a8SLaurent Pinchart 	WARN_ON(qh->slot == -1);
19717ef077a8SLaurent Pinchart 
19727ef077a8SLaurent Pinchart 	/* We need to forcefully reclaim the slot since some transfers never
19737ef077a8SLaurent Pinchart 	   return, e.g. interrupt transfers and NAKed bulk transfers. */
19747ef077a8SLaurent Pinchart 	if (usb_pipecontrol(urb->pipe) || usb_pipebulk(urb->pipe)) {
19757d1d3882SRui Miguel Silva 		if (qh->slot != -1) {
197660d789f3SRui Miguel Silva 			skip_map = isp1760_hcd_read(hcd, HC_ATL_PTD_SKIPMAP);
19777ef077a8SLaurent Pinchart 			skip_map |= (1 << qh->slot);
197860d789f3SRui Miguel Silva 			isp1760_hcd_write(hcd, HC_ATL_PTD_SKIPMAP, skip_map);
197960d789f3SRui Miguel Silva 			ndelay(100);
19807d1d3882SRui Miguel Silva 		}
19817ef077a8SLaurent Pinchart 		priv->atl_slots[qh->slot].qh = NULL;
19827ef077a8SLaurent Pinchart 		priv->atl_slots[qh->slot].qtd = NULL;
19837ef077a8SLaurent Pinchart 	} else {
19847d1d3882SRui Miguel Silva 		if (qh->slot != -1) {
198560d789f3SRui Miguel Silva 			skip_map = isp1760_hcd_read(hcd, HC_INT_PTD_SKIPMAP);
19867ef077a8SLaurent Pinchart 			skip_map |= (1 << qh->slot);
198760d789f3SRui Miguel Silva 			isp1760_hcd_write(hcd, HC_INT_PTD_SKIPMAP, skip_map);
19887d1d3882SRui Miguel Silva 		}
19897ef077a8SLaurent Pinchart 		priv->int_slots[qh->slot].qh = NULL;
19907ef077a8SLaurent Pinchart 		priv->int_slots[qh->slot].qtd = NULL;
19917ef077a8SLaurent Pinchart 	}
19927ef077a8SLaurent Pinchart 
19937ef077a8SLaurent Pinchart 	qh->slot = -1;
19947ef077a8SLaurent Pinchart }
19957ef077a8SLaurent Pinchart 
19967ef077a8SLaurent Pinchart /*
19977ef077a8SLaurent Pinchart  * Retire the qtds beginning at 'qtd' and belonging all to the same urb, killing
19987ef077a8SLaurent Pinchart  * any active transfer belonging to the urb in the process.
19997ef077a8SLaurent Pinchart  */
dequeue_urb_from_qtd(struct usb_hcd * hcd,struct isp1760_qh * qh,struct isp1760_qtd * qtd)20007ef077a8SLaurent Pinchart static void dequeue_urb_from_qtd(struct usb_hcd *hcd, struct isp1760_qh *qh,
20017ef077a8SLaurent Pinchart 						struct isp1760_qtd *qtd)
20027ef077a8SLaurent Pinchart {
20037ef077a8SLaurent Pinchart 	struct urb *urb;
20047ef077a8SLaurent Pinchart 	int urb_was_running;
20057ef077a8SLaurent Pinchart 
20067ef077a8SLaurent Pinchart 	urb = qtd->urb;
20077ef077a8SLaurent Pinchart 	urb_was_running = 0;
20087ef077a8SLaurent Pinchart 	list_for_each_entry_from(qtd, &qh->qtd_list, qtd_list) {
20097ef077a8SLaurent Pinchart 		if (qtd->urb != urb)
20107ef077a8SLaurent Pinchart 			break;
20117ef077a8SLaurent Pinchart 
20127ef077a8SLaurent Pinchart 		if (qtd->status >= QTD_XFER_STARTED)
20137ef077a8SLaurent Pinchart 			urb_was_running = 1;
20147ef077a8SLaurent Pinchart 		if (last_qtd_of_urb(qtd, qh) &&
20157ef077a8SLaurent Pinchart 					(qtd->status >= QTD_XFER_COMPLETE))
20167ef077a8SLaurent Pinchart 			urb_was_running = 0;
20177ef077a8SLaurent Pinchart 
20187ef077a8SLaurent Pinchart 		if (qtd->status == QTD_XFER_STARTED)
20197ef077a8SLaurent Pinchart 			kill_transfer(hcd, urb, qh);
20207ef077a8SLaurent Pinchart 		qtd->status = QTD_RETIRE;
20217ef077a8SLaurent Pinchart 	}
20227ef077a8SLaurent Pinchart 
20237ef077a8SLaurent Pinchart 	if ((urb->dev->speed != USB_SPEED_HIGH) && urb_was_running) {
20247ef077a8SLaurent Pinchart 		qh->tt_buffer_dirty = 1;
20257ef077a8SLaurent Pinchart 		if (usb_hub_clear_tt_buffer(urb))
20267ef077a8SLaurent Pinchart 			/* Clear failed; let's hope things work anyway */
20277ef077a8SLaurent Pinchart 			qh->tt_buffer_dirty = 0;
20287ef077a8SLaurent Pinchart 	}
20297ef077a8SLaurent Pinchart }
20307ef077a8SLaurent Pinchart 
isp1760_urb_dequeue(struct usb_hcd * hcd,struct urb * urb,int status)20317ef077a8SLaurent Pinchart static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
20327ef077a8SLaurent Pinchart 		int status)
20337ef077a8SLaurent Pinchart {
20347ef077a8SLaurent Pinchart 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
20357ef077a8SLaurent Pinchart 	unsigned long spinflags;
20367ef077a8SLaurent Pinchart 	struct isp1760_qh *qh;
20377ef077a8SLaurent Pinchart 	struct isp1760_qtd *qtd;
20387ef077a8SLaurent Pinchart 	int retval = 0;
20397ef077a8SLaurent Pinchart 
20407ef077a8SLaurent Pinchart 	spin_lock_irqsave(&priv->lock, spinflags);
20417ef077a8SLaurent Pinchart 	retval = usb_hcd_check_unlink_urb(hcd, urb, status);
20427ef077a8SLaurent Pinchart 	if (retval)
20437ef077a8SLaurent Pinchart 		goto out;
20447ef077a8SLaurent Pinchart 
20457ef077a8SLaurent Pinchart 	qh = urb->ep->hcpriv;
20467ef077a8SLaurent Pinchart 	if (!qh) {
20477ef077a8SLaurent Pinchart 		retval = -EINVAL;
20487ef077a8SLaurent Pinchart 		goto out;
20497ef077a8SLaurent Pinchart 	}
20507ef077a8SLaurent Pinchart 
20517ef077a8SLaurent Pinchart 	list_for_each_entry(qtd, &qh->qtd_list, qtd_list)
20527ef077a8SLaurent Pinchart 		if (qtd->urb == urb) {
20537ef077a8SLaurent Pinchart 			dequeue_urb_from_qtd(hcd, qh, qtd);
20547ef077a8SLaurent Pinchart 			list_move(&qtd->qtd_list, &qh->qtd_list);
20557ef077a8SLaurent Pinchart 			break;
20567ef077a8SLaurent Pinchart 		}
20577ef077a8SLaurent Pinchart 
20587ef077a8SLaurent Pinchart 	urb->status = status;
20597ef077a8SLaurent Pinchart 	schedule_ptds(hcd);
20607ef077a8SLaurent Pinchart 
20617ef077a8SLaurent Pinchart out:
20627ef077a8SLaurent Pinchart 	spin_unlock_irqrestore(&priv->lock, spinflags);
20637ef077a8SLaurent Pinchart 	return retval;
20647ef077a8SLaurent Pinchart }
20657ef077a8SLaurent Pinchart 
isp1760_endpoint_disable(struct usb_hcd * hcd,struct usb_host_endpoint * ep)20667ef077a8SLaurent Pinchart static void isp1760_endpoint_disable(struct usb_hcd *hcd,
20677ef077a8SLaurent Pinchart 		struct usb_host_endpoint *ep)
20687ef077a8SLaurent Pinchart {
20697ef077a8SLaurent Pinchart 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
20707ef077a8SLaurent Pinchart 	unsigned long spinflags;
20717ef077a8SLaurent Pinchart 	struct isp1760_qh *qh, *qh_iter;
20727ef077a8SLaurent Pinchart 	int i;
20737ef077a8SLaurent Pinchart 
20747ef077a8SLaurent Pinchart 	spin_lock_irqsave(&priv->lock, spinflags);
20757ef077a8SLaurent Pinchart 
20767ef077a8SLaurent Pinchart 	qh = ep->hcpriv;
20777ef077a8SLaurent Pinchart 	if (!qh)
20787ef077a8SLaurent Pinchart 		goto out;
20797ef077a8SLaurent Pinchart 
20807ef077a8SLaurent Pinchart 	WARN_ON(!list_empty(&qh->qtd_list));
20817ef077a8SLaurent Pinchart 
20827ef077a8SLaurent Pinchart 	for (i = 0; i < QH_END; i++)
20837ef077a8SLaurent Pinchart 		list_for_each_entry(qh_iter, &priv->qh_list[i], qh_list)
20847ef077a8SLaurent Pinchart 			if (qh_iter == qh) {
20857ef077a8SLaurent Pinchart 				list_del(&qh_iter->qh_list);
20867ef077a8SLaurent Pinchart 				i = QH_END;
20877ef077a8SLaurent Pinchart 				break;
20887ef077a8SLaurent Pinchart 			}
20897ef077a8SLaurent Pinchart 	qh_free(qh);
20907ef077a8SLaurent Pinchart 	ep->hcpriv = NULL;
20917ef077a8SLaurent Pinchart 
20927ef077a8SLaurent Pinchart 	schedule_ptds(hcd);
20937ef077a8SLaurent Pinchart 
20947ef077a8SLaurent Pinchart out:
20957ef077a8SLaurent Pinchart 	spin_unlock_irqrestore(&priv->lock, spinflags);
20967ef077a8SLaurent Pinchart }
20977ef077a8SLaurent Pinchart 
isp1760_hub_status_data(struct usb_hcd * hcd,char * buf)20987ef077a8SLaurent Pinchart static int isp1760_hub_status_data(struct usb_hcd *hcd, char *buf)
20997ef077a8SLaurent Pinchart {
21007ef077a8SLaurent Pinchart 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
21011da9e1c0SRui Miguel Silva 	u32 status = 0;
21027ef077a8SLaurent Pinchart 	int retval = 1;
21037ef077a8SLaurent Pinchart 	unsigned long flags;
21047ef077a8SLaurent Pinchart 
21057ef077a8SLaurent Pinchart 	/* if !PM, root hub timers won't get shut down ... */
21067ef077a8SLaurent Pinchart 	if (!HC_IS_RUNNING(hcd->state))
21077ef077a8SLaurent Pinchart 		return 0;
21087ef077a8SLaurent Pinchart 
21097ef077a8SLaurent Pinchart 	/* init status to no-changes */
21107ef077a8SLaurent Pinchart 	buf[0] = 0;
21117ef077a8SLaurent Pinchart 
21127ef077a8SLaurent Pinchart 	spin_lock_irqsave(&priv->lock, flags);
21137ef077a8SLaurent Pinchart 
21141da9e1c0SRui Miguel Silva 	if (isp1760_hcd_is_set(hcd, PORT_OWNER) &&
21151da9e1c0SRui Miguel Silva 	    isp1760_hcd_is_set(hcd, PORT_CSC)) {
21161da9e1c0SRui Miguel Silva 		isp1760_hcd_clear(hcd, PORT_CSC);
21177ef077a8SLaurent Pinchart 		goto done;
21187ef077a8SLaurent Pinchart 	}
21197ef077a8SLaurent Pinchart 
21207ef077a8SLaurent Pinchart 	/*
21217ef077a8SLaurent Pinchart 	 * Return status information even for ports with OWNER set.
21227ef077a8SLaurent Pinchart 	 * Otherwise hub_wq wouldn't see the disconnect event when a
21237ef077a8SLaurent Pinchart 	 * high-speed device is switched over to the companion
21247ef077a8SLaurent Pinchart 	 * controller by the user.
21257ef077a8SLaurent Pinchart 	 */
21261da9e1c0SRui Miguel Silva 	if (isp1760_hcd_is_set(hcd, PORT_CSC) ||
21271da9e1c0SRui Miguel Silva 	    (isp1760_hcd_is_set(hcd, PORT_RESUME) &&
21281da9e1c0SRui Miguel Silva 	     time_after_eq(jiffies, priv->reset_done))) {
21297ef077a8SLaurent Pinchart 		buf [0] |= 1 << (0 + 1);
21307ef077a8SLaurent Pinchart 		status = STS_PCD;
21317ef077a8SLaurent Pinchart 	}
21327ef077a8SLaurent Pinchart 	/* FIXME autosuspend idle root hubs */
21337ef077a8SLaurent Pinchart done:
21347ef077a8SLaurent Pinchart 	spin_unlock_irqrestore(&priv->lock, flags);
21357ef077a8SLaurent Pinchart 	return status ? retval : 0;
21367ef077a8SLaurent Pinchart }
21377ef077a8SLaurent Pinchart 
isp1760_hub_descriptor(struct isp1760_hcd * priv,struct usb_hub_descriptor * desc)21387ef077a8SLaurent Pinchart static void isp1760_hub_descriptor(struct isp1760_hcd *priv,
21397ef077a8SLaurent Pinchart 		struct usb_hub_descriptor *desc)
21407ef077a8SLaurent Pinchart {
21411da9e1c0SRui Miguel Silva 	int ports;
21427ef077a8SLaurent Pinchart 	u16 temp;
21437ef077a8SLaurent Pinchart 
214460d789f3SRui Miguel Silva 	ports = isp1760_hcd_n_ports(priv->hcd);
21451da9e1c0SRui Miguel Silva 
21461cf6563bSSergei Shtylyov 	desc->bDescriptorType = USB_DT_HUB;
21477ef077a8SLaurent Pinchart 	/* priv 1.0, 2.3.9 says 20ms max */
21487ef077a8SLaurent Pinchart 	desc->bPwrOn2PwrGood = 10;
21497ef077a8SLaurent Pinchart 	desc->bHubContrCurrent = 0;
21507ef077a8SLaurent Pinchart 
21517ef077a8SLaurent Pinchart 	desc->bNbrPorts = ports;
21527ef077a8SLaurent Pinchart 	temp = 1 + (ports / 8);
21537ef077a8SLaurent Pinchart 	desc->bDescLength = 7 + 2 * temp;
21547ef077a8SLaurent Pinchart 
21557ef077a8SLaurent Pinchart 	/* ports removable, and usb 1.0 legacy PortPwrCtrlMask */
21567ef077a8SLaurent Pinchart 	memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
21577ef077a8SLaurent Pinchart 	memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
21587ef077a8SLaurent Pinchart 
21597ef077a8SLaurent Pinchart 	/* per-port overcurrent reporting */
21604d4bac44SGreg Kroah-Hartman 	temp = HUB_CHAR_INDV_PORT_OCPM;
216160d789f3SRui Miguel Silva 	if (isp1760_hcd_ppc_is_set(priv->hcd))
21627ef077a8SLaurent Pinchart 		/* per-port power control */
21634d4bac44SGreg Kroah-Hartman 		temp |= HUB_CHAR_INDV_PORT_LPSM;
21647ef077a8SLaurent Pinchart 	else
21657ef077a8SLaurent Pinchart 		/* no power switching */
21664d4bac44SGreg Kroah-Hartman 		temp |= HUB_CHAR_NO_LPSM;
21677ef077a8SLaurent Pinchart 	desc->wHubCharacteristics = cpu_to_le16(temp);
21687ef077a8SLaurent Pinchart }
21697ef077a8SLaurent Pinchart 
21707ef077a8SLaurent Pinchart #define	PORT_WAKE_BITS	(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
21717ef077a8SLaurent Pinchart 
check_reset_complete(struct usb_hcd * hcd,int index)21721da9e1c0SRui Miguel Silva static void check_reset_complete(struct usb_hcd *hcd, int index)
21737ef077a8SLaurent Pinchart {
21741da9e1c0SRui Miguel Silva 	if (!(isp1760_hcd_is_set(hcd, PORT_CONNECT)))
21751da9e1c0SRui Miguel Silva 		return;
21767ef077a8SLaurent Pinchart 
21777ef077a8SLaurent Pinchart 	/* if reset finished and it's still not enabled -- handoff */
21781da9e1c0SRui Miguel Silva 	if (!isp1760_hcd_is_set(hcd, PORT_PE)) {
21797ef077a8SLaurent Pinchart 		dev_info(hcd->self.controller,
21801da9e1c0SRui Miguel Silva 			 "port %d full speed --> companion\n", index + 1);
21817ef077a8SLaurent Pinchart 
21821da9e1c0SRui Miguel Silva 		isp1760_hcd_set(hcd, PORT_OWNER);
21837ef077a8SLaurent Pinchart 
21841da9e1c0SRui Miguel Silva 		isp1760_hcd_clear(hcd, PORT_CSC);
21851da9e1c0SRui Miguel Silva 	} else {
21867ef077a8SLaurent Pinchart 		dev_info(hcd->self.controller, "port %d high speed\n",
21877ef077a8SLaurent Pinchart 			 index + 1);
21881da9e1c0SRui Miguel Silva 	}
21897ef077a8SLaurent Pinchart 
21901da9e1c0SRui Miguel Silva 	return;
21917ef077a8SLaurent Pinchart }
21927ef077a8SLaurent Pinchart 
isp1760_hub_control(struct usb_hcd * hcd,u16 typeReq,u16 wValue,u16 wIndex,char * buf,u16 wLength)21937ef077a8SLaurent Pinchart static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
21947ef077a8SLaurent Pinchart 		u16 wValue, u16 wIndex, char *buf, u16 wLength)
21957ef077a8SLaurent Pinchart {
21967ef077a8SLaurent Pinchart 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
21971da9e1c0SRui Miguel Silva 	u32 status;
21987ef077a8SLaurent Pinchart 	unsigned long flags;
21997ef077a8SLaurent Pinchart 	int retval = 0;
22001da9e1c0SRui Miguel Silva 	int ports;
22011da9e1c0SRui Miguel Silva 
220260d789f3SRui Miguel Silva 	ports = isp1760_hcd_n_ports(hcd);
22037ef077a8SLaurent Pinchart 
22047ef077a8SLaurent Pinchart 	/*
22057ef077a8SLaurent Pinchart 	 * FIXME:  support SetPortFeatures USB_PORT_FEAT_INDICATOR.
22067ef077a8SLaurent Pinchart 	 * HCS_INDICATOR may say we can change LEDs to off/amber/green.
22077ef077a8SLaurent Pinchart 	 * (track current state ourselves) ... blink for diagnostics,
22087ef077a8SLaurent Pinchart 	 * power, "this is the one", etc.  EHCI spec supports this.
22097ef077a8SLaurent Pinchart 	 */
22107ef077a8SLaurent Pinchart 
22117ef077a8SLaurent Pinchart 	spin_lock_irqsave(&priv->lock, flags);
22127ef077a8SLaurent Pinchart 	switch (typeReq) {
22137ef077a8SLaurent Pinchart 	case ClearHubFeature:
22147ef077a8SLaurent Pinchart 		switch (wValue) {
22157ef077a8SLaurent Pinchart 		case C_HUB_LOCAL_POWER:
22167ef077a8SLaurent Pinchart 		case C_HUB_OVER_CURRENT:
22177ef077a8SLaurent Pinchart 			/* no hub-wide feature/status flags */
22187ef077a8SLaurent Pinchart 			break;
22197ef077a8SLaurent Pinchart 		default:
22207ef077a8SLaurent Pinchart 			goto error;
22217ef077a8SLaurent Pinchart 		}
22227ef077a8SLaurent Pinchart 		break;
22237ef077a8SLaurent Pinchart 	case ClearPortFeature:
22247ef077a8SLaurent Pinchart 		if (!wIndex || wIndex > ports)
22257ef077a8SLaurent Pinchart 			goto error;
22267ef077a8SLaurent Pinchart 		wIndex--;
22277ef077a8SLaurent Pinchart 
22287ef077a8SLaurent Pinchart 		/*
22297ef077a8SLaurent Pinchart 		 * Even if OWNER is set, so the port is owned by the
22307ef077a8SLaurent Pinchart 		 * companion controller, hub_wq needs to be able to clear
22317ef077a8SLaurent Pinchart 		 * the port-change status bits (especially
22327ef077a8SLaurent Pinchart 		 * USB_PORT_STAT_C_CONNECTION).
22337ef077a8SLaurent Pinchart 		 */
22347ef077a8SLaurent Pinchart 
22357ef077a8SLaurent Pinchart 		switch (wValue) {
22367ef077a8SLaurent Pinchart 		case USB_PORT_FEAT_ENABLE:
22371da9e1c0SRui Miguel Silva 			isp1760_hcd_clear(hcd, PORT_PE);
22387ef077a8SLaurent Pinchart 			break;
22397ef077a8SLaurent Pinchart 		case USB_PORT_FEAT_C_ENABLE:
22407ef077a8SLaurent Pinchart 			/* XXX error? */
22417ef077a8SLaurent Pinchart 			break;
22427ef077a8SLaurent Pinchart 		case USB_PORT_FEAT_SUSPEND:
22431da9e1c0SRui Miguel Silva 			if (isp1760_hcd_is_set(hcd, PORT_RESET))
22447ef077a8SLaurent Pinchart 				goto error;
22457ef077a8SLaurent Pinchart 
22461da9e1c0SRui Miguel Silva 			if (isp1760_hcd_is_set(hcd, PORT_SUSPEND)) {
22471da9e1c0SRui Miguel Silva 				if (!isp1760_hcd_is_set(hcd, PORT_PE))
22487ef077a8SLaurent Pinchart 					goto error;
22497ef077a8SLaurent Pinchart 				/* resume signaling for 20 msec */
22501da9e1c0SRui Miguel Silva 				isp1760_hcd_clear(hcd, PORT_CSC);
22511da9e1c0SRui Miguel Silva 				isp1760_hcd_set(hcd, PORT_RESUME);
22521da9e1c0SRui Miguel Silva 
22537ef077a8SLaurent Pinchart 				priv->reset_done = jiffies +
225459c9904cSFelipe Balbi 					msecs_to_jiffies(USB_RESUME_TIMEOUT);
22557ef077a8SLaurent Pinchart 			}
22567ef077a8SLaurent Pinchart 			break;
22577ef077a8SLaurent Pinchart 		case USB_PORT_FEAT_C_SUSPEND:
22587ef077a8SLaurent Pinchart 			/* we auto-clear this feature */
22597ef077a8SLaurent Pinchart 			break;
22607ef077a8SLaurent Pinchart 		case USB_PORT_FEAT_POWER:
226160d789f3SRui Miguel Silva 			if (isp1760_hcd_ppc_is_set(hcd))
22621da9e1c0SRui Miguel Silva 				isp1760_hcd_clear(hcd, PORT_POWER);
22637ef077a8SLaurent Pinchart 			break;
22647ef077a8SLaurent Pinchart 		case USB_PORT_FEAT_C_CONNECTION:
22651da9e1c0SRui Miguel Silva 			isp1760_hcd_set(hcd, PORT_CSC);
22667ef077a8SLaurent Pinchart 			break;
22677ef077a8SLaurent Pinchart 		case USB_PORT_FEAT_C_OVER_CURRENT:
22687ef077a8SLaurent Pinchart 			/* XXX error ?*/
22697ef077a8SLaurent Pinchart 			break;
22707ef077a8SLaurent Pinchart 		case USB_PORT_FEAT_C_RESET:
22717ef077a8SLaurent Pinchart 			/* GetPortStatus clears reset */
22727ef077a8SLaurent Pinchart 			break;
22737ef077a8SLaurent Pinchart 		default:
22747ef077a8SLaurent Pinchart 			goto error;
22757ef077a8SLaurent Pinchart 		}
227660d789f3SRui Miguel Silva 		isp1760_hcd_read(hcd, CMD_RUN);
22777ef077a8SLaurent Pinchart 		break;
22787ef077a8SLaurent Pinchart 	case GetHubDescriptor:
22797ef077a8SLaurent Pinchart 		isp1760_hub_descriptor(priv, (struct usb_hub_descriptor *)
22807ef077a8SLaurent Pinchart 			buf);
22817ef077a8SLaurent Pinchart 		break;
22827ef077a8SLaurent Pinchart 	case GetHubStatus:
22837ef077a8SLaurent Pinchart 		/* no hub-wide feature/status flags */
22847ef077a8SLaurent Pinchart 		memset(buf, 0, 4);
22857ef077a8SLaurent Pinchart 		break;
22867ef077a8SLaurent Pinchart 	case GetPortStatus:
22877ef077a8SLaurent Pinchart 		if (!wIndex || wIndex > ports)
22887ef077a8SLaurent Pinchart 			goto error;
22897ef077a8SLaurent Pinchart 		wIndex--;
22907ef077a8SLaurent Pinchart 		status = 0;
22917ef077a8SLaurent Pinchart 
22927ef077a8SLaurent Pinchart 		/* wPortChange bits */
22931da9e1c0SRui Miguel Silva 		if (isp1760_hcd_is_set(hcd, PORT_CSC))
22947ef077a8SLaurent Pinchart 			status |= USB_PORT_STAT_C_CONNECTION << 16;
22957ef077a8SLaurent Pinchart 
22967ef077a8SLaurent Pinchart 		/* whoever resumes must GetPortStatus to complete it!! */
22971da9e1c0SRui Miguel Silva 		if (isp1760_hcd_is_set(hcd, PORT_RESUME)) {
22987ef077a8SLaurent Pinchart 			dev_err(hcd->self.controller, "Port resume should be skipped.\n");
22997ef077a8SLaurent Pinchart 
23007ef077a8SLaurent Pinchart 			/* Remote Wakeup received? */
23017ef077a8SLaurent Pinchart 			if (!priv->reset_done) {
23027ef077a8SLaurent Pinchart 				/* resume signaling for 20 msec */
23037ef077a8SLaurent Pinchart 				priv->reset_done = jiffies
23047ef077a8SLaurent Pinchart 						+ msecs_to_jiffies(20);
23057ef077a8SLaurent Pinchart 				/* check the port again */
23067ef077a8SLaurent Pinchart 				mod_timer(&hcd->rh_timer, priv->reset_done);
23077ef077a8SLaurent Pinchart 			}
23087ef077a8SLaurent Pinchart 
23097ef077a8SLaurent Pinchart 			/* resume completed? */
23107ef077a8SLaurent Pinchart 			else if (time_after_eq(jiffies,
23117ef077a8SLaurent Pinchart 					priv->reset_done)) {
23127ef077a8SLaurent Pinchart 				status |= USB_PORT_STAT_C_SUSPEND << 16;
23137ef077a8SLaurent Pinchart 				priv->reset_done = 0;
23147ef077a8SLaurent Pinchart 
23157ef077a8SLaurent Pinchart 				/* stop resume signaling */
23161da9e1c0SRui Miguel Silva 				isp1760_hcd_clear(hcd, PORT_CSC);
23171da9e1c0SRui Miguel Silva 
231860d789f3SRui Miguel Silva 				retval = isp1760_hcd_clear_and_wait(hcd,
23191da9e1c0SRui Miguel Silva 							  PORT_RESUME, 2000);
23207ef077a8SLaurent Pinchart 				if (retval != 0) {
23217ef077a8SLaurent Pinchart 					dev_err(hcd->self.controller,
23227ef077a8SLaurent Pinchart 						"port %d resume error %d\n",
23237ef077a8SLaurent Pinchart 						wIndex + 1, retval);
23247ef077a8SLaurent Pinchart 					goto error;
23257ef077a8SLaurent Pinchart 				}
23267ef077a8SLaurent Pinchart 			}
23277ef077a8SLaurent Pinchart 		}
23287ef077a8SLaurent Pinchart 
23297ef077a8SLaurent Pinchart 		/* whoever resets must GetPortStatus to complete it!! */
23301da9e1c0SRui Miguel Silva 		if (isp1760_hcd_is_set(hcd, PORT_RESET) &&
23311da9e1c0SRui Miguel Silva 		    time_after_eq(jiffies, priv->reset_done)) {
23327ef077a8SLaurent Pinchart 			status |= USB_PORT_STAT_C_RESET << 16;
23337ef077a8SLaurent Pinchart 			priv->reset_done = 0;
23347ef077a8SLaurent Pinchart 
23357ef077a8SLaurent Pinchart 			/* force reset to complete */
23367ef077a8SLaurent Pinchart 			/* REVISIT:  some hardware needs 550+ usec to clear
23377ef077a8SLaurent Pinchart 			 * this bit; seems too long to spin routinely...
23387ef077a8SLaurent Pinchart 			 */
233960d789f3SRui Miguel Silva 			retval = isp1760_hcd_clear_and_wait(hcd, PORT_RESET,
23401da9e1c0SRui Miguel Silva 							    750);
23417ef077a8SLaurent Pinchart 			if (retval != 0) {
23427ef077a8SLaurent Pinchart 				dev_err(hcd->self.controller, "port %d reset error %d\n",
23437ef077a8SLaurent Pinchart 					wIndex + 1, retval);
23447ef077a8SLaurent Pinchart 				goto error;
23457ef077a8SLaurent Pinchart 			}
23467ef077a8SLaurent Pinchart 
23477ef077a8SLaurent Pinchart 			/* see what we found out */
23481da9e1c0SRui Miguel Silva 			check_reset_complete(hcd, wIndex);
23497ef077a8SLaurent Pinchart 		}
23507ef077a8SLaurent Pinchart 		/*
23517ef077a8SLaurent Pinchart 		 * Even if OWNER is set, there's no harm letting hub_wq
23527ef077a8SLaurent Pinchart 		 * see the wPortStatus values (they should all be 0 except
23537ef077a8SLaurent Pinchart 		 * for PORT_POWER anyway).
23547ef077a8SLaurent Pinchart 		 */
23557ef077a8SLaurent Pinchart 
23561da9e1c0SRui Miguel Silva 		if (isp1760_hcd_is_set(hcd, PORT_OWNER))
23577ef077a8SLaurent Pinchart 			dev_err(hcd->self.controller, "PORT_OWNER is set\n");
23587ef077a8SLaurent Pinchart 
23591da9e1c0SRui Miguel Silva 		if (isp1760_hcd_is_set(hcd, PORT_CONNECT)) {
23607ef077a8SLaurent Pinchart 			status |= USB_PORT_STAT_CONNECTION;
23617ef077a8SLaurent Pinchart 			/* status may be from integrated TT */
23627ef077a8SLaurent Pinchart 			status |= USB_PORT_STAT_HIGH_SPEED;
23637ef077a8SLaurent Pinchart 		}
23641da9e1c0SRui Miguel Silva 		if (isp1760_hcd_is_set(hcd, PORT_PE))
23657ef077a8SLaurent Pinchart 			status |= USB_PORT_STAT_ENABLE;
23661da9e1c0SRui Miguel Silva 		if (isp1760_hcd_is_set(hcd, PORT_SUSPEND) &&
23671da9e1c0SRui Miguel Silva 		    isp1760_hcd_is_set(hcd, PORT_RESUME))
23687ef077a8SLaurent Pinchart 			status |= USB_PORT_STAT_SUSPEND;
23691da9e1c0SRui Miguel Silva 		if (isp1760_hcd_is_set(hcd, PORT_RESET))
23707ef077a8SLaurent Pinchart 			status |= USB_PORT_STAT_RESET;
23711da9e1c0SRui Miguel Silva 		if (isp1760_hcd_is_set(hcd, PORT_POWER))
23727ef077a8SLaurent Pinchart 			status |= USB_PORT_STAT_POWER;
23737ef077a8SLaurent Pinchart 
23747ef077a8SLaurent Pinchart 		put_unaligned(cpu_to_le32(status), (__le32 *) buf);
23757ef077a8SLaurent Pinchart 		break;
23767ef077a8SLaurent Pinchart 	case SetHubFeature:
23777ef077a8SLaurent Pinchart 		switch (wValue) {
23787ef077a8SLaurent Pinchart 		case C_HUB_LOCAL_POWER:
23797ef077a8SLaurent Pinchart 		case C_HUB_OVER_CURRENT:
23807ef077a8SLaurent Pinchart 			/* no hub-wide feature/status flags */
23817ef077a8SLaurent Pinchart 			break;
23827ef077a8SLaurent Pinchart 		default:
23837ef077a8SLaurent Pinchart 			goto error;
23847ef077a8SLaurent Pinchart 		}
23857ef077a8SLaurent Pinchart 		break;
23867ef077a8SLaurent Pinchart 	case SetPortFeature:
23877ef077a8SLaurent Pinchart 		wIndex &= 0xff;
23887ef077a8SLaurent Pinchart 		if (!wIndex || wIndex > ports)
23897ef077a8SLaurent Pinchart 			goto error;
23907ef077a8SLaurent Pinchart 		wIndex--;
239160d789f3SRui Miguel Silva 
23921da9e1c0SRui Miguel Silva 		if (isp1760_hcd_is_set(hcd, PORT_OWNER))
23937ef077a8SLaurent Pinchart 			break;
23947ef077a8SLaurent Pinchart 
23957ef077a8SLaurent Pinchart 		switch (wValue) {
23967ef077a8SLaurent Pinchart 		case USB_PORT_FEAT_ENABLE:
23971da9e1c0SRui Miguel Silva 			isp1760_hcd_set(hcd, PORT_PE);
23987ef077a8SLaurent Pinchart 			break;
23997ef077a8SLaurent Pinchart 
24007ef077a8SLaurent Pinchart 		case USB_PORT_FEAT_SUSPEND:
24011da9e1c0SRui Miguel Silva 			if (!isp1760_hcd_is_set(hcd, PORT_PE) ||
24021da9e1c0SRui Miguel Silva 			    isp1760_hcd_is_set(hcd, PORT_RESET))
24037ef077a8SLaurent Pinchart 				goto error;
24047ef077a8SLaurent Pinchart 
24051da9e1c0SRui Miguel Silva 			isp1760_hcd_set(hcd, PORT_SUSPEND);
24067ef077a8SLaurent Pinchart 			break;
24077ef077a8SLaurent Pinchart 		case USB_PORT_FEAT_POWER:
240860d789f3SRui Miguel Silva 			if (isp1760_hcd_ppc_is_set(hcd))
24091da9e1c0SRui Miguel Silva 				isp1760_hcd_set(hcd, PORT_POWER);
24107ef077a8SLaurent Pinchart 			break;
24117ef077a8SLaurent Pinchart 		case USB_PORT_FEAT_RESET:
24121da9e1c0SRui Miguel Silva 			if (isp1760_hcd_is_set(hcd, PORT_RESUME))
24137ef077a8SLaurent Pinchart 				goto error;
24147ef077a8SLaurent Pinchart 			/* line status bits may report this as low speed,
24157ef077a8SLaurent Pinchart 			 * which can be fine if this root hub has a
24167ef077a8SLaurent Pinchart 			 * transaction translator built in.
24177ef077a8SLaurent Pinchart 			 */
24181da9e1c0SRui Miguel Silva 			if ((isp1760_hcd_is_set(hcd, PORT_CONNECT) &&
24191da9e1c0SRui Miguel Silva 			     !isp1760_hcd_is_set(hcd, PORT_PE)) &&
24201da9e1c0SRui Miguel Silva 			    (isp1760_hcd_read(hcd, PORT_LSTATUS) == 1)) {
24211da9e1c0SRui Miguel Silva 				isp1760_hcd_set(hcd, PORT_OWNER);
24227ef077a8SLaurent Pinchart 			} else {
24231da9e1c0SRui Miguel Silva 				isp1760_hcd_set(hcd, PORT_RESET);
24241da9e1c0SRui Miguel Silva 				isp1760_hcd_clear(hcd, PORT_PE);
24257ef077a8SLaurent Pinchart 
24267ef077a8SLaurent Pinchart 				/*
24277ef077a8SLaurent Pinchart 				 * caller must wait, then call GetPortStatus
24287ef077a8SLaurent Pinchart 				 * usb 2.0 spec says 50 ms resets on root
24297ef077a8SLaurent Pinchart 				 */
24307ef077a8SLaurent Pinchart 				priv->reset_done = jiffies +
24317ef077a8SLaurent Pinchart 					msecs_to_jiffies(50);
24327ef077a8SLaurent Pinchart 			}
24337ef077a8SLaurent Pinchart 			break;
24347ef077a8SLaurent Pinchart 		default:
24357ef077a8SLaurent Pinchart 			goto error;
24367ef077a8SLaurent Pinchart 		}
24377ef077a8SLaurent Pinchart 		break;
24387ef077a8SLaurent Pinchart 
24397ef077a8SLaurent Pinchart 	default:
24407ef077a8SLaurent Pinchart error:
24417ef077a8SLaurent Pinchart 		/* "stall" on error */
24427ef077a8SLaurent Pinchart 		retval = -EPIPE;
24437ef077a8SLaurent Pinchart 	}
24447ef077a8SLaurent Pinchart 	spin_unlock_irqrestore(&priv->lock, flags);
24457ef077a8SLaurent Pinchart 	return retval;
24467ef077a8SLaurent Pinchart }
24477ef077a8SLaurent Pinchart 
isp1760_get_frame(struct usb_hcd * hcd)24487ef077a8SLaurent Pinchart static int isp1760_get_frame(struct usb_hcd *hcd)
24497ef077a8SLaurent Pinchart {
24507ef077a8SLaurent Pinchart 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
24517ef077a8SLaurent Pinchart 	u32 fr;
24527ef077a8SLaurent Pinchart 
24531da9e1c0SRui Miguel Silva 	fr = isp1760_hcd_read(hcd, HC_FRINDEX);
24547ef077a8SLaurent Pinchart 	return (fr >> 3) % priv->periodic_size;
24557ef077a8SLaurent Pinchart }
24567ef077a8SLaurent Pinchart 
isp1760_stop(struct usb_hcd * hcd)24577ef077a8SLaurent Pinchart static void isp1760_stop(struct usb_hcd *hcd)
24587ef077a8SLaurent Pinchart {
24597ef077a8SLaurent Pinchart 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
24607ef077a8SLaurent Pinchart 
24617ef077a8SLaurent Pinchart 	del_timer(&errata2_timer);
24627ef077a8SLaurent Pinchart 
24637ef077a8SLaurent Pinchart 	isp1760_hub_control(hcd, ClearPortFeature, USB_PORT_FEAT_POWER,	1,
24647ef077a8SLaurent Pinchart 			NULL, 0);
2465de0611b2SJia-Ju Bai 	msleep(20);
24667ef077a8SLaurent Pinchart 
24677ef077a8SLaurent Pinchart 	spin_lock_irq(&priv->lock);
24687ef077a8SLaurent Pinchart 	ehci_reset(hcd);
24697ef077a8SLaurent Pinchart 	/* Disable IRQ */
24701da9e1c0SRui Miguel Silva 	isp1760_hcd_clear(hcd, HW_GLOBAL_INTR_EN);
24717ef077a8SLaurent Pinchart 	spin_unlock_irq(&priv->lock);
24727ef077a8SLaurent Pinchart 
24731da9e1c0SRui Miguel Silva 	isp1760_hcd_clear(hcd, FLAG_CF);
24747ef077a8SLaurent Pinchart }
24757ef077a8SLaurent Pinchart 
isp1760_shutdown(struct usb_hcd * hcd)24767ef077a8SLaurent Pinchart static void isp1760_shutdown(struct usb_hcd *hcd)
24777ef077a8SLaurent Pinchart {
24787ef077a8SLaurent Pinchart 	isp1760_stop(hcd);
24797ef077a8SLaurent Pinchart 
24801da9e1c0SRui Miguel Silva 	isp1760_hcd_clear(hcd, HW_GLOBAL_INTR_EN);
24811da9e1c0SRui Miguel Silva 
24821da9e1c0SRui Miguel Silva 	isp1760_hcd_clear(hcd, CMD_RUN);
24837ef077a8SLaurent Pinchart }
24847ef077a8SLaurent Pinchart 
isp1760_clear_tt_buffer_complete(struct usb_hcd * hcd,struct usb_host_endpoint * ep)24857ef077a8SLaurent Pinchart static void isp1760_clear_tt_buffer_complete(struct usb_hcd *hcd,
24867ef077a8SLaurent Pinchart 						struct usb_host_endpoint *ep)
24877ef077a8SLaurent Pinchart {
24887ef077a8SLaurent Pinchart 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
24897ef077a8SLaurent Pinchart 	struct isp1760_qh *qh = ep->hcpriv;
24907ef077a8SLaurent Pinchart 	unsigned long spinflags;
24917ef077a8SLaurent Pinchart 
24927ef077a8SLaurent Pinchart 	if (!qh)
24937ef077a8SLaurent Pinchart 		return;
24947ef077a8SLaurent Pinchart 
24957ef077a8SLaurent Pinchart 	spin_lock_irqsave(&priv->lock, spinflags);
24967ef077a8SLaurent Pinchart 	qh->tt_buffer_dirty = 0;
24977ef077a8SLaurent Pinchart 	schedule_ptds(hcd);
24987ef077a8SLaurent Pinchart 	spin_unlock_irqrestore(&priv->lock, spinflags);
24997ef077a8SLaurent Pinchart }
25007ef077a8SLaurent Pinchart 
25017ef077a8SLaurent Pinchart 
25027ef077a8SLaurent Pinchart static const struct hc_driver isp1760_hc_driver = {
25037ef077a8SLaurent Pinchart 	.description		= "isp1760-hcd",
25047ef077a8SLaurent Pinchart 	.product_desc		= "NXP ISP1760 USB Host Controller",
25057ef077a8SLaurent Pinchart 	.hcd_priv_size		= sizeof(struct isp1760_hcd *),
25067ef077a8SLaurent Pinchart 	.irq			= isp1760_irq,
25077ef077a8SLaurent Pinchart 	.flags			= HCD_MEMORY | HCD_USB2,
25087ef077a8SLaurent Pinchart 	.reset			= isp1760_hc_setup,
25097ef077a8SLaurent Pinchart 	.start			= isp1760_run,
25107ef077a8SLaurent Pinchart 	.stop			= isp1760_stop,
25117ef077a8SLaurent Pinchart 	.shutdown		= isp1760_shutdown,
25127ef077a8SLaurent Pinchart 	.urb_enqueue		= isp1760_urb_enqueue,
25137ef077a8SLaurent Pinchart 	.urb_dequeue		= isp1760_urb_dequeue,
25147ef077a8SLaurent Pinchart 	.endpoint_disable	= isp1760_endpoint_disable,
25157ef077a8SLaurent Pinchart 	.get_frame_number	= isp1760_get_frame,
25167ef077a8SLaurent Pinchart 	.hub_status_data	= isp1760_hub_status_data,
25177ef077a8SLaurent Pinchart 	.hub_control		= isp1760_hub_control,
25187ef077a8SLaurent Pinchart 	.clear_tt_buffer_complete	= isp1760_clear_tt_buffer_complete,
25197ef077a8SLaurent Pinchart };
25207ef077a8SLaurent Pinchart 
isp1760_init_kmem_once(void)25217ef077a8SLaurent Pinchart int __init isp1760_init_kmem_once(void)
25227ef077a8SLaurent Pinchart {
25237ef077a8SLaurent Pinchart 	urb_listitem_cachep = kmem_cache_create("isp1760_urb_listitem",
2524f88c3fb8SLinus Torvalds 			sizeof(struct urb_listitem), 0, SLAB_TEMPORARY, NULL);
25257ef077a8SLaurent Pinchart 
25267ef077a8SLaurent Pinchart 	if (!urb_listitem_cachep)
25277ef077a8SLaurent Pinchart 		return -ENOMEM;
25287ef077a8SLaurent Pinchart 
25297ef077a8SLaurent Pinchart 	qtd_cachep = kmem_cache_create("isp1760_qtd",
2530f88c3fb8SLinus Torvalds 			sizeof(struct isp1760_qtd), 0, SLAB_TEMPORARY, NULL);
25317ef077a8SLaurent Pinchart 
25327ef077a8SLaurent Pinchart 	if (!qtd_cachep)
2533cbbdb3feSRui Miguel Silva 		goto destroy_urb_listitem;
25347ef077a8SLaurent Pinchart 
25357ef077a8SLaurent Pinchart 	qh_cachep = kmem_cache_create("isp1760_qh", sizeof(struct isp1760_qh),
2536f88c3fb8SLinus Torvalds 			0, SLAB_TEMPORARY, NULL);
25377ef077a8SLaurent Pinchart 
2538cbbdb3feSRui Miguel Silva 	if (!qh_cachep)
2539cbbdb3feSRui Miguel Silva 		goto destroy_qtd;
25407ef077a8SLaurent Pinchart 
25417ef077a8SLaurent Pinchart 	return 0;
2542cbbdb3feSRui Miguel Silva 
2543cbbdb3feSRui Miguel Silva destroy_qtd:
2544cbbdb3feSRui Miguel Silva 	kmem_cache_destroy(qtd_cachep);
2545cbbdb3feSRui Miguel Silva 
2546cbbdb3feSRui Miguel Silva destroy_urb_listitem:
2547cbbdb3feSRui Miguel Silva 	kmem_cache_destroy(urb_listitem_cachep);
2548cbbdb3feSRui Miguel Silva 
2549cbbdb3feSRui Miguel Silva 	return -ENOMEM;
25507ef077a8SLaurent Pinchart }
25517ef077a8SLaurent Pinchart 
isp1760_deinit_kmem_cache(void)25527ef077a8SLaurent Pinchart void isp1760_deinit_kmem_cache(void)
25537ef077a8SLaurent Pinchart {
25547ef077a8SLaurent Pinchart 	kmem_cache_destroy(qtd_cachep);
25557ef077a8SLaurent Pinchart 	kmem_cache_destroy(qh_cachep);
25567ef077a8SLaurent Pinchart 	kmem_cache_destroy(urb_listitem_cachep);
25577ef077a8SLaurent Pinchart }
25587ef077a8SLaurent Pinchart 
isp1760_hcd_register(struct isp1760_hcd * priv,struct resource * mem,int irq,unsigned long irqflags,struct device * dev)25591da9e1c0SRui Miguel Silva int isp1760_hcd_register(struct isp1760_hcd *priv, struct resource *mem,
25601da9e1c0SRui Miguel Silva 			 int irq, unsigned long irqflags,
25617ef077a8SLaurent Pinchart 			 struct device *dev)
25627ef077a8SLaurent Pinchart {
2563a74f639cSRui Miguel Silva 	const struct isp1760_memory_layout *mem_layout = priv->memory_layout;
25647ef077a8SLaurent Pinchart 	struct usb_hcd *hcd;
25657ef077a8SLaurent Pinchart 	int ret;
25667ef077a8SLaurent Pinchart 
25677ef077a8SLaurent Pinchart 	hcd = usb_create_hcd(&isp1760_hc_driver, dev, dev_name(dev));
25687ef077a8SLaurent Pinchart 	if (!hcd)
25697ef077a8SLaurent Pinchart 		return -ENOMEM;
25707ef077a8SLaurent Pinchart 
25717ef077a8SLaurent Pinchart 	*(struct isp1760_hcd **)hcd->hcd_priv = priv;
25727ef077a8SLaurent Pinchart 
25737ef077a8SLaurent Pinchart 	priv->hcd = hcd;
25747ef077a8SLaurent Pinchart 
257560d789f3SRui Miguel Silva 	priv->atl_slots = kcalloc(mem_layout->slot_num,
257660d789f3SRui Miguel Silva 				  sizeof(struct isp1760_slotinfo), GFP_KERNEL);
257760d789f3SRui Miguel Silva 	if (!priv->atl_slots) {
2578a74f639cSRui Miguel Silva 		ret = -ENOMEM;
2579a74f639cSRui Miguel Silva 		goto put_hcd;
2580a74f639cSRui Miguel Silva 	}
2581a74f639cSRui Miguel Silva 
258260d789f3SRui Miguel Silva 	priv->int_slots = kcalloc(mem_layout->slot_num,
2583a74f639cSRui Miguel Silva 				  sizeof(struct isp1760_slotinfo), GFP_KERNEL);
2584a74f639cSRui Miguel Silva 	if (!priv->int_slots) {
2585a74f639cSRui Miguel Silva 		ret = -ENOMEM;
2586a74f639cSRui Miguel Silva 		goto free_atl_slots;
2587a74f639cSRui Miguel Silva 	}
2588a74f639cSRui Miguel Silva 
25897ef077a8SLaurent Pinchart 	init_memory(priv);
25907ef077a8SLaurent Pinchart 
25917ef077a8SLaurent Pinchart 	hcd->irq = irq;
25927ef077a8SLaurent Pinchart 	hcd->rsrc_start = mem->start;
25937ef077a8SLaurent Pinchart 	hcd->rsrc_len = resource_size(mem);
25947ef077a8SLaurent Pinchart 
25954d4bac44SGreg Kroah-Hartman 	/* This driver doesn't support wakeup requests */
25964d4bac44SGreg Kroah-Hartman 	hcd->cant_recv_wakeups = 1;
25974d4bac44SGreg Kroah-Hartman 
25987ef077a8SLaurent Pinchart 	ret = usb_add_hcd(hcd, irq, irqflags);
25997ef077a8SLaurent Pinchart 	if (ret)
2600a74f639cSRui Miguel Silva 		goto free_int_slots;
26017ef077a8SLaurent Pinchart 
26027ef077a8SLaurent Pinchart 	device_wakeup_enable(hcd->self.controller);
26037ef077a8SLaurent Pinchart 
26047ef077a8SLaurent Pinchart 	return 0;
26057ef077a8SLaurent Pinchart 
2606a74f639cSRui Miguel Silva free_int_slots:
2607a74f639cSRui Miguel Silva 	kfree(priv->int_slots);
2608a74f639cSRui Miguel Silva free_atl_slots:
2609a74f639cSRui Miguel Silva 	kfree(priv->atl_slots);
2610a74f639cSRui Miguel Silva put_hcd:
26117ef077a8SLaurent Pinchart 	usb_put_hcd(hcd);
26127ef077a8SLaurent Pinchart 	return ret;
26137ef077a8SLaurent Pinchart }
26147ef077a8SLaurent Pinchart 
isp1760_hcd_unregister(struct isp1760_hcd * priv)26157ef077a8SLaurent Pinchart void isp1760_hcd_unregister(struct isp1760_hcd *priv)
26167ef077a8SLaurent Pinchart {
2617d21daf1eSLaurent Pinchart 	if (!priv->hcd)
2618d21daf1eSLaurent Pinchart 		return;
2619d21daf1eSLaurent Pinchart 
26207ef077a8SLaurent Pinchart 	usb_remove_hcd(priv->hcd);
26217ef077a8SLaurent Pinchart 	usb_put_hcd(priv->hcd);
2622a74f639cSRui Miguel Silva 	kfree(priv->atl_slots);
2623a74f639cSRui Miguel Silva 	kfree(priv->int_slots);
26247ef077a8SLaurent Pinchart }
2625