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