From 719a72b7c75bb239ca6184190ab994b71a31c6dc Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 17 Jul 2009 14:59:55 +0000 Subject: usb: r8a66597-hcd platform data on_chip support Convert the r8a66597-hcd driver to use the on_chip flag from platform data to enable on chip behaviour instead of relying on CONFIG_SUPERH_ON_CHIP_R8A66597 ugliness. This makes the code cleaner and also allows us to support both external and internal r8a66597 with the same kernel. It also makes the Kconfig part more future proof since we with this patch can add support for new processors with on-chip r8a66597 without modifying the Kconfig. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- include/linux/usb/r8a66597.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include/linux/usb') diff --git a/include/linux/usb/r8a66597.h b/include/linux/usb/r8a66597.h index e9f0384fa20c..460ee3f6a2c6 100644 --- a/include/linux/usb/r8a66597.h +++ b/include/linux/usb/r8a66597.h @@ -31,6 +31,9 @@ struct r8a66597_platdata { /* This ops can controll port power instead of DVSTCTR register. */ void (*port_power)(int port, int power); + /* set one = on chip controller, set zero = external controller */ + unsigned on_chip:1; + /* (external controller only) set R8A66597_PLATDATA_XTAL_nnMHZ */ unsigned xtal:2; -- cgit v1.2.3-71-gd317 From cf4f1e76c49dacfde0680b170b9a9b6a42f296bb Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 22 Jul 2009 14:32:03 +0000 Subject: usb: move r8a66597 register defines Move r8a66597 hardware register definitions from the host controller header file to the platform data header file. With this change in place we can easily share register definitions between the host controller driver and a future gadget driver. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- drivers/usb/host/r8a66597.h | 366 ------------------------------------------ include/linux/usb/r8a66597.h | 372 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 370 insertions(+), 368 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/host/r8a66597.h b/drivers/usb/host/r8a66597.h index eecbd917bc81..228e3fb23854 100644 --- a/drivers/usb/host/r8a66597.h +++ b/drivers/usb/host/r8a66597.h @@ -32,372 +32,6 @@ #include -#define SYSCFG0 0x00 -#define SYSCFG1 0x02 -#define SYSSTS0 0x04 -#define SYSSTS1 0x06 -#define DVSTCTR0 0x08 -#define DVSTCTR1 0x0A -#define TESTMODE 0x0C -#define PINCFG 0x0E -#define DMA0CFG 0x10 -#define DMA1CFG 0x12 -#define CFIFO 0x14 -#define D0FIFO 0x18 -#define D1FIFO 0x1C -#define CFIFOSEL 0x20 -#define CFIFOCTR 0x22 -#define CFIFOSIE 0x24 -#define D0FIFOSEL 0x28 -#define D0FIFOCTR 0x2A -#define D1FIFOSEL 0x2C -#define D1FIFOCTR 0x2E -#define INTENB0 0x30 -#define INTENB1 0x32 -#define INTENB2 0x34 -#define BRDYENB 0x36 -#define NRDYENB 0x38 -#define BEMPENB 0x3A -#define SOFCFG 0x3C -#define INTSTS0 0x40 -#define INTSTS1 0x42 -#define INTSTS2 0x44 -#define BRDYSTS 0x46 -#define NRDYSTS 0x48 -#define BEMPSTS 0x4A -#define FRMNUM 0x4C -#define UFRMNUM 0x4E -#define USBADDR 0x50 -#define USBREQ 0x54 -#define USBVAL 0x56 -#define USBINDX 0x58 -#define USBLENG 0x5A -#define DCPCFG 0x5C -#define DCPMAXP 0x5E -#define DCPCTR 0x60 -#define PIPESEL 0x64 -#define PIPECFG 0x68 -#define PIPEBUF 0x6A -#define PIPEMAXP 0x6C -#define PIPEPERI 0x6E -#define PIPE1CTR 0x70 -#define PIPE2CTR 0x72 -#define PIPE3CTR 0x74 -#define PIPE4CTR 0x76 -#define PIPE5CTR 0x78 -#define PIPE6CTR 0x7A -#define PIPE7CTR 0x7C -#define PIPE8CTR 0x7E -#define PIPE9CTR 0x80 -#define PIPE1TRE 0x90 -#define PIPE1TRN 0x92 -#define PIPE2TRE 0x94 -#define PIPE2TRN 0x96 -#define PIPE3TRE 0x98 -#define PIPE3TRN 0x9A -#define PIPE4TRE 0x9C -#define PIPE4TRN 0x9E -#define PIPE5TRE 0xA0 -#define PIPE5TRN 0xA2 -#define DEVADD0 0xD0 -#define DEVADD1 0xD2 -#define DEVADD2 0xD4 -#define DEVADD3 0xD6 -#define DEVADD4 0xD8 -#define DEVADD5 0xDA -#define DEVADD6 0xDC -#define DEVADD7 0xDE -#define DEVADD8 0xE0 -#define DEVADD9 0xE2 -#define DEVADDA 0xE4 - -/* System Configuration Control Register */ -#define XTAL 0xC000 /* b15-14: Crystal selection */ -#define XTAL48 0x8000 /* 48MHz */ -#define XTAL24 0x4000 /* 24MHz */ -#define XTAL12 0x0000 /* 12MHz */ -#define XCKE 0x2000 /* b13: External clock enable */ -#define PLLC 0x0800 /* b11: PLL control */ -#define SCKE 0x0400 /* b10: USB clock enable */ -#define PCSDIS 0x0200 /* b9: not CS wakeup */ -#define LPSME 0x0100 /* b8: Low power sleep mode */ -#define HSE 0x0080 /* b7: Hi-speed enable */ -#define DCFM 0x0040 /* b6: Controller function select */ -#define DRPD 0x0020 /* b5: D+/- pull down control */ -#define DPRPU 0x0010 /* b4: D+ pull up control */ -#define USBE 0x0001 /* b0: USB module operation enable */ - -/* System Configuration Status Register */ -#define OVCBIT 0x8000 /* b15-14: Over-current bit */ -#define OVCMON 0xC000 /* b15-14: Over-current monitor */ -#define SOFEA 0x0020 /* b5: SOF monitor */ -#define IDMON 0x0004 /* b3: ID-pin monitor */ -#define LNST 0x0003 /* b1-0: D+, D- line status */ -#define SE1 0x0003 /* SE1 */ -#define FS_KSTS 0x0002 /* Full-Speed K State */ -#define FS_JSTS 0x0001 /* Full-Speed J State */ -#define LS_JSTS 0x0002 /* Low-Speed J State */ -#define LS_KSTS 0x0001 /* Low-Speed K State */ -#define SE0 0x0000 /* SE0 */ - -/* Device State Control Register */ -#define EXTLP0 0x0400 /* b10: External port */ -#define VBOUT 0x0200 /* b9: VBUS output */ -#define WKUP 0x0100 /* b8: Remote wakeup */ -#define RWUPE 0x0080 /* b7: Remote wakeup sense */ -#define USBRST 0x0040 /* b6: USB reset enable */ -#define RESUME 0x0020 /* b5: Resume enable */ -#define UACT 0x0010 /* b4: USB bus enable */ -#define RHST 0x0007 /* b1-0: Reset handshake status */ -#define HSPROC 0x0004 /* HS handshake is processing */ -#define HSMODE 0x0003 /* Hi-Speed mode */ -#define FSMODE 0x0002 /* Full-Speed mode */ -#define LSMODE 0x0001 /* Low-Speed mode */ -#define UNDECID 0x0000 /* Undecided */ - -/* Test Mode Register */ -#define UTST 0x000F /* b3-0: Test select */ -#define H_TST_PACKET 0x000C /* HOST TEST Packet */ -#define H_TST_SE0_NAK 0x000B /* HOST TEST SE0 NAK */ -#define H_TST_K 0x000A /* HOST TEST K */ -#define H_TST_J 0x0009 /* HOST TEST J */ -#define H_TST_NORMAL 0x0000 /* HOST Normal Mode */ -#define P_TST_PACKET 0x0004 /* PERI TEST Packet */ -#define P_TST_SE0_NAK 0x0003 /* PERI TEST SE0 NAK */ -#define P_TST_K 0x0002 /* PERI TEST K */ -#define P_TST_J 0x0001 /* PERI TEST J */ -#define P_TST_NORMAL 0x0000 /* PERI Normal Mode */ - -/* Data Pin Configuration Register */ -#define LDRV 0x8000 /* b15: Drive Current Adjust */ -#define VIF1 0x0000 /* VIF = 1.8V */ -#define VIF3 0x8000 /* VIF = 3.3V */ -#define INTA 0x0001 /* b1: USB INT-pin active */ - -/* DMAx Pin Configuration Register */ -#define DREQA 0x4000 /* b14: Dreq active select */ -#define BURST 0x2000 /* b13: Burst mode */ -#define DACKA 0x0400 /* b10: Dack active select */ -#define DFORM 0x0380 /* b9-7: DMA mode select */ -#define CPU_ADR_RD_WR 0x0000 /* Address + RD/WR mode (CPU bus) */ -#define CPU_DACK_RD_WR 0x0100 /* DACK + RD/WR mode (CPU bus) */ -#define CPU_DACK_ONLY 0x0180 /* DACK only mode (CPU bus) */ -#define SPLIT_DACK_ONLY 0x0200 /* DACK only mode (SPLIT bus) */ -#define DENDA 0x0040 /* b6: Dend active select */ -#define PKTM 0x0020 /* b5: Packet mode */ -#define DENDE 0x0010 /* b4: Dend enable */ -#define OBUS 0x0004 /* b2: OUTbus mode */ - -/* CFIFO/DxFIFO Port Select Register */ -#define RCNT 0x8000 /* b15: Read count mode */ -#define REW 0x4000 /* b14: Buffer rewind */ -#define DCLRM 0x2000 /* b13: DMA buffer clear mode */ -#define DREQE 0x1000 /* b12: DREQ output enable */ -#define MBW_8 0x0000 /* 8bit */ -#define MBW_16 0x0400 /* 16bit */ -#define MBW_32 0x0800 /* 32bit */ -#define BIGEND 0x0100 /* b8: Big endian mode */ -#define BYTE_LITTLE 0x0000 /* little dendian */ -#define BYTE_BIG 0x0100 /* big endifan */ -#define ISEL 0x0020 /* b5: DCP FIFO port direction select */ -#define CURPIPE 0x000F /* b2-0: PIPE select */ - -/* CFIFO/DxFIFO Port Control Register */ -#define BVAL 0x8000 /* b15: Buffer valid flag */ -#define BCLR 0x4000 /* b14: Buffer clear */ -#define FRDY 0x2000 /* b13: FIFO ready */ -#define DTLN 0x0FFF /* b11-0: FIFO received data length */ - -/* Interrupt Enable Register 0 */ -#define VBSE 0x8000 /* b15: VBUS interrupt */ -#define RSME 0x4000 /* b14: Resume interrupt */ -#define SOFE 0x2000 /* b13: Frame update interrupt */ -#define DVSE 0x1000 /* b12: Device state transition interrupt */ -#define CTRE 0x0800 /* b11: Control transfer stage transition interrupt */ -#define BEMPE 0x0400 /* b10: Buffer empty interrupt */ -#define NRDYE 0x0200 /* b9: Buffer not ready interrupt */ -#define BRDYE 0x0100 /* b8: Buffer ready interrupt */ - -/* Interrupt Enable Register 1 */ -#define OVRCRE 0x8000 /* b15: Over-current interrupt */ -#define BCHGE 0x4000 /* b14: USB us chenge interrupt */ -#define DTCHE 0x1000 /* b12: Detach sense interrupt */ -#define ATTCHE 0x0800 /* b11: Attach sense interrupt */ -#define EOFERRE 0x0040 /* b6: EOF error interrupt */ -#define SIGNE 0x0020 /* b5: SETUP IGNORE interrupt */ -#define SACKE 0x0010 /* b4: SETUP ACK interrupt */ - -/* BRDY Interrupt Enable/Status Register */ -#define BRDY9 0x0200 /* b9: PIPE9 */ -#define BRDY8 0x0100 /* b8: PIPE8 */ -#define BRDY7 0x0080 /* b7: PIPE7 */ -#define BRDY6 0x0040 /* b6: PIPE6 */ -#define BRDY5 0x0020 /* b5: PIPE5 */ -#define BRDY4 0x0010 /* b4: PIPE4 */ -#define BRDY3 0x0008 /* b3: PIPE3 */ -#define BRDY2 0x0004 /* b2: PIPE2 */ -#define BRDY1 0x0002 /* b1: PIPE1 */ -#define BRDY0 0x0001 /* b1: PIPE0 */ - -/* NRDY Interrupt Enable/Status Register */ -#define NRDY9 0x0200 /* b9: PIPE9 */ -#define NRDY8 0x0100 /* b8: PIPE8 */ -#define NRDY7 0x0080 /* b7: PIPE7 */ -#define NRDY6 0x0040 /* b6: PIPE6 */ -#define NRDY5 0x0020 /* b5: PIPE5 */ -#define NRDY4 0x0010 /* b4: PIPE4 */ -#define NRDY3 0x0008 /* b3: PIPE3 */ -#define NRDY2 0x0004 /* b2: PIPE2 */ -#define NRDY1 0x0002 /* b1: PIPE1 */ -#define NRDY0 0x0001 /* b1: PIPE0 */ - -/* BEMP Interrupt Enable/Status Register */ -#define BEMP9 0x0200 /* b9: PIPE9 */ -#define BEMP8 0x0100 /* b8: PIPE8 */ -#define BEMP7 0x0080 /* b7: PIPE7 */ -#define BEMP6 0x0040 /* b6: PIPE6 */ -#define BEMP5 0x0020 /* b5: PIPE5 */ -#define BEMP4 0x0010 /* b4: PIPE4 */ -#define BEMP3 0x0008 /* b3: PIPE3 */ -#define BEMP2 0x0004 /* b2: PIPE2 */ -#define BEMP1 0x0002 /* b1: PIPE1 */ -#define BEMP0 0x0001 /* b0: PIPE0 */ - -/* SOF Pin Configuration Register */ -#define TRNENSEL 0x0100 /* b8: Select transaction enable period */ -#define BRDYM 0x0040 /* b6: BRDY clear timing */ -#define INTL 0x0020 /* b5: Interrupt sense select */ -#define EDGESTS 0x0010 /* b4: */ -#define SOFMODE 0x000C /* b3-2: SOF pin select */ -#define SOF_125US 0x0008 /* SOF OUT 125us Frame Signal */ -#define SOF_1MS 0x0004 /* SOF OUT 1ms Frame Signal */ -#define SOF_DISABLE 0x0000 /* SOF OUT Disable */ - -/* Interrupt Status Register 0 */ -#define VBINT 0x8000 /* b15: VBUS interrupt */ -#define RESM 0x4000 /* b14: Resume interrupt */ -#define SOFR 0x2000 /* b13: SOF frame update interrupt */ -#define DVST 0x1000 /* b12: Device state transition interrupt */ -#define CTRT 0x0800 /* b11: Control transfer stage transition interrupt */ -#define BEMP 0x0400 /* b10: Buffer empty interrupt */ -#define NRDY 0x0200 /* b9: Buffer not ready interrupt */ -#define BRDY 0x0100 /* b8: Buffer ready interrupt */ -#define VBSTS 0x0080 /* b7: VBUS input port */ -#define DVSQ 0x0070 /* b6-4: Device state */ -#define DS_SPD_CNFG 0x0070 /* Suspend Configured */ -#define DS_SPD_ADDR 0x0060 /* Suspend Address */ -#define DS_SPD_DFLT 0x0050 /* Suspend Default */ -#define DS_SPD_POWR 0x0040 /* Suspend Powered */ -#define DS_SUSP 0x0040 /* Suspend */ -#define DS_CNFG 0x0030 /* Configured */ -#define DS_ADDS 0x0020 /* Address */ -#define DS_DFLT 0x0010 /* Default */ -#define DS_POWR 0x0000 /* Powered */ -#define DVSQS 0x0030 /* b5-4: Device state */ -#define VALID 0x0008 /* b3: Setup packet detected flag */ -#define CTSQ 0x0007 /* b2-0: Control transfer stage */ -#define CS_SQER 0x0006 /* Sequence error */ -#define CS_WRND 0x0005 /* Control write nodata status stage */ -#define CS_WRSS 0x0004 /* Control write status stage */ -#define CS_WRDS 0x0003 /* Control write data stage */ -#define CS_RDSS 0x0002 /* Control read status stage */ -#define CS_RDDS 0x0001 /* Control read data stage */ -#define CS_IDST 0x0000 /* Idle or setup stage */ - -/* Interrupt Status Register 1 */ -#define OVRCR 0x8000 /* b15: Over-current interrupt */ -#define BCHG 0x4000 /* b14: USB bus chenge interrupt */ -#define DTCH 0x1000 /* b12: Detach sense interrupt */ -#define ATTCH 0x0800 /* b11: Attach sense interrupt */ -#define EOFERR 0x0040 /* b6: EOF-error interrupt */ -#define SIGN 0x0020 /* b5: Setup ignore interrupt */ -#define SACK 0x0010 /* b4: Setup acknowledge interrupt */ - -/* Frame Number Register */ -#define OVRN 0x8000 /* b15: Overrun error */ -#define CRCE 0x4000 /* b14: Received data error */ -#define FRNM 0x07FF /* b10-0: Frame number */ - -/* Micro Frame Number Register */ -#define UFRNM 0x0007 /* b2-0: Micro frame number */ - -/* Default Control Pipe Maxpacket Size Register */ -/* Pipe Maxpacket Size Register */ -#define DEVSEL 0xF000 /* b15-14: Device address select */ -#define MAXP 0x007F /* b6-0: Maxpacket size of default control pipe */ - -/* Default Control Pipe Control Register */ -#define BSTS 0x8000 /* b15: Buffer status */ -#define SUREQ 0x4000 /* b14: Send USB request */ -#define CSCLR 0x2000 /* b13: complete-split status clear */ -#define CSSTS 0x1000 /* b12: complete-split status */ -#define SUREQCLR 0x0800 /* b11: stop setup request */ -#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */ -#define SQSET 0x0080 /* b7: Sequence toggle bit set */ -#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */ -#define PBUSY 0x0020 /* b5: pipe busy */ -#define PINGE 0x0010 /* b4: ping enable */ -#define CCPL 0x0004 /* b2: Enable control transfer complete */ -#define PID 0x0003 /* b1-0: Response PID */ -#define PID_STALL11 0x0003 /* STALL */ -#define PID_STALL 0x0002 /* STALL */ -#define PID_BUF 0x0001 /* BUF */ -#define PID_NAK 0x0000 /* NAK */ - -/* Pipe Window Select Register */ -#define PIPENM 0x0007 /* b2-0: Pipe select */ - -/* Pipe Configuration Register */ -#define R8A66597_TYP 0xC000 /* b15-14: Transfer type */ -#define R8A66597_ISO 0xC000 /* Isochronous */ -#define R8A66597_INT 0x8000 /* Interrupt */ -#define R8A66597_BULK 0x4000 /* Bulk */ -#define R8A66597_BFRE 0x0400 /* b10: Buffer ready interrupt mode select */ -#define R8A66597_DBLB 0x0200 /* b9: Double buffer mode select */ -#define R8A66597_CNTMD 0x0100 /* b8: Continuous transfer mode select */ -#define R8A66597_SHTNAK 0x0080 /* b7: Transfer end NAK */ -#define R8A66597_DIR 0x0010 /* b4: Transfer direction select */ -#define R8A66597_EPNUM 0x000F /* b3-0: Eendpoint number select */ - -/* Pipe Buffer Configuration Register */ -#define BUFSIZE 0x7C00 /* b14-10: Pipe buffer size */ -#define BUFNMB 0x007F /* b6-0: Pipe buffer number */ -#define PIPE0BUF 256 -#define PIPExBUF 64 - -/* Pipe Maxpacket Size Register */ -#define MXPS 0x07FF /* b10-0: Maxpacket size */ - -/* Pipe Cycle Configuration Register */ -#define IFIS 0x1000 /* b12: Isochronous in-buffer flush mode select */ -#define IITV 0x0007 /* b2-0: Isochronous interval */ - -/* Pipex Control Register */ -#define BSTS 0x8000 /* b15: Buffer status */ -#define INBUFM 0x4000 /* b14: IN buffer monitor (Only for PIPE1 to 5) */ -#define CSCLR 0x2000 /* b13: complete-split status clear */ -#define CSSTS 0x1000 /* b12: complete-split status */ -#define ATREPM 0x0400 /* b10: Auto repeat mode */ -#define ACLRM 0x0200 /* b9: Out buffer auto clear mode */ -#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */ -#define SQSET 0x0080 /* b7: Sequence toggle bit set */ -#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */ -#define PBUSY 0x0020 /* b5: pipe busy */ -#define PID 0x0003 /* b1-0: Response PID */ - -/* PIPExTRE */ -#define TRENB 0x0200 /* b9: Transaction counter enable */ -#define TRCLR 0x0100 /* b8: Transaction counter clear */ - -/* PIPExTRN */ -#define TRNCNT 0xFFFF /* b15-0: Transaction counter */ - -/* DEVADDx */ -#define UPPHUB 0x7800 -#define HUBPORT 0x0700 -#define USBSPD 0x00C0 -#define RTPORT 0x0001 - #define R8A66597_MAX_NUM_PIPE 10 #define R8A66597_BUF_BSIZE 8 #define R8A66597_MAX_DEVICE 10 diff --git a/include/linux/usb/r8a66597.h b/include/linux/usb/r8a66597.h index 460ee3f6a2c6..26d216734057 100644 --- a/include/linux/usb/r8a66597.h +++ b/include/linux/usb/r8a66597.h @@ -28,7 +28,7 @@ #define R8A66597_PLATDATA_XTAL_48MHZ 0x03 struct r8a66597_platdata { - /* This ops can controll port power instead of DVSTCTR register. */ + /* This callback can control port power instead of DVSTCTR register. */ void (*port_power)(int port, int power); /* set one = on chip controller, set zero = external controller */ @@ -43,5 +43,373 @@ struct r8a66597_platdata { /* set one = big endian, set zero = little endian */ unsigned endian:1; }; -#endif + +/* Register definitions */ +#define SYSCFG0 0x00 +#define SYSCFG1 0x02 +#define SYSSTS0 0x04 +#define SYSSTS1 0x06 +#define DVSTCTR0 0x08 +#define DVSTCTR1 0x0A +#define TESTMODE 0x0C +#define PINCFG 0x0E +#define DMA0CFG 0x10 +#define DMA1CFG 0x12 +#define CFIFO 0x14 +#define D0FIFO 0x18 +#define D1FIFO 0x1C +#define CFIFOSEL 0x20 +#define CFIFOCTR 0x22 +#define CFIFOSIE 0x24 +#define D0FIFOSEL 0x28 +#define D0FIFOCTR 0x2A +#define D1FIFOSEL 0x2C +#define D1FIFOCTR 0x2E +#define INTENB0 0x30 +#define INTENB1 0x32 +#define INTENB2 0x34 +#define BRDYENB 0x36 +#define NRDYENB 0x38 +#define BEMPENB 0x3A +#define SOFCFG 0x3C +#define INTSTS0 0x40 +#define INTSTS1 0x42 +#define INTSTS2 0x44 +#define BRDYSTS 0x46 +#define NRDYSTS 0x48 +#define BEMPSTS 0x4A +#define FRMNUM 0x4C +#define UFRMNUM 0x4E +#define USBADDR 0x50 +#define USBREQ 0x54 +#define USBVAL 0x56 +#define USBINDX 0x58 +#define USBLENG 0x5A +#define DCPCFG 0x5C +#define DCPMAXP 0x5E +#define DCPCTR 0x60 +#define PIPESEL 0x64 +#define PIPECFG 0x68 +#define PIPEBUF 0x6A +#define PIPEMAXP 0x6C +#define PIPEPERI 0x6E +#define PIPE1CTR 0x70 +#define PIPE2CTR 0x72 +#define PIPE3CTR 0x74 +#define PIPE4CTR 0x76 +#define PIPE5CTR 0x78 +#define PIPE6CTR 0x7A +#define PIPE7CTR 0x7C +#define PIPE8CTR 0x7E +#define PIPE9CTR 0x80 +#define PIPE1TRE 0x90 +#define PIPE1TRN 0x92 +#define PIPE2TRE 0x94 +#define PIPE2TRN 0x96 +#define PIPE3TRE 0x98 +#define PIPE3TRN 0x9A +#define PIPE4TRE 0x9C +#define PIPE4TRN 0x9E +#define PIPE5TRE 0xA0 +#define PIPE5TRN 0xA2 +#define DEVADD0 0xD0 +#define DEVADD1 0xD2 +#define DEVADD2 0xD4 +#define DEVADD3 0xD6 +#define DEVADD4 0xD8 +#define DEVADD5 0xDA +#define DEVADD6 0xDC +#define DEVADD7 0xDE +#define DEVADD8 0xE0 +#define DEVADD9 0xE2 +#define DEVADDA 0xE4 + +/* System Configuration Control Register */ +#define XTAL 0xC000 /* b15-14: Crystal selection */ +#define XTAL48 0x8000 /* 48MHz */ +#define XTAL24 0x4000 /* 24MHz */ +#define XTAL12 0x0000 /* 12MHz */ +#define XCKE 0x2000 /* b13: External clock enable */ +#define PLLC 0x0800 /* b11: PLL control */ +#define SCKE 0x0400 /* b10: USB clock enable */ +#define PCSDIS 0x0200 /* b9: not CS wakeup */ +#define LPSME 0x0100 /* b8: Low power sleep mode */ +#define HSE 0x0080 /* b7: Hi-speed enable */ +#define DCFM 0x0040 /* b6: Controller function select */ +#define DRPD 0x0020 /* b5: D+/- pull down control */ +#define DPRPU 0x0010 /* b4: D+ pull up control */ +#define USBE 0x0001 /* b0: USB module operation enable */ + +/* System Configuration Status Register */ +#define OVCBIT 0x8000 /* b15-14: Over-current bit */ +#define OVCMON 0xC000 /* b15-14: Over-current monitor */ +#define SOFEA 0x0020 /* b5: SOF monitor */ +#define IDMON 0x0004 /* b3: ID-pin monitor */ +#define LNST 0x0003 /* b1-0: D+, D- line status */ +#define SE1 0x0003 /* SE1 */ +#define FS_KSTS 0x0002 /* Full-Speed K State */ +#define FS_JSTS 0x0001 /* Full-Speed J State */ +#define LS_JSTS 0x0002 /* Low-Speed J State */ +#define LS_KSTS 0x0001 /* Low-Speed K State */ +#define SE0 0x0000 /* SE0 */ + +/* Device State Control Register */ +#define EXTLP0 0x0400 /* b10: External port */ +#define VBOUT 0x0200 /* b9: VBUS output */ +#define WKUP 0x0100 /* b8: Remote wakeup */ +#define RWUPE 0x0080 /* b7: Remote wakeup sense */ +#define USBRST 0x0040 /* b6: USB reset enable */ +#define RESUME 0x0020 /* b5: Resume enable */ +#define UACT 0x0010 /* b4: USB bus enable */ +#define RHST 0x0007 /* b1-0: Reset handshake status */ +#define HSPROC 0x0004 /* HS handshake is processing */ +#define HSMODE 0x0003 /* Hi-Speed mode */ +#define FSMODE 0x0002 /* Full-Speed mode */ +#define LSMODE 0x0001 /* Low-Speed mode */ +#define UNDECID 0x0000 /* Undecided */ + +/* Test Mode Register */ +#define UTST 0x000F /* b3-0: Test select */ +#define H_TST_PACKET 0x000C /* HOST TEST Packet */ +#define H_TST_SE0_NAK 0x000B /* HOST TEST SE0 NAK */ +#define H_TST_K 0x000A /* HOST TEST K */ +#define H_TST_J 0x0009 /* HOST TEST J */ +#define H_TST_NORMAL 0x0000 /* HOST Normal Mode */ +#define P_TST_PACKET 0x0004 /* PERI TEST Packet */ +#define P_TST_SE0_NAK 0x0003 /* PERI TEST SE0 NAK */ +#define P_TST_K 0x0002 /* PERI TEST K */ +#define P_TST_J 0x0001 /* PERI TEST J */ +#define P_TST_NORMAL 0x0000 /* PERI Normal Mode */ + +/* Data Pin Configuration Register */ +#define LDRV 0x8000 /* b15: Drive Current Adjust */ +#define VIF1 0x0000 /* VIF = 1.8V */ +#define VIF3 0x8000 /* VIF = 3.3V */ +#define INTA 0x0001 /* b1: USB INT-pin active */ + +/* DMAx Pin Configuration Register */ +#define DREQA 0x4000 /* b14: Dreq active select */ +#define BURST 0x2000 /* b13: Burst mode */ +#define DACKA 0x0400 /* b10: Dack active select */ +#define DFORM 0x0380 /* b9-7: DMA mode select */ +#define CPU_ADR_RD_WR 0x0000 /* Address + RD/WR mode (CPU bus) */ +#define CPU_DACK_RD_WR 0x0100 /* DACK + RD/WR mode (CPU bus) */ +#define CPU_DACK_ONLY 0x0180 /* DACK only mode (CPU bus) */ +#define SPLIT_DACK_ONLY 0x0200 /* DACK only mode (SPLIT bus) */ +#define DENDA 0x0040 /* b6: Dend active select */ +#define PKTM 0x0020 /* b5: Packet mode */ +#define DENDE 0x0010 /* b4: Dend enable */ +#define OBUS 0x0004 /* b2: OUTbus mode */ + +/* CFIFO/DxFIFO Port Select Register */ +#define RCNT 0x8000 /* b15: Read count mode */ +#define REW 0x4000 /* b14: Buffer rewind */ +#define DCLRM 0x2000 /* b13: DMA buffer clear mode */ +#define DREQE 0x1000 /* b12: DREQ output enable */ +#define MBW_8 0x0000 /* 8bit */ +#define MBW_16 0x0400 /* 16bit */ +#define MBW_32 0x0800 /* 32bit */ +#define BIGEND 0x0100 /* b8: Big endian mode */ +#define BYTE_LITTLE 0x0000 /* little dendian */ +#define BYTE_BIG 0x0100 /* big endifan */ +#define ISEL 0x0020 /* b5: DCP FIFO port direction select */ +#define CURPIPE 0x000F /* b2-0: PIPE select */ + +/* CFIFO/DxFIFO Port Control Register */ +#define BVAL 0x8000 /* b15: Buffer valid flag */ +#define BCLR 0x4000 /* b14: Buffer clear */ +#define FRDY 0x2000 /* b13: FIFO ready */ +#define DTLN 0x0FFF /* b11-0: FIFO received data length */ + +/* Interrupt Enable Register 0 */ +#define VBSE 0x8000 /* b15: VBUS interrupt */ +#define RSME 0x4000 /* b14: Resume interrupt */ +#define SOFE 0x2000 /* b13: Frame update interrupt */ +#define DVSE 0x1000 /* b12: Device state transition interrupt */ +#define CTRE 0x0800 /* b11: Control transfer stage transition interrupt */ +#define BEMPE 0x0400 /* b10: Buffer empty interrupt */ +#define NRDYE 0x0200 /* b9: Buffer not ready interrupt */ +#define BRDYE 0x0100 /* b8: Buffer ready interrupt */ + +/* Interrupt Enable Register 1 */ +#define OVRCRE 0x8000 /* b15: Over-current interrupt */ +#define BCHGE 0x4000 /* b14: USB us chenge interrupt */ +#define DTCHE 0x1000 /* b12: Detach sense interrupt */ +#define ATTCHE 0x0800 /* b11: Attach sense interrupt */ +#define EOFERRE 0x0040 /* b6: EOF error interrupt */ +#define SIGNE 0x0020 /* b5: SETUP IGNORE interrupt */ +#define SACKE 0x0010 /* b4: SETUP ACK interrupt */ + +/* BRDY Interrupt Enable/Status Register */ +#define BRDY9 0x0200 /* b9: PIPE9 */ +#define BRDY8 0x0100 /* b8: PIPE8 */ +#define BRDY7 0x0080 /* b7: PIPE7 */ +#define BRDY6 0x0040 /* b6: PIPE6 */ +#define BRDY5 0x0020 /* b5: PIPE5 */ +#define BRDY4 0x0010 /* b4: PIPE4 */ +#define BRDY3 0x0008 /* b3: PIPE3 */ +#define BRDY2 0x0004 /* b2: PIPE2 */ +#define BRDY1 0x0002 /* b1: PIPE1 */ +#define BRDY0 0x0001 /* b1: PIPE0 */ + +/* NRDY Interrupt Enable/Status Register */ +#define NRDY9 0x0200 /* b9: PIPE9 */ +#define NRDY8 0x0100 /* b8: PIPE8 */ +#define NRDY7 0x0080 /* b7: PIPE7 */ +#define NRDY6 0x0040 /* b6: PIPE6 */ +#define NRDY5 0x0020 /* b5: PIPE5 */ +#define NRDY4 0x0010 /* b4: PIPE4 */ +#define NRDY3 0x0008 /* b3: PIPE3 */ +#define NRDY2 0x0004 /* b2: PIPE2 */ +#define NRDY1 0x0002 /* b1: PIPE1 */ +#define NRDY0 0x0001 /* b1: PIPE0 */ + +/* BEMP Interrupt Enable/Status Register */ +#define BEMP9 0x0200 /* b9: PIPE9 */ +#define BEMP8 0x0100 /* b8: PIPE8 */ +#define BEMP7 0x0080 /* b7: PIPE7 */ +#define BEMP6 0x0040 /* b6: PIPE6 */ +#define BEMP5 0x0020 /* b5: PIPE5 */ +#define BEMP4 0x0010 /* b4: PIPE4 */ +#define BEMP3 0x0008 /* b3: PIPE3 */ +#define BEMP2 0x0004 /* b2: PIPE2 */ +#define BEMP1 0x0002 /* b1: PIPE1 */ +#define BEMP0 0x0001 /* b0: PIPE0 */ + +/* SOF Pin Configuration Register */ +#define TRNENSEL 0x0100 /* b8: Select transaction enable period */ +#define BRDYM 0x0040 /* b6: BRDY clear timing */ +#define INTL 0x0020 /* b5: Interrupt sense select */ +#define EDGESTS 0x0010 /* b4: */ +#define SOFMODE 0x000C /* b3-2: SOF pin select */ +#define SOF_125US 0x0008 /* SOF OUT 125us Frame Signal */ +#define SOF_1MS 0x0004 /* SOF OUT 1ms Frame Signal */ +#define SOF_DISABLE 0x0000 /* SOF OUT Disable */ + +/* Interrupt Status Register 0 */ +#define VBINT 0x8000 /* b15: VBUS interrupt */ +#define RESM 0x4000 /* b14: Resume interrupt */ +#define SOFR 0x2000 /* b13: SOF frame update interrupt */ +#define DVST 0x1000 /* b12: Device state transition interrupt */ +#define CTRT 0x0800 /* b11: Control transfer stage transition interrupt */ +#define BEMP 0x0400 /* b10: Buffer empty interrupt */ +#define NRDY 0x0200 /* b9: Buffer not ready interrupt */ +#define BRDY 0x0100 /* b8: Buffer ready interrupt */ +#define VBSTS 0x0080 /* b7: VBUS input port */ +#define DVSQ 0x0070 /* b6-4: Device state */ +#define DS_SPD_CNFG 0x0070 /* Suspend Configured */ +#define DS_SPD_ADDR 0x0060 /* Suspend Address */ +#define DS_SPD_DFLT 0x0050 /* Suspend Default */ +#define DS_SPD_POWR 0x0040 /* Suspend Powered */ +#define DS_SUSP 0x0040 /* Suspend */ +#define DS_CNFG 0x0030 /* Configured */ +#define DS_ADDS 0x0020 /* Address */ +#define DS_DFLT 0x0010 /* Default */ +#define DS_POWR 0x0000 /* Powered */ +#define DVSQS 0x0030 /* b5-4: Device state */ +#define VALID 0x0008 /* b3: Setup packet detected flag */ +#define CTSQ 0x0007 /* b2-0: Control transfer stage */ +#define CS_SQER 0x0006 /* Sequence error */ +#define CS_WRND 0x0005 /* Control write nodata status stage */ +#define CS_WRSS 0x0004 /* Control write status stage */ +#define CS_WRDS 0x0003 /* Control write data stage */ +#define CS_RDSS 0x0002 /* Control read status stage */ +#define CS_RDDS 0x0001 /* Control read data stage */ +#define CS_IDST 0x0000 /* Idle or setup stage */ + +/* Interrupt Status Register 1 */ +#define OVRCR 0x8000 /* b15: Over-current interrupt */ +#define BCHG 0x4000 /* b14: USB bus chenge interrupt */ +#define DTCH 0x1000 /* b12: Detach sense interrupt */ +#define ATTCH 0x0800 /* b11: Attach sense interrupt */ +#define EOFERR 0x0040 /* b6: EOF-error interrupt */ +#define SIGN 0x0020 /* b5: Setup ignore interrupt */ +#define SACK 0x0010 /* b4: Setup acknowledge interrupt */ + +/* Frame Number Register */ +#define OVRN 0x8000 /* b15: Overrun error */ +#define CRCE 0x4000 /* b14: Received data error */ +#define FRNM 0x07FF /* b10-0: Frame number */ + +/* Micro Frame Number Register */ +#define UFRNM 0x0007 /* b2-0: Micro frame number */ + +/* Default Control Pipe Maxpacket Size Register */ +/* Pipe Maxpacket Size Register */ +#define DEVSEL 0xF000 /* b15-14: Device address select */ +#define MAXP 0x007F /* b6-0: Maxpacket size of default control pipe */ + +/* Default Control Pipe Control Register */ +#define BSTS 0x8000 /* b15: Buffer status */ +#define SUREQ 0x4000 /* b14: Send USB request */ +#define CSCLR 0x2000 /* b13: complete-split status clear */ +#define CSSTS 0x1000 /* b12: complete-split status */ +#define SUREQCLR 0x0800 /* b11: stop setup request */ +#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */ +#define SQSET 0x0080 /* b7: Sequence toggle bit set */ +#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */ +#define PBUSY 0x0020 /* b5: pipe busy */ +#define PINGE 0x0010 /* b4: ping enable */ +#define CCPL 0x0004 /* b2: Enable control transfer complete */ +#define PID 0x0003 /* b1-0: Response PID */ +#define PID_STALL11 0x0003 /* STALL */ +#define PID_STALL 0x0002 /* STALL */ +#define PID_BUF 0x0001 /* BUF */ +#define PID_NAK 0x0000 /* NAK */ + +/* Pipe Window Select Register */ +#define PIPENM 0x0007 /* b2-0: Pipe select */ + +/* Pipe Configuration Register */ +#define R8A66597_TYP 0xC000 /* b15-14: Transfer type */ +#define R8A66597_ISO 0xC000 /* Isochronous */ +#define R8A66597_INT 0x8000 /* Interrupt */ +#define R8A66597_BULK 0x4000 /* Bulk */ +#define R8A66597_BFRE 0x0400 /* b10: Buffer ready interrupt mode select */ +#define R8A66597_DBLB 0x0200 /* b9: Double buffer mode select */ +#define R8A66597_CNTMD 0x0100 /* b8: Continuous transfer mode select */ +#define R8A66597_SHTNAK 0x0080 /* b7: Transfer end NAK */ +#define R8A66597_DIR 0x0010 /* b4: Transfer direction select */ +#define R8A66597_EPNUM 0x000F /* b3-0: Eendpoint number select */ + +/* Pipe Buffer Configuration Register */ +#define BUFSIZE 0x7C00 /* b14-10: Pipe buffer size */ +#define BUFNMB 0x007F /* b6-0: Pipe buffer number */ +#define PIPE0BUF 256 +#define PIPExBUF 64 + +/* Pipe Maxpacket Size Register */ +#define MXPS 0x07FF /* b10-0: Maxpacket size */ + +/* Pipe Cycle Configuration Register */ +#define IFIS 0x1000 /* b12: Isochronous in-buffer flush mode select */ +#define IITV 0x0007 /* b2-0: Isochronous interval */ + +/* Pipex Control Register */ +#define BSTS 0x8000 /* b15: Buffer status */ +#define INBUFM 0x4000 /* b14: IN buffer monitor (Only for PIPE1 to 5) */ +#define CSCLR 0x2000 /* b13: complete-split status clear */ +#define CSSTS 0x1000 /* b12: complete-split status */ +#define ATREPM 0x0400 /* b10: Auto repeat mode */ +#define ACLRM 0x0200 /* b9: Out buffer auto clear mode */ +#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */ +#define SQSET 0x0080 /* b7: Sequence toggle bit set */ +#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */ +#define PBUSY 0x0020 /* b5: pipe busy */ +#define PID 0x0003 /* b1-0: Response PID */ + +/* PIPExTRE */ +#define TRENB 0x0200 /* b9: Transaction counter enable */ +#define TRCLR 0x0100 /* b8: Transaction counter clear */ + +/* PIPExTRN */ +#define TRNCNT 0xFFFF /* b15-0: Transaction counter */ + +/* DEVADDx */ +#define UPPHUB 0x7800 +#define HUBPORT 0x0700 +#define USBSPD 0x00C0 +#define RTPORT 0x0001 + +#endif /* __LINUX_USB_R8A66597_H */ -- cgit v1.2.3-71-gd317 From 2c59b0b70b9d5d61c726f179724660c4c2423f31 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 22 Jul 2009 14:41:35 +0000 Subject: usb: m66592-udc platform data on_chip support Convert the m66592-udc driver to use the on_chip flag from platform data to enable on chip behaviour instead of relying on CONFIG_SUPERH_BUILT_IN_M66592 ugliness. This makes the code cleaner and also allows us to support both external and internal m66592 with the same kernel. It also makes the Kconfig part more future proof since we with this patch can add support for new processors with on-chip m66592 without modifying the Kconfig. The patch adds a m66592 header file for platform data and ties in platform data to the existing m66592 devices. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- arch/sh/boards/mach-highlander/setup.c | 7 + arch/sh/boards/mach-x3proto/setup.c | 7 + arch/sh/kernel/cpu/sh4a/setup-sh7722.c | 8 +- drivers/usb/gadget/Kconfig | 10 -- drivers/usb/gadget/m66592-udc.c | 252 +++++++++++++++++++-------------- drivers/usb/gadget/m66592-udc.h | 89 ++++++------ include/linux/usb/m66592.h | 44 ++++++ 7 files changed, 257 insertions(+), 160 deletions(-) create mode 100644 include/linux/usb/m66592.h (limited to 'include/linux/usb') diff --git a/arch/sh/boards/mach-highlander/setup.c b/arch/sh/boards/mach-highlander/setup.c index 1639f8915000..566e69d8d729 100644 --- a/arch/sh/boards/mach-highlander/setup.c +++ b/arch/sh/boards/mach-highlander/setup.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,11 @@ static struct platform_device r8a66597_usb_host_device = { .resource = r8a66597_usb_host_resources, }; +static struct m66592_platdata usbf_platdata = { + .xtal = M66592_PLATDATA_XTAL_24MHZ, + .vif = 1, +}; + static struct resource m66592_usb_peripheral_resources[] = { [0] = { .name = "m66592_udc", @@ -81,6 +87,7 @@ static struct platform_device m66592_usb_peripheral_device = { .dev = { .dma_mask = NULL, /* don't use dma */ .coherent_dma_mask = 0xffffffff, + .platform_data = &usbf_platdata, }, .num_resources = ARRAY_SIZE(m66592_usb_peripheral_resources), .resource = m66592_usb_peripheral_resources, diff --git a/arch/sh/boards/mach-x3proto/setup.c b/arch/sh/boards/mach-x3proto/setup.c index 8913ae39a802..efe4cb9f8a77 100644 --- a/arch/sh/boards/mach-x3proto/setup.c +++ b/arch/sh/boards/mach-x3proto/setup.c @@ -17,6 +17,7 @@ #include #include #include +#include #include static struct resource heartbeat_resources[] = { @@ -89,6 +90,11 @@ static struct platform_device r8a66597_usb_host_device = { .resource = r8a66597_usb_host_resources, }; +static struct m66592_platdata usbf_platdata = { + .xtal = M66592_PLATDATA_XTAL_24MHZ, + .vif = 1, +}; + static struct resource m66592_usb_peripheral_resources[] = { [0] = { .name = "m66592_udc", @@ -109,6 +115,7 @@ static struct platform_device m66592_usb_peripheral_device = { .dev = { .dma_mask = NULL, /* don't use dma */ .coherent_dma_mask = 0xffffffff, + .platform_data = &usbf_platdata, }, .num_resources = ARRAY_SIZE(m66592_usb_peripheral_resources), .resource = m66592_usb_peripheral_resources, diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c index ea524a2da3e4..0bad14a44238 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -47,9 +48,13 @@ static struct platform_device rtc_device = { .resource = rtc_resources, }; +static struct m66592_platdata usbf_platdata = { + .on_chip = 1, +}; + static struct resource usbf_resources[] = { [0] = { - .name = "m66592_udc", + .name = "USBF", .start = 0x04480000, .end = 0x044800FF, .flags = IORESOURCE_MEM, @@ -67,6 +72,7 @@ static struct platform_device usbf_device = { .dev = { .dma_mask = NULL, .coherent_dma_mask = 0xffffffff, + .platform_data = &usbf_platdata, }, .num_resources = ARRAY_SIZE(usbf_resources), .resource = usbf_resources, diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 7f8e83a954ac..b7f10bc25c2c 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -360,16 +360,6 @@ config USB_M66592 default USB_GADGET select USB_GADGET_SELECTED -config SUPERH_BUILT_IN_M66592 - boolean "Enable SuperH built-in USB like the M66592" - depends on USB_GADGET_M66592 && CPU_SUBTYPE_SH7722 - help - SH7722 has USB like the M66592. - - The transfer rate is very slow when use "Ethernet Gadget". - However, this problem is improved if change a value of - NET_IP_ALIGN to 4. - # # Controllers available only in discrete form (and all PCI controllers) # diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index 0dddd2f8ff35..a61c70caff12 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -31,38 +31,12 @@ #include "m66592-udc.h" - MODULE_DESCRIPTION("M66592 USB gadget driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Yoshihiro Shimoda"); MODULE_ALIAS("platform:m66592_udc"); -#define DRIVER_VERSION "26 Jun 2009" - -/* module parameters */ -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) -static unsigned short endian = M66592_LITTLE; -module_param(endian, ushort, 0644); -MODULE_PARM_DESC(endian, "data endian: big=0, little=0 (default=0)"); -#else -static unsigned short clock = M66592_XTAL24; -module_param(clock, ushort, 0644); -MODULE_PARM_DESC(clock, "input clock: 48MHz=32768, 24MHz=16384, 12MHz=0 " - "(default=16384)"); - -static unsigned short vif = M66592_LDRV; -module_param(vif, ushort, 0644); -MODULE_PARM_DESC(vif, "input VIF: 3.3V=32768, 1.5V=0 (default=32768)"); - -static unsigned short endian; -module_param(endian, ushort, 0644); -MODULE_PARM_DESC(endian, "data endian: big=256, little=0 (default=0)"); - -static unsigned short irq_sense = M66592_INTL; -module_param(irq_sense, ushort, 0644); -MODULE_PARM_DESC(irq_sense, "IRQ sense: low level=2, falling edge=0 " - "(default=2)"); -#endif +#define DRIVER_VERSION "21 July 2009" static const char udc_name[] = "m66592_udc"; static const char *m66592_ep_name[] = { @@ -244,6 +218,7 @@ static inline int get_buffer_size(struct m66592 *m66592, u16 pipenum) static inline void pipe_change(struct m66592 *m66592, u16 pipenum) { struct m66592_ep *ep = m66592->pipenum2ep[pipenum]; + unsigned short mbw; if (ep->use_dma) return; @@ -252,7 +227,12 @@ static inline void pipe_change(struct m66592 *m66592, u16 pipenum) ndelay(450); - m66592_bset(m66592, M66592_MBW, ep->fifosel); + if (m66592->pdata->on_chip) + mbw = M66592_MBW_32; + else + mbw = M66592_MBW_16; + + m66592_bset(m66592, mbw, ep->fifosel); } static int pipe_buffer_setting(struct m66592 *m66592, @@ -332,6 +312,7 @@ static void pipe_buffer_release(struct m66592 *m66592, static void pipe_initialize(struct m66592_ep *ep) { struct m66592 *m66592 = ep->m66592; + unsigned short mbw; m66592_mdfy(m66592, 0, M66592_CURPIPE, ep->fifosel); @@ -343,7 +324,12 @@ static void pipe_initialize(struct m66592_ep *ep) ndelay(450); - m66592_bset(m66592, M66592_MBW, ep->fifosel); + if (m66592->pdata->on_chip) + mbw = M66592_MBW_32; + else + mbw = M66592_MBW_16; + + m66592_bset(m66592, mbw, ep->fifosel); } } @@ -359,15 +345,13 @@ static void m66592_ep_setting(struct m66592 *m66592, struct m66592_ep *ep, ep->fifosel = M66592_D0FIFOSEL; ep->fifoctr = M66592_D0FIFOCTR; ep->fifotrn = M66592_D0FIFOTRN; -#if !defined(CONFIG_SUPERH_BUILT_IN_M66592) - } else if (m66592->num_dma == 1) { + } else if (!m66592->pdata->on_chip && m66592->num_dma == 1) { m66592->num_dma++; ep->use_dma = 1; ep->fifoaddr = M66592_D1FIFO; ep->fifosel = M66592_D1FIFOSEL; ep->fifoctr = M66592_D1FIFOCTR; ep->fifotrn = M66592_D1FIFOTRN; -#endif } else { ep->use_dma = 0; ep->fifoaddr = M66592_CFIFO; @@ -612,76 +596,120 @@ static void start_ep0(struct m66592_ep *ep, struct m66592_request *req) } } -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) static void init_controller(struct m66592 *m66592) { - m66592_bset(m66592, M66592_HSE, M66592_SYSCFG); /* High spd */ - m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG); - m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG); - m66592_bset(m66592, M66592_USBE, M66592_SYSCFG); + unsigned int endian; - /* This is a workaound for SH7722 2nd cut */ - m66592_bset(m66592, 0x8000, M66592_DVSTCTR); - m66592_bset(m66592, 0x1000, M66592_TESTMODE); - m66592_bclr(m66592, 0x8000, M66592_DVSTCTR); + if (m66592->pdata->on_chip) { + if (m66592->pdata->endian) + endian = 0; /* big endian */ + else + endian = M66592_LITTLE; /* little endian */ - m66592_bset(m66592, M66592_INTL, M66592_INTENB1); + m66592_bset(m66592, M66592_HSE, M66592_SYSCFG); /* High spd */ + m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG); + m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG); + m66592_bset(m66592, M66592_USBE, M66592_SYSCFG); - m66592_write(m66592, 0, M66592_CFBCFG); - m66592_write(m66592, 0, M66592_D0FBCFG); - m66592_bset(m66592, endian, M66592_CFBCFG); - m66592_bset(m66592, endian, M66592_D0FBCFG); -} -#else /* #if defined(CONFIG_SUPERH_BUILT_IN_M66592) */ -static void init_controller(struct m66592 *m66592) -{ - m66592_bset(m66592, (vif & M66592_LDRV) | (endian & M66592_BIGEND), - M66592_PINCFG); - m66592_bset(m66592, M66592_HSE, M66592_SYSCFG); /* High spd */ - m66592_mdfy(m66592, clock & M66592_XTAL, M66592_XTAL, M66592_SYSCFG); + /* This is a workaound for SH7722 2nd cut */ + m66592_bset(m66592, 0x8000, M66592_DVSTCTR); + m66592_bset(m66592, 0x1000, M66592_TESTMODE); + m66592_bclr(m66592, 0x8000, M66592_DVSTCTR); - m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG); - m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG); - m66592_bset(m66592, M66592_USBE, M66592_SYSCFG); + m66592_bset(m66592, M66592_INTL, M66592_INTENB1); + + m66592_write(m66592, 0, M66592_CFBCFG); + m66592_write(m66592, 0, M66592_D0FBCFG); + m66592_bset(m66592, endian, M66592_CFBCFG); + m66592_bset(m66592, endian, M66592_D0FBCFG); + } else { + unsigned int clock, vif, irq_sense; + + if (m66592->pdata->endian) + endian = M66592_BIGEND; /* big endian */ + else + endian = 0; /* little endian */ + + if (m66592->pdata->vif) + vif = M66592_LDRV; /* 3.3v */ + else + vif = 0; /* 1.5v */ + + switch (m66592->pdata->xtal) { + case M66592_PLATDATA_XTAL_12MHZ: + clock = M66592_XTAL12; + break; + case M66592_PLATDATA_XTAL_24MHZ: + clock = M66592_XTAL24; + break; + case M66592_PLATDATA_XTAL_48MHZ: + clock = M66592_XTAL48; + break; + default: + pr_warning("m66592-udc: xtal configuration error\n"); + clock = 0; + } - m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG); + switch (m66592->irq_trigger) { + case IRQF_TRIGGER_LOW: + irq_sense = M66592_INTL; + break; + case IRQF_TRIGGER_FALLING: + irq_sense = 0; + break; + default: + pr_warning("m66592-udc: irq trigger config error\n"); + irq_sense = 0; + } - msleep(3); + m66592_bset(m66592, + (vif & M66592_LDRV) | (endian & M66592_BIGEND), + M66592_PINCFG); + m66592_bset(m66592, M66592_HSE, M66592_SYSCFG); /* High spd */ + m66592_mdfy(m66592, clock & M66592_XTAL, M66592_XTAL, + M66592_SYSCFG); + m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG); + m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG); + m66592_bset(m66592, M66592_USBE, M66592_SYSCFG); - m66592_bset(m66592, M66592_RCKE | M66592_PLLC, M66592_SYSCFG); + m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG); + + msleep(3); - msleep(1); + m66592_bset(m66592, M66592_RCKE | M66592_PLLC, M66592_SYSCFG); - m66592_bset(m66592, M66592_SCKE, M66592_SYSCFG); + msleep(1); - m66592_bset(m66592, irq_sense & M66592_INTL, M66592_INTENB1); - m66592_write(m66592, M66592_BURST | M66592_CPU_ADR_RD_WR, - M66592_DMA0CFG); + m66592_bset(m66592, M66592_SCKE, M66592_SYSCFG); + + m66592_bset(m66592, irq_sense & M66592_INTL, M66592_INTENB1); + m66592_write(m66592, M66592_BURST | M66592_CPU_ADR_RD_WR, + M66592_DMA0CFG); + } } -#endif /* #if defined(CONFIG_SUPERH_BUILT_IN_M66592) */ static void disable_controller(struct m66592 *m66592) { -#if !defined(CONFIG_SUPERH_BUILT_IN_M66592) - m66592_bclr(m66592, M66592_SCKE, M66592_SYSCFG); - udelay(1); - m66592_bclr(m66592, M66592_PLLC, M66592_SYSCFG); - udelay(1); - m66592_bclr(m66592, M66592_RCKE, M66592_SYSCFG); - udelay(1); - m66592_bclr(m66592, M66592_XCKE, M66592_SYSCFG); -#endif + if (!m66592->pdata->on_chip) { + m66592_bclr(m66592, M66592_SCKE, M66592_SYSCFG); + udelay(1); + m66592_bclr(m66592, M66592_PLLC, M66592_SYSCFG); + udelay(1); + m66592_bclr(m66592, M66592_RCKE, M66592_SYSCFG); + udelay(1); + m66592_bclr(m66592, M66592_XCKE, M66592_SYSCFG); + } } static void m66592_start_xclock(struct m66592 *m66592) { -#if !defined(CONFIG_SUPERH_BUILT_IN_M66592) u16 tmp; - tmp = m66592_read(m66592, M66592_SYSCFG); - if (!(tmp & M66592_XCKE)) - m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG); -#endif + if (!m66592->pdata->on_chip) { + tmp = m66592_read(m66592, M66592_SYSCFG); + if (!(tmp & M66592_XCKE)) + m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG); + } } /*-------------------------------------------------------------------------*/ @@ -1169,8 +1197,7 @@ static irqreturn_t m66592_irq(int irq, void *_m66592) intsts0 = m66592_read(m66592, M66592_INTSTS0); intenb0 = m66592_read(m66592, M66592_INTENB0); -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) - if (!intsts0 && !intenb0) { + if (m66592->pdata->on_chip && !intsts0 && !intenb0) { /* * When USB clock stops, it cannot read register. Even if a * clock stops, the interrupt occurs. So this driver turn on @@ -1180,7 +1207,6 @@ static irqreturn_t m66592_irq(int irq, void *_m66592) intsts0 = m66592_read(m66592, M66592_INTSTS0); intenb0 = m66592_read(m66592, M66592_INTENB0); } -#endif savepipe = m66592_read(m66592, M66592_CFIFOSEL); @@ -1526,9 +1552,11 @@ static int __exit m66592_remove(struct platform_device *pdev) iounmap(m66592->reg); free_irq(platform_get_irq(pdev, 0), m66592); m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req); -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK) - clk_disable(m66592->clk); - clk_put(m66592->clk); +#ifdef CONFIG_HAVE_CLK + if (m66592->pdata->on_chip) { + clk_disable(m66592->clk); + clk_put(m66592->clk); + } #endif kfree(m66592); return 0; @@ -1540,11 +1568,10 @@ static void nop_completion(struct usb_ep *ep, struct usb_request *r) static int __init m66592_probe(struct platform_device *pdev) { - struct resource *res; - int irq; + struct resource *res, *ires; void __iomem *reg = NULL; struct m66592 *m66592 = NULL; -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK) +#ifdef CONFIG_HAVE_CLK char clk_name[8]; #endif int ret = 0; @@ -1557,10 +1584,11 @@ static int __init m66592_probe(struct platform_device *pdev) goto clean_up; } - irq = platform_get_irq(pdev, 0); - if (irq < 0) { + ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!ires) { ret = -ENODEV; - pr_err("platform_get_irq error.\n"); + dev_err(&pdev->dev, + "platform_get_resource IORESOURCE_IRQ error.\n"); goto clean_up; } @@ -1571,6 +1599,12 @@ static int __init m66592_probe(struct platform_device *pdev) goto clean_up; } + if (pdev->dev.platform_data == NULL) { + dev_err(&pdev->dev, "no platform data\n"); + ret = -ENODEV; + goto clean_up; + } + /* initialize ucd */ m66592 = kzalloc(sizeof(struct m66592), GFP_KERNEL); if (m66592 == NULL) { @@ -1578,6 +1612,9 @@ static int __init m66592_probe(struct platform_device *pdev) goto clean_up; } + m66592->pdata = pdev->dev.platform_data; + m66592->irq_trigger = ires->flags & IRQF_TRIGGER_MASK; + spin_lock_init(&m66592->lock); dev_set_drvdata(&pdev->dev, m66592); @@ -1595,22 +1632,25 @@ static int __init m66592_probe(struct platform_device *pdev) m66592->timer.data = (unsigned long)m66592; m66592->reg = reg; - ret = request_irq(irq, m66592_irq, IRQF_DISABLED | IRQF_SHARED, + ret = request_irq(ires->start, m66592_irq, IRQF_DISABLED | IRQF_SHARED, udc_name, m66592); if (ret < 0) { pr_err("request_irq error (%d)\n", ret); goto clean_up; } -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK) - snprintf(clk_name, sizeof(clk_name), "usbf%d", pdev->id); - m66592->clk = clk_get(&pdev->dev, clk_name); - if (IS_ERR(m66592->clk)) { - dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); - ret = PTR_ERR(m66592->clk); - goto clean_up2; +#ifdef CONFIG_HAVE_CLK + if (m66592->pdata->on_chip) { + snprintf(clk_name, sizeof(clk_name), "usbf%d", pdev->id); + m66592->clk = clk_get(&pdev->dev, clk_name); + if (IS_ERR(m66592->clk)) { + dev_err(&pdev->dev, "cannot get clock \"%s\"\n", + clk_name); + ret = PTR_ERR(m66592->clk); + goto clean_up2; + } + clk_enable(m66592->clk); } - clk_enable(m66592->clk); #endif INIT_LIST_HEAD(&m66592->gadget.ep_list); m66592->gadget.ep0 = &m66592->ep[0].ep; @@ -1652,12 +1692,14 @@ static int __init m66592_probe(struct platform_device *pdev) return 0; clean_up3: -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK) - clk_disable(m66592->clk); - clk_put(m66592->clk); +#ifdef CONFIG_HAVE_CLK + if (m66592->pdata->on_chip) { + clk_disable(m66592->clk); + clk_put(m66592->clk); + } clean_up2: #endif - free_irq(irq, m66592); + free_irq(ires->start, m66592); clean_up: if (m66592) { if (m66592->ep0_req) diff --git a/drivers/usb/gadget/m66592-udc.h b/drivers/usb/gadget/m66592-udc.h index 9a9c2bf9fbd5..8b960deed680 100644 --- a/drivers/usb/gadget/m66592-udc.h +++ b/drivers/usb/gadget/m66592-udc.h @@ -23,10 +23,12 @@ #ifndef __M66592_UDC_H__ #define __M66592_UDC_H__ -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK) +#ifdef CONFIG_HAVE_CLK #include #endif +#include + #define M66592_SYSCFG 0x00 #define M66592_XTAL 0xC000 /* b15-14: Crystal selection */ #define M66592_XTAL48 0x8000 /* 48MHz */ @@ -76,11 +78,11 @@ #define M66592_P_TST_J 0x0001 /* PERI TEST J */ #define M66592_P_TST_NORMAL 0x0000 /* PERI Normal Mode */ -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) +/* built-in registers */ #define M66592_CFBCFG 0x0A #define M66592_D0FBCFG 0x0C #define M66592_LITTLE 0x0100 /* b8: Little endian mode */ -#else +/* external chip case */ #define M66592_PINCFG 0x0A #define M66592_LDRV 0x8000 /* b15: Drive Current Adjust */ #define M66592_BIGEND 0x0100 /* b8: Big endian mode */ @@ -100,8 +102,8 @@ #define M66592_PKTM 0x0020 /* b5: Packet mode */ #define M66592_DENDE 0x0010 /* b4: Dend enable */ #define M66592_OBUS 0x0004 /* b2: OUTbus mode */ -#endif /* #if defined(CONFIG_SUPERH_BUILT_IN_M66592) */ +/* common case */ #define M66592_CFIFO 0x10 #define M66592_D0FIFO 0x14 #define M66592_D1FIFO 0x18 @@ -113,13 +115,9 @@ #define M66592_REW 0x4000 /* b14: Buffer rewind */ #define M66592_DCLRM 0x2000 /* b13: DMA buffer clear mode */ #define M66592_DREQE 0x1000 /* b12: DREQ output enable */ -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) -#define M66592_MBW 0x0800 /* b11: Maximum bit width for FIFO */ -#else -#define M66592_MBW 0x0400 /* b10: Maximum bit width for FIFO */ -#define M66592_MBW_8 0x0000 /* 8bit */ -#define M66592_MBW_16 0x0400 /* 16bit */ -#endif /* #if defined(CONFIG_SUPERH_BUILT_IN_M66592) */ +#define M66592_MBW_8 0x0000 /* 8bit */ +#define M66592_MBW_16 0x0400 /* 16bit */ +#define M66592_MBW_32 0x0800 /* 32bit */ #define M66592_TRENB 0x0200 /* b9: Transaction counter enable */ #define M66592_TRCLR 0x0100 /* b8: Transaction counter clear */ #define M66592_DEZPM 0x0080 /* b7: Zero-length packet mode */ @@ -480,9 +478,11 @@ struct m66592_ep { struct m66592 { spinlock_t lock; void __iomem *reg; -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK) +#ifdef CONFIG_HAVE_CLK struct clk *clk; #endif + struct m66592_platdata *pdata; + unsigned long irq_trigger; struct usb_gadget gadget; struct usb_gadget_driver *driver; @@ -546,13 +546,13 @@ static inline void m66592_read_fifo(struct m66592 *m66592, { unsigned long fifoaddr = (unsigned long)m66592->reg + offset; -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) - len = (len + 3) / 4; - insl(fifoaddr, buf, len); -#else - len = (len + 1) / 2; - insw(fifoaddr, buf, len); -#endif + if (m66592->pdata->on_chip) { + len = (len + 3) / 4; + insl(fifoaddr, buf, len); + } else { + len = (len + 1) / 2; + insw(fifoaddr, buf, len); + } } static inline void m66592_write(struct m66592 *m66592, u16 val, @@ -566,33 +566,34 @@ static inline void m66592_write_fifo(struct m66592 *m66592, void *buf, unsigned long len) { unsigned long fifoaddr = (unsigned long)m66592->reg + offset; -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) - unsigned long count; - unsigned char *pb; - int i; - - count = len / 4; - outsl(fifoaddr, buf, count); - - if (len & 0x00000003) { - pb = buf + count * 4; - for (i = 0; i < (len & 0x00000003); i++) { - if (m66592_read(m66592, M66592_CFBCFG)) /* little */ - outb(pb[i], fifoaddr + (3 - i)); - else - outb(pb[i], fifoaddr + i); + + if (m66592->pdata->on_chip) { + unsigned long count; + unsigned char *pb; + int i; + + count = len / 4; + outsl(fifoaddr, buf, count); + + if (len & 0x00000003) { + pb = buf + count * 4; + for (i = 0; i < (len & 0x00000003); i++) { + if (m66592_read(m66592, M66592_CFBCFG)) /* le */ + outb(pb[i], fifoaddr + (3 - i)); + else + outb(pb[i], fifoaddr + i); + } + } + } else { + unsigned long odd = len & 0x0001; + + len = len / 2; + outsw(fifoaddr, buf, len); + if (odd) { + unsigned char *p = buf + len*2; + outb(*p, fifoaddr); } } -#else - unsigned long odd = len & 0x0001; - - len = len / 2; - outsw(fifoaddr, buf, len); - if (odd) { - unsigned char *p = buf + len*2; - outb(*p, fifoaddr); - } -#endif /* #if defined(CONFIG_SUPERH_BUILT_IN_M66592) */ } static inline void m66592_mdfy(struct m66592 *m66592, u16 val, u16 pat, diff --git a/include/linux/usb/m66592.h b/include/linux/usb/m66592.h new file mode 100644 index 000000000000..cda9625e7df0 --- /dev/null +++ b/include/linux/usb/m66592.h @@ -0,0 +1,44 @@ +/* + * M66592 driver platform data + * + * Copyright (C) 2009 Renesas Solutions Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __LINUX_USB_M66592_H +#define __LINUX_USB_M66592_H + +#define M66592_PLATDATA_XTAL_12MHZ 0x01 +#define M66592_PLATDATA_XTAL_24MHZ 0x02 +#define M66592_PLATDATA_XTAL_48MHZ 0x03 + +struct m66592_platdata { + /* one = on chip controller, zero = external controller */ + unsigned on_chip:1; + + /* one = big endian, zero = little endian */ + unsigned endian:1; + + /* (external controller only) M66592_PLATDATA_XTAL_nnMHZ */ + unsigned xtal:2; + + /* (external controller only) one = 3.3V, zero = 1.5V */ + unsigned vif:1; + +}; + +#endif /* __LINUX_USB_M66592_H */ + -- cgit v1.2.3-71-gd317 From de05f63430e8cb8cde0a21260bc6b01765111e5c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 26 Jun 2009 12:15:38 -0300 Subject: V4L/DVB (12187): uvcvideo: Move UVC definitions to linux/usb/video.h To make UVC constants accessible by a future UVC gadget driver, move them from drivers/media/video/uvc/uvcvideo.h to include/linux/usb/video.h. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/uvc/uvcvideo.h | 136 +----------------------------- include/linux/usb/video.h | 164 +++++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+), 135 deletions(-) create mode 100644 include/linux/usb/video.h (limited to 'include/linux/usb') diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index cc5023e09ca9..5cf68f590725 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -67,144 +67,12 @@ struct uvc_xu_control { #ifdef __KERNEL__ #include +#include /* -------------------------------------------------------------------------- * UVC constants */ -#define UVC_SC_UNDEFINED 0x00 -#define UVC_SC_VIDEOCONTROL 0x01 -#define UVC_SC_VIDEOSTREAMING 0x02 -#define UVC_SC_VIDEO_INTERFACE_COLLECTION 0x03 - -#define UVC_PC_PROTOCOL_UNDEFINED 0x00 - -/* VideoControl class specific interface descriptor */ -#define UVC_VC_DESCRIPTOR_UNDEFINED 0x00 -#define UVC_VC_HEADER 0x01 -#define UVC_VC_INPUT_TERMINAL 0x02 -#define UVC_VC_OUTPUT_TERMINAL 0x03 -#define UVC_VC_SELECTOR_UNIT 0x04 -#define UVC_VC_PROCESSING_UNIT 0x05 -#define UVC_VC_EXTENSION_UNIT 0x06 - -/* VideoStreaming class specific interface descriptor */ -#define UVC_VS_UNDEFINED 0x00 -#define UVC_VS_INPUT_HEADER 0x01 -#define UVC_VS_OUTPUT_HEADER 0x02 -#define UVC_VS_STILL_IMAGE_FRAME 0x03 -#define UVC_VS_FORMAT_UNCOMPRESSED 0x04 -#define UVC_VS_FRAME_UNCOMPRESSED 0x05 -#define UVC_VS_FORMAT_MJPEG 0x06 -#define UVC_VS_FRAME_MJPEG 0x07 -#define UVC_VS_FORMAT_MPEG2TS 0x0a -#define UVC_VS_FORMAT_DV 0x0c -#define UVC_VS_COLORFORMAT 0x0d -#define UVC_VS_FORMAT_FRAME_BASED 0x10 -#define UVC_VS_FRAME_FRAME_BASED 0x11 -#define UVC_VS_FORMAT_STREAM_BASED 0x12 - -/* Endpoint type */ -#define UVC_EP_UNDEFINED 0x00 -#define UVC_EP_GENERAL 0x01 -#define UVC_EP_ENDPOINT 0x02 -#define UVC_EP_INTERRUPT 0x03 - -/* Request codes */ -#define UVC_RC_UNDEFINED 0x00 -#define UVC_SET_CUR 0x01 -#define UVC_GET_CUR 0x81 -#define UVC_GET_MIN 0x82 -#define UVC_GET_MAX 0x83 -#define UVC_GET_RES 0x84 -#define UVC_GET_LEN 0x85 -#define UVC_GET_INFO 0x86 -#define UVC_GET_DEF 0x87 - -/* VideoControl interface controls */ -#define UVC_VC_CONTROL_UNDEFINED 0x00 -#define UVC_VC_VIDEO_POWER_MODE_CONTROL 0x01 -#define UVC_VC_REQUEST_ERROR_CODE_CONTROL 0x02 - -/* Terminal controls */ -#define UVC_TE_CONTROL_UNDEFINED 0x00 - -/* Selector Unit controls */ -#define UVC_SU_CONTROL_UNDEFINED 0x00 -#define UVC_SU_INPUT_SELECT_CONTROL 0x01 - -/* Camera Terminal controls */ -#define UVC_CT_CONTROL_UNDEFINED 0x00 -#define UVC_CT_SCANNING_MODE_CONTROL 0x01 -#define UVC_CT_AE_MODE_CONTROL 0x02 -#define UVC_CT_AE_PRIORITY_CONTROL 0x03 -#define UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x04 -#define UVC_CT_EXPOSURE_TIME_RELATIVE_CONTROL 0x05 -#define UVC_CT_FOCUS_ABSOLUTE_CONTROL 0x06 -#define UVC_CT_FOCUS_RELATIVE_CONTROL 0x07 -#define UVC_CT_FOCUS_AUTO_CONTROL 0x08 -#define UVC_CT_IRIS_ABSOLUTE_CONTROL 0x09 -#define UVC_CT_IRIS_RELATIVE_CONTROL 0x0a -#define UVC_CT_ZOOM_ABSOLUTE_CONTROL 0x0b -#define UVC_CT_ZOOM_RELATIVE_CONTROL 0x0c -#define UVC_CT_PANTILT_ABSOLUTE_CONTROL 0x0d -#define UVC_CT_PANTILT_RELATIVE_CONTROL 0x0e -#define UVC_CT_ROLL_ABSOLUTE_CONTROL 0x0f -#define UVC_CT_ROLL_RELATIVE_CONTROL 0x10 -#define UVC_CT_PRIVACY_CONTROL 0x11 - -/* Processing Unit controls */ -#define UVC_PU_CONTROL_UNDEFINED 0x00 -#define UVC_PU_BACKLIGHT_COMPENSATION_CONTROL 0x01 -#define UVC_PU_BRIGHTNESS_CONTROL 0x02 -#define UVC_PU_CONTRAST_CONTROL 0x03 -#define UVC_PU_GAIN_CONTROL 0x04 -#define UVC_PU_POWER_LINE_FREQUENCY_CONTROL 0x05 -#define UVC_PU_HUE_CONTROL 0x06 -#define UVC_PU_SATURATION_CONTROL 0x07 -#define UVC_PU_SHARPNESS_CONTROL 0x08 -#define UVC_PU_GAMMA_CONTROL 0x09 -#define UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x0a -#define UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x0b -#define UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL 0x0c -#define UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x0d -#define UVC_PU_DIGITAL_MULTIPLIER_CONTROL 0x0e -#define UVC_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x0f -#define UVC_PU_HUE_AUTO_CONTROL 0x10 -#define UVC_PU_ANALOG_VIDEO_STANDARD_CONTROL 0x11 -#define UVC_PU_ANALOG_LOCK_STATUS_CONTROL 0x12 - -/* VideoStreaming interface controls */ -#define UVC_VS_CONTROL_UNDEFINED 0x00 -#define UVC_VS_PROBE_CONTROL 0x01 -#define UVC_VS_COMMIT_CONTROL 0x02 -#define UVC_VS_STILL_PROBE_CONTROL 0x03 -#define UVC_VS_STILL_COMMIT_CONTROL 0x04 -#define UVC_VS_STILL_IMAGE_TRIGGER_CONTROL 0x05 -#define UVC_VS_STREAM_ERROR_CODE_CONTROL 0x06 -#define UVC_VS_GENERATE_KEY_FRAME_CONTROL 0x07 -#define UVC_VS_UPDATE_FRAME_SEGMENT_CONTROL 0x08 -#define UVC_VS_SYNC_DELAY_CONTROL 0x09 - -#define UVC_TT_VENDOR_SPECIFIC 0x0100 -#define UVC_TT_STREAMING 0x0101 - -/* Input Terminal types */ -#define UVC_ITT_VENDOR_SPECIFIC 0x0200 -#define UVC_ITT_CAMERA 0x0201 -#define UVC_ITT_MEDIA_TRANSPORT_INPUT 0x0202 - -/* Output Terminal types */ -#define UVC_OTT_VENDOR_SPECIFIC 0x0300 -#define UVC_OTT_DISPLAY 0x0301 -#define UVC_OTT_MEDIA_TRANSPORT_OUTPUT 0x0302 - -/* External Terminal types */ -#define UVC_EXTERNAL_VENDOR_SPECIFIC 0x0400 -#define UVC_COMPOSITE_CONNECTOR 0x0401 -#define UVC_SVIDEO_CONNECTOR 0x0402 -#define UVC_COMPONENT_CONNECTOR 0x0403 - #define UVC_TERM_INPUT 0x0000 #define UVC_TERM_OUTPUT 0x8000 @@ -216,8 +84,6 @@ struct uvc_xu_control { #define UVC_ENTITY_IS_OTERM(entity) \ (((entity)->type & 0x8000) == UVC_TERM_OUTPUT) -#define UVC_STATUS_TYPE_CONTROL 1 -#define UVC_STATUS_TYPE_STREAMING 2 /* ------------------------------------------------------------------------ * GUIDs diff --git a/include/linux/usb/video.h b/include/linux/usb/video.h new file mode 100644 index 000000000000..be436d9ee479 --- /dev/null +++ b/include/linux/usb/video.h @@ -0,0 +1,164 @@ +/* + * USB Video Class definitions. + * + * Copyright (C) 2009 Laurent Pinchart + * + * This file holds USB constants and structures defined by the USB Device + * Class Definition for Video Devices. Unless otherwise stated, comments + * below reference relevant sections of the USB Video Class 1.1 specification + * available at + * + * http://www.usb.org/developers/devclass_docs/USB_Video_Class_1_1.zip + */ + +#ifndef __LINUX_USB_VIDEO_H +#define __LINUX_USB_VIDEO_H + +#include + +/* -------------------------------------------------------------------------- + * UVC constants + */ + +/* A.2. Video Interface Subclass Codes */ +#define UVC_SC_UNDEFINED 0x00 +#define UVC_SC_VIDEOCONTROL 0x01 +#define UVC_SC_VIDEOSTREAMING 0x02 +#define UVC_SC_VIDEO_INTERFACE_COLLECTION 0x03 + +/* A.3. Video Interface Protocol Codes */ +#define UVC_PC_PROTOCOL_UNDEFINED 0x00 + +/* A.5. Video Class-Specific VC Interface Descriptor Subtypes */ +#define UVC_VC_DESCRIPTOR_UNDEFINED 0x00 +#define UVC_VC_HEADER 0x01 +#define UVC_VC_INPUT_TERMINAL 0x02 +#define UVC_VC_OUTPUT_TERMINAL 0x03 +#define UVC_VC_SELECTOR_UNIT 0x04 +#define UVC_VC_PROCESSING_UNIT 0x05 +#define UVC_VC_EXTENSION_UNIT 0x06 + +/* A.6. Video Class-Specific VS Interface Descriptor Subtypes */ +#define UVC_VS_UNDEFINED 0x00 +#define UVC_VS_INPUT_HEADER 0x01 +#define UVC_VS_OUTPUT_HEADER 0x02 +#define UVC_VS_STILL_IMAGE_FRAME 0x03 +#define UVC_VS_FORMAT_UNCOMPRESSED 0x04 +#define UVC_VS_FRAME_UNCOMPRESSED 0x05 +#define UVC_VS_FORMAT_MJPEG 0x06 +#define UVC_VS_FRAME_MJPEG 0x07 +#define UVC_VS_FORMAT_MPEG2TS 0x0a +#define UVC_VS_FORMAT_DV 0x0c +#define UVC_VS_COLORFORMAT 0x0d +#define UVC_VS_FORMAT_FRAME_BASED 0x10 +#define UVC_VS_FRAME_FRAME_BASED 0x11 +#define UVC_VS_FORMAT_STREAM_BASED 0x12 + +/* A.7. Video Class-Specific Endpoint Descriptor Subtypes */ +#define UVC_EP_UNDEFINED 0x00 +#define UVC_EP_GENERAL 0x01 +#define UVC_EP_ENDPOINT 0x02 +#define UVC_EP_INTERRUPT 0x03 + +/* A.8. Video Class-Specific Request Codes */ +#define UVC_RC_UNDEFINED 0x00 +#define UVC_SET_CUR 0x01 +#define UVC_GET_CUR 0x81 +#define UVC_GET_MIN 0x82 +#define UVC_GET_MAX 0x83 +#define UVC_GET_RES 0x84 +#define UVC_GET_LEN 0x85 +#define UVC_GET_INFO 0x86 +#define UVC_GET_DEF 0x87 + +/* A.9.1. VideoControl Interface Control Selectors */ +#define UVC_VC_CONTROL_UNDEFINED 0x00 +#define UVC_VC_VIDEO_POWER_MODE_CONTROL 0x01 +#define UVC_VC_REQUEST_ERROR_CODE_CONTROL 0x02 + +/* A.9.2. Terminal Control Selectors */ +#define UVC_TE_CONTROL_UNDEFINED 0x00 + +/* A.9.3. Selector Unit Control Selectors */ +#define UVC_SU_CONTROL_UNDEFINED 0x00 +#define UVC_SU_INPUT_SELECT_CONTROL 0x01 + +/* A.9.4. Camera Terminal Control Selectors */ +#define UVC_CT_CONTROL_UNDEFINED 0x00 +#define UVC_CT_SCANNING_MODE_CONTROL 0x01 +#define UVC_CT_AE_MODE_CONTROL 0x02 +#define UVC_CT_AE_PRIORITY_CONTROL 0x03 +#define UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x04 +#define UVC_CT_EXPOSURE_TIME_RELATIVE_CONTROL 0x05 +#define UVC_CT_FOCUS_ABSOLUTE_CONTROL 0x06 +#define UVC_CT_FOCUS_RELATIVE_CONTROL 0x07 +#define UVC_CT_FOCUS_AUTO_CONTROL 0x08 +#define UVC_CT_IRIS_ABSOLUTE_CONTROL 0x09 +#define UVC_CT_IRIS_RELATIVE_CONTROL 0x0a +#define UVC_CT_ZOOM_ABSOLUTE_CONTROL 0x0b +#define UVC_CT_ZOOM_RELATIVE_CONTROL 0x0c +#define UVC_CT_PANTILT_ABSOLUTE_CONTROL 0x0d +#define UVC_CT_PANTILT_RELATIVE_CONTROL 0x0e +#define UVC_CT_ROLL_ABSOLUTE_CONTROL 0x0f +#define UVC_CT_ROLL_RELATIVE_CONTROL 0x10 +#define UVC_CT_PRIVACY_CONTROL 0x11 + +/* A.9.5. Processing Unit Control Selectors */ +#define UVC_PU_CONTROL_UNDEFINED 0x00 +#define UVC_PU_BACKLIGHT_COMPENSATION_CONTROL 0x01 +#define UVC_PU_BRIGHTNESS_CONTROL 0x02 +#define UVC_PU_CONTRAST_CONTROL 0x03 +#define UVC_PU_GAIN_CONTROL 0x04 +#define UVC_PU_POWER_LINE_FREQUENCY_CONTROL 0x05 +#define UVC_PU_HUE_CONTROL 0x06 +#define UVC_PU_SATURATION_CONTROL 0x07 +#define UVC_PU_SHARPNESS_CONTROL 0x08 +#define UVC_PU_GAMMA_CONTROL 0x09 +#define UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x0a +#define UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x0b +#define UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL 0x0c +#define UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x0d +#define UVC_PU_DIGITAL_MULTIPLIER_CONTROL 0x0e +#define UVC_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x0f +#define UVC_PU_HUE_AUTO_CONTROL 0x10 +#define UVC_PU_ANALOG_VIDEO_STANDARD_CONTROL 0x11 +#define UVC_PU_ANALOG_LOCK_STATUS_CONTROL 0x12 + +/* A.9.7. VideoStreaming Interface Control Selectors */ +#define UVC_VS_CONTROL_UNDEFINED 0x00 +#define UVC_VS_PROBE_CONTROL 0x01 +#define UVC_VS_COMMIT_CONTROL 0x02 +#define UVC_VS_STILL_PROBE_CONTROL 0x03 +#define UVC_VS_STILL_COMMIT_CONTROL 0x04 +#define UVC_VS_STILL_IMAGE_TRIGGER_CONTROL 0x05 +#define UVC_VS_STREAM_ERROR_CODE_CONTROL 0x06 +#define UVC_VS_GENERATE_KEY_FRAME_CONTROL 0x07 +#define UVC_VS_UPDATE_FRAME_SEGMENT_CONTROL 0x08 +#define UVC_VS_SYNC_DELAY_CONTROL 0x09 + +/* B.1. USB Terminal Types */ +#define UVC_TT_VENDOR_SPECIFIC 0x0100 +#define UVC_TT_STREAMING 0x0101 + +/* B.2. Input Terminal Types */ +#define UVC_ITT_VENDOR_SPECIFIC 0x0200 +#define UVC_ITT_CAMERA 0x0201 +#define UVC_ITT_MEDIA_TRANSPORT_INPUT 0x0202 + +/* B.3. Output Terminal Types */ +#define UVC_OTT_VENDOR_SPECIFIC 0x0300 +#define UVC_OTT_DISPLAY 0x0301 +#define UVC_OTT_MEDIA_TRANSPORT_OUTPUT 0x0302 + +/* B.4. External Terminal Types */ +#define UVC_EXTERNAL_VENDOR_SPECIFIC 0x0400 +#define UVC_COMPOSITE_CONNECTOR 0x0401 +#define UVC_SVIDEO_CONNECTOR 0x0402 +#define UVC_COMPONENT_CONNECTOR 0x0403 + +/* 2.4.2.2. Status Packet Type */ +#define UVC_STATUS_TYPE_CONTROL 1 +#define UVC_STATUS_TYPE_STREAMING 2 + +#endif /* __LINUX_USB_VIDEO_H */ + -- cgit v1.2.3-71-gd317 From a509a7e478e4766114d69f12d19d644ac63e9765 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sat, 19 Sep 2009 13:13:26 -0700 Subject: tty: USB does not need the filp argument in the drivers And indeed none of them use it. Clean this up as it will make moving to a standard open method rather easier. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ark3116.c | 5 ++--- drivers/usb/serial/belkin_sa.c | 4 ++-- drivers/usb/serial/ch341.c | 5 ++--- drivers/usb/serial/console.c | 4 ++-- drivers/usb/serial/cp210x.c | 6 ++---- drivers/usb/serial/cyberjack.c | 4 ++-- drivers/usb/serial/cypress_m8.c | 6 ++---- drivers/usb/serial/digi_acceleport.c | 6 ++---- drivers/usb/serial/empeg.c | 8 +++----- drivers/usb/serial/ftdi_sio.c | 6 ++---- drivers/usb/serial/garmin_gps.c | 3 +-- drivers/usb/serial/generic.c | 3 +-- drivers/usb/serial/io_edgeport.c | 6 ++---- drivers/usb/serial/io_ti.c | 3 +-- drivers/usb/serial/ipaq.c | 9 ++------- drivers/usb/serial/ipw.c | 3 +-- drivers/usb/serial/ir-usb.c | 6 ++---- drivers/usb/serial/iuu_phoenix.c | 5 ++--- drivers/usb/serial/keyspan.c | 3 +-- drivers/usb/serial/keyspan.h | 3 +-- drivers/usb/serial/keyspan_pda.c | 2 +- drivers/usb/serial/kl5kusb105.c | 10 ++-------- drivers/usb/serial/kobil_sct.c | 6 ++---- drivers/usb/serial/mct_u232.c | 6 ++---- drivers/usb/serial/mos7720.c | 3 +-- drivers/usb/serial/mos7840.c | 3 +-- drivers/usb/serial/navman.c | 3 +-- drivers/usb/serial/omninet.c | 6 ++---- drivers/usb/serial/opticon.c | 3 +-- drivers/usb/serial/option.c | 6 ++---- drivers/usb/serial/oti6858.c | 6 ++---- drivers/usb/serial/pl2303.c | 5 +---- drivers/usb/serial/sierra.c | 3 +-- drivers/usb/serial/spcp8x5.c | 5 +---- drivers/usb/serial/symbolserial.c | 3 +-- drivers/usb/serial/ti_usb_3410_5052.c | 6 ++---- drivers/usb/serial/usb-serial.c | 2 +- drivers/usb/serial/usb_debug.c | 5 ++--- drivers/usb/serial/visor.c | 6 ++---- drivers/usb/serial/whiteheat.c | 5 ++--- include/linux/usb/serial.h | 7 +++---- 41 files changed, 68 insertions(+), 131 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index aec61880f36c..7ddde4ddfb4b 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -318,8 +318,7 @@ static void ark3116_set_termios(struct tty_struct *tty, return; } -static int ark3116_open(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp) +static int ark3116_open(struct tty_struct *tty, struct usb_serial_port *port) { struct ktermios tmp_termios; struct usb_serial *serial = port->serial; @@ -334,7 +333,7 @@ static int ark3116_open(struct tty_struct *tty, struct usb_serial_port *port, return -ENOMEM; } - result = usb_serial_generic_open(tty, port, filp); + result = usb_serial_generic_open(tty, port); if (result) goto err_out; diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 7033b031b443..a0467bc61627 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -92,7 +92,7 @@ static int debug; static int belkin_sa_startup(struct usb_serial *serial); static void belkin_sa_release(struct usb_serial *serial); static int belkin_sa_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp); + struct usb_serial_port *port); static void belkin_sa_close(struct usb_serial_port *port); static void belkin_sa_read_int_callback(struct urb *urb); static void belkin_sa_set_termios(struct tty_struct *tty, @@ -213,7 +213,7 @@ static void belkin_sa_release(struct usb_serial *serial) static int belkin_sa_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) + struct usb_serial_port *port) { int retval = 0; diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 2830766f5b39..8c894a7d5dcf 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -300,8 +300,7 @@ static void ch341_close(struct usb_serial_port *port) /* open this device, set default parameters */ -static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp) +static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct ch341_private *priv = usb_get_serial_port_data(serial->port[0]); @@ -333,7 +332,7 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port, return -EPROTO; } - r = usb_serial_generic_open(tty, port, filp); + r = usb_serial_generic_open(tty, port); out: return r; } diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index 0e4f2e41ace5..be086e4c76b6 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -150,9 +150,9 @@ static int usb_console_setup(struct console *co, char *options) /* only call the device specific open if this * is the first time the port is opened */ if (serial->type->open) - retval = serial->type->open(NULL, port, NULL); + retval = serial->type->open(NULL, port); else - retval = usb_serial_generic_open(NULL, port, NULL); + retval = usb_serial_generic_open(NULL, port); if (retval) { err("could not open USB console port"); diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index b5275c4a8eed..4a208fe85bc9 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -33,8 +33,7 @@ /* * Function Prototypes */ -static int cp210x_open(struct tty_struct *, struct usb_serial_port *, - struct file *); +static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *); static void cp210x_cleanup(struct usb_serial_port *); static void cp210x_close(struct usb_serial_port *); static void cp210x_get_termios(struct tty_struct *, @@ -368,8 +367,7 @@ static unsigned int cp210x_quantise_baudrate(unsigned int baud) { return baud; } -static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp) +static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port) { struct usb_serial *serial = port->serial; int result; diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 336523fd7366..b0f6402a91ca 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -61,7 +61,7 @@ static int cyberjack_startup(struct usb_serial *serial); static void cyberjack_disconnect(struct usb_serial *serial); static void cyberjack_release(struct usb_serial *serial); static int cyberjack_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp); + struct usb_serial_port *port); static void cyberjack_close(struct usb_serial_port *port); static int cyberjack_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); @@ -173,7 +173,7 @@ static void cyberjack_release(struct usb_serial *serial) } static int cyberjack_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) + struct usb_serial_port *port) { struct cyberjack_private *priv; unsigned long flags; diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 59adfe123110..df1adb9a4367 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -172,8 +172,7 @@ static int cypress_earthmate_startup(struct usb_serial *serial); static int cypress_hidcom_startup(struct usb_serial *serial); static int cypress_ca42v2_startup(struct usb_serial *serial); static void cypress_release(struct usb_serial *serial); -static int cypress_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp); +static int cypress_open(struct tty_struct *tty, struct usb_serial_port *port); static void cypress_close(struct usb_serial_port *port); static void cypress_dtr_rts(struct usb_serial_port *port, int on); static int cypress_write(struct tty_struct *tty, struct usb_serial_port *port, @@ -633,8 +632,7 @@ static void cypress_release(struct usb_serial *serial) } -static int cypress_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static int cypress_open(struct tty_struct *tty, struct usb_serial_port *port) { struct cypress_private *priv = usb_get_serial_port_data(port); struct usb_serial *serial = port->serial; diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index f4808091c47c..ab3dd991586b 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -453,8 +453,7 @@ static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, static void digi_write_bulk_callback(struct urb *urb); static int digi_write_room(struct tty_struct *tty); static int digi_chars_in_buffer(struct tty_struct *tty); -static int digi_open(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp); +static int digi_open(struct tty_struct *tty, struct usb_serial_port *port); static void digi_close(struct usb_serial_port *port); static int digi_carrier_raised(struct usb_serial_port *port); static void digi_dtr_rts(struct usb_serial_port *port, int on); @@ -1347,8 +1346,7 @@ static int digi_carrier_raised(struct usb_serial_port *port) return 0; } -static int digi_open(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp) +static int digi_open(struct tty_struct *tty, struct usb_serial_port *port) { int ret; unsigned char buf[32]; diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index 80cb3471adbe..da559a773b51 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -79,8 +79,7 @@ static int debug; #define EMPEG_PRODUCT_ID 0x0001 /* function prototypes for an empeg-car player */ -static int empeg_open(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp); +static int empeg_open(struct tty_struct *tty, struct usb_serial_port *port); static void empeg_close(struct usb_serial_port *port); static int empeg_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, @@ -142,8 +141,7 @@ static int bytes_out; /****************************************************************************** * Empeg specific driver functions ******************************************************************************/ -static int empeg_open(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp) +static int empeg_open(struct tty_struct *tty,struct usb_serial_port *port) { struct usb_serial *serial = port->serial; int result = 0; @@ -151,7 +149,7 @@ static int empeg_open(struct tty_struct *tty, struct usb_serial_port *port, dbg("%s - port %d", __func__, port->number); /* Force default termio settings */ - empeg_set_termios(tty, port, NULL) ; + empeg_set_termios(tty, port, NULL); bytes_in = 0; bytes_out = 0; diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 8fec5d4455c9..76a17f915eef 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -747,8 +747,7 @@ static int ftdi_sio_probe(struct usb_serial *serial, const struct usb_device_id *id); static int ftdi_sio_port_probe(struct usb_serial_port *port); static int ftdi_sio_port_remove(struct usb_serial_port *port); -static int ftdi_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp); +static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port); static void ftdi_close(struct usb_serial_port *port); static void ftdi_dtr_rts(struct usb_serial_port *port, int on); static int ftdi_write(struct tty_struct *tty, struct usb_serial_port *port, @@ -1680,8 +1679,7 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port) return 0; } -static int ftdi_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port) { /* ftdi_open */ struct usb_device *dev = port->serial->dev; struct ftdi_private *priv = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 8839f1c70b7f..20432d345529 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -933,8 +933,7 @@ static int garmin_init_session(struct usb_serial_port *port) -static int garmin_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static int garmin_open(struct tty_struct *tty, struct usb_serial_port *port) { unsigned long flags; int status = 0; diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index ce57f6a32bdf..d9398e9f30ce 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -114,8 +114,7 @@ void usb_serial_generic_deregister(void) #endif } -int usb_serial_generic_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port) { struct usb_serial *serial = port->serial; int result = 0; diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 0191693625d6..dc0f832657e6 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -205,8 +205,7 @@ static void edge_bulk_out_data_callback(struct urb *urb); static void edge_bulk_out_cmd_callback(struct urb *urb); /* function prototypes for the usbserial callbacks */ -static int edge_open(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp); +static int edge_open(struct tty_struct *tty, struct usb_serial_port *port); static void edge_close(struct usb_serial_port *port); static int edge_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); @@ -852,8 +851,7 @@ static void edge_bulk_out_cmd_callback(struct urb *urb) * If successful, we return 0 * Otherwise we return a negative error number. *****************************************************************************/ -static int edge_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static int edge_open(struct tty_struct *tty, struct usb_serial_port *port) { struct edgeport_port *edge_port = usb_get_serial_port_data(port); struct usb_serial *serial; diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index e8bc42f92e79..d4cc0f7af400 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -1831,8 +1831,7 @@ static void edge_bulk_out_callback(struct urb *urb) tty_kref_put(tty); } -static int edge_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static int edge_open(struct tty_struct *tty, struct usb_serial_port *port) { struct edgeport_port *edge_port = usb_get_serial_port_data(port); struct edgeport_serial *edge_serial; diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index 2545d45ce16f..24fcc64b837d 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -75,7 +75,7 @@ static int initial_wait; /* Function prototypes for an ipaq */ static int ipaq_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp); + struct usb_serial_port *port); static void ipaq_close(struct usb_serial_port *port); static int ipaq_calc_num_ports(struct usb_serial *serial); static int ipaq_startup(struct usb_serial *serial); @@ -587,7 +587,7 @@ static int bytes_in; static int bytes_out; static int ipaq_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) + struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct ipaq_private *priv; @@ -628,11 +628,6 @@ static int ipaq_open(struct tty_struct *tty, priv->free_len += PACKET_SIZE; } - if (tty) { - /* FIXME: These two are bogus */ - tty->raw = 1; - tty->real_raw = 1; - } /* * Lose the small buffers usbserial provides. Make larger ones. */ diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index 29ad038b9c8d..727d323f092a 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -193,8 +193,7 @@ static void ipw_read_bulk_callback(struct urb *urb) return; } -static int ipw_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static int ipw_open(struct tty_struct *tty, struct usb_serial_port *port) { struct usb_device *dev = port->serial->dev; u8 buf_flow_static[16] = IPW_BYTES_FLOWINIT; diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 66009b6b763a..95d8d26b9a44 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -86,8 +86,7 @@ static int buffer_size; static int xbof = -1; static int ir_startup (struct usb_serial *serial); -static int ir_open(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filep); +static int ir_open(struct tty_struct *tty, struct usb_serial_port *port); static void ir_close(struct usb_serial_port *port); static int ir_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); @@ -296,8 +295,7 @@ static int ir_startup(struct usb_serial *serial) return 0; } -static int ir_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static int ir_open(struct tty_struct *tty, struct usb_serial_port *port) { char *buffer; int result = 0; diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 96873a7a32b0..f3f9be0469c5 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -1018,8 +1018,7 @@ static void iuu_close(struct usb_serial_port *port) } } -static int iuu_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port) { struct usb_serial *serial = port->serial; u8 *buf; @@ -1077,7 +1076,7 @@ static int iuu_open(struct tty_struct *tty, tty->termios->c_iflag = 0; priv->termios_initialized = 1; priv->poll = 0; - } + } spin_unlock_irqrestore(&priv->lock, flags); /* initialize writebuf */ diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 2594b8743d3f..f8c4b07033ff 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -1209,8 +1209,7 @@ static int keyspan_write_room(struct tty_struct *tty) } -static int keyspan_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static int keyspan_open(struct tty_struct *tty, struct usb_serial_port *port) { struct keyspan_port_private *p_priv; struct keyspan_serial_private *s_priv; diff --git a/drivers/usb/serial/keyspan.h b/drivers/usb/serial/keyspan.h index 3107ed15af64..30771e5b3973 100644 --- a/drivers/usb/serial/keyspan.h +++ b/drivers/usb/serial/keyspan.h @@ -36,8 +36,7 @@ /* Function prototypes for Keyspan serial converter */ static int keyspan_open (struct tty_struct *tty, - struct usb_serial_port *port, - struct file *filp); + struct usb_serial_port *port); static void keyspan_close (struct usb_serial_port *port); static void keyspan_dtr_rts (struct usb_serial_port *port, int on); static int keyspan_startup (struct usb_serial *serial); diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index d0b12e40c2b1..257c16cc6b2a 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -681,7 +681,7 @@ static int keyspan_pda_carrier_raised(struct usb_serial_port *port) static int keyspan_pda_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) + struct usb_serial_port *port) { struct usb_serial *serial = port->serial; unsigned char room; diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 0f44bb8e8d4f..a61673133d7d 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -75,8 +75,7 @@ static int debug; static int klsi_105_startup(struct usb_serial *serial); static void klsi_105_disconnect(struct usb_serial *serial); static void klsi_105_release(struct usb_serial *serial); -static int klsi_105_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp); +static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port); static void klsi_105_close(struct usb_serial_port *port); static int klsi_105_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); @@ -358,8 +357,7 @@ static void klsi_105_release(struct usb_serial *serial) } } /* klsi_105_release */ -static int klsi_105_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port) { struct klsi_105_private *priv = usb_get_serial_port_data(port); int retval = 0; @@ -371,10 +369,6 @@ static int klsi_105_open(struct tty_struct *tty, dbg("%s port %d", __func__, port->number); - /* force low_latency on so that our tty_push actually forces - * the data through - * tty->low_latency = 1; */ - /* Do a defined restart: * Set up sane default baud rate and send the 'READ_ON' * vendor command. diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 6db0e561f680..97901578343a 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -70,8 +70,7 @@ static int debug; /* Function prototypes */ static int kobil_startup(struct usb_serial *serial); static void kobil_release(struct usb_serial *serial); -static int kobil_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp); +static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port); static void kobil_close(struct usb_serial_port *port); static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); @@ -211,8 +210,7 @@ static void kobil_release(struct usb_serial *serial) } -static int kobil_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) { int result = 0; struct kobil_private *priv; diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index d8825e159aa5..d501aefa2628 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -93,8 +93,7 @@ static int debug; */ static int mct_u232_startup(struct usb_serial *serial); static void mct_u232_release(struct usb_serial *serial); -static int mct_u232_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp); +static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port); static void mct_u232_close(struct usb_serial_port *port); static void mct_u232_dtr_rts(struct usb_serial_port *port, int on); static void mct_u232_read_int_callback(struct urb *urb); @@ -421,8 +420,7 @@ static void mct_u232_release(struct usb_serial *serial) } } /* mct_u232_release */ -static int mct_u232_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct mct_u232_private *priv = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 4342a8a0eac9..763e32a44be0 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -319,8 +319,7 @@ static int send_mos_cmd(struct usb_serial *serial, __u8 request, __u16 value, return status; } -static int mos7720_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static int mos7720_open(struct tty_struct *tty, struct usb_serial_port *port) { struct usb_serial *serial; struct usb_serial_port *port0; diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index b93f0f992d9f..f11abf52be7d 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -824,8 +824,7 @@ static int mos7840_serial_probe(struct usb_serial *serial, * Otherwise we return a negative error number. *****************************************************************************/ -static int mos7840_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port) { int response; int j; diff --git a/drivers/usb/serial/navman.c b/drivers/usb/serial/navman.c index f5f3751a888c..5ceaa4c6be09 100644 --- a/drivers/usb/serial/navman.c +++ b/drivers/usb/serial/navman.c @@ -80,8 +80,7 @@ exit: __func__, result); } -static int navman_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static int navman_open(struct tty_struct *tty, struct usb_serial_port *port) { int result = 0; diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 56857ddbd70b..062265038bf0 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -64,8 +64,7 @@ static int debug; #define BT_IGNITIONPRO_ID 0x2000 /* function prototypes */ -static int omninet_open(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp); +static int omninet_open(struct tty_struct *tty, struct usb_serial_port *port); static void omninet_close(struct usb_serial_port *port); static void omninet_read_bulk_callback(struct urb *urb); static void omninet_write_bulk_callback(struct urb *urb); @@ -163,8 +162,7 @@ static int omninet_attach(struct usb_serial *serial) return 0; } -static int omninet_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static int omninet_open(struct tty_struct *tty, struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct usb_serial_port *wport; diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index 336bba79ad32..1085a577c5c1 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -144,8 +144,7 @@ exit: spin_unlock(&priv->lock); } -static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp) +static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port) { struct opticon_private *priv = usb_get_serial_data(port->serial); unsigned long flags; diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index c784ddbe7b61..fe47051dbef2 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -45,8 +45,7 @@ /* Function prototypes */ static int option_probe(struct usb_serial *serial, const struct usb_device_id *id); -static int option_open(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp); +static int option_open(struct tty_struct *tty, struct usb_serial_port *port); static void option_close(struct usb_serial_port *port); static void option_dtr_rts(struct usb_serial_port *port, int on); @@ -961,8 +960,7 @@ static int option_chars_in_buffer(struct tty_struct *tty) return data_len; } -static int option_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static int option_open(struct tty_struct *tty, struct usb_serial_port *port) { struct option_port_private *portdata; int i, err; diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index 3cece27325e7..e90f88a3b040 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -141,8 +141,7 @@ struct oti6858_control_pkt { && ((a)->frame_fmt == (priv)->pending_setup.frame_fmt)) /* function prototypes */ -static int oti6858_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp); +static int oti6858_open(struct tty_struct *tty, struct usb_serial_port *port); static void oti6858_close(struct usb_serial_port *port); static void oti6858_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); @@ -566,8 +565,7 @@ static void oti6858_set_termios(struct tty_struct *tty, spin_unlock_irqrestore(&priv->lock, flags); } -static int oti6858_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static int oti6858_open(struct tty_struct *tty, struct usb_serial_port *port) { struct oti6858_private *priv = usb_get_serial_port_data(port); struct ktermios tmp_termios; diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 3e86815b2705..a63ea99936f7 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -691,8 +691,7 @@ static void pl2303_close(struct usb_serial_port *port) } -static int pl2303_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port) { struct ktermios tmp_termios; struct usb_serial *serial = port->serial; @@ -714,8 +713,6 @@ static int pl2303_open(struct tty_struct *tty, if (tty) pl2303_set_termios(tty, port, &tmp_termios); - /* FIXME: need to assert RTS and DTR if CRTSCTS off */ - dbg("%s - submitting read urb", __func__); port->read_urb->dev = serial->dev; result = usb_submit_urb(port->read_urb, GFP_KERNEL); diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index f48d05e0acc1..55391bbe1230 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -734,8 +734,7 @@ static void sierra_close(struct usb_serial_port *port) } } -static int sierra_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static int sierra_open(struct tty_struct *tty, struct usb_serial_port *port) { struct sierra_port_private *portdata; struct usb_serial *serial = port->serial; diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 3c249d8e8b8e..8b312a05a353 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -623,8 +623,7 @@ static void spcp8x5_set_termios(struct tty_struct *tty, /* open the serial port. do some usb system call. set termios and get the line * status of the device. then submit the read urb */ -static int spcp8x5_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static int spcp8x5_open(struct tty_struct *tty, struct usb_serial_port *port) { struct ktermios tmp_termios; struct usb_serial *serial = port->serial; @@ -658,8 +657,6 @@ static int spcp8x5_open(struct tty_struct *tty, priv->line_status = status & 0xf0 ; spin_unlock_irqrestore(&priv->lock, flags); - /* FIXME: need to assert RTS and DTR if CRTSCTS off */ - dbg("%s - submitting read urb", __func__); port->read_urb->dev = serial->dev; ret = usb_submit_urb(port->read_urb, GFP_KERNEL); diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c index 6157fac9366b..cb7e95f9fcbf 100644 --- a/drivers/usb/serial/symbolserial.c +++ b/drivers/usb/serial/symbolserial.c @@ -124,8 +124,7 @@ exit: spin_unlock(&priv->lock); } -static int symbol_open(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp) +static int symbol_open(struct tty_struct *tty, struct usb_serial_port *port) { struct symbol_private *priv = usb_get_serial_data(port->serial); unsigned long flags; diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 3bc609fe2242..1e9dc8821698 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -98,8 +98,7 @@ struct ti_device { static int ti_startup(struct usb_serial *serial); static void ti_release(struct usb_serial *serial); -static int ti_open(struct tty_struct *tty, struct usb_serial_port *port, - struct file *file); +static int ti_open(struct tty_struct *tty, struct usb_serial_port *port); static void ti_close(struct usb_serial_port *port); static int ti_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *data, int count); @@ -492,8 +491,7 @@ static void ti_release(struct usb_serial *serial) } -static int ti_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *file) +static int ti_open(struct tty_struct *tty, struct usb_serial_port *port) { struct ti_port *tport = usb_get_serial_port_data(port); struct ti_device *tdev; diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 31c7a0939b98..3dda6841e724 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -244,7 +244,7 @@ static int serial_open (struct tty_struct *tty, struct file *filp) /* only call the device specific open if this * is the first time the port is opened */ - retval = serial->type->open(tty, port, filp); + retval = serial->type->open(tty, port); if (retval) goto bailout_interface_put; mutex_unlock(&serial->disc_mutex); diff --git a/drivers/usb/serial/usb_debug.c b/drivers/usb/serial/usb_debug.c index 614800972dc3..7b5bfc4edd3d 100644 --- a/drivers/usb/serial/usb_debug.c +++ b/drivers/usb/serial/usb_debug.c @@ -43,11 +43,10 @@ static struct usb_driver debug_driver = { .no_dynamic_id = 1, }; -static int usb_debug_open(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp) +static int usb_debug_open(struct tty_struct *tty, struct usb_serial_port *port) { port->bulk_out_size = USB_DEBUG_MAX_PACKET_SIZE; - return usb_serial_generic_open(tty, port, filp); + return usb_serial_generic_open(tty, port); } /* This HW really does not support a serial break, so one will be diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index f5d0f64dcc52..1aa5d20a5d99 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -36,8 +36,7 @@ #define DRIVER_DESC "USB HandSpring Visor / Palm OS driver" /* function prototypes for a handspring visor */ -static int visor_open(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp); +static int visor_open(struct tty_struct *tty, struct usb_serial_port *port); static void visor_close(struct usb_serial_port *port); static int visor_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); @@ -273,8 +272,7 @@ static int stats; /****************************************************************************** * Handspring Visor specific driver functions ******************************************************************************/ -static int visor_open(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp) +static int visor_open(struct tty_struct *tty, struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct visor_private *priv = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 8d126dd7a02e..81f2ae505966 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -146,7 +146,7 @@ static int whiteheat_firmware_attach(struct usb_serial *serial); static int whiteheat_attach(struct usb_serial *serial); static void whiteheat_release(struct usb_serial *serial); static int whiteheat_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp); + struct usb_serial_port *port); static void whiteheat_close(struct usb_serial_port *port); static int whiteheat_write(struct tty_struct *tty, struct usb_serial_port *port, @@ -659,8 +659,7 @@ static void whiteheat_release(struct usb_serial *serial) return; } -static int whiteheat_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +static int whiteheat_open(struct tty_struct *tty, struct usb_serial_port *port) { int retval = 0; diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 0ec50ba62139..1cbaf4a6b449 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -238,9 +238,8 @@ struct usb_serial_driver { int (*resume)(struct usb_serial *serial); /* serial function calls */ - /* Called by console with tty = NULL and by tty */ - int (*open)(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp); + /* Called by console and by the tty layer */ + int (*open)(struct tty_struct *tty, struct usb_serial_port *port); void (*close)(struct usb_serial_port *port); int (*write)(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); @@ -300,7 +299,7 @@ static inline void usb_serial_console_disconnect(struct usb_serial *serial) {} extern struct usb_serial *usb_serial_get_by_index(unsigned int minor); extern void usb_serial_put(struct usb_serial *serial); extern int usb_serial_generic_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp); + struct usb_serial_port *port); extern int usb_serial_generic_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); extern void usb_serial_generic_close(struct usb_serial_port *port); -- cgit v1.2.3-71-gd317 From fe1ae7fdd2ee603f2d95f04e09a68f7f79045127 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sat, 19 Sep 2009 13:13:33 -0700 Subject: tty: USB serial termios bits Various drivers have hacks to mangle termios structures. This stems from the fact there is no nice setup hook for configuring the termios settings when the port is created Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/tty_io.c | 1 + drivers/usb/serial/ark3116.c | 46 ++++++++++------------------------------ drivers/usb/serial/cypress_m8.c | 12 +++-------- drivers/usb/serial/empeg.c | 12 +++-------- drivers/usb/serial/iuu_phoenix.c | 31 ++++++++++++--------------- drivers/usb/serial/kobil_sct.c | 22 +++++++++---------- drivers/usb/serial/oti6858.c | 21 +++++++++--------- drivers/usb/serial/spcp8x5.c | 21 +++++++++--------- drivers/usb/serial/usb-serial.c | 38 ++++++++++++++++++++++++++++++++- drivers/usb/serial/whiteheat.c | 6 +++--- include/linux/usb/serial.h | 3 +++ 11 files changed, 106 insertions(+), 107 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 385cca7074da..05f443c47bbe 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1184,6 +1184,7 @@ int tty_init_termios(struct tty_struct *tty) tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios); return 0; } +EXPORT_SYMBOL_GPL(tty_init_termios); /** * tty_driver_install_tty() - install a tty entry in the driver diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 7ddde4ddfb4b..5d25d3e52bf6 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -35,11 +35,6 @@ static struct usb_device_id id_table [] = { }; MODULE_DEVICE_TABLE(usb, id_table); -struct ark3116_private { - spinlock_t lock; - u8 termios_initialized; -}; - static inline void ARK3116_SND(struct usb_serial *serial, int seq, __u8 request, __u8 requesttype, __u16 value, __u16 index) @@ -82,22 +77,11 @@ static inline void ARK3116_RCV_QUIET(struct usb_serial *serial, static int ark3116_attach(struct usb_serial *serial) { char *buf; - struct ark3116_private *priv; - int i; - - for (i = 0; i < serial->num_ports; ++i) { - priv = kzalloc(sizeof(struct ark3116_private), GFP_KERNEL); - if (!priv) - goto cleanup; - spin_lock_init(&priv->lock); - - usb_set_serial_port_data(serial->port[i], priv); - } buf = kmalloc(1, GFP_KERNEL); if (!buf) { dbg("error kmalloc -> out of mem?"); - goto cleanup; + return -ENOMEM; } /* 3 */ @@ -149,13 +133,16 @@ static int ark3116_attach(struct usb_serial *serial) kfree(buf); return 0; +} -cleanup: - for (--i; i >= 0; --i) { - kfree(usb_get_serial_port_data(serial->port[i])); - usb_set_serial_port_data(serial->port[i], NULL); - } - return -ENOMEM; +static void ark3116_init_termios(struct tty_struct *tty) +{ + struct ktermios *termios = tty->termios; + *termios = tty_std_termios; + termios->c_cflag = B9600 | CS8 + | CREAD | HUPCL | CLOCAL; + termios->c_ispeed = 9600; + termios->c_ospeed = 9600; } static void ark3116_set_termios(struct tty_struct *tty, @@ -163,10 +150,8 @@ static void ark3116_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; - struct ark3116_private *priv = usb_get_serial_port_data(port); struct ktermios *termios = tty->termios; unsigned int cflag = termios->c_cflag; - unsigned long flags; int baud; int ark3116_baud; char *buf; @@ -176,16 +161,6 @@ static void ark3116_set_termios(struct tty_struct *tty, dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); - if (!priv->termios_initialized) { - *termios = tty_std_termios; - termios->c_cflag = B9600 | CS8 - | CREAD | HUPCL | CLOCAL; - termios->c_ispeed = 9600; - termios->c_ospeed = 9600; - priv->termios_initialized = 1; - } - spin_unlock_irqrestore(&priv->lock, flags); cflag = termios->c_cflag; termios->c_cflag &= ~(CMSPAR|CRTSCTS); @@ -454,6 +429,7 @@ static struct usb_serial_driver ark3116_device = { .num_ports = 1, .attach = ark3116_attach, .set_termios = ark3116_set_termios, + .init_termios = ark3116_init_termios, .ioctl = ark3116_ioctl, .tiocmget = ark3116_tiocmget, .open = ark3116_open, diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index df1adb9a4367..e0a8b715f2f2 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -657,15 +657,7 @@ static int cypress_open(struct tty_struct *tty, struct usb_serial_port *port) spin_unlock_irqrestore(&priv->lock, flags); /* Set termios */ - result = cypress_write(tty, port, NULL, 0); - - if (result) { - dev_err(&port->dev, - "%s - failed setting the control lines - error %d\n", - __func__, result); - return result; - } else - dbg("%s - success setting the control lines", __func__); + cypress_send(port); if (tty) cypress_set_termios(tty, port, &priv->tmp_termios); @@ -1003,6 +995,8 @@ static void cypress_set_termios(struct tty_struct *tty, dbg("%s - port %d", __func__, port->number); spin_lock_irqsave(&priv->lock, flags); + /* We can't clean this one up as we don't know the device type + early enough */ if (!priv->termios_initialized) { if (priv->chiptype == CT_EARTHMATE) { *(tty->termios) = tty_std_termios; diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index da559a773b51..33c9e9cf9eb2 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -89,8 +89,7 @@ static int empeg_chars_in_buffer(struct tty_struct *tty); static void empeg_throttle(struct tty_struct *tty); static void empeg_unthrottle(struct tty_struct *tty); static int empeg_startup(struct usb_serial *serial); -static void empeg_set_termios(struct tty_struct *tty, - struct usb_serial_port *port, struct ktermios *old_termios); +static void empeg_init_termios(struct tty_struct *tty); static void empeg_write_bulk_callback(struct urb *urb); static void empeg_read_bulk_callback(struct urb *urb); @@ -122,7 +121,7 @@ static struct usb_serial_driver empeg_device = { .throttle = empeg_throttle, .unthrottle = empeg_unthrottle, .attach = empeg_startup, - .set_termios = empeg_set_termios, + .init_termios = empeg_init_termios, .write = empeg_write, .write_room = empeg_write_room, .chars_in_buffer = empeg_chars_in_buffer, @@ -148,9 +147,6 @@ static int empeg_open(struct tty_struct *tty,struct usb_serial_port *port) dbg("%s - port %d", __func__, port->number); - /* Force default termio settings */ - empeg_set_termios(tty, port, NULL); - bytes_in = 0; bytes_out = 0; @@ -423,11 +419,9 @@ static int empeg_startup(struct usb_serial *serial) } -static void empeg_set_termios(struct tty_struct *tty, - struct usb_serial_port *port, struct ktermios *old_termios) +static void empeg_init_termios(struct tty_struct *tty) { struct ktermios *termios = tty->termios; - dbg("%s - port %d", __func__, port->number); /* * The empeg-car player wants these particular tty settings. diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index f3f9be0469c5..6138c1cda35f 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -71,7 +71,6 @@ struct iuu_private { spinlock_t lock; /* store irq state */ wait_queue_head_t delta_msr_wait; u8 line_status; - u8 termios_initialized; int tiostatus; /* store IUART SIGNAL for tiocmget call */ u8 reset; /* if 1 reset is needed */ int poll; /* number of poll */ @@ -1018,13 +1017,24 @@ static void iuu_close(struct usb_serial_port *port) } } +static void iuu_init_termios(struct tty_struct *tty) +{ + *(tty->termios) = tty_std_termios; + tty->termios->c_cflag = CLOCAL | CREAD | CS8 | B9600 + | TIOCM_CTS | CSTOPB | PARENB; + tty->termios->c_ispeed = 9600; + tty->termios->c_ospeed = 9600; + tty->termios->c_lflag = 0; + tty->termios->c_oflag = 0; + tty->termios->c_iflag = 0; +} + static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port) { struct usb_serial *serial = port->serial; u8 *buf; int result; u32 actual; - unsigned long flags; struct iuu_private *priv = usb_get_serial_port_data(port); dbg("%s - port %d", __func__, port->number); @@ -1063,21 +1073,7 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port) port->bulk_in_buffer, 512, NULL, NULL); - /* set the termios structure */ - spin_lock_irqsave(&priv->lock, flags); - if (tty && !priv->termios_initialized) { - *(tty->termios) = tty_std_termios; - tty->termios->c_cflag = CLOCAL | CREAD | CS8 | B9600 - | TIOCM_CTS | CSTOPB | PARENB; - tty->termios->c_ispeed = 9600; - tty->termios->c_ospeed = 9600; - tty->termios->c_lflag = 0; - tty->termios->c_oflag = 0; - tty->termios->c_iflag = 0; - priv->termios_initialized = 1; - priv->poll = 0; - } - spin_unlock_irqrestore(&priv->lock, flags); + priv->poll = 0; /* initialize writebuf */ #define FISH(a, b, c, d) do { \ @@ -1200,6 +1196,7 @@ static struct usb_serial_driver iuu_device = { .tiocmget = iuu_tiocmget, .tiocmset = iuu_tiocmset, .set_termios = iuu_set_termios, + .init_termios = iuu_init_termios, .attach = iuu_startup, .release = iuu_release, }; diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 97901578343a..45ea694b3ae6 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -84,7 +84,7 @@ static void kobil_read_int_callback(struct urb *urb); static void kobil_write_callback(struct urb *purb); static void kobil_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); - +static void kobil_init_termios(struct tty_struct *tty); static struct usb_device_id id_table [] = { { USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_ADAPTER_B_PRODUCT_ID) }, @@ -119,6 +119,7 @@ static struct usb_serial_driver kobil_device = { .release = kobil_release, .ioctl = kobil_ioctl, .set_termios = kobil_set_termios, + .init_termios = kobil_init_termios, .tiocmget = kobil_tiocmget, .tiocmset = kobil_tiocmset, .open = kobil_open, @@ -209,6 +210,15 @@ static void kobil_release(struct usb_serial *serial) kfree(usb_get_serial_port_data(serial->port[i])); } +static void kobil_init_termios(struct tty_struct *tty) +{ + /* Default to echo off and other sane device settings */ + tty->termios->c_lflag = 0; + tty->termios->c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN | XCASE); + tty->termios->c_iflag = IGNBRK | IGNPAR | IXOFF; + /* do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D) */ + tty->termios->c_oflag &= ~ONLCR; +} static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) { @@ -224,16 +234,6 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) /* someone sets the dev to 0 if the close method has been called */ port->interrupt_in_urb->dev = port->serial->dev; - if (tty) { - - /* Default to echo off and other sane device settings */ - tty->termios->c_lflag = 0; - tty->termios->c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN | - XCASE); - tty->termios->c_iflag = IGNBRK | IGNPAR | IXOFF; - /* do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D) */ - tty->termios->c_oflag &= ~ONLCR; - } /* allocate memory for transfer buffer */ transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL); if (!transfer_buffer) diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index e90f88a3b040..0f4a70ce3823 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -145,6 +145,7 @@ static int oti6858_open(struct tty_struct *tty, struct usb_serial_port *port); static void oti6858_close(struct usb_serial_port *port); static void oti6858_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); +static void oti6858_init_termios(struct tty_struct *tty); static int oti6858_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); static void oti6858_read_int_callback(struct urb *urb); @@ -185,6 +186,7 @@ static struct usb_serial_driver oti6858_device = { .write = oti6858_write, .ioctl = oti6858_ioctl, .set_termios = oti6858_set_termios, + .init_termios = oti6858_init_termios, .tiocmget = oti6858_tiocmget, .tiocmset = oti6858_tiocmset, .read_bulk_callback = oti6858_read_bulk_callback, @@ -205,7 +207,6 @@ struct oti6858_private { struct { u8 read_urb_in_use; u8 write_urb_in_use; - u8 termios_initialized; } flags; struct delayed_work delayed_write_work; @@ -446,6 +447,14 @@ static int oti6858_chars_in_buffer(struct tty_struct *tty) return chars; } +static void oti6858_init_termios(struct tty_struct *tty) +{ + *(tty->termios) = tty_std_termios; + tty->termios->c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL; + tty->termios->c_ispeed = 38400; + tty->termios->c_ospeed = 38400; +} + static void oti6858_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { @@ -463,16 +472,6 @@ static void oti6858_set_termios(struct tty_struct *tty, return; } - spin_lock_irqsave(&priv->lock, flags); - if (!priv->flags.termios_initialized) { - *(tty->termios) = tty_std_termios; - tty->termios->c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL; - tty->termios->c_ispeed = 38400; - tty->termios->c_ospeed = 38400; - priv->flags.termios_initialized = 1; - } - spin_unlock_irqrestore(&priv->lock, flags); - cflag = tty->termios->c_cflag; spin_lock_irqsave(&priv->lock, flags); diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 8b312a05a353..61e7c40b94fb 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -299,7 +299,6 @@ struct spcp8x5_private { wait_queue_head_t delta_msr_wait; u8 line_control; u8 line_status; - u8 termios_initialized; }; /* desc : when device plug in,this function would be called. @@ -498,6 +497,15 @@ static void spcp8x5_close(struct usb_serial_port *port) dev_dbg(&port->dev, "usb_unlink_urb(read_urb) = %d\n", result); } +static void spcp8x5_init_termios(struct tty_struct *tty) +{ + /* for the 1st time call this function */ + *(tty->termios) = tty_std_termios; + tty->termios->c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL; + tty->termios->c_ispeed = 115200; + tty->termios->c_ospeed = 115200; +} + /* set the serial param for transfer. we should check if we really need to * transfer. if we set flow control we should do this too. */ static void spcp8x5_set_termios(struct tty_struct *tty, @@ -514,16 +522,6 @@ static void spcp8x5_set_termios(struct tty_struct *tty, int i; u8 control; - /* for the 1st time call this function */ - spin_lock_irqsave(&priv->lock, flags); - if (!priv->termios_initialized) { - *(tty->termios) = tty_std_termios; - tty->termios->c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL; - tty->termios->c_ispeed = 115200; - tty->termios->c_ospeed = 115200; - priv->termios_initialized = 1; - } - spin_unlock_irqrestore(&priv->lock, flags); /* check that they really want us to change something */ if (!tty_termios_hw_change(tty->termios, old_termios)) @@ -1008,6 +1006,7 @@ static struct usb_serial_driver spcp8x5_device = { .carrier_raised = spcp8x5_carrier_raised, .write = spcp8x5_write, .set_termios = spcp8x5_set_termios, + .init_termios = spcp8x5_init_termios, .ioctl = spcp8x5_ioctl, .tiocmget = spcp8x5_tiocmget, .tiocmset = spcp8x5_tiocmset, diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 3dda6841e724..80c1f4d8e910 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -721,6 +721,41 @@ static const struct tty_port_operations serial_port_ops = { .dtr_rts = serial_dtr_rts, }; +/** + * serial_install - install tty + * @driver: the driver (USB in our case) + * @tty: the tty being created + * + * Create the termios objects for this tty. We use the default USB + * serial ones but permit them to be overriddenby serial->type->termios. + * This lets us remove all the ugly hackery + */ + +static int serial_install(struct tty_driver *driver, struct tty_struct *tty) +{ + int idx = tty->index; + struct usb_serial *serial; + int retval; + + /* If the termios setup has yet to be done */ + if (tty->driver->termios[idx] == NULL) { + /* perform the standard setup */ + retval = tty_init_termios(tty); + if (retval) + return retval; + /* allow the driver to update it */ + serial = usb_serial_get_by_index(tty->index); + if (serial->type->init_termios) + serial->type->init_termios(tty); + usb_serial_put(serial); + } + /* Final install (we use the default method) */ + tty_driver_kref_get(driver); + tty->count++; + driver->ttys[idx] = tty; + return 0; +} + int usb_serial_probe(struct usb_interface *interface, const struct usb_device_id *id) { @@ -1228,7 +1263,8 @@ static const struct tty_operations serial_ops = { .chars_in_buffer = serial_chars_in_buffer, .tiocmget = serial_tiocmget, .tiocmset = serial_tiocmset, - .shutdown = serial_do_free, + .shutdown = serial_do_free, + .install = serial_install, .proc_fops = &serial_proc_fops, }; diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 81f2ae505966..62424eec33ec 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -259,7 +259,7 @@ static int firm_send_command(struct usb_serial_port *port, __u8 command, __u8 *data, __u8 datasize); static int firm_open(struct usb_serial_port *port); static int firm_close(struct usb_serial_port *port); -static int firm_setup_port(struct tty_struct *tty); +static void firm_setup_port(struct tty_struct *tty); static int firm_set_rts(struct usb_serial_port *port, __u8 onoff); static int firm_set_dtr(struct usb_serial_port *port, __u8 onoff); static int firm_set_break(struct usb_serial_port *port, __u8 onoff); @@ -1210,7 +1210,7 @@ static int firm_close(struct usb_serial_port *port) } -static int firm_setup_port(struct tty_struct *tty) +static void firm_setup_port(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct whiteheat_port_settings port_settings; @@ -1285,7 +1285,7 @@ static int firm_setup_port(struct tty_struct *tty) port_settings.lloop = 0; /* now send the message to the device */ - return firm_send_command(port, WHITEHEAT_SETUP_PORT, + firm_send_command(port, WHITEHEAT_SETUP_PORT, (__u8 *)&port_settings, sizeof(port_settings)); } diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 1cbaf4a6b449..7b85e327af91 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -260,6 +260,9 @@ struct usb_serial_driver { be an attached tty at this point */ void (*dtr_rts)(struct usb_serial_port *port, int on); int (*carrier_raised)(struct usb_serial_port *port); + /* Called by the usb serial hooks to allow the user to rework the + termios state */ + void (*init_termios)(struct tty_struct *tty); /* USB events */ void (*read_int_callback)(struct urb *urb); void (*write_int_callback)(struct urb *urb); -- cgit v1.2.3-71-gd317 From 85e08ca54c5c203cd2638f0fc8fa899a539f6254 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 21 Jun 2009 23:19:23 +0200 Subject: USB: Move endpoint sync type definitions from usb/audio.h to usb/ch9.h And use the new definitions in the USB Audio Class gadget driver. Signed-off-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_audio.c | 2 +- include/linux/usb/audio.h | 10 ---------- include/linux/usb/ch9.h | 6 ++++++ 3 files changed, 7 insertions(+), 11 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/gadget/f_audio.c b/drivers/usb/gadget/f_audio.c index 66527ba2d2ea..76afbd1b515f 100644 --- a/drivers/usb/gadget/f_audio.c +++ b/drivers/usb/gadget/f_audio.c @@ -174,7 +174,7 @@ static struct usb_endpoint_descriptor as_out_ep_desc __initdata = { .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_AS_ENDPOINT_ADAPTIVE + .bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE | USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = __constant_cpu_to_le16(OUT_EP_MAX_PACKET_SIZE), .bInterval = 4, diff --git a/include/linux/usb/audio.h b/include/linux/usb/audio.h index b5744bc218ab..bcf4fb4f5e82 100644 --- a/include/linux/usb/audio.h +++ b/include/linux/usb/audio.h @@ -46,12 +46,6 @@ #define MIDI_IN_JACK 0x02 #define MIDI_OUT_JACK 0x03 -/* endpoint attributes */ -#define EP_ATTR_MASK 0x0c -#define EP_ATTR_ASYNC 0x04 -#define EP_ATTR_ADAPTIVE 0x08 -#define EP_ATTR_SYNC 0x0c - /* cs endpoint attributes */ #define EP_CS_ATTR_SAMPLE_RATE 0x01 #define EP_CS_ATTR_PITCH_CONTROL 0x02 @@ -244,10 +238,6 @@ struct usb_as_formate_type_i_discrete_descriptor_##n { \ #define USB_AS_FORMAT_TYPE_II 0x2 #define USB_AS_FORMAT_TYPE_III 0x3 -#define USB_AS_ENDPOINT_ASYNC (1 << 2) -#define USB_AS_ENDPOINT_ADAPTIVE (2 << 2) -#define USB_AS_ENDPOINT_SYNC (3 << 2) - struct usb_as_iso_endpoint_descriptor { __u8 bLength; /* in bytes: 7 */ __u8 bDescriptorType; /* USB_DT_CS_ENDPOINT */ diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index 93223638f702..8f8b7411b87b 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -348,6 +348,12 @@ struct usb_endpoint_descriptor { #define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */ #define USB_ENDPOINT_DIR_MASK 0x80 +#define USB_ENDPOINT_SYNCTYPE 0x0c +#define USB_ENDPOINT_SYNC_NONE (0 << 2) +#define USB_ENDPOINT_SYNC_ASYNC (1 << 2) +#define USB_ENDPOINT_SYNC_ADAPTIVE (2 << 2) +#define USB_ENDPOINT_SYNC_SYNC (3 << 2) + #define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */ #define USB_ENDPOINT_XFER_CONTROL 0 #define USB_ENDPOINT_XFER_ISOC 1 -- cgit v1.2.3-71-gd317 From 315ad3028c8aae14891797040f855fc3291a076b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 21 Jun 2009 23:20:39 +0200 Subject: USB: Move vendor subclass definition from usb/audio.h to usb/ch9.h USB_SUBCLASS_VENDOR_SPEC is common to several USB classes and as such belongs to usb/ch9.h. Signed-off-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/audio.h | 1 - include/linux/usb/ch9.h | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include/linux/usb') diff --git a/include/linux/usb/audio.h b/include/linux/usb/audio.h index bcf4fb4f5e82..f75092aba02f 100644 --- a/include/linux/usb/audio.h +++ b/include/linux/usb/audio.h @@ -24,7 +24,6 @@ #define USB_SUBCLASS_AUDIOCONTROL 0x01 #define USB_SUBCLASS_AUDIOSTREAMING 0x02 #define USB_SUBCLASS_MIDISTREAMING 0x03 -#define USB_SUBCLASS_VENDOR_SPEC 0xff /* A.5 Audio Class-Specific AC interface Descriptor Subtypes*/ #define HEADER 0x01 diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index 8f8b7411b87b..94012e649d86 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -258,6 +258,8 @@ struct usb_device_descriptor { #define USB_CLASS_APP_SPEC 0xfe #define USB_CLASS_VENDOR_SPEC 0xff +#define USB_SUBCLASS_VENDOR_SPEC 0xff + /*-------------------------------------------------------------------------*/ /* USB_DT_CONFIG: Configuration descriptor information. -- cgit v1.2.3-71-gd317 From 512ad27d8667158747de2e8da8a23e8f50e91856 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 21 Jun 2009 23:23:05 +0200 Subject: USB audio gadget: Prefix all macro definitions with UAC_ in linux/usb/audio.h linux/usb/audio.h is a public header file that includes definitions exported to userspace. To avoid namespace clashes, prefix all macro definitions with UAC_. Existing macros and structures prefixed with USB_AC_ and USB_AS_ are renamed for consistency. Signed-off-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/audio.c | 24 ++-- drivers/usb/gadget/f_audio.c | 80 +++++++------- drivers/usb/gadget/gmidi.c | 8 +- include/linux/usb/audio.h | 258 ++++++++++++++++++++++--------------------- 4 files changed, 188 insertions(+), 182 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c index 9f80f4e970bd..a3a0f4a27ef0 100644 --- a/drivers/usb/gadget/audio.c +++ b/drivers/usb/gadget/audio.c @@ -106,20 +106,20 @@ static int audio_set_endpoint_req(struct usb_configuration *c, ctrl->bRequest, w_value, len, ep); switch (ctrl->bRequest) { - case SET_CUR: + case UAC_SET_CUR: value = 0; break; - case SET_MIN: + case UAC_SET_MIN: break; - case SET_MAX: + case UAC_SET_MAX: break; - case SET_RES: + case UAC_SET_RES: break; - case SET_MEM: + case UAC_SET_MEM: break; default: @@ -142,13 +142,13 @@ static int audio_get_endpoint_req(struct usb_configuration *c, ctrl->bRequest, w_value, len, ep); switch (ctrl->bRequest) { - case GET_CUR: - case GET_MIN: - case GET_MAX: - case GET_RES: + case UAC_GET_CUR: + case UAC_GET_MIN: + case UAC_GET_MAX: + case UAC_GET_RES: value = 3; break; - case GET_MEM: + case UAC_GET_MEM: break; default: break; @@ -171,11 +171,11 @@ audio_setup(struct usb_configuration *c, const struct usb_ctrlrequest *ctrl) * Audio class messages; interface activation uses set_alt(). */ switch (ctrl->bRequestType) { - case USB_AUDIO_SET_ENDPOINT: + case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: value = audio_set_endpoint_req(c, ctrl); break; - case USB_AUDIO_GET_ENDPOINT: + case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: value = audio_get_endpoint_req(c, ctrl); break; diff --git a/drivers/usb/gadget/f_audio.c b/drivers/usb/gadget/f_audio.c index 76afbd1b515f..7b05b3c8c0b1 100644 --- a/drivers/usb/gadget/f_audio.c +++ b/drivers/usb/gadget/f_audio.c @@ -50,16 +50,16 @@ static struct usb_interface_descriptor ac_interface_desc __initdata = { .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, }; -DECLARE_USB_AC_HEADER_DESCRIPTOR(2); +DECLARE_UAC_AC_HEADER_DESCRIPTOR(2); -#define USB_DT_AC_HEADER_LENGH USB_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES) +#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES) /* B.3.2 Class-Specific AC Interface Descriptor */ -static struct usb_ac_header_descriptor_2 ac_header_desc = { - .bLength = USB_DT_AC_HEADER_LENGH, +static struct uac_ac_header_descriptor_2 ac_header_desc = { + .bLength = UAC_DT_AC_HEADER_LENGTH, .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = HEADER, + .bDescriptorSubtype = UAC_HEADER, .bcdADC = __constant_cpu_to_le16(0x0100), - .wTotalLength = __constant_cpu_to_le16(USB_DT_AC_HEADER_LENGH), + .wTotalLength = __constant_cpu_to_le16(UAC_DT_AC_HEADER_LENGTH), .bInCollection = F_AUDIO_NUM_INTERFACES, .baInterfaceNr = { [0] = F_AUDIO_AC_INTERFACE, @@ -68,33 +68,33 @@ static struct usb_ac_header_descriptor_2 ac_header_desc = { }; #define INPUT_TERMINAL_ID 1 -static struct usb_input_terminal_descriptor input_terminal_desc = { - .bLength = USB_DT_AC_INPUT_TERMINAL_SIZE, +static struct uac_input_terminal_descriptor input_terminal_desc = { + .bLength = UAC_DT_INPUT_TERMINAL_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = INPUT_TERMINAL, + .bDescriptorSubtype = UAC_INPUT_TERMINAL, .bTerminalID = INPUT_TERMINAL_ID, - .wTerminalType = USB_AC_TERMINAL_STREAMING, + .wTerminalType = UAC_TERMINAL_STREAMING, .bAssocTerminal = 0, .wChannelConfig = 0x3, }; -DECLARE_USB_AC_FEATURE_UNIT_DESCRIPTOR(0); +DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0); #define FEATURE_UNIT_ID 2 -static struct usb_ac_feature_unit_descriptor_0 feature_unit_desc = { - .bLength = USB_DT_AC_FEATURE_UNIT_SIZE(0), +static struct uac_feature_unit_descriptor_0 feature_unit_desc = { + .bLength = UAC_DT_FEATURE_UNIT_SIZE(0), .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = FEATURE_UNIT, + .bDescriptorSubtype = UAC_FEATURE_UNIT, .bUnitID = FEATURE_UNIT_ID, .bSourceID = INPUT_TERMINAL_ID, .bControlSize = 2, - .bmaControls[0] = (FU_MUTE | FU_VOLUME), + .bmaControls[0] = (UAC_FU_MUTE | UAC_FU_VOLUME), }; static struct usb_audio_control mute_control = { .list = LIST_HEAD_INIT(mute_control.list), .name = "Mute Control", - .type = MUTE_CONTROL, + .type = UAC_MUTE_CONTROL, /* Todo: add real Mute control code */ .set = generic_set_cmd, .get = generic_get_cmd, @@ -103,7 +103,7 @@ static struct usb_audio_control mute_control = { static struct usb_audio_control volume_control = { .list = LIST_HEAD_INIT(volume_control.list), .name = "Volume Control", - .type = VOLUME_CONTROL, + .type = UAC_VOLUME_CONTROL, /* Todo: add real Volume control code */ .set = generic_set_cmd, .get = generic_get_cmd, @@ -113,17 +113,17 @@ static struct usb_audio_control_selector feature_unit = { .list = LIST_HEAD_INIT(feature_unit.list), .id = FEATURE_UNIT_ID, .name = "Mute & Volume Control", - .type = FEATURE_UNIT, + .type = UAC_FEATURE_UNIT, .desc = (struct usb_descriptor_header *)&feature_unit_desc, }; #define OUTPUT_TERMINAL_ID 3 -static struct usb_output_terminal_descriptor output_terminal_desc = { - .bLength = USB_DT_AC_OUTPUT_TERMINAL_SIZE, +static struct uac_output_terminal_descriptor output_terminal_desc = { + .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = OUTPUT_TERMINAL, + .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, .bTerminalID = OUTPUT_TERMINAL_ID, - .wTerminalType = USB_AC_OUTPUT_TERMINAL_SPEAKER, + .wTerminalType = UAC_OUTPUT_TERMINAL_SPEAKER, .bAssocTerminal = FEATURE_UNIT_ID, .bSourceID = FEATURE_UNIT_ID, }; @@ -148,22 +148,22 @@ static struct usb_interface_descriptor as_interface_alt_1_desc = { }; /* B.4.2 Class-Specific AS Interface Descriptor */ -static struct usb_as_header_descriptor as_header_desc = { - .bLength = USB_DT_AS_HEADER_SIZE, +static struct uac_as_header_descriptor as_header_desc = { + .bLength = UAC_DT_AS_HEADER_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = AS_GENERAL, + .bDescriptorSubtype = UAC_AS_GENERAL, .bTerminalLink = INPUT_TERMINAL_ID, .bDelay = 1, - .wFormatTag = USB_AS_AUDIO_FORMAT_TYPE_I_PCM, + .wFormatTag = UAC_FORMAT_TYPE_I_PCM, }; -DECLARE_USB_AS_FORMAT_TYPE_I_DISCRETE_DESC(1); +DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1); -static struct usb_as_formate_type_i_discrete_descriptor_1 as_type_i_desc = { - .bLength = USB_AS_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1), +static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = { + .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1), .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = FORMAT_TYPE, - .bFormatType = USB_AS_FORMAT_TYPE_I, + .bDescriptorSubtype = UAC_FORMAT_TYPE, + .bFormatType = UAC_FORMAT_TYPE_I, .bSubframeSize = 2, .bBitResolution = 16, .bSamFreqType = 1, @@ -181,10 +181,10 @@ static struct usb_endpoint_descriptor as_out_ep_desc __initdata = { }; /* Class-specific AS ISO OUT Endpoint Descriptor */ -static struct usb_as_iso_endpoint_descriptor as_iso_out_desc __initdata = { - .bLength = USB_AS_ISO_ENDPOINT_DESC_SIZE, +static struct uac_iso_endpoint_descriptor as_iso_out_desc __initdata = { + .bLength = UAC_ISO_ENDPOINT_DESC_SIZE, .bDescriptorType = USB_DT_CS_ENDPOINT, - .bDescriptorSubtype = EP_GENERAL, + .bDescriptorSubtype = UAC_EP_GENERAL, .bmAttributes = 1, .bLockDelayUnits = 1, .wLockDelay = __constant_cpu_to_le16(1), @@ -456,11 +456,11 @@ f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) * Audio class messages; interface activation uses set_alt(). */ switch (ctrl->bRequestType) { - case USB_AUDIO_SET_INTF: + case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE: value = audio_set_intf_req(f, ctrl); break; - case USB_AUDIO_GET_INTF: + case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE: value = audio_get_intf_req(f, ctrl); break; @@ -642,10 +642,10 @@ int __init control_selector_init(struct f_audio *audio) list_add(&mute_control.list, &feature_unit.control); list_add(&volume_control.list, &feature_unit.control); - volume_control.data[_CUR] = 0xffc0; - volume_control.data[_MIN] = 0xe3a0; - volume_control.data[_MAX] = 0xfff0; - volume_control.data[_RES] = 0x0030; + volume_control.data[UAC__CUR] = 0xffc0; + volume_control.data[UAC__MIN] = 0xe3a0; + volume_control.data[UAC__MAX] = 0xfff0; + volume_control.data[UAC__RES] = 0x0030; return 0; } diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index b9312dc6e041..d0b1e836f0e0 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -191,7 +191,7 @@ module_param(qlen, uint, S_IRUGO); #define GMIDI_MS_INTERFACE 1 #define GMIDI_NUM_INTERFACES 2 -DECLARE_USB_AC_HEADER_DESCRIPTOR(1); +DECLARE_UAC_AC_HEADER_DESCRIPTOR(1); DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1); DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(1); @@ -237,12 +237,12 @@ static const struct usb_interface_descriptor ac_interface_desc = { }; /* B.3.2 Class-Specific AC Interface Descriptor */ -static const struct usb_ac_header_descriptor_1 ac_header_desc = { - .bLength = USB_DT_AC_HEADER_SIZE(1), +static const struct uac_ac_header_descriptor_1 ac_header_desc = { + .bLength = UAC_DT_AC_HEADER_SIZE(1), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = USB_MS_HEADER, .bcdADC = cpu_to_le16(0x0100), - .wTotalLength = cpu_to_le16(USB_DT_AC_HEADER_SIZE(1)), + .wTotalLength = cpu_to_le16(UAC_DT_AC_HEADER_SIZE(1)), .bInCollection = 1, .baInterfaceNr = { [0] = GMIDI_MS_INTERFACE, diff --git a/include/linux/usb/audio.h b/include/linux/usb/audio.h index f75092aba02f..c3edfcb72c34 100644 --- a/include/linux/usb/audio.h +++ b/include/linux/usb/audio.h @@ -25,80 +25,77 @@ #define USB_SUBCLASS_AUDIOSTREAMING 0x02 #define USB_SUBCLASS_MIDISTREAMING 0x03 -/* A.5 Audio Class-Specific AC interface Descriptor Subtypes*/ -#define HEADER 0x01 -#define INPUT_TERMINAL 0x02 -#define OUTPUT_TERMINAL 0x03 -#define MIXER_UNIT 0x04 -#define SELECTOR_UNIT 0x05 -#define FEATURE_UNIT 0x06 -#define PROCESSING_UNIT 0x07 -#define EXTENSION_UNIT 0x08 - -#define AS_GENERAL 0x01 -#define FORMAT_TYPE 0x02 -#define FORMAT_SPECIFIC 0x03 - -#define EP_GENERAL 0x01 - -#define MS_GENERAL 0x01 -#define MIDI_IN_JACK 0x02 -#define MIDI_OUT_JACK 0x03 - -/* cs endpoint attributes */ -#define EP_CS_ATTR_SAMPLE_RATE 0x01 -#define EP_CS_ATTR_PITCH_CONTROL 0x02 -#define EP_CS_ATTR_FILL_MAX 0x80 - -/* Audio Class specific Request Codes */ -#define USB_AUDIO_SET_INTF 0x21 -#define USB_AUDIO_SET_ENDPOINT 0x22 -#define USB_AUDIO_GET_INTF 0xa1 -#define USB_AUDIO_GET_ENDPOINT 0xa2 - -#define SET_ 0x00 -#define GET_ 0x80 - -#define _CUR 0x1 -#define _MIN 0x2 -#define _MAX 0x3 -#define _RES 0x4 -#define _MEM 0x5 - -#define SET_CUR (SET_ | _CUR) -#define GET_CUR (GET_ | _CUR) -#define SET_MIN (SET_ | _MIN) -#define GET_MIN (GET_ | _MIN) -#define SET_MAX (SET_ | _MAX) -#define GET_MAX (GET_ | _MAX) -#define SET_RES (SET_ | _RES) -#define GET_RES (GET_ | _RES) -#define SET_MEM (SET_ | _MEM) -#define GET_MEM (GET_ | _MEM) - -#define GET_STAT 0xff - -#define USB_AC_TERMINAL_UNDEFINED 0x100 -#define USB_AC_TERMINAL_STREAMING 0x101 -#define USB_AC_TERMINAL_VENDOR_SPEC 0x1FF +/* A.5 Audio Class-Specific AC Interface Descriptor Subtypes */ +#define UAC_HEADER 0x01 +#define UAC_INPUT_TERMINAL 0x02 +#define UAC_OUTPUT_TERMINAL 0x03 +#define UAC_MIXER_UNIT 0x04 +#define UAC_SELECTOR_UNIT 0x05 +#define UAC_FEATURE_UNIT 0x06 +#define UAC_PROCESSING_UNIT 0x07 +#define UAC_EXTENSION_UNIT 0x08 + +/* A.6 Audio Class-Specific AS Interface Descriptor Subtypes */ +#define UAC_AS_GENERAL 0x01 +#define UAC_FORMAT_TYPE 0x02 +#define UAC_FORMAT_SPECIFIC 0x03 + +/* A.8 Audio Class-Specific Endpoint Descriptor Subtypes */ +#define UAC_EP_GENERAL 0x01 + +/* A.9 Audio Class-Specific Request Codes */ +#define UAC_SET_ 0x00 +#define UAC_GET_ 0x80 + +#define UAC__CUR 0x1 +#define UAC__MIN 0x2 +#define UAC__MAX 0x3 +#define UAC__RES 0x4 +#define UAC__MEM 0x5 + +#define UAC_SET_CUR (UAC_SET_ | UAC__CUR) +#define UAC_GET_CUR (UAC_GET_ | UAC__CUR) +#define UAC_SET_MIN (UAC_SET_ | UAC__MIN) +#define UAC_GET_MIN (UAC_GET_ | UAC__MIN) +#define UAC_SET_MAX (UAC_SET_ | UAC__MAX) +#define UAC_GET_MAX (UAC_GET_ | UAC__MAX) +#define UAC_SET_RES (UAC_SET_ | UAC__RES) +#define UAC_GET_RES (UAC_GET_ | UAC__RES) +#define UAC_SET_MEM (UAC_SET_ | UAC__MEM) +#define UAC_GET_MEM (UAC_GET_ | UAC__MEM) + +#define UAC_GET_STAT 0xff + +/* MIDI - A.1 MS Class-Specific Interface Descriptor Subtypes */ +#define UAC_MS_HEADER 0x01 +#define UAC_MIDI_IN_JACK 0x02 +#define UAC_MIDI_OUT_JACK 0x03 + +/* MIDI - A.1 MS Class-Specific Endpoint Descriptor Subtypes */ +#define UAC_MS_GENERAL 0x01 + +/* Terminals - 2.1 USB Terminal Types */ +#define UAC_TERMINAL_UNDEFINED 0x100 +#define UAC_TERMINAL_STREAMING 0x101 +#define UAC_TERMINAL_VENDOR_SPEC 0x1FF /* Terminal Control Selectors */ /* 4.3.2 Class-Specific AC Interface Descriptor */ -struct usb_ac_header_descriptor { +struct uac_ac_header_descriptor { __u8 bLength; /* 8 + n */ __u8 bDescriptorType; /* USB_DT_CS_INTERFACE */ - __u8 bDescriptorSubtype; /* USB_MS_HEADER */ + __u8 bDescriptorSubtype; /* UAC_MS_HEADER */ __le16 bcdADC; /* 0x0100 */ __le16 wTotalLength; /* includes Unit and Terminal desc. */ __u8 bInCollection; /* n */ __u8 baInterfaceNr[]; /* [n] */ } __attribute__ ((packed)); -#define USB_DT_AC_HEADER_SIZE(n) (8 + (n)) +#define UAC_DT_AC_HEADER_SIZE(n) (8 + (n)) /* As above, but more useful for defining your own descriptors: */ -#define DECLARE_USB_AC_HEADER_DESCRIPTOR(n) \ -struct usb_ac_header_descriptor_##n { \ +#define DECLARE_UAC_AC_HEADER_DESCRIPTOR(n) \ +struct uac_ac_header_descriptor_##n { \ __u8 bLength; \ __u8 bDescriptorType; \ __u8 bDescriptorSubtype; \ @@ -109,7 +106,7 @@ struct usb_ac_header_descriptor_##n { \ } __attribute__ ((packed)) /* 4.3.2.1 Input Terminal Descriptor */ -struct usb_input_terminal_descriptor { +struct uac_input_terminal_descriptor { __u8 bLength; /* in bytes: 12 */ __u8 bDescriptorType; /* CS_INTERFACE descriptor type */ __u8 bDescriptorSubtype; /* INPUT_TERMINAL descriptor subtype */ @@ -122,18 +119,19 @@ struct usb_input_terminal_descriptor { __u8 iTerminal; } __attribute__ ((packed)); -#define USB_DT_AC_INPUT_TERMINAL_SIZE 12 +#define UAC_DT_INPUT_TERMINAL_SIZE 12 -#define USB_AC_INPUT_TERMINAL_UNDEFINED 0x200 -#define USB_AC_INPUT_TERMINAL_MICROPHONE 0x201 -#define USB_AC_INPUT_TERMINAL_DESKTOP_MICROPHONE 0x202 -#define USB_AC_INPUT_TERMINAL_PERSONAL_MICROPHONE 0x203 -#define USB_AC_INPUT_TERMINAL_OMNI_DIR_MICROPHONE 0x204 -#define USB_AC_INPUT_TERMINAL_MICROPHONE_ARRAY 0x205 -#define USB_AC_INPUT_TERMINAL_PROC_MICROPHONE_ARRAY 0x206 +/* Terminals - 2.2 Input Terminal Types */ +#define UAC_INPUT_TERMINAL_UNDEFINED 0x200 +#define UAC_INPUT_TERMINAL_MICROPHONE 0x201 +#define UAC_INPUT_TERMINAL_DESKTOP_MICROPHONE 0x202 +#define UAC_INPUT_TERMINAL_PERSONAL_MICROPHONE 0x203 +#define UAC_INPUT_TERMINAL_OMNI_DIR_MICROPHONE 0x204 +#define UAC_INPUT_TERMINAL_MICROPHONE_ARRAY 0x205 +#define UAC_INPUT_TERMINAL_PROC_MICROPHONE_ARRAY 0x206 /* 4.3.2.2 Output Terminal Descriptor */ -struct usb_output_terminal_descriptor { +struct uac_output_terminal_descriptor { __u8 bLength; /* in bytes: 9 */ __u8 bDescriptorType; /* CS_INTERFACE descriptor type */ __u8 bDescriptorSubtype; /* OUTPUT_TERMINAL descriptor subtype */ @@ -144,23 +142,24 @@ struct usb_output_terminal_descriptor { __u8 iTerminal; } __attribute__ ((packed)); -#define USB_DT_AC_OUTPUT_TERMINAL_SIZE 9 +#define UAC_DT_OUTPUT_TERMINAL_SIZE 9 -#define USB_AC_OUTPUT_TERMINAL_UNDEFINED 0x300 -#define USB_AC_OUTPUT_TERMINAL_SPEAKER 0x301 -#define USB_AC_OUTPUT_TERMINAL_HEADPHONES 0x302 -#define USB_AC_OUTPUT_TERMINAL_HEAD_MOUNTED_DISPLAY_AUDIO 0x303 -#define USB_AC_OUTPUT_TERMINAL_DESKTOP_SPEAKER 0x304 -#define USB_AC_OUTPUT_TERMINAL_ROOM_SPEAKER 0x305 -#define USB_AC_OUTPUT_TERMINAL_COMMUNICATION_SPEAKER 0x306 -#define USB_AC_OUTPUT_TERMINAL_LOW_FREQ_EFFECTS_SPEAKER 0x307 +/* Terminals - 2.3 Output Terminal Types */ +#define UAC_OUTPUT_TERMINAL_UNDEFINED 0x300 +#define UAC_OUTPUT_TERMINAL_SPEAKER 0x301 +#define UAC_OUTPUT_TERMINAL_HEADPHONES 0x302 +#define UAC_OUTPUT_TERMINAL_HEAD_MOUNTED_DISPLAY_AUDIO 0x303 +#define UAC_OUTPUT_TERMINAL_DESKTOP_SPEAKER 0x304 +#define UAC_OUTPUT_TERMINAL_ROOM_SPEAKER 0x305 +#define UAC_OUTPUT_TERMINAL_COMMUNICATION_SPEAKER 0x306 +#define UAC_OUTPUT_TERMINAL_LOW_FREQ_EFFECTS_SPEAKER 0x307 /* Set bControlSize = 2 as default setting */ -#define USB_DT_AC_FEATURE_UNIT_SIZE(ch) (7 + ((ch) + 1) * 2) +#define UAC_DT_FEATURE_UNIT_SIZE(ch) (7 + ((ch) + 1) * 2) /* As above, but more useful for defining your own descriptors: */ -#define DECLARE_USB_AC_FEATURE_UNIT_DESCRIPTOR(ch) \ -struct usb_ac_feature_unit_descriptor_##ch { \ +#define DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(ch) \ +struct uac_feature_unit_descriptor_##ch { \ __u8 bLength; \ __u8 bDescriptorType; \ __u8 bDescriptorSubtype; \ @@ -172,7 +171,7 @@ struct usb_ac_feature_unit_descriptor_##ch { \ } __attribute__ ((packed)) /* 4.5.2 Class-Specific AS Interface Descriptor */ -struct usb_as_header_descriptor { +struct uac_as_header_descriptor { __u8 bLength; /* in bytes: 7 */ __u8 bDescriptorType; /* USB_DT_CS_INTERFACE */ __u8 bDescriptorSubtype; /* AS_GENERAL */ @@ -181,16 +180,17 @@ struct usb_as_header_descriptor { __le16 wFormatTag; /* The Audio Data Format */ } __attribute__ ((packed)); -#define USB_DT_AS_HEADER_SIZE 7 +#define UAC_DT_AS_HEADER_SIZE 7 -#define USB_AS_AUDIO_FORMAT_TYPE_I_UNDEFINED 0x0 -#define USB_AS_AUDIO_FORMAT_TYPE_I_PCM 0x1 -#define USB_AS_AUDIO_FORMAT_TYPE_I_PCM8 0x2 -#define USB_AS_AUDIO_FORMAT_TYPE_I_IEEE_FLOAT 0x3 -#define USB_AS_AUDIO_FORMAT_TYPE_I_ALAW 0x4 -#define USB_AS_AUDIO_FORMAT_TYPE_I_MULAW 0x5 +/* Formats - A.1.1 Audio Data Format Type I Codes */ +#define UAC_FORMAT_TYPE_I_UNDEFINED 0x0 +#define UAC_FORMAT_TYPE_I_PCM 0x1 +#define UAC_FORMAT_TYPE_I_PCM8 0x2 +#define UAC_FORMAT_TYPE_I_IEEE_FLOAT 0x3 +#define UAC_FORMAT_TYPE_I_ALAW 0x4 +#define UAC_FORMAT_TYPE_I_MULAW 0x5 -struct usb_as_format_type_i_continuous_descriptor { +struct uac_format_type_i_continuous_descriptor { __u8 bLength; /* in bytes: 8 + (ns * 3) */ __u8 bDescriptorType; /* USB_DT_CS_INTERFACE */ __u8 bDescriptorSubtype; /* FORMAT_TYPE */ @@ -203,9 +203,9 @@ struct usb_as_format_type_i_continuous_descriptor { __u8 tUpperSamFreq[3]; } __attribute__ ((packed)); -#define USB_AS_FORMAT_TYPE_I_CONTINUOUS_DESC_SIZE 14 +#define UAC_FORMAT_TYPE_I_CONTINUOUS_DESC_SIZE 14 -struct usb_as_formate_type_i_discrete_descriptor { +struct uac_format_type_i_discrete_descriptor { __u8 bLength; /* in bytes: 8 + (ns * 3) */ __u8 bDescriptorType; /* USB_DT_CS_INTERFACE */ __u8 bDescriptorSubtype; /* FORMAT_TYPE */ @@ -217,8 +217,8 @@ struct usb_as_formate_type_i_discrete_descriptor { __u8 tSamFreq[][3]; } __attribute__ ((packed)); -#define DECLARE_USB_AS_FORMAT_TYPE_I_DISCRETE_DESC(n) \ -struct usb_as_formate_type_i_discrete_descriptor_##n { \ +#define DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(n) \ +struct uac_format_type_i_discrete_descriptor_##n { \ __u8 bLength; \ __u8 bDescriptorType; \ __u8 bDescriptorSubtype; \ @@ -230,14 +230,15 @@ struct usb_as_formate_type_i_discrete_descriptor_##n { \ __u8 tSamFreq[n][3]; \ } __attribute__ ((packed)) -#define USB_AS_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(n) (8 + (n * 3)) +#define UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(n) (8 + (n * 3)) -#define USB_AS_FORMAT_TYPE_UNDEFINED 0x0 -#define USB_AS_FORMAT_TYPE_I 0x1 -#define USB_AS_FORMAT_TYPE_II 0x2 -#define USB_AS_FORMAT_TYPE_III 0x3 +/* Formats - A.2 Format Type Codes */ +#define UAC_FORMAT_TYPE_UNDEFINED 0x0 +#define UAC_FORMAT_TYPE_I 0x1 +#define UAC_FORMAT_TYPE_II 0x2 +#define UAC_FORMAT_TYPE_III 0x3 -struct usb_as_iso_endpoint_descriptor { +struct uac_iso_endpoint_descriptor { __u8 bLength; /* in bytes: 7 */ __u8 bDescriptorType; /* USB_DT_CS_ENDPOINT */ __u8 bDescriptorSubtype; /* EP_GENERAL */ @@ -245,30 +246,35 @@ struct usb_as_iso_endpoint_descriptor { __u8 bLockDelayUnits; __le16 wLockDelay; }; -#define USB_AS_ISO_ENDPOINT_DESC_SIZE 7 - -#define FU_CONTROL_UNDEFINED 0x00 -#define MUTE_CONTROL 0x01 -#define VOLUME_CONTROL 0x02 -#define BASS_CONTROL 0x03 -#define MID_CONTROL 0x04 -#define TREBLE_CONTROL 0x05 -#define GRAPHIC_EQUALIZER_CONTROL 0x06 -#define AUTOMATIC_GAIN_CONTROL 0x07 -#define DELAY_CONTROL 0x08 -#define BASS_BOOST_CONTROL 0x09 -#define LOUDNESS_CONTROL 0x0a - -#define FU_MUTE (1 << (MUTE_CONTROL - 1)) -#define FU_VOLUME (1 << (VOLUME_CONTROL - 1)) -#define FU_BASS (1 << (BASS_CONTROL - 1)) -#define FU_MID (1 << (MID_CONTROL - 1)) -#define FU_TREBLE (1 << (TREBLE_CONTROL - 1)) -#define FU_GRAPHIC_EQ (1 << (GRAPHIC_EQUALIZER_CONTROL - 1)) -#define FU_AUTO_GAIN (1 << (AUTOMATIC_GAIN_CONTROL - 1)) -#define FU_DELAY (1 << (DELAY_CONTROL - 1)) -#define FU_BASS_BOOST (1 << (BASS_BOOST_CONTROL - 1)) -#define FU_LOUDNESS (1 << (LOUDNESS_CONTROL - 1)) +#define UAC_ISO_ENDPOINT_DESC_SIZE 7 + +#define UAC_EP_CS_ATTR_SAMPLE_RATE 0x01 +#define UAC_EP_CS_ATTR_PITCH_CONTROL 0x02 +#define UAC_EP_CS_ATTR_FILL_MAX 0x80 + +/* A.10.2 Feature Unit Control Selectors */ +#define UAC_FU_CONTROL_UNDEFINED 0x00 +#define UAC_MUTE_CONTROL 0x01 +#define UAC_VOLUME_CONTROL 0x02 +#define UAC_BASS_CONTROL 0x03 +#define UAC_MID_CONTROL 0x04 +#define UAC_TREBLE_CONTROL 0x05 +#define UAC_GRAPHIC_EQUALIZER_CONTROL 0x06 +#define UAC_AUTOMATIC_GAIN_CONTROL 0x07 +#define UAC_DELAY_CONTROL 0x08 +#define UAC_BASS_BOOST_CONTROL 0x09 +#define UAC_LOUDNESS_CONTROL 0x0a + +#define UAC_FU_MUTE (1 << (UAC_MUTE_CONTROL - 1)) +#define UAC_FU_VOLUME (1 << (UAC_VOLUME_CONTROL - 1)) +#define UAC_FU_BASS (1 << (UAC_BASS_CONTROL - 1)) +#define UAC_FU_MID (1 << (UAC_MID_CONTROL - 1)) +#define UAC_FU_TREBLE (1 << (UAC_TREBLE_CONTROL - 1)) +#define UAC_FU_GRAPHIC_EQ (1 << (UAC_GRAPHIC_EQUALIZER_CONTROL - 1)) +#define UAC_FU_AUTO_GAIN (1 << (UAC_AUTOMATIC_GAIN_CONTROL - 1)) +#define UAC_FU_DELAY (1 << (UAC_DELAY_CONTROL - 1)) +#define UAC_FU_BASS_BOOST (1 << (UAC_BASS_BOOST_CONTROL - 1)) +#define UAC_FU_LOUDNESS (1 << (UAC_LOUDNESS_CONTROL - 1)) struct usb_audio_control { struct list_head list; -- cgit v1.2.3-71-gd317 From b95cd7ec3e93bae199e820bd65b21b23e4538acc Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 21 Jun 2009 23:21:55 +0200 Subject: USB audio gadget: Un-inline generic_[gs]et_cmd Those functions are used only used to fill the set/get members of usb_audio_control. It doesn't make much sense to inline them. Signed-off-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_audio.c | 15 +++++++++++++++ include/linux/usb/audio.h | 12 ------------ 2 files changed, 15 insertions(+), 12 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/gadget/f_audio.c b/drivers/usb/gadget/f_audio.c index 7b05b3c8c0b1..98e9bb977291 100644 --- a/drivers/usb/gadget/f_audio.c +++ b/drivers/usb/gadget/f_audio.c @@ -28,6 +28,9 @@ static int audio_buf_size = 48000; module_param(audio_buf_size, int, S_IRUGO); MODULE_PARM_DESC(audio_buf_size, "Audio buffer size"); +static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value); +static int generic_get_cmd(struct usb_audio_control *con, u8 cmd); + /* * DESCRIPTORS ... most are static, but strings and full * configuration descriptors are built on demand. @@ -632,6 +635,18 @@ f_audio_unbind(struct usb_configuration *c, struct usb_function *f) /*-------------------------------------------------------------------------*/ +static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value) +{ + con->data[cmd] = value; + + return 0; +} + +static int generic_get_cmd(struct usb_audio_control *con, u8 cmd) +{ + return con->data[cmd]; +} + /* Todo: add more control selecotor dynamically */ int __init control_selector_init(struct f_audio *audio) { diff --git a/include/linux/usb/audio.h b/include/linux/usb/audio.h index c3edfcb72c34..7b33c493917f 100644 --- a/include/linux/usb/audio.h +++ b/include/linux/usb/audio.h @@ -285,18 +285,6 @@ struct usb_audio_control { int (*get)(struct usb_audio_control *con, u8 cmd); }; -static inline int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value) -{ - con->data[cmd] = value; - - return 0; -} - -static inline int generic_get_cmd(struct usb_audio_control *con, u8 cmd) -{ - return con->data[cmd]; -} - struct usb_audio_control_selector { struct list_head list; struct list_head control; -- cgit v1.2.3-71-gd317 From 331ac6b288d9f3689514ced1878041fb0df7e13c Mon Sep 17 00:00:00 2001 From: Alek Du Date: Mon, 13 Jul 2009 12:41:20 +0800 Subject: USB: EHCI: Add Intel Moorestown EHCI controller HOSTPCx extensions and support phy low power mode The Intel Moorestown EHCI controller supports non-standard HOSTPCx register extension. This register controls the LPM behaviour and controls the behaviour of each USB port. Signed-off-by: Jacob Pan Signed-off-by: Alek Du Acked-by: Alan Stern Cc: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 6 +++++ drivers/usb/host/ehci-hub.c | 62 ++++++++++++++++++++++++++++++++++++++++---- drivers/usb/host/ehci.h | 3 ++- include/linux/usb/ehci_def.h | 13 ++++++++++ 4 files changed, 78 insertions(+), 6 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 7bee1638dfd7..5fbc67c5a913 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -249,6 +249,12 @@ static int ehci_reset (struct ehci_hcd *ehci) retval = handshake (ehci, &ehci->regs->command, CMD_RESET, 0, 250 * 1000); + if (ehci->has_hostpc) { + ehci_writel(ehci, USBMODE_EX_HC | USBMODE_EX_VBPS, + (u32 __iomem *)(((u8 *)ehci->regs) + USBMODE_EX)); + ehci_writel(ehci, TXFIFO_DEFAULT, + (u32 __iomem *)(((u8 *)ehci->regs) + TXFILLTUNING)); + } if (retval) return retval; diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index f46ad27c9a90..6fef1ee7d101 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -111,6 +111,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) struct ehci_hcd *ehci = hcd_to_ehci (hcd); int port; int mask; + u32 __iomem *hostpc_reg = NULL; ehci_dbg(ehci, "suspend root hub\n"); @@ -142,6 +143,9 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; u32 t2 = t1; + if (ehci->has_hostpc) + hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs + + HOSTPC0 + 4 * (port & 0xff)); /* keep track of which ports we suspend */ if (t1 & PORT_OWNER) set_bit(port, &ehci->owned_ports); @@ -151,15 +155,37 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) } /* enable remote wakeup on all ports */ - if (hcd->self.root_hub->do_remote_wakeup) - t2 |= PORT_WAKE_BITS; - else + if (hcd->self.root_hub->do_remote_wakeup) { + /* only enable appropriate wake bits, otherwise the + * hardware can not go phy low power mode. If a race + * condition happens here(connection change during bits + * set), the port change detection will finally fix it. + */ + if (t1 & PORT_CONNECT) { + t2 |= PORT_WKOC_E | PORT_WKDISC_E; + t2 &= ~PORT_WKCONN_E; + } else { + t2 |= PORT_WKOC_E | PORT_WKCONN_E; + t2 &= ~PORT_WKDISC_E; + } + } else t2 &= ~PORT_WAKE_BITS; if (t1 != t2) { ehci_vdbg (ehci, "port %d, %08x -> %08x\n", port + 1, t1, t2); ehci_writel(ehci, t2, reg); + if (hostpc_reg) { + u32 t3; + + msleep(5);/* 5ms for HCD enter low pwr mode */ + t3 = ehci_readl(ehci, hostpc_reg); + ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg); + t3 = ehci_readl(ehci, hostpc_reg); + ehci_dbg(ehci, "Port%d phy low pwr mode %s\n", + port, (t3 & HOSTPC_PHCD) ? + "succeeded" : "failed"); + } } } @@ -563,7 +589,8 @@ static int ehci_hub_control ( int ports = HCS_N_PORTS (ehci->hcs_params); u32 __iomem *status_reg = &ehci->regs->port_status[ (wIndex & 0xff) - 1]; - u32 temp, status; + u32 __iomem *hostpc_reg = NULL; + u32 temp, temp1, status; unsigned long flags; int retval = 0; unsigned selector; @@ -575,6 +602,9 @@ static int ehci_hub_control ( * power, "this is the one", etc. EHCI spec supports this. */ + if (ehci->has_hostpc) + hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs + + HOSTPC0 + 4 * ((wIndex & 0xff) - 1)); spin_lock_irqsave (&ehci->lock, flags); switch (typeReq) { case ClearHubFeature: @@ -773,7 +803,11 @@ static int ehci_hub_control ( if (temp & PORT_CONNECT) { status |= 1 << USB_PORT_FEAT_CONNECTION; // status may be from integrated TT - status |= ehci_port_speed(ehci, temp); + if (ehci->has_hostpc) { + temp1 = ehci_readl(ehci, hostpc_reg); + status |= ehci_port_speed(ehci, temp1); + } else + status |= ehci_port_speed(ehci, temp); } if (temp & PORT_PE) status |= 1 << USB_PORT_FEAT_ENABLE; @@ -832,6 +866,24 @@ static int ehci_hub_control ( || (temp & PORT_RESET) != 0) goto error; ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); + /* After above check the port must be connected. + * Set appropriate bit thus could put phy into low power + * mode if we have hostpc feature + */ + if (hostpc_reg) { + temp &= ~PORT_WKCONN_E; + temp |= (PORT_WKDISC_E | PORT_WKOC_E); + ehci_writel(ehci, temp | PORT_SUSPEND, + status_reg); + msleep(5);/* 5ms for HCD enter low pwr mode */ + temp1 = ehci_readl(ehci, hostpc_reg); + ehci_writel(ehci, temp1 | HOSTPC_PHCD, + hostpc_reg); + temp1 = ehci_readl(ehci, hostpc_reg); + ehci_dbg(ehci, "Port%d phy low pwr mode %s\n", + wIndex, (temp1 & HOSTPC_PHCD) ? + "succeeded" : "failed"); + } set_bit(wIndex, &ehci->suspended_ports); break; case USB_PORT_FEAT_POWER: diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index fa12f20fbfe2..ec3dba6b8e48 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -136,6 +136,7 @@ struct ehci_hcd { /* one per controller */ #define OHCI_HCCTRL_OFFSET 0x4 #define OHCI_HCCTRL_LEN 0x4 __hc32 *ohci_hcctrl_reg; + unsigned has_hostpc:1; u8 sbrn; /* packed release number */ @@ -548,7 +549,7 @@ static inline unsigned int ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) { if (ehci_is_TDI(ehci)) { - switch ((portsc>>26)&3) { + switch ((portsc >> (ehci->has_hostpc ? 25 : 26)) & 3) { case 0: return 0; case 1: diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h index 5b88e36c9103..4c4b701ff265 100644 --- a/include/linux/usb/ehci_def.h +++ b/include/linux/usb/ehci_def.h @@ -132,6 +132,19 @@ struct ehci_regs { #define USBMODE_CM_HC (3<<0) /* host controller mode */ #define USBMODE_CM_IDLE (0<<0) /* idle state */ +/* Moorestown has some non-standard registers, partially due to the fact that + * its EHCI controller has both TT and LPM support. HOSTPCx are extentions to + * PORTSCx + */ +#define HOSTPC0 0x84 /* HOSTPC extension */ +#define HOSTPC_PHCD (1<<22) /* Phy clock disable */ +#define HOSTPC_PSPD (3<<25) /* Port speed detection */ +#define USBMODE_EX 0xc8 /* USB Device mode extension */ +#define USBMODE_EX_VBPS (1<<5) /* VBus Power Select On */ +#define USBMODE_EX_HC (3<<0) /* host controller mode */ +#define TXFILLTUNING 0x24 /* TX FIFO Tuning register */ +#define TXFIFO_DEFAULT (8<<16) /* FIFO burst threshold 8 */ + /* Appendix C, Debug port ... intended for use with special "debug devices" * that can help if there's no serial console. (nonstandard enumeration.) */ -- cgit v1.2.3-71-gd317 From 9da69c604d87afea37b5411867bb76e3c624cc92 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Wed, 15 Jul 2009 23:22:54 -0400 Subject: USB: isp1760: allow platform devices to customize devflags Platform device support was merged earlier, but support for boards to customize the devflags aspect of the controller was not. We want this on Blackfin systems to control the bus width, but might as well expose all of the fields while we're at it. Signed-off-by: Michael Hennerich Signed-off-by: Bryan Wu Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1760-hcd.c | 4 ++++ drivers/usb/host/isp1760-hcd.h | 2 ++ drivers/usb/host/isp1760-if.c | 21 ++++++++++++++++++++- include/linux/usb/isp1760.h | 18 ++++++++++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 include/linux/usb/isp1760.h (limited to 'include/linux/usb') diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 15438469f21a..9600a58299db 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -386,6 +386,10 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) hwmode |= HW_DACK_POL_HIGH; if (priv->devflags & ISP1760_FLAG_DREQ_POL_HIGH) hwmode |= HW_DREQ_POL_HIGH; + if (priv->devflags & ISP1760_FLAG_INTR_POL_HIGH) + hwmode |= HW_INTR_HIGH_ACT; + if (priv->devflags & ISP1760_FLAG_INTR_EDGE_TRIG) + hwmode |= HW_INTR_EDGE_TRIG; /* * We have to set this first in case we're in 16-bit mode. diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index 462f4943cb1b..6931ef5c9650 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -142,6 +142,8 @@ typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh, #define ISP1760_FLAG_DACK_POL_HIGH 0x00000010 /* DACK active high */ #define ISP1760_FLAG_DREQ_POL_HIGH 0x00000020 /* DREQ active high */ #define ISP1760_FLAG_ISP1761 0x00000040 /* Chip is ISP1761 */ +#define ISP1760_FLAG_INTR_POL_HIGH 0x00000080 /* Interrupt polarity active high */ +#define ISP1760_FLAG_INTR_EDGE_TRIG 0x00000100 /* Interrupt edge triggered */ /* chip memory management */ struct memory_chunk { diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index d4feebfc63bd..1c9f977a5c9c 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -3,6 +3,7 @@ * Currently there is support for * - OpenFirmware * - PCI + * - PDEV (generic platform device centralized driver model) * * (c) 2007 Sebastian Siewior * @@ -11,6 +12,7 @@ #include #include #include +#include #include "../core/hcd.h" #include "isp1760-hcd.h" @@ -308,6 +310,8 @@ static int __devinit isp1760_plat_probe(struct platform_device *pdev) struct resource *mem_res; struct resource *irq_res; resource_size_t mem_size; + struct isp1760_platform_data *priv = pdev->dev.platform_data; + unsigned int devflags = 0; unsigned long irqflags = IRQF_SHARED | IRQF_DISABLED; mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -330,8 +334,23 @@ static int __devinit isp1760_plat_probe(struct platform_device *pdev) } irqflags |= irq_res->flags & IRQF_TRIGGER_MASK; + if (priv) { + if (priv->is_isp1761) + devflags |= ISP1760_FLAG_ISP1761; + if (priv->bus_width_16) + devflags |= ISP1760_FLAG_BUS_WIDTH_16; + if (priv->port1_otg) + devflags |= ISP1760_FLAG_OTG_EN; + if (priv->analog_oc) + devflags |= ISP1760_FLAG_ANALOG_OC; + if (priv->dack_polarity_high) + devflags |= ISP1760_FLAG_DACK_POL_HIGH; + if (priv->dreq_polarity_high) + devflags |= ISP1760_FLAG_DREQ_POL_HIGH; + } + hcd = isp1760_register(mem_res->start, mem_size, irq_res->start, - irqflags, &pdev->dev, dev_name(&pdev->dev), 0); + irqflags, &pdev->dev, dev_name(&pdev->dev), devflags); if (IS_ERR(hcd)) { pr_warning("isp1760: Failed to register the HCD device\n"); ret = -ENODEV; diff --git a/include/linux/usb/isp1760.h b/include/linux/usb/isp1760.h new file mode 100644 index 000000000000..de7de53c5531 --- /dev/null +++ b/include/linux/usb/isp1760.h @@ -0,0 +1,18 @@ +/* + * board initialization should put one of these into dev->platform_data + * and place the isp1760 onto platform_bus named "isp1760-hcd". + */ + +#ifndef __LINUX_USB_ISP1760_H +#define __LINUX_USB_ISP1760_H + +struct isp1760_platform_data { + unsigned is_isp1761:1; /* Chip is ISP1761 */ + unsigned bus_width_16:1; /* 16/32-bit data bus width */ + unsigned port1_otg:1; /* Port 1 supports OTG */ + unsigned analog_oc:1; /* Analog overcurrent */ + unsigned dack_polarity_high:1; /* DACK active high */ + unsigned dreq_polarity_high:1; /* DREQ active high */ +}; + +#endif /* __LINUX_USB_ISP1760_H */ -- cgit v1.2.3-71-gd317 From a9d43091c5be1e7a60d5abe84be4f3050236b26a Mon Sep 17 00:00:00 2001 From: Lothar Wassmann Date: Thu, 16 Jul 2009 20:51:21 -0400 Subject: USB: NXP ISP1362 USB host driver Signed-off-by: Lothar Wassmann Signed-off-by: Michael Hennerich Signed-off-by: Bryan Wu Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Makefile | 1 + drivers/usb/host/Kconfig | 12 + drivers/usb/host/Makefile | 1 + drivers/usb/host/isp1362-hcd.c | 2905 ++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/isp1362.h | 1079 +++++++++++++++ include/linux/usb/isp1362.h | 46 + 6 files changed, 4044 insertions(+) create mode 100644 drivers/usb/host/isp1362-hcd.c create mode 100644 drivers/usb/host/isp1362.h create mode 100644 include/linux/usb/isp1362.h (limited to 'include/linux/usb') diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 19cb7d5480d7..dbe4fb694ad2 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_USB_UHCI_HCD) += host/ obj-$(CONFIG_USB_FHCI_HCD) += host/ obj-$(CONFIG_USB_XHCI_HCD) += host/ obj-$(CONFIG_USB_SL811_HCD) += host/ +obj-$(CONFIG_USB_ISP1362_HCD) += host/ obj-$(CONFIG_USB_U132_HCD) += host/ obj-$(CONFIG_USB_R8A66597_HCD) += host/ obj-$(CONFIG_USB_HWA_HCD) += host/ diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 41b8d7de2e07..9b43b226817f 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -159,6 +159,18 @@ config USB_ISP1760_HCD To compile this driver as a module, choose M here: the module will be called isp1760. +config USB_ISP1362_HCD + tristate "ISP1362 HCD support" + depends on USB + default N + ---help--- + Supports the Philips ISP1362 chip as a host controller + + This driver does not support isochronous transfers. + + To compile this driver as a module, choose M here: the + module will be called isp1362-hcd. + config USB_OHCI_HCD tristate "OHCI HCD support" depends on USB && USB_ARCH_HAS_OHCI diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 289d748bb414..f58b2494c44a 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_PCI) += pci-quirks.o obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o +obj-$(CONFIG_USB_ISP1362_HCD) += isp1362-hcd.o obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o obj-$(CONFIG_USB_FHCI_HCD) += fhci.o diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c new file mode 100644 index 000000000000..5819e10a146c --- /dev/null +++ b/drivers/usb/host/isp1362-hcd.c @@ -0,0 +1,2905 @@ +/* + * ISP1362 HCD (Host Controller Driver) for USB. + * + * Copyright (C) 2005 Lothar Wassmann + * + * Derived from the SL811 HCD, rewritten for ISP116x. + * Copyright (C) 2005 Olav Kongas + * + * Portions: + * Copyright (C) 2004 Psion Teklogix (for NetBook PRO) + * Copyright (C) 2004 David Brownell + */ + +/* + * The ISP1362 chip requires a large delay (300ns and 462ns) between + * accesses to the address and data register. + * The following timing options exist: + * + * 1. Configure your memory controller to add such delays if it can (the best) + * 2. Implement platform-specific delay function possibly + * combined with configuring the memory controller; see + * include/linux/usb_isp1362.h for more info. + * 3. Use ndelay (easiest, poorest). + * + * Use the corresponding macros USE_PLATFORM_DELAY and USE_NDELAY in the + * platform specific section of isp1362.h to select the appropriate variant. + * + * Also note that according to the Philips "ISP1362 Errata" document + * Rev 1.00 from 27 May data corruption may occur when the #WR signal + * is reasserted (even with #CS deasserted) within 132ns after a + * write cycle to any controller register. If the hardware doesn't + * implement the recommended fix (gating the #WR with #CS) software + * must ensure that no further write cycle (not necessarily to the chip!) + * is issued by the CPU within this interval. + + * For PXA25x this can be ensured by using VLIO with the maximum + * recovery time (MSCx = 0x7f8c) with a memory clock of 99.53 MHz. + */ + +#ifdef CONFIG_USB_DEBUG +# define ISP1362_DEBUG +#else +# undef ISP1362_DEBUG +#endif + +/* + * The PXA255 UDC apparently doesn't handle GET_STATUS, GET_CONFIG and + * GET_INTERFACE requests correctly when the SETUP and DATA stages of the + * requests are carried out in separate frames. This will delay any SETUP + * packets until the start of the next frame so that this situation is + * unlikely to occur (and makes usbtest happy running with a PXA255 target + * device). + */ +#undef BUGGY_PXA2XX_UDC_USBTEST + +#undef PTD_TRACE +#undef URB_TRACE +#undef VERBOSE +#undef REGISTERS + +/* This enables a memory test on the ISP1362 chip memory to make sure the + * chip access timing is correct. + */ +#undef CHIP_BUFFER_TEST + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static int dbg_level; +#ifdef ISP1362_DEBUG +module_param(dbg_level, int, 0644); +#else +module_param(dbg_level, int, 0); +#define STUB_DEBUG_FILE +#endif + +#include "../core/hcd.h" +#include "../core/usb.h" +#include "isp1362.h" + + +#define DRIVER_VERSION "2005-04-04" +#define DRIVER_DESC "ISP1362 USB Host Controller Driver" + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +static const char hcd_name[] = "isp1362-hcd"; + +static void isp1362_hc_stop(struct usb_hcd *hcd); +static int isp1362_hc_start(struct usb_hcd *hcd); + +/*-------------------------------------------------------------------------*/ + +/* + * When called from the interrupthandler only isp1362_hcd->irqenb is modified, + * since the interrupt handler will write isp1362_hcd->irqenb to HCuPINT upon + * completion. + * We don't need a 'disable' counterpart, since interrupts will be disabled + * only by the interrupt handler. + */ +static inline void isp1362_enable_int(struct isp1362_hcd *isp1362_hcd, u16 mask) +{ + if ((isp1362_hcd->irqenb | mask) == isp1362_hcd->irqenb) + return; + if (mask & ~isp1362_hcd->irqenb) + isp1362_write_reg16(isp1362_hcd, HCuPINT, mask & ~isp1362_hcd->irqenb); + isp1362_hcd->irqenb |= mask; + if (isp1362_hcd->irq_active) + return; + isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb); +} + +/*-------------------------------------------------------------------------*/ + +static inline struct isp1362_ep_queue *get_ptd_queue(struct isp1362_hcd *isp1362_hcd, + u16 offset) +{ + struct isp1362_ep_queue *epq = NULL; + + if (offset < isp1362_hcd->istl_queue[1].buf_start) + epq = &isp1362_hcd->istl_queue[0]; + else if (offset < isp1362_hcd->intl_queue.buf_start) + epq = &isp1362_hcd->istl_queue[1]; + else if (offset < isp1362_hcd->atl_queue.buf_start) + epq = &isp1362_hcd->intl_queue; + else if (offset < isp1362_hcd->atl_queue.buf_start + + isp1362_hcd->atl_queue.buf_size) + epq = &isp1362_hcd->atl_queue; + + if (epq) + DBG(1, "%s: PTD $%04x is on %s queue\n", __func__, offset, epq->name); + else + pr_warning("%s: invalid PTD $%04x\n", __func__, offset); + + return epq; +} + +static inline int get_ptd_offset(struct isp1362_ep_queue *epq, u8 index) +{ + int offset; + + if (index * epq->blk_size > epq->buf_size) { + pr_warning("%s: Bad %s index %d(%d)\n", __func__, epq->name, index, + epq->buf_size / epq->blk_size); + return -EINVAL; + } + offset = epq->buf_start + index * epq->blk_size; + DBG(3, "%s: %s PTD[%02x] # %04x\n", __func__, epq->name, index, offset); + + return offset; +} + +/*-------------------------------------------------------------------------*/ + +static inline u16 max_transfer_size(struct isp1362_ep_queue *epq, size_t size, + int mps) +{ + u16 xfer_size = min_t(size_t, MAX_XFER_SIZE, size); + + xfer_size = min_t(size_t, xfer_size, epq->buf_avail * epq->blk_size - PTD_HEADER_SIZE); + if (xfer_size < size && xfer_size % mps) + xfer_size -= xfer_size % mps; + + return xfer_size; +} + +static int claim_ptd_buffers(struct isp1362_ep_queue *epq, + struct isp1362_ep *ep, u16 len) +{ + int ptd_offset = -EINVAL; + int index; + int num_ptds = ((len + PTD_HEADER_SIZE - 1) / epq->blk_size) + 1; + int found = -1; + int last = -1; + + BUG_ON(len > epq->buf_size); + + if (!epq->buf_avail) + return -ENOMEM; + + if (ep->num_ptds) + pr_err("%s: %s len %d/%d num_ptds %d buf_map %08lx skip_map %08lx\n", __func__, + epq->name, len, epq->blk_size, num_ptds, epq->buf_map, epq->skip_map); + BUG_ON(ep->num_ptds != 0); + + for (index = 0; index <= epq->buf_count - num_ptds; index++) { + if (test_bit(index, &epq->buf_map)) + continue; + found = index; + for (last = index + 1; last < index + num_ptds; last++) { + if (test_bit(last, &epq->buf_map)) { + found = -1; + break; + } + } + if (found >= 0) + break; + } + if (found < 0) + return -EOVERFLOW; + + DBG(1, "%s: Found %d PTDs[%d] for %d/%d byte\n", __func__, + num_ptds, found, len, (int)(epq->blk_size - PTD_HEADER_SIZE)); + ptd_offset = get_ptd_offset(epq, found); + WARN_ON(ptd_offset < 0); + ep->ptd_offset = ptd_offset; + ep->num_ptds += num_ptds; + epq->buf_avail -= num_ptds; + BUG_ON(epq->buf_avail > epq->buf_count); + ep->ptd_index = found; + for (index = found; index < last; index++) + __set_bit(index, &epq->buf_map); + DBG(1, "%s: Done %s PTD[%d] $%04x, avail %d count %d claimed %d %08lx:%08lx\n", + __func__, epq->name, ep->ptd_index, ep->ptd_offset, + epq->buf_avail, epq->buf_count, num_ptds, epq->buf_map, epq->skip_map); + + return found; +} + +static inline void release_ptd_buffers(struct isp1362_ep_queue *epq, struct isp1362_ep *ep) +{ + int index = ep->ptd_index; + int last = ep->ptd_index + ep->num_ptds; + + if (last > epq->buf_count) + pr_err("%s: ep %p req %d len %d %s PTD[%d] $%04x num_ptds %d buf_count %d buf_avail %d buf_map %08lx skip_map %08lx\n", + __func__, ep, ep->num_req, ep->length, epq->name, ep->ptd_index, + ep->ptd_offset, ep->num_ptds, epq->buf_count, epq->buf_avail, + epq->buf_map, epq->skip_map); + BUG_ON(last > epq->buf_count); + + for (; index < last; index++) { + __clear_bit(index, &epq->buf_map); + __set_bit(index, &epq->skip_map); + } + epq->buf_avail += ep->num_ptds; + epq->ptd_count--; + + BUG_ON(epq->buf_avail > epq->buf_count); + BUG_ON(epq->ptd_count > epq->buf_count); + + DBG(1, "%s: Done %s PTDs $%04x released %d avail %d count %d\n", + __func__, epq->name, + ep->ptd_offset, ep->num_ptds, epq->buf_avail, epq->buf_count); + DBG(1, "%s: buf_map %08lx skip_map %08lx\n", __func__, + epq->buf_map, epq->skip_map); + + ep->num_ptds = 0; + ep->ptd_offset = -EINVAL; + ep->ptd_index = -EINVAL; +} + +/*-------------------------------------------------------------------------*/ + +/* + Set up PTD's. +*/ +static void prepare_ptd(struct isp1362_hcd *isp1362_hcd, struct urb *urb, + struct isp1362_ep *ep, struct isp1362_ep_queue *epq, + u16 fno) +{ + struct ptd *ptd; + int toggle; + int dir; + u16 len; + size_t buf_len = urb->transfer_buffer_length - urb->actual_length; + + DBG(3, "%s: %s ep %p\n", __func__, epq->name, ep); + + ptd = &ep->ptd; + + ep->data = (unsigned char *)urb->transfer_buffer + urb->actual_length; + + switch (ep->nextpid) { + case USB_PID_IN: + toggle = usb_gettoggle(urb->dev, ep->epnum, 0); + dir = PTD_DIR_IN; + if (usb_pipecontrol(urb->pipe)) { + len = min_t(size_t, ep->maxpacket, buf_len); + } else if (usb_pipeisoc(urb->pipe)) { + len = min_t(size_t, urb->iso_frame_desc[fno].length, MAX_XFER_SIZE); + ep->data = urb->transfer_buffer + urb->iso_frame_desc[fno].offset; + } else + len = max_transfer_size(epq, buf_len, ep->maxpacket); + DBG(1, "%s: IN len %d/%d/%d from URB\n", __func__, len, ep->maxpacket, + (int)buf_len); + break; + case USB_PID_OUT: + toggle = usb_gettoggle(urb->dev, ep->epnum, 1); + dir = PTD_DIR_OUT; + if (usb_pipecontrol(urb->pipe)) + len = min_t(size_t, ep->maxpacket, buf_len); + else if (usb_pipeisoc(urb->pipe)) + len = min_t(size_t, urb->iso_frame_desc[0].length, MAX_XFER_SIZE); + else + len = max_transfer_size(epq, buf_len, ep->maxpacket); + if (len == 0) + pr_info("%s: Sending ZERO packet: %d\n", __func__, + urb->transfer_flags & URB_ZERO_PACKET); + DBG(1, "%s: OUT len %d/%d/%d from URB\n", __func__, len, ep->maxpacket, + (int)buf_len); + break; + case USB_PID_SETUP: + toggle = 0; + dir = PTD_DIR_SETUP; + len = sizeof(struct usb_ctrlrequest); + DBG(1, "%s: SETUP len %d\n", __func__, len); + ep->data = urb->setup_packet; + break; + case USB_PID_ACK: + toggle = 1; + len = 0; + dir = (urb->transfer_buffer_length && usb_pipein(urb->pipe)) ? + PTD_DIR_OUT : PTD_DIR_IN; + DBG(1, "%s: ACK len %d\n", __func__, len); + break; + default: + toggle = dir = len = 0; + pr_err("%s@%d: ep->nextpid %02x\n", __func__, __LINE__, ep->nextpid); + BUG_ON(1); + } + + ep->length = len; + if (!len) + ep->data = NULL; + + ptd->count = PTD_CC_MSK | PTD_ACTIVE_MSK | PTD_TOGGLE(toggle); + ptd->mps = PTD_MPS(ep->maxpacket) | PTD_SPD(urb->dev->speed == USB_SPEED_LOW) | + PTD_EP(ep->epnum); + ptd->len = PTD_LEN(len) | PTD_DIR(dir); + ptd->faddr = PTD_FA(usb_pipedevice(urb->pipe)); + + if (usb_pipeint(urb->pipe)) { + ptd->faddr |= PTD_SF_INT(ep->branch); + ptd->faddr |= PTD_PR(ep->interval ? __ffs(ep->interval) : 0); + } + if (usb_pipeisoc(urb->pipe)) + ptd->faddr |= PTD_SF_ISO(fno); + + DBG(1, "%s: Finished\n", __func__); +} + +static void isp1362_write_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep, + struct isp1362_ep_queue *epq) +{ + struct ptd *ptd = &ep->ptd; + int len = PTD_GET_DIR(ptd) == PTD_DIR_IN ? 0 : ep->length; + + _BUG_ON(ep->ptd_offset < 0); + + prefetch(ptd); + isp1362_write_buffer(isp1362_hcd, ptd, ep->ptd_offset, PTD_HEADER_SIZE); + if (len) + isp1362_write_buffer(isp1362_hcd, ep->data, + ep->ptd_offset + PTD_HEADER_SIZE, len); + + dump_ptd(ptd); + dump_ptd_out_data(ptd, ep->data); +} + +static void isp1362_read_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep, + struct isp1362_ep_queue *epq) +{ + struct ptd *ptd = &ep->ptd; + int act_len; + + WARN_ON(list_empty(&ep->active)); + BUG_ON(ep->ptd_offset < 0); + + list_del_init(&ep->active); + DBG(1, "%s: ep %p removed from active list %p\n", __func__, ep, &epq->active); + + prefetchw(ptd); + isp1362_read_buffer(isp1362_hcd, ptd, ep->ptd_offset, PTD_HEADER_SIZE); + dump_ptd(ptd); + act_len = PTD_GET_COUNT(ptd); + if (PTD_GET_DIR(ptd) != PTD_DIR_IN || act_len == 0) + return; + if (act_len > ep->length) + pr_err("%s: ep %p PTD $%04x act_len %d ep->length %d\n", __func__, ep, + ep->ptd_offset, act_len, ep->length); + BUG_ON(act_len > ep->length); + /* Only transfer the amount of data that has actually been overwritten + * in the chip buffer. We don't want any data that doesn't belong to the + * transfer to leak out of the chip to the callers transfer buffer! + */ + prefetchw(ep->data); + isp1362_read_buffer(isp1362_hcd, ep->data, + ep->ptd_offset + PTD_HEADER_SIZE, act_len); + dump_ptd_in_data(ptd, ep->data); +} + +/* + * INT PTDs will stay in the chip until data is available. + * This function will remove a PTD from the chip when the URB is dequeued. + * Must be called with the spinlock held and IRQs disabled + */ +static void remove_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep) + +{ + int index; + struct isp1362_ep_queue *epq; + + DBG(1, "%s: ep %p PTD[%d] $%04x\n", __func__, ep, ep->ptd_index, ep->ptd_offset); + BUG_ON(ep->ptd_offset < 0); + + epq = get_ptd_queue(isp1362_hcd, ep->ptd_offset); + BUG_ON(!epq); + + /* put ep in remove_list for cleanup */ + WARN_ON(!list_empty(&ep->remove_list)); + list_add_tail(&ep->remove_list, &isp1362_hcd->remove_list); + /* let SOF interrupt handle the cleanup */ + isp1362_enable_int(isp1362_hcd, HCuPINT_SOF); + + index = ep->ptd_index; + if (index < 0) + /* ISO queues don't have SKIP registers */ + return; + + DBG(1, "%s: Disabling PTD[%02x] $%04x %08lx|%08x\n", __func__, + index, ep->ptd_offset, epq->skip_map, 1 << index); + + /* prevent further processing of PTD (will be effective after next SOF) */ + epq->skip_map |= 1 << index; + if (epq == &isp1362_hcd->atl_queue) { + DBG(2, "%s: ATLSKIP = %08x -> %08lx\n", __func__, + isp1362_read_reg32(isp1362_hcd, HCATLSKIP), epq->skip_map); + isp1362_write_reg32(isp1362_hcd, HCATLSKIP, epq->skip_map); + if (~epq->skip_map == 0) + isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE); + } else if (epq == &isp1362_hcd->intl_queue) { + DBG(2, "%s: INTLSKIP = %08x -> %08lx\n", __func__, + isp1362_read_reg32(isp1362_hcd, HCINTLSKIP), epq->skip_map); + isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, epq->skip_map); + if (~epq->skip_map == 0) + isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE); + } +} + +/* + Take done or failed requests out of schedule. Give back + processed urbs. +*/ +static void finish_request(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep, + struct urb *urb, int status) + __releases(isp1362_hcd->lock) + __acquires(isp1362_hcd->lock) +{ + urb->hcpriv = NULL; + ep->error_count = 0; + + if (usb_pipecontrol(urb->pipe)) + ep->nextpid = USB_PID_SETUP; + + URB_DBG("%s: req %d FA %d ep%d%s %s: len %d/%d %s stat %d\n", __func__, + ep->num_req, usb_pipedevice(urb->pipe), + usb_pipeendpoint(urb->pipe), + !usb_pipein(urb->pipe) ? "out" : "in", + usb_pipecontrol(urb->pipe) ? "ctrl" : + usb_pipeint(urb->pipe) ? "int" : + usb_pipebulk(urb->pipe) ? "bulk" : + "iso", + urb->actual_length, urb->transfer_buffer_length, + !(urb->transfer_flags & URB_SHORT_NOT_OK) ? + "short_ok" : "", urb->status); + + + usb_hcd_unlink_urb_from_ep(isp1362_hcd_to_hcd(isp1362_hcd), urb); + spin_unlock(&isp1362_hcd->lock); + usb_hcd_giveback_urb(isp1362_hcd_to_hcd(isp1362_hcd), urb, status); + spin_lock(&isp1362_hcd->lock); + + /* take idle endpoints out of the schedule right away */ + if (!list_empty(&ep->hep->urb_list)) + return; + + /* async deschedule */ + if (!list_empty(&ep->schedule)) { + list_del_init(&ep->schedule); + return; + } + + + if (ep->interval) { + /* periodic deschedule */ + DBG(1, "deschedule qh%d/%p branch %d load %d bandwidth %d -> %d\n", ep->interval, + ep, ep->branch, ep->load, + isp1362_hcd->load[ep->branch], + isp1362_hcd->load[ep->branch] - ep->load); + isp1362_hcd->load[ep->branch] -= ep->load; + ep->branch = PERIODIC_SIZE; + } +} + +/* + * Analyze transfer results, handle partial transfers and errors +*/ +static void postproc_ep(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep) +{ + struct urb *urb = get_urb(ep); + struct usb_device *udev; + struct ptd *ptd; + int short_ok; + u16 len; + int urbstat = -EINPROGRESS; + u8 cc; + + DBG(2, "%s: ep %p req %d\n", __func__, ep, ep->num_req); + + udev = urb->dev; + ptd = &ep->ptd; + cc = PTD_GET_CC(ptd); + if (cc == PTD_NOTACCESSED) { + pr_err("%s: req %d PTD %p Untouched by ISP1362\n", __func__, + ep->num_req, ptd); + cc = PTD_DEVNOTRESP; + } + + short_ok = !(urb->transfer_flags & URB_SHORT_NOT_OK); + len = urb->transfer_buffer_length - urb->actual_length; + + /* Data underrun is special. For allowed underrun + we clear the error and continue as normal. For + forbidden underrun we finish the DATA stage + immediately while for control transfer, + we do a STATUS stage. + */ + if (cc == PTD_DATAUNDERRUN) { + if (short_ok) { + DBG(1, "%s: req %d Allowed data underrun short_%sok %d/%d/%d byte\n", + __func__, ep->num_req, short_ok ? "" : "not_", + PTD_GET_COUNT(ptd), ep->maxpacket, len); + cc = PTD_CC_NOERROR; + urbstat = 0; + } else { + DBG(1, "%s: req %d Data Underrun %s nextpid %02x short_%sok %d/%d/%d byte\n", + __func__, ep->num_req, + usb_pipein(urb->pipe) ? "IN" : "OUT", ep->nextpid, + short_ok ? "" : "not_", + PTD_GET_COUNT(ptd), ep->maxpacket, len); + if (usb_pipecontrol(urb->pipe)) { + ep->nextpid = USB_PID_ACK; + /* save the data underrun error code for later and + * procede with the status stage + */ + urb->actual_length += PTD_GET_COUNT(ptd); + BUG_ON(urb->actual_length > urb->transfer_buffer_length); + + if (urb->status == -EINPROGRESS) + urb->status = cc_to_error[PTD_DATAUNDERRUN]; + } else { + usb_settoggle(udev, ep->epnum, ep->nextpid == USB_PID_OUT, + PTD_GET_TOGGLE(ptd)); + urbstat = cc_to_error[PTD_DATAUNDERRUN]; + } + goto out; + } + } + + if (cc != PTD_CC_NOERROR) { + if (++ep->error_count >= 3 || cc == PTD_CC_STALL || cc == PTD_DATAOVERRUN) { + urbstat = cc_to_error[cc]; + DBG(1, "%s: req %d nextpid %02x, status %d, error %d, error_count %d\n", + __func__, ep->num_req, ep->nextpid, urbstat, cc, + ep->error_count); + } + goto out; + } + + switch (ep->nextpid) { + case USB_PID_OUT: + if (PTD_GET_COUNT(ptd) != ep->length) + pr_err("%s: count=%d len=%d\n", __func__, + PTD_GET_COUNT(ptd), ep->length); + BUG_ON(PTD_GET_COUNT(ptd) != ep->length); + urb->actual_length += ep->length; + BUG_ON(urb->actual_length > urb->transfer_buffer_length); + usb_settoggle(udev, ep->epnum, 1, PTD_GET_TOGGLE(ptd)); + if (urb->actual_length == urb->transfer_buffer_length) { + DBG(3, "%s: req %d xfer complete %d/%d status %d -> 0\n", __func__, + ep->num_req, len, ep->maxpacket, urbstat); + if (usb_pipecontrol(urb->pipe)) { + DBG(3, "%s: req %d %s Wait for ACK\n", __func__, + ep->num_req, + usb_pipein(urb->pipe) ? "IN" : "OUT"); + ep->nextpid = USB_PID_ACK; + } else { + if (len % ep->maxpacket || + !(urb->transfer_flags & URB_ZERO_PACKET)) { + urbstat = 0; + DBG(3, "%s: req %d URB %s status %d count %d/%d/%d\n", + __func__, ep->num_req, usb_pipein(urb->pipe) ? "IN" : "OUT", + urbstat, len, ep->maxpacket, urb->actual_length); + } + } + } + break; + case USB_PID_IN: + len = PTD_GET_COUNT(ptd); + BUG_ON(len > ep->length); + urb->actual_length += len; + BUG_ON(urb->actual_length > urb->transfer_buffer_length); + usb_settoggle(udev, ep->epnum, 0, PTD_GET_TOGGLE(ptd)); + /* if transfer completed or (allowed) data underrun */ + if ((urb->transfer_buffer_length == urb->actual_length) || + len % ep->maxpacket) { + DBG(3, "%s: req %d xfer complete %d/%d status %d -> 0\n", __func__, + ep->num_req, len, ep->maxpacket, urbstat); + if (usb_pipecontrol(urb->pipe)) { + DBG(3, "%s: req %d %s Wait for ACK\n", __func__, + ep->num_req, + usb_pipein(urb->pipe) ? "IN" : "OUT"); + ep->nextpid = USB_PID_ACK; + } else { + urbstat = 0; + DBG(3, "%s: req %d URB %s status %d count %d/%d/%d\n", + __func__, ep->num_req, usb_pipein(urb->pipe) ? "IN" : "OUT", + urbstat, len, ep->maxpacket, urb->actual_length); + } + } + break; + case USB_PID_SETUP: + if (urb->transfer_buffer_length == urb->actual_length) { + ep->nextpid = USB_PID_ACK; + } else if (usb_pipeout(urb->pipe)) { + usb_settoggle(udev, 0, 1, 1); + ep->nextpid = USB_PID_OUT; + } else { + usb_settoggle(udev, 0, 0, 1); + ep->nextpid = USB_PID_IN; + } + break; + case USB_PID_ACK: + DBG(3, "%s: req %d got ACK %d -> 0\n", __func__, ep->num_req, + urbstat); + WARN_ON(urbstat != -EINPROGRESS); + urbstat = 0; + ep->nextpid = 0; + break; + default: + BUG_ON(1); + } + + out: + if (urbstat != -EINPROGRESS) { + DBG(2, "%s: Finishing ep %p req %d urb %p status %d\n", __func__, + ep, ep->num_req, urb, urbstat); + finish_request(isp1362_hcd, ep, urb, urbstat); + } +} + +static void finish_unlinks(struct isp1362_hcd *isp1362_hcd) +{ + struct isp1362_ep *ep; + struct isp1362_ep *tmp; + + list_for_each_entry_safe(ep, tmp, &isp1362_hcd->remove_list, remove_list) { + struct isp1362_ep_queue *epq = + get_ptd_queue(isp1362_hcd, ep->ptd_offset); + int index = ep->ptd_index; + + BUG_ON(epq == NULL); + if (index >= 0) { + DBG(1, "%s: remove PTD[%d] $%04x\n", __func__, index, ep->ptd_offset); + BUG_ON(ep->num_ptds == 0); + release_ptd_buffers(epq, ep); + } + if (!list_empty(&ep->hep->urb_list)) { + struct urb *urb = get_urb(ep); + + DBG(1, "%s: Finishing req %d ep %p from remove_list\n", __func__, + ep->num_req, ep); + finish_request(isp1362_hcd, ep, urb, -ESHUTDOWN); + } + WARN_ON(list_empty(&ep->active)); + if (!list_empty(&ep->active)) { + list_del_init(&ep->active); + DBG(1, "%s: ep %p removed from active list\n", __func__, ep); + } + list_del_init(&ep->remove_list); + DBG(1, "%s: ep %p removed from remove_list\n", __func__, ep); + } + DBG(1, "%s: Done\n", __func__); +} + +static inline void enable_atl_transfers(struct isp1362_hcd *isp1362_hcd, int count) +{ + if (count > 0) { + if (count < isp1362_hcd->atl_queue.ptd_count) + isp1362_write_reg16(isp1362_hcd, HCATLDTC, count); + isp1362_enable_int(isp1362_hcd, HCuPINT_ATL); + isp1362_write_reg32(isp1362_hcd, HCATLSKIP, isp1362_hcd->atl_queue.skip_map); + isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE); + } else + isp1362_enable_int(isp1362_hcd, HCuPINT_SOF); +} + +static inline void enable_intl_transfers(struct isp1362_hcd *isp1362_hcd) +{ + isp1362_enable_int(isp1362_hcd, HCuPINT_INTL); + isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE); + isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, isp1362_hcd->intl_queue.skip_map); +} + +static inline void enable_istl_transfers(struct isp1362_hcd *isp1362_hcd, int flip) +{ + isp1362_enable_int(isp1362_hcd, flip ? HCuPINT_ISTL1 : HCuPINT_ISTL0); + isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, flip ? + HCBUFSTAT_ISTL1_FULL : HCBUFSTAT_ISTL0_FULL); +} + +static int submit_req(struct isp1362_hcd *isp1362_hcd, struct urb *urb, + struct isp1362_ep *ep, struct isp1362_ep_queue *epq) +{ + int index = epq->free_ptd; + + prepare_ptd(isp1362_hcd, urb, ep, epq, 0); + index = claim_ptd_buffers(epq, ep, ep->length); + if (index == -ENOMEM) { + DBG(1, "%s: req %d No free %s PTD available: %d, %08lx:%08lx\n", __func__, + ep->num_req, epq->name, ep->num_ptds, epq->buf_map, epq->skip_map); + return index; + } else if (index == -EOVERFLOW) { + DBG(1, "%s: req %d Not enough space for %d byte %s PTD %d %08lx:%08lx\n", + __func__, ep->num_req, ep->length, epq->name, ep->num_ptds, + epq->buf_map, epq->skip_map); + return index; + } else + BUG_ON(index < 0); + list_add_tail(&ep->active, &epq->active); + DBG(1, "%s: ep %p req %d len %d added to active list %p\n", __func__, + ep, ep->num_req, ep->length, &epq->active); + DBG(1, "%s: Submitting %s PTD $%04x for ep %p req %d\n", __func__, epq->name, + ep->ptd_offset, ep, ep->num_req); + isp1362_write_ptd(isp1362_hcd, ep, epq); + __clear_bit(ep->ptd_index, &epq->skip_map); + + return 0; +} + +static void start_atl_transfers(struct isp1362_hcd *isp1362_hcd) +{ + int ptd_count = 0; + struct isp1362_ep_queue *epq = &isp1362_hcd->atl_queue; + struct isp1362_ep *ep; + int defer = 0; + + if (atomic_read(&epq->finishing)) { + DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name); + return; + } + + list_for_each_entry(ep, &isp1362_hcd->async, schedule) { + struct urb *urb = get_urb(ep); + int ret; + + if (!list_empty(&ep->active)) { + DBG(2, "%s: Skipping active %s ep %p\n", __func__, epq->name, ep); + continue; + } + + DBG(1, "%s: Processing %s ep %p req %d\n", __func__, epq->name, + ep, ep->num_req); + + ret = submit_req(isp1362_hcd, urb, ep, epq); + if (ret == -ENOMEM) { + defer = 1; + break; + } else if (ret == -EOVERFLOW) { + defer = 1; + continue; + } +#ifdef BUGGY_PXA2XX_UDC_USBTEST + defer = ep->nextpid == USB_PID_SETUP; +#endif + ptd_count++; + } + + /* Avoid starving of endpoints */ + if (isp1362_hcd->async.next != isp1362_hcd->async.prev) { + DBG(2, "%s: Cycling ASYNC schedule %d\n", __func__, ptd_count); + list_move(&isp1362_hcd->async, isp1362_hcd->async.next); + } + if (ptd_count || defer) + enable_atl_transfers(isp1362_hcd, defer ? 0 : ptd_count); + + epq->ptd_count += ptd_count; + if (epq->ptd_count > epq->stat_maxptds) { + epq->stat_maxptds = epq->ptd_count; + DBG(0, "%s: max_ptds: %d\n", __func__, epq->stat_maxptds); + } +} + +static void start_intl_transfers(struct isp1362_hcd *isp1362_hcd) +{ + int ptd_count = 0; + struct isp1362_ep_queue *epq = &isp1362_hcd->intl_queue; + struct isp1362_ep *ep; + + if (atomic_read(&epq->finishing)) { + DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name); + return; + } + + list_for_each_entry(ep, &isp1362_hcd->periodic, schedule) { + struct urb *urb = get_urb(ep); + int ret; + + if (!list_empty(&ep->active)) { + DBG(1, "%s: Skipping active %s ep %p\n", __func__, + epq->name, ep); + continue; + } + + DBG(1, "%s: Processing %s ep %p req %d\n", __func__, + epq->name, ep, ep->num_req); + ret = submit_req(isp1362_hcd, urb, ep, epq); + if (ret == -ENOMEM) + break; + else if (ret == -EOVERFLOW) + continue; + ptd_count++; + } + + if (ptd_count) { + static int last_count; + + if (ptd_count != last_count) { + DBG(0, "%s: ptd_count: %d\n", __func__, ptd_count); + last_count = ptd_count; + } + enable_intl_transfers(isp1362_hcd); + } + + epq->ptd_count += ptd_count; + if (epq->ptd_count > epq->stat_maxptds) + epq->stat_maxptds = epq->ptd_count; +} + +static inline int next_ptd(struct isp1362_ep_queue *epq, struct isp1362_ep *ep) +{ + u16 ptd_offset = ep->ptd_offset; + int num_ptds = (ep->length + PTD_HEADER_SIZE + (epq->blk_size - 1)) / epq->blk_size; + + DBG(2, "%s: PTD offset $%04x + %04x => %d * %04x -> $%04x\n", __func__, ptd_offset, + ep->length, num_ptds, epq->blk_size, ptd_offset + num_ptds * epq->blk_size); + + ptd_offset += num_ptds * epq->blk_size; + if (ptd_offset < epq->buf_start + epq->buf_size) + return ptd_offset; + else + return -ENOMEM; +} + +static void start_iso_transfers(struct isp1362_hcd *isp1362_hcd) +{ + int ptd_count = 0; + int flip = isp1362_hcd->istl_flip; + struct isp1362_ep_queue *epq; + int ptd_offset; + struct isp1362_ep *ep; + struct isp1362_ep *tmp; + u16 fno = isp1362_read_reg32(isp1362_hcd, HCFMNUM); + + fill2: + epq = &isp1362_hcd->istl_queue[flip]; + if (atomic_read(&epq->finishing)) { + DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name); + return; + } + + if (!list_empty(&epq->active)) + return; + + ptd_offset = epq->buf_start; + list_for_each_entry_safe(ep, tmp, &isp1362_hcd->isoc, schedule) { + struct urb *urb = get_urb(ep); + s16 diff = fno - (u16)urb->start_frame; + + DBG(1, "%s: Processing %s ep %p\n", __func__, epq->name, ep); + + if (diff > urb->number_of_packets) { + /* time frame for this URB has elapsed */ + finish_request(isp1362_hcd, ep, urb, -EOVERFLOW); + continue; + } else if (diff < -1) { + /* URB is not due in this frame or the next one. + * Comparing with '-1' instead of '0' accounts for double + * buffering in the ISP1362 which enables us to queue the PTD + * one frame ahead of time + */ + } else if (diff == -1) { + /* submit PTD's that are due in the next frame */ + prepare_ptd(isp1362_hcd, urb, ep, epq, fno); + if (ptd_offset + PTD_HEADER_SIZE + ep->length > + epq->buf_start + epq->buf_size) { + pr_err("%s: Not enough ISO buffer space for %d byte PTD\n", + __func__, ep->length); + continue; + } + ep->ptd_offset = ptd_offset; + list_add_tail(&ep->active, &epq->active); + + ptd_offset = next_ptd(epq, ep); + if (ptd_offset < 0) { + pr_warning("%s: req %d No more %s PTD buffers available\n", __func__, + ep->num_req, epq->name); + break; + } + } + } + list_for_each_entry(ep, &epq->active, active) { + if (epq->active.next == &ep->active) + ep->ptd.mps |= PTD_LAST_MSK; + isp1362_write_ptd(isp1362_hcd, ep, epq); + ptd_count++; + } + + if (ptd_count) + enable_istl_transfers(isp1362_hcd, flip); + + epq->ptd_count += ptd_count; + if (epq->ptd_count > epq->stat_maxptds) + epq->stat_maxptds = epq->ptd_count; + + /* check, whether the second ISTL buffer may also be filled */ + if (!(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & + (flip ? HCBUFSTAT_ISTL0_FULL : HCBUFSTAT_ISTL1_FULL))) { + fno++; + ptd_count = 0; + flip = 1 - flip; + goto fill2; + } +} + +static void finish_transfers(struct isp1362_hcd *isp1362_hcd, unsigned long done_map, + struct isp1362_ep_queue *epq) +{ + struct isp1362_ep *ep; + struct isp1362_ep *tmp; + + if (list_empty(&epq->active)) { + DBG(1, "%s: Nothing to do for %s queue\n", __func__, epq->name); + return; + } + + DBG(1, "%s: Finishing %s transfers %08lx\n", __func__, epq->name, done_map); + + atomic_inc(&epq->finishing); + list_for_each_entry_safe(ep, tmp, &epq->active, active) { + int index = ep->ptd_index; + + DBG(1, "%s: Checking %s PTD[%02x] $%04x\n", __func__, epq->name, + index, ep->ptd_offset); + + BUG_ON(index < 0); + if (__test_and_clear_bit(index, &done_map)) { + isp1362_read_ptd(isp1362_hcd, ep, epq); + epq->free_ptd = index; + BUG_ON(ep->num_ptds == 0); + release_ptd_buffers(epq, ep); + + DBG(1, "%s: ep %p req %d removed from active list\n", __func__, + ep, ep->num_req); + if (!list_empty(&ep->remove_list)) { + list_del_init(&ep->remove_list); + DBG(1, "%s: ep %p removed from remove list\n", __func__, ep); + } + DBG(1, "%s: Postprocessing %s ep %p req %d\n", __func__, epq->name, + ep, ep->num_req); + postproc_ep(isp1362_hcd, ep); + } + if (!done_map) + break; + } + if (done_map) + pr_warning("%s: done_map not clear: %08lx:%08lx\n", __func__, done_map, + epq->skip_map); + atomic_dec(&epq->finishing); +} + +static void finish_iso_transfers(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep_queue *epq) +{ + struct isp1362_ep *ep; + struct isp1362_ep *tmp; + + if (list_empty(&epq->active)) { + DBG(1, "%s: Nothing to do for %s queue\n", __func__, epq->name); + return; + } + + DBG(1, "%s: Finishing %s transfers\n", __func__, epq->name); + + atomic_inc(&epq->finishing); + list_for_each_entry_safe(ep, tmp, &epq->active, active) { + DBG(1, "%s: Checking PTD $%04x\n", __func__, ep->ptd_offset); + + isp1362_read_ptd(isp1362_hcd, ep, epq); + DBG(1, "%s: Postprocessing %s ep %p\n", __func__, epq->name, ep); + postproc_ep(isp1362_hcd, ep); + } + WARN_ON(epq->blk_size != 0); + atomic_dec(&epq->finishing); +} + +static irqreturn_t isp1362_irq(struct usb_hcd *hcd) +{ + int handled = 0; + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + u16 irqstat; + u16 svc_mask; + + spin_lock(&isp1362_hcd->lock); + + BUG_ON(isp1362_hcd->irq_active++); + + isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0); + + irqstat = isp1362_read_reg16(isp1362_hcd, HCuPINT); + DBG(3, "%s: got IRQ %04x:%04x\n", __func__, irqstat, isp1362_hcd->irqenb); + + /* only handle interrupts that are currently enabled */ + irqstat &= isp1362_hcd->irqenb; + isp1362_write_reg16(isp1362_hcd, HCuPINT, irqstat); + svc_mask = irqstat; + + if (irqstat & HCuPINT_SOF) { + isp1362_hcd->irqenb &= ~HCuPINT_SOF; + isp1362_hcd->irq_stat[ISP1362_INT_SOF]++; + handled = 1; + svc_mask &= ~HCuPINT_SOF; + DBG(3, "%s: SOF\n", __func__); + isp1362_hcd->fmindex = isp1362_read_reg32(isp1362_hcd, HCFMNUM); + if (!list_empty(&isp1362_hcd->remove_list)) + finish_unlinks(isp1362_hcd); + if (!list_empty(&isp1362_hcd->async) && !(irqstat & HCuPINT_ATL)) { + if (list_empty(&isp1362_hcd->atl_queue.active)) { + start_atl_transfers(isp1362_hcd); + } else { + isp1362_enable_int(isp1362_hcd, HCuPINT_ATL); + isp1362_write_reg32(isp1362_hcd, HCATLSKIP, + isp1362_hcd->atl_queue.skip_map); + isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE); + } + } + } + + if (irqstat & HCuPINT_ISTL0) { + isp1362_hcd->irq_stat[ISP1362_INT_ISTL0]++; + handled = 1; + svc_mask &= ~HCuPINT_ISTL0; + isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ISTL0_FULL); + DBG(1, "%s: ISTL0\n", __func__); + WARN_ON((int)!!isp1362_hcd->istl_flip); + WARN_ON(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & HCBUFSTAT_ISTL0_ACTIVE); + WARN_ON(!isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & HCBUFSTAT_ISTL0_DONE); + isp1362_hcd->irqenb &= ~HCuPINT_ISTL0; + } + + if (irqstat & HCuPINT_ISTL1) { + isp1362_hcd->irq_stat[ISP1362_INT_ISTL1]++; + handled = 1; + svc_mask &= ~HCuPINT_ISTL1; + isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ISTL1_FULL); + DBG(1, "%s: ISTL1\n", __func__); + WARN_ON(!(int)isp1362_hcd->istl_flip); + WARN_ON(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & HCBUFSTAT_ISTL1_ACTIVE); + WARN_ON(!isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & HCBUFSTAT_ISTL1_DONE); + isp1362_hcd->irqenb &= ~HCuPINT_ISTL1; + } + + if (irqstat & (HCuPINT_ISTL0 | HCuPINT_ISTL1)) { + WARN_ON((irqstat & (HCuPINT_ISTL0 | HCuPINT_ISTL1)) == + (HCuPINT_ISTL0 | HCuPINT_ISTL1)); + finish_iso_transfers(isp1362_hcd, + &isp1362_hcd->istl_queue[isp1362_hcd->istl_flip]); + start_iso_transfers(isp1362_hcd); + isp1362_hcd->istl_flip = 1 - isp1362_hcd->istl_flip; + } + + if (irqstat & HCuPINT_INTL) { + u32 done_map = isp1362_read_reg32(isp1362_hcd, HCINTLDONE); + u32 skip_map = isp1362_read_reg32(isp1362_hcd, HCINTLSKIP); + isp1362_hcd->irq_stat[ISP1362_INT_INTL]++; + + DBG(2, "%s: INTL\n", __func__); + + svc_mask &= ~HCuPINT_INTL; + + isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, skip_map | done_map); + if (~(done_map | skip_map) == 0) + /* All PTDs are finished, disable INTL processing entirely */ + isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE); + + handled = 1; + WARN_ON(!done_map); + if (done_map) { + DBG(3, "%s: INTL done_map %08x\n", __func__, done_map); + finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->intl_queue); + start_intl_transfers(isp1362_hcd); + } + } + + if (irqstat & HCuPINT_ATL) { + u32 done_map = isp1362_read_reg32(isp1362_hcd, HCATLDONE); + u32 skip_map = isp1362_read_reg32(isp1362_hcd, HCATLSKIP); + isp1362_hcd->irq_stat[ISP1362_INT_ATL]++; + + DBG(2, "%s: ATL\n", __func__); + + svc_mask &= ~HCuPINT_ATL; + + isp1362_write_reg32(isp1362_hcd, HCATLSKIP, skip_map | done_map); + if (~(done_map | skip_map) == 0) + isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE); + if (done_map) { + DBG(3, "%s: ATL done_map %08x\n", __func__, done_map); + finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->atl_queue); + start_atl_transfers(isp1362_hcd); + } + handled = 1; + } + + if (irqstat & HCuPINT_OPR) { + u32 intstat = isp1362_read_reg32(isp1362_hcd, HCINTSTAT); + isp1362_hcd->irq_stat[ISP1362_INT_OPR]++; + + svc_mask &= ~HCuPINT_OPR; + DBG(2, "%s: OPR %08x:%08x\n", __func__, intstat, isp1362_hcd->intenb); + intstat &= isp1362_hcd->intenb; + if (intstat & OHCI_INTR_UE) { + pr_err("Unrecoverable error\n"); + /* FIXME: do here reset or cleanup or whatever */ + } + if (intstat & OHCI_INTR_RHSC) { + isp1362_hcd->rhstatus = isp1362_read_reg32(isp1362_hcd, HCRHSTATUS); + isp1362_hcd->rhport[0] = isp1362_read_reg32(isp1362_hcd, HCRHPORT1); + isp1362_hcd->rhport[1] = isp1362_read_reg32(isp1362_hcd, HCRHPORT2); + } + if (intstat & OHCI_INTR_RD) { + pr_info("%s: RESUME DETECTED\n", __func__); + isp1362_show_reg(isp1362_hcd, HCCONTROL); + usb_hcd_resume_root_hub(hcd); + } + isp1362_write_reg32(isp1362_hcd, HCINTSTAT, intstat); + irqstat &= ~HCuPINT_OPR; + handled = 1; + } + + if (irqstat & HCuPINT_SUSP) { + isp1362_hcd->irq_stat[ISP1362_INT_SUSP]++; + handled = 1; + svc_mask &= ~HCuPINT_SUSP; + + pr_info("%s: SUSPEND IRQ\n", __func__); + } + + if (irqstat & HCuPINT_CLKRDY) { + isp1362_hcd->irq_stat[ISP1362_INT_CLKRDY]++; + handled = 1; + isp1362_hcd->irqenb &= ~HCuPINT_CLKRDY; + svc_mask &= ~HCuPINT_CLKRDY; + pr_info("%s: CLKRDY IRQ\n", __func__); + } + + if (svc_mask) + pr_err("%s: Unserviced interrupt(s) %04x\n", __func__, svc_mask); + + isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb); + isp1362_hcd->irq_active--; + spin_unlock(&isp1362_hcd->lock); + + return IRQ_RETVAL(handled); +} + +/*-------------------------------------------------------------------------*/ + +#define MAX_PERIODIC_LOAD 900 /* out of 1000 usec */ +static int balance(struct isp1362_hcd *isp1362_hcd, u16 interval, u16 load) +{ + int i, branch = -ENOSPC; + + /* search for the least loaded schedule branch of that interval + * which has enough bandwidth left unreserved. + */ + for (i = 0; i < interval; i++) { + if (branch < 0 || isp1362_hcd->load[branch] > isp1362_hcd->load[i]) { + int j; + + for (j = i; j < PERIODIC_SIZE; j += interval) { + if ((isp1362_hcd->load[j] + load) > MAX_PERIODIC_LOAD) { + pr_err("%s: new load %d load[%02x] %d max %d\n", __func__, + load, j, isp1362_hcd->load[j], MAX_PERIODIC_LOAD); + break; + } + } + if (j < PERIODIC_SIZE) + continue; + branch = i; + } + } + return branch; +} + +/* NB! ALL the code above this point runs with isp1362_hcd->lock + held, irqs off +*/ + +/*-------------------------------------------------------------------------*/ + +static int isp1362_urb_enqueue(struct usb_hcd *hcd, + struct urb *urb, + gfp_t mem_flags) +{ + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + struct usb_device *udev = urb->dev; + unsigned int pipe = urb->pipe; + int is_out = !usb_pipein(pipe); + int type = usb_pipetype(pipe); + int epnum = usb_pipeendpoint(pipe); + struct usb_host_endpoint *hep = urb->ep; + struct isp1362_ep *ep = NULL; + unsigned long flags; + int retval = 0; + + DBG(3, "%s: urb %p\n", __func__, urb); + + if (type == PIPE_ISOCHRONOUS) { + pr_err("Isochronous transfers not supported\n"); + return -ENOSPC; + } + + URB_DBG("%s: FA %d ep%d%s %s: len %d %s%s\n", __func__, + usb_pipedevice(pipe), epnum, + is_out ? "out" : "in", + usb_pipecontrol(pipe) ? "ctrl" : + usb_pipeint(pipe) ? "int" : + usb_pipebulk(pipe) ? "bulk" : + "iso", + urb->transfer_buffer_length, + (urb->transfer_flags & URB_ZERO_PACKET) ? "ZERO_PACKET " : "", + !(urb->transfer_flags & URB_SHORT_NOT_OK) ? + "short_ok" : ""); + + /* avoid all allocations within spinlocks: request or endpoint */ + if (!hep->hcpriv) { + ep = kcalloc(1, sizeof *ep, mem_flags); + if (!ep) + return -ENOMEM; + } + spin_lock_irqsave(&isp1362_hcd->lock, flags); + + /* don't submit to a dead or disabled port */ + if (!((isp1362_hcd->rhport[0] | isp1362_hcd->rhport[1]) & + (1 << USB_PORT_FEAT_ENABLE)) || + !HC_IS_RUNNING(hcd->state)) { + kfree(ep); + retval = -ENODEV; + goto fail_not_linked; + } + + retval = usb_hcd_link_urb_to_ep(hcd, urb); + if (retval) { + kfree(ep); + goto fail_not_linked; + } + + if (hep->hcpriv) { + ep = hep->hcpriv; + } else { + INIT_LIST_HEAD(&ep->schedule); + INIT_LIST_HEAD(&ep->active); + INIT_LIST_HEAD(&ep->remove_list); + ep->udev = usb_get_dev(udev); + ep->hep = hep; + ep->epnum = epnum; + ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out); + ep->ptd_offset = -EINVAL; + ep->ptd_index = -EINVAL; + usb_settoggle(udev, epnum, is_out, 0); + + if (type == PIPE_CONTROL) + ep->nextpid = USB_PID_SETUP; + else if (is_out) + ep->nextpid = USB_PID_OUT; + else + ep->nextpid = USB_PID_IN; + + switch (type) { + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + if (urb->interval > PERIODIC_SIZE) + urb->interval = PERIODIC_SIZE; + ep->interval = urb->interval; + ep->branch = PERIODIC_SIZE; + ep->load = usb_calc_bus_time(udev->speed, !is_out, + (type == PIPE_ISOCHRONOUS), + usb_maxpacket(udev, pipe, is_out)) / 1000; + break; + } + hep->hcpriv = ep; + } + ep->num_req = isp1362_hcd->req_serial++; + + /* maybe put endpoint into schedule */ + switch (type) { + case PIPE_CONTROL: + case PIPE_BULK: + if (list_empty(&ep->schedule)) { + DBG(1, "%s: Adding ep %p req %d to async schedule\n", + __func__, ep, ep->num_req); + list_add_tail(&ep->schedule, &isp1362_hcd->async); + } + break; + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + urb->interval = ep->interval; + + /* urb submitted for already existing EP */ + if (ep->branch < PERIODIC_SIZE) + break; + + retval = balance(isp1362_hcd, ep->interval, ep->load); + if (retval < 0) { + pr_err("%s: balance returned %d\n", __func__, retval); + goto fail; + } + ep->branch = retval; + retval = 0; + isp1362_hcd->fmindex = isp1362_read_reg32(isp1362_hcd, HCFMNUM); + DBG(1, "%s: Current frame %04x branch %02x start_frame %04x(%04x)\n", + __func__, isp1362_hcd->fmindex, ep->branch, + ((isp1362_hcd->fmindex + PERIODIC_SIZE - 1) & + ~(PERIODIC_SIZE - 1)) + ep->branch, + (isp1362_hcd->fmindex & (PERIODIC_SIZE - 1)) + ep->branch); + + if (list_empty(&ep->schedule)) { + if (type == PIPE_ISOCHRONOUS) { + u16 frame = isp1362_hcd->fmindex; + + frame += max_t(u16, 8, ep->interval); + frame &= ~(ep->interval - 1); + frame |= ep->branch; + if (frame_before(frame, isp1362_hcd->fmindex)) + frame += ep->interval; + urb->start_frame = frame; + + DBG(1, "%s: Adding ep %p to isoc schedule\n", __func__, ep); + list_add_tail(&ep->schedule, &isp1362_hcd->isoc); + } else { + DBG(1, "%s: Adding ep %p to periodic schedule\n", __func__, ep); + list_add_tail(&ep->schedule, &isp1362_hcd->periodic); + } + } else + DBG(1, "%s: ep %p already scheduled\n", __func__, ep); + + DBG(2, "%s: load %d bandwidth %d -> %d\n", __func__, + ep->load / ep->interval, isp1362_hcd->load[ep->branch], + isp1362_hcd->load[ep->branch] + ep->load); + isp1362_hcd->load[ep->branch] += ep->load; + } + + urb->hcpriv = hep; + ALIGNSTAT(isp1362_hcd, urb->transfer_buffer); + + switch (type) { + case PIPE_CONTROL: + case PIPE_BULK: + start_atl_transfers(isp1362_hcd); + break; + case PIPE_INTERRUPT: + start_intl_transfers(isp1362_hcd); + break; + case PIPE_ISOCHRONOUS: + start_iso_transfers(isp1362_hcd); + break; + default: + BUG(); + } + fail: + if (retval) + usb_hcd_unlink_urb_from_ep(hcd, urb); + + + fail_not_linked: + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + if (retval) + DBG(0, "%s: urb %p failed with %d\n", __func__, urb, retval); + return retval; +} + +static int isp1362_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +{ + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + struct usb_host_endpoint *hep; + unsigned long flags; + struct isp1362_ep *ep; + int retval = 0; + + DBG(3, "%s: urb %p\n", __func__, urb); + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + retval = usb_hcd_check_unlink_urb(hcd, urb, status); + if (retval) + goto done; + + hep = urb->hcpriv; + + if (!hep) { + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + return -EIDRM; + } + + ep = hep->hcpriv; + if (ep) { + /* In front of queue? */ + if (ep->hep->urb_list.next == &urb->urb_list) { + if (!list_empty(&ep->active)) { + DBG(1, "%s: urb %p ep %p req %d active PTD[%d] $%04x\n", __func__, + urb, ep, ep->num_req, ep->ptd_index, ep->ptd_offset); + /* disable processing and queue PTD for removal */ + remove_ptd(isp1362_hcd, ep); + urb = NULL; + } + } + if (urb) { + DBG(1, "%s: Finishing ep %p req %d\n", __func__, ep, + ep->num_req); + finish_request(isp1362_hcd, ep, urb, status); + } else + DBG(1, "%s: urb %p active; wait4irq\n", __func__, urb); + } else { + pr_warning("%s: No EP in URB %p\n", __func__, urb); + retval = -EINVAL; + } +done: + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + + DBG(3, "%s: exit\n", __func__); + + return retval; +} + +static void isp1362_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) +{ + struct isp1362_ep *ep = hep->hcpriv; + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + unsigned long flags; + + DBG(1, "%s: ep %p\n", __func__, ep); + if (!ep) + return; + spin_lock_irqsave(&isp1362_hcd->lock, flags); + if (!list_empty(&hep->urb_list)) { + if (!list_empty(&ep->active) && list_empty(&ep->remove_list)) { + DBG(1, "%s: Removing ep %p req %d PTD[%d] $%04x\n", __func__, + ep, ep->num_req, ep->ptd_index, ep->ptd_offset); + remove_ptd(isp1362_hcd, ep); + pr_info("%s: Waiting for Interrupt to clean up\n", __func__); + } + } + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + /* Wait for interrupt to clear out active list */ + while (!list_empty(&ep->active)) + msleep(1); + + DBG(1, "%s: Freeing EP %p\n", __func__, ep); + + usb_put_dev(ep->udev); + kfree(ep); + hep->hcpriv = NULL; +} + +static int isp1362_get_frame(struct usb_hcd *hcd) +{ + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + u32 fmnum; + unsigned long flags; + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + fmnum = isp1362_read_reg32(isp1362_hcd, HCFMNUM); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + + return (int)fmnum; +} + +/*-------------------------------------------------------------------------*/ + +/* Adapted from ohci-hub.c */ +static int isp1362_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + int ports, i, changed = 0; + unsigned long flags; + + if (!HC_IS_RUNNING(hcd->state)) + return -ESHUTDOWN; + + /* Report no status change now, if we are scheduled to be + called later */ + if (timer_pending(&hcd->rh_timer)) + return 0; + + ports = isp1362_hcd->rhdesca & RH_A_NDP; + BUG_ON(ports > 2); + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + /* init status */ + if (isp1362_hcd->rhstatus & (RH_HS_LPSC | RH_HS_OCIC)) + buf[0] = changed = 1; + else + buf[0] = 0; + + for (i = 0; i < ports; i++) { + u32 status = isp1362_hcd->rhport[i]; + + if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC | + RH_PS_OCIC | RH_PS_PRSC)) { + changed = 1; + buf[0] |= 1 << (i + 1); + continue; + } + + if (!(status & RH_PS_CCS)) + continue; + } + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + return changed; +} + +static void isp1362_hub_descriptor(struct isp1362_hcd *isp1362_hcd, + struct usb_hub_descriptor *desc) +{ + u32 reg = isp1362_hcd->rhdesca; + + DBG(3, "%s: enter\n", __func__); + + desc->bDescriptorType = 0x29; + desc->bDescLength = 9; + desc->bHubContrCurrent = 0; + desc->bNbrPorts = reg & 0x3; + /* Power switching, device type, overcurrent. */ + desc->wHubCharacteristics = cpu_to_le16((reg >> 8) & 0x1f); + DBG(0, "%s: hubcharacteristics = %02x\n", __func__, cpu_to_le16((reg >> 8) & 0x1f)); + desc->bPwrOn2PwrGood = (reg >> 24) & 0xff; + /* two bitmaps: ports removable, and legacy PortPwrCtrlMask */ + desc->bitmap[0] = desc->bNbrPorts == 1 ? 1 << 1 : 3 << 1; + desc->bitmap[1] = ~0; + + DBG(3, "%s: exit\n", __func__); +} + +/* Adapted from ohci-hub.c */ +static int isp1362_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + int retval = 0; + unsigned long flags; + unsigned long t1; + int ports = isp1362_hcd->rhdesca & RH_A_NDP; + u32 tmp = 0; + + switch (typeReq) { + case ClearHubFeature: + DBG(0, "ClearHubFeature: "); + switch (wValue) { + case C_HUB_OVER_CURRENT: + _DBG(0, "C_HUB_OVER_CURRENT\n"); + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_OCIC); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + case C_HUB_LOCAL_POWER: + _DBG(0, "C_HUB_LOCAL_POWER\n"); + break; + default: + goto error; + } + break; + case SetHubFeature: + DBG(0, "SetHubFeature: "); + switch (wValue) { + case C_HUB_OVER_CURRENT: + case C_HUB_LOCAL_POWER: + _DBG(0, "C_HUB_OVER_CURRENT or C_HUB_LOCAL_POWER\n"); + break; + default: + goto error; + } + break; + case GetHubDescriptor: + DBG(0, "GetHubDescriptor\n"); + isp1362_hub_descriptor(isp1362_hcd, (struct usb_hub_descriptor *)buf); + break; + case GetHubStatus: + DBG(0, "GetHubStatus\n"); + put_unaligned(cpu_to_le32(0), (__le32 *) buf); + break; + case GetPortStatus: +#ifndef VERBOSE + DBG(0, "GetPortStatus\n"); +#endif + if (!wIndex || wIndex > ports) + goto error; + tmp = isp1362_hcd->rhport[--wIndex]; + put_unaligned(cpu_to_le32(tmp), (__le32 *) buf); + break; + case ClearPortFeature: + DBG(0, "ClearPortFeature: "); + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + _DBG(0, "USB_PORT_FEAT_ENABLE\n"); + tmp = RH_PS_CCS; + break; + case USB_PORT_FEAT_C_ENABLE: + _DBG(0, "USB_PORT_FEAT_C_ENABLE\n"); + tmp = RH_PS_PESC; + break; + case USB_PORT_FEAT_SUSPEND: + _DBG(0, "USB_PORT_FEAT_SUSPEND\n"); + tmp = RH_PS_POCI; + break; + case USB_PORT_FEAT_C_SUSPEND: + _DBG(0, "USB_PORT_FEAT_C_SUSPEND\n"); + tmp = RH_PS_PSSC; + break; + case USB_PORT_FEAT_POWER: + _DBG(0, "USB_PORT_FEAT_POWER\n"); + tmp = RH_PS_LSDA; + + break; + case USB_PORT_FEAT_C_CONNECTION: + _DBG(0, "USB_PORT_FEAT_C_CONNECTION\n"); + tmp = RH_PS_CSC; + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + _DBG(0, "USB_PORT_FEAT_C_OVER_CURRENT\n"); + tmp = RH_PS_OCIC; + break; + case USB_PORT_FEAT_C_RESET: + _DBG(0, "USB_PORT_FEAT_C_RESET\n"); + tmp = RH_PS_PRSC; + break; + default: + goto error; + } + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, tmp); + isp1362_hcd->rhport[wIndex] = + isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + break; + case SetPortFeature: + DBG(0, "SetPortFeature: "); + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + _DBG(0, "USB_PORT_FEAT_SUSPEND\n"); +#ifdef CONFIG_USB_OTG + if (ohci->hcd.self.otg_port == (wIndex + 1) && + ohci->hcd.self.b_hnp_enable) { + start_hnp(ohci); + break; + } +#endif + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, RH_PS_PSS); + isp1362_hcd->rhport[wIndex] = + isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + break; + case USB_PORT_FEAT_POWER: + _DBG(0, "USB_PORT_FEAT_POWER\n"); + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, RH_PS_PPS); + isp1362_hcd->rhport[wIndex] = + isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + break; + case USB_PORT_FEAT_RESET: + _DBG(0, "USB_PORT_FEAT_RESET\n"); + spin_lock_irqsave(&isp1362_hcd->lock, flags); + + t1 = jiffies + msecs_to_jiffies(USB_RESET_WIDTH); + while (time_before(jiffies, t1)) { + /* spin until any current reset finishes */ + for (;;) { + tmp = isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex); + if (!(tmp & RH_PS_PRS)) + break; + udelay(500); + } + if (!(tmp & RH_PS_CCS)) + break; + /* Reset lasts 10ms (claims datasheet) */ + isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, (RH_PS_PRS)); + + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + msleep(10); + spin_lock_irqsave(&isp1362_hcd->lock, flags); + } + + isp1362_hcd->rhport[wIndex] = isp1362_read_reg32(isp1362_hcd, + HCRHPORT1 + wIndex); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + break; + default: + goto error; + } + break; + + default: + error: + /* "protocol stall" on error */ + _DBG(0, "PROTOCOL STALL\n"); + retval = -EPIPE; + } + + return retval; +} + +#ifdef CONFIG_PM +static int isp1362_bus_suspend(struct usb_hcd *hcd) +{ + int status = 0; + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + unsigned long flags; + + if (time_before(jiffies, isp1362_hcd->next_statechange)) + msleep(5); + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + + isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL); + switch (isp1362_hcd->hc_control & OHCI_CTRL_HCFS) { + case OHCI_USB_RESUME: + DBG(0, "%s: resume/suspend?\n", __func__); + isp1362_hcd->hc_control &= ~OHCI_CTRL_HCFS; + isp1362_hcd->hc_control |= OHCI_USB_RESET; + isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control); + /* FALL THROUGH */ + case OHCI_USB_RESET: + status = -EBUSY; + pr_warning("%s: needs reinit!\n", __func__); + goto done; + case OHCI_USB_SUSPEND: + pr_warning("%s: already suspended?\n", __func__); + goto done; + } + DBG(0, "%s: suspend root hub\n", __func__); + + /* First stop any processing */ + hcd->state = HC_STATE_QUIESCING; + if (!list_empty(&isp1362_hcd->atl_queue.active) || + !list_empty(&isp1362_hcd->intl_queue.active) || + !list_empty(&isp1362_hcd->istl_queue[0] .active) || + !list_empty(&isp1362_hcd->istl_queue[1] .active)) { + int limit; + + isp1362_write_reg32(isp1362_hcd, HCATLSKIP, ~0); + isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, ~0); + isp1362_write_reg16(isp1362_hcd, HCBUFSTAT, 0); + isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0); + isp1362_write_reg32(isp1362_hcd, HCINTSTAT, OHCI_INTR_SF); + + DBG(0, "%s: stopping schedules ...\n", __func__); + limit = 2000; + while (limit > 0) { + udelay(250); + limit -= 250; + if (isp1362_read_reg32(isp1362_hcd, HCINTSTAT) & OHCI_INTR_SF) + break; + } + mdelay(7); + if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ATL) { + u32 done_map = isp1362_read_reg32(isp1362_hcd, HCATLDONE); + finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->atl_queue); + } + if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_INTL) { + u32 done_map = isp1362_read_reg32(isp1362_hcd, HCINTLDONE); + finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->intl_queue); + } + if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ISTL0) + finish_iso_transfers(isp1362_hcd, &isp1362_hcd->istl_queue[0]); + if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ISTL1) + finish_iso_transfers(isp1362_hcd, &isp1362_hcd->istl_queue[1]); + } + DBG(0, "%s: HCINTSTAT: %08x\n", __func__, + isp1362_read_reg32(isp1362_hcd, HCINTSTAT)); + isp1362_write_reg32(isp1362_hcd, HCINTSTAT, + isp1362_read_reg32(isp1362_hcd, HCINTSTAT)); + + /* Suspend hub */ + isp1362_hcd->hc_control = OHCI_USB_SUSPEND; + isp1362_show_reg(isp1362_hcd, HCCONTROL); + isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control); + isp1362_show_reg(isp1362_hcd, HCCONTROL); + +#if 1 + isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL); + if ((isp1362_hcd->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_SUSPEND) { + pr_err("%s: controller won't suspend %08x\n", __func__, + isp1362_hcd->hc_control); + status = -EBUSY; + } else +#endif + { + /* no resumes until devices finish suspending */ + isp1362_hcd->next_statechange = jiffies + msecs_to_jiffies(5); + } +done: + if (status == 0) { + hcd->state = HC_STATE_SUSPENDED; + DBG(0, "%s: HCD suspended: %08x\n", __func__, + isp1362_read_reg32(isp1362_hcd, HCCONTROL)); + } + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + return status; +} + +static int isp1362_bus_resume(struct usb_hcd *hcd) +{ + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + u32 port; + unsigned long flags; + int status = -EINPROGRESS; + + if (time_before(jiffies, isp1362_hcd->next_statechange)) + msleep(5); + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL); + pr_info("%s: HCCONTROL: %08x\n", __func__, isp1362_hcd->hc_control); + if (hcd->state == HC_STATE_RESUMING) { + pr_warning("%s: duplicate resume\n", __func__); + status = 0; + } else + switch (isp1362_hcd->hc_control & OHCI_CTRL_HCFS) { + case OHCI_USB_SUSPEND: + DBG(0, "%s: resume root hub\n", __func__); + isp1362_hcd->hc_control &= ~OHCI_CTRL_HCFS; + isp1362_hcd->hc_control |= OHCI_USB_RESUME; + isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control); + break; + case OHCI_USB_RESUME: + /* HCFS changes sometime after INTR_RD */ + DBG(0, "%s: remote wakeup\n", __func__); + break; + case OHCI_USB_OPER: + DBG(0, "%s: odd resume\n", __func__); + status = 0; + hcd->self.root_hub->dev.power.power_state = PMSG_ON; + break; + default: /* RESET, we lost power */ + DBG(0, "%s: root hub hardware reset\n", __func__); + status = -EBUSY; + } + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + if (status == -EBUSY) { + DBG(0, "%s: Restarting HC\n", __func__); + isp1362_hc_stop(hcd); + return isp1362_hc_start(hcd); + } + if (status != -EINPROGRESS) + return status; + spin_lock_irqsave(&isp1362_hcd->lock, flags); + port = isp1362_read_reg32(isp1362_hcd, HCRHDESCA) & RH_A_NDP; + while (port--) { + u32 stat = isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + port); + + /* force global, not selective, resume */ + if (!(stat & RH_PS_PSS)) { + DBG(0, "%s: Not Resuming RH port %d\n", __func__, port); + continue; + } + DBG(0, "%s: Resuming RH port %d\n", __func__, port); + isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + port, RH_PS_POCI); + } + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + + /* Some controllers (lucent) need extra-long delays */ + hcd->state = HC_STATE_RESUMING; + mdelay(20 /* usb 11.5.1.10 */ + 15); + + isp1362_hcd->hc_control = OHCI_USB_OPER; + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_show_reg(isp1362_hcd, HCCONTROL); + isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + /* TRSMRCY */ + msleep(10); + + /* keep it alive for ~5x suspend + resume costs */ + isp1362_hcd->next_statechange = jiffies + msecs_to_jiffies(250); + + hcd->self.root_hub->dev.power.power_state = PMSG_ON; + hcd->state = HC_STATE_RUNNING; + return 0; +} +#else +#define isp1362_bus_suspend NULL +#define isp1362_bus_resume NULL +#endif + +/*-------------------------------------------------------------------------*/ + +#ifdef STUB_DEBUG_FILE + +static inline void create_debug_file(struct isp1362_hcd *isp1362_hcd) +{ +} +static inline void remove_debug_file(struct isp1362_hcd *isp1362_hcd) +{ +} + +#else + +#include +#include + +static void dump_irq(struct seq_file *s, char *label, u16 mask) +{ + seq_printf(s, "%-15s %04x%s%s%s%s%s%s\n", label, mask, + mask & HCuPINT_CLKRDY ? " clkrdy" : "", + mask & HCuPINT_SUSP ? " susp" : "", + mask & HCuPINT_OPR ? " opr" : "", + mask & HCuPINT_EOT ? " eot" : "", + mask & HCuPINT_ATL ? " atl" : "", + mask & HCuPINT_SOF ? " sof" : ""); +} + +static void dump_int(struct seq_file *s, char *label, u32 mask) +{ + seq_printf(s, "%-15s %08x%s%s%s%s%s%s%s\n", label, mask, + mask & OHCI_INTR_MIE ? " MIE" : "", + mask & OHCI_INTR_RHSC ? " rhsc" : "", + mask & OHCI_INTR_FNO ? " fno" : "", + mask & OHCI_INTR_UE ? " ue" : "", + mask & OHCI_INTR_RD ? " rd" : "", + mask & OHCI_INTR_SF ? " sof" : "", + mask & OHCI_INTR_SO ? " so" : ""); +} + +static void dump_ctrl(struct seq_file *s, char *label, u32 mask) +{ + seq_printf(s, "%-15s %08x%s%s%s\n", label, mask, + mask & OHCI_CTRL_RWC ? " rwc" : "", + mask & OHCI_CTRL_RWE ? " rwe" : "", + ({ + char *hcfs; + switch (mask & OHCI_CTRL_HCFS) { + case OHCI_USB_OPER: + hcfs = " oper"; + break; + case OHCI_USB_RESET: + hcfs = " reset"; + break; + case OHCI_USB_RESUME: + hcfs = " resume"; + break; + case OHCI_USB_SUSPEND: + hcfs = " suspend"; + break; + default: + hcfs = " ?"; + } + hcfs; + })); +} + +static void dump_regs(struct seq_file *s, struct isp1362_hcd *isp1362_hcd) +{ + seq_printf(s, "HCREVISION [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCREVISION), + isp1362_read_reg32(isp1362_hcd, HCREVISION)); + seq_printf(s, "HCCONTROL [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCCONTROL), + isp1362_read_reg32(isp1362_hcd, HCCONTROL)); + seq_printf(s, "HCCMDSTAT [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCCMDSTAT), + isp1362_read_reg32(isp1362_hcd, HCCMDSTAT)); + seq_printf(s, "HCINTSTAT [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTSTAT), + isp1362_read_reg32(isp1362_hcd, HCINTSTAT)); + seq_printf(s, "HCINTENB [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTENB), + isp1362_read_reg32(isp1362_hcd, HCINTENB)); + seq_printf(s, "HCFMINTVL [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMINTVL), + isp1362_read_reg32(isp1362_hcd, HCFMINTVL)); + seq_printf(s, "HCFMREM [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMREM), + isp1362_read_reg32(isp1362_hcd, HCFMREM)); + seq_printf(s, "HCFMNUM [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMNUM), + isp1362_read_reg32(isp1362_hcd, HCFMNUM)); + seq_printf(s, "HCLSTHRESH [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCLSTHRESH), + isp1362_read_reg32(isp1362_hcd, HCLSTHRESH)); + seq_printf(s, "HCRHDESCA [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHDESCA), + isp1362_read_reg32(isp1362_hcd, HCRHDESCA)); + seq_printf(s, "HCRHDESCB [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHDESCB), + isp1362_read_reg32(isp1362_hcd, HCRHDESCB)); + seq_printf(s, "HCRHSTATUS [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHSTATUS), + isp1362_read_reg32(isp1362_hcd, HCRHSTATUS)); + seq_printf(s, "HCRHPORT1 [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHPORT1), + isp1362_read_reg32(isp1362_hcd, HCRHPORT1)); + seq_printf(s, "HCRHPORT2 [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHPORT2), + isp1362_read_reg32(isp1362_hcd, HCRHPORT2)); + seq_printf(s, "\n"); + seq_printf(s, "HCHWCFG [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCHWCFG), + isp1362_read_reg16(isp1362_hcd, HCHWCFG)); + seq_printf(s, "HCDMACFG [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCDMACFG), + isp1362_read_reg16(isp1362_hcd, HCDMACFG)); + seq_printf(s, "HCXFERCTR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCXFERCTR), + isp1362_read_reg16(isp1362_hcd, HCXFERCTR)); + seq_printf(s, "HCuPINT [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCuPINT), + isp1362_read_reg16(isp1362_hcd, HCuPINT)); + seq_printf(s, "HCuPINTENB [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCuPINTENB), + isp1362_read_reg16(isp1362_hcd, HCuPINTENB)); + seq_printf(s, "HCCHIPID [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCCHIPID), + isp1362_read_reg16(isp1362_hcd, HCCHIPID)); + seq_printf(s, "HCSCRATCH [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCSCRATCH), + isp1362_read_reg16(isp1362_hcd, HCSCRATCH)); + seq_printf(s, "HCBUFSTAT [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCBUFSTAT), + isp1362_read_reg16(isp1362_hcd, HCBUFSTAT)); + seq_printf(s, "HCDIRADDR [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCDIRADDR), + isp1362_read_reg32(isp1362_hcd, HCDIRADDR)); +#if 0 + seq_printf(s, "HCDIRDATA [%02x] %04x\n", ISP1362_REG_NO(HCDIRDATA), + isp1362_read_reg16(isp1362_hcd, HCDIRDATA)); +#endif + seq_printf(s, "HCISTLBUFSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCISTLBUFSZ), + isp1362_read_reg16(isp1362_hcd, HCISTLBUFSZ)); + seq_printf(s, "HCISTLRATE [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCISTLRATE), + isp1362_read_reg16(isp1362_hcd, HCISTLRATE)); + seq_printf(s, "\n"); + seq_printf(s, "HCINTLBUFSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLBUFSZ), + isp1362_read_reg16(isp1362_hcd, HCINTLBUFSZ)); + seq_printf(s, "HCINTLBLKSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLBLKSZ), + isp1362_read_reg16(isp1362_hcd, HCINTLBLKSZ)); + seq_printf(s, "HCINTLDONE [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLDONE), + isp1362_read_reg32(isp1362_hcd, HCINTLDONE)); + seq_printf(s, "HCINTLSKIP [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLSKIP), + isp1362_read_reg32(isp1362_hcd, HCINTLSKIP)); + seq_printf(s, "HCINTLLAST [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLLAST), + isp1362_read_reg32(isp1362_hcd, HCINTLLAST)); + seq_printf(s, "HCINTLCURR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLCURR), + isp1362_read_reg16(isp1362_hcd, HCINTLCURR)); + seq_printf(s, "\n"); + seq_printf(s, "HCATLBUFSZ [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLBUFSZ), + isp1362_read_reg16(isp1362_hcd, HCATLBUFSZ)); + seq_printf(s, "HCATLBLKSZ [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLBLKSZ), + isp1362_read_reg16(isp1362_hcd, HCATLBLKSZ)); +#if 0 + seq_printf(s, "HCATLDONE [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDONE), + isp1362_read_reg32(isp1362_hcd, HCATLDONE)); +#endif + seq_printf(s, "HCATLSKIP [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLSKIP), + isp1362_read_reg32(isp1362_hcd, HCATLSKIP)); + seq_printf(s, "HCATLLAST [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLLAST), + isp1362_read_reg32(isp1362_hcd, HCATLLAST)); + seq_printf(s, "HCATLCURR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLCURR), + isp1362_read_reg16(isp1362_hcd, HCATLCURR)); + seq_printf(s, "\n"); + seq_printf(s, "HCATLDTC [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDTC), + isp1362_read_reg16(isp1362_hcd, HCATLDTC)); + seq_printf(s, "HCATLDTCTO [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDTCTO), + isp1362_read_reg16(isp1362_hcd, HCATLDTCTO)); +} + +static int proc_isp1362_show(struct seq_file *s, void *unused) +{ + struct isp1362_hcd *isp1362_hcd = s->private; + struct isp1362_ep *ep; + int i; + + seq_printf(s, "%s\n%s version %s\n", + isp1362_hcd_to_hcd(isp1362_hcd)->product_desc, hcd_name, DRIVER_VERSION); + + /* collect statistics to help estimate potential win for + * DMA engines that care about alignment (PXA) + */ + seq_printf(s, "alignment: 16b/%ld 8b/%ld 4b/%ld 2b/%ld 1b/%ld\n", + isp1362_hcd->stat16, isp1362_hcd->stat8, isp1362_hcd->stat4, + isp1362_hcd->stat2, isp1362_hcd->stat1); + seq_printf(s, "max # ptds in ATL fifo: %d\n", isp1362_hcd->atl_queue.stat_maxptds); + seq_printf(s, "max # ptds in INTL fifo: %d\n", isp1362_hcd->intl_queue.stat_maxptds); + seq_printf(s, "max # ptds in ISTL fifo: %d\n", + max(isp1362_hcd->istl_queue[0] .stat_maxptds, + isp1362_hcd->istl_queue[1] .stat_maxptds)); + + /* FIXME: don't show the following in suspended state */ + spin_lock_irq(&isp1362_hcd->lock); + + dump_irq(s, "hc_irq_enable", isp1362_read_reg16(isp1362_hcd, HCuPINTENB)); + dump_irq(s, "hc_irq_status", isp1362_read_reg16(isp1362_hcd, HCuPINT)); + dump_int(s, "ohci_int_enable", isp1362_read_reg32(isp1362_hcd, HCINTENB)); + dump_int(s, "ohci_int_status", isp1362_read_reg32(isp1362_hcd, HCINTSTAT)); + dump_ctrl(s, "ohci_control", isp1362_read_reg32(isp1362_hcd, HCCONTROL)); + + for (i = 0; i < NUM_ISP1362_IRQS; i++) + if (isp1362_hcd->irq_stat[i]) + seq_printf(s, "%-15s: %d\n", + ISP1362_INT_NAME(i), isp1362_hcd->irq_stat[i]); + + dump_regs(s, isp1362_hcd); + list_for_each_entry(ep, &isp1362_hcd->async, schedule) { + struct urb *urb; + + seq_printf(s, "%p, ep%d%s, maxpacket %d:\n", ep, ep->epnum, + ({ + char *s; + switch (ep->nextpid) { + case USB_PID_IN: + s = "in"; + break; + case USB_PID_OUT: + s = "out"; + break; + case USB_PID_SETUP: + s = "setup"; + break; + case USB_PID_ACK: + s = "status"; + break; + default: + s = "?"; + break; + }; + s;}), ep->maxpacket) ; + list_for_each_entry(urb, &ep->hep->urb_list, urb_list) { + seq_printf(s, " urb%p, %d/%d\n", urb, + urb->actual_length, + urb->transfer_buffer_length); + } + } + if (!list_empty(&isp1362_hcd->async)) + seq_printf(s, "\n"); + dump_ptd_queue(&isp1362_hcd->atl_queue); + + seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE); + + list_for_each_entry(ep, &isp1362_hcd->periodic, schedule) { + seq_printf(s, "branch:%2d load:%3d PTD[%d] $%04x:\n", ep->branch, + isp1362_hcd->load[ep->branch], ep->ptd_index, ep->ptd_offset); + + seq_printf(s, " %d/%p (%sdev%d ep%d%s max %d)\n", + ep->interval, ep, + (ep->udev->speed == USB_SPEED_FULL) ? "" : "ls ", + ep->udev->devnum, ep->epnum, + (ep->epnum == 0) ? "" : + ((ep->nextpid == USB_PID_IN) ? + "in" : "out"), ep->maxpacket); + } + dump_ptd_queue(&isp1362_hcd->intl_queue); + + seq_printf(s, "ISO:\n"); + + list_for_each_entry(ep, &isp1362_hcd->isoc, schedule) { + seq_printf(s, " %d/%p (%sdev%d ep%d%s max %d)\n", + ep->interval, ep, + (ep->udev->speed == USB_SPEED_FULL) ? "" : "ls ", + ep->udev->devnum, ep->epnum, + (ep->epnum == 0) ? "" : + ((ep->nextpid == USB_PID_IN) ? + "in" : "out"), ep->maxpacket); + } + + spin_unlock_irq(&isp1362_hcd->lock); + seq_printf(s, "\n"); + + return 0; +} + +static int proc_isp1362_open(struct inode *inode, struct file *file) +{ + return single_open(file, proc_isp1362_show, PDE(inode)->data); +} + +static const struct file_operations proc_ops = { + .open = proc_isp1362_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* expect just one isp1362_hcd per system */ +static const char proc_filename[] = "driver/isp1362"; + +static void create_debug_file(struct isp1362_hcd *isp1362_hcd) +{ + struct proc_dir_entry *pde; + + pde = create_proc_entry(proc_filename, 0, NULL); + if (pde == NULL) { + pr_warning("%s: Failed to create debug file '%s'\n", __func__, proc_filename); + return; + } + + pde->proc_fops = &proc_ops; + pde->data = isp1362_hcd; + isp1362_hcd->pde = pde; +} + +static void remove_debug_file(struct isp1362_hcd *isp1362_hcd) +{ + if (isp1362_hcd->pde) + remove_proc_entry(proc_filename, 0); +} + +#endif + +/*-------------------------------------------------------------------------*/ + +static void isp1362_sw_reset(struct isp1362_hcd *isp1362_hcd) +{ + int tmp = 20; + unsigned long flags; + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + + isp1362_write_reg16(isp1362_hcd, HCSWRES, HCSWRES_MAGIC); + isp1362_write_reg32(isp1362_hcd, HCCMDSTAT, OHCI_HCR); + while (--tmp) { + mdelay(1); + if (!(isp1362_read_reg32(isp1362_hcd, HCCMDSTAT) & OHCI_HCR)) + break; + } + if (!tmp) + pr_err("Software reset timeout\n"); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); +} + +static int isp1362_mem_config(struct usb_hcd *hcd) +{ + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + unsigned long flags; + u32 total; + u16 istl_size = ISP1362_ISTL_BUFSIZE; + u16 intl_blksize = ISP1362_INTL_BLKSIZE + PTD_HEADER_SIZE; + u16 intl_size = ISP1362_INTL_BUFFERS * intl_blksize; + u16 atl_blksize = ISP1362_ATL_BLKSIZE + PTD_HEADER_SIZE; + u16 atl_buffers = (ISP1362_BUF_SIZE - (istl_size + intl_size)) / atl_blksize; + u16 atl_size; + int i; + + WARN_ON(istl_size & 3); + WARN_ON(atl_blksize & 3); + WARN_ON(intl_blksize & 3); + WARN_ON(atl_blksize < PTD_HEADER_SIZE); + WARN_ON(intl_blksize < PTD_HEADER_SIZE); + + BUG_ON((unsigned)ISP1362_INTL_BUFFERS > 32); + if (atl_buffers > 32) + atl_buffers = 32; + atl_size = atl_buffers * atl_blksize; + total = atl_size + intl_size + istl_size; + dev_info(hcd->self.controller, "ISP1362 Memory usage:\n"); + dev_info(hcd->self.controller, " ISTL: 2 * %4d: %4d @ $%04x:$%04x\n", + istl_size / 2, istl_size, 0, istl_size / 2); + dev_info(hcd->self.controller, " INTL: %4d * (%3u+8): %4d @ $%04x\n", + ISP1362_INTL_BUFFERS, intl_blksize - PTD_HEADER_SIZE, + intl_size, istl_size); + dev_info(hcd->self.controller, " ATL : %4d * (%3u+8): %4d @ $%04x\n", + atl_buffers, atl_blksize - PTD_HEADER_SIZE, + atl_size, istl_size + intl_size); + dev_info(hcd->self.controller, " USED/FREE: %4d %4d\n", total, + ISP1362_BUF_SIZE - total); + + if (total > ISP1362_BUF_SIZE) { + dev_err(hcd->self.controller, "%s: Memory requested: %d, available %d\n", + __func__, total, ISP1362_BUF_SIZE); + return -ENOMEM; + } + + total = istl_size + intl_size + atl_size; + spin_lock_irqsave(&isp1362_hcd->lock, flags); + + for (i = 0; i < 2; i++) { + isp1362_hcd->istl_queue[i].buf_start = i * istl_size / 2, + isp1362_hcd->istl_queue[i].buf_size = istl_size / 2; + isp1362_hcd->istl_queue[i].blk_size = 4; + INIT_LIST_HEAD(&isp1362_hcd->istl_queue[i].active); + snprintf(isp1362_hcd->istl_queue[i].name, + sizeof(isp1362_hcd->istl_queue[i].name), "ISTL%d", i); + DBG(3, "%s: %5s buf $%04x %d\n", __func__, + isp1362_hcd->istl_queue[i].name, + isp1362_hcd->istl_queue[i].buf_start, + isp1362_hcd->istl_queue[i].buf_size); + } + isp1362_write_reg16(isp1362_hcd, HCISTLBUFSZ, istl_size / 2); + + isp1362_hcd->intl_queue.buf_start = istl_size; + isp1362_hcd->intl_queue.buf_size = intl_size; + isp1362_hcd->intl_queue.buf_count = ISP1362_INTL_BUFFERS; + isp1362_hcd->intl_queue.blk_size = intl_blksize; + isp1362_hcd->intl_queue.buf_avail = isp1362_hcd->intl_queue.buf_count; + isp1362_hcd->intl_queue.skip_map = ~0; + INIT_LIST_HEAD(&isp1362_hcd->intl_queue.active); + + isp1362_write_reg16(isp1362_hcd, HCINTLBUFSZ, + isp1362_hcd->intl_queue.buf_size); + isp1362_write_reg16(isp1362_hcd, HCINTLBLKSZ, + isp1362_hcd->intl_queue.blk_size - PTD_HEADER_SIZE); + isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, ~0); + isp1362_write_reg32(isp1362_hcd, HCINTLLAST, + 1 << (ISP1362_INTL_BUFFERS - 1)); + + isp1362_hcd->atl_queue.buf_start = istl_size + intl_size; + isp1362_hcd->atl_queue.buf_size = atl_size; + isp1362_hcd->atl_queue.buf_count = atl_buffers; + isp1362_hcd->atl_queue.blk_size = atl_blksize; + isp1362_hcd->atl_queue.buf_avail = isp1362_hcd->atl_queue.buf_count; + isp1362_hcd->atl_queue.skip_map = ~0; + INIT_LIST_HEAD(&isp1362_hcd->atl_queue.active); + + isp1362_write_reg16(isp1362_hcd, HCATLBUFSZ, + isp1362_hcd->atl_queue.buf_size); + isp1362_write_reg16(isp1362_hcd, HCATLBLKSZ, + isp1362_hcd->atl_queue.blk_size - PTD_HEADER_SIZE); + isp1362_write_reg32(isp1362_hcd, HCATLSKIP, ~0); + isp1362_write_reg32(isp1362_hcd, HCATLLAST, + 1 << (atl_buffers - 1)); + + snprintf(isp1362_hcd->atl_queue.name, + sizeof(isp1362_hcd->atl_queue.name), "ATL"); + snprintf(isp1362_hcd->intl_queue.name, + sizeof(isp1362_hcd->intl_queue.name), "INTL"); + DBG(3, "%s: %5s buf $%04x %2d * %4d = %4d\n", __func__, + isp1362_hcd->intl_queue.name, + isp1362_hcd->intl_queue.buf_start, + ISP1362_INTL_BUFFERS, isp1362_hcd->intl_queue.blk_size, + isp1362_hcd->intl_queue.buf_size); + DBG(3, "%s: %5s buf $%04x %2d * %4d = %4d\n", __func__, + isp1362_hcd->atl_queue.name, + isp1362_hcd->atl_queue.buf_start, + atl_buffers, isp1362_hcd->atl_queue.blk_size, + isp1362_hcd->atl_queue.buf_size); + + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + + return 0; +} + +static int isp1362_hc_reset(struct usb_hcd *hcd) +{ + int ret = 0; + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + unsigned long t; + unsigned long timeout = 100; + unsigned long flags; + int clkrdy = 0; + + pr_info("%s:\n", __func__); + + if (isp1362_hcd->board && isp1362_hcd->board->reset) { + isp1362_hcd->board->reset(hcd->self.controller, 1); + msleep(20); + if (isp1362_hcd->board->clock) + isp1362_hcd->board->clock(hcd->self.controller, 1); + isp1362_hcd->board->reset(hcd->self.controller, 0); + } else + isp1362_sw_reset(isp1362_hcd); + + /* chip has been reset. First we need to see a clock */ + t = jiffies + msecs_to_jiffies(timeout); + while (!clkrdy && time_before_eq(jiffies, t)) { + spin_lock_irqsave(&isp1362_hcd->lock, flags); + clkrdy = isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_CLKRDY; + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + if (!clkrdy) + msleep(4); + } + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_CLKRDY); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + if (!clkrdy) { + pr_err("Clock not ready after %lums\n", timeout); + ret = -ENODEV; + } + return ret; +} + +static void isp1362_hc_stop(struct usb_hcd *hcd) +{ + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + unsigned long flags; + u32 tmp; + + pr_info("%s:\n", __func__); + + del_timer_sync(&hcd->rh_timer); + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + + isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0); + + /* Switch off power for all ports */ + tmp = isp1362_read_reg32(isp1362_hcd, HCRHDESCA); + tmp &= ~(RH_A_NPS | RH_A_PSM); + isp1362_write_reg32(isp1362_hcd, HCRHDESCA, tmp); + isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPS); + + /* Reset the chip */ + if (isp1362_hcd->board && isp1362_hcd->board->reset) + isp1362_hcd->board->reset(hcd->self.controller, 1); + else + isp1362_sw_reset(isp1362_hcd); + + if (isp1362_hcd->board && isp1362_hcd->board->clock) + isp1362_hcd->board->clock(hcd->self.controller, 0); + + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); +} + +#ifdef CHIP_BUFFER_TEST +static int isp1362_chip_test(struct isp1362_hcd *isp1362_hcd) +{ + int ret = 0; + u16 *ref; + unsigned long flags; + + ref = kmalloc(2 * ISP1362_BUF_SIZE, GFP_KERNEL); + if (ref) { + int offset; + u16 *tst = &ref[ISP1362_BUF_SIZE / 2]; + + for (offset = 0; offset < ISP1362_BUF_SIZE / 2; offset++) { + ref[offset] = ~offset; + tst[offset] = offset; + } + + for (offset = 0; offset < 4; offset++) { + int j; + + for (j = 0; j < 8; j++) { + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_write_buffer(isp1362_hcd, (u8 *)ref + offset, 0, j); + isp1362_read_buffer(isp1362_hcd, (u8 *)tst + offset, 0, j); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + + if (memcmp(ref, tst, j)) { + ret = -ENODEV; + pr_err("%s: memory check with %d byte offset %d failed\n", + __func__, j, offset); + dump_data((u8 *)ref + offset, j); + dump_data((u8 *)tst + offset, j); + } + } + } + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_write_buffer(isp1362_hcd, ref, 0, ISP1362_BUF_SIZE); + isp1362_read_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + + if (memcmp(ref, tst, ISP1362_BUF_SIZE)) { + ret = -ENODEV; + pr_err("%s: memory check failed\n", __func__); + dump_data((u8 *)tst, ISP1362_BUF_SIZE / 2); + } + + for (offset = 0; offset < 256; offset++) { + int test_size = 0; + + yield(); + + memset(tst, 0, ISP1362_BUF_SIZE); + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_write_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE); + isp1362_read_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + if (memcmp(tst, tst + (ISP1362_BUF_SIZE / (2 * sizeof(*tst))), + ISP1362_BUF_SIZE / 2)) { + pr_err("%s: Failed to clear buffer\n", __func__); + dump_data((u8 *)tst, ISP1362_BUF_SIZE); + break; + } + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_write_buffer(isp1362_hcd, ref, offset * 2, PTD_HEADER_SIZE); + isp1362_write_buffer(isp1362_hcd, ref + PTD_HEADER_SIZE / sizeof(*ref), + offset * 2 + PTD_HEADER_SIZE, test_size); + isp1362_read_buffer(isp1362_hcd, tst, offset * 2, + PTD_HEADER_SIZE + test_size); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + if (memcmp(ref, tst, PTD_HEADER_SIZE + test_size)) { + dump_data(((u8 *)ref) + offset, PTD_HEADER_SIZE + test_size); + dump_data((u8 *)tst, PTD_HEADER_SIZE + test_size); + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_read_buffer(isp1362_hcd, tst, offset * 2, + PTD_HEADER_SIZE + test_size); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + if (memcmp(ref, tst, PTD_HEADER_SIZE + test_size)) { + ret = -ENODEV; + pr_err("%s: memory check with offset %02x failed\n", + __func__, offset); + break; + } + pr_warning("%s: memory check with offset %02x ok after second read\n", + __func__, offset); + } + } + kfree(ref); + } + return ret; +} +#endif + +static int isp1362_hc_start(struct usb_hcd *hcd) +{ + int ret; + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + struct isp1362_platform_data *board = isp1362_hcd->board; + u16 hwcfg; + u16 chipid; + unsigned long flags; + + pr_info("%s:\n", __func__); + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + chipid = isp1362_read_reg16(isp1362_hcd, HCCHIPID); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + + if ((chipid & HCCHIPID_MASK) != HCCHIPID_MAGIC) { + pr_err("%s: Invalid chip ID %04x\n", __func__, chipid); + return -ENODEV; + } + +#ifdef CHIP_BUFFER_TEST + ret = isp1362_chip_test(isp1362_hcd); + if (ret) + return -ENODEV; +#endif + spin_lock_irqsave(&isp1362_hcd->lock, flags); + /* clear interrupt status and disable all interrupt sources */ + isp1362_write_reg16(isp1362_hcd, HCuPINT, 0xff); + isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0); + + /* HW conf */ + hwcfg = HCHWCFG_INT_ENABLE | HCHWCFG_DBWIDTH(1); + if (board->sel15Kres) + hwcfg |= HCHWCFG_PULLDOWN_DS2 | + (MAX_ROOT_PORTS > 1) ? HCHWCFG_PULLDOWN_DS1 : 0; + if (board->clknotstop) + hwcfg |= HCHWCFG_CLKNOTSTOP; + if (board->oc_enable) + hwcfg |= HCHWCFG_ANALOG_OC; + if (board->int_act_high) + hwcfg |= HCHWCFG_INT_POL; + if (board->int_edge_triggered) + hwcfg |= HCHWCFG_INT_TRIGGER; + if (board->dreq_act_high) + hwcfg |= HCHWCFG_DREQ_POL; + if (board->dack_act_high) + hwcfg |= HCHWCFG_DACK_POL; + isp1362_write_reg16(isp1362_hcd, HCHWCFG, hwcfg); + isp1362_show_reg(isp1362_hcd, HCHWCFG); + isp1362_write_reg16(isp1362_hcd, HCDMACFG, 0); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + + ret = isp1362_mem_config(hcd); + if (ret) + return ret; + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + + /* Root hub conf */ + isp1362_hcd->rhdesca = 0; + if (board->no_power_switching) + isp1362_hcd->rhdesca |= RH_A_NPS; + if (board->power_switching_mode) + isp1362_hcd->rhdesca |= RH_A_PSM; + if (board->potpg) + isp1362_hcd->rhdesca |= (board->potpg << 24) & RH_A_POTPGT; + else + isp1362_hcd->rhdesca |= (25 << 24) & RH_A_POTPGT; + + isp1362_write_reg32(isp1362_hcd, HCRHDESCA, isp1362_hcd->rhdesca & ~RH_A_OCPM); + isp1362_write_reg32(isp1362_hcd, HCRHDESCA, isp1362_hcd->rhdesca | RH_A_OCPM); + isp1362_hcd->rhdesca = isp1362_read_reg32(isp1362_hcd, HCRHDESCA); + + isp1362_hcd->rhdescb = RH_B_PPCM; + isp1362_write_reg32(isp1362_hcd, HCRHDESCB, isp1362_hcd->rhdescb); + isp1362_hcd->rhdescb = isp1362_read_reg32(isp1362_hcd, HCRHDESCB); + + isp1362_read_reg32(isp1362_hcd, HCFMINTVL); + isp1362_write_reg32(isp1362_hcd, HCFMINTVL, (FSMP(FI) << 16) | FI); + isp1362_write_reg32(isp1362_hcd, HCLSTHRESH, LSTHRESH); + + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + + isp1362_hcd->hc_control = OHCI_USB_OPER; + hcd->state = HC_STATE_RUNNING; + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + /* Set up interrupts */ + isp1362_hcd->intenb = OHCI_INTR_MIE | OHCI_INTR_RHSC | OHCI_INTR_UE; + isp1362_hcd->intenb |= OHCI_INTR_RD; + isp1362_hcd->irqenb = HCuPINT_OPR | HCuPINT_SUSP; + isp1362_write_reg32(isp1362_hcd, HCINTENB, isp1362_hcd->intenb); + isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb); + + /* Go operational */ + isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control); + /* enable global power */ + isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPSC | RH_HS_DRWE); + + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static struct hc_driver isp1362_hc_driver = { + .description = hcd_name, + .product_desc = "ISP1362 Host Controller", + .hcd_priv_size = sizeof(struct isp1362_hcd), + + .irq = isp1362_irq, + .flags = HCD_USB11 | HCD_MEMORY, + + .reset = isp1362_hc_reset, + .start = isp1362_hc_start, + .stop = isp1362_hc_stop, + + .urb_enqueue = isp1362_urb_enqueue, + .urb_dequeue = isp1362_urb_dequeue, + .endpoint_disable = isp1362_endpoint_disable, + + .get_frame_number = isp1362_get_frame, + + .hub_status_data = isp1362_hub_status_data, + .hub_control = isp1362_hub_control, + .bus_suspend = isp1362_bus_suspend, + .bus_resume = isp1362_bus_resume, +}; + +/*-------------------------------------------------------------------------*/ + +#define resource_len(r) (((r)->end - (r)->start) + 1) + +static int __devexit isp1362_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + struct resource *res; + + remove_debug_file(isp1362_hcd); + DBG(0, "%s: Removing HCD\n", __func__); + usb_remove_hcd(hcd); + + DBG(0, "%s: Unmapping data_reg @ %08x\n", __func__, + (u32)isp1362_hcd->data_reg); + iounmap(isp1362_hcd->data_reg); + + DBG(0, "%s: Unmapping addr_reg @ %08x\n", __func__, + (u32)isp1362_hcd->addr_reg); + iounmap(isp1362_hcd->addr_reg); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + DBG(0, "%s: release mem_region: %08lx\n", __func__, (long unsigned int)res->start); + if (res) + release_mem_region(res->start, resource_len(res)); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + DBG(0, "%s: release mem_region: %08lx\n", __func__, (long unsigned int)res->start); + if (res) + release_mem_region(res->start, resource_len(res)); + + DBG(0, "%s: put_hcd\n", __func__); + usb_put_hcd(hcd); + DBG(0, "%s: Done\n", __func__); + + return 0; +} + +static int __init isp1362_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + struct isp1362_hcd *isp1362_hcd; + struct resource *addr, *data; + void __iomem *addr_reg; + void __iomem *data_reg; + int irq; + int retval = 0; + + /* basic sanity checks first. board-specific init logic should + * have initialized this the three resources and probably board + * specific platform_data. we don't probe for IRQs, and do only + * minimal sanity checking. + */ + if (pdev->num_resources < 3) { + retval = -ENODEV; + goto err1; + } + + data = platform_get_resource(pdev, IORESOURCE_MEM, 0); + addr = platform_get_resource(pdev, IORESOURCE_MEM, 1); + irq = platform_get_irq(pdev, 0); + if (!addr || !data || irq < 0) { + retval = -ENODEV; + goto err1; + } + +#ifdef CONFIG_USB_HCD_DMA + if (pdev->dev.dma_mask) { + struct resource *dma_res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + + if (!dma_res) { + retval = -ENODEV; + goto err1; + } + isp1362_hcd->data_dma = dma_res->start; + isp1362_hcd->max_dma_size = resource_len(dma_res); + } +#else + if (pdev->dev.dma_mask) { + DBG(1, "won't do DMA"); + retval = -ENODEV; + goto err1; + } +#endif + + if (!request_mem_region(addr->start, resource_len(addr), hcd_name)) { + retval = -EBUSY; + goto err1; + } + addr_reg = ioremap(addr->start, resource_len(addr)); + if (addr_reg == NULL) { + retval = -ENOMEM; + goto err2; + } + + if (!request_mem_region(data->start, resource_len(data), hcd_name)) { + retval = -EBUSY; + goto err3; + } + data_reg = ioremap(data->start, resource_len(data)); + if (data_reg == NULL) { + retval = -ENOMEM; + goto err4; + } + + /* allocate and initialize hcd */ + hcd = usb_create_hcd(&isp1362_hc_driver, &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + retval = -ENOMEM; + goto err5; + } + hcd->rsrc_start = data->start; + isp1362_hcd = hcd_to_isp1362_hcd(hcd); + isp1362_hcd->data_reg = data_reg; + isp1362_hcd->addr_reg = addr_reg; + + isp1362_hcd->next_statechange = jiffies; + spin_lock_init(&isp1362_hcd->lock); + INIT_LIST_HEAD(&isp1362_hcd->async); + INIT_LIST_HEAD(&isp1362_hcd->periodic); + INIT_LIST_HEAD(&isp1362_hcd->isoc); + INIT_LIST_HEAD(&isp1362_hcd->remove_list); + isp1362_hcd->board = pdev->dev.platform_data; +#if USE_PLATFORM_DELAY + if (!isp1362_hcd->board->delay) { + dev_err(hcd->self.controller, "No platform delay function given\n"); + retval = -ENODEV; + goto err6; + } +#endif + +#ifdef CONFIG_ARM + if (isp1362_hcd->board) + set_irq_type(irq, isp1362_hcd->board->int_act_high ? IRQT_RISING : IRQT_FALLING); +#endif + + retval = usb_add_hcd(hcd, irq, IRQF_TRIGGER_LOW | IRQF_DISABLED | IRQF_SHARED); + if (retval != 0) + goto err6; + pr_info("%s, irq %d\n", hcd->product_desc, irq); + + create_debug_file(isp1362_hcd); + + return 0; + + err6: + DBG(0, "%s: Freeing dev %08x\n", __func__, (u32)isp1362_hcd); + usb_put_hcd(hcd); + err5: + DBG(0, "%s: Unmapping data_reg @ %08x\n", __func__, (u32)data_reg); + iounmap(data_reg); + err4: + DBG(0, "%s: Releasing mem region %08lx\n", __func__, (long unsigned int)data->start); + release_mem_region(data->start, resource_len(data)); + err3: + DBG(0, "%s: Unmapping addr_reg @ %08x\n", __func__, (u32)addr_reg); + iounmap(addr_reg); + err2: + DBG(0, "%s: Releasing mem region %08lx\n", __func__, (long unsigned int)addr->start); + release_mem_region(addr->start, resource_len(addr)); + err1: + pr_err("%s: init error, %d\n", __func__, retval); + + return retval; +} + +#ifdef CONFIG_PM +static int isp1362_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + unsigned long flags; + int retval = 0; + + DBG(0, "%s: Suspending device\n", __func__); + + if (state.event == PM_EVENT_FREEZE) { + DBG(0, "%s: Suspending root hub\n", __func__); + retval = isp1362_bus_suspend(hcd); + } else { + DBG(0, "%s: Suspending RH ports\n", __func__); + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPS); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + } + if (retval == 0) + pdev->dev.power.power_state = state; + return retval; +} + +static int isp1362_resume(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + unsigned long flags; + + DBG(0, "%s: Resuming\n", __func__); + + if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { + DBG(0, "%s: Resume RH ports\n", __func__); + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPSC); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + return 0; + } + + pdev->dev.power.power_state = PMSG_ON; + + return isp1362_bus_resume(isp1362_hcd_to_hcd(isp1362_hcd)); +} +#else +#define isp1362_suspend NULL +#define isp1362_resume NULL +#endif + +static struct platform_driver isp1362_driver = { + .probe = isp1362_probe, + .remove = __devexit_p(isp1362_remove), + + .suspend = isp1362_suspend, + .resume = isp1362_resume, + .driver = { + .name = (char *)hcd_name, + .owner = THIS_MODULE, + }, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init isp1362_init(void) +{ + if (usb_disabled()) + return -ENODEV; + pr_info("driver %s, %s\n", hcd_name, DRIVER_VERSION); + return platform_driver_register(&isp1362_driver); +} +module_init(isp1362_init); + +static void __exit isp1362_cleanup(void) +{ + platform_driver_unregister(&isp1362_driver); +} +module_exit(isp1362_cleanup); diff --git a/drivers/usb/host/isp1362.h b/drivers/usb/host/isp1362.h new file mode 100644 index 000000000000..26e44fc8776f --- /dev/null +++ b/drivers/usb/host/isp1362.h @@ -0,0 +1,1079 @@ +/* + * ISP1362 HCD (Host Controller Driver) for USB. + * + * COPYRIGHT (C) by L. Wassmann + */ + +/* ------------------------------------------------------------------------- */ +/* + * Platform specific compile time options + */ +#if defined(CONFIG_ARCH_KARO) +#include +#include +#include + +#define USE_32BIT 1 + + +/* These options are mutually eclusive */ +#define USE_PLATFORM_DELAY 1 +#define USE_NDELAY 0 +/* + * MAX_ROOT_PORTS: Number of downstream ports + * + * The chip has two USB ports, one of which can be configured as + * an USB device port, so the value of this constant is implementation + * specific. + */ +#define MAX_ROOT_PORTS 2 +#define DUMMY_DELAY_ACCESS do {} while (0) + +/* insert platform specific definitions for other machines here */ +#elif defined(CONFIG_BLACKFIN) + +#include +#define USE_32BIT 0 +#define MAX_ROOT_PORTS 2 +#define USE_PLATFORM_DELAY 0 +#define USE_NDELAY 1 + +#define DUMMY_DELAY_ACCESS \ + do { \ + bfin_read16(ASYNC_BANK0_BASE); \ + bfin_read16(ASYNC_BANK0_BASE); \ + bfin_read16(ASYNC_BANK0_BASE); \ + } while (0) + +#undef insw +#undef outsw + +#define insw delayed_insw +#define outsw delayed_outsw + +static inline void delayed_outsw(unsigned int addr, void *buf, int len) +{ + unsigned short *bp = (unsigned short *)buf; + while (len--) { + DUMMY_DELAY_ACCESS; + outw(*bp++, addr); + } +} + +static inline void delayed_insw(unsigned int addr, void *buf, int len) +{ + unsigned short *bp = (unsigned short *)buf; + while (len--) { + DUMMY_DELAY_ACCESS; + *bp++ = inw((void *)addr); + } +} + +#else + +#define MAX_ROOT_PORTS 2 + +#define USE_32BIT 0 + +/* These options are mutually eclusive */ +#define USE_PLATFORM_DELAY 0 +#define USE_NDELAY 0 + +#define DUMMY_DELAY_ACCESS do {} while (0) + +#endif + + +/* ------------------------------------------------------------------------- */ + +#define USB_RESET_WIDTH 50 +#define MAX_XFER_SIZE 1023 + +/* Buffer sizes */ +#define ISP1362_BUF_SIZE 4096 +#define ISP1362_ISTL_BUFSIZE 512 +#define ISP1362_INTL_BLKSIZE 64 +#define ISP1362_INTL_BUFFERS 16 +#define ISP1362_ATL_BLKSIZE 64 + +#define ISP1362_REG_WRITE_OFFSET 0x80 + +#ifdef ISP1362_DEBUG +typedef const unsigned int isp1362_reg_t; + +#define REG_WIDTH_16 0x000 +#define REG_WIDTH_32 0x100 +#define REG_WIDTH_MASK 0x100 +#define REG_NO_MASK 0x0ff + +#define REG_ACCESS_R 0x200 +#define REG_ACCESS_W 0x400 +#define REG_ACCESS_RW 0x600 +#define REG_ACCESS_MASK 0x600 + +#define ISP1362_REG_NO(r) ((r) & REG_NO_MASK) + +#define _BUG_ON(x) BUG_ON(x) +#define _WARN_ON(x) WARN_ON(x) + +#define ISP1362_REG(name, addr, width, rw) \ +static isp1362_reg_t ISP1362_REG_##name = ((addr) | (width) | (rw)) + +#define REG_ACCESS_TEST(r) BUG_ON(((r) & ISP1362_REG_WRITE_OFFSET) && !((r) & REG_ACCESS_W)) +#define REG_WIDTH_TEST(r, w) BUG_ON(((r) & REG_WIDTH_MASK) != (w)) +#else +typedef const unsigned char isp1362_reg_t; +#define ISP1362_REG_NO(r) (r) +#define _BUG_ON(x) do {} while (0) +#define _WARN_ON(x) do {} while (0) + +#define ISP1362_REG(name, addr, width, rw) \ +static isp1362_reg_t ISP1362_REG_##name = addr + +#define REG_ACCESS_TEST(r) do {} while (0) +#define REG_WIDTH_TEST(r, w) do {} while (0) +#endif + +/* OHCI compatible registers */ +/* + * Note: Some of the ISP1362 'OHCI' registers implement only + * a subset of the bits defined in the OHCI spec. + * + * Bitmasks for the individual bits of these registers are defined in "ohci.h" + */ +ISP1362_REG(HCREVISION, 0x00, REG_WIDTH_32, REG_ACCESS_R); +ISP1362_REG(HCCONTROL, 0x01, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCCMDSTAT, 0x02, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCINTSTAT, 0x03, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCINTENB, 0x04, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCINTDIS, 0x05, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCFMINTVL, 0x0d, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCFMREM, 0x0e, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCFMNUM, 0x0f, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCLSTHRESH, 0x11, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCRHDESCA, 0x12, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCRHDESCB, 0x13, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCRHSTATUS, 0x14, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCRHPORT1, 0x15, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCRHPORT2, 0x16, REG_WIDTH_32, REG_ACCESS_RW); + +/* Philips ISP1362 specific registers */ +ISP1362_REG(HCHWCFG, 0x20, REG_WIDTH_16, REG_ACCESS_RW); +#define HCHWCFG_DISABLE_SUSPEND (1 << 15) +#define HCHWCFG_GLOBAL_PWRDOWN (1 << 14) +#define HCHWCFG_PULLDOWN_DS1 (1 << 13) +#define HCHWCFG_PULLDOWN_DS2 (1 << 12) +#define HCHWCFG_CLKNOTSTOP (1 << 11) +#define HCHWCFG_ANALOG_OC (1 << 10) +#define HCHWCFG_ONEINT (1 << 9) +#define HCHWCFG_DACK_MODE (1 << 8) +#define HCHWCFG_ONEDMA (1 << 7) +#define HCHWCFG_DACK_POL (1 << 6) +#define HCHWCFG_DREQ_POL (1 << 5) +#define HCHWCFG_DBWIDTH_MASK (0x03 << 3) +#define HCHWCFG_DBWIDTH(n) (((n) << 3) & HCHWCFG_DBWIDTH_MASK) +#define HCHWCFG_INT_POL (1 << 2) +#define HCHWCFG_INT_TRIGGER (1 << 1) +#define HCHWCFG_INT_ENABLE (1 << 0) + +ISP1362_REG(HCDMACFG, 0x21, REG_WIDTH_16, REG_ACCESS_RW); +#define HCDMACFG_CTR_ENABLE (1 << 7) +#define HCDMACFG_BURST_LEN_MASK (0x03 << 5) +#define HCDMACFG_BURST_LEN(n) (((n) << 5) & HCDMACFG_BURST_LEN_MASK) +#define HCDMACFG_BURST_LEN_1 HCDMACFG_BURST_LEN(0) +#define HCDMACFG_BURST_LEN_4 HCDMACFG_BURST_LEN(1) +#define HCDMACFG_BURST_LEN_8 HCDMACFG_BURST_LEN(2) +#define HCDMACFG_DMA_ENABLE (1 << 4) +#define HCDMACFG_BUF_TYPE_MASK (0x07 << 1) +#define HCDMACFG_BUF_TYPE(n) (((n) << 1) & HCDMACFG_BUF_TYPE_MASK) +#define HCDMACFG_BUF_ISTL0 HCDMACFG_BUF_TYPE(0) +#define HCDMACFG_BUF_ISTL1 HCDMACFG_BUF_TYPE(1) +#define HCDMACFG_BUF_INTL HCDMACFG_BUF_TYPE(2) +#define HCDMACFG_BUF_ATL HCDMACFG_BUF_TYPE(3) +#define HCDMACFG_BUF_DIRECT HCDMACFG_BUF_TYPE(4) +#define HCDMACFG_DMA_RW_SELECT (1 << 0) + +ISP1362_REG(HCXFERCTR, 0x22, REG_WIDTH_16, REG_ACCESS_RW); + +ISP1362_REG(HCuPINT, 0x24, REG_WIDTH_16, REG_ACCESS_RW); +#define HCuPINT_SOF (1 << 0) +#define HCuPINT_ISTL0 (1 << 1) +#define HCuPINT_ISTL1 (1 << 2) +#define HCuPINT_EOT (1 << 3) +#define HCuPINT_OPR (1 << 4) +#define HCuPINT_SUSP (1 << 5) +#define HCuPINT_CLKRDY (1 << 6) +#define HCuPINT_INTL (1 << 7) +#define HCuPINT_ATL (1 << 8) +#define HCuPINT_OTG (1 << 9) + +ISP1362_REG(HCuPINTENB, 0x25, REG_WIDTH_16, REG_ACCESS_RW); +/* same bit definitions apply as for HCuPINT */ + +ISP1362_REG(HCCHIPID, 0x27, REG_WIDTH_16, REG_ACCESS_R); +#define HCCHIPID_MASK 0xff00 +#define HCCHIPID_MAGIC 0x3600 + +ISP1362_REG(HCSCRATCH, 0x28, REG_WIDTH_16, REG_ACCESS_RW); + +ISP1362_REG(HCSWRES, 0x29, REG_WIDTH_16, REG_ACCESS_W); +#define HCSWRES_MAGIC 0x00f6 + +ISP1362_REG(HCBUFSTAT, 0x2c, REG_WIDTH_16, REG_ACCESS_RW); +#define HCBUFSTAT_ISTL0_FULL (1 << 0) +#define HCBUFSTAT_ISTL1_FULL (1 << 1) +#define HCBUFSTAT_INTL_ACTIVE (1 << 2) +#define HCBUFSTAT_ATL_ACTIVE (1 << 3) +#define HCBUFSTAT_RESET_HWPP (1 << 4) +#define HCBUFSTAT_ISTL0_ACTIVE (1 << 5) +#define HCBUFSTAT_ISTL1_ACTIVE (1 << 6) +#define HCBUFSTAT_ISTL0_DONE (1 << 8) +#define HCBUFSTAT_ISTL1_DONE (1 << 9) +#define HCBUFSTAT_PAIRED_PTDPP (1 << 10) + +ISP1362_REG(HCDIRADDR, 0x32, REG_WIDTH_32, REG_ACCESS_RW); +#define HCDIRADDR_ADDR_MASK 0x0000ffff +#define HCDIRADDR_ADDR(n) (((n) << 0) & HCDIRADDR_ADDR_MASK) +#define HCDIRADDR_COUNT_MASK 0xffff0000 +#define HCDIRADDR_COUNT(n) (((n) << 16) & HCDIRADDR_COUNT_MASK) +ISP1362_REG(HCDIRDATA, 0x45, REG_WIDTH_16, REG_ACCESS_RW); + +ISP1362_REG(HCISTLBUFSZ, 0x30, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(HCISTL0PORT, 0x40, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(HCISTL1PORT, 0x42, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(HCISTLRATE, 0x47, REG_WIDTH_16, REG_ACCESS_RW); + +ISP1362_REG(HCINTLBUFSZ, 0x33, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(HCINTLPORT, 0x43, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(HCINTLBLKSZ, 0x53, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(HCINTLDONE, 0x17, REG_WIDTH_32, REG_ACCESS_R); +ISP1362_REG(HCINTLSKIP, 0x18, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCINTLLAST, 0x19, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCINTLCURR, 0x1a, REG_WIDTH_16, REG_ACCESS_R); + +ISP1362_REG(HCATLBUFSZ, 0x34, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(HCATLPORT, 0x44, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(HCATLBLKSZ, 0x54, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(HCATLDONE, 0x1b, REG_WIDTH_32, REG_ACCESS_R); +ISP1362_REG(HCATLSKIP, 0x1c, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCATLLAST, 0x1d, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCATLCURR, 0x1e, REG_WIDTH_16, REG_ACCESS_R); + +ISP1362_REG(HCATLDTC, 0x51, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(HCATLDTCTO, 0x52, REG_WIDTH_16, REG_ACCESS_RW); + + +ISP1362_REG(OTGCONTROL, 0x62, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(OTGSTATUS, 0x67, REG_WIDTH_16, REG_ACCESS_R); +ISP1362_REG(OTGINT, 0x68, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(OTGINTENB, 0x69, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(OTGTIMER, 0x6A, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(OTGALTTMR, 0x6C, REG_WIDTH_16, REG_ACCESS_RW); + +/* Philips transfer descriptor, cpu-endian */ +struct ptd { + u16 count; +#define PTD_COUNT_MSK (0x3ff << 0) +#define PTD_TOGGLE_MSK (1 << 10) +#define PTD_ACTIVE_MSK (1 << 11) +#define PTD_CC_MSK (0xf << 12) + u16 mps; +#define PTD_MPS_MSK (0x3ff << 0) +#define PTD_SPD_MSK (1 << 10) +#define PTD_LAST_MSK (1 << 11) +#define PTD_EP_MSK (0xf << 12) + u16 len; +#define PTD_LEN_MSK (0x3ff << 0) +#define PTD_DIR_MSK (3 << 10) +#define PTD_DIR_SETUP (0) +#define PTD_DIR_OUT (1) +#define PTD_DIR_IN (2) + u16 faddr; +#define PTD_FA_MSK (0x7f << 0) +/* PTD Byte 7: [StartingFrame (if ISO PTD) | StartingFrame[0..4], PollingRate[0..2] (if INT PTD)] */ +#define PTD_SF_ISO_MSK (0xff << 8) +#define PTD_SF_INT_MSK (0x1f << 8) +#define PTD_PR_MSK (0x07 << 13) +} __attribute__ ((packed, aligned(2))); +#define PTD_HEADER_SIZE sizeof(struct ptd) + +/* ------------------------------------------------------------------------- */ +/* Copied from ohci.h: */ +/* + * Hardware transfer status codes -- CC from PTD + */ +#define PTD_CC_NOERROR 0x00 +#define PTD_CC_CRC 0x01 +#define PTD_CC_BITSTUFFING 0x02 +#define PTD_CC_DATATOGGLEM 0x03 +#define PTD_CC_STALL 0x04 +#define PTD_DEVNOTRESP 0x05 +#define PTD_PIDCHECKFAIL 0x06 +#define PTD_UNEXPECTEDPID 0x07 +#define PTD_DATAOVERRUN 0x08 +#define PTD_DATAUNDERRUN 0x09 + /* 0x0A, 0x0B reserved for hardware */ +#define PTD_BUFFEROVERRUN 0x0C +#define PTD_BUFFERUNDERRUN 0x0D + /* 0x0E, 0x0F reserved for HCD */ +#define PTD_NOTACCESSED 0x0F + + +/* 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 */ -ETIMEDOUT, + /* PIDCheck */ -EPROTO, + /* UnExpPID */ -EPROTO, + /* DataOver */ -EOVERFLOW, + /* DataUnder */ -EREMOTEIO, + /* (for hw) */ -EIO, + /* (for hw) */ -EIO, + /* BufferOver */ -ECOMM, + /* BuffUnder */ -ENOSR, + /* (for HCD) */ -EALREADY, + /* (for HCD) */ -EALREADY +}; + + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ +#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ +#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +# define OHCI_USB_RESET (0 << 6) +# define OHCI_USB_RESUME (1 << 6) +# define OHCI_USB_OPER (2 << 6) +# define OHCI_USB_SUSPEND (3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR (1 << 0) /* host controller reset */ +#define OHCI_SOC (3 << 16) /* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ +#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ +#define OHCI_INTR_SF (1 << 2) /* start frame */ +#define OHCI_INTR_RD (1 << 3) /* resume detect */ +#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ +#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ +#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ +#define OHCI_INTR_OC (1 << 30) /* ownership change */ +#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status*/ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + +#define FI 0x2edf /* 12000 bits per frame (-1) */ +#define FSMP(fi) (0x7fff & ((6 * ((fi) - 210)) / 7)) +#define LSTHRESH 0x628 /* lowspeed bit threshold */ + +/* ------------------------------------------------------------------------- */ + +/* PTD accessor macros. */ +#define PTD_GET_COUNT(p) (((p)->count & PTD_COUNT_MSK) >> 0) +#define PTD_COUNT(v) (((v) << 0) & PTD_COUNT_MSK) +#define PTD_GET_TOGGLE(p) (((p)->count & PTD_TOGGLE_MSK) >> 10) +#define PTD_TOGGLE(v) (((v) << 10) & PTD_TOGGLE_MSK) +#define PTD_GET_ACTIVE(p) (((p)->count & PTD_ACTIVE_MSK) >> 11) +#define PTD_ACTIVE(v) (((v) << 11) & PTD_ACTIVE_MSK) +#define PTD_GET_CC(p) (((p)->count & PTD_CC_MSK) >> 12) +#define PTD_CC(v) (((v) << 12) & PTD_CC_MSK) +#define PTD_GET_MPS(p) (((p)->mps & PTD_MPS_MSK) >> 0) +#define PTD_MPS(v) (((v) << 0) & PTD_MPS_MSK) +#define PTD_GET_SPD(p) (((p)->mps & PTD_SPD_MSK) >> 10) +#define PTD_SPD(v) (((v) << 10) & PTD_SPD_MSK) +#define PTD_GET_LAST(p) (((p)->mps & PTD_LAST_MSK) >> 11) +#define PTD_LAST(v) (((v) << 11) & PTD_LAST_MSK) +#define PTD_GET_EP(p) (((p)->mps & PTD_EP_MSK) >> 12) +#define PTD_EP(v) (((v) << 12) & PTD_EP_MSK) +#define PTD_GET_LEN(p) (((p)->len & PTD_LEN_MSK) >> 0) +#define PTD_LEN(v) (((v) << 0) & PTD_LEN_MSK) +#define PTD_GET_DIR(p) (((p)->len & PTD_DIR_MSK) >> 10) +#define PTD_DIR(v) (((v) << 10) & PTD_DIR_MSK) +#define PTD_GET_FA(p) (((p)->faddr & PTD_FA_MSK) >> 0) +#define PTD_FA(v) (((v) << 0) & PTD_FA_MSK) +#define PTD_GET_SF_INT(p) (((p)->faddr & PTD_SF_INT_MSK) >> 8) +#define PTD_SF_INT(v) (((v) << 8) & PTD_SF_INT_MSK) +#define PTD_GET_SF_ISO(p) (((p)->faddr & PTD_SF_ISO_MSK) >> 8) +#define PTD_SF_ISO(v) (((v) << 8) & PTD_SF_ISO_MSK) +#define PTD_GET_PR(p) (((p)->faddr & PTD_PR_MSK) >> 13) +#define PTD_PR(v) (((v) << 13) & PTD_PR_MSK) + +#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */ +#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE) + +struct isp1362_ep { + struct usb_host_endpoint *hep; + struct usb_device *udev; + + /* philips transfer descriptor */ + struct ptd ptd; + + u8 maxpacket; + u8 epnum; + u8 nextpid; + u16 error_count; + u16 length; /* of current packet */ + s16 ptd_offset; /* buffer offset in ISP1362 where + PTD has been stored + (for access thru HCDIRDATA) */ + int ptd_index; + int num_ptds; + void *data; /* to databuf */ + /* queue of active EPs (the ones transmitted to the chip) */ + struct list_head active; + + /* periodic schedule */ + u8 branch; + u16 interval; + u16 load; + u16 last_iso; + + /* async schedule */ + struct list_head schedule; /* list of all EPs that need processing */ + struct list_head remove_list; + int num_req; +}; + +struct isp1362_ep_queue { + struct list_head active; /* list of PTDs currently processed by HC */ + atomic_t finishing; + unsigned long buf_map; + unsigned long skip_map; + int free_ptd; + u16 buf_start; + u16 buf_size; + u16 blk_size; /* PTD buffer block size for ATL and INTL */ + u8 buf_count; + u8 buf_avail; + char name[16]; + + /* for statistical tracking */ + u8 stat_maxptds; /* Max # of ptds seen simultaneously in fifo */ + u8 ptd_count; /* number of ptds submitted to this queue */ +}; + +struct isp1362_hcd { + spinlock_t lock; + void __iomem *addr_reg; + void __iomem *data_reg; + + struct isp1362_platform_data *board; + + struct proc_dir_entry *pde; + unsigned long stat1, stat2, stat4, stat8, stat16; + + /* HC registers */ + u32 intenb; /* "OHCI" interrupts */ + u16 irqenb; /* uP interrupts */ + + /* Root hub registers */ + u32 rhdesca; + u32 rhdescb; + u32 rhstatus; + u32 rhport[MAX_ROOT_PORTS]; + unsigned long next_statechange; + + /* HC control reg shadow copy */ + u32 hc_control; + + /* async schedule: control, bulk */ + struct list_head async; + + /* periodic schedule: int */ + u16 load[PERIODIC_SIZE]; + struct list_head periodic; + u16 fmindex; + + /* periodic schedule: isochronous */ + struct list_head isoc; + int istl_flip:1; + int irq_active:1; + + /* Schedules for the current frame */ + struct isp1362_ep_queue atl_queue; + struct isp1362_ep_queue intl_queue; + struct isp1362_ep_queue istl_queue[2]; + + /* list of PTDs retrieved from HC */ + struct list_head remove_list; + enum { + ISP1362_INT_SOF, + ISP1362_INT_ISTL0, + ISP1362_INT_ISTL1, + ISP1362_INT_EOT, + ISP1362_INT_OPR, + ISP1362_INT_SUSP, + ISP1362_INT_CLKRDY, + ISP1362_INT_INTL, + ISP1362_INT_ATL, + ISP1362_INT_OTG, + NUM_ISP1362_IRQS + } IRQ_NAMES; + unsigned int irq_stat[NUM_ISP1362_IRQS]; + int req_serial; +}; + +static inline const char *ISP1362_INT_NAME(int n) +{ + switch (n) { + case ISP1362_INT_SOF: return "SOF"; + case ISP1362_INT_ISTL0: return "ISTL0"; + case ISP1362_INT_ISTL1: return "ISTL1"; + case ISP1362_INT_EOT: return "EOT"; + case ISP1362_INT_OPR: return "OPR"; + case ISP1362_INT_SUSP: return "SUSP"; + case ISP1362_INT_CLKRDY: return "CLKRDY"; + case ISP1362_INT_INTL: return "INTL"; + case ISP1362_INT_ATL: return "ATL"; + case ISP1362_INT_OTG: return "OTG"; + default: return "unknown"; + } +} + +static inline void ALIGNSTAT(struct isp1362_hcd *isp1362_hcd, void *ptr) +{ + unsigned p = (unsigned)ptr; + if (!(p & 0xf)) + isp1362_hcd->stat16++; + else if (!(p & 0x7)) + isp1362_hcd->stat8++; + else if (!(p & 0x3)) + isp1362_hcd->stat4++; + else if (!(p & 0x1)) + isp1362_hcd->stat2++; + else + isp1362_hcd->stat1++; +} + +static inline struct isp1362_hcd *hcd_to_isp1362_hcd(struct usb_hcd *hcd) +{ + return (struct isp1362_hcd *) (hcd->hcd_priv); +} + +static inline struct usb_hcd *isp1362_hcd_to_hcd(struct isp1362_hcd *isp1362_hcd) +{ + return container_of((void *)isp1362_hcd, struct usb_hcd, hcd_priv); +} + +#define frame_before(f1, f2) ((s16)((u16)f1 - (u16)f2) < 0) + +/* + * ISP1362 HW Interface + */ + +#ifdef ISP1362_DEBUG +#define DBG(level, fmt...) \ + do { \ + if (dbg_level > level) \ + pr_debug(fmt); \ + } while (0) +#define _DBG(level, fmt...) \ + do { \ + if (dbg_level > level) \ + printk(fmt); \ + } while (0) +#else +#define DBG(fmt...) do {} while (0) +#define _DBG DBG +#endif + +#ifdef VERBOSE +# define VDBG(fmt...) DBG(3, fmt) +#else +# define VDBG(fmt...) do {} while (0) +#endif + +#ifdef REGISTERS +# define RDBG(fmt...) DBG(1, fmt) +#else +# define RDBG(fmt...) do {} while (0) +#endif + +#ifdef URB_TRACE +#define URB_DBG(fmt...) DBG(0, fmt) +#else +#define URB_DBG(fmt...) do {} while (0) +#endif + + +#if USE_PLATFORM_DELAY +#if USE_NDELAY +#error USE_PLATFORM_DELAY and USE_NDELAY defined simultaneously. +#endif +#define isp1362_delay(h, d) (h)->board->delay(isp1362_hcd_to_hcd(h)->self.controller, d) +#elif USE_NDELAY +#define isp1362_delay(h, d) ndelay(d) +#else +#define isp1362_delay(h, d) do {} while (0) +#endif + +#define get_urb(ep) ({ \ + BUG_ON(list_empty(&ep->hep->urb_list)); \ + container_of(ep->hep->urb_list.next, struct urb, urb_list); \ +}) + +/* basic access functions for ISP1362 chip registers */ +/* NOTE: The contents of the address pointer register cannot be read back! The driver must ensure, + * that all register accesses are performed with interrupts disabled, since the interrupt + * handler has no way of restoring the previous state. + */ +static void isp1362_write_addr(struct isp1362_hcd *isp1362_hcd, isp1362_reg_t reg) +{ + /*_BUG_ON((reg & ISP1362_REG_WRITE_OFFSET) && !(reg & REG_ACCESS_W));*/ + REG_ACCESS_TEST(reg); + _BUG_ON(!irqs_disabled()); + DUMMY_DELAY_ACCESS; + writew(ISP1362_REG_NO(reg), isp1362_hcd->addr_reg); + DUMMY_DELAY_ACCESS; + isp1362_delay(isp1362_hcd, 1); +} + +static void isp1362_write_data16(struct isp1362_hcd *isp1362_hcd, u16 val) +{ + _BUG_ON(!irqs_disabled()); + DUMMY_DELAY_ACCESS; + writew(val, isp1362_hcd->data_reg); +} + +static u16 isp1362_read_data16(struct isp1362_hcd *isp1362_hcd) +{ + u16 val; + + _BUG_ON(!irqs_disabled()); + DUMMY_DELAY_ACCESS; + val = readw(isp1362_hcd->data_reg); + + return val; +} + +static void isp1362_write_data32(struct isp1362_hcd *isp1362_hcd, u32 val) +{ + _BUG_ON(!irqs_disabled()); +#if USE_32BIT + DUMMY_DELAY_ACCESS; + writel(val, isp1362_hcd->data_reg); +#else + DUMMY_DELAY_ACCESS; + writew((u16)val, isp1362_hcd->data_reg); + DUMMY_DELAY_ACCESS; + writew(val >> 16, isp1362_hcd->data_reg); +#endif +} + +static u32 isp1362_read_data32(struct isp1362_hcd *isp1362_hcd) +{ + u32 val; + + _BUG_ON(!irqs_disabled()); +#if USE_32BIT + DUMMY_DELAY_ACCESS; + val = readl(isp1362_hcd->data_reg); +#else + DUMMY_DELAY_ACCESS; + val = (u32)readw(isp1362_hcd->data_reg); + DUMMY_DELAY_ACCESS; + val |= (u32)readw(isp1362_hcd->data_reg) << 16; +#endif + return val; +} + +/* use readsw/writesw to access the fifo whenever possible */ +/* assume HCDIRDATA or XFERCTR & addr_reg have been set up */ +static void isp1362_read_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 len) +{ + u8 *dp = buf; + u16 data; + + if (!len) + return; + + _BUG_ON(!irqs_disabled()); + + RDBG("%s: Reading %d byte from fifo to mem @ %p\n", __func__, len, buf); +#if USE_32BIT + if (len >= 4) { + RDBG("%s: Using readsl for %d dwords\n", __func__, len >> 2); + readsl(isp1362_hcd->data_reg, dp, len >> 2); + dp += len & ~3; + len &= 3; + } +#endif + if (len >= 2) { + RDBG("%s: Using readsw for %d words\n", __func__, len >> 1); + insw((unsigned long)isp1362_hcd->data_reg, dp, len >> 1); + dp += len & ~1; + len &= 1; + } + + BUG_ON(len & ~1); + if (len > 0) { + data = isp1362_read_data16(isp1362_hcd); + RDBG("%s: Reading trailing byte %02x to mem @ %08x\n", __func__, + (u8)data, (u32)dp); + *dp = (u8)data; + } +} + +static void isp1362_write_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 len) +{ + u8 *dp = buf; + u16 data; + + if (!len) + return; + + if ((unsigned)dp & 0x1) { + /* not aligned */ + for (; len > 1; len -= 2) { + data = *dp++; + data |= *dp++ << 8; + isp1362_write_data16(isp1362_hcd, data); + } + if (len) + isp1362_write_data16(isp1362_hcd, *dp); + return; + } + + _BUG_ON(!irqs_disabled()); + + RDBG("%s: Writing %d byte to fifo from memory @%p\n", __func__, len, buf); +#if USE_32BIT + if (len >= 4) { + RDBG("%s: Using writesl for %d dwords\n", __func__, len >> 2); + writesl(isp1362_hcd->data_reg, dp, len >> 2); + dp += len & ~3; + len &= 3; + } +#endif + if (len >= 2) { + RDBG("%s: Using writesw for %d words\n", __func__, len >> 1); + outsw((unsigned long)isp1362_hcd->data_reg, dp, len >> 1); + dp += len & ~1; + len &= 1; + } + + BUG_ON(len & ~1); + if (len > 0) { + /* finally write any trailing byte; we don't need to care + * about the high byte of the last word written + */ + data = (u16)*dp; + RDBG("%s: Sending trailing byte %02x from mem @ %08x\n", __func__, + data, (u32)dp); + isp1362_write_data16(isp1362_hcd, data); + } +} + +#define isp1362_read_reg16(d, r) ({ \ + u16 __v; \ + REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_16); \ + isp1362_write_addr(d, ISP1362_REG_##r); \ + __v = isp1362_read_data16(d); \ + RDBG("%s: Read %04x from %s[%02x]\n", __func__, __v, #r, \ + ISP1362_REG_NO(ISP1362_REG_##r)); \ + __v; \ +}) + +#define isp1362_read_reg32(d, r) ({ \ + u32 __v; \ + REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_32); \ + isp1362_write_addr(d, ISP1362_REG_##r); \ + __v = isp1362_read_data32(d); \ + RDBG("%s: Read %08x from %s[%02x]\n", __func__, __v, #r, \ + ISP1362_REG_NO(ISP1362_REG_##r)); \ + __v; \ +}) + +#define isp1362_write_reg16(d, r, v) { \ + REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_16); \ + isp1362_write_addr(d, (ISP1362_REG_##r) | ISP1362_REG_WRITE_OFFSET); \ + isp1362_write_data16(d, (u16)(v)); \ + RDBG("%s: Wrote %04x to %s[%02x]\n", __func__, (u16)(v), #r, \ + ISP1362_REG_NO(ISP1362_REG_##r)); \ +} + +#define isp1362_write_reg32(d, r, v) { \ + REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_32); \ + isp1362_write_addr(d, (ISP1362_REG_##r) | ISP1362_REG_WRITE_OFFSET); \ + isp1362_write_data32(d, (u32)(v)); \ + RDBG("%s: Wrote %08x to %s[%02x]\n", __func__, (u32)(v), #r, \ + ISP1362_REG_NO(ISP1362_REG_##r)); \ +} + +#define isp1362_set_mask16(d, r, m) { \ + u16 __v; \ + __v = isp1362_read_reg16(d, r); \ + if ((__v | m) != __v) \ + isp1362_write_reg16(d, r, __v | m); \ +} + +#define isp1362_clr_mask16(d, r, m) { \ + u16 __v; \ + __v = isp1362_read_reg16(d, r); \ + if ((__v & ~m) != __v) \ + isp1362_write_reg16(d, r, __v & ~m); \ +} + +#define isp1362_set_mask32(d, r, m) { \ + u32 __v; \ + __v = isp1362_read_reg32(d, r); \ + if ((__v | m) != __v) \ + isp1362_write_reg32(d, r, __v | m); \ +} + +#define isp1362_clr_mask32(d, r, m) { \ + u32 __v; \ + __v = isp1362_read_reg32(d, r); \ + if ((__v & ~m) != __v) \ + isp1362_write_reg32(d, r, __v & ~m); \ +} + +#ifdef ISP1362_DEBUG +#define isp1362_show_reg(d, r) { \ + if ((ISP1362_REG_##r & REG_WIDTH_MASK) == REG_WIDTH_32) \ + DBG(0, "%-12s[%02x]: %08x\n", #r, \ + ISP1362_REG_NO(ISP1362_REG_##r), isp1362_read_reg32(d, r)); \ + else \ + DBG(0, "%-12s[%02x]: %04x\n", #r, \ + ISP1362_REG_NO(ISP1362_REG_##r), isp1362_read_reg16(d, r)); \ +} +#else +#define isp1362_show_reg(d, r) do {} while (0) +#endif + +static void __attribute__((__unused__)) isp1362_show_regs(struct isp1362_hcd *isp1362_hcd) +{ + isp1362_show_reg(isp1362_hcd, HCREVISION); + isp1362_show_reg(isp1362_hcd, HCCONTROL); + isp1362_show_reg(isp1362_hcd, HCCMDSTAT); + isp1362_show_reg(isp1362_hcd, HCINTSTAT); + isp1362_show_reg(isp1362_hcd, HCINTENB); + isp1362_show_reg(isp1362_hcd, HCFMINTVL); + isp1362_show_reg(isp1362_hcd, HCFMREM); + isp1362_show_reg(isp1362_hcd, HCFMNUM); + isp1362_show_reg(isp1362_hcd, HCLSTHRESH); + isp1362_show_reg(isp1362_hcd, HCRHDESCA); + isp1362_show_reg(isp1362_hcd, HCRHDESCB); + isp1362_show_reg(isp1362_hcd, HCRHSTATUS); + isp1362_show_reg(isp1362_hcd, HCRHPORT1); + isp1362_show_reg(isp1362_hcd, HCRHPORT2); + + isp1362_show_reg(isp1362_hcd, HCHWCFG); + isp1362_show_reg(isp1362_hcd, HCDMACFG); + isp1362_show_reg(isp1362_hcd, HCXFERCTR); + isp1362_show_reg(isp1362_hcd, HCuPINT); + + if (in_interrupt()) + DBG(0, "%-12s[%02x]: %04x\n", "HCuPINTENB", + ISP1362_REG_NO(ISP1362_REG_HCuPINTENB), isp1362_hcd->irqenb); + else + isp1362_show_reg(isp1362_hcd, HCuPINTENB); + isp1362_show_reg(isp1362_hcd, HCCHIPID); + isp1362_show_reg(isp1362_hcd, HCSCRATCH); + isp1362_show_reg(isp1362_hcd, HCBUFSTAT); + isp1362_show_reg(isp1362_hcd, HCDIRADDR); + /* Access would advance fifo + * isp1362_show_reg(isp1362_hcd, HCDIRDATA); + */ + isp1362_show_reg(isp1362_hcd, HCISTLBUFSZ); + isp1362_show_reg(isp1362_hcd, HCISTLRATE); + isp1362_show_reg(isp1362_hcd, HCINTLBUFSZ); + isp1362_show_reg(isp1362_hcd, HCINTLBLKSZ); + isp1362_show_reg(isp1362_hcd, HCINTLDONE); + isp1362_show_reg(isp1362_hcd, HCINTLSKIP); + isp1362_show_reg(isp1362_hcd, HCINTLLAST); + isp1362_show_reg(isp1362_hcd, HCINTLCURR); + isp1362_show_reg(isp1362_hcd, HCATLBUFSZ); + isp1362_show_reg(isp1362_hcd, HCATLBLKSZ); + /* only valid after ATL_DONE interrupt + * isp1362_show_reg(isp1362_hcd, HCATLDONE); + */ + isp1362_show_reg(isp1362_hcd, HCATLSKIP); + isp1362_show_reg(isp1362_hcd, HCATLLAST); + isp1362_show_reg(isp1362_hcd, HCATLCURR); + isp1362_show_reg(isp1362_hcd, HCATLDTC); + isp1362_show_reg(isp1362_hcd, HCATLDTCTO); +} + +static void isp1362_write_diraddr(struct isp1362_hcd *isp1362_hcd, u16 offset, u16 len) +{ + _BUG_ON(offset & 1); + _BUG_ON(offset >= ISP1362_BUF_SIZE); + _BUG_ON(len > ISP1362_BUF_SIZE); + _BUG_ON(offset + len > ISP1362_BUF_SIZE); + len = (len + 1) & ~1; + + isp1362_clr_mask16(isp1362_hcd, HCDMACFG, HCDMACFG_CTR_ENABLE); + isp1362_write_reg32(isp1362_hcd, HCDIRADDR, + HCDIRADDR_ADDR(offset) | HCDIRADDR_COUNT(len)); +} + +static void isp1362_read_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 offset, int len) +{ + _BUG_ON(offset & 1); + + isp1362_write_diraddr(isp1362_hcd, offset, len); + + DBG(3, "%s: Reading %d byte from buffer @%04x to memory @ %08x\n", __func__, + len, offset, (u32)buf); + + isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT); + _WARN_ON((isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT)); + + isp1362_write_addr(isp1362_hcd, ISP1362_REG_HCDIRDATA); + + isp1362_read_fifo(isp1362_hcd, buf, len); + _WARN_ON(!(isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT)); + isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT); + _WARN_ON((isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT)); +} + +static void isp1362_write_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 offset, int len) +{ + _BUG_ON(offset & 1); + + isp1362_write_diraddr(isp1362_hcd, offset, len); + + DBG(3, "%s: Writing %d byte to buffer @%04x from memory @ %08x\n", __func__, + len, offset, (u32)buf); + + isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT); + _WARN_ON((isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT)); + + isp1362_write_addr(isp1362_hcd, ISP1362_REG_HCDIRDATA | ISP1362_REG_WRITE_OFFSET); + isp1362_write_fifo(isp1362_hcd, buf, len); + + _WARN_ON(!(isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT)); + isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT); + _WARN_ON((isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT)); +} + +static void __attribute__((unused)) dump_data(char *buf, int len) +{ + if (dbg_level > 0) { + int k; + int lf = 0; + + for (k = 0; k < len; ++k) { + if (!lf) + DBG(0, "%04x:", k); + printk(" %02x", ((u8 *) buf)[k]); + lf = 1; + if (!k) + continue; + if (k % 16 == 15) { + printk("\n"); + lf = 0; + continue; + } + if (k % 8 == 7) + printk(" "); + if (k % 4 == 3) + printk(" "); + } + if (lf) + printk("\n"); + } +} + +#if defined(ISP1362_DEBUG) && defined(PTD_TRACE) + +static void dump_ptd(struct ptd *ptd) +{ + DBG(0, "EP %p: CC=%x EP=%d DIR=%x CNT=%d LEN=%d MPS=%d TGL=%x ACT=%x FA=%d SPD=%x SF=%x PR=%x LST=%x\n", + container_of(ptd, struct isp1362_ep, ptd), + PTD_GET_CC(ptd), PTD_GET_EP(ptd), PTD_GET_DIR(ptd), + PTD_GET_COUNT(ptd), PTD_GET_LEN(ptd), PTD_GET_MPS(ptd), + PTD_GET_TOGGLE(ptd), PTD_GET_ACTIVE(ptd), PTD_GET_FA(ptd), + PTD_GET_SPD(ptd), PTD_GET_SF_INT(ptd), PTD_GET_PR(ptd), PTD_GET_LAST(ptd)); + DBG(0, " %04x %04x %04x %04x\n", ptd->count, ptd->mps, ptd->len, ptd->faddr); +} + +static void dump_ptd_out_data(struct ptd *ptd, u8 *buf) +{ + if (dbg_level > 0) { + if (PTD_GET_DIR(ptd) != PTD_DIR_IN && PTD_GET_LEN(ptd)) { + DBG(0, "--out->\n"); + dump_data(buf, PTD_GET_LEN(ptd)); + } + } +} + +static void dump_ptd_in_data(struct ptd *ptd, u8 *buf) +{ + if (dbg_level > 0) { + if (PTD_GET_DIR(ptd) == PTD_DIR_IN && PTD_GET_COUNT(ptd)) { + DBG(0, "<--in--\n"); + dump_data(buf, PTD_GET_COUNT(ptd)); + } + DBG(0, "-----\n"); + } +} + +static void dump_ptd_queue(struct isp1362_ep_queue *epq) +{ + struct isp1362_ep *ep; + int dbg = dbg_level; + + dbg_level = 1; + list_for_each_entry(ep, &epq->active, active) { + dump_ptd(&ep->ptd); + dump_data(ep->data, ep->length); + } + dbg_level = dbg; +} +#else +#define dump_ptd(ptd) do {} while (0) +#define dump_ptd_in_data(ptd, buf) do {} while (0) +#define dump_ptd_out_data(ptd, buf) do {} while (0) +#define dump_ptd_data(ptd, buf) do {} while (0) +#define dump_ptd_queue(epq) do {} while (0) +#endif diff --git a/include/linux/usb/isp1362.h b/include/linux/usb/isp1362.h new file mode 100644 index 000000000000..642684bb9292 --- /dev/null +++ b/include/linux/usb/isp1362.h @@ -0,0 +1,46 @@ +/* + * board initialization code should put one of these into dev->platform_data + * and place the isp1362 onto platform_bus. + */ + +#ifndef __LINUX_USB_ISP1362_H__ +#define __LINUX_USB_ISP1362_H__ + +struct isp1362_platform_data { + /* Enable internal pulldown resistors on downstream ports */ + unsigned sel15Kres:1; + /* Clock cannot be stopped */ + unsigned clknotstop:1; + /* On-chip overcurrent protection */ + unsigned oc_enable:1; + /* INT output polarity */ + unsigned int_act_high:1; + /* INT edge or level triggered */ + unsigned int_edge_triggered:1; + /* DREQ output polarity */ + unsigned dreq_act_high:1; + /* DACK input polarity */ + unsigned dack_act_high:1; + /* chip can be resumed via H_WAKEUP pin */ + unsigned remote_wakeup_connected:1; + /* Switch or not to switch (keep always powered) */ + unsigned no_power_switching:1; + /* Ganged port power switching (0) or individual port power switching (1) */ + unsigned power_switching_mode:1; + /* Given port_power, msec/2 after power on till power good */ + u8 potpg; + /* Hardware reset set/clear */ + void (*reset) (struct device *dev, int set); + /* Clock start/stop */ + void (*clock) (struct device *dev, int start); + /* Inter-io delay (ns). The chip is picky about access timings; it + * expects at least: + * 110ns delay between consecutive accesses to DATA_REG, + * 300ns delay between access to ADDR_REG and DATA_REG (registers) + * 462ns delay between access to ADDR_REG and DATA_REG (buffer memory) + * WE MUST NOT be activated during these intervals (even without CS!) + */ + void (*delay) (struct device *dev, unsigned int delay); +}; + +#endif -- cgit v1.2.3-71-gd317 From c35013087aa9b10e4674b53b7c8f7966de83c194 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 29 Jul 2009 14:23:25 +0300 Subject: USB: audio: guard kernel-only code with __KERNEL__ include/linux/usb/audio.h is exported to userspace, so part of this file that is for internal kernel usage need to be guarded with ifdef __KERNEL__. This way make headers_install will stript it out. Signed-off-by: Michael S. Tsirkin Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/audio.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/linux/usb') diff --git a/include/linux/usb/audio.h b/include/linux/usb/audio.h index 7b33c493917f..eaf9dffe0a01 100644 --- a/include/linux/usb/audio.h +++ b/include/linux/usb/audio.h @@ -276,6 +276,8 @@ struct uac_iso_endpoint_descriptor { #define UAC_FU_BASS_BOOST (1 << (UAC_BASS_BOOST_CONTROL - 1)) #define UAC_FU_LOUDNESS (1 << (UAC_LOUDNESS_CONTROL - 1)) +#ifdef __KERNEL__ + struct usb_audio_control { struct list_head list; const char *name; @@ -294,4 +296,6 @@ struct usb_audio_control_selector { struct usb_descriptor_header *desc; }; +#endif /* __KERNEL__ */ + #endif /* __LINUX_USB_AUDIO_H */ -- cgit v1.2.3-71-gd317 From 8e8dce065088833fc418bfa5fbf035cb0726c04c Mon Sep 17 00:00:00 2001 From: David VomLehn Date: Fri, 28 Aug 2009 12:54:27 -0700 Subject: USB: use kfifo to buffer usb-generic serial writes When do_output_char() attempts to write a carriage return/line feed sequence, it first checks to see how much buffer room is available. If there are at least two characters free, it will write the carriage return/line feed with two calls to tty_put_char(). It calls the tty_operation functions write() for devices that don't support the tty_operations function put_char(). If the USB generic serial device's write URB is not in use, it will return the buffer size when asked how much room is available. The write() of the carriage return will cause it to mark the write URB busy, so the subsequent write() of the line feed will be ignored. This patch uses the kfifo infrastructure to implement a write FIFO that accurately returns the amount of space available in the buffer. Signed-off-by: David VomLehn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 206 ++++++++++++++++++++++++---------------- drivers/usb/serial/usb-serial.c | 7 ++ include/linux/usb/serial.h | 2 + 3 files changed, 134 insertions(+), 81 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index d9398e9f30ce..deba08c7a015 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -19,7 +19,7 @@ #include #include #include - +#include static int debug; @@ -166,24 +166,6 @@ static void generic_cleanup(struct usb_serial_port *port) } } -int usb_serial_generic_resume(struct usb_serial *serial) -{ - struct usb_serial_port *port; - int i, c = 0, r; - - for (i = 0; i < serial->num_ports; i++) { - port = serial->port[i]; - if (port->port.count && port->read_urb) { - r = usb_submit_urb(port->read_urb, GFP_NOIO); - if (r < 0) - c++; - } - } - - return c ? -EIO : 0; -} -EXPORT_SYMBOL_GPL(usb_serial_generic_resume); - void usb_serial_generic_close(struct usb_serial_port *port) { dbg("%s - port %d", __func__, port->number); @@ -272,12 +254,81 @@ error_no_buffer: return bwrite; } +/** + * usb_serial_generic_write_start - kick off an URB write + * @port: Pointer to the &struct usb_serial_port data + * + * Returns the number of bytes queued on success. This will be zero if there + * was nothing to send. Otherwise, it returns a negative errno value + */ +static int usb_serial_generic_write_start(struct usb_serial_port *port) +{ + struct usb_serial *serial = port->serial; + unsigned char *data; + int result; + int count; + unsigned long flags; + bool start_io; + + /* Atomically determine whether we can and need to start a USB + * operation. */ + spin_lock_irqsave(&port->lock, flags); + if (port->write_urb_busy) + start_io = false; + else { + start_io = (__kfifo_len(port->write_fifo) != 0); + port->write_urb_busy = start_io; + } + spin_unlock_irqrestore(&port->lock, flags); + + if (!start_io) + return 0; + + data = port->write_urb->transfer_buffer; + count = kfifo_get(port->write_fifo, data, port->bulk_out_size); + usb_serial_debug_data(debug, &port->dev, __func__, count, data); + + /* set up our urb */ + usb_fill_bulk_urb(port->write_urb, serial->dev, + usb_sndbulkpipe(serial->dev, + port->bulk_out_endpointAddress), + port->write_urb->transfer_buffer, count, + ((serial->type->write_bulk_callback) ? + serial->type->write_bulk_callback : + usb_serial_generic_write_bulk_callback), + port); + + /* send the data out the bulk port */ + result = usb_submit_urb(port->write_urb, GFP_ATOMIC); + if (result) { + dev_err(&port->dev, + "%s - failed submitting write urb, error %d\n", + __func__, result); + /* don't have to grab the lock here, as we will + retry if != 0 */ + port->write_urb_busy = 0; + } else + result = count; + + return result; +} + +/** + * usb_serial_generic_write - generic write function for serial USB devices + * @tty: Pointer to &struct tty_struct for the device + * @port: Pointer to the &usb_serial_port structure for the device + * @buf: Pointer to the data to write + * @count: Number of bytes to write + * + * Returns the number of characters actually written, which may be anything + * from zero to @count. If an error occurs, it returns the negative errno + * value. + */ int usb_serial_generic_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { struct usb_serial *serial = port->serial; int result; - unsigned char *data; dbg("%s - port %d", __func__, port->number); @@ -287,57 +338,20 @@ int usb_serial_generic_write(struct tty_struct *tty, } /* only do something if we have a bulk out endpoint */ - if (serial->num_bulk_out) { - unsigned long flags; - - if (serial->type->max_in_flight_urbs) - return usb_serial_multi_urb_write(tty, port, - buf, count); - - spin_lock_irqsave(&port->lock, flags); - if (port->write_urb_busy) { - spin_unlock_irqrestore(&port->lock, flags); - dbg("%s - already writing", __func__); - return 0; - } - port->write_urb_busy = 1; - spin_unlock_irqrestore(&port->lock, flags); - - count = (count > port->bulk_out_size) ? - port->bulk_out_size : count; - - memcpy(port->write_urb->transfer_buffer, buf, count); - data = port->write_urb->transfer_buffer; - usb_serial_debug_data(debug, &port->dev, __func__, count, data); + if (!serial->num_bulk_out) + return 0; - /* set up our urb */ - usb_fill_bulk_urb(port->write_urb, serial->dev, - usb_sndbulkpipe(serial->dev, - port->bulk_out_endpointAddress), - port->write_urb->transfer_buffer, count, - ((serial->type->write_bulk_callback) ? - serial->type->write_bulk_callback : - usb_serial_generic_write_bulk_callback), - port); + if (serial->type->max_in_flight_urbs) + return usb_serial_multi_urb_write(tty, port, + buf, count); - /* send the data out the bulk port */ - port->write_urb_busy = 1; - result = usb_submit_urb(port->write_urb, GFP_ATOMIC); - if (result) { - dev_err(&port->dev, - "%s - failed submitting write urb, error %d\n", - __func__, result); - /* don't have to grab the lock here, as we will - retry if != 0 */ - port->write_urb_busy = 0; - } else - result = count; + count = kfifo_put(port->write_fifo, buf, count); + result = usb_serial_generic_write_start(port); - return result; - } + if (result >= 0) + result = count; - /* no bulk out, so return 0 bytes written */ - return 0; + return result; } EXPORT_SYMBOL_GPL(usb_serial_generic_write); @@ -355,9 +369,8 @@ int usb_serial_generic_write_room(struct tty_struct *tty) room = port->bulk_out_size * (serial->type->max_in_flight_urbs - port->urbs_in_flight); - } else if (serial->num_bulk_out && !(port->write_urb_busy)) { - room = port->bulk_out_size; - } + } else if (serial->num_bulk_out) + room = port->write_fifo->size - __kfifo_len(port->write_fifo); spin_unlock_irqrestore(&port->lock, flags); dbg("%s - returns %d", __func__, room); @@ -377,11 +390,8 @@ int usb_serial_generic_chars_in_buffer(struct tty_struct *tty) spin_lock_irqsave(&port->lock, flags); chars = port->tx_bytes_flight; spin_unlock_irqrestore(&port->lock, flags); - } else if (serial->num_bulk_out) { - /* FIXME: Locking */ - if (port->write_urb_busy) - chars = port->write_urb->transfer_buffer_length; - } + } else if (serial->num_bulk_out) + chars = kfifo_len(port->write_fifo); dbg("%s - returns %d", __func__, chars); return chars; @@ -485,16 +495,23 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb) if (port->urbs_in_flight < 0) port->urbs_in_flight = 0; spin_unlock_irqrestore(&port->lock, flags); + + if (status) { + dbg("%s - nonzero multi-urb write bulk status " + "received: %d", __func__, status); + return; + } } else { - /* Handle the case for single urb mode */ port->write_urb_busy = 0; - } - if (status) { - dbg("%s - nonzero write bulk status received: %d", - __func__, status); - return; + if (status) { + dbg("%s - nonzero multi-urb write bulk status " + "received: %d", __func__, status); + kfifo_reset(port->write_fifo); + } else + usb_serial_generic_write_start(port); } + usb_serial_port_softint(port); } EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback); @@ -559,6 +576,33 @@ int usb_serial_handle_break(struct usb_serial_port *port) } EXPORT_SYMBOL_GPL(usb_serial_handle_break); +int usb_serial_generic_resume(struct usb_serial *serial) +{ + struct usb_serial_port *port; + int i, c = 0, r; + + for (i = 0; i < serial->num_ports; i++) { + port = serial->port[i]; + if (!port->port.count) + continue; + + if (port->read_urb) { + r = usb_submit_urb(port->read_urb, GFP_NOIO); + if (r < 0) + c++; + } + + if (port->write_urb) { + r = usb_serial_generic_write_start(port); + if (r < 0) + c++; + } + } + + return c ? -EIO : 0; +} +EXPORT_SYMBOL_GPL(usb_serial_generic_resume); + void usb_serial_generic_disconnect(struct usb_serial *serial) { int i; diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 45975b4984ea..ff75a3589e7e 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "pl2303.h" /* @@ -625,6 +626,8 @@ static void port_release(struct device *dev) usb_free_urb(port->write_urb); usb_free_urb(port->interrupt_in_urb); usb_free_urb(port->interrupt_out_urb); + if (!IS_ERR(port->write_fifo) && port->write_fifo) + kfifo_free(port->write_fifo); kfree(port->bulk_in_buffer); kfree(port->bulk_out_buffer); kfree(port->interrupt_in_buffer); @@ -964,6 +967,10 @@ int usb_serial_probe(struct usb_interface *interface, dev_err(&interface->dev, "No free urbs available\n"); goto probe_error; } + port->write_fifo = kfifo_alloc(PAGE_SIZE, GFP_KERNEL, + &port->lock); + if (IS_ERR(port->write_fifo)) + goto probe_error; buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); port->bulk_out_size = buffer_size; port->bulk_out_endpointAddress = endpoint->bEndpointAddress; diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 7b85e327af91..c17eb64d7213 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -59,6 +59,7 @@ enum port_dev_state { * @bulk_out_buffer: pointer to the bulk out buffer for this port. * @bulk_out_size: the size of the bulk_out_buffer, in bytes. * @write_urb: pointer to the bulk out struct urb for this port. + * @write_fifo: kfifo used to buffer outgoing data * @write_urb_busy: port`s writing status * @bulk_out_endpointAddress: endpoint address for the bulk out pipe for this * port. @@ -96,6 +97,7 @@ struct usb_serial_port { unsigned char *bulk_out_buffer; int bulk_out_size; struct urb *write_urb; + struct kfifo *write_fifo; int write_urb_busy; __u8 bulk_out_endpointAddress; -- cgit v1.2.3-71-gd317 From df6c516900d48df3581b23d37d6516a22ec4f2ca Mon Sep 17 00:00:00 2001 From: Jason Wessel Date: Thu, 20 Aug 2009 15:39:48 -0500 Subject: USB: ehci,dbgp,early_printk: split ehci debug driver from early_printk.c Move the dbgp early printk driver in advance of refactoring and adding new code, so the changes to this code are tracked separately from the move of the code. The drivers/usb/early directory will be the location of the current and future early usb code for driving usb devices prior initializing the standard interrupt driven USB drivers. Signed-off-by: Jason Wessel Cc: Ingo Molnar Cc: Andrew Morton Cc: Yinghai Lu Cc: "Eric W. Biederman" Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/early_printk.c | 715 ---------------------------------------- drivers/usb/Makefile | 1 + drivers/usb/early/Makefile | 5 + drivers/usb/early/ehci-dbgp.c | 723 +++++++++++++++++++++++++++++++++++++++++ include/linux/usb/ehci_def.h | 6 + 5 files changed, 735 insertions(+), 715 deletions(-) create mode 100644 drivers/usb/early/Makefile create mode 100644 drivers/usb/early/ehci-dbgp.c (limited to 'include/linux/usb') diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c index b11cab3c323a..519a5e10be53 100644 --- a/arch/x86/kernel/early_printk.c +++ b/arch/x86/kernel/early_printk.c @@ -160,721 +160,6 @@ static struct console early_serial_console = { .index = -1, }; -#ifdef CONFIG_EARLY_PRINTK_DBGP - -static struct ehci_caps __iomem *ehci_caps; -static struct ehci_regs __iomem *ehci_regs; -static struct ehci_dbg_port __iomem *ehci_debug; -static unsigned int dbgp_endpoint_out; - -struct ehci_dev { - u32 bus; - u32 slot; - u32 func; -}; - -static struct ehci_dev ehci_dev; - -#define USB_DEBUG_DEVNUM 127 - -#define DBGP_DATA_TOGGLE 0x8800 - -static inline u32 dbgp_pid_update(u32 x, u32 tok) -{ - return ((x ^ DBGP_DATA_TOGGLE) & 0xffff00) | (tok & 0xff); -} - -static inline u32 dbgp_len_update(u32 x, u32 len) -{ - return (x & ~0x0f) | (len & 0x0f); -} - -/* - * USB Packet IDs (PIDs) - */ - -/* token */ -#define USB_PID_OUT 0xe1 -#define USB_PID_IN 0x69 -#define USB_PID_SOF 0xa5 -#define USB_PID_SETUP 0x2d -/* handshake */ -#define USB_PID_ACK 0xd2 -#define USB_PID_NAK 0x5a -#define USB_PID_STALL 0x1e -#define USB_PID_NYET 0x96 -/* data */ -#define USB_PID_DATA0 0xc3 -#define USB_PID_DATA1 0x4b -#define USB_PID_DATA2 0x87 -#define USB_PID_MDATA 0x0f -/* Special */ -#define USB_PID_PREAMBLE 0x3c -#define USB_PID_ERR 0x3c -#define USB_PID_SPLIT 0x78 -#define USB_PID_PING 0xb4 -#define USB_PID_UNDEF_0 0xf0 - -#define USB_PID_DATA_TOGGLE 0x88 -#define DBGP_CLAIM (DBGP_OWNER | DBGP_ENABLED | DBGP_INUSE) - -#define PCI_CAP_ID_EHCI_DEBUG 0xa - -#define HUB_ROOT_RESET_TIME 50 /* times are in msec */ -#define HUB_SHORT_RESET_TIME 10 -#define HUB_LONG_RESET_TIME 200 -#define HUB_RESET_TIMEOUT 500 - -#define DBGP_MAX_PACKET 8 - -static int dbgp_wait_until_complete(void) -{ - u32 ctrl; - int loop = 0x100000; - - do { - ctrl = readl(&ehci_debug->control); - /* Stop when the transaction is finished */ - if (ctrl & DBGP_DONE) - break; - } while (--loop > 0); - - if (!loop) - return -1; - - /* - * Now that we have observed the completed transaction, - * clear the done bit. - */ - writel(ctrl | DBGP_DONE, &ehci_debug->control); - return (ctrl & DBGP_ERROR) ? -DBGP_ERRCODE(ctrl) : DBGP_LEN(ctrl); -} - -static void __init dbgp_mdelay(int ms) -{ - int i; - - while (ms--) { - for (i = 0; i < 1000; i++) - outb(0x1, 0x80); - } -} - -static void dbgp_breath(void) -{ - /* Sleep to give the debug port a chance to breathe */ -} - -static int dbgp_wait_until_done(unsigned ctrl) -{ - u32 pids, lpid; - int ret; - int loop = 3; - -retry: - writel(ctrl | DBGP_GO, &ehci_debug->control); - ret = dbgp_wait_until_complete(); - pids = readl(&ehci_debug->pids); - lpid = DBGP_PID_GET(pids); - - if (ret < 0) - return ret; - - /* - * If the port is getting full or it has dropped data - * start pacing ourselves, not necessary but it's friendly. - */ - if ((lpid == USB_PID_NAK) || (lpid == USB_PID_NYET)) - dbgp_breath(); - - /* If I get a NACK reissue the transmission */ - if (lpid == USB_PID_NAK) { - if (--loop > 0) - goto retry; - } - - return ret; -} - -static void dbgp_set_data(const void *buf, int size) -{ - const unsigned char *bytes = buf; - u32 lo, hi; - int i; - - lo = hi = 0; - for (i = 0; i < 4 && i < size; i++) - lo |= bytes[i] << (8*i); - for (; i < 8 && i < size; i++) - hi |= bytes[i] << (8*(i - 4)); - writel(lo, &ehci_debug->data03); - writel(hi, &ehci_debug->data47); -} - -static void __init dbgp_get_data(void *buf, int size) -{ - unsigned char *bytes = buf; - u32 lo, hi; - int i; - - lo = readl(&ehci_debug->data03); - hi = readl(&ehci_debug->data47); - for (i = 0; i < 4 && i < size; i++) - bytes[i] = (lo >> (8*i)) & 0xff; - for (; i < 8 && i < size; i++) - bytes[i] = (hi >> (8*(i - 4))) & 0xff; -} - -static int dbgp_bulk_write(unsigned devnum, unsigned endpoint, - const char *bytes, int size) -{ - u32 pids, addr, ctrl; - int ret; - - if (size > DBGP_MAX_PACKET) - return -1; - - addr = DBGP_EPADDR(devnum, endpoint); - - pids = readl(&ehci_debug->pids); - pids = dbgp_pid_update(pids, USB_PID_OUT); - - ctrl = readl(&ehci_debug->control); - ctrl = dbgp_len_update(ctrl, size); - ctrl |= DBGP_OUT; - ctrl |= DBGP_GO; - - dbgp_set_data(bytes, size); - writel(addr, &ehci_debug->address); - writel(pids, &ehci_debug->pids); - - ret = dbgp_wait_until_done(ctrl); - if (ret < 0) - return ret; - - return ret; -} - -static int __init dbgp_bulk_read(unsigned devnum, unsigned endpoint, void *data, - int size) -{ - u32 pids, addr, ctrl; - int ret; - - if (size > DBGP_MAX_PACKET) - return -1; - - addr = DBGP_EPADDR(devnum, endpoint); - - pids = readl(&ehci_debug->pids); - pids = dbgp_pid_update(pids, USB_PID_IN); - - ctrl = readl(&ehci_debug->control); - ctrl = dbgp_len_update(ctrl, size); - ctrl &= ~DBGP_OUT; - ctrl |= DBGP_GO; - - writel(addr, &ehci_debug->address); - writel(pids, &ehci_debug->pids); - ret = dbgp_wait_until_done(ctrl); - if (ret < 0) - return ret; - - if (size > ret) - size = ret; - dbgp_get_data(data, size); - return ret; -} - -static int __init dbgp_control_msg(unsigned devnum, int requesttype, - int request, int value, int index, void *data, int size) -{ - u32 pids, addr, ctrl; - struct usb_ctrlrequest req; - int read; - int ret; - - read = (requesttype & USB_DIR_IN) != 0; - if (size > (read ? DBGP_MAX_PACKET:0)) - return -1; - - /* Compute the control message */ - req.bRequestType = requesttype; - req.bRequest = request; - req.wValue = cpu_to_le16(value); - req.wIndex = cpu_to_le16(index); - req.wLength = cpu_to_le16(size); - - pids = DBGP_PID_SET(USB_PID_DATA0, USB_PID_SETUP); - addr = DBGP_EPADDR(devnum, 0); - - ctrl = readl(&ehci_debug->control); - ctrl = dbgp_len_update(ctrl, sizeof(req)); - ctrl |= DBGP_OUT; - ctrl |= DBGP_GO; - - /* Send the setup message */ - dbgp_set_data(&req, sizeof(req)); - writel(addr, &ehci_debug->address); - writel(pids, &ehci_debug->pids); - ret = dbgp_wait_until_done(ctrl); - if (ret < 0) - return ret; - - /* Read the result */ - return dbgp_bulk_read(devnum, 0, data, size); -} - - -/* Find a PCI capability */ -static u32 __init find_cap(u32 num, u32 slot, u32 func, int cap) -{ - u8 pos; - int bytes; - - if (!(read_pci_config_16(num, slot, func, PCI_STATUS) & - PCI_STATUS_CAP_LIST)) - return 0; - - pos = read_pci_config_byte(num, slot, func, PCI_CAPABILITY_LIST); - for (bytes = 0; bytes < 48 && pos >= 0x40; bytes++) { - u8 id; - - pos &= ~3; - id = read_pci_config_byte(num, slot, func, pos+PCI_CAP_LIST_ID); - if (id == 0xff) - break; - if (id == cap) - return pos; - - pos = read_pci_config_byte(num, slot, func, - pos+PCI_CAP_LIST_NEXT); - } - return 0; -} - -static u32 __init __find_dbgp(u32 bus, u32 slot, u32 func) -{ - u32 class; - - class = read_pci_config(bus, slot, func, PCI_CLASS_REVISION); - if ((class >> 8) != PCI_CLASS_SERIAL_USB_EHCI) - return 0; - - return find_cap(bus, slot, func, PCI_CAP_ID_EHCI_DEBUG); -} - -static u32 __init find_dbgp(int ehci_num, u32 *rbus, u32 *rslot, u32 *rfunc) -{ - u32 bus, slot, func; - - for (bus = 0; bus < 256; bus++) { - for (slot = 0; slot < 32; slot++) { - for (func = 0; func < 8; func++) { - unsigned cap; - - cap = __find_dbgp(bus, slot, func); - - if (!cap) - continue; - if (ehci_num-- != 0) - continue; - *rbus = bus; - *rslot = slot; - *rfunc = func; - return cap; - } - } - } - return 0; -} - -static int __init ehci_reset_port(int port) -{ - u32 portsc; - u32 delay_time, delay; - int loop; - - /* Reset the usb debug port */ - portsc = readl(&ehci_regs->port_status[port - 1]); - portsc &= ~PORT_PE; - portsc |= PORT_RESET; - writel(portsc, &ehci_regs->port_status[port - 1]); - - delay = HUB_ROOT_RESET_TIME; - for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT; - delay_time += delay) { - dbgp_mdelay(delay); - - portsc = readl(&ehci_regs->port_status[port - 1]); - if (portsc & PORT_RESET) { - /* force reset to complete */ - loop = 2; - writel(portsc & ~(PORT_RWC_BITS | PORT_RESET), - &ehci_regs->port_status[port - 1]); - do { - portsc = readl(&ehci_regs->port_status[port-1]); - } while ((portsc & PORT_RESET) && (--loop > 0)); - } - - /* Device went away? */ - if (!(portsc & PORT_CONNECT)) - return -ENOTCONN; - - /* bomb out completely if something weird happend */ - if ((portsc & PORT_CSC)) - return -EINVAL; - - /* If we've finished resetting, then break out of the loop */ - if (!(portsc & PORT_RESET) && (portsc & PORT_PE)) - return 0; - } - return -EBUSY; -} - -static int __init ehci_wait_for_port(int port) -{ - u32 status; - int ret, reps; - - for (reps = 0; reps < 3; reps++) { - dbgp_mdelay(100); - status = readl(&ehci_regs->status); - if (status & STS_PCD) { - ret = ehci_reset_port(port); - if (ret == 0) - return 0; - } - } - return -ENOTCONN; -} - -#ifdef DBGP_DEBUG -# define dbgp_printk early_printk -#else -static inline void dbgp_printk(const char *fmt, ...) { } -#endif - -typedef void (*set_debug_port_t)(int port); - -static void __init default_set_debug_port(int port) -{ -} - -static set_debug_port_t __initdata set_debug_port = default_set_debug_port; - -static void __init nvidia_set_debug_port(int port) -{ - u32 dword; - dword = read_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, - 0x74); - dword &= ~(0x0f<<12); - dword |= ((port & 0x0f)<<12); - write_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, 0x74, - dword); - dbgp_printk("set debug port to %d\n", port); -} - -static void __init detect_set_debug_port(void) -{ - u32 vendorid; - - vendorid = read_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, - 0x00); - - if ((vendorid & 0xffff) == 0x10de) { - dbgp_printk("using nvidia set_debug_port\n"); - set_debug_port = nvidia_set_debug_port; - } -} - -static int __init ehci_setup(void) -{ - struct usb_debug_descriptor dbgp_desc; - u32 cmd, ctrl, status, portsc, hcs_params; - u32 debug_port, new_debug_port = 0, n_ports; - u32 devnum; - int ret, i; - int loop; - int port_map_tried; - int playtimes = 3; - -try_next_time: - port_map_tried = 0; - -try_next_port: - - hcs_params = readl(&ehci_caps->hcs_params); - debug_port = HCS_DEBUG_PORT(hcs_params); - n_ports = HCS_N_PORTS(hcs_params); - - dbgp_printk("debug_port: %d\n", debug_port); - dbgp_printk("n_ports: %d\n", n_ports); - - for (i = 1; i <= n_ports; i++) { - portsc = readl(&ehci_regs->port_status[i-1]); - dbgp_printk("portstatus%d: %08x\n", i, portsc); - } - - if (port_map_tried && (new_debug_port != debug_port)) { - if (--playtimes) { - set_debug_port(new_debug_port); - goto try_next_time; - } - return -1; - } - - loop = 100000; - /* Reset the EHCI controller */ - cmd = readl(&ehci_regs->command); - cmd |= CMD_RESET; - writel(cmd, &ehci_regs->command); - do { - cmd = readl(&ehci_regs->command); - } while ((cmd & CMD_RESET) && (--loop > 0)); - - if (!loop) { - dbgp_printk("can not reset ehci\n"); - return -1; - } - dbgp_printk("ehci reset done\n"); - - /* Claim ownership, but do not enable yet */ - ctrl = readl(&ehci_debug->control); - ctrl |= DBGP_OWNER; - ctrl &= ~(DBGP_ENABLED | DBGP_INUSE); - writel(ctrl, &ehci_debug->control); - - /* Start the ehci running */ - cmd = readl(&ehci_regs->command); - cmd &= ~(CMD_LRESET | CMD_IAAD | CMD_PSE | CMD_ASE | CMD_RESET); - cmd |= CMD_RUN; - writel(cmd, &ehci_regs->command); - - /* Ensure everything is routed to the EHCI */ - writel(FLAG_CF, &ehci_regs->configured_flag); - - /* Wait until the controller is no longer halted */ - loop = 10; - do { - status = readl(&ehci_regs->status); - } while ((status & STS_HALT) && (--loop > 0)); - - if (!loop) { - dbgp_printk("ehci can be started\n"); - return -1; - } - dbgp_printk("ehci started\n"); - - /* Wait for a device to show up in the debug port */ - ret = ehci_wait_for_port(debug_port); - if (ret < 0) { - dbgp_printk("No device found in debug port\n"); - goto next_debug_port; - } - dbgp_printk("ehci wait for port done\n"); - - /* Enable the debug port */ - ctrl = readl(&ehci_debug->control); - ctrl |= DBGP_CLAIM; - writel(ctrl, &ehci_debug->control); - ctrl = readl(&ehci_debug->control); - if ((ctrl & DBGP_CLAIM) != DBGP_CLAIM) { - dbgp_printk("No device in debug port\n"); - writel(ctrl & ~DBGP_CLAIM, &ehci_debug->control); - goto err; - } - dbgp_printk("debug ported enabled\n"); - - /* Completely transfer the debug device to the debug controller */ - portsc = readl(&ehci_regs->port_status[debug_port - 1]); - portsc &= ~PORT_PE; - writel(portsc, &ehci_regs->port_status[debug_port - 1]); - - dbgp_mdelay(100); - - /* Find the debug device and make it device number 127 */ - for (devnum = 0; devnum <= 127; devnum++) { - ret = dbgp_control_msg(devnum, - USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE, - USB_REQ_GET_DESCRIPTOR, (USB_DT_DEBUG << 8), 0, - &dbgp_desc, sizeof(dbgp_desc)); - if (ret > 0) - break; - } - if (devnum > 127) { - dbgp_printk("Could not find attached debug device\n"); - goto err; - } - if (ret < 0) { - dbgp_printk("Attached device is not a debug device\n"); - goto err; - } - dbgp_endpoint_out = dbgp_desc.bDebugOutEndpoint; - - /* Move the device to 127 if it isn't already there */ - if (devnum != USB_DEBUG_DEVNUM) { - ret = dbgp_control_msg(devnum, - USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, - USB_REQ_SET_ADDRESS, USB_DEBUG_DEVNUM, 0, NULL, 0); - if (ret < 0) { - dbgp_printk("Could not move attached device to %d\n", - USB_DEBUG_DEVNUM); - goto err; - } - devnum = USB_DEBUG_DEVNUM; - dbgp_printk("debug device renamed to 127\n"); - } - - /* Enable the debug interface */ - ret = dbgp_control_msg(USB_DEBUG_DEVNUM, - USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, - USB_REQ_SET_FEATURE, USB_DEVICE_DEBUG_MODE, 0, NULL, 0); - if (ret < 0) { - dbgp_printk(" Could not enable the debug device\n"); - goto err; - } - dbgp_printk("debug interface enabled\n"); - - /* Perform a small write to get the even/odd data state in sync - */ - ret = dbgp_bulk_write(USB_DEBUG_DEVNUM, dbgp_endpoint_out, " ", 1); - if (ret < 0) { - dbgp_printk("dbgp_bulk_write failed: %d\n", ret); - goto err; - } - dbgp_printk("small write doned\n"); - - return 0; -err: - /* Things didn't work so remove my claim */ - ctrl = readl(&ehci_debug->control); - ctrl &= ~(DBGP_CLAIM | DBGP_OUT); - writel(ctrl, &ehci_debug->control); - return -1; - -next_debug_port: - port_map_tried |= (1<<(debug_port - 1)); - new_debug_port = ((debug_port-1+1)%n_ports) + 1; - if (port_map_tried != ((1<> 29) & 0x7; - bar = (bar * 4) + 0xc; - offset = (debug_port >> 16) & 0xfff; - dbgp_printk("bar: %02x offset: %03x\n", bar, offset); - if (bar != PCI_BASE_ADDRESS_0) { - dbgp_printk("only debug ports on bar 1 handled.\n"); - - return -1; - } - - bar_val = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_0); - dbgp_printk("bar_val: %02x offset: %03x\n", bar_val, offset); - if (bar_val & ~PCI_BASE_ADDRESS_MEM_MASK) { - dbgp_printk("only simple 32bit mmio bars supported\n"); - - return -1; - } - - /* double check if the mem space is enabled */ - byte = read_pci_config_byte(bus, slot, func, 0x04); - if (!(byte & 0x2)) { - byte |= 0x02; - write_pci_config_byte(bus, slot, func, 0x04, byte); - dbgp_printk("mmio for ehci enabled\n"); - } - - /* - * FIXME I don't have the bar size so just guess PAGE_SIZE is more - * than enough. 1K is the biggest I have seen. - */ - set_fixmap_nocache(FIX_DBGP_BASE, bar_val & PAGE_MASK); - ehci_bar = (void __iomem *)__fix_to_virt(FIX_DBGP_BASE); - ehci_bar += bar_val & ~PAGE_MASK; - dbgp_printk("ehci_bar: %p\n", ehci_bar); - - ehci_caps = ehci_bar; - ehci_regs = ehci_bar + HC_LENGTH(readl(&ehci_caps->hc_capbase)); - ehci_debug = ehci_bar + offset; - ehci_dev.bus = bus; - ehci_dev.slot = slot; - ehci_dev.func = func; - - detect_set_debug_port(); - - ret = ehci_setup(); - if (ret < 0) { - dbgp_printk("ehci_setup failed\n"); - ehci_debug = NULL; - - return -1; - } - - return 0; -} - -static void early_dbgp_write(struct console *con, const char *str, u32 n) -{ - int chunk, ret; - - if (!ehci_debug) - return; - while (n > 0) { - chunk = n; - if (chunk > DBGP_MAX_PACKET) - chunk = DBGP_MAX_PACKET; - ret = dbgp_bulk_write(USB_DEBUG_DEVNUM, - dbgp_endpoint_out, str, chunk); - str += chunk; - n -= chunk; - } -} - -static struct console early_dbgp_console = { - .name = "earlydbg", - .write = early_dbgp_write, - .flags = CON_PRINTBUFFER, - .index = -1, -}; -#endif - /* Direct interface for emergencies */ static struct console *early_console = &early_vga_console; static int __initdata early_console_initialized; diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index dbe4fb694ad2..be3c9b80bc9f 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_USB_MICROTEK) += image/ obj-$(CONFIG_USB_SERIAL) += serial/ obj-$(CONFIG_USB) += misc/ +obj-y += early/ obj-$(CONFIG_USB_ATM) += atm/ obj-$(CONFIG_USB_SPEEDTOUCH) += atm/ diff --git a/drivers/usb/early/Makefile b/drivers/usb/early/Makefile new file mode 100644 index 000000000000..dfedee8c45b6 --- /dev/null +++ b/drivers/usb/early/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for early USB devices +# + +obj-$(CONFIG_EARLY_PRINTK_DBGP) += ehci-dbgp.o diff --git a/drivers/usb/early/ehci-dbgp.c b/drivers/usb/early/ehci-dbgp.c new file mode 100644 index 000000000000..821b7b21c29c --- /dev/null +++ b/drivers/usb/early/ehci-dbgp.c @@ -0,0 +1,723 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct ehci_caps __iomem *ehci_caps; +static struct ehci_regs __iomem *ehci_regs; +static struct ehci_dbg_port __iomem *ehci_debug; +static unsigned int dbgp_endpoint_out; + +struct ehci_dev { + u32 bus; + u32 slot; + u32 func; +}; + +static struct ehci_dev ehci_dev; + +#define USB_DEBUG_DEVNUM 127 + +#define DBGP_DATA_TOGGLE 0x8800 + +static inline u32 dbgp_pid_update(u32 x, u32 tok) +{ + return ((x ^ DBGP_DATA_TOGGLE) & 0xffff00) | (tok & 0xff); +} + +static inline u32 dbgp_len_update(u32 x, u32 len) +{ + return (x & ~0x0f) | (len & 0x0f); +} + +/* + * USB Packet IDs (PIDs) + */ + +/* token */ +#define USB_PID_OUT 0xe1 +#define USB_PID_IN 0x69 +#define USB_PID_SOF 0xa5 +#define USB_PID_SETUP 0x2d +/* handshake */ +#define USB_PID_ACK 0xd2 +#define USB_PID_NAK 0x5a +#define USB_PID_STALL 0x1e +#define USB_PID_NYET 0x96 +/* data */ +#define USB_PID_DATA0 0xc3 +#define USB_PID_DATA1 0x4b +#define USB_PID_DATA2 0x87 +#define USB_PID_MDATA 0x0f +/* Special */ +#define USB_PID_PREAMBLE 0x3c +#define USB_PID_ERR 0x3c +#define USB_PID_SPLIT 0x78 +#define USB_PID_PING 0xb4 +#define USB_PID_UNDEF_0 0xf0 + +#define USB_PID_DATA_TOGGLE 0x88 +#define DBGP_CLAIM (DBGP_OWNER | DBGP_ENABLED | DBGP_INUSE) + +#define PCI_CAP_ID_EHCI_DEBUG 0xa + +#define HUB_ROOT_RESET_TIME 50 /* times are in msec */ +#define HUB_SHORT_RESET_TIME 10 +#define HUB_LONG_RESET_TIME 200 +#define HUB_RESET_TIMEOUT 500 + +#define DBGP_MAX_PACKET 8 + +static int dbgp_wait_until_complete(void) +{ + u32 ctrl; + int loop = 0x100000; + + do { + ctrl = readl(&ehci_debug->control); + /* Stop when the transaction is finished */ + if (ctrl & DBGP_DONE) + break; + } while (--loop > 0); + + if (!loop) + return -1; + + /* + * Now that we have observed the completed transaction, + * clear the done bit. + */ + writel(ctrl | DBGP_DONE, &ehci_debug->control); + return (ctrl & DBGP_ERROR) ? -DBGP_ERRCODE(ctrl) : DBGP_LEN(ctrl); +} + +static void __init dbgp_mdelay(int ms) +{ + int i; + + while (ms--) { + for (i = 0; i < 1000; i++) + outb(0x1, 0x80); + } +} + +static void dbgp_breath(void) +{ + /* Sleep to give the debug port a chance to breathe */ +} + +static int dbgp_wait_until_done(unsigned ctrl) +{ + u32 pids, lpid; + int ret; + int loop = 3; + +retry: + writel(ctrl | DBGP_GO, &ehci_debug->control); + ret = dbgp_wait_until_complete(); + pids = readl(&ehci_debug->pids); + lpid = DBGP_PID_GET(pids); + + if (ret < 0) + return ret; + + /* + * If the port is getting full or it has dropped data + * start pacing ourselves, not necessary but it's friendly. + */ + if ((lpid == USB_PID_NAK) || (lpid == USB_PID_NYET)) + dbgp_breath(); + + /* If I get a NACK reissue the transmission */ + if (lpid == USB_PID_NAK) { + if (--loop > 0) + goto retry; + } + + return ret; +} + +static void dbgp_set_data(const void *buf, int size) +{ + const unsigned char *bytes = buf; + u32 lo, hi; + int i; + + lo = hi = 0; + for (i = 0; i < 4 && i < size; i++) + lo |= bytes[i] << (8*i); + for (; i < 8 && i < size; i++) + hi |= bytes[i] << (8*(i - 4)); + writel(lo, &ehci_debug->data03); + writel(hi, &ehci_debug->data47); +} + +static void __init dbgp_get_data(void *buf, int size) +{ + unsigned char *bytes = buf; + u32 lo, hi; + int i; + + lo = readl(&ehci_debug->data03); + hi = readl(&ehci_debug->data47); + for (i = 0; i < 4 && i < size; i++) + bytes[i] = (lo >> (8*i)) & 0xff; + for (; i < 8 && i < size; i++) + bytes[i] = (hi >> (8*(i - 4))) & 0xff; +} + +static int dbgp_bulk_write(unsigned devnum, unsigned endpoint, + const char *bytes, int size) +{ + u32 pids, addr, ctrl; + int ret; + + if (size > DBGP_MAX_PACKET) + return -1; + + addr = DBGP_EPADDR(devnum, endpoint); + + pids = readl(&ehci_debug->pids); + pids = dbgp_pid_update(pids, USB_PID_OUT); + + ctrl = readl(&ehci_debug->control); + ctrl = dbgp_len_update(ctrl, size); + ctrl |= DBGP_OUT; + ctrl |= DBGP_GO; + + dbgp_set_data(bytes, size); + writel(addr, &ehci_debug->address); + writel(pids, &ehci_debug->pids); + + ret = dbgp_wait_until_done(ctrl); + if (ret < 0) + return ret; + + return ret; +} + +static int __init dbgp_bulk_read(unsigned devnum, unsigned endpoint, void *data, + int size) +{ + u32 pids, addr, ctrl; + int ret; + + if (size > DBGP_MAX_PACKET) + return -1; + + addr = DBGP_EPADDR(devnum, endpoint); + + pids = readl(&ehci_debug->pids); + pids = dbgp_pid_update(pids, USB_PID_IN); + + ctrl = readl(&ehci_debug->control); + ctrl = dbgp_len_update(ctrl, size); + ctrl &= ~DBGP_OUT; + ctrl |= DBGP_GO; + + writel(addr, &ehci_debug->address); + writel(pids, &ehci_debug->pids); + ret = dbgp_wait_until_done(ctrl); + if (ret < 0) + return ret; + + if (size > ret) + size = ret; + dbgp_get_data(data, size); + return ret; +} + +static int __init dbgp_control_msg(unsigned devnum, int requesttype, + int request, int value, int index, void *data, int size) +{ + u32 pids, addr, ctrl; + struct usb_ctrlrequest req; + int read; + int ret; + + read = (requesttype & USB_DIR_IN) != 0; + if (size > (read ? DBGP_MAX_PACKET:0)) + return -1; + + /* Compute the control message */ + req.bRequestType = requesttype; + req.bRequest = request; + req.wValue = cpu_to_le16(value); + req.wIndex = cpu_to_le16(index); + req.wLength = cpu_to_le16(size); + + pids = DBGP_PID_SET(USB_PID_DATA0, USB_PID_SETUP); + addr = DBGP_EPADDR(devnum, 0); + + ctrl = readl(&ehci_debug->control); + ctrl = dbgp_len_update(ctrl, sizeof(req)); + ctrl |= DBGP_OUT; + ctrl |= DBGP_GO; + + /* Send the setup message */ + dbgp_set_data(&req, sizeof(req)); + writel(addr, &ehci_debug->address); + writel(pids, &ehci_debug->pids); + ret = dbgp_wait_until_done(ctrl); + if (ret < 0) + return ret; + + /* Read the result */ + return dbgp_bulk_read(devnum, 0, data, size); +} + + +/* Find a PCI capability */ +static u32 __init find_cap(u32 num, u32 slot, u32 func, int cap) +{ + u8 pos; + int bytes; + + if (!(read_pci_config_16(num, slot, func, PCI_STATUS) & + PCI_STATUS_CAP_LIST)) + return 0; + + pos = read_pci_config_byte(num, slot, func, PCI_CAPABILITY_LIST); + for (bytes = 0; bytes < 48 && pos >= 0x40; bytes++) { + u8 id; + + pos &= ~3; + id = read_pci_config_byte(num, slot, func, pos+PCI_CAP_LIST_ID); + if (id == 0xff) + break; + if (id == cap) + return pos; + + pos = read_pci_config_byte(num, slot, func, + pos+PCI_CAP_LIST_NEXT); + } + return 0; +} + +static u32 __init __find_dbgp(u32 bus, u32 slot, u32 func) +{ + u32 class; + + class = read_pci_config(bus, slot, func, PCI_CLASS_REVISION); + if ((class >> 8) != PCI_CLASS_SERIAL_USB_EHCI) + return 0; + + return find_cap(bus, slot, func, PCI_CAP_ID_EHCI_DEBUG); +} + +static u32 __init find_dbgp(int ehci_num, u32 *rbus, u32 *rslot, u32 *rfunc) +{ + u32 bus, slot, func; + + for (bus = 0; bus < 256; bus++) { + for (slot = 0; slot < 32; slot++) { + for (func = 0; func < 8; func++) { + unsigned cap; + + cap = __find_dbgp(bus, slot, func); + + if (!cap) + continue; + if (ehci_num-- != 0) + continue; + *rbus = bus; + *rslot = slot; + *rfunc = func; + return cap; + } + } + } + return 0; +} + +static int __init ehci_reset_port(int port) +{ + u32 portsc; + u32 delay_time, delay; + int loop; + + /* Reset the usb debug port */ + portsc = readl(&ehci_regs->port_status[port - 1]); + portsc &= ~PORT_PE; + portsc |= PORT_RESET; + writel(portsc, &ehci_regs->port_status[port - 1]); + + delay = HUB_ROOT_RESET_TIME; + for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT; + delay_time += delay) { + dbgp_mdelay(delay); + + portsc = readl(&ehci_regs->port_status[port - 1]); + if (portsc & PORT_RESET) { + /* force reset to complete */ + loop = 2; + writel(portsc & ~(PORT_RWC_BITS | PORT_RESET), + &ehci_regs->port_status[port - 1]); + do { + portsc = readl(&ehci_regs->port_status[port-1]); + } while ((portsc & PORT_RESET) && (--loop > 0)); + } + + /* Device went away? */ + if (!(portsc & PORT_CONNECT)) + return -ENOTCONN; + + /* bomb out completely if something weird happend */ + if ((portsc & PORT_CSC)) + return -EINVAL; + + /* If we've finished resetting, then break out of the loop */ + if (!(portsc & PORT_RESET) && (portsc & PORT_PE)) + return 0; + } + return -EBUSY; +} + +static int __init ehci_wait_for_port(int port) +{ + u32 status; + int ret, reps; + + for (reps = 0; reps < 3; reps++) { + dbgp_mdelay(100); + status = readl(&ehci_regs->status); + if (status & STS_PCD) { + ret = ehci_reset_port(port); + if (ret == 0) + return 0; + } + } + return -ENOTCONN; +} + +#ifdef DBGP_DEBUG +# define dbgp_printk early_printk +#else +static inline void dbgp_printk(const char *fmt, ...) { } +#endif + +typedef void (*set_debug_port_t)(int port); + +static void __init default_set_debug_port(int port) +{ +} + +static set_debug_port_t __initdata set_debug_port = default_set_debug_port; + +static void __init nvidia_set_debug_port(int port) +{ + u32 dword; + dword = read_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, + 0x74); + dword &= ~(0x0f<<12); + dword |= ((port & 0x0f)<<12); + write_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, 0x74, + dword); + dbgp_printk("set debug port to %d\n", port); +} + +static void __init detect_set_debug_port(void) +{ + u32 vendorid; + + vendorid = read_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, + 0x00); + + if ((vendorid & 0xffff) == 0x10de) { + dbgp_printk("using nvidia set_debug_port\n"); + set_debug_port = nvidia_set_debug_port; + } +} + +static int __init ehci_setup(void) +{ + struct usb_debug_descriptor dbgp_desc; + u32 cmd, ctrl, status, portsc, hcs_params; + u32 debug_port, new_debug_port = 0, n_ports; + u32 devnum; + int ret, i; + int loop; + int port_map_tried; + int playtimes = 3; + +try_next_time: + port_map_tried = 0; + +try_next_port: + + hcs_params = readl(&ehci_caps->hcs_params); + debug_port = HCS_DEBUG_PORT(hcs_params); + n_ports = HCS_N_PORTS(hcs_params); + + dbgp_printk("debug_port: %d\n", debug_port); + dbgp_printk("n_ports: %d\n", n_ports); + + for (i = 1; i <= n_ports; i++) { + portsc = readl(&ehci_regs->port_status[i-1]); + dbgp_printk("portstatus%d: %08x\n", i, portsc); + } + + if (port_map_tried && (new_debug_port != debug_port)) { + if (--playtimes) { + set_debug_port(new_debug_port); + goto try_next_time; + } + return -1; + } + + loop = 100000; + /* Reset the EHCI controller */ + cmd = readl(&ehci_regs->command); + cmd |= CMD_RESET; + writel(cmd, &ehci_regs->command); + do { + cmd = readl(&ehci_regs->command); + } while ((cmd & CMD_RESET) && (--loop > 0)); + + if (!loop) { + dbgp_printk("can not reset ehci\n"); + return -1; + } + dbgp_printk("ehci reset done\n"); + + /* Claim ownership, but do not enable yet */ + ctrl = readl(&ehci_debug->control); + ctrl |= DBGP_OWNER; + ctrl &= ~(DBGP_ENABLED | DBGP_INUSE); + writel(ctrl, &ehci_debug->control); + + /* Start the ehci running */ + cmd = readl(&ehci_regs->command); + cmd &= ~(CMD_LRESET | CMD_IAAD | CMD_PSE | CMD_ASE | CMD_RESET); + cmd |= CMD_RUN; + writel(cmd, &ehci_regs->command); + + /* Ensure everything is routed to the EHCI */ + writel(FLAG_CF, &ehci_regs->configured_flag); + + /* Wait until the controller is no longer halted */ + loop = 10; + do { + status = readl(&ehci_regs->status); + } while ((status & STS_HALT) && (--loop > 0)); + + if (!loop) { + dbgp_printk("ehci can be started\n"); + return -1; + } + dbgp_printk("ehci started\n"); + + /* Wait for a device to show up in the debug port */ + ret = ehci_wait_for_port(debug_port); + if (ret < 0) { + dbgp_printk("No device found in debug port\n"); + goto next_debug_port; + } + dbgp_printk("ehci wait for port done\n"); + + /* Enable the debug port */ + ctrl = readl(&ehci_debug->control); + ctrl |= DBGP_CLAIM; + writel(ctrl, &ehci_debug->control); + ctrl = readl(&ehci_debug->control); + if ((ctrl & DBGP_CLAIM) != DBGP_CLAIM) { + dbgp_printk("No device in debug port\n"); + writel(ctrl & ~DBGP_CLAIM, &ehci_debug->control); + goto err; + } + dbgp_printk("debug ported enabled\n"); + + /* Completely transfer the debug device to the debug controller */ + portsc = readl(&ehci_regs->port_status[debug_port - 1]); + portsc &= ~PORT_PE; + writel(portsc, &ehci_regs->port_status[debug_port - 1]); + + dbgp_mdelay(100); + + /* Find the debug device and make it device number 127 */ + for (devnum = 0; devnum <= 127; devnum++) { + ret = dbgp_control_msg(devnum, + USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + USB_REQ_GET_DESCRIPTOR, (USB_DT_DEBUG << 8), 0, + &dbgp_desc, sizeof(dbgp_desc)); + if (ret > 0) + break; + } + if (devnum > 127) { + dbgp_printk("Could not find attached debug device\n"); + goto err; + } + if (ret < 0) { + dbgp_printk("Attached device is not a debug device\n"); + goto err; + } + dbgp_endpoint_out = dbgp_desc.bDebugOutEndpoint; + + /* Move the device to 127 if it isn't already there */ + if (devnum != USB_DEBUG_DEVNUM) { + ret = dbgp_control_msg(devnum, + USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + USB_REQ_SET_ADDRESS, USB_DEBUG_DEVNUM, 0, NULL, 0); + if (ret < 0) { + dbgp_printk("Could not move attached device to %d\n", + USB_DEBUG_DEVNUM); + goto err; + } + devnum = USB_DEBUG_DEVNUM; + dbgp_printk("debug device renamed to 127\n"); + } + + /* Enable the debug interface */ + ret = dbgp_control_msg(USB_DEBUG_DEVNUM, + USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + USB_REQ_SET_FEATURE, USB_DEVICE_DEBUG_MODE, 0, NULL, 0); + if (ret < 0) { + dbgp_printk(" Could not enable the debug device\n"); + goto err; + } + dbgp_printk("debug interface enabled\n"); + + /* Perform a small write to get the even/odd data state in sync + */ + ret = dbgp_bulk_write(USB_DEBUG_DEVNUM, dbgp_endpoint_out, " ", 1); + if (ret < 0) { + dbgp_printk("dbgp_bulk_write failed: %d\n", ret); + goto err; + } + dbgp_printk("small write doned\n"); + + return 0; +err: + /* Things didn't work so remove my claim */ + ctrl = readl(&ehci_debug->control); + ctrl &= ~(DBGP_CLAIM | DBGP_OUT); + writel(ctrl, &ehci_debug->control); + return -1; + +next_debug_port: + port_map_tried |= (1<<(debug_port - 1)); + new_debug_port = ((debug_port-1+1)%n_ports) + 1; + if (port_map_tried != ((1<> 29) & 0x7; + bar = (bar * 4) + 0xc; + offset = (debug_port >> 16) & 0xfff; + dbgp_printk("bar: %02x offset: %03x\n", bar, offset); + if (bar != PCI_BASE_ADDRESS_0) { + dbgp_printk("only debug ports on bar 1 handled.\n"); + + return -1; + } + + bar_val = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_0); + dbgp_printk("bar_val: %02x offset: %03x\n", bar_val, offset); + if (bar_val & ~PCI_BASE_ADDRESS_MEM_MASK) { + dbgp_printk("only simple 32bit mmio bars supported\n"); + + return -1; + } + + /* double check if the mem space is enabled */ + byte = read_pci_config_byte(bus, slot, func, 0x04); + if (!(byte & 0x2)) { + byte |= 0x02; + write_pci_config_byte(bus, slot, func, 0x04, byte); + dbgp_printk("mmio for ehci enabled\n"); + } + + /* + * FIXME I don't have the bar size so just guess PAGE_SIZE is more + * than enough. 1K is the biggest I have seen. + */ + set_fixmap_nocache(FIX_DBGP_BASE, bar_val & PAGE_MASK); + ehci_bar = (void __iomem *)__fix_to_virt(FIX_DBGP_BASE); + ehci_bar += bar_val & ~PAGE_MASK; + dbgp_printk("ehci_bar: %p\n", ehci_bar); + + ehci_caps = ehci_bar; + ehci_regs = ehci_bar + HC_LENGTH(readl(&ehci_caps->hc_capbase)); + ehci_debug = ehci_bar + offset; + ehci_dev.bus = bus; + ehci_dev.slot = slot; + ehci_dev.func = func; + + detect_set_debug_port(); + + ret = ehci_setup(); + if (ret < 0) { + dbgp_printk("ehci_setup failed\n"); + ehci_debug = NULL; + + return -1; + } + + return 0; +} + +static void early_dbgp_write(struct console *con, const char *str, u32 n) +{ + int chunk, ret; + + if (!ehci_debug) + return; + while (n > 0) { + chunk = n; + if (chunk > DBGP_MAX_PACKET) + chunk = DBGP_MAX_PACKET; + ret = dbgp_bulk_write(USB_DEBUG_DEVNUM, + dbgp_endpoint_out, str, chunk); + str += chunk; + n -= chunk; + } +} + +struct console early_dbgp_console = { + .name = "earlydbg", + .write = early_dbgp_write, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h index 4c4b701ff265..1deac2a7b12e 100644 --- a/include/linux/usb/ehci_def.h +++ b/include/linux/usb/ehci_def.h @@ -170,4 +170,10 @@ struct ehci_dbg_port { #define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep)) } __attribute__ ((packed)); +#ifdef CONFIG_EARLY_PRINTK_DBGP +#include +extern int __init early_dbgp_init(char *s); +extern struct console early_dbgp_console; +#endif /* CONFIG_EARLY_PRINTK_DBGP */ + #endif /* __LINUX_USB_EHCI_DEF_H */ -- cgit v1.2.3-71-gd317 From 917778267fbe67703ab7d5c6f0b7a05d4c3df485 Mon Sep 17 00:00:00 2001 From: Jason Wessel Date: Thu, 20 Aug 2009 15:39:53 -0500 Subject: USB: ehci-dbgp: stability improvements and external re-init This patch implements several changes: 1) Improve the capability to debug the dbgp driver The dbgp_ehci_status() was added in a number of places to report the critical ehci registers to diagnose the cause of a failure of the ehci-dbgp driver. 2) Capability to survive the host controller initialization The dbgp_external_startup(), dbgp_not_safe, and dbgp_phys_port were added so as to allow the ehci-dbgp to re-initialize after the ehci host controller is reset by the standard host controller driver. This same routine is common for the early startup or re-initialization. This resulted in the need to move some of the initialization code out of the __init section because the ehci driver has the possibility to be loaded later on as a kernel module. 3) Stability improvements for device initialization The device enumeration from 0 to 127 has the possibility to fail the first time after a warm reset on some older EHCI debug controllers. The enumeration will be tried up to 3 times to account for this failure case. The dbg_wait_until_complete() was changed to wait up to 250 ms before failing which only comes into play during device initialization. The maximum delay will never get hit during the course of normal operation of the driver, unless the device got unplugged or there was a ehci controller failure, in which case the dbgp device driver will shut itself down. Signed-off-by: Jason Wessel Cc: Ingo Molnar Cc: Andrew Morton Cc: dbrownell@users.sourceforge.net Cc: Yinghai Lu Cc: "Eric W. Biederman" Signed-off-by: Greg Kroah-Hartman --- drivers/usb/early/ehci-dbgp.c | 442 ++++++++++++++++++++++++++++-------------- include/linux/usb/ehci_def.h | 5 + 2 files changed, 299 insertions(+), 148 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/early/ehci-dbgp.c b/drivers/usb/early/ehci-dbgp.c index 6198ebded3a4..06e05ea17871 100644 --- a/drivers/usb/early/ehci-dbgp.c +++ b/drivers/usb/early/ehci-dbgp.c @@ -1,5 +1,19 @@ +/* + * Standalone EHCI usb debug driver + * + * Originally written by: + * Eric W. Biederman" and + * Yinghai Lu + * + * Changes for early/late printk and HW errata: + * Jason Wessel + * Copyright (C) 2009 Wind River Systems, Inc. + * + */ + #include #include +#include #include #include #include @@ -9,15 +23,37 @@ #include #include -#ifdef DBGP_DEBUG -# define dbgp_printk printk -#else -static inline void dbgp_printk(const char *fmt, ...) { } -#endif +/* The code here is intended to talk directly to the EHCI debug port + * and does not require that you have any kind of USB host controller + * drivers or USB device drivers compiled into the kernel. + * + * If you make a change to anything in here, the following test cases + * need to pass where a USB debug device works in the following + * configurations. + * + * 1. boot args: earlyprintk=dbgp + * o kernel compiled with # CONFIG_USB_EHCI_HCD is not set + * o kernel compiled with CONFIG_USB_EHCI_HCD=y + * 2. boot args: earlyprintk=dbgp,keep + * o kernel compiled with # CONFIG_USB_EHCI_HCD is not set + * o kernel compiled with CONFIG_USB_EHCI_HCD=y + * 3. boot args: earlyprintk=dbgp console=ttyUSB0 + * o kernel has CONFIG_USB_EHCI_HCD=y and + * CONFIG_USB_SERIAL_DEBUG=y + * 4. boot args: earlyprintk=vga,dbgp + * o kernel compiled with # CONFIG_USB_EHCI_HCD is not set + * o kernel compiled with CONFIG_USB_EHCI_HCD=y + * + * For the 4th configuration you can turn on or off the DBGP_DEBUG + * such that you can debug the dbgp device's driver code. + */ + +static int dbgp_phys_port = 1; static struct ehci_caps __iomem *ehci_caps; static struct ehci_regs __iomem *ehci_regs; static struct ehci_dbg_port __iomem *ehci_debug; +static int dbgp_not_safe; /* Cannot use debug device during ehci reset */ static unsigned int dbgp_endpoint_out; struct ehci_dev { @@ -32,6 +68,26 @@ static struct ehci_dev ehci_dev; #define DBGP_DATA_TOGGLE 0x8800 +#ifdef DBGP_DEBUG +#define dbgp_printk printk +static void dbgp_ehci_status(char *str) +{ + if (!ehci_debug) + return; + dbgp_printk("dbgp: %s\n", str); + dbgp_printk(" Debug control: %08x", readl(&ehci_debug->control)); + dbgp_printk(" ehci cmd : %08x", readl(&ehci_regs->command)); + dbgp_printk(" ehci conf flg: %08x\n", + readl(&ehci_regs->configured_flag)); + dbgp_printk(" ehci status : %08x", readl(&ehci_regs->status)); + dbgp_printk(" ehci portsc : %08x\n", + readl(&ehci_regs->port_status[dbgp_phys_port - 1])); +} +#else +static inline void dbgp_ehci_status(char *str) { } +static inline void dbgp_printk(const char *fmt, ...) { } +#endif + static inline u32 dbgp_pid_update(u32 x, u32 tok) { return ((x ^ DBGP_DATA_TOGGLE) & 0xffff00) | (tok & 0xff); @@ -79,21 +135,23 @@ static inline u32 dbgp_len_update(u32 x, u32 len) #define HUB_RESET_TIMEOUT 500 #define DBGP_MAX_PACKET 8 +#define DBGP_TIMEOUT (250 * 1000) static int dbgp_wait_until_complete(void) { u32 ctrl; - int loop = 0x100000; + int loop = DBGP_TIMEOUT; do { ctrl = readl(&ehci_debug->control); /* Stop when the transaction is finished */ if (ctrl & DBGP_DONE) break; + udelay(1); } while (--loop > 0); if (!loop) - return -1; + return -DBGP_TIMEOUT; /* * Now that we have observed the completed transaction, @@ -103,7 +161,7 @@ static int dbgp_wait_until_complete(void) return (ctrl & DBGP_ERROR) ? -DBGP_ERRCODE(ctrl) : DBGP_LEN(ctrl); } -static void __init dbgp_mdelay(int ms) +static inline void dbgp_mdelay(int ms) { int i; @@ -130,8 +188,17 @@ retry: pids = readl(&ehci_debug->pids); lpid = DBGP_PID_GET(pids); - if (ret < 0) + if (ret < 0) { + /* A -DBGP_TIMEOUT failure here means the device has + * failed, perhaps because it was unplugged, in which + * case we do not want to hang the system so the dbgp + * will be marked as unsafe to use. EHCI reset is the + * only way to recover if you unplug the dbgp device. + */ + if (ret == -DBGP_TIMEOUT && !dbgp_not_safe) + dbgp_not_safe = 1; return ret; + } /* * If the port is getting full or it has dropped data @@ -149,7 +216,7 @@ retry: return ret; } -static void dbgp_set_data(const void *buf, int size) +static inline void dbgp_set_data(const void *buf, int size) { const unsigned char *bytes = buf; u32 lo, hi; @@ -164,7 +231,7 @@ static void dbgp_set_data(const void *buf, int size) writel(hi, &ehci_debug->data47); } -static void __init dbgp_get_data(void *buf, int size) +static inline void dbgp_get_data(void *buf, int size) { unsigned char *bytes = buf; u32 lo, hi; @@ -208,7 +275,7 @@ static int dbgp_bulk_write(unsigned devnum, unsigned endpoint, return ret; } -static int __init dbgp_bulk_read(unsigned devnum, unsigned endpoint, void *data, +static int dbgp_bulk_read(unsigned devnum, unsigned endpoint, void *data, int size) { u32 pids, addr, ctrl; @@ -239,7 +306,7 @@ static int __init dbgp_bulk_read(unsigned devnum, unsigned endpoint, void *data, return ret; } -static int __init dbgp_control_msg(unsigned devnum, int requesttype, +static int dbgp_control_msg(unsigned devnum, int requesttype, int request, int value, int index, void *data, int size) { u32 pids, addr, ctrl; @@ -342,13 +409,179 @@ static u32 __init find_dbgp(int ehci_num, u32 *rbus, u32 *rslot, u32 *rfunc) return 0; } +static int dbgp_ehci_startup(void) +{ + u32 ctrl, cmd, status; + int loop; + + /* Claim ownership, but do not enable yet */ + ctrl = readl(&ehci_debug->control); + ctrl |= DBGP_OWNER; + ctrl &= ~(DBGP_ENABLED | DBGP_INUSE); + writel(ctrl, &ehci_debug->control); + udelay(1); + + dbgp_ehci_status("EHCI startup"); + /* Start the ehci running */ + cmd = readl(&ehci_regs->command); + cmd &= ~(CMD_LRESET | CMD_IAAD | CMD_PSE | CMD_ASE | CMD_RESET); + cmd |= CMD_RUN; + writel(cmd, &ehci_regs->command); + + /* Ensure everything is routed to the EHCI */ + writel(FLAG_CF, &ehci_regs->configured_flag); + + /* Wait until the controller is no longer halted */ + loop = 10; + do { + status = readl(&ehci_regs->status); + if (!(status & STS_HALT)) + break; + udelay(1); + } while (--loop > 0); + + if (!loop) { + dbgp_printk("ehci can not be started\n"); + return -ENODEV; + } + dbgp_printk("ehci started\n"); + return 0; +} + +static int dbgp_ehci_controller_reset(void) +{ + int loop = 250 * 1000; + u32 cmd; + + /* Reset the EHCI controller */ + cmd = readl(&ehci_regs->command); + cmd |= CMD_RESET; + writel(cmd, &ehci_regs->command); + do { + cmd = readl(&ehci_regs->command); + } while ((cmd & CMD_RESET) && (--loop > 0)); + + if (!loop) { + dbgp_printk("can not reset ehci\n"); + return -1; + } + dbgp_ehci_status("ehci reset done"); + return 0; +} +static int ehci_wait_for_port(int port); +/* Return 0 on success + * Return -ENODEV for any general failure + * Return -EIO if wait for port fails + */ +int dbgp_external_startup(void) +{ + int devnum; + struct usb_debug_descriptor dbgp_desc; + int ret; + u32 ctrl, portsc; + int dbg_port = dbgp_phys_port; + int tries = 3; + + ret = dbgp_ehci_startup(); + if (ret) + return ret; + + /* Wait for a device to show up in the debug port */ + ret = ehci_wait_for_port(dbg_port); + if (ret < 0) { + portsc = readl(&ehci_regs->port_status[dbg_port - 1]); + dbgp_printk("No device found in debug port\n"); + return -EIO; + } + dbgp_ehci_status("wait for port done"); + + /* Enable the debug port */ + ctrl = readl(&ehci_debug->control); + ctrl |= DBGP_CLAIM; + writel(ctrl, &ehci_debug->control); + ctrl = readl(&ehci_debug->control); + if ((ctrl & DBGP_CLAIM) != DBGP_CLAIM) { + dbgp_printk("No device in debug port\n"); + writel(ctrl & ~DBGP_CLAIM, &ehci_debug->control); + return -ENODEV; + } + dbgp_ehci_status("debug ported enabled"); + + /* Completely transfer the debug device to the debug controller */ + portsc = readl(&ehci_regs->port_status[dbg_port - 1]); + portsc &= ~PORT_PE; + writel(portsc, &ehci_regs->port_status[dbg_port - 1]); + + dbgp_mdelay(100); + +try_again: + /* Find the debug device and make it device number 127 */ + for (devnum = 0; devnum <= 127; devnum++) { + ret = dbgp_control_msg(devnum, + USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + USB_REQ_GET_DESCRIPTOR, (USB_DT_DEBUG << 8), 0, + &dbgp_desc, sizeof(dbgp_desc)); + if (ret > 0) + break; + } + if (devnum > 127) { + dbgp_printk("Could not find attached debug device\n"); + goto err; + } + if (ret < 0) { + dbgp_printk("Attached device is not a debug device\n"); + goto err; + } + dbgp_endpoint_out = dbgp_desc.bDebugOutEndpoint; + + /* Move the device to 127 if it isn't already there */ + if (devnum != USB_DEBUG_DEVNUM) { + ret = dbgp_control_msg(devnum, + USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + USB_REQ_SET_ADDRESS, USB_DEBUG_DEVNUM, 0, NULL, 0); + if (ret < 0) { + dbgp_printk("Could not move attached device to %d\n", + USB_DEBUG_DEVNUM); + goto err; + } + devnum = USB_DEBUG_DEVNUM; + dbgp_printk("debug device renamed to 127\n"); + } + + /* Enable the debug interface */ + ret = dbgp_control_msg(USB_DEBUG_DEVNUM, + USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + USB_REQ_SET_FEATURE, USB_DEVICE_DEBUG_MODE, 0, NULL, 0); + if (ret < 0) { + dbgp_printk(" Could not enable the debug device\n"); + goto err; + } + dbgp_printk("debug interface enabled\n"); + /* Perform a small write to get the even/odd data state in sync + */ + ret = dbgp_bulk_write(USB_DEBUG_DEVNUM, dbgp_endpoint_out, " ", 1); + if (ret < 0) { + dbgp_printk("dbgp_bulk_write failed: %d\n", ret); + goto err; + } + dbgp_printk("small write doned\n"); + dbgp_not_safe = 0; + + return 0; +err: + if (tries--) + goto try_again; + return -ENODEV; +} +EXPORT_SYMBOL_GPL(dbgp_external_startup); + static int __init ehci_reset_port(int port) { u32 portsc; u32 delay_time, delay; int loop; - dbgp_printk("ehci_reset_port %i\n", port); + dbgp_ehci_status("reset port"); /* Reset the usb debug port */ portsc = readl(&ehci_regs->port_status[port - 1]); portsc &= ~PORT_PE; @@ -388,7 +621,7 @@ static int __init ehci_reset_port(int port) return -EBUSY; } -static int __init ehci_wait_for_port(int port) +static int ehci_wait_for_port(int port) { u32 status; int ret, reps; @@ -487,12 +720,9 @@ static void __init early_ehci_bios_handoff(void) static int __init ehci_setup(void) { - struct usb_debug_descriptor dbgp_desc; - u32 cmd, ctrl, status, portsc, hcs_params; + u32 ctrl, portsc, hcs_params; u32 debug_port, new_debug_port = 0, n_ports; - u32 devnum; int ret, i; - int loop; int port_map_tried; int playtimes = 3; @@ -505,10 +735,12 @@ try_next_port: hcs_params = readl(&ehci_caps->hcs_params); debug_port = HCS_DEBUG_PORT(hcs_params); + dbgp_phys_port = debug_port; n_ports = HCS_N_PORTS(hcs_params); dbgp_printk("debug_port: %d\n", debug_port); dbgp_printk("n_ports: %d\n", n_ports); + dbgp_ehci_status(""); for (i = 1; i <= n_ports; i++) { portsc = readl(&ehci_regs->port_status[i-1]); @@ -523,138 +755,27 @@ try_next_port: return -1; } - loop = 250 * 1000; - /* Reset the EHCI controller */ - cmd = readl(&ehci_regs->command); - cmd |= CMD_RESET; - writel(cmd, &ehci_regs->command); - do { - cmd = readl(&ehci_regs->command); - } while ((cmd & CMD_RESET) && (--loop > 0)); - - if (!loop) { - dbgp_printk("can not reset ehci\n"); - return -1; - } - dbgp_printk("ehci reset done\n"); - - /* Claim ownership, but do not enable yet */ - ctrl = readl(&ehci_debug->control); - ctrl |= DBGP_OWNER; - ctrl &= ~(DBGP_ENABLED | DBGP_INUSE); - writel(ctrl, &ehci_debug->control); - udelay(1); - - /* Start the ehci running */ - cmd = readl(&ehci_regs->command); - cmd &= ~(CMD_LRESET | CMD_IAAD | CMD_PSE | CMD_ASE | CMD_RESET); - cmd |= CMD_RUN; - writel(cmd, &ehci_regs->command); - - /* Ensure everything is routed to the EHCI */ - writel(FLAG_CF, &ehci_regs->configured_flag); - - /* Wait until the controller is no longer halted */ - loop = 10; - do { - status = readl(&ehci_regs->status); - if (!(status & STS_HALT)) - break; - udelay(1); - } while (--loop > 0); - - if (!loop) { - dbgp_printk("ehci can not be started\n"); - return -1; + /* Only reset the controller if it is not already in the + * configured state */ + if (!(readl(&ehci_regs->configured_flag) & FLAG_CF)) { + if (dbgp_ehci_controller_reset() != 0) + return -1; + } else { + dbgp_ehci_status("ehci skip - already configured"); } - dbgp_printk("ehci started\n"); - /* Wait for a device to show up in the debug port */ - ret = ehci_wait_for_port(debug_port); - if (ret < 0) { - dbgp_printk("No device found in debug port\n"); + ret = dbgp_external_startup(); + if (ret == -EIO) goto next_debug_port; - } - dbgp_printk("ehci wait for port done\n"); - - /* Enable the debug port */ - ctrl = readl(&ehci_debug->control); - ctrl |= DBGP_CLAIM; - writel(ctrl, &ehci_debug->control); - ctrl = readl(&ehci_debug->control); - if ((ctrl & DBGP_CLAIM) != DBGP_CLAIM) { - dbgp_printk("No device in debug port\n"); - writel(ctrl & ~DBGP_CLAIM, &ehci_debug->control); - goto err; - } - dbgp_printk("debug ported enabled\n"); - /* Completely transfer the debug device to the debug controller */ - portsc = readl(&ehci_regs->port_status[debug_port - 1]); - portsc &= ~PORT_PE; - writel(portsc, &ehci_regs->port_status[debug_port - 1]); - - dbgp_mdelay(100); - - /* Find the debug device and make it device number 127 */ - for (devnum = 0; devnum <= 127; devnum++) { - ret = dbgp_control_msg(devnum, - USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE, - USB_REQ_GET_DESCRIPTOR, (USB_DT_DEBUG << 8), 0, - &dbgp_desc, sizeof(dbgp_desc)); - if (ret > 0) - break; - } - if (devnum > 127) { - dbgp_printk("Could not find attached debug device\n"); - goto err; - } if (ret < 0) { - dbgp_printk("Attached device is not a debug device\n"); - goto err; - } - dbgp_endpoint_out = dbgp_desc.bDebugOutEndpoint; - - /* Move the device to 127 if it isn't already there */ - if (devnum != USB_DEBUG_DEVNUM) { - ret = dbgp_control_msg(devnum, - USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, - USB_REQ_SET_ADDRESS, USB_DEBUG_DEVNUM, 0, NULL, 0); - if (ret < 0) { - dbgp_printk("Could not move attached device to %d\n", - USB_DEBUG_DEVNUM); - goto err; - } - devnum = USB_DEBUG_DEVNUM; - dbgp_printk("debug device renamed to 127\n"); - } - - /* Enable the debug interface */ - ret = dbgp_control_msg(USB_DEBUG_DEVNUM, - USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, - USB_REQ_SET_FEATURE, USB_DEVICE_DEBUG_MODE, 0, NULL, 0); - if (ret < 0) { - dbgp_printk(" Could not enable the debug device\n"); - goto err; - } - dbgp_printk("debug interface enabled\n"); - - /* Perform a small write to get the even/odd data state in sync - */ - ret = dbgp_bulk_write(USB_DEBUG_DEVNUM, dbgp_endpoint_out, " ", 1); - if (ret < 0) { - dbgp_printk("dbgp_bulk_write failed: %d\n", ret); - goto err; + /* Things didn't work so remove my claim */ + ctrl = readl(&ehci_debug->control); + ctrl &= ~(DBGP_CLAIM | DBGP_OUT); + writel(ctrl, &ehci_debug->control); + return -1; } - dbgp_printk("small write doned\n"); - return 0; -err: - /* Things didn't work so remove my claim */ - ctrl = readl(&ehci_debug->control); - ctrl &= ~(DBGP_CLAIM | DBGP_OUT); - writel(ctrl, &ehci_debug->control); - return -1; next_debug_port: port_map_tried |= (1<<(debug_port - 1)); @@ -749,6 +870,7 @@ int __init early_dbgp_init(char *s) return -1; } + dbgp_ehci_status("early_init_complete"); return 0; } @@ -758,9 +880,27 @@ static void early_dbgp_write(struct console *con, const char *str, u32 n) int chunk, ret; char buf[DBGP_MAX_PACKET]; int use_cr = 0; + u32 cmd, ctrl; + int reset_run = 0; - if (!ehci_debug) + if (!ehci_debug || dbgp_not_safe) return; + + cmd = readl(&ehci_regs->command); + if (unlikely(!(cmd & CMD_RUN))) { + /* If the ehci controller is not in the run state do extended + * checks to see if the acpi or some other initialization also + * reset the ehci debug port */ + ctrl = readl(&ehci_debug->control); + if (!(ctrl & DBGP_ENABLED)) { + dbgp_not_safe = 1; + dbgp_external_startup(); + } else { + cmd |= CMD_RUN; + writel(cmd, &ehci_regs->command); + reset_run = 1; + } + } while (n > 0) { for (chunk = 0; chunk < DBGP_MAX_PACKET && n > 0; str++, chunk++, n--) { @@ -775,8 +915,15 @@ static void early_dbgp_write(struct console *con, const char *str, u32 n) use_cr = 0; buf[chunk] = *str; } - ret = dbgp_bulk_write(USB_DEBUG_DEVNUM, - dbgp_endpoint_out, buf, chunk); + if (chunk > 0) { + ret = dbgp_bulk_write(USB_DEBUG_DEVNUM, + dbgp_endpoint_out, buf, chunk); + } + } + if (unlikely(reset_run)) { + cmd = readl(&ehci_regs->command); + cmd &= ~CMD_RUN; + writel(cmd, &ehci_regs->command); } } @@ -786,4 +933,3 @@ struct console early_dbgp_console = { .flags = CON_PRINTBUFFER, .index = -1, }; - diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h index 1deac2a7b12e..07851e05d763 100644 --- a/include/linux/usb/ehci_def.h +++ b/include/linux/usb/ehci_def.h @@ -176,4 +176,9 @@ extern int __init early_dbgp_init(char *s); extern struct console early_dbgp_console; #endif /* CONFIG_EARLY_PRINTK_DBGP */ +#ifdef CONFIG_EARLY_PRINTK_DBGP +/* Call backs from ehci host driver to ehci debug driver */ +extern int dbgp_external_startup(void); +#endif + #endif /* __LINUX_USB_EHCI_DEF_H */ -- cgit v1.2.3-71-gd317 From 8d053c79f22462f55c02c8083580730b922cf7b4 Mon Sep 17 00:00:00 2001 From: Jason Wessel Date: Thu, 20 Aug 2009 15:39:54 -0500 Subject: USB: ehci-dbgp,ehci: Allow early or late use of the dbgp device If the EHCI debug port is initialized and in use, the EHCI host controller driver must follow two rules. 1) If the EHCI host driver issues a controller reset, the debug controller driver re-initialization must get called after the reset is completed. 2) The EHCI host driver should ignore any requests to the physical EHCI debug port when the EHCI debug port is in use. The code to check for the debug port was moved from ehci_pci_reinit() to ehci_pci_setup because it must get called prior to ehci_reset() which will clear the debug port registers. Signed-off-by: Jason Wessel Cc: Alan Stern Cc: dbrownell@users.sourceforge.net Cc: Ingo Molnar Cc: Andrew Morton Cc: Yinghai Lu Cc: "Eric W. Biederman" Signed-off-by: Greg Kroah-Hartman --- drivers/usb/early/ehci-dbgp.c | 23 +++++++++++++++++++++++ drivers/usb/host/ehci-hcd.c | 8 ++++++++ drivers/usb/host/ehci-hub.c | 10 ++++++++++ drivers/usb/host/ehci-pci.c | 39 +++++++++++++++++++-------------------- include/linux/usb/ehci_def.h | 10 ++++++++++ 5 files changed, 70 insertions(+), 20 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/early/ehci-dbgp.c b/drivers/usb/early/ehci-dbgp.c index 06e05ea17871..b88cb65b64e0 100644 --- a/drivers/usb/early/ehci-dbgp.c +++ b/drivers/usb/early/ehci-dbgp.c @@ -933,3 +933,26 @@ struct console early_dbgp_console = { .flags = CON_PRINTBUFFER, .index = -1, }; + +int dbgp_reset_prep(void) +{ + u32 ctrl; + + dbgp_not_safe = 1; + if (!ehci_debug) + return 0; + + if (early_dbgp_console.index != -1 && + !(early_dbgp_console.flags & CON_BOOT)) + return 1; + /* This means the console is not initialized, or should get + * shutdown so as to allow for reuse of the usb device, which + * means it is time to shutdown the usb debug port. */ + ctrl = readl(&ehci_debug->control); + if (ctrl & DBGP_ENABLED) { + ctrl &= ~(DBGP_CLAIM); + writel(ctrl, &ehci_debug->control); + } + return 0; +} +EXPORT_SYMBOL_GPL(dbgp_reset_prep); diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 4f89d7ffd53a..9835e0713943 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -240,6 +240,11 @@ static int ehci_reset (struct ehci_hcd *ehci) int retval; u32 command = ehci_readl(ehci, &ehci->regs->command); + /* If the EHCI debug controller is active, special care must be + * taken before and after a host controller reset */ + if (ehci->debug && !dbgp_reset_prep()) + ehci->debug = NULL; + command |= CMD_RESET; dbg_cmd (ehci, "reset", command); ehci_writel(ehci, command, &ehci->regs->command); @@ -260,6 +265,9 @@ static int ehci_reset (struct ehci_hcd *ehci) if (ehci_is_TDI(ehci)) tdi_reset (ehci); + if (ehci->debug) + dbgp_external_startup(); + return retval; } diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 818647c33da8..6b5e4d18d4bf 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -855,6 +855,15 @@ static int ehci_hub_control ( case SetPortFeature: selector = wIndex >> 8; wIndex &= 0xff; + if (unlikely(ehci->debug)) { + /* If the debug port is active any port + * feature requests should get denied */ + if (wIndex == HCS_DEBUG_PORT(ehci->hcs_params) && + (readl(&ehci->debug->control) & DBGP_ENABLED)) { + retval = -ENODEV; + goto error_exit; + } + } if (!wIndex || wIndex > ports) goto error; wIndex--; @@ -951,6 +960,7 @@ error: /* "stall" on error */ retval = -EPIPE; } +error_exit: spin_unlock_irqrestore (&ehci->lock, flags); return retval; } diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index a88ad517ec5c..378861b9d79a 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -27,28 +27,8 @@ /* called after powerup, by probe or system-pm "wakeup" */ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev) { - u32 temp; int retval; - /* optional debug port, normally in the first BAR */ - temp = pci_find_capability(pdev, 0x0a); - if (temp) { - pci_read_config_dword(pdev, temp, &temp); - temp >>= 16; - if ((temp & (3 << 13)) == (1 << 13)) { - temp &= 0x1fff; - ehci->debug = ehci_to_hcd(ehci)->regs + temp; - temp = ehci_readl(ehci, &ehci->debug->control); - ehci_info(ehci, "debug port %d%s\n", - HCS_DEBUG_PORT(ehci->hcs_params), - (temp & DBGP_ENABLED) - ? " IN USE" - : ""); - if (!(temp & DBGP_ENABLED)) - ehci->debug = NULL; - } - } - /* we expect static quirk code to handle the "extended capabilities" * (currently just BIOS handoff) allowed starting with EHCI 0.96 */ @@ -195,6 +175,25 @@ static int ehci_pci_setup(struct usb_hcd *hcd) break; } + /* optional debug port, normally in the first BAR */ + temp = pci_find_capability(pdev, 0x0a); + if (temp) { + pci_read_config_dword(pdev, temp, &temp); + temp >>= 16; + if ((temp & (3 << 13)) == (1 << 13)) { + temp &= 0x1fff; + ehci->debug = ehci_to_hcd(ehci)->regs + temp; + temp = ehci_readl(ehci, &ehci->debug->control); + ehci_info(ehci, "debug port %d%s\n", + HCS_DEBUG_PORT(ehci->hcs_params), + (temp & DBGP_ENABLED) + ? " IN USE" + : ""); + if (!(temp & DBGP_ENABLED)) + ehci->debug = NULL; + } + } + ehci_reset(ehci); /* at least the Genesys GL880S needs fixup here */ diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h index 07851e05d763..1909d924f816 100644 --- a/include/linux/usb/ehci_def.h +++ b/include/linux/usb/ehci_def.h @@ -179,6 +179,16 @@ extern struct console early_dbgp_console; #ifdef CONFIG_EARLY_PRINTK_DBGP /* Call backs from ehci host driver to ehci debug driver */ extern int dbgp_external_startup(void); +extern int dbgp_reset_prep(void); +#else +static inline int dbgp_reset_prep(void) +{ + return 1; +} +static inline int dbgp_external_startup(void) +{ + return -1; +} #endif #endif /* __LINUX_USB_EHCI_DEF_H */ -- cgit v1.2.3-71-gd317 From aab2d4086a1876fcff282aa36e2d4a92aa9935c9 Mon Sep 17 00:00:00 2001 From: Jason Wessel Date: Thu, 20 Aug 2009 15:39:55 -0500 Subject: USB: ehci-dbgp: errata for EHCI debug controller initialization On some EHCI usb debug controllers, the EHCI debug device will fail to be seen after a port reset, after a warm reset. Two options exist to get the device to initialize correctly. Option 1 is to unplug and plug in the device. Option 2 is to use the EHCI port test to get the usb debug device to start talking again. At that point the debug controller port reset will succeed. Signed-off-by: Jason Wessel Cc: Ingo Molnar Cc: Andrew Morton Cc: Yinghai Lu Cc: "Eric W. Biederman" CC: dbrownell@users.sourceforge.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/early/ehci-dbgp.c | 23 ++++++++++++++++++++++- include/linux/usb/ehci_def.h | 1 + 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/early/ehci-dbgp.c b/drivers/usb/early/ehci-dbgp.c index b88cb65b64e0..f0a41c647bef 100644 --- a/drivers/usb/early/ehci-dbgp.c +++ b/drivers/usb/early/ehci-dbgp.c @@ -478,10 +478,13 @@ int dbgp_external_startup(void) int devnum; struct usb_debug_descriptor dbgp_desc; int ret; - u32 ctrl, portsc; + u32 ctrl, portsc, cmd; int dbg_port = dbgp_phys_port; int tries = 3; + int reset_port_tries = 1; + int try_hard_once = 1; +try_port_reset_again: ret = dbgp_ehci_startup(); if (ret) return ret; @@ -490,6 +493,24 @@ int dbgp_external_startup(void) ret = ehci_wait_for_port(dbg_port); if (ret < 0) { portsc = readl(&ehci_regs->port_status[dbg_port - 1]); + if (!(portsc & PORT_CONNECT) && try_hard_once) { + /* Last ditch effort to try to force enable + * the debug device by using the packet test + * ehci command to try and wake it up. */ + try_hard_once = 0; + cmd = readl(&ehci_regs->command); + cmd &= ~CMD_RUN; + writel(cmd, &ehci_regs->command); + portsc = readl(&ehci_regs->port_status[dbg_port - 1]); + portsc |= PORT_TEST_PKT; + writel(portsc, &ehci_regs->port_status[dbg_port - 1]); + dbgp_ehci_status("Trying to force debug port online"); + mdelay(50); + dbgp_ehci_controller_reset(); + goto try_port_reset_again; + } else if (reset_port_tries--) { + goto try_port_reset_again; + } dbgp_printk("No device found in debug port\n"); return -EIO; } diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h index 1909d924f816..af4b86f3aca3 100644 --- a/include/linux/usb/ehci_def.h +++ b/include/linux/usb/ehci_def.h @@ -105,6 +105,7 @@ struct ehci_regs { #define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ #define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */ /* 19:16 for port testing */ +#define PORT_TEST_PKT (0x4<<16) /* Port Test Control - packet test */ #define PORT_LED_OFF (0<<14) #define PORT_LED_AMBER (1<<14) #define PORT_LED_GREEN (2<<14) -- cgit v1.2.3-71-gd317