Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: (143 commits) USB: xhci depends on PCI. USB: xhci: Add Makefile, MAINTAINERS, and Kconfig entries. USB: xhci: Respect critical sections. USB: xHCI: Fix interrupt moderation. USB: xhci: Remove packed attribute from structures. usb; xhci: Fix TRB offset calculations. USB: xhci: replace if-elseif-else with switch-case USB: xhci: Make xhci-mem.c include linux/dmapool.h USB: xhci: drop spinlock in xhci_urb_enqueue() error path. USB: Change names of SuperSpeed ep companion descriptor structs. USB: xhci: Avoid compiler reordering in Link TRB giveback. USB: xhci: Clean up xhci_irq() function. USB: xhci: Avoid global namespace pollution. USB: xhci: Fix Link TRB handoff bit twiddling. USB: xhci: Fix register write order. USB: xhci: fix some compiler warnings in xhci.h USB: xhci: fix lots of compiler warnings. USB: xhci: use xhci_handle_event instead of handle_event USB: xhci: URB cancellation support. USB: xhci: Scatter gather list support for bulk transfers. ...
This commit is contained in:
commit
e1f5b94fd0
@ -6165,6 +6165,12 @@ L: linux-wireless@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/net/wireless/rndis_wlan.c
|
||||
|
||||
USB XHCI DRIVER
|
||||
P: Sarah Sharp
|
||||
M: sarah.a.sharp@intel.com
|
||||
L: linux-usb@vger.kernel.org
|
||||
S: Supported
|
||||
|
||||
USB ZC0301 DRIVER
|
||||
P: Luca Risolia
|
||||
M: luca.risolia@studio.unibo.it
|
||||
|
50
arch/arm/plat-s3c/include/plat/regs-usb-hsotg-phy.h
Normal file
50
arch/arm/plat-s3c/include/plat/regs-usb-hsotg-phy.h
Normal file
@ -0,0 +1,50 @@
|
||||
/* arch/arm/plat-s3c/include/plat/regs-usb-hsotg-phy.h
|
||||
*
|
||||
* Copyright 2008 Openmoko, Inc.
|
||||
* Copyright 2008 Simtec Electronics
|
||||
* http://armlinux.simtec.co.uk/
|
||||
* Ben Dooks <ben@simtec.co.uk>
|
||||
*
|
||||
* S3C - USB2.0 Highspeed/OtG device PHY registers
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/* Note, this is a seperate header file as some of the clock framework
|
||||
* needs to touch this if the clk_48m is used as the USB OHCI or other
|
||||
* peripheral source.
|
||||
*/
|
||||
|
||||
#ifndef __PLAT_S3C64XX_REGS_USB_HSOTG_PHY_H
|
||||
#define __PLAT_S3C64XX_REGS_USB_HSOTG_PHY_H __FILE__
|
||||
|
||||
/* S3C64XX_PA_USB_HSPHY */
|
||||
|
||||
#define S3C_HSOTG_PHYREG(x) ((x) + S3C_VA_USB_HSPHY)
|
||||
|
||||
#define S3C_PHYPWR S3C_HSOTG_PHYREG(0x00)
|
||||
#define SRC_PHYPWR_OTG_DISABLE (1 << 4)
|
||||
#define SRC_PHYPWR_ANALOG_POWERDOWN (1 << 3)
|
||||
#define SRC_PHYPWR_FORCE_SUSPEND (1 << 1)
|
||||
|
||||
#define S3C_PHYCLK S3C_HSOTG_PHYREG(0x04)
|
||||
#define S3C_PHYCLK_MODE_USB11 (1 << 6)
|
||||
#define S3C_PHYCLK_EXT_OSC (1 << 5)
|
||||
#define S3C_PHYCLK_CLK_FORCE (1 << 4)
|
||||
#define S3C_PHYCLK_ID_PULL (1 << 2)
|
||||
#define S3C_PHYCLK_CLKSEL_MASK (0x3 << 0)
|
||||
#define S3C_PHYCLK_CLKSEL_SHIFT (0)
|
||||
#define S3C_PHYCLK_CLKSEL_48M (0x0 << 0)
|
||||
#define S3C_PHYCLK_CLKSEL_12M (0x2 << 0)
|
||||
#define S3C_PHYCLK_CLKSEL_24M (0x3 << 0)
|
||||
|
||||
#define S3C_RSTCON S3C_HSOTG_PHYREG(0x08)
|
||||
#define S3C_RSTCON_PHYCLK (1 << 2)
|
||||
#define S3C_RSTCON_HCLK (1 << 2)
|
||||
#define S3C_RSTCON_PHY (1 << 0)
|
||||
|
||||
#define S3C_PHYTUNE S3C_HSOTG_PHYREG(0x20)
|
||||
|
||||
#endif /* __PLAT_S3C64XX_REGS_USB_HSOTG_PHY_H */
|
377
arch/arm/plat-s3c/include/plat/regs-usb-hsotg.h
Normal file
377
arch/arm/plat-s3c/include/plat/regs-usb-hsotg.h
Normal file
@ -0,0 +1,377 @@
|
||||
/* arch/arm/plat-s3c/include/plat/regs-usb-hsotg.h
|
||||
*
|
||||
* Copyright 2008 Openmoko, Inc.
|
||||
* Copyright 2008 Simtec Electronics
|
||||
* http://armlinux.simtec.co.uk/
|
||||
* Ben Dooks <ben@simtec.co.uk>
|
||||
*
|
||||
* S3C - USB2.0 Highspeed/OtG device block registers
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __PLAT_S3C64XX_REGS_USB_HSOTG_H
|
||||
#define __PLAT_S3C64XX_REGS_USB_HSOTG_H __FILE__
|
||||
|
||||
#define S3C_HSOTG_REG(x) (x)
|
||||
|
||||
#define S3C_GOTGCTL S3C_HSOTG_REG(0x000)
|
||||
#define S3C_GOTGCTL_BSESVLD (1 << 19)
|
||||
#define S3C_GOTGCTL_ASESVLD (1 << 18)
|
||||
#define S3C_GOTGCTL_DBNC_SHORT (1 << 17)
|
||||
#define S3C_GOTGCTL_CONID_B (1 << 16)
|
||||
#define S3C_GOTGCTL_DEVHNPEN (1 << 11)
|
||||
#define S3C_GOTGCTL_HSSETHNPEN (1 << 10)
|
||||
#define S3C_GOTGCTL_HNPREQ (1 << 9)
|
||||
#define S3C_GOTGCTL_HSTNEGSCS (1 << 8)
|
||||
#define S3C_GOTGCTL_SESREQ (1 << 1)
|
||||
#define S3C_GOTGCTL_SESREQSCS (1 << 0)
|
||||
|
||||
#define S3C_GOTGINT S3C_HSOTG_REG(0x004)
|
||||
#define S3C_GOTGINT_DbnceDone (1 << 19)
|
||||
#define S3C_GOTGINT_ADevTOUTChg (1 << 18)
|
||||
#define S3C_GOTGINT_HstNegDet (1 << 17)
|
||||
#define S3C_GOTGINT_HstnegSucStsChng (1 << 9)
|
||||
#define S3C_GOTGINT_SesReqSucStsChng (1 << 8)
|
||||
#define S3C_GOTGINT_SesEndDet (1 << 2)
|
||||
|
||||
#define S3C_GAHBCFG S3C_HSOTG_REG(0x008)
|
||||
#define S3C_GAHBCFG_PTxFEmpLvl (1 << 8)
|
||||
#define S3C_GAHBCFG_NPTxFEmpLvl (1 << 7)
|
||||
#define S3C_GAHBCFG_DMAEn (1 << 5)
|
||||
#define S3C_GAHBCFG_HBstLen_MASK (0xf << 1)
|
||||
#define S3C_GAHBCFG_HBstLen_SHIFT (1)
|
||||
#define S3C_GAHBCFG_HBstLen_Single (0x0 << 1)
|
||||
#define S3C_GAHBCFG_HBstLen_Incr (0x1 << 1)
|
||||
#define S3C_GAHBCFG_HBstLen_Incr4 (0x3 << 1)
|
||||
#define S3C_GAHBCFG_HBstLen_Incr8 (0x5 << 1)
|
||||
#define S3C_GAHBCFG_HBstLen_Incr16 (0x7 << 1)
|
||||
#define S3C_GAHBCFG_GlblIntrEn (1 << 0)
|
||||
|
||||
#define S3C_GUSBCFG S3C_HSOTG_REG(0x00C)
|
||||
#define S3C_GUSBCFG_PHYLPClkSel (1 << 15)
|
||||
#define S3C_GUSBCFG_HNPCap (1 << 9)
|
||||
#define S3C_GUSBCFG_SRPCap (1 << 8)
|
||||
#define S3C_GUSBCFG_PHYIf16 (1 << 3)
|
||||
#define S3C_GUSBCFG_TOutCal_MASK (0x7 << 0)
|
||||
#define S3C_GUSBCFG_TOutCal_SHIFT (0)
|
||||
#define S3C_GUSBCFG_TOutCal_LIMIT (0x7)
|
||||
#define S3C_GUSBCFG_TOutCal(_x) ((_x) << 0)
|
||||
|
||||
#define S3C_GRSTCTL S3C_HSOTG_REG(0x010)
|
||||
|
||||
#define S3C_GRSTCTL_AHBIdle (1 << 31)
|
||||
#define S3C_GRSTCTL_DMAReq (1 << 30)
|
||||
#define S3C_GRSTCTL_TxFNum_MASK (0x1f << 6)
|
||||
#define S3C_GRSTCTL_TxFNum_SHIFT (6)
|
||||
#define S3C_GRSTCTL_TxFNum_LIMIT (0x1f)
|
||||
#define S3C_GRSTCTL_TxFNum(_x) ((_x) << 6)
|
||||
#define S3C_GRSTCTL_TxFFlsh (1 << 5)
|
||||
#define S3C_GRSTCTL_RxFFlsh (1 << 4)
|
||||
#define S3C_GRSTCTL_INTknQFlsh (1 << 3)
|
||||
#define S3C_GRSTCTL_FrmCntrRst (1 << 2)
|
||||
#define S3C_GRSTCTL_HSftRst (1 << 1)
|
||||
#define S3C_GRSTCTL_CSftRst (1 << 0)
|
||||
|
||||
#define S3C_GINTSTS S3C_HSOTG_REG(0x014)
|
||||
#define S3C_GINTMSK S3C_HSOTG_REG(0x018)
|
||||
|
||||
#define S3C_GINTSTS_WkUpInt (1 << 31)
|
||||
#define S3C_GINTSTS_SessReqInt (1 << 30)
|
||||
#define S3C_GINTSTS_DisconnInt (1 << 29)
|
||||
#define S3C_GINTSTS_ConIDStsChng (1 << 28)
|
||||
#define S3C_GINTSTS_PTxFEmp (1 << 26)
|
||||
#define S3C_GINTSTS_HChInt (1 << 25)
|
||||
#define S3C_GINTSTS_PrtInt (1 << 24)
|
||||
#define S3C_GINTSTS_FetSusp (1 << 22)
|
||||
#define S3C_GINTSTS_incompIP (1 << 21)
|
||||
#define S3C_GINTSTS_IncomplSOIN (1 << 20)
|
||||
#define S3C_GINTSTS_OEPInt (1 << 19)
|
||||
#define S3C_GINTSTS_IEPInt (1 << 18)
|
||||
#define S3C_GINTSTS_EPMis (1 << 17)
|
||||
#define S3C_GINTSTS_EOPF (1 << 15)
|
||||
#define S3C_GINTSTS_ISOutDrop (1 << 14)
|
||||
#define S3C_GINTSTS_EnumDone (1 << 13)
|
||||
#define S3C_GINTSTS_USBRst (1 << 12)
|
||||
#define S3C_GINTSTS_USBSusp (1 << 11)
|
||||
#define S3C_GINTSTS_ErlySusp (1 << 10)
|
||||
#define S3C_GINTSTS_GOUTNakEff (1 << 7)
|
||||
#define S3C_GINTSTS_GINNakEff (1 << 6)
|
||||
#define S3C_GINTSTS_NPTxFEmp (1 << 5)
|
||||
#define S3C_GINTSTS_RxFLvl (1 << 4)
|
||||
#define S3C_GINTSTS_SOF (1 << 3)
|
||||
#define S3C_GINTSTS_OTGInt (1 << 2)
|
||||
#define S3C_GINTSTS_ModeMis (1 << 1)
|
||||
#define S3C_GINTSTS_CurMod_Host (1 << 0)
|
||||
|
||||
#define S3C_GRXSTSR S3C_HSOTG_REG(0x01C)
|
||||
#define S3C_GRXSTSP S3C_HSOTG_REG(0x020)
|
||||
|
||||
#define S3C_GRXSTS_FN_MASK (0x7f << 25)
|
||||
#define S3C_GRXSTS_FN_SHIFT (25)
|
||||
|
||||
#define S3C_GRXSTS_PktSts_MASK (0xf << 17)
|
||||
#define S3C_GRXSTS_PktSts_SHIFT (17)
|
||||
#define S3C_GRXSTS_PktSts_GlobalOutNAK (0x1 << 17)
|
||||
#define S3C_GRXSTS_PktSts_OutRX (0x2 << 17)
|
||||
#define S3C_GRXSTS_PktSts_OutDone (0x3 << 17)
|
||||
#define S3C_GRXSTS_PktSts_SetupDone (0x4 << 17)
|
||||
#define S3C_GRXSTS_PktSts_SetupRX (0x6 << 17)
|
||||
|
||||
#define S3C_GRXSTS_DPID_MASK (0x3 << 15)
|
||||
#define S3C_GRXSTS_DPID_SHIFT (15)
|
||||
#define S3C_GRXSTS_ByteCnt_MASK (0x7ff << 4)
|
||||
#define S3C_GRXSTS_ByteCnt_SHIFT (4)
|
||||
#define S3C_GRXSTS_EPNum_MASK (0xf << 0)
|
||||
#define S3C_GRXSTS_EPNum_SHIFT (0)
|
||||
|
||||
#define S3C_GRXFSIZ S3C_HSOTG_REG(0x024)
|
||||
|
||||
#define S3C_GNPTXFSIZ S3C_HSOTG_REG(0x028)
|
||||
|
||||
#define S3C_GNPTXFSIZ_NPTxFDep_MASK (0xffff << 16)
|
||||
#define S3C_GNPTXFSIZ_NPTxFDep_SHIFT (16)
|
||||
#define S3C_GNPTXFSIZ_NPTxFDep_LIMIT (0xffff)
|
||||
#define S3C_GNPTXFSIZ_NPTxFDep(_x) ((_x) << 16)
|
||||
#define S3C_GNPTXFSIZ_NPTxFStAddr_MASK (0xffff << 0)
|
||||
#define S3C_GNPTXFSIZ_NPTxFStAddr_SHIFT (0)
|
||||
#define S3C_GNPTXFSIZ_NPTxFStAddr_LIMIT (0xffff)
|
||||
#define S3C_GNPTXFSIZ_NPTxFStAddr(_x) ((_x) << 0)
|
||||
|
||||
#define S3C_GNPTXSTS S3C_HSOTG_REG(0x02C)
|
||||
|
||||
#define S3C_GNPTXSTS_NPtxQTop_MASK (0x7f << 24)
|
||||
#define S3C_GNPTXSTS_NPtxQTop_SHIFT (24)
|
||||
|
||||
#define S3C_GNPTXSTS_NPTxQSpcAvail_MASK (0xff << 16)
|
||||
#define S3C_GNPTXSTS_NPTxQSpcAvail_SHIFT (16)
|
||||
#define S3C_GNPTXSTS_NPTxQSpcAvail_GET(_v) (((_v) >> 16) & 0xff)
|
||||
|
||||
#define S3C_GNPTXSTS_NPTxFSpcAvail_MASK (0xffff << 0)
|
||||
#define S3C_GNPTXSTS_NPTxFSpcAvail_SHIFT (0)
|
||||
#define S3C_GNPTXSTS_NPTxFSpcAvail_GET(_v) (((_v) >> 0) & 0xffff)
|
||||
|
||||
|
||||
#define S3C_HPTXFSIZ S3C_HSOTG_REG(0x100)
|
||||
|
||||
#define S3C_DPTXFSIZn(_a) S3C_HSOTG_REG(0x104 + (((_a) - 1) * 4))
|
||||
|
||||
#define S3C_DPTXFSIZn_DPTxFSize_MASK (0xffff << 16)
|
||||
#define S3C_DPTXFSIZn_DPTxFSize_SHIFT (16)
|
||||
#define S3C_DPTXFSIZn_DPTxFSize_GET(_v) (((_v) >> 16) & 0xffff)
|
||||
#define S3C_DPTXFSIZn_DPTxFSize_LIMIT (0xffff)
|
||||
#define S3C_DPTXFSIZn_DPTxFSize(_x) ((_x) << 16)
|
||||
|
||||
#define S3C_DPTXFSIZn_DPTxFStAddr_MASK (0xffff << 0)
|
||||
#define S3C_DPTXFSIZn_DPTxFStAddr_SHIFT (0)
|
||||
|
||||
/* Device mode registers */
|
||||
#define S3C_DCFG S3C_HSOTG_REG(0x800)
|
||||
|
||||
#define S3C_DCFG_EPMisCnt_MASK (0x1f << 18)
|
||||
#define S3C_DCFG_EPMisCnt_SHIFT (18)
|
||||
#define S3C_DCFG_EPMisCnt_LIMIT (0x1f)
|
||||
#define S3C_DCFG_EPMisCnt(_x) ((_x) << 18)
|
||||
|
||||
#define S3C_DCFG_PerFrInt_MASK (0x3 << 11)
|
||||
#define S3C_DCFG_PerFrInt_SHIFT (11)
|
||||
#define S3C_DCFG_PerFrInt_LIMIT (0x3)
|
||||
#define S3C_DCFG_PerFrInt(_x) ((_x) << 11)
|
||||
|
||||
#define S3C_DCFG_DevAddr_MASK (0x7f << 4)
|
||||
#define S3C_DCFG_DevAddr_SHIFT (4)
|
||||
#define S3C_DCFG_DevAddr_LIMIT (0x7f)
|
||||
#define S3C_DCFG_DevAddr(_x) ((_x) << 4)
|
||||
|
||||
#define S3C_DCFG_NZStsOUTHShk (1 << 2)
|
||||
|
||||
#define S3C_DCFG_DevSpd_MASK (0x3 << 0)
|
||||
#define S3C_DCFG_DevSpd_SHIFT (0)
|
||||
#define S3C_DCFG_DevSpd_HS (0x0 << 0)
|
||||
#define S3C_DCFG_DevSpd_FS (0x1 << 0)
|
||||
#define S3C_DCFG_DevSpd_LS (0x2 << 0)
|
||||
#define S3C_DCFG_DevSpd_FS48 (0x3 << 0)
|
||||
|
||||
#define S3C_DCTL S3C_HSOTG_REG(0x804)
|
||||
|
||||
#define S3C_DCTL_PWROnPrgDone (1 << 11)
|
||||
#define S3C_DCTL_CGOUTNak (1 << 10)
|
||||
#define S3C_DCTL_SGOUTNak (1 << 9)
|
||||
#define S3C_DCTL_CGNPInNAK (1 << 8)
|
||||
#define S3C_DCTL_SGNPInNAK (1 << 7)
|
||||
#define S3C_DCTL_TstCtl_MASK (0x7 << 4)
|
||||
#define S3C_DCTL_TstCtl_SHIFT (4)
|
||||
#define S3C_DCTL_GOUTNakSts (1 << 3)
|
||||
#define S3C_DCTL_GNPINNakSts (1 << 2)
|
||||
#define S3C_DCTL_SftDiscon (1 << 1)
|
||||
#define S3C_DCTL_RmtWkUpSig (1 << 0)
|
||||
|
||||
#define S3C_DSTS S3C_HSOTG_REG(0x808)
|
||||
|
||||
#define S3C_DSTS_SOFFN_MASK (0x3fff << 8)
|
||||
#define S3C_DSTS_SOFFN_SHIFT (8)
|
||||
#define S3C_DSTS_SOFFN_LIMIT (0x3fff)
|
||||
#define S3C_DSTS_SOFFN(_x) ((_x) << 8)
|
||||
#define S3C_DSTS_ErraticErr (1 << 3)
|
||||
#define S3C_DSTS_EnumSpd_MASK (0x3 << 1)
|
||||
#define S3C_DSTS_EnumSpd_SHIFT (1)
|
||||
#define S3C_DSTS_EnumSpd_HS (0x0 << 1)
|
||||
#define S3C_DSTS_EnumSpd_FS (0x1 << 1)
|
||||
#define S3C_DSTS_EnumSpd_LS (0x2 << 1)
|
||||
#define S3C_DSTS_EnumSpd_FS48 (0x3 << 1)
|
||||
|
||||
#define S3C_DSTS_SuspSts (1 << 0)
|
||||
|
||||
#define S3C_DIEPMSK S3C_HSOTG_REG(0x810)
|
||||
|
||||
#define S3C_DIEPMSK_INEPNakEffMsk (1 << 6)
|
||||
#define S3C_DIEPMSK_INTknEPMisMsk (1 << 5)
|
||||
#define S3C_DIEPMSK_INTknTXFEmpMsk (1 << 4)
|
||||
#define S3C_DIEPMSK_TimeOUTMsk (1 << 3)
|
||||
#define S3C_DIEPMSK_AHBErrMsk (1 << 2)
|
||||
#define S3C_DIEPMSK_EPDisbldMsk (1 << 1)
|
||||
#define S3C_DIEPMSK_XferComplMsk (1 << 0)
|
||||
|
||||
#define S3C_DOEPMSK S3C_HSOTG_REG(0x814)
|
||||
|
||||
#define S3C_DOEPMSK_Back2BackSetup (1 << 6)
|
||||
#define S3C_DOEPMSK_OUTTknEPdisMsk (1 << 4)
|
||||
#define S3C_DOEPMSK_SetupMsk (1 << 3)
|
||||
#define S3C_DOEPMSK_AHBErrMsk (1 << 2)
|
||||
#define S3C_DOEPMSK_EPDisbldMsk (1 << 1)
|
||||
#define S3C_DOEPMSK_XferComplMsk (1 << 0)
|
||||
|
||||
#define S3C_DAINT S3C_HSOTG_REG(0x818)
|
||||
#define S3C_DAINTMSK S3C_HSOTG_REG(0x81C)
|
||||
|
||||
#define S3C_DAINT_OutEP_SHIFT (16)
|
||||
#define S3C_DAINT_OutEP(x) (1 << ((x) + 16))
|
||||
#define S3C_DAINT_InEP(x) (1 << (x))
|
||||
|
||||
#define S3C_DTKNQR1 S3C_HSOTG_REG(0x820)
|
||||
#define S3C_DTKNQR2 S3C_HSOTG_REG(0x824)
|
||||
#define S3C_DTKNQR3 S3C_HSOTG_REG(0x830)
|
||||
#define S3C_DTKNQR4 S3C_HSOTG_REG(0x834)
|
||||
|
||||
#define S3C_DVBUSDIS S3C_HSOTG_REG(0x828)
|
||||
#define S3C_DVBUSPULSE S3C_HSOTG_REG(0x82C)
|
||||
|
||||
#define S3C_DIEPCTL0 S3C_HSOTG_REG(0x900)
|
||||
#define S3C_DOEPCTL0 S3C_HSOTG_REG(0xB00)
|
||||
#define S3C_DIEPCTL(_a) S3C_HSOTG_REG(0x900 + ((_a) * 0x20))
|
||||
#define S3C_DOEPCTL(_a) S3C_HSOTG_REG(0xB00 + ((_a) * 0x20))
|
||||
|
||||
/* EP0 specialness:
|
||||
* bits[29..28] - reserved (no SetD0PID, SetD1PID)
|
||||
* bits[25..22] - should always be zero, this isn't a periodic endpoint
|
||||
* bits[10..0] - MPS setting differenct for EP0
|
||||
*/
|
||||
#define S3C_D0EPCTL_MPS_MASK (0x3 << 0)
|
||||
#define S3C_D0EPCTL_MPS_SHIFT (0)
|
||||
#define S3C_D0EPCTL_MPS_64 (0x0 << 0)
|
||||
#define S3C_D0EPCTL_MPS_32 (0x1 << 0)
|
||||
#define S3C_D0EPCTL_MPS_16 (0x2 << 0)
|
||||
#define S3C_D0EPCTL_MPS_8 (0x3 << 0)
|
||||
|
||||
#define S3C_DxEPCTL_EPEna (1 << 31)
|
||||
#define S3C_DxEPCTL_EPDis (1 << 30)
|
||||
#define S3C_DxEPCTL_SetD1PID (1 << 29)
|
||||
#define S3C_DxEPCTL_SetOddFr (1 << 29)
|
||||
#define S3C_DxEPCTL_SetD0PID (1 << 28)
|
||||
#define S3C_DxEPCTL_SetEvenFr (1 << 28)
|
||||
#define S3C_DxEPCTL_SNAK (1 << 27)
|
||||
#define S3C_DxEPCTL_CNAK (1 << 26)
|
||||
#define S3C_DxEPCTL_TxFNum_MASK (0xf << 22)
|
||||
#define S3C_DxEPCTL_TxFNum_SHIFT (22)
|
||||
#define S3C_DxEPCTL_TxFNum_LIMIT (0xf)
|
||||
#define S3C_DxEPCTL_TxFNum(_x) ((_x) << 22)
|
||||
|
||||
#define S3C_DxEPCTL_Stall (1 << 21)
|
||||
#define S3C_DxEPCTL_Snp (1 << 20)
|
||||
#define S3C_DxEPCTL_EPType_MASK (0x3 << 18)
|
||||
#define S3C_DxEPCTL_EPType_SHIFT (18)
|
||||
#define S3C_DxEPCTL_EPType_Control (0x0 << 18)
|
||||
#define S3C_DxEPCTL_EPType_Iso (0x1 << 18)
|
||||
#define S3C_DxEPCTL_EPType_Bulk (0x2 << 18)
|
||||
#define S3C_DxEPCTL_EPType_Intterupt (0x3 << 18)
|
||||
|
||||
#define S3C_DxEPCTL_NAKsts (1 << 17)
|
||||
#define S3C_DxEPCTL_DPID (1 << 16)
|
||||
#define S3C_DxEPCTL_EOFrNum (1 << 16)
|
||||
#define S3C_DxEPCTL_USBActEp (1 << 15)
|
||||
#define S3C_DxEPCTL_NextEp_MASK (0xf << 11)
|
||||
#define S3C_DxEPCTL_NextEp_SHIFT (11)
|
||||
#define S3C_DxEPCTL_NextEp_LIMIT (0xf)
|
||||
#define S3C_DxEPCTL_NextEp(_x) ((_x) << 11)
|
||||
|
||||
#define S3C_DxEPCTL_MPS_MASK (0x7ff << 0)
|
||||
#define S3C_DxEPCTL_MPS_SHIFT (0)
|
||||
#define S3C_DxEPCTL_MPS_LIMIT (0x7ff)
|
||||
#define S3C_DxEPCTL_MPS(_x) ((_x) << 0)
|
||||
|
||||
#define S3C_DIEPINT(_a) S3C_HSOTG_REG(0x908 + ((_a) * 0x20))
|
||||
#define S3C_DOEPINT(_a) S3C_HSOTG_REG(0xB08 + ((_a) * 0x20))
|
||||
|
||||
#define S3C_DxEPINT_INEPNakEff (1 << 6)
|
||||
#define S3C_DxEPINT_Back2BackSetup (1 << 6)
|
||||
#define S3C_DxEPINT_INTknEPMis (1 << 5)
|
||||
#define S3C_DxEPINT_INTknTXFEmp (1 << 4)
|
||||
#define S3C_DxEPINT_OUTTknEPdis (1 << 4)
|
||||
#define S3C_DxEPINT_Timeout (1 << 3)
|
||||
#define S3C_DxEPINT_Setup (1 << 3)
|
||||
#define S3C_DxEPINT_AHBErr (1 << 2)
|
||||
#define S3C_DxEPINT_EPDisbld (1 << 1)
|
||||
#define S3C_DxEPINT_XferCompl (1 << 0)
|
||||
|
||||
#define S3C_DIEPTSIZ0 S3C_HSOTG_REG(0x910)
|
||||
|
||||
#define S3C_DIEPTSIZ0_PktCnt_MASK (0x3 << 19)
|
||||
#define S3C_DIEPTSIZ0_PktCnt_SHIFT (19)
|
||||
#define S3C_DIEPTSIZ0_PktCnt_LIMIT (0x3)
|
||||
#define S3C_DIEPTSIZ0_PktCnt(_x) ((_x) << 19)
|
||||
|
||||
#define S3C_DIEPTSIZ0_XferSize_MASK (0x7f << 0)
|
||||
#define S3C_DIEPTSIZ0_XferSize_SHIFT (0)
|
||||
#define S3C_DIEPTSIZ0_XferSize_LIMIT (0x7f)
|
||||
#define S3C_DIEPTSIZ0_XferSize(_x) ((_x) << 0)
|
||||
|
||||
|
||||
#define DOEPTSIZ0 S3C_HSOTG_REG(0xB10)
|
||||
#define S3C_DOEPTSIZ0_SUPCnt_MASK (0x3 << 29)
|
||||
#define S3C_DOEPTSIZ0_SUPCnt_SHIFT (29)
|
||||
#define S3C_DOEPTSIZ0_SUPCnt_LIMIT (0x3)
|
||||
#define S3C_DOEPTSIZ0_SUPCnt(_x) ((_x) << 29)
|
||||
|
||||
#define S3C_DOEPTSIZ0_PktCnt (1 << 19)
|
||||
#define S3C_DOEPTSIZ0_XferSize_MASK (0x7f << 0)
|
||||
#define S3C_DOEPTSIZ0_XferSize_SHIFT (0)
|
||||
|
||||
#define S3C_DIEPTSIZ(_a) S3C_HSOTG_REG(0x910 + ((_a) * 0x20))
|
||||
#define S3C_DOEPTSIZ(_a) S3C_HSOTG_REG(0xB10 + ((_a) * 0x20))
|
||||
|
||||
#define S3C_DxEPTSIZ_MC_MASK (0x3 << 29)
|
||||
#define S3C_DxEPTSIZ_MC_SHIFT (29)
|
||||
#define S3C_DxEPTSIZ_MC_LIMIT (0x3)
|
||||
#define S3C_DxEPTSIZ_MC(_x) ((_x) << 29)
|
||||
|
||||
#define S3C_DxEPTSIZ_PktCnt_MASK (0x3ff << 19)
|
||||
#define S3C_DxEPTSIZ_PktCnt_SHIFT (19)
|
||||
#define S3C_DxEPTSIZ_PktCnt_GET(_v) (((_v) >> 19) & 0x3ff)
|
||||
#define S3C_DxEPTSIZ_PktCnt_LIMIT (0x3ff)
|
||||
#define S3C_DxEPTSIZ_PktCnt(_x) ((_x) << 19)
|
||||
|
||||
#define S3C_DxEPTSIZ_XferSize_MASK (0x7ffff << 0)
|
||||
#define S3C_DxEPTSIZ_XferSize_SHIFT (0)
|
||||
#define S3C_DxEPTSIZ_XferSize_GET(_v) (((_v) >> 0) & 0x7ffff)
|
||||
#define S3C_DxEPTSIZ_XferSize_LIMIT (0x7ffff)
|
||||
#define S3C_DxEPTSIZ_XferSize(_x) ((_x) << 0)
|
||||
|
||||
|
||||
#define S3C_DIEPDMA(_a) S3C_HSOTG_REG(0x914 + ((_a) * 0x20))
|
||||
#define S3C_DOEPDMA(_a) S3C_HSOTG_REG(0xB14 + ((_a) * 0x20))
|
||||
|
||||
#define S3C_EPFIFO(_a) S3C_HSOTG_REG(0x1000 + ((_a) * 0x1000))
|
||||
|
||||
#endif /* __PLAT_S3C64XX_REGS_USB_HSOTG_H */
|
@ -24,6 +24,11 @@
|
||||
#include <asm/setup.h>
|
||||
#include "pci.h"
|
||||
|
||||
const char *pci_power_names[] = {
|
||||
"error", "D0", "D1", "D2", "D3hot", "D3cold", "unknown",
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(pci_power_names);
|
||||
|
||||
unsigned int pci_pm_d3_delay = PCI_PM_D3_WAIT;
|
||||
|
||||
#ifdef CONFIG_PCI_DOMAINS
|
||||
|
@ -2336,7 +2336,7 @@ static int ATEN2011_startup(struct usb_serial *serial)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ATEN2011_shutdown(struct usb_serial *serial)
|
||||
static void ATEN2011_release(struct usb_serial *serial)
|
||||
{
|
||||
int i;
|
||||
struct ATENINTL_port *ATEN2011_port;
|
||||
@ -2382,7 +2382,7 @@ static struct usb_serial_driver aten_serial_driver = {
|
||||
.tiocmget = ATEN2011_tiocmget,
|
||||
.tiocmset = ATEN2011_tiocmset,
|
||||
.attach = ATEN2011_startup,
|
||||
.shutdown = ATEN2011_shutdown,
|
||||
.release = ATEN2011_release,
|
||||
.read_bulk_callback = ATEN2011_bulk_in_callback,
|
||||
.read_int_callback = ATEN2011_interrupt_callback,
|
||||
};
|
||||
|
@ -64,6 +64,7 @@ config USB_ARCH_HAS_EHCI
|
||||
config USB
|
||||
tristate "Support for Host-side USB"
|
||||
depends on USB_ARCH_HAS_HCD
|
||||
select NLS # for UTF-8 strings
|
||||
---help---
|
||||
Universal Serial Bus (USB) is a specification for a serial bus
|
||||
subsystem which offers higher speeds and more features than the
|
||||
|
@ -14,6 +14,7 @@ obj-$(CONFIG_USB_ISP116X_HCD) += host/
|
||||
obj-$(CONFIG_USB_OHCI_HCD) += host/
|
||||
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_U132_HCD) += host/
|
||||
obj-$(CONFIG_USB_R8A66597_HCD) += host/
|
||||
|
@ -937,9 +937,9 @@ static int acm_probe(struct usb_interface *intf,
|
||||
int buflen = intf->altsetting->extralen;
|
||||
struct usb_interface *control_interface;
|
||||
struct usb_interface *data_interface;
|
||||
struct usb_endpoint_descriptor *epctrl;
|
||||
struct usb_endpoint_descriptor *epread;
|
||||
struct usb_endpoint_descriptor *epwrite;
|
||||
struct usb_endpoint_descriptor *epctrl = NULL;
|
||||
struct usb_endpoint_descriptor *epread = NULL;
|
||||
struct usb_endpoint_descriptor *epwrite = NULL;
|
||||
struct usb_device *usb_dev = interface_to_usbdev(intf);
|
||||
struct acm *acm;
|
||||
int minor;
|
||||
@ -952,6 +952,7 @@ static int acm_probe(struct usb_interface *intf,
|
||||
unsigned long quirks;
|
||||
int num_rx_buf;
|
||||
int i;
|
||||
int combined_interfaces = 0;
|
||||
|
||||
/* normal quirks */
|
||||
quirks = (unsigned long)id->driver_info;
|
||||
@ -1033,9 +1034,15 @@ next_desc:
|
||||
data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
|
||||
control_interface = intf;
|
||||
} else {
|
||||
dev_dbg(&intf->dev,
|
||||
"No union descriptor, giving up\n");
|
||||
return -ENODEV;
|
||||
if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
|
||||
dev_dbg(&intf->dev,"No union descriptor, giving up\n");
|
||||
return -ENODEV;
|
||||
} else {
|
||||
dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
|
||||
combined_interfaces = 1;
|
||||
control_interface = data_interface = intf;
|
||||
goto look_for_collapsed_interface;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
|
||||
@ -1049,6 +1056,36 @@ next_desc:
|
||||
if (data_interface_num != call_interface_num)
|
||||
dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
|
||||
|
||||
if (control_interface == data_interface) {
|
||||
/* some broken devices designed for windows work this way */
|
||||
dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
|
||||
combined_interfaces = 1;
|
||||
/* a popular other OS doesn't use it */
|
||||
quirks |= NO_CAP_LINE;
|
||||
if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
|
||||
dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
look_for_collapsed_interface:
|
||||
for (i = 0; i < 3; i++) {
|
||||
struct usb_endpoint_descriptor *ep;
|
||||
ep = &data_interface->cur_altsetting->endpoint[i].desc;
|
||||
|
||||
if (usb_endpoint_is_int_in(ep))
|
||||
epctrl = ep;
|
||||
else if (usb_endpoint_is_bulk_out(ep))
|
||||
epwrite = ep;
|
||||
else if (usb_endpoint_is_bulk_in(ep))
|
||||
epread = ep;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!epctrl || !epread || !epwrite)
|
||||
return -ENODEV;
|
||||
else
|
||||
goto made_compressed_probe;
|
||||
}
|
||||
|
||||
skip_normal_probe:
|
||||
|
||||
/*workaround for switched interfaces */
|
||||
@ -1068,10 +1105,11 @@ skip_normal_probe:
|
||||
}
|
||||
|
||||
/* Accept probe requests only for the control interface */
|
||||
if (intf != control_interface)
|
||||
if (!combined_interfaces && intf != control_interface)
|
||||
return -ENODEV;
|
||||
|
||||
if (usb_interface_claimed(data_interface)) { /* valid in this context */
|
||||
if (!combined_interfaces && usb_interface_claimed(data_interface)) {
|
||||
/* valid in this context */
|
||||
dev_dbg(&intf->dev, "The data interface isn't available\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
@ -1095,6 +1133,7 @@ skip_normal_probe:
|
||||
epread = epwrite;
|
||||
epwrite = t;
|
||||
}
|
||||
made_compressed_probe:
|
||||
dbg("interfaces are valid");
|
||||
for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
|
||||
|
||||
@ -1112,12 +1151,15 @@ skip_normal_probe:
|
||||
ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
|
||||
readsize = le16_to_cpu(epread->wMaxPacketSize) *
|
||||
(quirks == SINGLE_RX_URB ? 1 : 2);
|
||||
acm->combined_interfaces = combined_interfaces;
|
||||
acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
|
||||
acm->control = control_interface;
|
||||
acm->data = data_interface;
|
||||
acm->minor = minor;
|
||||
acm->dev = usb_dev;
|
||||
acm->ctrl_caps = ac_management_function;
|
||||
if (quirks & NO_CAP_LINE)
|
||||
acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
|
||||
acm->ctrlsize = ctrlsize;
|
||||
acm->readsize = readsize;
|
||||
acm->rx_buflimit = num_rx_buf;
|
||||
@ -1223,9 +1265,10 @@ skip_normal_probe:
|
||||
|
||||
skip_countries:
|
||||
usb_fill_int_urb(acm->ctrlurb, usb_dev,
|
||||
usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
|
||||
acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
|
||||
epctrl->bInterval);
|
||||
usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
|
||||
acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
|
||||
/* works around buggy devices */
|
||||
epctrl->bInterval ? epctrl->bInterval : 0xff);
|
||||
acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
acm->ctrlurb->transfer_dma = acm->ctrl_dma;
|
||||
|
||||
@ -1312,7 +1355,8 @@ static void acm_disconnect(struct usb_interface *intf)
|
||||
acm->ctrl_dma);
|
||||
acm_read_buffers_free(acm);
|
||||
|
||||
usb_driver_release_interface(&acm_driver, intf == acm->control ?
|
||||
if (!acm->combined_interfaces)
|
||||
usb_driver_release_interface(&acm_driver, intf == acm->control ?
|
||||
acm->data : acm->control);
|
||||
|
||||
if (acm->port.count == 0) {
|
||||
@ -1451,6 +1495,9 @@ static struct usb_device_id acm_ids[] = {
|
||||
Maybe we should define a new
|
||||
quirk for this. */
|
||||
},
|
||||
{ USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
|
||||
.driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
|
||||
},
|
||||
|
||||
/* control interfaces with various AT-command sets */
|
||||
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
|
||||
|
@ -125,6 +125,7 @@ struct acm {
|
||||
unsigned char clocal; /* termios CLOCAL */
|
||||
unsigned int ctrl_caps; /* control capabilities from the class specific header */
|
||||
unsigned int susp_count; /* number of suspended interfaces */
|
||||
int combined_interfaces:1; /* control and data collapsed */
|
||||
struct acm_wb *delayed_wb; /* write queued for a device about to be woken */
|
||||
};
|
||||
|
||||
@ -133,3 +134,4 @@ struct acm {
|
||||
/* constants describing various quirks and errors */
|
||||
#define NO_UNION_NORMAL 1
|
||||
#define SINGLE_RX_URB 2
|
||||
#define NO_CAP_LINE 4
|
||||
|
@ -927,21 +927,27 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
switch (cmd) {
|
||||
case USBTMC_IOCTL_CLEAR_OUT_HALT:
|
||||
retval = usbtmc_ioctl_clear_out_halt(data);
|
||||
break;
|
||||
|
||||
case USBTMC_IOCTL_CLEAR_IN_HALT:
|
||||
retval = usbtmc_ioctl_clear_in_halt(data);
|
||||
break;
|
||||
|
||||
case USBTMC_IOCTL_INDICATOR_PULSE:
|
||||
retval = usbtmc_ioctl_indicator_pulse(data);
|
||||
break;
|
||||
|
||||
case USBTMC_IOCTL_CLEAR:
|
||||
retval = usbtmc_ioctl_clear(data);
|
||||
break;
|
||||
|
||||
case USBTMC_IOCTL_ABORT_BULK_OUT:
|
||||
retval = usbtmc_ioctl_abort_bulk_out(data);
|
||||
break;
|
||||
|
||||
case USBTMC_IOCTL_ABORT_BULK_IN:
|
||||
retval = usbtmc_ioctl_abort_bulk_in(data);
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->io_mutex);
|
||||
|
@ -28,7 +28,7 @@ comment "Miscellaneous USB options"
|
||||
depends on USB
|
||||
|
||||
config USB_DEVICEFS
|
||||
bool "USB device filesystem"
|
||||
bool "USB device filesystem (DEPRECATED)" if EMBEDDED
|
||||
depends on USB
|
||||
---help---
|
||||
If you say Y here (and to "/proc file system support" in the "File
|
||||
@ -46,11 +46,15 @@ config USB_DEVICEFS
|
||||
For the format of the various /proc/bus/usb/ files, please read
|
||||
<file:Documentation/usb/proc_usb_info.txt>.
|
||||
|
||||
Usbfs files can't handle Access Control Lists (ACL), which are the
|
||||
default way to grant access to USB devices for untrusted users of a
|
||||
desktop system. The usbfs functionality is replaced by real
|
||||
device-nodes managed by udev. These nodes live in /dev/bus/usb and
|
||||
are used by libusb.
|
||||
Modern Linux systems do not use this.
|
||||
|
||||
Usbfs entries are files and not character devices; usbfs can't
|
||||
handle Access Control Lists (ACL) which are the default way to
|
||||
grant access to USB devices for untrusted users of a desktop
|
||||
system.
|
||||
|
||||
The usbfs functionality is replaced by real device-nodes managed by
|
||||
udev. These nodes lived in /dev/bus/usb and are used by libusb.
|
||||
|
||||
config USB_DEVICE_CLASS
|
||||
bool "USB device class-devices (DEPRECATED)"
|
||||
|
@ -4,14 +4,14 @@
|
||||
|
||||
usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \
|
||||
config.o file.o buffer.o sysfs.o endpoint.o \
|
||||
devio.o notify.o generic.o quirks.o
|
||||
devio.o notify.o generic.o quirks.o devices.o
|
||||
|
||||
ifeq ($(CONFIG_PCI),y)
|
||||
usbcore-objs += hcd-pci.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_USB_DEVICEFS),y)
|
||||
usbcore-objs += inode.o devices.o
|
||||
usbcore-objs += inode.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_USB) += usbcore.o
|
||||
|
@ -19,6 +19,32 @@ static inline const char *plural(int n)
|
||||
return (n == 1 ? "" : "s");
|
||||
}
|
||||
|
||||
/* FIXME: this is a kludge */
|
||||
static int find_next_descriptor_more(unsigned char *buffer, int size,
|
||||
int dt1, int dt2, int dt3, int *num_skipped)
|
||||
{
|
||||
struct usb_descriptor_header *h;
|
||||
int n = 0;
|
||||
unsigned char *buffer0 = buffer;
|
||||
|
||||
/* Find the next descriptor of type dt1 or dt2 or dt3 */
|
||||
while (size > 0) {
|
||||
h = (struct usb_descriptor_header *) buffer;
|
||||
if (h->bDescriptorType == dt1 || h->bDescriptorType == dt2 ||
|
||||
h->bDescriptorType == dt3)
|
||||
break;
|
||||
buffer += h->bLength;
|
||||
size -= h->bLength;
|
||||
++n;
|
||||
}
|
||||
|
||||
/* Store the number of descriptors skipped and return the
|
||||
* number of bytes skipped */
|
||||
if (num_skipped)
|
||||
*num_skipped = n;
|
||||
return buffer - buffer0;
|
||||
}
|
||||
|
||||
static int find_next_descriptor(unsigned char *buffer, int size,
|
||||
int dt1, int dt2, int *num_skipped)
|
||||
{
|
||||
@ -43,6 +69,129 @@ static int find_next_descriptor(unsigned char *buffer, int size,
|
||||
return buffer - buffer0;
|
||||
}
|
||||
|
||||
static int usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
|
||||
int inum, int asnum, struct usb_host_endpoint *ep,
|
||||
int num_ep, unsigned char *buffer, int size)
|
||||
{
|
||||
unsigned char *buffer_start = buffer;
|
||||
struct usb_ss_ep_comp_descriptor *desc;
|
||||
int retval;
|
||||
int num_skipped;
|
||||
int max_tx;
|
||||
int i;
|
||||
|
||||
/* Allocate space for the SS endpoint companion descriptor */
|
||||
ep->ss_ep_comp = kzalloc(sizeof(struct usb_host_ss_ep_comp),
|
||||
GFP_KERNEL);
|
||||
if (!ep->ss_ep_comp)
|
||||
return -ENOMEM;
|
||||
desc = (struct usb_ss_ep_comp_descriptor *) buffer;
|
||||
if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP) {
|
||||
dev_warn(ddev, "No SuperSpeed endpoint companion for config %d "
|
||||
" interface %d altsetting %d ep %d: "
|
||||
"using minimum values\n",
|
||||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||||
ep->ss_ep_comp->desc.bLength = USB_DT_SS_EP_COMP_SIZE;
|
||||
ep->ss_ep_comp->desc.bDescriptorType = USB_DT_SS_ENDPOINT_COMP;
|
||||
ep->ss_ep_comp->desc.bMaxBurst = 0;
|
||||
/*
|
||||
* Leave bmAttributes as zero, which will mean no streams for
|
||||
* bulk, and isoc won't support multiple bursts of packets.
|
||||
* With bursts of only one packet, and a Mult of 1, the max
|
||||
* amount of data moved per endpoint service interval is one
|
||||
* packet.
|
||||
*/
|
||||
if (usb_endpoint_xfer_isoc(&ep->desc) ||
|
||||
usb_endpoint_xfer_int(&ep->desc))
|
||||
ep->ss_ep_comp->desc.wBytesPerInterval =
|
||||
ep->desc.wMaxPacketSize;
|
||||
/*
|
||||
* The next descriptor is for an Endpoint or Interface,
|
||||
* no extra descriptors to copy into the companion structure,
|
||||
* and we didn't eat up any of the buffer.
|
||||
*/
|
||||
retval = 0;
|
||||
goto valid;
|
||||
}
|
||||
memcpy(&ep->ss_ep_comp->desc, desc, USB_DT_SS_EP_COMP_SIZE);
|
||||
desc = &ep->ss_ep_comp->desc;
|
||||
buffer += desc->bLength;
|
||||
size -= desc->bLength;
|
||||
|
||||
/* Eat up the other descriptors we don't care about */
|
||||
ep->ss_ep_comp->extra = buffer;
|
||||
i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
|
||||
USB_DT_INTERFACE, &num_skipped);
|
||||
ep->ss_ep_comp->extralen = i;
|
||||
buffer += i;
|
||||
size -= i;
|
||||
retval = buffer - buffer_start + i;
|
||||
if (num_skipped > 0)
|
||||
dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
|
||||
num_skipped, plural(num_skipped),
|
||||
"SuperSpeed endpoint companion");
|
||||
|
||||
/* Check the various values */
|
||||
if (usb_endpoint_xfer_control(&ep->desc) && desc->bMaxBurst != 0) {
|
||||
dev_warn(ddev, "Control endpoint with bMaxBurst = %d in "
|
||||
"config %d interface %d altsetting %d ep %d: "
|
||||
"setting to zero\n", desc->bMaxBurst,
|
||||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||||
desc->bMaxBurst = 0;
|
||||
}
|
||||
if (desc->bMaxBurst > 15) {
|
||||
dev_warn(ddev, "Endpoint with bMaxBurst = %d in "
|
||||
"config %d interface %d altsetting %d ep %d: "
|
||||
"setting to 15\n", desc->bMaxBurst,
|
||||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||||
desc->bMaxBurst = 15;
|
||||
}
|
||||
if ((usb_endpoint_xfer_control(&ep->desc) || usb_endpoint_xfer_int(&ep->desc))
|
||||
&& desc->bmAttributes != 0) {
|
||||
dev_warn(ddev, "%s endpoint with bmAttributes = %d in "
|
||||
"config %d interface %d altsetting %d ep %d: "
|
||||
"setting to zero\n",
|
||||
usb_endpoint_xfer_control(&ep->desc) ? "Control" : "Bulk",
|
||||
desc->bmAttributes,
|
||||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||||
desc->bmAttributes = 0;
|
||||
}
|
||||
if (usb_endpoint_xfer_bulk(&ep->desc) && desc->bmAttributes > 16) {
|
||||
dev_warn(ddev, "Bulk endpoint with more than 65536 streams in "
|
||||
"config %d interface %d altsetting %d ep %d: "
|
||||
"setting to max\n",
|
||||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||||
desc->bmAttributes = 16;
|
||||
}
|
||||
if (usb_endpoint_xfer_isoc(&ep->desc) && desc->bmAttributes > 2) {
|
||||
dev_warn(ddev, "Isoc endpoint has Mult of %d in "
|
||||
"config %d interface %d altsetting %d ep %d: "
|
||||
"setting to 3\n", desc->bmAttributes + 1,
|
||||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||||
desc->bmAttributes = 2;
|
||||
}
|
||||
if (usb_endpoint_xfer_isoc(&ep->desc)) {
|
||||
max_tx = ep->desc.wMaxPacketSize * (desc->bMaxBurst + 1) *
|
||||
(desc->bmAttributes + 1);
|
||||
} else if (usb_endpoint_xfer_int(&ep->desc)) {
|
||||
max_tx = ep->desc.wMaxPacketSize * (desc->bMaxBurst + 1);
|
||||
} else {
|
||||
goto valid;
|
||||
}
|
||||
if (desc->wBytesPerInterval > max_tx) {
|
||||
dev_warn(ddev, "%s endpoint with wBytesPerInterval of %d in "
|
||||
"config %d interface %d altsetting %d ep %d: "
|
||||
"setting to %d\n",
|
||||
usb_endpoint_xfer_isoc(&ep->desc) ? "Isoc" : "Int",
|
||||
desc->wBytesPerInterval,
|
||||
cfgno, inum, asnum, ep->desc.bEndpointAddress,
|
||||
max_tx);
|
||||
desc->wBytesPerInterval = max_tx;
|
||||
}
|
||||
valid:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
|
||||
int asnum, struct usb_host_interface *ifp, int num_ep,
|
||||
unsigned char *buffer, int size)
|
||||
@ -50,7 +199,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
|
||||
unsigned char *buffer0 = buffer;
|
||||
struct usb_endpoint_descriptor *d;
|
||||
struct usb_host_endpoint *endpoint;
|
||||
int n, i, j;
|
||||
int n, i, j, retval;
|
||||
|
||||
d = (struct usb_endpoint_descriptor *) buffer;
|
||||
buffer += d->bLength;
|
||||
@ -92,6 +241,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
|
||||
if (usb_endpoint_xfer_int(d)) {
|
||||
i = 1;
|
||||
switch (to_usb_device(ddev)->speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
case USB_SPEED_HIGH:
|
||||
/* Many device manufacturers are using full-speed
|
||||
* bInterval values in high-speed interrupt endpoint
|
||||
@ -161,17 +311,39 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
|
||||
cfgno, inum, asnum, d->bEndpointAddress,
|
||||
maxp);
|
||||
}
|
||||
/* Allocate room for and parse any SS endpoint companion descriptors */
|
||||
if (to_usb_device(ddev)->speed == USB_SPEED_SUPER) {
|
||||
endpoint->extra = buffer;
|
||||
i = find_next_descriptor_more(buffer, size, USB_DT_SS_ENDPOINT_COMP,
|
||||
USB_DT_ENDPOINT, USB_DT_INTERFACE, &n);
|
||||
endpoint->extralen = i;
|
||||
buffer += i;
|
||||
size -= i;
|
||||
|
||||
/* Skip over any Class Specific or Vendor Specific descriptors;
|
||||
* find the next endpoint or interface descriptor */
|
||||
endpoint->extra = buffer;
|
||||
i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
|
||||
USB_DT_INTERFACE, &n);
|
||||
endpoint->extralen = i;
|
||||
if (size > 0) {
|
||||
retval = usb_parse_ss_endpoint_companion(ddev, cfgno,
|
||||
inum, asnum, endpoint, num_ep, buffer,
|
||||
size);
|
||||
if (retval >= 0) {
|
||||
buffer += retval;
|
||||
retval = buffer - buffer0;
|
||||
}
|
||||
} else {
|
||||
retval = buffer - buffer0;
|
||||
}
|
||||
} else {
|
||||
/* Skip over any Class Specific or Vendor Specific descriptors;
|
||||
* find the next endpoint or interface descriptor */
|
||||
endpoint->extra = buffer;
|
||||
i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
|
||||
USB_DT_INTERFACE, &n);
|
||||
endpoint->extralen = i;
|
||||
retval = buffer - buffer0 + i;
|
||||
}
|
||||
if (n > 0)
|
||||
dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
|
||||
n, plural(n), "endpoint");
|
||||
return buffer - buffer0 + i;
|
||||
return retval;
|
||||
|
||||
skip_to_next_endpoint_or_interface_descriptor:
|
||||
i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
|
||||
@ -452,6 +624,8 @@ static int usb_parse_configuration(struct device *ddev, int cfgidx,
|
||||
kref_init(&intfc->ref);
|
||||
}
|
||||
|
||||
/* FIXME: parse the BOS descriptor */
|
||||
|
||||
/* Skip over any Class Specific or Vendor Specific descriptors;
|
||||
* find the first interface descriptor */
|
||||
config->extra = buffer;
|
||||
|
@ -154,16 +154,11 @@ static const struct usb_device_id *usb_match_dynamic_id(struct usb_interface *in
|
||||
static int usb_probe_device(struct device *dev)
|
||||
{
|
||||
struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);
|
||||
struct usb_device *udev;
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
int error = -ENODEV;
|
||||
|
||||
dev_dbg(dev, "%s\n", __func__);
|
||||
|
||||
if (!is_usb_device(dev)) /* Sanity check */
|
||||
return error;
|
||||
|
||||
udev = to_usb_device(dev);
|
||||
|
||||
/* TODO: Add real matching code */
|
||||
|
||||
/* The device should always appear to be in use
|
||||
@ -203,18 +198,13 @@ static void usb_cancel_queued_reset(struct usb_interface *iface)
|
||||
static int usb_probe_interface(struct device *dev)
|
||||
{
|
||||
struct usb_driver *driver = to_usb_driver(dev->driver);
|
||||
struct usb_interface *intf;
|
||||
struct usb_device *udev;
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
const struct usb_device_id *id;
|
||||
int error = -ENODEV;
|
||||
|
||||
dev_dbg(dev, "%s\n", __func__);
|
||||
|
||||
if (is_usb_device(dev)) /* Sanity check */
|
||||
return error;
|
||||
|
||||
intf = to_usb_interface(dev);
|
||||
udev = interface_to_usbdev(intf);
|
||||
intf->needs_binding = 0;
|
||||
|
||||
if (udev->authorized == 0) {
|
||||
@ -385,7 +375,6 @@ void usb_driver_release_interface(struct usb_driver *driver,
|
||||
struct usb_interface *iface)
|
||||
{
|
||||
struct device *dev = &iface->dev;
|
||||
struct usb_device *udev = interface_to_usbdev(iface);
|
||||
|
||||
/* this should never happen, don't release something that's not ours */
|
||||
if (!dev->driver || dev->driver != &driver->drvwrap.driver)
|
||||
@ -394,23 +383,19 @@ void usb_driver_release_interface(struct usb_driver *driver,
|
||||
/* don't release from within disconnect() */
|
||||
if (iface->condition != USB_INTERFACE_BOUND)
|
||||
return;
|
||||
iface->condition = USB_INTERFACE_UNBINDING;
|
||||
|
||||
/* don't release if the interface hasn't been added yet */
|
||||
/* Release via the driver core only if the interface
|
||||
* has already been registered
|
||||
*/
|
||||
if (device_is_registered(dev)) {
|
||||
iface->condition = USB_INTERFACE_UNBINDING;
|
||||
device_release_driver(dev);
|
||||
} else {
|
||||
iface->condition = USB_INTERFACE_UNBOUND;
|
||||
usb_cancel_queued_reset(iface);
|
||||
down(&dev->sem);
|
||||
usb_unbind_interface(dev);
|
||||
dev->driver = NULL;
|
||||
up(&dev->sem);
|
||||
}
|
||||
dev->driver = NULL;
|
||||
usb_set_intfdata(iface, NULL);
|
||||
|
||||
usb_pm_lock(udev);
|
||||
iface->condition = USB_INTERFACE_UNBOUND;
|
||||
mark_quiesced(iface);
|
||||
iface->needs_remote_wakeup = 0;
|
||||
usb_pm_unlock(udev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_driver_release_interface);
|
||||
|
||||
@ -598,7 +583,7 @@ static int usb_device_match(struct device *dev, struct device_driver *drv)
|
||||
/* TODO: Add real matching code */
|
||||
return 1;
|
||||
|
||||
} else {
|
||||
} else if (is_usb_interface(dev)) {
|
||||
struct usb_interface *intf;
|
||||
struct usb_driver *usb_drv;
|
||||
const struct usb_device_id *id;
|
||||
@ -630,11 +615,14 @@ static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
/* driver is often null here; dev_dbg() would oops */
|
||||
pr_debug("usb %s: uevent\n", dev_name(dev));
|
||||
|
||||
if (is_usb_device(dev))
|
||||
if (is_usb_device(dev)) {
|
||||
usb_dev = to_usb_device(dev);
|
||||
else {
|
||||
} else if (is_usb_interface(dev)) {
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
|
||||
usb_dev = interface_to_usbdev(intf);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (usb_dev->devnum < 0) {
|
||||
@ -1762,6 +1750,7 @@ int usb_suspend(struct device *dev, pm_message_t msg)
|
||||
int usb_resume(struct device *dev, pm_message_t msg)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
int status;
|
||||
|
||||
udev = to_usb_device(dev);
|
||||
|
||||
@ -1771,7 +1760,14 @@ int usb_resume(struct device *dev, pm_message_t msg)
|
||||
*/
|
||||
if (udev->skip_sys_resume)
|
||||
return 0;
|
||||
return usb_external_resume_device(udev, msg);
|
||||
status = usb_external_resume_device(udev, msg);
|
||||
|
||||
/* Avoid PM error messages for devices disconnected while suspended
|
||||
* as we'll display regular disconnect messages just a bit later.
|
||||
*/
|
||||
if (status == -ENODEV)
|
||||
return 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
@ -15,19 +15,18 @@
|
||||
#include <linux/usb.h>
|
||||
#include "usb.h"
|
||||
|
||||
#define MAX_ENDPOINT_MINORS (64*128*32)
|
||||
static int usb_endpoint_major;
|
||||
static DEFINE_IDR(endpoint_idr);
|
||||
|
||||
struct ep_device {
|
||||
struct usb_endpoint_descriptor *desc;
|
||||
struct usb_device *udev;
|
||||
struct device dev;
|
||||
int minor;
|
||||
};
|
||||
#define to_ep_device(_dev) \
|
||||
container_of(_dev, struct ep_device, dev)
|
||||
|
||||
struct device_type usb_ep_device_type = {
|
||||
.name = "usb_endpoint",
|
||||
};
|
||||
|
||||
struct ep_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct usb_device *,
|
||||
@ -160,118 +159,10 @@ static struct attribute_group *ep_dev_groups[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static int usb_endpoint_major_init(void)
|
||||
{
|
||||
dev_t dev;
|
||||
int error;
|
||||
|
||||
error = alloc_chrdev_region(&dev, 0, MAX_ENDPOINT_MINORS,
|
||||
"usb_endpoint");
|
||||
if (error) {
|
||||
printk(KERN_ERR "Unable to get a dynamic major for "
|
||||
"usb endpoints.\n");
|
||||
return error;
|
||||
}
|
||||
usb_endpoint_major = MAJOR(dev);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static void usb_endpoint_major_cleanup(void)
|
||||
{
|
||||
unregister_chrdev_region(MKDEV(usb_endpoint_major, 0),
|
||||
MAX_ENDPOINT_MINORS);
|
||||
}
|
||||
|
||||
static int endpoint_get_minor(struct ep_device *ep_dev)
|
||||
{
|
||||
static DEFINE_MUTEX(minor_lock);
|
||||
int retval = -ENOMEM;
|
||||
int id;
|
||||
|
||||
mutex_lock(&minor_lock);
|
||||
if (idr_pre_get(&endpoint_idr, GFP_KERNEL) == 0)
|
||||
goto exit;
|
||||
|
||||
retval = idr_get_new(&endpoint_idr, ep_dev, &id);
|
||||
if (retval < 0) {
|
||||
if (retval == -EAGAIN)
|
||||
retval = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
ep_dev->minor = id & MAX_ID_MASK;
|
||||
exit:
|
||||
mutex_unlock(&minor_lock);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void endpoint_free_minor(struct ep_device *ep_dev)
|
||||
{
|
||||
idr_remove(&endpoint_idr, ep_dev->minor);
|
||||
}
|
||||
|
||||
static struct endpoint_class {
|
||||
struct kref kref;
|
||||
struct class *class;
|
||||
} *ep_class;
|
||||
|
||||
static int init_endpoint_class(void)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (ep_class != NULL) {
|
||||
kref_get(&ep_class->kref);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ep_class = kmalloc(sizeof(*ep_class), GFP_KERNEL);
|
||||
if (!ep_class) {
|
||||
result = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
kref_init(&ep_class->kref);
|
||||
ep_class->class = class_create(THIS_MODULE, "usb_endpoint");
|
||||
if (IS_ERR(ep_class->class)) {
|
||||
result = PTR_ERR(ep_class->class);
|
||||
goto class_create_error;
|
||||
}
|
||||
|
||||
result = usb_endpoint_major_init();
|
||||
if (result)
|
||||
goto endpoint_major_error;
|
||||
|
||||
goto exit;
|
||||
|
||||
endpoint_major_error:
|
||||
class_destroy(ep_class->class);
|
||||
class_create_error:
|
||||
kfree(ep_class);
|
||||
ep_class = NULL;
|
||||
exit:
|
||||
return result;
|
||||
}
|
||||
|
||||
static void release_endpoint_class(struct kref *kref)
|
||||
{
|
||||
/* Ok, we cheat as we know we only have one ep_class */
|
||||
class_destroy(ep_class->class);
|
||||
kfree(ep_class);
|
||||
ep_class = NULL;
|
||||
usb_endpoint_major_cleanup();
|
||||
}
|
||||
|
||||
static void destroy_endpoint_class(void)
|
||||
{
|
||||
if (ep_class)
|
||||
kref_put(&ep_class->kref, release_endpoint_class);
|
||||
}
|
||||
|
||||
static void ep_device_release(struct device *dev)
|
||||
{
|
||||
struct ep_device *ep_dev = to_ep_device(dev);
|
||||
|
||||
endpoint_free_minor(ep_dev);
|
||||
kfree(ep_dev);
|
||||
}
|
||||
|
||||
@ -279,62 +170,32 @@ int usb_create_ep_devs(struct device *parent,
|
||||
struct usb_host_endpoint *endpoint,
|
||||
struct usb_device *udev)
|
||||
{
|
||||
char name[8];
|
||||
struct ep_device *ep_dev;
|
||||
int retval;
|
||||
|
||||
retval = init_endpoint_class();
|
||||
if (retval)
|
||||
goto exit;
|
||||
|
||||
ep_dev = kzalloc(sizeof(*ep_dev), GFP_KERNEL);
|
||||
if (!ep_dev) {
|
||||
retval = -ENOMEM;
|
||||
goto error_alloc;
|
||||
}
|
||||
|
||||
retval = endpoint_get_minor(ep_dev);
|
||||
if (retval) {
|
||||
dev_err(parent, "can not allocate minor number for %s\n",
|
||||
dev_name(&ep_dev->dev));
|
||||
goto error_register;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ep_dev->desc = &endpoint->desc;
|
||||
ep_dev->udev = udev;
|
||||
ep_dev->dev.groups = ep_dev_groups;
|
||||
ep_dev->dev.devt = MKDEV(usb_endpoint_major, ep_dev->minor);
|
||||
ep_dev->dev.class = ep_class->class;
|
||||
ep_dev->dev.type = &usb_ep_device_type;
|
||||
ep_dev->dev.parent = parent;
|
||||
ep_dev->dev.release = ep_device_release;
|
||||
dev_set_name(&ep_dev->dev, "usbdev%d.%d_ep%02x",
|
||||
udev->bus->busnum, udev->devnum,
|
||||
endpoint->desc.bEndpointAddress);
|
||||
dev_set_name(&ep_dev->dev, "ep_%02x", endpoint->desc.bEndpointAddress);
|
||||
|
||||
retval = device_register(&ep_dev->dev);
|
||||
if (retval)
|
||||
goto error_chrdev;
|
||||
goto error_register;
|
||||
|
||||
/* create the symlink to the old-style "ep_XX" directory */
|
||||
sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress);
|
||||
retval = sysfs_create_link(&parent->kobj, &ep_dev->dev.kobj, name);
|
||||
if (retval)
|
||||
goto error_link;
|
||||
endpoint->ep_dev = ep_dev;
|
||||
return retval;
|
||||
|
||||
error_link:
|
||||
device_unregister(&ep_dev->dev);
|
||||
destroy_endpoint_class();
|
||||
return retval;
|
||||
|
||||
error_chrdev:
|
||||
endpoint_free_minor(ep_dev);
|
||||
|
||||
error_register:
|
||||
kfree(ep_dev);
|
||||
error_alloc:
|
||||
destroy_endpoint_class();
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
@ -344,12 +205,7 @@ void usb_remove_ep_devs(struct usb_host_endpoint *endpoint)
|
||||
struct ep_device *ep_dev = endpoint->ep_dev;
|
||||
|
||||
if (ep_dev) {
|
||||
char name[8];
|
||||
|
||||
sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress);
|
||||
sysfs_remove_link(&ep_dev->dev.parent->kobj, name);
|
||||
device_unregister(&ep_dev->dev);
|
||||
endpoint->ep_dev = NULL;
|
||||
destroy_endpoint_class();
|
||||
}
|
||||
}
|
||||
|
@ -185,180 +185,6 @@ void usb_hcd_pci_remove(struct pci_dev *dev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_pci_remove);
|
||||
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
/**
|
||||
* usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
|
||||
* @dev: USB Host Controller being suspended
|
||||
* @message: Power Management message describing this state transition
|
||||
*
|
||||
* Store this function in the HCD's struct pci_driver as .suspend.
|
||||
*/
|
||||
int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message)
|
||||
{
|
||||
struct usb_hcd *hcd = pci_get_drvdata(dev);
|
||||
int retval = 0;
|
||||
int wake, w;
|
||||
int has_pci_pm;
|
||||
|
||||
/* Root hub suspend should have stopped all downstream traffic,
|
||||
* and all bus master traffic. And done so for both the interface
|
||||
* and the stub usb_device (which we check here). But maybe it
|
||||
* didn't; writing sysfs power/state files ignores such rules...
|
||||
*
|
||||
* We must ignore the FREEZE vs SUSPEND distinction here, because
|
||||
* otherwise the swsusp will save (and restore) garbage state.
|
||||
*/
|
||||
if (!(hcd->state == HC_STATE_SUSPENDED ||
|
||||
hcd->state == HC_STATE_HALT)) {
|
||||
dev_warn(&dev->dev, "Root hub is not suspended\n");
|
||||
retval = -EBUSY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* We might already be suspended (runtime PM -- not yet written) */
|
||||
if (dev->current_state != PCI_D0)
|
||||
goto done;
|
||||
|
||||
if (hcd->driver->pci_suspend) {
|
||||
retval = hcd->driver->pci_suspend(hcd, message);
|
||||
suspend_report_result(hcd->driver->pci_suspend, retval);
|
||||
if (retval)
|
||||
goto done;
|
||||
}
|
||||
|
||||
synchronize_irq(dev->irq);
|
||||
|
||||
/* Downstream ports from this root hub should already be quiesced, so
|
||||
* there will be no DMA activity. Now we can shut down the upstream
|
||||
* link (except maybe for PME# resume signaling) and enter some PCI
|
||||
* low power state, if the hardware allows.
|
||||
*/
|
||||
pci_disable_device(dev);
|
||||
|
||||
pci_save_state(dev);
|
||||
|
||||
/* Don't fail on error to enable wakeup. We rely on pci code
|
||||
* to reject requests the hardware can't implement, rather
|
||||
* than coding the same thing.
|
||||
*/
|
||||
wake = (hcd->state == HC_STATE_SUSPENDED &&
|
||||
device_may_wakeup(&dev->dev));
|
||||
w = pci_wake_from_d3(dev, wake);
|
||||
if (w < 0)
|
||||
wake = w;
|
||||
dev_dbg(&dev->dev, "wakeup: %d\n", wake);
|
||||
|
||||
/* Don't change state if we don't need to */
|
||||
if (message.event == PM_EVENT_FREEZE ||
|
||||
message.event == PM_EVENT_PRETHAW) {
|
||||
dev_dbg(&dev->dev, "--> no state change\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
|
||||
if (!has_pci_pm) {
|
||||
dev_dbg(&dev->dev, "--> PCI D0 legacy\n");
|
||||
} else {
|
||||
|
||||
/* NOTE: dev->current_state becomes nonzero only here, and
|
||||
* only for devices that support PCI PM. Also, exiting
|
||||
* PCI_D3 (but not PCI_D1 or PCI_D2) is allowed to reset
|
||||
* some device state (e.g. as part of clock reinit).
|
||||
*/
|
||||
retval = pci_set_power_state(dev, PCI_D3hot);
|
||||
suspend_report_result(pci_set_power_state, retval);
|
||||
if (retval == 0) {
|
||||
dev_dbg(&dev->dev, "--> PCI D3\n");
|
||||
} else {
|
||||
dev_dbg(&dev->dev, "PCI D3 suspend fail, %d\n",
|
||||
retval);
|
||||
pci_restore_state(dev);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
if (retval == 0) {
|
||||
/* Disable ASIC clocks for USB */
|
||||
if (machine_is(powermac)) {
|
||||
struct device_node *of_node;
|
||||
|
||||
of_node = pci_device_to_OF_node(dev);
|
||||
if (of_node)
|
||||
pmac_call_feature(PMAC_FTR_USB_ENABLE,
|
||||
of_node, 0, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend);
|
||||
|
||||
/**
|
||||
* usb_hcd_pci_resume - power management resume of a PCI-based HCD
|
||||
* @dev: USB Host Controller being resumed
|
||||
*
|
||||
* Store this function in the HCD's struct pci_driver as .resume.
|
||||
*/
|
||||
int usb_hcd_pci_resume(struct pci_dev *dev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
int retval;
|
||||
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
/* Reenable ASIC clocks for USB */
|
||||
if (machine_is(powermac)) {
|
||||
struct device_node *of_node;
|
||||
|
||||
of_node = pci_device_to_OF_node(dev);
|
||||
if (of_node)
|
||||
pmac_call_feature(PMAC_FTR_USB_ENABLE,
|
||||
of_node, 0, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
pci_restore_state(dev);
|
||||
|
||||
hcd = pci_get_drvdata(dev);
|
||||
if (hcd->state != HC_STATE_SUSPENDED) {
|
||||
dev_dbg(hcd->self.controller,
|
||||
"can't resume, not suspended!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
pci_enable_wake(dev, PCI_D0, false);
|
||||
|
||||
retval = pci_enable_device(dev);
|
||||
if (retval < 0) {
|
||||
dev_err(&dev->dev, "can't re-enable after resume, %d!\n",
|
||||
retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
pci_set_master(dev);
|
||||
|
||||
/* yes, ignore this result too... */
|
||||
(void) pci_wake_from_d3(dev, 0);
|
||||
|
||||
clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
|
||||
|
||||
if (hcd->driver->pci_resume) {
|
||||
retval = hcd->driver->pci_resume(hcd);
|
||||
if (retval) {
|
||||
dev_err(hcd->self.controller,
|
||||
"PCI post-resume error %d!\n", retval);
|
||||
usb_hc_died(hcd);
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_pci_resume);
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
/**
|
||||
* usb_hcd_pci_shutdown - shutdown host controller
|
||||
* @dev: USB Host Controller being shutdown
|
||||
@ -376,3 +202,181 @@ void usb_hcd_pci_shutdown(struct pci_dev *dev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int check_root_hub_suspended(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
|
||||
|
||||
if (!(hcd->state == HC_STATE_SUSPENDED ||
|
||||
hcd->state == HC_STATE_HALT)) {
|
||||
dev_warn(dev, "Root hub is not suspended\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hcd_pci_suspend(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
|
||||
int retval;
|
||||
|
||||
/* Root hub suspend should have stopped all downstream traffic,
|
||||
* and all bus master traffic. And done so for both the interface
|
||||
* and the stub usb_device (which we check here). But maybe it
|
||||
* didn't; writing sysfs power/state files ignores such rules...
|
||||
*/
|
||||
retval = check_root_hub_suspended(dev);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
/* We might already be suspended (runtime PM -- not yet written) */
|
||||
if (pci_dev->current_state != PCI_D0)
|
||||
return retval;
|
||||
|
||||
if (hcd->driver->pci_suspend) {
|
||||
retval = hcd->driver->pci_suspend(hcd);
|
||||
suspend_report_result(hcd->driver->pci_suspend, retval);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
synchronize_irq(pci_dev->irq);
|
||||
|
||||
/* Downstream ports from this root hub should already be quiesced, so
|
||||
* there will be no DMA activity. Now we can shut down the upstream
|
||||
* link (except maybe for PME# resume signaling). We'll enter a
|
||||
* low power state during suspend_noirq, if the hardware allows.
|
||||
*/
|
||||
pci_disable_device(pci_dev);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int hcd_pci_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
|
||||
int retval;
|
||||
|
||||
retval = check_root_hub_suspended(dev);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
pci_save_state(pci_dev);
|
||||
|
||||
/* If the root hub is HALTed rather than SUSPENDed,
|
||||
* disallow remote wakeup.
|
||||
*/
|
||||
if (hcd->state == HC_STATE_HALT)
|
||||
device_set_wakeup_enable(dev, 0);
|
||||
dev_dbg(dev, "wakeup: %d\n", device_may_wakeup(dev));
|
||||
|
||||
/* Possibly enable remote wakeup,
|
||||
* choose the appropriate low-power state, and go to that state.
|
||||
*/
|
||||
retval = pci_prepare_to_sleep(pci_dev);
|
||||
if (retval == -EIO) { /* Low-power not supported */
|
||||
dev_dbg(dev, "--> PCI D0 legacy\n");
|
||||
retval = 0;
|
||||
} else if (retval == 0) {
|
||||
dev_dbg(dev, "--> PCI %s\n",
|
||||
pci_power_name(pci_dev->current_state));
|
||||
} else {
|
||||
suspend_report_result(pci_prepare_to_sleep, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
/* Disable ASIC clocks for USB */
|
||||
if (machine_is(powermac)) {
|
||||
struct device_node *of_node;
|
||||
|
||||
of_node = pci_device_to_OF_node(pci_dev);
|
||||
if (of_node)
|
||||
pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
|
||||
}
|
||||
#endif
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int hcd_pci_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
/* Reenable ASIC clocks for USB */
|
||||
if (machine_is(powermac)) {
|
||||
struct device_node *of_node;
|
||||
|
||||
of_node = pci_device_to_OF_node(pci_dev);
|
||||
if (of_node)
|
||||
pmac_call_feature(PMAC_FTR_USB_ENABLE,
|
||||
of_node, 0, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Go back to D0 and disable remote wakeup */
|
||||
pci_back_from_sleep(pci_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int resume_common(struct device *dev, bool hibernated)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
|
||||
int retval;
|
||||
|
||||
if (hcd->state != HC_STATE_SUSPENDED) {
|
||||
dev_dbg(dev, "can't resume, not suspended!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
retval = pci_enable_device(pci_dev);
|
||||
if (retval < 0) {
|
||||
dev_err(dev, "can't re-enable after resume, %d!\n", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
pci_set_master(pci_dev);
|
||||
|
||||
clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
|
||||
|
||||
if (hcd->driver->pci_resume) {
|
||||
retval = hcd->driver->pci_resume(hcd, hibernated);
|
||||
if (retval) {
|
||||
dev_err(dev, "PCI post-resume error %d!\n", retval);
|
||||
usb_hc_died(hcd);
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int hcd_pci_resume(struct device *dev)
|
||||
{
|
||||
return resume_common(dev, false);
|
||||
}
|
||||
|
||||
static int hcd_pci_restore(struct device *dev)
|
||||
{
|
||||
return resume_common(dev, true);
|
||||
}
|
||||
|
||||
struct dev_pm_ops usb_hcd_pci_pm_ops = {
|
||||
.suspend = hcd_pci_suspend,
|
||||
.suspend_noirq = hcd_pci_suspend_noirq,
|
||||
.resume_noirq = hcd_pci_resume_noirq,
|
||||
.resume = hcd_pci_resume,
|
||||
.freeze = check_root_hub_suspended,
|
||||
.freeze_noirq = check_root_hub_suspended,
|
||||
.thaw_noirq = NULL,
|
||||
.thaw = NULL,
|
||||
.poweroff = hcd_pci_suspend,
|
||||
.poweroff_noirq = hcd_pci_suspend_noirq,
|
||||
.restore_noirq = hcd_pci_resume_noirq,
|
||||
.restore = hcd_pci_restore,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops);
|
||||
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
@ -128,6 +128,27 @@ static inline int is_root_hub(struct usb_device *udev)
|
||||
#define KERNEL_REL ((LINUX_VERSION_CODE >> 16) & 0x0ff)
|
||||
#define KERNEL_VER ((LINUX_VERSION_CODE >> 8) & 0x0ff)
|
||||
|
||||
/* usb 3.0 root hub device descriptor */
|
||||
static const u8 usb3_rh_dev_descriptor[18] = {
|
||||
0x12, /* __u8 bLength; */
|
||||
0x01, /* __u8 bDescriptorType; Device */
|
||||
0x00, 0x03, /* __le16 bcdUSB; v3.0 */
|
||||
|
||||
0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
|
||||
0x00, /* __u8 bDeviceSubClass; */
|
||||
0x03, /* __u8 bDeviceProtocol; USB 3.0 hub */
|
||||
0x09, /* __u8 bMaxPacketSize0; 2^9 = 512 Bytes */
|
||||
|
||||
0x6b, 0x1d, /* __le16 idVendor; Linux Foundation */
|
||||
0x02, 0x00, /* __le16 idProduct; device 0x0002 */
|
||||
KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */
|
||||
|
||||
0x03, /* __u8 iManufacturer; */
|
||||
0x02, /* __u8 iProduct; */
|
||||
0x01, /* __u8 iSerialNumber; */
|
||||
0x01 /* __u8 bNumConfigurations; */
|
||||
};
|
||||
|
||||
/* usb 2.0 root hub device descriptor */
|
||||
static const u8 usb2_rh_dev_descriptor [18] = {
|
||||
0x12, /* __u8 bLength; */
|
||||
@ -273,6 +294,47 @@ static const u8 hs_rh_config_descriptor [] = {
|
||||
0x0c /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */
|
||||
};
|
||||
|
||||
static const u8 ss_rh_config_descriptor[] = {
|
||||
/* one configuration */
|
||||
0x09, /* __u8 bLength; */
|
||||
0x02, /* __u8 bDescriptorType; Configuration */
|
||||
0x19, 0x00, /* __le16 wTotalLength; FIXME */
|
||||
0x01, /* __u8 bNumInterfaces; (1) */
|
||||
0x01, /* __u8 bConfigurationValue; */
|
||||
0x00, /* __u8 iConfiguration; */
|
||||
0xc0, /* __u8 bmAttributes;
|
||||
Bit 7: must be set,
|
||||
6: Self-powered,
|
||||
5: Remote wakeup,
|
||||
4..0: resvd */
|
||||
0x00, /* __u8 MaxPower; */
|
||||
|
||||
/* one interface */
|
||||
0x09, /* __u8 if_bLength; */
|
||||
0x04, /* __u8 if_bDescriptorType; Interface */
|
||||
0x00, /* __u8 if_bInterfaceNumber; */
|
||||
0x00, /* __u8 if_bAlternateSetting; */
|
||||
0x01, /* __u8 if_bNumEndpoints; */
|
||||
0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
|
||||
0x00, /* __u8 if_bInterfaceSubClass; */
|
||||
0x00, /* __u8 if_bInterfaceProtocol; */
|
||||
0x00, /* __u8 if_iInterface; */
|
||||
|
||||
/* one endpoint (status change endpoint) */
|
||||
0x07, /* __u8 ep_bLength; */
|
||||
0x05, /* __u8 ep_bDescriptorType; Endpoint */
|
||||
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
|
||||
0x03, /* __u8 ep_bmAttributes; Interrupt */
|
||||
/* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8)
|
||||
* see hub.c:hub_configure() for details. */
|
||||
(USB_MAXCHILDREN + 1 + 7) / 8, 0x00,
|
||||
0x0c /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */
|
||||
/*
|
||||
* All 3.0 hubs should have an endpoint companion descriptor,
|
||||
* but we're ignoring that for now. FIXME?
|
||||
*/
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
@ -426,23 +488,39 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
|
||||
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
|
||||
switch (wValue & 0xff00) {
|
||||
case USB_DT_DEVICE << 8:
|
||||
if (hcd->driver->flags & HCD_USB2)
|
||||
switch (hcd->driver->flags & HCD_MASK) {
|
||||
case HCD_USB3:
|
||||
bufp = usb3_rh_dev_descriptor;
|
||||
break;
|
||||
case HCD_USB2:
|
||||
bufp = usb2_rh_dev_descriptor;
|
||||
else if (hcd->driver->flags & HCD_USB11)
|
||||
break;
|
||||
case HCD_USB11:
|
||||
bufp = usb11_rh_dev_descriptor;
|
||||
else
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
len = 18;
|
||||
if (hcd->has_tt)
|
||||
patch_protocol = 1;
|
||||
break;
|
||||
case USB_DT_CONFIG << 8:
|
||||
if (hcd->driver->flags & HCD_USB2) {
|
||||
switch (hcd->driver->flags & HCD_MASK) {
|
||||
case HCD_USB3:
|
||||
bufp = ss_rh_config_descriptor;
|
||||
len = sizeof ss_rh_config_descriptor;
|
||||
break;
|
||||
case HCD_USB2:
|
||||
bufp = hs_rh_config_descriptor;
|
||||
len = sizeof hs_rh_config_descriptor;
|
||||
} else {
|
||||
break;
|
||||
case HCD_USB11:
|
||||
bufp = fs_rh_config_descriptor;
|
||||
len = sizeof fs_rh_config_descriptor;
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
if (device_can_wakeup(&hcd->self.root_hub->dev))
|
||||
patch_wakeup = 1;
|
||||
@ -755,23 +833,6 @@ static struct attribute_group usb_bus_attr_group = {
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct class *usb_host_class;
|
||||
|
||||
int usb_host_init(void)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
usb_host_class = class_create(THIS_MODULE, "usb_host");
|
||||
if (IS_ERR(usb_host_class))
|
||||
retval = PTR_ERR(usb_host_class);
|
||||
return retval;
|
||||
}
|
||||
|
||||
void usb_host_cleanup(void)
|
||||
{
|
||||
class_destroy(usb_host_class);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_bus_init - shared initialization code
|
||||
* @bus: the bus structure being initialized
|
||||
@ -818,12 +879,6 @@ static int usb_register_bus(struct usb_bus *bus)
|
||||
set_bit (busnum, busmap.busmap);
|
||||
bus->busnum = busnum;
|
||||
|
||||
bus->dev = device_create(usb_host_class, bus->controller, MKDEV(0, 0),
|
||||
bus, "usb_host%d", busnum);
|
||||
result = PTR_ERR(bus->dev);
|
||||
if (IS_ERR(bus->dev))
|
||||
goto error_create_class_dev;
|
||||
|
||||
/* Add it to the local list of buses */
|
||||
list_add (&bus->bus_list, &usb_bus_list);
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
@ -834,8 +889,6 @@ static int usb_register_bus(struct usb_bus *bus)
|
||||
"number %d\n", bus->busnum);
|
||||
return 0;
|
||||
|
||||
error_create_class_dev:
|
||||
clear_bit(busnum, busmap.busmap);
|
||||
error_find_busnum:
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
return result;
|
||||
@ -865,8 +918,6 @@ static void usb_deregister_bus (struct usb_bus *bus)
|
||||
usb_notify_remove_bus(bus);
|
||||
|
||||
clear_bit (bus->busnum, busmap.busmap);
|
||||
|
||||
device_unregister(bus->dev);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1199,7 +1250,8 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
|
||||
|
||||
/* Map the URB's buffers for DMA access.
|
||||
* Lower level HCD code should use *_dma exclusively,
|
||||
* unless it uses pio or talks to another transport.
|
||||
* unless it uses pio or talks to another transport,
|
||||
* or uses the provided scatter gather list for bulk.
|
||||
*/
|
||||
if (is_root_hub(urb->dev))
|
||||
return 0;
|
||||
@ -1520,6 +1572,92 @@ rescan:
|
||||
}
|
||||
}
|
||||
|
||||
/* Check whether a new configuration or alt setting for an interface
|
||||
* will exceed the bandwidth for the bus (or the host controller resources).
|
||||
* Only pass in a non-NULL config or interface, not both!
|
||||
* Passing NULL for both new_config and new_intf means the device will be
|
||||
* de-configured by issuing a set configuration 0 command.
|
||||
*/
|
||||
int usb_hcd_check_bandwidth(struct usb_device *udev,
|
||||
struct usb_host_config *new_config,
|
||||
struct usb_interface *new_intf)
|
||||
{
|
||||
int num_intfs, i, j;
|
||||
struct usb_interface_cache *intf_cache;
|
||||
struct usb_host_interface *alt = 0;
|
||||
int ret = 0;
|
||||
struct usb_hcd *hcd;
|
||||
struct usb_host_endpoint *ep;
|
||||
|
||||
hcd = bus_to_hcd(udev->bus);
|
||||
if (!hcd->driver->check_bandwidth)
|
||||
return 0;
|
||||
|
||||
/* Configuration is being removed - set configuration 0 */
|
||||
if (!new_config && !new_intf) {
|
||||
for (i = 1; i < 16; ++i) {
|
||||
ep = udev->ep_out[i];
|
||||
if (ep)
|
||||
hcd->driver->drop_endpoint(hcd, udev, ep);
|
||||
ep = udev->ep_in[i];
|
||||
if (ep)
|
||||
hcd->driver->drop_endpoint(hcd, udev, ep);
|
||||
}
|
||||
hcd->driver->check_bandwidth(hcd, udev);
|
||||
return 0;
|
||||
}
|
||||
/* Check if the HCD says there's enough bandwidth. Enable all endpoints
|
||||
* each interface's alt setting 0 and ask the HCD to check the bandwidth
|
||||
* of the bus. There will always be bandwidth for endpoint 0, so it's
|
||||
* ok to exclude it.
|
||||
*/
|
||||
if (new_config) {
|
||||
num_intfs = new_config->desc.bNumInterfaces;
|
||||
/* Remove endpoints (except endpoint 0, which is always on the
|
||||
* schedule) from the old config from the schedule
|
||||
*/
|
||||
for (i = 1; i < 16; ++i) {
|
||||
ep = udev->ep_out[i];
|
||||
if (ep) {
|
||||
ret = hcd->driver->drop_endpoint(hcd, udev, ep);
|
||||
if (ret < 0)
|
||||
goto reset;
|
||||
}
|
||||
ep = udev->ep_in[i];
|
||||
if (ep) {
|
||||
ret = hcd->driver->drop_endpoint(hcd, udev, ep);
|
||||
if (ret < 0)
|
||||
goto reset;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < num_intfs; ++i) {
|
||||
|
||||
/* Dig the endpoints for alt setting 0 out of the
|
||||
* interface cache for this interface
|
||||
*/
|
||||
intf_cache = new_config->intf_cache[i];
|
||||
for (j = 0; j < intf_cache->num_altsetting; j++) {
|
||||
if (intf_cache->altsetting[j].desc.bAlternateSetting == 0)
|
||||
alt = &intf_cache->altsetting[j];
|
||||
}
|
||||
if (!alt) {
|
||||
printk(KERN_DEBUG "Did not find alt setting 0 for intf %d\n", i);
|
||||
continue;
|
||||
}
|
||||
for (j = 0; j < alt->desc.bNumEndpoints; j++) {
|
||||
ret = hcd->driver->add_endpoint(hcd, udev, &alt->endpoint[j]);
|
||||
if (ret < 0)
|
||||
goto reset;
|
||||
}
|
||||
}
|
||||
}
|
||||
ret = hcd->driver->check_bandwidth(hcd, udev);
|
||||
reset:
|
||||
if (ret < 0)
|
||||
hcd->driver->reset_bandwidth(hcd, udev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Disables the endpoint: synchronizes with the hcd to make sure all
|
||||
* endpoint state is gone from hardware. usb_hcd_flush_endpoint() must
|
||||
* have been called previously. Use for set_configuration, set_interface,
|
||||
@ -1897,8 +2035,20 @@ int usb_add_hcd(struct usb_hcd *hcd,
|
||||
retval = -ENOMEM;
|
||||
goto err_allocate_root_hub;
|
||||
}
|
||||
rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH :
|
||||
USB_SPEED_FULL;
|
||||
|
||||
switch (hcd->driver->flags & HCD_MASK) {
|
||||
case HCD_USB11:
|
||||
rhdev->speed = USB_SPEED_FULL;
|
||||
break;
|
||||
case HCD_USB2:
|
||||
rhdev->speed = USB_SPEED_HIGH;
|
||||
break;
|
||||
case HCD_USB3:
|
||||
rhdev->speed = USB_SPEED_SUPER;
|
||||
break;
|
||||
default:
|
||||
goto err_allocate_root_hub;
|
||||
}
|
||||
hcd->self.root_hub = rhdev;
|
||||
|
||||
/* wakeup flag init defaults to "everything works" for root hubs,
|
||||
|
@ -173,6 +173,8 @@ struct hc_driver {
|
||||
#define HCD_LOCAL_MEM 0x0002 /* HC needs local memory */
|
||||
#define HCD_USB11 0x0010 /* USB 1.1 */
|
||||
#define HCD_USB2 0x0020 /* USB 2.0 */
|
||||
#define HCD_USB3 0x0040 /* USB 3.0 */
|
||||
#define HCD_MASK 0x0070
|
||||
|
||||
/* called to init HCD and root hub */
|
||||
int (*reset) (struct usb_hcd *hcd);
|
||||
@ -182,10 +184,10 @@ struct hc_driver {
|
||||
* a whole, not just the root hub; they're for PCI bus glue.
|
||||
*/
|
||||
/* called after suspending the hub, before entering D3 etc */
|
||||
int (*pci_suspend) (struct usb_hcd *hcd, pm_message_t message);
|
||||
int (*pci_suspend)(struct usb_hcd *hcd);
|
||||
|
||||
/* called after entering D0 (etc), before resuming the hub */
|
||||
int (*pci_resume) (struct usb_hcd *hcd);
|
||||
int (*pci_resume)(struct usb_hcd *hcd, bool hibernated);
|
||||
|
||||
/* cleanly make HCD stop writing memory and doing I/O */
|
||||
void (*stop) (struct usb_hcd *hcd);
|
||||
@ -224,6 +226,43 @@ struct hc_driver {
|
||||
void (*relinquish_port)(struct usb_hcd *, int);
|
||||
/* has a port been handed over to a companion? */
|
||||
int (*port_handed_over)(struct usb_hcd *, int);
|
||||
|
||||
/* xHCI specific functions */
|
||||
/* Called by usb_alloc_dev to alloc HC device structures */
|
||||
int (*alloc_dev)(struct usb_hcd *, struct usb_device *);
|
||||
/* Called by usb_release_dev to free HC device structures */
|
||||
void (*free_dev)(struct usb_hcd *, struct usb_device *);
|
||||
|
||||
/* Bandwidth computation functions */
|
||||
/* Note that add_endpoint() can only be called once per endpoint before
|
||||
* check_bandwidth() or reset_bandwidth() must be called.
|
||||
* drop_endpoint() can only be called once per endpoint also.
|
||||
* A call to xhci_drop_endpoint() followed by a call to xhci_add_endpoint() will
|
||||
* add the endpoint to the schedule with possibly new parameters denoted by a
|
||||
* different endpoint descriptor in usb_host_endpoint.
|
||||
* A call to xhci_add_endpoint() followed by a call to xhci_drop_endpoint() is
|
||||
* not allowed.
|
||||
*/
|
||||
/* Allocate endpoint resources and add them to a new schedule */
|
||||
int (*add_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *);
|
||||
/* Drop an endpoint from a new schedule */
|
||||
int (*drop_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *);
|
||||
/* Check that a new hardware configuration, set using
|
||||
* endpoint_enable and endpoint_disable, does not exceed bus
|
||||
* bandwidth. This must be called before any set configuration
|
||||
* or set interface requests are sent to the device.
|
||||
*/
|
||||
int (*check_bandwidth)(struct usb_hcd *, struct usb_device *);
|
||||
/* Reset the device schedule to the last known good schedule,
|
||||
* which was set from a previous successful call to
|
||||
* check_bandwidth(). This reverts any add_endpoint() and
|
||||
* drop_endpoint() calls since that last successful call.
|
||||
* Used for when a check_bandwidth() call fails due to resource
|
||||
* or bandwidth constraints.
|
||||
*/
|
||||
void (*reset_bandwidth)(struct usb_hcd *, struct usb_device *);
|
||||
/* Returns the hardware-chosen device address */
|
||||
int (*address_device)(struct usb_hcd *, struct usb_device *udev);
|
||||
};
|
||||
|
||||
extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
|
||||
@ -242,6 +281,9 @@ extern void usb_hcd_disable_endpoint(struct usb_device *udev,
|
||||
extern void usb_hcd_reset_endpoint(struct usb_device *udev,
|
||||
struct usb_host_endpoint *ep);
|
||||
extern void usb_hcd_synchronize_unlinks(struct usb_device *udev);
|
||||
extern int usb_hcd_check_bandwidth(struct usb_device *udev,
|
||||
struct usb_host_config *new_config,
|
||||
struct usb_interface *new_intf);
|
||||
extern int usb_hcd_get_frame_number(struct usb_device *udev);
|
||||
|
||||
extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
|
||||
@ -261,14 +303,11 @@ struct pci_device_id;
|
||||
extern int usb_hcd_pci_probe(struct pci_dev *dev,
|
||||
const struct pci_device_id *id);
|
||||
extern void usb_hcd_pci_remove(struct pci_dev *dev);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
extern int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t msg);
|
||||
extern int usb_hcd_pci_resume(struct pci_dev *dev);
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
extern void usb_hcd_pci_shutdown(struct pci_dev *dev);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
extern struct dev_pm_ops usb_hcd_pci_pm_ops;
|
||||
#endif
|
||||
#endif /* CONFIG_PCI */
|
||||
|
||||
/* pci-ish (pdev null is ok) buffer alloc/mapping support */
|
||||
|
@ -155,6 +155,8 @@ static inline char *portspeed(int portstatus)
|
||||
return "480 Mb/s";
|
||||
else if (portstatus & (1 << USB_PORT_FEAT_LOWSPEED))
|
||||
return "1.5 Mb/s";
|
||||
else if (portstatus & (1 << USB_PORT_FEAT_SUPERSPEED))
|
||||
return "5.0 Gb/s";
|
||||
else
|
||||
return "12 Mb/s";
|
||||
}
|
||||
@ -457,13 +459,13 @@ static void hub_tt_kevent (struct work_struct *work)
|
||||
|
||||
spin_lock_irqsave (&hub->tt.lock, flags);
|
||||
while (--limit && !list_empty (&hub->tt.clear_list)) {
|
||||
struct list_head *temp;
|
||||
struct list_head *next;
|
||||
struct usb_tt_clear *clear;
|
||||
struct usb_device *hdev = hub->hdev;
|
||||
int status;
|
||||
|
||||
temp = hub->tt.clear_list.next;
|
||||
clear = list_entry (temp, struct usb_tt_clear, clear_list);
|
||||
next = hub->tt.clear_list.next;
|
||||
clear = list_entry (next, struct usb_tt_clear, clear_list);
|
||||
list_del (&clear->clear_list);
|
||||
|
||||
/* drop lock so HCD can concurrently report other TT errors */
|
||||
@ -951,6 +953,9 @@ static int hub_configure(struct usb_hub *hub,
|
||||
ret);
|
||||
hub->tt.hub = hdev;
|
||||
break;
|
||||
case 3:
|
||||
/* USB 3.0 hubs don't have a TT */
|
||||
break;
|
||||
default:
|
||||
dev_dbg(hub_dev, "Unrecognized hub protocol %d\n",
|
||||
hdev->descriptor.bDeviceProtocol);
|
||||
@ -1323,6 +1328,11 @@ EXPORT_SYMBOL_GPL(usb_set_device_state);
|
||||
* 0 is reserved by USB for default address; (b) Linux's USB stack
|
||||
* uses always #1 for the root hub of the controller. So USB stack's
|
||||
* port #1, which is wusb virtual-port #0 has address #2.
|
||||
*
|
||||
* Devices connected under xHCI are not as simple. The host controller
|
||||
* supports virtualization, so the hardware assigns device addresses and
|
||||
* the HCD must setup data structures before issuing a set address
|
||||
* command to the hardware.
|
||||
*/
|
||||
static void choose_address(struct usb_device *udev)
|
||||
{
|
||||
@ -1642,6 +1652,9 @@ int usb_new_device(struct usb_device *udev)
|
||||
err = usb_configure_device(udev); /* detect & probe dev/intfs */
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
dev_dbg(&udev->dev, "udev %d, busnum %d, minor = %d\n",
|
||||
udev->devnum, udev->bus->busnum,
|
||||
(((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
|
||||
/* export the usbdev device-node for libusb */
|
||||
udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
|
||||
(((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
|
||||
@ -2395,19 +2408,29 @@ EXPORT_SYMBOL_GPL(usb_ep0_reinit);
|
||||
static int hub_set_address(struct usb_device *udev, int devnum)
|
||||
{
|
||||
int retval;
|
||||
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
||||
|
||||
if (devnum <= 1)
|
||||
/*
|
||||
* The host controller will choose the device address,
|
||||
* instead of the core having chosen it earlier
|
||||
*/
|
||||
if (!hcd->driver->address_device && devnum <= 1)
|
||||
return -EINVAL;
|
||||
if (udev->state == USB_STATE_ADDRESS)
|
||||
return 0;
|
||||
if (udev->state != USB_STATE_DEFAULT)
|
||||
return -EINVAL;
|
||||
retval = usb_control_msg(udev, usb_sndaddr0pipe(),
|
||||
USB_REQ_SET_ADDRESS, 0, devnum, 0,
|
||||
NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
if (hcd->driver->address_device) {
|
||||
retval = hcd->driver->address_device(hcd, udev);
|
||||
} else {
|
||||
retval = usb_control_msg(udev, usb_sndaddr0pipe(),
|
||||
USB_REQ_SET_ADDRESS, 0, devnum, 0,
|
||||
NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
if (retval == 0)
|
||||
update_address(udev, devnum);
|
||||
}
|
||||
if (retval == 0) {
|
||||
/* Device now using proper address. */
|
||||
update_address(udev, devnum);
|
||||
usb_set_device_state(udev, USB_STATE_ADDRESS);
|
||||
usb_ep0_reinit(udev);
|
||||
}
|
||||
@ -2430,6 +2453,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
static DEFINE_MUTEX(usb_address0_mutex);
|
||||
|
||||
struct usb_device *hdev = hub->hdev;
|
||||
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
|
||||
int i, j, retval;
|
||||
unsigned delay = HUB_SHORT_RESET_TIME;
|
||||
enum usb_device_speed oldspeed = udev->speed;
|
||||
@ -2452,11 +2476,24 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
|
||||
mutex_lock(&usb_address0_mutex);
|
||||
|
||||
/* Reset the device; full speed may morph to high speed */
|
||||
retval = hub_port_reset(hub, port1, udev, delay);
|
||||
if (retval < 0) /* error or disconnect */
|
||||
if ((hcd->driver->flags & HCD_USB3) && udev->config) {
|
||||
/* FIXME this will need special handling by the xHCI driver. */
|
||||
dev_dbg(&udev->dev,
|
||||
"xHCI reset of configured device "
|
||||
"not supported yet.\n");
|
||||
retval = -EINVAL;
|
||||
goto fail;
|
||||
/* success, speed is known */
|
||||
} else if (!udev->config && oldspeed == USB_SPEED_SUPER) {
|
||||
/* Don't reset USB 3.0 devices during an initial setup */
|
||||
usb_set_device_state(udev, USB_STATE_DEFAULT);
|
||||
} else {
|
||||
/* Reset the device; full speed may morph to high speed */
|
||||
/* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
|
||||
retval = hub_port_reset(hub, port1, udev, delay);
|
||||
if (retval < 0) /* error or disconnect */
|
||||
goto fail;
|
||||
/* success, speed is known */
|
||||
}
|
||||
retval = -ENODEV;
|
||||
|
||||
if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) {
|
||||
@ -2471,6 +2508,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
* reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
|
||||
*/
|
||||
switch (udev->speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
case USB_SPEED_VARIABLE: /* fixed at 512 */
|
||||
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
|
||||
break;
|
||||
@ -2496,16 +2534,20 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
case USB_SPEED_LOW: speed = "low"; break;
|
||||
case USB_SPEED_FULL: speed = "full"; break;
|
||||
case USB_SPEED_HIGH: speed = "high"; break;
|
||||
case USB_SPEED_SUPER:
|
||||
speed = "super";
|
||||
break;
|
||||
case USB_SPEED_VARIABLE:
|
||||
speed = "variable";
|
||||
type = "Wireless ";
|
||||
break;
|
||||
default: speed = "?"; break;
|
||||
}
|
||||
dev_info (&udev->dev,
|
||||
"%s %s speed %sUSB device using %s and address %d\n",
|
||||
(udev->config) ? "reset" : "new", speed, type,
|
||||
udev->bus->controller->driver->name, devnum);
|
||||
if (udev->speed != USB_SPEED_SUPER)
|
||||
dev_info(&udev->dev,
|
||||
"%s %s speed %sUSB device using %s and address %d\n",
|
||||
(udev->config) ? "reset" : "new", speed, type,
|
||||
udev->bus->controller->driver->name, devnum);
|
||||
|
||||
/* Set up TT records, if needed */
|
||||
if (hdev->tt) {
|
||||
@ -2530,7 +2572,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
* value.
|
||||
*/
|
||||
for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
|
||||
if (USE_NEW_SCHEME(retry_counter)) {
|
||||
/*
|
||||
* An xHCI controller cannot send any packets to a device until
|
||||
* a set address command successfully completes.
|
||||
*/
|
||||
if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3)) {
|
||||
struct usb_device_descriptor *buf;
|
||||
int r = 0;
|
||||
|
||||
@ -2596,7 +2642,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
* unauthorized address in the Connect Ack sequence;
|
||||
* authorization will assign the final address.
|
||||
*/
|
||||
if (udev->wusb == 0) {
|
||||
if (udev->wusb == 0) {
|
||||
for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
|
||||
retval = hub_set_address(udev, devnum);
|
||||
if (retval >= 0)
|
||||
@ -2609,13 +2655,20 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
devnum, retval);
|
||||
goto fail;
|
||||
}
|
||||
if (udev->speed == USB_SPEED_SUPER) {
|
||||
devnum = udev->devnum;
|
||||
dev_info(&udev->dev,
|
||||
"%s SuperSpeed USB device using %s and address %d\n",
|
||||
(udev->config) ? "reset" : "new",
|
||||
udev->bus->controller->driver->name, devnum);
|
||||
}
|
||||
|
||||
/* cope with hardware quirkiness:
|
||||
* - let SET_ADDRESS settle, some device hardware wants it
|
||||
* - read ep0 maxpacket even for high and low speed,
|
||||
*/
|
||||
msleep(10);
|
||||
if (USE_NEW_SCHEME(retry_counter))
|
||||
if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3))
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2634,8 +2687,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
if (retval)
|
||||
goto fail;
|
||||
|
||||
i = udev->descriptor.bMaxPacketSize0 == 0xff? /* wusb device? */
|
||||
512 : udev->descriptor.bMaxPacketSize0;
|
||||
if (udev->descriptor.bMaxPacketSize0 == 0xff ||
|
||||
udev->speed == USB_SPEED_SUPER)
|
||||
i = 512;
|
||||
else
|
||||
i = udev->descriptor.bMaxPacketSize0;
|
||||
if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {
|
||||
if (udev->speed != USB_SPEED_FULL ||
|
||||
!(i == 8 || i == 16 || i == 32 || i == 64)) {
|
||||
@ -2847,19 +2903,41 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
|
||||
}
|
||||
|
||||
usb_set_device_state(udev, USB_STATE_POWERED);
|
||||
udev->speed = USB_SPEED_UNKNOWN;
|
||||
udev->bus_mA = hub->mA_per_port;
|
||||
udev->level = hdev->level + 1;
|
||||
udev->wusb = hub_is_wusb(hub);
|
||||
|
||||
/* set the address */
|
||||
choose_address(udev);
|
||||
if (udev->devnum <= 0) {
|
||||
status = -ENOTCONN; /* Don't retry */
|
||||
goto loop;
|
||||
/*
|
||||
* USB 3.0 devices are reset automatically before the connect
|
||||
* port status change appears, and the root hub port status
|
||||
* shows the correct speed. We also get port change
|
||||
* notifications for USB 3.0 devices from the USB 3.0 portion of
|
||||
* an external USB 3.0 hub, but this isn't handled correctly yet
|
||||
* FIXME.
|
||||
*/
|
||||
|
||||
if (!(hcd->driver->flags & HCD_USB3))
|
||||
udev->speed = USB_SPEED_UNKNOWN;
|
||||
else if ((hdev->parent == NULL) &&
|
||||
(portstatus & (1 << USB_PORT_FEAT_SUPERSPEED)))
|
||||
udev->speed = USB_SPEED_SUPER;
|
||||
else
|
||||
udev->speed = USB_SPEED_UNKNOWN;
|
||||
|
||||
/*
|
||||
* xHCI needs to issue an address device command later
|
||||
* in the hub_port_init sequence for SS/HS/FS/LS devices.
|
||||
*/
|
||||
if (!(hcd->driver->flags & HCD_USB3)) {
|
||||
/* set the address */
|
||||
choose_address(udev);
|
||||
if (udev->devnum <= 0) {
|
||||
status = -ENOTCONN; /* Don't retry */
|
||||
goto loop;
|
||||
}
|
||||
}
|
||||
|
||||
/* reset and get descriptor */
|
||||
/* reset (non-USB 3.0 devices) and get descriptor */
|
||||
status = hub_port_init(hub, udev, port1, i);
|
||||
if (status < 0)
|
||||
goto loop;
|
||||
|
@ -47,7 +47,10 @@
|
||||
#define USB_PORT_FEAT_L1 5 /* L1 suspend */
|
||||
#define USB_PORT_FEAT_POWER 8
|
||||
#define USB_PORT_FEAT_LOWSPEED 9
|
||||
/* This value was never in Table 11-17 */
|
||||
#define USB_PORT_FEAT_HIGHSPEED 10
|
||||
/* This value is also fake */
|
||||
#define USB_PORT_FEAT_SUPERSPEED 11
|
||||
#define USB_PORT_FEAT_C_CONNECTION 16
|
||||
#define USB_PORT_FEAT_C_ENABLE 17
|
||||
#define USB_PORT_FEAT_C_SUSPEND 18
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <linux/mm.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/nls.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/usb/quirks.h>
|
||||
@ -364,6 +365,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
|
||||
int i;
|
||||
int urb_flags;
|
||||
int dma;
|
||||
int use_sg;
|
||||
|
||||
if (!io || !dev || !sg
|
||||
|| usb_pipecontrol(pipe)
|
||||
@ -391,7 +393,19 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
|
||||
if (io->entries <= 0)
|
||||
return io->entries;
|
||||
|
||||
io->urbs = kmalloc(io->entries * sizeof *io->urbs, mem_flags);
|
||||
/* If we're running on an xHCI host controller, queue the whole scatter
|
||||
* gather list with one call to urb_enqueue(). This is only for bulk,
|
||||
* as that endpoint type does not care how the data gets broken up
|
||||
* across frames.
|
||||
*/
|
||||
if (usb_pipebulk(pipe) &&
|
||||
bus_to_hcd(dev->bus)->driver->flags & HCD_USB3) {
|
||||
io->urbs = kmalloc(sizeof *io->urbs, mem_flags);
|
||||
use_sg = true;
|
||||
} else {
|
||||
io->urbs = kmalloc(io->entries * sizeof *io->urbs, mem_flags);
|
||||
use_sg = false;
|
||||
}
|
||||
if (!io->urbs)
|
||||
goto nomem;
|
||||
|
||||
@ -401,62 +415,92 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
|
||||
if (usb_pipein(pipe))
|
||||
urb_flags |= URB_SHORT_NOT_OK;
|
||||
|
||||
for_each_sg(sg, sg, io->entries, i) {
|
||||
unsigned len;
|
||||
|
||||
io->urbs[i] = usb_alloc_urb(0, mem_flags);
|
||||
if (!io->urbs[i]) {
|
||||
io->entries = i;
|
||||
if (use_sg) {
|
||||
io->urbs[0] = usb_alloc_urb(0, mem_flags);
|
||||
if (!io->urbs[0]) {
|
||||
io->entries = 0;
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
io->urbs[i]->dev = NULL;
|
||||
io->urbs[i]->pipe = pipe;
|
||||
io->urbs[i]->interval = period;
|
||||
io->urbs[i]->transfer_flags = urb_flags;
|
||||
io->urbs[0]->dev = NULL;
|
||||
io->urbs[0]->pipe = pipe;
|
||||
io->urbs[0]->interval = period;
|
||||
io->urbs[0]->transfer_flags = urb_flags;
|
||||
|
||||
io->urbs[i]->complete = sg_complete;
|
||||
io->urbs[i]->context = io;
|
||||
io->urbs[0]->complete = sg_complete;
|
||||
io->urbs[0]->context = io;
|
||||
/* A length of zero means transfer the whole sg list */
|
||||
io->urbs[0]->transfer_buffer_length = length;
|
||||
if (length == 0) {
|
||||
for_each_sg(sg, sg, io->entries, i) {
|
||||
io->urbs[0]->transfer_buffer_length +=
|
||||
sg_dma_len(sg);
|
||||
}
|
||||
}
|
||||
io->urbs[0]->sg = io;
|
||||
io->urbs[0]->num_sgs = io->entries;
|
||||
io->entries = 1;
|
||||
} else {
|
||||
for_each_sg(sg, sg, io->entries, i) {
|
||||
unsigned len;
|
||||
|
||||
/*
|
||||
* Some systems need to revert to PIO when DMA is temporarily
|
||||
* unavailable. For their sakes, both transfer_buffer and
|
||||
* transfer_dma are set when possible. However this can only
|
||||
* work on systems without:
|
||||
*
|
||||
* - HIGHMEM, since DMA buffers located in high memory are
|
||||
* not directly addressable by the CPU for PIO;
|
||||
*
|
||||
* - IOMMU, since dma_map_sg() is allowed to use an IOMMU to
|
||||
* make virtually discontiguous buffers be "dma-contiguous"
|
||||
* so that PIO and DMA need diferent numbers of URBs.
|
||||
*
|
||||
* So when HIGHMEM or IOMMU are in use, transfer_buffer is NULL
|
||||
* to prevent stale pointers and to help spot bugs.
|
||||
*/
|
||||
if (dma) {
|
||||
io->urbs[i]->transfer_dma = sg_dma_address(sg);
|
||||
len = sg_dma_len(sg);
|
||||
io->urbs[i] = usb_alloc_urb(0, mem_flags);
|
||||
if (!io->urbs[i]) {
|
||||
io->entries = i;
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
io->urbs[i]->dev = NULL;
|
||||
io->urbs[i]->pipe = pipe;
|
||||
io->urbs[i]->interval = period;
|
||||
io->urbs[i]->transfer_flags = urb_flags;
|
||||
|
||||
io->urbs[i]->complete = sg_complete;
|
||||
io->urbs[i]->context = io;
|
||||
|
||||
/*
|
||||
* Some systems need to revert to PIO when DMA is
|
||||
* temporarily unavailable. For their sakes, both
|
||||
* transfer_buffer and transfer_dma are set when
|
||||
* possible. However this can only work on systems
|
||||
* without:
|
||||
*
|
||||
* - HIGHMEM, since DMA buffers located in high memory
|
||||
* are not directly addressable by the CPU for PIO;
|
||||
*
|
||||
* - IOMMU, since dma_map_sg() is allowed to use an
|
||||
* IOMMU to make virtually discontiguous buffers be
|
||||
* "dma-contiguous" so that PIO and DMA need diferent
|
||||
* numbers of URBs.
|
||||
*
|
||||
* So when HIGHMEM or IOMMU are in use, transfer_buffer
|
||||
* is NULL to prevent stale pointers and to help spot
|
||||
* bugs.
|
||||
*/
|
||||
if (dma) {
|
||||
io->urbs[i]->transfer_dma = sg_dma_address(sg);
|
||||
len = sg_dma_len(sg);
|
||||
#if defined(CONFIG_HIGHMEM) || defined(CONFIG_GART_IOMMU)
|
||||
io->urbs[i]->transfer_buffer = NULL;
|
||||
io->urbs[i]->transfer_buffer = NULL;
|
||||
#else
|
||||
io->urbs[i]->transfer_buffer = sg_virt(sg);
|
||||
io->urbs[i]->transfer_buffer = sg_virt(sg);
|
||||
#endif
|
||||
} else {
|
||||
/* hc may use _only_ transfer_buffer */
|
||||
io->urbs[i]->transfer_buffer = sg_virt(sg);
|
||||
len = sg->length;
|
||||
}
|
||||
} else {
|
||||
/* hc may use _only_ transfer_buffer */
|
||||
io->urbs[i]->transfer_buffer = sg_virt(sg);
|
||||
len = sg->length;
|
||||
}
|
||||
|
||||
if (length) {
|
||||
len = min_t(unsigned, len, length);
|
||||
length -= len;
|
||||
if (length == 0)
|
||||
io->entries = i + 1;
|
||||
if (length) {
|
||||
len = min_t(unsigned, len, length);
|
||||
length -= len;
|
||||
if (length == 0)
|
||||
io->entries = i + 1;
|
||||
}
|
||||
io->urbs[i]->transfer_buffer_length = len;
|
||||
}
|
||||
io->urbs[i]->transfer_buffer_length = len;
|
||||
io->urbs[--i]->transfer_flags &= ~URB_NO_INTERRUPT;
|
||||
}
|
||||
io->urbs[--i]->transfer_flags &= ~URB_NO_INTERRUPT;
|
||||
|
||||
/* transaction state */
|
||||
io->count = io->entries;
|
||||
@ -509,6 +553,10 @@ EXPORT_SYMBOL_GPL(usb_sg_init);
|
||||
* could be transferred. That capability is less useful for low or full
|
||||
* speed interrupt endpoints, which allow at most one packet per millisecond,
|
||||
* of at most 8 or 64 bytes (respectively).
|
||||
*
|
||||
* It is not necessary to call this function to reserve bandwidth for devices
|
||||
* under an xHCI host controller, as the bandwidth is reserved when the
|
||||
* configuration or interface alt setting is selected.
|
||||
*/
|
||||
void usb_sg_wait(struct usb_sg_request *io)
|
||||
{
|
||||
@ -759,7 +807,7 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid,
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_string - returns ISO 8859-1 version of a string descriptor
|
||||
* usb_string - returns UTF-8 version of a string descriptor
|
||||
* @dev: the device whose string descriptor is being retrieved
|
||||
* @index: the number of the descriptor
|
||||
* @buf: where to put the string
|
||||
@ -767,17 +815,10 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid,
|
||||
* Context: !in_interrupt ()
|
||||
*
|
||||
* This converts the UTF-16LE encoded strings returned by devices, from
|
||||
* usb_get_string_descriptor(), to null-terminated ISO-8859-1 encoded ones
|
||||
* that are more usable in most kernel contexts. Note that all characters
|
||||
* in the chosen descriptor that can't be encoded using ISO-8859-1
|
||||
* are converted to the question mark ("?") character, and this function
|
||||
* usb_get_string_descriptor(), to null-terminated UTF-8 encoded ones
|
||||
* that are more usable in most kernel contexts. Note that this function
|
||||
* chooses strings in the first language supported by the device.
|
||||
*
|
||||
* The ASCII (or, redundantly, "US-ASCII") character set is the seven-bit
|
||||
* subset of ISO 8859-1. ISO-8859-1 is the eight-bit subset of Unicode,
|
||||
* and is appropriate for use many uses of English and several other
|
||||
* Western European languages. (But it doesn't include the "Euro" symbol.)
|
||||
*
|
||||
* This call is synchronous, and may not be used in an interrupt context.
|
||||
*
|
||||
* Returns length of the string (>= 0) or usb_control_msg status (< 0).
|
||||
@ -786,7 +827,6 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
|
||||
{
|
||||
unsigned char *tbuf;
|
||||
int err;
|
||||
unsigned int u, idx;
|
||||
|
||||
if (dev->state == USB_STATE_SUSPENDED)
|
||||
return -EHOSTUNREACH;
|
||||
@ -821,16 +861,9 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
|
||||
goto errout;
|
||||
|
||||
size--; /* leave room for trailing NULL char in output buffer */
|
||||
for (idx = 0, u = 2; u < err; u += 2) {
|
||||
if (idx >= size)
|
||||
break;
|
||||
if (tbuf[u+1]) /* high byte */
|
||||
buf[idx++] = '?'; /* non ISO-8859-1 character */
|
||||
else
|
||||
buf[idx++] = tbuf[u];
|
||||
}
|
||||
buf[idx] = 0;
|
||||
err = idx;
|
||||
err = utf16s_to_utf8s((wchar_t *) &tbuf[2], (err - 2) / 2,
|
||||
UTF16_LITTLE_ENDIAN, buf, size);
|
||||
buf[err] = 0;
|
||||
|
||||
if (tbuf[1] != USB_DT_STRING)
|
||||
dev_dbg(&dev->dev,
|
||||
@ -843,6 +876,9 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_string);
|
||||
|
||||
/* one UTF-8-encoded 16-bit character has at most three bytes */
|
||||
#define MAX_USB_STRING_SIZE (127 * 3 + 1)
|
||||
|
||||
/**
|
||||
* usb_cache_string - read a string descriptor and cache it for later use
|
||||
* @udev: the device whose string descriptor is being read
|
||||
@ -860,9 +896,9 @@ char *usb_cache_string(struct usb_device *udev, int index)
|
||||
if (index <= 0)
|
||||
return NULL;
|
||||
|
||||
buf = kmalloc(256, GFP_KERNEL);
|
||||
buf = kmalloc(MAX_USB_STRING_SIZE, GFP_KERNEL);
|
||||
if (buf) {
|
||||
len = usb_string(udev, index, buf, 256);
|
||||
len = usb_string(udev, index, buf, MAX_USB_STRING_SIZE);
|
||||
if (len > 0) {
|
||||
smallbuf = kmalloc(++len, GFP_KERNEL);
|
||||
if (!smallbuf)
|
||||
@ -1664,6 +1700,21 @@ free_interfaces:
|
||||
if (ret)
|
||||
goto free_interfaces;
|
||||
|
||||
/* Make sure we have bandwidth (and available HCD resources) for this
|
||||
* configuration. Remove endpoints from the schedule if we're dropping
|
||||
* this configuration to set configuration 0. After this point, the
|
||||
* host controller will not allow submissions to dropped endpoints. If
|
||||
* this call fails, the device state is unchanged.
|
||||
*/
|
||||
if (cp)
|
||||
ret = usb_hcd_check_bandwidth(dev, cp, NULL);
|
||||
else
|
||||
ret = usb_hcd_check_bandwidth(dev, NULL, NULL);
|
||||
if (ret < 0) {
|
||||
usb_autosuspend_device(dev);
|
||||
goto free_interfaces;
|
||||
}
|
||||
|
||||
/* if it's already configured, clear out old state first.
|
||||
* getting rid of old interfaces means unbinding their drivers.
|
||||
*/
|
||||
@ -1686,6 +1737,7 @@ free_interfaces:
|
||||
dev->actconfig = cp;
|
||||
if (!cp) {
|
||||
usb_set_device_state(dev, USB_STATE_ADDRESS);
|
||||
usb_hcd_check_bandwidth(dev, NULL, NULL);
|
||||
usb_autosuspend_device(dev);
|
||||
goto free_interfaces;
|
||||
}
|
||||
|
@ -552,8 +552,8 @@ static struct attribute *dev_string_attrs[] = {
|
||||
static mode_t dev_string_attrs_are_visible(struct kobject *kobj,
|
||||
struct attribute *a, int n)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(
|
||||
container_of(kobj, struct device, kobj));
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
|
||||
if (a == &dev_attr_manufacturer.attr) {
|
||||
if (udev->manufacturer == NULL)
|
||||
@ -585,8 +585,8 @@ static ssize_t
|
||||
read_descriptors(struct kobject *kobj, struct bin_attribute *attr,
|
||||
char *buf, loff_t off, size_t count)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(
|
||||
container_of(kobj, struct device, kobj));
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
size_t nleft = count;
|
||||
size_t srclen, n;
|
||||
int cfgno;
|
||||
@ -786,8 +786,8 @@ static struct attribute *intf_assoc_attrs[] = {
|
||||
static mode_t intf_assoc_attrs_are_visible(struct kobject *kobj,
|
||||
struct attribute *a, int n)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(
|
||||
container_of(kobj, struct device, kobj));
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
|
||||
if (intf->intf_assoc == NULL)
|
||||
return 0;
|
||||
|
@ -241,6 +241,12 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb);
|
||||
* If the USB subsystem can't allocate sufficient bandwidth to perform
|
||||
* the periodic request, submitting such a periodic request should fail.
|
||||
*
|
||||
* For devices under xHCI, the bandwidth is reserved at configuration time, or
|
||||
* when the alt setting is selected. If there is not enough bus bandwidth, the
|
||||
* configuration/alt setting request will fail. Therefore, submissions to
|
||||
* periodic endpoints on devices under xHCI should never fail due to bandwidth
|
||||
* constraints.
|
||||
*
|
||||
* Device drivers must explicitly request that repetition, by ensuring that
|
||||
* some URB is always on the endpoint's queue (except possibly for short
|
||||
* periods during completion callacks). When there is no longer an urb
|
||||
@ -351,6 +357,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
|
||||
if (xfertype == USB_ENDPOINT_XFER_ISOC) {
|
||||
int n, len;
|
||||
|
||||
/* FIXME SuperSpeed isoc endpoints have up to 16 bursts */
|
||||
/* "high bandwidth" mode, 1-3 packets/uframe? */
|
||||
if (dev->speed == USB_SPEED_HIGH) {
|
||||
int mult = 1 + ((max >> 11) & 0x03);
|
||||
@ -426,6 +433,11 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
|
||||
return -EINVAL;
|
||||
/* too big? */
|
||||
switch (dev->speed) {
|
||||
case USB_SPEED_SUPER: /* units are 125us */
|
||||
/* Handle up to 2^(16-1) microframes */
|
||||
if (urb->interval > (1 << 15))
|
||||
return -EINVAL;
|
||||
max = 1 << 15;
|
||||
case USB_SPEED_HIGH: /* units are microframes */
|
||||
/* NOTE usb handles 2^15 */
|
||||
if (urb->interval > (1024 * 8))
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <linux/usb.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <linux/scatterlist.h>
|
||||
@ -139,8 +140,7 @@ static int __find_interface(struct device *dev, void *data)
|
||||
struct find_interface_arg *arg = data;
|
||||
struct usb_interface *intf;
|
||||
|
||||
/* can't look at usb devices, only interfaces */
|
||||
if (is_usb_device(dev))
|
||||
if (!is_usb_interface(dev))
|
||||
return 0;
|
||||
|
||||
intf = to_usb_interface(dev);
|
||||
@ -184,11 +184,16 @@ EXPORT_SYMBOL_GPL(usb_find_interface);
|
||||
static void usb_release_dev(struct device *dev)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
struct usb_hcd *hcd;
|
||||
|
||||
udev = to_usb_device(dev);
|
||||
hcd = bus_to_hcd(udev->bus);
|
||||
|
||||
usb_destroy_configuration(udev);
|
||||
usb_put_hcd(bus_to_hcd(udev->bus));
|
||||
/* Root hubs aren't real devices, so don't free HCD resources */
|
||||
if (hcd->driver->free_dev && udev->parent)
|
||||
hcd->driver->free_dev(hcd, udev);
|
||||
usb_put_hcd(hcd);
|
||||
kfree(udev->product);
|
||||
kfree(udev->manufacturer);
|
||||
kfree(udev->serial);
|
||||
@ -359,6 +364,13 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
|
||||
kfree(dev);
|
||||
return NULL;
|
||||
}
|
||||
/* Root hubs aren't true devices, so don't allocate HCD resources */
|
||||
if (usb_hcd->driver->alloc_dev && parent &&
|
||||
!usb_hcd->driver->alloc_dev(usb_hcd, dev)) {
|
||||
usb_put_hcd(bus_to_hcd(bus));
|
||||
kfree(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
device_initialize(&dev->dev);
|
||||
dev->dev.bus = &usb_bus_type;
|
||||
@ -386,18 +398,24 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
|
||||
*/
|
||||
if (unlikely(!parent)) {
|
||||
dev->devpath[0] = '0';
|
||||
dev->route = 0;
|
||||
|
||||
dev->dev.parent = bus->controller;
|
||||
dev_set_name(&dev->dev, "usb%d", bus->busnum);
|
||||
root_hub = 1;
|
||||
} else {
|
||||
/* match any labeling on the hubs; it's one-based */
|
||||
if (parent->devpath[0] == '0')
|
||||
if (parent->devpath[0] == '0') {
|
||||
snprintf(dev->devpath, sizeof dev->devpath,
|
||||
"%d", port1);
|
||||
else
|
||||
/* Root ports are not counted in route string */
|
||||
dev->route = 0;
|
||||
} else {
|
||||
snprintf(dev->devpath, sizeof dev->devpath,
|
||||
"%s.%d", parent->devpath, port1);
|
||||
dev->route = parent->route +
|
||||
(port1 << ((parent->level - 1)*4));
|
||||
}
|
||||
|
||||
dev->dev.parent = &parent->dev;
|
||||
dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath);
|
||||
@ -810,12 +828,12 @@ void usb_buffer_dmasync(struct urb *urb)
|
||||
return;
|
||||
|
||||
if (controller->dma_mask) {
|
||||
dma_sync_single(controller,
|
||||
dma_sync_single_for_cpu(controller,
|
||||
urb->transfer_dma, urb->transfer_buffer_length,
|
||||
usb_pipein(urb->pipe)
|
||||
? DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
if (usb_pipecontrol(urb->pipe))
|
||||
dma_sync_single(controller,
|
||||
dma_sync_single_for_cpu(controller,
|
||||
urb->setup_dma,
|
||||
sizeof(struct usb_ctrlrequest),
|
||||
DMA_TO_DEVICE);
|
||||
@ -933,8 +951,8 @@ void usb_buffer_dmasync_sg(const struct usb_device *dev, int is_in,
|
||||
|| !controller->dma_mask)
|
||||
return;
|
||||
|
||||
dma_sync_sg(controller, sg, n_hw_ents,
|
||||
is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
dma_sync_sg_for_cpu(controller, sg, n_hw_ents,
|
||||
is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_buffer_dmasync_sg);
|
||||
#endif
|
||||
@ -1012,6 +1030,35 @@ static struct notifier_block usb_bus_nb = {
|
||||
.notifier_call = usb_bus_notify,
|
||||
};
|
||||
|
||||
struct dentry *usb_debug_root;
|
||||
EXPORT_SYMBOL_GPL(usb_debug_root);
|
||||
|
||||
struct dentry *usb_debug_devices;
|
||||
|
||||
static int usb_debugfs_init(void)
|
||||
{
|
||||
usb_debug_root = debugfs_create_dir("usb", NULL);
|
||||
if (!usb_debug_root)
|
||||
return -ENOENT;
|
||||
|
||||
usb_debug_devices = debugfs_create_file("devices", 0444,
|
||||
usb_debug_root, NULL,
|
||||
&usbfs_devices_fops);
|
||||
if (!usb_debug_devices) {
|
||||
debugfs_remove(usb_debug_root);
|
||||
usb_debug_root = NULL;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_debugfs_cleanup(void)
|
||||
{
|
||||
debugfs_remove(usb_debug_devices);
|
||||
debugfs_remove(usb_debug_root);
|
||||
}
|
||||
|
||||
/*
|
||||
* Init
|
||||
*/
|
||||
@ -1023,6 +1070,10 @@ static int __init usb_init(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
retval = usb_debugfs_init();
|
||||
if (retval)
|
||||
goto out;
|
||||
|
||||
retval = ksuspend_usb_init();
|
||||
if (retval)
|
||||
goto out;
|
||||
@ -1032,9 +1083,6 @@ static int __init usb_init(void)
|
||||
retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb);
|
||||
if (retval)
|
||||
goto bus_notifier_failed;
|
||||
retval = usb_host_init();
|
||||
if (retval)
|
||||
goto host_init_failed;
|
||||
retval = usb_major_init();
|
||||
if (retval)
|
||||
goto major_init_failed;
|
||||
@ -1064,8 +1112,6 @@ usb_devio_init_failed:
|
||||
driver_register_failed:
|
||||
usb_major_cleanup();
|
||||
major_init_failed:
|
||||
usb_host_cleanup();
|
||||
host_init_failed:
|
||||
bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
|
||||
bus_notifier_failed:
|
||||
bus_unregister(&usb_bus_type);
|
||||
@ -1090,10 +1136,10 @@ static void __exit usb_exit(void)
|
||||
usb_deregister(&usbfs_driver);
|
||||
usb_devio_cleanup();
|
||||
usb_hub_cleanup();
|
||||
usb_host_cleanup();
|
||||
bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
|
||||
bus_unregister(&usb_bus_type);
|
||||
ksuspend_usb_cleanup();
|
||||
usb_debugfs_cleanup();
|
||||
}
|
||||
|
||||
subsys_initcall(usb_init);
|
||||
|
@ -41,8 +41,6 @@ extern int usb_hub_init(void);
|
||||
extern void usb_hub_cleanup(void);
|
||||
extern int usb_major_init(void);
|
||||
extern void usb_major_cleanup(void);
|
||||
extern int usb_host_init(void);
|
||||
extern void usb_host_cleanup(void);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
@ -106,6 +104,7 @@ extern struct workqueue_struct *ksuspend_usb_wq;
|
||||
extern struct bus_type usb_bus_type;
|
||||
extern struct device_type usb_device_type;
|
||||
extern struct device_type usb_if_device_type;
|
||||
extern struct device_type usb_ep_device_type;
|
||||
extern struct usb_device_driver usb_generic_driver;
|
||||
|
||||
static inline int is_usb_device(const struct device *dev)
|
||||
@ -113,6 +112,16 @@ static inline int is_usb_device(const struct device *dev)
|
||||
return dev->type == &usb_device_type;
|
||||
}
|
||||
|
||||
static inline int is_usb_interface(const struct device *dev)
|
||||
{
|
||||
return dev->type == &usb_if_device_type;
|
||||
}
|
||||
|
||||
static inline int is_usb_endpoint(const struct device *dev)
|
||||
{
|
||||
return dev->type == &usb_ep_device_type;
|
||||
}
|
||||
|
||||
/* Do the same for device drivers and interface drivers. */
|
||||
|
||||
static inline int is_usb_device_driver(struct device_driver *drv)
|
||||
|
@ -156,7 +156,7 @@ config USB_ATMEL_USBA
|
||||
|
||||
config USB_GADGET_FSL_USB2
|
||||
boolean "Freescale Highspeed USB DR Peripheral Controller"
|
||||
depends on FSL_SOC
|
||||
depends on FSL_SOC || ARCH_MXC
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
Some of Freescale PowerPC processors have a High Speed
|
||||
@ -253,7 +253,7 @@ config USB_PXA25X_SMALL
|
||||
|
||||
config USB_GADGET_PXA27X
|
||||
boolean "PXA 27x"
|
||||
depends on ARCH_PXA && PXA27x
|
||||
depends on ARCH_PXA && (PXA27x || PXA3xx)
|
||||
select USB_OTG_UTILS
|
||||
help
|
||||
Intel's PXA 27x series XScale ARM v5TE processors include
|
||||
@ -272,6 +272,20 @@ config USB_PXA27X
|
||||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
config USB_GADGET_S3C_HSOTG
|
||||
boolean "S3C HS/OtG USB Device controller"
|
||||
depends on S3C_DEV_USB_HSOTG
|
||||
select USB_GADGET_S3C_HSOTG_PIO
|
||||
help
|
||||
The Samsung S3C64XX USB2.0 high-speed gadget controller
|
||||
integrated into the S3C64XX series SoC.
|
||||
|
||||
config USB_S3C_HSOTG
|
||||
tristate
|
||||
depends on USB_GADGET_S3C_HSOTG
|
||||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
config USB_GADGET_S3C2410
|
||||
boolean "S3C2410 USB Device Controller"
|
||||
depends on ARCH_S3C2410
|
||||
@ -460,6 +474,27 @@ config USB_GOKU
|
||||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
config USB_GADGET_LANGWELL
|
||||
boolean "Intel Langwell USB Device Controller"
|
||||
depends on PCI
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
Intel Langwell USB Device Controller is a High-Speed USB
|
||||
On-The-Go device controller.
|
||||
|
||||
The number of programmable endpoints is different through
|
||||
controller revision.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "langwell_udc" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_LANGWELL
|
||||
tristate
|
||||
depends on USB_GADGET_LANGWELL
|
||||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
|
||||
#
|
||||
# LAST -- dummy/emulated controller
|
||||
@ -566,6 +601,20 @@ config USB_ZERO_HNPTEST
|
||||
the "B-Peripheral" role, that device will use HNP to let this
|
||||
one serve as the USB host instead (in the "B-Host" role).
|
||||
|
||||
config USB_AUDIO
|
||||
tristate "Audio Gadget (EXPERIMENTAL)"
|
||||
depends on SND
|
||||
help
|
||||
Gadget Audio is compatible with USB Audio Class specification 1.0.
|
||||
It will include at least one AudioControl interface, zero or more
|
||||
AudioStream interface and zero or more MIDIStream interface.
|
||||
|
||||
Gadget Audio will use on-board ALSA (CONFIG_SND) audio card to
|
||||
playback or capture audio stream.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "g_audio".
|
||||
|
||||
config USB_ETH
|
||||
tristate "Ethernet Gadget (with CDC Ethernet support)"
|
||||
depends on NET
|
||||
|
@ -18,14 +18,21 @@ obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o
|
||||
obj-$(CONFIG_USB_AT91) += at91_udc.o
|
||||
obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
|
||||
obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
|
||||
fsl_usb2_udc-objs := fsl_udc_core.o
|
||||
ifeq ($(CONFIG_ARCH_MXC),y)
|
||||
fsl_usb2_udc-objs += fsl_mx3_udc.o
|
||||
endif
|
||||
obj-$(CONFIG_USB_M66592) += m66592-udc.o
|
||||
obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
|
||||
obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o
|
||||
obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o
|
||||
obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o
|
||||
|
||||
#
|
||||
# USB gadget drivers
|
||||
#
|
||||
g_zero-objs := zero.o
|
||||
g_audio-objs := audio.o
|
||||
g_ether-objs := ether.o
|
||||
g_serial-objs := serial.o
|
||||
g_midi-objs := gmidi.o
|
||||
@ -35,6 +42,7 @@ g_printer-objs := printer.o
|
||||
g_cdc-objs := cdc2.o
|
||||
|
||||
obj-$(CONFIG_USB_ZERO) += g_zero.o
|
||||
obj-$(CONFIG_USB_AUDIO) += g_audio.o
|
||||
obj-$(CONFIG_USB_ETH) += g_ether.o
|
||||
obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
|
||||
obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o
|
||||
|
@ -485,7 +485,7 @@ static int at91_ep_enable(struct usb_ep *_ep,
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
|
||||
tmp = usb_endpoint_type(desc);
|
||||
switch (tmp) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
DBG("only one control endpoint\n");
|
||||
@ -517,7 +517,7 @@ ok:
|
||||
local_irq_save(flags);
|
||||
|
||||
/* initialize endpoint to match this descriptor */
|
||||
ep->is_in = (desc->bEndpointAddress & USB_DIR_IN) != 0;
|
||||
ep->is_in = usb_endpoint_dir_in(desc);
|
||||
ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC);
|
||||
ep->stopped = 0;
|
||||
if (ep->is_in)
|
||||
|
@ -326,13 +326,7 @@ static int vbus_is_present(struct usba_udc *udc)
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_AVR32)
|
||||
|
||||
static void toggle_bias(int is_on)
|
||||
{
|
||||
}
|
||||
|
||||
#elif defined(CONFIG_ARCH_AT91)
|
||||
#if defined(CONFIG_ARCH_AT91SAM9RL)
|
||||
|
||||
#include <mach/at91_pmc.h>
|
||||
|
||||
@ -346,7 +340,13 @@ static void toggle_bias(int is_on)
|
||||
at91_sys_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN));
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ARCH_AT91 */
|
||||
#else
|
||||
|
||||
static void toggle_bias(int is_on)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ARCH_AT91SAM9RL */
|
||||
|
||||
static void next_fifo_transaction(struct usba_ep *ep, struct usba_request *req)
|
||||
{
|
||||
@ -550,12 +550,12 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
|
||||
DBG(DBG_HW, "%s: EPT_SIZE = %lu (maxpacket = %lu)\n",
|
||||
ep->ep.name, ept_cfg, maxpacket);
|
||||
|
||||
if ((desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
|
||||
if (usb_endpoint_dir_in(desc)) {
|
||||
ep->is_in = 1;
|
||||
ept_cfg |= USBA_EPT_DIR_IN;
|
||||
}
|
||||
|
||||
switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
|
||||
switch (usb_endpoint_type(desc)) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL);
|
||||
ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE);
|
||||
|
302
drivers/usb/gadget/audio.c
Normal file
302
drivers/usb/gadget/audio.c
Normal file
@ -0,0 +1,302 @@
|
||||
/*
|
||||
* audio.c -- Audio gadget driver
|
||||
*
|
||||
* Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
|
||||
* Copyright (C) 2008 Analog Devices, Inc
|
||||
*
|
||||
* Enter bugs at http://blackfin.uclinux.org/
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
/* #define VERBOSE_DEBUG */
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/utsname.h>
|
||||
|
||||
#include "u_audio.h"
|
||||
|
||||
#define DRIVER_DESC "Linux USB Audio Gadget"
|
||||
#define DRIVER_VERSION "Dec 18, 2008"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Kbuild is not very cooperative with respect to linking separately
|
||||
* compiled library objects into one module. So for now we won't use
|
||||
* separate compilation ... ensuring init/exit sections work to shrink
|
||||
* the runtime footprint, and giving us at least some parts of what
|
||||
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
|
||||
*/
|
||||
#include "composite.c"
|
||||
#include "usbstring.c"
|
||||
#include "config.c"
|
||||
#include "epautoconf.c"
|
||||
|
||||
#include "u_audio.c"
|
||||
#include "f_audio.c"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
|
||||
* Instead: allocate your own, using normal USB-IF procedures.
|
||||
*/
|
||||
|
||||
/* Thanks to NetChip Technologies for donating this product ID. */
|
||||
#define AUDIO_VENDOR_NUM 0x0525 /* NetChip */
|
||||
#define AUDIO_PRODUCT_NUM 0xa4a1 /* Linux-USB Audio Gadget */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct usb_device_descriptor device_desc = {
|
||||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = __constant_cpu_to_le16(0x200),
|
||||
|
||||
.bDeviceClass = USB_CLASS_PER_INTERFACE,
|
||||
.bDeviceSubClass = 0,
|
||||
.bDeviceProtocol = 0,
|
||||
/* .bMaxPacketSize0 = f(hardware) */
|
||||
|
||||
/* Vendor and product id defaults change according to what configs
|
||||
* we support. (As does bNumConfigurations.) These values can
|
||||
* also be overridden by module parameters.
|
||||
*/
|
||||
.idVendor = __constant_cpu_to_le16(AUDIO_VENDOR_NUM),
|
||||
.idProduct = __constant_cpu_to_le16(AUDIO_PRODUCT_NUM),
|
||||
/* .bcdDevice = f(hardware) */
|
||||
/* .iManufacturer = DYNAMIC */
|
||||
/* .iProduct = DYNAMIC */
|
||||
/* NO SERIAL NUMBER */
|
||||
.bNumConfigurations = 1,
|
||||
};
|
||||
|
||||
static struct usb_otg_descriptor otg_descriptor = {
|
||||
.bLength = sizeof otg_descriptor,
|
||||
.bDescriptorType = USB_DT_OTG,
|
||||
|
||||
/* REVISIT SRP-only hardware is possible, although
|
||||
* it would not be called "OTG" ...
|
||||
*/
|
||||
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
|
||||
};
|
||||
|
||||
static const struct usb_descriptor_header *otg_desc[] = {
|
||||
(struct usb_descriptor_header *) &otg_descriptor,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Handle USB audio endpoint set/get command in setup class request
|
||||
*/
|
||||
|
||||
static int audio_set_endpoint_req(struct usb_configuration *c,
|
||||
const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
int value = -EOPNOTSUPP;
|
||||
u16 ep = le16_to_cpu(ctrl->wIndex);
|
||||
u16 len = le16_to_cpu(ctrl->wLength);
|
||||
u16 w_value = le16_to_cpu(ctrl->wValue);
|
||||
|
||||
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
|
||||
ctrl->bRequest, w_value, len, ep);
|
||||
|
||||
switch (ctrl->bRequest) {
|
||||
case SET_CUR:
|
||||
value = 0;
|
||||
break;
|
||||
|
||||
case SET_MIN:
|
||||
break;
|
||||
|
||||
case SET_MAX:
|
||||
break;
|
||||
|
||||
case SET_RES:
|
||||
break;
|
||||
|
||||
case SET_MEM:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static int audio_get_endpoint_req(struct usb_configuration *c,
|
||||
const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
int value = -EOPNOTSUPP;
|
||||
u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
|
||||
u16 len = le16_to_cpu(ctrl->wLength);
|
||||
u16 w_value = le16_to_cpu(ctrl->wValue);
|
||||
|
||||
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
|
||||
ctrl->bRequest, w_value, len, ep);
|
||||
|
||||
switch (ctrl->bRequest) {
|
||||
case GET_CUR:
|
||||
case GET_MIN:
|
||||
case GET_MAX:
|
||||
case GET_RES:
|
||||
value = 3;
|
||||
break;
|
||||
case GET_MEM:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static int
|
||||
audio_setup(struct usb_configuration *c, const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct usb_request *req = cdev->req;
|
||||
int value = -EOPNOTSUPP;
|
||||
u16 w_index = le16_to_cpu(ctrl->wIndex);
|
||||
u16 w_value = le16_to_cpu(ctrl->wValue);
|
||||
u16 w_length = le16_to_cpu(ctrl->wLength);
|
||||
|
||||
/* composite driver infrastructure handles everything except
|
||||
* Audio class messages; interface activation uses set_alt().
|
||||
*/
|
||||
switch (ctrl->bRequestType) {
|
||||
case USB_AUDIO_SET_ENDPOINT:
|
||||
value = audio_set_endpoint_req(c, ctrl);
|
||||
break;
|
||||
|
||||
case USB_AUDIO_GET_ENDPOINT:
|
||||
value = audio_get_endpoint_req(c, ctrl);
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR(cdev, "Invalid control req%02x.%02x v%04x i%04x l%d\n",
|
||||
ctrl->bRequestType, ctrl->bRequest,
|
||||
w_value, w_index, w_length);
|
||||
}
|
||||
|
||||
/* respond with data transfer or status phase? */
|
||||
if (value >= 0) {
|
||||
DBG(cdev, "Audio req%02x.%02x v%04x i%04x l%d\n",
|
||||
ctrl->bRequestType, ctrl->bRequest,
|
||||
w_value, w_index, w_length);
|
||||
req->zero = 0;
|
||||
req->length = value;
|
||||
value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
|
||||
if (value < 0)
|
||||
ERROR(cdev, "Audio response on err %d\n", value);
|
||||
}
|
||||
|
||||
/* device either stalls (value < 0) or reports success */
|
||||
return value;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init audio_do_config(struct usb_configuration *c)
|
||||
{
|
||||
/* FIXME alloc iConfiguration string, set it in c->strings */
|
||||
|
||||
if (gadget_is_otg(c->cdev->gadget)) {
|
||||
c->descriptors = otg_desc;
|
||||
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
}
|
||||
|
||||
audio_bind_config(c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_configuration audio_config_driver = {
|
||||
.label = DRIVER_DESC,
|
||||
.bind = audio_do_config,
|
||||
.setup = audio_setup,
|
||||
.bConfigurationValue = 1,
|
||||
/* .iConfiguration = DYNAMIC */
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init audio_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int gcnum;
|
||||
int status;
|
||||
|
||||
gcnum = usb_gadget_controller_number(cdev->gadget);
|
||||
if (gcnum >= 0)
|
||||
device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
|
||||
else {
|
||||
ERROR(cdev, "controller '%s' not recognized; trying %s\n",
|
||||
cdev->gadget->name,
|
||||
audio_config_driver.label);
|
||||
device_desc.bcdDevice =
|
||||
__constant_cpu_to_le16(0x0300 | 0x0099);
|
||||
}
|
||||
|
||||
/* device descriptor strings: manufacturer, product */
|
||||
snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
|
||||
init_utsname()->sysname, init_utsname()->release,
|
||||
cdev->gadget->name);
|
||||
status = usb_string_id(cdev);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
strings_dev[STRING_MANUFACTURER_IDX].id = status;
|
||||
device_desc.iManufacturer = status;
|
||||
|
||||
status = usb_string_id(cdev);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
strings_dev[STRING_PRODUCT_IDX].id = status;
|
||||
device_desc.iProduct = status;
|
||||
|
||||
status = usb_add_config(cdev, &audio_config_driver);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
|
||||
INFO(cdev, "%s, version: %s\n", DRIVER_DESC, DRIVER_VERSION);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int __exit audio_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_composite_driver audio_driver = {
|
||||
.name = "g_audio",
|
||||
.dev = &device_desc,
|
||||
.strings = audio_strings,
|
||||
.bind = audio_bind,
|
||||
.unbind = __exit_p(audio_unbind),
|
||||
};
|
||||
|
||||
static int __init init(void)
|
||||
{
|
||||
return usb_composite_register(&audio_driver);
|
||||
}
|
||||
module_init(init);
|
||||
|
||||
static void __exit cleanup(void)
|
||||
{
|
||||
usb_composite_unregister(&audio_driver);
|
||||
}
|
||||
module_exit(cleanup);
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_AUTHOR("Bryan Wu <cooloney@kernel.org>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -1977,9 +1977,9 @@ static int ep_enable(struct usb_ep *ep,
|
||||
if (!list_empty(&mEp->qh[mEp->dir].queue))
|
||||
warn("enabling a non-empty endpoint!");
|
||||
|
||||
mEp->dir = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? TX : RX;
|
||||
mEp->num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
|
||||
mEp->type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
|
||||
mEp->dir = usb_endpoint_dir_in(desc) ? TX : RX;
|
||||
mEp->num = usb_endpoint_num(desc);
|
||||
mEp->type = usb_endpoint_type(desc);
|
||||
|
||||
mEp->ep.maxpacket = __constant_le16_to_cpu(desc->wMaxPacketSize);
|
||||
|
||||
|
707
drivers/usb/gadget/f_audio.c
Normal file
707
drivers/usb/gadget/f_audio.c
Normal file
@ -0,0 +1,707 @@
|
||||
/*
|
||||
* f_audio.c -- USB Audio class function driver
|
||||
*
|
||||
* Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
|
||||
* Copyright (C) 2008 Analog Devices, Inc
|
||||
*
|
||||
* Enter bugs at http://blackfin.uclinux.org/
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <asm/atomic.h>
|
||||
|
||||
#include "u_audio.h"
|
||||
|
||||
#define OUT_EP_MAX_PACKET_SIZE 200
|
||||
static int req_buf_size = OUT_EP_MAX_PACKET_SIZE;
|
||||
module_param(req_buf_size, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size");
|
||||
|
||||
static int req_count = 256;
|
||||
module_param(req_count, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count");
|
||||
|
||||
static int audio_buf_size = 48000;
|
||||
module_param(audio_buf_size, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(audio_buf_size, "Audio buffer size");
|
||||
|
||||
/*
|
||||
* DESCRIPTORS ... most are static, but strings and full
|
||||
* configuration descriptors are built on demand.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We have two interfaces- AudioControl and AudioStreaming
|
||||
* TODO: only supcard playback currently
|
||||
*/
|
||||
#define F_AUDIO_AC_INTERFACE 0
|
||||
#define F_AUDIO_AS_INTERFACE 1
|
||||
#define F_AUDIO_NUM_INTERFACES 2
|
||||
|
||||
/* B.3.1 Standard AC Interface Descriptor */
|
||||
static struct usb_interface_descriptor ac_interface_desc __initdata = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bNumEndpoints = 0,
|
||||
.bInterfaceClass = USB_CLASS_AUDIO,
|
||||
.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
|
||||
};
|
||||
|
||||
DECLARE_USB_AC_HEADER_DESCRIPTOR(2);
|
||||
|
||||
#define USB_DT_AC_HEADER_LENGH USB_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,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubtype = HEADER,
|
||||
.bcdADC = __constant_cpu_to_le16(0x0100),
|
||||
.wTotalLength = __constant_cpu_to_le16(USB_DT_AC_HEADER_LENGH),
|
||||
.bInCollection = F_AUDIO_NUM_INTERFACES,
|
||||
.baInterfaceNr = {
|
||||
[0] = F_AUDIO_AC_INTERFACE,
|
||||
[1] = F_AUDIO_AS_INTERFACE,
|
||||
}
|
||||
};
|
||||
|
||||
#define INPUT_TERMINAL_ID 1
|
||||
static struct usb_input_terminal_descriptor input_terminal_desc = {
|
||||
.bLength = USB_DT_AC_INPUT_TERMINAL_SIZE,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubtype = INPUT_TERMINAL,
|
||||
.bTerminalID = INPUT_TERMINAL_ID,
|
||||
.wTerminalType = USB_AC_TERMINAL_STREAMING,
|
||||
.bAssocTerminal = 0,
|
||||
.wChannelConfig = 0x3,
|
||||
};
|
||||
|
||||
DECLARE_USB_AC_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),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubtype = FEATURE_UNIT,
|
||||
.bUnitID = FEATURE_UNIT_ID,
|
||||
.bSourceID = INPUT_TERMINAL_ID,
|
||||
.bControlSize = 2,
|
||||
.bmaControls[0] = (FU_MUTE | FU_VOLUME),
|
||||
};
|
||||
|
||||
static struct usb_audio_control mute_control = {
|
||||
.list = LIST_HEAD_INIT(mute_control.list),
|
||||
.name = "Mute Control",
|
||||
.type = MUTE_CONTROL,
|
||||
/* Todo: add real Mute control code */
|
||||
.set = generic_set_cmd,
|
||||
.get = generic_get_cmd,
|
||||
};
|
||||
|
||||
static struct usb_audio_control volume_control = {
|
||||
.list = LIST_HEAD_INIT(volume_control.list),
|
||||
.name = "Volume Control",
|
||||
.type = VOLUME_CONTROL,
|
||||
/* Todo: add real Volume control code */
|
||||
.set = generic_set_cmd,
|
||||
.get = generic_get_cmd,
|
||||
};
|
||||
|
||||
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,
|
||||
.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,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubtype = OUTPUT_TERMINAL,
|
||||
.bTerminalID = OUTPUT_TERMINAL_ID,
|
||||
.wTerminalType = USB_AC_OUTPUT_TERMINAL_SPEAKER,
|
||||
.bAssocTerminal = FEATURE_UNIT_ID,
|
||||
.bSourceID = FEATURE_UNIT_ID,
|
||||
};
|
||||
|
||||
/* B.4.1 Standard AS Interface Descriptor */
|
||||
static struct usb_interface_descriptor as_interface_alt_0_desc = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bAlternateSetting = 0,
|
||||
.bNumEndpoints = 0,
|
||||
.bInterfaceClass = USB_CLASS_AUDIO,
|
||||
.bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
|
||||
};
|
||||
|
||||
static struct usb_interface_descriptor as_interface_alt_1_desc = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bAlternateSetting = 1,
|
||||
.bNumEndpoints = 1,
|
||||
.bInterfaceClass = USB_CLASS_AUDIO,
|
||||
.bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
|
||||
};
|
||||
|
||||
/* B.4.2 Class-Specific AS Interface Descriptor */
|
||||
static struct usb_as_header_descriptor as_header_desc = {
|
||||
.bLength = USB_DT_AS_HEADER_SIZE,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubtype = AS_GENERAL,
|
||||
.bTerminalLink = INPUT_TERMINAL_ID,
|
||||
.bDelay = 1,
|
||||
.wFormatTag = USB_AS_AUDIO_FORMAT_TYPE_I_PCM,
|
||||
};
|
||||
|
||||
DECLARE_USB_AS_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),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubtype = FORMAT_TYPE,
|
||||
.bFormatType = USB_AS_FORMAT_TYPE_I,
|
||||
.bSubframeSize = 2,
|
||||
.bBitResolution = 16,
|
||||
.bSamFreqType = 1,
|
||||
};
|
||||
|
||||
/* Standard ISO OUT Endpoint Descriptor */
|
||||
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
|
||||
| USB_ENDPOINT_XFER_ISOC,
|
||||
.wMaxPacketSize = __constant_cpu_to_le16(OUT_EP_MAX_PACKET_SIZE),
|
||||
.bInterval = 4,
|
||||
};
|
||||
|
||||
/* 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,
|
||||
.bDescriptorType = USB_DT_CS_ENDPOINT,
|
||||
.bDescriptorSubtype = EP_GENERAL,
|
||||
.bmAttributes = 1,
|
||||
.bLockDelayUnits = 1,
|
||||
.wLockDelay = __constant_cpu_to_le16(1),
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *f_audio_desc[] __initdata = {
|
||||
(struct usb_descriptor_header *)&ac_interface_desc,
|
||||
(struct usb_descriptor_header *)&ac_header_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&input_terminal_desc,
|
||||
(struct usb_descriptor_header *)&output_terminal_desc,
|
||||
(struct usb_descriptor_header *)&feature_unit_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&as_interface_alt_0_desc,
|
||||
(struct usb_descriptor_header *)&as_interface_alt_1_desc,
|
||||
(struct usb_descriptor_header *)&as_header_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&as_type_i_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&as_out_ep_desc,
|
||||
(struct usb_descriptor_header *)&as_iso_out_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* string IDs are assigned dynamically */
|
||||
|
||||
#define STRING_MANUFACTURER_IDX 0
|
||||
#define STRING_PRODUCT_IDX 1
|
||||
|
||||
static char manufacturer[50];
|
||||
|
||||
static struct usb_string strings_dev[] = {
|
||||
[STRING_MANUFACTURER_IDX].s = manufacturer,
|
||||
[STRING_PRODUCT_IDX].s = DRIVER_DESC,
|
||||
{ } /* end of list */
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings stringtab_dev = {
|
||||
.language = 0x0409, /* en-us */
|
||||
.strings = strings_dev,
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings *audio_strings[] = {
|
||||
&stringtab_dev,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/*
|
||||
* This function is an ALSA sound card following USB Audio Class Spec 1.0.
|
||||
*/
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
struct f_audio_buf {
|
||||
u8 *buf;
|
||||
int actual;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static struct f_audio_buf *f_audio_buffer_alloc(int buf_size)
|
||||
{
|
||||
struct f_audio_buf *copy_buf;
|
||||
|
||||
copy_buf = kzalloc(sizeof *copy_buf, GFP_ATOMIC);
|
||||
if (!copy_buf)
|
||||
return (struct f_audio_buf *)-ENOMEM;
|
||||
|
||||
copy_buf->buf = kzalloc(buf_size, GFP_ATOMIC);
|
||||
if (!copy_buf->buf) {
|
||||
kfree(copy_buf);
|
||||
return (struct f_audio_buf *)-ENOMEM;
|
||||
}
|
||||
|
||||
return copy_buf;
|
||||
}
|
||||
|
||||
static void f_audio_buffer_free(struct f_audio_buf *audio_buf)
|
||||
{
|
||||
kfree(audio_buf->buf);
|
||||
kfree(audio_buf);
|
||||
}
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
struct f_audio {
|
||||
struct gaudio card;
|
||||
|
||||
/* endpoints handle full and/or high speeds */
|
||||
struct usb_ep *out_ep;
|
||||
struct usb_endpoint_descriptor *out_desc;
|
||||
|
||||
spinlock_t lock;
|
||||
struct f_audio_buf *copy_buf;
|
||||
struct work_struct playback_work;
|
||||
struct list_head play_queue;
|
||||
|
||||
/* Control Set command */
|
||||
struct list_head cs;
|
||||
u8 set_cmd;
|
||||
struct usb_audio_control *set_con;
|
||||
};
|
||||
|
||||
static inline struct f_audio *func_to_audio(struct usb_function *f)
|
||||
{
|
||||
return container_of(f, struct f_audio, card.func);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void f_audio_playback_work(struct work_struct *data)
|
||||
{
|
||||
struct f_audio *audio = container_of(data, struct f_audio,
|
||||
playback_work);
|
||||
struct f_audio_buf *play_buf;
|
||||
|
||||
spin_lock_irq(&audio->lock);
|
||||
if (list_empty(&audio->play_queue)) {
|
||||
spin_unlock_irq(&audio->lock);
|
||||
return;
|
||||
}
|
||||
play_buf = list_first_entry(&audio->play_queue,
|
||||
struct f_audio_buf, list);
|
||||
list_del(&play_buf->list);
|
||||
spin_unlock_irq(&audio->lock);
|
||||
|
||||
u_audio_playback(&audio->card, play_buf->buf, play_buf->actual);
|
||||
f_audio_buffer_free(play_buf);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct f_audio *audio = req->context;
|
||||
struct usb_composite_dev *cdev = audio->card.func.config->cdev;
|
||||
struct f_audio_buf *copy_buf = audio->copy_buf;
|
||||
int err;
|
||||
|
||||
if (!copy_buf)
|
||||
return -EINVAL;
|
||||
|
||||
/* Copy buffer is full, add it to the play_queue */
|
||||
if (audio_buf_size - copy_buf->actual < req->actual) {
|
||||
list_add_tail(©_buf->list, &audio->play_queue);
|
||||
schedule_work(&audio->playback_work);
|
||||
copy_buf = f_audio_buffer_alloc(audio_buf_size);
|
||||
if (copy_buf < 0)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(copy_buf->buf + copy_buf->actual, req->buf, req->actual);
|
||||
copy_buf->actual += req->actual;
|
||||
audio->copy_buf = copy_buf;
|
||||
|
||||
err = usb_ep_queue(ep, req, GFP_ATOMIC);
|
||||
if (err)
|
||||
ERROR(cdev, "%s queue req: %d\n", ep->name, err);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static void f_audio_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct f_audio *audio = req->context;
|
||||
int status = req->status;
|
||||
u32 data = 0;
|
||||
struct usb_ep *out_ep = audio->out_ep;
|
||||
|
||||
switch (status) {
|
||||
|
||||
case 0: /* normal completion? */
|
||||
if (ep == out_ep)
|
||||
f_audio_out_ep_complete(ep, req);
|
||||
else if (audio->set_con) {
|
||||
memcpy(&data, req->buf, req->length);
|
||||
audio->set_con->set(audio->set_con, audio->set_cmd,
|
||||
le16_to_cpu(data));
|
||||
audio->set_con = NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int audio_set_intf_req(struct usb_function *f,
|
||||
const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct f_audio *audio = func_to_audio(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
struct usb_request *req = cdev->req;
|
||||
u8 id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
|
||||
u16 len = le16_to_cpu(ctrl->wLength);
|
||||
u16 w_value = le16_to_cpu(ctrl->wValue);
|
||||
u8 con_sel = (w_value >> 8) & 0xFF;
|
||||
u8 cmd = (ctrl->bRequest & 0x0F);
|
||||
struct usb_audio_control_selector *cs;
|
||||
struct usb_audio_control *con;
|
||||
|
||||
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n",
|
||||
ctrl->bRequest, w_value, len, id);
|
||||
|
||||
list_for_each_entry(cs, &audio->cs, list) {
|
||||
if (cs->id == id) {
|
||||
list_for_each_entry(con, &cs->control, list) {
|
||||
if (con->type == con_sel) {
|
||||
audio->set_con = con;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
audio->set_cmd = cmd;
|
||||
req->context = audio;
|
||||
req->complete = f_audio_complete;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int audio_get_intf_req(struct usb_function *f,
|
||||
const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct f_audio *audio = func_to_audio(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
struct usb_request *req = cdev->req;
|
||||
int value = -EOPNOTSUPP;
|
||||
u8 id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
|
||||
u16 len = le16_to_cpu(ctrl->wLength);
|
||||
u16 w_value = le16_to_cpu(ctrl->wValue);
|
||||
u8 con_sel = (w_value >> 8) & 0xFF;
|
||||
u8 cmd = (ctrl->bRequest & 0x0F);
|
||||
struct usb_audio_control_selector *cs;
|
||||
struct usb_audio_control *con;
|
||||
|
||||
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n",
|
||||
ctrl->bRequest, w_value, len, id);
|
||||
|
||||
list_for_each_entry(cs, &audio->cs, list) {
|
||||
if (cs->id == id) {
|
||||
list_for_each_entry(con, &cs->control, list) {
|
||||
if (con->type == con_sel && con->get) {
|
||||
value = con->get(con, cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
req->context = audio;
|
||||
req->complete = f_audio_complete;
|
||||
memcpy(req->buf, &value, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int
|
||||
f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
struct usb_request *req = cdev->req;
|
||||
int value = -EOPNOTSUPP;
|
||||
u16 w_index = le16_to_cpu(ctrl->wIndex);
|
||||
u16 w_value = le16_to_cpu(ctrl->wValue);
|
||||
u16 w_length = le16_to_cpu(ctrl->wLength);
|
||||
|
||||
/* composite driver infrastructure handles everything except
|
||||
* Audio class messages; interface activation uses set_alt().
|
||||
*/
|
||||
switch (ctrl->bRequestType) {
|
||||
case USB_AUDIO_SET_INTF:
|
||||
value = audio_set_intf_req(f, ctrl);
|
||||
break;
|
||||
|
||||
case USB_AUDIO_GET_INTF:
|
||||
value = audio_get_intf_req(f, ctrl);
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
|
||||
ctrl->bRequestType, ctrl->bRequest,
|
||||
w_value, w_index, w_length);
|
||||
}
|
||||
|
||||
/* respond with data transfer or status phase? */
|
||||
if (value >= 0) {
|
||||
DBG(cdev, "audio req%02x.%02x v%04x i%04x l%d\n",
|
||||
ctrl->bRequestType, ctrl->bRequest,
|
||||
w_value, w_index, w_length);
|
||||
req->zero = 0;
|
||||
req->length = value;
|
||||
value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
|
||||
if (value < 0)
|
||||
ERROR(cdev, "audio response on err %d\n", value);
|
||||
}
|
||||
|
||||
/* device either stalls (value < 0) or reports success */
|
||||
return value;
|
||||
}
|
||||
|
||||
static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
||||
{
|
||||
struct f_audio *audio = func_to_audio(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
struct usb_ep *out_ep = audio->out_ep;
|
||||
struct usb_request *req;
|
||||
int i = 0, err = 0;
|
||||
|
||||
DBG(cdev, "intf %d, alt %d\n", intf, alt);
|
||||
|
||||
if (intf == 1) {
|
||||
if (alt == 1) {
|
||||
usb_ep_enable(out_ep, audio->out_desc);
|
||||
out_ep->driver_data = audio;
|
||||
audio->copy_buf = f_audio_buffer_alloc(audio_buf_size);
|
||||
|
||||
/*
|
||||
* allocate a bunch of read buffers
|
||||
* and queue them all at once.
|
||||
*/
|
||||
for (i = 0; i < req_count && err == 0; i++) {
|
||||
req = usb_ep_alloc_request(out_ep, GFP_ATOMIC);
|
||||
if (req) {
|
||||
req->buf = kzalloc(req_buf_size,
|
||||
GFP_ATOMIC);
|
||||
if (req->buf) {
|
||||
req->length = req_buf_size;
|
||||
req->context = audio;
|
||||
req->complete =
|
||||
f_audio_complete;
|
||||
err = usb_ep_queue(out_ep,
|
||||
req, GFP_ATOMIC);
|
||||
if (err)
|
||||
ERROR(cdev,
|
||||
"%s queue req: %d\n",
|
||||
out_ep->name, err);
|
||||
} else
|
||||
err = -ENOMEM;
|
||||
} else
|
||||
err = -ENOMEM;
|
||||
}
|
||||
|
||||
} else {
|
||||
struct f_audio_buf *copy_buf = audio->copy_buf;
|
||||
if (copy_buf) {
|
||||
list_add_tail(©_buf->list,
|
||||
&audio->play_queue);
|
||||
schedule_work(&audio->playback_work);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void f_audio_disable(struct usb_function *f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void f_audio_build_desc(struct f_audio *audio)
|
||||
{
|
||||
struct gaudio *card = &audio->card;
|
||||
u8 *sam_freq;
|
||||
int rate;
|
||||
|
||||
/* Set channel numbers */
|
||||
input_terminal_desc.bNrChannels = u_audio_get_playback_channels(card);
|
||||
as_type_i_desc.bNrChannels = u_audio_get_playback_channels(card);
|
||||
|
||||
/* Set sample rates */
|
||||
rate = u_audio_get_playback_rate(card);
|
||||
sam_freq = as_type_i_desc.tSamFreq[0];
|
||||
memcpy(sam_freq, &rate, 3);
|
||||
|
||||
/* Todo: Set Sample bits and other parameters */
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* audio function driver setup/binding */
|
||||
static int __init
|
||||
f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct f_audio *audio = func_to_audio(f);
|
||||
int status;
|
||||
struct usb_ep *ep;
|
||||
|
||||
f_audio_build_desc(audio);
|
||||
|
||||
/* allocate instance-specific interface IDs, and patch descriptors */
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
ac_interface_desc.bInterfaceNumber = status;
|
||||
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
as_interface_alt_0_desc.bInterfaceNumber = status;
|
||||
as_interface_alt_1_desc.bInterfaceNumber = status;
|
||||
|
||||
status = -ENODEV;
|
||||
|
||||
/* allocate instance-specific endpoints */
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc);
|
||||
if (!ep)
|
||||
goto fail;
|
||||
audio->out_ep = ep;
|
||||
ep->driver_data = cdev; /* claim */
|
||||
|
||||
status = -ENOMEM;
|
||||
|
||||
/* supcard all relevant hardware speeds... we expect that when
|
||||
* hardware is dual speed, all bulk-capable endpoints work at
|
||||
* both speeds
|
||||
*/
|
||||
|
||||
/* copy descriptors, and track endpoint copies */
|
||||
if (gadget_is_dualspeed(c->cdev->gadget)) {
|
||||
c->highspeed = true;
|
||||
f->hs_descriptors = usb_copy_descriptors(f_audio_desc);
|
||||
} else
|
||||
f->descriptors = usb_copy_descriptors(f_audio_desc);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void
|
||||
f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct f_audio *audio = func_to_audio(f);
|
||||
|
||||
usb_free_descriptors(f->descriptors);
|
||||
kfree(audio);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* Todo: add more control selecotor dynamically */
|
||||
int __init control_selector_init(struct f_audio *audio)
|
||||
{
|
||||
INIT_LIST_HEAD(&audio->cs);
|
||||
list_add(&feature_unit.list, &audio->cs);
|
||||
|
||||
INIT_LIST_HEAD(&feature_unit.control);
|
||||
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;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* audio_bind_config - add USB audio fucntion to a configuration
|
||||
* @c: the configuration to supcard the USB audio function
|
||||
* Context: single threaded during gadget setup
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*/
|
||||
int __init audio_bind_config(struct usb_configuration *c)
|
||||
{
|
||||
struct f_audio *audio;
|
||||
int status;
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
audio = kzalloc(sizeof *audio, GFP_KERNEL);
|
||||
if (!audio)
|
||||
return -ENOMEM;
|
||||
|
||||
audio->card.func.name = "g_audio";
|
||||
audio->card.gadget = c->cdev->gadget;
|
||||
|
||||
INIT_LIST_HEAD(&audio->play_queue);
|
||||
spin_lock_init(&audio->lock);
|
||||
|
||||
/* set up ASLA audio devices */
|
||||
status = gaudio_setup(&audio->card);
|
||||
if (status < 0)
|
||||
goto setup_fail;
|
||||
|
||||
audio->card.func.strings = audio_strings;
|
||||
audio->card.func.bind = f_audio_bind;
|
||||
audio->card.func.unbind = f_audio_unbind;
|
||||
audio->card.func.set_alt = f_audio_set_alt;
|
||||
audio->card.func.setup = f_audio_setup;
|
||||
audio->card.func.disable = f_audio_disable;
|
||||
audio->out_desc = &as_out_ep_desc;
|
||||
|
||||
control_selector_init(audio);
|
||||
|
||||
INIT_WORK(&audio->playback_work, f_audio_playback_work);
|
||||
|
||||
status = usb_add_function(c, &audio->card.func);
|
||||
if (status)
|
||||
goto add_fail;
|
||||
|
||||
INFO(c->cdev, "audio_buf_size %d, req_buf_size %d, req_count %d\n",
|
||||
audio_buf_size, req_buf_size, req_count);
|
||||
|
||||
return status;
|
||||
|
||||
add_fail:
|
||||
gaudio_cleanup(&audio->card);
|
||||
setup_fail:
|
||||
kfree(audio);
|
||||
return status;
|
||||
}
|
@ -475,7 +475,9 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
||||
if (rndis->port.in_ep->driver_data) {
|
||||
DBG(cdev, "reset rndis\n");
|
||||
gether_disconnect(&rndis->port);
|
||||
} else {
|
||||
}
|
||||
|
||||
if (!rndis->port.in) {
|
||||
DBG(cdev, "init rndis\n");
|
||||
rndis->port.in = ep_choose(cdev->gadget,
|
||||
rndis->hs.in, rndis->fs.in);
|
||||
|
@ -248,6 +248,8 @@
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/utsname.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
@ -799,29 +801,9 @@ static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
|
||||
|
||||
/* Routines for unaligned data access */
|
||||
|
||||
static u16 get_be16(u8 *buf)
|
||||
static u32 get_unaligned_be24(u8 *buf)
|
||||
{
|
||||
return ((u16) buf[0] << 8) | ((u16) buf[1]);
|
||||
}
|
||||
|
||||
static u32 get_be32(u8 *buf)
|
||||
{
|
||||
return ((u32) buf[0] << 24) | ((u32) buf[1] << 16) |
|
||||
((u32) buf[2] << 8) | ((u32) buf[3]);
|
||||
}
|
||||
|
||||
static void put_be16(u8 *buf, u16 val)
|
||||
{
|
||||
buf[0] = val >> 8;
|
||||
buf[1] = val;
|
||||
}
|
||||
|
||||
static void put_be32(u8 *buf, u32 val)
|
||||
{
|
||||
buf[0] = val >> 24;
|
||||
buf[1] = val >> 16;
|
||||
buf[2] = val >> 8;
|
||||
buf[3] = val & 0xff;
|
||||
return 0xffffff & (u32) get_unaligned_be32(buf - 1);
|
||||
}
|
||||
|
||||
|
||||
@ -1582,9 +1564,9 @@ static int do_read(struct fsg_dev *fsg)
|
||||
/* Get the starting Logical Block Address and check that it's
|
||||
* not too big */
|
||||
if (fsg->cmnd[0] == SC_READ_6)
|
||||
lba = (fsg->cmnd[1] << 16) | get_be16(&fsg->cmnd[2]);
|
||||
lba = get_unaligned_be24(&fsg->cmnd[1]);
|
||||
else {
|
||||
lba = get_be32(&fsg->cmnd[2]);
|
||||
lba = get_unaligned_be32(&fsg->cmnd[2]);
|
||||
|
||||
/* We allow DPO (Disable Page Out = don't save data in the
|
||||
* cache) and FUA (Force Unit Access = don't read from the
|
||||
@ -1717,9 +1699,9 @@ static int do_write(struct fsg_dev *fsg)
|
||||
/* Get the starting Logical Block Address and check that it's
|
||||
* not too big */
|
||||
if (fsg->cmnd[0] == SC_WRITE_6)
|
||||
lba = (fsg->cmnd[1] << 16) | get_be16(&fsg->cmnd[2]);
|
||||
lba = get_unaligned_be24(&fsg->cmnd[1]);
|
||||
else {
|
||||
lba = get_be32(&fsg->cmnd[2]);
|
||||
lba = get_unaligned_be32(&fsg->cmnd[2]);
|
||||
|
||||
/* We allow DPO (Disable Page Out = don't save data in the
|
||||
* cache) and FUA (Force Unit Access = write directly to the
|
||||
@ -1940,7 +1922,7 @@ static int do_verify(struct fsg_dev *fsg)
|
||||
|
||||
/* Get the starting Logical Block Address and check that it's
|
||||
* not too big */
|
||||
lba = get_be32(&fsg->cmnd[2]);
|
||||
lba = get_unaligned_be32(&fsg->cmnd[2]);
|
||||
if (lba >= curlun->num_sectors) {
|
||||
curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
|
||||
return -EINVAL;
|
||||
@ -1953,7 +1935,7 @@ static int do_verify(struct fsg_dev *fsg)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
verification_length = get_be16(&fsg->cmnd[7]);
|
||||
verification_length = get_unaligned_be16(&fsg->cmnd[7]);
|
||||
if (unlikely(verification_length == 0))
|
||||
return -EIO; // No default reply
|
||||
|
||||
@ -2103,7 +2085,7 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
|
||||
memset(buf, 0, 18);
|
||||
buf[0] = valid | 0x70; // Valid, current error
|
||||
buf[2] = SK(sd);
|
||||
put_be32(&buf[3], sdinfo); // Sense information
|
||||
put_unaligned_be32(sdinfo, &buf[3]); /* Sense information */
|
||||
buf[7] = 18 - 8; // Additional sense length
|
||||
buf[12] = ASC(sd);
|
||||
buf[13] = ASCQ(sd);
|
||||
@ -2114,7 +2096,7 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
|
||||
static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh)
|
||||
{
|
||||
struct lun *curlun = fsg->curlun;
|
||||
u32 lba = get_be32(&fsg->cmnd[2]);
|
||||
u32 lba = get_unaligned_be32(&fsg->cmnd[2]);
|
||||
int pmi = fsg->cmnd[8];
|
||||
u8 *buf = (u8 *) bh->buf;
|
||||
|
||||
@ -2124,8 +2106,9 @@ static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
put_be32(&buf[0], curlun->num_sectors - 1); // Max logical block
|
||||
put_be32(&buf[4], 512); // Block length
|
||||
put_unaligned_be32(curlun->num_sectors - 1, &buf[0]);
|
||||
/* Max logical block */
|
||||
put_unaligned_be32(512, &buf[4]); /* Block length */
|
||||
return 8;
|
||||
}
|
||||
|
||||
@ -2144,7 +2127,7 @@ static void store_cdrom_address(u8 *dest, int msf, u32 addr)
|
||||
dest[0] = 0; /* Reserved */
|
||||
} else {
|
||||
/* Absolute sector */
|
||||
put_be32(dest, addr);
|
||||
put_unaligned_be32(addr, dest);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2152,7 +2135,7 @@ static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh)
|
||||
{
|
||||
struct lun *curlun = fsg->curlun;
|
||||
int msf = fsg->cmnd[1] & 0x02;
|
||||
u32 lba = get_be32(&fsg->cmnd[2]);
|
||||
u32 lba = get_unaligned_be32(&fsg->cmnd[2]);
|
||||
u8 *buf = (u8 *) bh->buf;
|
||||
|
||||
if ((fsg->cmnd[1] & ~0x02) != 0) { /* Mask away MSF */
|
||||
@ -2252,10 +2235,13 @@ static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
|
||||
buf[2] = 0x04; // Write cache enable,
|
||||
// Read cache not disabled
|
||||
// No cache retention priorities
|
||||
put_be16(&buf[4], 0xffff); // Don't disable prefetch
|
||||
// Minimum prefetch = 0
|
||||
put_be16(&buf[8], 0xffff); // Maximum prefetch
|
||||
put_be16(&buf[10], 0xffff); // Maximum prefetch ceiling
|
||||
put_unaligned_be16(0xffff, &buf[4]);
|
||||
/* Don't disable prefetch */
|
||||
/* Minimum prefetch = 0 */
|
||||
put_unaligned_be16(0xffff, &buf[8]);
|
||||
/* Maximum prefetch */
|
||||
put_unaligned_be16(0xffff, &buf[10]);
|
||||
/* Maximum prefetch ceiling */
|
||||
}
|
||||
buf += 12;
|
||||
}
|
||||
@ -2272,7 +2258,7 @@ static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
|
||||
if (mscmnd == SC_MODE_SENSE_6)
|
||||
buf0[0] = len - 1;
|
||||
else
|
||||
put_be16(buf0, len - 2);
|
||||
put_unaligned_be16(len - 2, buf0);
|
||||
return len;
|
||||
}
|
||||
|
||||
@ -2360,9 +2346,10 @@ static int do_read_format_capacities(struct fsg_dev *fsg,
|
||||
buf[3] = 8; // Only the Current/Maximum Capacity Descriptor
|
||||
buf += 4;
|
||||
|
||||
put_be32(&buf[0], curlun->num_sectors); // Number of blocks
|
||||
put_be32(&buf[4], 512); // Block length
|
||||
buf[4] = 0x02; // Current capacity
|
||||
put_unaligned_be32(curlun->num_sectors, &buf[0]);
|
||||
/* Number of blocks */
|
||||
put_unaligned_be32(512, &buf[4]); /* Block length */
|
||||
buf[4] = 0x02; /* Current capacity */
|
||||
return 12;
|
||||
}
|
||||
|
||||
@ -2882,7 +2869,7 @@ static int do_scsi_command(struct fsg_dev *fsg)
|
||||
break;
|
||||
|
||||
case SC_MODE_SELECT_10:
|
||||
fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
|
||||
fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
|
||||
if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST,
|
||||
(1<<1) | (3<<7), 0,
|
||||
"MODE SELECT(10)")) == 0)
|
||||
@ -2898,7 +2885,7 @@ static int do_scsi_command(struct fsg_dev *fsg)
|
||||
break;
|
||||
|
||||
case SC_MODE_SENSE_10:
|
||||
fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
|
||||
fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
|
||||
if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
|
||||
(1<<1) | (1<<2) | (3<<7), 0,
|
||||
"MODE SENSE(10)")) == 0)
|
||||
@ -2923,7 +2910,8 @@ static int do_scsi_command(struct fsg_dev *fsg)
|
||||
break;
|
||||
|
||||
case SC_READ_10:
|
||||
fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]) << 9;
|
||||
fsg->data_size_from_cmnd =
|
||||
get_unaligned_be16(&fsg->cmnd[7]) << 9;
|
||||
if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
|
||||
(1<<1) | (0xf<<2) | (3<<7), 1,
|
||||
"READ(10)")) == 0)
|
||||
@ -2931,7 +2919,8 @@ static int do_scsi_command(struct fsg_dev *fsg)
|
||||
break;
|
||||
|
||||
case SC_READ_12:
|
||||
fsg->data_size_from_cmnd = get_be32(&fsg->cmnd[6]) << 9;
|
||||
fsg->data_size_from_cmnd =
|
||||
get_unaligned_be32(&fsg->cmnd[6]) << 9;
|
||||
if ((reply = check_command(fsg, 12, DATA_DIR_TO_HOST,
|
||||
(1<<1) | (0xf<<2) | (0xf<<6), 1,
|
||||
"READ(12)")) == 0)
|
||||
@ -2949,7 +2938,7 @@ static int do_scsi_command(struct fsg_dev *fsg)
|
||||
case SC_READ_HEADER:
|
||||
if (!mod_data.cdrom)
|
||||
goto unknown_cmnd;
|
||||
fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
|
||||
fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
|
||||
if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
|
||||
(3<<7) | (0x1f<<1), 1,
|
||||
"READ HEADER")) == 0)
|
||||
@ -2959,7 +2948,7 @@ static int do_scsi_command(struct fsg_dev *fsg)
|
||||
case SC_READ_TOC:
|
||||
if (!mod_data.cdrom)
|
||||
goto unknown_cmnd;
|
||||
fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
|
||||
fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
|
||||
if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
|
||||
(7<<6) | (1<<1), 1,
|
||||
"READ TOC")) == 0)
|
||||
@ -2967,7 +2956,7 @@ static int do_scsi_command(struct fsg_dev *fsg)
|
||||
break;
|
||||
|
||||
case SC_READ_FORMAT_CAPACITIES:
|
||||
fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
|
||||
fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
|
||||
if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
|
||||
(3<<7), 1,
|
||||
"READ FORMAT CAPACITIES")) == 0)
|
||||
@ -3025,7 +3014,8 @@ static int do_scsi_command(struct fsg_dev *fsg)
|
||||
break;
|
||||
|
||||
case SC_WRITE_10:
|
||||
fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]) << 9;
|
||||
fsg->data_size_from_cmnd =
|
||||
get_unaligned_be16(&fsg->cmnd[7]) << 9;
|
||||
if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST,
|
||||
(1<<1) | (0xf<<2) | (3<<7), 1,
|
||||
"WRITE(10)")) == 0)
|
||||
@ -3033,7 +3023,8 @@ static int do_scsi_command(struct fsg_dev *fsg)
|
||||
break;
|
||||
|
||||
case SC_WRITE_12:
|
||||
fsg->data_size_from_cmnd = get_be32(&fsg->cmnd[6]) << 9;
|
||||
fsg->data_size_from_cmnd =
|
||||
get_unaligned_be32(&fsg->cmnd[6]) << 9;
|
||||
if ((reply = check_command(fsg, 12, DATA_DIR_FROM_HOST,
|
||||
(1<<1) | (0xf<<2) | (0xf<<6), 1,
|
||||
"WRITE(12)")) == 0)
|
||||
|
95
drivers/usb/gadget/fsl_mx3_udc.c
Normal file
95
drivers/usb/gadget/fsl_mx3_udc.c
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2009
|
||||
* Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
|
||||
*
|
||||
* Description:
|
||||
* Helper routines for i.MX3x SoCs from Freescale, needed by the fsl_usb2_udc.c
|
||||
* driver to function correctly on these systems.
|
||||
*
|
||||
* 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; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/fsl_devices.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
static struct clk *mxc_ahb_clk;
|
||||
static struct clk *mxc_usb_clk;
|
||||
|
||||
int fsl_udc_clk_init(struct platform_device *pdev)
|
||||
{
|
||||
struct fsl_usb2_platform_data *pdata;
|
||||
unsigned long freq;
|
||||
int ret;
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
|
||||
mxc_ahb_clk = clk_get(&pdev->dev, "usb_ahb");
|
||||
if (IS_ERR(mxc_ahb_clk))
|
||||
return PTR_ERR(mxc_ahb_clk);
|
||||
|
||||
ret = clk_enable(mxc_ahb_clk);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "clk_enable(\"usb_ahb\") failed\n");
|
||||
goto eenahb;
|
||||
}
|
||||
|
||||
/* make sure USB_CLK is running at 60 MHz +/- 1000 Hz */
|
||||
mxc_usb_clk = clk_get(&pdev->dev, "usb");
|
||||
if (IS_ERR(mxc_usb_clk)) {
|
||||
dev_err(&pdev->dev, "clk_get(\"usb\") failed\n");
|
||||
ret = PTR_ERR(mxc_usb_clk);
|
||||
goto egusb;
|
||||
}
|
||||
|
||||
freq = clk_get_rate(mxc_usb_clk);
|
||||
if (pdata->phy_mode != FSL_USB2_PHY_ULPI &&
|
||||
(freq < 59999000 || freq > 60001000)) {
|
||||
dev_err(&pdev->dev, "USB_CLK=%lu, should be 60MHz\n", freq);
|
||||
goto eclkrate;
|
||||
}
|
||||
|
||||
ret = clk_enable(mxc_usb_clk);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "clk_enable(\"usb_clk\") failed\n");
|
||||
goto eenusb;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
eenusb:
|
||||
eclkrate:
|
||||
clk_put(mxc_usb_clk);
|
||||
mxc_usb_clk = NULL;
|
||||
egusb:
|
||||
clk_disable(mxc_ahb_clk);
|
||||
eenahb:
|
||||
clk_put(mxc_ahb_clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void fsl_udc_clk_finalize(struct platform_device *pdev)
|
||||
{
|
||||
struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
|
||||
|
||||
/* ULPI transceivers don't need usbpll */
|
||||
if (pdata->phy_mode == FSL_USB2_PHY_ULPI) {
|
||||
clk_disable(mxc_usb_clk);
|
||||
clk_put(mxc_usb_clk);
|
||||
mxc_usb_clk = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void fsl_udc_clk_release(void)
|
||||
{
|
||||
if (mxc_usb_clk) {
|
||||
clk_disable(mxc_usb_clk);
|
||||
clk_put(mxc_usb_clk);
|
||||
}
|
||||
clk_disable(mxc_ahb_clk);
|
||||
clk_put(mxc_ahb_clk);
|
||||
}
|
@ -38,6 +38,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/fsl_devices.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/io.h>
|
||||
@ -57,7 +58,9 @@ static const char driver_name[] = "fsl-usb2-udc";
|
||||
static const char driver_desc[] = DRIVER_DESC;
|
||||
|
||||
static struct usb_dr_device *dr_regs;
|
||||
#ifndef CONFIG_ARCH_MXC
|
||||
static struct usb_sys_interface *usb_sys_regs;
|
||||
#endif
|
||||
|
||||
/* it is initialized in probe() */
|
||||
static struct fsl_udc *udc_controller = NULL;
|
||||
@ -174,10 +177,34 @@ static void nuke(struct fsl_ep *ep, int status)
|
||||
|
||||
static int dr_controller_setup(struct fsl_udc *udc)
|
||||
{
|
||||
unsigned int tmp = 0, portctrl = 0, ctrl = 0;
|
||||
unsigned int tmp, portctrl;
|
||||
#ifndef CONFIG_ARCH_MXC
|
||||
unsigned int ctrl;
|
||||
#endif
|
||||
unsigned long timeout;
|
||||
#define FSL_UDC_RESET_TIMEOUT 1000
|
||||
|
||||
/* Config PHY interface */
|
||||
portctrl = fsl_readl(&dr_regs->portsc1);
|
||||
portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH);
|
||||
switch (udc->phy_mode) {
|
||||
case FSL_USB2_PHY_ULPI:
|
||||
portctrl |= PORTSCX_PTS_ULPI;
|
||||
break;
|
||||
case FSL_USB2_PHY_UTMI_WIDE:
|
||||
portctrl |= PORTSCX_PTW_16BIT;
|
||||
/* fall through */
|
||||
case FSL_USB2_PHY_UTMI:
|
||||
portctrl |= PORTSCX_PTS_UTMI;
|
||||
break;
|
||||
case FSL_USB2_PHY_SERIAL:
|
||||
portctrl |= PORTSCX_PTS_FSLS;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
fsl_writel(portctrl, &dr_regs->portsc1);
|
||||
|
||||
/* Stop and reset the usb controller */
|
||||
tmp = fsl_readl(&dr_regs->usbcmd);
|
||||
tmp &= ~USB_CMD_RUN_STOP;
|
||||
@ -215,31 +242,12 @@ static int dr_controller_setup(struct fsl_udc *udc)
|
||||
udc->ep_qh, (int)tmp,
|
||||
fsl_readl(&dr_regs->endpointlistaddr));
|
||||
|
||||
/* Config PHY interface */
|
||||
portctrl = fsl_readl(&dr_regs->portsc1);
|
||||
portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH);
|
||||
switch (udc->phy_mode) {
|
||||
case FSL_USB2_PHY_ULPI:
|
||||
portctrl |= PORTSCX_PTS_ULPI;
|
||||
break;
|
||||
case FSL_USB2_PHY_UTMI_WIDE:
|
||||
portctrl |= PORTSCX_PTW_16BIT;
|
||||
/* fall through */
|
||||
case FSL_USB2_PHY_UTMI:
|
||||
portctrl |= PORTSCX_PTS_UTMI;
|
||||
break;
|
||||
case FSL_USB2_PHY_SERIAL:
|
||||
portctrl |= PORTSCX_PTS_FSLS;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
fsl_writel(portctrl, &dr_regs->portsc1);
|
||||
|
||||
/* Config control enable i/o output, cpu endian register */
|
||||
#ifndef CONFIG_ARCH_MXC
|
||||
ctrl = __raw_readl(&usb_sys_regs->control);
|
||||
ctrl |= USB_CTRL_IOENB;
|
||||
__raw_writel(ctrl, &usb_sys_regs->control);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
|
||||
/* Turn on cache snooping hardware, since some PowerPC platforms
|
||||
@ -2043,6 +2051,7 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,
|
||||
size -= t;
|
||||
next += t;
|
||||
|
||||
#ifndef CONFIG_ARCH_MXC
|
||||
tmp_reg = usb_sys_regs->snoop1;
|
||||
t = scnprintf(next, size, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg);
|
||||
size -= t;
|
||||
@ -2053,6 +2062,7 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,
|
||||
tmp_reg);
|
||||
size -= t;
|
||||
next += t;
|
||||
#endif
|
||||
|
||||
/* ------fsl_udc, fsl_ep, fsl_request structure information ----- */
|
||||
ep = &udc->eps[0];
|
||||
@ -2263,14 +2273,21 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
|
||||
goto err_kfree;
|
||||
}
|
||||
|
||||
dr_regs = ioremap(res->start, res->end - res->start + 1);
|
||||
dr_regs = ioremap(res->start, resource_size(res));
|
||||
if (!dr_regs) {
|
||||
ret = -ENOMEM;
|
||||
goto err_release_mem_region;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_ARCH_MXC
|
||||
usb_sys_regs = (struct usb_sys_interface *)
|
||||
((u32)dr_regs + USB_DR_SYS_OFFSET);
|
||||
#endif
|
||||
|
||||
/* Initialize USB clocks */
|
||||
ret = fsl_udc_clk_init(pdev);
|
||||
if (ret < 0)
|
||||
goto err_iounmap_noclk;
|
||||
|
||||
/* Read Device Controller Capability Parameters register */
|
||||
dccparams = fsl_readl(&dr_regs->dccparams);
|
||||
@ -2308,6 +2325,8 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
|
||||
* leave usbintr reg untouched */
|
||||
dr_controller_setup(udc_controller);
|
||||
|
||||
fsl_udc_clk_finalize(pdev);
|
||||
|
||||
/* Setup gadget structure */
|
||||
udc_controller->gadget.ops = &fsl_gadget_ops;
|
||||
udc_controller->gadget.is_dualspeed = 1;
|
||||
@ -2362,6 +2381,8 @@ err_unregister:
|
||||
err_free_irq:
|
||||
free_irq(udc_controller->irq, udc_controller);
|
||||
err_iounmap:
|
||||
fsl_udc_clk_release();
|
||||
err_iounmap_noclk:
|
||||
iounmap(dr_regs);
|
||||
err_release_mem_region:
|
||||
release_mem_region(res->start, res->end - res->start + 1);
|
||||
@ -2384,6 +2405,8 @@ static int __exit fsl_udc_remove(struct platform_device *pdev)
|
||||
return -ENODEV;
|
||||
udc_controller->done = &done;
|
||||
|
||||
fsl_udc_clk_release();
|
||||
|
||||
/* DR has been stopped in usb_gadget_unregister_driver() */
|
||||
remove_proc_file();
|
||||
|
@ -563,4 +563,22 @@ static void dump_msg(const char *label, const u8 * buf, unsigned int length)
|
||||
* 2 + ((windex & USB_DIR_IN) ? 1 : 0))
|
||||
#define get_pipe_by_ep(EP) (ep_index(EP) * 2 + ep_is_in(EP))
|
||||
|
||||
struct platform_device;
|
||||
#ifdef CONFIG_ARCH_MXC
|
||||
int fsl_udc_clk_init(struct platform_device *pdev);
|
||||
void fsl_udc_clk_finalize(struct platform_device *pdev);
|
||||
void fsl_udc_clk_release(void);
|
||||
#else
|
||||
static inline int fsl_udc_clk_init(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void fsl_udc_clk_finalize(struct platform_device *pdev)
|
||||
{
|
||||
}
|
||||
static inline void fsl_udc_clk_release(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -137,6 +137,12 @@
|
||||
#define gadget_is_musbhdrc(g) 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_LANGWELL
|
||||
#define gadget_is_langwell(g) (!strcmp("langwell_udc", (g)->name))
|
||||
#else
|
||||
#define gadget_is_langwell(g) 0
|
||||
#endif
|
||||
|
||||
/* from Montavista kernel (?) */
|
||||
#ifdef CONFIG_USB_GADGET_MPC8272
|
||||
#define gadget_is_mpc8272(g) !strcmp("mpc8272_udc", (g)->name)
|
||||
@ -231,6 +237,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
|
||||
return 0x22;
|
||||
else if (gadget_is_ci13xxx(gadget))
|
||||
return 0x23;
|
||||
else if (gadget_is_langwell(gadget))
|
||||
return 0x24;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
|
@ -110,10 +110,10 @@ goku_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
|
||||
return -EINVAL;
|
||||
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
|
||||
return -ESHUTDOWN;
|
||||
if (ep->num != (desc->bEndpointAddress & 0x0f))
|
||||
if (ep->num != usb_endpoint_num(desc))
|
||||
return -EINVAL;
|
||||
|
||||
switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
|
||||
switch (usb_endpoint_type(desc)) {
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
break;
|
||||
@ -142,7 +142,7 @@ goku_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
|
||||
/* ep1/ep2 dma direction is chosen early; it works in the other
|
||||
* direction, with pio. be cautious with out-dma.
|
||||
*/
|
||||
ep->is_in = (USB_DIR_IN & desc->bEndpointAddress) != 0;
|
||||
ep->is_in = usb_endpoint_dir_in(desc);
|
||||
if (ep->is_in) {
|
||||
mode |= 1;
|
||||
ep->dma = (use_dma != 0) && (ep->num == UDC_MSTRD_ENDPOINT);
|
||||
|
@ -415,6 +415,13 @@ static int write_packet(struct imx_ep_struct *imx_ep, struct imx_request *req)
|
||||
u8 *buf;
|
||||
int length, count, temp;
|
||||
|
||||
if (unlikely(__raw_readl(imx_ep->imx_usb->base +
|
||||
USB_EP_STAT(EP_NO(imx_ep))) & EPSTAT_ZLPS)) {
|
||||
D_TRX(imx_ep->imx_usb->dev, "<%s> zlp still queued in EP %s\n",
|
||||
__func__, imx_ep->ep.name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf = req->req.buf + req->req.actual;
|
||||
prefetch(buf);
|
||||
|
||||
@ -734,9 +741,12 @@ static struct usb_request *imx_ep_alloc_request
|
||||
{
|
||||
struct imx_request *req;
|
||||
|
||||
if (!usb_ep)
|
||||
return NULL;
|
||||
|
||||
req = kzalloc(sizeof *req, gfp_flags);
|
||||
if (!req || !usb_ep)
|
||||
return 0;
|
||||
if (!req)
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&req->queue);
|
||||
req->in_use = 0;
|
||||
|
@ -384,9 +384,8 @@ ep_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
|
||||
return value;
|
||||
|
||||
/* halt any endpoint by doing a "wrong direction" i/o call */
|
||||
if (data->desc.bEndpointAddress & USB_DIR_IN) {
|
||||
if ((data->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
|
||||
== USB_ENDPOINT_XFER_ISOC)
|
||||
if (usb_endpoint_dir_in(&data->desc)) {
|
||||
if (usb_endpoint_xfer_isoc(&data->desc))
|
||||
return -EINVAL;
|
||||
DBG (data->dev, "%s halt\n", data->name);
|
||||
spin_lock_irq (&data->dev->lock);
|
||||
@ -428,9 +427,8 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
|
||||
return value;
|
||||
|
||||
/* halt any endpoint by doing a "wrong direction" i/o call */
|
||||
if (!(data->desc.bEndpointAddress & USB_DIR_IN)) {
|
||||
if ((data->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
|
||||
== USB_ENDPOINT_XFER_ISOC)
|
||||
if (!usb_endpoint_dir_in(&data->desc)) {
|
||||
if (usb_endpoint_xfer_isoc(&data->desc))
|
||||
return -EINVAL;
|
||||
DBG (data->dev, "%s halt\n", data->name);
|
||||
spin_lock_irq (&data->dev->lock);
|
||||
@ -691,7 +689,7 @@ ep_aio_read(struct kiocb *iocb, const struct iovec *iov,
|
||||
struct ep_data *epdata = iocb->ki_filp->private_data;
|
||||
char *buf;
|
||||
|
||||
if (unlikely(epdata->desc.bEndpointAddress & USB_DIR_IN))
|
||||
if (unlikely(usb_endpoint_dir_in(&epdata->desc)))
|
||||
return -EINVAL;
|
||||
|
||||
buf = kmalloc(iocb->ki_left, GFP_KERNEL);
|
||||
@ -711,7 +709,7 @@ ep_aio_write(struct kiocb *iocb, const struct iovec *iov,
|
||||
size_t len = 0;
|
||||
int i = 0;
|
||||
|
||||
if (unlikely(!(epdata->desc.bEndpointAddress & USB_DIR_IN)))
|
||||
if (unlikely(!usb_endpoint_dir_in(&epdata->desc)))
|
||||
return -EINVAL;
|
||||
|
||||
buf = kmalloc(iocb->ki_left, GFP_KERNEL);
|
||||
|
3373
drivers/usb/gadget/langwell_udc.c
Normal file
3373
drivers/usb/gadget/langwell_udc.c
Normal file
File diff suppressed because it is too large
Load Diff
228
drivers/usb/gadget/langwell_udc.h
Normal file
228
drivers/usb/gadget/langwell_udc.h
Normal file
@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Intel Langwell USB Device Controller driver
|
||||
* Copyright (C) 2008-2009, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/usb/langwell_udc.h>
|
||||
|
||||
#if defined(CONFIG_USB_LANGWELL_OTG)
|
||||
#include <linux/usb/langwell_otg.h>
|
||||
#endif
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* driver data structures and utilities */
|
||||
|
||||
/*
|
||||
* dTD: Device Endpoint Transfer Descriptor
|
||||
* describe to the device controller the location and quantity of
|
||||
* data to be send/received for given transfer
|
||||
*/
|
||||
struct langwell_dtd {
|
||||
u32 dtd_next;
|
||||
/* bits 31:5, next transfer element pointer */
|
||||
#define DTD_NEXT(d) (((d)>>5)&0x7ffffff)
|
||||
#define DTD_NEXT_MASK (0x7ffffff << 5)
|
||||
/* terminate */
|
||||
#define DTD_TERM BIT(0)
|
||||
/* bits 7:0, execution back states */
|
||||
u32 dtd_status:8;
|
||||
#define DTD_STATUS(d) (((d)>>0)&0xff)
|
||||
#define DTD_STS_ACTIVE BIT(7) /* active */
|
||||
#define DTD_STS_HALTED BIT(6) /* halted */
|
||||
#define DTD_STS_DBE BIT(5) /* data buffer error */
|
||||
#define DTD_STS_TRE BIT(3) /* transaction error */
|
||||
/* bits 9:8 */
|
||||
u32 dtd_res0:2;
|
||||
/* bits 11:10, multipier override */
|
||||
u32 dtd_multo:2;
|
||||
#define DTD_MULTO (BIT(11) | BIT(10))
|
||||
/* bits 14:12 */
|
||||
u32 dtd_res1:3;
|
||||
/* bit 15, interrupt on complete */
|
||||
u32 dtd_ioc:1;
|
||||
#define DTD_IOC BIT(15)
|
||||
/* bits 30:16, total bytes */
|
||||
u32 dtd_total:15;
|
||||
#define DTD_TOTAL(d) (((d)>>16)&0x7fff)
|
||||
#define DTD_MAX_TRANSFER_LENGTH 0x4000
|
||||
/* bit 31 */
|
||||
u32 dtd_res2:1;
|
||||
/* dTD buffer pointer page 0 to 4 */
|
||||
u32 dtd_buf[5];
|
||||
#define DTD_OFFSET_MASK 0xfff
|
||||
/* bits 31:12, buffer pointer */
|
||||
#define DTD_BUFFER(d) (((d)>>12)&0x3ff)
|
||||
/* bits 11:0, current offset */
|
||||
#define DTD_C_OFFSET(d) (((d)>>0)&0xfff)
|
||||
/* bits 10:0, frame number */
|
||||
#define DTD_FRAME(d) (((d)>>0)&0x7ff)
|
||||
|
||||
/* driver-private parts */
|
||||
|
||||
/* dtd dma address */
|
||||
dma_addr_t dtd_dma;
|
||||
/* next dtd virtual address */
|
||||
struct langwell_dtd *next_dtd_virt;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* dQH: Device Endpoint Queue Head
|
||||
* describe where all transfers are managed
|
||||
* 48-byte data structure, aligned on 64-byte boundary
|
||||
*
|
||||
* These are associated with dTD structure
|
||||
*/
|
||||
struct langwell_dqh {
|
||||
/* endpoint capabilities and characteristics */
|
||||
u32 dqh_res0:15; /* bits 14:0 */
|
||||
u32 dqh_ios:1; /* bit 15, interrupt on setup */
|
||||
#define DQH_IOS BIT(15)
|
||||
u32 dqh_mpl:11; /* bits 26:16, maximum packet length */
|
||||
#define DQH_MPL (0x7ff << 16)
|
||||
u32 dqh_res1:2; /* bits 28:27 */
|
||||
u32 dqh_zlt:1; /* bit 29, zero length termination */
|
||||
#define DQH_ZLT BIT(29)
|
||||
u32 dqh_mult:2; /* bits 31:30 */
|
||||
#define DQH_MULT (BIT(30) | BIT(31))
|
||||
|
||||
/* current dTD pointer */
|
||||
u32 dqh_current; /* locate the transfer in progress */
|
||||
#define DQH_C_DTD(e) \
|
||||
(((e)>>5)&0x7ffffff) /* bits 31:5, current dTD pointer */
|
||||
|
||||
/* transfer overlay, hardware parts of a struct langwell_dtd */
|
||||
u32 dtd_next;
|
||||
u32 dtd_status:8; /* bits 7:0, execution back states */
|
||||
u32 dtd_res0:2; /* bits 9:8 */
|
||||
u32 dtd_multo:2; /* bits 11:10, multipier override */
|
||||
u32 dtd_res1:3; /* bits 14:12 */
|
||||
u32 dtd_ioc:1; /* bit 15, interrupt on complete */
|
||||
u32 dtd_total:15; /* bits 30:16, total bytes */
|
||||
u32 dtd_res2:1; /* bit 31 */
|
||||
u32 dtd_buf[5]; /* dTD buffer pointer page 0 to 4 */
|
||||
|
||||
u32 dqh_res2;
|
||||
struct usb_ctrlrequest dqh_setup; /* setup packet buffer */
|
||||
} __attribute__ ((aligned(64)));
|
||||
|
||||
|
||||
/* endpoint data structure */
|
||||
struct langwell_ep {
|
||||
struct usb_ep ep;
|
||||
dma_addr_t dma;
|
||||
struct langwell_udc *dev;
|
||||
unsigned long irqs;
|
||||
struct list_head queue;
|
||||
struct langwell_dqh *dqh;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
char name[14];
|
||||
unsigned stopped:1,
|
||||
ep_type:2,
|
||||
ep_num:8;
|
||||
};
|
||||
|
||||
|
||||
/* request data structure */
|
||||
struct langwell_request {
|
||||
struct usb_request req;
|
||||
struct langwell_dtd *dtd, *head, *tail;
|
||||
struct langwell_ep *ep;
|
||||
dma_addr_t dtd_dma;
|
||||
struct list_head queue;
|
||||
unsigned dtd_count;
|
||||
unsigned mapped:1;
|
||||
};
|
||||
|
||||
|
||||
/* ep0 transfer state */
|
||||
enum ep0_state {
|
||||
WAIT_FOR_SETUP,
|
||||
DATA_STATE_XMIT,
|
||||
DATA_STATE_NEED_ZLP,
|
||||
WAIT_FOR_OUT_STATUS,
|
||||
DATA_STATE_RECV,
|
||||
};
|
||||
|
||||
|
||||
/* device suspend state */
|
||||
enum lpm_state {
|
||||
LPM_L0, /* on */
|
||||
LPM_L1, /* LPM L1 sleep */
|
||||
LPM_L2, /* suspend */
|
||||
LPM_L3, /* off */
|
||||
};
|
||||
|
||||
|
||||
/* device data structure */
|
||||
struct langwell_udc {
|
||||
/* each pci device provides one gadget, several endpoints */
|
||||
struct usb_gadget gadget;
|
||||
spinlock_t lock; /* device lock */
|
||||
struct langwell_ep *ep;
|
||||
struct usb_gadget_driver *driver;
|
||||
struct otg_transceiver *transceiver;
|
||||
u8 dev_addr;
|
||||
u32 usb_state;
|
||||
u32 resume_state;
|
||||
u32 bus_reset;
|
||||
enum lpm_state lpm_state;
|
||||
enum ep0_state ep0_state;
|
||||
u32 ep0_dir;
|
||||
u16 dciversion;
|
||||
unsigned ep_max;
|
||||
unsigned devcap:1,
|
||||
enabled:1,
|
||||
region:1,
|
||||
got_irq:1,
|
||||
powered:1,
|
||||
remote_wakeup:1,
|
||||
rate:1,
|
||||
is_reset:1,
|
||||
softconnected:1,
|
||||
vbus_active:1,
|
||||
suspended:1,
|
||||
stopped:1,
|
||||
lpm:1; /* LPM capability */
|
||||
|
||||
/* pci state used to access those endpoints */
|
||||
struct pci_dev *pdev;
|
||||
|
||||
/* Langwell otg transceiver */
|
||||
struct langwell_otg *lotg;
|
||||
|
||||
/* control registers */
|
||||
struct langwell_cap_regs __iomem *cap_regs;
|
||||
struct langwell_op_regs __iomem *op_regs;
|
||||
|
||||
struct usb_ctrlrequest local_setup_buff;
|
||||
struct langwell_dqh *ep_dqh;
|
||||
size_t ep_dqh_size;
|
||||
dma_addr_t ep_dqh_dma;
|
||||
|
||||
/* ep0 status request */
|
||||
struct langwell_request *status_req;
|
||||
|
||||
/* dma pool */
|
||||
struct dma_pool *dtd_pool;
|
||||
|
||||
/* make sure release() is done */
|
||||
struct completion *done;
|
||||
};
|
||||
|
@ -38,7 +38,6 @@
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <mach/pxa2xx-regs.h> /* FIXME: for PSSR */
|
||||
#include <mach/udc.h>
|
||||
|
||||
#include "pxa27x_udc.h"
|
||||
@ -473,6 +472,23 @@ static inline void udc_clear_mask_UDCCR(struct pxa_udc *udc, int mask)
|
||||
(udccr & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS));
|
||||
}
|
||||
|
||||
/**
|
||||
* ep_write_UDCCSR - set bits in UDCCSR
|
||||
* @udc: udc device
|
||||
* @mask: bits to set in UDCCR
|
||||
*
|
||||
* Sets bits in UDCCSR (UDCCSR0 and UDCCSR*).
|
||||
*
|
||||
* A specific case is applied to ep0 : the ACM bit is always set to 1, for
|
||||
* SET_INTERFACE and SET_CONFIGURATION.
|
||||
*/
|
||||
static inline void ep_write_UDCCSR(struct pxa_ep *ep, int mask)
|
||||
{
|
||||
if (is_ep0(ep))
|
||||
mask |= UDCCSR0_ACM;
|
||||
udc_ep_writel(ep, UDCCSR, mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* ep_count_bytes_remain - get how many bytes in udc endpoint
|
||||
* @ep: udc endpoint
|
||||
@ -861,7 +877,7 @@ static int read_packet(struct pxa_ep *ep, struct pxa27x_request *req)
|
||||
*buf++ = udc_ep_readl(ep, UDCDR);
|
||||
req->req.actual += count;
|
||||
|
||||
udc_ep_writel(ep, UDCCSR, UDCCSR_PC);
|
||||
ep_write_UDCCSR(ep, UDCCSR_PC);
|
||||
|
||||
return count;
|
||||
}
|
||||
@ -969,12 +985,12 @@ static int write_fifo(struct pxa_ep *ep, struct pxa27x_request *req)
|
||||
if (udccsr & UDCCSR_PC) {
|
||||
ep_vdbg(ep, "Clearing Transmit Complete, udccsr=%x\n",
|
||||
udccsr);
|
||||
udc_ep_writel(ep, UDCCSR, UDCCSR_PC);
|
||||
ep_write_UDCCSR(ep, UDCCSR_PC);
|
||||
}
|
||||
if (udccsr & UDCCSR_TRN) {
|
||||
ep_vdbg(ep, "Clearing Underrun on, udccsr=%x\n",
|
||||
udccsr);
|
||||
udc_ep_writel(ep, UDCCSR, UDCCSR_TRN);
|
||||
ep_write_UDCCSR(ep, UDCCSR_TRN);
|
||||
}
|
||||
|
||||
count = write_packet(ep, req, max);
|
||||
@ -996,7 +1012,7 @@ static int write_fifo(struct pxa_ep *ep, struct pxa27x_request *req)
|
||||
}
|
||||
|
||||
if (is_short)
|
||||
udc_ep_writel(ep, UDCCSR, UDCCSR_SP);
|
||||
ep_write_UDCCSR(ep, UDCCSR_SP);
|
||||
|
||||
/* requests complete when all IN data is in the FIFO */
|
||||
if (is_last) {
|
||||
@ -1029,7 +1045,7 @@ static int read_ep0_fifo(struct pxa_ep *ep, struct pxa27x_request *req)
|
||||
|
||||
while (epout_has_pkt(ep)) {
|
||||
count = read_packet(ep, req);
|
||||
udc_ep_writel(ep, UDCCSR, UDCCSR0_OPC);
|
||||
ep_write_UDCCSR(ep, UDCCSR0_OPC);
|
||||
inc_ep_stats_bytes(ep, count, !USB_DIR_IN);
|
||||
|
||||
is_short = (count < ep->fifo_size);
|
||||
@ -1074,7 +1090,7 @@ static int write_ep0_fifo(struct pxa_ep *ep, struct pxa27x_request *req)
|
||||
|
||||
/* Sends either a short packet or a 0 length packet */
|
||||
if (unlikely(is_short))
|
||||
udc_ep_writel(ep, UDCCSR, UDCCSR0_IPR);
|
||||
ep_write_UDCCSR(ep, UDCCSR0_IPR);
|
||||
|
||||
ep_dbg(ep, "in %d bytes%s%s, %d left, req=%p, udccsr0=0x%03x\n",
|
||||
count, is_short ? "/S" : "", is_last ? "/L" : "",
|
||||
@ -1277,7 +1293,7 @@ static int pxa_ep_set_halt(struct usb_ep *_ep, int value)
|
||||
|
||||
/* FST, FEF bits are the same for control and non control endpoints */
|
||||
rc = 0;
|
||||
udc_ep_writel(ep, UDCCSR, UDCCSR_FST | UDCCSR_FEF);
|
||||
ep_write_UDCCSR(ep, UDCCSR_FST | UDCCSR_FEF);
|
||||
if (is_ep0(ep))
|
||||
set_ep0state(ep->dev, STALL);
|
||||
|
||||
@ -1343,7 +1359,7 @@ static void pxa_ep_fifo_flush(struct usb_ep *_ep)
|
||||
udc_ep_readl(ep, UDCDR);
|
||||
} else {
|
||||
/* most IN status is the same, but ISO can't stall */
|
||||
udc_ep_writel(ep, UDCCSR,
|
||||
ep_write_UDCCSR(ep,
|
||||
UDCCSR_PC | UDCCSR_FEF | UDCCSR_TRN
|
||||
| (EPXFERTYPE_is_ISO(ep) ? 0 : UDCCSR_SST));
|
||||
}
|
||||
@ -1728,6 +1744,7 @@ static void udc_enable(struct pxa_udc *udc)
|
||||
memset(&udc->stats, 0, sizeof(udc->stats));
|
||||
|
||||
udc_set_mask_UDCCR(udc, UDCCR_UDE);
|
||||
ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_ACM);
|
||||
udelay(2);
|
||||
if (udc_readl(udc, UDCCR) & UDCCR_EMCE)
|
||||
dev_err(udc->dev, "Configuration errors, udc disabled\n");
|
||||
@ -1893,6 +1910,15 @@ static void handle_ep0_ctrl_req(struct pxa_udc *udc,
|
||||
|
||||
nuke(ep, -EPROTO);
|
||||
|
||||
/*
|
||||
* In the PXA320 manual, in the section about Back-to-Back setup
|
||||
* packets, it describes this situation. The solution is to set OPC to
|
||||
* get rid of the status packet, and then continue with the setup
|
||||
* packet. Generalize to pxa27x CPUs.
|
||||
*/
|
||||
if (epout_has_pkt(ep) && (ep_count_bytes_remain(ep) == 0))
|
||||
ep_write_UDCCSR(ep, UDCCSR0_OPC);
|
||||
|
||||
/* read SETUP packet */
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (unlikely(ep_is_empty(ep)))
|
||||
@ -1919,7 +1945,7 @@ static void handle_ep0_ctrl_req(struct pxa_udc *udc,
|
||||
set_ep0state(udc, OUT_DATA_STAGE);
|
||||
|
||||
/* Tell UDC to enter Data Stage */
|
||||
udc_ep_writel(ep, UDCCSR, UDCCSR0_SA | UDCCSR0_OPC);
|
||||
ep_write_UDCCSR(ep, UDCCSR0_SA | UDCCSR0_OPC);
|
||||
|
||||
i = udc->driver->setup(&udc->gadget, &u.r);
|
||||
if (i < 0)
|
||||
@ -1929,7 +1955,7 @@ out:
|
||||
stall:
|
||||
ep_dbg(ep, "protocol STALL, udccsr0=%03x err %d\n",
|
||||
udc_ep_readl(ep, UDCCSR), i);
|
||||
udc_ep_writel(ep, UDCCSR, UDCCSR0_FST | UDCCSR0_FTF);
|
||||
ep_write_UDCCSR(ep, UDCCSR0_FST | UDCCSR0_FTF);
|
||||
set_ep0state(udc, STALL);
|
||||
goto out;
|
||||
}
|
||||
@ -1966,6 +1992,8 @@ stall:
|
||||
* cleared by software.
|
||||
* - clearing UDCCSR0_OPC always flushes ep0. If in setup stage, never do it
|
||||
* before reading ep0.
|
||||
* This is true only for PXA27x. This is not true anymore for PXA3xx family
|
||||
* (check Back-to-Back setup packet in developers guide).
|
||||
* - irq can be called on a "packet complete" event (opc_irq=1), while
|
||||
* UDCCSR0_OPC is not yet raised (delta can be as big as 100ms
|
||||
* from experimentation).
|
||||
@ -1998,7 +2026,7 @@ static void handle_ep0(struct pxa_udc *udc, int fifo_irq, int opc_irq)
|
||||
if (udccsr0 & UDCCSR0_SST) {
|
||||
ep_dbg(ep, "clearing stall status\n");
|
||||
nuke(ep, -EPIPE);
|
||||
udc_ep_writel(ep, UDCCSR, UDCCSR0_SST);
|
||||
ep_write_UDCCSR(ep, UDCCSR0_SST);
|
||||
ep0_idle(udc);
|
||||
}
|
||||
|
||||
@ -2023,7 +2051,7 @@ static void handle_ep0(struct pxa_udc *udc, int fifo_irq, int opc_irq)
|
||||
break;
|
||||
case IN_DATA_STAGE: /* GET_DESCRIPTOR */
|
||||
if (epout_has_pkt(ep))
|
||||
udc_ep_writel(ep, UDCCSR, UDCCSR0_OPC);
|
||||
ep_write_UDCCSR(ep, UDCCSR0_OPC);
|
||||
if (req && !ep_is_full(ep))
|
||||
completed = write_ep0_fifo(ep, req);
|
||||
if (completed)
|
||||
@ -2036,7 +2064,7 @@ static void handle_ep0(struct pxa_udc *udc, int fifo_irq, int opc_irq)
|
||||
ep0_end_out_req(ep, req);
|
||||
break;
|
||||
case STALL:
|
||||
udc_ep_writel(ep, UDCCSR, UDCCSR0_FST);
|
||||
ep_write_UDCCSR(ep, UDCCSR0_FST);
|
||||
break;
|
||||
case IN_STATUS_STAGE:
|
||||
/*
|
||||
@ -2131,6 +2159,7 @@ static void pxa27x_change_configuration(struct pxa_udc *udc, int config)
|
||||
|
||||
set_ep0state(udc, WAIT_ACK_SET_CONF_INTERF);
|
||||
udc->driver->setup(&udc->gadget, &req);
|
||||
ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_AREN);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2159,6 +2188,7 @@ static void pxa27x_change_interface(struct pxa_udc *udc, int iface, int alt)
|
||||
|
||||
set_ep0state(udc, WAIT_ACK_SET_CONF_INTERF);
|
||||
udc->driver->setup(&udc->gadget, &req);
|
||||
ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_AREN);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2280,7 +2310,7 @@ static void irq_udc_reset(struct pxa_udc *udc)
|
||||
memset(&udc->stats, 0, sizeof udc->stats);
|
||||
|
||||
nuke(ep, -EPROTO);
|
||||
udc_ep_writel(ep, UDCCSR, UDCCSR0_FTF | UDCCSR0_OPC);
|
||||
ep_write_UDCCSR(ep, UDCCSR0_FTF | UDCCSR0_OPC);
|
||||
ep0_idle(udc);
|
||||
}
|
||||
|
||||
@ -2479,6 +2509,12 @@ static void pxa_udc_shutdown(struct platform_device *_dev)
|
||||
udc_disable(udc);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CPU_PXA27x
|
||||
extern void pxa27x_clear_otgph(void);
|
||||
#else
|
||||
#define pxa27x_clear_otgph() do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/**
|
||||
* pxa_udc_suspend - Suspend udc device
|
||||
@ -2546,8 +2582,7 @@ static int pxa_udc_resume(struct platform_device *_dev)
|
||||
* Software must configure the USB OTG pad, UDC, and UHC
|
||||
* to the state they were in before entering sleep mode.
|
||||
*/
|
||||
if (cpu_is_pxa27x())
|
||||
PSSR |= PSSR_OTGPH;
|
||||
pxa27x_clear_otgph();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2571,7 +2606,7 @@ static struct platform_driver udc_driver = {
|
||||
|
||||
static int __init udc_init(void)
|
||||
{
|
||||
if (!cpu_is_pxa27x())
|
||||
if (!cpu_is_pxa27x() && !cpu_is_pxa3xx())
|
||||
return -ENODEV;
|
||||
|
||||
printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION);
|
||||
|
@ -130,6 +130,8 @@
|
||||
#define UP2OCR_HXOE (1 << 17) /* Transceiver Output Enable */
|
||||
#define UP2OCR_SEOS (1 << 24) /* Single-Ended Output Select */
|
||||
|
||||
#define UDCCSR0_ACM (1 << 9) /* Ack Control Mode */
|
||||
#define UDCCSR0_AREN (1 << 8) /* Ack Response Enable */
|
||||
#define UDCCSR0_SA (1 << 7) /* Setup Active */
|
||||
#define UDCCSR0_RNE (1 << 6) /* Receive FIFO Not Empty */
|
||||
#define UDCCSR0_FST (1 << 5) /* Force Stall */
|
||||
|
3269
drivers/usb/gadget/s3c-hsotg.c
Normal file
3269
drivers/usb/gadget/s3c-hsotg.c
Normal file
File diff suppressed because it is too large
Load Diff
319
drivers/usb/gadget/u_audio.c
Normal file
319
drivers/usb/gadget/u_audio.c
Normal file
@ -0,0 +1,319 @@
|
||||
/*
|
||||
* u_audio.c -- ALSA audio utilities for Gadget stack
|
||||
*
|
||||
* Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
|
||||
* Copyright (C) 2008 Analog Devices, Inc
|
||||
*
|
||||
* Enter bugs at http://blackfin.uclinux.org/
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/syscalls.h>
|
||||
|
||||
#include "u_audio.h"
|
||||
|
||||
/*
|
||||
* This component encapsulates the ALSA devices for USB audio gadget
|
||||
*/
|
||||
|
||||
#define FILE_PCM_PLAYBACK "/dev/snd/pcmC0D0p"
|
||||
#define FILE_PCM_CAPTURE "/dev/snd/pcmC0D0c"
|
||||
#define FILE_CONTROL "/dev/snd/controlC0"
|
||||
|
||||
static char *fn_play = FILE_PCM_PLAYBACK;
|
||||
module_param(fn_play, charp, S_IRUGO);
|
||||
MODULE_PARM_DESC(fn_play, "Playback PCM device file name");
|
||||
|
||||
static char *fn_cap = FILE_PCM_CAPTURE;
|
||||
module_param(fn_cap, charp, S_IRUGO);
|
||||
MODULE_PARM_DESC(fn_cap, "Capture PCM device file name");
|
||||
|
||||
static char *fn_cntl = FILE_CONTROL;
|
||||
module_param(fn_cntl, charp, S_IRUGO);
|
||||
MODULE_PARM_DESC(fn_cntl, "Control device file name");
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Some ALSA internal helper functions
|
||||
*/
|
||||
static int snd_interval_refine_set(struct snd_interval *i, unsigned int val)
|
||||
{
|
||||
struct snd_interval t;
|
||||
t.empty = 0;
|
||||
t.min = t.max = val;
|
||||
t.openmin = t.openmax = 0;
|
||||
t.integer = 1;
|
||||
return snd_interval_refine(i, &t);
|
||||
}
|
||||
|
||||
static int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params,
|
||||
snd_pcm_hw_param_t var, unsigned int val,
|
||||
int dir)
|
||||
{
|
||||
int changed;
|
||||
if (hw_is_mask(var)) {
|
||||
struct snd_mask *m = hw_param_mask(params, var);
|
||||
if (val == 0 && dir < 0) {
|
||||
changed = -EINVAL;
|
||||
snd_mask_none(m);
|
||||
} else {
|
||||
if (dir > 0)
|
||||
val++;
|
||||
else if (dir < 0)
|
||||
val--;
|
||||
changed = snd_mask_refine_set(
|
||||
hw_param_mask(params, var), val);
|
||||
}
|
||||
} else if (hw_is_interval(var)) {
|
||||
struct snd_interval *i = hw_param_interval(params, var);
|
||||
if (val == 0 && dir < 0) {
|
||||
changed = -EINVAL;
|
||||
snd_interval_none(i);
|
||||
} else if (dir == 0)
|
||||
changed = snd_interval_refine_set(i, val);
|
||||
else {
|
||||
struct snd_interval t;
|
||||
t.openmin = 1;
|
||||
t.openmax = 1;
|
||||
t.empty = 0;
|
||||
t.integer = 0;
|
||||
if (dir < 0) {
|
||||
t.min = val - 1;
|
||||
t.max = val;
|
||||
} else {
|
||||
t.min = val;
|
||||
t.max = val+1;
|
||||
}
|
||||
changed = snd_interval_refine(i, &t);
|
||||
}
|
||||
} else
|
||||
return -EINVAL;
|
||||
if (changed) {
|
||||
params->cmask |= 1 << var;
|
||||
params->rmask |= 1 << var;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Set default hardware params
|
||||
*/
|
||||
static int playback_default_hw_params(struct gaudio_snd_dev *snd)
|
||||
{
|
||||
struct snd_pcm_substream *substream = snd->substream;
|
||||
struct snd_pcm_hw_params *params;
|
||||
snd_pcm_sframes_t result;
|
||||
|
||||
/*
|
||||
* SNDRV_PCM_ACCESS_RW_INTERLEAVED,
|
||||
* SNDRV_PCM_FORMAT_S16_LE
|
||||
* CHANNELS: 2
|
||||
* RATE: 48000
|
||||
*/
|
||||
snd->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
|
||||
snd->format = SNDRV_PCM_FORMAT_S16_LE;
|
||||
snd->channels = 2;
|
||||
snd->rate = 48000;
|
||||
|
||||
params = kzalloc(sizeof(*params), GFP_KERNEL);
|
||||
if (!params)
|
||||
return -ENOMEM;
|
||||
|
||||
_snd_pcm_hw_params_any(params);
|
||||
_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS,
|
||||
snd->access, 0);
|
||||
_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT,
|
||||
snd->format, 0);
|
||||
_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
snd->channels, 0);
|
||||
_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE,
|
||||
snd->rate, 0);
|
||||
|
||||
snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
|
||||
snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, params);
|
||||
|
||||
result = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL);
|
||||
if (result < 0) {
|
||||
ERROR(snd->card,
|
||||
"Preparing sound card failed: %d\n", (int)result);
|
||||
kfree(params);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Store the hardware parameters */
|
||||
snd->access = params_access(params);
|
||||
snd->format = params_format(params);
|
||||
snd->channels = params_channels(params);
|
||||
snd->rate = params_rate(params);
|
||||
|
||||
kfree(params);
|
||||
|
||||
INFO(snd->card,
|
||||
"Hardware params: access %x, format %x, channels %d, rate %d\n",
|
||||
snd->access, snd->format, snd->channels, snd->rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Playback audio buffer data by ALSA PCM device
|
||||
*/
|
||||
static size_t u_audio_playback(struct gaudio *card, void *buf, size_t count)
|
||||
{
|
||||
struct gaudio_snd_dev *snd = &card->playback;
|
||||
struct snd_pcm_substream *substream = snd->substream;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
mm_segment_t old_fs;
|
||||
ssize_t result;
|
||||
snd_pcm_sframes_t frames;
|
||||
|
||||
try_again:
|
||||
if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
|
||||
runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
|
||||
result = snd_pcm_kernel_ioctl(substream,
|
||||
SNDRV_PCM_IOCTL_PREPARE, NULL);
|
||||
if (result < 0) {
|
||||
ERROR(card, "Preparing sound card failed: %d\n",
|
||||
(int)result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
frames = bytes_to_frames(runtime, count);
|
||||
old_fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
result = snd_pcm_lib_write(snd->substream, buf, frames);
|
||||
if (result != frames) {
|
||||
ERROR(card, "Playback error: %d\n", (int)result);
|
||||
set_fs(old_fs);
|
||||
goto try_again;
|
||||
}
|
||||
set_fs(old_fs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int u_audio_get_playback_channels(struct gaudio *card)
|
||||
{
|
||||
return card->playback.channels;
|
||||
}
|
||||
|
||||
static int u_audio_get_playback_rate(struct gaudio *card)
|
||||
{
|
||||
return card->playback.rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open ALSA PCM and control device files
|
||||
* Initial the PCM or control device
|
||||
*/
|
||||
static int gaudio_open_snd_dev(struct gaudio *card)
|
||||
{
|
||||
struct snd_pcm_file *pcm_file;
|
||||
struct gaudio_snd_dev *snd;
|
||||
|
||||
if (!card)
|
||||
return -ENODEV;
|
||||
|
||||
/* Open control device */
|
||||
snd = &card->control;
|
||||
snd->filp = filp_open(fn_cntl, O_RDWR, 0);
|
||||
if (IS_ERR(snd->filp)) {
|
||||
int ret = PTR_ERR(snd->filp);
|
||||
ERROR(card, "unable to open sound control device file: %s\n",
|
||||
fn_cntl);
|
||||
snd->filp = NULL;
|
||||
return ret;
|
||||
}
|
||||
snd->card = card;
|
||||
|
||||
/* Open PCM playback device and setup substream */
|
||||
snd = &card->playback;
|
||||
snd->filp = filp_open(fn_play, O_WRONLY, 0);
|
||||
if (IS_ERR(snd->filp)) {
|
||||
ERROR(card, "No such PCM playback device: %s\n", fn_play);
|
||||
snd->filp = NULL;
|
||||
}
|
||||
pcm_file = snd->filp->private_data;
|
||||
snd->substream = pcm_file->substream;
|
||||
snd->card = card;
|
||||
playback_default_hw_params(snd);
|
||||
|
||||
/* Open PCM capture device and setup substream */
|
||||
snd = &card->capture;
|
||||
snd->filp = filp_open(fn_cap, O_RDONLY, 0);
|
||||
if (IS_ERR(snd->filp)) {
|
||||
ERROR(card, "No such PCM capture device: %s\n", fn_cap);
|
||||
snd->filp = NULL;
|
||||
}
|
||||
pcm_file = snd->filp->private_data;
|
||||
snd->substream = pcm_file->substream;
|
||||
snd->card = card;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close ALSA PCM and control device files
|
||||
*/
|
||||
static int gaudio_close_snd_dev(struct gaudio *gau)
|
||||
{
|
||||
struct gaudio_snd_dev *snd;
|
||||
|
||||
/* Close control device */
|
||||
snd = &gau->control;
|
||||
if (!IS_ERR(snd->filp))
|
||||
filp_close(snd->filp, current->files);
|
||||
|
||||
/* Close PCM playback device and setup substream */
|
||||
snd = &gau->playback;
|
||||
if (!IS_ERR(snd->filp))
|
||||
filp_close(snd->filp, current->files);
|
||||
|
||||
/* Close PCM capture device and setup substream */
|
||||
snd = &gau->capture;
|
||||
if (!IS_ERR(snd->filp))
|
||||
filp_close(snd->filp, current->files);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* gaudio_setup - setup ALSA interface and preparing for USB transfer
|
||||
*
|
||||
* This sets up PCM, mixer or MIDI ALSA devices fore USB gadget using.
|
||||
*
|
||||
* Returns negative errno, or zero on success
|
||||
*/
|
||||
int __init gaudio_setup(struct gaudio *card)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = gaudio_open_snd_dev(card);
|
||||
if (ret)
|
||||
ERROR(card, "we need at least one control device\n");
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* gaudio_cleanup - remove ALSA device interface
|
||||
*
|
||||
* This is called to free all resources allocated by @gaudio_setup().
|
||||
*/
|
||||
void gaudio_cleanup(struct gaudio *card)
|
||||
{
|
||||
if (card)
|
||||
gaudio_close_snd_dev(card);
|
||||
}
|
||||
|
56
drivers/usb/gadget/u_audio.h
Normal file
56
drivers/usb/gadget/u_audio.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* u_audio.h -- interface to USB gadget "ALSA AUDIO" utilities
|
||||
*
|
||||
* Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
|
||||
* Copyright (C) 2008 Analog Devices, Inc
|
||||
*
|
||||
* Enter bugs at http://blackfin.uclinux.org/
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#ifndef __U_AUDIO_H
|
||||
#define __U_AUDIO_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/usb/audio.h>
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include "gadget_chips.h"
|
||||
|
||||
/*
|
||||
* This represents the USB side of an audio card device, managed by a USB
|
||||
* function which provides control and stream interfaces.
|
||||
*/
|
||||
|
||||
struct gaudio_snd_dev {
|
||||
struct gaudio *card;
|
||||
struct file *filp;
|
||||
struct snd_pcm_substream *substream;
|
||||
int access;
|
||||
int format;
|
||||
int channels;
|
||||
int rate;
|
||||
};
|
||||
|
||||
struct gaudio {
|
||||
struct usb_function func;
|
||||
struct usb_gadget *gadget;
|
||||
|
||||
/* ALSA sound device interfaces */
|
||||
struct gaudio_snd_dev control;
|
||||
struct gaudio_snd_dev playback;
|
||||
struct gaudio_snd_dev capture;
|
||||
|
||||
/* TODO */
|
||||
};
|
||||
|
||||
int gaudio_setup(struct gaudio *card);
|
||||
void gaudio_cleanup(struct gaudio *card);
|
||||
|
||||
#endif /* __U_AUDIO_H */
|
@ -371,6 +371,7 @@ __acquires(&port->port_lock)
|
||||
|
||||
req->length = len;
|
||||
list_del(&req->list);
|
||||
req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0);
|
||||
|
||||
pr_vdebug(PREFIX "%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n",
|
||||
port->port_num, len, *((u8 *)req->buf),
|
||||
|
@ -17,6 +17,26 @@ config USB_C67X00_HCD
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called c67x00.
|
||||
|
||||
config USB_XHCI_HCD
|
||||
tristate "xHCI HCD (USB 3.0) support (EXPERIMENTAL)"
|
||||
depends on USB && PCI && EXPERIMENTAL
|
||||
---help---
|
||||
The eXtensible Host Controller Interface (xHCI) is standard for USB 3.0
|
||||
"SuperSpeed" host controller hardware.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called xhci-hcd.
|
||||
|
||||
config USB_XHCI_HCD_DEBUGGING
|
||||
bool "Debugging for the xHCI host controller"
|
||||
depends on USB_XHCI_HCD
|
||||
---help---
|
||||
Say 'Y' to turn on debugging for the xHCI host controller driver.
|
||||
This will spew debugging output, even in interrupt context.
|
||||
This should only be used for debugging xHCI driver bugs.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config USB_EHCI_HCD
|
||||
tristate "EHCI HCD (USB 2.0) support"
|
||||
depends on USB && USB_ARCH_HAS_EHCI
|
||||
|
@ -12,6 +12,7 @@ fhci-objs := fhci-hcd.o fhci-hub.o fhci-q.o fhci-mem.o \
|
||||
ifeq ($(CONFIG_FHCI_DEBUG),y)
|
||||
fhci-objs += fhci-dbg.o
|
||||
endif
|
||||
xhci-objs := xhci-hcd.o xhci-mem.o xhci-pci.o xhci-ring.o xhci-hub.o xhci-dbg.o
|
||||
|
||||
obj-$(CONFIG_USB_WHCI_HCD) += whci/
|
||||
|
||||
@ -23,6 +24,7 @@ obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-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
|
||||
obj-$(CONFIG_USB_XHCI_HCD) += xhci.o
|
||||
obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
|
||||
obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o
|
||||
obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
|
||||
|
@ -97,6 +97,7 @@ static const struct hc_driver ehci_au1xxx_hc_driver = {
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
|
@ -309,6 +309,7 @@ static const struct hc_driver ehci_fsl_hc_driver = {
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
|
@ -1024,6 +1024,51 @@ done:
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
struct ehci_qh *qh;
|
||||
int eptype = usb_endpoint_type(&ep->desc);
|
||||
|
||||
if (eptype != USB_ENDPOINT_XFER_BULK && eptype != USB_ENDPOINT_XFER_INT)
|
||||
return;
|
||||
|
||||
rescan:
|
||||
spin_lock_irq(&ehci->lock);
|
||||
qh = ep->hcpriv;
|
||||
|
||||
/* For Bulk and Interrupt endpoints we maintain the toggle state
|
||||
* in the hardware; the toggle bits in udev aren't used at all.
|
||||
* When an endpoint is reset by usb_clear_halt() we must reset
|
||||
* the toggle bit in the QH.
|
||||
*/
|
||||
if (qh) {
|
||||
if (!list_empty(&qh->qtd_list)) {
|
||||
WARN_ONCE(1, "clear_halt for a busy endpoint\n");
|
||||
} else if (qh->qh_state == QH_STATE_IDLE) {
|
||||
qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
|
||||
} else {
|
||||
/* It's not safe to write into the overlay area
|
||||
* while the QH is active. Unlink it first and
|
||||
* wait for the unlink to complete.
|
||||
*/
|
||||
if (qh->qh_state == QH_STATE_LINKED) {
|
||||
if (eptype == USB_ENDPOINT_XFER_BULK) {
|
||||
unlink_async(ehci, qh);
|
||||
} else {
|
||||
intr_deschedule(ehci, qh);
|
||||
(void) qh_schedule(ehci, qh);
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&ehci->lock);
|
||||
schedule_timeout_uninterruptible(1);
|
||||
goto rescan;
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&ehci->lock);
|
||||
}
|
||||
|
||||
static int ehci_get_frame (struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
||||
@ -1097,7 +1142,7 @@ static int __init ehci_hcd_init(void)
|
||||
sizeof(struct ehci_itd), sizeof(struct ehci_sitd));
|
||||
|
||||
#ifdef DEBUG
|
||||
ehci_debug_root = debugfs_create_dir("ehci", NULL);
|
||||
ehci_debug_root = debugfs_create_dir("ehci", usb_debug_root);
|
||||
if (!ehci_debug_root) {
|
||||
retval = -ENOENT;
|
||||
goto err_debug;
|
||||
|
@ -391,7 +391,7 @@ static inline void create_companion_file(struct ehci_hcd *ehci)
|
||||
|
||||
/* with integrated TT there is no companion! */
|
||||
if (!ehci_is_TDI(ehci))
|
||||
i = device_create_file(ehci_to_hcd(ehci)->self.dev,
|
||||
i = device_create_file(ehci_to_hcd(ehci)->self.controller,
|
||||
&dev_attr_companion);
|
||||
}
|
||||
|
||||
@ -399,7 +399,7 @@ static inline void remove_companion_file(struct ehci_hcd *ehci)
|
||||
{
|
||||
/* with integrated TT there is no companion! */
|
||||
if (!ehci_is_TDI(ehci))
|
||||
device_remove_file(ehci_to_hcd(ehci)->self.dev,
|
||||
device_remove_file(ehci_to_hcd(ehci)->self.controller,
|
||||
&dev_attr_companion);
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,7 @@ static const struct hc_driver ixp4xx_ehci_hc_driver = {
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
.get_frame_number = ehci_get_frame,
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
|
@ -149,6 +149,7 @@ static const struct hc_driver ehci_orion_hc_driver = {
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
@ -187,7 +188,7 @@ ehci_orion_conf_mbus_windows(struct usb_hcd *hcd,
|
||||
}
|
||||
}
|
||||
|
||||
static int __init ehci_orion_drv_probe(struct platform_device *pdev)
|
||||
static int __devinit ehci_orion_drv_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct orion_ehci_data *pd = pdev->dev.platform_data;
|
||||
struct resource *res;
|
||||
|
@ -268,7 +268,7 @@ done:
|
||||
* Also they depend on separate root hub suspend/resume.
|
||||
*/
|
||||
|
||||
static int ehci_pci_suspend(struct usb_hcd *hcd, pm_message_t message)
|
||||
static int ehci_pci_suspend(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
unsigned long flags;
|
||||
@ -293,12 +293,6 @@ static int ehci_pci_suspend(struct usb_hcd *hcd, pm_message_t message)
|
||||
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
|
||||
(void)ehci_readl(ehci, &ehci->regs->intr_enable);
|
||||
|
||||
/* make sure snapshot being resumed re-enumerates everything */
|
||||
if (message.event == PM_EVENT_PRETHAW) {
|
||||
ehci_halt(ehci);
|
||||
ehci_reset(ehci);
|
||||
}
|
||||
|
||||
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
bail:
|
||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||
@ -309,7 +303,7 @@ static int ehci_pci_suspend(struct usb_hcd *hcd, pm_message_t message)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ehci_pci_resume(struct usb_hcd *hcd)
|
||||
static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
|
||||
@ -322,10 +316,12 @@ static int ehci_pci_resume(struct usb_hcd *hcd)
|
||||
/* Mark hardware accessible again as we are out of D3 state by now */
|
||||
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
|
||||
/* If CF is still set, we maintained PCI Vaux power.
|
||||
/* If CF is still set and we aren't resuming from hibernation
|
||||
* then we maintained PCI Vaux power.
|
||||
* Just undo the effect of ehci_pci_suspend().
|
||||
*/
|
||||
if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) {
|
||||
if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF &&
|
||||
!hibernated) {
|
||||
int mask = INTR_MASK;
|
||||
|
||||
if (!hcd->self.root_hub->do_remote_wakeup)
|
||||
@ -335,7 +331,6 @@ static int ehci_pci_resume(struct usb_hcd *hcd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
ehci_dbg(ehci, "lost power, restarting\n");
|
||||
usb_root_hub_lost_power(hcd->self.root_hub);
|
||||
|
||||
/* Else reset, to cope with power loss or flush-to-storage
|
||||
@ -393,6 +388,7 @@ static const struct hc_driver ehci_pci_hc_driver = {
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
@ -429,10 +425,11 @@ static struct pci_driver ehci_pci_driver = {
|
||||
|
||||
.probe = usb_hcd_pci_probe,
|
||||
.remove = usb_hcd_pci_remove,
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = usb_hcd_pci_suspend,
|
||||
.resume = usb_hcd_pci_resume,
|
||||
#endif
|
||||
.shutdown = usb_hcd_pci_shutdown,
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.driver = {
|
||||
.pm = &usb_hcd_pci_pm_ops
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
@ -61,6 +61,7 @@ static const struct hc_driver ehci_ppc_of_hc_driver = {
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
|
@ -65,6 +65,7 @@ static const struct hc_driver ps3_ehci_hc_driver = {
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
.get_frame_number = ehci_get_frame,
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
|
@ -93,22 +93,6 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
|
||||
qh->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma);
|
||||
qh->hw_alt_next = EHCI_LIST_END(ehci);
|
||||
|
||||
/* Except for control endpoints, we make hardware maintain data
|
||||
* toggle (like OHCI) ... here (re)initialize the toggle in the QH,
|
||||
* and set the pseudo-toggle in udev. Only usb_clear_halt() will
|
||||
* ever clear it.
|
||||
*/
|
||||
if (!(qh->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) {
|
||||
unsigned is_out, epnum;
|
||||
|
||||
is_out = !(qtd->hw_token & cpu_to_hc32(ehci, 1 << 8));
|
||||
epnum = (hc32_to_cpup(ehci, &qh->hw_info1) >> 8) & 0x0f;
|
||||
if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) {
|
||||
qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
|
||||
usb_settoggle (qh->dev, epnum, is_out, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
|
||||
wmb ();
|
||||
qh->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING);
|
||||
@ -850,7 +834,6 @@ done:
|
||||
qh->qh_state = QH_STATE_IDLE;
|
||||
qh->hw_info1 = cpu_to_hc32(ehci, info1);
|
||||
qh->hw_info2 = cpu_to_hc32(ehci, info2);
|
||||
usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1);
|
||||
qh_refresh (ehci, qh);
|
||||
return qh;
|
||||
}
|
||||
@ -881,7 +864,7 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
}
|
||||
}
|
||||
|
||||
/* clear halt and/or toggle; and maybe recover from silicon quirk */
|
||||
/* clear halt and maybe recover from silicon quirk */
|
||||
if (qh->qh_state == QH_STATE_IDLE)
|
||||
qh_refresh (ehci, qh);
|
||||
|
||||
|
@ -760,8 +760,10 @@ static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
if (status) {
|
||||
/* "normal" case, uframing flexible except with splits */
|
||||
if (qh->period) {
|
||||
frame = qh->period - 1;
|
||||
do {
|
||||
int i;
|
||||
|
||||
for (i = qh->period; status && i > 0; --i) {
|
||||
frame = ++ehci->random_frame % qh->period;
|
||||
for (uframe = 0; uframe < 8; uframe++) {
|
||||
status = check_intr_schedule (ehci,
|
||||
frame, uframe, qh,
|
||||
@ -769,7 +771,7 @@ static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
if (status == 0)
|
||||
break;
|
||||
}
|
||||
} while (status && frame--);
|
||||
}
|
||||
|
||||
/* qh->period == 0 means every uframe */
|
||||
} else {
|
||||
|
@ -116,6 +116,7 @@ struct ehci_hcd { /* one per controller */
|
||||
struct timer_list watchdog;
|
||||
unsigned long actions;
|
||||
unsigned stamp;
|
||||
unsigned random_frame;
|
||||
unsigned long next_statechange;
|
||||
u32 command;
|
||||
|
||||
|
@ -108,7 +108,7 @@ void fhci_dfs_create(struct fhci_hcd *fhci)
|
||||
{
|
||||
struct device *dev = fhci_to_hcd(fhci)->self.controller;
|
||||
|
||||
fhci->dfs_root = debugfs_create_dir(dev_name(dev), NULL);
|
||||
fhci->dfs_root = debugfs_create_dir(dev_name(dev), usb_debug_root);
|
||||
if (!fhci->dfs_root) {
|
||||
WARN_ON(1);
|
||||
return;
|
||||
|
@ -172,25 +172,6 @@ error_cluster_id_get:
|
||||
|
||||
}
|
||||
|
||||
static int hwahc_op_suspend(struct usb_hcd *usb_hcd, pm_message_t msg)
|
||||
{
|
||||
struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
|
||||
struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
|
||||
dev_err(wusbhc->dev, "%s (%p [%p], 0x%lx) UNIMPLEMENTED\n", __func__,
|
||||
usb_hcd, hwahc, *(unsigned long *) &msg);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int hwahc_op_resume(struct usb_hcd *usb_hcd)
|
||||
{
|
||||
struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
|
||||
struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
|
||||
|
||||
dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__,
|
||||
usb_hcd, hwahc);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
/*
|
||||
* No need to abort pipes, as when this is called, all the children
|
||||
* has been disconnected and that has done it [through
|
||||
@ -598,8 +579,6 @@ static struct hc_driver hwahc_hc_driver = {
|
||||
.flags = HCD_USB2, /* FIXME */
|
||||
.reset = hwahc_op_reset,
|
||||
.start = hwahc_op_start,
|
||||
.pci_suspend = hwahc_op_suspend,
|
||||
.pci_resume = hwahc_op_resume,
|
||||
.stop = hwahc_op_stop,
|
||||
.get_frame_number = hwahc_op_get_frame_number,
|
||||
.urb_enqueue = hwahc_op_urb_enqueue,
|
||||
|
@ -431,7 +431,7 @@ static struct dentry *ohci_debug_root;
|
||||
|
||||
struct debug_buffer {
|
||||
ssize_t (*fill_func)(struct debug_buffer *); /* fill method */
|
||||
struct device *dev;
|
||||
struct ohci_hcd *ohci;
|
||||
struct mutex mutex; /* protect filling of buffer */
|
||||
size_t count; /* number of characters filled into buffer */
|
||||
char *page;
|
||||
@ -505,15 +505,11 @@ show_list (struct ohci_hcd *ohci, char *buf, size_t count, struct ed *ed)
|
||||
|
||||
static ssize_t fill_async_buffer(struct debug_buffer *buf)
|
||||
{
|
||||
struct usb_bus *bus;
|
||||
struct usb_hcd *hcd;
|
||||
struct ohci_hcd *ohci;
|
||||
size_t temp;
|
||||
unsigned long flags;
|
||||
|
||||
bus = dev_get_drvdata(buf->dev);
|
||||
hcd = bus_to_hcd(bus);
|
||||
ohci = hcd_to_ohci(hcd);
|
||||
ohci = buf->ohci;
|
||||
|
||||
/* display control and bulk lists together, for simplicity */
|
||||
spin_lock_irqsave (&ohci->lock, flags);
|
||||
@ -529,8 +525,6 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf)
|
||||
|
||||
static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
|
||||
{
|
||||
struct usb_bus *bus;
|
||||
struct usb_hcd *hcd;
|
||||
struct ohci_hcd *ohci;
|
||||
struct ed **seen, *ed;
|
||||
unsigned long flags;
|
||||
@ -542,9 +536,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
|
||||
return 0;
|
||||
seen_count = 0;
|
||||
|
||||
bus = (struct usb_bus *)dev_get_drvdata(buf->dev);
|
||||
hcd = bus_to_hcd(bus);
|
||||
ohci = hcd_to_ohci(hcd);
|
||||
ohci = buf->ohci;
|
||||
next = buf->page;
|
||||
size = PAGE_SIZE;
|
||||
|
||||
@ -626,7 +618,6 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
|
||||
|
||||
static ssize_t fill_registers_buffer(struct debug_buffer *buf)
|
||||
{
|
||||
struct usb_bus *bus;
|
||||
struct usb_hcd *hcd;
|
||||
struct ohci_hcd *ohci;
|
||||
struct ohci_regs __iomem *regs;
|
||||
@ -635,9 +626,8 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
|
||||
char *next;
|
||||
u32 rdata;
|
||||
|
||||
bus = (struct usb_bus *)dev_get_drvdata(buf->dev);
|
||||
hcd = bus_to_hcd(bus);
|
||||
ohci = hcd_to_ohci(hcd);
|
||||
ohci = buf->ohci;
|
||||
hcd = ohci_to_hcd(ohci);
|
||||
regs = ohci->regs;
|
||||
next = buf->page;
|
||||
size = PAGE_SIZE;
|
||||
@ -710,7 +700,7 @@ done:
|
||||
return PAGE_SIZE - size;
|
||||
}
|
||||
|
||||
static struct debug_buffer *alloc_buffer(struct device *dev,
|
||||
static struct debug_buffer *alloc_buffer(struct ohci_hcd *ohci,
|
||||
ssize_t (*fill_func)(struct debug_buffer *))
|
||||
{
|
||||
struct debug_buffer *buf;
|
||||
@ -718,7 +708,7 @@ static struct debug_buffer *alloc_buffer(struct device *dev,
|
||||
buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL);
|
||||
|
||||
if (buf) {
|
||||
buf->dev = dev;
|
||||
buf->ohci = ohci;
|
||||
buf->fill_func = fill_func;
|
||||
mutex_init(&buf->mutex);
|
||||
}
|
||||
@ -810,26 +800,25 @@ static int debug_registers_open(struct inode *inode, struct file *file)
|
||||
static inline void create_debug_files (struct ohci_hcd *ohci)
|
||||
{
|
||||
struct usb_bus *bus = &ohci_to_hcd(ohci)->self;
|
||||
struct device *dev = bus->dev;
|
||||
|
||||
ohci->debug_dir = debugfs_create_dir(bus->bus_name, ohci_debug_root);
|
||||
if (!ohci->debug_dir)
|
||||
goto dir_error;
|
||||
|
||||
ohci->debug_async = debugfs_create_file("async", S_IRUGO,
|
||||
ohci->debug_dir, dev,
|
||||
ohci->debug_dir, ohci,
|
||||
&debug_async_fops);
|
||||
if (!ohci->debug_async)
|
||||
goto async_error;
|
||||
|
||||
ohci->debug_periodic = debugfs_create_file("periodic", S_IRUGO,
|
||||
ohci->debug_dir, dev,
|
||||
ohci->debug_dir, ohci,
|
||||
&debug_periodic_fops);
|
||||
if (!ohci->debug_periodic)
|
||||
goto periodic_error;
|
||||
|
||||
ohci->debug_registers = debugfs_create_file("registers", S_IRUGO,
|
||||
ohci->debug_dir, dev,
|
||||
ohci->debug_dir, ohci,
|
||||
&debug_registers_fops);
|
||||
if (!ohci->debug_registers)
|
||||
goto registers_error;
|
||||
|
@ -571,7 +571,7 @@ static int ohci_init (struct ohci_hcd *ohci)
|
||||
*/
|
||||
static int ohci_run (struct ohci_hcd *ohci)
|
||||
{
|
||||
u32 mask, temp;
|
||||
u32 mask, val;
|
||||
int first = ohci->fminterval == 0;
|
||||
struct usb_hcd *hcd = ohci_to_hcd(ohci);
|
||||
|
||||
@ -580,8 +580,8 @@ static int ohci_run (struct ohci_hcd *ohci)
|
||||
/* boot firmware should have set this up (5.1.1.3.1) */
|
||||
if (first) {
|
||||
|
||||
temp = ohci_readl (ohci, &ohci->regs->fminterval);
|
||||
ohci->fminterval = temp & 0x3fff;
|
||||
val = ohci_readl (ohci, &ohci->regs->fminterval);
|
||||
ohci->fminterval = val & 0x3fff;
|
||||
if (ohci->fminterval != FI)
|
||||
ohci_dbg (ohci, "fminterval delta %d\n",
|
||||
ohci->fminterval - FI);
|
||||
@ -600,25 +600,25 @@ static int ohci_run (struct ohci_hcd *ohci)
|
||||
|
||||
switch (ohci->hc_control & OHCI_CTRL_HCFS) {
|
||||
case OHCI_USB_OPER:
|
||||
temp = 0;
|
||||
val = 0;
|
||||
break;
|
||||
case OHCI_USB_SUSPEND:
|
||||
case OHCI_USB_RESUME:
|
||||
ohci->hc_control &= OHCI_CTRL_RWC;
|
||||
ohci->hc_control |= OHCI_USB_RESUME;
|
||||
temp = 10 /* msec wait */;
|
||||
val = 10 /* msec wait */;
|
||||
break;
|
||||
// case OHCI_USB_RESET:
|
||||
default:
|
||||
ohci->hc_control &= OHCI_CTRL_RWC;
|
||||
ohci->hc_control |= OHCI_USB_RESET;
|
||||
temp = 50 /* msec wait */;
|
||||
val = 50 /* msec wait */;
|
||||
break;
|
||||
}
|
||||
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
|
||||
// flush the writes
|
||||
(void) ohci_readl (ohci, &ohci->regs->control);
|
||||
msleep(temp);
|
||||
msleep(val);
|
||||
|
||||
memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
|
||||
|
||||
@ -628,9 +628,9 @@ static int ohci_run (struct ohci_hcd *ohci)
|
||||
retry:
|
||||
/* HC Reset requires max 10 us delay */
|
||||
ohci_writel (ohci, OHCI_HCR, &ohci->regs->cmdstatus);
|
||||
temp = 30; /* ... allow extra time */
|
||||
val = 30; /* ... allow extra time */
|
||||
while ((ohci_readl (ohci, &ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
|
||||
if (--temp == 0) {
|
||||
if (--val == 0) {
|
||||
spin_unlock_irq (&ohci->lock);
|
||||
ohci_err (ohci, "USB HC reset timed out!\n");
|
||||
return -1;
|
||||
@ -699,23 +699,23 @@ retry:
|
||||
ohci_writel (ohci, mask, &ohci->regs->intrenable);
|
||||
|
||||
/* handle root hub init quirks ... */
|
||||
temp = roothub_a (ohci);
|
||||
temp &= ~(RH_A_PSM | RH_A_OCPM);
|
||||
val = roothub_a (ohci);
|
||||
val &= ~(RH_A_PSM | RH_A_OCPM);
|
||||
if (ohci->flags & OHCI_QUIRK_SUPERIO) {
|
||||
/* NSC 87560 and maybe others */
|
||||
temp |= RH_A_NOCP;
|
||||
temp &= ~(RH_A_POTPGT | RH_A_NPS);
|
||||
ohci_writel (ohci, temp, &ohci->regs->roothub.a);
|
||||
val |= RH_A_NOCP;
|
||||
val &= ~(RH_A_POTPGT | RH_A_NPS);
|
||||
ohci_writel (ohci, val, &ohci->regs->roothub.a);
|
||||
} else if ((ohci->flags & OHCI_QUIRK_AMD756) ||
|
||||
(ohci->flags & OHCI_QUIRK_HUB_POWER)) {
|
||||
/* hub power always on; required for AMD-756 and some
|
||||
* Mac platforms. ganged overcurrent reporting, if any.
|
||||
*/
|
||||
temp |= RH_A_NPS;
|
||||
ohci_writel (ohci, temp, &ohci->regs->roothub.a);
|
||||
val |= RH_A_NPS;
|
||||
ohci_writel (ohci, val, &ohci->regs->roothub.a);
|
||||
}
|
||||
ohci_writel (ohci, RH_HS_LPSC, &ohci->regs->roothub.status);
|
||||
ohci_writel (ohci, (temp & RH_A_NPS) ? 0 : RH_B_PPCM,
|
||||
ohci_writel (ohci, (val & RH_A_NPS) ? 0 : RH_B_PPCM,
|
||||
&ohci->regs->roothub.b);
|
||||
// flush those writes
|
||||
(void) ohci_readl (ohci, &ohci->regs->control);
|
||||
@ -724,7 +724,7 @@ retry:
|
||||
spin_unlock_irq (&ohci->lock);
|
||||
|
||||
// POTPGT delay is bits 24-31, in 2 ms units.
|
||||
mdelay ((temp >> 23) & 0x1fe);
|
||||
mdelay ((val >> 23) & 0x1fe);
|
||||
hcd->state = HC_STATE_RUNNING;
|
||||
|
||||
if (quirk_zfmicro(ohci)) {
|
||||
@ -1105,7 +1105,7 @@ static int __init ohci_hcd_mod_init(void)
|
||||
set_bit(USB_OHCI_LOADED, &usb_hcds_loaded);
|
||||
|
||||
#ifdef DEBUG
|
||||
ohci_debug_root = debugfs_create_dir("ohci", NULL);
|
||||
ohci_debug_root = debugfs_create_dir("ohci", usb_debug_root);
|
||||
if (!ohci_debug_root) {
|
||||
retval = -ENOENT;
|
||||
goto error_debug;
|
||||
|
@ -372,7 +372,7 @@ static int __devinit ohci_pci_start (struct usb_hcd *hcd)
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
|
||||
static int ohci_pci_suspend(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
|
||||
unsigned long flags;
|
||||
@ -394,10 +394,6 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
|
||||
ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
|
||||
(void)ohci_readl(ohci, &ohci->regs->intrdisable);
|
||||
|
||||
/* make sure snapshot being resumed re-enumerates everything */
|
||||
if (message.event == PM_EVENT_PRETHAW)
|
||||
ohci_usb_reset(ohci);
|
||||
|
||||
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
bail:
|
||||
spin_unlock_irqrestore (&ohci->lock, flags);
|
||||
@ -406,9 +402,14 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
|
||||
}
|
||||
|
||||
|
||||
static int ohci_pci_resume (struct usb_hcd *hcd)
|
||||
static int ohci_pci_resume(struct usb_hcd *hcd, bool hibernated)
|
||||
{
|
||||
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
|
||||
/* Make sure resume from hibernation re-enumerates everything */
|
||||
if (hibernated)
|
||||
ohci_usb_reset(hcd_to_ohci(hcd));
|
||||
|
||||
ohci_finish_controller_resume(hcd);
|
||||
return 0;
|
||||
}
|
||||
@ -484,12 +485,11 @@ static struct pci_driver ohci_pci_driver = {
|
||||
|
||||
.probe = usb_hcd_pci_probe,
|
||||
.remove = usb_hcd_pci_remove,
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = usb_hcd_pci_suspend,
|
||||
.resume = usb_hcd_pci_resume,
|
||||
#endif
|
||||
|
||||
.shutdown = usb_hcd_pci_shutdown,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.driver = {
|
||||
.pm = &usb_hcd_pci_pm_ops
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/acpi.h>
|
||||
#include "pci-quirks.h"
|
||||
#include "xhci-ext-caps.h"
|
||||
|
||||
|
||||
#define UHCI_USBLEGSUP 0xc0 /* legacy support */
|
||||
@ -341,7 +342,127 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* handshake - spin reading a register until handshake completes
|
||||
* @ptr: address of hc register to be read
|
||||
* @mask: bits to look at in result of read
|
||||
* @done: value of those bits when handshake succeeds
|
||||
* @wait_usec: timeout in microseconds
|
||||
* @delay_usec: delay in microseconds to wait between polling
|
||||
*
|
||||
* Polls a register every delay_usec microseconds.
|
||||
* Returns 0 when the mask bits have the value done.
|
||||
* Returns -ETIMEDOUT if this condition is not true after
|
||||
* wait_usec microseconds have passed.
|
||||
*/
|
||||
static int handshake(void __iomem *ptr, u32 mask, u32 done,
|
||||
int wait_usec, int delay_usec)
|
||||
{
|
||||
u32 result;
|
||||
|
||||
do {
|
||||
result = readl(ptr);
|
||||
result &= mask;
|
||||
if (result == done)
|
||||
return 0;
|
||||
udelay(delay_usec);
|
||||
wait_usec -= delay_usec;
|
||||
} while (wait_usec > 0);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* PCI Quirks for xHCI.
|
||||
*
|
||||
* Takes care of the handoff between the Pre-OS (i.e. BIOS) and the OS.
|
||||
* It signals to the BIOS that the OS wants control of the host controller,
|
||||
* and then waits 5 seconds for the BIOS to hand over control.
|
||||
* If we timeout, assume the BIOS is broken and take control anyway.
|
||||
*/
|
||||
static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev)
|
||||
{
|
||||
void __iomem *base;
|
||||
int ext_cap_offset;
|
||||
void __iomem *op_reg_base;
|
||||
u32 val;
|
||||
int timeout;
|
||||
|
||||
if (!mmio_resource_enabled(pdev, 0))
|
||||
return;
|
||||
|
||||
base = ioremap_nocache(pci_resource_start(pdev, 0),
|
||||
pci_resource_len(pdev, 0));
|
||||
if (base == NULL)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Find the Legacy Support Capability register -
|
||||
* this is optional for xHCI host controllers.
|
||||
*/
|
||||
ext_cap_offset = xhci_find_next_cap_offset(base, XHCI_HCC_PARAMS_OFFSET);
|
||||
do {
|
||||
if (!ext_cap_offset)
|
||||
/* We've reached the end of the extended capabilities */
|
||||
goto hc_init;
|
||||
val = readl(base + ext_cap_offset);
|
||||
if (XHCI_EXT_CAPS_ID(val) == XHCI_EXT_CAPS_LEGACY)
|
||||
break;
|
||||
ext_cap_offset = xhci_find_next_cap_offset(base, ext_cap_offset);
|
||||
} while (1);
|
||||
|
||||
/* If the BIOS owns the HC, signal that the OS wants it, and wait */
|
||||
if (val & XHCI_HC_BIOS_OWNED) {
|
||||
writel(val & XHCI_HC_OS_OWNED, base + ext_cap_offset);
|
||||
|
||||
/* Wait for 5 seconds with 10 microsecond polling interval */
|
||||
timeout = handshake(base + ext_cap_offset, XHCI_HC_BIOS_OWNED,
|
||||
0, 5000, 10);
|
||||
|
||||
/* Assume a buggy BIOS and take HC ownership anyway */
|
||||
if (timeout) {
|
||||
dev_warn(&pdev->dev, "xHCI BIOS handoff failed"
|
||||
" (BIOS bug ?) %08x\n", val);
|
||||
writel(val & ~XHCI_HC_BIOS_OWNED, base + ext_cap_offset);
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable any BIOS SMIs */
|
||||
writel(XHCI_LEGACY_DISABLE_SMI,
|
||||
base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET);
|
||||
|
||||
hc_init:
|
||||
op_reg_base = base + XHCI_HC_LENGTH(readl(base));
|
||||
|
||||
/* Wait for the host controller to be ready before writing any
|
||||
* operational or runtime registers. Wait 5 seconds and no more.
|
||||
*/
|
||||
timeout = handshake(op_reg_base + XHCI_STS_OFFSET, XHCI_STS_CNR, 0,
|
||||
5000, 10);
|
||||
/* Assume a buggy HC and start HC initialization anyway */
|
||||
if (timeout) {
|
||||
val = readl(op_reg_base + XHCI_STS_OFFSET);
|
||||
dev_warn(&pdev->dev,
|
||||
"xHCI HW not ready after 5 sec (HC bug?) "
|
||||
"status = 0x%x\n", val);
|
||||
}
|
||||
|
||||
/* Send the halt and disable interrupts command */
|
||||
val = readl(op_reg_base + XHCI_CMD_OFFSET);
|
||||
val &= ~(XHCI_CMD_RUN | XHCI_IRQS);
|
||||
writel(val, op_reg_base + XHCI_CMD_OFFSET);
|
||||
|
||||
/* Wait for the HC to halt - poll every 125 usec (one microframe). */
|
||||
timeout = handshake(op_reg_base + XHCI_STS_OFFSET, XHCI_STS_HALT, 1,
|
||||
XHCI_MAX_HALT_USEC, 125);
|
||||
if (timeout) {
|
||||
val = readl(op_reg_base + XHCI_STS_OFFSET);
|
||||
dev_warn(&pdev->dev,
|
||||
"xHCI HW did not halt within %d usec "
|
||||
"status = 0x%x\n", XHCI_MAX_HALT_USEC, val);
|
||||
}
|
||||
|
||||
iounmap(base);
|
||||
}
|
||||
|
||||
static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev)
|
||||
{
|
||||
@ -351,5 +472,7 @@ static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev)
|
||||
quirk_usb_handoff_ohci(pdev);
|
||||
else if (pdev->class == PCI_CLASS_SERIAL_USB_EHCI)
|
||||
quirk_usb_disable_ehci(pdev);
|
||||
else if (pdev->class == PCI_CLASS_SERIAL_USB_XHCI)
|
||||
quirk_usb_handoff_xhci(pdev);
|
||||
}
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, quirk_usb_early_handoff);
|
||||
|
@ -46,31 +46,10 @@ MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Yoshihiro Shimoda");
|
||||
MODULE_ALIAS("platform:r8a66597_hcd");
|
||||
|
||||
#define DRIVER_VERSION "10 Apr 2008"
|
||||
#define DRIVER_VERSION "2009-05-26"
|
||||
|
||||
static const char hcd_name[] = "r8a66597_hcd";
|
||||
|
||||
/* module parameters */
|
||||
#if !defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
|
||||
static unsigned short clock = XTAL12;
|
||||
module_param(clock, ushort, 0644);
|
||||
MODULE_PARM_DESC(clock, "input clock: 48MHz=32768, 24MHz=16384, 12MHz=0 "
|
||||
"(default=0)");
|
||||
#endif
|
||||
|
||||
static unsigned short vif = 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 = 0xff;
|
||||
module_param(irq_sense, ushort, 0644);
|
||||
MODULE_PARM_DESC(irq_sense, "IRQ sense: low level=32, falling edge=0 "
|
||||
"(default=32)");
|
||||
|
||||
static void packet_write(struct r8a66597 *r8a66597, u16 pipenum);
|
||||
static int r8a66597_get_frame(struct usb_hcd *hcd);
|
||||
|
||||
@ -136,7 +115,8 @@ static int r8a66597_clock_enable(struct r8a66597 *r8a66597)
|
||||
}
|
||||
} while ((tmp & USBE) != USBE);
|
||||
r8a66597_bclr(r8a66597, USBE, SYSCFG0);
|
||||
r8a66597_mdfy(r8a66597, clock, XTAL, SYSCFG0);
|
||||
r8a66597_mdfy(r8a66597, get_xtal_from_pdata(r8a66597->pdata), XTAL,
|
||||
SYSCFG0);
|
||||
|
||||
i = 0;
|
||||
r8a66597_bset(r8a66597, XCKE, SYSCFG0);
|
||||
@ -203,6 +183,9 @@ static void r8a66597_disable_port(struct r8a66597 *r8a66597, int port)
|
||||
static int enable_controller(struct r8a66597 *r8a66597)
|
||||
{
|
||||
int ret, port;
|
||||
u16 vif = r8a66597->pdata->vif ? LDRV : 0;
|
||||
u16 irq_sense = r8a66597->irq_sense_low ? INTL : 0;
|
||||
u16 endian = r8a66597->pdata->endian ? BIGEND : 0;
|
||||
|
||||
ret = r8a66597_clock_enable(r8a66597);
|
||||
if (ret < 0)
|
||||
@ -2373,7 +2356,7 @@ static int __init_or_module r8a66597_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init r8a66597_probe(struct platform_device *pdev)
|
||||
static int __devinit r8a66597_probe(struct platform_device *pdev)
|
||||
{
|
||||
#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
|
||||
char clk_name[8];
|
||||
@ -2418,6 +2401,12 @@ static int __init r8a66597_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 hcd */
|
||||
hcd = usb_create_hcd(&r8a66597_hc_driver, &pdev->dev, (char *)hcd_name);
|
||||
if (!hcd) {
|
||||
@ -2428,6 +2417,8 @@ static int __init r8a66597_probe(struct platform_device *pdev)
|
||||
r8a66597 = hcd_to_r8a66597(hcd);
|
||||
memset(r8a66597, 0, sizeof(struct r8a66597));
|
||||
dev_set_drvdata(&pdev->dev, r8a66597);
|
||||
r8a66597->pdata = pdev->dev.platform_data;
|
||||
r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW;
|
||||
|
||||
#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
|
||||
snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id);
|
||||
@ -2458,29 +2449,6 @@ static int __init r8a66597_probe(struct platform_device *pdev)
|
||||
|
||||
hcd->rsrc_start = res->start;
|
||||
|
||||
/* irq_sense setting on cmdline takes precedence over resource
|
||||
* settings, so the introduction of irqflags in IRQ resourse
|
||||
* won't disturb existing setups */
|
||||
switch (irq_sense) {
|
||||
case INTL:
|
||||
irq_trigger = IRQF_TRIGGER_LOW;
|
||||
break;
|
||||
case 0:
|
||||
irq_trigger = IRQF_TRIGGER_FALLING;
|
||||
break;
|
||||
case 0xff:
|
||||
if (irq_trigger)
|
||||
irq_sense = (irq_trigger & IRQF_TRIGGER_LOW) ?
|
||||
INTL : 0;
|
||||
else {
|
||||
irq_sense = INTL;
|
||||
irq_trigger = IRQF_TRIGGER_LOW;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev, "Unknown irq_sense value.\n");
|
||||
}
|
||||
|
||||
ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | irq_trigger);
|
||||
if (ret != 0) {
|
||||
dev_err(&pdev->dev, "Failed to add hcd\n");
|
||||
|
@ -30,6 +30,8 @@
|
||||
#include <linux/clk.h>
|
||||
#endif
|
||||
|
||||
#include <linux/usb/r8a66597.h>
|
||||
|
||||
#define SYSCFG0 0x00
|
||||
#define SYSCFG1 0x02
|
||||
#define SYSSTS0 0x04
|
||||
@ -488,6 +490,7 @@ struct r8a66597 {
|
||||
#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
|
||||
struct clk *clk;
|
||||
#endif
|
||||
struct r8a66597_platdata *pdata;
|
||||
struct r8a66597_device device0;
|
||||
struct r8a66597_root_hub root_hub[R8A66597_MAX_ROOT_HUB];
|
||||
struct list_head pipe_queue[R8A66597_MAX_NUM_PIPE];
|
||||
@ -506,6 +509,7 @@ struct r8a66597 {
|
||||
unsigned long child_connect_map[4];
|
||||
|
||||
unsigned bus_suspended:1;
|
||||
unsigned irq_sense_low:1;
|
||||
};
|
||||
|
||||
static inline struct r8a66597 *hcd_to_r8a66597(struct usb_hcd *hcd)
|
||||
@ -660,10 +664,36 @@ static inline void r8a66597_port_power(struct r8a66597 *r8a66597, int port,
|
||||
{
|
||||
unsigned long dvstctr_reg = get_dvstctr_reg(port);
|
||||
|
||||
if (power)
|
||||
r8a66597_bset(r8a66597, VBOUT, dvstctr_reg);
|
||||
else
|
||||
r8a66597_bclr(r8a66597, VBOUT, dvstctr_reg);
|
||||
if (r8a66597->pdata->port_power) {
|
||||
r8a66597->pdata->port_power(port, power);
|
||||
} else {
|
||||
if (power)
|
||||
r8a66597_bset(r8a66597, VBOUT, dvstctr_reg);
|
||||
else
|
||||
r8a66597_bclr(r8a66597, VBOUT, dvstctr_reg);
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 get_xtal_from_pdata(struct r8a66597_platdata *pdata)
|
||||
{
|
||||
u16 clock = 0;
|
||||
|
||||
switch (pdata->xtal) {
|
||||
case R8A66597_PLATDATA_XTAL_12MHZ:
|
||||
clock = XTAL12;
|
||||
break;
|
||||
case R8A66597_PLATDATA_XTAL_24MHZ:
|
||||
clock = XTAL24;
|
||||
break;
|
||||
case R8A66597_PLATDATA_XTAL_48MHZ:
|
||||
clock = XTAL48;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "r8a66597: platdata clock is wrong.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return clock;
|
||||
}
|
||||
|
||||
#define get_pipectr_addr(pipenum) (PIPE1CTR + (pipenum - 1) * 2)
|
||||
|
@ -769,7 +769,7 @@ static int uhci_rh_resume(struct usb_hcd *hcd)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int uhci_pci_suspend(struct usb_hcd *hcd, pm_message_t message)
|
||||
static int uhci_pci_suspend(struct usb_hcd *hcd)
|
||||
{
|
||||
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
|
||||
int rc = 0;
|
||||
@ -795,10 +795,6 @@ static int uhci_pci_suspend(struct usb_hcd *hcd, pm_message_t message)
|
||||
|
||||
/* FIXME: Enable non-PME# remote wakeup? */
|
||||
|
||||
/* make sure snapshot being resumed re-enumerates everything */
|
||||
if (message.event == PM_EVENT_PRETHAW)
|
||||
uhci_hc_died(uhci);
|
||||
|
||||
done_okay:
|
||||
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
done:
|
||||
@ -806,7 +802,7 @@ done:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int uhci_pci_resume(struct usb_hcd *hcd)
|
||||
static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
|
||||
{
|
||||
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
|
||||
|
||||
@ -820,6 +816,10 @@ static int uhci_pci_resume(struct usb_hcd *hcd)
|
||||
|
||||
spin_lock_irq(&uhci->lock);
|
||||
|
||||
/* Make sure resume from hibernation re-enumerates everything */
|
||||
if (hibernated)
|
||||
uhci_hc_died(uhci);
|
||||
|
||||
/* FIXME: Disable non-PME# remote wakeup? */
|
||||
|
||||
/* The firmware or a boot kernel may have changed the controller
|
||||
@ -940,10 +940,11 @@ static struct pci_driver uhci_pci_driver = {
|
||||
.remove = usb_hcd_pci_remove,
|
||||
.shutdown = uhci_shutdown,
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = usb_hcd_pci_suspend,
|
||||
.resume = usb_hcd_pci_resume,
|
||||
#endif /* PM */
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.driver = {
|
||||
.pm = &usb_hcd_pci_pm_ops
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
static int __init uhci_hcd_init(void)
|
||||
@ -961,7 +962,7 @@ static int __init uhci_hcd_init(void)
|
||||
errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);
|
||||
if (!errbuf)
|
||||
goto errbuf_failed;
|
||||
uhci_debugfs_root = debugfs_create_dir("uhci", NULL);
|
||||
uhci_debugfs_root = debugfs_create_dir("uhci", usb_debug_root);
|
||||
if (!uhci_debugfs_root)
|
||||
goto debug_failed;
|
||||
}
|
||||
|
@ -260,7 +260,7 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci,
|
||||
INIT_LIST_HEAD(&qh->node);
|
||||
|
||||
if (udev) { /* Normal QH */
|
||||
qh->type = hep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
|
||||
qh->type = usb_endpoint_type(&hep->desc);
|
||||
if (qh->type != USB_ENDPOINT_XFER_ISOC) {
|
||||
qh->dummy_td = uhci_alloc_td(uhci);
|
||||
if (!qh->dummy_td) {
|
||||
|
485
drivers/usb/host/xhci-dbg.c
Normal file
485
drivers/usb/host/xhci-dbg.c
Normal file
@ -0,0 +1,485 @@
|
||||
/*
|
||||
* xHCI host controller driver
|
||||
*
|
||||
* Copyright (C) 2008 Intel Corp.
|
||||
*
|
||||
* Author: Sarah Sharp
|
||||
* Some code borrowed from the Linux EHCI driver.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "xhci.h"
|
||||
|
||||
#define XHCI_INIT_VALUE 0x0
|
||||
|
||||
/* Add verbose debugging later, just print everything for now */
|
||||
|
||||
void xhci_dbg_regs(struct xhci_hcd *xhci)
|
||||
{
|
||||
u32 temp;
|
||||
|
||||
xhci_dbg(xhci, "// xHCI capability registers at %p:\n",
|
||||
xhci->cap_regs);
|
||||
temp = xhci_readl(xhci, &xhci->cap_regs->hc_capbase);
|
||||
xhci_dbg(xhci, "// @%p = 0x%x (CAPLENGTH AND HCIVERSION)\n",
|
||||
&xhci->cap_regs->hc_capbase, temp);
|
||||
xhci_dbg(xhci, "// CAPLENGTH: 0x%x\n",
|
||||
(unsigned int) HC_LENGTH(temp));
|
||||
#if 0
|
||||
xhci_dbg(xhci, "// HCIVERSION: 0x%x\n",
|
||||
(unsigned int) HC_VERSION(temp));
|
||||
#endif
|
||||
|
||||
xhci_dbg(xhci, "// xHCI operational registers at %p:\n", xhci->op_regs);
|
||||
|
||||
temp = xhci_readl(xhci, &xhci->cap_regs->run_regs_off);
|
||||
xhci_dbg(xhci, "// @%p = 0x%x RTSOFF\n",
|
||||
&xhci->cap_regs->run_regs_off,
|
||||
(unsigned int) temp & RTSOFF_MASK);
|
||||
xhci_dbg(xhci, "// xHCI runtime registers at %p:\n", xhci->run_regs);
|
||||
|
||||
temp = xhci_readl(xhci, &xhci->cap_regs->db_off);
|
||||
xhci_dbg(xhci, "// @%p = 0x%x DBOFF\n", &xhci->cap_regs->db_off, temp);
|
||||
xhci_dbg(xhci, "// Doorbell array at %p:\n", xhci->dba);
|
||||
}
|
||||
|
||||
static void xhci_print_cap_regs(struct xhci_hcd *xhci)
|
||||
{
|
||||
u32 temp;
|
||||
|
||||
xhci_dbg(xhci, "xHCI capability registers at %p:\n", xhci->cap_regs);
|
||||
|
||||
temp = xhci_readl(xhci, &xhci->cap_regs->hc_capbase);
|
||||
xhci_dbg(xhci, "CAPLENGTH AND HCIVERSION 0x%x:\n",
|
||||
(unsigned int) temp);
|
||||
xhci_dbg(xhci, "CAPLENGTH: 0x%x\n",
|
||||
(unsigned int) HC_LENGTH(temp));
|
||||
xhci_dbg(xhci, "HCIVERSION: 0x%x\n",
|
||||
(unsigned int) HC_VERSION(temp));
|
||||
|
||||
temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params1);
|
||||
xhci_dbg(xhci, "HCSPARAMS 1: 0x%x\n",
|
||||
(unsigned int) temp);
|
||||
xhci_dbg(xhci, " Max device slots: %u\n",
|
||||
(unsigned int) HCS_MAX_SLOTS(temp));
|
||||
xhci_dbg(xhci, " Max interrupters: %u\n",
|
||||
(unsigned int) HCS_MAX_INTRS(temp));
|
||||
xhci_dbg(xhci, " Max ports: %u\n",
|
||||
(unsigned int) HCS_MAX_PORTS(temp));
|
||||
|
||||
temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params2);
|
||||
xhci_dbg(xhci, "HCSPARAMS 2: 0x%x\n",
|
||||
(unsigned int) temp);
|
||||
xhci_dbg(xhci, " Isoc scheduling threshold: %u\n",
|
||||
(unsigned int) HCS_IST(temp));
|
||||
xhci_dbg(xhci, " Maximum allowed segments in event ring: %u\n",
|
||||
(unsigned int) HCS_ERST_MAX(temp));
|
||||
|
||||
temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params3);
|
||||
xhci_dbg(xhci, "HCSPARAMS 3 0x%x:\n",
|
||||
(unsigned int) temp);
|
||||
xhci_dbg(xhci, " Worst case U1 device exit latency: %u\n",
|
||||
(unsigned int) HCS_U1_LATENCY(temp));
|
||||
xhci_dbg(xhci, " Worst case U2 device exit latency: %u\n",
|
||||
(unsigned int) HCS_U2_LATENCY(temp));
|
||||
|
||||
temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params);
|
||||
xhci_dbg(xhci, "HCC PARAMS 0x%x:\n", (unsigned int) temp);
|
||||
xhci_dbg(xhci, " HC generates %s bit addresses\n",
|
||||
HCC_64BIT_ADDR(temp) ? "64" : "32");
|
||||
/* FIXME */
|
||||
xhci_dbg(xhci, " FIXME: more HCCPARAMS debugging\n");
|
||||
|
||||
temp = xhci_readl(xhci, &xhci->cap_regs->run_regs_off);
|
||||
xhci_dbg(xhci, "RTSOFF 0x%x:\n", temp & RTSOFF_MASK);
|
||||
}
|
||||
|
||||
static void xhci_print_command_reg(struct xhci_hcd *xhci)
|
||||
{
|
||||
u32 temp;
|
||||
|
||||
temp = xhci_readl(xhci, &xhci->op_regs->command);
|
||||
xhci_dbg(xhci, "USBCMD 0x%x:\n", temp);
|
||||
xhci_dbg(xhci, " HC is %s\n",
|
||||
(temp & CMD_RUN) ? "running" : "being stopped");
|
||||
xhci_dbg(xhci, " HC has %sfinished hard reset\n",
|
||||
(temp & CMD_RESET) ? "not " : "");
|
||||
xhci_dbg(xhci, " Event Interrupts %s\n",
|
||||
(temp & CMD_EIE) ? "enabled " : "disabled");
|
||||
xhci_dbg(xhci, " Host System Error Interrupts %s\n",
|
||||
(temp & CMD_EIE) ? "enabled " : "disabled");
|
||||
xhci_dbg(xhci, " HC has %sfinished light reset\n",
|
||||
(temp & CMD_LRESET) ? "not " : "");
|
||||
}
|
||||
|
||||
static void xhci_print_status(struct xhci_hcd *xhci)
|
||||
{
|
||||
u32 temp;
|
||||
|
||||
temp = xhci_readl(xhci, &xhci->op_regs->status);
|
||||
xhci_dbg(xhci, "USBSTS 0x%x:\n", temp);
|
||||
xhci_dbg(xhci, " Event ring is %sempty\n",
|
||||
(temp & STS_EINT) ? "not " : "");
|
||||
xhci_dbg(xhci, " %sHost System Error\n",
|
||||
(temp & STS_FATAL) ? "WARNING: " : "No ");
|
||||
xhci_dbg(xhci, " HC is %s\n",
|
||||
(temp & STS_HALT) ? "halted" : "running");
|
||||
}
|
||||
|
||||
static void xhci_print_op_regs(struct xhci_hcd *xhci)
|
||||
{
|
||||
xhci_dbg(xhci, "xHCI operational registers at %p:\n", xhci->op_regs);
|
||||
xhci_print_command_reg(xhci);
|
||||
xhci_print_status(xhci);
|
||||
}
|
||||
|
||||
static void xhci_print_ports(struct xhci_hcd *xhci)
|
||||
{
|
||||
u32 __iomem *addr;
|
||||
int i, j;
|
||||
int ports;
|
||||
char *names[NUM_PORT_REGS] = {
|
||||
"status",
|
||||
"power",
|
||||
"link",
|
||||
"reserved",
|
||||
};
|
||||
|
||||
ports = HCS_MAX_PORTS(xhci->hcs_params1);
|
||||
addr = &xhci->op_regs->port_status_base;
|
||||
for (i = 0; i < ports; i++) {
|
||||
for (j = 0; j < NUM_PORT_REGS; ++j) {
|
||||
xhci_dbg(xhci, "%p port %s reg = 0x%x\n",
|
||||
addr, names[j],
|
||||
(unsigned int) xhci_readl(xhci, addr));
|
||||
addr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void xhci_print_ir_set(struct xhci_hcd *xhci, struct xhci_intr_reg *ir_set, int set_num)
|
||||
{
|
||||
void *addr;
|
||||
u32 temp;
|
||||
|
||||
addr = &ir_set->irq_pending;
|
||||
temp = xhci_readl(xhci, addr);
|
||||
if (temp == XHCI_INIT_VALUE)
|
||||
return;
|
||||
|
||||
xhci_dbg(xhci, " %p: ir_set[%i]\n", ir_set, set_num);
|
||||
|
||||
xhci_dbg(xhci, " %p: ir_set.pending = 0x%x\n", addr,
|
||||
(unsigned int)temp);
|
||||
|
||||
addr = &ir_set->irq_control;
|
||||
temp = xhci_readl(xhci, addr);
|
||||
xhci_dbg(xhci, " %p: ir_set.control = 0x%x\n", addr,
|
||||
(unsigned int)temp);
|
||||
|
||||
addr = &ir_set->erst_size;
|
||||
temp = xhci_readl(xhci, addr);
|
||||
xhci_dbg(xhci, " %p: ir_set.erst_size = 0x%x\n", addr,
|
||||
(unsigned int)temp);
|
||||
|
||||
addr = &ir_set->rsvd;
|
||||
temp = xhci_readl(xhci, addr);
|
||||
if (temp != XHCI_INIT_VALUE)
|
||||
xhci_dbg(xhci, " WARN: %p: ir_set.rsvd = 0x%x\n",
|
||||
addr, (unsigned int)temp);
|
||||
|
||||
addr = &ir_set->erst_base[0];
|
||||
temp = xhci_readl(xhci, addr);
|
||||
xhci_dbg(xhci, " %p: ir_set.erst_base[0] = 0x%x\n",
|
||||
addr, (unsigned int) temp);
|
||||
|
||||
addr = &ir_set->erst_base[1];
|
||||
temp = xhci_readl(xhci, addr);
|
||||
xhci_dbg(xhci, " %p: ir_set.erst_base[1] = 0x%x\n",
|
||||
addr, (unsigned int) temp);
|
||||
|
||||
addr = &ir_set->erst_dequeue[0];
|
||||
temp = xhci_readl(xhci, addr);
|
||||
xhci_dbg(xhci, " %p: ir_set.erst_dequeue[0] = 0x%x\n",
|
||||
addr, (unsigned int) temp);
|
||||
|
||||
addr = &ir_set->erst_dequeue[1];
|
||||
temp = xhci_readl(xhci, addr);
|
||||
xhci_dbg(xhci, " %p: ir_set.erst_dequeue[1] = 0x%x\n",
|
||||
addr, (unsigned int) temp);
|
||||
}
|
||||
|
||||
void xhci_print_run_regs(struct xhci_hcd *xhci)
|
||||
{
|
||||
u32 temp;
|
||||
int i;
|
||||
|
||||
xhci_dbg(xhci, "xHCI runtime registers at %p:\n", xhci->run_regs);
|
||||
temp = xhci_readl(xhci, &xhci->run_regs->microframe_index);
|
||||
xhci_dbg(xhci, " %p: Microframe index = 0x%x\n",
|
||||
&xhci->run_regs->microframe_index,
|
||||
(unsigned int) temp);
|
||||
for (i = 0; i < 7; ++i) {
|
||||
temp = xhci_readl(xhci, &xhci->run_regs->rsvd[i]);
|
||||
if (temp != XHCI_INIT_VALUE)
|
||||
xhci_dbg(xhci, " WARN: %p: Rsvd[%i] = 0x%x\n",
|
||||
&xhci->run_regs->rsvd[i],
|
||||
i, (unsigned int) temp);
|
||||
}
|
||||
}
|
||||
|
||||
void xhci_print_registers(struct xhci_hcd *xhci)
|
||||
{
|
||||
xhci_print_cap_regs(xhci);
|
||||
xhci_print_op_regs(xhci);
|
||||
xhci_print_ports(xhci);
|
||||
}
|
||||
|
||||
void xhci_print_trb_offsets(struct xhci_hcd *xhci, union xhci_trb *trb)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i)
|
||||
xhci_dbg(xhci, "Offset 0x%x = 0x%x\n",
|
||||
i*4, trb->generic.field[i]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug a transfer request block (TRB).
|
||||
*/
|
||||
void xhci_debug_trb(struct xhci_hcd *xhci, union xhci_trb *trb)
|
||||
{
|
||||
u64 address;
|
||||
u32 type = xhci_readl(xhci, &trb->link.control) & TRB_TYPE_BITMASK;
|
||||
|
||||
switch (type) {
|
||||
case TRB_TYPE(TRB_LINK):
|
||||
xhci_dbg(xhci, "Link TRB:\n");
|
||||
xhci_print_trb_offsets(xhci, trb);
|
||||
|
||||
address = trb->link.segment_ptr[0] +
|
||||
(((u64) trb->link.segment_ptr[1]) << 32);
|
||||
xhci_dbg(xhci, "Next ring segment DMA address = 0x%llx\n", address);
|
||||
|
||||
xhci_dbg(xhci, "Interrupter target = 0x%x\n",
|
||||
GET_INTR_TARGET(trb->link.intr_target));
|
||||
xhci_dbg(xhci, "Cycle bit = %u\n",
|
||||
(unsigned int) (trb->link.control & TRB_CYCLE));
|
||||
xhci_dbg(xhci, "Toggle cycle bit = %u\n",
|
||||
(unsigned int) (trb->link.control & LINK_TOGGLE));
|
||||
xhci_dbg(xhci, "No Snoop bit = %u\n",
|
||||
(unsigned int) (trb->link.control & TRB_NO_SNOOP));
|
||||
break;
|
||||
case TRB_TYPE(TRB_TRANSFER):
|
||||
address = trb->trans_event.buffer[0] +
|
||||
(((u64) trb->trans_event.buffer[1]) << 32);
|
||||
/*
|
||||
* FIXME: look at flags to figure out if it's an address or if
|
||||
* the data is directly in the buffer field.
|
||||
*/
|
||||
xhci_dbg(xhci, "DMA address or buffer contents= %llu\n", address);
|
||||
break;
|
||||
case TRB_TYPE(TRB_COMPLETION):
|
||||
address = trb->event_cmd.cmd_trb[0] +
|
||||
(((u64) trb->event_cmd.cmd_trb[1]) << 32);
|
||||
xhci_dbg(xhci, "Command TRB pointer = %llu\n", address);
|
||||
xhci_dbg(xhci, "Completion status = %u\n",
|
||||
(unsigned int) GET_COMP_CODE(trb->event_cmd.status));
|
||||
xhci_dbg(xhci, "Flags = 0x%x\n", (unsigned int) trb->event_cmd.flags);
|
||||
break;
|
||||
default:
|
||||
xhci_dbg(xhci, "Unknown TRB with TRB type ID %u\n",
|
||||
(unsigned int) type>>10);
|
||||
xhci_print_trb_offsets(xhci, trb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug a segment with an xHCI ring.
|
||||
*
|
||||
* @return The Link TRB of the segment, or NULL if there is no Link TRB
|
||||
* (which is a bug, since all segments must have a Link TRB).
|
||||
*
|
||||
* Prints out all TRBs in the segment, even those after the Link TRB.
|
||||
*
|
||||
* XXX: should we print out TRBs that the HC owns? As long as we don't
|
||||
* write, that should be fine... We shouldn't expect that the memory pointed to
|
||||
* by the TRB is valid at all. Do we care about ones the HC owns? Probably,
|
||||
* for HC debugging.
|
||||
*/
|
||||
void xhci_debug_segment(struct xhci_hcd *xhci, struct xhci_segment *seg)
|
||||
{
|
||||
int i;
|
||||
u32 addr = (u32) seg->dma;
|
||||
union xhci_trb *trb = seg->trbs;
|
||||
|
||||
for (i = 0; i < TRBS_PER_SEGMENT; ++i) {
|
||||
trb = &seg->trbs[i];
|
||||
xhci_dbg(xhci, "@%08x %08x %08x %08x %08x\n", addr,
|
||||
(unsigned int) trb->link.segment_ptr[0],
|
||||
(unsigned int) trb->link.segment_ptr[1],
|
||||
(unsigned int) trb->link.intr_target,
|
||||
(unsigned int) trb->link.control);
|
||||
addr += sizeof(*trb);
|
||||
}
|
||||
}
|
||||
|
||||
void xhci_dbg_ring_ptrs(struct xhci_hcd *xhci, struct xhci_ring *ring)
|
||||
{
|
||||
xhci_dbg(xhci, "Ring deq = %p (virt), 0x%llx (dma)\n",
|
||||
ring->dequeue,
|
||||
(unsigned long long)xhci_trb_virt_to_dma(ring->deq_seg,
|
||||
ring->dequeue));
|
||||
xhci_dbg(xhci, "Ring deq updated %u times\n",
|
||||
ring->deq_updates);
|
||||
xhci_dbg(xhci, "Ring enq = %p (virt), 0x%llx (dma)\n",
|
||||
ring->enqueue,
|
||||
(unsigned long long)xhci_trb_virt_to_dma(ring->enq_seg,
|
||||
ring->enqueue));
|
||||
xhci_dbg(xhci, "Ring enq updated %u times\n",
|
||||
ring->enq_updates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Debugging for an xHCI ring, which is a queue broken into multiple segments.
|
||||
*
|
||||
* Print out each segment in the ring. Check that the DMA address in
|
||||
* each link segment actually matches the segment's stored DMA address.
|
||||
* Check that the link end bit is only set at the end of the ring.
|
||||
* Check that the dequeue and enqueue pointers point to real data in this ring
|
||||
* (not some other ring).
|
||||
*/
|
||||
void xhci_debug_ring(struct xhci_hcd *xhci, struct xhci_ring *ring)
|
||||
{
|
||||
/* FIXME: Throw an error if any segment doesn't have a Link TRB */
|
||||
struct xhci_segment *seg;
|
||||
struct xhci_segment *first_seg = ring->first_seg;
|
||||
xhci_debug_segment(xhci, first_seg);
|
||||
|
||||
if (!ring->enq_updates && !ring->deq_updates) {
|
||||
xhci_dbg(xhci, " Ring has not been updated\n");
|
||||
return;
|
||||
}
|
||||
for (seg = first_seg->next; seg != first_seg; seg = seg->next)
|
||||
xhci_debug_segment(xhci, seg);
|
||||
}
|
||||
|
||||
void xhci_dbg_erst(struct xhci_hcd *xhci, struct xhci_erst *erst)
|
||||
{
|
||||
u32 addr = (u32) erst->erst_dma_addr;
|
||||
int i;
|
||||
struct xhci_erst_entry *entry;
|
||||
|
||||
for (i = 0; i < erst->num_entries; ++i) {
|
||||
entry = &erst->entries[i];
|
||||
xhci_dbg(xhci, "@%08x %08x %08x %08x %08x\n",
|
||||
(unsigned int) addr,
|
||||
(unsigned int) entry->seg_addr[0],
|
||||
(unsigned int) entry->seg_addr[1],
|
||||
(unsigned int) entry->seg_size,
|
||||
(unsigned int) entry->rsvd);
|
||||
addr += sizeof(*entry);
|
||||
}
|
||||
}
|
||||
|
||||
void xhci_dbg_cmd_ptrs(struct xhci_hcd *xhci)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = xhci_readl(xhci, &xhci->op_regs->cmd_ring[0]);
|
||||
xhci_dbg(xhci, "// xHC command ring deq ptr low bits + flags = 0x%x\n", val);
|
||||
val = xhci_readl(xhci, &xhci->op_regs->cmd_ring[1]);
|
||||
xhci_dbg(xhci, "// xHC command ring deq ptr high bits = 0x%x\n", val);
|
||||
}
|
||||
|
||||
void xhci_dbg_ctx(struct xhci_hcd *xhci, struct xhci_device_control *ctx, dma_addr_t dma, unsigned int last_ep)
|
||||
{
|
||||
int i, j;
|
||||
int last_ep_ctx = 31;
|
||||
/* Fields are 32 bits wide, DMA addresses are in bytes */
|
||||
int field_size = 32 / 8;
|
||||
|
||||
xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - drop flags\n",
|
||||
&ctx->drop_flags, (unsigned long long)dma,
|
||||
ctx->drop_flags);
|
||||
dma += field_size;
|
||||
xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - add flags\n",
|
||||
&ctx->add_flags, (unsigned long long)dma,
|
||||
ctx->add_flags);
|
||||
dma += field_size;
|
||||
for (i = 0; i > 6; ++i) {
|
||||
xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - rsvd[%d]\n",
|
||||
&ctx->rsvd[i], (unsigned long long)dma,
|
||||
ctx->rsvd[i], i);
|
||||
dma += field_size;
|
||||
}
|
||||
|
||||
xhci_dbg(xhci, "Slot Context:\n");
|
||||
xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - dev_info\n",
|
||||
&ctx->slot.dev_info,
|
||||
(unsigned long long)dma, ctx->slot.dev_info);
|
||||
dma += field_size;
|
||||
xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - dev_info2\n",
|
||||
&ctx->slot.dev_info2,
|
||||
(unsigned long long)dma, ctx->slot.dev_info2);
|
||||
dma += field_size;
|
||||
xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - tt_info\n",
|
||||
&ctx->slot.tt_info,
|
||||
(unsigned long long)dma, ctx->slot.tt_info);
|
||||
dma += field_size;
|
||||
xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - dev_state\n",
|
||||
&ctx->slot.dev_state,
|
||||
(unsigned long long)dma, ctx->slot.dev_state);
|
||||
dma += field_size;
|
||||
for (i = 0; i > 4; ++i) {
|
||||
xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - rsvd[%d]\n",
|
||||
&ctx->slot.reserved[i], (unsigned long long)dma,
|
||||
ctx->slot.reserved[i], i);
|
||||
dma += field_size;
|
||||
}
|
||||
|
||||
if (last_ep < 31)
|
||||
last_ep_ctx = last_ep + 1;
|
||||
for (i = 0; i < last_ep_ctx; ++i) {
|
||||
xhci_dbg(xhci, "Endpoint %02d Context:\n", i);
|
||||
xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - ep_info\n",
|
||||
&ctx->ep[i].ep_info,
|
||||
(unsigned long long)dma, ctx->ep[i].ep_info);
|
||||
dma += field_size;
|
||||
xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - ep_info2\n",
|
||||
&ctx->ep[i].ep_info2,
|
||||
(unsigned long long)dma, ctx->ep[i].ep_info2);
|
||||
dma += field_size;
|
||||
xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - deq[0]\n",
|
||||
&ctx->ep[i].deq[0],
|
||||
(unsigned long long)dma, ctx->ep[i].deq[0]);
|
||||
dma += field_size;
|
||||
xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - deq[1]\n",
|
||||
&ctx->ep[i].deq[1],
|
||||
(unsigned long long)dma, ctx->ep[i].deq[1]);
|
||||
dma += field_size;
|
||||
xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - tx_info\n",
|
||||
&ctx->ep[i].tx_info,
|
||||
(unsigned long long)dma, ctx->ep[i].tx_info);
|
||||
dma += field_size;
|
||||
for (j = 0; j < 3; ++j) {
|
||||
xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - rsvd[%d]\n",
|
||||
&ctx->ep[i].reserved[j],
|
||||
(unsigned long long)dma,
|
||||
ctx->ep[i].reserved[j], j);
|
||||
dma += field_size;
|
||||
}
|
||||
}
|
||||
}
|
145
drivers/usb/host/xhci-ext-caps.h
Normal file
145
drivers/usb/host/xhci-ext-caps.h
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* xHCI host controller driver
|
||||
*
|
||||
* Copyright (C) 2008 Intel Corp.
|
||||
*
|
||||
* Author: Sarah Sharp
|
||||
* Some code borrowed from the Linux EHCI driver.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
/* Up to 16 microframes to halt an HC - one microframe is 125 microsectonds */
|
||||
#define XHCI_MAX_HALT_USEC (16*125)
|
||||
/* HC not running - set to 1 when run/stop bit is cleared. */
|
||||
#define XHCI_STS_HALT (1<<0)
|
||||
|
||||
/* HCCPARAMS offset from PCI base address */
|
||||
#define XHCI_HCC_PARAMS_OFFSET 0x10
|
||||
/* HCCPARAMS contains the first extended capability pointer */
|
||||
#define XHCI_HCC_EXT_CAPS(p) (((p)>>16)&0xffff)
|
||||
|
||||
/* Command and Status registers offset from the Operational Registers address */
|
||||
#define XHCI_CMD_OFFSET 0x00
|
||||
#define XHCI_STS_OFFSET 0x04
|
||||
|
||||
#define XHCI_MAX_EXT_CAPS 50
|
||||
|
||||
/* Capability Register */
|
||||
/* bits 7:0 - how long is the Capabilities register */
|
||||
#define XHCI_HC_LENGTH(p) (((p)>>00)&0x00ff)
|
||||
|
||||
/* Extended capability register fields */
|
||||
#define XHCI_EXT_CAPS_ID(p) (((p)>>0)&0xff)
|
||||
#define XHCI_EXT_CAPS_NEXT(p) (((p)>>8)&0xff)
|
||||
#define XHCI_EXT_CAPS_VAL(p) ((p)>>16)
|
||||
/* Extended capability IDs - ID 0 reserved */
|
||||
#define XHCI_EXT_CAPS_LEGACY 1
|
||||
#define XHCI_EXT_CAPS_PROTOCOL 2
|
||||
#define XHCI_EXT_CAPS_PM 3
|
||||
#define XHCI_EXT_CAPS_VIRT 4
|
||||
#define XHCI_EXT_CAPS_ROUTE 5
|
||||
/* IDs 6-9 reserved */
|
||||
#define XHCI_EXT_CAPS_DEBUG 10
|
||||
/* USB Legacy Support Capability - section 7.1.1 */
|
||||
#define XHCI_HC_BIOS_OWNED (1 << 16)
|
||||
#define XHCI_HC_OS_OWNED (1 << 24)
|
||||
|
||||
/* USB Legacy Support Capability - section 7.1.1 */
|
||||
/* Add this offset, plus the value of xECP in HCCPARAMS to the base address */
|
||||
#define XHCI_LEGACY_SUPPORT_OFFSET (0x00)
|
||||
|
||||
/* USB Legacy Support Control and Status Register - section 7.1.2 */
|
||||
/* Add this offset, plus the value of xECP in HCCPARAMS to the base address */
|
||||
#define XHCI_LEGACY_CONTROL_OFFSET (0x04)
|
||||
/* bits 1:2, 5:12, and 17:19 need to be preserved; bits 21:28 should be zero */
|
||||
#define XHCI_LEGACY_DISABLE_SMI ((0x3 << 1) + (0xff << 5) + (0x7 << 17))
|
||||
|
||||
/* command register values to disable interrupts and halt the HC */
|
||||
/* start/stop HC execution - do not write unless HC is halted*/
|
||||
#define XHCI_CMD_RUN (1 << 0)
|
||||
/* Event Interrupt Enable - get irq when EINT bit is set in USBSTS register */
|
||||
#define XHCI_CMD_EIE (1 << 2)
|
||||
/* Host System Error Interrupt Enable - get irq when HSEIE bit set in USBSTS */
|
||||
#define XHCI_CMD_HSEIE (1 << 3)
|
||||
/* Enable Wrap Event - '1' means xHC generates an event when MFINDEX wraps. */
|
||||
#define XHCI_CMD_EWE (1 << 10)
|
||||
|
||||
#define XHCI_IRQS (XHCI_CMD_EIE | XHCI_CMD_HSEIE | XHCI_CMD_EWE)
|
||||
|
||||
/* true: Controller Not Ready to accept doorbell or op reg writes after reset */
|
||||
#define XHCI_STS_CNR (1 << 11)
|
||||
|
||||
#include <linux/io.h>
|
||||
|
||||
/**
|
||||
* Return the next extended capability pointer register.
|
||||
*
|
||||
* @base PCI register base address.
|
||||
*
|
||||
* @ext_offset Offset of the 32-bit register that contains the extended
|
||||
* capabilites pointer. If searching for the first extended capability, pass
|
||||
* in XHCI_HCC_PARAMS_OFFSET. If searching for the next extended capability,
|
||||
* pass in the offset of the current extended capability register.
|
||||
*
|
||||
* Returns 0 if there is no next extended capability register or returns the register offset
|
||||
* from the PCI registers base address.
|
||||
*/
|
||||
static inline int xhci_find_next_cap_offset(void __iomem *base, int ext_offset)
|
||||
{
|
||||
u32 next;
|
||||
|
||||
next = readl(base + ext_offset);
|
||||
|
||||
if (ext_offset == XHCI_HCC_PARAMS_OFFSET)
|
||||
/* Find the first extended capability */
|
||||
next = XHCI_HCC_EXT_CAPS(next);
|
||||
else
|
||||
/* Find the next extended capability */
|
||||
next = XHCI_EXT_CAPS_NEXT(next);
|
||||
if (!next)
|
||||
return 0;
|
||||
/*
|
||||
* Address calculation from offset of extended capabilities
|
||||
* (or HCCPARAMS) register - see section 5.3.6 and section 7.
|
||||
*/
|
||||
return ext_offset + (next << 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the offset of the extended capabilities with capability ID id.
|
||||
*
|
||||
* @base PCI MMIO registers base address.
|
||||
* @ext_offset Offset from base of the first extended capability to look at,
|
||||
* or the address of HCCPARAMS.
|
||||
* @id Extended capability ID to search for.
|
||||
*
|
||||
* This uses an arbitrary limit of XHCI_MAX_EXT_CAPS extended capabilities
|
||||
* to make sure that the list doesn't contain a loop.
|
||||
*/
|
||||
static inline int xhci_find_ext_cap_by_id(void __iomem *base, int ext_offset, int id)
|
||||
{
|
||||
u32 val;
|
||||
int limit = XHCI_MAX_EXT_CAPS;
|
||||
|
||||
while (ext_offset && limit > 0) {
|
||||
val = readl(base + ext_offset);
|
||||
if (XHCI_EXT_CAPS_ID(val) == id)
|
||||
break;
|
||||
ext_offset = xhci_find_next_cap_offset(base, ext_offset);
|
||||
limit--;
|
||||
}
|
||||
if (limit > 0)
|
||||
return ext_offset;
|
||||
return 0;
|
||||
}
|
1274
drivers/usb/host/xhci-hcd.c
Normal file
1274
drivers/usb/host/xhci-hcd.c
Normal file
File diff suppressed because it is too large
Load Diff
308
drivers/usb/host/xhci-hub.c
Normal file
308
drivers/usb/host/xhci-hub.c
Normal file
@ -0,0 +1,308 @@
|
||||
/*
|
||||
* xHCI host controller driver
|
||||
*
|
||||
* Copyright (C) 2008 Intel Corp.
|
||||
*
|
||||
* Author: Sarah Sharp
|
||||
* Some code borrowed from the Linux EHCI driver.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "xhci.h"
|
||||
|
||||
static void xhci_hub_descriptor(struct xhci_hcd *xhci,
|
||||
struct usb_hub_descriptor *desc)
|
||||
{
|
||||
int ports;
|
||||
u16 temp;
|
||||
|
||||
ports = HCS_MAX_PORTS(xhci->hcs_params1);
|
||||
|
||||
/* USB 3.0 hubs have a different descriptor, but we fake this for now */
|
||||
desc->bDescriptorType = 0x29;
|
||||
desc->bPwrOn2PwrGood = 10; /* xhci section 5.4.9 says 20ms max */
|
||||
desc->bHubContrCurrent = 0;
|
||||
|
||||
desc->bNbrPorts = ports;
|
||||
temp = 1 + (ports / 8);
|
||||
desc->bDescLength = 7 + 2 * temp;
|
||||
|
||||
/* Why does core/hcd.h define bitmap? It's just confusing. */
|
||||
memset(&desc->DeviceRemovable[0], 0, temp);
|
||||
memset(&desc->DeviceRemovable[temp], 0xff, temp);
|
||||
|
||||
/* Ugh, these should be #defines, FIXME */
|
||||
/* Using table 11-13 in USB 2.0 spec. */
|
||||
temp = 0;
|
||||
/* Bits 1:0 - support port power switching, or power always on */
|
||||
if (HCC_PPC(xhci->hcc_params))
|
||||
temp |= 0x0001;
|
||||
else
|
||||
temp |= 0x0002;
|
||||
/* Bit 2 - root hubs are not part of a compound device */
|
||||
/* Bits 4:3 - individual port over current protection */
|
||||
temp |= 0x0008;
|
||||
/* Bits 6:5 - no TTs in root ports */
|
||||
/* Bit 7 - no port indicators */
|
||||
desc->wHubCharacteristics = (__force __u16) cpu_to_le16(temp);
|
||||
}
|
||||
|
||||
static unsigned int xhci_port_speed(unsigned int port_status)
|
||||
{
|
||||
if (DEV_LOWSPEED(port_status))
|
||||
return 1 << USB_PORT_FEAT_LOWSPEED;
|
||||
if (DEV_HIGHSPEED(port_status))
|
||||
return 1 << USB_PORT_FEAT_HIGHSPEED;
|
||||
if (DEV_SUPERSPEED(port_status))
|
||||
return 1 << USB_PORT_FEAT_SUPERSPEED;
|
||||
/*
|
||||
* FIXME: Yes, we should check for full speed, but the core uses that as
|
||||
* a default in portspeed() in usb/core/hub.c (which is the only place
|
||||
* USB_PORT_FEAT_*SPEED is used).
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* These bits are Read Only (RO) and should be saved and written to the
|
||||
* registers: 0, 3, 10:13, 30
|
||||
* connect status, over-current status, port speed, and device removable.
|
||||
* connect status and port speed are also sticky - meaning they're in
|
||||
* the AUX well and they aren't changed by a hot, warm, or cold reset.
|
||||
*/
|
||||
#define XHCI_PORT_RO ((1<<0) | (1<<3) | (0xf<<10) | (1<<30))
|
||||
/*
|
||||
* These bits are RW; writing a 0 clears the bit, writing a 1 sets the bit:
|
||||
* bits 5:8, 9, 14:15, 25:27
|
||||
* link state, port power, port indicator state, "wake on" enable state
|
||||
*/
|
||||
#define XHCI_PORT_RWS ((0xf<<5) | (1<<9) | (0x3<<14) | (0x7<<25))
|
||||
/*
|
||||
* These bits are RW; writing a 1 sets the bit, writing a 0 has no effect:
|
||||
* bit 4 (port reset)
|
||||
*/
|
||||
#define XHCI_PORT_RW1S ((1<<4))
|
||||
/*
|
||||
* These bits are RW; writing a 1 clears the bit, writing a 0 has no effect:
|
||||
* bits 1, 17, 18, 19, 20, 21, 22, 23
|
||||
* port enable/disable, and
|
||||
* change bits: connect, PED, warm port reset changed (reserved zero for USB 2.0 ports),
|
||||
* over-current, reset, link state, and L1 change
|
||||
*/
|
||||
#define XHCI_PORT_RW1CS ((1<<1) | (0x7f<<17))
|
||||
/*
|
||||
* Bit 16 is RW, and writing a '1' to it causes the link state control to be
|
||||
* latched in
|
||||
*/
|
||||
#define XHCI_PORT_RW ((1<<16))
|
||||
/*
|
||||
* These bits are Reserved Zero (RsvdZ) and zero should be written to them:
|
||||
* bits 2, 24, 28:31
|
||||
*/
|
||||
#define XHCI_PORT_RZ ((1<<2) | (1<<24) | (0xf<<28))
|
||||
|
||||
/*
|
||||
* Given a port state, this function returns a value that would result in the
|
||||
* port being in the same state, if the value was written to the port status
|
||||
* control register.
|
||||
* Save Read Only (RO) bits and save read/write bits where
|
||||
* writing a 0 clears the bit and writing a 1 sets the bit (RWS).
|
||||
* For all other types (RW1S, RW1CS, RW, and RZ), writing a '0' has no effect.
|
||||
*/
|
||||
static u32 xhci_port_state_to_neutral(u32 state)
|
||||
{
|
||||
/* Save read-only status and port state */
|
||||
return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS);
|
||||
}
|
||||
|
||||
int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
u16 wIndex, char *buf, u16 wLength)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
int ports;
|
||||
unsigned long flags;
|
||||
u32 temp, status;
|
||||
int retval = 0;
|
||||
u32 __iomem *addr;
|
||||
char *port_change_bit;
|
||||
|
||||
ports = HCS_MAX_PORTS(xhci->hcs_params1);
|
||||
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
switch (typeReq) {
|
||||
case GetHubStatus:
|
||||
/* No power source, over-current reported per port */
|
||||
memset(buf, 0, 4);
|
||||
break;
|
||||
case GetHubDescriptor:
|
||||
xhci_hub_descriptor(xhci, (struct usb_hub_descriptor *) buf);
|
||||
break;
|
||||
case GetPortStatus:
|
||||
if (!wIndex || wIndex > ports)
|
||||
goto error;
|
||||
wIndex--;
|
||||
status = 0;
|
||||
addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS*(wIndex & 0xff);
|
||||
temp = xhci_readl(xhci, addr);
|
||||
xhci_dbg(xhci, "get port status, actual port %d status = 0x%x\n", wIndex, temp);
|
||||
|
||||
/* wPortChange bits */
|
||||
if (temp & PORT_CSC)
|
||||
status |= 1 << USB_PORT_FEAT_C_CONNECTION;
|
||||
if (temp & PORT_PEC)
|
||||
status |= 1 << USB_PORT_FEAT_C_ENABLE;
|
||||
if ((temp & PORT_OCC))
|
||||
status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
|
||||
/*
|
||||
* FIXME ignoring suspend, reset, and USB 2.1/3.0 specific
|
||||
* changes
|
||||
*/
|
||||
if (temp & PORT_CONNECT) {
|
||||
status |= 1 << USB_PORT_FEAT_CONNECTION;
|
||||
status |= xhci_port_speed(temp);
|
||||
}
|
||||
if (temp & PORT_PE)
|
||||
status |= 1 << USB_PORT_FEAT_ENABLE;
|
||||
if (temp & PORT_OC)
|
||||
status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
|
||||
if (temp & PORT_RESET)
|
||||
status |= 1 << USB_PORT_FEAT_RESET;
|
||||
if (temp & PORT_POWER)
|
||||
status |= 1 << USB_PORT_FEAT_POWER;
|
||||
xhci_dbg(xhci, "Get port status returned 0x%x\n", status);
|
||||
put_unaligned(cpu_to_le32(status), (__le32 *) buf);
|
||||
break;
|
||||
case SetPortFeature:
|
||||
wIndex &= 0xff;
|
||||
if (!wIndex || wIndex > ports)
|
||||
goto error;
|
||||
wIndex--;
|
||||
addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS*(wIndex & 0xff);
|
||||
temp = xhci_readl(xhci, addr);
|
||||
temp = xhci_port_state_to_neutral(temp);
|
||||
switch (wValue) {
|
||||
case USB_PORT_FEAT_POWER:
|
||||
/*
|
||||
* Turn on ports, even if there isn't per-port switching.
|
||||
* HC will report connect events even before this is set.
|
||||
* However, khubd will ignore the roothub events until
|
||||
* the roothub is registered.
|
||||
*/
|
||||
xhci_writel(xhci, temp | PORT_POWER, addr);
|
||||
|
||||
temp = xhci_readl(xhci, addr);
|
||||
xhci_dbg(xhci, "set port power, actual port %d status = 0x%x\n", wIndex, temp);
|
||||
break;
|
||||
case USB_PORT_FEAT_RESET:
|
||||
temp = (temp | PORT_RESET);
|
||||
xhci_writel(xhci, temp, addr);
|
||||
|
||||
temp = xhci_readl(xhci, addr);
|
||||
xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp);
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
temp = xhci_readl(xhci, addr); /* unblock any posted writes */
|
||||
break;
|
||||
case ClearPortFeature:
|
||||
if (!wIndex || wIndex > ports)
|
||||
goto error;
|
||||
wIndex--;
|
||||
addr = &xhci->op_regs->port_status_base +
|
||||
NUM_PORT_REGS*(wIndex & 0xff);
|
||||
temp = xhci_readl(xhci, addr);
|
||||
temp = xhci_port_state_to_neutral(temp);
|
||||
switch (wValue) {
|
||||
case USB_PORT_FEAT_C_RESET:
|
||||
status = PORT_RC;
|
||||
port_change_bit = "reset";
|
||||
break;
|
||||
case USB_PORT_FEAT_C_CONNECTION:
|
||||
status = PORT_CSC;
|
||||
port_change_bit = "connect";
|
||||
break;
|
||||
case USB_PORT_FEAT_C_OVER_CURRENT:
|
||||
status = PORT_OCC;
|
||||
port_change_bit = "over-current";
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
/* Change bits are all write 1 to clear */
|
||||
xhci_writel(xhci, temp | status, addr);
|
||||
temp = xhci_readl(xhci, addr);
|
||||
xhci_dbg(xhci, "clear port %s change, actual port %d status = 0x%x\n",
|
||||
port_change_bit, wIndex, temp);
|
||||
temp = xhci_readl(xhci, addr); /* unblock any posted writes */
|
||||
break;
|
||||
default:
|
||||
error:
|
||||
/* "stall" on error */
|
||||
retval = -EPIPE;
|
||||
}
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 0 if the status hasn't changed, or the number of bytes in buf.
|
||||
* Ports are 0-indexed from the HCD point of view,
|
||||
* and 1-indexed from the USB core pointer of view.
|
||||
* xHCI instances can have up to 127 ports, so FIXME if you see more than 15.
|
||||
*
|
||||
* Note that the status change bits will be cleared as soon as a port status
|
||||
* change event is generated, so we use the saved status from that event.
|
||||
*/
|
||||
int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 temp, status;
|
||||
int i, retval;
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
int ports;
|
||||
u32 __iomem *addr;
|
||||
|
||||
ports = HCS_MAX_PORTS(xhci->hcs_params1);
|
||||
|
||||
/* Initial status is no changes */
|
||||
buf[0] = 0;
|
||||
status = 0;
|
||||
if (ports > 7) {
|
||||
buf[1] = 0;
|
||||
retval = 2;
|
||||
} else {
|
||||
retval = 1;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
/* For each port, did anything change? If so, set that bit in buf. */
|
||||
for (i = 0; i < ports; i++) {
|
||||
addr = &xhci->op_regs->port_status_base +
|
||||
NUM_PORT_REGS*i;
|
||||
temp = xhci_readl(xhci, addr);
|
||||
if (temp & (PORT_CSC | PORT_PEC | PORT_OCC)) {
|
||||
if (i < 7)
|
||||
buf[0] |= 1 << (i + 1);
|
||||
else
|
||||
buf[1] |= 1 << (i - 7);
|
||||
status = 1;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
return status ? retval : 0;
|
||||
}
|
769
drivers/usb/host/xhci-mem.c
Normal file
769
drivers/usb/host/xhci-mem.c
Normal file
@ -0,0 +1,769 @@
|
||||
/*
|
||||
* xHCI host controller driver
|
||||
*
|
||||
* Copyright (C) 2008 Intel Corp.
|
||||
*
|
||||
* Author: Sarah Sharp
|
||||
* Some code borrowed from the Linux EHCI driver.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/dmapool.h>
|
||||
|
||||
#include "xhci.h"
|
||||
|
||||
/*
|
||||
* Allocates a generic ring segment from the ring pool, sets the dma address,
|
||||
* initializes the segment to zero, and sets the private next pointer to NULL.
|
||||
*
|
||||
* Section 4.11.1.1:
|
||||
* "All components of all Command and Transfer TRBs shall be initialized to '0'"
|
||||
*/
|
||||
static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci, gfp_t flags)
|
||||
{
|
||||
struct xhci_segment *seg;
|
||||
dma_addr_t dma;
|
||||
|
||||
seg = kzalloc(sizeof *seg, flags);
|
||||
if (!seg)
|
||||
return 0;
|
||||
xhci_dbg(xhci, "Allocating priv segment structure at %p\n", seg);
|
||||
|
||||
seg->trbs = dma_pool_alloc(xhci->segment_pool, flags, &dma);
|
||||
if (!seg->trbs) {
|
||||
kfree(seg);
|
||||
return 0;
|
||||
}
|
||||
xhci_dbg(xhci, "// Allocating segment at %p (virtual) 0x%llx (DMA)\n",
|
||||
seg->trbs, (unsigned long long)dma);
|
||||
|
||||
memset(seg->trbs, 0, SEGMENT_SIZE);
|
||||
seg->dma = dma;
|
||||
seg->next = NULL;
|
||||
|
||||
return seg;
|
||||
}
|
||||
|
||||
static void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg)
|
||||
{
|
||||
if (!seg)
|
||||
return;
|
||||
if (seg->trbs) {
|
||||
xhci_dbg(xhci, "Freeing DMA segment at %p (virtual) 0x%llx (DMA)\n",
|
||||
seg->trbs, (unsigned long long)seg->dma);
|
||||
dma_pool_free(xhci->segment_pool, seg->trbs, seg->dma);
|
||||
seg->trbs = NULL;
|
||||
}
|
||||
xhci_dbg(xhci, "Freeing priv segment structure at %p\n", seg);
|
||||
kfree(seg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make the prev segment point to the next segment.
|
||||
*
|
||||
* Change the last TRB in the prev segment to be a Link TRB which points to the
|
||||
* DMA address of the next segment. The caller needs to set any Link TRB
|
||||
* related flags, such as End TRB, Toggle Cycle, and no snoop.
|
||||
*/
|
||||
static void xhci_link_segments(struct xhci_hcd *xhci, struct xhci_segment *prev,
|
||||
struct xhci_segment *next, bool link_trbs)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (!prev || !next)
|
||||
return;
|
||||
prev->next = next;
|
||||
if (link_trbs) {
|
||||
prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr[0] = next->dma;
|
||||
|
||||
/* Set the last TRB in the segment to have a TRB type ID of Link TRB */
|
||||
val = prev->trbs[TRBS_PER_SEGMENT-1].link.control;
|
||||
val &= ~TRB_TYPE_BITMASK;
|
||||
val |= TRB_TYPE(TRB_LINK);
|
||||
prev->trbs[TRBS_PER_SEGMENT-1].link.control = val;
|
||||
}
|
||||
xhci_dbg(xhci, "Linking segment 0x%llx to segment 0x%llx (DMA)\n",
|
||||
(unsigned long long)prev->dma,
|
||||
(unsigned long long)next->dma);
|
||||
}
|
||||
|
||||
/* XXX: Do we need the hcd structure in all these functions? */
|
||||
void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring)
|
||||
{
|
||||
struct xhci_segment *seg;
|
||||
struct xhci_segment *first_seg;
|
||||
|
||||
if (!ring || !ring->first_seg)
|
||||
return;
|
||||
first_seg = ring->first_seg;
|
||||
seg = first_seg->next;
|
||||
xhci_dbg(xhci, "Freeing ring at %p\n", ring);
|
||||
while (seg != first_seg) {
|
||||
struct xhci_segment *next = seg->next;
|
||||
xhci_segment_free(xhci, seg);
|
||||
seg = next;
|
||||
}
|
||||
xhci_segment_free(xhci, first_seg);
|
||||
ring->first_seg = NULL;
|
||||
kfree(ring);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ring with zero or more segments.
|
||||
*
|
||||
* Link each segment together into a ring.
|
||||
* Set the end flag and the cycle toggle bit on the last segment.
|
||||
* See section 4.9.1 and figures 15 and 16.
|
||||
*/
|
||||
static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
|
||||
unsigned int num_segs, bool link_trbs, gfp_t flags)
|
||||
{
|
||||
struct xhci_ring *ring;
|
||||
struct xhci_segment *prev;
|
||||
|
||||
ring = kzalloc(sizeof *(ring), flags);
|
||||
xhci_dbg(xhci, "Allocating ring at %p\n", ring);
|
||||
if (!ring)
|
||||
return 0;
|
||||
|
||||
INIT_LIST_HEAD(&ring->td_list);
|
||||
INIT_LIST_HEAD(&ring->cancelled_td_list);
|
||||
if (num_segs == 0)
|
||||
return ring;
|
||||
|
||||
ring->first_seg = xhci_segment_alloc(xhci, flags);
|
||||
if (!ring->first_seg)
|
||||
goto fail;
|
||||
num_segs--;
|
||||
|
||||
prev = ring->first_seg;
|
||||
while (num_segs > 0) {
|
||||
struct xhci_segment *next;
|
||||
|
||||
next = xhci_segment_alloc(xhci, flags);
|
||||
if (!next)
|
||||
goto fail;
|
||||
xhci_link_segments(xhci, prev, next, link_trbs);
|
||||
|
||||
prev = next;
|
||||
num_segs--;
|
||||
}
|
||||
xhci_link_segments(xhci, prev, ring->first_seg, link_trbs);
|
||||
|
||||
if (link_trbs) {
|
||||
/* See section 4.9.2.1 and 6.4.4.1 */
|
||||
prev->trbs[TRBS_PER_SEGMENT-1].link.control |= (LINK_TOGGLE);
|
||||
xhci_dbg(xhci, "Wrote link toggle flag to"
|
||||
" segment %p (virtual), 0x%llx (DMA)\n",
|
||||
prev, (unsigned long long)prev->dma);
|
||||
}
|
||||
/* The ring is empty, so the enqueue pointer == dequeue pointer */
|
||||
ring->enqueue = ring->first_seg->trbs;
|
||||
ring->enq_seg = ring->first_seg;
|
||||
ring->dequeue = ring->enqueue;
|
||||
ring->deq_seg = ring->first_seg;
|
||||
/* The ring is initialized to 0. The producer must write 1 to the cycle
|
||||
* bit to handover ownership of the TRB, so PCS = 1. The consumer must
|
||||
* compare CCS to the cycle bit to check ownership, so CCS = 1.
|
||||
*/
|
||||
ring->cycle_state = 1;
|
||||
|
||||
return ring;
|
||||
|
||||
fail:
|
||||
xhci_ring_free(xhci, ring);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* All the xhci_tds in the ring's TD list should be freed at this point */
|
||||
void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
|
||||
{
|
||||
struct xhci_virt_device *dev;
|
||||
int i;
|
||||
|
||||
/* Slot ID 0 is reserved */
|
||||
if (slot_id == 0 || !xhci->devs[slot_id])
|
||||
return;
|
||||
|
||||
dev = xhci->devs[slot_id];
|
||||
xhci->dcbaa->dev_context_ptrs[2*slot_id] = 0;
|
||||
xhci->dcbaa->dev_context_ptrs[2*slot_id + 1] = 0;
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
for (i = 0; i < 31; ++i)
|
||||
if (dev->ep_rings[i])
|
||||
xhci_ring_free(xhci, dev->ep_rings[i]);
|
||||
|
||||
if (dev->in_ctx)
|
||||
dma_pool_free(xhci->device_pool,
|
||||
dev->in_ctx, dev->in_ctx_dma);
|
||||
if (dev->out_ctx)
|
||||
dma_pool_free(xhci->device_pool,
|
||||
dev->out_ctx, dev->out_ctx_dma);
|
||||
kfree(xhci->devs[slot_id]);
|
||||
xhci->devs[slot_id] = 0;
|
||||
}
|
||||
|
||||
int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
|
||||
struct usb_device *udev, gfp_t flags)
|
||||
{
|
||||
dma_addr_t dma;
|
||||
struct xhci_virt_device *dev;
|
||||
|
||||
/* Slot ID 0 is reserved */
|
||||
if (slot_id == 0 || xhci->devs[slot_id]) {
|
||||
xhci_warn(xhci, "Bad Slot ID %d\n", slot_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
xhci->devs[slot_id] = kzalloc(sizeof(*xhci->devs[slot_id]), flags);
|
||||
if (!xhci->devs[slot_id])
|
||||
return 0;
|
||||
dev = xhci->devs[slot_id];
|
||||
|
||||
/* Allocate the (output) device context that will be used in the HC */
|
||||
dev->out_ctx = dma_pool_alloc(xhci->device_pool, flags, &dma);
|
||||
if (!dev->out_ctx)
|
||||
goto fail;
|
||||
dev->out_ctx_dma = dma;
|
||||
xhci_dbg(xhci, "Slot %d output ctx = 0x%llx (dma)\n", slot_id,
|
||||
(unsigned long long)dma);
|
||||
memset(dev->out_ctx, 0, sizeof(*dev->out_ctx));
|
||||
|
||||
/* Allocate the (input) device context for address device command */
|
||||
dev->in_ctx = dma_pool_alloc(xhci->device_pool, flags, &dma);
|
||||
if (!dev->in_ctx)
|
||||
goto fail;
|
||||
dev->in_ctx_dma = dma;
|
||||
xhci_dbg(xhci, "Slot %d input ctx = 0x%llx (dma)\n", slot_id,
|
||||
(unsigned long long)dma);
|
||||
memset(dev->in_ctx, 0, sizeof(*dev->in_ctx));
|
||||
|
||||
/* Allocate endpoint 0 ring */
|
||||
dev->ep_rings[0] = xhci_ring_alloc(xhci, 1, true, flags);
|
||||
if (!dev->ep_rings[0])
|
||||
goto fail;
|
||||
|
||||
init_completion(&dev->cmd_completion);
|
||||
|
||||
/*
|
||||
* Point to output device context in dcbaa; skip the output control
|
||||
* context, which is eight 32 bit fields (or 32 bytes long)
|
||||
*/
|
||||
xhci->dcbaa->dev_context_ptrs[2*slot_id] =
|
||||
(u32) dev->out_ctx_dma + (32);
|
||||
xhci_dbg(xhci, "Set slot id %d dcbaa entry %p to 0x%llx\n",
|
||||
slot_id,
|
||||
&xhci->dcbaa->dev_context_ptrs[2*slot_id],
|
||||
(unsigned long long)dev->out_ctx_dma);
|
||||
xhci->dcbaa->dev_context_ptrs[2*slot_id + 1] = 0;
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
xhci_free_virt_device(xhci, slot_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Setup an xHCI virtual device for a Set Address command */
|
||||
int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *udev)
|
||||
{
|
||||
struct xhci_virt_device *dev;
|
||||
struct xhci_ep_ctx *ep0_ctx;
|
||||
struct usb_device *top_dev;
|
||||
|
||||
dev = xhci->devs[udev->slot_id];
|
||||
/* Slot ID 0 is reserved */
|
||||
if (udev->slot_id == 0 || !dev) {
|
||||
xhci_warn(xhci, "Slot ID %d is not assigned to this device\n",
|
||||
udev->slot_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
ep0_ctx = &dev->in_ctx->ep[0];
|
||||
|
||||
/* 2) New slot context and endpoint 0 context are valid*/
|
||||
dev->in_ctx->add_flags = SLOT_FLAG | EP0_FLAG;
|
||||
|
||||
/* 3) Only the control endpoint is valid - one endpoint context */
|
||||
dev->in_ctx->slot.dev_info |= LAST_CTX(1);
|
||||
|
||||
switch (udev->speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
dev->in_ctx->slot.dev_info |= (u32) udev->route;
|
||||
dev->in_ctx->slot.dev_info |= (u32) SLOT_SPEED_SS;
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
dev->in_ctx->slot.dev_info |= (u32) SLOT_SPEED_HS;
|
||||
break;
|
||||
case USB_SPEED_FULL:
|
||||
dev->in_ctx->slot.dev_info |= (u32) SLOT_SPEED_FS;
|
||||
break;
|
||||
case USB_SPEED_LOW:
|
||||
dev->in_ctx->slot.dev_info |= (u32) SLOT_SPEED_LS;
|
||||
break;
|
||||
case USB_SPEED_VARIABLE:
|
||||
xhci_dbg(xhci, "FIXME xHCI doesn't support wireless speeds\n");
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
/* Speed was set earlier, this shouldn't happen. */
|
||||
BUG();
|
||||
}
|
||||
/* Find the root hub port this device is under */
|
||||
for (top_dev = udev; top_dev->parent && top_dev->parent->parent;
|
||||
top_dev = top_dev->parent)
|
||||
/* Found device below root hub */;
|
||||
dev->in_ctx->slot.dev_info2 |= (u32) ROOT_HUB_PORT(top_dev->portnum);
|
||||
xhci_dbg(xhci, "Set root hub portnum to %d\n", top_dev->portnum);
|
||||
|
||||
/* Is this a LS/FS device under a HS hub? */
|
||||
/*
|
||||
* FIXME: I don't think this is right, where does the TT info for the
|
||||
* roothub or parent hub come from?
|
||||
*/
|
||||
if ((udev->speed == USB_SPEED_LOW || udev->speed == USB_SPEED_FULL) &&
|
||||
udev->tt) {
|
||||
dev->in_ctx->slot.tt_info = udev->tt->hub->slot_id;
|
||||
dev->in_ctx->slot.tt_info |= udev->ttport << 8;
|
||||
}
|
||||
xhci_dbg(xhci, "udev->tt = %p\n", udev->tt);
|
||||
xhci_dbg(xhci, "udev->ttport = 0x%x\n", udev->ttport);
|
||||
|
||||
/* Step 4 - ring already allocated */
|
||||
/* Step 5 */
|
||||
ep0_ctx->ep_info2 = EP_TYPE(CTRL_EP);
|
||||
/*
|
||||
* See section 4.3 bullet 6:
|
||||
* The default Max Packet size for ep0 is "8 bytes for a USB2
|
||||
* LS/FS/HS device or 512 bytes for a USB3 SS device"
|
||||
* XXX: Not sure about wireless USB devices.
|
||||
*/
|
||||
if (udev->speed == USB_SPEED_SUPER)
|
||||
ep0_ctx->ep_info2 |= MAX_PACKET(512);
|
||||
else
|
||||
ep0_ctx->ep_info2 |= MAX_PACKET(8);
|
||||
/* EP 0 can handle "burst" sizes of 1, so Max Burst Size field is 0 */
|
||||
ep0_ctx->ep_info2 |= MAX_BURST(0);
|
||||
ep0_ctx->ep_info2 |= ERROR_COUNT(3);
|
||||
|
||||
ep0_ctx->deq[0] =
|
||||
dev->ep_rings[0]->first_seg->dma;
|
||||
ep0_ctx->deq[0] |= dev->ep_rings[0]->cycle_state;
|
||||
ep0_ctx->deq[1] = 0;
|
||||
|
||||
/* Steps 7 and 8 were done in xhci_alloc_virt_device() */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return the polling or NAK interval.
|
||||
*
|
||||
* The polling interval is expressed in "microframes". If xHCI's Interval field
|
||||
* is set to N, it will service the endpoint every 2^(Interval)*125us.
|
||||
*
|
||||
* The NAK interval is one NAK per 1 to 255 microframes, or no NAKs if interval
|
||||
* is set to 0.
|
||||
*/
|
||||
static inline unsigned int xhci_get_endpoint_interval(struct usb_device *udev,
|
||||
struct usb_host_endpoint *ep)
|
||||
{
|
||||
unsigned int interval = 0;
|
||||
|
||||
switch (udev->speed) {
|
||||
case USB_SPEED_HIGH:
|
||||
/* Max NAK rate */
|
||||
if (usb_endpoint_xfer_control(&ep->desc) ||
|
||||
usb_endpoint_xfer_bulk(&ep->desc))
|
||||
interval = ep->desc.bInterval;
|
||||
/* Fall through - SS and HS isoc/int have same decoding */
|
||||
case USB_SPEED_SUPER:
|
||||
if (usb_endpoint_xfer_int(&ep->desc) ||
|
||||
usb_endpoint_xfer_isoc(&ep->desc)) {
|
||||
if (ep->desc.bInterval == 0)
|
||||
interval = 0;
|
||||
else
|
||||
interval = ep->desc.bInterval - 1;
|
||||
if (interval > 15)
|
||||
interval = 15;
|
||||
if (interval != ep->desc.bInterval + 1)
|
||||
dev_warn(&udev->dev, "ep %#x - rounding interval to %d microframes\n",
|
||||
ep->desc.bEndpointAddress, 1 << interval);
|
||||
}
|
||||
break;
|
||||
/* Convert bInterval (in 1-255 frames) to microframes and round down to
|
||||
* nearest power of 2.
|
||||
*/
|
||||
case USB_SPEED_FULL:
|
||||
case USB_SPEED_LOW:
|
||||
if (usb_endpoint_xfer_int(&ep->desc) ||
|
||||
usb_endpoint_xfer_isoc(&ep->desc)) {
|
||||
interval = fls(8*ep->desc.bInterval) - 1;
|
||||
if (interval > 10)
|
||||
interval = 10;
|
||||
if (interval < 3)
|
||||
interval = 3;
|
||||
if ((1 << interval) != 8*ep->desc.bInterval)
|
||||
dev_warn(&udev->dev, "ep %#x - rounding interval to %d microframes\n",
|
||||
ep->desc.bEndpointAddress, 1 << interval);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
return EP_INTERVAL(interval);
|
||||
}
|
||||
|
||||
static inline u32 xhci_get_endpoint_type(struct usb_device *udev,
|
||||
struct usb_host_endpoint *ep)
|
||||
{
|
||||
int in;
|
||||
u32 type;
|
||||
|
||||
in = usb_endpoint_dir_in(&ep->desc);
|
||||
if (usb_endpoint_xfer_control(&ep->desc)) {
|
||||
type = EP_TYPE(CTRL_EP);
|
||||
} else if (usb_endpoint_xfer_bulk(&ep->desc)) {
|
||||
if (in)
|
||||
type = EP_TYPE(BULK_IN_EP);
|
||||
else
|
||||
type = EP_TYPE(BULK_OUT_EP);
|
||||
} else if (usb_endpoint_xfer_isoc(&ep->desc)) {
|
||||
if (in)
|
||||
type = EP_TYPE(ISOC_IN_EP);
|
||||
else
|
||||
type = EP_TYPE(ISOC_OUT_EP);
|
||||
} else if (usb_endpoint_xfer_int(&ep->desc)) {
|
||||
if (in)
|
||||
type = EP_TYPE(INT_IN_EP);
|
||||
else
|
||||
type = EP_TYPE(INT_OUT_EP);
|
||||
} else {
|
||||
BUG();
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
int xhci_endpoint_init(struct xhci_hcd *xhci,
|
||||
struct xhci_virt_device *virt_dev,
|
||||
struct usb_device *udev,
|
||||
struct usb_host_endpoint *ep,
|
||||
gfp_t mem_flags)
|
||||
{
|
||||
unsigned int ep_index;
|
||||
struct xhci_ep_ctx *ep_ctx;
|
||||
struct xhci_ring *ep_ring;
|
||||
unsigned int max_packet;
|
||||
unsigned int max_burst;
|
||||
|
||||
ep_index = xhci_get_endpoint_index(&ep->desc);
|
||||
ep_ctx = &virt_dev->in_ctx->ep[ep_index];
|
||||
|
||||
/* Set up the endpoint ring */
|
||||
virt_dev->new_ep_rings[ep_index] = xhci_ring_alloc(xhci, 1, true, mem_flags);
|
||||
if (!virt_dev->new_ep_rings[ep_index])
|
||||
return -ENOMEM;
|
||||
ep_ring = virt_dev->new_ep_rings[ep_index];
|
||||
ep_ctx->deq[0] = ep_ring->first_seg->dma | ep_ring->cycle_state;
|
||||
ep_ctx->deq[1] = 0;
|
||||
|
||||
ep_ctx->ep_info = xhci_get_endpoint_interval(udev, ep);
|
||||
|
||||
/* FIXME dig Mult and streams info out of ep companion desc */
|
||||
|
||||
/* Allow 3 retries for everything but isoc */
|
||||
if (!usb_endpoint_xfer_isoc(&ep->desc))
|
||||
ep_ctx->ep_info2 = ERROR_COUNT(3);
|
||||
else
|
||||
ep_ctx->ep_info2 = ERROR_COUNT(0);
|
||||
|
||||
ep_ctx->ep_info2 |= xhci_get_endpoint_type(udev, ep);
|
||||
|
||||
/* Set the max packet size and max burst */
|
||||
switch (udev->speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
max_packet = ep->desc.wMaxPacketSize;
|
||||
ep_ctx->ep_info2 |= MAX_PACKET(max_packet);
|
||||
/* dig out max burst from ep companion desc */
|
||||
max_packet = ep->ss_ep_comp->desc.bMaxBurst;
|
||||
ep_ctx->ep_info2 |= MAX_BURST(max_packet);
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
/* bits 11:12 specify the number of additional transaction
|
||||
* opportunities per microframe (USB 2.0, section 9.6.6)
|
||||
*/
|
||||
if (usb_endpoint_xfer_isoc(&ep->desc) ||
|
||||
usb_endpoint_xfer_int(&ep->desc)) {
|
||||
max_burst = (ep->desc.wMaxPacketSize & 0x1800) >> 11;
|
||||
ep_ctx->ep_info2 |= MAX_BURST(max_burst);
|
||||
}
|
||||
/* Fall through */
|
||||
case USB_SPEED_FULL:
|
||||
case USB_SPEED_LOW:
|
||||
max_packet = ep->desc.wMaxPacketSize & 0x3ff;
|
||||
ep_ctx->ep_info2 |= MAX_PACKET(max_packet);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
/* FIXME Debug endpoint context */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xhci_endpoint_zero(struct xhci_hcd *xhci,
|
||||
struct xhci_virt_device *virt_dev,
|
||||
struct usb_host_endpoint *ep)
|
||||
{
|
||||
unsigned int ep_index;
|
||||
struct xhci_ep_ctx *ep_ctx;
|
||||
|
||||
ep_index = xhci_get_endpoint_index(&ep->desc);
|
||||
ep_ctx = &virt_dev->in_ctx->ep[ep_index];
|
||||
|
||||
ep_ctx->ep_info = 0;
|
||||
ep_ctx->ep_info2 = 0;
|
||||
ep_ctx->deq[0] = 0;
|
||||
ep_ctx->deq[1] = 0;
|
||||
ep_ctx->tx_info = 0;
|
||||
/* Don't free the endpoint ring until the set interface or configuration
|
||||
* request succeeds.
|
||||
*/
|
||||
}
|
||||
|
||||
void xhci_mem_cleanup(struct xhci_hcd *xhci)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
|
||||
int size;
|
||||
int i;
|
||||
|
||||
/* Free the Event Ring Segment Table and the actual Event Ring */
|
||||
xhci_writel(xhci, 0, &xhci->ir_set->erst_size);
|
||||
xhci_writel(xhci, 0, &xhci->ir_set->erst_base[0]);
|
||||
xhci_writel(xhci, 0, &xhci->ir_set->erst_base[1]);
|
||||
xhci_writel(xhci, 0, &xhci->ir_set->erst_dequeue[0]);
|
||||
xhci_writel(xhci, 0, &xhci->ir_set->erst_dequeue[1]);
|
||||
size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries);
|
||||
if (xhci->erst.entries)
|
||||
pci_free_consistent(pdev, size,
|
||||
xhci->erst.entries, xhci->erst.erst_dma_addr);
|
||||
xhci->erst.entries = NULL;
|
||||
xhci_dbg(xhci, "Freed ERST\n");
|
||||
if (xhci->event_ring)
|
||||
xhci_ring_free(xhci, xhci->event_ring);
|
||||
xhci->event_ring = NULL;
|
||||
xhci_dbg(xhci, "Freed event ring\n");
|
||||
|
||||
xhci_writel(xhci, 0, &xhci->op_regs->cmd_ring[0]);
|
||||
xhci_writel(xhci, 0, &xhci->op_regs->cmd_ring[1]);
|
||||
if (xhci->cmd_ring)
|
||||
xhci_ring_free(xhci, xhci->cmd_ring);
|
||||
xhci->cmd_ring = NULL;
|
||||
xhci_dbg(xhci, "Freed command ring\n");
|
||||
|
||||
for (i = 1; i < MAX_HC_SLOTS; ++i)
|
||||
xhci_free_virt_device(xhci, i);
|
||||
|
||||
if (xhci->segment_pool)
|
||||
dma_pool_destroy(xhci->segment_pool);
|
||||
xhci->segment_pool = NULL;
|
||||
xhci_dbg(xhci, "Freed segment pool\n");
|
||||
|
||||
if (xhci->device_pool)
|
||||
dma_pool_destroy(xhci->device_pool);
|
||||
xhci->device_pool = NULL;
|
||||
xhci_dbg(xhci, "Freed device context pool\n");
|
||||
|
||||
xhci_writel(xhci, 0, &xhci->op_regs->dcbaa_ptr[0]);
|
||||
xhci_writel(xhci, 0, &xhci->op_regs->dcbaa_ptr[1]);
|
||||
if (xhci->dcbaa)
|
||||
pci_free_consistent(pdev, sizeof(*xhci->dcbaa),
|
||||
xhci->dcbaa, xhci->dcbaa->dma);
|
||||
xhci->dcbaa = NULL;
|
||||
|
||||
xhci->page_size = 0;
|
||||
xhci->page_shift = 0;
|
||||
}
|
||||
|
||||
int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
||||
{
|
||||
dma_addr_t dma;
|
||||
struct device *dev = xhci_to_hcd(xhci)->self.controller;
|
||||
unsigned int val, val2;
|
||||
struct xhci_segment *seg;
|
||||
u32 page_size;
|
||||
int i;
|
||||
|
||||
page_size = xhci_readl(xhci, &xhci->op_regs->page_size);
|
||||
xhci_dbg(xhci, "Supported page size register = 0x%x\n", page_size);
|
||||
for (i = 0; i < 16; i++) {
|
||||
if ((0x1 & page_size) != 0)
|
||||
break;
|
||||
page_size = page_size >> 1;
|
||||
}
|
||||
if (i < 16)
|
||||
xhci_dbg(xhci, "Supported page size of %iK\n", (1 << (i+12)) / 1024);
|
||||
else
|
||||
xhci_warn(xhci, "WARN: no supported page size\n");
|
||||
/* Use 4K pages, since that's common and the minimum the HC supports */
|
||||
xhci->page_shift = 12;
|
||||
xhci->page_size = 1 << xhci->page_shift;
|
||||
xhci_dbg(xhci, "HCD page size set to %iK\n", xhci->page_size / 1024);
|
||||
|
||||
/*
|
||||
* Program the Number of Device Slots Enabled field in the CONFIG
|
||||
* register with the max value of slots the HC can handle.
|
||||
*/
|
||||
val = HCS_MAX_SLOTS(xhci_readl(xhci, &xhci->cap_regs->hcs_params1));
|
||||
xhci_dbg(xhci, "// xHC can handle at most %d device slots.\n",
|
||||
(unsigned int) val);
|
||||
val2 = xhci_readl(xhci, &xhci->op_regs->config_reg);
|
||||
val |= (val2 & ~HCS_SLOTS_MASK);
|
||||
xhci_dbg(xhci, "// Setting Max device slots reg = 0x%x.\n",
|
||||
(unsigned int) val);
|
||||
xhci_writel(xhci, val, &xhci->op_regs->config_reg);
|
||||
|
||||
/*
|
||||
* Section 5.4.8 - doorbell array must be
|
||||
* "physically contiguous and 64-byte (cache line) aligned".
|
||||
*/
|
||||
xhci->dcbaa = pci_alloc_consistent(to_pci_dev(dev),
|
||||
sizeof(*xhci->dcbaa), &dma);
|
||||
if (!xhci->dcbaa)
|
||||
goto fail;
|
||||
memset(xhci->dcbaa, 0, sizeof *(xhci->dcbaa));
|
||||
xhci->dcbaa->dma = dma;
|
||||
xhci_dbg(xhci, "// Device context base array address = 0x%llx (DMA), %p (virt)\n",
|
||||
(unsigned long long)xhci->dcbaa->dma, xhci->dcbaa);
|
||||
xhci_writel(xhci, dma, &xhci->op_regs->dcbaa_ptr[0]);
|
||||
xhci_writel(xhci, (u32) 0, &xhci->op_regs->dcbaa_ptr[1]);
|
||||
|
||||
/*
|
||||
* Initialize the ring segment pool. The ring must be a contiguous
|
||||
* structure comprised of TRBs. The TRBs must be 16 byte aligned,
|
||||
* however, the command ring segment needs 64-byte aligned segments,
|
||||
* so we pick the greater alignment need.
|
||||
*/
|
||||
xhci->segment_pool = dma_pool_create("xHCI ring segments", dev,
|
||||
SEGMENT_SIZE, 64, xhci->page_size);
|
||||
/* See Table 46 and Note on Figure 55 */
|
||||
/* FIXME support 64-byte contexts */
|
||||
xhci->device_pool = dma_pool_create("xHCI input/output contexts", dev,
|
||||
sizeof(struct xhci_device_control),
|
||||
64, xhci->page_size);
|
||||
if (!xhci->segment_pool || !xhci->device_pool)
|
||||
goto fail;
|
||||
|
||||
/* Set up the command ring to have one segments for now. */
|
||||
xhci->cmd_ring = xhci_ring_alloc(xhci, 1, true, flags);
|
||||
if (!xhci->cmd_ring)
|
||||
goto fail;
|
||||
xhci_dbg(xhci, "Allocated command ring at %p\n", xhci->cmd_ring);
|
||||
xhci_dbg(xhci, "First segment DMA is 0x%llx\n",
|
||||
(unsigned long long)xhci->cmd_ring->first_seg->dma);
|
||||
|
||||
/* Set the address in the Command Ring Control register */
|
||||
val = xhci_readl(xhci, &xhci->op_regs->cmd_ring[0]);
|
||||
val = (val & ~CMD_RING_ADDR_MASK) |
|
||||
(xhci->cmd_ring->first_seg->dma & CMD_RING_ADDR_MASK) |
|
||||
xhci->cmd_ring->cycle_state;
|
||||
xhci_dbg(xhci, "// Setting command ring address low bits to 0x%x\n", val);
|
||||
xhci_writel(xhci, val, &xhci->op_regs->cmd_ring[0]);
|
||||
xhci_dbg(xhci, "// Setting command ring address high bits to 0x0\n");
|
||||
xhci_writel(xhci, (u32) 0, &xhci->op_regs->cmd_ring[1]);
|
||||
xhci_dbg_cmd_ptrs(xhci);
|
||||
|
||||
val = xhci_readl(xhci, &xhci->cap_regs->db_off);
|
||||
val &= DBOFF_MASK;
|
||||
xhci_dbg(xhci, "// Doorbell array is located at offset 0x%x"
|
||||
" from cap regs base addr\n", val);
|
||||
xhci->dba = (void *) xhci->cap_regs + val;
|
||||
xhci_dbg_regs(xhci);
|
||||
xhci_print_run_regs(xhci);
|
||||
/* Set ir_set to interrupt register set 0 */
|
||||
xhci->ir_set = (void *) xhci->run_regs->ir_set;
|
||||
|
||||
/*
|
||||
* Event ring setup: Allocate a normal ring, but also setup
|
||||
* the event ring segment table (ERST). Section 4.9.3.
|
||||
*/
|
||||
xhci_dbg(xhci, "// Allocating event ring\n");
|
||||
xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, false, flags);
|
||||
if (!xhci->event_ring)
|
||||
goto fail;
|
||||
|
||||
xhci->erst.entries = pci_alloc_consistent(to_pci_dev(dev),
|
||||
sizeof(struct xhci_erst_entry)*ERST_NUM_SEGS, &dma);
|
||||
if (!xhci->erst.entries)
|
||||
goto fail;
|
||||
xhci_dbg(xhci, "// Allocated event ring segment table at 0x%llx\n",
|
||||
(unsigned long long)dma);
|
||||
|
||||
memset(xhci->erst.entries, 0, sizeof(struct xhci_erst_entry)*ERST_NUM_SEGS);
|
||||
xhci->erst.num_entries = ERST_NUM_SEGS;
|
||||
xhci->erst.erst_dma_addr = dma;
|
||||
xhci_dbg(xhci, "Set ERST to 0; private num segs = %i, virt addr = %p, dma addr = 0x%llx\n",
|
||||
xhci->erst.num_entries,
|
||||
xhci->erst.entries,
|
||||
(unsigned long long)xhci->erst.erst_dma_addr);
|
||||
|
||||
/* set ring base address and size for each segment table entry */
|
||||
for (val = 0, seg = xhci->event_ring->first_seg; val < ERST_NUM_SEGS; val++) {
|
||||
struct xhci_erst_entry *entry = &xhci->erst.entries[val];
|
||||
entry->seg_addr[0] = seg->dma;
|
||||
entry->seg_addr[1] = 0;
|
||||
entry->seg_size = TRBS_PER_SEGMENT;
|
||||
entry->rsvd = 0;
|
||||
seg = seg->next;
|
||||
}
|
||||
|
||||
/* set ERST count with the number of entries in the segment table */
|
||||
val = xhci_readl(xhci, &xhci->ir_set->erst_size);
|
||||
val &= ERST_SIZE_MASK;
|
||||
val |= ERST_NUM_SEGS;
|
||||
xhci_dbg(xhci, "// Write ERST size = %i to ir_set 0 (some bits preserved)\n",
|
||||
val);
|
||||
xhci_writel(xhci, val, &xhci->ir_set->erst_size);
|
||||
|
||||
xhci_dbg(xhci, "// Set ERST entries to point to event ring.\n");
|
||||
/* set the segment table base address */
|
||||
xhci_dbg(xhci, "// Set ERST base address for ir_set 0 = 0x%llx\n",
|
||||
(unsigned long long)xhci->erst.erst_dma_addr);
|
||||
val = xhci_readl(xhci, &xhci->ir_set->erst_base[0]);
|
||||
val &= ERST_PTR_MASK;
|
||||
val |= (xhci->erst.erst_dma_addr & ~ERST_PTR_MASK);
|
||||
xhci_writel(xhci, val, &xhci->ir_set->erst_base[0]);
|
||||
xhci_writel(xhci, 0, &xhci->ir_set->erst_base[1]);
|
||||
|
||||
/* Set the event ring dequeue address */
|
||||
xhci_set_hc_event_deq(xhci);
|
||||
xhci_dbg(xhci, "Wrote ERST address to ir_set 0.\n");
|
||||
xhci_print_ir_set(xhci, xhci->ir_set, 0);
|
||||
|
||||
/*
|
||||
* XXX: Might need to set the Interrupter Moderation Register to
|
||||
* something other than the default (~1ms minimum between interrupts).
|
||||
* See section 5.5.1.2.
|
||||
*/
|
||||
init_completion(&xhci->addr_dev);
|
||||
for (i = 0; i < MAX_HC_SLOTS; ++i)
|
||||
xhci->devs[i] = 0;
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
xhci_warn(xhci, "Couldn't initialize memory\n");
|
||||
xhci_mem_cleanup(xhci);
|
||||
return -ENOMEM;
|
||||
}
|
166
drivers/usb/host/xhci-pci.c
Normal file
166
drivers/usb/host/xhci-pci.c
Normal file
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* xHCI host controller driver PCI Bus Glue.
|
||||
*
|
||||
* Copyright (C) 2008 Intel Corp.
|
||||
*
|
||||
* Author: Sarah Sharp
|
||||
* Some code borrowed from the Linux EHCI driver.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "xhci.h"
|
||||
|
||||
static const char hcd_name[] = "xhci_hcd";
|
||||
|
||||
/* called after powerup, by probe or system-pm "wakeup" */
|
||||
static int xhci_pci_reinit(struct xhci_hcd *xhci, struct pci_dev *pdev)
|
||||
{
|
||||
/*
|
||||
* TODO: Implement finding debug ports later.
|
||||
* TODO: see if there are any quirks that need to be added to handle
|
||||
* new extended capabilities.
|
||||
*/
|
||||
|
||||
/* PCI Memory-Write-Invalidate cycle support is optional (uncommon) */
|
||||
if (!pci_set_mwi(pdev))
|
||||
xhci_dbg(xhci, "MWI active\n");
|
||||
|
||||
xhci_dbg(xhci, "Finished xhci_pci_reinit\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* called during probe() after chip reset completes */
|
||||
static int xhci_pci_setup(struct usb_hcd *hcd)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
|
||||
int retval;
|
||||
|
||||
xhci->cap_regs = hcd->regs;
|
||||
xhci->op_regs = hcd->regs +
|
||||
HC_LENGTH(xhci_readl(xhci, &xhci->cap_regs->hc_capbase));
|
||||
xhci->run_regs = hcd->regs +
|
||||
(xhci_readl(xhci, &xhci->cap_regs->run_regs_off) & RTSOFF_MASK);
|
||||
/* Cache read-only capability registers */
|
||||
xhci->hcs_params1 = xhci_readl(xhci, &xhci->cap_regs->hcs_params1);
|
||||
xhci->hcs_params2 = xhci_readl(xhci, &xhci->cap_regs->hcs_params2);
|
||||
xhci->hcs_params3 = xhci_readl(xhci, &xhci->cap_regs->hcs_params3);
|
||||
xhci->hcc_params = xhci_readl(xhci, &xhci->cap_regs->hcc_params);
|
||||
xhci_print_registers(xhci);
|
||||
|
||||
/* Make sure the HC is halted. */
|
||||
retval = xhci_halt(xhci);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
xhci_dbg(xhci, "Resetting HCD\n");
|
||||
/* Reset the internal HC memory state and registers. */
|
||||
retval = xhci_reset(xhci);
|
||||
if (retval)
|
||||
return retval;
|
||||
xhci_dbg(xhci, "Reset complete\n");
|
||||
|
||||
xhci_dbg(xhci, "Calling HCD init\n");
|
||||
/* Initialize HCD and host controller data structures. */
|
||||
retval = xhci_init(hcd);
|
||||
if (retval)
|
||||
return retval;
|
||||
xhci_dbg(xhci, "Called HCD init\n");
|
||||
|
||||
pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &xhci->sbrn);
|
||||
xhci_dbg(xhci, "Got SBRN %u\n", (unsigned int) xhci->sbrn);
|
||||
|
||||
/* Find any debug ports */
|
||||
return xhci_pci_reinit(xhci, pdev);
|
||||
}
|
||||
|
||||
static const struct hc_driver xhci_pci_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "xHCI Host Controller",
|
||||
.hcd_priv_size = sizeof(struct xhci_hcd),
|
||||
|
||||
/*
|
||||
* generic hardware linkage
|
||||
*/
|
||||
.irq = xhci_irq,
|
||||
.flags = HCD_MEMORY | HCD_USB3,
|
||||
|
||||
/*
|
||||
* basic lifecycle operations
|
||||
*/
|
||||
.reset = xhci_pci_setup,
|
||||
.start = xhci_run,
|
||||
/* suspend and resume implemented later */
|
||||
.stop = xhci_stop,
|
||||
.shutdown = xhci_shutdown,
|
||||
|
||||
/*
|
||||
* managing i/o requests and associated device resources
|
||||
*/
|
||||
.urb_enqueue = xhci_urb_enqueue,
|
||||
.urb_dequeue = xhci_urb_dequeue,
|
||||
.alloc_dev = xhci_alloc_dev,
|
||||
.free_dev = xhci_free_dev,
|
||||
.add_endpoint = xhci_add_endpoint,
|
||||
.drop_endpoint = xhci_drop_endpoint,
|
||||
.check_bandwidth = xhci_check_bandwidth,
|
||||
.reset_bandwidth = xhci_reset_bandwidth,
|
||||
.address_device = xhci_address_device,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
*/
|
||||
.get_frame_number = xhci_get_frame,
|
||||
|
||||
/* Root hub support */
|
||||
.hub_control = xhci_hub_control,
|
||||
.hub_status_data = xhci_hub_status_data,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* PCI driver selection metadata; PCI hotplugging uses this */
|
||||
static const struct pci_device_id pci_ids[] = { {
|
||||
/* handle any USB 3.0 xHCI controller */
|
||||
PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_XHCI, ~0),
|
||||
.driver_data = (unsigned long) &xhci_pci_hc_driver,
|
||||
},
|
||||
{ /* end: all zeroes */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, pci_ids);
|
||||
|
||||
/* pci driver glue; this is a "new style" PCI driver module */
|
||||
static struct pci_driver xhci_pci_driver = {
|
||||
.name = (char *) hcd_name,
|
||||
.id_table = pci_ids,
|
||||
|
||||
.probe = usb_hcd_pci_probe,
|
||||
.remove = usb_hcd_pci_remove,
|
||||
/* suspend and resume implemented later */
|
||||
|
||||
.shutdown = usb_hcd_pci_shutdown,
|
||||
};
|
||||
|
||||
int xhci_register_pci()
|
||||
{
|
||||
return pci_register_driver(&xhci_pci_driver);
|
||||
}
|
||||
|
||||
void xhci_unregister_pci()
|
||||
{
|
||||
pci_unregister_driver(&xhci_pci_driver);
|
||||
}
|
1648
drivers/usb/host/xhci-ring.c
Normal file
1648
drivers/usb/host/xhci-ring.c
Normal file
File diff suppressed because it is too large
Load Diff
1157
drivers/usb/host/xhci.h
Normal file
1157
drivers/usb/host/xhci.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
||||
|
||||
config USB_SISUSBVGA
|
||||
tristate "USB 2.0 SVGA dongle support (Net2280/SiS315)"
|
||||
depends on USB && USB_EHCI_HCD
|
||||
depends on USB && (USB_MUSB_HDRC || USB_EHCI_HCD)
|
||||
---help---
|
||||
Say Y here if you intend to attach a USB2VGA dongle based on a
|
||||
Net2280 and a SiS315 chip.
|
||||
|
@ -1072,23 +1072,34 @@ static int unlink1 (struct usbtest_dev *dev, int pipe, int size, int async)
|
||||
*/
|
||||
msleep (jiffies % (2 * INTERRUPT_RATE));
|
||||
if (async) {
|
||||
retry:
|
||||
retval = usb_unlink_urb (urb);
|
||||
if (retval == -EBUSY || retval == -EIDRM) {
|
||||
/* we can't unlink urbs while they're completing.
|
||||
* or if they've completed, and we haven't resubmitted.
|
||||
* "normal" drivers would prevent resubmission, but
|
||||
* since we're testing unlink paths, we can't.
|
||||
*/
|
||||
ERROR(dev, "unlink retry\n");
|
||||
goto retry;
|
||||
while (!completion_done(&completion)) {
|
||||
retval = usb_unlink_urb(urb);
|
||||
|
||||
switch (retval) {
|
||||
case -EBUSY:
|
||||
case -EIDRM:
|
||||
/* we can't unlink urbs while they're completing
|
||||
* or if they've completed, and we haven't
|
||||
* resubmitted. "normal" drivers would prevent
|
||||
* resubmission, but since we're testing unlink
|
||||
* paths, we can't.
|
||||
*/
|
||||
ERROR(dev, "unlink retry\n");
|
||||
continue;
|
||||
case 0:
|
||||
case -EINPROGRESS:
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(&dev->intf->dev,
|
||||
"unlink fail %d\n", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
} else
|
||||
usb_kill_urb (urb);
|
||||
if (!(retval == 0 || retval == -EINPROGRESS)) {
|
||||
dev_err(&dev->intf->dev, "unlink fail %d\n", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
wait_for_completion (&completion);
|
||||
retval = urb->status;
|
||||
|
@ -733,7 +733,7 @@ int __init mon_text_init(void)
|
||||
{
|
||||
struct dentry *mondir;
|
||||
|
||||
mondir = debugfs_create_dir("usbmon", NULL);
|
||||
mondir = debugfs_create_dir("usbmon", usb_debug_root);
|
||||
if (IS_ERR(mondir)) {
|
||||
printk(KERN_NOTICE TAG ": debugfs is not available\n");
|
||||
return -ENODEV;
|
||||
|
@ -10,6 +10,7 @@ comment "Enable Host or Gadget support to see Inventra options"
|
||||
config USB_MUSB_HDRC
|
||||
depends on (USB || USB_GADGET) && HAVE_CLK
|
||||
depends on !SUPERH
|
||||
select NOP_USB_XCEIV if ARCH_DAVINCI
|
||||
select TWL4030_USB if MACH_OMAP_3430SDP
|
||||
select USB_OTG_UTILS
|
||||
tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)'
|
||||
@ -55,6 +56,7 @@ comment "Blackfin high speed USB Support"
|
||||
config USB_TUSB6010
|
||||
boolean "TUSB 6010 support"
|
||||
depends on USB_MUSB_HDRC && !USB_MUSB_SOC
|
||||
select NOP_USB_XCEIV
|
||||
default y
|
||||
help
|
||||
The TUSB 6010 chip, from Texas Instruments, connects a discrete
|
||||
|
@ -143,7 +143,7 @@ static void musb_conn_timer_handler(unsigned long _musb)
|
||||
u16 val;
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
switch (musb->xceiv.state) {
|
||||
switch (musb->xceiv->state) {
|
||||
case OTG_STATE_A_IDLE:
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
/* Start a new session */
|
||||
@ -154,7 +154,7 @@ static void musb_conn_timer_handler(unsigned long _musb)
|
||||
val = musb_readw(musb->mregs, MUSB_DEVCTL);
|
||||
if (!(val & MUSB_DEVCTL_BDEVICE)) {
|
||||
gpio_set_value(musb->config->gpio_vrsel, 1);
|
||||
musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
|
||||
musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
|
||||
} else {
|
||||
gpio_set_value(musb->config->gpio_vrsel, 0);
|
||||
|
||||
@ -247,6 +247,11 @@ int __init musb_platform_init(struct musb *musb)
|
||||
}
|
||||
gpio_direction_output(musb->config->gpio_vrsel, 0);
|
||||
|
||||
usb_nop_xceiv_register();
|
||||
musb->xceiv = otg_get_transceiver();
|
||||
if (!musb->xceiv)
|
||||
return -ENODEV;
|
||||
|
||||
if (ANOMALY_05000346) {
|
||||
bfin_write_USB_APHY_CALIB(ANOMALY_05000346_value);
|
||||
SSYNC();
|
||||
@ -291,7 +296,7 @@ int __init musb_platform_init(struct musb *musb)
|
||||
musb_conn_timer_handler, (unsigned long) musb);
|
||||
}
|
||||
if (is_peripheral_enabled(musb))
|
||||
musb->xceiv.set_power = bfin_set_power;
|
||||
musb->xceiv->set_power = bfin_set_power;
|
||||
|
||||
musb->isr = blackfin_interrupt;
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
* The TUSB6020, using VLYNQ, has CPPI that looks much like DaVinci.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include "musb_core.h"
|
||||
@ -1145,17 +1146,27 @@ static bool cppi_rx_scan(struct cppi *cppi, unsigned ch)
|
||||
return completed;
|
||||
}
|
||||
|
||||
void cppi_completion(struct musb *musb, u32 rx, u32 tx)
|
||||
irqreturn_t cppi_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
void __iomem *tibase;
|
||||
int i, index;
|
||||
struct musb *musb = dev_id;
|
||||
struct cppi *cppi;
|
||||
void __iomem *tibase;
|
||||
struct musb_hw_ep *hw_ep = NULL;
|
||||
u32 rx, tx;
|
||||
int i, index;
|
||||
|
||||
cppi = container_of(musb->dma_controller, struct cppi, controller);
|
||||
|
||||
tibase = musb->ctrl_base;
|
||||
|
||||
tx = musb_readl(tibase, DAVINCI_TXCPPI_MASKED_REG);
|
||||
rx = musb_readl(tibase, DAVINCI_RXCPPI_MASKED_REG);
|
||||
|
||||
if (!tx && !rx)
|
||||
return IRQ_NONE;
|
||||
|
||||
DBG(4, "CPPI IRQ Tx%x Rx%x\n", tx, rx);
|
||||
|
||||
/* process TX channels */
|
||||
for (index = 0; tx; tx = tx >> 1, index++) {
|
||||
struct cppi_channel *tx_ch;
|
||||
@ -1273,6 +1284,8 @@ void cppi_completion(struct musb *musb, u32 rx, u32 tx)
|
||||
|
||||
/* write to CPPI EOI register to re-enable interrupts */
|
||||
musb_writel(tibase, DAVINCI_CPPI_EOI_REG, 0);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Instantiate a software object representing a DMA controller. */
|
||||
@ -1280,6 +1293,9 @@ struct dma_controller *__init
|
||||
dma_controller_create(struct musb *musb, void __iomem *mregs)
|
||||
{
|
||||
struct cppi *controller;
|
||||
struct device *dev = musb->controller;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
int irq = platform_get_irq(pdev, 1);
|
||||
|
||||
controller = kzalloc(sizeof *controller, GFP_KERNEL);
|
||||
if (!controller)
|
||||
@ -1310,6 +1326,15 @@ dma_controller_create(struct musb *musb, void __iomem *mregs)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (irq > 0) {
|
||||
if (request_irq(irq, cppi_interrupt, 0, "cppi-dma", musb)) {
|
||||
dev_err(dev, "request_irq %d failed!\n", irq);
|
||||
dma_controller_destroy(&controller->controller);
|
||||
return NULL;
|
||||
}
|
||||
controller->irq = irq;
|
||||
}
|
||||
|
||||
return &controller->controller;
|
||||
}
|
||||
|
||||
@ -1322,6 +1347,9 @@ void dma_controller_destroy(struct dma_controller *c)
|
||||
|
||||
cppi = container_of(c, struct cppi, controller);
|
||||
|
||||
if (cppi->irq)
|
||||
free_irq(cppi->irq, cppi->musb);
|
||||
|
||||
/* assert: caller stopped the controller first */
|
||||
dma_pool_destroy(cppi->pool);
|
||||
|
||||
|
@ -119,6 +119,8 @@ struct cppi {
|
||||
void __iomem *mregs; /* Mentor regs */
|
||||
void __iomem *tibase; /* TI/CPPI regs */
|
||||
|
||||
int irq;
|
||||
|
||||
struct cppi_channel tx[4];
|
||||
struct cppi_channel rx[4];
|
||||
|
||||
@ -127,7 +129,7 @@ struct cppi {
|
||||
struct list_head tx_complete;
|
||||
};
|
||||
|
||||
/* irq handling hook */
|
||||
extern void cppi_completion(struct musb *, u32 rx, u32 tx);
|
||||
/* CPPI IRQ handler */
|
||||
extern irqreturn_t cppi_interrupt(int, void *);
|
||||
|
||||
#endif /* end of ifndef _CPPI_DMA_H_ */
|
||||
|
@ -215,7 +215,7 @@ static void otg_timer(unsigned long _musb)
|
||||
DBG(7, "poll devctl %02x (%s)\n", devctl, otg_state_string(musb));
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
switch (musb->xceiv.state) {
|
||||
switch (musb->xceiv->state) {
|
||||
case OTG_STATE_A_WAIT_VFALL:
|
||||
/* Wait till VBUS falls below SessionEnd (~0.2V); the 1.3 RTL
|
||||
* seems to mis-handle session "start" otherwise (or in our
|
||||
@ -226,7 +226,7 @@ static void otg_timer(unsigned long _musb)
|
||||
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
|
||||
break;
|
||||
}
|
||||
musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
|
||||
musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
|
||||
musb_writel(musb->ctrl_base, DAVINCI_USB_INT_SET_REG,
|
||||
MUSB_INTR_VBUSERROR << DAVINCI_USB_USBINT_SHIFT);
|
||||
break;
|
||||
@ -251,7 +251,7 @@ static void otg_timer(unsigned long _musb)
|
||||
if (devctl & MUSB_DEVCTL_BDEVICE)
|
||||
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
|
||||
else
|
||||
musb->xceiv.state = OTG_STATE_A_IDLE;
|
||||
musb->xceiv->state = OTG_STATE_A_IDLE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -265,6 +265,7 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
|
||||
irqreturn_t retval = IRQ_NONE;
|
||||
struct musb *musb = __hci;
|
||||
void __iomem *tibase = musb->ctrl_base;
|
||||
struct cppi *cppi;
|
||||
u32 tmp;
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
@ -281,16 +282,9 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
|
||||
/* CPPI interrupts share the same IRQ line, but have their own
|
||||
* mask, state, "vector", and EOI registers.
|
||||
*/
|
||||
if (is_cppi_enabled()) {
|
||||
u32 cppi_tx = musb_readl(tibase, DAVINCI_TXCPPI_MASKED_REG);
|
||||
u32 cppi_rx = musb_readl(tibase, DAVINCI_RXCPPI_MASKED_REG);
|
||||
|
||||
if (cppi_tx || cppi_rx) {
|
||||
DBG(4, "CPPI IRQ t%x r%x\n", cppi_tx, cppi_rx);
|
||||
cppi_completion(musb, cppi_rx, cppi_tx);
|
||||
retval = IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
cppi = container_of(musb->dma_controller, struct cppi, controller);
|
||||
if (is_cppi_enabled() && musb->dma_controller && !cppi->irq)
|
||||
retval = cppi_interrupt(irq, __hci);
|
||||
|
||||
/* ack and handle non-CPPI interrupts */
|
||||
tmp = musb_readl(tibase, DAVINCI_USB_INT_SRC_MASKED_REG);
|
||||
@ -331,21 +325,21 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
|
||||
* to stop registering in devctl.
|
||||
*/
|
||||
musb->int_usb &= ~MUSB_INTR_VBUSERROR;
|
||||
musb->xceiv.state = OTG_STATE_A_WAIT_VFALL;
|
||||
musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
|
||||
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
|
||||
WARNING("VBUS error workaround (delay coming)\n");
|
||||
} else if (is_host_enabled(musb) && drvvbus) {
|
||||
musb->is_active = 1;
|
||||
MUSB_HST_MODE(musb);
|
||||
musb->xceiv.default_a = 1;
|
||||
musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
|
||||
musb->xceiv->default_a = 1;
|
||||
musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
|
||||
portstate(musb->port1_status |= USB_PORT_STAT_POWER);
|
||||
del_timer(&otg_workaround);
|
||||
} else {
|
||||
musb->is_active = 0;
|
||||
MUSB_DEV_MODE(musb);
|
||||
musb->xceiv.default_a = 0;
|
||||
musb->xceiv.state = OTG_STATE_B_IDLE;
|
||||
musb->xceiv->default_a = 0;
|
||||
musb->xceiv->state = OTG_STATE_B_IDLE;
|
||||
portstate(musb->port1_status &= ~USB_PORT_STAT_POWER);
|
||||
}
|
||||
|
||||
@ -367,17 +361,12 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
|
||||
|
||||
/* poll for ID change */
|
||||
if (is_otg_enabled(musb)
|
||||
&& musb->xceiv.state == OTG_STATE_B_IDLE)
|
||||
&& musb->xceiv->state == OTG_STATE_B_IDLE)
|
||||
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
|
||||
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
|
||||
/* REVISIT we sometimes get unhandled IRQs
|
||||
* (e.g. ep0). not clear why...
|
||||
*/
|
||||
if (retval != IRQ_HANDLED)
|
||||
DBG(5, "unhandled? %08x\n", tmp);
|
||||
return IRQ_HANDLED;
|
||||
return retval;
|
||||
}
|
||||
|
||||
int musb_platform_set_mode(struct musb *musb, u8 mode)
|
||||
@ -391,6 +380,11 @@ int __init musb_platform_init(struct musb *musb)
|
||||
void __iomem *tibase = musb->ctrl_base;
|
||||
u32 revision;
|
||||
|
||||
usb_nop_xceiv_register();
|
||||
musb->xceiv = otg_get_transceiver();
|
||||
if (!musb->xceiv)
|
||||
return -ENODEV;
|
||||
|
||||
musb->mregs += DAVINCI_BASE_OFFSET;
|
||||
|
||||
clk_enable(musb->clock);
|
||||
@ -398,7 +392,7 @@ int __init musb_platform_init(struct musb *musb)
|
||||
/* returns zero if e.g. not clocked */
|
||||
revision = musb_readl(tibase, DAVINCI_USB_VERSION_REG);
|
||||
if (revision == 0)
|
||||
return -ENODEV;
|
||||
goto fail;
|
||||
|
||||
if (is_host_enabled(musb))
|
||||
setup_timer(&otg_workaround, otg_timer, (unsigned long) musb);
|
||||
@ -432,6 +426,10 @@ int __init musb_platform_init(struct musb *musb)
|
||||
|
||||
musb->isr = davinci_interrupt;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
usb_nop_xceiv_unregister();
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int musb_platform_exit(struct musb *musb)
|
||||
@ -442,7 +440,7 @@ int musb_platform_exit(struct musb *musb)
|
||||
davinci_source_power(musb, 0 /*off*/, 1);
|
||||
|
||||
/* delay, to avoid problems with module reload */
|
||||
if (is_host_enabled(musb) && musb->xceiv.default_a) {
|
||||
if (is_host_enabled(musb) && musb->xceiv->default_a) {
|
||||
int maxdelay = 30;
|
||||
u8 devctl, warn = 0;
|
||||
|
||||
@ -471,5 +469,7 @@ int musb_platform_exit(struct musb *musb)
|
||||
|
||||
clk_disable(musb->clock);
|
||||
|
||||
usb_nop_xceiv_unregister();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -112,6 +112,7 @@
|
||||
#include "davinci.h"
|
||||
#endif
|
||||
|
||||
#define TA_WAIT_BCON(m) max_t(int, (m)->a_wait_bcon, OTG_TIME_A_WAIT_BCON)
|
||||
|
||||
|
||||
unsigned musb_debug;
|
||||
@ -267,7 +268,7 @@ void musb_load_testpacket(struct musb *musb)
|
||||
|
||||
const char *otg_state_string(struct musb *musb)
|
||||
{
|
||||
switch (musb->xceiv.state) {
|
||||
switch (musb->xceiv->state) {
|
||||
case OTG_STATE_A_IDLE: return "a_idle";
|
||||
case OTG_STATE_A_WAIT_VRISE: return "a_wait_vrise";
|
||||
case OTG_STATE_A_WAIT_BCON: return "a_wait_bcon";
|
||||
@ -287,12 +288,6 @@ const char *otg_state_string(struct musb *musb)
|
||||
|
||||
#ifdef CONFIG_USB_MUSB_OTG
|
||||
|
||||
/*
|
||||
* See also USB_OTG_1-3.pdf 6.6.5 Timers
|
||||
* REVISIT: Are the other timers done in the hardware?
|
||||
*/
|
||||
#define TB_ASE0_BRST 100 /* Min 3.125 ms */
|
||||
|
||||
/*
|
||||
* Handles OTG hnp timeouts, such as b_ase0_brst
|
||||
*/
|
||||
@ -302,16 +297,18 @@ void musb_otg_timer_func(unsigned long data)
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
switch (musb->xceiv.state) {
|
||||
switch (musb->xceiv->state) {
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
DBG(1, "HNP: b_wait_acon timeout; back to b_peripheral\n");
|
||||
musb_g_disconnect(musb);
|
||||
musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
|
||||
musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
|
||||
musb->is_active = 0;
|
||||
break;
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
DBG(1, "HNP: a_wait_bcon timeout; back to a_host\n");
|
||||
musb_hnp_stop(musb);
|
||||
DBG(1, "HNP: %s timeout\n", otg_state_string(musb));
|
||||
musb_set_vbus(musb, 0);
|
||||
musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
|
||||
break;
|
||||
default:
|
||||
DBG(1, "HNP: Unhandled mode %s\n", otg_state_string(musb));
|
||||
@ -320,10 +317,8 @@ void musb_otg_timer_func(unsigned long data)
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
}
|
||||
|
||||
static DEFINE_TIMER(musb_otg_timer, musb_otg_timer_func, 0, 0);
|
||||
|
||||
/*
|
||||
* Stops the B-device HNP state. Caller must take care of locking.
|
||||
* Stops the HNP transition. Caller must take care of locking.
|
||||
*/
|
||||
void musb_hnp_stop(struct musb *musb)
|
||||
{
|
||||
@ -331,20 +326,17 @@ void musb_hnp_stop(struct musb *musb)
|
||||
void __iomem *mbase = musb->mregs;
|
||||
u8 reg;
|
||||
|
||||
switch (musb->xceiv.state) {
|
||||
DBG(1, "HNP: stop from %s\n", otg_state_string(musb));
|
||||
|
||||
switch (musb->xceiv->state) {
|
||||
case OTG_STATE_A_PERIPHERAL:
|
||||
case OTG_STATE_A_WAIT_VFALL:
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
DBG(1, "HNP: Switching back to A-host\n");
|
||||
musb_g_disconnect(musb);
|
||||
musb->xceiv.state = OTG_STATE_A_IDLE;
|
||||
MUSB_HST_MODE(musb);
|
||||
musb->is_active = 0;
|
||||
DBG(1, "HNP: back to %s\n", otg_state_string(musb));
|
||||
break;
|
||||
case OTG_STATE_B_HOST:
|
||||
DBG(1, "HNP: Disabling HR\n");
|
||||
hcd->self.is_b_host = 0;
|
||||
musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
|
||||
musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
|
||||
MUSB_DEV_MODE(musb);
|
||||
reg = musb_readb(mbase, MUSB_POWER);
|
||||
reg |= MUSB_POWER_SUSPENDM;
|
||||
@ -402,7 +394,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
|
||||
if (devctl & MUSB_DEVCTL_HM) {
|
||||
#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
||||
switch (musb->xceiv.state) {
|
||||
switch (musb->xceiv->state) {
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
/* remote wakeup? later, GetPortStatus
|
||||
* will stop RESUME signaling
|
||||
@ -425,12 +417,12 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
musb->rh_timer = jiffies
|
||||
+ msecs_to_jiffies(20);
|
||||
|
||||
musb->xceiv.state = OTG_STATE_A_HOST;
|
||||
musb->xceiv->state = OTG_STATE_A_HOST;
|
||||
musb->is_active = 1;
|
||||
usb_hcd_resume_root_hub(musb_to_hcd(musb));
|
||||
break;
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
|
||||
musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
|
||||
musb->is_active = 1;
|
||||
MUSB_DEV_MODE(musb);
|
||||
break;
|
||||
@ -441,11 +433,11 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
switch (musb->xceiv.state) {
|
||||
switch (musb->xceiv->state) {
|
||||
#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
/* possibly DISCONNECT is upcoming */
|
||||
musb->xceiv.state = OTG_STATE_A_HOST;
|
||||
musb->xceiv->state = OTG_STATE_A_HOST;
|
||||
usb_hcd_resume_root_hub(musb_to_hcd(musb));
|
||||
break;
|
||||
#endif
|
||||
@ -490,7 +482,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
*/
|
||||
musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
|
||||
musb->ep0_stage = MUSB_EP0_START;
|
||||
musb->xceiv.state = OTG_STATE_A_IDLE;
|
||||
musb->xceiv->state = OTG_STATE_A_IDLE;
|
||||
MUSB_HST_MODE(musb);
|
||||
musb_set_vbus(musb, 1);
|
||||
|
||||
@ -516,7 +508,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
* REVISIT: do delays from lots of DEBUG_KERNEL checks
|
||||
* make trouble here, keeping VBUS < 4.4V ?
|
||||
*/
|
||||
switch (musb->xceiv.state) {
|
||||
switch (musb->xceiv->state) {
|
||||
case OTG_STATE_A_HOST:
|
||||
/* recovery is dicey once we've gotten past the
|
||||
* initial stages of enumeration, but if VBUS
|
||||
@ -594,37 +586,40 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
if (devctl & MUSB_DEVCTL_LSDEV)
|
||||
musb->port1_status |= USB_PORT_STAT_LOW_SPEED;
|
||||
|
||||
/* indicate new connection to OTG machine */
|
||||
switch (musb->xceiv->state) {
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
if (int_usb & MUSB_INTR_SUSPEND) {
|
||||
DBG(1, "HNP: SUSPEND+CONNECT, now b_host\n");
|
||||
int_usb &= ~MUSB_INTR_SUSPEND;
|
||||
goto b_host;
|
||||
} else
|
||||
DBG(1, "CONNECT as b_peripheral???\n");
|
||||
break;
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
DBG(1, "HNP: CONNECT, now b_host\n");
|
||||
b_host:
|
||||
musb->xceiv->state = OTG_STATE_B_HOST;
|
||||
hcd->self.is_b_host = 1;
|
||||
musb->ignore_disconnect = 0;
|
||||
del_timer(&musb->otg_timer);
|
||||
break;
|
||||
default:
|
||||
if ((devctl & MUSB_DEVCTL_VBUS)
|
||||
== (3 << MUSB_DEVCTL_VBUS_SHIFT)) {
|
||||
musb->xceiv->state = OTG_STATE_A_HOST;
|
||||
hcd->self.is_b_host = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* poke the root hub */
|
||||
MUSB_HST_MODE(musb);
|
||||
if (hcd->status_urb)
|
||||
usb_hcd_poll_rh_status(hcd);
|
||||
else
|
||||
usb_hcd_resume_root_hub(hcd);
|
||||
|
||||
MUSB_HST_MODE(musb);
|
||||
|
||||
/* indicate new connection to OTG machine */
|
||||
switch (musb->xceiv.state) {
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
if (int_usb & MUSB_INTR_SUSPEND) {
|
||||
DBG(1, "HNP: SUSPEND+CONNECT, now b_host\n");
|
||||
musb->xceiv.state = OTG_STATE_B_HOST;
|
||||
hcd->self.is_b_host = 1;
|
||||
int_usb &= ~MUSB_INTR_SUSPEND;
|
||||
} else
|
||||
DBG(1, "CONNECT as b_peripheral???\n");
|
||||
break;
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
DBG(1, "HNP: Waiting to switch to b_host state\n");
|
||||
musb->xceiv.state = OTG_STATE_B_HOST;
|
||||
hcd->self.is_b_host = 1;
|
||||
break;
|
||||
default:
|
||||
if ((devctl & MUSB_DEVCTL_VBUS)
|
||||
== (3 << MUSB_DEVCTL_VBUS_SHIFT)) {
|
||||
musb->xceiv.state = OTG_STATE_A_HOST;
|
||||
hcd->self.is_b_host = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
DBG(1, "CONNECT (%s) devctl %02x\n",
|
||||
otg_state_string(musb), devctl);
|
||||
}
|
||||
@ -650,7 +645,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
}
|
||||
} else if (is_peripheral_capable()) {
|
||||
DBG(1, "BUS RESET as %s\n", otg_state_string(musb));
|
||||
switch (musb->xceiv.state) {
|
||||
switch (musb->xceiv->state) {
|
||||
#ifdef CONFIG_USB_OTG
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
/* We need to ignore disconnect on suspend
|
||||
@ -661,24 +656,27 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
musb_g_reset(musb);
|
||||
/* FALLTHROUGH */
|
||||
case OTG_STATE_A_WAIT_BCON: /* OPT TD.4.7-900ms */
|
||||
DBG(1, "HNP: Setting timer as %s\n",
|
||||
otg_state_string(musb));
|
||||
musb_otg_timer.data = (unsigned long)musb;
|
||||
mod_timer(&musb_otg_timer, jiffies
|
||||
+ msecs_to_jiffies(100));
|
||||
/* never use invalid T(a_wait_bcon) */
|
||||
DBG(1, "HNP: in %s, %d msec timeout\n",
|
||||
otg_state_string(musb),
|
||||
TA_WAIT_BCON(musb));
|
||||
mod_timer(&musb->otg_timer, jiffies
|
||||
+ msecs_to_jiffies(TA_WAIT_BCON(musb)));
|
||||
break;
|
||||
case OTG_STATE_A_PERIPHERAL:
|
||||
musb_hnp_stop(musb);
|
||||
musb->ignore_disconnect = 0;
|
||||
del_timer(&musb->otg_timer);
|
||||
musb_g_reset(musb);
|
||||
break;
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
DBG(1, "HNP: RESET (%s), to b_peripheral\n",
|
||||
otg_state_string(musb));
|
||||
musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
|
||||
musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
|
||||
musb_g_reset(musb);
|
||||
break;
|
||||
#endif
|
||||
case OTG_STATE_B_IDLE:
|
||||
musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
|
||||
musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
|
||||
/* FALLTHROUGH */
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
musb_g_reset(musb);
|
||||
@ -763,7 +761,7 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
|
||||
MUSB_MODE(musb), devctl);
|
||||
handled = IRQ_HANDLED;
|
||||
|
||||
switch (musb->xceiv.state) {
|
||||
switch (musb->xceiv->state) {
|
||||
#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
||||
case OTG_STATE_A_HOST:
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
@ -776,7 +774,16 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
|
||||
#endif /* HOST */
|
||||
#ifdef CONFIG_USB_MUSB_OTG
|
||||
case OTG_STATE_B_HOST:
|
||||
musb_hnp_stop(musb);
|
||||
/* REVISIT this behaves for "real disconnect"
|
||||
* cases; make sure the other transitions from
|
||||
* from B_HOST act right too. The B_HOST code
|
||||
* in hnp_stop() is currently not used...
|
||||
*/
|
||||
musb_root_disconnect(musb);
|
||||
musb_to_hcd(musb)->self.is_b_host = 0;
|
||||
musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
|
||||
MUSB_DEV_MODE(musb);
|
||||
musb_g_disconnect(musb);
|
||||
break;
|
||||
case OTG_STATE_A_PERIPHERAL:
|
||||
musb_hnp_stop(musb);
|
||||
@ -805,26 +812,35 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
|
||||
otg_state_string(musb), devctl, power);
|
||||
handled = IRQ_HANDLED;
|
||||
|
||||
switch (musb->xceiv.state) {
|
||||
switch (musb->xceiv->state) {
|
||||
#ifdef CONFIG_USB_MUSB_OTG
|
||||
case OTG_STATE_A_PERIPHERAL:
|
||||
/*
|
||||
* We cannot stop HNP here, devctl BDEVICE might be
|
||||
* still set.
|
||||
/* We also come here if the cable is removed, since
|
||||
* this silicon doesn't report ID-no-longer-grounded.
|
||||
*
|
||||
* We depend on T(a_wait_bcon) to shut us down, and
|
||||
* hope users don't do anything dicey during this
|
||||
* undesired detour through A_WAIT_BCON.
|
||||
*/
|
||||
musb_hnp_stop(musb);
|
||||
usb_hcd_resume_root_hub(musb_to_hcd(musb));
|
||||
musb_root_disconnect(musb);
|
||||
musb_platform_try_idle(musb, jiffies
|
||||
+ msecs_to_jiffies(musb->a_wait_bcon
|
||||
? : OTG_TIME_A_WAIT_BCON));
|
||||
break;
|
||||
#endif
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
musb_g_suspend(musb);
|
||||
musb->is_active = is_otg_enabled(musb)
|
||||
&& musb->xceiv.gadget->b_hnp_enable;
|
||||
&& musb->xceiv->gadget->b_hnp_enable;
|
||||
if (musb->is_active) {
|
||||
#ifdef CONFIG_USB_MUSB_OTG
|
||||
musb->xceiv.state = OTG_STATE_B_WAIT_ACON;
|
||||
musb->xceiv->state = OTG_STATE_B_WAIT_ACON;
|
||||
DBG(1, "HNP: Setting timer for b_ase0_brst\n");
|
||||
musb_otg_timer.data = (unsigned long)musb;
|
||||
mod_timer(&musb_otg_timer, jiffies
|
||||
+ msecs_to_jiffies(TB_ASE0_BRST));
|
||||
mod_timer(&musb->otg_timer, jiffies
|
||||
+ msecs_to_jiffies(
|
||||
OTG_TIME_B_ASE0_BRST));
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
@ -834,9 +850,9 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
|
||||
+ msecs_to_jiffies(musb->a_wait_bcon));
|
||||
break;
|
||||
case OTG_STATE_A_HOST:
|
||||
musb->xceiv.state = OTG_STATE_A_SUSPEND;
|
||||
musb->xceiv->state = OTG_STATE_A_SUSPEND;
|
||||
musb->is_active = is_otg_enabled(musb)
|
||||
&& musb->xceiv.host->b_hnp_enable;
|
||||
&& musb->xceiv->host->b_hnp_enable;
|
||||
break;
|
||||
case OTG_STATE_B_HOST:
|
||||
/* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */
|
||||
@ -1068,14 +1084,13 @@ static struct fifo_cfg __initdata mode_4_cfg[] = {
|
||||
{ .hw_ep_num = 8, .style = FIFO_RX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 9, .style = FIFO_TX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 9, .style = FIFO_RX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 10, .style = FIFO_TX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 10, .style = FIFO_RX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 11, .style = FIFO_TX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 11, .style = FIFO_RX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 12, .style = FIFO_TX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 12, .style = FIFO_RX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 13, .style = FIFO_TX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 13, .style = FIFO_RX, .maxpacket = 512, },
|
||||
{ .hw_ep_num = 10, .style = FIFO_TX, .maxpacket = 256, },
|
||||
{ .hw_ep_num = 10, .style = FIFO_RX, .maxpacket = 64, },
|
||||
{ .hw_ep_num = 11, .style = FIFO_TX, .maxpacket = 256, },
|
||||
{ .hw_ep_num = 11, .style = FIFO_RX, .maxpacket = 64, },
|
||||
{ .hw_ep_num = 12, .style = FIFO_TX, .maxpacket = 256, },
|
||||
{ .hw_ep_num = 12, .style = FIFO_RX, .maxpacket = 64, },
|
||||
{ .hw_ep_num = 13, .style = FIFO_RXTX, .maxpacket = 4096, },
|
||||
{ .hw_ep_num = 14, .style = FIFO_RXTX, .maxpacket = 1024, },
|
||||
{ .hw_ep_num = 15, .style = FIFO_RXTX, .maxpacket = 1024, },
|
||||
};
|
||||
@ -1335,11 +1350,11 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb)
|
||||
}
|
||||
if (reg & MUSB_CONFIGDATA_HBRXE) {
|
||||
strcat(aInfo, ", HB-ISO Rx");
|
||||
strcat(aInfo, " (X)"); /* no driver support */
|
||||
musb->hb_iso_rx = true;
|
||||
}
|
||||
if (reg & MUSB_CONFIGDATA_HBTXE) {
|
||||
strcat(aInfo, ", HB-ISO Tx");
|
||||
strcat(aInfo, " (X)"); /* no driver support */
|
||||
musb->hb_iso_tx = true;
|
||||
}
|
||||
if (reg & MUSB_CONFIGDATA_SOFTCONE)
|
||||
strcat(aInfo, ", SoftConn");
|
||||
@ -1481,13 +1496,7 @@ static irqreturn_t generic_interrupt(int irq, void *__hci)
|
||||
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
|
||||
/* REVISIT we sometimes get spurious IRQs on g_ep0
|
||||
* not clear why...
|
||||
*/
|
||||
if (retval != IRQ_HANDLED)
|
||||
DBG(5, "spurious?\n");
|
||||
|
||||
return IRQ_HANDLED;
|
||||
return retval;
|
||||
}
|
||||
|
||||
#else
|
||||
@ -1687,8 +1696,9 @@ musb_vbus_store(struct device *dev, struct device_attribute *attr,
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
musb->a_wait_bcon = val;
|
||||
if (musb->xceiv.state == OTG_STATE_A_WAIT_BCON)
|
||||
/* force T(a_wait_bcon) to be zero/unlimited *OR* valid */
|
||||
musb->a_wait_bcon = val ? max_t(int, val, OTG_TIME_A_WAIT_BCON) : 0 ;
|
||||
if (musb->xceiv->state == OTG_STATE_A_WAIT_BCON)
|
||||
musb->is_active = 0;
|
||||
musb_platform_try_idle(musb, jiffies + msecs_to_jiffies(val));
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
@ -1706,10 +1716,13 @@ musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
val = musb->a_wait_bcon;
|
||||
/* FIXME get_vbus_status() is normally #defined as false...
|
||||
* and is effectively TUSB-specific.
|
||||
*/
|
||||
vbus = musb_platform_get_vbus_status(musb);
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
|
||||
return sprintf(buf, "Vbus %s, timeout %lu\n",
|
||||
return sprintf(buf, "Vbus %s, timeout %lu msec\n",
|
||||
vbus ? "on" : "off", val);
|
||||
}
|
||||
static DEVICE_ATTR(vbus, 0644, musb_vbus_show, musb_vbus_store);
|
||||
@ -1749,8 +1762,8 @@ static void musb_irq_work(struct work_struct *data)
|
||||
struct musb *musb = container_of(data, struct musb, irq_work);
|
||||
static int old_state;
|
||||
|
||||
if (musb->xceiv.state != old_state) {
|
||||
old_state = musb->xceiv.state;
|
||||
if (musb->xceiv->state != old_state) {
|
||||
old_state = musb->xceiv->state;
|
||||
sysfs_notify(&musb->controller->kobj, NULL, "mode");
|
||||
}
|
||||
}
|
||||
@ -1782,6 +1795,7 @@ allocate_instance(struct device *dev,
|
||||
hcd->uses_new_polling = 1;
|
||||
|
||||
musb->vbuserr_retry = VBUSERR_RETRY_COUNT;
|
||||
musb->a_wait_bcon = OTG_TIME_A_WAIT_BCON;
|
||||
#else
|
||||
musb = kzalloc(sizeof *musb, GFP_KERNEL);
|
||||
if (!musb)
|
||||
@ -1847,7 +1861,7 @@ static void musb_free(struct musb *musb)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_MUSB_OTG
|
||||
put_device(musb->xceiv.dev);
|
||||
put_device(musb->xceiv->dev);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
||||
@ -1928,10 +1942,18 @@ bad_config:
|
||||
}
|
||||
}
|
||||
|
||||
/* assume vbus is off */
|
||||
|
||||
/* platform adjusts musb->mregs and musb->isr if needed,
|
||||
* and activates clocks
|
||||
/* The musb_platform_init() call:
|
||||
* - adjusts musb->mregs and musb->isr if needed,
|
||||
* - may initialize an integrated tranceiver
|
||||
* - initializes musb->xceiv, usually by otg_get_transceiver()
|
||||
* - activates clocks.
|
||||
* - stops powering VBUS
|
||||
* - assigns musb->board_set_vbus if host mode is enabled
|
||||
*
|
||||
* There are various transciever configurations. Blackfin,
|
||||
* DaVinci, TUSB60x0, and others integrate them. OMAP3 uses
|
||||
* external/discrete ones in various flavors (twl4030 family,
|
||||
* isp1504, non-OTG, etc) mostly hooking up through ULPI.
|
||||
*/
|
||||
musb->isr = generic_interrupt;
|
||||
status = musb_platform_init(musb);
|
||||
@ -1968,6 +1990,10 @@ bad_config:
|
||||
if (status < 0)
|
||||
goto fail2;
|
||||
|
||||
#ifdef CONFIG_USB_OTG
|
||||
setup_timer(&musb->otg_timer, musb_otg_timer_func, (unsigned long) musb);
|
||||
#endif
|
||||
|
||||
/* Init IRQ workqueue before request_irq */
|
||||
INIT_WORK(&musb->irq_work, musb_irq_work);
|
||||
|
||||
@ -1999,17 +2025,17 @@ bad_config:
|
||||
? "DMA" : "PIO",
|
||||
musb->nIrq);
|
||||
|
||||
#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
||||
/* host side needs more setup, except for no-host modes */
|
||||
if (musb->board_mode != MUSB_PERIPHERAL) {
|
||||
/* host side needs more setup */
|
||||
if (is_host_enabled(musb)) {
|
||||
struct usb_hcd *hcd = musb_to_hcd(musb);
|
||||
|
||||
if (musb->board_mode == MUSB_OTG)
|
||||
otg_set_host(musb->xceiv, &hcd->self);
|
||||
|
||||
if (is_otg_enabled(musb))
|
||||
hcd->self.otg_port = 1;
|
||||
musb->xceiv.host = &hcd->self;
|
||||
musb->xceiv->host = &hcd->self;
|
||||
hcd->power_budget = 2 * (plat->power ? : 250);
|
||||
}
|
||||
#endif /* CONFIG_USB_MUSB_HDRC_HCD */
|
||||
|
||||
/* For the host-only role, we can activate right away.
|
||||
* (We expect the ID pin to be forcibly grounded!!)
|
||||
@ -2017,8 +2043,8 @@ bad_config:
|
||||
*/
|
||||
if (!is_otg_enabled(musb) && is_host_enabled(musb)) {
|
||||
MUSB_HST_MODE(musb);
|
||||
musb->xceiv.default_a = 1;
|
||||
musb->xceiv.state = OTG_STATE_A_IDLE;
|
||||
musb->xceiv->default_a = 1;
|
||||
musb->xceiv->state = OTG_STATE_A_IDLE;
|
||||
|
||||
status = usb_add_hcd(musb_to_hcd(musb), -1, 0);
|
||||
if (status)
|
||||
@ -2033,8 +2059,8 @@ bad_config:
|
||||
|
||||
} else /* peripheral is enabled */ {
|
||||
MUSB_DEV_MODE(musb);
|
||||
musb->xceiv.default_a = 0;
|
||||
musb->xceiv.state = OTG_STATE_B_IDLE;
|
||||
musb->xceiv->default_a = 0;
|
||||
musb->xceiv->state = OTG_STATE_B_IDLE;
|
||||
|
||||
status = musb_gadget_setup(musb);
|
||||
if (status)
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
@ -171,7 +172,8 @@ enum musb_h_ep0_state {
|
||||
|
||||
/* peripheral side ep0 states */
|
||||
enum musb_g_ep0_state {
|
||||
MUSB_EP0_STAGE_SETUP, /* idle, waiting for setup */
|
||||
MUSB_EP0_STAGE_IDLE, /* idle, waiting for SETUP */
|
||||
MUSB_EP0_STAGE_SETUP, /* received SETUP */
|
||||
MUSB_EP0_STAGE_TX, /* IN data */
|
||||
MUSB_EP0_STAGE_RX, /* OUT data */
|
||||
MUSB_EP0_STAGE_STATUSIN, /* (after OUT data) */
|
||||
@ -179,10 +181,15 @@ enum musb_g_ep0_state {
|
||||
MUSB_EP0_STAGE_ACKWAIT, /* after zlp, before statusin */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* OTG protocol constants */
|
||||
/*
|
||||
* OTG protocol constants. See USB OTG 1.3 spec,
|
||||
* sections 5.5 "Device Timings" and 6.6.5 "Timers".
|
||||
*/
|
||||
#define OTG_TIME_A_WAIT_VRISE 100 /* msec (max) */
|
||||
#define OTG_TIME_A_WAIT_BCON 0 /* 0=infinite; min 1000 msec */
|
||||
#define OTG_TIME_A_IDLE_BDIS 200 /* msec (min) */
|
||||
#define OTG_TIME_A_WAIT_BCON 1100 /* min 1 second */
|
||||
#define OTG_TIME_A_AIDL_BDIS 200 /* min 200 msec */
|
||||
#define OTG_TIME_B_ASE0_BRST 100 /* min 3.125 ms */
|
||||
|
||||
|
||||
/*************************** REGISTER ACCESS ********************************/
|
||||
|
||||
@ -331,6 +338,8 @@ struct musb {
|
||||
struct list_head control; /* of musb_qh */
|
||||
struct list_head in_bulk; /* of musb_qh */
|
||||
struct list_head out_bulk; /* of musb_qh */
|
||||
|
||||
struct timer_list otg_timer;
|
||||
#endif
|
||||
|
||||
/* called with IRQs blocked; ON/nonzero implies starting a session,
|
||||
@ -355,7 +364,7 @@ struct musb {
|
||||
u16 int_rx;
|
||||
u16 int_tx;
|
||||
|
||||
struct otg_transceiver xceiv;
|
||||
struct otg_transceiver *xceiv;
|
||||
|
||||
int nIrq;
|
||||
unsigned irq_wake:1;
|
||||
@ -386,6 +395,9 @@ struct musb {
|
||||
unsigned is_multipoint:1;
|
||||
unsigned ignore_disconnect:1; /* during bus resets */
|
||||
|
||||
unsigned hb_iso_rx:1; /* high bandwidth iso rx? */
|
||||
unsigned hb_iso_tx:1; /* high bandwidth iso tx? */
|
||||
|
||||
#ifdef C_MP_TX
|
||||
unsigned bulk_split:1;
|
||||
#define can_bulk_split(musb,type) \
|
||||
|
@ -310,7 +310,7 @@ static void txstate(struct musb *musb, struct musb_request *req)
|
||||
/* setup DMA, then program endpoint CSR */
|
||||
request_size = min(request->length,
|
||||
musb_ep->dma->max_len);
|
||||
if (request_size <= musb_ep->packet_sz)
|
||||
if (request_size < musb_ep->packet_sz)
|
||||
musb_ep->dma->desired_mode = 0;
|
||||
else
|
||||
musb_ep->dma->desired_mode = 1;
|
||||
@ -349,7 +349,8 @@ static void txstate(struct musb *musb, struct musb_request *req)
|
||||
#elif defined(CONFIG_USB_TI_CPPI_DMA)
|
||||
/* program endpoint CSR first, then setup DMA */
|
||||
csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY);
|
||||
csr |= MUSB_TXCSR_MODE | MUSB_TXCSR_DMAENAB;
|
||||
csr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_DMAMODE |
|
||||
MUSB_TXCSR_MODE;
|
||||
musb_writew(epio, MUSB_TXCSR,
|
||||
(MUSB_TXCSR_P_WZC_BITS & ~MUSB_TXCSR_P_UNDERRUN)
|
||||
| csr);
|
||||
@ -1405,7 +1406,7 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget)
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
|
||||
switch (musb->xceiv.state) {
|
||||
switch (musb->xceiv->state) {
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
/* NOTE: OTG state machine doesn't include B_SUSPENDED;
|
||||
* that's part of the standard usb 1.1 state machine, and
|
||||
@ -1507,9 +1508,9 @@ static int musb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA)
|
||||
{
|
||||
struct musb *musb = gadget_to_musb(gadget);
|
||||
|
||||
if (!musb->xceiv.set_power)
|
||||
if (!musb->xceiv->set_power)
|
||||
return -EOPNOTSUPP;
|
||||
return otg_set_power(&musb->xceiv, mA);
|
||||
return otg_set_power(musb->xceiv, mA);
|
||||
}
|
||||
|
||||
static int musb_gadget_pullup(struct usb_gadget *gadget, int is_on)
|
||||
@ -1732,11 +1733,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
|
||||
/* REVISIT always use otg_set_peripheral(), handling
|
||||
* issues including the root hub one below ...
|
||||
*/
|
||||
musb->xceiv.gadget = &musb->g;
|
||||
musb->xceiv.state = OTG_STATE_B_IDLE;
|
||||
otg_set_peripheral(musb->xceiv, &musb->g);
|
||||
musb->is_active = 1;
|
||||
|
||||
/* FIXME this ignores the softconnect flag. Drivers are
|
||||
@ -1748,6 +1745,8 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
|
||||
if (!is_otg_enabled(musb))
|
||||
musb_start(musb);
|
||||
|
||||
otg_set_peripheral(musb->xceiv, &musb->g);
|
||||
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
|
||||
if (is_otg_enabled(musb)) {
|
||||
@ -1761,8 +1760,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
|
||||
if (retval < 0) {
|
||||
DBG(1, "add_hcd failed, %d\n", retval);
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
musb->xceiv.gadget = NULL;
|
||||
musb->xceiv.state = OTG_STATE_UNDEFINED;
|
||||
otg_set_peripheral(musb->xceiv, NULL);
|
||||
musb->gadget_driver = NULL;
|
||||
musb->g.dev.driver = NULL;
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
@ -1845,8 +1843,9 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
|
||||
|
||||
(void) musb_gadget_vbus_draw(&musb->g, 0);
|
||||
|
||||
musb->xceiv.state = OTG_STATE_UNDEFINED;
|
||||
musb->xceiv->state = OTG_STATE_UNDEFINED;
|
||||
stop_activity(musb, driver);
|
||||
otg_set_peripheral(musb->xceiv, NULL);
|
||||
|
||||
DBG(3, "unregistering driver %s\n", driver->function);
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
@ -1882,7 +1881,7 @@ EXPORT_SYMBOL(usb_gadget_unregister_driver);
|
||||
void musb_g_resume(struct musb *musb)
|
||||
{
|
||||
musb->is_suspended = 0;
|
||||
switch (musb->xceiv.state) {
|
||||
switch (musb->xceiv->state) {
|
||||
case OTG_STATE_B_IDLE:
|
||||
break;
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
@ -1908,10 +1907,10 @@ void musb_g_suspend(struct musb *musb)
|
||||
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
|
||||
DBG(3, "devctl %02x\n", devctl);
|
||||
|
||||
switch (musb->xceiv.state) {
|
||||
switch (musb->xceiv->state) {
|
||||
case OTG_STATE_B_IDLE:
|
||||
if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS)
|
||||
musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
|
||||
musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
|
||||
break;
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
musb->is_suspended = 1;
|
||||
@ -1957,22 +1956,24 @@ void musb_g_disconnect(struct musb *musb)
|
||||
spin_lock(&musb->lock);
|
||||
}
|
||||
|
||||
switch (musb->xceiv.state) {
|
||||
switch (musb->xceiv->state) {
|
||||
default:
|
||||
#ifdef CONFIG_USB_MUSB_OTG
|
||||
DBG(2, "Unhandled disconnect %s, setting a_idle\n",
|
||||
otg_state_string(musb));
|
||||
musb->xceiv.state = OTG_STATE_A_IDLE;
|
||||
musb->xceiv->state = OTG_STATE_A_IDLE;
|
||||
MUSB_HST_MODE(musb);
|
||||
break;
|
||||
case OTG_STATE_A_PERIPHERAL:
|
||||
musb->xceiv.state = OTG_STATE_A_WAIT_VFALL;
|
||||
musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
|
||||
MUSB_HST_MODE(musb);
|
||||
break;
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
case OTG_STATE_B_HOST:
|
||||
#endif
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
case OTG_STATE_B_IDLE:
|
||||
musb->xceiv.state = OTG_STATE_B_IDLE;
|
||||
musb->xceiv->state = OTG_STATE_B_IDLE;
|
||||
break;
|
||||
case OTG_STATE_B_SRP_INIT:
|
||||
break;
|
||||
@ -2028,10 +2029,10 @@ __acquires(musb->lock)
|
||||
* or else after HNP, as A-Device
|
||||
*/
|
||||
if (devctl & MUSB_DEVCTL_BDEVICE) {
|
||||
musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
|
||||
musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
|
||||
musb->g.is_a_peripheral = 0;
|
||||
} else if (is_otg_enabled(musb)) {
|
||||
musb->xceiv.state = OTG_STATE_A_PERIPHERAL;
|
||||
musb->xceiv->state = OTG_STATE_A_PERIPHERAL;
|
||||
musb->g.is_a_peripheral = 1;
|
||||
} else
|
||||
WARN_ON(1);
|
||||
|
@ -4,6 +4,7 @@
|
||||
* Copyright 2005 Mentor Graphics Corporation
|
||||
* Copyright (C) 2005-2006 by Texas Instruments
|
||||
* Copyright (C) 2006-2007 Nokia Corporation
|
||||
* Copyright (C) 2008-2009 MontaVista Software, Inc. <source@mvista.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -58,7 +59,8 @@
|
||||
static char *decode_ep0stage(u8 stage)
|
||||
{
|
||||
switch (stage) {
|
||||
case MUSB_EP0_STAGE_SETUP: return "idle";
|
||||
case MUSB_EP0_STAGE_IDLE: return "idle";
|
||||
case MUSB_EP0_STAGE_SETUP: return "setup";
|
||||
case MUSB_EP0_STAGE_TX: return "in";
|
||||
case MUSB_EP0_STAGE_RX: return "out";
|
||||
case MUSB_EP0_STAGE_ACKWAIT: return "wait";
|
||||
@ -628,7 +630,7 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
|
||||
musb_writew(regs, MUSB_CSR0,
|
||||
csr & ~MUSB_CSR0_P_SENTSTALL);
|
||||
retval = IRQ_HANDLED;
|
||||
musb->ep0_state = MUSB_EP0_STAGE_SETUP;
|
||||
musb->ep0_state = MUSB_EP0_STAGE_IDLE;
|
||||
csr = musb_readw(regs, MUSB_CSR0);
|
||||
}
|
||||
|
||||
@ -636,7 +638,18 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
|
||||
if (csr & MUSB_CSR0_P_SETUPEND) {
|
||||
musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SVDSETUPEND);
|
||||
retval = IRQ_HANDLED;
|
||||
musb->ep0_state = MUSB_EP0_STAGE_SETUP;
|
||||
/* Transition into the early status phase */
|
||||
switch (musb->ep0_state) {
|
||||
case MUSB_EP0_STAGE_TX:
|
||||
musb->ep0_state = MUSB_EP0_STAGE_STATUSOUT;
|
||||
break;
|
||||
case MUSB_EP0_STAGE_RX:
|
||||
musb->ep0_state = MUSB_EP0_STAGE_STATUSIN;
|
||||
break;
|
||||
default:
|
||||
ERR("SetupEnd came in a wrong ep0stage %s",
|
||||
decode_ep0stage(musb->ep0_state));
|
||||
}
|
||||
csr = musb_readw(regs, MUSB_CSR0);
|
||||
/* NOTE: request may need completion */
|
||||
}
|
||||
@ -697,11 +710,31 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
|
||||
if (req)
|
||||
musb_g_ep0_giveback(musb, req);
|
||||
}
|
||||
|
||||
/*
|
||||
* In case when several interrupts can get coalesced,
|
||||
* check to see if we've already received a SETUP packet...
|
||||
*/
|
||||
if (csr & MUSB_CSR0_RXPKTRDY)
|
||||
goto setup;
|
||||
|
||||
retval = IRQ_HANDLED;
|
||||
musb->ep0_state = MUSB_EP0_STAGE_IDLE;
|
||||
break;
|
||||
|
||||
case MUSB_EP0_STAGE_IDLE:
|
||||
/*
|
||||
* This state is typically (but not always) indiscernible
|
||||
* from the status states since the corresponding interrupts
|
||||
* tend to happen within too little period of time (with only
|
||||
* a zero-length packet in between) and so get coalesced...
|
||||
*/
|
||||
retval = IRQ_HANDLED;
|
||||
musb->ep0_state = MUSB_EP0_STAGE_SETUP;
|
||||
/* FALLTHROUGH */
|
||||
|
||||
case MUSB_EP0_STAGE_SETUP:
|
||||
setup:
|
||||
if (csr & MUSB_CSR0_RXPKTRDY) {
|
||||
struct usb_ctrlrequest setup;
|
||||
int handled = 0;
|
||||
@ -783,7 +816,7 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
|
||||
stall:
|
||||
DBG(3, "stall (%d)\n", handled);
|
||||
musb->ackpend |= MUSB_CSR0_P_SENDSTALL;
|
||||
musb->ep0_state = MUSB_EP0_STAGE_SETUP;
|
||||
musb->ep0_state = MUSB_EP0_STAGE_IDLE;
|
||||
finish:
|
||||
musb_writew(regs, MUSB_CSR0,
|
||||
musb->ackpend);
|
||||
@ -803,7 +836,7 @@ finish:
|
||||
/* "can't happen" */
|
||||
WARN_ON(1);
|
||||
musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SENDSTALL);
|
||||
musb->ep0_state = MUSB_EP0_STAGE_SETUP;
|
||||
musb->ep0_state = MUSB_EP0_STAGE_IDLE;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -959,7 +992,7 @@ static int musb_g_ep0_halt(struct usb_ep *e, int value)
|
||||
|
||||
csr |= MUSB_CSR0_P_SENDSTALL;
|
||||
musb_writew(regs, MUSB_CSR0, csr);
|
||||
musb->ep0_state = MUSB_EP0_STAGE_SETUP;
|
||||
musb->ep0_state = MUSB_EP0_STAGE_IDLE;
|
||||
musb->ackpend = 0;
|
||||
break;
|
||||
default:
|
||||
|
@ -181,6 +181,19 @@ static inline void musb_h_tx_dma_start(struct musb_hw_ep *ep)
|
||||
musb_writew(ep->regs, MUSB_TXCSR, txcsr);
|
||||
}
|
||||
|
||||
static void musb_ep_set_qh(struct musb_hw_ep *ep, int is_in, struct musb_qh *qh)
|
||||
{
|
||||
if (is_in != 0 || ep->is_shared_fifo)
|
||||
ep->in_qh = qh;
|
||||
if (is_in == 0 || ep->is_shared_fifo)
|
||||
ep->out_qh = qh;
|
||||
}
|
||||
|
||||
static struct musb_qh *musb_ep_get_qh(struct musb_hw_ep *ep, int is_in)
|
||||
{
|
||||
return is_in ? ep->in_qh : ep->out_qh;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start the URB at the front of an endpoint's queue
|
||||
* end must be claimed from the caller.
|
||||
@ -210,7 +223,6 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
/* control transfers always start with SETUP */
|
||||
is_in = 0;
|
||||
hw_ep->out_qh = qh;
|
||||
musb->ep0_stage = MUSB_EP0_START;
|
||||
buf = urb->setup_packet;
|
||||
len = 8;
|
||||
@ -239,10 +251,7 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
|
||||
epnum, buf + offset, len);
|
||||
|
||||
/* Configure endpoint */
|
||||
if (is_in || hw_ep->is_shared_fifo)
|
||||
hw_ep->in_qh = qh;
|
||||
else
|
||||
hw_ep->out_qh = qh;
|
||||
musb_ep_set_qh(hw_ep, is_in, qh);
|
||||
musb_ep_program(musb, epnum, urb, !is_in, buf, offset, len);
|
||||
|
||||
/* transmit may have more work: start it when it is time */
|
||||
@ -286,9 +295,8 @@ start:
|
||||
}
|
||||
}
|
||||
|
||||
/* caller owns controller lock, irqs are blocked */
|
||||
static void
|
||||
__musb_giveback(struct musb *musb, struct urb *urb, int status)
|
||||
/* Context: caller owns controller lock, IRQs are blocked */
|
||||
static void musb_giveback(struct musb *musb, struct urb *urb, int status)
|
||||
__releases(musb->lock)
|
||||
__acquires(musb->lock)
|
||||
{
|
||||
@ -321,60 +329,57 @@ __acquires(musb->lock)
|
||||
spin_lock(&musb->lock);
|
||||
}
|
||||
|
||||
/* for bulk/interrupt endpoints only */
|
||||
static inline void
|
||||
musb_save_toggle(struct musb_hw_ep *ep, int is_in, struct urb *urb)
|
||||
/* For bulk/interrupt endpoints only */
|
||||
static inline void musb_save_toggle(struct musb_qh *qh, int is_in,
|
||||
struct urb *urb)
|
||||
{
|
||||
struct usb_device *udev = urb->dev;
|
||||
void __iomem *epio = qh->hw_ep->regs;
|
||||
u16 csr;
|
||||
void __iomem *epio = ep->regs;
|
||||
struct musb_qh *qh;
|
||||
|
||||
/* FIXME: the current Mentor DMA code seems to have
|
||||
/*
|
||||
* FIXME: the current Mentor DMA code seems to have
|
||||
* problems getting toggle correct.
|
||||
*/
|
||||
|
||||
if (is_in || ep->is_shared_fifo)
|
||||
qh = ep->in_qh;
|
||||
if (is_in)
|
||||
csr = musb_readw(epio, MUSB_RXCSR) & MUSB_RXCSR_H_DATATOGGLE;
|
||||
else
|
||||
qh = ep->out_qh;
|
||||
csr = musb_readw(epio, MUSB_TXCSR) & MUSB_TXCSR_H_DATATOGGLE;
|
||||
|
||||
if (!is_in) {
|
||||
csr = musb_readw(epio, MUSB_TXCSR);
|
||||
usb_settoggle(udev, qh->epnum, 1,
|
||||
(csr & MUSB_TXCSR_H_DATATOGGLE)
|
||||
? 1 : 0);
|
||||
} else {
|
||||
csr = musb_readw(epio, MUSB_RXCSR);
|
||||
usb_settoggle(udev, qh->epnum, 0,
|
||||
(csr & MUSB_RXCSR_H_DATATOGGLE)
|
||||
? 1 : 0);
|
||||
}
|
||||
usb_settoggle(urb->dev, qh->epnum, !is_in, csr ? 1 : 0);
|
||||
}
|
||||
|
||||
/* caller owns controller lock, irqs are blocked */
|
||||
static struct musb_qh *
|
||||
musb_giveback(struct musb_qh *qh, struct urb *urb, int status)
|
||||
/*
|
||||
* Advance this hardware endpoint's queue, completing the specified URB and
|
||||
* advancing to either the next URB queued to that qh, or else invalidating
|
||||
* that qh and advancing to the next qh scheduled after the current one.
|
||||
*
|
||||
* Context: caller owns controller lock, IRQs are blocked
|
||||
*/
|
||||
static void musb_advance_schedule(struct musb *musb, struct urb *urb,
|
||||
struct musb_hw_ep *hw_ep, int is_in)
|
||||
{
|
||||
struct musb_qh *qh = musb_ep_get_qh(hw_ep, is_in);
|
||||
struct musb_hw_ep *ep = qh->hw_ep;
|
||||
struct musb *musb = ep->musb;
|
||||
int is_in = usb_pipein(urb->pipe);
|
||||
int ready = qh->is_ready;
|
||||
int status;
|
||||
|
||||
status = (urb->status == -EINPROGRESS) ? 0 : urb->status;
|
||||
|
||||
/* save toggle eagerly, for paranoia */
|
||||
switch (qh->type) {
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
musb_save_toggle(ep, is_in, urb);
|
||||
musb_save_toggle(qh, is_in, urb);
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
if (status == 0 && urb->error_count)
|
||||
if (urb->error_count)
|
||||
status = -EXDEV;
|
||||
break;
|
||||
}
|
||||
|
||||
qh->is_ready = 0;
|
||||
__musb_giveback(musb, urb, status);
|
||||
musb_giveback(musb, urb, status);
|
||||
qh->is_ready = ready;
|
||||
|
||||
/* reclaim resources (and bandwidth) ASAP; deschedule it, and
|
||||
@ -388,11 +393,8 @@ musb_giveback(struct musb_qh *qh, struct urb *urb, int status)
|
||||
else
|
||||
ep->tx_reinit = 1;
|
||||
|
||||
/* clobber old pointers to this qh */
|
||||
if (is_in || ep->is_shared_fifo)
|
||||
ep->in_qh = NULL;
|
||||
else
|
||||
ep->out_qh = NULL;
|
||||
/* Clobber old pointers to this qh */
|
||||
musb_ep_set_qh(ep, is_in, NULL);
|
||||
qh->hep->hcpriv = NULL;
|
||||
|
||||
switch (qh->type) {
|
||||
@ -421,36 +423,10 @@ musb_giveback(struct musb_qh *qh, struct urb *urb, int status)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return qh;
|
||||
}
|
||||
|
||||
/*
|
||||
* Advance this hardware endpoint's queue, completing the specified urb and
|
||||
* advancing to either the next urb queued to that qh, or else invalidating
|
||||
* that qh and advancing to the next qh scheduled after the current one.
|
||||
*
|
||||
* Context: caller owns controller lock, irqs are blocked
|
||||
*/
|
||||
static void
|
||||
musb_advance_schedule(struct musb *musb, struct urb *urb,
|
||||
struct musb_hw_ep *hw_ep, int is_in)
|
||||
{
|
||||
struct musb_qh *qh;
|
||||
|
||||
if (is_in || hw_ep->is_shared_fifo)
|
||||
qh = hw_ep->in_qh;
|
||||
else
|
||||
qh = hw_ep->out_qh;
|
||||
|
||||
if (urb->status == -EINPROGRESS)
|
||||
qh = musb_giveback(qh, urb, 0);
|
||||
else
|
||||
qh = musb_giveback(qh, urb, urb->status);
|
||||
|
||||
if (qh != NULL && qh->is_ready) {
|
||||
DBG(4, "... next ep%d %cX urb %p\n",
|
||||
hw_ep->epnum, is_in ? 'R' : 'T',
|
||||
next_urb(qh));
|
||||
hw_ep->epnum, is_in ? 'R' : 'T', next_urb(qh));
|
||||
musb_start_urb(musb, is_in, qh);
|
||||
}
|
||||
}
|
||||
@ -629,7 +605,8 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep)
|
||||
musb_writeb(ep->regs, MUSB_RXTYPE, qh->type_reg);
|
||||
musb_writeb(ep->regs, MUSB_RXINTERVAL, qh->intv_reg);
|
||||
/* NOTE: bulk combining rewrites high bits of maxpacket */
|
||||
musb_writew(ep->regs, MUSB_RXMAXP, qh->maxpacket);
|
||||
musb_writew(ep->regs, MUSB_RXMAXP,
|
||||
qh->maxpacket | ((qh->hb_mult - 1) << 11));
|
||||
|
||||
ep->rx_reinit = 0;
|
||||
}
|
||||
@ -651,9 +628,10 @@ static bool musb_tx_dma_program(struct dma_controller *dma,
|
||||
csr = musb_readw(epio, MUSB_TXCSR);
|
||||
if (length > pkt_size) {
|
||||
mode = 1;
|
||||
csr |= MUSB_TXCSR_AUTOSET
|
||||
| MUSB_TXCSR_DMAMODE
|
||||
| MUSB_TXCSR_DMAENAB;
|
||||
csr |= MUSB_TXCSR_DMAMODE | MUSB_TXCSR_DMAENAB;
|
||||
/* autoset shouldn't be set in high bandwidth */
|
||||
if (qh->hb_mult == 1)
|
||||
csr |= MUSB_TXCSR_AUTOSET;
|
||||
} else {
|
||||
mode = 0;
|
||||
csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAMODE);
|
||||
@ -703,15 +681,8 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
|
||||
void __iomem *mbase = musb->mregs;
|
||||
struct musb_hw_ep *hw_ep = musb->endpoints + epnum;
|
||||
void __iomem *epio = hw_ep->regs;
|
||||
struct musb_qh *qh;
|
||||
u16 packet_sz;
|
||||
|
||||
if (!is_out || hw_ep->is_shared_fifo)
|
||||
qh = hw_ep->in_qh;
|
||||
else
|
||||
qh = hw_ep->out_qh;
|
||||
|
||||
packet_sz = qh->maxpacket;
|
||||
struct musb_qh *qh = musb_ep_get_qh(hw_ep, !is_out);
|
||||
u16 packet_sz = qh->maxpacket;
|
||||
|
||||
DBG(3, "%s hw%d urb %p spd%d dev%d ep%d%s "
|
||||
"h_addr%02x h_port%02x bytes %d\n",
|
||||
@ -1129,17 +1100,14 @@ void musb_host_tx(struct musb *musb, u8 epnum)
|
||||
u16 tx_csr;
|
||||
size_t length = 0;
|
||||
size_t offset = 0;
|
||||
struct urb *urb;
|
||||
struct musb_hw_ep *hw_ep = musb->endpoints + epnum;
|
||||
void __iomem *epio = hw_ep->regs;
|
||||
struct musb_qh *qh = hw_ep->is_shared_fifo ? hw_ep->in_qh
|
||||
: hw_ep->out_qh;
|
||||
struct musb_qh *qh = hw_ep->out_qh;
|
||||
struct urb *urb = next_urb(qh);
|
||||
u32 status = 0;
|
||||
void __iomem *mbase = musb->mregs;
|
||||
struct dma_channel *dma;
|
||||
|
||||
urb = next_urb(qh);
|
||||
|
||||
musb_ep_select(mbase, epnum);
|
||||
tx_csr = musb_readw(epio, MUSB_TXCSR);
|
||||
|
||||
@ -1427,7 +1395,7 @@ static void musb_bulk_rx_nak_timeout(struct musb *musb, struct musb_hw_ep *ep)
|
||||
urb->actual_length += dma->actual_len;
|
||||
dma->actual_len = 0L;
|
||||
}
|
||||
musb_save_toggle(ep, 1, urb);
|
||||
musb_save_toggle(cur_qh, 1, urb);
|
||||
|
||||
/* move cur_qh to end of queue */
|
||||
list_move_tail(&cur_qh->ring, &musb->in_bulk);
|
||||
@ -1531,6 +1499,10 @@ void musb_host_rx(struct musb *musb, u8 epnum)
|
||||
/* packet error reported later */
|
||||
iso_err = true;
|
||||
}
|
||||
} else if (rx_csr & MUSB_RXCSR_INCOMPRX) {
|
||||
DBG(3, "end %d high bandwidth incomplete ISO packet RX\n",
|
||||
epnum);
|
||||
status = -EPROTO;
|
||||
}
|
||||
|
||||
/* faults abort the transfer */
|
||||
@ -1738,7 +1710,11 @@ void musb_host_rx(struct musb *musb, u8 epnum)
|
||||
val &= ~MUSB_RXCSR_H_AUTOREQ;
|
||||
else
|
||||
val |= MUSB_RXCSR_H_AUTOREQ;
|
||||
val |= MUSB_RXCSR_AUTOCLEAR | MUSB_RXCSR_DMAENAB;
|
||||
val |= MUSB_RXCSR_DMAENAB;
|
||||
|
||||
/* autoclear shouldn't be set in high bandwidth */
|
||||
if (qh->hb_mult == 1)
|
||||
val |= MUSB_RXCSR_AUTOCLEAR;
|
||||
|
||||
musb_writew(epio, MUSB_RXCSR,
|
||||
MUSB_RXCSR_H_WZC_BITS | val);
|
||||
@ -1817,19 +1793,17 @@ static int musb_schedule(
|
||||
epnum++, hw_ep++) {
|
||||
int diff;
|
||||
|
||||
if (is_in || hw_ep->is_shared_fifo) {
|
||||
if (hw_ep->in_qh != NULL)
|
||||
continue;
|
||||
} else if (hw_ep->out_qh != NULL)
|
||||
if (musb_ep_get_qh(hw_ep, is_in) != NULL)
|
||||
continue;
|
||||
|
||||
if (hw_ep == musb->bulk_ep)
|
||||
continue;
|
||||
|
||||
if (is_in)
|
||||
diff = hw_ep->max_packet_sz_rx - qh->maxpacket;
|
||||
diff = hw_ep->max_packet_sz_rx;
|
||||
else
|
||||
diff = hw_ep->max_packet_sz_tx - qh->maxpacket;
|
||||
diff = hw_ep->max_packet_sz_tx;
|
||||
diff -= (qh->maxpacket * qh->hb_mult);
|
||||
|
||||
if (diff >= 0 && best_diff > diff) {
|
||||
best_diff = diff;
|
||||
@ -1932,15 +1906,27 @@ static int musb_urb_enqueue(
|
||||
qh->is_ready = 1;
|
||||
|
||||
qh->maxpacket = le16_to_cpu(epd->wMaxPacketSize);
|
||||
qh->type = usb_endpoint_type(epd);
|
||||
|
||||
/* no high bandwidth support yet */
|
||||
if (qh->maxpacket & ~0x7ff) {
|
||||
ret = -EMSGSIZE;
|
||||
goto done;
|
||||
/* Bits 11 & 12 of wMaxPacketSize encode high bandwidth multiplier.
|
||||
* Some musb cores don't support high bandwidth ISO transfers; and
|
||||
* we don't (yet!) support high bandwidth interrupt transfers.
|
||||
*/
|
||||
qh->hb_mult = 1 + ((qh->maxpacket >> 11) & 0x03);
|
||||
if (qh->hb_mult > 1) {
|
||||
int ok = (qh->type == USB_ENDPOINT_XFER_ISOC);
|
||||
|
||||
if (ok)
|
||||
ok = (usb_pipein(urb->pipe) && musb->hb_iso_rx)
|
||||
|| (usb_pipeout(urb->pipe) && musb->hb_iso_tx);
|
||||
if (!ok) {
|
||||
ret = -EMSGSIZE;
|
||||
goto done;
|
||||
}
|
||||
qh->maxpacket &= 0x7ff;
|
||||
}
|
||||
|
||||
qh->epnum = usb_endpoint_num(epd);
|
||||
qh->type = usb_endpoint_type(epd);
|
||||
|
||||
/* NOTE: urb->dev->devnum is wrong during SET_ADDRESS */
|
||||
qh->addr_reg = (u8) usb_pipedevice(urb->pipe);
|
||||
@ -2052,14 +2038,15 @@ done:
|
||||
* called with controller locked, irqs blocked
|
||||
* that hardware queue advances to the next transfer, unless prevented
|
||||
*/
|
||||
static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh, int is_in)
|
||||
static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh)
|
||||
{
|
||||
struct musb_hw_ep *ep = qh->hw_ep;
|
||||
void __iomem *epio = ep->regs;
|
||||
unsigned hw_end = ep->epnum;
|
||||
void __iomem *regs = ep->musb->mregs;
|
||||
u16 csr;
|
||||
int is_in = usb_pipein(urb->pipe);
|
||||
int status = 0;
|
||||
u16 csr;
|
||||
|
||||
musb_ep_select(regs, hw_end);
|
||||
|
||||
@ -2112,14 +2099,14 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||
{
|
||||
struct musb *musb = hcd_to_musb(hcd);
|
||||
struct musb_qh *qh;
|
||||
struct list_head *sched;
|
||||
unsigned long flags;
|
||||
int is_in = usb_pipein(urb->pipe);
|
||||
int ret;
|
||||
|
||||
DBG(4, "urb=%p, dev%d ep%d%s\n", urb,
|
||||
usb_pipedevice(urb->pipe),
|
||||
usb_pipeendpoint(urb->pipe),
|
||||
usb_pipein(urb->pipe) ? "in" : "out");
|
||||
is_in ? "in" : "out");
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
ret = usb_hcd_check_unlink_urb(hcd, urb, status);
|
||||
@ -2130,47 +2117,25 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||
if (!qh)
|
||||
goto done;
|
||||
|
||||
/* Any URB not actively programmed into endpoint hardware can be
|
||||
/*
|
||||
* Any URB not actively programmed into endpoint hardware can be
|
||||
* immediately given back; that's any URB not at the head of an
|
||||
* endpoint queue, unless someday we get real DMA queues. And even
|
||||
* if it's at the head, it might not be known to the hardware...
|
||||
*
|
||||
* Otherwise abort current transfer, pending dma, etc.; urb->status
|
||||
* Otherwise abort current transfer, pending DMA, etc.; urb->status
|
||||
* has already been updated. This is a synchronous abort; it'd be
|
||||
* OK to hold off until after some IRQ, though.
|
||||
*
|
||||
* NOTE: qh is invalid unless !list_empty(&hep->urb_list)
|
||||
*/
|
||||
if (!qh->is_ready || urb->urb_list.prev != &qh->hep->urb_list)
|
||||
ret = -EINPROGRESS;
|
||||
else {
|
||||
switch (qh->type) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
sched = &musb->control;
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
if (qh->mux == 1) {
|
||||
if (usb_pipein(urb->pipe))
|
||||
sched = &musb->in_bulk;
|
||||
else
|
||||
sched = &musb->out_bulk;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* REVISIT when we get a schedule tree, periodic
|
||||
* transfers won't always be at the head of a
|
||||
* singleton queue...
|
||||
*/
|
||||
sched = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE: qh is invalid unless !list_empty(&hep->urb_list) */
|
||||
if (ret < 0 || (sched && qh != first_qh(sched))) {
|
||||
if (!qh->is_ready
|
||||
|| urb->urb_list.prev != &qh->hep->urb_list
|
||||
|| musb_ep_get_qh(qh->hw_ep, is_in) != qh) {
|
||||
int ready = qh->is_ready;
|
||||
|
||||
ret = 0;
|
||||
qh->is_ready = 0;
|
||||
__musb_giveback(musb, urb, 0);
|
||||
musb_giveback(musb, urb, 0);
|
||||
qh->is_ready = ready;
|
||||
|
||||
/* If nothing else (usually musb_giveback) is using it
|
||||
@ -2182,7 +2147,7 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||
kfree(qh);
|
||||
}
|
||||
} else
|
||||
ret = musb_cleanup_urb(urb, qh, urb->pipe & USB_DIR_IN);
|
||||
ret = musb_cleanup_urb(urb, qh);
|
||||
done:
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
return ret;
|
||||
@ -2192,13 +2157,11 @@ done:
|
||||
static void
|
||||
musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
|
||||
{
|
||||
u8 epnum = hep->desc.bEndpointAddress;
|
||||
u8 is_in = hep->desc.bEndpointAddress & USB_DIR_IN;
|
||||
unsigned long flags;
|
||||
struct musb *musb = hcd_to_musb(hcd);
|
||||
u8 is_in = epnum & USB_DIR_IN;
|
||||
struct musb_qh *qh;
|
||||
struct urb *urb;
|
||||
struct list_head *sched;
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
|
||||
@ -2206,31 +2169,11 @@ musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
|
||||
if (qh == NULL)
|
||||
goto exit;
|
||||
|
||||
switch (qh->type) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
sched = &musb->control;
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
if (qh->mux == 1) {
|
||||
if (is_in)
|
||||
sched = &musb->in_bulk;
|
||||
else
|
||||
sched = &musb->out_bulk;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* REVISIT when we get a schedule tree, periodic transfers
|
||||
* won't always be at the head of a singleton queue...
|
||||
*/
|
||||
sched = NULL;
|
||||
break;
|
||||
}
|
||||
/* NOTE: qh is invalid unless !list_empty(&hep->urb_list) */
|
||||
|
||||
/* NOTE: qh is invalid unless !list_empty(&hep->urb_list) */
|
||||
|
||||
/* kick first urb off the hardware, if needed */
|
||||
/* Kick the first URB off the hardware, if needed */
|
||||
qh->is_ready = 0;
|
||||
if (!sched || qh == first_qh(sched)) {
|
||||
if (musb_ep_get_qh(qh->hw_ep, is_in) == qh) {
|
||||
urb = next_urb(qh);
|
||||
|
||||
/* make software (then hardware) stop ASAP */
|
||||
@ -2238,7 +2181,7 @@ musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
|
||||
urb->status = -ESHUTDOWN;
|
||||
|
||||
/* cleanup */
|
||||
musb_cleanup_urb(urb, qh, urb->pipe & USB_DIR_IN);
|
||||
musb_cleanup_urb(urb, qh);
|
||||
|
||||
/* Then nuke all the others ... and advance the
|
||||
* queue on hw_ep (e.g. bulk ring) when we're done.
|
||||
@ -2254,7 +2197,7 @@ musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
|
||||
* will activate any of these as it advances.
|
||||
*/
|
||||
while (!list_empty(&hep->urb_list))
|
||||
__musb_giveback(musb, next_urb(qh), -ESHUTDOWN);
|
||||
musb_giveback(musb, next_urb(qh), -ESHUTDOWN);
|
||||
|
||||
hep->hcpriv = NULL;
|
||||
list_del(&qh->ring);
|
||||
@ -2293,7 +2236,7 @@ static int musb_bus_suspend(struct usb_hcd *hcd)
|
||||
{
|
||||
struct musb *musb = hcd_to_musb(hcd);
|
||||
|
||||
if (musb->xceiv.state == OTG_STATE_A_SUSPEND)
|
||||
if (musb->xceiv->state == OTG_STATE_A_SUSPEND)
|
||||
return 0;
|
||||
|
||||
if (is_host_active(musb) && musb->is_active) {
|
||||
|
@ -67,6 +67,7 @@ struct musb_qh {
|
||||
u8 is_ready; /* safe to modify hw_ep */
|
||||
u8 type; /* XFERTYPE_* */
|
||||
u8 epnum;
|
||||
u8 hb_mult; /* high bandwidth pkts per uf */
|
||||
u16 maxpacket;
|
||||
u16 frame; /* for periodic schedule */
|
||||
unsigned iso_idx; /* in urb->iso_frame_desc[] */
|
||||
|
@ -78,18 +78,22 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend)
|
||||
DBG(3, "Root port suspended, power %02x\n", power);
|
||||
|
||||
musb->port1_status |= USB_PORT_STAT_SUSPEND;
|
||||
switch (musb->xceiv.state) {
|
||||
switch (musb->xceiv->state) {
|
||||
case OTG_STATE_A_HOST:
|
||||
musb->xceiv.state = OTG_STATE_A_SUSPEND;
|
||||
musb->xceiv->state = OTG_STATE_A_SUSPEND;
|
||||
musb->is_active = is_otg_enabled(musb)
|
||||
&& musb->xceiv.host->b_hnp_enable;
|
||||
&& musb->xceiv->host->b_hnp_enable;
|
||||
if (musb->is_active)
|
||||
mod_timer(&musb->otg_timer, jiffies
|
||||
+ msecs_to_jiffies(
|
||||
OTG_TIME_A_AIDL_BDIS));
|
||||
musb_platform_try_idle(musb, 0);
|
||||
break;
|
||||
#ifdef CONFIG_USB_MUSB_OTG
|
||||
case OTG_STATE_B_HOST:
|
||||
musb->xceiv.state = OTG_STATE_B_WAIT_ACON;
|
||||
musb->xceiv->state = OTG_STATE_B_WAIT_ACON;
|
||||
musb->is_active = is_otg_enabled(musb)
|
||||
&& musb->xceiv.host->b_hnp_enable;
|
||||
&& musb->xceiv->host->b_hnp_enable;
|
||||
musb_platform_try_idle(musb, 0);
|
||||
break;
|
||||
#endif
|
||||
@ -116,7 +120,7 @@ static void musb_port_reset(struct musb *musb, bool do_reset)
|
||||
void __iomem *mbase = musb->mregs;
|
||||
|
||||
#ifdef CONFIG_USB_MUSB_OTG
|
||||
if (musb->xceiv.state == OTG_STATE_B_IDLE) {
|
||||
if (musb->xceiv->state == OTG_STATE_B_IDLE) {
|
||||
DBG(2, "HNP: Returning from HNP; no hub reset from b_idle\n");
|
||||
musb->port1_status &= ~USB_PORT_STAT_RESET;
|
||||
return;
|
||||
@ -186,14 +190,23 @@ void musb_root_disconnect(struct musb *musb)
|
||||
usb_hcd_poll_rh_status(musb_to_hcd(musb));
|
||||
musb->is_active = 0;
|
||||
|
||||
switch (musb->xceiv.state) {
|
||||
case OTG_STATE_A_HOST:
|
||||
switch (musb->xceiv->state) {
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
|
||||
#ifdef CONFIG_USB_MUSB_OTG
|
||||
if (is_otg_enabled(musb)
|
||||
&& musb->xceiv->host->b_hnp_enable) {
|
||||
musb->xceiv->state = OTG_STATE_A_PERIPHERAL;
|
||||
musb->g.is_a_peripheral = 1;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
/* FALLTHROUGH */
|
||||
case OTG_STATE_A_HOST:
|
||||
musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
|
||||
musb->is_active = 0;
|
||||
break;
|
||||
case OTG_STATE_A_WAIT_VFALL:
|
||||
musb->xceiv.state = OTG_STATE_B_IDLE;
|
||||
musb->xceiv->state = OTG_STATE_B_IDLE;
|
||||
break;
|
||||
default:
|
||||
DBG(1, "host disconnect (%s)\n", otg_state_string(musb));
|
||||
@ -332,7 +345,7 @@ int musb_hub_control(
|
||||
musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
|
||||
usb_hcd_poll_rh_status(musb_to_hcd(musb));
|
||||
/* NOTE: it might really be A_WAIT_BCON ... */
|
||||
musb->xceiv.state = OTG_STATE_A_HOST;
|
||||
musb->xceiv->state = OTG_STATE_A_HOST;
|
||||
}
|
||||
|
||||
put_unaligned(cpu_to_le32(musb->port1_status
|
||||
|
@ -44,7 +44,6 @@
|
||||
#define get_cpu_rev() 2
|
||||
#endif
|
||||
|
||||
#define MUSB_TIMEOUT_A_WAIT_BCON 1100
|
||||
|
||||
static struct timer_list musb_idle_timer;
|
||||
|
||||
@ -61,17 +60,17 @@ static void musb_do_idle(unsigned long _musb)
|
||||
|
||||
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
|
||||
|
||||
switch (musb->xceiv.state) {
|
||||
switch (musb->xceiv->state) {
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
devctl &= ~MUSB_DEVCTL_SESSION;
|
||||
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
|
||||
|
||||
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
|
||||
if (devctl & MUSB_DEVCTL_BDEVICE) {
|
||||
musb->xceiv.state = OTG_STATE_B_IDLE;
|
||||
musb->xceiv->state = OTG_STATE_B_IDLE;
|
||||
MUSB_DEV_MODE(musb);
|
||||
} else {
|
||||
musb->xceiv.state = OTG_STATE_A_IDLE;
|
||||
musb->xceiv->state = OTG_STATE_A_IDLE;
|
||||
MUSB_HST_MODE(musb);
|
||||
}
|
||||
break;
|
||||
@ -89,7 +88,7 @@ static void musb_do_idle(unsigned long _musb)
|
||||
musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
|
||||
usb_hcd_poll_rh_status(musb_to_hcd(musb));
|
||||
/* NOTE: it might really be A_WAIT_BCON ... */
|
||||
musb->xceiv.state = OTG_STATE_A_HOST;
|
||||
musb->xceiv->state = OTG_STATE_A_HOST;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
@ -97,9 +96,9 @@ static void musb_do_idle(unsigned long _musb)
|
||||
case OTG_STATE_A_HOST:
|
||||
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
|
||||
if (devctl & MUSB_DEVCTL_BDEVICE)
|
||||
musb->xceiv.state = OTG_STATE_B_IDLE;
|
||||
musb->xceiv->state = OTG_STATE_B_IDLE;
|
||||
else
|
||||
musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
|
||||
musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
@ -118,7 +117,7 @@ void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
|
||||
|
||||
/* Never idle if active, or when VBUS timeout is not set as host */
|
||||
if (musb->is_active || ((musb->a_wait_bcon == 0)
|
||||
&& (musb->xceiv.state == OTG_STATE_A_WAIT_BCON))) {
|
||||
&& (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) {
|
||||
DBG(4, "%s active, deleting timer\n", otg_state_string(musb));
|
||||
del_timer(&musb_idle_timer);
|
||||
last_timer = jiffies;
|
||||
@ -163,8 +162,8 @@ static void omap_set_vbus(struct musb *musb, int is_on)
|
||||
|
||||
if (is_on) {
|
||||
musb->is_active = 1;
|
||||
musb->xceiv.default_a = 1;
|
||||
musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
|
||||
musb->xceiv->default_a = 1;
|
||||
musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
|
||||
devctl |= MUSB_DEVCTL_SESSION;
|
||||
|
||||
MUSB_HST_MODE(musb);
|
||||
@ -175,8 +174,8 @@ static void omap_set_vbus(struct musb *musb, int is_on)
|
||||
* jumping right to B_IDLE...
|
||||
*/
|
||||
|
||||
musb->xceiv.default_a = 0;
|
||||
musb->xceiv.state = OTG_STATE_B_IDLE;
|
||||
musb->xceiv->default_a = 0;
|
||||
musb->xceiv->state = OTG_STATE_B_IDLE;
|
||||
devctl &= ~MUSB_DEVCTL_SESSION;
|
||||
|
||||
MUSB_DEV_MODE(musb);
|
||||
@ -188,10 +187,6 @@ static void omap_set_vbus(struct musb *musb, int is_on)
|
||||
otg_state_string(musb),
|
||||
musb_readb(musb->mregs, MUSB_DEVCTL));
|
||||
}
|
||||
static int omap_set_power(struct otg_transceiver *x, unsigned mA)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int musb_platform_resume(struct musb *musb);
|
||||
|
||||
@ -202,24 +197,6 @@ int musb_platform_set_mode(struct musb *musb, u8 musb_mode)
|
||||
devctl |= MUSB_DEVCTL_SESSION;
|
||||
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
|
||||
|
||||
switch (musb_mode) {
|
||||
#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
||||
case MUSB_HOST:
|
||||
otg_set_host(&musb->xceiv, musb->xceiv.host);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
|
||||
case MUSB_PERIPHERAL:
|
||||
otg_set_peripheral(&musb->xceiv, musb->xceiv.gadget);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_USB_MUSB_OTG
|
||||
case MUSB_OTG:
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -231,6 +208,16 @@ int __init musb_platform_init(struct musb *musb)
|
||||
omap_cfg_reg(AE5_2430_USB0HS_STP);
|
||||
#endif
|
||||
|
||||
/* We require some kind of external transceiver, hooked
|
||||
* up through ULPI. TWL4030-family PMICs include one,
|
||||
* which needs a driver, drivers aren't always needed.
|
||||
*/
|
||||
musb->xceiv = otg_get_transceiver();
|
||||
if (!musb->xceiv) {
|
||||
pr_err("HS USB OTG: no transceiver configured\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
musb_platform_resume(musb);
|
||||
|
||||
l = omap_readl(OTG_SYSCONFIG);
|
||||
@ -240,7 +227,12 @@ int __init musb_platform_init(struct musb *musb)
|
||||
l &= ~AUTOIDLE; /* disable auto idle */
|
||||
l &= ~NOIDLE; /* remove possible noidle */
|
||||
l |= SMARTIDLE; /* enable smart idle */
|
||||
l |= AUTOIDLE; /* enable auto idle */
|
||||
/*
|
||||
* MUSB AUTOIDLE don't work in 3430.
|
||||
* Workaround by Richard Woodruff/TI
|
||||
*/
|
||||
if (!cpu_is_omap3430())
|
||||
l |= AUTOIDLE; /* enable auto idle */
|
||||
omap_writel(l, OTG_SYSCONFIG);
|
||||
|
||||
l = omap_readl(OTG_INTERFSEL);
|
||||
@ -257,9 +249,6 @@ int __init musb_platform_init(struct musb *musb)
|
||||
|
||||
if (is_host_enabled(musb))
|
||||
musb->board_set_vbus = omap_set_vbus;
|
||||
if (is_peripheral_enabled(musb))
|
||||
musb->xceiv.set_power = omap_set_power;
|
||||
musb->a_wait_bcon = MUSB_TIMEOUT_A_WAIT_BCON;
|
||||
|
||||
setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb);
|
||||
|
||||
@ -282,8 +271,7 @@ int musb_platform_suspend(struct musb *musb)
|
||||
l |= ENABLEWAKEUP; /* enable wakeup */
|
||||
omap_writel(l, OTG_SYSCONFIG);
|
||||
|
||||
if (musb->xceiv.set_suspend)
|
||||
musb->xceiv.set_suspend(&musb->xceiv, 1);
|
||||
otg_set_suspend(musb->xceiv, 1);
|
||||
|
||||
if (musb->set_clock)
|
||||
musb->set_clock(musb->clock, 0);
|
||||
@ -300,8 +288,7 @@ static int musb_platform_resume(struct musb *musb)
|
||||
if (!musb->clock)
|
||||
return 0;
|
||||
|
||||
if (musb->xceiv.set_suspend)
|
||||
musb->xceiv.set_suspend(&musb->xceiv, 0);
|
||||
otg_set_suspend(musb->xceiv, 0);
|
||||
|
||||
if (musb->set_clock)
|
||||
musb->set_clock(musb->clock, 1);
|
||||
|
@ -259,6 +259,8 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *buf)
|
||||
tusb_fifo_read_unaligned(fifo, buf, len);
|
||||
}
|
||||
|
||||
static struct musb *the_musb;
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
|
||||
|
||||
/* This is used by gadget drivers, and OTG transceiver logic, allowing
|
||||
@ -269,7 +271,7 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *buf)
|
||||
*/
|
||||
static int tusb_draw_power(struct otg_transceiver *x, unsigned mA)
|
||||
{
|
||||
struct musb *musb = container_of(x, struct musb, xceiv);
|
||||
struct musb *musb = the_musb;
|
||||
void __iomem *tbase = musb->ctrl_base;
|
||||
u32 reg;
|
||||
|
||||
@ -419,7 +421,7 @@ static void musb_do_idle(unsigned long _musb)
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
|
||||
switch (musb->xceiv.state) {
|
||||
switch (musb->xceiv->state) {
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
if ((musb->a_wait_bcon != 0)
|
||||
&& (musb->idle_timeout == 0
|
||||
@ -483,7 +485,7 @@ void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
|
||||
|
||||
/* Never idle if active, or when VBUS timeout is not set as host */
|
||||
if (musb->is_active || ((musb->a_wait_bcon == 0)
|
||||
&& (musb->xceiv.state == OTG_STATE_A_WAIT_BCON))) {
|
||||
&& (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) {
|
||||
DBG(4, "%s active, deleting timer\n", otg_state_string(musb));
|
||||
del_timer(&musb_idle_timer);
|
||||
last_timer = jiffies;
|
||||
@ -532,8 +534,8 @@ static void tusb_source_power(struct musb *musb, int is_on)
|
||||
if (musb->set_clock)
|
||||
musb->set_clock(musb->clock, 1);
|
||||
timer = OTG_TIMER_MS(OTG_TIME_A_WAIT_VRISE);
|
||||
musb->xceiv.default_a = 1;
|
||||
musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
|
||||
musb->xceiv->default_a = 1;
|
||||
musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
|
||||
devctl |= MUSB_DEVCTL_SESSION;
|
||||
|
||||
conf |= TUSB_DEV_CONF_USB_HOST_MODE;
|
||||
@ -546,24 +548,24 @@ static void tusb_source_power(struct musb *musb, int is_on)
|
||||
/* If ID pin is grounded, we want to be a_idle */
|
||||
otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT);
|
||||
if (!(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS)) {
|
||||
switch (musb->xceiv.state) {
|
||||
switch (musb->xceiv->state) {
|
||||
case OTG_STATE_A_WAIT_VRISE:
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
musb->xceiv.state = OTG_STATE_A_WAIT_VFALL;
|
||||
musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
|
||||
break;
|
||||
case OTG_STATE_A_WAIT_VFALL:
|
||||
musb->xceiv.state = OTG_STATE_A_IDLE;
|
||||
musb->xceiv->state = OTG_STATE_A_IDLE;
|
||||
break;
|
||||
default:
|
||||
musb->xceiv.state = OTG_STATE_A_IDLE;
|
||||
musb->xceiv->state = OTG_STATE_A_IDLE;
|
||||
}
|
||||
musb->is_active = 0;
|
||||
musb->xceiv.default_a = 1;
|
||||
musb->xceiv->default_a = 1;
|
||||
MUSB_HST_MODE(musb);
|
||||
} else {
|
||||
musb->is_active = 0;
|
||||
musb->xceiv.default_a = 0;
|
||||
musb->xceiv.state = OTG_STATE_B_IDLE;
|
||||
musb->xceiv->default_a = 0;
|
||||
musb->xceiv->state = OTG_STATE_B_IDLE;
|
||||
MUSB_DEV_MODE(musb);
|
||||
}
|
||||
|
||||
@ -674,7 +676,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
|
||||
else
|
||||
default_a = is_host_enabled(musb);
|
||||
DBG(2, "Default-%c\n", default_a ? 'A' : 'B');
|
||||
musb->xceiv.default_a = default_a;
|
||||
musb->xceiv->default_a = default_a;
|
||||
tusb_source_power(musb, default_a);
|
||||
|
||||
/* Don't allow idling immediately */
|
||||
@ -686,7 +688,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
|
||||
if (int_src & TUSB_INT_SRC_VBUS_SENSE_CHNG) {
|
||||
|
||||
/* B-dev state machine: no vbus ~= disconnect */
|
||||
if ((is_otg_enabled(musb) && !musb->xceiv.default_a)
|
||||
if ((is_otg_enabled(musb) && !musb->xceiv->default_a)
|
||||
|| !is_host_enabled(musb)) {
|
||||
#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
||||
/* ? musb_root_disconnect(musb); */
|
||||
@ -701,9 +703,9 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
|
||||
|
||||
if (otg_stat & TUSB_DEV_OTG_STAT_SESS_END) {
|
||||
DBG(1, "Forcing disconnect (no interrupt)\n");
|
||||
if (musb->xceiv.state != OTG_STATE_B_IDLE) {
|
||||
if (musb->xceiv->state != OTG_STATE_B_IDLE) {
|
||||
/* INTR_DISCONNECT can hide... */
|
||||
musb->xceiv.state = OTG_STATE_B_IDLE;
|
||||
musb->xceiv->state = OTG_STATE_B_IDLE;
|
||||
musb->int_usb |= MUSB_INTR_DISCONNECT;
|
||||
}
|
||||
musb->is_active = 0;
|
||||
@ -717,7 +719,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
|
||||
DBG(2, "vbus change, %s, otg %03x\n",
|
||||
otg_state_string(musb), otg_stat);
|
||||
|
||||
switch (musb->xceiv.state) {
|
||||
switch (musb->xceiv->state) {
|
||||
case OTG_STATE_A_IDLE:
|
||||
DBG(2, "Got SRP, turning on VBUS\n");
|
||||
musb_set_vbus(musb, 1);
|
||||
@ -765,7 +767,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
|
||||
|
||||
DBG(4, "%s timer, %03x\n", otg_state_string(musb), otg_stat);
|
||||
|
||||
switch (musb->xceiv.state) {
|
||||
switch (musb->xceiv->state) {
|
||||
case OTG_STATE_A_WAIT_VRISE:
|
||||
/* VBUS has probably been valid for a while now,
|
||||
* but may well have bounced out of range a bit
|
||||
@ -777,7 +779,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
|
||||
DBG(2, "devctl %02x\n", devctl);
|
||||
break;
|
||||
}
|
||||
musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
|
||||
musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
|
||||
musb->is_active = 0;
|
||||
idle_timeout = jiffies
|
||||
+ msecs_to_jiffies(musb->a_wait_bcon);
|
||||
@ -1093,9 +1095,14 @@ int __init musb_platform_init(struct musb *musb)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct resource *mem;
|
||||
void __iomem *sync;
|
||||
void __iomem *sync = NULL;
|
||||
int ret;
|
||||
|
||||
usb_nop_xceiv_register();
|
||||
musb->xceiv = otg_get_transceiver();
|
||||
if (!musb->xceiv)
|
||||
return -ENODEV;
|
||||
|
||||
pdev = to_platform_device(musb->controller);
|
||||
|
||||
/* dma address for async dma */
|
||||
@ -1106,14 +1113,16 @@ int __init musb_platform_init(struct musb *musb)
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (!mem) {
|
||||
pr_debug("no sync dma resource?\n");
|
||||
return -ENODEV;
|
||||
ret = -ENODEV;
|
||||
goto done;
|
||||
}
|
||||
musb->sync = mem->start;
|
||||
|
||||
sync = ioremap(mem->start, mem->end - mem->start + 1);
|
||||
if (!sync) {
|
||||
pr_debug("ioremap for sync failed\n");
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
musb->sync_va = sync;
|
||||
|
||||
@ -1126,28 +1135,37 @@ int __init musb_platform_init(struct musb *musb)
|
||||
if (ret) {
|
||||
printk(KERN_ERR "Could not start tusb6010 (%d)\n",
|
||||
ret);
|
||||
return -ENODEV;
|
||||
goto done;
|
||||
}
|
||||
musb->isr = tusb_interrupt;
|
||||
|
||||
if (is_host_enabled(musb))
|
||||
musb->board_set_vbus = tusb_source_power;
|
||||
if (is_peripheral_enabled(musb))
|
||||
musb->xceiv.set_power = tusb_draw_power;
|
||||
if (is_peripheral_enabled(musb)) {
|
||||
musb->xceiv->set_power = tusb_draw_power;
|
||||
the_musb = musb;
|
||||
}
|
||||
|
||||
setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb);
|
||||
|
||||
done:
|
||||
if (ret < 0) {
|
||||
if (sync)
|
||||
iounmap(sync);
|
||||
usb_nop_xceiv_unregister();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int musb_platform_exit(struct musb *musb)
|
||||
{
|
||||
del_timer_sync(&musb_idle_timer);
|
||||
the_musb = NULL;
|
||||
|
||||
if (musb->board_set_power)
|
||||
musb->board_set_power(0);
|
||||
|
||||
iounmap(musb->sync_va);
|
||||
|
||||
usb_nop_xceiv_unregister();
|
||||
return 0;
|
||||
}
|
||||
|
@ -59,4 +59,18 @@ config NOP_USB_XCEIV
|
||||
built-in with usb ip or which are autonomous and doesn't require any
|
||||
phy programming such as ISP1x04 etc.
|
||||
|
||||
config USB_LANGWELL_OTG
|
||||
tristate "Intel Langwell USB OTG dual-role support"
|
||||
depends on USB && MRST
|
||||
select USB_OTG
|
||||
select USB_OTG_UTILS
|
||||
help
|
||||
Say Y here if you want to build Intel Langwell USB OTG
|
||||
transciever driver in kernel. This driver implements role
|
||||
switch between EHCI host driver and Langwell USB OTG
|
||||
client driver.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called langwell_otg.
|
||||
|
||||
endif # USB || OTG
|
||||
|
@ -9,6 +9,7 @@ obj-$(CONFIG_USB_OTG_UTILS) += otg.o
|
||||
obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o
|
||||
obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
|
||||
obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o
|
||||
obj-$(CONFIG_USB_LANGWELL_OTG) += langwell_otg.o
|
||||
obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o
|
||||
|
||||
ccflags-$(CONFIG_USB_DEBUG) += -DDEBUG
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user