mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-30 21:44:27 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			756 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			756 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * ADM5120 HCD (Host Controller Driver) for USB
 | |
|  *
 | |
|  * Copyright (C) 2007-2008 Gabor Juhos <juhosg@openwrt.org>
 | |
|  *
 | |
|  * This file was derived from: drivers/usb/host/ohci.h
 | |
|  *   (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
 | |
|  *   (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
 | |
|  *
 | |
|  *  This program is free software; you can redistribute it and/or modify it
 | |
|  *  under the terms of the GNU General Public License version 2 as published
 | |
|  *  by the Free Software Foundation.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to
 | |
|  * __leXX (normally) or __beXX (given OHCI_BIG_ENDIAN), depending on the
 | |
|  * host controller implementation.
 | |
|  */
 | |
| typedef __u32 __bitwise __hc32;
 | |
| typedef __u16 __bitwise __hc16;
 | |
| 
 | |
| /*
 | |
|  * OHCI Endpoint Descriptor (ED) ... holds TD queue
 | |
|  * See OHCI spec, section 4.2
 | |
|  *
 | |
|  * This is a "Queue Head" for those transfers, which is why
 | |
|  * both EHCI and UHCI call similar structures a "QH".
 | |
|  */
 | |
| 
 | |
| #define TD_DATALEN_MAX	4096
 | |
| 
 | |
| #define ED_ALIGN	16
 | |
| #define ED_MASK	((u32)~(ED_ALIGN-1))	/* strip hw status in low addr bits */
 | |
| 
 | |
| struct ed {
 | |
| 	/* first fields are hardware-specified */
 | |
| 	__hc32			hwINFO;      /* endpoint config bitmap */
 | |
| 	/* info bits defined by hcd */
 | |
| #define ED_DEQUEUE	(1 << 27)
 | |
| 	/* info bits defined by the hardware */
 | |
| #define ED_MPS_SHIFT	16
 | |
| #define ED_MPS_MASK	((1 << 11)-1)
 | |
| #define ED_MPS_GET(x)	(((x) >> ED_MPS_SHIFT) & ED_MPS_MASK)
 | |
| #define ED_ISO		(1 << 15)		/* isochronous endpoint */
 | |
| #define ED_SKIP		(1 << 14)
 | |
| #define ED_SPEED_FULL	(1 << 13)		/* fullspeed device */
 | |
| #define ED_INT		(1 << 11)		/* interrupt endpoint */
 | |
| #define ED_EN_SHIFT	7			/* endpoint shift */
 | |
| #define ED_EN_MASK	((1 << 4)-1)		/* endpoint mask */
 | |
| #define ED_EN_GET(x)	(((x) >> ED_EN_SHIFT) & ED_EN_MASK)
 | |
| #define ED_FA_MASK	((1 << 7)-1)		/* function address mask */
 | |
| #define ED_FA_GET(x)	((x) & ED_FA_MASK)
 | |
| 	__hc32			hwTailP;	/* tail of TD list */
 | |
| 	__hc32			hwHeadP;	/* head of TD list (hc r/w) */
 | |
| #define ED_C		(0x02)			/* toggle carry */
 | |
| #define ED_H		(0x01)			/* halted */
 | |
| 	__hc32			hwNextED;	/* next ED in list */
 | |
| 
 | |
| 	/* rest are purely for the driver's use */
 | |
| 	dma_addr_t		dma;		/* addr of ED */
 | |
| 	struct td		*dummy;		/* next TD to activate */
 | |
| 
 | |
| 	struct list_head	urb_list;	/* list of our URBs */
 | |
| 
 | |
| 	/* host's view of schedule */
 | |
| 	struct ed		*ed_next;	/* on schedule list */
 | |
| 	struct ed		*ed_prev;	/* for non-interrupt EDs */
 | |
| 	struct ed		*ed_rm_next;	/* on rm list */
 | |
| 	struct list_head	td_list;	/* "shadow list" of our TDs */
 | |
| 
 | |
| 	/* create --> IDLE --> OPER --> ... --> IDLE --> destroy
 | |
| 	 * usually:  OPER --> UNLINK --> (IDLE | OPER) --> ...
 | |
| 	 */
 | |
| 	u8			state;		/* ED_{IDLE,UNLINK,OPER} */
 | |
| #define ED_IDLE		0x00		/* NOT linked to HC */
 | |
| #define ED_UNLINK	0x01		/* being unlinked from hc */
 | |
| #define ED_OPER		0x02		/* IS linked to hc */
 | |
| 
 | |
| 	u8			type;		/* PIPE_{BULK,...} */
 | |
| 
 | |
| 	/* periodic scheduling params (for intr and iso) */
 | |
| 	u8			branch;
 | |
| 	u16			interval;
 | |
| 	u16			load;
 | |
| 	u16			last_iso;	/* iso only */
 | |
| 
 | |
| 	/* HC may see EDs on rm_list until next frame (frame_no == tick) */
 | |
| 	u16			tick;
 | |
| } __attribute__ ((aligned(ED_ALIGN)));
 | |
| 
 | |
| /*
 | |
|  * OHCI Transfer Descriptor (TD) ... one per transfer segment
 | |
|  * See OHCI spec, sections 4.3.1 (general = control/bulk/interrupt)
 | |
|  * and 4.3.2 (iso)
 | |
|  */
 | |
| 
 | |
| #define TD_ALIGN	32
 | |
| #define TD_MASK	((u32)~(TD_ALIGN-1))	/* strip hw status in low addr bits */
 | |
| 
 | |
| struct td {
 | |
| 	/* first fields are hardware-specified */
 | |
| 	__hc32		hwINFO;		/* transfer info bitmask */
 | |
| 
 | |
| 	/* hwINFO bits */
 | |
| #define TD_OWN		(1 << 31)		/* owner of the descriptor */
 | |
| #define TD_CC_SHIFT	27			/* condition code */
 | |
| #define TD_CC_MASK	0xf
 | |
| #define TD_CC		(TD_CC_MASK << TD_CC_SHIFT)
 | |
| #define TD_CC_GET(x)	(((x) >> TD_CC_SHIFT) & TD_CC_MASK)
 | |
| 
 | |
| #define TD_EC_SHIFT	25			/* error count */
 | |
| #define TD_EC_MASK	0x3
 | |
| #define TD_EC		(TD_EC_MASK << TD_EC_SHIFT)
 | |
| #define TD_EC_GET(x)	((x >> TD_EC_SHIFT) & TD_EC_MASK)
 | |
| #define TD_T_SHIFT	23			/* data toggle state */
 | |
| #define TD_T_MASK	0x3
 | |
| #define TD_T		(TD_T_MASK << TD_T_SHIFT)
 | |
| #define TD_T_DATA0	(0x2 << TD_T_SHIFT)	/* DATA0 */
 | |
| #define TD_T_DATA1	(0x3 << TD_T_SHIFT)	/* DATA1 */
 | |
| #define TD_T_CARRY	(0x0 << TD_T_SHIFT)	/* uses ED_C */
 | |
| #define TD_T_GET(x)	(((x) >> TD_T_SHIFT) & TD_T_MASK)
 | |
| #define TD_DP_SHIFT	21			/* direction/pid */
 | |
| #define TD_DP_MASK	0x3
 | |
| #define TD_DP		(TD_DP_MASK << TD_DP_SHIFT)
 | |
| #define TD_DP_GET	(((x) >> TD_DP_SHIFT) & TD_DP_MASK)
 | |
| #define TD_DP_SETUP	(0x0 << TD_DP_SHIFT)	/* SETUP pid */
 | |
| #define TD_DP_OUT	(0x1 << TD_DP_SHIFT)	/* OUT pid */
 | |
| #define TD_DP_IN	(0x2 << TD_DP_SHIFT)	/* IN pid */
 | |
| #define TD_ISI_SHIFT	8			/* Interrupt Service Interval */
 | |
| #define TD_ISI_MASK	0x3f
 | |
| #define TD_ISI_GET(x)	(((x) >> TD_ISI_SHIFT) & TD_ISI_MASK)
 | |
| #define TD_FN_MASK	0x3f			/* frame number */
 | |
| #define TD_FN_GET(x)	((x) & TD_FN_MASK)
 | |
| 
 | |
| 	__hc32		hwDBP;		/* Data Buffer Pointer (or 0) */
 | |
| 	__hc32		hwCBL;		/* Controller/Buffer Length */
 | |
| 
 | |
| 	/* hwCBL bits */
 | |
| #define TD_BL_MASK	0xffff		/* buffer length */
 | |
| #define TD_BL_GET(x)	((x) & TD_BL_MASK)
 | |
| #define TD_IE		(1 << 16)	/* interrupt enable */
 | |
| 	__hc32		hwNextTD;	/* Next TD Pointer */
 | |
| 
 | |
| 	/* rest are purely for the driver's use */
 | |
| 	__u8		index;
 | |
| 	struct ed	*ed;
 | |
| 	struct td	*td_hash;	/* dma-->td hashtable */
 | |
| 	struct td	*next_dl_td;
 | |
| 	struct urb	*urb;
 | |
| 
 | |
| 	dma_addr_t	td_dma;		/* addr of this TD */
 | |
| 	dma_addr_t	data_dma;	/* addr of data it points to */
 | |
| 
 | |
| 	struct list_head td_list;	/* "shadow list", TDs on same ED */
 | |
| 
 | |
| 	u32		flags;
 | |
| #define TD_FLAG_DONE	(1 << 17)	/* retired to done list */
 | |
| #define TD_FLAG_ISO	(1 << 16)	/* copy of ED_ISO */
 | |
| } __attribute__ ((aligned(TD_ALIGN)));	/* c/b/i need 16; only iso needs 32 */
 | |
| 
 | |
| /*
 | |
|  * Hardware transfer status codes -- CC from td->hwINFO
 | |
|  */
 | |
| #define TD_CC_NOERROR		0x00
 | |
| #define TD_CC_CRC		0x01
 | |
| #define TD_CC_BITSTUFFING	0x02
 | |
| #define TD_CC_DATATOGGLEM	0x03
 | |
| #define TD_CC_STALL		0x04
 | |
| #define TD_CC_DEVNOTRESP	0x05
 | |
| #define TD_CC_PIDCHECKFAIL	0x06
 | |
| #define TD_CC_UNEXPECTEDPID	0x07
 | |
| #define TD_CC_DATAOVERRUN	0x08
 | |
| #define TD_CC_DATAUNDERRUN	0x09
 | |
|     /* 0x0A, 0x0B reserved for hardware */
 | |
| #define TD_CC_BUFFEROVERRUN	0x0C
 | |
| #define TD_CC_BUFFERUNDERRUN	0x0D
 | |
|     /* 0x0E, 0x0F reserved for HCD */
 | |
| #define TD_CC_HCD0		0x0E
 | |
| #define TD_CC_NOTACCESSED	0x0F
 | |
| 
 | |
| /*
 | |
|  * preshifted status codes
 | |
|  */
 | |
| #define TD_SCC_NOTACCESSED	(TD_CC_NOTACCESSED << TD_CC_SHIFT)
 | |
| 
 | |
| 
 | |
| /* map OHCI TD status codes (CC) to errno values */
 | |
| static const int cc_to_error[16] = {
 | |
| 	/* No  Error  */	0,
 | |
| 	/* CRC Error  */	-EILSEQ,
 | |
| 	/* Bit Stuff  */	-EPROTO,
 | |
| 	/* Data Togg  */	-EILSEQ,
 | |
| 	/* Stall      */	-EPIPE,
 | |
| 	/* DevNotResp */	-ETIME,
 | |
| 	/* PIDCheck   */	-EPROTO,
 | |
| 	/* UnExpPID   */	-EPROTO,
 | |
| 	/* DataOver   */	-EOVERFLOW,
 | |
| 	/* DataUnder  */	-EREMOTEIO,
 | |
| 	/* (for hw)   */	-EIO,
 | |
| 	/* (for hw)   */	-EIO,
 | |
| 	/* BufferOver */	-ECOMM,
 | |
| 	/* BuffUnder  */	-ENOSR,
 | |
| 	/* (for HCD)  */	-EALREADY,
 | |
| 	/* (for HCD)  */	-EALREADY
 | |
| };
 | |
| 
 | |
| #define NUM_INTS	32
 | |
| 
 | |
| /*
 | |
|  * This is the structure of the OHCI controller's memory mapped I/O region.
 | |
|  * You must use readl() and writel() (in <asm/io.h>) to access these fields!!
 | |
|  * Layout is in section 7 (and appendix B) of the spec.
 | |
|  */
 | |
| struct admhcd_regs {
 | |
| 	__hc32	gencontrol;	/* General Control */
 | |
| 	__hc32	int_status;	/* Interrupt Status */
 | |
| 	__hc32	int_enable;	/* Interrupt Enable */
 | |
| 	__hc32	reserved00;
 | |
| 	__hc32	host_control;	/* Host General Control */
 | |
| 	__hc32	reserved01;
 | |
| 	__hc32	fminterval;	/* Frame Interval */
 | |
| 	__hc32	fmnumber;	/* Frame Number */
 | |
| 	__hc32	reserved02;
 | |
| 	__hc32	reserved03;
 | |
| 	__hc32	reserved04;
 | |
| 	__hc32	reserved05;
 | |
| 	__hc32	reserved06;
 | |
| 	__hc32	reserved07;
 | |
| 	__hc32	reserved08;
 | |
| 	__hc32	reserved09;
 | |
| 	__hc32	reserved10;
 | |
| 	__hc32	reserved11;
 | |
| 	__hc32	reserved12;
 | |
| 	__hc32	reserved13;
 | |
| 	__hc32	reserved14;
 | |
| 	__hc32	reserved15;
 | |
| 	__hc32	reserved16;
 | |
| 	__hc32	reserved17;
 | |
| 	__hc32	reserved18;
 | |
| 	__hc32	reserved19;
 | |
| 	__hc32	reserved20;
 | |
| 	__hc32	reserved21;
 | |
| 	__hc32	lsthresh;	/* Low Speed Threshold */
 | |
| 	__hc32	rhdesc;		/* Root Hub Descriptor */
 | |
| #define MAX_ROOT_PORTS	2
 | |
| 	__hc32	portstatus[MAX_ROOT_PORTS]; /* Port Status */
 | |
| 	__hc32	hosthead;	/* Host Descriptor Head */
 | |
| } __attribute__ ((aligned(32)));
 | |
| 
 | |
| /*
 | |
|  * General Control register bits
 | |
|  */
 | |
| #define ADMHC_CTRL_UHFE	(1 << 0)	/* USB Host Function Enable */
 | |
| #define ADMHC_CTRL_SIR	(1 << 1)	/* Software Interrupt request */
 | |
| #define ADMHC_CTRL_DMAA	(1 << 2)	/* DMA Arbitration Control */
 | |
| #define ADMHC_CTRL_SR	(1 << 3)	/* Software Reset */
 | |
| 
 | |
| /*
 | |
|  * Host General Control register bits
 | |
|  */
 | |
| #define ADMHC_HC_BUSS		0x3		/* USB bus state */
 | |
| #define   ADMHC_BUSS_RESET	0x0
 | |
| #define   ADMHC_BUSS_RESUME	0x1
 | |
| #define   ADMHC_BUSS_OPER	0x2
 | |
| #define   ADMHC_BUSS_SUSPEND	0x3
 | |
| #define ADMHC_HC_DMAE		(1 << 2)	/* DMA enable */
 | |
| 
 | |
| /*
 | |
|  * Interrupt Status/Enable register bits
 | |
|  */
 | |
| #define ADMHC_INTR_SOFI	(1 << 4)	/* start of frame */
 | |
| #define ADMHC_INTR_RESI	(1 << 5)	/* resume detected */
 | |
| #define ADMHC_INTR_6	(1 << 6)	/* unknown */
 | |
| #define ADMHC_INTR_7	(1 << 7)	/* unknown */
 | |
| #define ADMHC_INTR_BABI	(1 << 8)	/* babble detected */
 | |
| #define ADMHC_INTR_INSM	(1 << 9)	/* root hub status change */
 | |
| #define ADMHC_INTR_SO	(1 << 10)	/* scheduling overrun */
 | |
| #define ADMHC_INTR_FNO	(1 << 11)	/* frame number overflow */
 | |
| #define ADMHC_INTR_TDC	(1 << 20)	/* transfer descriptor completed */
 | |
| #define ADMHC_INTR_SWI	(1 << 29)	/* software interrupt */
 | |
| #define ADMHC_INTR_FATI	(1 << 30)	/* fatal error */
 | |
| #define ADMHC_INTR_INTA	(1 << 31)	/* interrupt active */
 | |
| 
 | |
| #define ADMHC_INTR_MIE	(1 << 31)	/* master interrupt enable */
 | |
| 
 | |
| /*
 | |
|  * SOF Frame Interval register bits
 | |
|  */
 | |
| #define ADMHC_SFI_FI_MASK	((1 << 14)-1)	/* Frame Interval value */
 | |
| #define ADMHC_SFI_FSLDP_SHIFT	16
 | |
| #define ADMHC_SFI_FSLDP_MASK	((1 << 15)-1)
 | |
| #define ADMHC_SFI_FIT		(1 << 31)	/* Frame Interval Toggle */
 | |
| 
 | |
| /*
 | |
|  * SOF Frame Number register bits
 | |
|  */
 | |
| #define ADMHC_SFN_FN_MASK	((1 << 16)-1)	/* Frame Number Mask */
 | |
| #define ADMHC_SFN_FR_SHIFT	16		/* Frame Remaining Shift */
 | |
| #define ADMHC_SFN_FR_MASK	((1 << 14)-1)	/* Frame Remaining Mask */
 | |
| #define ADMHC_SFN_FRT		(1 << 31)	/* Frame Remaining Toggle */
 | |
| 
 | |
| /*
 | |
|  * Root Hub Descriptor register bits
 | |
|  */
 | |
| #define ADMHC_RH_NUMP	0xff		/* number of ports */
 | |
| #define	ADMHC_RH_PSM	(1 << 8)	/* power switching mode */
 | |
| #define	ADMHC_RH_NPS	(1 << 9)	/* no power switching */
 | |
| #define	ADMHC_RH_OCPM	(1 << 10)	/* over current protection mode */
 | |
| #define	ADMHC_RH_NOCP	(1 << 11)	/* no over current protection */
 | |
| #define	ADMHC_RH_PPCM	(0xff << 16)	/* port power control */
 | |
| 
 | |
| #define ADMHC_RH_LPS	(1 << 24)	/* local power switch */
 | |
| #define ADMHC_RH_OCI	(1 << 25)	/* over current indicator */
 | |
| 
 | |
| /* status change bits */
 | |
| #define ADMHC_RH_LPSC	(1 << 26)	/* local power switch change */
 | |
| #define ADMHC_RH_OCIC	(1 << 27)	/* over current indicator change */
 | |
| 
 | |
| #define ADMHC_RH_DRWE	(1 << 28)	/* device remote wakeup enable */
 | |
| #define ADMHC_RH_CRWE	(1 << 29)	/* clear remote wakeup enable */
 | |
| 
 | |
| #define ADMHC_RH_CGP	(1 << 24)	/* clear global power */
 | |
| #define ADMHC_RH_SGP	(1 << 26)	/* set global power */
 | |
| 
 | |
| /*
 | |
|  * Port Status register bits
 | |
|  */
 | |
| #define ADMHC_PS_CCS	(1 << 0)	/* current connect status */
 | |
| #define ADMHC_PS_PES	(1 << 1)	/* port enable status */
 | |
| #define ADMHC_PS_PSS	(1 << 2)	/* port suspend status */
 | |
| #define ADMHC_PS_POCI	(1 << 3)	/* port over current indicator */
 | |
| #define ADMHC_PS_PRS	(1 << 4)	/* port reset status */
 | |
| #define ADMHC_PS_PPS	(1 << 8)	/* port power status */
 | |
| #define ADMHC_PS_LSDA	(1 << 9)	/* low speed device attached */
 | |
| 
 | |
| /* status change bits */
 | |
| #define ADMHC_PS_CSC	(1 << 16)	/* connect status change */
 | |
| #define ADMHC_PS_PESC	(1 << 17)	/* port enable status change */
 | |
| #define ADMHC_PS_PSSC	(1 << 18)	/* port suspend status change */
 | |
| #define ADMHC_PS_OCIC	(1 << 19)	/* over current indicator change */
 | |
| #define ADMHC_PS_PRSC	(1 << 20)	/* port reset status change */
 | |
| 
 | |
| /* port feature bits */
 | |
| #define ADMHC_PS_CPE	(1 << 0)	/* clear port enable */
 | |
| #define ADMHC_PS_SPE	(1 << 1)	/* set port enable */
 | |
| #define ADMHC_PS_SPS	(1 << 2)	/* set port suspend */
 | |
| #define ADMHC_PS_CPS	(1 << 3)	/* clear suspend status */
 | |
| #define ADMHC_PS_SPR	(1 << 4)	/* set port reset */
 | |
| #define ADMHC_PS_SPP	(1 << 8)	/* set port power */
 | |
| #define ADMHC_PS_CPP	(1 << 9)	/* clear port power */
 | |
| 
 | |
| /*
 | |
|  * the POTPGT value is not defined in the ADMHC, so define a dummy value
 | |
|  */
 | |
| #define ADMHC_POTPGT	2		/* in ms */
 | |
| 
 | |
| /* hcd-private per-urb state */
 | |
| struct urb_priv {
 | |
| 	struct ed		*ed;
 | |
| 	struct list_head	pending;	/* URBs on the same ED */
 | |
| 
 | |
| 	u32			td_cnt;		/* # tds in this request */
 | |
| 	u32			td_idx;		/* index of the current td */
 | |
| 	struct td		*td[0];		/* all TDs in this request */
 | |
| };
 | |
| 
 | |
| #define TD_HASH_SIZE    64    /* power'o'two */
 | |
| /* sizeof (struct td) ~= 64 == 2^6 ... */
 | |
| #define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 6)) % TD_HASH_SIZE)
 | |
| 
 | |
| /*
 | |
|  * This is the full ADMHCD controller description
 | |
|  *
 | |
|  * Note how the "proper" USB information is just
 | |
|  * a subset of what the full implementation needs. (Linus)
 | |
|  */
 | |
| 
 | |
| struct admhcd {
 | |
| 	spinlock_t		lock;
 | |
| 
 | |
| 	/*
 | |
| 	 * I/O memory used to communicate with the HC (dma-consistent)
 | |
| 	 */
 | |
| 	struct admhcd_regs __iomem *regs;
 | |
| 
 | |
| 	/*
 | |
| 	 * hcd adds to schedule for a live hc any time, but removals finish
 | |
| 	 * only at the start of the next frame.
 | |
| 	 */
 | |
| 
 | |
| 	struct ed		*ed_head;
 | |
| 	struct ed		*ed_tails[4];
 | |
| 
 | |
| 	struct ed		*ed_rm_list;	/* to be removed */
 | |
| 
 | |
| 	struct ed		*periodic[NUM_INTS];	/* shadow int_table */
 | |
| 
 | |
| #if 0	/* TODO: remove? */
 | |
| 	/*
 | |
| 	 * OTG controllers and transceivers need software interaction;
 | |
| 	 * other external transceivers should be software-transparent
 | |
| 	 */
 | |
| 	struct otg_transceiver	*transceiver;
 | |
| 	void (*start_hnp)(struct admhcd *ahcd);
 | |
| #endif
 | |
| 
 | |
| 	/*
 | |
| 	 * memory management for queue data structures
 | |
| 	 */
 | |
| 	struct dma_pool		*td_cache;
 | |
| 	struct dma_pool		*ed_cache;
 | |
| 	struct td		*td_hash[TD_HASH_SIZE];
 | |
| 	struct list_head	pending;
 | |
| 
 | |
| 	/*
 | |
| 	 * driver state
 | |
| 	 */
 | |
| 	int			num_ports;
 | |
| 	int			load[NUM_INTS];
 | |
| 	u32			host_control;	/* copy of the host_control reg */
 | |
| 	unsigned long		next_statechange;	/* suspend/resume */
 | |
| 	u32			fminterval;		/* saved register */
 | |
| 	unsigned		autostop:1;	/* rh auto stopping/stopped */
 | |
| 
 | |
| 	unsigned long		flags;		/* for HC bugs */
 | |
| #define	OHCI_QUIRK_AMD756	0x01			/* erratum #4 */
 | |
| #define	OHCI_QUIRK_SUPERIO	0x02			/* natsemi */
 | |
| #define	OHCI_QUIRK_INITRESET	0x04			/* SiS, OPTi, ... */
 | |
| #define	OHCI_QUIRK_BE_DESC	0x08			/* BE descriptors */
 | |
| #define	OHCI_QUIRK_BE_MMIO	0x10			/* BE registers */
 | |
| #define	OHCI_QUIRK_ZFMICRO	0x20			/* Compaq ZFMicro chipset*/
 | |
| 	/* there are also chip quirks/bugs in init logic */
 | |
| 
 | |
| #ifdef DEBUG
 | |
| 	struct dentry		*debug_dir;
 | |
| 	struct dentry		*debug_async;
 | |
| 	struct dentry		*debug_periodic;
 | |
| 	struct dentry		*debug_registers;
 | |
| #endif
 | |
| };
 | |
| 
 | |
| /* convert between an hcd pointer and the corresponding ahcd_hcd */
 | |
| static inline struct admhcd *hcd_to_admhcd(struct usb_hcd *hcd)
 | |
| {
 | |
| 	return (struct admhcd *)(hcd->hcd_priv);
 | |
| }
 | |
| static inline struct usb_hcd *admhcd_to_hcd(const struct admhcd *ahcd)
 | |
| {
 | |
| 	return container_of((void *)ahcd, struct usb_hcd, hcd_priv);
 | |
| }
 | |
| 
 | |
| /*-------------------------------------------------------------------------*/
 | |
| 
 | |
| #ifndef DEBUG
 | |
| #define STUB_DEBUG_FILES
 | |
| #endif	/* DEBUG */
 | |
| 
 | |
| #ifdef DEBUG
 | |
| #	define admhc_dbg(ahcd, fmt, args...) \
 | |
| 		printk(KERN_DEBUG "adm5120-hcd: " fmt, ## args)
 | |
| #else
 | |
| #	define admhc_dbg(ahcd, fmt, args...) do { } while (0)
 | |
| #endif
 | |
| 
 | |
| #define admhc_err(ahcd, fmt, args...) \
 | |
| 	printk(KERN_ERR "adm5120-hcd: " fmt, ## args)
 | |
| #define admhc_info(ahcd, fmt, args...) \
 | |
| 	printk(KERN_INFO "adm5120-hcd: " fmt, ## args)
 | |
| #define admhc_warn(ahcd, fmt, args...) \
 | |
| 	printk(KERN_WARNING "adm5120-hcd: " fmt, ## args)
 | |
| 
 | |
| #ifdef ADMHC_VERBOSE_DEBUG
 | |
| #	define admhc_vdbg admhc_dbg
 | |
| #else
 | |
| #	define admhc_vdbg(ahcd, fmt, args...) do { } while (0)
 | |
| #endif
 | |
| 
 | |
| /*-------------------------------------------------------------------------*/
 | |
| 
 | |
| /*
 | |
|  * While most USB host controllers implement their registers and
 | |
|  * in-memory communication descriptors in little-endian format,
 | |
|  * a minority (notably the IBM STB04XXX and the Motorola MPC5200
 | |
|  * processors) implement them in big endian format.
 | |
|  *
 | |
|  * In addition some more exotic implementations like the Toshiba
 | |
|  * Spider (aka SCC) cell southbridge are "mixed" endian, that is,
 | |
|  * they have a different endianness for registers vs. in-memory
 | |
|  * descriptors.
 | |
|  *
 | |
|  * This attempts to support either format at compile time without a
 | |
|  * runtime penalty, or both formats with the additional overhead
 | |
|  * of checking a flag bit.
 | |
|  *
 | |
|  * That leads to some tricky Kconfig rules howevber. There are
 | |
|  * different defaults based on some arch/ppc platforms, though
 | |
|  * the basic rules are:
 | |
|  *
 | |
|  * Controller type              Kconfig options needed
 | |
|  * ---------------              ----------------------
 | |
|  * little endian                CONFIG_USB_ADMHC_LITTLE_ENDIAN
 | |
|  *
 | |
|  * fully big endian             CONFIG_USB_ADMHC_BIG_ENDIAN_DESC _and_
 | |
|  *                              CONFIG_USB_ADMHC_BIG_ENDIAN_MMIO
 | |
|  *
 | |
|  * mixed endian                 CONFIG_USB_ADMHC_LITTLE_ENDIAN _and_
 | |
|  *                              CONFIG_USB_OHCI_BIG_ENDIAN_{MMIO,DESC}
 | |
|  *
 | |
|  * (If you have a mixed endian controller, you -must- also define
 | |
|  * CONFIG_USB_ADMHC_LITTLE_ENDIAN or things will not work when building
 | |
|  * both your mixed endian and a fully big endian controller support in
 | |
|  * the same kernel image).
 | |
|  */
 | |
| 
 | |
| #ifdef CONFIG_USB_ADMHC_BIG_ENDIAN_DESC
 | |
| #ifdef CONFIG_USB_ADMHC_LITTLE_ENDIAN
 | |
| #define big_endian_desc(ahcd)	(ahcd->flags & OHCI_QUIRK_BE_DESC)
 | |
| #else
 | |
| #define big_endian_desc(ahcd)	1		/* only big endian */
 | |
| #endif
 | |
| #else
 | |
| #define big_endian_desc(ahcd)	0		/* only little endian */
 | |
| #endif
 | |
| 
 | |
| #ifdef CONFIG_USB_ADMHC_BIG_ENDIAN_MMIO
 | |
| #ifdef CONFIG_USB_ADMHC_LITTLE_ENDIAN
 | |
| #define big_endian_mmio(ahcd)	(ahcd->flags & OHCI_QUIRK_BE_MMIO)
 | |
| #else
 | |
| #define big_endian_mmio(ahcd)	1		/* only big endian */
 | |
| #endif
 | |
| #else
 | |
| #define big_endian_mmio(ahcd)	0		/* only little endian */
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * Big-endian read/write functions are arch-specific.
 | |
|  * Other arches can be added if/when they're needed.
 | |
|  *
 | |
|  */
 | |
| static inline unsigned int admhc_readl(const struct admhcd *ahcd,
 | |
| 	__hc32 __iomem *regs)
 | |
| {
 | |
| #ifdef CONFIG_USB_ADMHC_BIG_ENDIAN_MMIO
 | |
| 	return big_endian_mmio(ahcd) ?
 | |
| 		readl_be(regs) :
 | |
| 		readl(regs);
 | |
| #else
 | |
| 	return readl(regs);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| static inline void admhc_writel(const struct admhcd *ahcd,
 | |
| 	const unsigned int val, __hc32 __iomem *regs)
 | |
| {
 | |
| #ifdef CONFIG_USB_ADMHC_BIG_ENDIAN_MMIO
 | |
| 	big_endian_mmio(ahcd) ?
 | |
| 		writel_be(val, regs) :
 | |
| 		writel(val, regs);
 | |
| #else
 | |
| 		writel(val, regs);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| static inline void admhc_writel_flush(const struct admhcd *ahcd)
 | |
| {
 | |
| #if 0
 | |
| 	/* TODO: remove? */
 | |
| 	(void) admhc_readl(ahcd, &ahcd->regs->gencontrol);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| 
 | |
| /*-------------------------------------------------------------------------*/
 | |
| 
 | |
| /* cpu to ahcd */
 | |
| static inline __hc16 cpu_to_hc16(const struct admhcd *ahcd, const u16 x)
 | |
| {
 | |
| 	return big_endian_desc(ahcd) ?
 | |
| 		(__force __hc16)cpu_to_be16(x) :
 | |
| 		(__force __hc16)cpu_to_le16(x);
 | |
| }
 | |
| 
 | |
| static inline __hc16 cpu_to_hc16p(const struct admhcd *ahcd, const u16 *x)
 | |
| {
 | |
| 	return big_endian_desc(ahcd) ?
 | |
| 		cpu_to_be16p(x) :
 | |
| 		cpu_to_le16p(x);
 | |
| }
 | |
| 
 | |
| static inline __hc32 cpu_to_hc32(const struct admhcd *ahcd, const u32 x)
 | |
| {
 | |
| 	return big_endian_desc(ahcd) ?
 | |
| 		(__force __hc32)cpu_to_be32(x) :
 | |
| 		(__force __hc32)cpu_to_le32(x);
 | |
| }
 | |
| 
 | |
| static inline __hc32 cpu_to_hc32p(const struct admhcd *ahcd, const u32 *x)
 | |
| {
 | |
| 	return big_endian_desc(ahcd) ?
 | |
| 		cpu_to_be32p(x) :
 | |
| 		cpu_to_le32p(x);
 | |
| }
 | |
| 
 | |
| /* ahcd to cpu */
 | |
| static inline u16 hc16_to_cpu(const struct admhcd *ahcd, const __hc16 x)
 | |
| {
 | |
| 	return big_endian_desc(ahcd) ?
 | |
| 		be16_to_cpu((__force __be16)x) :
 | |
| 		le16_to_cpu((__force __le16)x);
 | |
| }
 | |
| 
 | |
| static inline u16 hc16_to_cpup(const struct admhcd *ahcd, const __hc16 *x)
 | |
| {
 | |
| 	return big_endian_desc(ahcd) ?
 | |
| 		be16_to_cpup((__force __be16 *)x) :
 | |
| 		le16_to_cpup((__force __le16 *)x);
 | |
| }
 | |
| 
 | |
| static inline u32 hc32_to_cpu(const struct admhcd *ahcd, const __hc32 x)
 | |
| {
 | |
| 	return big_endian_desc(ahcd) ?
 | |
| 		be32_to_cpu((__force __be32)x) :
 | |
| 		le32_to_cpu((__force __le32)x);
 | |
| }
 | |
| 
 | |
| static inline u32 hc32_to_cpup(const struct admhcd *ahcd, const __hc32 *x)
 | |
| {
 | |
| 	return big_endian_desc(ahcd) ?
 | |
| 		be32_to_cpup((__force __be32 *)x) :
 | |
| 		le32_to_cpup((__force __le32 *)x);
 | |
| }
 | |
| 
 | |
| /*-------------------------------------------------------------------------*/
 | |
| 
 | |
| static inline u16 admhc_frame_no(const struct admhcd *ahcd)
 | |
| {
 | |
| 	u32	t;
 | |
| 
 | |
| 	t = admhc_readl(ahcd, &ahcd->regs->fmnumber) & ADMHC_SFN_FN_MASK;
 | |
| 	return (u16)t;
 | |
| }
 | |
| 
 | |
| static inline u16 admhc_frame_remain(const struct admhcd *ahcd)
 | |
| {
 | |
| 	u32	t;
 | |
| 
 | |
| 	t = admhc_readl(ahcd, &ahcd->regs->fmnumber) >> ADMHC_SFN_FR_SHIFT;
 | |
| 	t &= ADMHC_SFN_FR_MASK;
 | |
| 	return (u16)t;
 | |
| }
 | |
| 
 | |
| /*-------------------------------------------------------------------------*/
 | |
| 
 | |
| static inline void admhc_disable(struct admhcd *ahcd)
 | |
| {
 | |
| 	admhcd_to_hcd(ahcd)->state = HC_STATE_HALT;
 | |
| }
 | |
| 
 | |
| #define	FI		0x2edf		/* 12000 bits per frame (-1) */
 | |
| #define	FSLDP(fi)	(0x7fff & ((6 * ((fi) - 1200)) / 7))
 | |
| #define	FIT		ADMHC_SFI_FIT
 | |
| #define LSTHRESH	0x628		/* lowspeed bit threshold */
 | |
| 
 | |
| static inline void periodic_reinit(struct admhcd *ahcd)
 | |
| {
 | |
| #if 0
 | |
| 	u32	fi = ahcd->fminterval & ADMHC_SFI_FI_MASK;
 | |
| 	u32	fit = admhc_readl(ahcd, &ahcd->regs->fminterval) & FIT;
 | |
| 
 | |
| 	/* TODO: adjust FSLargestDataPacket value too? */
 | |
| 	admhc_writel(ahcd, (fit ^ FIT) | ahcd->fminterval,
 | |
| 					&ahcd->regs->fminterval);
 | |
| #else
 | |
| 	u32	fit = admhc_readl(ahcd, &ahcd->regs->fminterval) & FIT;
 | |
| 
 | |
| 	/* TODO: adjust FSLargestDataPacket value too? */
 | |
| 	admhc_writel(ahcd, (fit ^ FIT) | ahcd->fminterval,
 | |
| 					&ahcd->regs->fminterval);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| static inline u32 admhc_read_rhdesc(struct admhcd *ahcd)
 | |
| {
 | |
| 	return admhc_readl(ahcd, &ahcd->regs->rhdesc);
 | |
| }
 | |
| 
 | |
| static inline u32 admhc_read_portstatus(struct admhcd *ahcd, int port)
 | |
| {
 | |
| 	return admhc_readl(ahcd, &ahcd->regs->portstatus[port]);
 | |
| }
 | |
| 
 | |
| static inline void admhc_write_portstatus(struct admhcd *ahcd, int port,
 | |
| 		u32 value)
 | |
| {
 | |
| 	admhc_writel(ahcd, value, &ahcd->regs->portstatus[port]);
 | |
| }
 | |
| 
 | |
| static inline void roothub_write_status(struct admhcd *ahcd, u32 value)
 | |
| {
 | |
| 	/* FIXME: read-only bits must be masked out */
 | |
| 	admhc_writel(ahcd, value, &ahcd->regs->rhdesc);
 | |
| }
 | |
| 
 | |
| static inline void admhc_intr_disable(struct admhcd *ahcd, u32 ints)
 | |
| {
 | |
| 	u32	t;
 | |
| 
 | |
| 	t = admhc_readl(ahcd, &ahcd->regs->int_enable);
 | |
| 	t &= ~(ints);
 | |
| 	admhc_writel(ahcd, t, &ahcd->regs->int_enable);
 | |
| 	/* TODO: flush writes ?*/
 | |
| }
 | |
| 
 | |
| static inline void admhc_intr_enable(struct admhcd *ahcd, u32 ints)
 | |
| {
 | |
| 	u32	t;
 | |
| 
 | |
| 	t = admhc_readl(ahcd, &ahcd->regs->int_enable);
 | |
| 	t |= ints;
 | |
| 	admhc_writel(ahcd, t, &ahcd->regs->int_enable);
 | |
| 	/* TODO: flush writes ?*/
 | |
| }
 | |
| 
 | |
| static inline void admhc_intr_ack(struct admhcd *ahcd, u32 ints)
 | |
| {
 | |
| 	admhc_writel(ahcd, ints, &ahcd->regs->int_status);
 | |
| }
 | |
| 
 | |
| static inline void admhc_dma_enable(struct admhcd *ahcd)
 | |
| {
 | |
| 	u32 t;
 | |
| 
 | |
| 	t = admhc_readl(ahcd, &ahcd->regs->host_control);
 | |
| 	if (t & ADMHC_HC_DMAE)
 | |
| 		return;
 | |
| 
 | |
| 	t |= ADMHC_HC_DMAE;
 | |
| 	admhc_writel(ahcd, t, &ahcd->regs->host_control);
 | |
| 	admhc_vdbg(ahcd, "DMA enabled\n");
 | |
| }
 | |
| 
 | |
| static inline void admhc_dma_disable(struct admhcd *ahcd)
 | |
| {
 | |
| 	u32 t;
 | |
| 
 | |
| 	t = admhc_readl(ahcd, &ahcd->regs->host_control);
 | |
| 	if (!(t & ADMHC_HC_DMAE))
 | |
| 		return;
 | |
| 
 | |
| 	t &= ~ADMHC_HC_DMAE;
 | |
| 	admhc_writel(ahcd, t, &ahcd->regs->host_control);
 | |
| 	admhc_vdbg(ahcd, "DMA disabled\n");
 | |
| }
 |