diff --git a/i2cspi_BaS_gcc/include/config.h b/i2cspi_BaS_gcc/include/config.h new file mode 100644 index 0000000..391e448 --- /dev/null +++ b/i2cspi_BaS_gcc/include/config.h @@ -0,0 +1,111 @@ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +/* DEBUG */ +#define DEBUG + +/* DBUG */ +#define DBUG + +/* PCI XBIOS */ +#undef PCI_XBIOS /* faster by cookie */ + +/* NETWORK */ +#ifdef COLDFIRE +#define NETWORK +#define ETHERNET_PORT 0 /* FEC channel */ +#undef TEST_NETWORK +#undef DEBUG_PRINT +#define LWIP +#define WEB_LIGHT +#undef FTP_SERVER +#define ERRNO +#define MCD_PROGQUERY +#undef MCD_DEBUG + +#ifdef MCF5445X /* target untested */ +#ifndef LWIP +#undef NETWORK /* to do */ +#endif /* LWIP */ +#endif /* MCF5445X */ + +/* XBIOS */ +#define TOS_ATARI_LOGO /* defined for use TOS4.04 logo */ + +/* BDOS */ +#define NEWCODE +#endif /* COLDFIRE */ + +/* fVDI */ +#define FVDI_STRUCT_2006 + +/* NVDI */ +#define PATCH_NVDI + +/* VDI */ +#undef TOS_TABLES /* defined for use TOS4.04 index tables */ + +/* X86 emulator */ +#undef DEBUG_X86EMU +#undef DEBUG_X86EMU_PCI +#define __BIG_ENDIAN__ +#define NO_LONG_LONG + +/* Radeon */ +#define DEFAULT_MONITOR_LAYOUT "" +#define ATI_LOGO +#define CONFIG_FB_RADEON_I2C +#define CONFIG_FB_MODE_HELPERS +#undef RADEON_TILING /* normally faster but tile 16 x 16 is not compatible with accel.c read_pixel, blit/expand_area and writes on screen frame buffer */ +#ifndef COLDFIRE +#define RADEON_RENDER +#endif +#undef RADEON_THEATRE /* unfinished */ +#define RADEON_DIRECT_ACCESS /* MMIO access faster but don't check endian - little -> big conversion !!! */ + +/* Radeon VIDIX */ +#undef VIDIX_FILTER +#undef VIDIX_ENABLE_BM /* unfinished */ + +/* RTC M5485EVB */ +#undef USE_RTC + +/* LynxEM M5485EVB */ +#ifdef COLDFIRE +#ifndef MCF5445X +#ifndef MCF547X +#undef CONFIG_VIDEO_SMI_LYNXEM +#endif +#endif +#endif /* COLDFIRE */ + +/* XBIOS Setscreen */ +#define MAX_WIDTH_EMU_MONO 1024 +#define MAX_HEIGHT_EMU_MONO 768 + +/* AC97 */ +#define SOUND_AC97 + +/* USB */ +#undef USB_DEVICE /* Coldfire USB device */ +#define USB_BUFFER_SIZE 0x10000 +#define CONFIG_USB_OHCI /* PCI USB 1.1 */ +#define CONFIG_USB_EHCI /* PCI USB 2.0 */ +#undef CONFIG_EHCI_DCACHE +#define CONFIG_SYS_OHCI_SWAP_REG_ACCESS +#define CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS 15 +#define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS 15 +#if defined(COLDFIRE) && !defined(MCF547X) +#undef CONFIG_USB_INTERRUPT_POLLING +#else +#define CONFIG_USB_INTERRUPT_POLLING +#endif /* COLDFIRE */ +#undef CONFIG_LEGACY_USB_INIT_SEQ +#define CONFIG_USB_KEYBOARD +#define CONFIG_USB_MOUSE +#define CONFIG_USB_STORAGE +#ifdef COLDFIRE +#define CONFIG_USB_MEM_NO_CACHE +#endif /* COLDFIRE */ + +#endif /* _CONFIG_H_ */ diff --git a/i2cspi_BaS_gcc/include/ehci.h b/i2cspi_BaS_gcc/include/ehci.h new file mode 100644 index 0000000..4d37076 --- /dev/null +++ b/i2cspi_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/i2cspi_BaS_gcc/include/mod_devicetable.h b/i2cspi_BaS_gcc/include/mod_devicetable.h new file mode 100644 index 0000000..8e480b5 --- /dev/null +++ b/i2cspi_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/i2cspi_BaS_gcc/include/ohci.h b/i2cspi_BaS_gcc/include/ohci.h new file mode 100644 index 0000000..165325f --- /dev/null +++ b/i2cspi_BaS_gcc/include/ohci.h @@ -0,0 +1,480 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2001 David Brownell + * + * usb-ohci.h + */ + +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))); + + +/* + * Maximum number of root hub ports. + */ +#ifndef CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS +# error "CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS undefined!" +#endif + +/* + * 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[CONFIG_SYS_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 */ + 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/i2cspi_BaS_gcc/include/scsi.h b/i2cspi_BaS_gcc/include/scsi.h new file mode 100644 index 0000000..a42c0a3 --- /dev/null +++ b/i2cspi_BaS_gcc/include/scsi.h @@ -0,0 +1,209 @@ +/* + * (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 + * + */ + #ifndef _SCSI_H + #define _SCSI_H + +typedef struct SCSI_cmd_block{ + unsigned char cmd[16]; /* command */ + unsigned char sense_buf[64]; /* for request sense */ + unsigned char status; /* SCSI Status */ + unsigned char target; /* Target ID */ + unsigned char lun; /* Target LUN */ + unsigned char cmdlen; /* command len */ + unsigned long datalen; /* Total data length */ + unsigned char * pdata; /* pointer to data */ + unsigned char msgout[12]; /* Messge out buffer (NOT USED) */ + unsigned char msgin[12]; /* Message in buffer */ + unsigned char sensecmdlen; /* Sense command len */ + unsigned long sensedatalen; /* Sense data len */ + unsigned char sensecmd[6]; /* Sense command */ + unsigned long contr_stat; /* Controller Status */ + unsigned long trans_bytes; /* tranfered bytes */ + + unsigned int priv; +}ccb; + +/*----------------------------------------------------------- +** +** SCSI constants. +** +**----------------------------------------------------------- +*/ + +/* +** Messages +*/ + +#define M_COMPLETE (0x00) +#define M_EXTENDED (0x01) +#define M_SAVE_DP (0x02) +#define M_RESTORE_DP (0x03) +#define M_DISCONNECT (0x04) +#define M_ID_ERROR (0x05) +#define M_ABORT (0x06) +#define M_REJECT (0x07) +#define M_NOOP (0x08) +#define M_PARITY (0x09) +#define M_LCOMPLETE (0x0a) +#define M_FCOMPLETE (0x0b) +#define M_RESET (0x0c) +#define M_ABORT_TAG (0x0d) +#define M_CLEAR_QUEUE (0x0e) +#define M_INIT_REC (0x0f) +#define M_REL_REC (0x10) +#define M_TERMINATE (0x11) +#define M_SIMPLE_TAG (0x20) +#define M_HEAD_TAG (0x21) +#define M_ORDERED_TAG (0x22) +#define M_IGN_RESIDUE (0x23) +#define M_IDENTIFY (0x80) + +#define M_X_MODIFY_DP (0x00) +#define M_X_SYNC_REQ (0x01) +#define M_X_WIDE_REQ (0x03) +#define M_X_PPR_REQ (0x04) + + +/* +** Status +*/ + +#define S_GOOD (0x00) +#define S_CHECK_COND (0x02) +#define S_COND_MET (0x04) +#define S_BUSY (0x08) +#define S_INT (0x10) +#define S_INT_COND_MET (0x14) +#define S_CONFLICT (0x18) +#define S_TERMINATED (0x20) +#define S_QUEUE_FULL (0x28) +#define S_ILLEGAL (0xff) +#define S_SENSE (0x80) + +/* + * Sense_keys + */ + +#define SENSE_NO_SENSE 0x0 +#define SENSE_RECOVERED_ERROR 0x1 +#define SENSE_NOT_READY 0x2 +#define SENSE_MEDIUM_ERROR 0x3 +#define SENSE_HARDWARE_ERROR 0x4 +#define SENSE_ILLEGAL_REQUEST 0x5 +#define SENSE_UNIT_ATTENTION 0x6 +#define SENSE_DATA_PROTECT 0x7 +#define SENSE_BLANK_CHECK 0x8 +#define SENSE_VENDOR_SPECIFIC 0x9 +#define SENSE_COPY_ABORTED 0xA +#define SENSE_ABORTED_COMMAND 0xB +#define SENSE_VOLUME_OVERFLOW 0xD +#define SENSE_MISCOMPARE 0xE + + +#define SCSI_CHANGE_DEF 0x40 /* Change Definition (Optional) */ +#define SCSI_COMPARE 0x39 /* Compare (O) */ +#define SCSI_COPY 0x18 /* Copy (O) */ +#define SCSI_COP_VERIFY 0x3A /* Copy and Verify (O) */ +#define SCSI_INQUIRY 0x12 /* Inquiry (MANDATORY) */ +#define SCSI_LOG_SELECT 0x4C /* Log Select (O) */ +#define SCSI_LOG_SENSE 0x4D /* Log Sense (O) */ +#define SCSI_MODE_SEL6 0x15 /* Mode Select 6-byte (Device Specific) */ +#define SCSI_MODE_SEL10 0x55 /* Mode Select 10-byte (Device Specific) */ +#define SCSI_MODE_SEN6 0x1A /* Mode Sense 6-byte (Device Specific) */ +#define SCSI_MODE_SEN10 0x5A /* Mode Sense 10-byte (Device Specific) */ +#define SCSI_READ_BUFF 0x3C /* Read Buffer (O) */ +#define SCSI_REQ_SENSE 0x03 /* Request Sense (MANDATORY) */ +#define SCSI_SEND_DIAG 0x1D /* Send Diagnostic (O) */ +#define SCSI_TST_U_RDY 0x00 /* Test Unit Ready (MANDATORY) */ +#define SCSI_WRITE_BUFF 0x3B /* Write Buffer (O) */ +/*************************************************************************** + * %%% Commands Unique to Direct Access Devices %%% + ***************************************************************************/ +#define SCSI_COMPARE 0x39 /* Compare (O) */ +#define SCSI_FORMAT 0x04 /* Format Unit (MANDATORY) */ +#define SCSI_LCK_UN_CAC 0x36 /* Lock Unlock Cache (O) */ +#define SCSI_PREFETCH 0x34 /* Prefetch (O) */ +#define SCSI_MED_REMOVL 0x1E /* Prevent/Allow medium Removal (O) */ +#define SCSI_READ6 0x08 /* Read 6-byte (MANDATORY) */ +#define SCSI_READ10 0x28 /* Read 10-byte (MANDATORY) */ +#define SCSI_RD_CAPAC 0x25 /* Read Capacity (MANDATORY) */ +#define SCSI_RD_DEFECT 0x37 /* Read Defect Data (O) */ +#define SCSI_READ_LONG 0x3E /* Read Long (O) */ +#define SCSI_REASS_BLK 0x07 /* Reassign Blocks (O) */ +#define SCSI_RCV_DIAG 0x1C /* Receive Diagnostic Results (O) */ +#define SCSI_RELEASE 0x17 /* Release Unit (MANDATORY) */ +#define SCSI_REZERO 0x01 /* Rezero Unit (O) */ +#define SCSI_SRCH_DAT_E 0x31 /* Search Data Equal (O) */ +#define SCSI_SRCH_DAT_H 0x30 /* Search Data High (O) */ +#define SCSI_SRCH_DAT_L 0x32 /* Search Data Low (O) */ +#define SCSI_SEEK6 0x0B /* Seek 6-Byte (O) */ +#define SCSI_SEEK10 0x2B /* Seek 10-Byte (O) */ +#define SCSI_SEND_DIAG 0x1D /* Send Diagnostics (MANDATORY) */ +#define SCSI_SET_LIMIT 0x33 /* Set Limits (O) */ +#define SCSI_START_STP 0x1B /* Start/Stop Unit (O) */ +#define SCSI_SYNC_CACHE 0x35 /* Synchronize Cache (O) */ +#define SCSI_VERIFY 0x2F /* Verify (O) */ +#define SCSI_WRITE6 0x0A /* Write 6-Byte (MANDATORY) */ +#define SCSI_WRITE10 0x2A /* Write 10-Byte (MANDATORY) */ +#define SCSI_WRT_VERIFY 0x2E /* Write and Verify (O) */ +#define SCSI_WRITE_LONG 0x3F /* Write Long (O) */ +#define SCSI_WRITE_SAME 0x41 /* Write Same (O) */ + +#if 0 +/**************************************************************************** + * decleration of functions which have to reside in the LowLevel Part Driver + */ + +void scsi_print_error(ccb *pccb); +int scsi_exec(ccb *pccb); +void scsi_bus_reset(void); +void scsi_low_level_init(int busdevfunc); + + +/*************************************************************************** + * functions residing inside cmd_scsi.c + */ +void scsi_init(void); + +#endif + +#define SCSI_IDENTIFY 0xC0 /* not used */ + +/* Hardware errors */ +#define SCSI_SEL_TIME_OUT 0x00000101 /* Selection time out */ +#define SCSI_HNS_TIME_OUT 0x00000102 /* Handshake */ +#define SCSI_MA_TIME_OUT 0x00000103 /* Phase error */ +#define SCSI_UNEXP_DIS 0x00000104 /* unexpected disconnect */ + +#define SCSI_INT_STATE 0x00010000 /* unknown Interrupt number is stored in 16 LSB */ + + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#endif /* _SCSI_H */ diff --git a/i2cspi_BaS_gcc/sources/cmd_usb.c b/i2cspi_BaS_gcc/sources/cmd_usb.c new file mode 100644 index 0000000..d6057e5 --- /dev/null +++ b/i2cspi_BaS_gcc/sources/cmd_usb.c @@ -0,0 +1,514 @@ +/* + * (C) Copyright 2001 + * Denis Peter, MPL AG Switzerland + * + * Most of this source has been derived from the Linux USB + * project. + * + * 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 + * + */ + +#include "config.h" +#include +#include "usb.h" + +#undef RESET_START_STOP_CMDS +#undef CONFIG_USB_STORAGE + +#if defined(CONFIG_USB_UHCI) || defined(CONFIG_USB_OHCI) || defined(CONFIG_USB_EHCI) + +#ifdef CONFIG_USB_STORAGE +extern int usb_stor_curr_dev; /* current device */ +#endif + +#if defined(COLDFIRE) && defined(NETWORK) && defined(LWIP) +extern long pxCurrentTCB, tid_TOS; +// #define info(format, arg...) do { if(pxCurrentTCB == tid_TOS) kprint(format, ## arg); else board_printf(format, ## arg); } while(0) +#else +#define info(format, arg...) kprint(format, ## arg) +#endif + +#if defined(COLDFIRE) && defined(NETWORK) && defined(LWIP) + +typedef struct +{ + int dest; + void (*func)(char); + char *loc; +} PRINTK_INFO; + +#define DEST_CONSOLE (1) +#define DEST_STRING (2) + +extern int printk(PRINTK_INFO *info, const char *fmt, va_list ap); + +static void info(const char *const fmt, ...) +{ + va_list ap; + PRINTK_INFO info; + static char buf[1024]; + info.dest = DEST_STRING; + info.loc = buf; + va_start(ap, fmt); + printk(&info, fmt, ap); + *info.loc = '\0'; + if(pxCurrentTCB == tid_TOS) + kprint(buf); + else + board_printf(buf); + va_end(ap); +} + +#endif /* defined(COLDFIRE) && defined(NETWORK) && defined(LWIP) */ + +/* some display routines (info command) */ +char *usb_get_class_desc(unsigned char dclass) +{ + switch (dclass) + { + case USB_CLASS_PER_INTERFACE: return "See Interface"; + case USB_CLASS_AUDIO: return "Audio"; + case USB_CLASS_COMM: return "Communication"; + case USB_CLASS_HID: return "Human Interface"; + case USB_CLASS_PRINTER: return "Printer"; + case USB_CLASS_MASS_STORAGE: return "Mass Storage"; + case USB_CLASS_HUB: return "Hub"; + case USB_CLASS_DATA: return "CDC Data"; + case USB_CLASS_VENDOR_SPEC: return "Vendor specific"; + default: return ""; + } +} + +void usb_display_class_sub(unsigned char dclass, unsigned char subclass, unsigned char proto) +{ + switch(dclass) + { + case USB_CLASS_PER_INTERFACE: + info("See Interface"); + break; + case USB_CLASS_HID: + info("Human Interface, Subclass: "); + switch(subclass) + { + case USB_SUB_HID_NONE: info("None"); break; + case USB_SUB_HID_BOOT: + info("Boot "); + switch(proto) + { + case USB_PROT_HID_NONE: info("None"); break; + case USB_PROT_HID_KEYBOARD: info("Keyboard"); break; + case USB_PROT_HID_MOUSE: info("Mouse"); break; + default: info("reserved"); break; + } + break; + default: info("reserved"); break; + } + break; + case USB_CLASS_MASS_STORAGE: + info("Mass Storage, "); + switch(subclass) + { + case US_SC_RBC: info("RBC "); break; + case US_SC_8020: info("SFF-8020i (ATAPI)"); break; + case US_SC_QIC: info("QIC-157 (Tape)"); break; + case US_SC_UFI: info("UFI"); break; + case US_SC_8070: info("SFF-8070"); break; + case US_SC_SCSI: info("Transp. SCSI"); break; + default: info("reserved"); break; + } + info(", "); + switch (proto) + { + case US_PR_CB: info("Command/Bulk"); break; + case US_PR_CBI: info("Command/Bulk/Int"); break; + case US_PR_BULK: info("Bulk only"); break; + default: info("reserved"); break; + } + break; + default: info("%s", usb_get_class_desc(dclass)); break; + } +} + +void usb_display_string(struct usb_device *dev, int index) +{ + char buffer[256]; + if(index != 0) + { + if(usb_string(dev, index, &buffer[0], 256) > 0) + info("String: \"%s\"", buffer); + } +} + +void usb_display_desc(struct usb_device *dev) +{ + if(dev->descriptor.bDescriptorType == USB_DT_DEVICE) + { + info("%d: %s, USB Revision %x.%x\r\n", dev->devnum, usb_get_class_desc(dev->config.if_desc[0].bInterfaceClass), (dev->descriptor.bcdUSB>>8) & 0xff, dev->descriptor.bcdUSB & 0xff); + if(strlen(dev->mf) || strlen(dev->prod) || strlen(dev->serial)) + info(" - %s %s %s\r\n", dev->mf, dev->prod, dev->serial); + if(dev->descriptor.bDeviceClass) + { + info(" - Class: "); + usb_display_class_sub(dev->descriptor.bDeviceClass, dev->descriptor.bDeviceSubClass, dev->descriptor.bDeviceProtocol); + info("\r\n"); + } + else + info(" - Class: (from Interface) %s\r\n", usb_get_class_desc(dev->config.if_desc[0].bInterfaceClass)); + info(" - PacketSize: %d Configurations: %d\r\n", + dev->descriptor.bMaxPacketSize0, + dev->descriptor.bNumConfigurations); + info(" - Vendor: 0x%04x Product 0x%04x Version %d.%d\r\n", dev->descriptor.idVendor, dev->descriptor.idProduct, (dev->descriptor.bcdDevice>>8) & 0xff, dev->descriptor.bcdDevice & 0xff); + } +} + +void usb_display_conf_desc(struct usb_config_descriptor *config, struct usb_device *dev) +{ + info(" Configuration: %d\r\n", config->bConfigurationValue); + info(" - Interfaces: %d %s%s%dmA\r\n", config->bNumInterfaces, (config->bmAttributes & 0x40) ? "Self Powered " : "Bus Powered ", (config->bmAttributes & 0x20) ? "Remote Wakeup " : "", config->MaxPower*2); + if(config->iConfiguration) + { + info(" - "); + usb_display_string(dev, config->iConfiguration); + info("\r\n"); + } +} + +void usb_display_if_desc(struct usb_interface_descriptor *ifdesc, struct usb_device *dev) +{ + info(" Interface: %d\r\n", ifdesc->bInterfaceNumber); + info(" - Alternate Setting %d, Endpoints: %d\r\n", ifdesc->bAlternateSetting, ifdesc->bNumEndpoints); + info(" - Class "); + usb_display_class_sub(ifdesc->bInterfaceClass, ifdesc->bInterfaceSubClass, ifdesc->bInterfaceProtocol); + info("\r\n"); + if(ifdesc->iInterface) + { + info(" - "); + usb_display_string(dev, ifdesc->iInterface); + info("\r\n"); + } +} + +void usb_display_ep_desc(struct usb_endpoint_descriptor *epdesc) +{ + info(" - Endpoint %d %s ", epdesc->bEndpointAddress & 0xf, (epdesc->bEndpointAddress & 0x80) ? "In" : "Out"); + switch((epdesc->bmAttributes & 0x03)) + { + case 0: info("Control"); break; + case 1: info("Isochronous"); break; + case 2: info("Bulk"); break; + case 3: info("Interrupt"); break; + } + info(" MaxPacket %d", epdesc->wMaxPacketSize); + if((epdesc->bmAttributes & 0x03) == 0x3) + info(" Interval %dms", epdesc->bInterval); + info("\r\n"); +} + +/* main routine to diasplay the configs, interfaces and endpoints */ +void usb_display_config(struct usb_device *dev) +{ + struct usb_config_descriptor *config; + struct usb_interface_descriptor *ifdesc; + struct usb_endpoint_descriptor *epdesc; + int i, ii; + config = &dev->config; + usb_display_conf_desc(config, dev); + for(i = 0; i < config->no_of_if; i++) + { + ifdesc = &config->if_desc[i]; + usb_display_if_desc(ifdesc, dev); + for(ii = 0; ii < ifdesc->no_of_ep; ii++) + { + epdesc = &ifdesc->ep_desc[ii]; + usb_display_ep_desc(epdesc); + } + } + info("\r\n"); +} + +static inline char *portspeed(int speed) +{ + if(speed == USB_SPEED_HIGH) + return "480 Mb/s"; + else if(speed == USB_SPEED_LOW) + return "1.5 Mb/s"; + else + return "12 Mb/s"; +} + +/* shows the device tree recursively */ +void usb_show_tree_graph(struct usb_device *dev, char *pre) +{ + int i, index; + int has_child, last_child, port; + index = strlen(pre); + info(" %s", pre); + /* check if the device has connected children */ + has_child = 0; + for(i = 0; i < dev->maxchild; i++) + { + if(dev->children[i] != NULL) + has_child = 1; + } + /* check if we are the last one */ + last_child = 1; + if(dev->parent != NULL) + { + for(i = 0; i < dev->parent->maxchild; i++) + { + /* search for children */ + if(dev->parent->children[i] == dev) + { + /* found our pointer, see if we have a little sister */ + port = i; + while(i++ < dev->parent->maxchild) + { + if(dev->parent->children[i] != NULL) + { + /* found a sister */ + last_child = 0; + break; + } /* if */ + } /* while */ + } /* device found */ + } /* for all children of the parent */ + info("\b+-"); + /* correct last child */ + if(last_child) + pre[index-1] = ' '; + } /* if not root hub */ + else + info(" "); + info("%d ", dev->devnum); + pre[index++] = ' '; + pre[index++] = has_child ? '|' : ' '; + pre[index] = 0; + info(" %s (%s, %dmA)\r\n", usb_get_class_desc(dev->config.if_desc[0].bInterfaceClass), portspeed(dev->speed), dev->config.MaxPower * 2); + if(strlen(dev->mf) || strlen(dev->prod) || strlen(dev->serial)) + info(" %s %s %s %s\r\n", pre, dev->mf, dev->prod, dev->serial); + info(" %s\r\n", pre); + if(dev->maxchild > 0) + { + for(i = 0; i < dev->maxchild; i++) + { + if(dev->children[i] != NULL) + { + usb_show_tree_graph(dev->children[i], pre); + pre[index] = 0; + } + } + } +} + +/* main routine for the tree command */ +void usb_show_tree(struct usb_device *dev) +{ + char preamble[32]; + memset(preamble, 0, 32); + usb_show_tree_graph(dev, &preamble[0]); +} + +/****************************************************************************** + * usb command intepreter + */ +int uif_cmd_usb(int argc, char **argv) +{ + int i; + struct usb_device *dev = NULL; +#ifdef CONFIG_USB_STORAGE + block_dev_desc_t *stor_dev; +#endif +#ifdef RESET_START_STOP_CMDS + extern char usb_started; + if((strncmp(argv[1], "reset", 5) == 0) || (strncmp(argv[1], "start", 5) == 0)) + { + usb_stop(); + info("(Re)start USB...\r\n"); + i = usb_init(0, NULL); +#ifdef CONFIG_USB_STORAGE + /* try to recognize storage devices immediately */ + if(i >= 0) + usb_stor_curr_dev = usb_stor_scan(); +#endif /* CONFIG_USB_STORAGE */ + return 0; + } + if(strncmp(argv[1], "stop", 4) == 0) + { +#ifdef CONFIG_USB_KEYBOARD + if(argc == 2) + { + if(usb_kbd_deregister() != 0) + { + info("USB not stopped: usbkbd still using USB\r\n"); + return 1; + } + } + else /* forced stop, switch console in to serial */ + usb_kbd_deregister(); +#endif /* CONFIG_USB_KEYBOARD */ + info("stopping USB..\r\n"); + usb_stop(); + return 0; + } + if(!usb_started) + { + info("USB is stopped. Please issue 'usb start' first.\r\n"); + return 1; + } +#endif /* RESET_START_STOP_CMDS */ + if(strncmp(argv[1], "tree", 4) == 0) + { + info("\r\nUSB Device Tree:\r\n"); + for(i = 0; i < USB_MAX_BUS; i++) + { + struct usb_device *dev = usb_get_dev_index(0, i); + if(dev == NULL) + break; + info(" USB controller %d:\r\n", i); + usb_show_tree(dev); + } + return 0; + } + if(strncmp(argv[1], "inf", 3) == 0) + { + int b, d; + if(argc == 2) + { + for(b = 0; b < USB_MAX_BUS; b++) + { + for(d = 0; d < USB_MAX_DEVICE; d++) + { + dev = usb_get_dev_index(d, b); + if(dev == NULL) + break; + if(!d) + info("USB controller %d:\r\n\n", b); + usb_display_desc(dev); + usb_display_config(dev); + } + } + return 0; + } + else + { + int d; + i = strtoul(argv[2], NULL, 16); + info("config for device %d\r\n", i); + for(d = 0; d < USB_MAX_DEVICE; d++) + { + dev = usb_get_dev_index(d, 0); + if(dev == NULL) + break; + if(dev->devnum == i) + break; + } + if(dev == NULL) + { + info("*** NO Device avaiable ***\r\n"); + return 0; + } + else + { + usb_display_desc(dev); + usb_display_config(dev); + } + } + return 0; + } +#ifdef CONFIG_USB_STORAGE + if(strncmp(argv[1], "stor", 4) == 0) + return usb_stor_info(); + if(strcmp(argv[1], "read") == 0) + { + if(usb_stor_curr_dev < 0) + { + info("no current device selected\r\n"); + return 1; + } + if(argc == 5) + { + unsigned long addr = strtoul(argv[2], NULL, 16); + unsigned long blk = strtoul(argv[3], NULL, 16); + unsigned long cnt = strtoul(argv[4], NULL, 16); + unsigned long n; + info("\r\nUSB read: device %d block # %ld, count %ld ... ", usb_stor_curr_dev, blk, cnt); + stor_dev = usb_stor_get_dev(usb_stor_curr_dev); + n = stor_dev->block_read(usb_stor_curr_dev, blk, cnt, (unsigned long *)addr); + info("%ld blocks read: %s\r\n", n, (n == cnt) ? "OK" : "ERROR"); + if(n == cnt) + return 0; + return 1; + } + } + if(strncmp(argv[1], "dev", 3) == 0) + { + if(argc == 3) + { + int dev = (int)strtoul(argv[2], NULL, 10); + info("\r\nUSB device %d: ", dev); + if(dev >= USB_MAX_STOR_DEV) + { + info("unknown device\r\n"); + return 1; + } + info("\r\n Device %d: ", dev); + stor_dev = usb_stor_get_dev(dev); + dev_print(stor_dev); + if(stor_dev->type == DEV_TYPE_UNKNOWN) + return 1; + usb_stor_curr_dev = dev; + info("... is now current device\r\n"); + return 0; + } + else + { + info("\r\nUSB device %d: ", usb_stor_curr_dev); + stor_dev = usb_stor_get_dev(usb_stor_curr_dev); + dev_print(stor_dev); + if(stor_dev->type == DEV_TYPE_UNKNOWN) + return 1; + return 0; + } + return 0; + } + info( +#ifdef RESET_START_STOP_CMDS + "usb reset - reset (rescan) USB controller\r\n" + "usb stop [f] - stop USB [f]=force stop\r\n" +#endif + "usb tree - show USB device tree\r\n" + "usb info [dev] - show available USB devices\r\n" + "usb storage - show details of USB storage devices\r\n" + "usb dev [dev] - show or set current USB storage device\r\n" + "usb read addr blk# cnt - read `cnt' blocks starting at block `blk#'\r\n" + " to memory address `addr'\r\n"); +#else /* !CONFIG_USB_STORAGE */ + info( +#ifdef RESET_START_STOP_CMDS + "usb reset - reset (rescan) USB controller\r\n" + "usb stop [f] - stop USB [f]=force stop\r\n" +#endif + "usb tree - show USB device tree\r\n" + "usb info [dev] - show available USB devices\r\n"); +#endif /* CONFIG_USB_STORAGE */ + return 1; +} + +#endif /* CONFIG_USB_UHCI || CONFIG_USB_OHCI || CONFIG_USB_EHCI */ diff --git a/i2cspi_BaS_gcc/sources/ehci-hcd.c b/i2cspi_BaS_gcc/sources/ehci-hcd.c new file mode 100644 index 0000000..253e4f8 --- /dev/null +++ b/i2cspi_BaS_gcc/sources/ehci-hcd.c @@ -0,0 +1,1187 @@ +/*- + * 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(NETWORK) && defined(LWIP) +#include "../freertos/FreeRTOS.h" +#include "../freertos/queue.h" +extern xQueueHandle queue_poll_hub; +#define USB_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; +} gehci; + +#ifdef DEBUG +#define debug(format, arg...) board_printf("DEBUG: " format, ## arg) +#else +#define debug(format, arg...) do {} while (0) +#endif /* DEBUG */ +#if defined(DEBUG) || defined(USB_POLL_HUB) +#define err(format, arg...) sprintD(usb_error_str, "ERROR: " format "\r\n", ## arg); board_printf(usb_error_str) +#else +#define err(format, arg...) sprintD(usb_error_str, "ERROR: " format "\r\n", ## arg); kprint(usb_error_str) +#endif /* DEBUG */ +#ifdef SHOW_INFO +#ifdef COLDFIRE +#define info(format, arg...) board_printf("INFO: " format "\r\n", ## arg) +#else +#define info(format, arg...) board_printf("INFO: " format "\r\n", ## arg) +#endif /* COLDFIRE */ +#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 NETWORK +#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 */ +#endif /* NETWORK */ +#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 */ + +typedef struct +{ + long ident; + union + { + long l; + short i[2]; + char c[4]; + } v; +} COOKIE; + +extern COOKIE *get_cookie(long id); +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) +{ + 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 NETWORK +#ifdef LWIP + extern unsigned long pxCurrentTCB, tid_TOS; + extern void flush_dc(void); + if(pxCurrentTCB != tid_TOS) + flush_dc(); + else +#endif /* LWIP */ +#endif /* NETWORK */ +#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; + do + { + result = ehci_readl(ptr); + if(result == ~(uint32_t)0) + return -1; + result &= mask; + if(result == done) + return 0; + udelay(1); + usec--; + } + 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 */ + 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 = "\16\3u\0-\0b\0o\0o\0t\0"; + srclen = 14; + break; + case 2: /* Product */ + srcptr = "\52\3E\0H\0C\0I\0 \0H\0o\0s\0t\0 \0C\0o\0n\0t\0r\0o\0l\0l\0e\0r\0"; + srclen = 42; + 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; +} + +#ifdef CONFIG_USB_INTERRUPT_POLLING +void ehci_usb_event_poll(int interrupt) +{ + if(interrupt); +} +#else +void ehci_usb_enable_interrupt(int enable) +{ + if(enable); +} +#endif + +static int handle_usb_interrupt(struct ehci *ehci) +{ + uint32_t status; +#ifndef CONFIG_USB_MEM_NO_CACHE +#ifdef COLDFIRE /* no bus snooping on Coldfire */ +#ifdef NETWORK +#ifdef LWIP + extern unsigned long pxCurrentTCB, tid_TOS; + extern void flush_dc(void); + if(pxCurrentTCB != tid_TOS) + flush_dc(); + else +#endif /* LWIP */ +#endif /* NETWORK */ +#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_USB_MEM_NO_CACHE */ + 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) + 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 + } + } + ehci_writel(&ehci->hcor->or_usbsts, status); + return 1; /* clear interrupt, 0: disable interrupt */ +} + +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; + 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 + COOKIE *p = 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(&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%08x start 0x%08x \r\n offset 0x%08x dmaoffset 0x%08x length 0x%08x", + 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); + /* 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); + /* 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); + 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); + /* 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 */ + /* 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/i2cspi_BaS_gcc/sources/ohci-hcd.c b/i2cspi_BaS_gcc/sources/ohci-hcd.c new file mode 100644 index 0000000..506cf50 --- /dev/null +++ b/i2cspi_BaS_gcc/sources/ohci-hcd.c @@ -0,0 +1,2122 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB and PCI bus. + * + * Interrupt support is added. Now, it has been tested + * on ULI1575 chip 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 "config.h" + +#ifdef CONFIG_USB_OHCI + +#include "usb.h" +#include "ohci.h" + +#if defined(COLDFIRE) && defined(NETWORK) && defined(LWIP) +#include "../freertos/FreeRTOS.h" +#include "../freertos/queue.h" +extern xQueueHandle queue_poll_hub; +#define USB_POLL_HUB +#endif + + +#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...) board_printf("DEBUG: " format "\r\n", ## arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif /* DEBUG */ +#if defined(DEBUG) || defined(USB_POLL_HUB) +#define err(format, arg...) sprintD(usb_error_str, "ERROR: " format "\r\n", ## arg); if(ohci->irq) board_printf(usb_error_str) +#else +#define err(format, arg...) sprintD(usb_error_str, "ERROR: " format "\r\n", ## arg); if(ohci->irq) kprint(usb_error_str) +#endif +#ifdef SHOW_INFO +#ifdef COLDFIRE +#define info(format, arg...) board_printf("INFO: " format "\r\n", ## arg) +#else +#define info(format, arg...) board_printf("INFO: " format "\r\n", ## arg) +#endif /* COLDFIRE */ +#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) + +typedef struct +{ + long ident; + union + { + long l; + short i[2]; + char c[4]; + } v; +} COOKIE; + +extern COOKIE *get_cookie(long id); +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 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); + 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; +} + +/*-------------------------------------------------------------------------*/ + +/* 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); + 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[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 */ +#ifndef CONFIG_USB_MEM_NO_CACHE +#ifdef COLDFIRE /* no bus snooping on Coldfire */ +#ifdef NETWORK +#ifdef LWIP + extern unsigned long pxCurrentTCB, tid_TOS; + extern void flush_dc(void); + if(pxCurrentTCB != tid_TOS) + flush_dc(); + else +#endif /* LWIP */ +#endif /* NETWORK */ +#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 +#else /* !COLDFIRE */ + asm volatile (" cpusha DC\n\t"); +#endif /* COLDFIRE */ +#endif /* CONFIG_USB_MEM_NO_CACHE */ +#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 err = read_config_longword(handle, PCIIDR, &id); +#else + long err = Read_config_longword(handle, PCIIDR, &id); +#endif + if((err >= 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; + (unsigned long)pci_rsc_desc += (unsigned long)pci_rsc_desc->next; + } + 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)) + { +#ifdef MCF547X + dbg("USB OHCI set 48MHz clock\r\n"); +#ifdef PCI_XBIOS + write_config_longword(ohci->handle, 0xE4, 0x21); // oscillator & disable ehci +#else + Write_config_longword(ohci->handle, 0xE4, 0x21); // oscillator & disable ehci +#endif +#else /* !MCF547X */ +#if 0 +#ifdef PCI_XBIOS + write_config_longword(ohci->handle, 0xE4, 0x01); // disable ehci +#else + Write_config_longword(ohci->handle, 0xE4, 0x01); // disable ehci +#endif +#endif +#endif /* MCF547X */ + } +#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)) + { + 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 + } +#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 ;\r\n", 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); + /* 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); +#ifdef OHCI_USE_NPS + /* required for AMD-756 and some Mac platforms */ + writel((roothub_a(ohci) | RH_A_NPS) & ~RH_A_PSM, &ohci->regs->roothub.a); + writel(RH_HS_LPSC, &ohci->regs->roothub.status); +#endif /* OHCI_USE_NPS */ +#define mdelay(n) ({unsigned long msec = (n); while (msec--) udelay(1000); }) + /* POTPGT delay is bits 24-31, in 2 ms units. */ + mdelay((roothub_a(ohci) >> 23) & 0x1fe); + /* connect the virtual root hub */ + ohci->rh.devnum = 0; + return 0; +} + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_USB_INTERRUPT_POLLING + +/* Poll USB interrupt. */ +void ohci_usb_event_poll(int interrupt) +{ + if(ohci_inited) + { + int i; +#ifndef CONFIG_USB_MEM_NO_CACHE +#ifdef COLDFIRE /* no bus snooping on Coldfire */ +#ifdef NETWORK +#ifdef LWIP + extern unsigned long pxCurrentTCB, tid_TOS; + extern void flush_dc(void); + if(pxCurrentTCB != tid_TOS) + flush_dc(); + else +#endif /* LWIP */ +#endif /* NETWORK */ +#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 +#else /* !COLDFIRE */ + asm volatile (" cpusha DC\n\t"); +#endif /* COLDFIRE */ +#endif /* CONFIG_USB_MEM_NO_CACHE */ + for(i = 0; i < (sizeof(gohci) / sizeof(ohci_t)); i++) + { + ohci_t *ohci = &gohci[i]; + if(!ohci->handle) + continue; + 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 0 + if((ohci->hcca->done_head != 0) && !(m32_swap(ohci->hcca->done_head) & 0x01)) + ints = OHCI_INTR_WDH; + else +#endif + { + 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) +{ +#ifndef CONFIG_USB_MEM_NO_CACHE +#ifdef COLDFIRE /* no bus snooping on Coldfire */ +#if 1 +#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 +#else + extern void flush_dc(void); + flush_dc(); /* native interrupt */ +#endif +#else /* !COLDFIRE */ + asm volatile (" cpusha DC\n\t"); +#endif /* COLDFIRE */ +#endif /* CONFIG_USB_MEM_NO_CACHE */ + 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; + COOKIE *p = 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; /* 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%08x start 0x%08x \r\n offset 0x%08x dmaoffset 0x%08x length 0x%08x", + 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); +#else + if(ohci->irq) + wait_ms(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 */ +