diff --git a/BaS_gcc/Makefile b/BaS_gcc/Makefile index c5d97d4..3e08bd1 100644 --- a/BaS_gcc/Makefile +++ b/BaS_gcc/Makefile @@ -83,6 +83,9 @@ CSRCS= \ $(SRCDIR)/MCD_tasks.c \ $(SRCDIR)/MCD_tasksInit.c \ \ + $(SRCDIR)/ohci-hcd.c \ + $(SRCDIR)/ehci-hcd.c \ + \ $(SRCDIR)/basflash.c \ $(SRCDIR)/basflash_start.c @@ -195,7 +198,7 @@ $(foreach DIR,$(TRGTDIRS),$(eval $(call EX_TEMPLATE,$(DIR)))) depend: $(ASRCS) $(CSRCS) - rm -f depend - for d in $(TRGTDIRS);\ + for d in $(TRGTDIRS)\ do $(CC) $(CFLAGS) $(INCLUDE) -M $(ASRCS) $(CSRCS) | sed -e "s#^\(.*\).o:#$$d/objs/\1.o:#" >> depend; \ done diff --git a/BaS_gcc/include/ehci.h b/BaS_gcc/include/ehci.h new file mode 100644 index 0000000..4d37076 --- /dev/null +++ b/BaS_gcc/include/ehci.h @@ -0,0 +1,207 @@ +/*- + * Copyright (c) 2007-2008, Juniper Networks, Inc. + * Copyright (c) 2008, Michael Trimarchi + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef USB_EHCI_H +#define USB_EHCI_H + +#if !defined(CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) +#define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS 2 +#endif + +/* (shifted) direction/type/recipient from the USB 2.0 spec, table 9.2 */ +#define DeviceRequest \ + ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE) << 8) + +#define DeviceOutRequest \ + ((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE) << 8) + +#define InterfaceRequest \ + ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8) + +#define EndpointRequest \ + ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8) + +#define EndpointOutRequest \ + ((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8) + +/* + * Register Space. + */ +struct ehci_hccr { + uint32_t cr_capbase; +#define HC_LENGTH(p) (((p) >> 0) & 0x00ff) +#define HC_VERSION(p) (((p) >> 16) & 0xffff) + uint32_t cr_hcsparams; +#define HCS_PPC(p) ((p) & (1 << 4)) +#define HCS_INDICATOR(p) ((p) & (1 << 16)) /* Port indicators */ +#define HCS_N_PORTS(p) (((p) >> 0) & 0xf) + uint32_t cr_hccparams; + uint8_t cr_hcsp_portrt[8]; +} __attribute__ ((packed)); + +struct ehci_hcor { + uint32_t or_usbcmd; +#define CMD_PARK (1 << 11) /* enable "park" */ +#define CMD_PARK_CNT(c) (((c) >> 8) & 3) /* how many transfers to park */ +#define CMD_ASE (1 << 5) /* async schedule enable */ +#define CMD_LRESET (1 << 7) /* partial reset */ +#define CMD_IAAD (1 << 5) /* "doorbell" interrupt */ +#define CMD_PSE (1 << 4) /* periodic schedule enable */ +#define CMD_RESET (1 << 1) /* reset HC not bus */ +#define CMD_RUN (1 << 0) /* start/stop HC */ + uint32_t or_usbsts; +#define STD_ASS (1 << 15) +#define STS_PSSTAT (1 << 14) +#define STS_RECL ( 1 << 13) +#define STS_HALT (1 << 12) +#define STS_IAA (1 << 5) +#define STS_HSE (1 << 4) +#define STS_FLR (1 << 3) +#define STS_PCD (1 << 2) +#define STS_USBERRINT (1 << 1) +#define STS_USBINT (1 << 0) + uint32_t or_usbintr; +#define INTR_IAAE (1 << 5) +#define INTR_HSEE (1 << 4) +#define INTR_FLRE (1 << 3) +#define INTR_PCDE (1 << 2) +#define INTR_USBERRINTE (1 << 1) +#define INTR_USBINTE (1 << 0) + uint32_t or_frindex; + uint32_t or_ctrldssegment; + uint32_t or_periodiclistbase; + uint32_t or_asynclistaddr; + uint32_t _reserved_[9]; + uint32_t or_configflag; +#define FLAG_CF (1 << 0) /* true: we'll support "high speed" */ + uint32_t or_portsc[CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS]; + uint32_t or_systune; +} __attribute__ ((packed)); + +#define USBMODE 0x68 /* USB Device mode */ +#define USBMODE_SDIS (1 << 3) /* Stream disable */ +#define USBMODE_BE (1 << 2) /* BE/LE endiannes select */ +#define USBMODE_CM_HC (3 << 0) /* host controller mode */ +#define USBMODE_CM_IDLE (0 << 0) /* idle state */ + +/* Interface descriptor */ +struct usb_linux_interface_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned char bInterfaceNumber; + unsigned char bAlternateSetting; + unsigned char bNumEndpoints; + unsigned char bInterfaceClass; + unsigned char bInterfaceSubClass; + unsigned char bInterfaceProtocol; + unsigned char iInterface; +} __attribute__ ((packed)); + +/* Configuration descriptor information.. */ +struct usb_linux_config_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short wTotalLength; + unsigned char bNumInterfaces; + unsigned char bConfigurationValue; + unsigned char iConfiguration; + unsigned char bmAttributes; + unsigned char MaxPower; +} __attribute__ ((packed)); + +#if defined CONFIG_EHCI_DESC_BIG_ENDIAN +#define ehci_readl(x) (*((volatile u32 *)(x))) +#define ehci_writel(a, b) (*((volatile u32 *)(a)) = ((volatile u32)b)) +#else +#define ehci_readl(x) cpu_to_le32((*((volatile u32 *)(x)))) +#define ehci_writel(a, b) (*((volatile u32 *)(a)) = cpu_to_le32(((volatile u32)b))) +#endif + +#if defined CONFIG_EHCI_MMIO_BIG_ENDIAN +#define hc32_to_cpu(x) be32_to_cpu((x)) +#define cpu_to_hc32(x) cpu_to_be32((x)) +#else +#define hc32_to_cpu(x) le32_to_cpu((x)) +#define cpu_to_hc32(x) cpu_to_le32((x)) +#endif + +#define EHCI_PS_WKOC_E (1 << 22) /* RW wake on over current */ +#define EHCI_PS_WKDSCNNT_E (1 << 21) /* RW wake on disconnect */ +#define EHCI_PS_WKCNNT_E (1 << 20) /* RW wake on connect */ +#define EHCI_PS_PO (1 << 13) /* RW port owner */ +#define EHCI_PS_PP (1 << 12) /* RW,RO port power */ +#define EHCI_PS_LS (3 << 10) /* RO line status */ +#define EHCI_PS_PR (1 << 8) /* RW port reset */ +#define EHCI_PS_SUSP (1 << 7) /* RW suspend */ +#define EHCI_PS_FPR (1 << 6) /* RW force port resume */ +#define EHCI_PS_OCC (1 << 5) /* RWC over current change */ +#define EHCI_PS_OCA (1 << 4) /* RO over current active */ +#define EHCI_PS_PEC (1 << 3) /* RWC port enable change */ +#define EHCI_PS_PE (1 << 2) /* RW port enable */ +#define EHCI_PS_CSC (1 << 1) /* RWC connect status change */ +#define EHCI_PS_CS (1 << 0) /* RO connect status */ +#define EHCI_PS_CLEAR (EHCI_PS_OCC | EHCI_PS_PEC | EHCI_PS_CSC) + +#define EHCI_PS_IS_LOWSPEED(x) (((x) & EHCI_PS_LS) == (1 << 10)) + +/* + * Schedule Interface Space. + * + * IMPORTANT: Software must ensure that no interface data structure + * reachable by the EHCI host controller spans a 4K page boundary! + * + * Periodic transfers (i.e. isochronous and interrupt transfers) are + * not supported. + */ + +/* Queue Element Transfer Descriptor (qTD). */ +struct qTD { + uint32_t qt_next; +#define QT_NEXT_TERMINATE 1 + uint32_t qt_altnext; + uint32_t qt_token; + uint32_t qt_buffer[5]; +}; + +/* Queue Head (QH). */ +struct QH { + uint32_t qh_link; +#define QH_LINK_TERMINATE 1 +#define QH_LINK_TYPE_ITD 0 +#define QH_LINK_TYPE_QH 2 +#define QH_LINK_TYPE_SITD 4 +#define QH_LINK_TYPE_FSTN 6 + uint32_t qh_endpt1; + uint32_t qh_endpt2; + uint32_t qh_curtd; + struct qTD qh_overlay; + /* + * Add dummy fill value to make the size of this struct + * aligned to 32 bytes + */ + uint8_t fill[16]; +}; + +/* Low level init functions */ +int ehci_hcd_init(void); +int ehci_hcd_stop(void); + +#endif /* USB_EHCI_H */ diff --git a/BaS_gcc/include/mod_devicetable.h b/BaS_gcc/include/mod_devicetable.h new file mode 100644 index 0000000..8e480b5 --- /dev/null +++ b/BaS_gcc/include/mod_devicetable.h @@ -0,0 +1,165 @@ +#ifndef MOD_DEVICETABLE_H +#define MOD_DEVICETABLE_H + +#define PCI_ANY_ID (~0) + +struct pci_device_id { + unsigned long vendor, device; /* Vendor and device ID or PCI_ANY_ID*/ + unsigned long subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */ + unsigned long class, class_mask; /* (class,subclass,prog-if) triplet */ + unsigned long driver_data; /* Data private to the driver */ +}; + +#define IEEE1394_MATCH_VENDOR_ID 0x0001 +#define IEEE1394_MATCH_MODEL_ID 0x0002 +#define IEEE1394_MATCH_SPECIFIER_ID 0x0004 +#define IEEE1394_MATCH_VERSION 0x0008 + +struct ieee1394_device_id { + unsigned long match_flags; + unsigned long vendor_id; + unsigned long model_id; + unsigned long specifier_id; + unsigned long version; + unsigned long driver_data; +}; + +/* + * Device table entry for "new style" table-driven USB drivers. + * User mode code can read these tables to choose which modules to load. + * Declare the table as a MODULE_DEVICE_TABLE. + * + * A probe() parameter will point to a matching entry from this table. + * Use the driver_info field for each match to hold information tied + * to that match: device quirks, etc. + * + * Terminate the driver's table with an all-zeroes entry. + * Use the flag values to control which fields are compared. + */ + +/** + * struct usb_device_id - identifies USB devices for probing and hotplugging + * @match_flags: Bit mask controlling of the other fields are used to match + * against new devices. Any field except for driver_info may be used, + * although some only make sense in conjunction with other fields. + * This is usually set by a USB_DEVICE_*() macro, which sets all + * other fields in this structure except for driver_info. + * @idVendor: USB vendor ID for a device; numbers are assigned + * by the USB forum to its members. + * @idProduct: Vendor-assigned product ID. + * @bcdDevice_lo: Low end of range of vendor-assigned product version numbers. + * This is also used to identify individual product versions, for + * a range consisting of a single device. + * @bcdDevice_hi: High end of version number range. The range of product + * versions is inclusive. + * @bDeviceClass: Class of device; numbers are assigned + * by the USB forum. Products may choose to implement classes, + * or be vendor-specific. Device classes specify behavior of all + * the interfaces on a devices. + * @bDeviceSubClass: Subclass of device; associated with bDeviceClass. + * @bDeviceProtocol: Protocol of device; associated with bDeviceClass. + * @bInterfaceClass: Class of interface; numbers are assigned + * by the USB forum. Products may choose to implement classes, + * or be vendor-specific. Interface classes specify behavior only + * of a given interface; other interfaces may support other classes. + * @bInterfaceSubClass: Subclass of interface; associated with bInterfaceClass. + * @bInterfaceProtocol: Protocol of interface; associated with bInterfaceClass. + * @driver_info: Holds information used by the driver. Usually it holds + * a pointer to a descriptor understood by the driver, or perhaps + * device flags. + * + * In most cases, drivers will create a table of device IDs by using + * USB_DEVICE(), or similar macros designed for that purpose. + * They will then export it to userspace using MODULE_DEVICE_TABLE(), + * and provide it to the USB core through their usb_driver structure. + * + * See the usb_match_id() function for information about how matches are + * performed. Briefly, you will normally use one of several macros to help + * construct these entries. Each entry you provide will either identify + * one or more specific products, or will identify a class of products + * which have agreed to behave the same. You should put the more specific + * matches towards the beginning of your table, so that driver_info can + * record quirks of specific products. + */ +struct usb_device_id { + /* which fields to match against? */ + unsigned short match_flags; + + /* Used for product specific matches; range is inclusive */ + unsigned short idVendor; + unsigned short idProduct; + unsigned short bcdDevice_lo; + unsigned short bcdDevice_hi; + + /* Used for device class matches */ + unsigned char bDeviceClass; + unsigned char bDeviceSubClass; + unsigned char bDeviceProtocol; + + /* Used for interface class matches */ + unsigned char bInterfaceClass; + unsigned char bInterfaceSubClass; + unsigned char bInterfaceProtocol; + + /* not matched against */ + unsigned long driver_info; +}; + +/* Some useful macros to use to create struct usb_device_id */ +#define USB_DEVICE_ID_MATCH_VENDOR 0x0001 +#define USB_DEVICE_ID_MATCH_PRODUCT 0x0002 +#define USB_DEVICE_ID_MATCH_DEV_LO 0x0004 +#define USB_DEVICE_ID_MATCH_DEV_HI 0x0008 +#define USB_DEVICE_ID_MATCH_DEV_CLASS 0x0010 +#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS 0x0020 +#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL 0x0040 +#define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080 +#define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100 +#define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200 + +/* s390 CCW devices */ +struct ccw_device_id { + unsigned short match_flags; /* which fields to match against */ + + unsigned short cu_type; /* control unit type */ + unsigned short dev_type; /* device type */ + unsigned char cu_model; /* control unit model */ + unsigned char dev_model; /* device model */ + + unsigned long driver_info; +}; + +#define CCW_DEVICE_ID_MATCH_CU_TYPE 0x01 +#define CCW_DEVICE_ID_MATCH_CU_MODEL 0x02 +#define CCW_DEVICE_ID_MATCH_DEVICE_TYPE 0x04 +#define CCW_DEVICE_ID_MATCH_DEVICE_MODEL 0x08 + + +#define PNP_ID_LEN 8 +#define PNP_MAX_DEVICES 8 + +struct pnp_device_id { + unsigned char id[PNP_ID_LEN]; + unsigned long driver_data; +}; + +struct pnp_card_device_id { + unsigned char id[PNP_ID_LEN]; + unsigned long driver_data; + struct { + unsigned char id[PNP_ID_LEN]; + } devs[PNP_MAX_DEVICES]; +}; + + +#define SERIO_ANY 0xff + +struct serio_device_id { + unsigned char type; + unsigned char extra; + unsigned char id; + unsigned char proto; +}; + + +#endif /* MOD_DEVICETABLE_H */ diff --git a/BaS_gcc/include/ohci.h b/BaS_gcc/include/ohci.h new file mode 100644 index 0000000..6dc25c2 --- /dev/null +++ b/BaS_gcc/include/ohci.h @@ -0,0 +1,475 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2001 David Brownell + * + * usb-ohci.h + */ + +#define USB_OHCI_MAX_ROOT_PORTS 4 + +static int cc_to_error[16] = { + +/* mapping of the OHCI CC status to error codes */ + /* No Error */ 0, + /* CRC Error */ USB_ST_CRC_ERR, + /* Bit Stuff */ USB_ST_BIT_ERR, + /* Data Togg */ USB_ST_CRC_ERR, + /* Stall */ USB_ST_STALLED, + /* DevNotResp */ -1, + /* PIDCheck */ USB_ST_BIT_ERR, + /* UnExpPID */ USB_ST_BIT_ERR, + /* DataOver */ USB_ST_BUF_ERR, + /* DataUnder */ USB_ST_BUF_ERR, + /* reservd */ -1, + /* reservd */ -1, + /* BufferOver */ USB_ST_BUF_ERR, + /* BuffUnder */ USB_ST_BUF_ERR, + /* Not Access */ -1, + /* Not Access */ -1 +}; + +static const char *cc_to_string[16] = { + "No Error", + "CRC: Last data packet from endpoint contained a CRC error.", + "BITSTUFFING:\r\nLast data packet from endpoint contained a bit stuffing violation", + "DATATOGGLEMISMATCH:\r\n Last packet from endpoint had data toggle PID\r\n" \ + "that did not match the expected value.", + "STALL: TD was moved to the Done Queue because the endpoint returned a STALL PID", + "DEVICENOTRESPONDING:\r\nDevice did not respond to token (IN) or did\r\n" \ + "not provide a handshake (OUT)", + "PIDCHECKFAILURE:\r\nCheck bits on PID from endpoint failed on data PID\r\n"\ + "(IN) or handshake (OUT)", + "UNEXPECTEDPID:\r\nReceive PID was not valid when encountered or PID\r\n" \ + "value is not defined.", + "DATAOVERRUN:\r\nThe amount of data returned by the endpoint exceeded\r\n" \ + "either the size of the maximum data packet allowed\r\n" \ + "from the endpoint (found in MaximumPacketSize field\r\n" \ + "of ED) or the remaining buffer size.", + "DATAUNDERRUN:\r\nThe endpoint returned less than MaximumPacketSize\r\n" \ + "and that amount was not sufficient to fill the\r\n" \ + "specified buffer", + "reserved1", + "reserved2", + "BUFFEROVERRUN:\r\nDuring an IN, HC received data from endpoint faster\r\n" \ + "than it could be written to system memory", + "BUFFERUNDERRUN:\r\nDuring an OUT, HC could not retrieve data from\r\n" \ + "system memory fast enough to keep up with data USB data rate.", + "NOT ACCESSED:\r\nThis code is set by software before the TD is placed\r\n" \ + "on a list to be processed by the HC.(1)", + "NOT ACCESSED:\r\nThis code is set by software before the TD is placed\r\n" \ + "on a list to be processed by the HC.(2)", +}; + +/* ED States */ + +#define ED_NEW 0x00 +#define ED_UNLINK 0x01 +#define ED_OPER 0x02 +#define ED_DEL 0x04 +#define ED_URB_DEL 0x08 + +/* usb_ohci_ed */ +struct ed { + __u32 hwINFO; + __u32 hwTailP; + __u32 hwHeadP; + __u32 hwNextED; + + struct ed *ed_prev; + __u8 int_period; + __u8 int_branch; + __u8 int_load; + __u8 int_interval; + __u8 state; + __u8 type; + __u16 last_iso; + struct ed *ed_rm_list; + + struct usb_device *usb_dev; + void *purb; + __u32 unused[2]; +} __attribute__((aligned(16))); +typedef struct ed ed_t; + + +/* TD info field */ +#define TD_CC 0xf0000000 +#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) +#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28) +#define TD_EC 0x0C000000 +#define TD_T 0x03000000 +#define TD_T_DATA0 0x02000000 +#define TD_T_DATA1 0x03000000 +#define TD_T_TOGGLE 0x00000000 +#define TD_R 0x00040000 +#define TD_DI 0x00E00000 +#define TD_DI_SET(X) (((X) & 0x07)<< 21) +#define TD_DP 0x00180000 +#define TD_DP_SETUP 0x00000000 +#define TD_DP_IN 0x00100000 +#define TD_DP_OUT 0x00080000 + +#define TD_ISO 0x00010000 +#define TD_DEL 0x00020000 + +/* CC Codes */ +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEM 0x03 +#define TD_CC_STALL 0x04 +#define TD_DEVNOTRESP 0x05 +#define TD_PIDCHECKFAIL 0x06 +#define TD_UNEXPECTEDPID 0x07 +#define TD_DATAOVERRUN 0x08 +#define TD_DATAUNDERRUN 0x09 +#define TD_BUFFEROVERRUN 0x0C +#define TD_BUFFERUNDERRUN 0x0D +#define TD_NOTACCESSED 0x0F + + +#define MAXPSW 1 + +struct td { + __u32 hwINFO; + __u32 hwCBP; /* Current Buffer Pointer */ + __u32 hwNextTD; /* Next TD Pointer */ + __u32 hwBE; /* Memory Buffer End Pointer */ + + __u16 hwPSW[MAXPSW]; + __u8 unused; + __u8 index; + struct ed *ed; + struct td *next_dl_td; + struct usb_device *usb_dev; + int transfer_len; + __u32 data; + + __u32 unused2[2]; +} __attribute__((aligned(32))); +typedef struct td td_t; + +#define OHCI_ED_SKIP (1 << 14) + +/* + * The HCCA (Host Controller Communications Area) is a 256 byte + * structure defined in the OHCI spec. that the host controller is + * told the base address of. It must be 256-byte aligned. + */ + +#define NUM_INTS 32 /* part of the OHCI standard */ +struct ohci_hcca { + __u32 int_table[NUM_INTS]; /* Interrupt ED table */ +#if defined(CONFIG_MPC5200) + __u16 pad1; /* set to 0 on each frame_no change */ + __u16 frame_no; /* current frame number */ +#else + __u16 frame_no; /* current frame number */ + __u16 pad1; /* set to 0 on each frame_no change */ +#endif + __u32 done_head; /* info returned for an interrupt */ + u8 reserved_for_hc[116]; +} __attribute__((aligned(256))); + +/* + * This is the structure of the OHCI controller's memory mapped I/O + * region. This is Memory Mapped I/O. You must use the readl() and + * writel() macros defined in asm/io.h to access these!! + */ +struct ohci_regs { + /* control and status registers */ + __u32 revision; + __u32 control; + __u32 cmdstatus; + __u32 intrstatus; + __u32 intrenable; + __u32 intrdisable; + /* memory pointers */ + __u32 hcca; + __u32 ed_periodcurrent; + __u32 ed_controlhead; + __u32 ed_controlcurrent; + __u32 ed_bulkhead; + __u32 ed_bulkcurrent; + __u32 donehead; + /* frame counters */ + __u32 fminterval; + __u32 fmremaining; + __u32 fmnumber; + __u32 periodicstart; + __u32 lsthresh; + /* Root hub ports */ + struct ohci_roothub_regs { + __u32 a; + __u32 b; + __u32 status; + __u32 portstatus[USB_OHCI_MAX_ROOT_PORTS]; + } roothub; +} __attribute__((aligned(32))); + +/* Some EHCI controls */ +#define EHCI_USBCMD_OFF 0x20 +#define EHCI_USBCMD_HCRESET (1 << 1) + +/* OHCI CONTROL AND STATUS REGISTER MASKS */ + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */ +#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */ +#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */ +#define OHCI_CTRL_CLE (1 << 4) /* control list enable */ +#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */ +#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ +#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ +#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ +#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +# define OHCI_USB_RESET (0 << 6) +# define OHCI_USB_RESUME (1 << 6) +# define OHCI_USB_OPER (2 << 6) +# define OHCI_USB_SUSPEND (3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR (1 << 0) /* host controller reset */ +#define OHCI_CLF (1 << 1) /* control list filled */ +#define OHCI_BLF (1 << 2) /* bulk list filled */ +#define OHCI_OCR (1 << 3) /* ownership change request */ +#define OHCI_SOC (3 << 16) /* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ +#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ +#define OHCI_INTR_SF (1 << 2) /* start frame */ +#define OHCI_INTR_RD (1 << 3) /* resume detect */ +#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ +#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ +#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ +#define OHCI_INTR_OC (1 << 30) /* ownership change */ +#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ + + +/* Virtual Root HUB */ +struct virt_root_hub { + int devnum; /* Address of Root Hub endpoint */ + void *dev; /* was urb */ + void *int_addr; + int send; + int interval; +}; + +/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */ + +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 + +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 + + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status*/ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + +/* urb */ +#define N_URB_TD 48 +typedef struct +{ + ed_t *ed; + __u16 length; /* number of tds associated with this request */ + __u16 td_cnt; /* number of tds already serviced */ + struct usb_device *dev; + int state; + unsigned long pipe; + void *transfer_buffer; + int transfer_buffer_length; + int interval; + int actual_length; + int finished; + td_t *td[N_URB_TD]; /* list pointer to all corresponding TDs associated with this request */ +} urb_priv_t; +#define URB_DEL 1 + +#define NUM_EDS 8 /* num of preallocated endpoint descriptors */ + +struct ohci_device { + ed_t ed[NUM_EDS]; + int ed_cnt; +}; + +/* + * This is the full ohci controller description + * + * Note how the "proper" USB information is just + * a subset of what the full implementation needs. (Linus) + */ + +typedef struct ohci { + /* ------- common part -------- */ + long handle; /* PCI BIOS */ + const struct pci_device_id *ent; + int usbnum; + /* ---- end of common part ---- */ + int big_endian; /* PCI BIOS */ + int controller; + struct ohci_hcca *hcca_unaligned; + struct ohci_hcca *hcca; /* hcca */ + td_t *td_unaligned; + struct ohci_device *ohci_dev_unaligned; + /* this allocates EDs for all possible endpoints */ + struct ohci_device *ohci_dev; + + int irq_enabled; + int stat_irq; + int irq; + int disabled; /* e.g. got a UE, we're hung */ + int sleeping; +#define OHCI_FLAGS_NEC 0x80000000 + unsigned long flags; /* for HC bugs */ + + unsigned long offset; + unsigned long dma_offset; + struct ohci_regs *regs; /* OHCI controller's memory */ + + int ohci_int_load[32]; /* load of the 32 Interrupt Chains (for load balancing)*/ + ed_t *ed_rm_list[2]; /* lists of all endpoints to be removed */ + ed_t *ed_bulktail; /* last endpoint of bulk list */ + ed_t *ed_controltail; /* last endpoint of control list */ + int intrstatus; + __u32 hc_control; /* copy of the hc control reg */ + __u32 ndp; /* copy NDP from roothub_a */ + struct virt_root_hub rh; + + const char *slot_name; + + /* device which was disconnected */ + struct usb_device *devgone; +} ohci_t; + +/* hcd */ +/* endpoint */ +static int ep_link(ohci_t * ohci, ed_t * ed); +static int ep_unlink(ohci_t * ohci, ed_t * ed); +static ed_t * ep_add_ed(ohci_t * ohci, struct usb_device * usb_dev, unsigned long pipe, int interval, int load); + +/*-------------------------------------------------------------------------*/ + +/* we need more TDs than EDs */ +#define NUM_TD 64 + +/* pointers to aligned storage */ +td_t *ptd; + +/* TDs ... */ +static inline struct td *td_alloc(struct usb_device *usb_dev) +{ + int i; + struct td *td; + td = NULL; + for(i = 0; i < NUM_TD; i++) + { + if(ptd[i].usb_dev == NULL) + { + td = &ptd[i]; + td->usb_dev = usb_dev; + break; + } + } + return td; +} + +static inline void ed_free(struct ed *ed) +{ + ed->usb_dev = NULL; +} + + diff --git a/BaS_gcc/include/part.h b/BaS_gcc/include/part.h new file mode 100644 index 0000000..b1ae121 --- /dev/null +++ b/BaS_gcc/include/part.h @@ -0,0 +1,89 @@ +/* + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#ifndef _PART_H +#define _PART_H + +typedef unsigned long long uint64_t; +typedef unsigned long lbaint_t; + +typedef struct block_dev_desc { + int if_type; /* type of the interface */ + int dev; /* device number */ + unsigned char part_type; /* partition type */ + unsigned char target; /* target SCSI ID */ + unsigned char lun; /* target LUN */ + unsigned char type; /* device type */ + unsigned char removable; /* removable device */ +#ifdef CONFIG_LBA48 + unsigned char lba48; /* device can use 48bit addr (ATA/ATAPI v7) */ +#endif + lbaint_t lba; /* number of blocks */ + unsigned long blksz; /* block size */ + char vendor [40+1]; /* IDE model, SCSI Vendor */ + char product[20+1]; /* IDE Serial no, SCSI product */ + char revision[8+1]; /* firmware revision */ + long (*block_read)(int dev, unsigned long start, lbaint_t blkcnt, void *buffer); + long (*block_write)(int dev, unsigned long start, lbaint_t blkcnt, const void *buffer); + void *priv; /* driver private struct pointer */ +}block_dev_desc_t; + +/* Interface types: */ +#define IF_TYPE_UNKNOWN 0 +#define IF_TYPE_IDE 1 +#define IF_TYPE_SCSI 2 +#define IF_TYPE_ATAPI 3 +#define IF_TYPE_USB 4 +#define IF_TYPE_DOC 5 +#define IF_TYPE_MMC 6 +#define IF_TYPE_SD 7 +#define IF_TYPE_SATA 8 + +/* Part types */ +#define PART_TYPE_UNKNOWN 0x00 +#define PART_TYPE_MAC 0x01 +#define PART_TYPE_DOS 0x02 +#define PART_TYPE_ISO 0x03 +#define PART_TYPE_AMIGA 0x04 +#define PART_TYPE_EFI 0x05 +#define PART_TYPE_GEMDOS 0x06 + +/* + * Type string for U-Boot bootable partitions + */ +#define BOOT_PART_TYPE "U-Boot" /* primary boot partition type */ +#define BOOT_PART_COMP "PPCBoot" /* PPCBoot compatibility type */ + +/* device types */ +#define DEV_TYPE_UNKNOWN 0xff /* not connected */ +#define DEV_TYPE_HARDDISK 0x00 /* harddisk */ +#define DEV_TYPE_TAPE 0x01 /* Tape */ +#define DEV_TYPE_CDROM 0x05 /* CD-ROM */ +#define DEV_TYPE_OPDISK 0x07 /* optical disk */ + +void print_part(block_dev_desc_t *dev_desc); +void init_part(block_dev_desc_t *dev_desc); +void dev_print(block_dev_desc_t *dev_desc); + +int fat_register_device(block_dev_desc_t *dev_desc, int part_no, unsigned long *part_type, unsigned long *part_offset, unsigned long *part_size); + +#endif /* _PART_H */ diff --git a/BaS_gcc/include/usb.h b/BaS_gcc/include/usb.h new file mode 100644 index 0000000..77ad4cc --- /dev/null +++ b/BaS_gcc/include/usb.h @@ -0,0 +1,485 @@ +/* + * (C) Copyright 2001 + * Denis Peter, MPL AG Switzerland + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Note: Part of this code has been derived from linux + * + */ +#ifndef _USB_H_ +#define _USB_H_ + +#include +#include +#include "pci.h" +#include "mod_devicetable.h" +//#include "pci_ids.h" +#include "part.h" + + +extern long *tab_funcs_pci; + +#define in8(addr) Fast_read_mem_byte(usb_handle,addr) +#define in16r(addr) Fast_read_mem_word(usb_handle,addr) +#define in32r(addr) Fast_read_mem_longword(usb_handle,addr) +#define out8(addr,val) Write_mem_byte(usb_handle,addr,val) +#define out16r(addr,val) Write_mem_word(usb_handle,addr,val) +#define out32r(addr,val) Write_mem_longword(usb_handle,addr,val) + + +#define __u8 unsigned char +#define __u16 unsigned short +#define __u32 unsigned long +#define u8 unsigned char +#define u16 unsigned short +#define u32 unsigned long +#define uint8_t unsigned char +#define uint32_t unsigned long +#define uint16_t unsigned short + +extern void kprint(const char *fmt, ...); +extern int sprintD(char *s, const char *fmt, ...); + +#include "usb_defs.h" + +/* Everything is aribtrary */ +#define USB_ALTSETTINGALLOC 4 +#define USB_MAXALTSETTING 128 /* Hard limit */ + +#define USB_MAX_BUS 3 +#define USB_MAX_DEVICE 16 +#define USB_MAXCONFIG 8 +#define USB_MAXINTERFACES 8 +#define USB_MAXENDPOINTS 16 +#define USB_MAXCHILDREN 8 /* This is arbitrary */ +#define USB_MAX_HUB 16 + +#define USB_CNTL_TIMEOUT 100 /* 100ms timeout */ + +/* String descriptor */ +struct usb_string_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short wData[1]; +} __attribute__ ((packed)); + +/* device request (setup) */ +struct devrequest { + unsigned char requesttype; + unsigned char request; + unsigned short value; + unsigned short index; + unsigned short length; +} __attribute__ ((packed)); + +/* All standard descriptors have these 2 fields in common */ +struct usb_descriptor_header { + unsigned char bLength; + unsigned char bDescriptorType; +} __attribute__ ((packed)); + +/* Device descriptor */ +struct usb_device_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short bcdUSB; + unsigned char bDeviceClass; + unsigned char bDeviceSubClass; + unsigned char bDeviceProtocol; + unsigned char bMaxPacketSize0; + unsigned short idVendor; + unsigned short idProduct; + unsigned short bcdDevice; + unsigned char iManufacturer; + unsigned char iProduct; + unsigned char iSerialNumber; + unsigned char bNumConfigurations; +} __attribute__ ((packed)); + +/* Endpoint descriptor */ +struct usb_endpoint_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned char bEndpointAddress; + unsigned char bmAttributes; + unsigned short wMaxPacketSize; + unsigned char bInterval; + unsigned char bRefresh; + unsigned char bSynchAddress; +} __attribute__ ((packed)) __attribute__ ((aligned(2))); + +/* Interface descriptor */ +struct usb_interface_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned char bInterfaceNumber; + unsigned char bAlternateSetting; + unsigned char bNumEndpoints; + unsigned char bInterfaceClass; + unsigned char bInterfaceSubClass; + unsigned char bInterfaceProtocol; + unsigned char iInterface; + + unsigned char no_of_ep; + unsigned char num_altsetting; + unsigned char act_altsetting; + + struct usb_endpoint_descriptor ep_desc[USB_MAXENDPOINTS]; +} __attribute__ ((packed)); + + +/* Configuration descriptor information.. */ +struct usb_config_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short wTotalLength; + unsigned char bNumInterfaces; + unsigned char bConfigurationValue; + unsigned char iConfiguration; + unsigned char bmAttributes; + unsigned char MaxPower; + + unsigned char no_of_if; /* number of interfaces */ + struct usb_interface_descriptor if_desc[USB_MAXINTERFACES]; +} __attribute__ ((packed)); + +enum { + /* Maximum packet size; encoded as 0,1,2,3 = 8,16,32,64 */ + PACKET_SIZE_8 = 0, + PACKET_SIZE_16 = 1, + PACKET_SIZE_32 = 2, + PACKET_SIZE_64 = 3, +}; + +struct usb_device { + int devnum; /* Device number on USB bus */ + int speed; /* full/low/high */ + char mf[32]; /* manufacturer */ + char prod[32]; /* product */ + char serial[32]; /* serial number */ + + /* Maximum packet size; one of: PACKET_SIZE_* */ + int maxpacketsize; + /* one bit for each endpoint ([0] = IN, [1] = OUT) */ + unsigned int toggle[2]; + /* endpoint halts; one bit per endpoint # & direction; + * [0] = IN, [1] = OUT + */ + unsigned int halted[2]; + int epmaxpacketin[16]; /* INput endpoint specific maximums */ + int epmaxpacketout[16]; /* OUTput endpoint specific maximums */ + + int configno; /* selected config number */ + struct usb_device_descriptor descriptor; /* Device Descriptor */ + struct usb_config_descriptor config; /* config descriptor */ + + int have_langid; /* whether string_langid is valid yet */ + int string_langid; /* language ID for strings */ + int (*irq_handle)(struct usb_device *dev); + unsigned long irq_status; + int irq_act_len; /* transfered bytes */ + void *privptr; + /* + * Child devices - if this is a hub device + * Each instance needs its own set of data structures. + */ + unsigned long status; + int act_len; /* transfered bytes */ + int maxchild; /* Number of ports if hub */ + int portnr; + struct usb_device *parent; + struct usb_device *children[USB_MAXCHILDREN]; + void *priv_hcd; + int (*deregister)(struct usb_device *dev); + + struct usb_hub_device *hub; + int usbnum; +}; + +typedef struct +{ + long ident; + union + { + long l; + short i[2]; + char c[4]; + } v; +} USB_COOKIE; + +/********************************************************************** + * this is how the lowlevel part communicate with the outer world + */ + +int ohci_usb_lowlevel_init(long handle, const struct pci_device_id *ent, void **priv); +int ohci_usb_lowlevel_stop(void *priv); +int ohci_submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len); +int ohci_submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, struct devrequest *setup); +int ohci_submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, int interval); +#ifdef CONFIG_USB_INTERRUPT_POLLING +void ohci_usb_event_poll(int interrupt); +#else +void ohci_usb_enable_interrupt(int enable); +#endif /* CONFIG_USB_INTERRUPT_POLLING */ + +int ehci_usb_lowlevel_init(long handle, const struct pci_device_id *ent, void **priv); +int ehci_usb_lowlevel_stop(void *priv); +int ehci_submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len); +int ehci_submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, struct devrequest *setup); +int ehci_submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, int interval); +#ifdef CONFIG_USB_INTERRUPT_POLLING +void ehci_usb_event_poll(int interrupt); +#else +void ehci_usb_enable_interrupt(int enable); +#endif /* CONFIG_USB_INTERRUPT_POLLING */ + +#ifdef CONFIG_USB_INTERRUPT_POLLING +void usb_event_poll(int interrupt); +#else +void usb_enable_interrupt(int enable); +#endif /* CONFIG_USB_INTERRUPT_POLLING */ + +#define USB_MAX_STOR_DEV 5 +block_dev_desc_t *usb_stor_get_dev(int index); +int usb_stor_scan(void); +int usb_stor_info(void); +int usb_stor_register(struct usb_device *dev); +int usb_stor_deregister(struct usb_device *dev); + +int drv_usb_kbd_init(void); +int usb_kbd_register(struct usb_device *dev); +int usb_kbd_deregister(struct usb_device *dev); + +int drv_usb_mouse_init(void); +int usb_mouse_register(struct usb_device *dev); +int usb_mouse_deregister(struct usb_device *dev); + +extern char usb_error_str[256]; + +/* memory */ +void *usb_malloc(long amount); +int usb_free(void *addr); +int usb_mem_init(void); +void usb_mem_stop(void); + +/* routines */ +USB_COOKIE *usb_get_cookie(long id); +void usb_error_msg(const char *const fmt, ... ); +int usb_init(long handle, const struct pci_device_id *ent); /* initialize the USB Controller */ +int usb_stop(void); /* stop the USB Controller */ + +int usb_set_protocol(struct usb_device *dev, int ifnum, int protocol); +int usb_set_idle(struct usb_device *dev, int ifnum, int duration, int report_id); +struct usb_device *usb_get_dev_index(int index, int bus); +int usb_control_msg(struct usb_device *dev, unsigned int pipe, unsigned char request, unsigned char requesttype, unsigned short value, + unsigned short index, void *data, unsigned short size, int timeout); +int usb_bulk_msg(struct usb_device *dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout); +int usb_submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, int interval); +void usb_disable_asynch(int disable); +int usb_maxpacket(struct usb_device *dev, unsigned long pipe); +void wait_ms(unsigned long ms); +int usb_get_configuration_no(struct usb_device *dev, unsigned char *buffer, int cfgno); +int usb_get_report(struct usb_device *dev, int ifnum, unsigned char type, unsigned char id, void *buf, int size); +int usb_get_class_descriptor(struct usb_device *dev, int ifnum, unsigned char type, unsigned char id, void *buf, int size); +int usb_clear_halt(struct usb_device *dev, int pipe); +int usb_string(struct usb_device *dev, int index, char *buf, size_t size); +int usb_set_interface(struct usb_device *dev, int interface, int alternate); + +extern unsigned short swap_short(unsigned short val); +extern unsigned long swap_long(unsigned long val); + +#if 1 + #define __swap_16(x) swap_short(x) + #define __swap_32(x) swap_long(x) +#else +/* big endian -> little endian conversion */ +/* some CPUs are already little endian e.g. the ARM920T */ +#define __swap_16(x) \ + ({ unsigned short x_ = (unsigned short)x; \ + (unsigned short)( \ + ((x_ & 0x00FFU) << 8) | ((x_ & 0xFF00U) >> 8)); \ + }) +#define __swap_32(x) \ + ({ unsigned long x_ = (unsigned long)x; \ + (unsigned long)( \ + ((x_ & 0x000000FFUL) << 24) | \ + ((x_ & 0x0000FF00UL) << 8) | \ + ((x_ & 0x00FF0000UL) >> 8) | \ + ((x_ & 0xFF000000UL) >> 24)); \ + }) +#endif + +#define swap_16(x) __swap_16(x) +#define swap_32(x) __swap_32(x) + +#define le16_to_cpu cpu_to_le16 +#define le32_to_cpu cpu_to_le32 +#define cpu_to_be32(a) a + +static inline unsigned short cpu_to_le16(unsigned short val) +{ + return(swap_short(val)); +} +static inline unsigned long cpu_to_le32(unsigned long val) +{ + return(swap_long(val)); +} + +static inline void le16_to_cpus(unsigned short *val) +{ + *val = le16_to_cpu(*val); +} +static inline void le32_to_cpus(unsigned long *val) +{ + *val = le32_to_cpu(*val); +} + +/* + * Calling this entity a "pipe" is glorifying it. A USB pipe + * is something embarrassingly simple: it basically consists + * of the following information: + * - device number (7 bits) + * - endpoint number (4 bits) + * - current Data0/1 state (1 bit) + * - direction (1 bit) + * - speed (2 bits) + * - max packet size (2 bits: 8, 16, 32 or 64) + * - pipe type (2 bits: control, interrupt, bulk, isochronous) + * + * That's 18 bits. Really. Nothing more. And the USB people have + * documented these eighteen bits as some kind of glorious + * virtual data structure. + * + * Let's not fall in that trap. We'll just encode it as a simple + * unsigned int. The encoding is: + * + * - max size: bits 0-1 (00 = 8, 01 = 16, 10 = 32, 11 = 64) + * - direction: bit 7 (0 = Host-to-Device [Out], + * (1 = Device-to-Host [In]) + * - device: bits 8-14 + * - endpoint: bits 15-18 + * - Data0/1: bit 19 + * - speed: bit 26 (0 = Full, 1 = Low Speed, 2 = High) + * - pipe type: bits 30-31 (00 = isochronous, 01 = interrupt, + * 10 = control, 11 = bulk) + * + * Why? Because it's arbitrary, and whatever encoding we select is really + * up to us. This one happens to share a lot of bit positions with the UHCI + * specification, so that much of the uhci driver can just mask the bits + * appropriately. + */ +/* Create various pipes... */ +#define create_pipe(dev,endpoint) \ + (((dev)->devnum << 8) | (endpoint << 15) | \ + ((dev)->speed << 26) | (dev)->maxpacketsize) +#define default_pipe(dev) ((dev)->speed << 26) + +#define usb_sndctrlpipe(dev, endpoint) ((PIPE_CONTROL << 30) | \ + create_pipe(dev, endpoint)) +#define usb_rcvctrlpipe(dev, endpoint) ((PIPE_CONTROL << 30) | \ + create_pipe(dev, endpoint) | \ + USB_DIR_IN) +#define usb_sndisocpipe(dev, endpoint) ((PIPE_ISOCHRONOUS << 30) | \ + create_pipe(dev, endpoint)) +#define usb_rcvisocpipe(dev, endpoint) ((PIPE_ISOCHRONOUS << 30) | \ + create_pipe(dev, endpoint) | \ + USB_DIR_IN) +#define usb_sndbulkpipe(dev, endpoint) ((PIPE_BULK << 30) | \ + create_pipe(dev, endpoint)) +#define usb_rcvbulkpipe(dev, endpoint) ((PIPE_BULK << 30) | \ + create_pipe(dev, endpoint) | \ + USB_DIR_IN) +#define usb_sndintpipe(dev, endpoint) ((PIPE_INTERRUPT << 30) | \ + create_pipe(dev, endpoint)) +#define usb_rcvintpipe(dev, endpoint) ((PIPE_INTERRUPT << 30) | \ + create_pipe(dev, endpoint) | \ + USB_DIR_IN) +#define usb_snddefctrl(dev) ((PIPE_CONTROL << 30) | \ + default_pipe(dev)) +#define usb_rcvdefctrl(dev) ((PIPE_CONTROL << 30) | \ + default_pipe(dev) | \ + USB_DIR_IN) + +/* The D0/D1 toggle bits */ +#define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> ep) & 1) +#define usb_dotoggle(dev, ep, out) ((dev)->toggle[out] ^= (1 << ep)) +#define usb_settoggle(dev, ep, out, bit) ((dev)->toggle[out] = \ + ((dev)->toggle[out] & \ + ~(1 << ep)) | ((bit) << ep)) + +/* Endpoint halt control/status */ +#define usb_endpoint_out(ep_dir) (((ep_dir >> 7) & 1) ^ 1) +#define usb_endpoint_halt(dev, ep, out) ((dev)->halted[out] |= (1 << (ep))) +#define usb_endpoint_running(dev, ep, out) ((dev)->halted[out] &= ~(1 << (ep))) +#define usb_endpoint_halted(dev, ep, out) ((dev)->halted[out] & (1 << (ep))) + +#define usb_packetid(pipe) (((pipe) & USB_DIR_IN) ? USB_PID_IN : \ + USB_PID_OUT) + +#define usb_pipeout(pipe) ((((pipe) >> 7) & 1) ^ 1) +#define usb_pipein(pipe) (((pipe) >> 7) & 1) +#define usb_pipedevice(pipe) (((pipe) >> 8) & 0x7f) +#define usb_pipe_endpdev(pipe) (((pipe) >> 8) & 0x7ff) +#define usb_pipeendpoint(pipe) (((pipe) >> 15) & 0xf) +#define usb_pipedata(pipe) (((pipe) >> 19) & 1) +#define usb_pipespeed(pipe) (((pipe) >> 26) & 3) +#define usb_pipeslow(pipe) (usb_pipespeed(pipe) == USB_SPEED_LOW) +#define usb_pipetype(pipe) (((pipe) >> 30) & 3) +#define usb_pipeisoc(pipe) (usb_pipetype((pipe)) == PIPE_ISOCHRONOUS) +#define usb_pipeint(pipe) (usb_pipetype((pipe)) == PIPE_INTERRUPT) +#define usb_pipecontrol(pipe) (usb_pipetype((pipe)) == PIPE_CONTROL) +#define usb_pipebulk(pipe) (usb_pipetype((pipe)) == PIPE_BULK) + + +/************************************************************************* + * Hub Stuff + */ +struct usb_port_status { + unsigned short wPortStatus; + unsigned short wPortChange; +} __attribute__ ((packed)); + +struct usb_hub_status { + unsigned short wHubStatus; + unsigned short wHubChange; +} __attribute__ ((packed)); + + +/* Hub descriptor */ +struct usb_hub_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned char bNbrPorts; + unsigned short wHubCharacteristics; + unsigned char bPwrOn2PwrGood; + unsigned char bHubContrCurrent; + unsigned char DeviceRemovable[(USB_MAXCHILDREN+1+7)/8]; + unsigned char PortPowerCtrlMask[(USB_MAXCHILDREN+1+7)/8]; + /* DeviceRemovable and PortPwrCtrlMask want to be variable-length + bitmaps that hold max 255 entries. (bit0 is ignored) */ +} __attribute__ ((packed)); + + +struct usb_hub_device { + struct usb_device *pusb_dev; + struct usb_hub_descriptor desc; +}; + +#endif /*_USB_H_ */ diff --git a/BaS_gcc/include/usb_defs.h b/BaS_gcc/include/usb_defs.h new file mode 100644 index 0000000..8032e57 --- /dev/null +++ b/BaS_gcc/include/usb_defs.h @@ -0,0 +1,251 @@ +/* + * (C) Copyright 2001 + * Denis Peter, MPL AG Switzerland + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Note: Part of this code has been derived from linux + * + */ +#ifndef _USB_DEFS_H_ +#define _USB_DEFS_H_ + +/* USB constants */ + +/* Device and/or Interface Class codes */ +#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ +#define USB_CLASS_AUDIO 1 +#define USB_CLASS_COMM 2 +#define USB_CLASS_HID 3 +#define USB_CLASS_PRINTER 7 +#define USB_CLASS_MASS_STORAGE 8 +#define USB_CLASS_HUB 9 +#define USB_CLASS_DATA 10 +#define USB_CLASS_VENDOR_SPEC 0xff + +/* some HID sub classes */ +#define USB_SUB_HID_NONE 0 +#define USB_SUB_HID_BOOT 1 + +/* some UID Protocols */ +#define USB_PROT_HID_NONE 0 +#define USB_PROT_HID_KEYBOARD 1 +#define USB_PROT_HID_MOUSE 2 + + +/* Sub STORAGE Classes */ +#define US_SC_RBC 1 /* Typically, flash devices */ +#define US_SC_8020 2 /* CD-ROM */ +#define US_SC_QIC 3 /* QIC-157 Tapes */ +#define US_SC_UFI 4 /* Floppy */ +#define US_SC_8070 5 /* Removable media */ +#define US_SC_SCSI 6 /* Transparent */ +#define US_SC_MIN US_SC_RBC +#define US_SC_MAX US_SC_SCSI + +/* STORAGE Protocols */ +#define US_PR_CB 1 /* Control/Bulk w/o interrupt */ +#define US_PR_CBI 0 /* Control/Bulk/Interrupt */ +#define US_PR_BULK 0x50 /* bulk only */ + +/* USB types */ +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) + +/* USB recipients */ +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 + +/* USB directions */ +#define USB_DIR_OUT 0 +#define USB_DIR_IN 0x80 + +/* USB device speeds */ +#define USB_SPEED_FULL 0x0 /* 12Mbps */ +#define USB_SPEED_LOW 0x1 /* 1.5Mbps */ +#define USB_SPEED_HIGH 0x2 /* 480Mbps */ +#define USB_SPEED_RESERVED 0x3 + +/* Descriptor types */ +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 + +#define USB_DT_HID (USB_TYPE_CLASS | 0x01) +#define USB_DT_REPORT (USB_TYPE_CLASS | 0x02) +#define USB_DT_PHYSICAL (USB_TYPE_CLASS | 0x03) +#define USB_DT_HUB (USB_TYPE_CLASS | 0x09) + +/* Descriptor sizes per descriptor type */ +#define USB_DT_DEVICE_SIZE 18 +#define USB_DT_CONFIG_SIZE 9 +#define USB_DT_INTERFACE_SIZE 9 +#define USB_DT_ENDPOINT_SIZE 7 +#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define USB_DT_HUB_NONVAR_SIZE 7 +#define USB_DT_HID_SIZE 9 + +/* Endpoints */ +#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */ +#define USB_ENDPOINT_DIR_MASK 0x80 + +#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */ +#define USB_ENDPOINT_XFER_CONTROL 0 +#define USB_ENDPOINT_XFER_ISOC 1 +#define USB_ENDPOINT_XFER_BULK 2 +#define USB_ENDPOINT_XFER_INT 3 + +/* USB Packet IDs (PIDs) */ +#define USB_PID_UNDEF_0 0xf0 +#define USB_PID_OUT 0xe1 +#define USB_PID_ACK 0xd2 +#define USB_PID_DATA0 0xc3 +#define USB_PID_UNDEF_4 0xb4 +#define USB_PID_SOF 0xa5 +#define USB_PID_UNDEF_6 0x96 +#define USB_PID_UNDEF_7 0x87 +#define USB_PID_UNDEF_8 0x78 +#define USB_PID_IN 0x69 +#define USB_PID_NAK 0x5a +#define USB_PID_DATA1 0x4b +#define USB_PID_PREAMBLE 0x3c +#define USB_PID_SETUP 0x2d +#define USB_PID_STALL 0x1e +#define USB_PID_UNDEF_F 0x0f + +/* Standard requests */ +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +/* HID requests */ +#define USB_REQ_GET_REPORT 0x01 +#define USB_REQ_GET_IDLE 0x02 +#define USB_REQ_GET_PROTOCOL 0x03 +#define USB_REQ_SET_REPORT 0x09 +#define USB_REQ_SET_IDLE 0x0A +#define USB_REQ_SET_PROTOCOL 0x0B + + +/* "pipe" definitions */ + +#define PIPE_ISOCHRONOUS 0 +#define PIPE_INTERRUPT 1 +#define PIPE_CONTROL 2 +#define PIPE_BULK 3 +#define PIPE_DEVEP_MASK 0x0007ff00 + +#define USB_ISOCHRONOUS 0 +#define USB_INTERRUPT 1 +#define USB_CONTROL 2 +#define USB_BULK 3 + +/* USB-status codes: */ +#define USB_ST_ACTIVE 0x1 /* TD is active */ +#define USB_ST_STALLED 0x2 /* TD is stalled */ +#define USB_ST_BUF_ERR 0x4 /* buffer error */ +#define USB_ST_BABBLE_DET 0x8 /* Babble detected */ +#define USB_ST_NAK_REC 0x10 /* NAK Received*/ +#define USB_ST_CRC_ERR 0x20 /* CRC/timeout Error */ +#define USB_ST_BIT_ERR 0x40 /* Bitstuff error */ +#define USB_ST_NOT_PROC 0x80000000L /* Not yet processed */ + + +/************************************************************************* + * Hub defines + */ + +/* + * Hub request types + */ + +#define USB_RT_HUB (USB_TYPE_CLASS | USB_RECIP_DEVICE) +#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER) + +/* + * Hub Class feature numbers + */ +#define C_HUB_LOCAL_POWER 0 +#define C_HUB_OVER_CURRENT 1 + +/* + * Port feature numbers + */ +#define USB_PORT_FEAT_CONNECTION 0 +#define USB_PORT_FEAT_ENABLE 1 +#define USB_PORT_FEAT_SUSPEND 2 +#define USB_PORT_FEAT_OVER_CURRENT 3 +#define USB_PORT_FEAT_RESET 4 +#define USB_PORT_FEAT_POWER 8 +#define USB_PORT_FEAT_LOWSPEED 9 +#define USB_PORT_FEAT_HIGHSPEED 10 +#define USB_PORT_FEAT_C_CONNECTION 16 +#define USB_PORT_FEAT_C_ENABLE 17 +#define USB_PORT_FEAT_C_SUSPEND 18 +#define USB_PORT_FEAT_C_OVER_CURRENT 19 +#define USB_PORT_FEAT_C_RESET 20 + +/* wPortStatus bits */ +#define USB_PORT_STAT_CONNECTION 0x0001 +#define USB_PORT_STAT_ENABLE 0x0002 +#define USB_PORT_STAT_SUSPEND 0x0004 +#define USB_PORT_STAT_OVERCURRENT 0x0008 +#define USB_PORT_STAT_RESET 0x0010 +#define USB_PORT_STAT_POWER 0x0100 +#define USB_PORT_STAT_LOW_SPEED 0x0200 +#define USB_PORT_STAT_HIGH_SPEED 0x0400 /* support for EHCI */ +#define USB_PORT_STAT_SPEED \ + (USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED) + +/* wPortChange bits */ +#define USB_PORT_STAT_C_CONNECTION 0x0001 +#define USB_PORT_STAT_C_ENABLE 0x0002 +#define USB_PORT_STAT_C_SUSPEND 0x0004 +#define USB_PORT_STAT_C_OVERCURRENT 0x0008 +#define USB_PORT_STAT_C_RESET 0x0010 + +/* wHubCharacteristics (masks) */ +#define HUB_CHAR_LPSM 0x0003 +#define HUB_CHAR_COMPOUND 0x0004 +#define HUB_CHAR_OCPM 0x0018 + +/* + *Hub Status & Hub Change bit masks + */ +#define HUB_STATUS_LOCAL_POWER 0x0001 +#define HUB_STATUS_OVERCURRENT 0x0002 + +#define HUB_CHANGE_LOCAL_POWER 0x0001 +#define HUB_CHANGE_OVERCURRENT 0x0002 + +#endif /*_USB_DEFS_H_ */ diff --git a/BaS_gcc/sources/ehci-hcd.c b/BaS_gcc/sources/ehci-hcd.c new file mode 100644 index 0000000..b35ebbd --- /dev/null +++ b/BaS_gcc/sources/ehci-hcd.c @@ -0,0 +1,1272 @@ +/*- + * Copyright (c) 2007-2008, Juniper Networks, Inc. + * Copyright (c) 2008, Excito Elektronik i Skåne AB + * Copyright (c) 2008, Michael Trimarchi + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef CONFIG_USB_EHCI + +#include "usb.h" +#include "ehci.h" + +#if (defined(COLDFIRE) && defined(LWIP)) || defined (FREERTOS) +#include "../freertos/FreeRTOS.h" +#include "../freertos/queue.h" +extern xQueueHandle queue_poll_hub; +#endif + +#undef DEBUG +#undef SHOW_INFO + +char ehci_inited; +static int rootdev; + +static uint16_t portreset, companion; + +struct descriptor { + struct usb_hub_descriptor hub; + struct usb_device_descriptor device; + struct usb_linux_config_descriptor config; + struct usb_linux_interface_descriptor interface; + struct usb_endpoint_descriptor endpoint; +} __attribute__ ((packed)); + +static struct descriptor rom_descriptor = { + { + 0x8, /* bDescLength */ + 0x29, /* bDescriptorType: hub descriptor */ + 2, /* bNrPorts -- runtime modified */ + 0, /* wHubCharacteristics */ + 0xff, /* bPwrOn2PwrGood */ + 0, /* bHubCntrCurrent */ + {}, /* Device removable */ + {} /* at most 7 ports! XXX */ + }, + { + 0x12, /* bLength */ + 1, /* bDescriptorType: UDESC_DEVICE */ + 0x0002, /* bcdUSB: v2.0 */ + 9, /* bDeviceClass: UDCLASS_HUB */ + 0, /* bDeviceSubClass: UDSUBCLASS_HUB */ + 1, /* bDeviceProtocol: UDPROTO_HSHUBSTT */ + 64, /* bMaxPacketSize: 64 bytes */ + 0x0000, /* idVendor */ + 0x0000, /* idProduct */ + 0x0001, /* bcdDevice */ + 1, /* iManufacturer */ + 2, /* iProduct */ + 0, /* iSerialNumber */ + 1 /* bNumConfigurations: 1 */ + }, + { + 0x9, + 2, /* bDescriptorType: UDESC_CONFIG */ + (0x19 << 8), /* cpu_to_le16(0x19), */ + 1, /* bNumInterface */ + 1, /* bConfigurationValue */ + 0, /* iConfiguration */ + 0x40, /* bmAttributes: UC_SELF_POWER */ + 0 /* bMaxPower */ + }, + { + 0x9, /* bLength */ + 4, /* bDescriptorType: UDESC_INTERFACE */ + 0, /* bInterfaceNumber */ + 0, /* bAlternateSetting */ + 1, /* bNumEndpoints */ + 9, /* bInterfaceClass: UICLASS_HUB */ + 0, /* bInterfaceSubClass: UISUBCLASS_HUB */ + 0, /* bInterfaceProtocol: UIPROTO_HSHUBSTT */ + 0 /* iInterface */ + }, + { + 0x7, /* bLength */ + 5, /* bDescriptorType: UDESC_ENDPOINT */ + 0x81, /* bEndpointAddress: UE_DIR_IN | EHCI_INTR_ENDPT */ + 3, /* bmAttributes: UE_INTERRUPT */ + 8, 0, /* wMaxPacketSize */ + 255 /* bInterval */ + }, +}; + +#if defined(CONFIG_EHCI_IS_TDI) +#define ehci_is_TDI() (1) +#else +#define ehci_is_TDI() (0) +#endif + +struct pci_device_id ehci_usb_pci_table[] = { + { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_USB_2, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_SERIAL_USB_EHCI, 0, 0 }, /* NEC PCI OHCI module ids */ + { PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_ISP1561_2, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_SERIAL_USB_EHCI, 0, 0 }, /* Philips 1561 PCI OHCI module ids */ + /* Please add supported PCI OHCI controller ids here */ + { 0, 0, 0, 0, 0, 0, 0 } +}; + +static struct ehci { + /* ------- common part -------- */ + long handle; /* PCI BIOS */ + const struct pci_device_id *ent; + int usbnum; + /* ---- end of common part ---- */ + int big_endian; /* PCI BIOS */ + struct ehci_hccr *hccr; /* R/O registers, not need for volatile */ + volatile struct ehci_hcor *hcor; + struct QH *qh_list_unaligned; + struct QH *qh_list; + struct QH *qh_unaligned; + struct QH *qh; + struct qTD *td_unaligned[3]; + struct qTD *td[3]; + struct descriptor *descriptor; + int irq; + unsigned long dma_offset; + const char *slot_name; +#ifndef COLDFIRE + /* CTPCI anti-freeze */ + long (*ctpci_dma_lock)(long mode); +#endif +} gehci; + +#ifdef DEBUG +#define debug(format, arg...) board_printf("DEBUG: " format, ## arg) +#else +#define debug(format, arg...) do {} while (0) +#endif /* DEBUG */ +#define err usb_error_msg +#ifdef SHOW_INFO +#define info(format, arg...) board_printf("INFO: " format "\r\n", ## arg) +#else +#define info(format, arg...) do {} while (0) +#endif + +#ifdef COLDFIRE + +static inline void flush_dcache_range(void *begin, void *end) +{ +#ifndef CONFIG_USB_MEM_NO_CACHE +#ifdef LWIP + extern unsigned long pxCurrentTCB, tid_TOS; + extern void flush_dc(void); + if(begin); + if(end); + if(pxCurrentTCB != tid_TOS) + flush_dc(); + else +#endif /* LWIP */ +#if (__GNUC__ > 3) + asm volatile (" .chip 68060\n\t cpusha DC\n\t .chip 5485\n\t"); /* from CF68KLIB */ +#else + asm volatile (" .chip 68060\n\t cpusha DC\n\t .chip 5200\n\t"); /* from CF68KLIB */ +#endif +#endif /* CONFIG_USB_MEM_NO_CACHE */ +} +#define invalidate_dcache_range flush_dcache_range + +#else /* !COLDFIRE */ + +extern void cpush_dc(void *base, long size); +#define flush_dcache_range(begin, end) cpush_dc((void *)begin, (long)(end-begin)) +#define invalidate_dcache_range flush_dcache_range +#endif /* COLDFIRE */ + +extern void udelay(long usec); +extern void ltoa(char *buf, long n, unsigned long base); + +#if defined(CONFIG_EHCI_DCACHE) +/* + * Routines to handle (flush/invalidate) the dcache for the QH and qTD + * structures and data buffers. This is needed on platforms using this + * EHCI support with dcache enabled. + */ +static void flush_invalidate(u32 addr, int size, int flush) +{ +#ifndef COLDFIRE + if(addr >= *ramtop) /* memory above ramtop is uncached memory */ + return; +#endif + if(flush) + flush_dcache_range(addr, addr + size); + else + invalidate_dcache_range(addr, addr + size); +} + +static void cache_qtd(struct qTD *qtd, int flush) +{ + u32 *ptr = (u32 *)hc32_to_cpu(qtd->qt_buffer[0]); + int len = (qtd->qt_token & 0x7fff0000) >> 16; + flush_invalidate((u32)qtd, sizeof(struct qTD), flush); + if((ptr != NULL) && len) + { + ptr += gehci.dma_offset; + flush_invalidate((u32)ptr, len, flush); + } +} + +static inline struct QH *qh_addr(struct QH *qh) +{ + return (struct QH *)((u32)qh & 0xffffffe0); +} + +static void cache_qh(struct QH *qh, int flush) +{ + struct qTD *qtd; + struct qTD *next; + static struct qTD *first_qtd; + /* Walk the QH list and flush/invalidate all entries */ + while(1) + { + flush_invalidate((u32)qh_addr(qh), sizeof(struct QH), flush); + if((u32)qh & QH_LINK_TYPE_QH) + break; + qh = qh_addr(qh); + qh = (struct QH *)(hc32_to_cpu(qh->qh_link) + gehci.dma_offset); + } + qh = qh_addr(qh); + /* Save first qTD pointer, needed for invalidating pass on this QH */ + if(flush) + { + qtd = (struct qTD *)(hc32_to_cpu(*(u32 *)&qh->qh_overlay) & 0xffffffe0); + if(qtd != NULL) + qtd = (struct qTD *)(gehci.dma_offset + (u32)qtd); + first_qtd = qtd; + } + else + qtd = first_qtd; + /* Walk the qTD list and flush/invalidate all entries */ + while(1) + { + if(qtd == NULL) + break; + cache_qtd(qtd, flush); + next = (struct qTD *)((u32)hc32_to_cpu(qtd->qt_next) & 0xffffffe0); + if(next != NULL) + next = (struct qTD *)(gehci.dma_offset + (u32)next); + if(next == qtd) + break; + qtd = next; + } +} + +static inline void ehci_flush_dcache(struct QH *qh) +{ + cache_qh(qh, 1); +} + +static inline void ehci_invalidate_dcache(struct QH *qh) +{ + cache_qh(qh, 0); +} + +#else /* !CONFIG_EHCI_DCACHE */ + +static inline void ehci_flush_dcache(struct QH *qh) +{ + /* not need to flush cache with STRAM in writethough */ +} + +static inline void ehci_invalidate_dcache(struct QH *qh) +{ +#ifdef COLDFIRE /* no bus snooping on Coldfire */ +#ifdef LWIP + extern unsigned long pxCurrentTCB, tid_TOS; + extern void flush_dc(void); + if(pxCurrentTCB != tid_TOS) + flush_dc(); + else +#endif /* LWIP */ +#if (__GNUC__ > 3) + asm volatile (" .chip 68060\n\t cpusha DC\n\t .chip 5485\n\t"); /* from CF68KLIB */ +#else + asm volatile (" .chip 68060\n\t cpusha DC\n\t .chip 5200\n\t"); /* from CF68KLIB */ +#endif +#endif /* COLDFIRE */ +} + +#endif /* CONFIG_EHCI_DCACHE */ + +static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec) +{ + uint32_t result; +#ifndef COLDFIRE + extern void mdelay(long msec); + if(gehci.ctpci_dma_lock == NULL) + { + mdelay(10); + usec -= 10000; /* try to fix CTPCI freezes */ + } +#endif + do + { +#ifndef COLDFIRE + if(gehci.ctpci_dma_lock != NULL) + { + int i = 0; + while((i <= 10000) && gehci.ctpci_dma_lock(1)) + { + udelay(1); /* try to fix CTPCI freezes */ + i++; + } + if(i > 10000) + err("EHCI fail to lock DMA"); + } +#endif + result = ehci_readl(ptr); +#ifndef COLDFIRE + if(gehci.ctpci_dma_lock != NULL) + gehci.ctpci_dma_lock(0); +#endif + if(result == ~(uint32_t)0) + return -1; + result &= mask; + if(result == done) + return 0; +#ifdef COLDFIRE + udelay(1); + usec--; +#else /* !COLDFIRE */ + if(gehci.ctpci_dma_lock != NULL) + { + udelay(10); + usec -= 10; + } + else + { + mdelay(10); + usec -= 10000; /* try to fix CTPCI freezes */ + } +#endif /* COLDFIRE */ + } + while(usec > 0); + return -1; +} + +static void ehci_free(void *p, size_t sz) +{ +} + +static int ehci_reset(void) +{ + uint32_t cmd; + uint32_t tmp; + uint32_t *reg_ptr; + int ret = 0; +#ifdef MCF547X + if((gehci.ent->vendor == PCI_VENDOR_ID_NEC) + && (gehci.ent->device == PCI_DEVICE_ID_NEC_USB_2)) + { + debug("ehci_reset set 48MHz clock\r\n"); +#ifdef PCI_XBIOS + write_config_longword(gehci.handle, 0xE4, 0x20); // oscillator +#else + Write_config_longword(gehci.handle, 0xE4, 0x20); // oscillator +#endif + } +#endif /* MCF547X */ +#if !defined(COLDFIRE) && defined(CONFIG_USB_OHCI) && !defined(CONFIG_USB_INTERRUPT_POLLING) + { /* if driver started without PCI reset => disable OHCI interrupts */ +#define OHCI_INTRDISABLE 0x14 +#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ + short index = 0; + long handle; + do + { +#ifdef PCI_XBIOS + handle = find_pci_device(0x0000FFFFL, index++); +#else + handle = Find_pci_device(0x0000FFFFL, index++); +#endif + if((handle >= 0) && ((gehci.handle & 0xFFFF) == (handle & 0xFFFF))) + { + unsigned long class; +#ifdef PCI_XBIOS + long error = read_config_longword(handle, PCIREV, &class); +#else + long error = Read_config_longword(handle, PCIREV, &class); +#endif + if((error >= 0) && ((class >> 16) == PCI_CLASS_SERIAL_USB) && ((class >> 8) == PCI_CLASS_SERIAL_USB_OHCI)) + { + unsigned long usb_base_addr = 0xFFFFFFFF; + PCI_RSC_DESC *pci_rsc_desc; +#ifdef PCI_XBIOS + pci_rsc_desc = (PCI_RSC_DESC *)get_resource(handle); /* USB OHCI */ +#else + pci_rsc_desc = (PCI_RSC_DESC *)Get_resource(handle); /* USB OHCI */ +#endif + if((long)pci_rsc_desc >= 0) + { + unsigned short flags; + do + { + if(!(pci_rsc_desc->flags & FLG_IO)) + { + if(usb_base_addr == 0xFFFFFFFF) + { + unsigned long base = pci_rsc_desc->offset + pci_rsc_desc->start; + usb_base_addr = pci_rsc_desc->start; + ehci_writel(base + OHCI_INTRDISABLE, OHCI_INTR_MIE); + } + } + flags = pci_rsc_desc->flags; + pci_rsc_desc = (PCI_RSC_DESC *)((unsigned long)pci_rsc_desc->next + (unsigned long)pci_rsc_desc); + } + while(!(flags & FLG_LAST)); + } + } + } + } + while(handle >= 0); + } +#endif /* !defined(COLDFIRE) && defined(CONFIG_USB_OHCI) && !defined(CONFIG_USB_INTERRUPT_POLLING) */ + cmd = ehci_readl(&gehci.hcor->or_usbcmd); + cmd |= CMD_RESET; + ehci_writel(&gehci.hcor->or_usbcmd, cmd); + ret = handshake((uint32_t *)&gehci.hcor->or_usbcmd, CMD_RESET, 0, 250 * 1000); + if(ret < 0) + { + err("EHCI fail to reset"); + goto out; + } + if(ehci_is_TDI()) + { + reg_ptr = (uint32_t *)((u8 *)gehci.hcor + USBMODE); + tmp = ehci_readl(reg_ptr); + tmp |= USBMODE_CM_HC; +#if defined(CONFIG_EHCI_MMIO_BIG_ENDIAN) + tmp |= USBMODE_BE; +#endif + ehci_writel(reg_ptr, tmp); + } +out: + return ret; +} + +static void *ehci_alloc(size_t sz, size_t align) +{ + static int ntds; + void *p; + switch(sz) + { + case sizeof(struct QH): + p = gehci.qh; + ntds = 0; + break; + case sizeof(struct qTD): + if(ntds == 3) + { + debug("out of TDs\r\n"); + return NULL; + } + p = gehci.td[ntds]; + ntds++; + break; + default: + debug("unknown allocation size\r\n"); + return NULL; + } + memset(p, sz, 0); + return p; +} + +static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz) +{ + uint32_t addr, delta, next; + int idx; + addr = (uint32_t)buf; + idx = 0; + while(idx < 5) + { + td->qt_buffer[idx] = cpu_to_hc32(addr - gehci.dma_offset); + next = (addr + 4096) & ~4095; + delta = next - addr; + if(delta >= sz) + break; + sz -= delta; + addr = next; + idx++; + } + if(idx == 5) + { + debug("out of buffer pointers (%u bytes left)\r\n", sz); + return -1; + } + return 0; +} + +static int ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, int length, struct devrequest *req) +{ + struct QH *qh; + struct qTD *td; + volatile struct qTD *vtd; + unsigned long ts; + uint32_t *tdp; + uint32_t endpt, token, usbsts; + uint32_t c, toggle; + uint32_t cmd; + int ret = 0; + debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\r\n", dev, pipe, buffer, length, req); + if(req != NULL) + debug("ehci_submit_async req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u\r\n", + req->request, req->request, req->requesttype, req->requesttype, le16_to_cpu(req->value), le16_to_cpu(req->value), le16_to_cpu(req->index)); + qh = ehci_alloc(sizeof(struct QH), 32); + if(qh == NULL) + { + debug("unable to allocate QH\r\n"); + return -1; + } + qh->qh_link = cpu_to_hc32(((uint32_t)gehci.qh_list - gehci.dma_offset) | QH_LINK_TYPE_QH); + c = (usb_pipespeed(pipe) != USB_SPEED_HIGH && usb_pipeendpoint(pipe) == 0) ? 1 : 0; + endpt = (8 << 28) | (c << 27) | (usb_maxpacket(dev, pipe) << 16) | (0 << 15) | (1 << 14) | (usb_pipespeed(pipe) << 12) | (usb_pipeendpoint(pipe) << 8) | (0 << 7) | (usb_pipedevice(pipe) << 0); + qh->qh_endpt1 = cpu_to_hc32(endpt); + endpt = (1 << 30) | (dev->portnr << 23) | (dev->parent->devnum << 16) | (0 << 8) | (0 << 0); + qh->qh_endpt2 = cpu_to_hc32(endpt); + qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + qh->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + td = NULL; + tdp = &qh->qh_overlay.qt_next; + toggle = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); + if(req != NULL) + { + td = ehci_alloc(sizeof(struct qTD), 32); + if(td == NULL) + { + debug("unable to allocate SETUP td\r\n"); + goto fail; + } + td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + token = (0 << 31) | (sizeof(*req) << 16) | (0 << 15) | (0 << 12) | (3 << 10) | (2 << 8) | (0x80 << 0); + td->qt_token = cpu_to_hc32(token); + if(ehci_td_buffer(td, req, sizeof(*req)) != 0) + { + debug("unable construct SETUP td\r\n"); + ehci_free(td, sizeof(*td)); + goto fail; + } + *tdp = cpu_to_hc32((uint32_t)td - gehci.dma_offset); + tdp = &td->qt_next; + toggle = 1; + } + if(length > 0 || req == NULL) + { + td = ehci_alloc(sizeof(struct qTD), 32); + if(td == NULL) + { + debug("unable to allocate DATA td\r\n"); + goto fail; + } + td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + token = (toggle << 31) | (length << 16) | ((req == NULL ? 1 : 0) << 15) | (0 << 12) | (3 << 10) | ((usb_pipein(pipe) ? 1 : 0) << 8) | (0x80 << 0); + td->qt_token = cpu_to_hc32(token); + if(ehci_td_buffer(td, buffer, length) != 0) + { + debug("unable construct DATA td\r\n"); + ehci_free(td, sizeof(*td)); + goto fail; + } + *tdp = cpu_to_hc32((uint32_t)td - gehci.dma_offset); + tdp = &td->qt_next; + } + if(req != NULL) + { + td = ehci_alloc(sizeof(struct qTD), 32); + if(td == NULL) + { + debug("unable to allocate ACK td\r\n"); + goto fail; + } + td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + token = (toggle << 31) | (0 << 16) | (1 << 15) | (0 << 12) | (3 << 10) | ((usb_pipein(pipe) ? 0 : 1) << 8) | (0x80 << 0); + td->qt_token = cpu_to_hc32(token); + *tdp = cpu_to_hc32((uint32_t)td - gehci.dma_offset); + tdp = &td->qt_next; + } + gehci.qh_list->qh_link = cpu_to_hc32(((uint32_t)qh - gehci.dma_offset) | QH_LINK_TYPE_QH); + /* Flush dcache */ + ehci_flush_dcache(gehci.qh_list); + usbsts = ehci_readl(&gehci.hcor->or_usbsts); + ehci_writel(&gehci.hcor->or_usbsts, (usbsts & 0x3f)); + /* Enable async. schedule. */ + cmd = ehci_readl(&gehci.hcor->or_usbcmd); + cmd |= CMD_ASE; + ehci_writel(&gehci.hcor->or_usbcmd, cmd); + ret = handshake((uint32_t *)&gehci.hcor->or_usbsts, STD_ASS, STD_ASS, 100 * 1000); + if(ret < 0) + { + err("EHCI fail timeout STD_ASS set (usbsts=%#x)", ehci_readl(&gehci.hcor->or_usbsts)); + goto fail; + } + /* Wait for TDs to be processed. */ + ts = 0; + vtd = td; + do + { + /* Invalidate dcache */ + ehci_invalidate_dcache(gehci.qh_list); + token = hc32_to_cpu(vtd->qt_token); + if(!(token & 0x80)) + break; + wait_ms(1); + ts++; + } + while(ts < 1000); + /* Disable async schedule. */ + cmd = ehci_readl(&gehci.hcor->or_usbcmd); + cmd &= ~CMD_ASE; + ehci_writel(&gehci.hcor->or_usbcmd, cmd); + ret = handshake((uint32_t *)&gehci.hcor->or_usbsts, STD_ASS, 0, 100 * 1000); + if(ret < 0) + { + err("EHCI fail timeout STD_ASS reset (usbsts=%#x)", ehci_readl(&gehci.hcor->or_usbsts)); + goto fail; + } + gehci.qh_list->qh_link = cpu_to_hc32(((uint32_t)gehci.qh_list - gehci.dma_offset) | QH_LINK_TYPE_QH); + token = hc32_to_cpu(qh->qh_overlay.qt_token); + if(!(token & 0x80)) + { + debug("TOKEN=%#x\r\n", token); + switch(token & 0xfc) + { + case 0: + toggle = token >> 31; + usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), toggle); + dev->status = 0; + break; + case 0x40: + dev->status = USB_ST_STALLED; + break; + case 0xa0: + case 0x20: + dev->status = USB_ST_BUF_ERR; + break; + case 0x50: + case 0x10: + dev->status = USB_ST_BABBLE_DET; + break; + default: + dev->status = USB_ST_CRC_ERR; + break; + } + dev->act_len = length - ((token >> 16) & 0x7fff); + } + else + { + dev->act_len = 0; + debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\r\n", + dev->devnum, ehci_readl(&gehci.hcor->or_usbsts), ehci_readl(&gehci.hcor->or_portsc[0]), ehci_readl(&gehci.hcor->or_portsc[1])); + } + return (dev->status != USB_ST_NOT_PROC) ? 0 : -1; +fail: + td = (void *)hc32_to_cpu(qh->qh_overlay.qt_next); + if(td != (void *)QT_NEXT_TERMINATE) + td = (struct qTD *)(gehci.dma_offset + (u32)td); + while(td != (void *)QT_NEXT_TERMINATE) + { + qh->qh_overlay.qt_next = td->qt_next; + ehci_free(td, sizeof(*td)); + td = (void *)hc32_to_cpu(qh->qh_overlay.qt_next); + if(td != (void *)QT_NEXT_TERMINATE) + td = (struct qTD *)(gehci.dma_offset + (u32)td); + } + ehci_free(qh, sizeof(*qh)); + if(ehci_readl(&gehci.hcor->or_usbsts) & STS_HSE) /* Host System Error */ + { +#ifdef PCI_XBIOS + unsigned short status = fast_read_config_word(gehci.handle, PCISR); +#else + unsigned short status = Fast_read_config_word(gehci.handle, PCISR); +#endif + err("EHCI Host System Error, controller usb-%s disabled\r\n(SR:0x%04X%s%s%s%s%s%s)", gehci.slot_name, status & 0xFFFF, + status & 0x8000 ? ", Parity error" : "", status & 0x4000 ? ", Signaled system error" : "", status & 0x2000 ? ", Received master abort" : "", + status & 0x1000 ? ", Received target abort" : "", status & 0x800 ? ", Signaled target abort" : "", status & 0x100 ? ", Data parity error" : ""); + } + return -1; +} + +static inline int min3(int a, int b, int c) +{ + if(b < a) + a = b; + if(c < a) + a = c; + return a; +} + +static int ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, int length, struct devrequest *req) +{ + uint8_t tmpbuf[4]; + u16 typeReq; + void *srcptr = NULL; + int len, srclen; + uint32_t reg; + uint32_t *status_reg; + if(le16_to_cpu(req->index) > CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) + { + err("The request port(%d) is not configured", le16_to_cpu(req->index) - 1); + return -1; + } + status_reg = (uint32_t *)&gehci.hcor->or_portsc[le16_to_cpu(req->index) - 1]; + srclen = 0; + debug("ehci_submit_root req=%u (%#x), type=%u (%#x), value=%u, index=%u\r\n", + req->request, req->request, req->requesttype, req->requesttype, le16_to_cpu(req->value), le16_to_cpu(req->index)); + typeReq = req->request | req->requesttype << 8; + switch(typeReq) + { + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + switch(le16_to_cpu(req->value) >> 8) + { + case USB_DT_DEVICE: + debug("USB_DT_DEVICE request\r\n"); + srcptr = &gehci.descriptor->device; + srclen = 0x12; + break; + case USB_DT_CONFIG: + debug("USB_DT_CONFIG config\r\n"); + srcptr = &gehci.descriptor->config; + srclen = 0x19; + break; + case USB_DT_STRING: + debug("USB_DT_STRING config\r\n"); + switch(le16_to_cpu(req->value) & 0xff) + { + case 0: /* Language */ + srcptr = "\4\3\1\0"; + srclen = 4; + break; + case 1: /* Vendor */ + srcptr = "\2\3"; + srclen = 2; + break; + case 2: /* Product */ + srcptr = "\34\3E\0H\0C\0I\0 \0R\0o\0o\0t\0 \0H\0u\0b\0"; + srclen = 28; + break; + default: + debug("unknown value DT_STRING %x\r\n", + le16_to_cpu(req->value)); + goto unknown; + } + break; + default: + debug("unknown value %x\r\n", le16_to_cpu(req->value)); + goto unknown; + } + break; + case USB_REQ_GET_DESCRIPTOR | ((USB_DIR_IN | USB_RT_HUB) << 8): + switch(le16_to_cpu(req->value) >> 8) + { + case USB_DT_HUB: + debug("USB_DT_HUB config\r\n"); + srcptr = &gehci.descriptor->hub; + srclen = 0x8; + break; + default: + debug("unknown value %x\r\n", le16_to_cpu(req->value)); + goto unknown; + } + break; + case USB_REQ_SET_ADDRESS | (USB_RECIP_DEVICE << 8): + debug("USB_REQ_SET_ADDRESS\r\n"); + rootdev = le16_to_cpu(req->value); + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + debug("USB_REQ_SET_CONFIGURATION\r\n"); + /* Nothing to do */ + break; + case USB_REQ_GET_STATUS | ((USB_DIR_IN | USB_RT_HUB) << 8): + tmpbuf[0] = 1; /* USB_STATUS_SELFPOWERED */ + tmpbuf[1] = 0; + srcptr = tmpbuf; + srclen = 2; + break; + case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): + memset(tmpbuf, 0, 4); + reg = ehci_readl(status_reg); + if((reg & EHCI_PS_PR) && (portreset & (1 << le16_to_cpu(req->index)))) + { + int ret; + /* force reset to complete */ + reg = reg & ~(EHCI_PS_PR | EHCI_PS_CLEAR); + ehci_writel(status_reg, reg); + ret = handshake(status_reg, EHCI_PS_PR, 0, 2 * 1000); + if(!ret) + { + tmpbuf[0] |= USB_PORT_STAT_RESET; + reg = ehci_readl(status_reg); + } + else + err("port(%d) reset error", le16_to_cpu(req->index) - 1); + } + if(reg & EHCI_PS_CS) + tmpbuf[0] |= USB_PORT_STAT_CONNECTION; + if(reg & EHCI_PS_PE) + tmpbuf[0] |= USB_PORT_STAT_ENABLE; + if(reg & EHCI_PS_SUSP) + tmpbuf[0] |= USB_PORT_STAT_SUSPEND; + if(reg & EHCI_PS_OCA) + tmpbuf[0] |= USB_PORT_STAT_OVERCURRENT; + if(reg & EHCI_PS_PP) + tmpbuf[1] |= USB_PORT_STAT_POWER >> 8; + if(ehci_is_TDI()) + { + switch((reg >> 26) & 3) + { + case 0: break; + case 1: tmpbuf[1] |= USB_PORT_STAT_LOW_SPEED >> 8; break; + case 2: + default: tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; break; + } + } + else + tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; + if(reg & EHCI_PS_CSC) + tmpbuf[2] |= USB_PORT_STAT_C_CONNECTION; + if(reg & EHCI_PS_PEC) + tmpbuf[2] |= USB_PORT_STAT_C_ENABLE; + if(reg & EHCI_PS_OCC) + tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT; + if(portreset & (1 << le16_to_cpu(req->index))) + tmpbuf[2] |= USB_PORT_STAT_C_RESET; + srcptr = tmpbuf; + srclen = 4; + break; + case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): + reg = ehci_readl(status_reg); + reg &= ~EHCI_PS_CLEAR; + switch(le16_to_cpu(req->value)) + { + case USB_PORT_FEAT_ENABLE: + reg |= EHCI_PS_PE; + ehci_writel(status_reg, reg); + break; + case USB_PORT_FEAT_POWER: + if(HCS_PPC(ehci_readl(&gehci.hccr->cr_hcsparams))) + { + reg |= EHCI_PS_PP; + ehci_writel(status_reg, reg); + } + break; + case USB_PORT_FEAT_RESET: + if((reg & (EHCI_PS_PE | EHCI_PS_CS)) == EHCI_PS_CS && !ehci_is_TDI() && EHCI_PS_IS_LOWSPEED(reg)) + { + /* Low speed device, give up ownership. */ + debug("port %d low speed --> companion\r\n", le16_to_cpu(req->index)); + reg |= EHCI_PS_PO; + ehci_writel(status_reg, reg); + companion |= (1 << le16_to_cpu(req->index)); + break; + } + else + { + reg |= EHCI_PS_PR; + reg &= ~EHCI_PS_PE; + ehci_writel(status_reg, reg); + /* + * caller must wait, then call GetPortStatus + * usb 2.0 specification say 50 ms resets on root + */ + wait_ms(50); + portreset |= (1 << le16_to_cpu(req->index)); + } + break; + default: + debug("unknown feature %x\r\n", le16_to_cpu(req->value)); + goto unknown; + } + /* unblock posted writes */ + (void)ehci_readl(&gehci.hcor->or_usbcmd); + break; + case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): + reg = ehci_readl(status_reg); + switch(le16_to_cpu(req->value)) + { + case USB_PORT_FEAT_ENABLE: + reg &= ~EHCI_PS_PE; + break; + case USB_PORT_FEAT_C_ENABLE: + reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_PE; + break; + case USB_PORT_FEAT_POWER: + if(HCS_PPC(ehci_readl(&gehci.hccr->cr_hcsparams))) + reg = reg & ~(EHCI_PS_CLEAR | EHCI_PS_PP); + case USB_PORT_FEAT_C_CONNECTION: + reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_CSC; + break; + case USB_PORT_FEAT_OVER_CURRENT: + reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_OCC; + break; + case USB_PORT_FEAT_C_RESET: + portreset &= ~(1 << le16_to_cpu(req->index)); + break; + default: + debug("unknown feature %x\r\n", le16_to_cpu(req->value)); + goto unknown; + } + ehci_writel(status_reg, reg); + /* unblock posted write */ + (void)ehci_readl(&gehci.hcor->or_usbcmd); + break; + default: + debug("Unknown request\r\n"); + goto unknown; + } + wait_ms(1); + len = min3(srclen, le16_to_cpu(req->length), length); + if(srcptr != NULL && len > 0) + memcpy(buffer, srcptr, len); + else + debug("Len is 0\r\n"); + dev->act_len = len; + dev->status = 0; + return 0; +unknown: + debug("requesttype=%x, request=%x, value=%x, index=%x, length=%x\r\n", + req->requesttype, req->request, le16_to_cpu(req->value), le16_to_cpu(req->index), le16_to_cpu(req->length)); + dev->act_len = 0; + dev->status = USB_ST_STALLED; + return -1; +} + +/* an interrupt happens */ +static int hc_interrupt(struct ehci *ehci) +{ + uint32_t status = ehci_readl(&ehci->hcor->or_usbsts); + if(status & STS_PCD) /* port change detect */ + { + uint32_t reg = ehci_readl(&ehci->hccr->cr_hcsparams); + uint32_t i = HCS_N_PORTS(reg); + while(i) + { + uint32_t pstatus = ehci_readl(&ehci->hcor->or_portsc[i-1]); + if(pstatus & EHCI_PS_PO) + { + i--; + continue; + } + if(companion & (1 << i)) + { + /* Low speed device, give up ownership. */ + pstatus |= EHCI_PS_PO; + ehci_writel(&ehci->hcor->or_portsc[i-1], pstatus); + } +#ifdef USB_POLL_HUB + else if((queue_poll_hub != NULL) && (pstatus & EHCI_PS_CSC)) + { + portBASE_TYPE xNeedSwitch = pdFALSE; + xNeedSwitch = xQueueSendFromISR(queue_poll_hub, &ehci->usbnum, xNeedSwitch); + } /* to fix xNeedSwitch usage */ +#endif + i--; + } + } + ehci_writel(&ehci->hcor->or_usbsts, status); + return(1); /* interrupt was from this card */ +} + +#ifdef CONFIG_USB_INTERRUPT_POLLING + +void ehci_usb_event_poll(int interrupt) +{ + if(interrupt); + if(ehci_inited && gehci.handle) + hc_interrupt(&gehci); +} + +#else /* !CONFIG_USB_INTERRUPT_POLLING */ + +void ehci_usb_enable_interrupt(int enable) +{ + if(enable); +} + +static int handle_usb_interrupt(struct ehci *ehci) +{ + return(hc_interrupt(ehci)); +} + +#endif /* CONFIG_USB_INTERRUPT_POLLING */ + +static void hc_free_buffers(struct ehci *ehci) +{ + int i; + if(ehci->descriptor != NULL) + { + usb_free(ehci->descriptor); + ehci->descriptor = NULL; + } + for(i = 0; i < 3; i++) + { + if(ehci->td_unaligned[i] != NULL) + { + usb_free(ehci->td_unaligned[i]); + ehci->td_unaligned[i] = NULL; + } + } + if(ehci->qh_unaligned != NULL) + { + usb_free(ehci->qh_unaligned); + ehci->qh_unaligned = NULL; + } + if(ehci->qh_list_unaligned != NULL) + { + usb_free(ehci->qh_list_unaligned); + ehci->qh_list_unaligned = NULL; + } +} + +int ehci_usb_lowlevel_init(long handle, const struct pci_device_id *ent, void **priv) +{ + int i; + uint32_t reg; + uint32_t cmd; +#ifndef COLDFIRE + uint32_t tmp; +#endif + unsigned long usb_base_addr = 0xFFFFFFFF; + PCI_RSC_DESC *pci_rsc_desc; +#ifdef PCI_XBIOS + pci_rsc_desc = (PCI_RSC_DESC *)get_resource(handle); /* USB EHCI */ +#else + USB_COOKIE *p = usb_get_cookie('_PCI'); + PCI_COOKIE *bios_cookie = (PCI_COOKIE *)p->v.l; + if(bios_cookie == NULL) /* faster than XBIOS calls */ + return(-1); + tab_funcs_pci = &bios_cookie->routine[0]; + pci_rsc_desc = (PCI_RSC_DESC *)Get_resource(handle); /* USB EHCI */ +#endif + if(handle && (ent != NULL)) + { + memset(&gehci, 0, sizeof(struct ehci)); + gehci.handle = handle; + gehci.ent = ent; + } + else if(!gehci.handle) /* for restart USB cmd */ + return(-1); + gehci.qh_list_unaligned = (struct QH *)usb_malloc(sizeof(struct QH) + 32); + if(gehci.qh_list_unaligned == NULL) + { + debug("QHs malloc failed"); + hc_free_buffers(&gehci); + return(-1); + } + gehci.qh_list = (struct QH *)(((unsigned long)gehci.qh_list_unaligned + 31) & ~31); + memset(gehci.qh_list, 0, sizeof(struct QH)); + gehci.qh_unaligned = (struct QH *)usb_malloc(sizeof(struct QH) + 32); + if(gehci.qh_unaligned == NULL) + { + debug("QHs malloc failed"); + hc_free_buffers(&gehci); + return(-1); + } + gehci.qh = (struct QH *)(((unsigned long)gehci.qh_unaligned + 31) & ~31); + memset(gehci.qh, 0, sizeof(struct QH)); + for(i = 0; i < 3; i++) + { + gehci.td_unaligned[i] = (struct qTD *)usb_malloc(sizeof(struct qTD) + 32); + if(gehci.td_unaligned[i] == NULL) + { + debug("TDs malloc failed"); + hc_free_buffers(&gehci); + return(-1); + } + gehci.td[i] = (struct qTD *)(((unsigned long)gehci.td_unaligned[i] + 31) & ~31); + memset(gehci.td[i], 0, sizeof(struct qTD)); + } + gehci.descriptor = (struct descriptor *)usb_malloc(sizeof(struct descriptor)); + if(gehci.descriptor == NULL) + { + debug("decriptor malloc failed"); + hc_free_buffers(&gehci); + return(-1); + } + memcpy(gehci.descriptor, &rom_descriptor, sizeof(struct descriptor)); + if((long)pci_rsc_desc >= 0) + { + unsigned short flags; + do + { + debug("PCI USB descriptors: flags 0x%04x start 0x%08lx \r\n offset 0x%08lx dmaoffset 0x%08lx length 0x%08lx", + pci_rsc_desc->flags, pci_rsc_desc->start, pci_rsc_desc->offset, pci_rsc_desc->dmaoffset, pci_rsc_desc->length); + if(!(pci_rsc_desc->flags & FLG_IO)) + { + if(usb_base_addr == 0xFFFFFFFF) + { + usb_base_addr = pci_rsc_desc->start; + gehci.hccr = (struct ehci_hccr *)(pci_rsc_desc->offset + pci_rsc_desc->start); + gehci.dma_offset = pci_rsc_desc->dmaoffset; + if((pci_rsc_desc->flags & FLG_ENDMASK) == ORD_MOTOROLA) + gehci.big_endian = 0; /* host bridge make swapping intel -> motorola */ + else + gehci.big_endian = 1; /* driver must swapping intel -> motorola */ + } + } + flags = pci_rsc_desc->flags; + pci_rsc_desc = (PCI_RSC_DESC *)((unsigned long)pci_rsc_desc->next + (unsigned long)pci_rsc_desc); + } + while(!(flags & FLG_LAST)); + } + else + { + hc_free_buffers(&gehci); + return(-1); /* get_resource error */ + } + if(usb_base_addr == 0xFFFFFFFF) + { + hc_free_buffers(&gehci); + return(-1); + } + if(handle && (ent != NULL)) + { + switch(ent->vendor) + { + case PCI_VENDOR_ID_NEC: gehci.slot_name = "uPD720101"; break; + case PCI_VENDOR_ID_PHILIPS: gehci.slot_name = "isp1561"; break; + default: gehci.slot_name = "generic"; break; + } + } + gehci.hcor = (struct ehci_hcor *)((uint32_t)gehci.hccr + HC_LENGTH(ehci_readl(&gehci.hccr->cr_capbase))); + kprint("EHCI usb-%s, regs address 0x%08X, PCI handle 0x%X\r\n", gehci.slot_name, gehci.hccr, handle); +#ifndef COLDFIRE + tmp = dma_lock(-1); /* CTPCI */ + if((tmp == 0) || (tmp == 1)) + gehci.ctpci_dma_lock = (void *)dma_lock(-2); /* function exist */ +#endif + /* EHCI spec section 4.1 */ + if(ehci_reset() != 0) + { + hc_free_buffers(&gehci); + return(-1); + } + /* Set head of reclaim list */ + gehci.qh_list->qh_link = cpu_to_hc32(((uint32_t)gehci.qh_list - gehci.dma_offset) | QH_LINK_TYPE_QH); + gehci.qh_list->qh_endpt1 = cpu_to_hc32((1 << 15) | (USB_SPEED_HIGH << 12)); + gehci.qh_list->qh_curtd = cpu_to_hc32(QT_NEXT_TERMINATE); + gehci.qh_list->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + gehci.qh_list->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + gehci.qh_list->qh_overlay.qt_token = cpu_to_hc32(0x40); + /* Set async. queue head pointer. */ + ehci_writel(&gehci.hcor->or_asynclistaddr, (uint32_t)gehci.qh_list - gehci.dma_offset); + reg = ehci_readl(&gehci.hccr->cr_hcsparams); + gehci.descriptor->hub.bNbrPorts = HCS_N_PORTS(reg); + info("Register %x NbrPorts %d", reg, gehci.descriptor->hub.bNbrPorts); + /* Port Indicators */ + if(HCS_INDICATOR(reg)) + gehci.descriptor->hub.wHubCharacteristics |= 0x80; + /* Port Power Control */ + if(HCS_PPC(reg)) + gehci.descriptor->hub.wHubCharacteristics |= 0x01; + /* Start the host controller. */ + cmd = ehci_readl(&gehci.hcor->or_usbcmd); + /* + * Philips, Intel, and maybe others need CMD_RUN before the + * root hub will detect new devices (why?); NEC doesn't + */ + cmd &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); + cmd |= CMD_RUN; + ehci_writel(&gehci.hcor->or_usbcmd, cmd); + /* take control over the ports */ + ehci_writel(&gehci.hcor->or_configflag, FLAG_CF); + /* unblock posted write */ + cmd = ehci_readl(&gehci.hcor->or_usbcmd); + wait_ms(5); + reg = HC_VERSION(ehci_readl(&gehci.hccr->cr_capbase)); + info("USB EHCI %x.%02x", reg >> 8, reg & 0xff); +#ifndef CONFIG_USB_INTERRUPT_POLLING + /* turn on interrupts */ +#ifdef PCI_XBIOS + hook_interrupt(handle, handle_usb_interrupt, &gehci); +#else + Hook_interrupt(handle, (void *)handle_usb_interrupt, (unsigned long *)&gehci); +#endif /* PCI_BIOS */ + ehci_writel(&gehci.hcor->or_usbintr, INTR_PCDE); +#endif /* CONFIG_USB_INTERRUPT_POLLING */ + rootdev = 0; + if(priv != NULL) + *priv = (void *)&gehci; + ehci_inited = 1; + return(0); +} + +int ehci_usb_lowlevel_stop(void *priv) +{ + uint32_t cmd; + if(priv); + if(!ehci_inited) + return(0); +#ifndef CONFIG_USB_INTERRUPT_POLLING + /* turn off interrupts */ + ehci_writel(&gehci.hcor->or_usbintr, 0); +#ifdef PCI_XBIOS + unhook_interrupt(gehci.handle); +#else + Unhook_interrupt(gehci.handle); +#endif /* PCI_BIOS */ +#endif /* CONFIG_USB_INTERRUPT_POLLING */ + /* stop the controller */ + cmd = ehci_readl(&gehci.hcor->or_usbcmd); + cmd &= ~CMD_RUN; + ehci_writel(&gehci.hcor->or_usbcmd, cmd); + /* turn off all ports => todo */ + /* use the companions */ + ehci_writel(&gehci.hcor->or_configflag, 0); + /* unblock posted write */ + cmd = ehci_readl(&gehci.hcor->or_usbcmd); + ehci_reset(); + hc_free_buffers(&gehci); + ehci_inited = 0; + return(0); +} + +int ehci_submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int length) +{ + if(usb_pipetype(pipe) != PIPE_BULK) + { + debug("non-bulk pipe (type=%lu)", usb_pipetype(pipe)); + return -1; + } + return ehci_submit_async(dev, pipe, buffer, length, NULL); +} + +int ehci_submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int length, struct devrequest *setup) +{ + if(usb_pipetype(pipe) != PIPE_CONTROL) + { + debug("non-control pipe (type=%lu)", usb_pipetype(pipe)); + return -1; + } + if(usb_pipedevice(pipe) == rootdev) + { + if(rootdev == 0) + dev->speed = USB_SPEED_HIGH; + return ehci_submit_root(dev, pipe, buffer, length, setup); + } + return ehci_submit_async(dev, pipe, buffer, length, setup); +} + +int ehci_submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int length, int interval) +{ + debug("submit_int_msg dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d", dev, pipe, buffer, length, interval); + return -1; +} + +#endif /* CONFIG_USB_EHCI */ diff --git a/BaS_gcc/sources/ohci-hcd.c b/BaS_gcc/sources/ohci-hcd.c new file mode 100644 index 0000000..798ec49 --- /dev/null +++ b/BaS_gcc/sources/ohci-hcd.c @@ -0,0 +1,2063 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB and PCI bus. + * + * Interrupt support is added. Now, it has been tested + * on ULI1575.cpu and works well with USB keyboard. + * + * (C) Copyright 2007 + * Zhang Wei, Freescale Semiconductor, Inc. + * + * (C) Copyright 2003 + * Gary Jennejohn, DENX Software Engineering + * + * Note: Much of this code has been derived from Linux 2.4 + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2002 David Brownell + * + * Modified for the MP2USB by (C) Copyright 2005 Eric Benard + * ebenard@eukrea.com - based on s3c24x0's driver + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ +/* + * IMPORTANT NOTE + * this driver is intended for use with USB Mass Storage Devices + * (BBB) and USB keyboard. There is NO support for Isochronous pipes! + */ + + +#include "usb.h" +#include "ohci.h" + +extern xQueueHandle queue_poll_hub; + + +#undef DEBUG_PCIE + +#undef OHCI_USE_NPS /* force NoPowerSwitching mode */ + +#undef OHCI_VERBOSE_DEBUG /* not always helpful */ +#undef DEBUG +#undef SHOW_INFO +#undef OHCI_FILL_TRACE + +/* For initializing controller (mask in an HCFS mode too) */ +#define OHCI_CONTROL_INIT (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE + +/* + * e.g. PCI controllers need this + */ +#ifdef CONFIG_SYS_OHCI_SWAP_REG_ACCESS +# define readl(a) __swap_32(*((volatile u32 *)(a))) +# define writel(a, b) (*((volatile u32 *)(b)) = __swap_32((volatile u32)a)) +#else +# define readl(a) (*((volatile u32 *)(a))) +# define writel(a, b) (*((volatile u32 *)(b)) = ((volatile u32)a)) +#endif /* CONFIG_SYS_OHCI_SWAP_REG_ACCESS */ + +#define min_t(type, x, y) ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) + +struct pci_device_id ohci_usb_pci_table[] = { + { PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5237, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_SERIAL_USB_OHCI, 0, 0 }, /* ULI1575 PCI OHCI module ids */ + { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_USB, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_SERIAL_USB_OHCI, 0, 0 }, /* NEC PCI OHCI module ids */ + { PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_ISP1561, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_SERIAL_USB_OHCI, 0, 0 }, /* Philips 1561 PCI OHCI module ids */ + /* Please add supported PCI OHCI controller ids here */ + { 0, 0, 0, 0, 0, 0, 0 } +}; + +#ifdef DEBUG +#define dbg(format, arg...) do board_printf("DEBUG: " format "\r\n", ## arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif /* DEBUG */ +#define err usb_error_msg +#ifdef SHOW_INFO +#define info(format, arg...) do board_printf("INFO: " format "\r\n", ## arg) +#else +#define info(format, arg...) do {} while (0) +#endif + +#define m16_swap(x) cpu_to_le16(x) +#define m32_swap(x) cpu_to_le32(x) + +extern void udelay(long usec); + +/* global ohci_t */ +static ohci_t gohci[2]; +char ohci_inited; + +static inline u32 roothub_a(ohci_t *ohci) { return readl(&ohci->regs->roothub.a); } +static inline u32 roothub_b(ohci_t *ohci) { return readl(&ohci->regs->roothub.b); } +static inline u32 roothub_status(ohci_t *ohci) { return readl(&ohci->regs->roothub.status); } +static inline u32 roothub_portstatus(ohci_t *ohci, int i) { return readl(&ohci->regs->roothub.portstatus[i]); } + +/* forward declaration */ +static void flush_data_cache(ohci_t *ohci); +static int hc_interrupt(ohci_t *ohci); +static void td_submit_job(ohci_t *ohci, struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval); + +/*-------------------------------------------------------------------------* + * URB support functions + *-------------------------------------------------------------------------*/ + +/* free HCD-private data associated with this URB */ + +static void urb_free_priv(urb_priv_t *urb) +{ + int i; + struct td *td; + int last = urb->length - 1; + if(last >= 0) + { + for(i = 0; i <= last; i++) + { + td = urb->td[i]; + if(td) + { + td->usb_dev = NULL; + urb->td[i] = NULL; + } + } + } + usb_free(urb); +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +static int sohci_get_current_frame_number(ohci_t *ohci, struct usb_device *dev); + +/* debug| print the main components of an URB + * small: 0) header + data packets 1) just header */ + +static void pkt_print(ohci_t *ohci, urb_priv_t *purb, struct usb_device *dev, + unsigned long pipe, void *buffer, int transfer_len, + struct devrequest *setup, char *str, int small) +{ + dbg("%s URB:[%4x] dev:%2lu,ep:%2lu-%c,type:%s,len:%d/%d stat:%#lx", + str, + sohci_get_current_frame_number(ohci, dev), + usb_pipedevice(pipe), + usb_pipeendpoint(pipe), + usb_pipeout(pipe)? 'O': 'I', + usb_pipetype(pipe) < 2 ? \ + (usb_pipeint(pipe)? "INTR": "ISOC"): \ + (usb_pipecontrol(pipe)? "CTRL": "BULK"), + (purb ? purb->actual_length : 0), + transfer_len, dev->status); +#ifdef OHCI_VERBOSE_DEBUG + if(!small) + { + int i, len; + if(usb_pipecontrol(pipe)) + { + board_printf(__FILE__ ": cmd(8):"); + for(i = 0; i < 8 ; i++) + board_printf(" %02x", ((__u8 *)setup)[i]); + board_printf("\r\n"); + } + if(transfer_len > 0 && buffer) + { + board_printf(__FILE__ ": data(%d/%d):", (purb ? purb->actual_length : 0), transfer_len); + len = usb_pipeout(pipe)? transfer_len : (purb ? purb->actual_length : 0); + for(i = 0; i < 16 && i < len; i++) + board_printf(" %02x", ((__u8 *)buffer)[i]); + board_printf("%s\r\n", i < len? "...": ""); + } + } +#endif +} + +/* just for debugging; prints non-empty branches of the int ed tree + * inclusive iso eds */ +static void ep_print_int_eds(ohci_t *ohci, char *str) +{ + int i, j; + __u32 *ed_p; + for(i = 0; i < 32; i++) + { + j = 5; + ed_p = &(ohci->hcca->int_table[i]); + if(*ed_p == 0) + continue; + board_printf(__FILE__ ": %s branch int %2d(%2x):", str, i, i); + while(*ed_p != 0 && j--) + { + ed_t *ed = (ed_t *)m32_swap((unsigned long)ed_p); + board_printf(" ed: %4x;", ed->hwINFO); + ed_p = &ed->hwNextED; + } + board_printf("\r\n"); + } +} + +static void ohci_dump_intr_mask(char *label, __u32 mask) +{ + dbg("%s: 0x%08x%s%s%s%s%s%s%s%s%s", + label, + mask, + (mask & OHCI_INTR_MIE) ? " MIE" : "", + (mask & OHCI_INTR_OC) ? " OC" : "", + (mask & OHCI_INTR_RHSC) ? " RHSC" : "", + (mask & OHCI_INTR_FNO) ? " FNO" : "", + (mask & OHCI_INTR_UE) ? " UE" : "", + (mask & OHCI_INTR_RD) ? " RD" : "", + (mask & OHCI_INTR_SF) ? " SF" : "", + (mask & OHCI_INTR_WDH) ? " WDH" : "", + (mask & OHCI_INTR_SO) ? " SO" : "" + ); +} + +static void maybe_print_eds(ohci_t *controller, char *label, __u32 value) +{ + ed_t *edp; + value += controller->dma_offset; + edp = (ed_t *)value; + if(value && (value < 0xDFFFF0)) /* STRAM */ + { + dbg("%s %08x", label, value); + dbg("%08x", edp->hwINFO); + dbg("%08x", edp->hwTailP); + dbg("%08x", edp->hwHeadP); + dbg("%08x", edp->hwNextED); + } +} + +static char *hcfs2string(int state) +{ + switch(state) + { + case OHCI_USB_RESET: return "reset"; + case OHCI_USB_RESUME: return "resume"; + case OHCI_USB_OPER: return "operational"; + case OHCI_USB_SUSPEND: return "suspend"; + } + return "?"; +} + +/* dump control and status registers */ +static void ohci_dump_status(ohci_t *controller) +{ + struct ohci_regs *regs = controller->regs; + __u32 temp = readl(®s->revision) & 0xff; + if(temp != 0x10) + dbg("spec %d.%d", (temp >> 4), (temp & 0x0f)); + temp = readl(®s->control); + dbg("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp, + (temp & OHCI_CTRL_RWE) ? " RWE" : "", + (temp & OHCI_CTRL_RWC) ? " RWC" : "", + (temp & OHCI_CTRL_IR) ? " IR" : "", + hcfs2string(temp & OHCI_CTRL_HCFS), + (temp & OHCI_CTRL_BLE) ? " BLE" : "", + (temp & OHCI_CTRL_CLE) ? " CLE" : "", + (temp & OHCI_CTRL_IE) ? " IE" : "", + (temp & OHCI_CTRL_PLE) ? " PLE" : "", + temp & OHCI_CTRL_CBSR + ); + temp = readl(®s->cmdstatus); + dbg("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp, + (temp & OHCI_SOC) >> 16, + (temp & OHCI_OCR) ? " OCR" : "", + (temp & OHCI_BLF) ? " BLF" : "", + (temp & OHCI_CLF) ? " CLF" : "", + (temp & OHCI_HCR) ? " HCR" : "" + ); + ohci_dump_intr_mask("intrstatus", readl(®s->intrstatus)); + ohci_dump_intr_mask("intrenable", readl(®s->intrenable)); + maybe_print_eds(controller, "ed_periodcurrent", readl(®s->ed_periodcurrent)); + maybe_print_eds(controller, "ed_controlhead", readl(®s->ed_controlhead)); + maybe_print_eds(controller, "ed_controlcurrent", readl(®s->ed_controlcurrent)); + maybe_print_eds(controller, "ed_bulkhead", readl(®s->ed_bulkhead)); + maybe_print_eds(controller, "ed_bulkcurrent", readl(®s->ed_bulkcurrent)); + maybe_print_eds(controller, "donehead", readl(®s->donehead)); +} + +static void ohci_dump_roothub(ohci_t *controller, int verbose) +{ + __u32 temp, ndp, i; + temp = roothub_a(controller); +// ndp = (temp & RH_A_NDP); + ndp = controller->ndp; + if(verbose) + { + dbg("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, + ((temp & RH_A_POTPGT) >> 24) & 0xff, + (temp & RH_A_NOCP) ? " NOCP" : "", + (temp & RH_A_OCPM) ? " OCPM" : "", + (temp & RH_A_DT) ? " DT" : "", + (temp & RH_A_NPS) ? " NPS" : "", + (temp & RH_A_PSM) ? " PSM" : "", + ndp + ); + temp = roothub_b(controller); + dbg("roothub.b: %08x PPCM=%04x DR=%04x", + temp, + (temp & RH_B_PPCM) >> 16, + (temp & RH_B_DR) + ); + temp = roothub_status(controller); + dbg("roothub.status: %08x%s%s%s%s%s%s", + temp, + (temp & RH_HS_CRWE) ? " CRWE" : "", + (temp & RH_HS_OCIC) ? " OCIC" : "", + (temp & RH_HS_LPSC) ? " LPSC" : "", + (temp & RH_HS_DRWE) ? " DRWE" : "", + (temp & RH_HS_OCI) ? " OCI" : "", + (temp & RH_HS_LPS) ? " LPS" : "" + ); + } + for(i = 0; i < ndp; i++) + { + temp = roothub_portstatus(controller, i); + dbg("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", + i, + temp, + (temp & RH_PS_PRSC) ? " PRSC" : "", + (temp & RH_PS_OCIC) ? " OCIC" : "", + (temp & RH_PS_PSSC) ? " PSSC" : "", + (temp & RH_PS_PESC) ? " PESC" : "", + (temp & RH_PS_CSC) ? " CSC" : "", + + (temp & RH_PS_LSDA) ? " LSDA" : "", + (temp & RH_PS_PPS) ? " PPS" : "", + (temp & RH_PS_PRS) ? " PRS" : "", + (temp & RH_PS_POCI) ? " POCI" : "", + (temp & RH_PS_PSS) ? " PSS" : "", + + (temp & RH_PS_PES) ? " PES" : "", + (temp & RH_PS_CCS) ? " CCS" : "" + ); + } +} + +static void ohci_dump(ohci_t *ohci, int verbose) +{ + dbg("OHCI controller usb-%s-%c state", ohci->slot_name, (char)ohci->controller + '0'); + /* dumps some of the state we know about */ + ohci_dump_status(ohci); + if(verbose) + ep_print_int_eds(ohci, "hcca"); + dbg("hcca frame #%04x", ohci->hcca->frame_no); + ohci_dump_roothub(ohci, 1); +} +#endif /* DEBUG */ + +/*-------------------------------------------------------------------------* + * Interface functions (URB) + *-------------------------------------------------------------------------*/ + +/* get a transfer request */ + +static int sohci_submit_job(ohci_t *ohci, urb_priv_t *urb, struct devrequest *setup) +{ + ed_t *ed; + urb_priv_t *purb_priv = urb; + int i, size = 0; + struct usb_device *dev = urb->dev; + unsigned long pipe = urb->pipe; + void *buffer = urb->transfer_buffer; + int transfer_len = urb->transfer_buffer_length; + int interval = urb->interval; + /* when controller's hung, permit only roothub cleanup attempts + * such as powering down ports */ + if(ohci->disabled) + { + urb_free_priv(purb_priv); // added + err("sohci_submit_job: EPIPE"); + return -1; + } + /* we're about to begin a new transaction here so mark the + * URB unfinished */ + urb->finished = 0; + /* every endpoint has a ed, locate and fill it */ + ed = ep_add_ed(ohci, dev, pipe, interval, 1); + if(!ed) + { + urb_free_priv(purb_priv); // added + err("sohci_submit_job: ENOMEM"); + return -1; + } + /* for the private part of the URB we need the number of TDs (size) */ + switch(usb_pipetype(pipe)) + { + case PIPE_BULK: /* one TD for every 4096 Byte */ + size = (transfer_len - 1) / 4096 + 1; + break; + case PIPE_CONTROL:/* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ + size = (transfer_len == 0) ? 2: (transfer_len - 1) / 4096 + 3; + break; + case PIPE_INTERRUPT: /* 1 TD */ + size = 1; + break; + } + ed->purb = urb; + if(size >= (N_URB_TD - 1)) + { + urb_free_priv(purb_priv); // added + err("need %d TDs, only have %d", size, N_URB_TD); + return -1; + } + purb_priv->pipe = pipe; + /* fill the private part of the URB */ + purb_priv->length = size; + purb_priv->ed = ed; + purb_priv->actual_length = 0; + /* allocate the TDs */ + /* note that td[0] was allocated in ep_add_ed */ + for(i = 0; i < size; i++) + { + purb_priv->td[i] = td_alloc(dev); + if(!purb_priv->td[i]) + { + purb_priv->length = i; + urb_free_priv(purb_priv); + err("sohci_submit_job: ENOMEM"); + return -1; + } + } + if(ed->state == ED_NEW || (ed->state & ED_DEL)) + { + urb_free_priv(purb_priv); + err("sohci_submit_job: EINVAL"); + return -1; + } + /* link the ed into a chain if is not already */ + if(ed->state != ED_OPER) + ep_link(ohci, ed); + /* fill the TDs and link it to the ed */ + td_submit_job(ohci, dev, pipe, buffer, transfer_len, setup, purb_priv, interval); + return 0; +} + +static inline int sohci_return_job(ohci_t *ohci, urb_priv_t *urb) +{ + struct ohci_regs *regs = ohci->regs; + switch(usb_pipetype(urb->pipe)) + { + case PIPE_INTERRUPT: + /* implicitly requeued */ + if((urb->dev->irq_handle != NULL) && (urb->dev->irq_act_len = urb->actual_length)) + { + writel(OHCI_INTR_WDH, ®s->intrenable); + readl(®s->intrenable); /* PCI posting flush */ + /* call interrupt device routine */ +// dbg("irq_handle device %d", urb->dev->devnum); + urb->dev->irq_handle(urb->dev); + writel(OHCI_INTR_WDH, ®s->intrdisable); + readl(®s->intrdisable); /* PCI posting flush */ + } + urb->actual_length = 0; + td_submit_job(ohci, urb->dev, urb->pipe, urb->transfer_buffer, urb->transfer_buffer_length, NULL, urb, urb->interval); + break; + case PIPE_CONTROL: + case PIPE_BULK: + break; + default: + return 0; + } + return 1; +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +/* tell us the current USB frame number */ + +static int sohci_get_current_frame_number(ohci_t *ohci, struct usb_device *usb_dev) +{ + return m16_swap(ohci->hcca->frame_no); +} +#endif + +/*-------------------------------------------------------------------------* + * ED handling functions + *-------------------------------------------------------------------------*/ + +/* search for the right branch to insert an interrupt ed into the int tree + * do some load ballancing; + * returns the branch and + * sets the interval to interval = 2^integer (ld (interval)) */ + +static int ep_int_ballance(ohci_t *ohci, int interval, int load) +{ + int i, branch = 0; + /* search for the least loaded interrupt endpoint + * branch of all 32 branches + */ + for(i = 0; i < 32; i++) + { + if(ohci->ohci_int_load[branch] > ohci->ohci_int_load[i]) + branch = i; + } + branch = branch % interval; + for(i = branch; i < 32; i += interval) + ohci->ohci_int_load[i] += load; + return branch; +} + +/*-------------------------------------------------------------------------*/ + +/* 2^int( ld (inter)) */ + +static int ep_2_n_interval(int inter) +{ + int i; + for(i = 0; ((inter >> i) > 1) && (i < 5); i++); + return 1 << i; +} + +/*-------------------------------------------------------------------------*/ + +/* the int tree is a binary tree + * in order to process it sequentially the indexes of the branches have to + * be mapped the mapping reverses the bits of a word of num_bits length */ +static int ep_rev(int num_bits, int word) +{ + int i, wout = 0; + for(i = 0; i < num_bits; i++) + wout |= (((word >> i) & 1) << (num_bits - i - 1)); + return wout; +} + +/*-------------------------------------------------------------------------* + * ED handling functions + *-------------------------------------------------------------------------*/ + +/* link an ed into one of the HC chains */ + +static int ep_link(ohci_t *ohci, ed_t *edi) +{ + volatile ed_t *ed = edi; + int int_branch; + int i; + int inter; + int interval; + int load; + __u32 *ed_p; + ed->state = ED_OPER; + ed->int_interval = 0; + switch(ed->type) + { + case PIPE_CONTROL: + ed->hwNextED = 0; + if(ohci->ed_controltail == NULL) + writel(ed - ohci->dma_offset, &ohci->regs->ed_controlhead); + else + ohci->ed_controltail->hwNextED = m32_swap((unsigned long)ed - ohci->dma_offset); + ed->ed_prev = ohci->ed_controltail; + if(!ohci->ed_controltail && !ohci->ed_rm_list[0] && !ohci->ed_rm_list[1] && !ohci->sleeping) + { + ohci->hc_control |= OHCI_CTRL_CLE; + writel(ohci->hc_control, &ohci->regs->control); + } + ohci->ed_controltail = edi; + break; + case PIPE_BULK: + ed->hwNextED = 0; + if(ohci->ed_bulktail == NULL) + writel(ed - ohci->dma_offset, &ohci->regs->ed_bulkhead); + else + ohci->ed_bulktail->hwNextED = m32_swap((unsigned long)ed - ohci->dma_offset); + ed->ed_prev = ohci->ed_bulktail; + if(!ohci->ed_bulktail && !ohci->ed_rm_list[0] && !ohci->ed_rm_list[1] && !ohci->sleeping) + { + ohci->hc_control |= OHCI_CTRL_BLE; + writel(ohci->hc_control, &ohci->regs->control); + } + ohci->ed_bulktail = edi; + break; + case PIPE_INTERRUPT: + load = ed->int_load; + interval = ep_2_n_interval(ed->int_period); + ed->int_interval = interval; + int_branch = ep_int_ballance(ohci, interval, load); + ed->int_branch = int_branch; + for(i = 0; i < ep_rev(6, interval); i += inter) + { + inter = 1; + for(ed_p = &(ohci->hcca->int_table[ep_rev(5, i) + int_branch]); + (*ed_p != 0) && (((ed_t *)ed_p)->int_interval >= interval); + ed_p = &(((ed_t *)ed_p)->hwNextED)) + inter = ep_rev(6, ((ed_t *)ed_p)->int_interval); + ed->hwNextED = *ed_p; + *ed_p = m32_swap((unsigned long)ed - ohci->dma_offset); + } + break; + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* scan the periodic table to find and unlink this ED */ +static void periodic_unlink(struct ohci *ohci, volatile struct ed *ed, unsigned index, unsigned period) +{ + for( ;index < NUM_INTS; index += period) + { + __u32 *ed_p = &ohci->hcca->int_table[index]; + /* ED might have been unlinked through another path */ + while(*ed_p != 0) + { + if((unsigned long)*ed_p == m32_swap((unsigned long)ed - ohci->dma_offset)) /* changed */ + { + *ed_p = ed->hwNextED; + break; + } + ed_p = &(((struct ed *)ed_p)->hwNextED); /* changed */ + } + } +} + +/* unlink an ed from one of the HC chains. + * just the link to the ed is unlinked. + * the link from the ed still points to another operational ed or 0 + * so the HC can eventually finish the processing of the unlinked ed */ + +static int ep_unlink(ohci_t *ohci, ed_t *edi) +{ + volatile ed_t *ed = edi; + int i; + ed->hwINFO |= m32_swap(OHCI_ED_SKIP); + switch(ed->type) + { + case PIPE_CONTROL: + if(ed->ed_prev == NULL) + { + if(!ed->hwNextED) + { + ohci->hc_control &= ~OHCI_CTRL_CLE; + writel(ohci->hc_control, &ohci->regs->control); + } + writel(m32_swap(*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_controlhead); + } + else + ed->ed_prev->hwNextED = ed->hwNextED; + if(ohci->ed_controltail == ed) + ohci->ed_controltail = ed->ed_prev; + else + ((ed_t *)(m32_swap(*((__u32 *)&ed->hwNextED)) + ohci->dma_offset))->ed_prev = ed->ed_prev; + break; + case PIPE_BULK: + if(ed->ed_prev == NULL) + { + if(!ed->hwNextED) + { + ohci->hc_control &= ~OHCI_CTRL_BLE; + writel(ohci->hc_control, &ohci->regs->control); + } + writel(m32_swap(*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_bulkhead); + } + else + ed->ed_prev->hwNextED = ed->hwNextED; + if(ohci->ed_bulktail == ed) + ohci->ed_bulktail = ed->ed_prev; + else + ((ed_t *)(m32_swap(*((__u32 *)&ed->hwNextED)) + ohci->dma_offset))->ed_prev = ed->ed_prev; + break; + case PIPE_INTERRUPT: + periodic_unlink(ohci, ed, 0, 1); + for(i = ed->int_branch; i < 32; i += ed->int_interval) + ohci->ohci_int_load[i] -= ed->int_load; + break; + } + ed->state = ED_UNLINK; + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* add/reinit an endpoint; this should be done once at the + * usb_set_configuration command, but the USB stack is a little bit + * stateless so we do it at every transaction if the state of the ed + * is ED_NEW then a dummy td is added and the state is changed to + * ED_UNLINK in all other cases the state is left unchanged the ed + * info fields are setted anyway even though most of them should not + * change + */ +static ed_t *ep_add_ed(ohci_t *ohci, struct usb_device *usb_dev, unsigned long pipe, int interval, int load) +{ + td_t *td; + ed_t *ed_ret; + volatile ed_t *ed; + struct ohci_device *ohci_dev = ohci->ohci_dev; + ed = ed_ret = &ohci_dev->ed[(usb_pipeendpoint(pipe) << 1) | (usb_pipecontrol(pipe)? 0: usb_pipeout(pipe))]; + if((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) + { + err("ep_add_ed: pending delete"); + /* pending delete request */ + return NULL; + } + if(ed->state == ED_NEW) + { + /* dummy td; end of td list for ed */ + td = td_alloc(usb_dev); + ed->hwTailP = m32_swap((unsigned long)td - ohci->dma_offset); + ed->hwHeadP = ed->hwTailP; + ed->state = ED_UNLINK; + ed->type = usb_pipetype(pipe); + ohci_dev->ed_cnt++; + } + ed->hwINFO = m32_swap(usb_pipedevice(pipe) + | usb_pipeendpoint(pipe) << 7 + | (usb_pipeisoc(pipe)? 0x8000: 0) + | (usb_pipecontrol(pipe)? 0: (usb_pipeout(pipe)? 0x800: 0x1000)) + | usb_pipeslow(pipe) << 13 + | usb_maxpacket(usb_dev, pipe) << 16); + if(ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) + { + ed->int_period = interval; + ed->int_load = load; + } + return ed_ret; +} + +/*-------------------------------------------------------------------------* + * TD handling functions + *-------------------------------------------------------------------------*/ + +/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ + +static void td_fill(ohci_t *ohci, unsigned int info, void *data, int len, + struct usb_device *dev, int index, urb_priv_t *urb_priv) +{ + volatile td_t *td, *td_pt; +#ifdef OHCI_FILL_TRACE + int i; +#endif + if(index > urb_priv->length) + { + err("index > length"); + return; + } + /* use this td as the next dummy */ + td_pt = urb_priv->td[index]; + td_pt->hwNextTD = 0; + /* fill the old dummy TD */ + td = urb_priv->td[index] = (td_t *)((m32_swap(urb_priv->ed->hwTailP) & ~0xf) + ohci->dma_offset); + td->ed = urb_priv->ed; + td->next_dl_td = NULL; + td->index = index; + td->data = (__u32)data; +#ifdef OHCI_FILL_TRACE + if(usb_pipebulk(urb_priv->pipe) && usb_pipeout(urb_priv->pipe)) + { + for(i = 0; i < len; i++) + board_printf("td->data[%d] %#2x ", i, ((unsigned char *)td->data)[i]); + board_printf("\r\n"); + } +#endif + if(!len) + data = NULL; + td->hwINFO = m32_swap(info); + if(data != NULL) + { + td->hwCBP = m32_swap((unsigned long)data - ohci->dma_offset); + td->hwBE = m32_swap((unsigned long)(data + len - 1 - ohci->dma_offset)); + } + else + { + td->hwCBP = 0; + td->hwBE = 0; + } + td->hwNextTD = m32_swap((unsigned long)td_pt - ohci->dma_offset); + /* append to queue */ + td->ed->hwTailP = td->hwNextTD; +#if 0 + if(data) + { + int i; + board_printf("td_fill: %08x %08x %08X %08X at 0x%08X\r\n", + m32_swap(td->hwINFO), m32_swap(td->hwCBP), m32_swap(td->hwNextTD), m32_swap(td->hwBE), td); + for(i = 0; i < len; i++) + board_printf("%02X ", *(unsigned char *)(data + i) & 0xff); + board_printf("\r\n"); + } + else + board_printf("td_fill: %08x %08x %08X %08X at 0x%08X\r\n", + m32_swap(td->hwINFO), m32_swap(td->hwCBP), m32_swap(td->hwNextTD), m32_swap(td->hwBE), td); +#endif +} + +/*-------------------------------------------------------------------------*/ + +/* prepare all TDs of a transfer */ + +static void td_submit_job(ohci_t *ohci, struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval) +{ + int data_len = transfer_len; + void *data; + int cnt = 0; + __u32 info = 0; + unsigned int toggle = 0; + /* OHCI handles the DATA-toggles itself, we just use the USB-toggle + * bits for reseting */ + if(usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) + toggle = TD_T_TOGGLE; + else + { + toggle = TD_T_DATA0; + usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 1); + } + urb->td_cnt = 0; + if(data_len) + data = buffer; + else + data = NULL; + switch(usb_pipetype(pipe)) + { + case PIPE_BULK: + info = usb_pipeout(pipe) ? TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; + while(data_len > 4096) + { + td_fill(ohci, info | (cnt? TD_T_TOGGLE : toggle), data, 4096, dev, cnt, urb); + data += 4096; data_len -= 4096; cnt++; + } + info = usb_pipeout(pipe) ? TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; + td_fill(ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, dev, cnt, urb); + cnt++; + if(!ohci->sleeping) /* start bulk list */ + writel(OHCI_BLF, &ohci->regs->cmdstatus); + break; + case PIPE_CONTROL: + /* Setup phase */ + info = TD_CC | TD_DP_SETUP | TD_T_DATA0; + td_fill(ohci, info, setup, 8, dev, cnt++, urb); + /* Optional Data phase */ + if(data_len > 0) + { + info = usb_pipeout(pipe) ? TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; + /* NOTE: mishandles transfers >8K, some >4K */ + td_fill(ohci, info, data, data_len, dev, cnt++, urb); + } + /* Status phase */ + info = usb_pipeout(pipe) ? TD_CC | TD_DP_IN | TD_T_DATA1 : TD_CC | TD_DP_OUT | TD_T_DATA1; + td_fill(ohci, info, data, 0, dev, cnt++, urb); + if(!ohci->sleeping) /* start Control list */ + writel(OHCI_CLF, &ohci->regs->cmdstatus); + break; + case PIPE_INTERRUPT: + info = usb_pipeout(urb->pipe) ? TD_CC | TD_DP_OUT | toggle : TD_CC | TD_R | TD_DP_IN | toggle; + td_fill(ohci, info, data, data_len, dev, cnt++, urb); + break; + } + if(urb->length != cnt) + dbg("TD LENGTH %d != CNT %d", urb->length, cnt); +} + +/*-------------------------------------------------------------------------* + * Done List handling functions + *-------------------------------------------------------------------------*/ + +/* calculate the transfer length and update the urb */ + +static void dl_transfer_length(ohci_t *ohci, td_t *td) +{ + __u32 tdINFO, tdBE, tdCBP; + urb_priv_t *lurb_priv = td->ed->purb; + tdINFO = m32_swap(td->hwINFO); + tdBE = m32_swap(td->hwBE); + tdCBP = m32_swap(td->hwCBP); + if(tdBE) + tdBE += ohci->dma_offset; + if(tdCBP) + tdCBP += ohci->dma_offset; + if(!(usb_pipecontrol(lurb_priv->pipe) && ((td->index == 0) || (td->index == lurb_priv->length - 1)))) + { + if(tdBE != 0) + { + if(td->hwCBP == 0) + lurb_priv->actual_length += (tdBE - td->data + 1); + else + lurb_priv->actual_length += (tdCBP - td->data); + } + } +} + +/*-------------------------------------------------------------------------*/ +static void check_status(ohci_t *ohci, td_t *td_list) +{ + urb_priv_t *lurb_priv = td_list->ed->purb; + int urb_len = lurb_priv->length; + __u32 *phwHeadP = &td_list->ed->hwHeadP; + int cc = TD_CC_GET(m32_swap(td_list->hwINFO)); + if(cc) + { + err("OHCI usb-%s-%c error: %s (%x)", ohci->slot_name, (char)ohci->controller + '0', cc_to_string[cc], cc); + if(*phwHeadP & m32_swap(0x1)) + { + if(lurb_priv && ((td_list->index + 1) < urb_len)) + { + *phwHeadP = (lurb_priv->td[urb_len - 1]->hwNextTD & m32_swap(0xfffffff0)) | (*phwHeadP & m32_swap(0x2)); + lurb_priv->td_cnt += urb_len - td_list->index - 1; + } + else + *phwHeadP &= m32_swap(0xfffffff2); + } +#ifdef CONFIG_MPC5200 + td_list->hwNextTD = 0; +#endif + } +} + +/* replies to the request have to be on a FIFO basis so + * we reverse the reversed done-list */ +static td_t *dl_reverse_done_list(ohci_t *ohci) +{ + __u32 td_list_hc; + td_t *td_rev = NULL; + td_t *td_list = NULL; + td_list_hc = m32_swap(ohci->hcca->done_head) & ~0xf; + if(td_list_hc) + td_list_hc += ohci->dma_offset; + ohci->hcca->done_head = 0; + while(td_list_hc) + { + td_list = (td_t *)td_list_hc; + check_status(ohci, td_list); + td_list->next_dl_td = td_rev; + td_rev = td_list; + td_list_hc = m32_swap(td_list->hwNextTD) & ~0xf; + if(td_list_hc) + td_list_hc += ohci->dma_offset; + } + return td_list; +} + +/*-------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------*/ + +static void finish_urb(ohci_t *ohci, urb_priv_t *urb, int status) +{ + if((status & (ED_OPER | ED_UNLINK)) && (urb->state != URB_DEL)) + urb->finished = sohci_return_job(ohci, urb); + else + dbg("finish_urb: strange.., ED state %x, \r\n", status); +} + +/* + * Used to take back a TD from the host controller. This would normally be + * called from within dl_done_list, however it may be called directly if the + * HC no longer sees the TD and it has not appeared on the donelist (after + * two frames). This bug has been observed on ZF Micro systems. + */ +static int takeback_td(ohci_t *ohci, td_t *td_list) +{ + ed_t *ed; + int cc; + int stat = 0; + /* urb_t *urb; */ + urb_priv_t *lurb_priv; + __u32 tdINFO, edHeadP, edTailP; + tdINFO = m32_swap(td_list->hwINFO); + ed = td_list->ed; + if(ed == NULL) + { + err("OHCI usb-%s-%c cannot get error code ED is null", ohci->slot_name, (char)ohci->controller + '0'); + return stat; + } + lurb_priv = ed->purb; + dl_transfer_length(ohci, td_list); + lurb_priv->td_cnt++; + /* error code of transfer */ + cc = TD_CC_GET(tdINFO); + if(cc) + { + err("OHCI usb-%s-%c error: %s (%x)", ohci->slot_name, (char)ohci->controller + '0', cc_to_string[cc], cc); + stat = cc_to_error[cc]; + } + /* see if this done list makes for all TD's of current URB, + * and mark the URB finished if so */ + if(lurb_priv->td_cnt == lurb_priv->length) + finish_urb(ohci, lurb_priv, ed->state); + if(ohci->irq) + dbg("dl_done_list: processing TD %x, len %x", lurb_priv->td_cnt, lurb_priv->length); + if(ed->state != ED_NEW && (!usb_pipeint(lurb_priv->pipe))) + { + edHeadP = m32_swap(ed->hwHeadP) & ~0xf; + edTailP = m32_swap(ed->hwTailP); + /* unlink eds if they are not busy */ + if((edHeadP == edTailP) && (ed->state == ED_OPER)) + ep_unlink(ohci, ed); + } + if(cc && (ed->type == PIPE_INTERRUPT)) /* added, but it's not the better method */ + ep_unlink(ohci, ed); + return stat; +} + +static int dl_done_list(ohci_t *ohci) +{ + int stat = 0; + td_t *td_list = dl_reverse_done_list(ohci); + while(td_list) + { + td_t *td_next = td_list->next_dl_td; + stat = takeback_td(ohci, td_list); + td_list = td_next; + } + return stat; +} + +/*-------------------------------------------------------------------------* + * Virtual Root Hub + *-------------------------------------------------------------------------*/ + +/* Device descriptor */ +static __u8 root_hub_dev_des[] = +{ + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x10, /* __u16 bcdUSB; v1.1 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x01, /* __u8 iProduct; */ + 0x00, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + +/* Configuration descriptor */ +static __u8 root_hub_config_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* 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; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + +static unsigned char root_hub_str_index0[] = +{ + 0x04, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 0x09, /* __u8 lang ID */ + 0x04, /* __u8 lang ID */ +}; + +static unsigned char root_hub_str_index1[] = +{ + 28, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 'O', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'H', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'C', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'I', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'R', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 't', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'H', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'u', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'b', /* __u8 Unicode */ + 0, /* __u8 Unicode */ +}; + +/* Hub class-specific descriptor is constructed dynamically */ + +/*-------------------------------------------------------------------------*/ + +#define OK(x) len = (x); break +#ifdef DEBUG +#define WR_RH_STAT(x) { info("WR:status %#8x", (x)); writel((x), &ohci->regs->roothub.status); } +#define WR_RH_PORTSTAT(x) { info("WR:portstatus[%d] %#8x", wIndex-1, (x)); writel((x), &ohci->regs->roothub.portstatus[wIndex-1]); } +#else +#define WR_RH_STAT(x) { writel((x), &ohci->regs->roothub.status); } +#define WR_RH_PORTSTAT(x) { writel((x), &ohci->regs->roothub.portstatus[wIndex-1]); } +#endif +#define RD_RH_STAT roothub_status(ohci) +#define RD_RH_PORTSTAT roothub_portstatus(ohci, wIndex-1) + +/* request to virtual root hub */ + +int rh_check_port_status(ohci_t *controller) +{ + __u32 temp, ndp, i; + int res = -1; + temp = roothub_a(controller); +// ndp = (temp & RH_A_NDP); + ndp = controller->ndp; + for(i = 0; i < ndp; i++) + { + temp = roothub_portstatus(controller, i); + /* check for a device disconnect */ + if(((temp & (RH_PS_PESC | RH_PS_CSC)) == (RH_PS_PESC | RH_PS_CSC)) && ((temp & RH_PS_CCS) == 0)) + { + res = i; + break; + } + /* check for a device connect */ + if((temp & RH_PS_CSC) && (temp & RH_PS_CCS)) + { + res = i; + break; + } + } + return res; +} + +static int ohci_submit_rh_msg(ohci_t *ohci, struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, struct devrequest *cmd) +{ + void *data = buffer; + int leni = transfer_len; + int len = 0; + int stat = 0; + __u32 datab[4]; + __u8 *data_buf = (__u8 *)datab; + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; +#ifdef DEBUG + pkt_print(ohci, NULL, dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); +#else + if(ohci->irq) + wait_ms(1); +#endif + if(usb_pipeint(pipe)) + { + info("Root-Hub submit IRQ: NOT implemented"); + return 0; + } + bmRType_bReq = cmd->requesttype | (cmd->request << 8); + wValue = le16_to_cpu(cmd->value); + wIndex = le16_to_cpu(cmd->index); + wLength = le16_to_cpu(cmd->length); + info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x", dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength); + switch(bmRType_bReq) + { + /* Request Destination: + without flags: Device, + RH_INTERFACE: interface, + RH_ENDPOINT: endpoint, + RH_CLASS means HUB here, + RH_OTHER | RH_CLASS almost ever means HUB_PORT here + */ + case RH_GET_STATUS: + *(__u16 *)data_buf = cpu_to_le16(1); + OK(2); + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *)data_buf = cpu_to_le16(0); + OK(2); + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *)data_buf = cpu_to_le16(0); + OK(2); + case RH_GET_STATUS | RH_CLASS: + *(__u32 *)data_buf = cpu_to_le32(RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); + OK(4); + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + *(__u32 *)data_buf = cpu_to_le32(RD_RH_PORTSTAT); + OK(4); + case RH_CLEAR_FEATURE | RH_ENDPOINT: + switch(wValue) + { + case (RH_ENDPOINT_STALL): OK(0); + } + break; + case RH_CLEAR_FEATURE | RH_CLASS: + switch(wValue) + { + case RH_C_HUB_LOCAL_POWER: OK(0); + case (RH_C_HUB_OVER_CURRENT): WR_RH_STAT(RH_HS_OCIC); OK(0); + } + break; + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch(wValue) + { + case (RH_PORT_ENABLE): WR_RH_PORTSTAT(RH_PS_CCS); OK(0); + case (RH_PORT_SUSPEND): WR_RH_PORTSTAT(RH_PS_POCI); OK(0); + case (RH_PORT_POWER): WR_RH_PORTSTAT(RH_PS_LSDA); OK(0); + case (RH_C_PORT_CONNECTION): WR_RH_PORTSTAT(RH_PS_CSC); OK(0); + case (RH_C_PORT_ENABLE): WR_RH_PORTSTAT(RH_PS_PESC); OK(0); + case (RH_C_PORT_SUSPEND): WR_RH_PORTSTAT(RH_PS_PSSC); OK(0); + case (RH_C_PORT_OVER_CURRENT):WR_RH_PORTSTAT(RH_PS_OCIC); OK(0); + case (RH_C_PORT_RESET): WR_RH_PORTSTAT(RH_PS_PRSC); OK(0); + } + break; + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch(wValue) + { + case (RH_PORT_SUSPEND): + WR_RH_PORTSTAT(RH_PS_PSS); + OK(0); + case (RH_PORT_RESET): /* BUG IN HUP CODE *********/ + if(RD_RH_PORTSTAT & RH_PS_CCS) + WR_RH_PORTSTAT(RH_PS_PRS); + OK(0); + case (RH_PORT_POWER): + WR_RH_PORTSTAT(RH_PS_PPS); + wait_ms(100); + OK(0); + case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/ + if(RD_RH_PORTSTAT & RH_PS_CCS) + WR_RH_PORTSTAT(RH_PS_PES); + OK(0); + } + break; + case RH_SET_ADDRESS: + ohci->rh.devnum = wValue; + OK(0); + case RH_GET_DESCRIPTOR: + switch((wValue & 0xff00) >> 8) + { + case(0x01): /* device descriptor */ + len = min_t(unsigned int, leni, min_t(unsigned int, sizeof(root_hub_dev_des), wLength)); + data_buf = root_hub_dev_des; + OK(len); + case(0x02): /* configuration descriptor */ + len = min_t(unsigned int, leni, min_t(unsigned int, sizeof(root_hub_config_des), wLength)); + data_buf = root_hub_config_des; + OK(len); + case(0x03): /* string descriptors */ + if(wValue == 0x0300) + { + len = min_t(unsigned int, leni, min_t(unsigned int, sizeof(root_hub_str_index0), wLength)); + data_buf = root_hub_str_index0; + OK(len); + } + if(wValue == 0x0301) + { + len = min_t(unsigned int, leni, min_t(unsigned int, sizeof(root_hub_str_index1), wLength)); + data_buf = root_hub_str_index1; + OK(len); + } + default: + stat = USB_ST_STALLED; + } + break; + case RH_GET_DESCRIPTOR | RH_CLASS: + { + __u32 temp = roothub_a(ohci); + data_buf[0] = 9; /* min length; */ + data_buf[1] = 0x29; +// data_buf[2] = temp & RH_A_NDP; + data_buf[2] = (__u8)ohci->ndp; + data_buf[3] = 0; + if(temp & RH_A_PSM) /* per-port power switching? */ + data_buf[3] |= 0x1; + if(temp & RH_A_NOCP) /* no overcurrent reporting? */ + data_buf[3] |= 0x10; + else if(temp & RH_A_OCPM) /* per-port overcurrent reporting? */ + data_buf[3] |= 0x8; + /* corresponds to data_buf[4-7] */ + datab[1] = 0; + data_buf[5] = (temp & RH_A_POTPGT) >> 24; + temp = roothub_b(ohci); + data_buf[7] = temp & RH_B_DR; + if(data_buf[2] < 7) + data_buf[8] = 0xff; + else + { + data_buf[0] += 2; + data_buf[8] = (temp & RH_B_DR) >> 8; + data_buf[10] = data_buf[9] = 0xff; + } + len = min_t(unsigned int, leni, min_t(unsigned int, data_buf [0], wLength)); + OK(len); + } + case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK(1); + case RH_SET_CONFIGURATION: WR_RH_STAT(0x10000); OK(0); + default: + dbg("unsupported root hub command"); + stat = USB_ST_STALLED; + } +#ifdef DEBUG + ohci_dump_roothub(ohci, 1); +#else + if(ohci->irq) + wait_ms(1); +#endif + len = min_t(int, len, leni); + if(data != data_buf) + memcpy(data, data_buf, len); + dev->act_len = len; + dev->status = stat; +#ifdef DEBUG + pkt_print(ohci, NULL, dev, pipe, buffer, transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/); +#else + if(ohci->irq) + wait_ms(1); +#endif + return stat; +} + +/*-------------------------------------------------------------------------*/ + +/* common code for handling submit messages - used for all but root hub accesses. */ + +static int submit_common_msg(ohci_t *ohci, struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, int interval) +{ + int stat = 0; + int maxsize = usb_maxpacket(dev, pipe); + int timeout; + urb_priv_t *urb = (urb_priv_t *)usb_malloc(sizeof(urb_priv_t)); + if(urb == NULL) + { + err("submit_common_msg malloc failed"); + return -1; + } + memset(urb, 0, sizeof(urb_priv_t)); + urb->dev = dev; + urb->pipe = pipe; + urb->transfer_buffer = buffer; + urb->transfer_buffer_length = transfer_len; + urb->interval = interval; + /* device pulled? Shortcut the action. */ + if(ohci->devgone == dev) + { + dev->status = USB_ST_CRC_ERR; + return 0; + } +#ifdef DEBUG + urb->actual_length = 0; + pkt_print(ohci, urb, dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); +#else + if(ohci->irq) + wait_ms(1); +#endif + if(!maxsize) + { + err("submit_common_message: pipesize for pipe %lx is zero", pipe); + return -1; + } + if(sohci_submit_job(ohci, urb, setup) < 0) + { + err("sohci_submit_job failed"); + return -1; + } +#if 0 + wait_ms(10); + /* ohci_dump_status(ohci); */ +#endif + /* allow more time for a BULK device to react - some are slow */ +#define BULK_TO 5000 /* timeout in milliseconds */ + if(usb_pipebulk(pipe)) + timeout = BULK_TO; + else + timeout = 1000; + /* wait for it to complete */ + while(ohci->irq) + { + /* check whether the controller is done */ + flush_data_cache(ohci); +#ifndef CONFIG_USB_INTERRUPT_POLLING + if(ohci->irq_enabled) + stat = ohci->stat_irq; + else +#endif + stat = hc_interrupt(ohci); + if(stat < 0) + { + stat = USB_ST_CRC_ERR; + break; + } + /* NOTE: since we are not interrupt driven in U-Boot and always + * handle only one URB at a time, we cannot assume the + * transaction finished on the first successful return from + * hc_interrupt().. unless the flag for current URB is set, + * meaning that all TD's to/from device got actually + * transferred and processed. If the current URB is not + * finished we need to re-iterate this loop so as + * hc_interrupt() gets called again as there needs to be some + * more TD's to process still */ + if((stat >= 0) && (stat != 0xff) && (urb->finished)) + { + /* 0xff is returned for an SF-interrupt */ + break; + } + if(--timeout) + { + wait_ms(1); +// if(!urb->finished) +// dbg("*"); + } + else + { + err("OHCI usb-%s-%c CTL:TIMEOUT", ohci->slot_name, (char)ohci->controller + '0'); + dbg("submit_common_msg: TO status %x\r\n", stat); + urb->finished = 1; + stat = USB_ST_CRC_ERR; + break; + } + } + dev->status = stat; + dev->act_len = transfer_len; +#ifdef DEBUG + pkt_print(ohci, urb, dev, pipe, buffer, transfer_len, setup, "RET(ctlr)", usb_pipein(pipe)); +#else + if(ohci->irq) + wait_ms(1); +#endif + /* free TDs in urb_priv */ + if(!usb_pipeint(pipe)) + urb_free_priv(urb); + return 0; +} + +/* submit routines called from usb.c */ +int ohci_submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len) +{ + info("submit_bulk_msg dev 0x%p ohci 0x%p buffer 0x%p len %d", dev, dev->priv_hcd, buffer, transfer_len); + return submit_common_msg((ohci_t *)dev->priv_hcd, dev, pipe, buffer, transfer_len, NULL, 0); +} + +int ohci_submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, struct devrequest *setup) +{ + ohci_t *ohci = (ohci_t *)dev->priv_hcd; + int maxsize = usb_maxpacket(dev, pipe); + info("submit_control_msg dev 0x%p ohci 0x%p", dev, ohci); +#ifdef DEBUG + pkt_print(ohci, NULL, dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); +#else + if(ohci->irq) + wait_ms(1); +#endif + if(!maxsize) + { + err("submit_control_message: pipesize for pipe %lx is zero", pipe); + return -1; + } + if(((pipe >> 8) & 0x7f) == ohci->rh.devnum) + { + ohci->rh.dev = dev; + /* root hub - redirect */ + return ohci_submit_rh_msg(ohci, dev, pipe, buffer, transfer_len, setup); + } + return submit_common_msg(ohci, dev, pipe, buffer, transfer_len, setup, 0); +} + +int ohci_submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, int interval) +{ + info("submit_int_msg dev 0x%p ohci 0x%p buffer 0x%p len %d", dev, dev->priv_hcd, buffer, transfer_len); + return submit_common_msg((ohci_t *)dev->priv_hcd, dev, pipe, buffer, transfer_len, NULL, interval); +} + +/*-------------------------------------------------------------------------* + * HC functions + *-------------------------------------------------------------------------*/ + +/* reset the HC and BUS */ + +static int hc_reset(ohci_t *ohci) +{ + int timeout = 30; + int smm_timeout = 50; /* 0,5 sec */ + dbg("%s\r\n", __FUNCTION__); +#ifndef CONFIG_USB_EHCI + if((ohci->ent->vendor == PCI_VENDOR_ID_PHILIPS) + && (ohci->ent->device == PCI_DEVICE_ID_PHILIPS_ISP1561)) + { +#define EHCI_USBCMD_OFF 0x20 +#define EHCI_USBCMD_HCRESET (1 << 1) + /* Some multi-function controllers (e.g. ISP1562) allow root hub + resetting via EHCI registers only. */ + short index = 0; + long handle; + do + { +#ifdef PCI_XBIOS + handle = find_pci_device(0x0000FFFFL, index++); +#else + handle = Find_pci_device(0x0000FFFFL, index++); +#endif + if(handle >= 0) + { + unsigned long id = 0; +#ifdef PCI_XBIOS + long error = read_config_longword(handle, PCIIDR, &id); +#else + long error = Read_config_longword(handle, PCIIDR, &id); +#endif + if((error >= 0) && (PCI_VENDOR_ID_PHILIPS == (id & 0xFFFF)) + && (PCI_DEVICE_ID_PHILIPS_ISP1561_2 == (id >> 16))) + { + int timeout = 1000; + unsigned long usb_base_addr = 0xFFFFFFFF; + PCI_RSC_DESC *pci_rsc_desc; +#ifdef PCI_XBIOS + pci_rsc_desc = (PCI_RSC_DESC *)get_resource(handle); /* USB OHCI */ +#else + pci_rsc_desc = (PCI_RSC_DESC *)Get_resource(handle); /* USB OHCI */ +#endif + if((long)pci_rsc_desc >= 0) + { + unsigned short flags; + do + { + if(!(pci_rsc_desc->flags & FLG_IO)) + { + if(usb_base_addr == 0xFFFFFFFF) + { + unsigned long base = pci_rsc_desc->offset + pci_rsc_desc->start; + usb_base_addr = pci_rsc_desc->start; + writel(readl(base + EHCI_USBCMD_OFF) | EHCI_USBCMD_HCRESET, base + EHCI_USBCMD_OFF); + while(readl(base + EHCI_USBCMD_OFF) & EHCI_USBCMD_HCRESET) + { + if(timeout-- <= 0) + { + err("USB RootHub reset timed out!\r\n"); + break; + } + udelay(1); + } + } + } + flags = pci_rsc_desc->flags; + pci_rsc_desc = (PCI_RSC_DESC *)((unsigned long)pci_rsc_desc->next + (unsigned long)pci_rsc_desc); + } + while(!(flags & FLG_LAST)); + } + } + } + } + while(handle >= 0); + } + if((ohci->controller == 0) && (ohci->ent->vendor == PCI_VENDOR_ID_NEC) + && (ohci->ent->device == PCI_DEVICE_ID_NEC_USB)) + { + if(ohci->handle == 1) /* NEC on motherboard has FPGA clock */ + { + dbg("USB OHCI set 48MHz clock\r\n"); + pci_write_config_longword(ohci->handle, 0xE4, 0x21); // oscillator & disable ehci + wait_ms(10); + } + else + { + pci_write_config_longword(ohci->handle, 0xE4, fast_read_config_longword(ohci->handle, 0xE4) | 0x01); // disable ehci + wait_ms(10); + } + } +#else /* CONFIG_USB_EHCI */ +#ifdef MCF547X + if((ohci->controller == 0) && (ohci->ent->vendor == PCI_VENDOR_ID_NEC) + && (ohci->ent->device == PCI_DEVICE_ID_NEC_USB) + && (ohci->handle == 1)) /* NEC on motherboard has FPGA clock */ + { + dbg("USB OHCI set 48MHz clock\r\n"); +#ifdef PCI_XBIOS + write_config_longword(ohci->handle, 0xE4, 0x20); // oscillator +#else + Write_config_longword(ohci->handle, 0xE4, 0x20); // oscillator +#endif + wait_ms(10); + } +#endif /* MCF547X */ +#endif /* CONFIG_USB_EHCI */ + if(readl(&ohci->regs->control) & OHCI_CTRL_IR) + { + /* SMM owns the HC */ + writel(OHCI_OCR, &ohci->regs->cmdstatus);/* request ownership */ + info("USB HC TakeOver from SMM"); + while(readl(&ohci->regs->control) & OHCI_CTRL_IR) + { + wait_ms(10); + if(--smm_timeout == 0) + { + err("USB HC TakeOver failed!"); + return -1; + } + } + } + /* Disable HC interrupts */ + writel(OHCI_INTR_MIE, &ohci->regs->intrdisable); + dbg("USB OHCI HC reset_hc usb-%s-%c: ctrl = 0x%X", ohci->slot_name, (char)ohci->controller + '0', readl(&ohci->regs->control)); + /* Reset USB (needed by some controllers) */ + ohci->hc_control = 0; + writel(ohci->hc_control, &ohci->regs->control); + wait_ms(50); + /* HC Reset requires max 10 us delay */ + writel(OHCI_HCR, &ohci->regs->cmdstatus); + while((readl(&ohci->regs->cmdstatus) & OHCI_HCR) != 0) + { + if(--timeout == 0) + { + err("USB HC reset timed out!"); + return -1; + } + udelay(1); + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* Start an OHCI controller, set the BUS operational + * enable interrupts + * connect the virtual root hub */ + +static int hc_start(ohci_t *ohci) +{ + __u32 mask; + unsigned int fminterval; + ohci->disabled = 1; + /* Tell the controller where the control and bulk lists are + * The lists are empty now. */ + writel(0, &ohci->regs->ed_controlhead); + writel(0, &ohci->regs->ed_bulkhead); + writel((__u32)ohci->hcca - ohci->dma_offset, &ohci->regs->hcca); /* a reset clears this */ + fminterval = 0x2edf; + writel((fminterval * 9) / 10, &ohci->regs->periodicstart); + fminterval |= ((((fminterval - 210) * 6) / 7) << 16); + writel(fminterval, &ohci->regs->fminterval); + writel(0x628, &ohci->regs->lsthresh); + /* start controller operations */ + ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; + ohci->disabled = 0; + writel(ohci->hc_control, &ohci->regs->control); + /* disable all interrupts */ + mask = (OHCI_INTR_SO | OHCI_INTR_WDH | OHCI_INTR_SF | OHCI_INTR_RD | + OHCI_INTR_UE | OHCI_INTR_FNO | OHCI_INTR_RHSC | OHCI_INTR_OC | OHCI_INTR_MIE); + writel(mask, &ohci->regs->intrdisable); + /* clear all interrupts */ + mask &= ~OHCI_INTR_MIE; + writel(mask, &ohci->regs->intrstatus); + /* Choose the interrupts we care about now - but w/o MIE */ + mask = OHCI_INTR_RHSC | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; + writel(mask, &ohci->regs->intrenable); + ohci->ndp = roothub_a(ohci); +#ifdef OHCI_USE_NPS + /* required for AMD-756 and some Mac platforms */ + writel((ohci->ndp | RH_A_NPS) & ~RH_A_PSM, &ohci->regs->roothub.a); + writel(RH_HS_LPSC, &ohci->regs->roothub.status); +#endif /* OHCI_USE_NPS */ + /* POTPGT delay is bits 24-31, in 2 ms units. */ + wait_ms((ohci->ndp >> 23) & 0x1fe); + ohci->ndp &= RH_A_NDP; + /* connect the virtual root hub */ + ohci->rh.devnum = 0; + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static void flush_data_cache(ohci_t *ohci) +{ + /* flush caches here */ +} + +#ifdef CONFIG_USB_INTERRUPT_POLLING + +/* Poll USB interrupt. */ +void ohci_usb_event_poll(int interrupt) +{ + if(ohci_inited) + { + int i; + for(i = 0; i < (sizeof(gohci) / sizeof(ohci_t)); i++) + { + ohci_t *ohci = &gohci[i]; + if(!ohci->handle || ohci->disabled) + continue; + else + { + flush_data_cache(ohci); + if(interrupt) + ohci->irq = 0; + hc_interrupt(ohci); + if(interrupt) + ohci->irq = -1; + } + } + } +} + +#endif /* CONFIG_USB_INTERRUPT_POLLING */ + +/* an interrupt happens */ +static int hc_interrupt(ohci_t *ohci) +{ + struct ohci_regs *regs = ohci->regs; + int ints, stat = -1; + if((ohci->hcca->done_head != 0) && !(m32_swap(ohci->hcca->done_head) & 0x01)) + ints = OHCI_INTR_WDH; + else + { + ints = readl(®s->intrstatus); + if(ints == ~(u32)0) + { + ohci->disabled++; + err("OHCI usb-%s-%c device removed!", ohci->slot_name, (char)ohci->controller + '0'); + return -1; + } + else + { + ints &= readl(®s->intrenable); + if(ints == 0) + { +// dbg("hc_interrupt: returning..\r\n"); + return 0xff; + } + } + } + if(ohci->irq) + dbg("Interrupt: 0x%x frame: 0x%x bus: %d", ints, le16_to_cpu(ohci->hcca->frame_no), ohci->controller); + if(ints & OHCI_INTR_RHSC) /* root hub status change */ + { +#ifdef USB_POLL_HUB + if((queue_poll_hub != NULL) && (rh_check_port_status(ohci) >= 0)) + { + if(ohci->irq) + xQueueAltSend(queue_poll_hub, (void *)&ohci->usbnum, 0); + else + { + portBASE_TYPE xNeedSwitch = pdFALSE; + xNeedSwitch = xQueueSendFromISR(queue_poll_hub, &ohci->usbnum, xNeedSwitch); + } /* to fix xNeedSwitch usage */ + } +#endif /* USB_POLL_HUB */ + stat = 0xff; + } + if(ints & OHCI_INTR_UE) /* e.g. due to PCI Master/Target Abort */ + { +#ifdef PCI_XBIOS + unsigned short status = fast_read_config_word(ohci->handle, PCISR); +#else + unsigned short status = Fast_read_config_word(ohci->handle, PCISR); +#endif + err("OHCI Unrecoverable Error, controller usb-%s-%c disabled\r\n(SR:0x%04X%s%s%s%s%s%s)", ohci->slot_name, (char)ohci->controller + '0', status & 0xFFFF, + status & 0x8000 ? ", Parity error" : "", status & 0x4000 ? ", Signaled system error" : "", status & 0x2000 ? ", Received master abort" : "", + status & 0x1000 ? ", Received target abort" : "", status & 0x800 ? ", Signaled target abort" : "", status & 0x100 ? ", Data parity error" : ""); + ohci->disabled++; +#ifdef DEBUG + ohci_dump(ohci, 1); +#else + if(ohci->irq) + wait_ms(1); +#endif + /* HC Reset */ + ohci->hc_control = 0; + writel(ohci->hc_control, &ohci->regs->control); + return -1; + } + if(ints & OHCI_INTR_WDH) + { + if(ohci->irq) + wait_ms(1); + writel(OHCI_INTR_WDH, ®s->intrdisable); + (void)readl(®s->intrdisable); /* flush */ + stat = dl_done_list(ohci); + writel(OHCI_INTR_WDH, ®s->intrenable); + (void)readl(®s->intrdisable); /* flush */ + } + if(ints & OHCI_INTR_SO) + { + dbg("USB Schedule overrun\r\n"); + writel(OHCI_INTR_SO, ®s->intrenable); + stat = -1; + } + /* FIXME: this assumes SOF (1/ms) interrupts don't get lost... */ + if(ints & OHCI_INTR_SF) + { + unsigned int frame = m16_swap(ohci->hcca->frame_no) & 1; + if(ohci->irq) + wait_ms(1); + writel(OHCI_INTR_SF, ®s->intrdisable); + if(ohci->ed_rm_list[frame] != NULL) + writel(OHCI_INTR_SF, ®s->intrenable); + stat = 0xff; + } + writel(ints, ®s->intrstatus); + return stat; +} + +#ifndef CONFIG_USB_INTERRUPT_POLLING + +static int handle_usb_interrupt(ohci_t *ohci) +{ + if(!ohci->irq_enabled) + return 0; + flush_data_cache(ohci); + ohci->irq = 0; + ohci->stat_irq = hc_interrupt(ohci); + ohci->irq = -1; + return 1; /* clear interrupt, 0: disable interrupt */ +} + +void ohci_usb_enable_interrupt(int enable) +{ + int i; + dbg("usb_enable_interrupt(%d)", enable); + for(i = 0; i < (sizeof(gohci) / sizeof(ohci_t)); i++) + { + ohci_t *ohci = &gohci[i]; + if(!ohci->handle) + continue; + ohci->irq_enabled = enable; + if(enable) + writel(OHCI_INTR_MIE, &ohci->regs->intrenable); + else + writel(OHCI_INTR_MIE, &ohci->regs->intrdisable); + } +} + +#endif /* !CONFIG_USB_INTERRUPT_POLLING */ + +/*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------*/ + +/* De-allocate all resources.. */ + +static void hc_release_ohci(ohci_t *ohci) +{ + dbg("USB HC release OHCI usb-%s-%c", ohci->slot_name, (char)ohci->controller + '0'); + if(!ohci->disabled) + hc_reset(ohci); +} + +static void hc_free_buffers(ohci_t *ohci) +{ + if(ohci->td_unaligned != NULL) + { + usb_free(ohci->td_unaligned); + ohci->td_unaligned = NULL; + } + if(ohci->ohci_dev_unaligned != NULL) + { + usb_free(ohci->ohci_dev_unaligned); + ohci->ohci_dev_unaligned = NULL; + } + if(ohci->hcca_unaligned != NULL) + { + usb_free(ohci->hcca_unaligned); + ohci->hcca_unaligned = NULL; + } +} + +/*-------------------------------------------------------------------------*/ + +/* + * low level initalisation routine, called from usb.c + */ +int ohci_usb_lowlevel_init(long handle, const struct pci_device_id *ent, void **priv) +{ + unsigned long usb_base_addr = 0xFFFFFFFF; + ohci_t *ohci = &gohci[(handle >> 16) & 1]; // function & 1 +#ifdef PCI_XBIOS + PCI_RSC_DESC *pci_rsc_desc = (PCI_RSC_DESC *)get_resource(handle); /* USB OHCI */ +#else + PCI_RSC_DESC *pci_rsc_desc; + USB_COOKIE *p = usb_get_cookie('_PCI'); + PCI_COOKIE *bios_cookie = (PCI_COOKIE *)p->v.l; + if(bios_cookie == NULL) /* faster than XBIOS calls */ + return(-1); + tab_funcs_pci = &bios_cookie->routine[0]; + pci_rsc_desc = (PCI_RSC_DESC *)Get_resource(handle); /* USB OHCI */ +#endif + if(handle && (ent != NULL)) + { + memset(ohci, 0, sizeof(ohci_t)); + ohci->handle = handle; + ohci->ent = ent; + } + else if(!ohci->handle) /* for restart USB cmd */ + return(-1); + info("ohci 0x%p", ohci); + ohci->controller = (ohci->handle >> 16) & 3; /* PCI function */ + /* this must be aligned to a 256 byte boundary */ + ohci->hcca_unaligned = (struct ohci_hcca *)usb_malloc(sizeof(struct ohci_hcca) + 256); + if(ohci->hcca_unaligned == NULL) + { + err("HCCA malloc failed"); + return(-1); + } + /* align the storage */ + ohci->hcca = (struct ohci_hcca *)(((unsigned long)ohci->hcca_unaligned + 255) & ~255); + memset(ohci->hcca, 0, sizeof(struct ohci_hcca)); + info("aligned ghcca 0x%p", ohci->hcca); + ohci->ohci_dev_unaligned = (struct ohci_device *)usb_malloc(sizeof(struct ohci_device) + 8); + if(ohci->ohci_dev_unaligned == NULL) + { + err("EDs malloc failed"); + hc_free_buffers(ohci); + return(-1); + } + ohci->ohci_dev = (struct ohci_device *)(((unsigned long)ohci->ohci_dev_unaligned + 7) & ~7); + memset(ohci->ohci_dev, 0, sizeof(struct ohci_device)); + info("aligned EDs 0x%p", ohci->ohci_dev); + ohci->td_unaligned = (td_t *)usb_malloc(sizeof(td_t) * (NUM_TD + 1)); + if(ohci->td_unaligned == NULL) + { + err("TDs malloc failed"); + hc_free_buffers(ohci); + return(-1); + } + ptd = (td_t *)(((unsigned long)ohci->td_unaligned + 7) & ~7); + memset(ptd, 0, sizeof(td_t) * NUM_TD); + info("aligned TDs 0x%p", ptd); + ohci->disabled = 1; + ohci->sleeping = 0; + ohci->irq = -1; + if((long)pci_rsc_desc >= 0) + { + unsigned short flags; + do + { + dbg("PCI USB descriptors: flags 0x%04x start 0x%08lx \r\n offset 0x%08lx dmaoffset 0x%08lx length 0x%08lx", + pci_rsc_desc->flags, pci_rsc_desc->start, pci_rsc_desc->offset, pci_rsc_desc->dmaoffset, pci_rsc_desc->length); + if(!(pci_rsc_desc->flags & FLG_IO)) + { + if(usb_base_addr == 0xFFFFFFFF) + { + usb_base_addr = pci_rsc_desc->start; + ohci->offset = pci_rsc_desc->offset; + ohci->regs = (void *)(pci_rsc_desc->offset + pci_rsc_desc->start); + ohci->dma_offset = pci_rsc_desc->dmaoffset; + /* big_endian unused actually */ + if((pci_rsc_desc->flags & FLG_ENDMASK) == ORD_MOTOROLA) + ohci->big_endian = 0; /* host bridge make swapping intel -> motorola */ + else + ohci->big_endian = 1; /* driver must swapping intel -> motorola */ + } + } + flags = pci_rsc_desc->flags; + pci_rsc_desc = (PCI_RSC_DESC *)((unsigned long)pci_rsc_desc->next + (unsigned long)pci_rsc_desc); + } + while(!(flags & FLG_LAST)); + } + else + { + hc_free_buffers(ohci); + return(-1); /* get_resource error */ + } + if(usb_base_addr == 0xFFFFFFFF) + { + hc_free_buffers(ohci); + return(-1); + } + if(handle && (ent != NULL)) + { + ohci->flags = 0; + switch(ent->vendor) + { + case PCI_VENDOR_ID_AL: ohci->slot_name = "uli1575"; break; + case PCI_VENDOR_ID_NEC: ohci->slot_name = "uPD720101"; ohci->flags |= OHCI_FLAGS_NEC; break; + case PCI_VENDOR_ID_PHILIPS: ohci->slot_name = "isp1561"; break; + default: ohci->slot_name = "generic"; break; + } + } + kprint("OHCI usb-%s-%c, regs address 0x%08X, PCI handle 0x%X\r\n", ohci->slot_name, (char)ohci->controller + '0', ohci->regs, handle); + if(hc_reset(ohci) < 0) + { + err("Can't reset OHCI usb-%s-%c", ohci->slot_name, (char)ohci->controller + '0'); + hc_release_ohci(ohci); + hc_free_buffers(ohci); + return(-1); + } + if(hc_start(ohci) < 0) + { + err("Can't start OHCI usb-%s-%c", ohci->slot_name, (char)ohci->controller + '0'); + hc_release_ohci(ohci); + hc_free_buffers(ohci); + /* Initialization failed */ + return(-1); + } +#ifdef DEBUG + ohci_dump(ohci, 1); +#endif +#ifndef CONFIG_USB_INTERRUPT_POLLING +#ifdef PCI_XBIOS + hook_interrupt(handle, handle_usb_interrupt, ohci); +#else + Hook_interrupt(handle, (void *)handle_usb_interrupt, (unsigned long *)ohci); +#endif /* PCI_BIOS */ +#endif /* CONFIG_USB_INTERRUPT_POLLING */ + if(priv != NULL) + *priv = (void *)ohci; + ohci_inited = 1; + return(0); +} + +int ohci_usb_lowlevel_stop(void *priv) +{ + /* this gets called really early - before the controller has */ + /* even been initialized! */ + ohci_t *ohci = (ohci_t *)priv; + if(!ohci_inited) + return(0); + if(ohci == NULL) + ohci = &gohci[0]; +#ifndef CONFIG_USB_INTERRUPT_POLLING +#ifdef PCI_XBIOS + unhook_interrupt(ohci->handle); +#else + Unhook_interrupt(ohci->handle); +#endif /* PCI_BIOS */ +#endif /* CONFIG_USB_INTERRUPT_POLLING */ + hc_reset(ohci); + hc_free_buffers(ohci); + /* This driver is no longer initialised. It needs a new low-level + * init (board/cpu) before it can be used again. */ + ohci_inited = 0; + return(0); +} + +#endif /* CONFIG_USB_OHCI */ +