162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2008 Rodolfo Giometti <giometti@linux.it> 462306a36Sopenharmony_ci * Copyright (c) 2008 Eurotech S.p.A. <info@eurtech.it> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This code is *strongly* based on EHCI-HCD code by David Brownell since 762306a36Sopenharmony_ci * the chip is a quasi-EHCI compatible. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/pci.h> 1262306a36Sopenharmony_ci#include <linux/dmapool.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/ioport.h> 1662306a36Sopenharmony_ci#include <linux/sched.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/errno.h> 1962306a36Sopenharmony_ci#include <linux/timer.h> 2062306a36Sopenharmony_ci#include <linux/list.h> 2162306a36Sopenharmony_ci#include <linux/interrupt.h> 2262306a36Sopenharmony_ci#include <linux/usb.h> 2362306a36Sopenharmony_ci#include <linux/usb/hcd.h> 2462306a36Sopenharmony_ci#include <linux/moduleparam.h> 2562306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2662306a36Sopenharmony_ci#include <linux/io.h> 2762306a36Sopenharmony_ci#include <linux/iopoll.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <asm/irq.h> 3062306a36Sopenharmony_ci#include <asm/unaligned.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include <linux/irq.h> 3362306a36Sopenharmony_ci#include <linux/platform_device.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define DRIVER_VERSION "0.0.50" 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define OXU_DEVICEID 0x00 3862306a36Sopenharmony_ci #define OXU_REV_MASK 0xffff0000 3962306a36Sopenharmony_ci #define OXU_REV_SHIFT 16 4062306a36Sopenharmony_ci #define OXU_REV_2100 0x2100 4162306a36Sopenharmony_ci #define OXU_BO_SHIFT 8 4262306a36Sopenharmony_ci #define OXU_BO_MASK (0x3 << OXU_BO_SHIFT) 4362306a36Sopenharmony_ci #define OXU_MAJ_REV_SHIFT 4 4462306a36Sopenharmony_ci #define OXU_MAJ_REV_MASK (0xf << OXU_MAJ_REV_SHIFT) 4562306a36Sopenharmony_ci #define OXU_MIN_REV_SHIFT 0 4662306a36Sopenharmony_ci #define OXU_MIN_REV_MASK (0xf << OXU_MIN_REV_SHIFT) 4762306a36Sopenharmony_ci#define OXU_HOSTIFCONFIG 0x04 4862306a36Sopenharmony_ci#define OXU_SOFTRESET 0x08 4962306a36Sopenharmony_ci #define OXU_SRESET (1 << 0) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define OXU_PIOBURSTREADCTRL 0x0C 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define OXU_CHIPIRQSTATUS 0x10 5462306a36Sopenharmony_ci#define OXU_CHIPIRQEN_SET 0x14 5562306a36Sopenharmony_ci#define OXU_CHIPIRQEN_CLR 0x18 5662306a36Sopenharmony_ci #define OXU_USBSPHLPWUI 0x00000080 5762306a36Sopenharmony_ci #define OXU_USBOTGLPWUI 0x00000040 5862306a36Sopenharmony_ci #define OXU_USBSPHI 0x00000002 5962306a36Sopenharmony_ci #define OXU_USBOTGI 0x00000001 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define OXU_CLKCTRL_SET 0x1C 6262306a36Sopenharmony_ci #define OXU_SYSCLKEN 0x00000008 6362306a36Sopenharmony_ci #define OXU_USBSPHCLKEN 0x00000002 6462306a36Sopenharmony_ci #define OXU_USBOTGCLKEN 0x00000001 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define OXU_ASO 0x68 6762306a36Sopenharmony_ci #define OXU_SPHPOEN 0x00000100 6862306a36Sopenharmony_ci #define OXU_OVRCCURPUPDEN 0x00000800 6962306a36Sopenharmony_ci #define OXU_ASO_OP (1 << 10) 7062306a36Sopenharmony_ci #define OXU_COMPARATOR 0x000004000 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define OXU_USBMODE 0x1A8 7362306a36Sopenharmony_ci #define OXU_VBPS 0x00000020 7462306a36Sopenharmony_ci #define OXU_ES_LITTLE 0x00000000 7562306a36Sopenharmony_ci #define OXU_CM_HOST_ONLY 0x00000003 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/* 7862306a36Sopenharmony_ci * Proper EHCI structs & defines 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* Magic numbers that can affect system performance */ 8262306a36Sopenharmony_ci#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ 8362306a36Sopenharmony_ci#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ 8462306a36Sopenharmony_ci#define EHCI_TUNE_RL_TT 0 8562306a36Sopenharmony_ci#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ 8662306a36Sopenharmony_ci#define EHCI_TUNE_MULT_TT 1 8762306a36Sopenharmony_ci#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistruct oxu_hcd; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* Section 2.2 Host Controller Capability Registers */ 9462306a36Sopenharmony_cistruct ehci_caps { 9562306a36Sopenharmony_ci /* these fields are specified as 8 and 16 bit registers, 9662306a36Sopenharmony_ci * but some hosts can't perform 8 or 16 bit PCI accesses. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci u32 hc_capbase; 9962306a36Sopenharmony_ci#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */ 10062306a36Sopenharmony_ci#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */ 10162306a36Sopenharmony_ci u32 hcs_params; /* HCSPARAMS - offset 0x4 */ 10262306a36Sopenharmony_ci#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */ 10362306a36Sopenharmony_ci#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */ 10462306a36Sopenharmony_ci#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */ 10562306a36Sopenharmony_ci#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */ 10662306a36Sopenharmony_ci#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */ 10762306a36Sopenharmony_ci#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */ 10862306a36Sopenharmony_ci#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci u32 hcc_params; /* HCCPARAMS - offset 0x8 */ 11162306a36Sopenharmony_ci#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */ 11262306a36Sopenharmony_ci#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */ 11362306a36Sopenharmony_ci#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */ 11462306a36Sopenharmony_ci#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ 11562306a36Sopenharmony_ci#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ 11662306a36Sopenharmony_ci#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */ 11762306a36Sopenharmony_ci u8 portroute[8]; /* nibbles for routing - offset 0xC */ 11862306a36Sopenharmony_ci} __packed; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* Section 2.3 Host Controller Operational Registers */ 12262306a36Sopenharmony_cistruct ehci_regs { 12362306a36Sopenharmony_ci /* USBCMD: offset 0x00 */ 12462306a36Sopenharmony_ci u32 command; 12562306a36Sopenharmony_ci/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ 12662306a36Sopenharmony_ci#define CMD_PARK (1<<11) /* enable "park" on async qh */ 12762306a36Sopenharmony_ci#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ 12862306a36Sopenharmony_ci#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */ 12962306a36Sopenharmony_ci#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ 13062306a36Sopenharmony_ci#define CMD_ASE (1<<5) /* async schedule enable */ 13162306a36Sopenharmony_ci#define CMD_PSE (1<<4) /* periodic schedule enable */ 13262306a36Sopenharmony_ci/* 3:2 is periodic frame list size */ 13362306a36Sopenharmony_ci#define CMD_RESET (1<<1) /* reset HC not bus */ 13462306a36Sopenharmony_ci#define CMD_RUN (1<<0) /* start/stop HC */ 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* USBSTS: offset 0x04 */ 13762306a36Sopenharmony_ci u32 status; 13862306a36Sopenharmony_ci#define STS_ASS (1<<15) /* Async Schedule Status */ 13962306a36Sopenharmony_ci#define STS_PSS (1<<14) /* Periodic Schedule Status */ 14062306a36Sopenharmony_ci#define STS_RECL (1<<13) /* Reclamation */ 14162306a36Sopenharmony_ci#define STS_HALT (1<<12) /* Not running (any reason) */ 14262306a36Sopenharmony_ci/* some bits reserved */ 14362306a36Sopenharmony_ci /* these STS_* flags are also intr_enable bits (USBINTR) */ 14462306a36Sopenharmony_ci#define STS_IAA (1<<5) /* Interrupted on async advance */ 14562306a36Sopenharmony_ci#define STS_FATAL (1<<4) /* such as some PCI access errors */ 14662306a36Sopenharmony_ci#define STS_FLR (1<<3) /* frame list rolled over */ 14762306a36Sopenharmony_ci#define STS_PCD (1<<2) /* port change detect */ 14862306a36Sopenharmony_ci#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ 14962306a36Sopenharmony_ci#define STS_INT (1<<0) /* "normal" completion (short, ...) */ 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* USBINTR: offset 0x08 */ 15462306a36Sopenharmony_ci u32 intr_enable; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* FRINDEX: offset 0x0C */ 15762306a36Sopenharmony_ci u32 frame_index; /* current microframe number */ 15862306a36Sopenharmony_ci /* CTRLDSSEGMENT: offset 0x10 */ 15962306a36Sopenharmony_ci u32 segment; /* address bits 63:32 if needed */ 16062306a36Sopenharmony_ci /* PERIODICLISTBASE: offset 0x14 */ 16162306a36Sopenharmony_ci u32 frame_list; /* points to periodic list */ 16262306a36Sopenharmony_ci /* ASYNCLISTADDR: offset 0x18 */ 16362306a36Sopenharmony_ci u32 async_next; /* address of next async queue head */ 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci u32 reserved[9]; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* CONFIGFLAG: offset 0x40 */ 16862306a36Sopenharmony_ci u32 configured_flag; 16962306a36Sopenharmony_ci#define FLAG_CF (1<<0) /* true: we'll support "high speed" */ 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* PORTSC: offset 0x44 */ 17262306a36Sopenharmony_ci u32 port_status[]; /* up to N_PORTS */ 17362306a36Sopenharmony_ci/* 31:23 reserved */ 17462306a36Sopenharmony_ci#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */ 17562306a36Sopenharmony_ci#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ 17662306a36Sopenharmony_ci#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */ 17762306a36Sopenharmony_ci/* 19:16 for port testing */ 17862306a36Sopenharmony_ci#define PORT_LED_OFF (0<<14) 17962306a36Sopenharmony_ci#define PORT_LED_AMBER (1<<14) 18062306a36Sopenharmony_ci#define PORT_LED_GREEN (2<<14) 18162306a36Sopenharmony_ci#define PORT_LED_MASK (3<<14) 18262306a36Sopenharmony_ci#define PORT_OWNER (1<<13) /* true: companion hc owns this port */ 18362306a36Sopenharmony_ci#define PORT_POWER (1<<12) /* true: has power (see PPC) */ 18462306a36Sopenharmony_ci#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ 18562306a36Sopenharmony_ci/* 11:10 for detecting lowspeed devices (reset vs release ownership) */ 18662306a36Sopenharmony_ci/* 9 reserved */ 18762306a36Sopenharmony_ci#define PORT_RESET (1<<8) /* reset port */ 18862306a36Sopenharmony_ci#define PORT_SUSPEND (1<<7) /* suspend port */ 18962306a36Sopenharmony_ci#define PORT_RESUME (1<<6) /* resume it */ 19062306a36Sopenharmony_ci#define PORT_OCC (1<<5) /* over current change */ 19162306a36Sopenharmony_ci#define PORT_OC (1<<4) /* over current active */ 19262306a36Sopenharmony_ci#define PORT_PEC (1<<3) /* port enable change */ 19362306a36Sopenharmony_ci#define PORT_PE (1<<2) /* port enable */ 19462306a36Sopenharmony_ci#define PORT_CSC (1<<1) /* connect status change */ 19562306a36Sopenharmony_ci#define PORT_CONNECT (1<<0) /* device connected */ 19662306a36Sopenharmony_ci#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) 19762306a36Sopenharmony_ci} __packed; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci/* Appendix C, Debug port ... intended for use with special "debug devices" 20062306a36Sopenharmony_ci * that can help if there's no serial console. (nonstandard enumeration.) 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_cistruct ehci_dbg_port { 20362306a36Sopenharmony_ci u32 control; 20462306a36Sopenharmony_ci#define DBGP_OWNER (1<<30) 20562306a36Sopenharmony_ci#define DBGP_ENABLED (1<<28) 20662306a36Sopenharmony_ci#define DBGP_DONE (1<<16) 20762306a36Sopenharmony_ci#define DBGP_INUSE (1<<10) 20862306a36Sopenharmony_ci#define DBGP_ERRCODE(x) (((x)>>7)&0x07) 20962306a36Sopenharmony_ci# define DBGP_ERR_BAD 1 21062306a36Sopenharmony_ci# define DBGP_ERR_SIGNAL 2 21162306a36Sopenharmony_ci#define DBGP_ERROR (1<<6) 21262306a36Sopenharmony_ci#define DBGP_GO (1<<5) 21362306a36Sopenharmony_ci#define DBGP_OUT (1<<4) 21462306a36Sopenharmony_ci#define DBGP_LEN(x) (((x)>>0)&0x0f) 21562306a36Sopenharmony_ci u32 pids; 21662306a36Sopenharmony_ci#define DBGP_PID_GET(x) (((x)>>16)&0xff) 21762306a36Sopenharmony_ci#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok)) 21862306a36Sopenharmony_ci u32 data03; 21962306a36Sopenharmony_ci u32 data47; 22062306a36Sopenharmony_ci u32 address; 22162306a36Sopenharmony_ci#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep)) 22262306a36Sopenharmony_ci} __packed; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci#define QTD_NEXT(dma) cpu_to_le32((u32)dma) 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/* 22762306a36Sopenharmony_ci * EHCI Specification 0.95 Section 3.5 22862306a36Sopenharmony_ci * QTD: describe data transfer components (buffer, direction, ...) 22962306a36Sopenharmony_ci * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram". 23062306a36Sopenharmony_ci * 23162306a36Sopenharmony_ci * These are associated only with "QH" (Queue Head) structures, 23262306a36Sopenharmony_ci * used with control, bulk, and interrupt transfers. 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_cistruct ehci_qtd { 23562306a36Sopenharmony_ci /* first part defined by EHCI spec */ 23662306a36Sopenharmony_ci __le32 hw_next; /* see EHCI 3.5.1 */ 23762306a36Sopenharmony_ci __le32 hw_alt_next; /* see EHCI 3.5.2 */ 23862306a36Sopenharmony_ci __le32 hw_token; /* see EHCI 3.5.3 */ 23962306a36Sopenharmony_ci#define QTD_TOGGLE (1 << 31) /* data toggle */ 24062306a36Sopenharmony_ci#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff) 24162306a36Sopenharmony_ci#define QTD_IOC (1 << 15) /* interrupt on complete */ 24262306a36Sopenharmony_ci#define QTD_CERR(tok) (((tok)>>10) & 0x3) 24362306a36Sopenharmony_ci#define QTD_PID(tok) (((tok)>>8) & 0x3) 24462306a36Sopenharmony_ci#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */ 24562306a36Sopenharmony_ci#define QTD_STS_HALT (1 << 6) /* halted on error */ 24662306a36Sopenharmony_ci#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */ 24762306a36Sopenharmony_ci#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */ 24862306a36Sopenharmony_ci#define QTD_STS_XACT (1 << 3) /* device gave illegal response */ 24962306a36Sopenharmony_ci#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */ 25062306a36Sopenharmony_ci#define QTD_STS_STS (1 << 1) /* split transaction state */ 25162306a36Sopenharmony_ci#define QTD_STS_PING (1 << 0) /* issue PING? */ 25262306a36Sopenharmony_ci __le32 hw_buf[5]; /* see EHCI 3.5.4 */ 25362306a36Sopenharmony_ci __le32 hw_buf_hi[5]; /* Appendix B */ 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* the rest is HCD-private */ 25662306a36Sopenharmony_ci dma_addr_t qtd_dma; /* qtd address */ 25762306a36Sopenharmony_ci struct list_head qtd_list; /* sw qtd list */ 25862306a36Sopenharmony_ci struct urb *urb; /* qtd's urb */ 25962306a36Sopenharmony_ci size_t length; /* length of buffer */ 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci u32 qtd_buffer_len; 26262306a36Sopenharmony_ci void *buffer; 26362306a36Sopenharmony_ci dma_addr_t buffer_dma; 26462306a36Sopenharmony_ci void *transfer_buffer; 26562306a36Sopenharmony_ci void *transfer_dma; 26662306a36Sopenharmony_ci} __aligned(32); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/* mask NakCnt+T in qh->hw_alt_next */ 26962306a36Sopenharmony_ci#define QTD_MASK cpu_to_le32 (~0x1f) 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1) 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci/* Type tag from {qh, itd, sitd, fstn}->hw_next */ 27462306a36Sopenharmony_ci#define Q_NEXT_TYPE(dma) ((dma) & cpu_to_le32 (3 << 1)) 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci/* values for that type tag */ 27762306a36Sopenharmony_ci#define Q_TYPE_QH cpu_to_le32 (1 << 1) 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci/* next async queue entry, or pointer to interrupt/periodic QH */ 28062306a36Sopenharmony_ci#define QH_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH) 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci/* for periodic/async schedules and qtd lists, mark end of list */ 28362306a36Sopenharmony_ci#define EHCI_LIST_END cpu_to_le32(1) /* "null pointer" to hw */ 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci/* 28662306a36Sopenharmony_ci * Entries in periodic shadow table are pointers to one of four kinds 28762306a36Sopenharmony_ci * of data structure. That's dictated by the hardware; a type tag is 28862306a36Sopenharmony_ci * encoded in the low bits of the hardware's periodic schedule. Use 28962306a36Sopenharmony_ci * Q_NEXT_TYPE to get the tag. 29062306a36Sopenharmony_ci * 29162306a36Sopenharmony_ci * For entries in the async schedule, the type tag always says "qh". 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_ciunion ehci_shadow { 29462306a36Sopenharmony_ci struct ehci_qh *qh; /* Q_TYPE_QH */ 29562306a36Sopenharmony_ci __le32 *hw_next; /* (all types) */ 29662306a36Sopenharmony_ci void *ptr; 29762306a36Sopenharmony_ci}; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci/* 30062306a36Sopenharmony_ci * EHCI Specification 0.95 Section 3.6 30162306a36Sopenharmony_ci * QH: describes control/bulk/interrupt endpoints 30262306a36Sopenharmony_ci * See Fig 3-7 "Queue Head Structure Layout". 30362306a36Sopenharmony_ci * 30462306a36Sopenharmony_ci * These appear in both the async and (for interrupt) periodic schedules. 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistruct ehci_qh { 30862306a36Sopenharmony_ci /* first part defined by EHCI spec */ 30962306a36Sopenharmony_ci __le32 hw_next; /* see EHCI 3.6.1 */ 31062306a36Sopenharmony_ci __le32 hw_info1; /* see EHCI 3.6.2 */ 31162306a36Sopenharmony_ci#define QH_HEAD 0x00008000 31262306a36Sopenharmony_ci __le32 hw_info2; /* see EHCI 3.6.2 */ 31362306a36Sopenharmony_ci#define QH_SMASK 0x000000ff 31462306a36Sopenharmony_ci#define QH_CMASK 0x0000ff00 31562306a36Sopenharmony_ci#define QH_HUBADDR 0x007f0000 31662306a36Sopenharmony_ci#define QH_HUBPORT 0x3f800000 31762306a36Sopenharmony_ci#define QH_MULT 0xc0000000 31862306a36Sopenharmony_ci __le32 hw_current; /* qtd list - see EHCI 3.6.4 */ 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* qtd overlay (hardware parts of a struct ehci_qtd) */ 32162306a36Sopenharmony_ci __le32 hw_qtd_next; 32262306a36Sopenharmony_ci __le32 hw_alt_next; 32362306a36Sopenharmony_ci __le32 hw_token; 32462306a36Sopenharmony_ci __le32 hw_buf[5]; 32562306a36Sopenharmony_ci __le32 hw_buf_hi[5]; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* the rest is HCD-private */ 32862306a36Sopenharmony_ci dma_addr_t qh_dma; /* address of qh */ 32962306a36Sopenharmony_ci union ehci_shadow qh_next; /* ptr to qh; or periodic */ 33062306a36Sopenharmony_ci struct list_head qtd_list; /* sw qtd list */ 33162306a36Sopenharmony_ci struct ehci_qtd *dummy; 33262306a36Sopenharmony_ci struct ehci_qh *reclaim; /* next to reclaim */ 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci struct oxu_hcd *oxu; 33562306a36Sopenharmony_ci struct kref kref; 33662306a36Sopenharmony_ci unsigned int stamp; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci u8 qh_state; 33962306a36Sopenharmony_ci#define QH_STATE_LINKED 1 /* HC sees this */ 34062306a36Sopenharmony_ci#define QH_STATE_UNLINK 2 /* HC may still see this */ 34162306a36Sopenharmony_ci#define QH_STATE_IDLE 3 /* HC doesn't see this */ 34262306a36Sopenharmony_ci#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on reclaim q */ 34362306a36Sopenharmony_ci#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */ 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* periodic schedule info */ 34662306a36Sopenharmony_ci u8 usecs; /* intr bandwidth */ 34762306a36Sopenharmony_ci u8 gap_uf; /* uframes split/csplit gap */ 34862306a36Sopenharmony_ci u8 c_usecs; /* ... split completion bw */ 34962306a36Sopenharmony_ci u16 tt_usecs; /* tt downstream bandwidth */ 35062306a36Sopenharmony_ci unsigned short period; /* polling interval */ 35162306a36Sopenharmony_ci unsigned short start; /* where polling starts */ 35262306a36Sopenharmony_ci#define NO_FRAME ((unsigned short)~0) /* pick new start */ 35362306a36Sopenharmony_ci struct usb_device *dev; /* access to TT */ 35462306a36Sopenharmony_ci} __aligned(32); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci/* 35762306a36Sopenharmony_ci * Proper OXU210HP structs 35862306a36Sopenharmony_ci */ 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci#define OXU_OTG_CORE_OFFSET 0x00400 36162306a36Sopenharmony_ci#define OXU_OTG_CAP_OFFSET (OXU_OTG_CORE_OFFSET + 0x100) 36262306a36Sopenharmony_ci#define OXU_SPH_CORE_OFFSET 0x00800 36362306a36Sopenharmony_ci#define OXU_SPH_CAP_OFFSET (OXU_SPH_CORE_OFFSET + 0x100) 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci#define OXU_OTG_MEM 0xE000 36662306a36Sopenharmony_ci#define OXU_SPH_MEM 0x16000 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci/* Only how many elements & element structure are specifies here. */ 36962306a36Sopenharmony_ci/* 2 host controllers are enabled - total size <= 28 kbytes */ 37062306a36Sopenharmony_ci#define DEFAULT_I_TDPS 1024 37162306a36Sopenharmony_ci#define QHEAD_NUM 16 37262306a36Sopenharmony_ci#define QTD_NUM 32 37362306a36Sopenharmony_ci#define SITD_NUM 8 37462306a36Sopenharmony_ci#define MURB_NUM 8 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci#define BUFFER_NUM 8 37762306a36Sopenharmony_ci#define BUFFER_SIZE 512 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistruct oxu_info { 38062306a36Sopenharmony_ci struct usb_hcd *hcd[2]; 38162306a36Sopenharmony_ci}; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistruct oxu_buf { 38462306a36Sopenharmony_ci u8 buffer[BUFFER_SIZE]; 38562306a36Sopenharmony_ci} __aligned(BUFFER_SIZE); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistruct oxu_onchip_mem { 38862306a36Sopenharmony_ci struct oxu_buf db_pool[BUFFER_NUM]; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci u32 frame_list[DEFAULT_I_TDPS]; 39162306a36Sopenharmony_ci struct ehci_qh qh_pool[QHEAD_NUM]; 39262306a36Sopenharmony_ci struct ehci_qtd qtd_pool[QTD_NUM]; 39362306a36Sopenharmony_ci} __aligned(4 << 10); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */ 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistruct oxu_murb { 39862306a36Sopenharmony_ci struct urb urb; 39962306a36Sopenharmony_ci struct urb *main; 40062306a36Sopenharmony_ci u8 last; 40162306a36Sopenharmony_ci}; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistruct oxu_hcd { /* one per controller */ 40462306a36Sopenharmony_ci unsigned int is_otg:1; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci u8 qh_used[QHEAD_NUM]; 40762306a36Sopenharmony_ci u8 qtd_used[QTD_NUM]; 40862306a36Sopenharmony_ci u8 db_used[BUFFER_NUM]; 40962306a36Sopenharmony_ci u8 murb_used[MURB_NUM]; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci struct oxu_onchip_mem __iomem *mem; 41262306a36Sopenharmony_ci spinlock_t mem_lock; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci struct timer_list urb_timer; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci struct ehci_caps __iomem *caps; 41762306a36Sopenharmony_ci struct ehci_regs __iomem *regs; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci u32 hcs_params; /* cached register copy */ 42062306a36Sopenharmony_ci spinlock_t lock; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* async schedule support */ 42362306a36Sopenharmony_ci struct ehci_qh *async; 42462306a36Sopenharmony_ci struct ehci_qh *reclaim; 42562306a36Sopenharmony_ci unsigned int reclaim_ready:1; 42662306a36Sopenharmony_ci unsigned int scanning:1; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* periodic schedule support */ 42962306a36Sopenharmony_ci unsigned int periodic_size; 43062306a36Sopenharmony_ci __le32 *periodic; /* hw periodic table */ 43162306a36Sopenharmony_ci dma_addr_t periodic_dma; 43262306a36Sopenharmony_ci unsigned int i_thresh; /* uframes HC might cache */ 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci union ehci_shadow *pshadow; /* mirror hw periodic table */ 43562306a36Sopenharmony_ci int next_uframe; /* scan periodic, start here */ 43662306a36Sopenharmony_ci unsigned int periodic_sched; /* periodic activity count */ 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* per root hub port */ 43962306a36Sopenharmony_ci unsigned long reset_done[EHCI_MAX_ROOT_PORTS]; 44062306a36Sopenharmony_ci /* bit vectors (one bit per port) */ 44162306a36Sopenharmony_ci unsigned long bus_suspended; /* which ports were 44262306a36Sopenharmony_ci * already suspended at the 44362306a36Sopenharmony_ci * start of a bus suspend 44462306a36Sopenharmony_ci */ 44562306a36Sopenharmony_ci unsigned long companion_ports;/* which ports are dedicated 44662306a36Sopenharmony_ci * to the companion controller 44762306a36Sopenharmony_ci */ 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci struct timer_list watchdog; 45062306a36Sopenharmony_ci unsigned long actions; 45162306a36Sopenharmony_ci unsigned int stamp; 45262306a36Sopenharmony_ci unsigned long next_statechange; 45362306a36Sopenharmony_ci u32 command; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* SILICON QUIRKS */ 45662306a36Sopenharmony_ci struct list_head urb_list; /* this is the head to urb 45762306a36Sopenharmony_ci * queue that didn't get enough 45862306a36Sopenharmony_ci * resources 45962306a36Sopenharmony_ci */ 46062306a36Sopenharmony_ci struct oxu_murb *murb_pool; /* murb per split big urb */ 46162306a36Sopenharmony_ci unsigned int urb_len; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci u8 sbrn; /* packed release number */ 46462306a36Sopenharmony_ci}; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci#define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */ 46762306a36Sopenharmony_ci#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ 46862306a36Sopenharmony_ci#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ 46962306a36Sopenharmony_ci#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */ 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cienum ehci_timer_action { 47262306a36Sopenharmony_ci TIMER_IO_WATCHDOG, 47362306a36Sopenharmony_ci TIMER_IAA_WATCHDOG, 47462306a36Sopenharmony_ci TIMER_ASYNC_SHRINK, 47562306a36Sopenharmony_ci TIMER_ASYNC_OFF, 47662306a36Sopenharmony_ci}; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci/* 47962306a36Sopenharmony_ci * Main defines 48062306a36Sopenharmony_ci */ 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci#define oxu_dbg(oxu, fmt, args...) \ 48362306a36Sopenharmony_ci dev_dbg(oxu_to_hcd(oxu)->self.controller , fmt , ## args) 48462306a36Sopenharmony_ci#define oxu_err(oxu, fmt, args...) \ 48562306a36Sopenharmony_ci dev_err(oxu_to_hcd(oxu)->self.controller , fmt , ## args) 48662306a36Sopenharmony_ci#define oxu_info(oxu, fmt, args...) \ 48762306a36Sopenharmony_ci dev_info(oxu_to_hcd(oxu)->self.controller , fmt , ## args) 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci#ifdef CONFIG_DYNAMIC_DEBUG 49062306a36Sopenharmony_ci#define DEBUG 49162306a36Sopenharmony_ci#endif 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cistatic inline struct usb_hcd *oxu_to_hcd(struct oxu_hcd *oxu) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci return container_of((void *) oxu, struct usb_hcd, hcd_priv); 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic inline struct oxu_hcd *hcd_to_oxu(struct usb_hcd *hcd) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci return (struct oxu_hcd *) (hcd->hcd_priv); 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci/* 50462306a36Sopenharmony_ci * Debug stuff 50562306a36Sopenharmony_ci */ 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci#undef OXU_URB_TRACE 50862306a36Sopenharmony_ci#undef OXU_VERBOSE_DEBUG 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci#ifdef OXU_VERBOSE_DEBUG 51162306a36Sopenharmony_ci#define oxu_vdbg oxu_dbg 51262306a36Sopenharmony_ci#else 51362306a36Sopenharmony_ci#define oxu_vdbg(oxu, fmt, args...) /* Nop */ 51462306a36Sopenharmony_ci#endif 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci#ifdef DEBUG 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cistatic int __attribute__((__unused__)) 51962306a36Sopenharmony_cidbg_status_buf(char *buf, unsigned len, const char *label, u32 status) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci return scnprintf(buf, len, "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", 52262306a36Sopenharmony_ci label, label[0] ? " " : "", status, 52362306a36Sopenharmony_ci (status & STS_ASS) ? " Async" : "", 52462306a36Sopenharmony_ci (status & STS_PSS) ? " Periodic" : "", 52562306a36Sopenharmony_ci (status & STS_RECL) ? " Recl" : "", 52662306a36Sopenharmony_ci (status & STS_HALT) ? " Halt" : "", 52762306a36Sopenharmony_ci (status & STS_IAA) ? " IAA" : "", 52862306a36Sopenharmony_ci (status & STS_FATAL) ? " FATAL" : "", 52962306a36Sopenharmony_ci (status & STS_FLR) ? " FLR" : "", 53062306a36Sopenharmony_ci (status & STS_PCD) ? " PCD" : "", 53162306a36Sopenharmony_ci (status & STS_ERR) ? " ERR" : "", 53262306a36Sopenharmony_ci (status & STS_INT) ? " INT" : "" 53362306a36Sopenharmony_ci ); 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic int __attribute__((__unused__)) 53762306a36Sopenharmony_cidbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci return scnprintf(buf, len, "%s%sintrenable %02x%s%s%s%s%s%s", 54062306a36Sopenharmony_ci label, label[0] ? " " : "", enable, 54162306a36Sopenharmony_ci (enable & STS_IAA) ? " IAA" : "", 54262306a36Sopenharmony_ci (enable & STS_FATAL) ? " FATAL" : "", 54362306a36Sopenharmony_ci (enable & STS_FLR) ? " FLR" : "", 54462306a36Sopenharmony_ci (enable & STS_PCD) ? " PCD" : "", 54562306a36Sopenharmony_ci (enable & STS_ERR) ? " ERR" : "", 54662306a36Sopenharmony_ci (enable & STS_INT) ? " INT" : "" 54762306a36Sopenharmony_ci ); 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic const char *const fls_strings[] = 55162306a36Sopenharmony_ci { "1024", "512", "256", "??" }; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic int dbg_command_buf(char *buf, unsigned len, 55462306a36Sopenharmony_ci const char *label, u32 command) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci return scnprintf(buf, len, 55762306a36Sopenharmony_ci "%s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s", 55862306a36Sopenharmony_ci label, label[0] ? " " : "", command, 55962306a36Sopenharmony_ci (command & CMD_PARK) ? "park" : "(park)", 56062306a36Sopenharmony_ci CMD_PARK_CNT(command), 56162306a36Sopenharmony_ci (command >> 16) & 0x3f, 56262306a36Sopenharmony_ci (command & CMD_LRESET) ? " LReset" : "", 56362306a36Sopenharmony_ci (command & CMD_IAAD) ? " IAAD" : "", 56462306a36Sopenharmony_ci (command & CMD_ASE) ? " Async" : "", 56562306a36Sopenharmony_ci (command & CMD_PSE) ? " Periodic" : "", 56662306a36Sopenharmony_ci fls_strings[(command >> 2) & 0x3], 56762306a36Sopenharmony_ci (command & CMD_RESET) ? " Reset" : "", 56862306a36Sopenharmony_ci (command & CMD_RUN) ? "RUN" : "HALT" 56962306a36Sopenharmony_ci ); 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic int dbg_port_buf(char *buf, unsigned len, const char *label, 57362306a36Sopenharmony_ci int port, u32 status) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci char *sig; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci /* signaling state */ 57862306a36Sopenharmony_ci switch (status & (3 << 10)) { 57962306a36Sopenharmony_ci case 0 << 10: 58062306a36Sopenharmony_ci sig = "se0"; 58162306a36Sopenharmony_ci break; 58262306a36Sopenharmony_ci case 1 << 10: 58362306a36Sopenharmony_ci sig = "k"; /* low speed */ 58462306a36Sopenharmony_ci break; 58562306a36Sopenharmony_ci case 2 << 10: 58662306a36Sopenharmony_ci sig = "j"; 58762306a36Sopenharmony_ci break; 58862306a36Sopenharmony_ci default: 58962306a36Sopenharmony_ci sig = "?"; 59062306a36Sopenharmony_ci break; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci return scnprintf(buf, len, 59462306a36Sopenharmony_ci "%s%sport %d status %06x%s%s sig=%s%s%s%s%s%s%s%s%s%s", 59562306a36Sopenharmony_ci label, label[0] ? " " : "", port, status, 59662306a36Sopenharmony_ci (status & PORT_POWER) ? " POWER" : "", 59762306a36Sopenharmony_ci (status & PORT_OWNER) ? " OWNER" : "", 59862306a36Sopenharmony_ci sig, 59962306a36Sopenharmony_ci (status & PORT_RESET) ? " RESET" : "", 60062306a36Sopenharmony_ci (status & PORT_SUSPEND) ? " SUSPEND" : "", 60162306a36Sopenharmony_ci (status & PORT_RESUME) ? " RESUME" : "", 60262306a36Sopenharmony_ci (status & PORT_OCC) ? " OCC" : "", 60362306a36Sopenharmony_ci (status & PORT_OC) ? " OC" : "", 60462306a36Sopenharmony_ci (status & PORT_PEC) ? " PEC" : "", 60562306a36Sopenharmony_ci (status & PORT_PE) ? " PE" : "", 60662306a36Sopenharmony_ci (status & PORT_CSC) ? " CSC" : "", 60762306a36Sopenharmony_ci (status & PORT_CONNECT) ? " CONNECT" : "" 60862306a36Sopenharmony_ci ); 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci#else 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic inline int __attribute__((__unused__)) 61462306a36Sopenharmony_cidbg_status_buf(char *buf, unsigned len, const char *label, u32 status) 61562306a36Sopenharmony_ci{ return 0; } 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic inline int __attribute__((__unused__)) 61862306a36Sopenharmony_cidbg_command_buf(char *buf, unsigned len, const char *label, u32 command) 61962306a36Sopenharmony_ci{ return 0; } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic inline int __attribute__((__unused__)) 62262306a36Sopenharmony_cidbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable) 62362306a36Sopenharmony_ci{ return 0; } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic inline int __attribute__((__unused__)) 62662306a36Sopenharmony_cidbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status) 62762306a36Sopenharmony_ci{ return 0; } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci#endif /* DEBUG */ 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci/* functions have the "wrong" filename when they're output... */ 63262306a36Sopenharmony_ci#define dbg_status(oxu, label, status) { \ 63362306a36Sopenharmony_ci char _buf[80]; \ 63462306a36Sopenharmony_ci dbg_status_buf(_buf, sizeof _buf, label, status); \ 63562306a36Sopenharmony_ci oxu_dbg(oxu, "%s\n", _buf); \ 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci#define dbg_cmd(oxu, label, command) { \ 63962306a36Sopenharmony_ci char _buf[80]; \ 64062306a36Sopenharmony_ci dbg_command_buf(_buf, sizeof _buf, label, command); \ 64162306a36Sopenharmony_ci oxu_dbg(oxu, "%s\n", _buf); \ 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci#define dbg_port(oxu, label, port, status) { \ 64562306a36Sopenharmony_ci char _buf[80]; \ 64662306a36Sopenharmony_ci dbg_port_buf(_buf, sizeof _buf, label, port, status); \ 64762306a36Sopenharmony_ci oxu_dbg(oxu, "%s\n", _buf); \ 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci/* 65162306a36Sopenharmony_ci * Module parameters 65262306a36Sopenharmony_ci */ 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci/* Initial IRQ latency: faster than hw default */ 65562306a36Sopenharmony_cistatic int log2_irq_thresh; /* 0 to 6 */ 65662306a36Sopenharmony_cimodule_param(log2_irq_thresh, int, S_IRUGO); 65762306a36Sopenharmony_ciMODULE_PARM_DESC(log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci/* Initial park setting: slower than hw default */ 66062306a36Sopenharmony_cistatic unsigned park; 66162306a36Sopenharmony_cimodule_param(park, uint, S_IRUGO); 66262306a36Sopenharmony_ciMODULE_PARM_DESC(park, "park setting; 1-3 back-to-back async packets"); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci/* For flakey hardware, ignore overcurrent indicators */ 66562306a36Sopenharmony_cistatic bool ignore_oc; 66662306a36Sopenharmony_cimodule_param(ignore_oc, bool, S_IRUGO); 66762306a36Sopenharmony_ciMODULE_PARM_DESC(ignore_oc, "ignore bogus hardware overcurrent indications"); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cistatic void ehci_work(struct oxu_hcd *oxu); 67162306a36Sopenharmony_cistatic int oxu_hub_control(struct usb_hcd *hcd, 67262306a36Sopenharmony_ci u16 typeReq, u16 wValue, u16 wIndex, 67362306a36Sopenharmony_ci char *buf, u16 wLength); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci/* 67662306a36Sopenharmony_ci * Local functions 67762306a36Sopenharmony_ci */ 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci/* Low level read/write registers functions */ 68062306a36Sopenharmony_cistatic inline u32 oxu_readl(void __iomem *base, u32 reg) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci return readl(base + reg); 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_cistatic inline void oxu_writel(void __iomem *base, u32 reg, u32 val) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci writel(val, base + reg); 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cistatic inline void timer_action_done(struct oxu_hcd *oxu, 69162306a36Sopenharmony_ci enum ehci_timer_action action) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci clear_bit(action, &oxu->actions); 69462306a36Sopenharmony_ci} 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_cistatic inline void timer_action(struct oxu_hcd *oxu, 69762306a36Sopenharmony_ci enum ehci_timer_action action) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci if (!test_and_set_bit(action, &oxu->actions)) { 70062306a36Sopenharmony_ci unsigned long t; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci switch (action) { 70362306a36Sopenharmony_ci case TIMER_IAA_WATCHDOG: 70462306a36Sopenharmony_ci t = EHCI_IAA_JIFFIES; 70562306a36Sopenharmony_ci break; 70662306a36Sopenharmony_ci case TIMER_IO_WATCHDOG: 70762306a36Sopenharmony_ci t = EHCI_IO_JIFFIES; 70862306a36Sopenharmony_ci break; 70962306a36Sopenharmony_ci case TIMER_ASYNC_OFF: 71062306a36Sopenharmony_ci t = EHCI_ASYNC_JIFFIES; 71162306a36Sopenharmony_ci break; 71262306a36Sopenharmony_ci case TIMER_ASYNC_SHRINK: 71362306a36Sopenharmony_ci default: 71462306a36Sopenharmony_ci t = EHCI_SHRINK_JIFFIES; 71562306a36Sopenharmony_ci break; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci t += jiffies; 71862306a36Sopenharmony_ci /* all timings except IAA watchdog can be overridden. 71962306a36Sopenharmony_ci * async queue SHRINK often precedes IAA. while it's ready 72062306a36Sopenharmony_ci * to go OFF neither can matter, and afterwards the IO 72162306a36Sopenharmony_ci * watchdog stops unless there's still periodic traffic. 72262306a36Sopenharmony_ci */ 72362306a36Sopenharmony_ci if (action != TIMER_IAA_WATCHDOG 72462306a36Sopenharmony_ci && t > oxu->watchdog.expires 72562306a36Sopenharmony_ci && timer_pending(&oxu->watchdog)) 72662306a36Sopenharmony_ci return; 72762306a36Sopenharmony_ci mod_timer(&oxu->watchdog, t); 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci/* 73262306a36Sopenharmony_ci * handshake - spin reading hc until handshake completes or fails 73362306a36Sopenharmony_ci * @ptr: address of hc register to be read 73462306a36Sopenharmony_ci * @mask: bits to look at in result of read 73562306a36Sopenharmony_ci * @done: value of those bits when handshake succeeds 73662306a36Sopenharmony_ci * @usec: timeout in microseconds 73762306a36Sopenharmony_ci * 73862306a36Sopenharmony_ci * Returns negative errno, or zero on success 73962306a36Sopenharmony_ci * 74062306a36Sopenharmony_ci * Success happens when the "mask" bits have the specified value (hardware 74162306a36Sopenharmony_ci * handshake done). There are two failure modes: "usec" have passed (major 74262306a36Sopenharmony_ci * hardware flakeout), or the register reads as all-ones (hardware removed). 74362306a36Sopenharmony_ci * 74462306a36Sopenharmony_ci * That last failure should_only happen in cases like physical cardbus eject 74562306a36Sopenharmony_ci * before driver shutdown. But it also seems to be caused by bugs in cardbus 74662306a36Sopenharmony_ci * bridge shutdown: shutting down the bridge before the devices using it. 74762306a36Sopenharmony_ci */ 74862306a36Sopenharmony_cistatic int handshake(struct oxu_hcd *oxu, void __iomem *ptr, 74962306a36Sopenharmony_ci u32 mask, u32 done, int usec) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci u32 result; 75262306a36Sopenharmony_ci int ret; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci ret = readl_poll_timeout_atomic(ptr, result, 75562306a36Sopenharmony_ci ((result & mask) == done || 75662306a36Sopenharmony_ci result == U32_MAX), 75762306a36Sopenharmony_ci 1, usec); 75862306a36Sopenharmony_ci if (result == U32_MAX) /* card removed */ 75962306a36Sopenharmony_ci return -ENODEV; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci return ret; 76262306a36Sopenharmony_ci} 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci/* Force HC to halt state from unknown (EHCI spec section 2.3) */ 76562306a36Sopenharmony_cistatic int ehci_halt(struct oxu_hcd *oxu) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci u32 temp = readl(&oxu->regs->status); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* disable any irqs left enabled by previous code */ 77062306a36Sopenharmony_ci writel(0, &oxu->regs->intr_enable); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci if ((temp & STS_HALT) != 0) 77362306a36Sopenharmony_ci return 0; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci temp = readl(&oxu->regs->command); 77662306a36Sopenharmony_ci temp &= ~CMD_RUN; 77762306a36Sopenharmony_ci writel(temp, &oxu->regs->command); 77862306a36Sopenharmony_ci return handshake(oxu, &oxu->regs->status, 77962306a36Sopenharmony_ci STS_HALT, STS_HALT, 16 * 125); 78062306a36Sopenharmony_ci} 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci/* Put TDI/ARC silicon into EHCI mode */ 78362306a36Sopenharmony_cistatic void tdi_reset(struct oxu_hcd *oxu) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci u32 __iomem *reg_ptr; 78662306a36Sopenharmony_ci u32 tmp; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci reg_ptr = (u32 __iomem *)(((u8 __iomem *)oxu->regs) + 0x68); 78962306a36Sopenharmony_ci tmp = readl(reg_ptr); 79062306a36Sopenharmony_ci tmp |= 0x3; 79162306a36Sopenharmony_ci writel(tmp, reg_ptr); 79262306a36Sopenharmony_ci} 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci/* Reset a non-running (STS_HALT == 1) controller */ 79562306a36Sopenharmony_cistatic int ehci_reset(struct oxu_hcd *oxu) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci int retval; 79862306a36Sopenharmony_ci u32 command = readl(&oxu->regs->command); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci command |= CMD_RESET; 80162306a36Sopenharmony_ci dbg_cmd(oxu, "reset", command); 80262306a36Sopenharmony_ci writel(command, &oxu->regs->command); 80362306a36Sopenharmony_ci oxu_to_hcd(oxu)->state = HC_STATE_HALT; 80462306a36Sopenharmony_ci oxu->next_statechange = jiffies; 80562306a36Sopenharmony_ci retval = handshake(oxu, &oxu->regs->command, 80662306a36Sopenharmony_ci CMD_RESET, 0, 250 * 1000); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (retval) 80962306a36Sopenharmony_ci return retval; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci tdi_reset(oxu); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci return retval; 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci/* Idle the controller (from running) */ 81762306a36Sopenharmony_cistatic void ehci_quiesce(struct oxu_hcd *oxu) 81862306a36Sopenharmony_ci{ 81962306a36Sopenharmony_ci u32 temp; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci#ifdef DEBUG 82262306a36Sopenharmony_ci BUG_ON(!HC_IS_RUNNING(oxu_to_hcd(oxu)->state)); 82362306a36Sopenharmony_ci#endif 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci /* wait for any schedule enables/disables to take effect */ 82662306a36Sopenharmony_ci temp = readl(&oxu->regs->command) << 10; 82762306a36Sopenharmony_ci temp &= STS_ASS | STS_PSS; 82862306a36Sopenharmony_ci if (handshake(oxu, &oxu->regs->status, STS_ASS | STS_PSS, 82962306a36Sopenharmony_ci temp, 16 * 125) != 0) { 83062306a36Sopenharmony_ci oxu_to_hcd(oxu)->state = HC_STATE_HALT; 83162306a36Sopenharmony_ci return; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci /* then disable anything that's still active */ 83562306a36Sopenharmony_ci temp = readl(&oxu->regs->command); 83662306a36Sopenharmony_ci temp &= ~(CMD_ASE | CMD_IAAD | CMD_PSE); 83762306a36Sopenharmony_ci writel(temp, &oxu->regs->command); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci /* hardware can take 16 microframes to turn off ... */ 84062306a36Sopenharmony_ci if (handshake(oxu, &oxu->regs->status, STS_ASS | STS_PSS, 84162306a36Sopenharmony_ci 0, 16 * 125) != 0) { 84262306a36Sopenharmony_ci oxu_to_hcd(oxu)->state = HC_STATE_HALT; 84362306a36Sopenharmony_ci return; 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci} 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_cistatic int check_reset_complete(struct oxu_hcd *oxu, int index, 84862306a36Sopenharmony_ci u32 __iomem *status_reg, int port_status) 84962306a36Sopenharmony_ci{ 85062306a36Sopenharmony_ci if (!(port_status & PORT_CONNECT)) { 85162306a36Sopenharmony_ci oxu->reset_done[index] = 0; 85262306a36Sopenharmony_ci return port_status; 85362306a36Sopenharmony_ci } 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci /* if reset finished and it's still not enabled -- handoff */ 85662306a36Sopenharmony_ci if (!(port_status & PORT_PE)) { 85762306a36Sopenharmony_ci oxu_dbg(oxu, "Failed to enable port %d on root hub TT\n", 85862306a36Sopenharmony_ci index+1); 85962306a36Sopenharmony_ci return port_status; 86062306a36Sopenharmony_ci } else 86162306a36Sopenharmony_ci oxu_dbg(oxu, "port %d high speed\n", index + 1); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci return port_status; 86462306a36Sopenharmony_ci} 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_cistatic void ehci_hub_descriptor(struct oxu_hcd *oxu, 86762306a36Sopenharmony_ci struct usb_hub_descriptor *desc) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci int ports = HCS_N_PORTS(oxu->hcs_params); 87062306a36Sopenharmony_ci u16 temp; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci desc->bDescriptorType = USB_DT_HUB; 87362306a36Sopenharmony_ci desc->bPwrOn2PwrGood = 10; /* oxu 1.0, 2.3.9 says 20ms max */ 87462306a36Sopenharmony_ci desc->bHubContrCurrent = 0; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci desc->bNbrPorts = ports; 87762306a36Sopenharmony_ci temp = 1 + (ports / 8); 87862306a36Sopenharmony_ci desc->bDescLength = 7 + 2 * temp; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci /* ports removable, and usb 1.0 legacy PortPwrCtrlMask */ 88162306a36Sopenharmony_ci memset(&desc->u.hs.DeviceRemovable[0], 0, temp); 88262306a36Sopenharmony_ci memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci temp = HUB_CHAR_INDV_PORT_OCPM; /* per-port overcurrent reporting */ 88562306a36Sopenharmony_ci if (HCS_PPC(oxu->hcs_params)) 88662306a36Sopenharmony_ci temp |= HUB_CHAR_INDV_PORT_LPSM; /* per-port power control */ 88762306a36Sopenharmony_ci else 88862306a36Sopenharmony_ci temp |= HUB_CHAR_NO_LPSM; /* no power switching */ 88962306a36Sopenharmony_ci desc->wHubCharacteristics = (__force __u16)cpu_to_le16(temp); 89062306a36Sopenharmony_ci} 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci/* Allocate an OXU210HP on-chip memory data buffer 89462306a36Sopenharmony_ci * 89562306a36Sopenharmony_ci * An on-chip memory data buffer is required for each OXU210HP USB transfer. 89662306a36Sopenharmony_ci * Each transfer descriptor has one or more on-chip memory data buffers. 89762306a36Sopenharmony_ci * 89862306a36Sopenharmony_ci * Data buffers are allocated from a fix sized pool of data blocks. 89962306a36Sopenharmony_ci * To minimise fragmentation and give reasonable memory utlisation, 90062306a36Sopenharmony_ci * data buffers are allocated with sizes the power of 2 multiples of 90162306a36Sopenharmony_ci * the block size, starting on an address a multiple of the allocated size. 90262306a36Sopenharmony_ci * 90362306a36Sopenharmony_ci * FIXME: callers of this function require a buffer to be allocated for 90462306a36Sopenharmony_ci * len=0. This is a waste of on-chip memory and should be fix. Then this 90562306a36Sopenharmony_ci * function should be changed to not allocate a buffer for len=0. 90662306a36Sopenharmony_ci */ 90762306a36Sopenharmony_cistatic int oxu_buf_alloc(struct oxu_hcd *oxu, struct ehci_qtd *qtd, int len) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci int n_blocks; /* minium blocks needed to hold len */ 91062306a36Sopenharmony_ci int a_blocks; /* blocks allocated */ 91162306a36Sopenharmony_ci int i, j; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci /* Don't allocte bigger than supported */ 91462306a36Sopenharmony_ci if (len > BUFFER_SIZE * BUFFER_NUM) { 91562306a36Sopenharmony_ci oxu_err(oxu, "buffer too big (%d)\n", len); 91662306a36Sopenharmony_ci return -ENOMEM; 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci spin_lock(&oxu->mem_lock); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci /* Number of blocks needed to hold len */ 92262306a36Sopenharmony_ci n_blocks = (len + BUFFER_SIZE - 1) / BUFFER_SIZE; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci /* Round the number of blocks up to the power of 2 */ 92562306a36Sopenharmony_ci for (a_blocks = 1; a_blocks < n_blocks; a_blocks <<= 1) 92662306a36Sopenharmony_ci ; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci /* Find a suitable available data buffer */ 92962306a36Sopenharmony_ci for (i = 0; i < BUFFER_NUM; 93062306a36Sopenharmony_ci i += max(a_blocks, (int)oxu->db_used[i])) { 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci /* Check all the required blocks are available */ 93362306a36Sopenharmony_ci for (j = 0; j < a_blocks; j++) 93462306a36Sopenharmony_ci if (oxu->db_used[i + j]) 93562306a36Sopenharmony_ci break; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci if (j != a_blocks) 93862306a36Sopenharmony_ci continue; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci /* Allocate blocks found! */ 94162306a36Sopenharmony_ci qtd->buffer = (void *) &oxu->mem->db_pool[i]; 94262306a36Sopenharmony_ci qtd->buffer_dma = virt_to_phys(qtd->buffer); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci qtd->qtd_buffer_len = BUFFER_SIZE * a_blocks; 94562306a36Sopenharmony_ci oxu->db_used[i] = a_blocks; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci spin_unlock(&oxu->mem_lock); 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci return 0; 95062306a36Sopenharmony_ci } 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci /* Failed */ 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci spin_unlock(&oxu->mem_lock); 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci return -ENOMEM; 95762306a36Sopenharmony_ci} 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_cistatic void oxu_buf_free(struct oxu_hcd *oxu, struct ehci_qtd *qtd) 96062306a36Sopenharmony_ci{ 96162306a36Sopenharmony_ci int index; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci spin_lock(&oxu->mem_lock); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci index = (qtd->buffer - (void *) &oxu->mem->db_pool[0]) 96662306a36Sopenharmony_ci / BUFFER_SIZE; 96762306a36Sopenharmony_ci oxu->db_used[index] = 0; 96862306a36Sopenharmony_ci qtd->qtd_buffer_len = 0; 96962306a36Sopenharmony_ci qtd->buffer_dma = 0; 97062306a36Sopenharmony_ci qtd->buffer = NULL; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci spin_unlock(&oxu->mem_lock); 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic inline void ehci_qtd_init(struct ehci_qtd *qtd, dma_addr_t dma) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci memset(qtd, 0, sizeof *qtd); 97862306a36Sopenharmony_ci qtd->qtd_dma = dma; 97962306a36Sopenharmony_ci qtd->hw_token = cpu_to_le32(QTD_STS_HALT); 98062306a36Sopenharmony_ci qtd->hw_next = EHCI_LIST_END; 98162306a36Sopenharmony_ci qtd->hw_alt_next = EHCI_LIST_END; 98262306a36Sopenharmony_ci INIT_LIST_HEAD(&qtd->qtd_list); 98362306a36Sopenharmony_ci} 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_cistatic inline void oxu_qtd_free(struct oxu_hcd *oxu, struct ehci_qtd *qtd) 98662306a36Sopenharmony_ci{ 98762306a36Sopenharmony_ci int index; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci if (qtd->buffer) 99062306a36Sopenharmony_ci oxu_buf_free(oxu, qtd); 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci spin_lock(&oxu->mem_lock); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci index = qtd - &oxu->mem->qtd_pool[0]; 99562306a36Sopenharmony_ci oxu->qtd_used[index] = 0; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci spin_unlock(&oxu->mem_lock); 99862306a36Sopenharmony_ci} 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_cistatic struct ehci_qtd *ehci_qtd_alloc(struct oxu_hcd *oxu) 100162306a36Sopenharmony_ci{ 100262306a36Sopenharmony_ci int i; 100362306a36Sopenharmony_ci struct ehci_qtd *qtd = NULL; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci spin_lock(&oxu->mem_lock); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci for (i = 0; i < QTD_NUM; i++) 100862306a36Sopenharmony_ci if (!oxu->qtd_used[i]) 100962306a36Sopenharmony_ci break; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci if (i < QTD_NUM) { 101262306a36Sopenharmony_ci qtd = (struct ehci_qtd *) &oxu->mem->qtd_pool[i]; 101362306a36Sopenharmony_ci memset(qtd, 0, sizeof *qtd); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci qtd->hw_token = cpu_to_le32(QTD_STS_HALT); 101662306a36Sopenharmony_ci qtd->hw_next = EHCI_LIST_END; 101762306a36Sopenharmony_ci qtd->hw_alt_next = EHCI_LIST_END; 101862306a36Sopenharmony_ci INIT_LIST_HEAD(&qtd->qtd_list); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci qtd->qtd_dma = virt_to_phys(qtd); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci oxu->qtd_used[i] = 1; 102362306a36Sopenharmony_ci } 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci spin_unlock(&oxu->mem_lock); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci return qtd; 102862306a36Sopenharmony_ci} 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_cistatic void oxu_qh_free(struct oxu_hcd *oxu, struct ehci_qh *qh) 103162306a36Sopenharmony_ci{ 103262306a36Sopenharmony_ci int index; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci spin_lock(&oxu->mem_lock); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci index = qh - &oxu->mem->qh_pool[0]; 103762306a36Sopenharmony_ci oxu->qh_used[index] = 0; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci spin_unlock(&oxu->mem_lock); 104062306a36Sopenharmony_ci} 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_cistatic void qh_destroy(struct kref *kref) 104362306a36Sopenharmony_ci{ 104462306a36Sopenharmony_ci struct ehci_qh *qh = container_of(kref, struct ehci_qh, kref); 104562306a36Sopenharmony_ci struct oxu_hcd *oxu = qh->oxu; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci /* clean qtds first, and know this is not linked */ 104862306a36Sopenharmony_ci if (!list_empty(&qh->qtd_list) || qh->qh_next.ptr) { 104962306a36Sopenharmony_ci oxu_dbg(oxu, "unused qh not empty!\n"); 105062306a36Sopenharmony_ci BUG(); 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ci if (qh->dummy) 105362306a36Sopenharmony_ci oxu_qtd_free(oxu, qh->dummy); 105462306a36Sopenharmony_ci oxu_qh_free(oxu, qh); 105562306a36Sopenharmony_ci} 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_cistatic struct ehci_qh *oxu_qh_alloc(struct oxu_hcd *oxu) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci int i; 106062306a36Sopenharmony_ci struct ehci_qh *qh = NULL; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci spin_lock(&oxu->mem_lock); 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci for (i = 0; i < QHEAD_NUM; i++) 106562306a36Sopenharmony_ci if (!oxu->qh_used[i]) 106662306a36Sopenharmony_ci break; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci if (i < QHEAD_NUM) { 106962306a36Sopenharmony_ci qh = (struct ehci_qh *) &oxu->mem->qh_pool[i]; 107062306a36Sopenharmony_ci memset(qh, 0, sizeof *qh); 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci kref_init(&qh->kref); 107362306a36Sopenharmony_ci qh->oxu = oxu; 107462306a36Sopenharmony_ci qh->qh_dma = virt_to_phys(qh); 107562306a36Sopenharmony_ci INIT_LIST_HEAD(&qh->qtd_list); 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci /* dummy td enables safe urb queuing */ 107862306a36Sopenharmony_ci qh->dummy = ehci_qtd_alloc(oxu); 107962306a36Sopenharmony_ci if (qh->dummy == NULL) { 108062306a36Sopenharmony_ci oxu_dbg(oxu, "no dummy td\n"); 108162306a36Sopenharmony_ci oxu->qh_used[i] = 0; 108262306a36Sopenharmony_ci qh = NULL; 108362306a36Sopenharmony_ci goto unlock; 108462306a36Sopenharmony_ci } 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci oxu->qh_used[i] = 1; 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ciunlock: 108962306a36Sopenharmony_ci spin_unlock(&oxu->mem_lock); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci return qh; 109262306a36Sopenharmony_ci} 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci/* to share a qh (cpu threads, or hc) */ 109562306a36Sopenharmony_cistatic inline struct ehci_qh *qh_get(struct ehci_qh *qh) 109662306a36Sopenharmony_ci{ 109762306a36Sopenharmony_ci kref_get(&qh->kref); 109862306a36Sopenharmony_ci return qh; 109962306a36Sopenharmony_ci} 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_cistatic inline void qh_put(struct ehci_qh *qh) 110262306a36Sopenharmony_ci{ 110362306a36Sopenharmony_ci kref_put(&qh->kref, qh_destroy); 110462306a36Sopenharmony_ci} 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_cistatic void oxu_murb_free(struct oxu_hcd *oxu, struct oxu_murb *murb) 110762306a36Sopenharmony_ci{ 110862306a36Sopenharmony_ci int index; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci spin_lock(&oxu->mem_lock); 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci index = murb - &oxu->murb_pool[0]; 111362306a36Sopenharmony_ci oxu->murb_used[index] = 0; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci spin_unlock(&oxu->mem_lock); 111662306a36Sopenharmony_ci} 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_cistatic struct oxu_murb *oxu_murb_alloc(struct oxu_hcd *oxu) 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci{ 112162306a36Sopenharmony_ci int i; 112262306a36Sopenharmony_ci struct oxu_murb *murb = NULL; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci spin_lock(&oxu->mem_lock); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci for (i = 0; i < MURB_NUM; i++) 112762306a36Sopenharmony_ci if (!oxu->murb_used[i]) 112862306a36Sopenharmony_ci break; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci if (i < MURB_NUM) { 113162306a36Sopenharmony_ci murb = &(oxu->murb_pool)[i]; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci oxu->murb_used[i] = 1; 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci spin_unlock(&oxu->mem_lock); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci return murb; 113962306a36Sopenharmony_ci} 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci/* The queue heads and transfer descriptors are managed from pools tied 114262306a36Sopenharmony_ci * to each of the "per device" structures. 114362306a36Sopenharmony_ci * This is the initialisation and cleanup code. 114462306a36Sopenharmony_ci */ 114562306a36Sopenharmony_cistatic void ehci_mem_cleanup(struct oxu_hcd *oxu) 114662306a36Sopenharmony_ci{ 114762306a36Sopenharmony_ci kfree(oxu->murb_pool); 114862306a36Sopenharmony_ci oxu->murb_pool = NULL; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci if (oxu->async) 115162306a36Sopenharmony_ci qh_put(oxu->async); 115262306a36Sopenharmony_ci oxu->async = NULL; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci del_timer(&oxu->urb_timer); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci oxu->periodic = NULL; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci /* shadow periodic table */ 115962306a36Sopenharmony_ci kfree(oxu->pshadow); 116062306a36Sopenharmony_ci oxu->pshadow = NULL; 116162306a36Sopenharmony_ci} 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci/* Remember to add cleanup code (above) if you add anything here. 116462306a36Sopenharmony_ci */ 116562306a36Sopenharmony_cistatic int ehci_mem_init(struct oxu_hcd *oxu, gfp_t flags) 116662306a36Sopenharmony_ci{ 116762306a36Sopenharmony_ci int i; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci for (i = 0; i < oxu->periodic_size; i++) 117062306a36Sopenharmony_ci oxu->mem->frame_list[i] = EHCI_LIST_END; 117162306a36Sopenharmony_ci for (i = 0; i < QHEAD_NUM; i++) 117262306a36Sopenharmony_ci oxu->qh_used[i] = 0; 117362306a36Sopenharmony_ci for (i = 0; i < QTD_NUM; i++) 117462306a36Sopenharmony_ci oxu->qtd_used[i] = 0; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci oxu->murb_pool = kcalloc(MURB_NUM, sizeof(struct oxu_murb), flags); 117762306a36Sopenharmony_ci if (!oxu->murb_pool) 117862306a36Sopenharmony_ci goto fail; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci for (i = 0; i < MURB_NUM; i++) 118162306a36Sopenharmony_ci oxu->murb_used[i] = 0; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci oxu->async = oxu_qh_alloc(oxu); 118462306a36Sopenharmony_ci if (!oxu->async) 118562306a36Sopenharmony_ci goto fail; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci oxu->periodic = (__le32 *) &oxu->mem->frame_list; 118862306a36Sopenharmony_ci oxu->periodic_dma = virt_to_phys(oxu->periodic); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci for (i = 0; i < oxu->periodic_size; i++) 119162306a36Sopenharmony_ci oxu->periodic[i] = EHCI_LIST_END; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci /* software shadow of hardware table */ 119462306a36Sopenharmony_ci oxu->pshadow = kcalloc(oxu->periodic_size, sizeof(void *), flags); 119562306a36Sopenharmony_ci if (oxu->pshadow != NULL) 119662306a36Sopenharmony_ci return 0; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_cifail: 119962306a36Sopenharmony_ci oxu_dbg(oxu, "couldn't init memory\n"); 120062306a36Sopenharmony_ci ehci_mem_cleanup(oxu); 120162306a36Sopenharmony_ci return -ENOMEM; 120262306a36Sopenharmony_ci} 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci/* Fill a qtd, returning how much of the buffer we were able to queue up. 120562306a36Sopenharmony_ci */ 120662306a36Sopenharmony_cistatic int qtd_fill(struct ehci_qtd *qtd, dma_addr_t buf, size_t len, 120762306a36Sopenharmony_ci int token, int maxpacket) 120862306a36Sopenharmony_ci{ 120962306a36Sopenharmony_ci int i, count; 121062306a36Sopenharmony_ci u64 addr = buf; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci /* one buffer entry per 4K ... first might be short or unaligned */ 121362306a36Sopenharmony_ci qtd->hw_buf[0] = cpu_to_le32((u32)addr); 121462306a36Sopenharmony_ci qtd->hw_buf_hi[0] = cpu_to_le32((u32)(addr >> 32)); 121562306a36Sopenharmony_ci count = 0x1000 - (buf & 0x0fff); /* rest of that page */ 121662306a36Sopenharmony_ci if (likely(len < count)) /* ... iff needed */ 121762306a36Sopenharmony_ci count = len; 121862306a36Sopenharmony_ci else { 121962306a36Sopenharmony_ci buf += 0x1000; 122062306a36Sopenharmony_ci buf &= ~0x0fff; 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci /* per-qtd limit: from 16K to 20K (best alignment) */ 122362306a36Sopenharmony_ci for (i = 1; count < len && i < 5; i++) { 122462306a36Sopenharmony_ci addr = buf; 122562306a36Sopenharmony_ci qtd->hw_buf[i] = cpu_to_le32((u32)addr); 122662306a36Sopenharmony_ci qtd->hw_buf_hi[i] = cpu_to_le32((u32)(addr >> 32)); 122762306a36Sopenharmony_ci buf += 0x1000; 122862306a36Sopenharmony_ci if ((count + 0x1000) < len) 122962306a36Sopenharmony_ci count += 0x1000; 123062306a36Sopenharmony_ci else 123162306a36Sopenharmony_ci count = len; 123262306a36Sopenharmony_ci } 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci /* short packets may only terminate transfers */ 123562306a36Sopenharmony_ci if (count != len) 123662306a36Sopenharmony_ci count -= (count % maxpacket); 123762306a36Sopenharmony_ci } 123862306a36Sopenharmony_ci qtd->hw_token = cpu_to_le32((count << 16) | token); 123962306a36Sopenharmony_ci qtd->length = count; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci return count; 124262306a36Sopenharmony_ci} 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_cistatic inline void qh_update(struct oxu_hcd *oxu, 124562306a36Sopenharmony_ci struct ehci_qh *qh, struct ehci_qtd *qtd) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci /* writes to an active overlay are unsafe */ 124862306a36Sopenharmony_ci BUG_ON(qh->qh_state != QH_STATE_IDLE); 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci qh->hw_qtd_next = QTD_NEXT(qtd->qtd_dma); 125162306a36Sopenharmony_ci qh->hw_alt_next = EHCI_LIST_END; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci /* Except for control endpoints, we make hardware maintain data 125462306a36Sopenharmony_ci * toggle (like OHCI) ... here (re)initialize the toggle in the QH, 125562306a36Sopenharmony_ci * and set the pseudo-toggle in udev. Only usb_clear_halt() will 125662306a36Sopenharmony_ci * ever clear it. 125762306a36Sopenharmony_ci */ 125862306a36Sopenharmony_ci if (!(qh->hw_info1 & cpu_to_le32(1 << 14))) { 125962306a36Sopenharmony_ci unsigned is_out, epnum; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci is_out = !(qtd->hw_token & cpu_to_le32(1 << 8)); 126262306a36Sopenharmony_ci epnum = (le32_to_cpup(&qh->hw_info1) >> 8) & 0x0f; 126362306a36Sopenharmony_ci if (unlikely(!usb_gettoggle(qh->dev, epnum, is_out))) { 126462306a36Sopenharmony_ci qh->hw_token &= ~cpu_to_le32(QTD_TOGGLE); 126562306a36Sopenharmony_ci usb_settoggle(qh->dev, epnum, is_out, 1); 126662306a36Sopenharmony_ci } 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */ 127062306a36Sopenharmony_ci wmb(); 127162306a36Sopenharmony_ci qh->hw_token &= cpu_to_le32(QTD_TOGGLE | QTD_STS_PING); 127262306a36Sopenharmony_ci} 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci/* If it weren't for a common silicon quirk (writing the dummy into the qh 127562306a36Sopenharmony_ci * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault 127662306a36Sopenharmony_ci * recovery (including urb dequeue) would need software changes to a QH... 127762306a36Sopenharmony_ci */ 127862306a36Sopenharmony_cistatic void qh_refresh(struct oxu_hcd *oxu, struct ehci_qh *qh) 127962306a36Sopenharmony_ci{ 128062306a36Sopenharmony_ci struct ehci_qtd *qtd; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci if (list_empty(&qh->qtd_list)) 128362306a36Sopenharmony_ci qtd = qh->dummy; 128462306a36Sopenharmony_ci else { 128562306a36Sopenharmony_ci qtd = list_entry(qh->qtd_list.next, 128662306a36Sopenharmony_ci struct ehci_qtd, qtd_list); 128762306a36Sopenharmony_ci /* first qtd may already be partially processed */ 128862306a36Sopenharmony_ci if (cpu_to_le32(qtd->qtd_dma) == qh->hw_current) 128962306a36Sopenharmony_ci qtd = NULL; 129062306a36Sopenharmony_ci } 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci if (qtd) 129362306a36Sopenharmony_ci qh_update(oxu, qh, qtd); 129462306a36Sopenharmony_ci} 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_cistatic void qtd_copy_status(struct oxu_hcd *oxu, struct urb *urb, 129762306a36Sopenharmony_ci size_t length, u32 token) 129862306a36Sopenharmony_ci{ 129962306a36Sopenharmony_ci /* count IN/OUT bytes, not SETUP (even short packets) */ 130062306a36Sopenharmony_ci if (likely(QTD_PID(token) != 2)) 130162306a36Sopenharmony_ci urb->actual_length += length - QTD_LENGTH(token); 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci /* don't modify error codes */ 130462306a36Sopenharmony_ci if (unlikely(urb->status != -EINPROGRESS)) 130562306a36Sopenharmony_ci return; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci /* force cleanup after short read; not always an error */ 130862306a36Sopenharmony_ci if (unlikely(IS_SHORT_READ(token))) 130962306a36Sopenharmony_ci urb->status = -EREMOTEIO; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci /* serious "can't proceed" faults reported by the hardware */ 131262306a36Sopenharmony_ci if (token & QTD_STS_HALT) { 131362306a36Sopenharmony_ci if (token & QTD_STS_BABBLE) { 131462306a36Sopenharmony_ci /* FIXME "must" disable babbling device's port too */ 131562306a36Sopenharmony_ci urb->status = -EOVERFLOW; 131662306a36Sopenharmony_ci } else if (token & QTD_STS_MMF) { 131762306a36Sopenharmony_ci /* fs/ls interrupt xfer missed the complete-split */ 131862306a36Sopenharmony_ci urb->status = -EPROTO; 131962306a36Sopenharmony_ci } else if (token & QTD_STS_DBE) { 132062306a36Sopenharmony_ci urb->status = (QTD_PID(token) == 1) /* IN ? */ 132162306a36Sopenharmony_ci ? -ENOSR /* hc couldn't read data */ 132262306a36Sopenharmony_ci : -ECOMM; /* hc couldn't write data */ 132362306a36Sopenharmony_ci } else if (token & QTD_STS_XACT) { 132462306a36Sopenharmony_ci /* timeout, bad crc, wrong PID, etc; retried */ 132562306a36Sopenharmony_ci if (QTD_CERR(token)) 132662306a36Sopenharmony_ci urb->status = -EPIPE; 132762306a36Sopenharmony_ci else { 132862306a36Sopenharmony_ci oxu_dbg(oxu, "devpath %s ep%d%s 3strikes\n", 132962306a36Sopenharmony_ci urb->dev->devpath, 133062306a36Sopenharmony_ci usb_pipeendpoint(urb->pipe), 133162306a36Sopenharmony_ci usb_pipein(urb->pipe) ? "in" : "out"); 133262306a36Sopenharmony_ci urb->status = -EPROTO; 133362306a36Sopenharmony_ci } 133462306a36Sopenharmony_ci /* CERR nonzero + no errors + halt --> stall */ 133562306a36Sopenharmony_ci } else if (QTD_CERR(token)) 133662306a36Sopenharmony_ci urb->status = -EPIPE; 133762306a36Sopenharmony_ci else /* unknown */ 133862306a36Sopenharmony_ci urb->status = -EPROTO; 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci oxu_vdbg(oxu, "dev%d ep%d%s qtd token %08x --> status %d\n", 134162306a36Sopenharmony_ci usb_pipedevice(urb->pipe), 134262306a36Sopenharmony_ci usb_pipeendpoint(urb->pipe), 134362306a36Sopenharmony_ci usb_pipein(urb->pipe) ? "in" : "out", 134462306a36Sopenharmony_ci token, urb->status); 134562306a36Sopenharmony_ci } 134662306a36Sopenharmony_ci} 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_cistatic void ehci_urb_done(struct oxu_hcd *oxu, struct urb *urb) 134962306a36Sopenharmony_ci__releases(oxu->lock) 135062306a36Sopenharmony_ci__acquires(oxu->lock) 135162306a36Sopenharmony_ci{ 135262306a36Sopenharmony_ci if (likely(urb->hcpriv != NULL)) { 135362306a36Sopenharmony_ci struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci /* S-mask in a QH means it's an interrupt urb */ 135662306a36Sopenharmony_ci if ((qh->hw_info2 & cpu_to_le32(QH_SMASK)) != 0) { 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci /* ... update hc-wide periodic stats (for usbfs) */ 135962306a36Sopenharmony_ci oxu_to_hcd(oxu)->self.bandwidth_int_reqs--; 136062306a36Sopenharmony_ci } 136162306a36Sopenharmony_ci qh_put(qh); 136262306a36Sopenharmony_ci } 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci urb->hcpriv = NULL; 136562306a36Sopenharmony_ci switch (urb->status) { 136662306a36Sopenharmony_ci case -EINPROGRESS: /* success */ 136762306a36Sopenharmony_ci urb->status = 0; 136862306a36Sopenharmony_ci break; 136962306a36Sopenharmony_ci default: /* fault */ 137062306a36Sopenharmony_ci break; 137162306a36Sopenharmony_ci case -EREMOTEIO: /* fault or normal */ 137262306a36Sopenharmony_ci if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) 137362306a36Sopenharmony_ci urb->status = 0; 137462306a36Sopenharmony_ci break; 137562306a36Sopenharmony_ci case -ECONNRESET: /* canceled */ 137662306a36Sopenharmony_ci case -ENOENT: 137762306a36Sopenharmony_ci break; 137862306a36Sopenharmony_ci } 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci#ifdef OXU_URB_TRACE 138162306a36Sopenharmony_ci oxu_dbg(oxu, "%s %s urb %p ep%d%s status %d len %d/%d\n", 138262306a36Sopenharmony_ci __func__, urb->dev->devpath, urb, 138362306a36Sopenharmony_ci usb_pipeendpoint(urb->pipe), 138462306a36Sopenharmony_ci usb_pipein(urb->pipe) ? "in" : "out", 138562306a36Sopenharmony_ci urb->status, 138662306a36Sopenharmony_ci urb->actual_length, urb->transfer_buffer_length); 138762306a36Sopenharmony_ci#endif 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci /* complete() can reenter this HCD */ 139062306a36Sopenharmony_ci spin_unlock(&oxu->lock); 139162306a36Sopenharmony_ci usb_hcd_giveback_urb(oxu_to_hcd(oxu), urb, urb->status); 139262306a36Sopenharmony_ci spin_lock(&oxu->lock); 139362306a36Sopenharmony_ci} 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_cistatic void start_unlink_async(struct oxu_hcd *oxu, struct ehci_qh *qh); 139662306a36Sopenharmony_cistatic void unlink_async(struct oxu_hcd *oxu, struct ehci_qh *qh); 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_cistatic void intr_deschedule(struct oxu_hcd *oxu, struct ehci_qh *qh); 139962306a36Sopenharmony_cistatic int qh_schedule(struct oxu_hcd *oxu, struct ehci_qh *qh); 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci#define HALT_BIT cpu_to_le32(QTD_STS_HALT) 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci/* Process and free completed qtds for a qh, returning URBs to drivers. 140462306a36Sopenharmony_ci * Chases up to qh->hw_current. Returns number of completions called, 140562306a36Sopenharmony_ci * indicating how much "real" work we did. 140662306a36Sopenharmony_ci */ 140762306a36Sopenharmony_cistatic unsigned qh_completions(struct oxu_hcd *oxu, struct ehci_qh *qh) 140862306a36Sopenharmony_ci{ 140962306a36Sopenharmony_ci struct ehci_qtd *last = NULL, *end = qh->dummy; 141062306a36Sopenharmony_ci struct ehci_qtd *qtd, *tmp; 141162306a36Sopenharmony_ci int stopped; 141262306a36Sopenharmony_ci unsigned count = 0; 141362306a36Sopenharmony_ci int do_status = 0; 141462306a36Sopenharmony_ci u8 state; 141562306a36Sopenharmony_ci struct oxu_murb *murb = NULL; 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci if (unlikely(list_empty(&qh->qtd_list))) 141862306a36Sopenharmony_ci return count; 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci /* completions (or tasks on other cpus) must never clobber HALT 142162306a36Sopenharmony_ci * till we've gone through and cleaned everything up, even when 142262306a36Sopenharmony_ci * they add urbs to this qh's queue or mark them for unlinking. 142362306a36Sopenharmony_ci * 142462306a36Sopenharmony_ci * NOTE: unlinking expects to be done in queue order. 142562306a36Sopenharmony_ci */ 142662306a36Sopenharmony_ci state = qh->qh_state; 142762306a36Sopenharmony_ci qh->qh_state = QH_STATE_COMPLETING; 142862306a36Sopenharmony_ci stopped = (state == QH_STATE_IDLE); 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci /* remove de-activated QTDs from front of queue. 143162306a36Sopenharmony_ci * after faults (including short reads), cleanup this urb 143262306a36Sopenharmony_ci * then let the queue advance. 143362306a36Sopenharmony_ci * if queue is stopped, handles unlinks. 143462306a36Sopenharmony_ci */ 143562306a36Sopenharmony_ci list_for_each_entry_safe(qtd, tmp, &qh->qtd_list, qtd_list) { 143662306a36Sopenharmony_ci struct urb *urb; 143762306a36Sopenharmony_ci u32 token = 0; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci urb = qtd->urb; 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci /* Clean up any state from previous QTD ...*/ 144262306a36Sopenharmony_ci if (last) { 144362306a36Sopenharmony_ci if (likely(last->urb != urb)) { 144462306a36Sopenharmony_ci if (last->urb->complete == NULL) { 144562306a36Sopenharmony_ci murb = (struct oxu_murb *) last->urb; 144662306a36Sopenharmony_ci last->urb = murb->main; 144762306a36Sopenharmony_ci if (murb->last) { 144862306a36Sopenharmony_ci ehci_urb_done(oxu, last->urb); 144962306a36Sopenharmony_ci count++; 145062306a36Sopenharmony_ci } 145162306a36Sopenharmony_ci oxu_murb_free(oxu, murb); 145262306a36Sopenharmony_ci } else { 145362306a36Sopenharmony_ci ehci_urb_done(oxu, last->urb); 145462306a36Sopenharmony_ci count++; 145562306a36Sopenharmony_ci } 145662306a36Sopenharmony_ci } 145762306a36Sopenharmony_ci oxu_qtd_free(oxu, last); 145862306a36Sopenharmony_ci last = NULL; 145962306a36Sopenharmony_ci } 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci /* ignore urbs submitted during completions we reported */ 146262306a36Sopenharmony_ci if (qtd == end) 146362306a36Sopenharmony_ci break; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci /* hardware copies qtd out of qh overlay */ 146662306a36Sopenharmony_ci rmb(); 146762306a36Sopenharmony_ci token = le32_to_cpu(qtd->hw_token); 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci /* always clean up qtds the hc de-activated */ 147062306a36Sopenharmony_ci if ((token & QTD_STS_ACTIVE) == 0) { 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci if ((token & QTD_STS_HALT) != 0) { 147362306a36Sopenharmony_ci stopped = 1; 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci /* magic dummy for some short reads; qh won't advance. 147662306a36Sopenharmony_ci * that silicon quirk can kick in with this dummy too. 147762306a36Sopenharmony_ci */ 147862306a36Sopenharmony_ci } else if (IS_SHORT_READ(token) && 147962306a36Sopenharmony_ci !(qtd->hw_alt_next & EHCI_LIST_END)) { 148062306a36Sopenharmony_ci stopped = 1; 148162306a36Sopenharmony_ci goto halt; 148262306a36Sopenharmony_ci } 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci /* stop scanning when we reach qtds the hc is using */ 148562306a36Sopenharmony_ci } else if (likely(!stopped && 148662306a36Sopenharmony_ci HC_IS_RUNNING(oxu_to_hcd(oxu)->state))) { 148762306a36Sopenharmony_ci break; 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci } else { 149062306a36Sopenharmony_ci stopped = 1; 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci if (unlikely(!HC_IS_RUNNING(oxu_to_hcd(oxu)->state))) 149362306a36Sopenharmony_ci urb->status = -ESHUTDOWN; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci /* ignore active urbs unless some previous qtd 149662306a36Sopenharmony_ci * for the urb faulted (including short read) or 149762306a36Sopenharmony_ci * its urb was canceled. we may patch qh or qtds. 149862306a36Sopenharmony_ci */ 149962306a36Sopenharmony_ci if (likely(urb->status == -EINPROGRESS)) 150062306a36Sopenharmony_ci continue; 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci /* issue status after short control reads */ 150362306a36Sopenharmony_ci if (unlikely(do_status != 0) 150462306a36Sopenharmony_ci && QTD_PID(token) == 0 /* OUT */) { 150562306a36Sopenharmony_ci do_status = 0; 150662306a36Sopenharmony_ci continue; 150762306a36Sopenharmony_ci } 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci /* token in overlay may be most current */ 151062306a36Sopenharmony_ci if (state == QH_STATE_IDLE 151162306a36Sopenharmony_ci && cpu_to_le32(qtd->qtd_dma) 151262306a36Sopenharmony_ci == qh->hw_current) 151362306a36Sopenharmony_ci token = le32_to_cpu(qh->hw_token); 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci /* force halt for unlinked or blocked qh, so we'll 151662306a36Sopenharmony_ci * patch the qh later and so that completions can't 151762306a36Sopenharmony_ci * activate it while we "know" it's stopped. 151862306a36Sopenharmony_ci */ 151962306a36Sopenharmony_ci if ((HALT_BIT & qh->hw_token) == 0) { 152062306a36Sopenharmony_cihalt: 152162306a36Sopenharmony_ci qh->hw_token |= HALT_BIT; 152262306a36Sopenharmony_ci wmb(); 152362306a36Sopenharmony_ci } 152462306a36Sopenharmony_ci } 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci /* Remove it from the queue */ 152762306a36Sopenharmony_ci qtd_copy_status(oxu, urb->complete ? 152862306a36Sopenharmony_ci urb : ((struct oxu_murb *) urb)->main, 152962306a36Sopenharmony_ci qtd->length, token); 153062306a36Sopenharmony_ci if ((usb_pipein(qtd->urb->pipe)) && 153162306a36Sopenharmony_ci (NULL != qtd->transfer_buffer)) 153262306a36Sopenharmony_ci memcpy(qtd->transfer_buffer, qtd->buffer, qtd->length); 153362306a36Sopenharmony_ci do_status = (urb->status == -EREMOTEIO) 153462306a36Sopenharmony_ci && usb_pipecontrol(urb->pipe); 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci if (stopped && qtd->qtd_list.prev != &qh->qtd_list) { 153762306a36Sopenharmony_ci last = list_entry(qtd->qtd_list.prev, 153862306a36Sopenharmony_ci struct ehci_qtd, qtd_list); 153962306a36Sopenharmony_ci last->hw_next = qtd->hw_next; 154062306a36Sopenharmony_ci } 154162306a36Sopenharmony_ci list_del(&qtd->qtd_list); 154262306a36Sopenharmony_ci last = qtd; 154362306a36Sopenharmony_ci } 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci /* last urb's completion might still need calling */ 154662306a36Sopenharmony_ci if (likely(last != NULL)) { 154762306a36Sopenharmony_ci if (last->urb->complete == NULL) { 154862306a36Sopenharmony_ci murb = (struct oxu_murb *) last->urb; 154962306a36Sopenharmony_ci last->urb = murb->main; 155062306a36Sopenharmony_ci if (murb->last) { 155162306a36Sopenharmony_ci ehci_urb_done(oxu, last->urb); 155262306a36Sopenharmony_ci count++; 155362306a36Sopenharmony_ci } 155462306a36Sopenharmony_ci oxu_murb_free(oxu, murb); 155562306a36Sopenharmony_ci } else { 155662306a36Sopenharmony_ci ehci_urb_done(oxu, last->urb); 155762306a36Sopenharmony_ci count++; 155862306a36Sopenharmony_ci } 155962306a36Sopenharmony_ci oxu_qtd_free(oxu, last); 156062306a36Sopenharmony_ci } 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci /* restore original state; caller must unlink or relink */ 156362306a36Sopenharmony_ci qh->qh_state = state; 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci /* be sure the hardware's done with the qh before refreshing 156662306a36Sopenharmony_ci * it after fault cleanup, or recovering from silicon wrongly 156762306a36Sopenharmony_ci * overlaying the dummy qtd (which reduces DMA chatter). 156862306a36Sopenharmony_ci */ 156962306a36Sopenharmony_ci if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END) { 157062306a36Sopenharmony_ci switch (state) { 157162306a36Sopenharmony_ci case QH_STATE_IDLE: 157262306a36Sopenharmony_ci qh_refresh(oxu, qh); 157362306a36Sopenharmony_ci break; 157462306a36Sopenharmony_ci case QH_STATE_LINKED: 157562306a36Sopenharmony_ci /* should be rare for periodic transfers, 157662306a36Sopenharmony_ci * except maybe high bandwidth ... 157762306a36Sopenharmony_ci */ 157862306a36Sopenharmony_ci if ((cpu_to_le32(QH_SMASK) 157962306a36Sopenharmony_ci & qh->hw_info2) != 0) { 158062306a36Sopenharmony_ci intr_deschedule(oxu, qh); 158162306a36Sopenharmony_ci (void) qh_schedule(oxu, qh); 158262306a36Sopenharmony_ci } else 158362306a36Sopenharmony_ci unlink_async(oxu, qh); 158462306a36Sopenharmony_ci break; 158562306a36Sopenharmony_ci /* otherwise, unlink already started */ 158662306a36Sopenharmony_ci } 158762306a36Sopenharmony_ci } 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci return count; 159062306a36Sopenharmony_ci} 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci/* High bandwidth multiplier, as encoded in highspeed endpoint descriptors */ 159362306a36Sopenharmony_ci#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) 159462306a36Sopenharmony_ci/* ... and packet size, for any kind of endpoint descriptor */ 159562306a36Sopenharmony_ci#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff) 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci/* Reverse of qh_urb_transaction: free a list of TDs. 159862306a36Sopenharmony_ci * used for cleanup after errors, before HC sees an URB's TDs. 159962306a36Sopenharmony_ci */ 160062306a36Sopenharmony_cistatic void qtd_list_free(struct oxu_hcd *oxu, 160162306a36Sopenharmony_ci struct urb *urb, struct list_head *head) 160262306a36Sopenharmony_ci{ 160362306a36Sopenharmony_ci struct ehci_qtd *qtd, *temp; 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci list_for_each_entry_safe(qtd, temp, head, qtd_list) { 160662306a36Sopenharmony_ci list_del(&qtd->qtd_list); 160762306a36Sopenharmony_ci oxu_qtd_free(oxu, qtd); 160862306a36Sopenharmony_ci } 160962306a36Sopenharmony_ci} 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci/* Create a list of filled qtds for this URB; won't link into qh. 161262306a36Sopenharmony_ci */ 161362306a36Sopenharmony_cistatic struct list_head *qh_urb_transaction(struct oxu_hcd *oxu, 161462306a36Sopenharmony_ci struct urb *urb, 161562306a36Sopenharmony_ci struct list_head *head, 161662306a36Sopenharmony_ci gfp_t flags) 161762306a36Sopenharmony_ci{ 161862306a36Sopenharmony_ci struct ehci_qtd *qtd, *qtd_prev; 161962306a36Sopenharmony_ci dma_addr_t buf; 162062306a36Sopenharmony_ci int len, maxpacket; 162162306a36Sopenharmony_ci int is_input; 162262306a36Sopenharmony_ci u32 token; 162362306a36Sopenharmony_ci void *transfer_buf = NULL; 162462306a36Sopenharmony_ci int ret; 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci /* 162762306a36Sopenharmony_ci * URBs map to sequences of QTDs: one logical transaction 162862306a36Sopenharmony_ci */ 162962306a36Sopenharmony_ci qtd = ehci_qtd_alloc(oxu); 163062306a36Sopenharmony_ci if (unlikely(!qtd)) 163162306a36Sopenharmony_ci return NULL; 163262306a36Sopenharmony_ci list_add_tail(&qtd->qtd_list, head); 163362306a36Sopenharmony_ci qtd->urb = urb; 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci token = QTD_STS_ACTIVE; 163662306a36Sopenharmony_ci token |= (EHCI_TUNE_CERR << 10); 163762306a36Sopenharmony_ci /* for split transactions, SplitXState initialized to zero */ 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci len = urb->transfer_buffer_length; 164062306a36Sopenharmony_ci is_input = usb_pipein(urb->pipe); 164162306a36Sopenharmony_ci if (!urb->transfer_buffer && urb->transfer_buffer_length && is_input) 164262306a36Sopenharmony_ci urb->transfer_buffer = phys_to_virt(urb->transfer_dma); 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci if (usb_pipecontrol(urb->pipe)) { 164562306a36Sopenharmony_ci /* SETUP pid */ 164662306a36Sopenharmony_ci ret = oxu_buf_alloc(oxu, qtd, sizeof(struct usb_ctrlrequest)); 164762306a36Sopenharmony_ci if (ret) 164862306a36Sopenharmony_ci goto cleanup; 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci qtd_fill(qtd, qtd->buffer_dma, sizeof(struct usb_ctrlrequest), 165162306a36Sopenharmony_ci token | (2 /* "setup" */ << 8), 8); 165262306a36Sopenharmony_ci memcpy(qtd->buffer, qtd->urb->setup_packet, 165362306a36Sopenharmony_ci sizeof(struct usb_ctrlrequest)); 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci /* ... and always at least one more pid */ 165662306a36Sopenharmony_ci token ^= QTD_TOGGLE; 165762306a36Sopenharmony_ci qtd_prev = qtd; 165862306a36Sopenharmony_ci qtd = ehci_qtd_alloc(oxu); 165962306a36Sopenharmony_ci if (unlikely(!qtd)) 166062306a36Sopenharmony_ci goto cleanup; 166162306a36Sopenharmony_ci qtd->urb = urb; 166262306a36Sopenharmony_ci qtd_prev->hw_next = QTD_NEXT(qtd->qtd_dma); 166362306a36Sopenharmony_ci list_add_tail(&qtd->qtd_list, head); 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ci /* for zero length DATA stages, STATUS is always IN */ 166662306a36Sopenharmony_ci if (len == 0) 166762306a36Sopenharmony_ci token |= (1 /* "in" */ << 8); 166862306a36Sopenharmony_ci } 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci /* 167162306a36Sopenharmony_ci * Data transfer stage: buffer setup 167262306a36Sopenharmony_ci */ 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci ret = oxu_buf_alloc(oxu, qtd, len); 167562306a36Sopenharmony_ci if (ret) 167662306a36Sopenharmony_ci goto cleanup; 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci buf = qtd->buffer_dma; 167962306a36Sopenharmony_ci transfer_buf = urb->transfer_buffer; 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci if (!is_input) 168262306a36Sopenharmony_ci memcpy(qtd->buffer, qtd->urb->transfer_buffer, len); 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci if (is_input) 168562306a36Sopenharmony_ci token |= (1 /* "in" */ << 8); 168662306a36Sopenharmony_ci /* else it's already initted to "out" pid (0 << 8) */ 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci maxpacket = usb_maxpacket(urb->dev, urb->pipe); 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci /* 169162306a36Sopenharmony_ci * buffer gets wrapped in one or more qtds; 169262306a36Sopenharmony_ci * last one may be "short" (including zero len) 169362306a36Sopenharmony_ci * and may serve as a control status ack 169462306a36Sopenharmony_ci */ 169562306a36Sopenharmony_ci for (;;) { 169662306a36Sopenharmony_ci int this_qtd_len; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci this_qtd_len = qtd_fill(qtd, buf, len, token, maxpacket); 169962306a36Sopenharmony_ci qtd->transfer_buffer = transfer_buf; 170062306a36Sopenharmony_ci len -= this_qtd_len; 170162306a36Sopenharmony_ci buf += this_qtd_len; 170262306a36Sopenharmony_ci transfer_buf += this_qtd_len; 170362306a36Sopenharmony_ci if (is_input) 170462306a36Sopenharmony_ci qtd->hw_alt_next = oxu->async->hw_alt_next; 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci /* qh makes control packets use qtd toggle; maybe switch it */ 170762306a36Sopenharmony_ci if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) 170862306a36Sopenharmony_ci token ^= QTD_TOGGLE; 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci if (likely(len <= 0)) 171162306a36Sopenharmony_ci break; 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci qtd_prev = qtd; 171462306a36Sopenharmony_ci qtd = ehci_qtd_alloc(oxu); 171562306a36Sopenharmony_ci if (unlikely(!qtd)) 171662306a36Sopenharmony_ci goto cleanup; 171762306a36Sopenharmony_ci if (likely(len > 0)) { 171862306a36Sopenharmony_ci ret = oxu_buf_alloc(oxu, qtd, len); 171962306a36Sopenharmony_ci if (ret) 172062306a36Sopenharmony_ci goto cleanup; 172162306a36Sopenharmony_ci } 172262306a36Sopenharmony_ci qtd->urb = urb; 172362306a36Sopenharmony_ci qtd_prev->hw_next = QTD_NEXT(qtd->qtd_dma); 172462306a36Sopenharmony_ci list_add_tail(&qtd->qtd_list, head); 172562306a36Sopenharmony_ci } 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci /* unless the bulk/interrupt caller wants a chance to clean 172862306a36Sopenharmony_ci * up after short reads, hc should advance qh past this urb 172962306a36Sopenharmony_ci */ 173062306a36Sopenharmony_ci if (likely((urb->transfer_flags & URB_SHORT_NOT_OK) == 0 173162306a36Sopenharmony_ci || usb_pipecontrol(urb->pipe))) 173262306a36Sopenharmony_ci qtd->hw_alt_next = EHCI_LIST_END; 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci /* 173562306a36Sopenharmony_ci * control requests may need a terminating data "status" ack; 173662306a36Sopenharmony_ci * bulk ones may need a terminating short packet (zero length). 173762306a36Sopenharmony_ci */ 173862306a36Sopenharmony_ci if (likely(urb->transfer_buffer_length != 0)) { 173962306a36Sopenharmony_ci int one_more = 0; 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci if (usb_pipecontrol(urb->pipe)) { 174262306a36Sopenharmony_ci one_more = 1; 174362306a36Sopenharmony_ci token ^= 0x0100; /* "in" <--> "out" */ 174462306a36Sopenharmony_ci token |= QTD_TOGGLE; /* force DATA1 */ 174562306a36Sopenharmony_ci } else if (usb_pipebulk(urb->pipe) 174662306a36Sopenharmony_ci && (urb->transfer_flags & URB_ZERO_PACKET) 174762306a36Sopenharmony_ci && !(urb->transfer_buffer_length % maxpacket)) { 174862306a36Sopenharmony_ci one_more = 1; 174962306a36Sopenharmony_ci } 175062306a36Sopenharmony_ci if (one_more) { 175162306a36Sopenharmony_ci qtd_prev = qtd; 175262306a36Sopenharmony_ci qtd = ehci_qtd_alloc(oxu); 175362306a36Sopenharmony_ci if (unlikely(!qtd)) 175462306a36Sopenharmony_ci goto cleanup; 175562306a36Sopenharmony_ci qtd->urb = urb; 175662306a36Sopenharmony_ci qtd_prev->hw_next = QTD_NEXT(qtd->qtd_dma); 175762306a36Sopenharmony_ci list_add_tail(&qtd->qtd_list, head); 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ci /* never any data in such packets */ 176062306a36Sopenharmony_ci qtd_fill(qtd, 0, 0, token, 0); 176162306a36Sopenharmony_ci } 176262306a36Sopenharmony_ci } 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci /* by default, enable interrupt on urb completion */ 176562306a36Sopenharmony_ci qtd->hw_token |= cpu_to_le32(QTD_IOC); 176662306a36Sopenharmony_ci return head; 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_cicleanup: 176962306a36Sopenharmony_ci qtd_list_free(oxu, urb, head); 177062306a36Sopenharmony_ci return NULL; 177162306a36Sopenharmony_ci} 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci/* Each QH holds a qtd list; a QH is used for everything except iso. 177462306a36Sopenharmony_ci * 177562306a36Sopenharmony_ci * For interrupt urbs, the scheduler must set the microframe scheduling 177662306a36Sopenharmony_ci * mask(s) each time the QH gets scheduled. For highspeed, that's 177762306a36Sopenharmony_ci * just one microframe in the s-mask. For split interrupt transactions 177862306a36Sopenharmony_ci * there are additional complications: c-mask, maybe FSTNs. 177962306a36Sopenharmony_ci */ 178062306a36Sopenharmony_cistatic struct ehci_qh *qh_make(struct oxu_hcd *oxu, 178162306a36Sopenharmony_ci struct urb *urb, gfp_t flags) 178262306a36Sopenharmony_ci{ 178362306a36Sopenharmony_ci struct ehci_qh *qh = oxu_qh_alloc(oxu); 178462306a36Sopenharmony_ci u32 info1 = 0, info2 = 0; 178562306a36Sopenharmony_ci int is_input, type; 178662306a36Sopenharmony_ci int maxp = 0; 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci if (!qh) 178962306a36Sopenharmony_ci return qh; 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci /* 179262306a36Sopenharmony_ci * init endpoint/device data for this QH 179362306a36Sopenharmony_ci */ 179462306a36Sopenharmony_ci info1 |= usb_pipeendpoint(urb->pipe) << 8; 179562306a36Sopenharmony_ci info1 |= usb_pipedevice(urb->pipe) << 0; 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci is_input = usb_pipein(urb->pipe); 179862306a36Sopenharmony_ci type = usb_pipetype(urb->pipe); 179962306a36Sopenharmony_ci maxp = usb_maxpacket(urb->dev, urb->pipe); 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci /* Compute interrupt scheduling parameters just once, and save. 180262306a36Sopenharmony_ci * - allowing for high bandwidth, how many nsec/uframe are used? 180362306a36Sopenharmony_ci * - split transactions need a second CSPLIT uframe; same question 180462306a36Sopenharmony_ci * - splits also need a schedule gap (for full/low speed I/O) 180562306a36Sopenharmony_ci * - qh has a polling interval 180662306a36Sopenharmony_ci * 180762306a36Sopenharmony_ci * For control/bulk requests, the HC or TT handles these. 180862306a36Sopenharmony_ci */ 180962306a36Sopenharmony_ci if (type == PIPE_INTERRUPT) { 181062306a36Sopenharmony_ci qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH, 181162306a36Sopenharmony_ci is_input, 0, 181262306a36Sopenharmony_ci hb_mult(maxp) * max_packet(maxp))); 181362306a36Sopenharmony_ci qh->start = NO_FRAME; 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci if (urb->dev->speed == USB_SPEED_HIGH) { 181662306a36Sopenharmony_ci qh->c_usecs = 0; 181762306a36Sopenharmony_ci qh->gap_uf = 0; 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci qh->period = urb->interval >> 3; 182062306a36Sopenharmony_ci if (qh->period == 0 && urb->interval != 1) { 182162306a36Sopenharmony_ci /* NOTE interval 2 or 4 uframes could work. 182262306a36Sopenharmony_ci * But interval 1 scheduling is simpler, and 182362306a36Sopenharmony_ci * includes high bandwidth. 182462306a36Sopenharmony_ci */ 182562306a36Sopenharmony_ci oxu_dbg(oxu, "intr period %d uframes, NYET!\n", 182662306a36Sopenharmony_ci urb->interval); 182762306a36Sopenharmony_ci goto done; 182862306a36Sopenharmony_ci } 182962306a36Sopenharmony_ci } else { 183062306a36Sopenharmony_ci struct usb_tt *tt = urb->dev->tt; 183162306a36Sopenharmony_ci int think_time; 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci /* gap is f(FS/LS transfer times) */ 183462306a36Sopenharmony_ci qh->gap_uf = 1 + usb_calc_bus_time(urb->dev->speed, 183562306a36Sopenharmony_ci is_input, 0, maxp) / (125 * 1000); 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci /* FIXME this just approximates SPLIT/CSPLIT times */ 183862306a36Sopenharmony_ci if (is_input) { /* SPLIT, gap, CSPLIT+DATA */ 183962306a36Sopenharmony_ci qh->c_usecs = qh->usecs + HS_USECS(0); 184062306a36Sopenharmony_ci qh->usecs = HS_USECS(1); 184162306a36Sopenharmony_ci } else { /* SPLIT+DATA, gap, CSPLIT */ 184262306a36Sopenharmony_ci qh->usecs += HS_USECS(1); 184362306a36Sopenharmony_ci qh->c_usecs = HS_USECS(0); 184462306a36Sopenharmony_ci } 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci think_time = tt ? tt->think_time : 0; 184762306a36Sopenharmony_ci qh->tt_usecs = NS_TO_US(think_time + 184862306a36Sopenharmony_ci usb_calc_bus_time(urb->dev->speed, 184962306a36Sopenharmony_ci is_input, 0, max_packet(maxp))); 185062306a36Sopenharmony_ci qh->period = urb->interval; 185162306a36Sopenharmony_ci } 185262306a36Sopenharmony_ci } 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci /* support for tt scheduling, and access to toggles */ 185562306a36Sopenharmony_ci qh->dev = urb->dev; 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci /* using TT? */ 185862306a36Sopenharmony_ci switch (urb->dev->speed) { 185962306a36Sopenharmony_ci case USB_SPEED_LOW: 186062306a36Sopenharmony_ci info1 |= (1 << 12); /* EPS "low" */ 186162306a36Sopenharmony_ci fallthrough; 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci case USB_SPEED_FULL: 186462306a36Sopenharmony_ci /* EPS 0 means "full" */ 186562306a36Sopenharmony_ci if (type != PIPE_INTERRUPT) 186662306a36Sopenharmony_ci info1 |= (EHCI_TUNE_RL_TT << 28); 186762306a36Sopenharmony_ci if (type == PIPE_CONTROL) { 186862306a36Sopenharmony_ci info1 |= (1 << 27); /* for TT */ 186962306a36Sopenharmony_ci info1 |= 1 << 14; /* toggle from qtd */ 187062306a36Sopenharmony_ci } 187162306a36Sopenharmony_ci info1 |= maxp << 16; 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci info2 |= (EHCI_TUNE_MULT_TT << 30); 187462306a36Sopenharmony_ci info2 |= urb->dev->ttport << 23; 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */ 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ci break; 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci case USB_SPEED_HIGH: /* no TT involved */ 188162306a36Sopenharmony_ci info1 |= (2 << 12); /* EPS "high" */ 188262306a36Sopenharmony_ci if (type == PIPE_CONTROL) { 188362306a36Sopenharmony_ci info1 |= (EHCI_TUNE_RL_HS << 28); 188462306a36Sopenharmony_ci info1 |= 64 << 16; /* usb2 fixed maxpacket */ 188562306a36Sopenharmony_ci info1 |= 1 << 14; /* toggle from qtd */ 188662306a36Sopenharmony_ci info2 |= (EHCI_TUNE_MULT_HS << 30); 188762306a36Sopenharmony_ci } else if (type == PIPE_BULK) { 188862306a36Sopenharmony_ci info1 |= (EHCI_TUNE_RL_HS << 28); 188962306a36Sopenharmony_ci info1 |= 512 << 16; /* usb2 fixed maxpacket */ 189062306a36Sopenharmony_ci info2 |= (EHCI_TUNE_MULT_HS << 30); 189162306a36Sopenharmony_ci } else { /* PIPE_INTERRUPT */ 189262306a36Sopenharmony_ci info1 |= max_packet(maxp) << 16; 189362306a36Sopenharmony_ci info2 |= hb_mult(maxp) << 30; 189462306a36Sopenharmony_ci } 189562306a36Sopenharmony_ci break; 189662306a36Sopenharmony_ci default: 189762306a36Sopenharmony_ci oxu_dbg(oxu, "bogus dev %p speed %d\n", urb->dev, urb->dev->speed); 189862306a36Sopenharmony_cidone: 189962306a36Sopenharmony_ci qh_put(qh); 190062306a36Sopenharmony_ci return NULL; 190162306a36Sopenharmony_ci } 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */ 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci /* init as live, toggle clear, advance to dummy */ 190662306a36Sopenharmony_ci qh->qh_state = QH_STATE_IDLE; 190762306a36Sopenharmony_ci qh->hw_info1 = cpu_to_le32(info1); 190862306a36Sopenharmony_ci qh->hw_info2 = cpu_to_le32(info2); 190962306a36Sopenharmony_ci usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), !is_input, 1); 191062306a36Sopenharmony_ci qh_refresh(oxu, qh); 191162306a36Sopenharmony_ci return qh; 191262306a36Sopenharmony_ci} 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci/* Move qh (and its qtds) onto async queue; maybe enable queue. 191562306a36Sopenharmony_ci */ 191662306a36Sopenharmony_cistatic void qh_link_async(struct oxu_hcd *oxu, struct ehci_qh *qh) 191762306a36Sopenharmony_ci{ 191862306a36Sopenharmony_ci __le32 dma = QH_NEXT(qh->qh_dma); 191962306a36Sopenharmony_ci struct ehci_qh *head; 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci /* (re)start the async schedule? */ 192262306a36Sopenharmony_ci head = oxu->async; 192362306a36Sopenharmony_ci timer_action_done(oxu, TIMER_ASYNC_OFF); 192462306a36Sopenharmony_ci if (!head->qh_next.qh) { 192562306a36Sopenharmony_ci u32 cmd = readl(&oxu->regs->command); 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci if (!(cmd & CMD_ASE)) { 192862306a36Sopenharmony_ci /* in case a clear of CMD_ASE didn't take yet */ 192962306a36Sopenharmony_ci (void)handshake(oxu, &oxu->regs->status, 193062306a36Sopenharmony_ci STS_ASS, 0, 150); 193162306a36Sopenharmony_ci cmd |= CMD_ASE | CMD_RUN; 193262306a36Sopenharmony_ci writel(cmd, &oxu->regs->command); 193362306a36Sopenharmony_ci oxu_to_hcd(oxu)->state = HC_STATE_RUNNING; 193462306a36Sopenharmony_ci /* posted write need not be known to HC yet ... */ 193562306a36Sopenharmony_ci } 193662306a36Sopenharmony_ci } 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci /* clear halt and/or toggle; and maybe recover from silicon quirk */ 193962306a36Sopenharmony_ci if (qh->qh_state == QH_STATE_IDLE) 194062306a36Sopenharmony_ci qh_refresh(oxu, qh); 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci /* splice right after start */ 194362306a36Sopenharmony_ci qh->qh_next = head->qh_next; 194462306a36Sopenharmony_ci qh->hw_next = head->hw_next; 194562306a36Sopenharmony_ci wmb(); 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci head->qh_next.qh = qh; 194862306a36Sopenharmony_ci head->hw_next = dma; 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci qh->qh_state = QH_STATE_LINKED; 195162306a36Sopenharmony_ci /* qtd completions reported later by interrupt */ 195262306a36Sopenharmony_ci} 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci#define QH_ADDR_MASK cpu_to_le32(0x7f) 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci/* 195762306a36Sopenharmony_ci * For control/bulk/interrupt, return QH with these TDs appended. 195862306a36Sopenharmony_ci * Allocates and initializes the QH if necessary. 195962306a36Sopenharmony_ci * Returns null if it can't allocate a QH it needs to. 196062306a36Sopenharmony_ci * If the QH has TDs (urbs) already, that's great. 196162306a36Sopenharmony_ci */ 196262306a36Sopenharmony_cistatic struct ehci_qh *qh_append_tds(struct oxu_hcd *oxu, 196362306a36Sopenharmony_ci struct urb *urb, struct list_head *qtd_list, 196462306a36Sopenharmony_ci int epnum, void **ptr) 196562306a36Sopenharmony_ci{ 196662306a36Sopenharmony_ci struct ehci_qh *qh = NULL; 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_ci qh = (struct ehci_qh *) *ptr; 196962306a36Sopenharmony_ci if (unlikely(qh == NULL)) { 197062306a36Sopenharmony_ci /* can't sleep here, we have oxu->lock... */ 197162306a36Sopenharmony_ci qh = qh_make(oxu, urb, GFP_ATOMIC); 197262306a36Sopenharmony_ci *ptr = qh; 197362306a36Sopenharmony_ci } 197462306a36Sopenharmony_ci if (likely(qh != NULL)) { 197562306a36Sopenharmony_ci struct ehci_qtd *qtd; 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ci if (unlikely(list_empty(qtd_list))) 197862306a36Sopenharmony_ci qtd = NULL; 197962306a36Sopenharmony_ci else 198062306a36Sopenharmony_ci qtd = list_entry(qtd_list->next, struct ehci_qtd, 198162306a36Sopenharmony_ci qtd_list); 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci /* control qh may need patching ... */ 198462306a36Sopenharmony_ci if (unlikely(epnum == 0)) { 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci /* usb_reset_device() briefly reverts to address 0 */ 198762306a36Sopenharmony_ci if (usb_pipedevice(urb->pipe) == 0) 198862306a36Sopenharmony_ci qh->hw_info1 &= ~QH_ADDR_MASK; 198962306a36Sopenharmony_ci } 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci /* just one way to queue requests: swap with the dummy qtd. 199262306a36Sopenharmony_ci * only hc or qh_refresh() ever modify the overlay. 199362306a36Sopenharmony_ci */ 199462306a36Sopenharmony_ci if (likely(qtd != NULL)) { 199562306a36Sopenharmony_ci struct ehci_qtd *dummy; 199662306a36Sopenharmony_ci dma_addr_t dma; 199762306a36Sopenharmony_ci __le32 token; 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci /* to avoid racing the HC, use the dummy td instead of 200062306a36Sopenharmony_ci * the first td of our list (becomes new dummy). both 200162306a36Sopenharmony_ci * tds stay deactivated until we're done, when the 200262306a36Sopenharmony_ci * HC is allowed to fetch the old dummy (4.10.2). 200362306a36Sopenharmony_ci */ 200462306a36Sopenharmony_ci token = qtd->hw_token; 200562306a36Sopenharmony_ci qtd->hw_token = HALT_BIT; 200662306a36Sopenharmony_ci wmb(); 200762306a36Sopenharmony_ci dummy = qh->dummy; 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ci dma = dummy->qtd_dma; 201062306a36Sopenharmony_ci *dummy = *qtd; 201162306a36Sopenharmony_ci dummy->qtd_dma = dma; 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci list_del(&qtd->qtd_list); 201462306a36Sopenharmony_ci list_add(&dummy->qtd_list, qtd_list); 201562306a36Sopenharmony_ci list_splice(qtd_list, qh->qtd_list.prev); 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci ehci_qtd_init(qtd, qtd->qtd_dma); 201862306a36Sopenharmony_ci qh->dummy = qtd; 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci /* hc must see the new dummy at list end */ 202162306a36Sopenharmony_ci dma = qtd->qtd_dma; 202262306a36Sopenharmony_ci qtd = list_entry(qh->qtd_list.prev, 202362306a36Sopenharmony_ci struct ehci_qtd, qtd_list); 202462306a36Sopenharmony_ci qtd->hw_next = QTD_NEXT(dma); 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci /* let the hc process these next qtds */ 202762306a36Sopenharmony_ci dummy->hw_token = (token & ~(0x80)); 202862306a36Sopenharmony_ci wmb(); 202962306a36Sopenharmony_ci dummy->hw_token = token; 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci urb->hcpriv = qh_get(qh); 203262306a36Sopenharmony_ci } 203362306a36Sopenharmony_ci } 203462306a36Sopenharmony_ci return qh; 203562306a36Sopenharmony_ci} 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_cistatic int submit_async(struct oxu_hcd *oxu, struct urb *urb, 203862306a36Sopenharmony_ci struct list_head *qtd_list, gfp_t mem_flags) 203962306a36Sopenharmony_ci{ 204062306a36Sopenharmony_ci int epnum = urb->ep->desc.bEndpointAddress; 204162306a36Sopenharmony_ci unsigned long flags; 204262306a36Sopenharmony_ci struct ehci_qh *qh = NULL; 204362306a36Sopenharmony_ci int rc = 0; 204462306a36Sopenharmony_ci#ifdef OXU_URB_TRACE 204562306a36Sopenharmony_ci struct ehci_qtd *qtd; 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci qtd = list_entry(qtd_list->next, struct ehci_qtd, qtd_list); 204862306a36Sopenharmony_ci 204962306a36Sopenharmony_ci oxu_dbg(oxu, "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n", 205062306a36Sopenharmony_ci __func__, urb->dev->devpath, urb, 205162306a36Sopenharmony_ci epnum & 0x0f, (epnum & USB_DIR_IN) ? "in" : "out", 205262306a36Sopenharmony_ci urb->transfer_buffer_length, 205362306a36Sopenharmony_ci qtd, urb->ep->hcpriv); 205462306a36Sopenharmony_ci#endif 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci spin_lock_irqsave(&oxu->lock, flags); 205762306a36Sopenharmony_ci if (unlikely(!HCD_HW_ACCESSIBLE(oxu_to_hcd(oxu)))) { 205862306a36Sopenharmony_ci rc = -ESHUTDOWN; 205962306a36Sopenharmony_ci goto done; 206062306a36Sopenharmony_ci } 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci qh = qh_append_tds(oxu, urb, qtd_list, epnum, &urb->ep->hcpriv); 206362306a36Sopenharmony_ci if (unlikely(qh == NULL)) { 206462306a36Sopenharmony_ci rc = -ENOMEM; 206562306a36Sopenharmony_ci goto done; 206662306a36Sopenharmony_ci } 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_ci /* Control/bulk operations through TTs don't need scheduling, 206962306a36Sopenharmony_ci * the HC and TT handle it when the TT has a buffer ready. 207062306a36Sopenharmony_ci */ 207162306a36Sopenharmony_ci if (likely(qh->qh_state == QH_STATE_IDLE)) 207262306a36Sopenharmony_ci qh_link_async(oxu, qh_get(qh)); 207362306a36Sopenharmony_cidone: 207462306a36Sopenharmony_ci spin_unlock_irqrestore(&oxu->lock, flags); 207562306a36Sopenharmony_ci if (unlikely(qh == NULL)) 207662306a36Sopenharmony_ci qtd_list_free(oxu, urb, qtd_list); 207762306a36Sopenharmony_ci return rc; 207862306a36Sopenharmony_ci} 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci/* The async qh for the qtds being reclaimed are now unlinked from the HC */ 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_cistatic void end_unlink_async(struct oxu_hcd *oxu) 208362306a36Sopenharmony_ci{ 208462306a36Sopenharmony_ci struct ehci_qh *qh = oxu->reclaim; 208562306a36Sopenharmony_ci struct ehci_qh *next; 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci timer_action_done(oxu, TIMER_IAA_WATCHDOG); 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci qh->qh_state = QH_STATE_IDLE; 209062306a36Sopenharmony_ci qh->qh_next.qh = NULL; 209162306a36Sopenharmony_ci qh_put(qh); /* refcount from reclaim */ 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_ci /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */ 209462306a36Sopenharmony_ci next = qh->reclaim; 209562306a36Sopenharmony_ci oxu->reclaim = next; 209662306a36Sopenharmony_ci oxu->reclaim_ready = 0; 209762306a36Sopenharmony_ci qh->reclaim = NULL; 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_ci qh_completions(oxu, qh); 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci if (!list_empty(&qh->qtd_list) 210262306a36Sopenharmony_ci && HC_IS_RUNNING(oxu_to_hcd(oxu)->state)) 210362306a36Sopenharmony_ci qh_link_async(oxu, qh); 210462306a36Sopenharmony_ci else { 210562306a36Sopenharmony_ci qh_put(qh); /* refcount from async list */ 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci /* it's not free to turn the async schedule on/off; leave it 210862306a36Sopenharmony_ci * active but idle for a while once it empties. 210962306a36Sopenharmony_ci */ 211062306a36Sopenharmony_ci if (HC_IS_RUNNING(oxu_to_hcd(oxu)->state) 211162306a36Sopenharmony_ci && oxu->async->qh_next.qh == NULL) 211262306a36Sopenharmony_ci timer_action(oxu, TIMER_ASYNC_OFF); 211362306a36Sopenharmony_ci } 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci if (next) { 211662306a36Sopenharmony_ci oxu->reclaim = NULL; 211762306a36Sopenharmony_ci start_unlink_async(oxu, next); 211862306a36Sopenharmony_ci } 211962306a36Sopenharmony_ci} 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ci/* makes sure the async qh will become idle */ 212262306a36Sopenharmony_ci/* caller must own oxu->lock */ 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_cistatic void start_unlink_async(struct oxu_hcd *oxu, struct ehci_qh *qh) 212562306a36Sopenharmony_ci{ 212662306a36Sopenharmony_ci int cmd = readl(&oxu->regs->command); 212762306a36Sopenharmony_ci struct ehci_qh *prev; 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_ci#ifdef DEBUG 213062306a36Sopenharmony_ci assert_spin_locked(&oxu->lock); 213162306a36Sopenharmony_ci BUG_ON(oxu->reclaim || (qh->qh_state != QH_STATE_LINKED 213262306a36Sopenharmony_ci && qh->qh_state != QH_STATE_UNLINK_WAIT)); 213362306a36Sopenharmony_ci#endif 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci /* stop async schedule right now? */ 213662306a36Sopenharmony_ci if (unlikely(qh == oxu->async)) { 213762306a36Sopenharmony_ci /* can't get here without STS_ASS set */ 213862306a36Sopenharmony_ci if (oxu_to_hcd(oxu)->state != HC_STATE_HALT 213962306a36Sopenharmony_ci && !oxu->reclaim) { 214062306a36Sopenharmony_ci /* ... and CMD_IAAD clear */ 214162306a36Sopenharmony_ci writel(cmd & ~CMD_ASE, &oxu->regs->command); 214262306a36Sopenharmony_ci wmb(); 214362306a36Sopenharmony_ci /* handshake later, if we need to */ 214462306a36Sopenharmony_ci timer_action_done(oxu, TIMER_ASYNC_OFF); 214562306a36Sopenharmony_ci } 214662306a36Sopenharmony_ci return; 214762306a36Sopenharmony_ci } 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci qh->qh_state = QH_STATE_UNLINK; 215062306a36Sopenharmony_ci oxu->reclaim = qh = qh_get(qh); 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ci prev = oxu->async; 215362306a36Sopenharmony_ci while (prev->qh_next.qh != qh) 215462306a36Sopenharmony_ci prev = prev->qh_next.qh; 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci prev->hw_next = qh->hw_next; 215762306a36Sopenharmony_ci prev->qh_next = qh->qh_next; 215862306a36Sopenharmony_ci wmb(); 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci if (unlikely(oxu_to_hcd(oxu)->state == HC_STATE_HALT)) { 216162306a36Sopenharmony_ci /* if (unlikely(qh->reclaim != 0)) 216262306a36Sopenharmony_ci * this will recurse, probably not much 216362306a36Sopenharmony_ci */ 216462306a36Sopenharmony_ci end_unlink_async(oxu); 216562306a36Sopenharmony_ci return; 216662306a36Sopenharmony_ci } 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ci oxu->reclaim_ready = 0; 216962306a36Sopenharmony_ci cmd |= CMD_IAAD; 217062306a36Sopenharmony_ci writel(cmd, &oxu->regs->command); 217162306a36Sopenharmony_ci (void) readl(&oxu->regs->command); 217262306a36Sopenharmony_ci timer_action(oxu, TIMER_IAA_WATCHDOG); 217362306a36Sopenharmony_ci} 217462306a36Sopenharmony_ci 217562306a36Sopenharmony_cistatic void scan_async(struct oxu_hcd *oxu) 217662306a36Sopenharmony_ci{ 217762306a36Sopenharmony_ci struct ehci_qh *qh; 217862306a36Sopenharmony_ci enum ehci_timer_action action = TIMER_IO_WATCHDOG; 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci if (!++(oxu->stamp)) 218162306a36Sopenharmony_ci oxu->stamp++; 218262306a36Sopenharmony_ci timer_action_done(oxu, TIMER_ASYNC_SHRINK); 218362306a36Sopenharmony_cirescan: 218462306a36Sopenharmony_ci qh = oxu->async->qh_next.qh; 218562306a36Sopenharmony_ci if (likely(qh != NULL)) { 218662306a36Sopenharmony_ci do { 218762306a36Sopenharmony_ci /* clean any finished work for this qh */ 218862306a36Sopenharmony_ci if (!list_empty(&qh->qtd_list) 218962306a36Sopenharmony_ci && qh->stamp != oxu->stamp) { 219062306a36Sopenharmony_ci int temp; 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_ci /* unlinks could happen here; completion 219362306a36Sopenharmony_ci * reporting drops the lock. rescan using 219462306a36Sopenharmony_ci * the latest schedule, but don't rescan 219562306a36Sopenharmony_ci * qhs we already finished (no looping). 219662306a36Sopenharmony_ci */ 219762306a36Sopenharmony_ci qh = qh_get(qh); 219862306a36Sopenharmony_ci qh->stamp = oxu->stamp; 219962306a36Sopenharmony_ci temp = qh_completions(oxu, qh); 220062306a36Sopenharmony_ci qh_put(qh); 220162306a36Sopenharmony_ci if (temp != 0) 220262306a36Sopenharmony_ci goto rescan; 220362306a36Sopenharmony_ci } 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ci /* unlink idle entries, reducing HC PCI usage as well 220662306a36Sopenharmony_ci * as HCD schedule-scanning costs. delay for any qh 220762306a36Sopenharmony_ci * we just scanned, there's a not-unusual case that it 220862306a36Sopenharmony_ci * doesn't stay idle for long. 220962306a36Sopenharmony_ci * (plus, avoids some kind of re-activation race.) 221062306a36Sopenharmony_ci */ 221162306a36Sopenharmony_ci if (list_empty(&qh->qtd_list)) { 221262306a36Sopenharmony_ci if (qh->stamp == oxu->stamp) 221362306a36Sopenharmony_ci action = TIMER_ASYNC_SHRINK; 221462306a36Sopenharmony_ci else if (!oxu->reclaim 221562306a36Sopenharmony_ci && qh->qh_state == QH_STATE_LINKED) 221662306a36Sopenharmony_ci start_unlink_async(oxu, qh); 221762306a36Sopenharmony_ci } 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci qh = qh->qh_next.qh; 222062306a36Sopenharmony_ci } while (qh); 222162306a36Sopenharmony_ci } 222262306a36Sopenharmony_ci if (action == TIMER_ASYNC_SHRINK) 222362306a36Sopenharmony_ci timer_action(oxu, TIMER_ASYNC_SHRINK); 222462306a36Sopenharmony_ci} 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci/* 222762306a36Sopenharmony_ci * periodic_next_shadow - return "next" pointer on shadow list 222862306a36Sopenharmony_ci * @periodic: host pointer to qh/itd/sitd 222962306a36Sopenharmony_ci * @tag: hardware tag for type of this record 223062306a36Sopenharmony_ci */ 223162306a36Sopenharmony_cistatic union ehci_shadow *periodic_next_shadow(union ehci_shadow *periodic, 223262306a36Sopenharmony_ci __le32 tag) 223362306a36Sopenharmony_ci{ 223462306a36Sopenharmony_ci switch (tag) { 223562306a36Sopenharmony_ci default: 223662306a36Sopenharmony_ci case Q_TYPE_QH: 223762306a36Sopenharmony_ci return &periodic->qh->qh_next; 223862306a36Sopenharmony_ci } 223962306a36Sopenharmony_ci} 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ci/* caller must hold oxu->lock */ 224262306a36Sopenharmony_cistatic void periodic_unlink(struct oxu_hcd *oxu, unsigned frame, void *ptr) 224362306a36Sopenharmony_ci{ 224462306a36Sopenharmony_ci union ehci_shadow *prev_p = &oxu->pshadow[frame]; 224562306a36Sopenharmony_ci __le32 *hw_p = &oxu->periodic[frame]; 224662306a36Sopenharmony_ci union ehci_shadow here = *prev_p; 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ci /* find predecessor of "ptr"; hw and shadow lists are in sync */ 224962306a36Sopenharmony_ci while (here.ptr && here.ptr != ptr) { 225062306a36Sopenharmony_ci prev_p = periodic_next_shadow(prev_p, Q_NEXT_TYPE(*hw_p)); 225162306a36Sopenharmony_ci hw_p = here.hw_next; 225262306a36Sopenharmony_ci here = *prev_p; 225362306a36Sopenharmony_ci } 225462306a36Sopenharmony_ci /* an interrupt entry (at list end) could have been shared */ 225562306a36Sopenharmony_ci if (!here.ptr) 225662306a36Sopenharmony_ci return; 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci /* update shadow and hardware lists ... the old "next" pointers 225962306a36Sopenharmony_ci * from ptr may still be in use, the caller updates them. 226062306a36Sopenharmony_ci */ 226162306a36Sopenharmony_ci *prev_p = *periodic_next_shadow(&here, Q_NEXT_TYPE(*hw_p)); 226262306a36Sopenharmony_ci *hw_p = *here.hw_next; 226362306a36Sopenharmony_ci} 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci/* how many of the uframe's 125 usecs are allocated? */ 226662306a36Sopenharmony_cistatic unsigned short periodic_usecs(struct oxu_hcd *oxu, 226762306a36Sopenharmony_ci unsigned frame, unsigned uframe) 226862306a36Sopenharmony_ci{ 226962306a36Sopenharmony_ci __le32 *hw_p = &oxu->periodic[frame]; 227062306a36Sopenharmony_ci union ehci_shadow *q = &oxu->pshadow[frame]; 227162306a36Sopenharmony_ci unsigned usecs = 0; 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci while (q->ptr) { 227462306a36Sopenharmony_ci switch (Q_NEXT_TYPE(*hw_p)) { 227562306a36Sopenharmony_ci case Q_TYPE_QH: 227662306a36Sopenharmony_ci default: 227762306a36Sopenharmony_ci /* is it in the S-mask? */ 227862306a36Sopenharmony_ci if (q->qh->hw_info2 & cpu_to_le32(1 << uframe)) 227962306a36Sopenharmony_ci usecs += q->qh->usecs; 228062306a36Sopenharmony_ci /* ... or C-mask? */ 228162306a36Sopenharmony_ci if (q->qh->hw_info2 & cpu_to_le32(1 << (8 + uframe))) 228262306a36Sopenharmony_ci usecs += q->qh->c_usecs; 228362306a36Sopenharmony_ci hw_p = &q->qh->hw_next; 228462306a36Sopenharmony_ci q = &q->qh->qh_next; 228562306a36Sopenharmony_ci break; 228662306a36Sopenharmony_ci } 228762306a36Sopenharmony_ci } 228862306a36Sopenharmony_ci#ifdef DEBUG 228962306a36Sopenharmony_ci if (usecs > 100) 229062306a36Sopenharmony_ci oxu_err(oxu, "uframe %d sched overrun: %d usecs\n", 229162306a36Sopenharmony_ci frame * 8 + uframe, usecs); 229262306a36Sopenharmony_ci#endif 229362306a36Sopenharmony_ci return usecs; 229462306a36Sopenharmony_ci} 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_cistatic int enable_periodic(struct oxu_hcd *oxu) 229762306a36Sopenharmony_ci{ 229862306a36Sopenharmony_ci u32 cmd; 229962306a36Sopenharmony_ci int status; 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci /* did clearing PSE did take effect yet? 230262306a36Sopenharmony_ci * takes effect only at frame boundaries... 230362306a36Sopenharmony_ci */ 230462306a36Sopenharmony_ci status = handshake(oxu, &oxu->regs->status, STS_PSS, 0, 9 * 125); 230562306a36Sopenharmony_ci if (status != 0) { 230662306a36Sopenharmony_ci oxu_to_hcd(oxu)->state = HC_STATE_HALT; 230762306a36Sopenharmony_ci usb_hc_died(oxu_to_hcd(oxu)); 230862306a36Sopenharmony_ci return status; 230962306a36Sopenharmony_ci } 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci cmd = readl(&oxu->regs->command) | CMD_PSE; 231262306a36Sopenharmony_ci writel(cmd, &oxu->regs->command); 231362306a36Sopenharmony_ci /* posted write ... PSS happens later */ 231462306a36Sopenharmony_ci oxu_to_hcd(oxu)->state = HC_STATE_RUNNING; 231562306a36Sopenharmony_ci 231662306a36Sopenharmony_ci /* make sure ehci_work scans these */ 231762306a36Sopenharmony_ci oxu->next_uframe = readl(&oxu->regs->frame_index) 231862306a36Sopenharmony_ci % (oxu->periodic_size << 3); 231962306a36Sopenharmony_ci return 0; 232062306a36Sopenharmony_ci} 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_cistatic int disable_periodic(struct oxu_hcd *oxu) 232362306a36Sopenharmony_ci{ 232462306a36Sopenharmony_ci u32 cmd; 232562306a36Sopenharmony_ci int status; 232662306a36Sopenharmony_ci 232762306a36Sopenharmony_ci /* did setting PSE not take effect yet? 232862306a36Sopenharmony_ci * takes effect only at frame boundaries... 232962306a36Sopenharmony_ci */ 233062306a36Sopenharmony_ci status = handshake(oxu, &oxu->regs->status, STS_PSS, STS_PSS, 9 * 125); 233162306a36Sopenharmony_ci if (status != 0) { 233262306a36Sopenharmony_ci oxu_to_hcd(oxu)->state = HC_STATE_HALT; 233362306a36Sopenharmony_ci usb_hc_died(oxu_to_hcd(oxu)); 233462306a36Sopenharmony_ci return status; 233562306a36Sopenharmony_ci } 233662306a36Sopenharmony_ci 233762306a36Sopenharmony_ci cmd = readl(&oxu->regs->command) & ~CMD_PSE; 233862306a36Sopenharmony_ci writel(cmd, &oxu->regs->command); 233962306a36Sopenharmony_ci /* posted write ... */ 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_ci oxu->next_uframe = -1; 234262306a36Sopenharmony_ci return 0; 234362306a36Sopenharmony_ci} 234462306a36Sopenharmony_ci 234562306a36Sopenharmony_ci/* periodic schedule slots have iso tds (normal or split) first, then a 234662306a36Sopenharmony_ci * sparse tree for active interrupt transfers. 234762306a36Sopenharmony_ci * 234862306a36Sopenharmony_ci * this just links in a qh; caller guarantees uframe masks are set right. 234962306a36Sopenharmony_ci * no FSTN support (yet; oxu 0.96+) 235062306a36Sopenharmony_ci */ 235162306a36Sopenharmony_cistatic int qh_link_periodic(struct oxu_hcd *oxu, struct ehci_qh *qh) 235262306a36Sopenharmony_ci{ 235362306a36Sopenharmony_ci unsigned i; 235462306a36Sopenharmony_ci unsigned period = qh->period; 235562306a36Sopenharmony_ci 235662306a36Sopenharmony_ci dev_dbg(&qh->dev->dev, 235762306a36Sopenharmony_ci "link qh%d-%04x/%p start %d [%d/%d us]\n", 235862306a36Sopenharmony_ci period, le32_to_cpup(&qh->hw_info2) & (QH_CMASK | QH_SMASK), 235962306a36Sopenharmony_ci qh, qh->start, qh->usecs, qh->c_usecs); 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_ci /* high bandwidth, or otherwise every microframe */ 236262306a36Sopenharmony_ci if (period == 0) 236362306a36Sopenharmony_ci period = 1; 236462306a36Sopenharmony_ci 236562306a36Sopenharmony_ci for (i = qh->start; i < oxu->periodic_size; i += period) { 236662306a36Sopenharmony_ci union ehci_shadow *prev = &oxu->pshadow[i]; 236762306a36Sopenharmony_ci __le32 *hw_p = &oxu->periodic[i]; 236862306a36Sopenharmony_ci union ehci_shadow here = *prev; 236962306a36Sopenharmony_ci __le32 type = 0; 237062306a36Sopenharmony_ci 237162306a36Sopenharmony_ci /* skip the iso nodes at list head */ 237262306a36Sopenharmony_ci while (here.ptr) { 237362306a36Sopenharmony_ci type = Q_NEXT_TYPE(*hw_p); 237462306a36Sopenharmony_ci if (type == Q_TYPE_QH) 237562306a36Sopenharmony_ci break; 237662306a36Sopenharmony_ci prev = periodic_next_shadow(prev, type); 237762306a36Sopenharmony_ci hw_p = &here.qh->hw_next; 237862306a36Sopenharmony_ci here = *prev; 237962306a36Sopenharmony_ci } 238062306a36Sopenharmony_ci 238162306a36Sopenharmony_ci /* sorting each branch by period (slow-->fast) 238262306a36Sopenharmony_ci * enables sharing interior tree nodes 238362306a36Sopenharmony_ci */ 238462306a36Sopenharmony_ci while (here.ptr && qh != here.qh) { 238562306a36Sopenharmony_ci if (qh->period > here.qh->period) 238662306a36Sopenharmony_ci break; 238762306a36Sopenharmony_ci prev = &here.qh->qh_next; 238862306a36Sopenharmony_ci hw_p = &here.qh->hw_next; 238962306a36Sopenharmony_ci here = *prev; 239062306a36Sopenharmony_ci } 239162306a36Sopenharmony_ci /* link in this qh, unless some earlier pass did that */ 239262306a36Sopenharmony_ci if (qh != here.qh) { 239362306a36Sopenharmony_ci qh->qh_next = here; 239462306a36Sopenharmony_ci if (here.qh) 239562306a36Sopenharmony_ci qh->hw_next = *hw_p; 239662306a36Sopenharmony_ci wmb(); 239762306a36Sopenharmony_ci prev->qh = qh; 239862306a36Sopenharmony_ci *hw_p = QH_NEXT(qh->qh_dma); 239962306a36Sopenharmony_ci } 240062306a36Sopenharmony_ci } 240162306a36Sopenharmony_ci qh->qh_state = QH_STATE_LINKED; 240262306a36Sopenharmony_ci qh_get(qh); 240362306a36Sopenharmony_ci 240462306a36Sopenharmony_ci /* update per-qh bandwidth for usbfs */ 240562306a36Sopenharmony_ci oxu_to_hcd(oxu)->self.bandwidth_allocated += qh->period 240662306a36Sopenharmony_ci ? ((qh->usecs + qh->c_usecs) / qh->period) 240762306a36Sopenharmony_ci : (qh->usecs * 8); 240862306a36Sopenharmony_ci 240962306a36Sopenharmony_ci /* maybe enable periodic schedule processing */ 241062306a36Sopenharmony_ci if (!oxu->periodic_sched++) 241162306a36Sopenharmony_ci return enable_periodic(oxu); 241262306a36Sopenharmony_ci 241362306a36Sopenharmony_ci return 0; 241462306a36Sopenharmony_ci} 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_cistatic void qh_unlink_periodic(struct oxu_hcd *oxu, struct ehci_qh *qh) 241762306a36Sopenharmony_ci{ 241862306a36Sopenharmony_ci unsigned i; 241962306a36Sopenharmony_ci unsigned period; 242062306a36Sopenharmony_ci 242162306a36Sopenharmony_ci /* FIXME: 242262306a36Sopenharmony_ci * IF this isn't high speed 242362306a36Sopenharmony_ci * and this qh is active in the current uframe 242462306a36Sopenharmony_ci * (and overlay token SplitXstate is false?) 242562306a36Sopenharmony_ci * THEN 242662306a36Sopenharmony_ci * qh->hw_info1 |= cpu_to_le32(1 << 7 "ignore"); 242762306a36Sopenharmony_ci */ 242862306a36Sopenharmony_ci 242962306a36Sopenharmony_ci /* high bandwidth, or otherwise part of every microframe */ 243062306a36Sopenharmony_ci period = qh->period; 243162306a36Sopenharmony_ci if (period == 0) 243262306a36Sopenharmony_ci period = 1; 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci for (i = qh->start; i < oxu->periodic_size; i += period) 243562306a36Sopenharmony_ci periodic_unlink(oxu, i, qh); 243662306a36Sopenharmony_ci 243762306a36Sopenharmony_ci /* update per-qh bandwidth for usbfs */ 243862306a36Sopenharmony_ci oxu_to_hcd(oxu)->self.bandwidth_allocated -= qh->period 243962306a36Sopenharmony_ci ? ((qh->usecs + qh->c_usecs) / qh->period) 244062306a36Sopenharmony_ci : (qh->usecs * 8); 244162306a36Sopenharmony_ci 244262306a36Sopenharmony_ci dev_dbg(&qh->dev->dev, 244362306a36Sopenharmony_ci "unlink qh%d-%04x/%p start %d [%d/%d us]\n", 244462306a36Sopenharmony_ci qh->period, 244562306a36Sopenharmony_ci le32_to_cpup(&qh->hw_info2) & (QH_CMASK | QH_SMASK), 244662306a36Sopenharmony_ci qh, qh->start, qh->usecs, qh->c_usecs); 244762306a36Sopenharmony_ci 244862306a36Sopenharmony_ci /* qh->qh_next still "live" to HC */ 244962306a36Sopenharmony_ci qh->qh_state = QH_STATE_UNLINK; 245062306a36Sopenharmony_ci qh->qh_next.ptr = NULL; 245162306a36Sopenharmony_ci qh_put(qh); 245262306a36Sopenharmony_ci 245362306a36Sopenharmony_ci /* maybe turn off periodic schedule */ 245462306a36Sopenharmony_ci oxu->periodic_sched--; 245562306a36Sopenharmony_ci if (!oxu->periodic_sched) 245662306a36Sopenharmony_ci (void) disable_periodic(oxu); 245762306a36Sopenharmony_ci} 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_cistatic void intr_deschedule(struct oxu_hcd *oxu, struct ehci_qh *qh) 246062306a36Sopenharmony_ci{ 246162306a36Sopenharmony_ci unsigned wait; 246262306a36Sopenharmony_ci 246362306a36Sopenharmony_ci qh_unlink_periodic(oxu, qh); 246462306a36Sopenharmony_ci 246562306a36Sopenharmony_ci /* simple/paranoid: always delay, expecting the HC needs to read 246662306a36Sopenharmony_ci * qh->hw_next or finish a writeback after SPLIT/CSPLIT ... and 246762306a36Sopenharmony_ci * expect hub_wq to clean up after any CSPLITs we won't issue. 246862306a36Sopenharmony_ci * active high speed queues may need bigger delays... 246962306a36Sopenharmony_ci */ 247062306a36Sopenharmony_ci if (list_empty(&qh->qtd_list) 247162306a36Sopenharmony_ci || (cpu_to_le32(QH_CMASK) & qh->hw_info2) != 0) 247262306a36Sopenharmony_ci wait = 2; 247362306a36Sopenharmony_ci else 247462306a36Sopenharmony_ci wait = 55; /* worst case: 3 * 1024 */ 247562306a36Sopenharmony_ci 247662306a36Sopenharmony_ci udelay(wait); 247762306a36Sopenharmony_ci qh->qh_state = QH_STATE_IDLE; 247862306a36Sopenharmony_ci qh->hw_next = EHCI_LIST_END; 247962306a36Sopenharmony_ci wmb(); 248062306a36Sopenharmony_ci} 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_cistatic int check_period(struct oxu_hcd *oxu, 248362306a36Sopenharmony_ci unsigned frame, unsigned uframe, 248462306a36Sopenharmony_ci unsigned period, unsigned usecs) 248562306a36Sopenharmony_ci{ 248662306a36Sopenharmony_ci int claimed; 248762306a36Sopenharmony_ci 248862306a36Sopenharmony_ci /* complete split running into next frame? 248962306a36Sopenharmony_ci * given FSTN support, we could sometimes check... 249062306a36Sopenharmony_ci */ 249162306a36Sopenharmony_ci if (uframe >= 8) 249262306a36Sopenharmony_ci return 0; 249362306a36Sopenharmony_ci 249462306a36Sopenharmony_ci /* 249562306a36Sopenharmony_ci * 80% periodic == 100 usec/uframe available 249662306a36Sopenharmony_ci * convert "usecs we need" to "max already claimed" 249762306a36Sopenharmony_ci */ 249862306a36Sopenharmony_ci usecs = 100 - usecs; 249962306a36Sopenharmony_ci 250062306a36Sopenharmony_ci /* we "know" 2 and 4 uframe intervals were rejected; so 250162306a36Sopenharmony_ci * for period 0, check _every_ microframe in the schedule. 250262306a36Sopenharmony_ci */ 250362306a36Sopenharmony_ci if (unlikely(period == 0)) { 250462306a36Sopenharmony_ci do { 250562306a36Sopenharmony_ci for (uframe = 0; uframe < 7; uframe++) { 250662306a36Sopenharmony_ci claimed = periodic_usecs(oxu, frame, uframe); 250762306a36Sopenharmony_ci if (claimed > usecs) 250862306a36Sopenharmony_ci return 0; 250962306a36Sopenharmony_ci } 251062306a36Sopenharmony_ci } while ((frame += 1) < oxu->periodic_size); 251162306a36Sopenharmony_ci 251262306a36Sopenharmony_ci /* just check the specified uframe, at that period */ 251362306a36Sopenharmony_ci } else { 251462306a36Sopenharmony_ci do { 251562306a36Sopenharmony_ci claimed = periodic_usecs(oxu, frame, uframe); 251662306a36Sopenharmony_ci if (claimed > usecs) 251762306a36Sopenharmony_ci return 0; 251862306a36Sopenharmony_ci } while ((frame += period) < oxu->periodic_size); 251962306a36Sopenharmony_ci } 252062306a36Sopenharmony_ci 252162306a36Sopenharmony_ci return 1; 252262306a36Sopenharmony_ci} 252362306a36Sopenharmony_ci 252462306a36Sopenharmony_cistatic int check_intr_schedule(struct oxu_hcd *oxu, 252562306a36Sopenharmony_ci unsigned frame, unsigned uframe, 252662306a36Sopenharmony_ci const struct ehci_qh *qh, __le32 *c_maskp) 252762306a36Sopenharmony_ci{ 252862306a36Sopenharmony_ci int retval = -ENOSPC; 252962306a36Sopenharmony_ci 253062306a36Sopenharmony_ci if (qh->c_usecs && uframe >= 6) /* FSTN territory? */ 253162306a36Sopenharmony_ci goto done; 253262306a36Sopenharmony_ci 253362306a36Sopenharmony_ci if (!check_period(oxu, frame, uframe, qh->period, qh->usecs)) 253462306a36Sopenharmony_ci goto done; 253562306a36Sopenharmony_ci if (!qh->c_usecs) { 253662306a36Sopenharmony_ci retval = 0; 253762306a36Sopenharmony_ci *c_maskp = 0; 253862306a36Sopenharmony_ci goto done; 253962306a36Sopenharmony_ci } 254062306a36Sopenharmony_ci 254162306a36Sopenharmony_cidone: 254262306a36Sopenharmony_ci return retval; 254362306a36Sopenharmony_ci} 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci/* "first fit" scheduling policy used the first time through, 254662306a36Sopenharmony_ci * or when the previous schedule slot can't be re-used. 254762306a36Sopenharmony_ci */ 254862306a36Sopenharmony_cistatic int qh_schedule(struct oxu_hcd *oxu, struct ehci_qh *qh) 254962306a36Sopenharmony_ci{ 255062306a36Sopenharmony_ci int status; 255162306a36Sopenharmony_ci unsigned uframe; 255262306a36Sopenharmony_ci __le32 c_mask; 255362306a36Sopenharmony_ci unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ 255462306a36Sopenharmony_ci 255562306a36Sopenharmony_ci qh_refresh(oxu, qh); 255662306a36Sopenharmony_ci qh->hw_next = EHCI_LIST_END; 255762306a36Sopenharmony_ci frame = qh->start; 255862306a36Sopenharmony_ci 255962306a36Sopenharmony_ci /* reuse the previous schedule slots, if we can */ 256062306a36Sopenharmony_ci if (frame < qh->period) { 256162306a36Sopenharmony_ci uframe = ffs(le32_to_cpup(&qh->hw_info2) & QH_SMASK); 256262306a36Sopenharmony_ci status = check_intr_schedule(oxu, frame, --uframe, 256362306a36Sopenharmony_ci qh, &c_mask); 256462306a36Sopenharmony_ci } else { 256562306a36Sopenharmony_ci uframe = 0; 256662306a36Sopenharmony_ci c_mask = 0; 256762306a36Sopenharmony_ci status = -ENOSPC; 256862306a36Sopenharmony_ci } 256962306a36Sopenharmony_ci 257062306a36Sopenharmony_ci /* else scan the schedule to find a group of slots such that all 257162306a36Sopenharmony_ci * uframes have enough periodic bandwidth available. 257262306a36Sopenharmony_ci */ 257362306a36Sopenharmony_ci if (status) { 257462306a36Sopenharmony_ci /* "normal" case, uframing flexible except with splits */ 257562306a36Sopenharmony_ci if (qh->period) { 257662306a36Sopenharmony_ci frame = qh->period - 1; 257762306a36Sopenharmony_ci do { 257862306a36Sopenharmony_ci for (uframe = 0; uframe < 8; uframe++) { 257962306a36Sopenharmony_ci status = check_intr_schedule(oxu, 258062306a36Sopenharmony_ci frame, uframe, qh, 258162306a36Sopenharmony_ci &c_mask); 258262306a36Sopenharmony_ci if (status == 0) 258362306a36Sopenharmony_ci break; 258462306a36Sopenharmony_ci } 258562306a36Sopenharmony_ci } while (status && frame--); 258662306a36Sopenharmony_ci 258762306a36Sopenharmony_ci /* qh->period == 0 means every uframe */ 258862306a36Sopenharmony_ci } else { 258962306a36Sopenharmony_ci frame = 0; 259062306a36Sopenharmony_ci status = check_intr_schedule(oxu, 0, 0, qh, &c_mask); 259162306a36Sopenharmony_ci } 259262306a36Sopenharmony_ci if (status) 259362306a36Sopenharmony_ci goto done; 259462306a36Sopenharmony_ci qh->start = frame; 259562306a36Sopenharmony_ci 259662306a36Sopenharmony_ci /* reset S-frame and (maybe) C-frame masks */ 259762306a36Sopenharmony_ci qh->hw_info2 &= cpu_to_le32(~(QH_CMASK | QH_SMASK)); 259862306a36Sopenharmony_ci qh->hw_info2 |= qh->period 259962306a36Sopenharmony_ci ? cpu_to_le32(1 << uframe) 260062306a36Sopenharmony_ci : cpu_to_le32(QH_SMASK); 260162306a36Sopenharmony_ci qh->hw_info2 |= c_mask; 260262306a36Sopenharmony_ci } else 260362306a36Sopenharmony_ci oxu_dbg(oxu, "reused qh %p schedule\n", qh); 260462306a36Sopenharmony_ci 260562306a36Sopenharmony_ci /* stuff into the periodic schedule */ 260662306a36Sopenharmony_ci status = qh_link_periodic(oxu, qh); 260762306a36Sopenharmony_cidone: 260862306a36Sopenharmony_ci return status; 260962306a36Sopenharmony_ci} 261062306a36Sopenharmony_ci 261162306a36Sopenharmony_cistatic int intr_submit(struct oxu_hcd *oxu, struct urb *urb, 261262306a36Sopenharmony_ci struct list_head *qtd_list, gfp_t mem_flags) 261362306a36Sopenharmony_ci{ 261462306a36Sopenharmony_ci unsigned epnum; 261562306a36Sopenharmony_ci unsigned long flags; 261662306a36Sopenharmony_ci struct ehci_qh *qh; 261762306a36Sopenharmony_ci int status = 0; 261862306a36Sopenharmony_ci struct list_head empty; 261962306a36Sopenharmony_ci 262062306a36Sopenharmony_ci /* get endpoint and transfer/schedule data */ 262162306a36Sopenharmony_ci epnum = urb->ep->desc.bEndpointAddress; 262262306a36Sopenharmony_ci 262362306a36Sopenharmony_ci spin_lock_irqsave(&oxu->lock, flags); 262462306a36Sopenharmony_ci 262562306a36Sopenharmony_ci if (unlikely(!HCD_HW_ACCESSIBLE(oxu_to_hcd(oxu)))) { 262662306a36Sopenharmony_ci status = -ESHUTDOWN; 262762306a36Sopenharmony_ci goto done; 262862306a36Sopenharmony_ci } 262962306a36Sopenharmony_ci 263062306a36Sopenharmony_ci /* get qh and force any scheduling errors */ 263162306a36Sopenharmony_ci INIT_LIST_HEAD(&empty); 263262306a36Sopenharmony_ci qh = qh_append_tds(oxu, urb, &empty, epnum, &urb->ep->hcpriv); 263362306a36Sopenharmony_ci if (qh == NULL) { 263462306a36Sopenharmony_ci status = -ENOMEM; 263562306a36Sopenharmony_ci goto done; 263662306a36Sopenharmony_ci } 263762306a36Sopenharmony_ci if (qh->qh_state == QH_STATE_IDLE) { 263862306a36Sopenharmony_ci status = qh_schedule(oxu, qh); 263962306a36Sopenharmony_ci if (status != 0) 264062306a36Sopenharmony_ci goto done; 264162306a36Sopenharmony_ci } 264262306a36Sopenharmony_ci 264362306a36Sopenharmony_ci /* then queue the urb's tds to the qh */ 264462306a36Sopenharmony_ci qh = qh_append_tds(oxu, urb, qtd_list, epnum, &urb->ep->hcpriv); 264562306a36Sopenharmony_ci BUG_ON(qh == NULL); 264662306a36Sopenharmony_ci 264762306a36Sopenharmony_ci /* ... update usbfs periodic stats */ 264862306a36Sopenharmony_ci oxu_to_hcd(oxu)->self.bandwidth_int_reqs++; 264962306a36Sopenharmony_ci 265062306a36Sopenharmony_cidone: 265162306a36Sopenharmony_ci spin_unlock_irqrestore(&oxu->lock, flags); 265262306a36Sopenharmony_ci if (status) 265362306a36Sopenharmony_ci qtd_list_free(oxu, urb, qtd_list); 265462306a36Sopenharmony_ci 265562306a36Sopenharmony_ci return status; 265662306a36Sopenharmony_ci} 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_cistatic inline int itd_submit(struct oxu_hcd *oxu, struct urb *urb, 265962306a36Sopenharmony_ci gfp_t mem_flags) 266062306a36Sopenharmony_ci{ 266162306a36Sopenharmony_ci oxu_dbg(oxu, "iso support is missing!\n"); 266262306a36Sopenharmony_ci return -ENOSYS; 266362306a36Sopenharmony_ci} 266462306a36Sopenharmony_ci 266562306a36Sopenharmony_cistatic inline int sitd_submit(struct oxu_hcd *oxu, struct urb *urb, 266662306a36Sopenharmony_ci gfp_t mem_flags) 266762306a36Sopenharmony_ci{ 266862306a36Sopenharmony_ci oxu_dbg(oxu, "split iso support is missing!\n"); 266962306a36Sopenharmony_ci return -ENOSYS; 267062306a36Sopenharmony_ci} 267162306a36Sopenharmony_ci 267262306a36Sopenharmony_cistatic void scan_periodic(struct oxu_hcd *oxu) 267362306a36Sopenharmony_ci{ 267462306a36Sopenharmony_ci unsigned frame, clock, now_uframe, mod; 267562306a36Sopenharmony_ci unsigned modified; 267662306a36Sopenharmony_ci 267762306a36Sopenharmony_ci mod = oxu->periodic_size << 3; 267862306a36Sopenharmony_ci 267962306a36Sopenharmony_ci /* 268062306a36Sopenharmony_ci * When running, scan from last scan point up to "now" 268162306a36Sopenharmony_ci * else clean up by scanning everything that's left. 268262306a36Sopenharmony_ci * Touches as few pages as possible: cache-friendly. 268362306a36Sopenharmony_ci */ 268462306a36Sopenharmony_ci now_uframe = oxu->next_uframe; 268562306a36Sopenharmony_ci if (HC_IS_RUNNING(oxu_to_hcd(oxu)->state)) 268662306a36Sopenharmony_ci clock = readl(&oxu->regs->frame_index); 268762306a36Sopenharmony_ci else 268862306a36Sopenharmony_ci clock = now_uframe + mod - 1; 268962306a36Sopenharmony_ci clock %= mod; 269062306a36Sopenharmony_ci 269162306a36Sopenharmony_ci for (;;) { 269262306a36Sopenharmony_ci union ehci_shadow q, *q_p; 269362306a36Sopenharmony_ci __le32 type, *hw_p; 269462306a36Sopenharmony_ci 269562306a36Sopenharmony_ci /* don't scan past the live uframe */ 269662306a36Sopenharmony_ci frame = now_uframe >> 3; 269762306a36Sopenharmony_ci if (frame != (clock >> 3)) { 269862306a36Sopenharmony_ci /* safe to scan the whole frame at once */ 269962306a36Sopenharmony_ci now_uframe |= 0x07; 270062306a36Sopenharmony_ci } 270162306a36Sopenharmony_ci 270262306a36Sopenharmony_cirestart: 270362306a36Sopenharmony_ci /* scan each element in frame's queue for completions */ 270462306a36Sopenharmony_ci q_p = &oxu->pshadow[frame]; 270562306a36Sopenharmony_ci hw_p = &oxu->periodic[frame]; 270662306a36Sopenharmony_ci q.ptr = q_p->ptr; 270762306a36Sopenharmony_ci type = Q_NEXT_TYPE(*hw_p); 270862306a36Sopenharmony_ci modified = 0; 270962306a36Sopenharmony_ci 271062306a36Sopenharmony_ci while (q.ptr != NULL) { 271162306a36Sopenharmony_ci union ehci_shadow temp; 271262306a36Sopenharmony_ci 271362306a36Sopenharmony_ci switch (type) { 271462306a36Sopenharmony_ci case Q_TYPE_QH: 271562306a36Sopenharmony_ci /* handle any completions */ 271662306a36Sopenharmony_ci temp.qh = qh_get(q.qh); 271762306a36Sopenharmony_ci type = Q_NEXT_TYPE(q.qh->hw_next); 271862306a36Sopenharmony_ci q = q.qh->qh_next; 271962306a36Sopenharmony_ci modified = qh_completions(oxu, temp.qh); 272062306a36Sopenharmony_ci if (unlikely(list_empty(&temp.qh->qtd_list))) 272162306a36Sopenharmony_ci intr_deschedule(oxu, temp.qh); 272262306a36Sopenharmony_ci qh_put(temp.qh); 272362306a36Sopenharmony_ci break; 272462306a36Sopenharmony_ci default: 272562306a36Sopenharmony_ci oxu_dbg(oxu, "corrupt type %d frame %d shadow %p\n", 272662306a36Sopenharmony_ci type, frame, q.ptr); 272762306a36Sopenharmony_ci q.ptr = NULL; 272862306a36Sopenharmony_ci } 272962306a36Sopenharmony_ci 273062306a36Sopenharmony_ci /* assume completion callbacks modify the queue */ 273162306a36Sopenharmony_ci if (unlikely(modified)) 273262306a36Sopenharmony_ci goto restart; 273362306a36Sopenharmony_ci } 273462306a36Sopenharmony_ci 273562306a36Sopenharmony_ci /* Stop when we catch up to the HC */ 273662306a36Sopenharmony_ci 273762306a36Sopenharmony_ci /* FIXME: this assumes we won't get lapped when 273862306a36Sopenharmony_ci * latencies climb; that should be rare, but... 273962306a36Sopenharmony_ci * detect it, and just go all the way around. 274062306a36Sopenharmony_ci * FLR might help detect this case, so long as latencies 274162306a36Sopenharmony_ci * don't exceed periodic_size msec (default 1.024 sec). 274262306a36Sopenharmony_ci */ 274362306a36Sopenharmony_ci 274462306a36Sopenharmony_ci /* FIXME: likewise assumes HC doesn't halt mid-scan */ 274562306a36Sopenharmony_ci 274662306a36Sopenharmony_ci if (now_uframe == clock) { 274762306a36Sopenharmony_ci unsigned now; 274862306a36Sopenharmony_ci 274962306a36Sopenharmony_ci if (!HC_IS_RUNNING(oxu_to_hcd(oxu)->state)) 275062306a36Sopenharmony_ci break; 275162306a36Sopenharmony_ci oxu->next_uframe = now_uframe; 275262306a36Sopenharmony_ci now = readl(&oxu->regs->frame_index) % mod; 275362306a36Sopenharmony_ci if (now_uframe == now) 275462306a36Sopenharmony_ci break; 275562306a36Sopenharmony_ci 275662306a36Sopenharmony_ci /* rescan the rest of this frame, then ... */ 275762306a36Sopenharmony_ci clock = now; 275862306a36Sopenharmony_ci } else { 275962306a36Sopenharmony_ci now_uframe++; 276062306a36Sopenharmony_ci now_uframe %= mod; 276162306a36Sopenharmony_ci } 276262306a36Sopenharmony_ci } 276362306a36Sopenharmony_ci} 276462306a36Sopenharmony_ci 276562306a36Sopenharmony_ci/* On some systems, leaving remote wakeup enabled prevents system shutdown. 276662306a36Sopenharmony_ci * The firmware seems to think that powering off is a wakeup event! 276762306a36Sopenharmony_ci * This routine turns off remote wakeup and everything else, on all ports. 276862306a36Sopenharmony_ci */ 276962306a36Sopenharmony_cistatic void ehci_turn_off_all_ports(struct oxu_hcd *oxu) 277062306a36Sopenharmony_ci{ 277162306a36Sopenharmony_ci int port = HCS_N_PORTS(oxu->hcs_params); 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_ci while (port--) 277462306a36Sopenharmony_ci writel(PORT_RWC_BITS, &oxu->regs->port_status[port]); 277562306a36Sopenharmony_ci} 277662306a36Sopenharmony_ci 277762306a36Sopenharmony_cistatic void ehci_port_power(struct oxu_hcd *oxu, int is_on) 277862306a36Sopenharmony_ci{ 277962306a36Sopenharmony_ci unsigned port; 278062306a36Sopenharmony_ci 278162306a36Sopenharmony_ci if (!HCS_PPC(oxu->hcs_params)) 278262306a36Sopenharmony_ci return; 278362306a36Sopenharmony_ci 278462306a36Sopenharmony_ci oxu_dbg(oxu, "...power%s ports...\n", is_on ? "up" : "down"); 278562306a36Sopenharmony_ci for (port = HCS_N_PORTS(oxu->hcs_params); port > 0; ) { 278662306a36Sopenharmony_ci if (is_on) 278762306a36Sopenharmony_ci oxu_hub_control(oxu_to_hcd(oxu), SetPortFeature, 278862306a36Sopenharmony_ci USB_PORT_FEAT_POWER, port--, NULL, 0); 278962306a36Sopenharmony_ci else 279062306a36Sopenharmony_ci oxu_hub_control(oxu_to_hcd(oxu), ClearPortFeature, 279162306a36Sopenharmony_ci USB_PORT_FEAT_POWER, port--, NULL, 0); 279262306a36Sopenharmony_ci } 279362306a36Sopenharmony_ci 279462306a36Sopenharmony_ci msleep(20); 279562306a36Sopenharmony_ci} 279662306a36Sopenharmony_ci 279762306a36Sopenharmony_ci/* Called from some interrupts, timers, and so on. 279862306a36Sopenharmony_ci * It calls driver completion functions, after dropping oxu->lock. 279962306a36Sopenharmony_ci */ 280062306a36Sopenharmony_cistatic void ehci_work(struct oxu_hcd *oxu) 280162306a36Sopenharmony_ci{ 280262306a36Sopenharmony_ci timer_action_done(oxu, TIMER_IO_WATCHDOG); 280362306a36Sopenharmony_ci if (oxu->reclaim_ready) 280462306a36Sopenharmony_ci end_unlink_async(oxu); 280562306a36Sopenharmony_ci 280662306a36Sopenharmony_ci /* another CPU may drop oxu->lock during a schedule scan while 280762306a36Sopenharmony_ci * it reports urb completions. this flag guards against bogus 280862306a36Sopenharmony_ci * attempts at re-entrant schedule scanning. 280962306a36Sopenharmony_ci */ 281062306a36Sopenharmony_ci if (oxu->scanning) 281162306a36Sopenharmony_ci return; 281262306a36Sopenharmony_ci oxu->scanning = 1; 281362306a36Sopenharmony_ci scan_async(oxu); 281462306a36Sopenharmony_ci if (oxu->next_uframe != -1) 281562306a36Sopenharmony_ci scan_periodic(oxu); 281662306a36Sopenharmony_ci oxu->scanning = 0; 281762306a36Sopenharmony_ci 281862306a36Sopenharmony_ci /* the IO watchdog guards against hardware or driver bugs that 281962306a36Sopenharmony_ci * misplace IRQs, and should let us run completely without IRQs. 282062306a36Sopenharmony_ci * such lossage has been observed on both VT6202 and VT8235. 282162306a36Sopenharmony_ci */ 282262306a36Sopenharmony_ci if (HC_IS_RUNNING(oxu_to_hcd(oxu)->state) && 282362306a36Sopenharmony_ci (oxu->async->qh_next.ptr != NULL || 282462306a36Sopenharmony_ci oxu->periodic_sched != 0)) 282562306a36Sopenharmony_ci timer_action(oxu, TIMER_IO_WATCHDOG); 282662306a36Sopenharmony_ci} 282762306a36Sopenharmony_ci 282862306a36Sopenharmony_cistatic void unlink_async(struct oxu_hcd *oxu, struct ehci_qh *qh) 282962306a36Sopenharmony_ci{ 283062306a36Sopenharmony_ci /* if we need to use IAA and it's busy, defer */ 283162306a36Sopenharmony_ci if (qh->qh_state == QH_STATE_LINKED 283262306a36Sopenharmony_ci && oxu->reclaim 283362306a36Sopenharmony_ci && HC_IS_RUNNING(oxu_to_hcd(oxu)->state)) { 283462306a36Sopenharmony_ci struct ehci_qh *last; 283562306a36Sopenharmony_ci 283662306a36Sopenharmony_ci for (last = oxu->reclaim; 283762306a36Sopenharmony_ci last->reclaim; 283862306a36Sopenharmony_ci last = last->reclaim) 283962306a36Sopenharmony_ci continue; 284062306a36Sopenharmony_ci qh->qh_state = QH_STATE_UNLINK_WAIT; 284162306a36Sopenharmony_ci last->reclaim = qh; 284262306a36Sopenharmony_ci 284362306a36Sopenharmony_ci /* bypass IAA if the hc can't care */ 284462306a36Sopenharmony_ci } else if (!HC_IS_RUNNING(oxu_to_hcd(oxu)->state) && oxu->reclaim) 284562306a36Sopenharmony_ci end_unlink_async(oxu); 284662306a36Sopenharmony_ci 284762306a36Sopenharmony_ci /* something else might have unlinked the qh by now */ 284862306a36Sopenharmony_ci if (qh->qh_state == QH_STATE_LINKED) 284962306a36Sopenharmony_ci start_unlink_async(oxu, qh); 285062306a36Sopenharmony_ci} 285162306a36Sopenharmony_ci 285262306a36Sopenharmony_ci/* 285362306a36Sopenharmony_ci * USB host controller methods 285462306a36Sopenharmony_ci */ 285562306a36Sopenharmony_ci 285662306a36Sopenharmony_cistatic irqreturn_t oxu210_hcd_irq(struct usb_hcd *hcd) 285762306a36Sopenharmony_ci{ 285862306a36Sopenharmony_ci struct oxu_hcd *oxu = hcd_to_oxu(hcd); 285962306a36Sopenharmony_ci u32 status, pcd_status = 0; 286062306a36Sopenharmony_ci int bh; 286162306a36Sopenharmony_ci 286262306a36Sopenharmony_ci spin_lock(&oxu->lock); 286362306a36Sopenharmony_ci 286462306a36Sopenharmony_ci status = readl(&oxu->regs->status); 286562306a36Sopenharmony_ci 286662306a36Sopenharmony_ci /* e.g. cardbus physical eject */ 286762306a36Sopenharmony_ci if (status == ~(u32) 0) { 286862306a36Sopenharmony_ci oxu_dbg(oxu, "device removed\n"); 286962306a36Sopenharmony_ci goto dead; 287062306a36Sopenharmony_ci } 287162306a36Sopenharmony_ci 287262306a36Sopenharmony_ci /* Shared IRQ? */ 287362306a36Sopenharmony_ci status &= INTR_MASK; 287462306a36Sopenharmony_ci if (!status || unlikely(hcd->state == HC_STATE_HALT)) { 287562306a36Sopenharmony_ci spin_unlock(&oxu->lock); 287662306a36Sopenharmony_ci return IRQ_NONE; 287762306a36Sopenharmony_ci } 287862306a36Sopenharmony_ci 287962306a36Sopenharmony_ci /* clear (just) interrupts */ 288062306a36Sopenharmony_ci writel(status, &oxu->regs->status); 288162306a36Sopenharmony_ci readl(&oxu->regs->command); /* unblock posted write */ 288262306a36Sopenharmony_ci bh = 0; 288362306a36Sopenharmony_ci 288462306a36Sopenharmony_ci#ifdef OXU_VERBOSE_DEBUG 288562306a36Sopenharmony_ci /* unrequested/ignored: Frame List Rollover */ 288662306a36Sopenharmony_ci dbg_status(oxu, "irq", status); 288762306a36Sopenharmony_ci#endif 288862306a36Sopenharmony_ci 288962306a36Sopenharmony_ci /* INT, ERR, and IAA interrupt rates can be throttled */ 289062306a36Sopenharmony_ci 289162306a36Sopenharmony_ci /* normal [4.15.1.2] or error [4.15.1.1] completion */ 289262306a36Sopenharmony_ci if (likely((status & (STS_INT|STS_ERR)) != 0)) 289362306a36Sopenharmony_ci bh = 1; 289462306a36Sopenharmony_ci 289562306a36Sopenharmony_ci /* complete the unlinking of some qh [4.15.2.3] */ 289662306a36Sopenharmony_ci if (status & STS_IAA) { 289762306a36Sopenharmony_ci oxu->reclaim_ready = 1; 289862306a36Sopenharmony_ci bh = 1; 289962306a36Sopenharmony_ci } 290062306a36Sopenharmony_ci 290162306a36Sopenharmony_ci /* remote wakeup [4.3.1] */ 290262306a36Sopenharmony_ci if (status & STS_PCD) { 290362306a36Sopenharmony_ci unsigned i = HCS_N_PORTS(oxu->hcs_params); 290462306a36Sopenharmony_ci pcd_status = status; 290562306a36Sopenharmony_ci 290662306a36Sopenharmony_ci /* resume root hub? */ 290762306a36Sopenharmony_ci if (!(readl(&oxu->regs->command) & CMD_RUN)) 290862306a36Sopenharmony_ci usb_hcd_resume_root_hub(hcd); 290962306a36Sopenharmony_ci 291062306a36Sopenharmony_ci while (i--) { 291162306a36Sopenharmony_ci int pstatus = readl(&oxu->regs->port_status[i]); 291262306a36Sopenharmony_ci 291362306a36Sopenharmony_ci if (pstatus & PORT_OWNER) 291462306a36Sopenharmony_ci continue; 291562306a36Sopenharmony_ci if (!(pstatus & PORT_RESUME) 291662306a36Sopenharmony_ci || oxu->reset_done[i] != 0) 291762306a36Sopenharmony_ci continue; 291862306a36Sopenharmony_ci 291962306a36Sopenharmony_ci /* start USB_RESUME_TIMEOUT resume signaling from this 292062306a36Sopenharmony_ci * port, and make hub_wq collect PORT_STAT_C_SUSPEND to 292162306a36Sopenharmony_ci * stop that signaling. 292262306a36Sopenharmony_ci */ 292362306a36Sopenharmony_ci oxu->reset_done[i] = jiffies + 292462306a36Sopenharmony_ci msecs_to_jiffies(USB_RESUME_TIMEOUT); 292562306a36Sopenharmony_ci oxu_dbg(oxu, "port %d remote wakeup\n", i + 1); 292662306a36Sopenharmony_ci mod_timer(&hcd->rh_timer, oxu->reset_done[i]); 292762306a36Sopenharmony_ci } 292862306a36Sopenharmony_ci } 292962306a36Sopenharmony_ci 293062306a36Sopenharmony_ci /* PCI errors [4.15.2.4] */ 293162306a36Sopenharmony_ci if (unlikely((status & STS_FATAL) != 0)) { 293262306a36Sopenharmony_ci /* bogus "fatal" IRQs appear on some chips... why? */ 293362306a36Sopenharmony_ci status = readl(&oxu->regs->status); 293462306a36Sopenharmony_ci dbg_cmd(oxu, "fatal", readl(&oxu->regs->command)); 293562306a36Sopenharmony_ci dbg_status(oxu, "fatal", status); 293662306a36Sopenharmony_ci if (status & STS_HALT) { 293762306a36Sopenharmony_ci oxu_err(oxu, "fatal error\n"); 293862306a36Sopenharmony_cidead: 293962306a36Sopenharmony_ci ehci_reset(oxu); 294062306a36Sopenharmony_ci writel(0, &oxu->regs->configured_flag); 294162306a36Sopenharmony_ci usb_hc_died(hcd); 294262306a36Sopenharmony_ci /* generic layer kills/unlinks all urbs, then 294362306a36Sopenharmony_ci * uses oxu_stop to clean up the rest 294462306a36Sopenharmony_ci */ 294562306a36Sopenharmony_ci bh = 1; 294662306a36Sopenharmony_ci } 294762306a36Sopenharmony_ci } 294862306a36Sopenharmony_ci 294962306a36Sopenharmony_ci if (bh) 295062306a36Sopenharmony_ci ehci_work(oxu); 295162306a36Sopenharmony_ci spin_unlock(&oxu->lock); 295262306a36Sopenharmony_ci if (pcd_status & STS_PCD) 295362306a36Sopenharmony_ci usb_hcd_poll_rh_status(hcd); 295462306a36Sopenharmony_ci return IRQ_HANDLED; 295562306a36Sopenharmony_ci} 295662306a36Sopenharmony_ci 295762306a36Sopenharmony_cistatic irqreturn_t oxu_irq(struct usb_hcd *hcd) 295862306a36Sopenharmony_ci{ 295962306a36Sopenharmony_ci struct oxu_hcd *oxu = hcd_to_oxu(hcd); 296062306a36Sopenharmony_ci int ret = IRQ_HANDLED; 296162306a36Sopenharmony_ci 296262306a36Sopenharmony_ci u32 status = oxu_readl(hcd->regs, OXU_CHIPIRQSTATUS); 296362306a36Sopenharmony_ci u32 enable = oxu_readl(hcd->regs, OXU_CHIPIRQEN_SET); 296462306a36Sopenharmony_ci 296562306a36Sopenharmony_ci /* Disable all interrupt */ 296662306a36Sopenharmony_ci oxu_writel(hcd->regs, OXU_CHIPIRQEN_CLR, enable); 296762306a36Sopenharmony_ci 296862306a36Sopenharmony_ci if ((oxu->is_otg && (status & OXU_USBOTGI)) || 296962306a36Sopenharmony_ci (!oxu->is_otg && (status & OXU_USBSPHI))) 297062306a36Sopenharmony_ci oxu210_hcd_irq(hcd); 297162306a36Sopenharmony_ci else 297262306a36Sopenharmony_ci ret = IRQ_NONE; 297362306a36Sopenharmony_ci 297462306a36Sopenharmony_ci /* Enable all interrupt back */ 297562306a36Sopenharmony_ci oxu_writel(hcd->regs, OXU_CHIPIRQEN_SET, enable); 297662306a36Sopenharmony_ci 297762306a36Sopenharmony_ci return ret; 297862306a36Sopenharmony_ci} 297962306a36Sopenharmony_ci 298062306a36Sopenharmony_cistatic void oxu_watchdog(struct timer_list *t) 298162306a36Sopenharmony_ci{ 298262306a36Sopenharmony_ci struct oxu_hcd *oxu = from_timer(oxu, t, watchdog); 298362306a36Sopenharmony_ci unsigned long flags; 298462306a36Sopenharmony_ci 298562306a36Sopenharmony_ci spin_lock_irqsave(&oxu->lock, flags); 298662306a36Sopenharmony_ci 298762306a36Sopenharmony_ci /* lost IAA irqs wedge things badly; seen with a vt8235 */ 298862306a36Sopenharmony_ci if (oxu->reclaim) { 298962306a36Sopenharmony_ci u32 status = readl(&oxu->regs->status); 299062306a36Sopenharmony_ci if (status & STS_IAA) { 299162306a36Sopenharmony_ci oxu_vdbg(oxu, "lost IAA\n"); 299262306a36Sopenharmony_ci writel(STS_IAA, &oxu->regs->status); 299362306a36Sopenharmony_ci oxu->reclaim_ready = 1; 299462306a36Sopenharmony_ci } 299562306a36Sopenharmony_ci } 299662306a36Sopenharmony_ci 299762306a36Sopenharmony_ci /* stop async processing after it's idled a bit */ 299862306a36Sopenharmony_ci if (test_bit(TIMER_ASYNC_OFF, &oxu->actions)) 299962306a36Sopenharmony_ci start_unlink_async(oxu, oxu->async); 300062306a36Sopenharmony_ci 300162306a36Sopenharmony_ci /* oxu could run by timer, without IRQs ... */ 300262306a36Sopenharmony_ci ehci_work(oxu); 300362306a36Sopenharmony_ci 300462306a36Sopenharmony_ci spin_unlock_irqrestore(&oxu->lock, flags); 300562306a36Sopenharmony_ci} 300662306a36Sopenharmony_ci 300762306a36Sopenharmony_ci/* One-time init, only for memory state. 300862306a36Sopenharmony_ci */ 300962306a36Sopenharmony_cistatic int oxu_hcd_init(struct usb_hcd *hcd) 301062306a36Sopenharmony_ci{ 301162306a36Sopenharmony_ci struct oxu_hcd *oxu = hcd_to_oxu(hcd); 301262306a36Sopenharmony_ci u32 temp; 301362306a36Sopenharmony_ci int retval; 301462306a36Sopenharmony_ci u32 hcc_params; 301562306a36Sopenharmony_ci 301662306a36Sopenharmony_ci spin_lock_init(&oxu->lock); 301762306a36Sopenharmony_ci 301862306a36Sopenharmony_ci timer_setup(&oxu->watchdog, oxu_watchdog, 0); 301962306a36Sopenharmony_ci 302062306a36Sopenharmony_ci /* 302162306a36Sopenharmony_ci * hw default: 1K periodic list heads, one per frame. 302262306a36Sopenharmony_ci * periodic_size can shrink by USBCMD update if hcc_params allows. 302362306a36Sopenharmony_ci */ 302462306a36Sopenharmony_ci oxu->periodic_size = DEFAULT_I_TDPS; 302562306a36Sopenharmony_ci retval = ehci_mem_init(oxu, GFP_KERNEL); 302662306a36Sopenharmony_ci if (retval < 0) 302762306a36Sopenharmony_ci return retval; 302862306a36Sopenharmony_ci 302962306a36Sopenharmony_ci /* controllers may cache some of the periodic schedule ... */ 303062306a36Sopenharmony_ci hcc_params = readl(&oxu->caps->hcc_params); 303162306a36Sopenharmony_ci if (HCC_ISOC_CACHE(hcc_params)) /* full frame cache */ 303262306a36Sopenharmony_ci oxu->i_thresh = 8; 303362306a36Sopenharmony_ci else /* N microframes cached */ 303462306a36Sopenharmony_ci oxu->i_thresh = 2 + HCC_ISOC_THRES(hcc_params); 303562306a36Sopenharmony_ci 303662306a36Sopenharmony_ci oxu->reclaim = NULL; 303762306a36Sopenharmony_ci oxu->reclaim_ready = 0; 303862306a36Sopenharmony_ci oxu->next_uframe = -1; 303962306a36Sopenharmony_ci 304062306a36Sopenharmony_ci /* 304162306a36Sopenharmony_ci * dedicate a qh for the async ring head, since we couldn't unlink 304262306a36Sopenharmony_ci * a 'real' qh without stopping the async schedule [4.8]. use it 304362306a36Sopenharmony_ci * as the 'reclamation list head' too. 304462306a36Sopenharmony_ci * its dummy is used in hw_alt_next of many tds, to prevent the qh 304562306a36Sopenharmony_ci * from automatically advancing to the next td after short reads. 304662306a36Sopenharmony_ci */ 304762306a36Sopenharmony_ci oxu->async->qh_next.qh = NULL; 304862306a36Sopenharmony_ci oxu->async->hw_next = QH_NEXT(oxu->async->qh_dma); 304962306a36Sopenharmony_ci oxu->async->hw_info1 = cpu_to_le32(QH_HEAD); 305062306a36Sopenharmony_ci oxu->async->hw_token = cpu_to_le32(QTD_STS_HALT); 305162306a36Sopenharmony_ci oxu->async->hw_qtd_next = EHCI_LIST_END; 305262306a36Sopenharmony_ci oxu->async->qh_state = QH_STATE_LINKED; 305362306a36Sopenharmony_ci oxu->async->hw_alt_next = QTD_NEXT(oxu->async->dummy->qtd_dma); 305462306a36Sopenharmony_ci 305562306a36Sopenharmony_ci /* clear interrupt enables, set irq latency */ 305662306a36Sopenharmony_ci if (log2_irq_thresh < 0 || log2_irq_thresh > 6) 305762306a36Sopenharmony_ci log2_irq_thresh = 0; 305862306a36Sopenharmony_ci temp = 1 << (16 + log2_irq_thresh); 305962306a36Sopenharmony_ci if (HCC_CANPARK(hcc_params)) { 306062306a36Sopenharmony_ci /* HW default park == 3, on hardware that supports it (like 306162306a36Sopenharmony_ci * NVidia and ALI silicon), maximizes throughput on the async 306262306a36Sopenharmony_ci * schedule by avoiding QH fetches between transfers. 306362306a36Sopenharmony_ci * 306462306a36Sopenharmony_ci * With fast usb storage devices and NForce2, "park" seems to 306562306a36Sopenharmony_ci * make problems: throughput reduction (!), data errors... 306662306a36Sopenharmony_ci */ 306762306a36Sopenharmony_ci if (park) { 306862306a36Sopenharmony_ci park = min(park, (unsigned) 3); 306962306a36Sopenharmony_ci temp |= CMD_PARK; 307062306a36Sopenharmony_ci temp |= park << 8; 307162306a36Sopenharmony_ci } 307262306a36Sopenharmony_ci oxu_dbg(oxu, "park %d\n", park); 307362306a36Sopenharmony_ci } 307462306a36Sopenharmony_ci if (HCC_PGM_FRAMELISTLEN(hcc_params)) { 307562306a36Sopenharmony_ci /* periodic schedule size can be smaller than default */ 307662306a36Sopenharmony_ci temp &= ~(3 << 2); 307762306a36Sopenharmony_ci temp |= (EHCI_TUNE_FLS << 2); 307862306a36Sopenharmony_ci } 307962306a36Sopenharmony_ci oxu->command = temp; 308062306a36Sopenharmony_ci 308162306a36Sopenharmony_ci return 0; 308262306a36Sopenharmony_ci} 308362306a36Sopenharmony_ci 308462306a36Sopenharmony_ci/* Called during probe() after chip reset completes. 308562306a36Sopenharmony_ci */ 308662306a36Sopenharmony_cistatic int oxu_reset(struct usb_hcd *hcd) 308762306a36Sopenharmony_ci{ 308862306a36Sopenharmony_ci struct oxu_hcd *oxu = hcd_to_oxu(hcd); 308962306a36Sopenharmony_ci 309062306a36Sopenharmony_ci spin_lock_init(&oxu->mem_lock); 309162306a36Sopenharmony_ci INIT_LIST_HEAD(&oxu->urb_list); 309262306a36Sopenharmony_ci oxu->urb_len = 0; 309362306a36Sopenharmony_ci 309462306a36Sopenharmony_ci if (oxu->is_otg) { 309562306a36Sopenharmony_ci oxu->caps = hcd->regs + OXU_OTG_CAP_OFFSET; 309662306a36Sopenharmony_ci oxu->regs = hcd->regs + OXU_OTG_CAP_OFFSET + \ 309762306a36Sopenharmony_ci HC_LENGTH(readl(&oxu->caps->hc_capbase)); 309862306a36Sopenharmony_ci 309962306a36Sopenharmony_ci oxu->mem = hcd->regs + OXU_SPH_MEM; 310062306a36Sopenharmony_ci } else { 310162306a36Sopenharmony_ci oxu->caps = hcd->regs + OXU_SPH_CAP_OFFSET; 310262306a36Sopenharmony_ci oxu->regs = hcd->regs + OXU_SPH_CAP_OFFSET + \ 310362306a36Sopenharmony_ci HC_LENGTH(readl(&oxu->caps->hc_capbase)); 310462306a36Sopenharmony_ci 310562306a36Sopenharmony_ci oxu->mem = hcd->regs + OXU_OTG_MEM; 310662306a36Sopenharmony_ci } 310762306a36Sopenharmony_ci 310862306a36Sopenharmony_ci oxu->hcs_params = readl(&oxu->caps->hcs_params); 310962306a36Sopenharmony_ci oxu->sbrn = 0x20; 311062306a36Sopenharmony_ci 311162306a36Sopenharmony_ci return oxu_hcd_init(hcd); 311262306a36Sopenharmony_ci} 311362306a36Sopenharmony_ci 311462306a36Sopenharmony_cistatic int oxu_run(struct usb_hcd *hcd) 311562306a36Sopenharmony_ci{ 311662306a36Sopenharmony_ci struct oxu_hcd *oxu = hcd_to_oxu(hcd); 311762306a36Sopenharmony_ci int retval; 311862306a36Sopenharmony_ci u32 temp, hcc_params; 311962306a36Sopenharmony_ci 312062306a36Sopenharmony_ci hcd->uses_new_polling = 1; 312162306a36Sopenharmony_ci 312262306a36Sopenharmony_ci /* EHCI spec section 4.1 */ 312362306a36Sopenharmony_ci retval = ehci_reset(oxu); 312462306a36Sopenharmony_ci if (retval != 0) { 312562306a36Sopenharmony_ci ehci_mem_cleanup(oxu); 312662306a36Sopenharmony_ci return retval; 312762306a36Sopenharmony_ci } 312862306a36Sopenharmony_ci writel(oxu->periodic_dma, &oxu->regs->frame_list); 312962306a36Sopenharmony_ci writel((u32) oxu->async->qh_dma, &oxu->regs->async_next); 313062306a36Sopenharmony_ci 313162306a36Sopenharmony_ci /* hcc_params controls whether oxu->regs->segment must (!!!) 313262306a36Sopenharmony_ci * be used; it constrains QH/ITD/SITD and QTD locations. 313362306a36Sopenharmony_ci * dma_pool consistent memory always uses segment zero. 313462306a36Sopenharmony_ci * streaming mappings for I/O buffers, like dma_map_single(), 313562306a36Sopenharmony_ci * can return segments above 4GB, if the device allows. 313662306a36Sopenharmony_ci * 313762306a36Sopenharmony_ci * NOTE: the dma mask is visible through dev->dma_mask, so 313862306a36Sopenharmony_ci * drivers can pass this info along ... like NETIF_F_HIGHDMA, 313962306a36Sopenharmony_ci * Scsi_Host.highmem_io, and so forth. It's readonly to all 314062306a36Sopenharmony_ci * host side drivers though. 314162306a36Sopenharmony_ci */ 314262306a36Sopenharmony_ci hcc_params = readl(&oxu->caps->hcc_params); 314362306a36Sopenharmony_ci if (HCC_64BIT_ADDR(hcc_params)) 314462306a36Sopenharmony_ci writel(0, &oxu->regs->segment); 314562306a36Sopenharmony_ci 314662306a36Sopenharmony_ci oxu->command &= ~(CMD_LRESET | CMD_IAAD | CMD_PSE | 314762306a36Sopenharmony_ci CMD_ASE | CMD_RESET); 314862306a36Sopenharmony_ci oxu->command |= CMD_RUN; 314962306a36Sopenharmony_ci writel(oxu->command, &oxu->regs->command); 315062306a36Sopenharmony_ci dbg_cmd(oxu, "init", oxu->command); 315162306a36Sopenharmony_ci 315262306a36Sopenharmony_ci /* 315362306a36Sopenharmony_ci * Start, enabling full USB 2.0 functionality ... usb 1.1 devices 315462306a36Sopenharmony_ci * are explicitly handed to companion controller(s), so no TT is 315562306a36Sopenharmony_ci * involved with the root hub. (Except where one is integrated, 315662306a36Sopenharmony_ci * and there's no companion controller unless maybe for USB OTG.) 315762306a36Sopenharmony_ci */ 315862306a36Sopenharmony_ci hcd->state = HC_STATE_RUNNING; 315962306a36Sopenharmony_ci writel(FLAG_CF, &oxu->regs->configured_flag); 316062306a36Sopenharmony_ci readl(&oxu->regs->command); /* unblock posted writes */ 316162306a36Sopenharmony_ci 316262306a36Sopenharmony_ci temp = HC_VERSION(readl(&oxu->caps->hc_capbase)); 316362306a36Sopenharmony_ci oxu_info(oxu, "USB %x.%x started, quasi-EHCI %x.%02x, driver %s%s\n", 316462306a36Sopenharmony_ci ((oxu->sbrn & 0xf0)>>4), (oxu->sbrn & 0x0f), 316562306a36Sopenharmony_ci temp >> 8, temp & 0xff, DRIVER_VERSION, 316662306a36Sopenharmony_ci ignore_oc ? ", overcurrent ignored" : ""); 316762306a36Sopenharmony_ci 316862306a36Sopenharmony_ci writel(INTR_MASK, &oxu->regs->intr_enable); /* Turn On Interrupts */ 316962306a36Sopenharmony_ci 317062306a36Sopenharmony_ci return 0; 317162306a36Sopenharmony_ci} 317262306a36Sopenharmony_ci 317362306a36Sopenharmony_cistatic void oxu_stop(struct usb_hcd *hcd) 317462306a36Sopenharmony_ci{ 317562306a36Sopenharmony_ci struct oxu_hcd *oxu = hcd_to_oxu(hcd); 317662306a36Sopenharmony_ci 317762306a36Sopenharmony_ci /* Turn off port power on all root hub ports. */ 317862306a36Sopenharmony_ci ehci_port_power(oxu, 0); 317962306a36Sopenharmony_ci 318062306a36Sopenharmony_ci /* no more interrupts ... */ 318162306a36Sopenharmony_ci del_timer_sync(&oxu->watchdog); 318262306a36Sopenharmony_ci 318362306a36Sopenharmony_ci spin_lock_irq(&oxu->lock); 318462306a36Sopenharmony_ci if (HC_IS_RUNNING(hcd->state)) 318562306a36Sopenharmony_ci ehci_quiesce(oxu); 318662306a36Sopenharmony_ci 318762306a36Sopenharmony_ci ehci_reset(oxu); 318862306a36Sopenharmony_ci writel(0, &oxu->regs->intr_enable); 318962306a36Sopenharmony_ci spin_unlock_irq(&oxu->lock); 319062306a36Sopenharmony_ci 319162306a36Sopenharmony_ci /* let companion controllers work when we aren't */ 319262306a36Sopenharmony_ci writel(0, &oxu->regs->configured_flag); 319362306a36Sopenharmony_ci 319462306a36Sopenharmony_ci /* root hub is shut down separately (first, when possible) */ 319562306a36Sopenharmony_ci spin_lock_irq(&oxu->lock); 319662306a36Sopenharmony_ci if (oxu->async) 319762306a36Sopenharmony_ci ehci_work(oxu); 319862306a36Sopenharmony_ci spin_unlock_irq(&oxu->lock); 319962306a36Sopenharmony_ci ehci_mem_cleanup(oxu); 320062306a36Sopenharmony_ci 320162306a36Sopenharmony_ci dbg_status(oxu, "oxu_stop completed", readl(&oxu->regs->status)); 320262306a36Sopenharmony_ci} 320362306a36Sopenharmony_ci 320462306a36Sopenharmony_ci/* Kick in for silicon on any bus (not just pci, etc). 320562306a36Sopenharmony_ci * This forcibly disables dma and IRQs, helping kexec and other cases 320662306a36Sopenharmony_ci * where the next system software may expect clean state. 320762306a36Sopenharmony_ci */ 320862306a36Sopenharmony_cistatic void oxu_shutdown(struct usb_hcd *hcd) 320962306a36Sopenharmony_ci{ 321062306a36Sopenharmony_ci struct oxu_hcd *oxu = hcd_to_oxu(hcd); 321162306a36Sopenharmony_ci 321262306a36Sopenharmony_ci (void) ehci_halt(oxu); 321362306a36Sopenharmony_ci ehci_turn_off_all_ports(oxu); 321462306a36Sopenharmony_ci 321562306a36Sopenharmony_ci /* make BIOS/etc use companion controller during reboot */ 321662306a36Sopenharmony_ci writel(0, &oxu->regs->configured_flag); 321762306a36Sopenharmony_ci 321862306a36Sopenharmony_ci /* unblock posted writes */ 321962306a36Sopenharmony_ci readl(&oxu->regs->configured_flag); 322062306a36Sopenharmony_ci} 322162306a36Sopenharmony_ci 322262306a36Sopenharmony_ci/* Non-error returns are a promise to giveback() the urb later 322362306a36Sopenharmony_ci * we drop ownership so next owner (or urb unlink) can get it 322462306a36Sopenharmony_ci * 322562306a36Sopenharmony_ci * urb + dev is in hcd.self.controller.urb_list 322662306a36Sopenharmony_ci * we're queueing TDs onto software and hardware lists 322762306a36Sopenharmony_ci * 322862306a36Sopenharmony_ci * hcd-specific init for hcpriv hasn't been done yet 322962306a36Sopenharmony_ci * 323062306a36Sopenharmony_ci * NOTE: control, bulk, and interrupt share the same code to append TDs 323162306a36Sopenharmony_ci * to a (possibly active) QH, and the same QH scanning code. 323262306a36Sopenharmony_ci */ 323362306a36Sopenharmony_cistatic int __oxu_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, 323462306a36Sopenharmony_ci gfp_t mem_flags) 323562306a36Sopenharmony_ci{ 323662306a36Sopenharmony_ci struct oxu_hcd *oxu = hcd_to_oxu(hcd); 323762306a36Sopenharmony_ci struct list_head qtd_list; 323862306a36Sopenharmony_ci 323962306a36Sopenharmony_ci INIT_LIST_HEAD(&qtd_list); 324062306a36Sopenharmony_ci 324162306a36Sopenharmony_ci switch (usb_pipetype(urb->pipe)) { 324262306a36Sopenharmony_ci case PIPE_CONTROL: 324362306a36Sopenharmony_ci case PIPE_BULK: 324462306a36Sopenharmony_ci default: 324562306a36Sopenharmony_ci if (!qh_urb_transaction(oxu, urb, &qtd_list, mem_flags)) 324662306a36Sopenharmony_ci return -ENOMEM; 324762306a36Sopenharmony_ci return submit_async(oxu, urb, &qtd_list, mem_flags); 324862306a36Sopenharmony_ci 324962306a36Sopenharmony_ci case PIPE_INTERRUPT: 325062306a36Sopenharmony_ci if (!qh_urb_transaction(oxu, urb, &qtd_list, mem_flags)) 325162306a36Sopenharmony_ci return -ENOMEM; 325262306a36Sopenharmony_ci return intr_submit(oxu, urb, &qtd_list, mem_flags); 325362306a36Sopenharmony_ci 325462306a36Sopenharmony_ci case PIPE_ISOCHRONOUS: 325562306a36Sopenharmony_ci if (urb->dev->speed == USB_SPEED_HIGH) 325662306a36Sopenharmony_ci return itd_submit(oxu, urb, mem_flags); 325762306a36Sopenharmony_ci else 325862306a36Sopenharmony_ci return sitd_submit(oxu, urb, mem_flags); 325962306a36Sopenharmony_ci } 326062306a36Sopenharmony_ci} 326162306a36Sopenharmony_ci 326262306a36Sopenharmony_ci/* This function is responsible for breaking URBs with big data size 326362306a36Sopenharmony_ci * into smaller size and processing small urbs in sequence. 326462306a36Sopenharmony_ci */ 326562306a36Sopenharmony_cistatic int oxu_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, 326662306a36Sopenharmony_ci gfp_t mem_flags) 326762306a36Sopenharmony_ci{ 326862306a36Sopenharmony_ci struct oxu_hcd *oxu = hcd_to_oxu(hcd); 326962306a36Sopenharmony_ci int num, rem; 327062306a36Sopenharmony_ci void *transfer_buffer; 327162306a36Sopenharmony_ci struct urb *murb; 327262306a36Sopenharmony_ci int i, ret; 327362306a36Sopenharmony_ci 327462306a36Sopenharmony_ci /* If not bulk pipe just enqueue the URB */ 327562306a36Sopenharmony_ci if (!usb_pipebulk(urb->pipe)) 327662306a36Sopenharmony_ci return __oxu_urb_enqueue(hcd, urb, mem_flags); 327762306a36Sopenharmony_ci 327862306a36Sopenharmony_ci /* Otherwise we should verify the USB transfer buffer size! */ 327962306a36Sopenharmony_ci transfer_buffer = urb->transfer_buffer; 328062306a36Sopenharmony_ci 328162306a36Sopenharmony_ci num = urb->transfer_buffer_length / 4096; 328262306a36Sopenharmony_ci rem = urb->transfer_buffer_length % 4096; 328362306a36Sopenharmony_ci if (rem != 0) 328462306a36Sopenharmony_ci num++; 328562306a36Sopenharmony_ci 328662306a36Sopenharmony_ci /* If URB is smaller than 4096 bytes just enqueue it! */ 328762306a36Sopenharmony_ci if (num == 1) 328862306a36Sopenharmony_ci return __oxu_urb_enqueue(hcd, urb, mem_flags); 328962306a36Sopenharmony_ci 329062306a36Sopenharmony_ci /* Ok, we have more job to do! :) */ 329162306a36Sopenharmony_ci 329262306a36Sopenharmony_ci for (i = 0; i < num - 1; i++) { 329362306a36Sopenharmony_ci /* Get free micro URB poll till a free urb is received */ 329462306a36Sopenharmony_ci 329562306a36Sopenharmony_ci do { 329662306a36Sopenharmony_ci murb = (struct urb *) oxu_murb_alloc(oxu); 329762306a36Sopenharmony_ci if (!murb) 329862306a36Sopenharmony_ci schedule(); 329962306a36Sopenharmony_ci } while (!murb); 330062306a36Sopenharmony_ci 330162306a36Sopenharmony_ci /* Coping the urb */ 330262306a36Sopenharmony_ci memcpy(murb, urb, sizeof(struct urb)); 330362306a36Sopenharmony_ci 330462306a36Sopenharmony_ci murb->transfer_buffer_length = 4096; 330562306a36Sopenharmony_ci murb->transfer_buffer = transfer_buffer + i * 4096; 330662306a36Sopenharmony_ci 330762306a36Sopenharmony_ci /* Null pointer for the encodes that this is a micro urb */ 330862306a36Sopenharmony_ci murb->complete = NULL; 330962306a36Sopenharmony_ci 331062306a36Sopenharmony_ci ((struct oxu_murb *) murb)->main = urb; 331162306a36Sopenharmony_ci ((struct oxu_murb *) murb)->last = 0; 331262306a36Sopenharmony_ci 331362306a36Sopenharmony_ci /* This loop is to guarantee urb to be processed when there's 331462306a36Sopenharmony_ci * not enough resources at a particular time by retrying. 331562306a36Sopenharmony_ci */ 331662306a36Sopenharmony_ci do { 331762306a36Sopenharmony_ci ret = __oxu_urb_enqueue(hcd, murb, mem_flags); 331862306a36Sopenharmony_ci if (ret) 331962306a36Sopenharmony_ci schedule(); 332062306a36Sopenharmony_ci } while (ret); 332162306a36Sopenharmony_ci } 332262306a36Sopenharmony_ci 332362306a36Sopenharmony_ci /* Last urb requires special handling */ 332462306a36Sopenharmony_ci 332562306a36Sopenharmony_ci /* Get free micro URB poll till a free urb is received */ 332662306a36Sopenharmony_ci do { 332762306a36Sopenharmony_ci murb = (struct urb *) oxu_murb_alloc(oxu); 332862306a36Sopenharmony_ci if (!murb) 332962306a36Sopenharmony_ci schedule(); 333062306a36Sopenharmony_ci } while (!murb); 333162306a36Sopenharmony_ci 333262306a36Sopenharmony_ci /* Coping the urb */ 333362306a36Sopenharmony_ci memcpy(murb, urb, sizeof(struct urb)); 333462306a36Sopenharmony_ci 333562306a36Sopenharmony_ci murb->transfer_buffer_length = rem > 0 ? rem : 4096; 333662306a36Sopenharmony_ci murb->transfer_buffer = transfer_buffer + (num - 1) * 4096; 333762306a36Sopenharmony_ci 333862306a36Sopenharmony_ci /* Null pointer for the encodes that this is a micro urb */ 333962306a36Sopenharmony_ci murb->complete = NULL; 334062306a36Sopenharmony_ci 334162306a36Sopenharmony_ci ((struct oxu_murb *) murb)->main = urb; 334262306a36Sopenharmony_ci ((struct oxu_murb *) murb)->last = 1; 334362306a36Sopenharmony_ci 334462306a36Sopenharmony_ci do { 334562306a36Sopenharmony_ci ret = __oxu_urb_enqueue(hcd, murb, mem_flags); 334662306a36Sopenharmony_ci if (ret) 334762306a36Sopenharmony_ci schedule(); 334862306a36Sopenharmony_ci } while (ret); 334962306a36Sopenharmony_ci 335062306a36Sopenharmony_ci return ret; 335162306a36Sopenharmony_ci} 335262306a36Sopenharmony_ci 335362306a36Sopenharmony_ci/* Remove from hardware lists. 335462306a36Sopenharmony_ci * Completions normally happen asynchronously 335562306a36Sopenharmony_ci */ 335662306a36Sopenharmony_cistatic int oxu_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) 335762306a36Sopenharmony_ci{ 335862306a36Sopenharmony_ci struct oxu_hcd *oxu = hcd_to_oxu(hcd); 335962306a36Sopenharmony_ci struct ehci_qh *qh; 336062306a36Sopenharmony_ci unsigned long flags; 336162306a36Sopenharmony_ci 336262306a36Sopenharmony_ci spin_lock_irqsave(&oxu->lock, flags); 336362306a36Sopenharmony_ci switch (usb_pipetype(urb->pipe)) { 336462306a36Sopenharmony_ci case PIPE_CONTROL: 336562306a36Sopenharmony_ci case PIPE_BULK: 336662306a36Sopenharmony_ci default: 336762306a36Sopenharmony_ci qh = (struct ehci_qh *) urb->hcpriv; 336862306a36Sopenharmony_ci if (!qh) 336962306a36Sopenharmony_ci break; 337062306a36Sopenharmony_ci unlink_async(oxu, qh); 337162306a36Sopenharmony_ci break; 337262306a36Sopenharmony_ci 337362306a36Sopenharmony_ci case PIPE_INTERRUPT: 337462306a36Sopenharmony_ci qh = (struct ehci_qh *) urb->hcpriv; 337562306a36Sopenharmony_ci if (!qh) 337662306a36Sopenharmony_ci break; 337762306a36Sopenharmony_ci switch (qh->qh_state) { 337862306a36Sopenharmony_ci case QH_STATE_LINKED: 337962306a36Sopenharmony_ci intr_deschedule(oxu, qh); 338062306a36Sopenharmony_ci fallthrough; 338162306a36Sopenharmony_ci case QH_STATE_IDLE: 338262306a36Sopenharmony_ci qh_completions(oxu, qh); 338362306a36Sopenharmony_ci break; 338462306a36Sopenharmony_ci default: 338562306a36Sopenharmony_ci oxu_dbg(oxu, "bogus qh %p state %d\n", 338662306a36Sopenharmony_ci qh, qh->qh_state); 338762306a36Sopenharmony_ci goto done; 338862306a36Sopenharmony_ci } 338962306a36Sopenharmony_ci 339062306a36Sopenharmony_ci /* reschedule QH iff another request is queued */ 339162306a36Sopenharmony_ci if (!list_empty(&qh->qtd_list) 339262306a36Sopenharmony_ci && HC_IS_RUNNING(hcd->state)) { 339362306a36Sopenharmony_ci int status; 339462306a36Sopenharmony_ci 339562306a36Sopenharmony_ci status = qh_schedule(oxu, qh); 339662306a36Sopenharmony_ci spin_unlock_irqrestore(&oxu->lock, flags); 339762306a36Sopenharmony_ci 339862306a36Sopenharmony_ci if (status != 0) { 339962306a36Sopenharmony_ci /* shouldn't happen often, but ... 340062306a36Sopenharmony_ci * FIXME kill those tds' urbs 340162306a36Sopenharmony_ci */ 340262306a36Sopenharmony_ci dev_err(hcd->self.controller, 340362306a36Sopenharmony_ci "can't reschedule qh %p, err %d\n", qh, 340462306a36Sopenharmony_ci status); 340562306a36Sopenharmony_ci } 340662306a36Sopenharmony_ci return status; 340762306a36Sopenharmony_ci } 340862306a36Sopenharmony_ci break; 340962306a36Sopenharmony_ci } 341062306a36Sopenharmony_cidone: 341162306a36Sopenharmony_ci spin_unlock_irqrestore(&oxu->lock, flags); 341262306a36Sopenharmony_ci return 0; 341362306a36Sopenharmony_ci} 341462306a36Sopenharmony_ci 341562306a36Sopenharmony_ci/* Bulk qh holds the data toggle */ 341662306a36Sopenharmony_cistatic void oxu_endpoint_disable(struct usb_hcd *hcd, 341762306a36Sopenharmony_ci struct usb_host_endpoint *ep) 341862306a36Sopenharmony_ci{ 341962306a36Sopenharmony_ci struct oxu_hcd *oxu = hcd_to_oxu(hcd); 342062306a36Sopenharmony_ci unsigned long flags; 342162306a36Sopenharmony_ci struct ehci_qh *qh, *tmp; 342262306a36Sopenharmony_ci 342362306a36Sopenharmony_ci /* ASSERT: any requests/urbs are being unlinked */ 342462306a36Sopenharmony_ci /* ASSERT: nobody can be submitting urbs for this any more */ 342562306a36Sopenharmony_ci 342662306a36Sopenharmony_cirescan: 342762306a36Sopenharmony_ci spin_lock_irqsave(&oxu->lock, flags); 342862306a36Sopenharmony_ci qh = ep->hcpriv; 342962306a36Sopenharmony_ci if (!qh) 343062306a36Sopenharmony_ci goto done; 343162306a36Sopenharmony_ci 343262306a36Sopenharmony_ci /* endpoints can be iso streams. for now, we don't 343362306a36Sopenharmony_ci * accelerate iso completions ... so spin a while. 343462306a36Sopenharmony_ci */ 343562306a36Sopenharmony_ci if (qh->hw_info1 == 0) { 343662306a36Sopenharmony_ci oxu_vdbg(oxu, "iso delay\n"); 343762306a36Sopenharmony_ci goto idle_timeout; 343862306a36Sopenharmony_ci } 343962306a36Sopenharmony_ci 344062306a36Sopenharmony_ci if (!HC_IS_RUNNING(hcd->state)) 344162306a36Sopenharmony_ci qh->qh_state = QH_STATE_IDLE; 344262306a36Sopenharmony_ci switch (qh->qh_state) { 344362306a36Sopenharmony_ci case QH_STATE_LINKED: 344462306a36Sopenharmony_ci for (tmp = oxu->async->qh_next.qh; 344562306a36Sopenharmony_ci tmp && tmp != qh; 344662306a36Sopenharmony_ci tmp = tmp->qh_next.qh) 344762306a36Sopenharmony_ci continue; 344862306a36Sopenharmony_ci /* periodic qh self-unlinks on empty */ 344962306a36Sopenharmony_ci if (!tmp) 345062306a36Sopenharmony_ci goto nogood; 345162306a36Sopenharmony_ci unlink_async(oxu, qh); 345262306a36Sopenharmony_ci fallthrough; 345362306a36Sopenharmony_ci case QH_STATE_UNLINK: /* wait for hw to finish? */ 345462306a36Sopenharmony_ciidle_timeout: 345562306a36Sopenharmony_ci spin_unlock_irqrestore(&oxu->lock, flags); 345662306a36Sopenharmony_ci schedule_timeout_uninterruptible(1); 345762306a36Sopenharmony_ci goto rescan; 345862306a36Sopenharmony_ci case QH_STATE_IDLE: /* fully unlinked */ 345962306a36Sopenharmony_ci if (list_empty(&qh->qtd_list)) { 346062306a36Sopenharmony_ci qh_put(qh); 346162306a36Sopenharmony_ci break; 346262306a36Sopenharmony_ci } 346362306a36Sopenharmony_ci fallthrough; 346462306a36Sopenharmony_ci default: 346562306a36Sopenharmony_cinogood: 346662306a36Sopenharmony_ci /* caller was supposed to have unlinked any requests; 346762306a36Sopenharmony_ci * that's not our job. just leak this memory. 346862306a36Sopenharmony_ci */ 346962306a36Sopenharmony_ci oxu_err(oxu, "qh %p (#%02x) state %d%s\n", 347062306a36Sopenharmony_ci qh, ep->desc.bEndpointAddress, qh->qh_state, 347162306a36Sopenharmony_ci list_empty(&qh->qtd_list) ? "" : "(has tds)"); 347262306a36Sopenharmony_ci break; 347362306a36Sopenharmony_ci } 347462306a36Sopenharmony_ci ep->hcpriv = NULL; 347562306a36Sopenharmony_cidone: 347662306a36Sopenharmony_ci spin_unlock_irqrestore(&oxu->lock, flags); 347762306a36Sopenharmony_ci} 347862306a36Sopenharmony_ci 347962306a36Sopenharmony_cistatic int oxu_get_frame(struct usb_hcd *hcd) 348062306a36Sopenharmony_ci{ 348162306a36Sopenharmony_ci struct oxu_hcd *oxu = hcd_to_oxu(hcd); 348262306a36Sopenharmony_ci 348362306a36Sopenharmony_ci return (readl(&oxu->regs->frame_index) >> 3) % 348462306a36Sopenharmony_ci oxu->periodic_size; 348562306a36Sopenharmony_ci} 348662306a36Sopenharmony_ci 348762306a36Sopenharmony_ci/* Build "status change" packet (one or two bytes) from HC registers */ 348862306a36Sopenharmony_cistatic int oxu_hub_status_data(struct usb_hcd *hcd, char *buf) 348962306a36Sopenharmony_ci{ 349062306a36Sopenharmony_ci struct oxu_hcd *oxu = hcd_to_oxu(hcd); 349162306a36Sopenharmony_ci u32 temp, mask, status = 0; 349262306a36Sopenharmony_ci int ports, i, retval = 1; 349362306a36Sopenharmony_ci unsigned long flags; 349462306a36Sopenharmony_ci 349562306a36Sopenharmony_ci /* if !PM, root hub timers won't get shut down ... */ 349662306a36Sopenharmony_ci if (!HC_IS_RUNNING(hcd->state)) 349762306a36Sopenharmony_ci return 0; 349862306a36Sopenharmony_ci 349962306a36Sopenharmony_ci /* init status to no-changes */ 350062306a36Sopenharmony_ci buf[0] = 0; 350162306a36Sopenharmony_ci ports = HCS_N_PORTS(oxu->hcs_params); 350262306a36Sopenharmony_ci if (ports > 7) { 350362306a36Sopenharmony_ci buf[1] = 0; 350462306a36Sopenharmony_ci retval++; 350562306a36Sopenharmony_ci } 350662306a36Sopenharmony_ci 350762306a36Sopenharmony_ci /* Some boards (mostly VIA?) report bogus overcurrent indications, 350862306a36Sopenharmony_ci * causing massive log spam unless we completely ignore them. It 350962306a36Sopenharmony_ci * may be relevant that VIA VT8235 controllers, where PORT_POWER is 351062306a36Sopenharmony_ci * always set, seem to clear PORT_OCC and PORT_CSC when writing to 351162306a36Sopenharmony_ci * PORT_POWER; that's surprising, but maybe within-spec. 351262306a36Sopenharmony_ci */ 351362306a36Sopenharmony_ci if (!ignore_oc) 351462306a36Sopenharmony_ci mask = PORT_CSC | PORT_PEC | PORT_OCC; 351562306a36Sopenharmony_ci else 351662306a36Sopenharmony_ci mask = PORT_CSC | PORT_PEC; 351762306a36Sopenharmony_ci 351862306a36Sopenharmony_ci /* no hub change reports (bit 0) for now (power, ...) */ 351962306a36Sopenharmony_ci 352062306a36Sopenharmony_ci /* port N changes (bit N)? */ 352162306a36Sopenharmony_ci spin_lock_irqsave(&oxu->lock, flags); 352262306a36Sopenharmony_ci for (i = 0; i < ports; i++) { 352362306a36Sopenharmony_ci temp = readl(&oxu->regs->port_status[i]); 352462306a36Sopenharmony_ci 352562306a36Sopenharmony_ci /* 352662306a36Sopenharmony_ci * Return status information even for ports with OWNER set. 352762306a36Sopenharmony_ci * Otherwise hub_wq wouldn't see the disconnect event when a 352862306a36Sopenharmony_ci * high-speed device is switched over to the companion 352962306a36Sopenharmony_ci * controller by the user. 353062306a36Sopenharmony_ci */ 353162306a36Sopenharmony_ci 353262306a36Sopenharmony_ci if (!(temp & PORT_CONNECT)) 353362306a36Sopenharmony_ci oxu->reset_done[i] = 0; 353462306a36Sopenharmony_ci if ((temp & mask) != 0 || ((temp & PORT_RESUME) != 0 && 353562306a36Sopenharmony_ci time_after_eq(jiffies, oxu->reset_done[i]))) { 353662306a36Sopenharmony_ci if (i < 7) 353762306a36Sopenharmony_ci buf[0] |= 1 << (i + 1); 353862306a36Sopenharmony_ci else 353962306a36Sopenharmony_ci buf[1] |= 1 << (i - 7); 354062306a36Sopenharmony_ci status = STS_PCD; 354162306a36Sopenharmony_ci } 354262306a36Sopenharmony_ci } 354362306a36Sopenharmony_ci /* FIXME autosuspend idle root hubs */ 354462306a36Sopenharmony_ci spin_unlock_irqrestore(&oxu->lock, flags); 354562306a36Sopenharmony_ci return status ? retval : 0; 354662306a36Sopenharmony_ci} 354762306a36Sopenharmony_ci 354862306a36Sopenharmony_ci/* Returns the speed of a device attached to a port on the root hub. */ 354962306a36Sopenharmony_cistatic inline unsigned int oxu_port_speed(struct oxu_hcd *oxu, 355062306a36Sopenharmony_ci unsigned int portsc) 355162306a36Sopenharmony_ci{ 355262306a36Sopenharmony_ci switch ((portsc >> 26) & 3) { 355362306a36Sopenharmony_ci case 0: 355462306a36Sopenharmony_ci return 0; 355562306a36Sopenharmony_ci case 1: 355662306a36Sopenharmony_ci return USB_PORT_STAT_LOW_SPEED; 355762306a36Sopenharmony_ci case 2: 355862306a36Sopenharmony_ci default: 355962306a36Sopenharmony_ci return USB_PORT_STAT_HIGH_SPEED; 356062306a36Sopenharmony_ci } 356162306a36Sopenharmony_ci} 356262306a36Sopenharmony_ci 356362306a36Sopenharmony_ci#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E) 356462306a36Sopenharmony_cistatic int oxu_hub_control(struct usb_hcd *hcd, u16 typeReq, 356562306a36Sopenharmony_ci u16 wValue, u16 wIndex, char *buf, u16 wLength) 356662306a36Sopenharmony_ci{ 356762306a36Sopenharmony_ci struct oxu_hcd *oxu = hcd_to_oxu(hcd); 356862306a36Sopenharmony_ci int ports = HCS_N_PORTS(oxu->hcs_params); 356962306a36Sopenharmony_ci u32 __iomem *status_reg = &oxu->regs->port_status[wIndex - 1]; 357062306a36Sopenharmony_ci u32 temp, status; 357162306a36Sopenharmony_ci unsigned long flags; 357262306a36Sopenharmony_ci int retval = 0; 357362306a36Sopenharmony_ci unsigned selector; 357462306a36Sopenharmony_ci 357562306a36Sopenharmony_ci /* 357662306a36Sopenharmony_ci * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. 357762306a36Sopenharmony_ci * HCS_INDICATOR may say we can change LEDs to off/amber/green. 357862306a36Sopenharmony_ci * (track current state ourselves) ... blink for diagnostics, 357962306a36Sopenharmony_ci * power, "this is the one", etc. EHCI spec supports this. 358062306a36Sopenharmony_ci */ 358162306a36Sopenharmony_ci 358262306a36Sopenharmony_ci spin_lock_irqsave(&oxu->lock, flags); 358362306a36Sopenharmony_ci switch (typeReq) { 358462306a36Sopenharmony_ci case ClearHubFeature: 358562306a36Sopenharmony_ci switch (wValue) { 358662306a36Sopenharmony_ci case C_HUB_LOCAL_POWER: 358762306a36Sopenharmony_ci case C_HUB_OVER_CURRENT: 358862306a36Sopenharmony_ci /* no hub-wide feature/status flags */ 358962306a36Sopenharmony_ci break; 359062306a36Sopenharmony_ci default: 359162306a36Sopenharmony_ci goto error; 359262306a36Sopenharmony_ci } 359362306a36Sopenharmony_ci break; 359462306a36Sopenharmony_ci case ClearPortFeature: 359562306a36Sopenharmony_ci if (!wIndex || wIndex > ports) 359662306a36Sopenharmony_ci goto error; 359762306a36Sopenharmony_ci wIndex--; 359862306a36Sopenharmony_ci temp = readl(status_reg); 359962306a36Sopenharmony_ci 360062306a36Sopenharmony_ci /* 360162306a36Sopenharmony_ci * Even if OWNER is set, so the port is owned by the 360262306a36Sopenharmony_ci * companion controller, hub_wq needs to be able to clear 360362306a36Sopenharmony_ci * the port-change status bits (especially 360462306a36Sopenharmony_ci * USB_PORT_STAT_C_CONNECTION). 360562306a36Sopenharmony_ci */ 360662306a36Sopenharmony_ci 360762306a36Sopenharmony_ci switch (wValue) { 360862306a36Sopenharmony_ci case USB_PORT_FEAT_ENABLE: 360962306a36Sopenharmony_ci writel(temp & ~PORT_PE, status_reg); 361062306a36Sopenharmony_ci break; 361162306a36Sopenharmony_ci case USB_PORT_FEAT_C_ENABLE: 361262306a36Sopenharmony_ci writel((temp & ~PORT_RWC_BITS) | PORT_PEC, status_reg); 361362306a36Sopenharmony_ci break; 361462306a36Sopenharmony_ci case USB_PORT_FEAT_SUSPEND: 361562306a36Sopenharmony_ci if (temp & PORT_RESET) 361662306a36Sopenharmony_ci goto error; 361762306a36Sopenharmony_ci if (temp & PORT_SUSPEND) { 361862306a36Sopenharmony_ci if ((temp & PORT_PE) == 0) 361962306a36Sopenharmony_ci goto error; 362062306a36Sopenharmony_ci /* resume signaling for 20 msec */ 362162306a36Sopenharmony_ci temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); 362262306a36Sopenharmony_ci writel(temp | PORT_RESUME, status_reg); 362362306a36Sopenharmony_ci oxu->reset_done[wIndex] = jiffies 362462306a36Sopenharmony_ci + msecs_to_jiffies(20); 362562306a36Sopenharmony_ci } 362662306a36Sopenharmony_ci break; 362762306a36Sopenharmony_ci case USB_PORT_FEAT_C_SUSPEND: 362862306a36Sopenharmony_ci /* we auto-clear this feature */ 362962306a36Sopenharmony_ci break; 363062306a36Sopenharmony_ci case USB_PORT_FEAT_POWER: 363162306a36Sopenharmony_ci if (HCS_PPC(oxu->hcs_params)) 363262306a36Sopenharmony_ci writel(temp & ~(PORT_RWC_BITS | PORT_POWER), 363362306a36Sopenharmony_ci status_reg); 363462306a36Sopenharmony_ci break; 363562306a36Sopenharmony_ci case USB_PORT_FEAT_C_CONNECTION: 363662306a36Sopenharmony_ci writel((temp & ~PORT_RWC_BITS) | PORT_CSC, status_reg); 363762306a36Sopenharmony_ci break; 363862306a36Sopenharmony_ci case USB_PORT_FEAT_C_OVER_CURRENT: 363962306a36Sopenharmony_ci writel((temp & ~PORT_RWC_BITS) | PORT_OCC, status_reg); 364062306a36Sopenharmony_ci break; 364162306a36Sopenharmony_ci case USB_PORT_FEAT_C_RESET: 364262306a36Sopenharmony_ci /* GetPortStatus clears reset */ 364362306a36Sopenharmony_ci break; 364462306a36Sopenharmony_ci default: 364562306a36Sopenharmony_ci goto error; 364662306a36Sopenharmony_ci } 364762306a36Sopenharmony_ci readl(&oxu->regs->command); /* unblock posted write */ 364862306a36Sopenharmony_ci break; 364962306a36Sopenharmony_ci case GetHubDescriptor: 365062306a36Sopenharmony_ci ehci_hub_descriptor(oxu, (struct usb_hub_descriptor *) 365162306a36Sopenharmony_ci buf); 365262306a36Sopenharmony_ci break; 365362306a36Sopenharmony_ci case GetHubStatus: 365462306a36Sopenharmony_ci /* no hub-wide feature/status flags */ 365562306a36Sopenharmony_ci memset(buf, 0, 4); 365662306a36Sopenharmony_ci break; 365762306a36Sopenharmony_ci case GetPortStatus: 365862306a36Sopenharmony_ci if (!wIndex || wIndex > ports) 365962306a36Sopenharmony_ci goto error; 366062306a36Sopenharmony_ci wIndex--; 366162306a36Sopenharmony_ci status = 0; 366262306a36Sopenharmony_ci temp = readl(status_reg); 366362306a36Sopenharmony_ci 366462306a36Sopenharmony_ci /* wPortChange bits */ 366562306a36Sopenharmony_ci if (temp & PORT_CSC) 366662306a36Sopenharmony_ci status |= USB_PORT_STAT_C_CONNECTION << 16; 366762306a36Sopenharmony_ci if (temp & PORT_PEC) 366862306a36Sopenharmony_ci status |= USB_PORT_STAT_C_ENABLE << 16; 366962306a36Sopenharmony_ci if ((temp & PORT_OCC) && !ignore_oc) 367062306a36Sopenharmony_ci status |= USB_PORT_STAT_C_OVERCURRENT << 16; 367162306a36Sopenharmony_ci 367262306a36Sopenharmony_ci /* whoever resumes must GetPortStatus to complete it!! */ 367362306a36Sopenharmony_ci if (temp & PORT_RESUME) { 367462306a36Sopenharmony_ci 367562306a36Sopenharmony_ci /* Remote Wakeup received? */ 367662306a36Sopenharmony_ci if (!oxu->reset_done[wIndex]) { 367762306a36Sopenharmony_ci /* resume signaling for 20 msec */ 367862306a36Sopenharmony_ci oxu->reset_done[wIndex] = jiffies 367962306a36Sopenharmony_ci + msecs_to_jiffies(20); 368062306a36Sopenharmony_ci /* check the port again */ 368162306a36Sopenharmony_ci mod_timer(&oxu_to_hcd(oxu)->rh_timer, 368262306a36Sopenharmony_ci oxu->reset_done[wIndex]); 368362306a36Sopenharmony_ci } 368462306a36Sopenharmony_ci 368562306a36Sopenharmony_ci /* resume completed? */ 368662306a36Sopenharmony_ci else if (time_after_eq(jiffies, 368762306a36Sopenharmony_ci oxu->reset_done[wIndex])) { 368862306a36Sopenharmony_ci status |= USB_PORT_STAT_C_SUSPEND << 16; 368962306a36Sopenharmony_ci oxu->reset_done[wIndex] = 0; 369062306a36Sopenharmony_ci 369162306a36Sopenharmony_ci /* stop resume signaling */ 369262306a36Sopenharmony_ci temp = readl(status_reg); 369362306a36Sopenharmony_ci writel(temp & ~(PORT_RWC_BITS | PORT_RESUME), 369462306a36Sopenharmony_ci status_reg); 369562306a36Sopenharmony_ci retval = handshake(oxu, status_reg, 369662306a36Sopenharmony_ci PORT_RESUME, 0, 2000 /* 2msec */); 369762306a36Sopenharmony_ci if (retval != 0) { 369862306a36Sopenharmony_ci oxu_err(oxu, 369962306a36Sopenharmony_ci "port %d resume error %d\n", 370062306a36Sopenharmony_ci wIndex + 1, retval); 370162306a36Sopenharmony_ci goto error; 370262306a36Sopenharmony_ci } 370362306a36Sopenharmony_ci temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); 370462306a36Sopenharmony_ci } 370562306a36Sopenharmony_ci } 370662306a36Sopenharmony_ci 370762306a36Sopenharmony_ci /* whoever resets must GetPortStatus to complete it!! */ 370862306a36Sopenharmony_ci if ((temp & PORT_RESET) 370962306a36Sopenharmony_ci && time_after_eq(jiffies, 371062306a36Sopenharmony_ci oxu->reset_done[wIndex])) { 371162306a36Sopenharmony_ci status |= USB_PORT_STAT_C_RESET << 16; 371262306a36Sopenharmony_ci oxu->reset_done[wIndex] = 0; 371362306a36Sopenharmony_ci 371462306a36Sopenharmony_ci /* force reset to complete */ 371562306a36Sopenharmony_ci writel(temp & ~(PORT_RWC_BITS | PORT_RESET), 371662306a36Sopenharmony_ci status_reg); 371762306a36Sopenharmony_ci /* REVISIT: some hardware needs 550+ usec to clear 371862306a36Sopenharmony_ci * this bit; seems too long to spin routinely... 371962306a36Sopenharmony_ci */ 372062306a36Sopenharmony_ci retval = handshake(oxu, status_reg, 372162306a36Sopenharmony_ci PORT_RESET, 0, 750); 372262306a36Sopenharmony_ci if (retval != 0) { 372362306a36Sopenharmony_ci oxu_err(oxu, "port %d reset error %d\n", 372462306a36Sopenharmony_ci wIndex + 1, retval); 372562306a36Sopenharmony_ci goto error; 372662306a36Sopenharmony_ci } 372762306a36Sopenharmony_ci 372862306a36Sopenharmony_ci /* see what we found out */ 372962306a36Sopenharmony_ci temp = check_reset_complete(oxu, wIndex, status_reg, 373062306a36Sopenharmony_ci readl(status_reg)); 373162306a36Sopenharmony_ci } 373262306a36Sopenharmony_ci 373362306a36Sopenharmony_ci /* transfer dedicated ports to the companion hc */ 373462306a36Sopenharmony_ci if ((temp & PORT_CONNECT) && 373562306a36Sopenharmony_ci test_bit(wIndex, &oxu->companion_ports)) { 373662306a36Sopenharmony_ci temp &= ~PORT_RWC_BITS; 373762306a36Sopenharmony_ci temp |= PORT_OWNER; 373862306a36Sopenharmony_ci writel(temp, status_reg); 373962306a36Sopenharmony_ci oxu_dbg(oxu, "port %d --> companion\n", wIndex + 1); 374062306a36Sopenharmony_ci temp = readl(status_reg); 374162306a36Sopenharmony_ci } 374262306a36Sopenharmony_ci 374362306a36Sopenharmony_ci /* 374462306a36Sopenharmony_ci * Even if OWNER is set, there's no harm letting hub_wq 374562306a36Sopenharmony_ci * see the wPortStatus values (they should all be 0 except 374662306a36Sopenharmony_ci * for PORT_POWER anyway). 374762306a36Sopenharmony_ci */ 374862306a36Sopenharmony_ci 374962306a36Sopenharmony_ci if (temp & PORT_CONNECT) { 375062306a36Sopenharmony_ci status |= USB_PORT_STAT_CONNECTION; 375162306a36Sopenharmony_ci /* status may be from integrated TT */ 375262306a36Sopenharmony_ci status |= oxu_port_speed(oxu, temp); 375362306a36Sopenharmony_ci } 375462306a36Sopenharmony_ci if (temp & PORT_PE) 375562306a36Sopenharmony_ci status |= USB_PORT_STAT_ENABLE; 375662306a36Sopenharmony_ci if (temp & (PORT_SUSPEND|PORT_RESUME)) 375762306a36Sopenharmony_ci status |= USB_PORT_STAT_SUSPEND; 375862306a36Sopenharmony_ci if (temp & PORT_OC) 375962306a36Sopenharmony_ci status |= USB_PORT_STAT_OVERCURRENT; 376062306a36Sopenharmony_ci if (temp & PORT_RESET) 376162306a36Sopenharmony_ci status |= USB_PORT_STAT_RESET; 376262306a36Sopenharmony_ci if (temp & PORT_POWER) 376362306a36Sopenharmony_ci status |= USB_PORT_STAT_POWER; 376462306a36Sopenharmony_ci 376562306a36Sopenharmony_ci#ifndef OXU_VERBOSE_DEBUG 376662306a36Sopenharmony_ci if (status & ~0xffff) /* only if wPortChange is interesting */ 376762306a36Sopenharmony_ci#endif 376862306a36Sopenharmony_ci dbg_port(oxu, "GetStatus", wIndex + 1, temp); 376962306a36Sopenharmony_ci put_unaligned(cpu_to_le32(status), (__le32 *) buf); 377062306a36Sopenharmony_ci break; 377162306a36Sopenharmony_ci case SetHubFeature: 377262306a36Sopenharmony_ci switch (wValue) { 377362306a36Sopenharmony_ci case C_HUB_LOCAL_POWER: 377462306a36Sopenharmony_ci case C_HUB_OVER_CURRENT: 377562306a36Sopenharmony_ci /* no hub-wide feature/status flags */ 377662306a36Sopenharmony_ci break; 377762306a36Sopenharmony_ci default: 377862306a36Sopenharmony_ci goto error; 377962306a36Sopenharmony_ci } 378062306a36Sopenharmony_ci break; 378162306a36Sopenharmony_ci case SetPortFeature: 378262306a36Sopenharmony_ci selector = wIndex >> 8; 378362306a36Sopenharmony_ci wIndex &= 0xff; 378462306a36Sopenharmony_ci if (!wIndex || wIndex > ports) 378562306a36Sopenharmony_ci goto error; 378662306a36Sopenharmony_ci wIndex--; 378762306a36Sopenharmony_ci temp = readl(status_reg); 378862306a36Sopenharmony_ci if (temp & PORT_OWNER) 378962306a36Sopenharmony_ci break; 379062306a36Sopenharmony_ci 379162306a36Sopenharmony_ci temp &= ~PORT_RWC_BITS; 379262306a36Sopenharmony_ci switch (wValue) { 379362306a36Sopenharmony_ci case USB_PORT_FEAT_SUSPEND: 379462306a36Sopenharmony_ci if ((temp & PORT_PE) == 0 379562306a36Sopenharmony_ci || (temp & PORT_RESET) != 0) 379662306a36Sopenharmony_ci goto error; 379762306a36Sopenharmony_ci if (device_may_wakeup(&hcd->self.root_hub->dev)) 379862306a36Sopenharmony_ci temp |= PORT_WAKE_BITS; 379962306a36Sopenharmony_ci writel(temp | PORT_SUSPEND, status_reg); 380062306a36Sopenharmony_ci break; 380162306a36Sopenharmony_ci case USB_PORT_FEAT_POWER: 380262306a36Sopenharmony_ci if (HCS_PPC(oxu->hcs_params)) 380362306a36Sopenharmony_ci writel(temp | PORT_POWER, status_reg); 380462306a36Sopenharmony_ci break; 380562306a36Sopenharmony_ci case USB_PORT_FEAT_RESET: 380662306a36Sopenharmony_ci if (temp & PORT_RESUME) 380762306a36Sopenharmony_ci goto error; 380862306a36Sopenharmony_ci /* line status bits may report this as low speed, 380962306a36Sopenharmony_ci * which can be fine if this root hub has a 381062306a36Sopenharmony_ci * transaction translator built in. 381162306a36Sopenharmony_ci */ 381262306a36Sopenharmony_ci oxu_vdbg(oxu, "port %d reset\n", wIndex + 1); 381362306a36Sopenharmony_ci temp |= PORT_RESET; 381462306a36Sopenharmony_ci temp &= ~PORT_PE; 381562306a36Sopenharmony_ci 381662306a36Sopenharmony_ci /* 381762306a36Sopenharmony_ci * caller must wait, then call GetPortStatus 381862306a36Sopenharmony_ci * usb 2.0 spec says 50 ms resets on root 381962306a36Sopenharmony_ci */ 382062306a36Sopenharmony_ci oxu->reset_done[wIndex] = jiffies 382162306a36Sopenharmony_ci + msecs_to_jiffies(50); 382262306a36Sopenharmony_ci writel(temp, status_reg); 382362306a36Sopenharmony_ci break; 382462306a36Sopenharmony_ci 382562306a36Sopenharmony_ci /* For downstream facing ports (these): one hub port is put 382662306a36Sopenharmony_ci * into test mode according to USB2 11.24.2.13, then the hub 382762306a36Sopenharmony_ci * must be reset (which for root hub now means rmmod+modprobe, 382862306a36Sopenharmony_ci * or else system reboot). See EHCI 2.3.9 and 4.14 for info 382962306a36Sopenharmony_ci * about the EHCI-specific stuff. 383062306a36Sopenharmony_ci */ 383162306a36Sopenharmony_ci case USB_PORT_FEAT_TEST: 383262306a36Sopenharmony_ci if (!selector || selector > 5) 383362306a36Sopenharmony_ci goto error; 383462306a36Sopenharmony_ci ehci_quiesce(oxu); 383562306a36Sopenharmony_ci ehci_halt(oxu); 383662306a36Sopenharmony_ci temp |= selector << 16; 383762306a36Sopenharmony_ci writel(temp, status_reg); 383862306a36Sopenharmony_ci break; 383962306a36Sopenharmony_ci 384062306a36Sopenharmony_ci default: 384162306a36Sopenharmony_ci goto error; 384262306a36Sopenharmony_ci } 384362306a36Sopenharmony_ci readl(&oxu->regs->command); /* unblock posted writes */ 384462306a36Sopenharmony_ci break; 384562306a36Sopenharmony_ci 384662306a36Sopenharmony_ci default: 384762306a36Sopenharmony_cierror: 384862306a36Sopenharmony_ci /* "stall" on error */ 384962306a36Sopenharmony_ci retval = -EPIPE; 385062306a36Sopenharmony_ci } 385162306a36Sopenharmony_ci spin_unlock_irqrestore(&oxu->lock, flags); 385262306a36Sopenharmony_ci return retval; 385362306a36Sopenharmony_ci} 385462306a36Sopenharmony_ci 385562306a36Sopenharmony_ci#ifdef CONFIG_PM 385662306a36Sopenharmony_ci 385762306a36Sopenharmony_cistatic int oxu_bus_suspend(struct usb_hcd *hcd) 385862306a36Sopenharmony_ci{ 385962306a36Sopenharmony_ci struct oxu_hcd *oxu = hcd_to_oxu(hcd); 386062306a36Sopenharmony_ci int port; 386162306a36Sopenharmony_ci int mask; 386262306a36Sopenharmony_ci 386362306a36Sopenharmony_ci oxu_dbg(oxu, "suspend root hub\n"); 386462306a36Sopenharmony_ci 386562306a36Sopenharmony_ci if (time_before(jiffies, oxu->next_statechange)) 386662306a36Sopenharmony_ci msleep(5); 386762306a36Sopenharmony_ci 386862306a36Sopenharmony_ci port = HCS_N_PORTS(oxu->hcs_params); 386962306a36Sopenharmony_ci spin_lock_irq(&oxu->lock); 387062306a36Sopenharmony_ci 387162306a36Sopenharmony_ci /* stop schedules, clean any completed work */ 387262306a36Sopenharmony_ci if (HC_IS_RUNNING(hcd->state)) { 387362306a36Sopenharmony_ci ehci_quiesce(oxu); 387462306a36Sopenharmony_ci hcd->state = HC_STATE_QUIESCING; 387562306a36Sopenharmony_ci } 387662306a36Sopenharmony_ci oxu->command = readl(&oxu->regs->command); 387762306a36Sopenharmony_ci if (oxu->reclaim) 387862306a36Sopenharmony_ci oxu->reclaim_ready = 1; 387962306a36Sopenharmony_ci ehci_work(oxu); 388062306a36Sopenharmony_ci 388162306a36Sopenharmony_ci /* Unlike other USB host controller types, EHCI doesn't have 388262306a36Sopenharmony_ci * any notion of "global" or bus-wide suspend. The driver has 388362306a36Sopenharmony_ci * to manually suspend all the active unsuspended ports, and 388462306a36Sopenharmony_ci * then manually resume them in the bus_resume() routine. 388562306a36Sopenharmony_ci */ 388662306a36Sopenharmony_ci oxu->bus_suspended = 0; 388762306a36Sopenharmony_ci while (port--) { 388862306a36Sopenharmony_ci u32 __iomem *reg = &oxu->regs->port_status[port]; 388962306a36Sopenharmony_ci u32 t1 = readl(reg) & ~PORT_RWC_BITS; 389062306a36Sopenharmony_ci u32 t2 = t1; 389162306a36Sopenharmony_ci 389262306a36Sopenharmony_ci /* keep track of which ports we suspend */ 389362306a36Sopenharmony_ci if ((t1 & PORT_PE) && !(t1 & PORT_OWNER) && 389462306a36Sopenharmony_ci !(t1 & PORT_SUSPEND)) { 389562306a36Sopenharmony_ci t2 |= PORT_SUSPEND; 389662306a36Sopenharmony_ci set_bit(port, &oxu->bus_suspended); 389762306a36Sopenharmony_ci } 389862306a36Sopenharmony_ci 389962306a36Sopenharmony_ci /* enable remote wakeup on all ports */ 390062306a36Sopenharmony_ci if (device_may_wakeup(&hcd->self.root_hub->dev)) 390162306a36Sopenharmony_ci t2 |= PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E; 390262306a36Sopenharmony_ci else 390362306a36Sopenharmony_ci t2 &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E); 390462306a36Sopenharmony_ci 390562306a36Sopenharmony_ci if (t1 != t2) { 390662306a36Sopenharmony_ci oxu_vdbg(oxu, "port %d, %08x -> %08x\n", 390762306a36Sopenharmony_ci port + 1, t1, t2); 390862306a36Sopenharmony_ci writel(t2, reg); 390962306a36Sopenharmony_ci } 391062306a36Sopenharmony_ci } 391162306a36Sopenharmony_ci 391262306a36Sopenharmony_ci spin_unlock_irq(&oxu->lock); 391362306a36Sopenharmony_ci /* turn off now-idle HC */ 391462306a36Sopenharmony_ci del_timer_sync(&oxu->watchdog); 391562306a36Sopenharmony_ci spin_lock_irq(&oxu->lock); 391662306a36Sopenharmony_ci ehci_halt(oxu); 391762306a36Sopenharmony_ci hcd->state = HC_STATE_SUSPENDED; 391862306a36Sopenharmony_ci 391962306a36Sopenharmony_ci /* allow remote wakeup */ 392062306a36Sopenharmony_ci mask = INTR_MASK; 392162306a36Sopenharmony_ci if (!device_may_wakeup(&hcd->self.root_hub->dev)) 392262306a36Sopenharmony_ci mask &= ~STS_PCD; 392362306a36Sopenharmony_ci writel(mask, &oxu->regs->intr_enable); 392462306a36Sopenharmony_ci readl(&oxu->regs->intr_enable); 392562306a36Sopenharmony_ci 392662306a36Sopenharmony_ci oxu->next_statechange = jiffies + msecs_to_jiffies(10); 392762306a36Sopenharmony_ci spin_unlock_irq(&oxu->lock); 392862306a36Sopenharmony_ci return 0; 392962306a36Sopenharmony_ci} 393062306a36Sopenharmony_ci 393162306a36Sopenharmony_ci/* Caller has locked the root hub, and should reset/reinit on error */ 393262306a36Sopenharmony_cistatic int oxu_bus_resume(struct usb_hcd *hcd) 393362306a36Sopenharmony_ci{ 393462306a36Sopenharmony_ci struct oxu_hcd *oxu = hcd_to_oxu(hcd); 393562306a36Sopenharmony_ci u32 temp; 393662306a36Sopenharmony_ci int i; 393762306a36Sopenharmony_ci 393862306a36Sopenharmony_ci if (time_before(jiffies, oxu->next_statechange)) 393962306a36Sopenharmony_ci msleep(5); 394062306a36Sopenharmony_ci spin_lock_irq(&oxu->lock); 394162306a36Sopenharmony_ci 394262306a36Sopenharmony_ci /* Ideally and we've got a real resume here, and no port's power 394362306a36Sopenharmony_ci * was lost. (For PCI, that means Vaux was maintained.) But we 394462306a36Sopenharmony_ci * could instead be restoring a swsusp snapshot -- so that BIOS was 394562306a36Sopenharmony_ci * the last user of the controller, not reset/pm hardware keeping 394662306a36Sopenharmony_ci * state we gave to it. 394762306a36Sopenharmony_ci */ 394862306a36Sopenharmony_ci temp = readl(&oxu->regs->intr_enable); 394962306a36Sopenharmony_ci oxu_dbg(oxu, "resume root hub%s\n", temp ? "" : " after power loss"); 395062306a36Sopenharmony_ci 395162306a36Sopenharmony_ci /* at least some APM implementations will try to deliver 395262306a36Sopenharmony_ci * IRQs right away, so delay them until we're ready. 395362306a36Sopenharmony_ci */ 395462306a36Sopenharmony_ci writel(0, &oxu->regs->intr_enable); 395562306a36Sopenharmony_ci 395662306a36Sopenharmony_ci /* re-init operational registers */ 395762306a36Sopenharmony_ci writel(0, &oxu->regs->segment); 395862306a36Sopenharmony_ci writel(oxu->periodic_dma, &oxu->regs->frame_list); 395962306a36Sopenharmony_ci writel((u32) oxu->async->qh_dma, &oxu->regs->async_next); 396062306a36Sopenharmony_ci 396162306a36Sopenharmony_ci /* restore CMD_RUN, framelist size, and irq threshold */ 396262306a36Sopenharmony_ci writel(oxu->command, &oxu->regs->command); 396362306a36Sopenharmony_ci 396462306a36Sopenharmony_ci /* Some controller/firmware combinations need a delay during which 396562306a36Sopenharmony_ci * they set up the port statuses. See Bugzilla #8190. */ 396662306a36Sopenharmony_ci mdelay(8); 396762306a36Sopenharmony_ci 396862306a36Sopenharmony_ci /* manually resume the ports we suspended during bus_suspend() */ 396962306a36Sopenharmony_ci i = HCS_N_PORTS(oxu->hcs_params); 397062306a36Sopenharmony_ci while (i--) { 397162306a36Sopenharmony_ci temp = readl(&oxu->regs->port_status[i]); 397262306a36Sopenharmony_ci temp &= ~(PORT_RWC_BITS 397362306a36Sopenharmony_ci | PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E); 397462306a36Sopenharmony_ci if (test_bit(i, &oxu->bus_suspended) && (temp & PORT_SUSPEND)) { 397562306a36Sopenharmony_ci oxu->reset_done[i] = jiffies + msecs_to_jiffies(20); 397662306a36Sopenharmony_ci temp |= PORT_RESUME; 397762306a36Sopenharmony_ci } 397862306a36Sopenharmony_ci writel(temp, &oxu->regs->port_status[i]); 397962306a36Sopenharmony_ci } 398062306a36Sopenharmony_ci i = HCS_N_PORTS(oxu->hcs_params); 398162306a36Sopenharmony_ci mdelay(20); 398262306a36Sopenharmony_ci while (i--) { 398362306a36Sopenharmony_ci temp = readl(&oxu->regs->port_status[i]); 398462306a36Sopenharmony_ci if (test_bit(i, &oxu->bus_suspended) && (temp & PORT_SUSPEND)) { 398562306a36Sopenharmony_ci temp &= ~(PORT_RWC_BITS | PORT_RESUME); 398662306a36Sopenharmony_ci writel(temp, &oxu->regs->port_status[i]); 398762306a36Sopenharmony_ci oxu_vdbg(oxu, "resumed port %d\n", i + 1); 398862306a36Sopenharmony_ci } 398962306a36Sopenharmony_ci } 399062306a36Sopenharmony_ci (void) readl(&oxu->regs->command); 399162306a36Sopenharmony_ci 399262306a36Sopenharmony_ci /* maybe re-activate the schedule(s) */ 399362306a36Sopenharmony_ci temp = 0; 399462306a36Sopenharmony_ci if (oxu->async->qh_next.qh) 399562306a36Sopenharmony_ci temp |= CMD_ASE; 399662306a36Sopenharmony_ci if (oxu->periodic_sched) 399762306a36Sopenharmony_ci temp |= CMD_PSE; 399862306a36Sopenharmony_ci if (temp) { 399962306a36Sopenharmony_ci oxu->command |= temp; 400062306a36Sopenharmony_ci writel(oxu->command, &oxu->regs->command); 400162306a36Sopenharmony_ci } 400262306a36Sopenharmony_ci 400362306a36Sopenharmony_ci oxu->next_statechange = jiffies + msecs_to_jiffies(5); 400462306a36Sopenharmony_ci hcd->state = HC_STATE_RUNNING; 400562306a36Sopenharmony_ci 400662306a36Sopenharmony_ci /* Now we can safely re-enable irqs */ 400762306a36Sopenharmony_ci writel(INTR_MASK, &oxu->regs->intr_enable); 400862306a36Sopenharmony_ci 400962306a36Sopenharmony_ci spin_unlock_irq(&oxu->lock); 401062306a36Sopenharmony_ci return 0; 401162306a36Sopenharmony_ci} 401262306a36Sopenharmony_ci 401362306a36Sopenharmony_ci#else 401462306a36Sopenharmony_ci 401562306a36Sopenharmony_cistatic int oxu_bus_suspend(struct usb_hcd *hcd) 401662306a36Sopenharmony_ci{ 401762306a36Sopenharmony_ci return 0; 401862306a36Sopenharmony_ci} 401962306a36Sopenharmony_ci 402062306a36Sopenharmony_cistatic int oxu_bus_resume(struct usb_hcd *hcd) 402162306a36Sopenharmony_ci{ 402262306a36Sopenharmony_ci return 0; 402362306a36Sopenharmony_ci} 402462306a36Sopenharmony_ci 402562306a36Sopenharmony_ci#endif /* CONFIG_PM */ 402662306a36Sopenharmony_ci 402762306a36Sopenharmony_cistatic const struct hc_driver oxu_hc_driver = { 402862306a36Sopenharmony_ci .description = "oxu210hp_hcd", 402962306a36Sopenharmony_ci .product_desc = "oxu210hp HCD", 403062306a36Sopenharmony_ci .hcd_priv_size = sizeof(struct oxu_hcd), 403162306a36Sopenharmony_ci 403262306a36Sopenharmony_ci /* 403362306a36Sopenharmony_ci * Generic hardware linkage 403462306a36Sopenharmony_ci */ 403562306a36Sopenharmony_ci .irq = oxu_irq, 403662306a36Sopenharmony_ci .flags = HCD_MEMORY | HCD_USB2, 403762306a36Sopenharmony_ci 403862306a36Sopenharmony_ci /* 403962306a36Sopenharmony_ci * Basic lifecycle operations 404062306a36Sopenharmony_ci */ 404162306a36Sopenharmony_ci .reset = oxu_reset, 404262306a36Sopenharmony_ci .start = oxu_run, 404362306a36Sopenharmony_ci .stop = oxu_stop, 404462306a36Sopenharmony_ci .shutdown = oxu_shutdown, 404562306a36Sopenharmony_ci 404662306a36Sopenharmony_ci /* 404762306a36Sopenharmony_ci * Managing i/o requests and associated device resources 404862306a36Sopenharmony_ci */ 404962306a36Sopenharmony_ci .urb_enqueue = oxu_urb_enqueue, 405062306a36Sopenharmony_ci .urb_dequeue = oxu_urb_dequeue, 405162306a36Sopenharmony_ci .endpoint_disable = oxu_endpoint_disable, 405262306a36Sopenharmony_ci 405362306a36Sopenharmony_ci /* 405462306a36Sopenharmony_ci * Scheduling support 405562306a36Sopenharmony_ci */ 405662306a36Sopenharmony_ci .get_frame_number = oxu_get_frame, 405762306a36Sopenharmony_ci 405862306a36Sopenharmony_ci /* 405962306a36Sopenharmony_ci * Root hub support 406062306a36Sopenharmony_ci */ 406162306a36Sopenharmony_ci .hub_status_data = oxu_hub_status_data, 406262306a36Sopenharmony_ci .hub_control = oxu_hub_control, 406362306a36Sopenharmony_ci .bus_suspend = oxu_bus_suspend, 406462306a36Sopenharmony_ci .bus_resume = oxu_bus_resume, 406562306a36Sopenharmony_ci}; 406662306a36Sopenharmony_ci 406762306a36Sopenharmony_ci/* 406862306a36Sopenharmony_ci * Module stuff 406962306a36Sopenharmony_ci */ 407062306a36Sopenharmony_ci 407162306a36Sopenharmony_cistatic void oxu_configuration(struct platform_device *pdev, void __iomem *base) 407262306a36Sopenharmony_ci{ 407362306a36Sopenharmony_ci u32 tmp; 407462306a36Sopenharmony_ci 407562306a36Sopenharmony_ci /* Initialize top level registers. 407662306a36Sopenharmony_ci * First write ever 407762306a36Sopenharmony_ci */ 407862306a36Sopenharmony_ci oxu_writel(base, OXU_HOSTIFCONFIG, 0x0000037D); 407962306a36Sopenharmony_ci oxu_writel(base, OXU_SOFTRESET, OXU_SRESET); 408062306a36Sopenharmony_ci oxu_writel(base, OXU_HOSTIFCONFIG, 0x0000037D); 408162306a36Sopenharmony_ci 408262306a36Sopenharmony_ci tmp = oxu_readl(base, OXU_PIOBURSTREADCTRL); 408362306a36Sopenharmony_ci oxu_writel(base, OXU_PIOBURSTREADCTRL, tmp | 0x0040); 408462306a36Sopenharmony_ci 408562306a36Sopenharmony_ci oxu_writel(base, OXU_ASO, OXU_SPHPOEN | OXU_OVRCCURPUPDEN | 408662306a36Sopenharmony_ci OXU_COMPARATOR | OXU_ASO_OP); 408762306a36Sopenharmony_ci 408862306a36Sopenharmony_ci tmp = oxu_readl(base, OXU_CLKCTRL_SET); 408962306a36Sopenharmony_ci oxu_writel(base, OXU_CLKCTRL_SET, tmp | OXU_SYSCLKEN | OXU_USBOTGCLKEN); 409062306a36Sopenharmony_ci 409162306a36Sopenharmony_ci /* Clear all top interrupt enable */ 409262306a36Sopenharmony_ci oxu_writel(base, OXU_CHIPIRQEN_CLR, 0xff); 409362306a36Sopenharmony_ci 409462306a36Sopenharmony_ci /* Clear all top interrupt status */ 409562306a36Sopenharmony_ci oxu_writel(base, OXU_CHIPIRQSTATUS, 0xff); 409662306a36Sopenharmony_ci 409762306a36Sopenharmony_ci /* Enable all needed top interrupt except OTG SPH core */ 409862306a36Sopenharmony_ci oxu_writel(base, OXU_CHIPIRQEN_SET, OXU_USBSPHLPWUI | OXU_USBOTGLPWUI); 409962306a36Sopenharmony_ci} 410062306a36Sopenharmony_ci 410162306a36Sopenharmony_cistatic int oxu_verify_id(struct platform_device *pdev, void __iomem *base) 410262306a36Sopenharmony_ci{ 410362306a36Sopenharmony_ci u32 id; 410462306a36Sopenharmony_ci static const char * const bo[] = { 410562306a36Sopenharmony_ci "reserved", 410662306a36Sopenharmony_ci "128-pin LQFP", 410762306a36Sopenharmony_ci "84-pin TFBGA", 410862306a36Sopenharmony_ci "reserved", 410962306a36Sopenharmony_ci }; 411062306a36Sopenharmony_ci 411162306a36Sopenharmony_ci /* Read controller signature register to find a match */ 411262306a36Sopenharmony_ci id = oxu_readl(base, OXU_DEVICEID); 411362306a36Sopenharmony_ci dev_info(&pdev->dev, "device ID %x\n", id); 411462306a36Sopenharmony_ci if ((id & OXU_REV_MASK) != (OXU_REV_2100 << OXU_REV_SHIFT)) 411562306a36Sopenharmony_ci return -1; 411662306a36Sopenharmony_ci 411762306a36Sopenharmony_ci dev_info(&pdev->dev, "found device %x %s (%04x:%04x)\n", 411862306a36Sopenharmony_ci id >> OXU_REV_SHIFT, 411962306a36Sopenharmony_ci bo[(id & OXU_BO_MASK) >> OXU_BO_SHIFT], 412062306a36Sopenharmony_ci (id & OXU_MAJ_REV_MASK) >> OXU_MAJ_REV_SHIFT, 412162306a36Sopenharmony_ci (id & OXU_MIN_REV_MASK) >> OXU_MIN_REV_SHIFT); 412262306a36Sopenharmony_ci 412362306a36Sopenharmony_ci return 0; 412462306a36Sopenharmony_ci} 412562306a36Sopenharmony_ci 412662306a36Sopenharmony_cistatic const struct hc_driver oxu_hc_driver; 412762306a36Sopenharmony_cistatic struct usb_hcd *oxu_create(struct platform_device *pdev, 412862306a36Sopenharmony_ci unsigned long memstart, unsigned long memlen, 412962306a36Sopenharmony_ci void __iomem *base, int irq, int otg) 413062306a36Sopenharmony_ci{ 413162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 413262306a36Sopenharmony_ci 413362306a36Sopenharmony_ci struct usb_hcd *hcd; 413462306a36Sopenharmony_ci struct oxu_hcd *oxu; 413562306a36Sopenharmony_ci int ret; 413662306a36Sopenharmony_ci 413762306a36Sopenharmony_ci /* Set endian mode and host mode */ 413862306a36Sopenharmony_ci oxu_writel(base + (otg ? OXU_OTG_CORE_OFFSET : OXU_SPH_CORE_OFFSET), 413962306a36Sopenharmony_ci OXU_USBMODE, 414062306a36Sopenharmony_ci OXU_CM_HOST_ONLY | OXU_ES_LITTLE | OXU_VBPS); 414162306a36Sopenharmony_ci 414262306a36Sopenharmony_ci hcd = usb_create_hcd(&oxu_hc_driver, dev, 414362306a36Sopenharmony_ci otg ? "oxu210hp_otg" : "oxu210hp_sph"); 414462306a36Sopenharmony_ci if (!hcd) 414562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 414662306a36Sopenharmony_ci 414762306a36Sopenharmony_ci hcd->rsrc_start = memstart; 414862306a36Sopenharmony_ci hcd->rsrc_len = memlen; 414962306a36Sopenharmony_ci hcd->regs = base; 415062306a36Sopenharmony_ci hcd->irq = irq; 415162306a36Sopenharmony_ci hcd->state = HC_STATE_HALT; 415262306a36Sopenharmony_ci 415362306a36Sopenharmony_ci oxu = hcd_to_oxu(hcd); 415462306a36Sopenharmony_ci oxu->is_otg = otg; 415562306a36Sopenharmony_ci 415662306a36Sopenharmony_ci ret = usb_add_hcd(hcd, irq, IRQF_SHARED); 415762306a36Sopenharmony_ci if (ret < 0) { 415862306a36Sopenharmony_ci usb_put_hcd(hcd); 415962306a36Sopenharmony_ci return ERR_PTR(ret); 416062306a36Sopenharmony_ci } 416162306a36Sopenharmony_ci 416262306a36Sopenharmony_ci device_wakeup_enable(hcd->self.controller); 416362306a36Sopenharmony_ci return hcd; 416462306a36Sopenharmony_ci} 416562306a36Sopenharmony_ci 416662306a36Sopenharmony_cistatic int oxu_init(struct platform_device *pdev, 416762306a36Sopenharmony_ci unsigned long memstart, unsigned long memlen, 416862306a36Sopenharmony_ci void __iomem *base, int irq) 416962306a36Sopenharmony_ci{ 417062306a36Sopenharmony_ci struct oxu_info *info = platform_get_drvdata(pdev); 417162306a36Sopenharmony_ci struct usb_hcd *hcd; 417262306a36Sopenharmony_ci int ret; 417362306a36Sopenharmony_ci 417462306a36Sopenharmony_ci /* First time configuration at start up */ 417562306a36Sopenharmony_ci oxu_configuration(pdev, base); 417662306a36Sopenharmony_ci 417762306a36Sopenharmony_ci ret = oxu_verify_id(pdev, base); 417862306a36Sopenharmony_ci if (ret) { 417962306a36Sopenharmony_ci dev_err(&pdev->dev, "no devices found!\n"); 418062306a36Sopenharmony_ci return -ENODEV; 418162306a36Sopenharmony_ci } 418262306a36Sopenharmony_ci 418362306a36Sopenharmony_ci /* Create the OTG controller */ 418462306a36Sopenharmony_ci hcd = oxu_create(pdev, memstart, memlen, base, irq, 1); 418562306a36Sopenharmony_ci if (IS_ERR(hcd)) { 418662306a36Sopenharmony_ci dev_err(&pdev->dev, "cannot create OTG controller!\n"); 418762306a36Sopenharmony_ci ret = PTR_ERR(hcd); 418862306a36Sopenharmony_ci goto error_create_otg; 418962306a36Sopenharmony_ci } 419062306a36Sopenharmony_ci info->hcd[0] = hcd; 419162306a36Sopenharmony_ci 419262306a36Sopenharmony_ci /* Create the SPH host controller */ 419362306a36Sopenharmony_ci hcd = oxu_create(pdev, memstart, memlen, base, irq, 0); 419462306a36Sopenharmony_ci if (IS_ERR(hcd)) { 419562306a36Sopenharmony_ci dev_err(&pdev->dev, "cannot create SPH controller!\n"); 419662306a36Sopenharmony_ci ret = PTR_ERR(hcd); 419762306a36Sopenharmony_ci goto error_create_sph; 419862306a36Sopenharmony_ci } 419962306a36Sopenharmony_ci info->hcd[1] = hcd; 420062306a36Sopenharmony_ci 420162306a36Sopenharmony_ci oxu_writel(base, OXU_CHIPIRQEN_SET, 420262306a36Sopenharmony_ci oxu_readl(base, OXU_CHIPIRQEN_SET) | 3); 420362306a36Sopenharmony_ci 420462306a36Sopenharmony_ci return 0; 420562306a36Sopenharmony_ci 420662306a36Sopenharmony_cierror_create_sph: 420762306a36Sopenharmony_ci usb_remove_hcd(info->hcd[0]); 420862306a36Sopenharmony_ci usb_put_hcd(info->hcd[0]); 420962306a36Sopenharmony_ci 421062306a36Sopenharmony_cierror_create_otg: 421162306a36Sopenharmony_ci return ret; 421262306a36Sopenharmony_ci} 421362306a36Sopenharmony_ci 421462306a36Sopenharmony_cistatic int oxu_drv_probe(struct platform_device *pdev) 421562306a36Sopenharmony_ci{ 421662306a36Sopenharmony_ci struct resource *res; 421762306a36Sopenharmony_ci void __iomem *base; 421862306a36Sopenharmony_ci unsigned long memstart, memlen; 421962306a36Sopenharmony_ci int irq, ret; 422062306a36Sopenharmony_ci struct oxu_info *info; 422162306a36Sopenharmony_ci 422262306a36Sopenharmony_ci if (usb_disabled()) 422362306a36Sopenharmony_ci return -ENODEV; 422462306a36Sopenharmony_ci 422562306a36Sopenharmony_ci /* 422662306a36Sopenharmony_ci * Get the platform resources 422762306a36Sopenharmony_ci */ 422862306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 422962306a36Sopenharmony_ci if (irq < 0) 423062306a36Sopenharmony_ci return irq; 423162306a36Sopenharmony_ci dev_dbg(&pdev->dev, "IRQ resource %d\n", irq); 423262306a36Sopenharmony_ci 423362306a36Sopenharmony_ci base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 423462306a36Sopenharmony_ci if (IS_ERR(base)) { 423562306a36Sopenharmony_ci ret = PTR_ERR(base); 423662306a36Sopenharmony_ci goto error; 423762306a36Sopenharmony_ci } 423862306a36Sopenharmony_ci memstart = res->start; 423962306a36Sopenharmony_ci memlen = resource_size(res); 424062306a36Sopenharmony_ci 424162306a36Sopenharmony_ci ret = irq_set_irq_type(irq, IRQF_TRIGGER_FALLING); 424262306a36Sopenharmony_ci if (ret) { 424362306a36Sopenharmony_ci dev_err(&pdev->dev, "error setting irq type\n"); 424462306a36Sopenharmony_ci ret = -EFAULT; 424562306a36Sopenharmony_ci goto error; 424662306a36Sopenharmony_ci } 424762306a36Sopenharmony_ci 424862306a36Sopenharmony_ci /* Allocate a driver data struct to hold useful info for both 424962306a36Sopenharmony_ci * SPH & OTG devices 425062306a36Sopenharmony_ci */ 425162306a36Sopenharmony_ci info = devm_kzalloc(&pdev->dev, sizeof(struct oxu_info), GFP_KERNEL); 425262306a36Sopenharmony_ci if (!info) { 425362306a36Sopenharmony_ci ret = -EFAULT; 425462306a36Sopenharmony_ci goto error; 425562306a36Sopenharmony_ci } 425662306a36Sopenharmony_ci platform_set_drvdata(pdev, info); 425762306a36Sopenharmony_ci 425862306a36Sopenharmony_ci ret = oxu_init(pdev, memstart, memlen, base, irq); 425962306a36Sopenharmony_ci if (ret < 0) { 426062306a36Sopenharmony_ci dev_dbg(&pdev->dev, "cannot init USB devices\n"); 426162306a36Sopenharmony_ci goto error; 426262306a36Sopenharmony_ci } 426362306a36Sopenharmony_ci 426462306a36Sopenharmony_ci dev_info(&pdev->dev, "devices enabled and running\n"); 426562306a36Sopenharmony_ci platform_set_drvdata(pdev, info); 426662306a36Sopenharmony_ci 426762306a36Sopenharmony_ci return 0; 426862306a36Sopenharmony_ci 426962306a36Sopenharmony_cierror: 427062306a36Sopenharmony_ci dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), ret); 427162306a36Sopenharmony_ci return ret; 427262306a36Sopenharmony_ci} 427362306a36Sopenharmony_ci 427462306a36Sopenharmony_cistatic void oxu_remove(struct platform_device *pdev, struct usb_hcd *hcd) 427562306a36Sopenharmony_ci{ 427662306a36Sopenharmony_ci usb_remove_hcd(hcd); 427762306a36Sopenharmony_ci usb_put_hcd(hcd); 427862306a36Sopenharmony_ci} 427962306a36Sopenharmony_ci 428062306a36Sopenharmony_cistatic void oxu_drv_remove(struct platform_device *pdev) 428162306a36Sopenharmony_ci{ 428262306a36Sopenharmony_ci struct oxu_info *info = platform_get_drvdata(pdev); 428362306a36Sopenharmony_ci 428462306a36Sopenharmony_ci oxu_remove(pdev, info->hcd[0]); 428562306a36Sopenharmony_ci oxu_remove(pdev, info->hcd[1]); 428662306a36Sopenharmony_ci} 428762306a36Sopenharmony_ci 428862306a36Sopenharmony_cistatic void oxu_drv_shutdown(struct platform_device *pdev) 428962306a36Sopenharmony_ci{ 429062306a36Sopenharmony_ci oxu_drv_remove(pdev); 429162306a36Sopenharmony_ci} 429262306a36Sopenharmony_ci 429362306a36Sopenharmony_ci#if 0 429462306a36Sopenharmony_ci/* FIXME: TODO */ 429562306a36Sopenharmony_cistatic int oxu_drv_suspend(struct device *dev) 429662306a36Sopenharmony_ci{ 429762306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 429862306a36Sopenharmony_ci struct usb_hcd *hcd = dev_get_drvdata(dev); 429962306a36Sopenharmony_ci 430062306a36Sopenharmony_ci return 0; 430162306a36Sopenharmony_ci} 430262306a36Sopenharmony_ci 430362306a36Sopenharmony_cistatic int oxu_drv_resume(struct device *dev) 430462306a36Sopenharmony_ci{ 430562306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 430662306a36Sopenharmony_ci struct usb_hcd *hcd = dev_get_drvdata(dev); 430762306a36Sopenharmony_ci 430862306a36Sopenharmony_ci return 0; 430962306a36Sopenharmony_ci} 431062306a36Sopenharmony_ci#else 431162306a36Sopenharmony_ci#define oxu_drv_suspend NULL 431262306a36Sopenharmony_ci#define oxu_drv_resume NULL 431362306a36Sopenharmony_ci#endif 431462306a36Sopenharmony_ci 431562306a36Sopenharmony_cistatic struct platform_driver oxu_driver = { 431662306a36Sopenharmony_ci .probe = oxu_drv_probe, 431762306a36Sopenharmony_ci .remove_new = oxu_drv_remove, 431862306a36Sopenharmony_ci .shutdown = oxu_drv_shutdown, 431962306a36Sopenharmony_ci .suspend = oxu_drv_suspend, 432062306a36Sopenharmony_ci .resume = oxu_drv_resume, 432162306a36Sopenharmony_ci .driver = { 432262306a36Sopenharmony_ci .name = "oxu210hp-hcd", 432362306a36Sopenharmony_ci .bus = &platform_bus_type 432462306a36Sopenharmony_ci } 432562306a36Sopenharmony_ci}; 432662306a36Sopenharmony_ci 432762306a36Sopenharmony_cimodule_platform_driver(oxu_driver); 432862306a36Sopenharmony_ci 432962306a36Sopenharmony_ciMODULE_DESCRIPTION("Oxford OXU210HP HCD driver - ver. " DRIVER_VERSION); 433062306a36Sopenharmony_ciMODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); 433162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 4332